list() {
+ return caller.invoke("session.commands.list", java.util.Map.of("sessionId", this.sessionId), SessionCommandsListResult.class);
+ }
+
+ /**
+ * Invokes {@code session.commands.invoke}.
+ *
+ * Note: the {@code sessionId} field in the params record is overridden
+ * by the session-scoped wrapper; any value provided is ignored.
+ * @since 1.0.0
+ */
+ public CompletableFuture invoke(SessionCommandsInvokeParams params) {
+ com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params);
+ _p.put("sessionId", this.sessionId);
+ return caller.invoke("session.commands.invoke", _p, Void.class);
+ }
+
/**
* Invokes {@code session.commands.handlePendingCommand}.
*
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsInvokeParams.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsInvokeParams.java
new file mode 100644
index 0000000000..141ec9524d
--- /dev/null
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsInvokeParams.java
@@ -0,0 +1,31 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+// AUTO-GENERATED FILE - DO NOT EDIT
+// Generated from: api.schema.json
+
+package com.github.copilot.sdk.generated.rpc;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import javax.annotation.processing.Generated;
+
+/**
+ * Request parameters for the {@code session.commands.invoke} RPC method.
+ *
+ * @since 1.0.0
+ */
+@javax.annotation.processing.Generated("copilot-sdk-codegen")
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record SessionCommandsInvokeParams(
+ /** Target session identifier */
+ @JsonProperty("sessionId") String sessionId,
+ /** Command name. Leading slashes are stripped and the name is matched case-insensitively. */
+ @JsonProperty("name") String name,
+ /** Raw input after the command name */
+ @JsonProperty("input") String input
+) {
+}
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsListParams.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsListParams.java
new file mode 100644
index 0000000000..dd51f0e768
--- /dev/null
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsListParams.java
@@ -0,0 +1,27 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+// AUTO-GENERATED FILE - DO NOT EDIT
+// Generated from: api.schema.json
+
+package com.github.copilot.sdk.generated.rpc;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import javax.annotation.processing.Generated;
+
+/**
+ * Request parameters for the {@code session.commands.list} RPC method.
+ *
+ * @since 1.0.0
+ */
+@javax.annotation.processing.Generated("copilot-sdk-codegen")
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record SessionCommandsListParams(
+ /** Target session identifier */
+ @JsonProperty("sessionId") String sessionId
+) {
+}
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsListResult.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsListResult.java
new file mode 100644
index 0000000000..e819ba64c1
--- /dev/null
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsListResult.java
@@ -0,0 +1,28 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+// AUTO-GENERATED FILE - DO NOT EDIT
+// Generated from: api.schema.json
+
+package com.github.copilot.sdk.generated.rpc;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
+import javax.annotation.processing.Generated;
+
+/**
+ * Result for the {@code session.commands.list} RPC method.
+ *
+ * @since 1.0.0
+ */
+@javax.annotation.processing.Generated("copilot-sdk-codegen")
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record SessionCommandsListResult(
+ /** Commands available in this session */
+ @JsonProperty("commands") List commands
+) {
+}
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteApi.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteApi.java
index df458acda5..790e1a7255 100644
--- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteApi.java
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteApi.java
@@ -18,6 +18,8 @@
@javax.annotation.processing.Generated("copilot-sdk-codegen")
public final class SessionRemoteApi {
+ private static final com.fasterxml.jackson.databind.ObjectMapper MAPPER = RpcMapper.INSTANCE;
+
private final RpcCaller caller;
private final String sessionId;
@@ -29,12 +31,17 @@ public final class SessionRemoteApi {
/**
* Invokes {@code session.remote.enable}.
+ *
+ * Note: the {@code sessionId} field in the params record is overridden
+ * by the session-scoped wrapper; any value provided is ignored.
*
* @apiNote This method is experimental and may change in a future version.
* @since 1.0.0
*/
- public CompletableFuture enable() {
- return caller.invoke("session.remote.enable", java.util.Map.of("sessionId", this.sessionId), SessionRemoteEnableResult.class);
+ public CompletableFuture enable(SessionRemoteEnableParams params) {
+ com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params);
+ _p.put("sessionId", this.sessionId);
+ return caller.invoke("session.remote.enable", _p, SessionRemoteEnableResult.class);
}
/**
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteEnableParams.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteEnableParams.java
index d8b7917b78..aa1fff7a23 100644
--- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteEnableParams.java
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteEnableParams.java
@@ -22,6 +22,8 @@
@JsonIgnoreProperties(ignoreUnknown = true)
public record SessionRemoteEnableParams(
/** Target session identifier */
- @JsonProperty("sessionId") String sessionId
+ @JsonProperty("sessionId") String sessionId,
+ /** Per-session remote mode. "off" disables remote, "export" exports session events to Mission Control without enabling remote steering, "on" enables both export and remote steering. */
+ @JsonProperty("mode") RemoteSessionMode mode
) {
}
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsApi.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsApi.java
index b32419a3e6..6f46d19d75 100644
--- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsApi.java
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsApi.java
@@ -75,8 +75,8 @@ public CompletableFuture disable(SessionSkillsDisableParams params) {
* @apiNote This method is experimental and may change in a future version.
* @since 1.0.0
*/
- public CompletableFuture reload() {
- return caller.invoke("session.skills.reload", java.util.Map.of("sessionId", this.sessionId), Void.class);
+ public CompletableFuture reload() {
+ return caller.invoke("session.skills.reload", java.util.Map.of("sessionId", this.sessionId), SessionSkillsReloadResult.class);
}
}
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsReloadResult.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsReloadResult.java
index 1bfca46e1a..1333d57cc1 100644
--- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsReloadResult.java
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsReloadResult.java
@@ -10,6 +10,7 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
import javax.annotation.processing.Generated;
/**
@@ -20,5 +21,10 @@
@javax.annotation.processing.Generated("copilot-sdk-codegen")
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
-public record SessionSkillsReloadResult() {
+public record SessionSkillsReloadResult(
+ /** Warnings emitted while loading skills (e.g. skills that loaded but had issues) */
+ @JsonProperty("warnings") List warnings,
+ /** Errors emitted while loading skills (e.g. skills that failed to load entirely) */
+ @JsonProperty("errors") List errors
+) {
}
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkParams.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkParams.java
index 06ea46c932..644be05ee0 100644
--- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkParams.java
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkParams.java
@@ -24,6 +24,8 @@ public record SessionsForkParams(
/** Source session ID to fork from */
@JsonProperty("sessionId") String sessionId,
/** Optional event ID boundary. When provided, the fork includes only events before this ID (exclusive). When omitted, all events are included. */
- @JsonProperty("toEventId") String toEventId
+ @JsonProperty("toEventId") String toEventId,
+ /** Optional friendly name to assign to the forked session. */
+ @JsonProperty("name") String name
) {
}
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkResult.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkResult.java
index 911574f568..77543916ec 100644
--- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkResult.java
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkResult.java
@@ -22,6 +22,8 @@
@JsonIgnoreProperties(ignoreUnknown = true)
public record SessionsForkResult(
/** The new forked session's ID */
- @JsonProperty("sessionId") String sessionId
+ @JsonProperty("sessionId") String sessionId,
+ /** Friendly name assigned to the forked session, if any. */
+ @JsonProperty("name") String name
) {
}
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInfo.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInfo.java
new file mode 100644
index 0000000000..e494f4894c
--- /dev/null
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInfo.java
@@ -0,0 +1,35 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+// AUTO-GENERATED FILE - DO NOT EDIT
+// Generated from: api.schema.json
+
+package com.github.copilot.sdk.generated.rpc;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
+import javax.annotation.processing.Generated;
+
+@javax.annotation.processing.Generated("copilot-sdk-codegen")
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record SlashCommandInfo(
+ /** Canonical command name without a leading slash */
+ @JsonProperty("name") String name,
+ /** Canonical aliases without leading slashes */
+ @JsonProperty("aliases") List aliases,
+ /** Human-readable command description */
+ @JsonProperty("description") String description,
+ /** Coarse command category for grouping and behavior: runtime built-in, skill-backed command, or SDK/client-owned command */
+ @JsonProperty("kind") SlashCommandKind kind,
+ /** Optional unstructured input hint */
+ @JsonProperty("input") SlashCommandInput input,
+ /** Whether the command may run while an agent turn is active */
+ @JsonProperty("allowDuringAgentExecution") Boolean allowDuringAgentExecution,
+ /** Whether the command is experimental */
+ @JsonProperty("experimental") Boolean experimental
+) {
+}
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInput.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInput.java
new file mode 100644
index 0000000000..186dec5a81
--- /dev/null
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInput.java
@@ -0,0 +1,33 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+// AUTO-GENERATED FILE - DO NOT EDIT
+// Generated from: api.schema.json
+
+package com.github.copilot.sdk.generated.rpc;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import javax.annotation.processing.Generated;
+
+/**
+ * Optional unstructured input hint
+ *
+ * @since 1.0.0
+ */
+@javax.annotation.processing.Generated("copilot-sdk-codegen")
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record SlashCommandInput(
+ /** Hint to display when command input has not been provided */
+ @JsonProperty("hint") String hint,
+ /** When true, the command requires non-empty input; clients should render the input hint as required */
+ @JsonProperty("required") Boolean required,
+ /** Optional completion hint for the input (e.g. 'directory' for filesystem path completion) */
+ @JsonProperty("completion") SlashCommandInputCompletion completion,
+ /** When true, clients should pass the full text after the command name as a single argument rather than splitting on whitespace */
+ @JsonProperty("preserveMultilineInput") Boolean preserveMultilineInput
+) {
+}
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInputCompletion.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInputCompletion.java
new file mode 100644
index 0000000000..c192fa9c0f
--- /dev/null
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInputCompletion.java
@@ -0,0 +1,33 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+// AUTO-GENERATED FILE - DO NOT EDIT
+// Generated from: api.schema.json
+
+package com.github.copilot.sdk.generated.rpc;
+
+import javax.annotation.processing.Generated;
+
+/**
+ * Optional completion hint for the input (e.g. 'directory' for filesystem path completion)
+ *
+ * @since 1.0.0
+ */
+@javax.annotation.processing.Generated("copilot-sdk-codegen")
+public enum SlashCommandInputCompletion {
+ /** The {@code directory} variant. */
+ DIRECTORY("directory");
+
+ private final String value;
+ SlashCommandInputCompletion(String value) { this.value = value; }
+ @com.fasterxml.jackson.annotation.JsonValue
+ public String getValue() { return value; }
+ @com.fasterxml.jackson.annotation.JsonCreator
+ public static SlashCommandInputCompletion fromValue(String value) {
+ for (SlashCommandInputCompletion v : values()) {
+ if (v.value.equals(value)) return v;
+ }
+ throw new IllegalArgumentException("Unknown SlashCommandInputCompletion value: " + value);
+ }
+}
diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandKind.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandKind.java
new file mode 100644
index 0000000000..1f08c4efcd
--- /dev/null
+++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandKind.java
@@ -0,0 +1,37 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+// AUTO-GENERATED FILE - DO NOT EDIT
+// Generated from: api.schema.json
+
+package com.github.copilot.sdk.generated.rpc;
+
+import javax.annotation.processing.Generated;
+
+/**
+ * Coarse command category for grouping and behavior: runtime built-in, skill-backed command, or SDK/client-owned command
+ *
+ * @since 1.0.0
+ */
+@javax.annotation.processing.Generated("copilot-sdk-codegen")
+public enum SlashCommandKind {
+ /** The {@code builtin} variant. */
+ BUILTIN("builtin"),
+ /** The {@code skill} variant. */
+ SKILL("skill"),
+ /** The {@code client} variant. */
+ CLIENT("client");
+
+ private final String value;
+ SlashCommandKind(String value) { this.value = value; }
+ @com.fasterxml.jackson.annotation.JsonValue
+ public String getValue() { return value; }
+ @com.fasterxml.jackson.annotation.JsonCreator
+ public static SlashCommandKind fromValue(String value) {
+ for (SlashCommandKind v : values()) {
+ if (v.value.equals(value)) return v;
+ }
+ throw new IllegalArgumentException("Unknown SlashCommandKind value: " + value);
+ }
+}
diff --git a/src/main/java/com/github/copilot/sdk/CliServerManager.java b/src/main/java/com/github/copilot/sdk/CliServerManager.java
index 3087966d39..bd4effe5a8 100644
--- a/src/main/java/com/github/copilot/sdk/CliServerManager.java
+++ b/src/main/java/com/github/copilot/sdk/CliServerManager.java
@@ -90,16 +90,16 @@ ProcessInfo startCliServer() throws IOException, InterruptedException {
}
// Default UseLoggedInUser to false when GitHubToken is provided
- boolean useLoggedInUser = options.getUseLoggedInUser() != null
- ? options.getUseLoggedInUser()
- : (options.getGitHubToken() == null || options.getGitHubToken().isEmpty());
+ boolean useLoggedInUser = options.getUseLoggedInUser()
+ .orElse(options.getGitHubToken() == null || options.getGitHubToken().isEmpty());
if (!useLoggedInUser) {
args.add("--no-auto-login");
}
- if (options.getSessionIdleTimeoutSeconds() != null && options.getSessionIdleTimeoutSeconds() > 0) {
+ if (options.getSessionIdleTimeoutSeconds().isPresent()
+ && options.getSessionIdleTimeoutSeconds().getAsInt() > 0) {
args.add("--session-idle-timeout");
- args.add(String.valueOf(options.getSessionIdleTimeoutSeconds()));
+ args.add(String.valueOf(options.getSessionIdleTimeoutSeconds().getAsInt()));
}
if (options.isRemote()) {
@@ -159,9 +159,9 @@ ProcessInfo startCliServer() throws IOException, InterruptedException {
if (telemetry.getSourceName() != null) {
pb.environment().put("COPILOT_OTEL_SOURCE_NAME", telemetry.getSourceName());
}
- if (telemetry.getCaptureContent() != null) {
+ if (telemetry.getCaptureContent().isPresent()) {
pb.environment().put("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT",
- telemetry.getCaptureContent() ? "true" : "false");
+ telemetry.getCaptureContent().get() ? "true" : "false");
}
}
diff --git a/src/main/java/com/github/copilot/sdk/CopilotClient.java b/src/main/java/com/github/copilot/sdk/CopilotClient.java
index 5b988d9d2b..4d0770319a 100644
--- a/src/main/java/com/github/copilot/sdk/CopilotClient.java
+++ b/src/main/java/com/github/copilot/sdk/CopilotClient.java
@@ -120,7 +120,7 @@ public CopilotClient(CopilotClientOptions options) {
// Validate auth options with external server
if (this.options.getCliUrl() != null && !this.options.getCliUrl().isEmpty()
- && (this.options.getGitHubToken() != null || this.options.getUseLoggedInUser() != null)) {
+ && (this.options.getGitHubToken() != null || this.options.getUseLoggedInUser().isPresent())) {
throw new IllegalArgumentException(
"GitHubToken and UseLoggedInUser cannot be used with CliUrl (external server manages its own auth)");
}
diff --git a/src/main/java/com/github/copilot/sdk/CopilotSession.java b/src/main/java/com/github/copilot/sdk/CopilotSession.java
index ad5561c09e..79a7f343b7 100644
--- a/src/main/java/com/github/copilot/sdk/CopilotSession.java
+++ b/src/main/java/com/github/copilot/sdk/CopilotSession.java
@@ -1118,9 +1118,9 @@ private void handleElicitationRequestAsync(ElicitationContext context, String re
*/
private void assertElicitation() {
SessionCapabilities caps = capabilities;
- if (caps == null || caps.getUi() == null || !Boolean.TRUE.equals(caps.getUi().getElicitation())) {
+ if (caps == null || caps.getUi() == null || !caps.getUi().getElicitation().orElse(false)) {
throw new IllegalStateException("Elicitation is not supported by the host. "
- + "Check session.getCapabilities().getUi()?.getElicitation() before calling UI methods.");
+ + "Check session.getCapabilities().getUi().getElicitation().orElse(false) before calling UI methods.");
}
}
@@ -1201,10 +1201,10 @@ public CompletableFuture input(String message, InputOptions options) {
field.put("title", options.getTitle());
if (options.getDescription() != null)
field.put("description", options.getDescription());
- if (options.getMinLength() != null)
- field.put("minLength", options.getMinLength());
- if (options.getMaxLength() != null)
- field.put("maxLength", options.getMaxLength());
+ if (options.getMinLength().isPresent())
+ field.put("minLength", options.getMinLength().getAsInt());
+ if (options.getMaxLength().isPresent())
+ field.put("maxLength", options.getMaxLength().getAsInt());
if (options.getFormat() != null)
field.put("format", options.getFormat());
if (options.getDefaultValue() != null)
@@ -1695,7 +1695,8 @@ public CompletableFuture setModel(String model, String reasoningEffort,
ModelCapabilitiesOverrideSupports supports = null;
if (modelCapabilities.getSupports() != null) {
var s = modelCapabilities.getSupports();
- supports = new ModelCapabilitiesOverrideSupports(s.getVision(), s.getReasoningEffort());
+ supports = new ModelCapabilitiesOverrideSupports(s.getVision().orElse(null),
+ s.getReasoningEffort().orElse(null));
}
ModelCapabilitiesOverrideLimits limits = null;
if (modelCapabilities.getLimits() != null) {
diff --git a/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java b/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java
index d9752db7ee..52bfb3337f 100644
--- a/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java
+++ b/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java
@@ -111,12 +111,18 @@ 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);
+ config.getEnableSessionTelemetry().ifPresent(request::setEnableSessionTelemetry);
+ if (config.getOnUserInputRequest() != null) {
+ request.setRequestUserInput(true);
+ }
+ if (config.getHooks() != null && config.getHooks().hasHooks()) {
+ request.setHooks(true);
+ }
request.setWorkingDirectory(config.getWorkingDirectory());
- request.setStreaming(config.isStreaming() ? true : null);
- request.setIncludeSubAgentStreamingEvents(config.getIncludeSubAgentStreamingEvents());
+ if (config.isStreaming()) {
+ request.setStreaming(true);
+ }
+ config.getIncludeSubAgentStreamingEvents().ifPresent(request::setIncludeSubAgentStreamingEvents);
request.setMcpServers(config.getMcpServers());
request.setCustomAgents(config.getCustomAgents());
request.setDefaultAgent(config.getDefaultAgent());
@@ -126,7 +132,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess
request.setInstructionDirectories(config.getInstructionDirectories());
request.setDisabledSkills(config.getDisabledSkills());
request.setConfigDir(config.getConfigDir());
- request.setEnableConfigDiscovery(config.getEnableConfigDiscovery());
+ config.getEnableConfigDiscovery().ifPresent(request::setEnableConfigDiscovery);
request.setModelCapabilities(config.getModelCapabilities());
if (config.getCommands() != null && !config.getCommands().isEmpty()) {
@@ -145,6 +151,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess
request.setRequestAutoModeSwitch(true);
}
request.setGitHubToken(config.getGitHubToken());
+ request.setRemoteSession(config.getRemoteSession());
return request;
}
@@ -194,15 +201,23 @@ 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);
+ config.getEnableSessionTelemetry().ifPresent(request::setEnableSessionTelemetry);
+ if (config.getOnUserInputRequest() != null) {
+ request.setRequestUserInput(true);
+ }
+ if (config.getHooks() != null && config.getHooks().hasHooks()) {
+ request.setHooks(true);
+ }
request.setWorkingDirectory(config.getWorkingDirectory());
request.setConfigDir(config.getConfigDir());
- request.setEnableConfigDiscovery(config.getEnableConfigDiscovery());
- request.setDisableResume(config.isDisableResume() ? true : null);
- request.setStreaming(config.isStreaming() ? true : null);
- request.setIncludeSubAgentStreamingEvents(config.getIncludeSubAgentStreamingEvents());
+ config.getEnableConfigDiscovery().ifPresent(request::setEnableConfigDiscovery);
+ if (config.isDisableResume()) {
+ request.setDisableResume(true);
+ }
+ if (config.isStreaming()) {
+ request.setStreaming(true);
+ }
+ config.getIncludeSubAgentStreamingEvents().ifPresent(request::setIncludeSubAgentStreamingEvents);
request.setMcpServers(config.getMcpServers());
request.setCustomAgents(config.getCustomAgents());
request.setDefaultAgent(config.getDefaultAgent());
@@ -229,6 +244,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo
request.setRequestAutoModeSwitch(true);
}
request.setGitHubToken(config.getGitHubToken());
+ request.setRemoteSession(config.getRemoteSession());
return request;
}
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 5517df631e..e4605ffe10 100644
--- a/src/main/java/com/github/copilot/sdk/json/CopilotClientOptions.java
+++ b/src/main/java/com/github/copilot/sdk/json/CopilotClientOptions.java
@@ -14,6 +14,9 @@
import java.util.function.Supplier;
import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.util.Optional;
+import java.util.OptionalInt;
/**
* Configuration options for creating a
@@ -499,34 +502,47 @@ public CopilotClientOptions setTelemetry(TelemetryConfig telemetry) {
/**
* Gets the server-wide idle timeout for sessions in seconds.
*
- * @return the session idle timeout in seconds, or {@code null} to disable
- * (sessions live indefinitely)
+ * @return an {@link OptionalInt} containing the session idle timeout in
+ * seconds, or {@link java.util.OptionalInt#empty()} if not set. Use
+ * {@link #clearSessionIdleTimeoutSeconds()} to revert to the default.
* @since 1.3.0
*/
- public Integer getSessionIdleTimeoutSeconds() {
- return sessionIdleTimeoutSeconds;
+ @JsonIgnore
+ public OptionalInt getSessionIdleTimeoutSeconds() {
+ return sessionIdleTimeoutSeconds == null ? OptionalInt.empty() : OptionalInt.of(sessionIdleTimeoutSeconds);
}
/**
* Sets the server-wide idle timeout for sessions in seconds.
*
* Sessions without activity for this duration are automatically cleaned up. Set
- * to {@code 0} or leave as {@code null} to disable (sessions live
- * indefinitely).
+ * to {@code 0} to disable (sessions live indefinitely). Use
+ * {@link #clearSessionIdleTimeoutSeconds()} to revert to the default.
*
* 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 sessionIdleTimeoutSeconds
- * the idle timeout in seconds, or {@code null} to disable
+ * the idle timeout in seconds
* @return this options instance for method chaining
* @since 1.3.0
*/
- public CopilotClientOptions setSessionIdleTimeoutSeconds(Integer sessionIdleTimeoutSeconds) {
+ public CopilotClientOptions setSessionIdleTimeoutSeconds(int sessionIdleTimeoutSeconds) {
this.sessionIdleTimeoutSeconds = sessionIdleTimeoutSeconds;
return this;
}
+ /**
+ * Clears the sessionIdleTimeoutSeconds setting, reverting to the default
+ * behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public CopilotClientOptions clearSessionIdleTimeoutSeconds() {
+ this.sessionIdleTimeoutSeconds = null;
+ return this;
+ }
+
/**
* Gets the connection token for the headless CLI server (TCP only).
*
@@ -555,11 +571,11 @@ public CopilotClientOptions setTcpConnectionToken(String tcpConnectionToken) {
/**
* Returns whether to use the logged-in user for authentication.
*
- * @return {@code true} to use logged-in user auth, {@code false} to use only
- * explicit tokens, or {@code null} to use default behavior
+ * @return an {@link Optional} containing the boolean value, or empty if not set
*/
- public Boolean getUseLoggedInUser() {
- return useLoggedInUser;
+ @JsonIgnore
+ public Optional getUseLoggedInUser() {
+ return Optional.ofNullable(useLoggedInUser);
}
/**
@@ -569,15 +585,23 @@ public Boolean getUseLoggedInUser() {
* auth. When false, only explicit tokens (gitHubToken or environment variables)
* are used. Default: true (but defaults to false when gitHubToken is provided).
*
- * Passing {@code null} is equivalent to passing {@link Boolean#FALSE}.
*
* @param useLoggedInUser
- * {@code true} to use logged-in user auth, {@code false} or
- * {@code null} otherwise
+ * {@code true} to use logged-in user auth, {@code false} otherwise
* @return this options instance for method chaining
*/
- public CopilotClientOptions setUseLoggedInUser(Boolean useLoggedInUser) {
- this.useLoggedInUser = useLoggedInUser != null ? useLoggedInUser : Boolean.FALSE;
+ public CopilotClientOptions setUseLoggedInUser(boolean useLoggedInUser) {
+ this.useLoggedInUser = useLoggedInUser;
+ return this;
+ }
+
+ /**
+ * Clears the useLoggedInUser setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public CopilotClientOptions clearUseLoggedInUser() {
+ this.useLoggedInUser = null;
return this;
}
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 12bab4154c..d6bcc7b2b7 100644
--- a/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java
+++ b/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java
@@ -124,6 +124,9 @@ public final class CreateSessionRequest {
@JsonProperty("gitHubToken")
private String gitHubToken;
+ @JsonProperty("remoteSession")
+ private String remoteSession;
+
/** Gets the model name. @return the model */
public String getModel() {
return model;
@@ -224,40 +227,68 @@ public Boolean getEnableSessionTelemetry() {
/**
* Sets enable session telemetry flag. @param enableSessionTelemetry the flag
*/
- public void setEnableSessionTelemetry(Boolean enableSessionTelemetry) {
+ public void setEnableSessionTelemetry(boolean enableSessionTelemetry) {
this.enableSessionTelemetry = enableSessionTelemetry;
}
+ /**
+ * Clears the enableSessionTelemetry setting, reverting to the default behavior.
+ */
+ public void clearEnableSessionTelemetry() {
+ this.enableSessionTelemetry = null;
+ }
+
/** Gets request permission flag. @return the flag */
public Boolean getRequestPermission() {
return requestPermission;
}
/** Sets request permission flag. @param requestPermission the flag */
- public void setRequestPermission(Boolean requestPermission) {
+ public void setRequestPermission(boolean requestPermission) {
this.requestPermission = requestPermission;
}
+ /**
+ * Clears the requestPermission setting, reverting to the default behavior.
+ */
+ public void clearRequestPermission() {
+ this.requestPermission = null;
+ }
+
/** Gets request user input flag. @return the flag */
public Boolean getRequestUserInput() {
return requestUserInput;
}
/** Sets request user input flag. @param requestUserInput the flag */
- public void setRequestUserInput(Boolean requestUserInput) {
+ public void setRequestUserInput(boolean requestUserInput) {
this.requestUserInput = requestUserInput;
}
+ /**
+ * Clears the requestUserInput setting, reverting to the default behavior.
+ */
+ public void clearRequestUserInput() {
+ this.requestUserInput = null;
+ }
+
/** Gets hooks flag. @return the flag */
public Boolean getHooks() {
return hooks;
}
/** Sets hooks flag. @param hooks the flag */
- public void setHooks(Boolean hooks) {
+ public void setHooks(boolean hooks) {
this.hooks = hooks;
}
+ /**
+ * Clears the hooks setting, reverting to the default behavior.
+ */
+ public void clearHooks() {
+ this.hooks = null;
+ }
+
/** Gets working directory. @return the working directory */
public String getWorkingDirectory() {
return workingDirectory;
@@ -274,10 +305,17 @@ public Boolean getStreaming() {
}
/** Sets streaming flag. @param streaming the flag */
- public void setStreaming(Boolean streaming) {
+ public void setStreaming(boolean streaming) {
this.streaming = streaming;
}
+ /**
+ * Clears the streaming setting, reverting to the default behavior.
+ */
+ public void clearStreaming() {
+ this.streaming = null;
+ }
+
/** Gets MCP servers. @return the servers map */
public Map getMcpServers() {
return mcpServers == null ? null : Collections.unmodifiableMap(mcpServers);
@@ -388,10 +426,17 @@ public Boolean getEnableConfigDiscovery() {
}
/** Sets enable config discovery flag. @param enableConfigDiscovery the flag */
- public void setEnableConfigDiscovery(Boolean enableConfigDiscovery) {
+ public void setEnableConfigDiscovery(boolean enableConfigDiscovery) {
this.enableConfigDiscovery = enableConfigDiscovery;
}
+ /**
+ * Clears the enableConfigDiscovery setting, reverting to the default behavior.
+ */
+ public void clearEnableConfigDiscovery() {
+ this.enableConfigDiscovery = null;
+ }
+
/** Gets include sub-agent streaming events flag. @return the flag */
public Boolean getIncludeSubAgentStreamingEvents() {
return includeSubAgentStreamingEvents;
@@ -401,10 +446,18 @@ public Boolean getIncludeSubAgentStreamingEvents() {
* Sets include sub-agent streaming events flag. @param
* includeSubAgentStreamingEvents the flag
*/
- public void setIncludeSubAgentStreamingEvents(Boolean includeSubAgentStreamingEvents) {
+ public void setIncludeSubAgentStreamingEvents(boolean includeSubAgentStreamingEvents) {
this.includeSubAgentStreamingEvents = includeSubAgentStreamingEvents;
}
+ /**
+ * Clears the includeSubAgentStreamingEvents setting, reverting to the default
+ * behavior.
+ */
+ public void clearIncludeSubAgentStreamingEvents() {
+ this.includeSubAgentStreamingEvents = null;
+ }
+
/** Gets the commands wire definitions. @return the commands */
public List getCommands() {
return commands == null ? null : Collections.unmodifiableList(commands);
@@ -421,10 +474,17 @@ public Boolean getRequestElicitation() {
}
/** Sets the requestElicitation flag. @param requestElicitation the flag */
- public void setRequestElicitation(Boolean requestElicitation) {
+ public void setRequestElicitation(boolean requestElicitation) {
this.requestElicitation = requestElicitation;
}
+ /**
+ * Clears the requestElicitation setting, reverting to the default behavior.
+ */
+ public void clearRequestElicitation() {
+ this.requestElicitation = null;
+ }
+
/** Gets the requestExitPlanMode flag. @return the flag */
public Boolean getRequestExitPlanMode() {
return requestExitPlanMode;
@@ -471,4 +531,16 @@ public String getGitHubToken() {
public void setGitHubToken(String gitHubToken) {
this.gitHubToken = gitHubToken;
}
+
+ /** Gets the remote session mode. @return the remote session mode */
+ public String getRemoteSession() {
+ return remoteSession;
+ }
+
+ /**
+ * Sets the remote session mode. @param remoteSession the remote session mode
+ */
+ public void setRemoteSession(String remoteSession) {
+ this.remoteSession = remoteSession;
+ }
}
diff --git a/src/main/java/com/github/copilot/sdk/json/CustomAgentConfig.java b/src/main/java/com/github/copilot/sdk/json/CustomAgentConfig.java
index 1421db603f..bb9520055e 100644
--- a/src/main/java/com/github/copilot/sdk/json/CustomAgentConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/CustomAgentConfig.java
@@ -10,6 +10,8 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.util.Optional;
/**
* Configuration for a custom agent in a Copilot session.
@@ -197,10 +199,12 @@ public CustomAgentConfig setMcpServers(Map mcpServers)
/**
* Gets whether inference mode is enabled.
*
- * @return the infer flag, or {@code null} if not set
+ * @return an {@link java.util.Optional} containing the infer flag, or
+ * {@link java.util.Optional#empty()} if not set
*/
- public Boolean getInfer() {
- return infer;
+ @JsonIgnore
+ public Optional getInfer() {
+ return Optional.ofNullable(infer);
}
/**
@@ -210,11 +214,21 @@ public Boolean getInfer() {
* {@code true} to enable inference mode
* @return this config for method chaining
*/
- public CustomAgentConfig setInfer(Boolean infer) {
+ public CustomAgentConfig setInfer(boolean infer) {
this.infer = infer;
return this;
}
+ /**
+ * Clears the infer setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public CustomAgentConfig clearInfer() {
+ this.infer = null;
+ return this;
+ }
+
/**
* Gets the list of skill names to preload into this agent's context.
*
diff --git a/src/main/java/com/github/copilot/sdk/json/InfiniteSessionConfig.java b/src/main/java/com/github/copilot/sdk/json/InfiniteSessionConfig.java
index d45851f8a1..561796ede7 100644
--- a/src/main/java/com/github/copilot/sdk/json/InfiniteSessionConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/InfiniteSessionConfig.java
@@ -6,6 +6,9 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.util.Optional;
+import java.util.OptionalDouble;
/**
* Configuration for infinite sessions with automatic context compaction and
@@ -43,10 +46,12 @@ public class InfiniteSessionConfig {
/**
* Gets whether infinite sessions are enabled.
*
- * @return {@code true} if enabled, {@code null} to use default (true)
+ * @return an {@link Optional} containing the boolean value, or empty to use
+ * default (true)
*/
- public Boolean getEnabled() {
- return enabled;
+ @JsonIgnore
+ public Optional getEnabled() {
+ return Optional.ofNullable(enabled);
}
/**
@@ -58,18 +63,32 @@ public Boolean getEnabled() {
* {@code true} to enable infinite sessions
* @return this config instance for method chaining
*/
- public InfiniteSessionConfig setEnabled(Boolean enabled) {
+ public InfiniteSessionConfig setEnabled(boolean enabled) {
this.enabled = enabled;
return this;
}
+ /**
+ * Clears the enabled setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public InfiniteSessionConfig clearEnabled() {
+ this.enabled = null;
+ return this;
+ }
+
/**
* Gets the background compaction threshold.
*
- * @return the threshold (0.0-1.0), or {@code null} to use default
+ * @return an {@link OptionalDouble} containing the threshold (0.0-1.0), or
+ * empty to use default
*/
- public Double getBackgroundCompactionThreshold() {
- return backgroundCompactionThreshold;
+ @JsonIgnore
+ public OptionalDouble getBackgroundCompactionThreshold() {
+ return backgroundCompactionThreshold == null
+ ? OptionalDouble.empty()
+ : OptionalDouble.of(backgroundCompactionThreshold);
}
/**
@@ -82,18 +101,33 @@ public Double getBackgroundCompactionThreshold() {
* the threshold (0.0-1.0)
* @return this config instance for method chaining
*/
- public InfiniteSessionConfig setBackgroundCompactionThreshold(Double backgroundCompactionThreshold) {
+ public InfiniteSessionConfig setBackgroundCompactionThreshold(double backgroundCompactionThreshold) {
this.backgroundCompactionThreshold = backgroundCompactionThreshold;
return this;
}
+ /**
+ * Clears the backgroundCompactionThreshold setting, reverting to the default
+ * behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public InfiniteSessionConfig clearBackgroundCompactionThreshold() {
+ this.backgroundCompactionThreshold = null;
+ return this;
+ }
+
/**
* Gets the buffer exhaustion threshold.
*
- * @return the threshold (0.0-1.0), or {@code null} to use default
+ * @return an {@link OptionalDouble} containing the threshold (0.0-1.0), or
+ * empty to use default
*/
- public Double getBufferExhaustionThreshold() {
- return bufferExhaustionThreshold;
+ @JsonIgnore
+ public OptionalDouble getBufferExhaustionThreshold() {
+ return bufferExhaustionThreshold == null
+ ? OptionalDouble.empty()
+ : OptionalDouble.of(bufferExhaustionThreshold);
}
/**
@@ -107,8 +141,20 @@ public Double getBufferExhaustionThreshold() {
* the threshold (0.0-1.0)
* @return this config instance for method chaining
*/
- public InfiniteSessionConfig setBufferExhaustionThreshold(Double bufferExhaustionThreshold) {
+ public InfiniteSessionConfig setBufferExhaustionThreshold(double bufferExhaustionThreshold) {
this.bufferExhaustionThreshold = bufferExhaustionThreshold;
return this;
}
+
+ /**
+ * Clears the bufferExhaustionThreshold setting, reverting to the default
+ * behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public InfiniteSessionConfig clearBufferExhaustionThreshold() {
+ this.bufferExhaustionThreshold = null;
+ return this;
+ }
+
}
diff --git a/src/main/java/com/github/copilot/sdk/json/InputOptions.java b/src/main/java/com/github/copilot/sdk/json/InputOptions.java
index 9b0b6c8dd8..ca1cc5d385 100644
--- a/src/main/java/com/github/copilot/sdk/json/InputOptions.java
+++ b/src/main/java/com/github/copilot/sdk/json/InputOptions.java
@@ -4,17 +4,25 @@
package com.github.copilot.sdk.json;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.OptionalInt;
+
/**
* Options for the {@link SessionUiApi#input(String, InputOptions)} convenience
* method.
*
* @since 1.0.0
*/
+@JsonInclude(JsonInclude.Include.NON_NULL)
public class InputOptions {
private String title;
private String description;
+ @JsonProperty("minLength")
private Integer minLength;
+ @JsonProperty("maxLength")
private Integer maxLength;
private String format;
private String defaultValue;
@@ -46,34 +54,66 @@ public InputOptions setDescription(String description) {
return this;
}
- /** Gets the minimum character length. @return the min length */
- public Integer getMinLength() {
- return minLength;
+ /**
+ * Gets the minimum character length.
+ *
+ * @return an {@link java.util.OptionalInt} containing the min length, or
+ * {@link java.util.OptionalInt#empty()} if not set
+ */
+ @JsonIgnore
+ public OptionalInt getMinLength() {
+ return minLength == null ? OptionalInt.empty() : OptionalInt.of(minLength);
}
/**
* Sets the minimum character length. @param minLength the min length @return
* this
*/
- public InputOptions setMinLength(Integer minLength) {
+ public InputOptions setMinLength(int minLength) {
this.minLength = minLength;
return this;
}
- /** Gets the maximum character length. @return the max length */
- public Integer getMaxLength() {
- return maxLength;
+ /**
+ * Clears the minLength setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public InputOptions clearMinLength() {
+ this.minLength = null;
+ return this;
+ }
+
+ /**
+ * Gets the maximum character length.
+ *
+ * @return an {@link java.util.OptionalInt} containing the max length, or
+ * {@link java.util.OptionalInt#empty()} if not set
+ */
+ @JsonIgnore
+ public OptionalInt getMaxLength() {
+ return maxLength == null ? OptionalInt.empty() : OptionalInt.of(maxLength);
}
/**
* Sets the maximum character length. @param maxLength the max length @return
* this
*/
- public InputOptions setMaxLength(Integer maxLength) {
+ public InputOptions setMaxLength(int maxLength) {
this.maxLength = maxLength;
return this;
}
+ /**
+ * Clears the maxLength setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public InputOptions clearMaxLength() {
+ this.maxLength = null;
+ return this;
+ }
+
/**
* Gets the semantic format hint (e.g., {@code "email"}, {@code "uri"},
* {@code "date"}, {@code "date-time"}).
diff --git a/src/main/java/com/github/copilot/sdk/json/ModelCapabilitiesOverride.java b/src/main/java/com/github/copilot/sdk/json/ModelCapabilitiesOverride.java
index 18701ad671..02727d4b5c 100644
--- a/src/main/java/com/github/copilot/sdk/json/ModelCapabilitiesOverride.java
+++ b/src/main/java/com/github/copilot/sdk/json/ModelCapabilitiesOverride.java
@@ -7,6 +7,9 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.util.Optional;
+import java.util.OptionalInt;
/**
* Per-property overrides for model capabilities, deep-merged over runtime
@@ -106,48 +109,73 @@ public static class Supports {
/**
* Gets the vision override.
*
- * @return {@code true} to enable vision, {@code false} to disable, or
- * {@code null} to use the runtime default
+ * @return an {@link java.util.Optional} containing {@code true} to enable
+ * vision or {@code false} to disable, or
+ * {@link java.util.Optional#empty()} to use the runtime default
*/
- public Boolean getVision() {
- return vision;
+ @JsonIgnore
+ public Optional getVision() {
+ return Optional.ofNullable(vision);
}
/**
- * Sets whether vision (image input) is enabled.
+ * Sets whether vision (image input) is enabled. Use {@link #clearVision()} to
+ * revert to the runtime default.
*
* @param vision
- * {@code true} to enable, {@code false} to disable, or {@code null}
- * to use the runtime default
+ * {@code true} to enable, {@code false} to disable
* @return this instance for method chaining
*/
- public Supports setVision(Boolean vision) {
+ public Supports setVision(boolean vision) {
this.vision = vision;
return this;
}
+ /**
+ * Clears the vision setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public Supports clearVision() {
+ this.vision = null;
+ return this;
+ }
+
/**
* Gets the reasoning effort override.
*
- * @return {@code true} to enable reasoning effort, {@code false} to disable, or
- * {@code null} to use the runtime default
+ * @return an {@link java.util.Optional} containing {@code true} to enable
+ * reasoning effort or {@code false} to disable, or
+ * {@link java.util.Optional#empty()} to use the runtime default
*/
- public Boolean getReasoningEffort() {
- return reasoningEffort;
+ @JsonIgnore
+ public Optional getReasoningEffort() {
+ return Optional.ofNullable(reasoningEffort);
}
/**
- * Sets whether reasoning effort configuration is enabled.
+ * Sets whether reasoning effort configuration is enabled. Use
+ * {@link #clearReasoningEffort()} to revert to the runtime default.
*
* @param reasoningEffort
- * {@code true} to enable, {@code false} to disable, or {@code null}
- * to use the runtime default
+ * {@code true} to enable, {@code false} to disable
* @return this instance for method chaining
*/
- public Supports setReasoningEffort(Boolean reasoningEffort) {
+ public Supports setReasoningEffort(boolean reasoningEffort) {
this.reasoningEffort = reasoningEffort;
return this;
}
+
+ /**
+ * Clears the reasoningEffort setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public Supports clearReasoningEffort() {
+ this.reasoningEffort = null;
+ return this;
+ }
+
}
/**
@@ -174,8 +202,9 @@ public static class Limits {
*
* @return the override value, or {@code null} to use the runtime default
*/
- public Integer getMaxPromptTokens() {
- return maxPromptTokens;
+ @JsonIgnore
+ public OptionalInt getMaxPromptTokens() {
+ return maxPromptTokens == null ? OptionalInt.empty() : OptionalInt.of(maxPromptTokens);
}
/**
@@ -185,18 +214,29 @@ public Integer getMaxPromptTokens() {
* the override value, or {@code null} to use the runtime default
* @return this instance for method chaining
*/
- public Limits setMaxPromptTokens(Integer maxPromptTokens) {
+ public Limits setMaxPromptTokens(int maxPromptTokens) {
this.maxPromptTokens = maxPromptTokens;
return this;
}
+ /**
+ * Clears the maxPromptTokens setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public Limits clearMaxPromptTokens() {
+ this.maxPromptTokens = null;
+ return this;
+ }
+
/**
* Gets the maximum output tokens override.
*
* @return the override value, or {@code null} to use the runtime default
*/
- public Integer getMaxOutputTokens() {
- return maxOutputTokens;
+ @JsonIgnore
+ public OptionalInt getMaxOutputTokens() {
+ return maxOutputTokens == null ? OptionalInt.empty() : OptionalInt.of(maxOutputTokens);
}
/**
@@ -206,18 +246,29 @@ public Integer getMaxOutputTokens() {
* the override value, or {@code null} to use the runtime default
* @return this instance for method chaining
*/
- public Limits setMaxOutputTokens(Integer maxOutputTokens) {
+ public Limits setMaxOutputTokens(int maxOutputTokens) {
this.maxOutputTokens = maxOutputTokens;
return this;
}
+ /**
+ * Clears the maxOutputTokens setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public Limits clearMaxOutputTokens() {
+ this.maxOutputTokens = null;
+ return this;
+ }
+
/**
* Gets the maximum context window tokens override.
*
* @return the override value, or {@code null} to use the runtime default
*/
- public Integer getMaxContextWindowTokens() {
- return maxContextWindowTokens;
+ @JsonIgnore
+ public OptionalInt getMaxContextWindowTokens() {
+ return maxContextWindowTokens == null ? OptionalInt.empty() : OptionalInt.of(maxContextWindowTokens);
}
/**
@@ -227,9 +278,20 @@ public Integer getMaxContextWindowTokens() {
* the override value, or {@code null} to use the runtime default
* @return this instance for method chaining
*/
- public Limits setMaxContextWindowTokens(Integer maxContextWindowTokens) {
+ public Limits setMaxContextWindowTokens(int maxContextWindowTokens) {
this.maxContextWindowTokens = maxContextWindowTokens;
return this;
}
+
+ /**
+ * Clears the maxContextWindowTokens setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public Limits clearMaxContextWindowTokens() {
+ this.maxContextWindowTokens = null;
+ return this;
+ }
+
}
}
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 8947696c9a..1c5e6fcc7f 100644
--- a/src/main/java/com/github/copilot/sdk/json/ProviderConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/ProviderConfig.java
@@ -9,6 +9,8 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.util.OptionalInt;
/**
* Configuration for a custom API provider (BYOK - Bring Your Own Key).
@@ -296,10 +298,12 @@ public ProviderConfig setWireModel(String wireModel) {
/**
* Gets the maximum prompt token override.
*
- * @return the max prompt tokens, or {@code null} if not set
+ * @return an {@link java.util.OptionalInt} containing the max prompt tokens, or
+ * {@link java.util.OptionalInt#empty()} if not set
*/
- public Integer getMaxPromptTokens() {
- return maxPromptTokens;
+ @JsonIgnore
+ public OptionalInt getMaxPromptTokens() {
+ return maxPromptTokens == null ? OptionalInt.empty() : OptionalInt.of(maxPromptTokens);
}
/**
@@ -314,18 +318,30 @@ public Integer getMaxPromptTokens() {
* the max prompt tokens
* @return this config for method chaining
*/
- public ProviderConfig setMaxPromptTokens(Integer maxPromptTokens) {
+ public ProviderConfig setMaxPromptTokens(int maxPromptTokens) {
this.maxPromptTokens = maxPromptTokens;
return this;
}
+ /**
+ * Clears the maxPromptTokens setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public ProviderConfig clearMaxPromptTokens() {
+ this.maxPromptTokens = null;
+ return this;
+ }
+
/**
* Gets the maximum output token override.
*
- * @return the max output tokens, or {@code null} if not set
+ * @return an {@link java.util.OptionalInt} containing the max output tokens, or
+ * {@link java.util.OptionalInt#empty()} if not set
*/
- public Integer getMaxOutputTokens() {
- return maxOutputTokens;
+ @JsonIgnore
+ public OptionalInt getMaxOutputTokens() {
+ return maxOutputTokens == null ? OptionalInt.empty() : OptionalInt.of(maxOutputTokens);
}
/**
@@ -338,8 +354,19 @@ public Integer getMaxOutputTokens() {
* the max output tokens
* @return this config for method chaining
*/
- public ProviderConfig setMaxOutputTokens(Integer maxOutputTokens) {
+ public ProviderConfig setMaxOutputTokens(int maxOutputTokens) {
this.maxOutputTokens = maxOutputTokens;
return this;
}
+
+ /**
+ * Clears the maxOutputTokens setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public ProviderConfig clearMaxOutputTokens() {
+ this.maxOutputTokens = null;
+ 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 f2caad7710..72c9f6f47a 100644
--- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java
@@ -11,8 +11,10 @@
import java.util.function.Consumer;
import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.github.copilot.sdk.generated.SessionEvent;
+import java.util.Optional;
/**
* Configuration for resuming an existing Copilot session.
@@ -69,6 +71,7 @@ public class ResumeSessionConfig {
private ExitPlanModeHandler onExitPlanMode;
private AutoModeSwitchHandler onAutoModeSwitch;
private String gitHubToken;
+ private String remoteSession;
/**
* Gets the AI model to use.
@@ -234,7 +237,7 @@ public ResumeSessionConfig setProvider(ProviderConfig provider) {
/**
* Enables or disables internal session telemetry for this session. When
- * {@code false}, disables session telemetry. When {@code null} (the default) or
+ * {@code false}, disables session telemetry. When unset (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
@@ -242,15 +245,17 @@ public ResumeSessionConfig setProvider(ProviderConfig provider) {
* CopilotClientOptions.TelemetryConfig}, which configures OpenTelemetry export
* for observability.
*
- * @return whether session telemetry is enabled
+ * @return an {@link java.util.Optional} containing whether session telemetry is
+ * enabled, or {@link java.util.Optional#empty()} for the default
*/
- public Boolean getEnableSessionTelemetry() {
- return enableSessionTelemetry;
+ @JsonIgnore
+ public Optional getEnableSessionTelemetry() {
+ return Optional.ofNullable(enableSessionTelemetry);
}
/**
* Enables or disables internal session telemetry for this session. When
- * {@code false}, disables session telemetry. When {@code null} (the default) or
+ * {@code false}, disables session telemetry. When unset (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.
@@ -259,11 +264,21 @@ public Boolean getEnableSessionTelemetry() {
* whether to enable session telemetry
* @return this config for method chaining
*/
- public ResumeSessionConfig setEnableSessionTelemetry(Boolean enableSessionTelemetry) {
+ public ResumeSessionConfig setEnableSessionTelemetry(boolean enableSessionTelemetry) {
this.enableSessionTelemetry = enableSessionTelemetry;
return this;
}
+ /**
+ * Clears the enableSessionTelemetry setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public ResumeSessionConfig clearEnableSessionTelemetry() {
+ this.enableSessionTelemetry = null;
+ return this;
+ }
+
/**
* Gets the reasoning effort level.
*
@@ -403,8 +418,9 @@ public ResumeSessionConfig setConfigDir(String configDir) {
* @return {@code true} to enable discovery, {@code false} to disable, or
* {@code null} to use the runtime default
*/
- public Boolean getEnableConfigDiscovery() {
- return enableConfigDiscovery;
+ @JsonIgnore
+ public Optional getEnableConfigDiscovery() {
+ return Optional.ofNullable(enableConfigDiscovery);
}
/**
@@ -420,19 +436,30 @@ public Boolean getEnableConfigDiscovery() {
* {@code null} to use the runtime default
* @return this config for method chaining
*/
- public ResumeSessionConfig setEnableConfigDiscovery(Boolean enableConfigDiscovery) {
+ public ResumeSessionConfig setEnableConfigDiscovery(boolean enableConfigDiscovery) {
this.enableConfigDiscovery = enableConfigDiscovery;
return this;
}
+ /**
+ * Clears the enableConfigDiscovery setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public ResumeSessionConfig clearEnableConfigDiscovery() {
+ this.enableConfigDiscovery = null;
+ return this;
+ }
+
/**
* Gets whether sub-agent streaming events are included.
*
* @return {@code true} to include sub-agent streaming events, {@code false} to
* suppress them, or {@code null} to use the runtime default
*/
- public Boolean getIncludeSubAgentStreamingEvents() {
- return includeSubAgentStreamingEvents;
+ @JsonIgnore
+ public Optional getIncludeSubAgentStreamingEvents() {
+ return Optional.ofNullable(includeSubAgentStreamingEvents);
}
/**
@@ -443,11 +470,22 @@ public Boolean getIncludeSubAgentStreamingEvents() {
* suppress
* @return this config for method chaining
*/
- public ResumeSessionConfig setIncludeSubAgentStreamingEvents(Boolean includeSubAgentStreamingEvents) {
+ public ResumeSessionConfig setIncludeSubAgentStreamingEvents(boolean includeSubAgentStreamingEvents) {
this.includeSubAgentStreamingEvents = includeSubAgentStreamingEvents;
return this;
}
+ /**
+ * Clears the includeSubAgentStreamingEvents setting, reverting to the default
+ * behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public ResumeSessionConfig clearIncludeSubAgentStreamingEvents() {
+ this.includeSubAgentStreamingEvents = null;
+ return this;
+ }
+
/**
* Gets the model capabilities override.
*
@@ -849,6 +887,34 @@ public ResumeSessionConfig setGitHubToken(String gitHubToken) {
return this;
}
+ /**
+ * Gets the per-session remote behavior control.
+ *
+ * See {@link SessionConfig#getRemoteSession()} for details on possible values.
+ *
+ * @return the remote session mode, or {@code null} if not set
+ * @since 1.4.0
+ */
+ public String getRemoteSession() {
+ return remoteSession;
+ }
+
+ /**
+ * Sets the per-session remote behavior control.
+ *
+ * See {@link SessionConfig#setRemoteSession(String)} for details on possible
+ * values.
+ *
+ * @param remoteSession
+ * the remote session mode
+ * @return this config for method chaining
+ * @since 1.4.0
+ */
+ public ResumeSessionConfig setRemoteSession(String remoteSession) {
+ this.remoteSession = remoteSession;
+ return this;
+ }
+
/**
* Creates a shallow clone of this {@code ResumeSessionConfig} instance.
*
@@ -898,6 +964,7 @@ public ResumeSessionConfig clone() {
copy.onExitPlanMode = this.onExitPlanMode;
copy.onAutoModeSwitch = this.onAutoModeSwitch;
copy.gitHubToken = this.gitHubToken;
+ copy.remoteSession = this.remoteSession;
return copy;
}
}
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 a1af269700..8aca77b7d2 100644
--- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java
+++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java
@@ -128,6 +128,9 @@ public final class ResumeSessionRequest {
@JsonProperty("gitHubToken")
private String gitHubToken;
+ @JsonProperty("remoteSession")
+ private String remoteSession;
+
/** Gets the session ID. @return the session ID */
public String getSessionId() {
return sessionId;
@@ -231,40 +234,68 @@ public Boolean getEnableSessionTelemetry() {
/**
* Sets enable session telemetry flag. @param enableSessionTelemetry the flag
*/
- public void setEnableSessionTelemetry(Boolean enableSessionTelemetry) {
+ public void setEnableSessionTelemetry(boolean enableSessionTelemetry) {
this.enableSessionTelemetry = enableSessionTelemetry;
}
+ /**
+ * Clears the enableSessionTelemetry setting, reverting to the default behavior.
+ */
+ public void clearEnableSessionTelemetry() {
+ this.enableSessionTelemetry = null;
+ }
+
/** Gets request permission flag. @return the flag */
public Boolean getRequestPermission() {
return requestPermission;
}
/** Sets request permission flag. @param requestPermission the flag */
- public void setRequestPermission(Boolean requestPermission) {
+ public void setRequestPermission(boolean requestPermission) {
this.requestPermission = requestPermission;
}
+ /**
+ * Clears the requestPermission setting, reverting to the default behavior.
+ */
+ public void clearRequestPermission() {
+ this.requestPermission = null;
+ }
+
/** Gets request user input flag. @return the flag */
public Boolean getRequestUserInput() {
return requestUserInput;
}
/** Sets request user input flag. @param requestUserInput the flag */
- public void setRequestUserInput(Boolean requestUserInput) {
+ public void setRequestUserInput(boolean requestUserInput) {
this.requestUserInput = requestUserInput;
}
+ /**
+ * Clears the requestUserInput setting, reverting to the default behavior.
+ */
+ public void clearRequestUserInput() {
+ this.requestUserInput = null;
+ }
+
/** Gets hooks flag. @return the flag */
public Boolean getHooks() {
return hooks;
}
/** Sets hooks flag. @param hooks the flag */
- public void setHooks(Boolean hooks) {
+ public void setHooks(boolean hooks) {
this.hooks = hooks;
}
+ /**
+ * Clears the hooks setting, reverting to the default behavior.
+ */
+ public void clearHooks() {
+ this.hooks = null;
+ }
+
/** Gets working directory. @return the working directory */
public String getWorkingDirectory() {
return workingDirectory;
@@ -291,30 +322,51 @@ public Boolean getEnableConfigDiscovery() {
}
/** Sets enable config discovery flag. @param enableConfigDiscovery the flag */
- public void setEnableConfigDiscovery(Boolean enableConfigDiscovery) {
+ public void setEnableConfigDiscovery(boolean enableConfigDiscovery) {
this.enableConfigDiscovery = enableConfigDiscovery;
}
+ /**
+ * Clears the enableConfigDiscovery setting, reverting to the default behavior.
+ */
+ public void clearEnableConfigDiscovery() {
+ this.enableConfigDiscovery = null;
+ }
+
/** Gets disable resume flag. @return the flag */
public Boolean getDisableResume() {
return disableResume;
}
/** Sets disable resume flag. @param disableResume the flag */
- public void setDisableResume(Boolean disableResume) {
+ public void setDisableResume(boolean disableResume) {
this.disableResume = disableResume;
}
+ /**
+ * Clears the disableResume setting, reverting to the default behavior.
+ */
+ public void clearDisableResume() {
+ this.disableResume = null;
+ }
+
/** Gets streaming flag. @return the flag */
public Boolean getStreaming() {
return streaming;
}
/** Sets streaming flag. @param streaming the flag */
- public void setStreaming(Boolean streaming) {
+ public void setStreaming(boolean streaming) {
this.streaming = streaming;
}
+ /**
+ * Clears the streaming setting, reverting to the default behavior.
+ */
+ public void clearStreaming() {
+ this.streaming = null;
+ }
+
/** Gets include sub-agent streaming events flag. @return the flag */
public Boolean getIncludeSubAgentStreamingEvents() {
return includeSubAgentStreamingEvents;
@@ -324,10 +376,18 @@ public Boolean getIncludeSubAgentStreamingEvents() {
* Sets include sub-agent streaming events flag. @param
* includeSubAgentStreamingEvents the flag
*/
- public void setIncludeSubAgentStreamingEvents(Boolean includeSubAgentStreamingEvents) {
+ public void setIncludeSubAgentStreamingEvents(boolean includeSubAgentStreamingEvents) {
this.includeSubAgentStreamingEvents = includeSubAgentStreamingEvents;
}
+ /**
+ * Clears the includeSubAgentStreamingEvents setting, reverting to the default
+ * behavior.
+ */
+ public void clearIncludeSubAgentStreamingEvents() {
+ this.includeSubAgentStreamingEvents = null;
+ }
+
/** Gets MCP servers. @return the servers map */
public Map getMcpServers() {
return mcpServers == null ? null : Collections.unmodifiableMap(mcpServers);
@@ -441,10 +501,17 @@ public Boolean getRequestElicitation() {
}
/** Sets the requestElicitation flag. @param requestElicitation the flag */
- public void setRequestElicitation(Boolean requestElicitation) {
+ public void setRequestElicitation(boolean requestElicitation) {
this.requestElicitation = requestElicitation;
}
+ /**
+ * Clears the requestElicitation setting, reverting to the default behavior.
+ */
+ public void clearRequestElicitation() {
+ this.requestElicitation = null;
+ }
+
/** Gets the requestExitPlanMode flag. @return the flag */
public Boolean getRequestExitPlanMode() {
return requestExitPlanMode;
@@ -491,4 +558,16 @@ public String getGitHubToken() {
public void setGitHubToken(String gitHubToken) {
this.gitHubToken = gitHubToken;
}
+
+ /** Gets the remote session mode. @return the remote session mode */
+ public String getRemoteSession() {
+ return remoteSession;
+ }
+
+ /**
+ * Sets the remote session mode. @param remoteSession the remote session mode
+ */
+ public void setRemoteSession(String remoteSession) {
+ this.remoteSession = remoteSession;
+ }
}
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 51cad884ed..f1a383402f 100644
--- a/src/main/java/com/github/copilot/sdk/json/SessionConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/SessionConfig.java
@@ -11,8 +11,10 @@
import java.util.function.Consumer;
import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.github.copilot.sdk.generated.SessionEvent;
+import java.util.Optional;
/**
* Configuration for creating a new Copilot session.
@@ -69,6 +71,7 @@ public class SessionConfig {
private ExitPlanModeHandler onExitPlanMode;
private AutoModeSwitchHandler onAutoModeSwitch;
private String gitHubToken;
+ private String remoteSession;
/**
* Gets the custom session ID.
@@ -288,7 +291,7 @@ public SessionConfig setProvider(ProviderConfig provider) {
/**
* Enables or disables internal session telemetry for this session. When
- * {@code false}, disables session telemetry. When {@code null} (the default) or
+ * {@code false}, disables session telemetry. When unset (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
@@ -296,15 +299,17 @@ public SessionConfig setProvider(ProviderConfig provider) {
* CopilotClientOptions.TelemetryConfig}, which configures OpenTelemetry export
* for observability.
*
- * @return whether session telemetry is enabled
+ * @return an {@link java.util.Optional} containing whether session telemetry is
+ * enabled, or {@link java.util.Optional#empty()} for the default
*/
- public Boolean getEnableSessionTelemetry() {
- return enableSessionTelemetry;
+ @JsonIgnore
+ public Optional getEnableSessionTelemetry() {
+ return Optional.ofNullable(enableSessionTelemetry);
}
/**
* Enables or disables internal session telemetry for this session. When
- * {@code false}, disables session telemetry. When {@code null} (the default) or
+ * {@code false}, disables session telemetry. When unset (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.
@@ -313,11 +318,21 @@ public Boolean getEnableSessionTelemetry() {
* whether to enable session telemetry
* @return this config instance for method chaining
*/
- public SessionConfig setEnableSessionTelemetry(Boolean enableSessionTelemetry) {
+ public SessionConfig setEnableSessionTelemetry(boolean enableSessionTelemetry) {
this.enableSessionTelemetry = enableSessionTelemetry;
return this;
}
+ /**
+ * Clears the enableSessionTelemetry setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public SessionConfig clearEnableSessionTelemetry() {
+ this.enableSessionTelemetry = null;
+ return this;
+ }
+
/**
* Gets the permission request handler.
*
@@ -658,11 +673,13 @@ public SessionConfig setConfigDir(String configDir) {
/**
* Gets whether automatic configuration discovery is enabled.
*
- * @return {@code true} to enable discovery, {@code false} to disable, or
- * {@code null} to use the runtime default
+ * @return an {@link java.util.Optional} containing {@code true} to enable
+ * discovery or {@code false} to disable, or
+ * {@link java.util.Optional#empty()} to use the runtime default
*/
- public Boolean getEnableConfigDiscovery() {
- return enableConfigDiscovery;
+ @JsonIgnore
+ public Optional getEnableConfigDiscovery() {
+ return Optional.ofNullable(enableConfigDiscovery);
}
/**
@@ -676,23 +693,34 @@ public Boolean getEnableConfigDiscovery() {
* name collision.
*
* @param enableConfigDiscovery
- * {@code true} to enable discovery, {@code false} to disable, or
- * {@code null} to use the runtime default
+ * {@code true} to enable discovery, {@code false} to disable
* @return this config instance for method chaining
*/
- public SessionConfig setEnableConfigDiscovery(Boolean enableConfigDiscovery) {
+ public SessionConfig setEnableConfigDiscovery(boolean enableConfigDiscovery) {
this.enableConfigDiscovery = enableConfigDiscovery;
return this;
}
+ /**
+ * Clears the enableConfigDiscovery setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public SessionConfig clearEnableConfigDiscovery() {
+ this.enableConfigDiscovery = null;
+ return this;
+ }
+
/**
* Gets whether sub-agent streaming events are included.
*
- * @return {@code true} to include sub-agent streaming events, {@code false} to
- * suppress them, or {@code null} to use the runtime default
+ * @return an {@link java.util.Optional} containing {@code true} to include
+ * sub-agent streaming events or {@code false} to suppress them, or
+ * {@link java.util.Optional#empty()} to use the runtime default
*/
- public Boolean getIncludeSubAgentStreamingEvents() {
- return includeSubAgentStreamingEvents;
+ @JsonIgnore
+ public Optional getIncludeSubAgentStreamingEvents() {
+ return Optional.ofNullable(includeSubAgentStreamingEvents);
}
/**
@@ -709,11 +737,22 @@ public Boolean getIncludeSubAgentStreamingEvents() {
* suppress
* @return this config instance for method chaining
*/
- public SessionConfig setIncludeSubAgentStreamingEvents(Boolean includeSubAgentStreamingEvents) {
+ public SessionConfig setIncludeSubAgentStreamingEvents(boolean includeSubAgentStreamingEvents) {
this.includeSubAgentStreamingEvents = includeSubAgentStreamingEvents;
return this;
}
+ /**
+ * Clears the includeSubAgentStreamingEvents setting, reverting to the default
+ * behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public SessionConfig clearIncludeSubAgentStreamingEvents() {
+ this.includeSubAgentStreamingEvents = null;
+ return this;
+ }
+
/**
* Gets the model capabilities override.
*
@@ -901,6 +940,45 @@ public SessionConfig setGitHubToken(String gitHubToken) {
return this;
}
+ /**
+ * Gets the per-session remote behavior control.
+ *
+ * Possible values:
+ *
+ * - {@code "off"} — local only, no remote export (default)
+ * - {@code "export"} — export session events to GitHub without enabling
+ * remote steering
+ * - {@code "on"} — export to GitHub AND enable remote steering
+ *
+ *
+ * @return the remote session mode, or {@code null} if not set
+ * @since 1.4.0
+ */
+ public String getRemoteSession() {
+ return remoteSession;
+ }
+
+ /**
+ * Sets the per-session remote behavior control.
+ *
+ * Possible values:
+ *
+ * - {@code "off"} — local only, no remote export (default)
+ * - {@code "export"} — export session events to GitHub without enabling
+ * remote steering
+ * - {@code "on"} — export to GitHub AND enable remote steering
+ *
+ *
+ * @param remoteSession
+ * the remote session mode
+ * @return this config instance for method chaining
+ * @since 1.4.0
+ */
+ public SessionConfig setRemoteSession(String remoteSession) {
+ this.remoteSession = remoteSession;
+ return this;
+ }
+
/**
* Creates a shallow clone of this {@code SessionConfig} instance.
*
@@ -950,6 +1028,7 @@ public SessionConfig clone() {
copy.onExitPlanMode = this.onExitPlanMode;
copy.onAutoModeSwitch = this.onAutoModeSwitch;
copy.gitHubToken = this.gitHubToken;
+ copy.remoteSession = this.remoteSession;
return copy;
}
}
diff --git a/src/main/java/com/github/copilot/sdk/json/SessionUiCapabilities.java b/src/main/java/com/github/copilot/sdk/json/SessionUiCapabilities.java
index 9b8e0b5872..d19d531eef 100644
--- a/src/main/java/com/github/copilot/sdk/json/SessionUiCapabilities.java
+++ b/src/main/java/com/github/copilot/sdk/json/SessionUiCapabilities.java
@@ -4,23 +4,31 @@
package com.github.copilot.sdk.json;
+import java.util.Optional;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
/**
* UI-specific capability flags for a session.
*
* @since 1.0.0
*/
+@JsonInclude(JsonInclude.Include.NON_NULL)
public class SessionUiCapabilities {
+ @JsonProperty("elicitation")
private Boolean elicitation;
/**
* Returns whether the host supports interactive elicitation dialogs.
*
- * @return {@code true} if elicitation is supported, {@code false} or
- * {@code null} otherwise
+ * @return an {@link Optional} containing the boolean value, or empty if not set
*/
- public Boolean getElicitation() {
- return elicitation;
+ @JsonIgnore
+ public Optional getElicitation() {
+ return Optional.ofNullable(elicitation);
}
/**
@@ -30,8 +38,19 @@ public Boolean getElicitation() {
* {@code true} if elicitation is supported
* @return this instance for method chaining
*/
- public SessionUiCapabilities setElicitation(Boolean elicitation) {
+ public SessionUiCapabilities setElicitation(boolean elicitation) {
this.elicitation = elicitation;
return this;
}
+
+ /**
+ * Clears the elicitation setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public SessionUiCapabilities clearElicitation() {
+ this.elicitation = null;
+ return this;
+ }
+
}
diff --git a/src/main/java/com/github/copilot/sdk/json/TelemetryConfig.java b/src/main/java/com/github/copilot/sdk/json/TelemetryConfig.java
index 8407ab6098..7272c98841 100644
--- a/src/main/java/com/github/copilot/sdk/json/TelemetryConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/TelemetryConfig.java
@@ -4,6 +4,11 @@
package com.github.copilot.sdk.json;
+import java.util.Optional;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
/**
* OpenTelemetry configuration for the Copilot CLI server.
*
@@ -21,6 +26,7 @@
* @see CopilotClientOptions#setTelemetry(TelemetryConfig)
* @since 1.2.0
*/
+@JsonInclude(JsonInclude.Include.NON_NULL)
public class TelemetryConfig {
private String otlpEndpoint;
@@ -128,11 +134,13 @@ public TelemetryConfig setSourceName(String sourceName) {
* Maps to the {@code OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT}
* environment variable.
*
- * @return {@code true} to capture content, {@code false} to suppress it, or
- * {@code null} to use the default
+ * @return an {@link java.util.Optional} containing {@code true} to capture
+ * content or {@code false} to suppress it, or
+ * {@link java.util.Optional#empty()} to use the default
*/
- public Boolean getCaptureContent() {
- return captureContent;
+ @JsonIgnore
+ public Optional getCaptureContent() {
+ return Optional.ofNullable(captureContent);
}
/**
@@ -142,8 +150,19 @@ public Boolean getCaptureContent() {
* {@code true} to capture content, {@code false} to suppress it
* @return this config for method chaining
*/
- public TelemetryConfig setCaptureContent(Boolean captureContent) {
+ public TelemetryConfig setCaptureContent(boolean captureContent) {
this.captureContent = captureContent;
return this;
}
+
+ /**
+ * Clears the captureContent setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public TelemetryConfig clearCaptureContent() {
+ this.captureContent = null;
+ return this;
+ }
+
}
diff --git a/src/main/java/com/github/copilot/sdk/json/UserInputRequest.java b/src/main/java/com/github/copilot/sdk/json/UserInputRequest.java
index 9b466f866c..23b0d88123 100644
--- a/src/main/java/com/github/copilot/sdk/json/UserInputRequest.java
+++ b/src/main/java/com/github/copilot/sdk/json/UserInputRequest.java
@@ -8,7 +8,10 @@
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import java.util.Optional;
/**
* Request for user input from the agent.
@@ -19,6 +22,7 @@
* @since 1.0.6
*/
@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserInputRequest {
@JsonProperty("question")
@@ -75,11 +79,13 @@ public UserInputRequest setChoices(List choices) {
/**
* Returns whether freeform text input is allowed.
*
- * @return {@code true} if freeform input is allowed, {@code null} if not
+ * @return an {@link java.util.Optional} containing {@code true} if freeform
+ * input is allowed, or {@link java.util.Optional#empty()} if not
* specified
*/
- public Boolean getAllowFreeform() {
- return allowFreeform;
+ @JsonIgnore
+ public Optional getAllowFreeform() {
+ return Optional.ofNullable(allowFreeform);
}
/**
@@ -89,8 +95,19 @@ public Boolean getAllowFreeform() {
* {@code true} to allow freeform input
* @return this instance for method chaining
*/
- public UserInputRequest setAllowFreeform(Boolean allowFreeform) {
+ public UserInputRequest setAllowFreeform(boolean allowFreeform) {
this.allowFreeform = allowFreeform;
return this;
}
+
+ /**
+ * Clears the allowFreeform setting, reverting to the default behavior.
+ *
+ * @return this instance for method chaining
+ */
+ public UserInputRequest clearAllowFreeform() {
+ this.allowFreeform = null;
+ return this;
+ }
+
}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 0000000000..d912fb420f
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,26 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+/**
+ * GitHub Copilot SDK for Java.
+ */
+module com.github.copilot.sdk.java {
+ requires transitive com.fasterxml.jackson.annotation;
+ requires com.fasterxml.jackson.core;
+ requires transitive com.fasterxml.jackson.databind;
+ requires com.fasterxml.jackson.datatype.jsr310;
+ requires static com.github.spotbugs.annotations;
+ requires static java.compiler;
+ requires static java.net.http;
+ requires java.logging;
+
+ exports com.github.copilot.sdk;
+ exports com.github.copilot.sdk.generated;
+ exports com.github.copilot.sdk.generated.rpc;
+ exports com.github.copilot.sdk.json;
+
+ opens com.github.copilot.sdk to com.fasterxml.jackson.databind;
+ opens com.github.copilot.sdk.generated to com.fasterxml.jackson.databind;
+ opens com.github.copilot.sdk.json to com.fasterxml.jackson.databind;
+}
diff --git a/src/site/markdown/cookbook/error-handling.md b/src/site/markdown/cookbook/error-handling.md
index 74732006ee..963b8b0933 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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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 78f914f103..2a7b5540f9 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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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 42bb1f6222..84da5a2556 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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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 119448a440..ef80fd3d09 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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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 627ee5b842..1036bb0f7d 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.3-beta-java.2-beta-java.1
+//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4
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/ConfigCloneTest.java b/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java
index 7abc47e4a4..09bd3ee385 100644
--- a/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java
+++ b/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java
@@ -203,7 +203,7 @@ void sessionConfigEnableSessionTelemetryCopied() {
SessionConfig cloned = original.clone();
- assertFalse(cloned.getEnableSessionTelemetry());
+ assertFalse(cloned.getEnableSessionTelemetry().orElse(true));
}
@Test
@@ -212,7 +212,7 @@ void sessionConfigEnableSessionTelemetryDefaultIsNull() {
SessionConfig cloned = original.clone();
- assertNull(cloned.getEnableSessionTelemetry());
+ assertTrue(cloned.getEnableSessionTelemetry().isEmpty());
}
@Test
@@ -222,7 +222,7 @@ void resumeSessionConfigEnableSessionTelemetryCopied() {
ResumeSessionConfig cloned = original.clone();
- assertFalse(cloned.getEnableSessionTelemetry());
+ assertFalse(cloned.getEnableSessionTelemetry().orElse(true));
}
@Test
@@ -231,7 +231,7 @@ void resumeSessionConfigEnableSessionTelemetryDefaultIsNull() {
ResumeSessionConfig cloned = original.clone();
- assertNull(cloned.getEnableSessionTelemetry());
+ assertTrue(cloned.getEnableSessionTelemetry().isEmpty());
}
@Test
@@ -301,11 +301,11 @@ void copilotClientOptionsSetTelemetry() {
}
@Test
- void copilotClientOptionsSetUseLoggedInUserNull() {
+ void copilotClientOptionsClearUseLoggedInUser() {
var opts = new CopilotClientOptions();
- opts.setUseLoggedInUser(null);
- // null → Boolean.FALSE
- assertEquals(Boolean.FALSE, opts.getUseLoggedInUser());
+ opts.setUseLoggedInUser(true);
+ opts.clearUseLoggedInUser();
+ assertTrue(opts.getUseLoggedInUser().isEmpty());
}
@Test
@@ -375,7 +375,7 @@ void copilotClientOptionsSessionIdleTimeoutCloned() {
CopilotClientOptions cloned = original.clone();
- assertEquals(600, cloned.getSessionIdleTimeoutSeconds());
+ assertEquals(600, cloned.getSessionIdleTimeoutSeconds().getAsInt());
}
@Test
diff --git a/src/test/java/com/github/copilot/sdk/CopilotClientTest.java b/src/test/java/com/github/copilot/sdk/CopilotClientTest.java
index b0430959b9..14ed8ca89e 100644
--- a/src/test/java/com/github/copilot/sdk/CopilotClientTest.java
+++ b/src/test/java/com/github/copilot/sdk/CopilotClientTest.java
@@ -20,6 +20,7 @@
import java.util.concurrent.ExecutionException;
import static org.junit.jupiter.api.Assertions.*;
+import java.util.Optional;
/**
* Tests for CopilotClient.
@@ -153,14 +154,14 @@ void testGitHubTokenOptionAccepted() {
void testUseLoggedInUserDefaultsToNull() {
var options = new CopilotClientOptions().setCliPath("/path/to/cli");
- assertNull(options.getUseLoggedInUser());
+ assertTrue(options.getUseLoggedInUser().isEmpty());
}
@Test
void testExplicitUseLoggedInUserFalse() {
var options = new CopilotClientOptions().setCliPath("/path/to/cli").setUseLoggedInUser(false);
- assertEquals(false, options.getUseLoggedInUser());
+ assertEquals(Optional.of(false), options.getUseLoggedInUser());
}
@Test
@@ -168,7 +169,7 @@ void testExplicitUseLoggedInUserTrueWithGitHubToken() {
var options = new CopilotClientOptions().setCliPath("/path/to/cli").setGitHubToken("gho_test_token")
.setUseLoggedInUser(true);
- assertEquals(true, options.getUseLoggedInUser());
+ assertEquals(Optional.of(true), options.getUseLoggedInUser());
}
@Test
@@ -189,14 +190,14 @@ void testUseLoggedInUserWithCliUrlThrows() {
void testSessionIdleTimeoutSecondsDefaultsToNull() {
var options = new CopilotClientOptions();
- assertNull(options.getSessionIdleTimeoutSeconds());
+ assertTrue(options.getSessionIdleTimeoutSeconds().isEmpty());
}
@Test
void testSessionIdleTimeoutSecondsOptionAccepted() {
var options = new CopilotClientOptions().setSessionIdleTimeoutSeconds(600);
- assertEquals(600, options.getSessionIdleTimeoutSeconds());
+ assertEquals(600, options.getSessionIdleTimeoutSeconds().getAsInt());
}
@Test
diff --git a/src/test/java/com/github/copilot/sdk/DataObjectCoverageTest.java b/src/test/java/com/github/copilot/sdk/DataObjectCoverageTest.java
index b61c93559b..2ff3600200 100644
--- a/src/test/java/com/github/copilot/sdk/DataObjectCoverageTest.java
+++ b/src/test/java/com/github/copilot/sdk/DataObjectCoverageTest.java
@@ -217,17 +217,16 @@ void mcpStdioServerConfigCoversGettersAndFluentSetters() {
@Test
void modelCapabilitiesOverrideCoversNestedSupportsAndLimits() {
- var supports = new ModelCapabilitiesOverride.Supports().setVision(Boolean.TRUE)
- .setReasoningEffort(Boolean.FALSE);
+ var supports = new ModelCapabilitiesOverride.Supports().setVision(true).setReasoningEffort(false);
var limits = new ModelCapabilitiesOverride.Limits().setMaxPromptTokens(2048).setMaxOutputTokens(512)
.setMaxContextWindowTokens(8192);
var override = new ModelCapabilitiesOverride().setSupports(supports).setLimits(limits);
- assertTrue(override.getSupports().getVision());
- assertFalse(override.getSupports().getReasoningEffort());
- assertEquals(2048, override.getLimits().getMaxPromptTokens());
- assertEquals(512, override.getLimits().getMaxOutputTokens());
- assertEquals(8192, override.getLimits().getMaxContextWindowTokens());
+ assertTrue(override.getSupports().getVision().orElse(false));
+ assertFalse(override.getSupports().getReasoningEffort().orElse(true));
+ assertEquals(2048, override.getLimits().getMaxPromptTokens().getAsInt());
+ assertEquals(512, override.getLimits().getMaxOutputTokens().getAsInt());
+ assertEquals(8192, override.getLimits().getMaxContextWindowTokens().getAsInt());
}
}
diff --git a/src/test/java/com/github/copilot/sdk/ElicitationTest.java b/src/test/java/com/github/copilot/sdk/ElicitationTest.java
index 6b748b4cc2..1f62451276 100644
--- a/src/test/java/com/github/copilot/sdk/ElicitationTest.java
+++ b/src/test/java/com/github/copilot/sdk/ElicitationTest.java
@@ -40,7 +40,7 @@ void sessionCapabilitiesTypesAreProperlyStructured() {
var capabilities = new SessionCapabilities().setUi(new SessionUiCapabilities().setElicitation(true));
assertNotNull(capabilities.getUi());
- assertTrue(capabilities.getUi().getElicitation());
+ assertTrue(capabilities.getUi().getElicitation().get());
// Test with null UI
var emptyCapabilities = new SessionCapabilities();
@@ -119,8 +119,8 @@ void inputOptionsHasAllFields() {
assertEquals("My Title", opts.getTitle());
assertEquals("My Desc", opts.getDescription());
- assertEquals(1, opts.getMinLength());
- assertEquals(100, opts.getMaxLength());
+ assertEquals(1, opts.getMinLength().getAsInt());
+ assertEquals(100, opts.getMaxLength().getAsInt());
assertEquals("email", opts.getFormat());
assertEquals("default@example.com", opts.getDefaultValue());
}
@@ -164,7 +164,7 @@ void buildCreateRequestSetsRequestElicitationWhenHandlerPresent() {
var request = SessionRequestBuilder.buildCreateRequest(config);
- assertTrue(Boolean.TRUE.equals(request.getRequestElicitation()));
+ assertEquals(Boolean.TRUE, request.getRequestElicitation());
}
@Test
@@ -186,6 +186,6 @@ void buildResumeRequestSetsRequestElicitationWhenHandlerPresent() {
var request = SessionRequestBuilder.buildResumeRequest("session-1", config);
- assertTrue(Boolean.TRUE.equals(request.getRequestElicitation()));
+ assertEquals(Boolean.TRUE, request.getRequestElicitation());
}
}
diff --git a/src/test/java/com/github/copilot/sdk/JsonIncludeNonNullTest.java b/src/test/java/com/github/copilot/sdk/JsonIncludeNonNullTest.java
new file mode 100644
index 0000000000..7465507f50
--- /dev/null
+++ b/src/test/java/com/github/copilot/sdk/JsonIncludeNonNullTest.java
@@ -0,0 +1,159 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.junit.jupiter.api.Test;
+
+import com.github.copilot.sdk.json.CopilotClientOptions;
+import com.github.copilot.sdk.json.CustomAgentConfig;
+import com.github.copilot.sdk.json.InfiniteSessionConfig;
+import com.github.copilot.sdk.json.InputOptions;
+import com.github.copilot.sdk.json.ModelCapabilitiesOverride;
+import com.github.copilot.sdk.json.ProviderConfig;
+import com.github.copilot.sdk.json.ResumeSessionConfig;
+import com.github.copilot.sdk.json.SessionConfig;
+import com.github.copilot.sdk.json.SessionUiCapabilities;
+import com.github.copilot.sdk.json.TelemetryConfig;
+import com.github.copilot.sdk.json.UserInputRequest;
+
+/**
+ * Verifies that public DTO classes in the {@code com.github.copilot.sdk.json}
+ * package are annotated with {@code @JsonInclude(JsonInclude.Include.NON_NULL)}
+ * so that null-valued fields are omitted during JSON serialization.
+ */
+class JsonIncludeNonNullTest {
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ // --- Annotation presence checks ---
+
+ @Test
+ void copilotClientOptionsHasNonNullAnnotation() {
+ assertHasNonNullInclude(CopilotClientOptions.class);
+ }
+
+ @Test
+ void sessionConfigHasNonNullAnnotation() {
+ assertHasNonNullInclude(SessionConfig.class);
+ }
+
+ @Test
+ void resumeSessionConfigHasNonNullAnnotation() {
+ assertHasNonNullInclude(ResumeSessionConfig.class);
+ }
+
+ @Test
+ void infiniteSessionConfigHasNonNullAnnotation() {
+ assertHasNonNullInclude(InfiniteSessionConfig.class);
+ }
+
+ @Test
+ void inputOptionsHasNonNullAnnotation() {
+ assertHasNonNullInclude(InputOptions.class);
+ }
+
+ @Test
+ void modelCapabilitiesOverrideHasNonNullAnnotation() {
+ assertHasNonNullInclude(ModelCapabilitiesOverride.class);
+ }
+
+ @Test
+ void providerConfigHasNonNullAnnotation() {
+ assertHasNonNullInclude(ProviderConfig.class);
+ }
+
+ @Test
+ void telemetryConfigHasNonNullAnnotation() {
+ assertHasNonNullInclude(TelemetryConfig.class);
+ }
+
+ @Test
+ void sessionUiCapabilitiesHasNonNullAnnotation() {
+ assertHasNonNullInclude(SessionUiCapabilities.class);
+ }
+
+ @Test
+ void customAgentConfigHasNonNullAnnotation() {
+ assertHasNonNullInclude(CustomAgentConfig.class);
+ }
+
+ @Test
+ void userInputRequestHasNonNullAnnotation() {
+ assertHasNonNullInclude(UserInputRequest.class);
+ }
+
+ // --- Serialization tests: null fields are omitted ---
+
+ @Test
+ void inputOptionsOmitsNullFieldsInJson() throws JsonProcessingException {
+ var opts = new InputOptions();
+ String json = MAPPER.writeValueAsString(opts);
+ assertEquals("{}", json, "All-null InputOptions should serialize to empty JSON");
+ }
+
+ @Test
+ void telemetryConfigOmitsNullFieldsInJson() throws JsonProcessingException {
+ var config = new TelemetryConfig();
+ String json = MAPPER.writeValueAsString(config);
+ assertEquals("{}", json, "All-null TelemetryConfig should serialize to empty JSON");
+ }
+
+ @Test
+ void sessionUiCapabilitiesOmitsNullFieldsInJson() throws JsonProcessingException {
+ var caps = new SessionUiCapabilities();
+ String json = MAPPER.writeValueAsString(caps);
+ assertEquals("{}", json, "All-null SessionUiCapabilities should serialize to empty JSON");
+ }
+
+ @Test
+ void userInputRequestOmitsNullFieldsInJson() throws JsonProcessingException {
+ var req = new UserInputRequest();
+ String json = MAPPER.writeValueAsString(req);
+ assertFalse(json.contains("null"), "UserInputRequest with no fields set should not contain 'null' values");
+ }
+
+ @Test
+ void inputOptionsIncludesSetFieldsInJson() throws JsonProcessingException {
+ var opts = new InputOptions();
+ opts.setMinLength(5);
+ opts.setMaxLength(100);
+ String json = MAPPER.writeValueAsString(opts);
+ assertTrue(json.contains("\"minLength\":5"), "Set minLength should appear in JSON");
+ assertTrue(json.contains("\"maxLength\":100"), "Set maxLength should appear in JSON");
+ assertFalse(json.contains("\"title\""), "Unset title should be omitted from JSON");
+ }
+
+ @Test
+ void telemetryConfigIncludesSetFieldsInJson() throws JsonProcessingException {
+ var config = new TelemetryConfig();
+ config.setOtlpEndpoint("http://localhost:4318");
+ String json = MAPPER.writeValueAsString(config);
+ assertTrue(json.contains("\"otlpEndpoint\":\"http://localhost:4318\""),
+ "Set otlpEndpoint should appear in JSON");
+ assertFalse(json.contains("\"filePath\""), "Unset filePath should be omitted from JSON");
+ }
+
+ @Test
+ void sessionUiCapabilitiesIncludesSetFieldsInJson() throws JsonProcessingException {
+ var caps = new SessionUiCapabilities();
+ caps.setElicitation(true);
+ String json = MAPPER.writeValueAsString(caps);
+ assertTrue(json.contains("\"elicitation\":true"), "Set elicitation should appear in JSON");
+ }
+
+ private void assertHasNonNullInclude(Class> clazz) {
+ JsonInclude annotation = clazz.getAnnotation(JsonInclude.class);
+ assertNotNull(annotation, clazz.getSimpleName() + " should be annotated with @JsonInclude");
+ assertEquals(JsonInclude.Include.NON_NULL, annotation.value(),
+ clazz.getSimpleName() + " @JsonInclude should use Include.NON_NULL");
+ }
+
+}
diff --git a/src/test/java/com/github/copilot/sdk/ModuleDescriptorTest.java b/src/test/java/com/github/copilot/sdk/ModuleDescriptorTest.java
new file mode 100644
index 0000000000..36be137345
--- /dev/null
+++ b/src/test/java/com/github/copilot/sdk/ModuleDescriptorTest.java
@@ -0,0 +1,28 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.lang.module.ModuleDescriptor;
+import org.junit.jupiter.api.Test;
+
+class ModuleDescriptorTest {
+
+ @Test
+ void sdkHasExplicitModuleDescriptor() {
+ Module module = CopilotClient.class.getModule();
+ assertTrue(module.isNamed());
+ assertEquals("com.github.copilot.sdk.java", module.getName());
+
+ ModuleDescriptor descriptor = module.getDescriptor();
+ assertTrue(descriptor.exports().stream().anyMatch(export -> export.source().equals("com.github.copilot.sdk")));
+ assertTrue(descriptor.exports().stream()
+ .anyMatch(export -> export.source().equals("com.github.copilot.sdk.json")));
+ assertTrue(descriptor.requires().stream()
+ .anyMatch(require -> require.name().equals("com.fasterxml.jackson.databind")));
+ }
+}
diff --git a/src/test/java/com/github/copilot/sdk/OptionalApiAndJacksonTest.java b/src/test/java/com/github/copilot/sdk/OptionalApiAndJacksonTest.java
new file mode 100644
index 0000000000..2a9770e2e2
--- /dev/null
+++ b/src/test/java/com/github/copilot/sdk/OptionalApiAndJacksonTest.java
@@ -0,0 +1,635 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.copilot.sdk.json.CopilotClientOptions;
+import com.github.copilot.sdk.json.CustomAgentConfig;
+import com.github.copilot.sdk.json.InfiniteSessionConfig;
+import com.github.copilot.sdk.json.InputOptions;
+import com.github.copilot.sdk.json.ModelCapabilitiesOverride;
+import com.github.copilot.sdk.json.ProviderConfig;
+import com.github.copilot.sdk.json.ResumeSessionConfig;
+import com.github.copilot.sdk.json.SessionConfig;
+import com.github.copilot.sdk.json.SessionUiCapabilities;
+import com.github.copilot.sdk.json.TelemetryConfig;
+import com.github.copilot.sdk.json.UserInputRequest;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Validates that every {@code clearXxx()} method resets its field to absent,
+ * that Optional-returning getters report the correct state, and that Jackson
+ * omits cleared fields from serialized output.
+ */
+class OptionalApiAndJacksonTest {
+
+ private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper();
+
+ // ── CopilotClientOptions ──────────────────────────────────────────
+
+ @Test
+ void copilotClientOptions_clearSessionIdleTimeoutSeconds() {
+ var opts = new CopilotClientOptions();
+ opts.setSessionIdleTimeoutSeconds(120);
+ assertFalse(opts.getSessionIdleTimeoutSeconds().isEmpty());
+
+ opts.clearSessionIdleTimeoutSeconds();
+ assertTrue(opts.getSessionIdleTimeoutSeconds().isEmpty());
+ }
+
+ @Test
+ void copilotClientOptions_clearUseLoggedInUser() {
+ var opts = new CopilotClientOptions();
+ opts.setUseLoggedInUser(true);
+ assertTrue(opts.getUseLoggedInUser().isPresent());
+
+ opts.clearUseLoggedInUser();
+ assertTrue(opts.getUseLoggedInUser().isEmpty());
+ }
+
+ // ── SessionConfig ─────────────────────────────────────────────────
+
+ @Test
+ void sessionConfig_clearEnableSessionTelemetry() {
+ var cfg = new SessionConfig();
+ cfg.setEnableSessionTelemetry(true);
+ assertTrue(cfg.getEnableSessionTelemetry().isPresent());
+
+ cfg.clearEnableSessionTelemetry();
+ assertTrue(cfg.getEnableSessionTelemetry().isEmpty());
+ }
+
+ @Test
+ void sessionConfig_clearEnableConfigDiscovery() {
+ var cfg = new SessionConfig();
+ cfg.setEnableConfigDiscovery(false);
+ assertTrue(cfg.getEnableConfigDiscovery().isPresent());
+
+ cfg.clearEnableConfigDiscovery();
+ assertTrue(cfg.getEnableConfigDiscovery().isEmpty());
+ }
+
+ @Test
+ void sessionConfig_clearIncludeSubAgentStreamingEvents() {
+ var cfg = new SessionConfig();
+ cfg.setIncludeSubAgentStreamingEvents(true);
+ assertTrue(cfg.getIncludeSubAgentStreamingEvents().isPresent());
+
+ cfg.clearIncludeSubAgentStreamingEvents();
+ assertTrue(cfg.getIncludeSubAgentStreamingEvents().isEmpty());
+ }
+
+ // ── ResumeSessionConfig ───────────────────────────────────────────
+
+ @Test
+ void resumeSessionConfig_clearEnableSessionTelemetry() {
+ var cfg = new ResumeSessionConfig();
+ cfg.setEnableSessionTelemetry(false);
+ assertTrue(cfg.getEnableSessionTelemetry().isPresent());
+
+ cfg.clearEnableSessionTelemetry();
+ assertTrue(cfg.getEnableSessionTelemetry().isEmpty());
+ }
+
+ @Test
+ void resumeSessionConfig_clearEnableConfigDiscovery() {
+ var cfg = new ResumeSessionConfig();
+ cfg.setEnableConfigDiscovery(true);
+ assertTrue(cfg.getEnableConfigDiscovery().isPresent());
+
+ cfg.clearEnableConfigDiscovery();
+ assertTrue(cfg.getEnableConfigDiscovery().isEmpty());
+ }
+
+ @Test
+ void resumeSessionConfig_clearIncludeSubAgentStreamingEvents() {
+ var cfg = new ResumeSessionConfig();
+ cfg.setIncludeSubAgentStreamingEvents(false);
+ assertTrue(cfg.getIncludeSubAgentStreamingEvents().isPresent());
+
+ cfg.clearIncludeSubAgentStreamingEvents();
+ assertTrue(cfg.getIncludeSubAgentStreamingEvents().isEmpty());
+ }
+
+ // ── InfiniteSessionConfig ─────────────────────────────────────────
+
+ @Test
+ void infiniteSessionConfig_clearEnabled() {
+ var cfg = new InfiniteSessionConfig();
+ cfg.setEnabled(true);
+ assertTrue(cfg.getEnabled().isPresent());
+
+ cfg.clearEnabled();
+ assertTrue(cfg.getEnabled().isEmpty());
+ }
+
+ @Test
+ void infiniteSessionConfig_clearBackgroundCompactionThreshold() {
+ var cfg = new InfiniteSessionConfig();
+ cfg.setBackgroundCompactionThreshold(0.75);
+ assertFalse(cfg.getBackgroundCompactionThreshold().isEmpty());
+
+ cfg.clearBackgroundCompactionThreshold();
+ assertTrue(cfg.getBackgroundCompactionThreshold().isEmpty());
+ }
+
+ @Test
+ void infiniteSessionConfig_clearBufferExhaustionThreshold() {
+ var cfg = new InfiniteSessionConfig();
+ cfg.setBufferExhaustionThreshold(0.9);
+ assertFalse(cfg.getBufferExhaustionThreshold().isEmpty());
+
+ cfg.clearBufferExhaustionThreshold();
+ assertTrue(cfg.getBufferExhaustionThreshold().isEmpty());
+ }
+
+ // ── InputOptions ──────────────────────────────────────────────────
+
+ @Test
+ void inputOptions_clearMinLength() {
+ var opts = new InputOptions();
+ opts.setMinLength(5);
+ assertFalse(opts.getMinLength().isEmpty());
+
+ opts.clearMinLength();
+ assertTrue(opts.getMinLength().isEmpty());
+ }
+
+ @Test
+ void inputOptions_clearMaxLength() {
+ var opts = new InputOptions();
+ opts.setMaxLength(100);
+ assertFalse(opts.getMaxLength().isEmpty());
+
+ opts.clearMaxLength();
+ assertTrue(opts.getMaxLength().isEmpty());
+ }
+
+ // ── ModelCapabilitiesOverride.Supports ─────────────────────────────
+
+ @Test
+ void supports_clearVision() {
+ var s = new ModelCapabilitiesOverride.Supports();
+ s.setVision(true);
+ assertTrue(s.getVision().isPresent());
+
+ s.clearVision();
+ assertTrue(s.getVision().isEmpty());
+ }
+
+ @Test
+ void supports_clearReasoningEffort() {
+ var s = new ModelCapabilitiesOverride.Supports();
+ s.setReasoningEffort(false);
+ assertTrue(s.getReasoningEffort().isPresent());
+
+ s.clearReasoningEffort();
+ assertTrue(s.getReasoningEffort().isEmpty());
+ }
+
+ // ── ModelCapabilitiesOverride.Limits ───────────────────────────────
+
+ @Test
+ void limits_clearMaxPromptTokens() {
+ var l = new ModelCapabilitiesOverride.Limits();
+ l.setMaxPromptTokens(4096);
+ assertFalse(l.getMaxPromptTokens().isEmpty());
+
+ l.clearMaxPromptTokens();
+ assertTrue(l.getMaxPromptTokens().isEmpty());
+ }
+
+ @Test
+ void limits_clearMaxOutputTokens() {
+ var l = new ModelCapabilitiesOverride.Limits();
+ l.setMaxOutputTokens(1024);
+ assertFalse(l.getMaxOutputTokens().isEmpty());
+
+ l.clearMaxOutputTokens();
+ assertTrue(l.getMaxOutputTokens().isEmpty());
+ }
+
+ @Test
+ void limits_clearMaxContextWindowTokens() {
+ var l = new ModelCapabilitiesOverride.Limits();
+ l.setMaxContextWindowTokens(16384);
+ assertFalse(l.getMaxContextWindowTokens().isEmpty());
+
+ l.clearMaxContextWindowTokens();
+ assertTrue(l.getMaxContextWindowTokens().isEmpty());
+ }
+
+ // ── ProviderConfig ────────────────────────────────────────────────
+
+ @Test
+ void providerConfig_clearMaxPromptTokens() {
+ var cfg = new ProviderConfig();
+ cfg.setMaxPromptTokens(2048);
+ assertFalse(cfg.getMaxPromptTokens().isEmpty());
+
+ cfg.clearMaxPromptTokens();
+ assertTrue(cfg.getMaxPromptTokens().isEmpty());
+ }
+
+ @Test
+ void providerConfig_clearMaxOutputTokens() {
+ var cfg = new ProviderConfig();
+ cfg.setMaxOutputTokens(512);
+ assertFalse(cfg.getMaxOutputTokens().isEmpty());
+
+ cfg.clearMaxOutputTokens();
+ assertTrue(cfg.getMaxOutputTokens().isEmpty());
+ }
+
+ // ── TelemetryConfig ───────────────────────────────────────────────
+
+ @Test
+ void telemetryConfig_clearCaptureContent() {
+ var cfg = new TelemetryConfig();
+ cfg.setCaptureContent(true);
+ assertTrue(cfg.getCaptureContent().isPresent());
+
+ cfg.clearCaptureContent();
+ assertTrue(cfg.getCaptureContent().isEmpty());
+ }
+
+ // ── SessionUiCapabilities ─────────────────────────────────────────
+
+ @Test
+ void sessionUiCapabilities_clearElicitation() {
+ var caps = new SessionUiCapabilities();
+ caps.setElicitation(true);
+ assertTrue(caps.getElicitation().isPresent());
+
+ caps.clearElicitation();
+ assertTrue(caps.getElicitation().isEmpty());
+ }
+
+ // ── CustomAgentConfig ─────────────────────────────────────────────
+
+ @Test
+ void customAgentConfig_clearInfer() {
+ var cfg = new CustomAgentConfig();
+ cfg.setInfer(true);
+ assertTrue(cfg.getInfer().isPresent());
+
+ cfg.clearInfer();
+ assertTrue(cfg.getInfer().isEmpty());
+ }
+
+ // ── UserInputRequest ──────────────────────────────────────────────
+
+ @Test
+ void userInputRequest_clearAllowFreeform() {
+ var req = new UserInputRequest();
+ req.setAllowFreeform(false);
+ assertTrue(req.getAllowFreeform().isPresent());
+
+ req.clearAllowFreeform();
+ assertTrue(req.getAllowFreeform().isEmpty());
+ }
+
+ // ── Value retrieval through Optional getters ────────────────────────
+
+ @Test
+ void copilotClientOptions_sessionIdleTimeoutSecondsValue() {
+ var opts = new CopilotClientOptions();
+ assertTrue(opts.getSessionIdleTimeoutSeconds().isEmpty());
+
+ opts.setSessionIdleTimeoutSeconds(300);
+ assertEquals(300, opts.getSessionIdleTimeoutSeconds().getAsInt());
+
+ opts.setSessionIdleTimeoutSeconds(0);
+ assertTrue(opts.getSessionIdleTimeoutSeconds().isPresent());
+ assertEquals(0, opts.getSessionIdleTimeoutSeconds().getAsInt());
+ }
+
+ @Test
+ void copilotClientOptions_useLoggedInUserValue() {
+ var opts = new CopilotClientOptions();
+ assertTrue(opts.getUseLoggedInUser().isEmpty());
+
+ opts.setUseLoggedInUser(true);
+ assertEquals(Boolean.TRUE, opts.getUseLoggedInUser().get());
+
+ opts.setUseLoggedInUser(false);
+ assertEquals(Boolean.FALSE, opts.getUseLoggedInUser().get());
+ }
+
+ @Test
+ void sessionConfig_enableSessionTelemetryValue() {
+ var cfg = new SessionConfig();
+ assertFalse(cfg.getEnableSessionTelemetry().orElse(false));
+
+ cfg.setEnableSessionTelemetry(true);
+ assertTrue(cfg.getEnableSessionTelemetry().orElse(false));
+
+ cfg.setEnableSessionTelemetry(false);
+ assertFalse(cfg.getEnableSessionTelemetry().orElse(true));
+ }
+
+ @Test
+ void sessionConfig_enableConfigDiscoveryValue() {
+ var cfg = new SessionConfig();
+ assertTrue(cfg.getEnableConfigDiscovery().isEmpty());
+
+ cfg.setEnableConfigDiscovery(true);
+ assertTrue(cfg.getEnableConfigDiscovery().get());
+
+ cfg.setEnableConfigDiscovery(false);
+ assertFalse(cfg.getEnableConfigDiscovery().get());
+ }
+
+ @Test
+ void sessionConfig_includeSubAgentStreamingEventsValue() {
+ var cfg = new SessionConfig();
+ assertTrue(cfg.getIncludeSubAgentStreamingEvents().isEmpty());
+
+ cfg.setIncludeSubAgentStreamingEvents(true);
+ assertTrue(cfg.getIncludeSubAgentStreamingEvents().get());
+ }
+
+ @Test
+ void resumeSessionConfig_enableSessionTelemetryValue() {
+ var cfg = new ResumeSessionConfig();
+ assertTrue(cfg.getEnableSessionTelemetry().isEmpty());
+
+ cfg.setEnableSessionTelemetry(true);
+ assertTrue(cfg.getEnableSessionTelemetry().get());
+
+ cfg.setEnableSessionTelemetry(false);
+ assertFalse(cfg.getEnableSessionTelemetry().get());
+ }
+
+ @Test
+ void resumeSessionConfig_enableConfigDiscoveryValue() {
+ var cfg = new ResumeSessionConfig();
+ assertTrue(cfg.getEnableConfigDiscovery().isEmpty());
+
+ cfg.setEnableConfigDiscovery(true);
+ assertTrue(cfg.getEnableConfigDiscovery().get());
+ }
+
+ @Test
+ void resumeSessionConfig_includeSubAgentStreamingEventsValue() {
+ var cfg = new ResumeSessionConfig();
+ assertTrue(cfg.getIncludeSubAgentStreamingEvents().isEmpty());
+
+ cfg.setIncludeSubAgentStreamingEvents(false);
+ assertFalse(cfg.getIncludeSubAgentStreamingEvents().get());
+ }
+
+ @Test
+ void infiniteSessionConfig_thresholdValues() {
+ var cfg = new InfiniteSessionConfig();
+ assertTrue(cfg.getBackgroundCompactionThreshold().isEmpty());
+ assertTrue(cfg.getBufferExhaustionThreshold().isEmpty());
+
+ cfg.setBackgroundCompactionThreshold(0.6);
+ cfg.setBufferExhaustionThreshold(0.85);
+ assertEquals(0.6, cfg.getBackgroundCompactionThreshold().getAsDouble(), 0.001);
+ assertEquals(0.85, cfg.getBufferExhaustionThreshold().getAsDouble(), 0.001);
+ }
+
+ @Test
+ void infiniteSessionConfig_enabledValue() {
+ var cfg = new InfiniteSessionConfig();
+ assertTrue(cfg.getEnabled().isEmpty());
+
+ cfg.setEnabled(true);
+ assertTrue(cfg.getEnabled().get());
+
+ cfg.setEnabled(false);
+ assertFalse(cfg.getEnabled().get());
+ }
+
+ @Test
+ void inputOptions_minAndMaxLengthValues() {
+ var opts = new InputOptions();
+ assertTrue(opts.getMinLength().isEmpty());
+ assertTrue(opts.getMaxLength().isEmpty());
+
+ opts.setMinLength(1);
+ opts.setMaxLength(255);
+ assertEquals(1, opts.getMinLength().getAsInt());
+ assertEquals(255, opts.getMaxLength().getAsInt());
+ }
+
+ @Test
+ void supports_visionAndReasoningEffortValues() {
+ var s = new ModelCapabilitiesOverride.Supports();
+ assertTrue(s.getVision().isEmpty());
+ assertTrue(s.getReasoningEffort().isEmpty());
+
+ s.setVision(true);
+ s.setReasoningEffort(false);
+ assertTrue(s.getVision().get());
+ assertFalse(s.getReasoningEffort().get());
+ }
+
+ @Test
+ void limits_tokenValues() {
+ var l = new ModelCapabilitiesOverride.Limits();
+ assertTrue(l.getMaxPromptTokens().isEmpty());
+ assertTrue(l.getMaxOutputTokens().isEmpty());
+ assertTrue(l.getMaxContextWindowTokens().isEmpty());
+
+ l.setMaxPromptTokens(4096);
+ l.setMaxOutputTokens(1024);
+ l.setMaxContextWindowTokens(16384);
+ assertEquals(4096, l.getMaxPromptTokens().getAsInt());
+ assertEquals(1024, l.getMaxOutputTokens().getAsInt());
+ assertEquals(16384, l.getMaxContextWindowTokens().getAsInt());
+ }
+
+ @Test
+ void providerConfig_tokenValues() {
+ var cfg = new ProviderConfig();
+ assertTrue(cfg.getMaxPromptTokens().isEmpty());
+ assertTrue(cfg.getMaxOutputTokens().isEmpty());
+
+ cfg.setMaxPromptTokens(8192);
+ cfg.setMaxOutputTokens(2048);
+ assertEquals(8192, cfg.getMaxPromptTokens().getAsInt());
+ assertEquals(2048, cfg.getMaxOutputTokens().getAsInt());
+ }
+
+ @Test
+ void telemetryConfig_captureContentValue() {
+ var cfg = new TelemetryConfig();
+ assertTrue(cfg.getCaptureContent().isEmpty());
+
+ cfg.setCaptureContent(true);
+ assertTrue(cfg.getCaptureContent().get());
+
+ cfg.setCaptureContent(false);
+ assertFalse(cfg.getCaptureContent().get());
+ }
+
+ @Test
+ void sessionUiCapabilities_elicitationValue() {
+ var caps = new SessionUiCapabilities();
+ assertTrue(caps.getElicitation().isEmpty());
+ assertFalse(caps.getElicitation().orElse(false));
+
+ caps.setElicitation(true);
+ assertTrue(caps.getElicitation().orElse(false));
+ }
+
+ @Test
+ void customAgentConfig_inferValue() {
+ var cfg = new CustomAgentConfig();
+ assertTrue(cfg.getInfer().isEmpty());
+
+ cfg.setInfer(true);
+ assertTrue(cfg.getInfer().get());
+
+ cfg.setInfer(false);
+ assertFalse(cfg.getInfer().get());
+ }
+
+ @Test
+ void userInputRequest_allowFreeformValue() {
+ var req = new UserInputRequest();
+ assertTrue(req.getAllowFreeform().isEmpty());
+
+ req.setAllowFreeform(true);
+ assertTrue(req.getAllowFreeform().get());
+
+ req.setAllowFreeform(false);
+ assertFalse(req.getAllowFreeform().get());
+ }
+
+ // ── JSON deserialization into Optional-returning classes ───────────
+
+ @Test
+ void jackson_deserializeSupportsWithFields() throws Exception {
+ String json = "{\"vision\":true,\"reasoningEffort\":false}";
+ var supports = MAPPER.readValue(json, ModelCapabilitiesOverride.Supports.class);
+ assertTrue(supports.getVision().get());
+ assertFalse(supports.getReasoningEffort().get());
+ }
+
+ @Test
+ void jackson_deserializeSupportsEmpty() throws Exception {
+ String json = "{}";
+ var supports = MAPPER.readValue(json, ModelCapabilitiesOverride.Supports.class);
+ assertTrue(supports.getVision().isEmpty());
+ assertTrue(supports.getReasoningEffort().isEmpty());
+ }
+
+ @Test
+ void jackson_deserializeLimitsWithFields() throws Exception {
+ String json = "{\"max_prompt_tokens\":4096,\"max_output_tokens\":1024,\"max_context_window_tokens\":16384}";
+ var limits = MAPPER.readValue(json, ModelCapabilitiesOverride.Limits.class);
+ assertEquals(4096, limits.getMaxPromptTokens().getAsInt());
+ assertEquals(1024, limits.getMaxOutputTokens().getAsInt());
+ assertEquals(16384, limits.getMaxContextWindowTokens().getAsInt());
+ }
+
+ @Test
+ void jackson_deserializeLimitsEmpty() throws Exception {
+ String json = "{}";
+ var limits = MAPPER.readValue(json, ModelCapabilitiesOverride.Limits.class);
+ assertTrue(limits.getMaxPromptTokens().isEmpty());
+ assertTrue(limits.getMaxOutputTokens().isEmpty());
+ assertTrue(limits.getMaxContextWindowTokens().isEmpty());
+ }
+
+ @Test
+ void jackson_deserializeInfiniteSessionConfigWithFields() throws Exception {
+ String json = "{\"enabled\":true,\"backgroundCompactionThreshold\":0.7,\"bufferExhaustionThreshold\":0.9}";
+ var cfg = MAPPER.readValue(json, InfiniteSessionConfig.class);
+ assertTrue(cfg.getEnabled().get());
+ assertEquals(0.7, cfg.getBackgroundCompactionThreshold().getAsDouble(), 0.001);
+ assertEquals(0.9, cfg.getBufferExhaustionThreshold().getAsDouble(), 0.001);
+ }
+
+ @Test
+ void jackson_deserializeInfiniteSessionConfigEmpty() throws Exception {
+ String json = "{}";
+ var cfg = MAPPER.readValue(json, InfiniteSessionConfig.class);
+ assertTrue(cfg.getEnabled().isEmpty());
+ assertTrue(cfg.getBackgroundCompactionThreshold().isEmpty());
+ assertTrue(cfg.getBufferExhaustionThreshold().isEmpty());
+ }
+
+ // ── Jackson serialization roundtrip ───────────────────────────────
+ //
+ // Classes whose fields carry @JsonProperty (InfiniteSessionConfig,
+ // ModelCapabilitiesOverride inner classes) are serialized via field
+ // access: Jackson writes the field when set and omits it when cleared.
+ //
+ // Classes without @JsonProperty on fields (SessionConfig,
+ // CopilotClientOptions, TelemetryConfig, ProviderConfig) are not
+ // directly serialized — their values are copied to wire DTOs by
+ // SessionRequestBuilder. The @JsonIgnore on their Optional-returning
+ // getters prevents Jackson from attempting to serialize Optional
+ // wrappers if the class is ever processed.
+
+ @Test
+ void jackson_infiniteSessionConfigClearedFieldsOmitted() throws Exception {
+ var cfg = new InfiniteSessionConfig();
+ cfg.setEnabled(true);
+ cfg.setBackgroundCompactionThreshold(0.75);
+ cfg.setBufferExhaustionThreshold(0.9);
+
+ String withFields = MAPPER.writeValueAsString(cfg);
+ assertTrue(withFields.contains("enabled"));
+ assertTrue(withFields.contains("backgroundCompactionThreshold"));
+ assertTrue(withFields.contains("bufferExhaustionThreshold"));
+
+ cfg.clearEnabled();
+ cfg.clearBackgroundCompactionThreshold();
+ cfg.clearBufferExhaustionThreshold();
+
+ String cleared = MAPPER.writeValueAsString(cfg);
+ assertFalse(cleared.contains("enabled"));
+ assertFalse(cleared.contains("backgroundCompactionThreshold"));
+ assertFalse(cleared.contains("bufferExhaustionThreshold"));
+ }
+
+ @Test
+ void jackson_modelCapabilitiesOverrideSupportsClearedFieldsOmitted() throws Exception {
+ var supports = new ModelCapabilitiesOverride.Supports();
+ supports.setVision(true);
+ supports.setReasoningEffort(false);
+
+ String withFields = MAPPER.writeValueAsString(supports);
+ assertTrue(withFields.contains("vision"));
+ assertTrue(withFields.contains("reasoningEffort"));
+
+ supports.clearVision();
+ supports.clearReasoningEffort();
+
+ String cleared = MAPPER.writeValueAsString(supports);
+ assertFalse(cleared.contains("vision"));
+ assertFalse(cleared.contains("reasoningEffort"));
+ }
+
+ @Test
+ void jackson_modelCapabilitiesOverrideLimitsClearedFieldsOmitted() throws Exception {
+ var limits = new ModelCapabilitiesOverride.Limits();
+ limits.setMaxPromptTokens(2048);
+ limits.setMaxOutputTokens(512);
+ limits.setMaxContextWindowTokens(16384);
+
+ String withFields = MAPPER.writeValueAsString(limits);
+ assertTrue(withFields.contains("max_prompt_tokens"));
+ assertTrue(withFields.contains("max_output_tokens"));
+ assertTrue(withFields.contains("max_context_window_tokens"));
+
+ limits.clearMaxPromptTokens();
+ limits.clearMaxOutputTokens();
+ limits.clearMaxContextWindowTokens();
+
+ String cleared = MAPPER.writeValueAsString(limits);
+ assertFalse(cleared.contains("max_prompt_tokens"));
+ assertFalse(cleared.contains("max_output_tokens"));
+ assertFalse(cleared.contains("max_context_window_tokens"));
+ }
+}
diff --git a/src/test/java/com/github/copilot/sdk/ProviderConfigTest.java b/src/test/java/com/github/copilot/sdk/ProviderConfigTest.java
index e7602619ca..6bbb3ae284 100644
--- a/src/test/java/com/github/copilot/sdk/ProviderConfigTest.java
+++ b/src/test/java/com/github/copilot/sdk/ProviderConfigTest.java
@@ -410,8 +410,8 @@ void testProviderModelIdAndWireModelSerialization() throws Exception {
ProviderConfig deserialized = MAPPER.readValue(MAPPER.writeValueAsString(provider), ProviderConfig.class);
assertEquals("gpt-4o", deserialized.getModelId());
assertEquals("my-finetune-v3", deserialized.getWireModel());
- assertEquals(100_000, deserialized.getMaxPromptTokens());
- assertEquals(4096, deserialized.getMaxOutputTokens());
+ assertEquals(100_000, deserialized.getMaxPromptTokens().getAsInt());
+ assertEquals(4096, deserialized.getMaxOutputTokens().getAsInt());
}
@Test
@@ -419,8 +419,8 @@ void testProviderModelFieldsDefaultToNull() {
var provider = new ProviderConfig();
assertNull(provider.getModelId());
assertNull(provider.getWireModel());
- assertNull(provider.getMaxPromptTokens());
- assertNull(provider.getMaxOutputTokens());
+ assertTrue(provider.getMaxPromptTokens().isEmpty());
+ assertTrue(provider.getMaxOutputTokens().isEmpty());
}
@Test
diff --git a/src/test/java/com/github/copilot/sdk/RemoteSessionTest.java b/src/test/java/com/github/copilot/sdk/RemoteSessionTest.java
new file mode 100644
index 0000000000..6e093db6ca
--- /dev/null
+++ b/src/test/java/com/github/copilot/sdk/RemoteSessionTest.java
@@ -0,0 +1,399 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.copilot.sdk.json.CreateSessionRequest;
+import com.github.copilot.sdk.json.ResumeSessionConfig;
+import com.github.copilot.sdk.json.ResumeSessionRequest;
+import com.github.copilot.sdk.json.SessionConfig;
+
+/**
+ * Tests for the {@code remoteSession} feature across all session config types.
+ *
+ * Validates the complete lifecycle of the remote session mode:
+ *
+ * - Getter/setter and fluent chaining on {@link SessionConfig} and
+ * {@link ResumeSessionConfig}
+ * - Propagation through {@link SessionRequestBuilder} into
+ * {@link CreateSessionRequest} and {@link ResumeSessionRequest}
+ * - JSON wire-format serialization: correct key, correct value, omission when
+ * unset
+ * - Defensive copy via {@code copy()} preserves the value
+ * - All three supported mode values ("off", "export", "on") are transmitted
+ * correctly
+ *
+ */
+class RemoteSessionTest {
+
+ private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper();
+
+ // =========================================================================
+ // SessionConfig getter/setter/copy
+ // =========================================================================
+
+ @Test
+ void sessionConfig_remoteSessionDefaultsToNull() {
+ var cfg = new SessionConfig();
+ assertNull(cfg.getRemoteSession(), "remoteSession should be null when not set");
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"off", "export", "on"})
+ void sessionConfig_setRemoteSessionReturnsSelf(String mode) {
+ var cfg = new SessionConfig();
+ SessionConfig result = cfg.setRemoteSession(mode);
+ assertSame(cfg, result, "setRemoteSession should return the same instance for chaining");
+ assertEquals(mode, cfg.getRemoteSession());
+ }
+
+ @Test
+ void sessionConfig_copyPreservesRemoteSession() {
+ var original = new SessionConfig().setRemoteSession("export");
+ var copy = original.clone();
+ assertEquals("export", copy.getRemoteSession());
+ }
+
+ @Test
+ void sessionConfig_copyPreservesNullRemoteSession() {
+ var original = new SessionConfig();
+ var copy = original.clone();
+ assertNull(copy.getRemoteSession());
+ }
+
+ @Test
+ void sessionConfig_setRemoteSessionToNullClearsValue() {
+ var cfg = new SessionConfig().setRemoteSession("on");
+ cfg.setRemoteSession(null);
+ assertNull(cfg.getRemoteSession());
+ }
+
+ // =========================================================================
+ // ResumeSessionConfig getter/setter/copy
+ // =========================================================================
+
+ @Test
+ void resumeSessionConfig_remoteSessionDefaultsToNull() {
+ var cfg = new ResumeSessionConfig();
+ assertNull(cfg.getRemoteSession(), "remoteSession should be null when not set");
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"off", "export", "on"})
+ void resumeSessionConfig_setRemoteSessionReturnsSelf(String mode) {
+ var cfg = new ResumeSessionConfig();
+ ResumeSessionConfig result = cfg.setRemoteSession(mode);
+ assertSame(cfg, result, "setRemoteSession should return the same instance for chaining");
+ assertEquals(mode, cfg.getRemoteSession());
+ }
+
+ @Test
+ void resumeSessionConfig_copyPreservesRemoteSession() {
+ var original = new ResumeSessionConfig().setRemoteSession("on");
+ var copy = original.clone();
+ assertEquals("on", copy.getRemoteSession());
+ }
+
+ // =========================================================================
+ // SessionRequestBuilder – CreateSessionRequest wiring
+ // =========================================================================
+
+ @ParameterizedTest
+ @ValueSource(strings = {"off", "export", "on"})
+ void buildCreateRequest_propagatesRemoteSession(String mode) {
+ var config = new SessionConfig().setRemoteSession(mode);
+ CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config);
+ assertEquals(mode, request.getRemoteSession());
+ }
+
+ @Test
+ void buildCreateRequest_nullConfig_remoteSessionIsNull() {
+ CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(null);
+ assertNull(request.getRemoteSession());
+ }
+
+ @Test
+ void buildCreateRequest_unsetRemoteSession_isNull() {
+ var config = new SessionConfig();
+ CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config);
+ assertNull(request.getRemoteSession());
+ }
+
+ // =========================================================================
+ // SessionRequestBuilder – ResumeSessionRequest wiring
+ // =========================================================================
+
+ @ParameterizedTest
+ @ValueSource(strings = {"off", "export", "on"})
+ void buildResumeRequest_propagatesRemoteSession(String mode) {
+ var config = new ResumeSessionConfig().setRemoteSession(mode);
+ ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config);
+ assertEquals(mode, request.getRemoteSession());
+ }
+
+ @Test
+ void buildResumeRequest_nullConfig_remoteSessionIsNull() {
+ ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", null);
+ assertNull(request.getRemoteSession());
+ }
+
+ @Test
+ void buildResumeRequest_unsetRemoteSession_isNull() {
+ var config = new ResumeSessionConfig();
+ ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config);
+ assertNull(request.getRemoteSession());
+ }
+
+ // =========================================================================
+ // JSON wire-format: CreateSessionRequest
+ // =========================================================================
+
+ @ParameterizedTest
+ @ValueSource(strings = {"off", "export", "on"})
+ void createRequest_serializesRemoteSessionCorrectly(String mode) throws Exception {
+ var config = new SessionConfig().setRemoteSession(mode);
+ CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config);
+
+ String json = MAPPER.writeValueAsString(request);
+ JsonNode tree = MAPPER.readTree(json);
+
+ assertTrue(tree.has("remoteSession"), "Serialized JSON should contain 'remoteSession' field for mode: " + mode);
+ assertEquals(mode, tree.get("remoteSession").asText());
+ }
+
+ @Test
+ void createRequest_omitsRemoteSessionWhenNull() throws Exception {
+ var config = new SessionConfig();
+ CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config);
+
+ String json = MAPPER.writeValueAsString(request);
+ JsonNode tree = MAPPER.readTree(json);
+
+ assertFalse(tree.has("remoteSession"), "Serialized JSON should omit 'remoteSession' when not set");
+ }
+
+ // =========================================================================
+ // JSON wire-format: ResumeSessionRequest
+ // =========================================================================
+
+ @ParameterizedTest
+ @ValueSource(strings = {"off", "export", "on"})
+ void resumeRequest_serializesRemoteSessionCorrectly(String mode) throws Exception {
+ var config = new ResumeSessionConfig().setRemoteSession(mode);
+ ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config);
+
+ String json = MAPPER.writeValueAsString(request);
+ JsonNode tree = MAPPER.readTree(json);
+
+ assertTrue(tree.has("remoteSession"), "Serialized JSON should contain 'remoteSession' field for mode: " + mode);
+ assertEquals(mode, tree.get("remoteSession").asText());
+ }
+
+ @Test
+ void resumeRequest_omitsRemoteSessionWhenNull() throws Exception {
+ var config = new ResumeSessionConfig();
+ ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config);
+
+ String json = MAPPER.writeValueAsString(request);
+ JsonNode tree = MAPPER.readTree(json);
+
+ assertFalse(tree.has("remoteSession"), "Serialized JSON should omit 'remoteSession' when not set");
+ }
+
+ // =========================================================================
+ // JSON round-trip: CreateSessionRequest
+ // =========================================================================
+
+ @ParameterizedTest
+ @ValueSource(strings = {"off", "export", "on"})
+ void createRequest_roundTripsRemoteSession(String mode) throws Exception {
+ var config = new SessionConfig().setRemoteSession(mode);
+ CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config);
+
+ String json = MAPPER.writeValueAsString(request);
+ CreateSessionRequest deserialized = MAPPER.readValue(json, CreateSessionRequest.class);
+ assertEquals(mode, deserialized.getRemoteSession());
+ }
+
+ @Test
+ void createRequest_roundTripsNullRemoteSession() throws Exception {
+ var config = new SessionConfig();
+ CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config);
+
+ String json = MAPPER.writeValueAsString(request);
+ CreateSessionRequest deserialized = MAPPER.readValue(json, CreateSessionRequest.class);
+ assertNull(deserialized.getRemoteSession());
+ }
+
+ // =========================================================================
+ // JSON round-trip: ResumeSessionRequest
+ // =========================================================================
+
+ @ParameterizedTest
+ @ValueSource(strings = {"off", "export", "on"})
+ void resumeRequest_roundTripsRemoteSession(String mode) throws Exception {
+ var config = new ResumeSessionConfig().setRemoteSession(mode);
+ ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config);
+
+ String json = MAPPER.writeValueAsString(request);
+ ResumeSessionRequest deserialized = MAPPER.readValue(json, ResumeSessionRequest.class);
+ assertEquals(mode, deserialized.getRemoteSession());
+ }
+
+ // =========================================================================
+ // Fluent chaining: remoteSession composes with other config options
+ // =========================================================================
+
+ @Test
+ void sessionConfig_remoteSessionComposesWithOtherFields() {
+ var config = new SessionConfig().setModel("gpt-4o").setRemoteSession("export").setReasoningEffort("high");
+
+ assertEquals("gpt-4o", config.getModel());
+ assertEquals("export", config.getRemoteSession());
+ assertEquals("high", config.getReasoningEffort());
+ }
+
+ @Test
+ void resumeSessionConfig_remoteSessionComposesWithOtherFields() {
+ var config = new ResumeSessionConfig().setModel("gpt-4o").setRemoteSession("on").setReasoningEffort("medium");
+
+ assertEquals("gpt-4o", config.getModel());
+ assertEquals("on", config.getRemoteSession());
+ assertEquals("medium", config.getReasoningEffort());
+ }
+
+ @Test
+ void createRequest_remoteSessionDoesNotAffectOtherFields() throws Exception {
+ var config = new SessionConfig().setModel("gpt-4o").setRemoteSession("export").setReasoningEffort("high")
+ .setGitHubToken("ghp_test");
+
+ CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config);
+ String json = MAPPER.writeValueAsString(request);
+ JsonNode tree = MAPPER.readTree(json);
+
+ assertEquals("export", tree.get("remoteSession").asText());
+ assertEquals("gpt-4o", tree.get("model").asText());
+ assertEquals("high", tree.get("reasoningEffort").asText());
+ assertEquals("ghp_test", tree.get("gitHubToken").asText());
+ }
+
+ @Test
+ void resumeRequest_remoteSessionDoesNotAffectOtherFields() throws Exception {
+ var config = new ResumeSessionConfig().setModel("gpt-4o").setRemoteSession("on").setReasoningEffort("medium")
+ .setGitHubToken("ghp_test");
+
+ ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config);
+ String json = MAPPER.writeValueAsString(request);
+ JsonNode tree = MAPPER.readTree(json);
+
+ assertEquals("on", tree.get("remoteSession").asText());
+ assertEquals("gpt-4o", tree.get("model").asText());
+ assertEquals("medium", tree.get("reasoningEffort").asText());
+ assertEquals("ghp_test", tree.get("gitHubToken").asText());
+ }
+
+ // =========================================================================
+ // Deserialization from raw JSON (simulates CLI response ingestion)
+ // =========================================================================
+
+ @Test
+ void createRequest_deserializesRemoteSessionFromRawJson() throws Exception {
+ String json = """
+ {
+ "sessionId": "test-session",
+ "remoteSession": "export",
+ "model": "gpt-4o"
+ }
+ """;
+ CreateSessionRequest request = MAPPER.readValue(json, CreateSessionRequest.class);
+ assertEquals("export", request.getRemoteSession());
+ assertEquals("test-session", request.getSessionId());
+ }
+
+ @Test
+ void resumeRequest_deserializesRemoteSessionFromRawJson() throws Exception {
+ String json = """
+ {
+ "sessionId": "resume-session",
+ "remoteSession": "on",
+ "model": "gpt-4o"
+ }
+ """;
+ ResumeSessionRequest request = MAPPER.readValue(json, ResumeSessionRequest.class);
+ assertEquals("on", request.getRemoteSession());
+ assertEquals("resume-session", request.getSessionId());
+ }
+
+ @Test
+ void createRequest_deserializesWithMissingRemoteSession() throws Exception {
+ String json = """
+ {
+ "sessionId": "test-session",
+ "model": "gpt-4o"
+ }
+ """;
+ CreateSessionRequest request = MAPPER.readValue(json, CreateSessionRequest.class);
+ assertNull(request.getRemoteSession());
+ }
+
+ // =========================================================================
+ // Handoff event with remoteSessionId (remote session lifecycle)
+ // =========================================================================
+
+ @Test
+ void handoffEvent_withRemoteSourceType_containsRemoteSessionId() throws Exception {
+ String json = """
+ {
+ "type": "session.handoff",
+ "data": {
+ "handoffTime": "2025-06-01T12:00:00Z",
+ "sourceType": "remote",
+ "remoteSessionId": "remote-sess-42",
+ "summary": "Session exported for remote execution",
+ "repository": {
+ "owner": "test-org",
+ "name": "test-repo",
+ "branch": "feature-branch"
+ }
+ }
+ }
+ """;
+
+ var event = (com.github.copilot.sdk.generated.SessionHandoffEvent) MAPPER.readValue(json,
+ com.github.copilot.sdk.generated.SessionEvent.class);
+ assertNotNull(event);
+ var data = event.getData();
+ assertEquals("remote-sess-42", data.remoteSessionId());
+ assertEquals(com.github.copilot.sdk.generated.HandoffSourceType.REMOTE, data.sourceType());
+ assertEquals("Session exported for remote execution", data.summary());
+ assertEquals("test-org", data.repository().owner());
+ assertEquals("test-repo", data.repository().name());
+ assertEquals("feature-branch", data.repository().branch());
+ }
+
+ @Test
+ void handoffEvent_withoutRemoteSessionId_fieldIsNull() throws Exception {
+ String json = """
+ {
+ "type": "session.handoff",
+ "data": {
+ "targetAgent": "local-agent"
+ }
+ }
+ """;
+
+ var event = (com.github.copilot.sdk.generated.SessionHandoffEvent) MAPPER.readValue(json,
+ com.github.copilot.sdk.generated.SessionEvent.class);
+ assertNotNull(event);
+ assertNull(event.getData().remoteSessionId());
+ }
+}
diff --git a/src/test/java/com/github/copilot/sdk/TelemetryConfigTest.java b/src/test/java/com/github/copilot/sdk/TelemetryConfigTest.java
index 99b360d2db..278777ce13 100644
--- a/src/test/java/com/github/copilot/sdk/TelemetryConfigTest.java
+++ b/src/test/java/com/github/copilot/sdk/TelemetryConfigTest.java
@@ -22,7 +22,7 @@ void defaultValuesAreNull() {
assertNull(config.getFilePath());
assertNull(config.getExporterType());
assertNull(config.getSourceName());
- assertNull(config.getCaptureContent());
+ assertTrue(config.getCaptureContent().isEmpty());
}
@Test
@@ -57,10 +57,10 @@ void sourceNameGetterSetter() {
void captureContentGetterSetter() {
var config = new TelemetryConfig();
config.setCaptureContent(true);
- assertTrue(config.getCaptureContent());
+ assertTrue(config.getCaptureContent().get());
config.setCaptureContent(false);
- assertFalse(config.getCaptureContent());
+ assertFalse(config.getCaptureContent().get());
}
@Test
@@ -72,6 +72,6 @@ void fluentChainingReturnsThis() {
assertEquals("/tmp/spans.json", config.getFilePath());
assertEquals("file", config.getExporterType());
assertEquals("sdk-test", config.getSourceName());
- assertTrue(config.getCaptureContent());
+ assertTrue(config.getCaptureContent().get());
}
}
diff --git a/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcApiCoverageTest.java b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcApiCoverageTest.java
index 89943e3758..10393abe09 100644
--- a/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcApiCoverageTest.java
+++ b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcApiCoverageTest.java
@@ -71,7 +71,7 @@ void serverRpc_sessions_fork_invokes_correct_method() {
var stub = new StubCaller();
var server = new ServerRpc(stub);
- var params = new SessionsForkParams("parent-session-id", null);
+ var params = new SessionsForkParams("parent-session-id", null, null);
server.sessions.fork(params);
assertEquals(1, stub.calls.size());
@@ -657,7 +657,7 @@ void serverRpc_sessionFs_setProvider_params_record() {
@Test
void sessionsForkParams_record() {
- var params = new SessionsForkParams("parent-id", "event-123");
+ var params = new SessionsForkParams("parent-id", "event-123", null);
assertEquals("parent-id", params.sessionId());
assertEquals("event-123", params.toEventId());
}
diff --git a/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java
index 48ade8ce11..50e65d3779 100644
--- a/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java
+++ b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java
@@ -67,7 +67,7 @@ void toolsListParams_record() {
@Test
void sessionsForkParams_record() {
- var params = new SessionsForkParams("sess-1", "event-abc");
+ var params = new SessionsForkParams("sess-1", "event-abc", null);
assertEquals("sess-1", params.sessionId());
assertEquals("event-abc", params.toEventId());
}
@@ -795,7 +795,7 @@ void sessionSkillsListResult_nested() {
@Test
void sessionSkillsReloadResult_empty() {
- assertNotNull(new SessionSkillsReloadResult());
+ assertNotNull(new SessionSkillsReloadResult(null, null));
}
@Test
@@ -863,7 +863,7 @@ void sessionWorkspaceReadFileResult_record() {
@Test
void sessionsForkResult_record() {
- var result = new SessionsForkResult("forked-sess-id");
+ var result = new SessionsForkResult("forked-sess-id", null);
assertEquals("forked-sess-id", result.sessionId());
}
@@ -917,8 +917,8 @@ void modelsListResult_nested() {
var limits = new ModelCapabilitiesLimits(100000L, 8192L, 128000L, null);
var capabilities = new ModelCapabilities(supports, limits);
var policy = new ModelPolicy("active", null);
- var billing = new ModelBilling(1.0);
- var modelItem = new Model("gpt-5", "GPT-5", capabilities, policy, billing, null, null);
+ var billing = new ModelBilling(1.0, null);
+ var modelItem = new Model("gpt-5", "GPT-5", capabilities, policy, billing, null, null, null, null);
var result = new ModelsListResult(List.of(modelItem));
assertEquals(1, result.models().size());