Skip to content

Commit 6e8b100

Browse files
Copilotedburns
andauthored
refactor: use Optional return types instead of nullable boxed primitives in public API
Change getter return types from nullable Boolean/Integer/Double to Optional<Boolean>/OptionalInt/OptionalDouble on all mutable config/builder classes. Setters now take primitive parameters. Add clearXxx() methods for resetting to null (server default). Add @JsonIgnore on Optional-returning getters to preserve Jackson serialization. Update all callers and tests. Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
1 parent f2654bc commit 6e8b100

29 files changed

Lines changed: 1267 additions & 179 deletions

refactor_optionals.py

Lines changed: 616 additions & 0 deletions
Large diffs are not rendered by default.

src/main/java/com/github/copilot/sdk/CliServerManager.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,16 @@ ProcessInfo startCliServer() throws IOException, InterruptedException {
9090
}
9191

9292
// Default UseLoggedInUser to false when GitHubToken is provided
93-
boolean useLoggedInUser = options.getUseLoggedInUser() != null
94-
? options.getUseLoggedInUser()
95-
: (options.getGitHubToken() == null || options.getGitHubToken().isEmpty());
93+
boolean useLoggedInUser = options.getUseLoggedInUser()
94+
.orElse(options.getGitHubToken() == null || options.getGitHubToken().isEmpty());
9695
if (!useLoggedInUser) {
9796
args.add("--no-auto-login");
9897
}
9998

100-
if (options.getSessionIdleTimeoutSeconds() != null && options.getSessionIdleTimeoutSeconds() > 0) {
99+
if (options.getSessionIdleTimeoutSeconds().isPresent()
100+
&& options.getSessionIdleTimeoutSeconds().getAsInt() > 0) {
101101
args.add("--session-idle-timeout");
102-
args.add(String.valueOf(options.getSessionIdleTimeoutSeconds()));
102+
args.add(String.valueOf(options.getSessionIdleTimeoutSeconds().getAsInt()));
103103
}
104104

105105
if (options.isRemote()) {
@@ -159,9 +159,9 @@ ProcessInfo startCliServer() throws IOException, InterruptedException {
159159
if (telemetry.getSourceName() != null) {
160160
pb.environment().put("COPILOT_OTEL_SOURCE_NAME", telemetry.getSourceName());
161161
}
162-
if (telemetry.getCaptureContent() != null) {
162+
if (telemetry.getCaptureContent().isPresent()) {
163163
pb.environment().put("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT",
164-
telemetry.getCaptureContent() ? "true" : "false");
164+
telemetry.getCaptureContent().get() ? "true" : "false");
165165
}
166166
}
167167

src/main/java/com/github/copilot/sdk/CopilotClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public CopilotClient(CopilotClientOptions options) {
120120

121121
// Validate auth options with external server
122122
if (this.options.getCliUrl() != null && !this.options.getCliUrl().isEmpty()
123-
&& (this.options.getGitHubToken() != null || this.options.getUseLoggedInUser() != null)) {
123+
&& (this.options.getGitHubToken() != null || this.options.getUseLoggedInUser().isPresent())) {
124124
throw new IllegalArgumentException(
125125
"GitHubToken and UseLoggedInUser cannot be used with CliUrl (external server manages its own auth)");
126126
}

src/main/java/com/github/copilot/sdk/CopilotSession.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,7 +1118,7 @@ private void handleElicitationRequestAsync(ElicitationContext context, String re
11181118
*/
11191119
private void assertElicitation() {
11201120
SessionCapabilities caps = capabilities;
1121-
if (caps == null || caps.getUi() == null || !Boolean.TRUE.equals(caps.getUi().getElicitation())) {
1121+
if (caps == null || caps.getUi() == null || !caps.getUi().getElicitation().orElse(false)) {
11221122
throw new IllegalStateException("Elicitation is not supported by the host. "
11231123
+ "Check session.getCapabilities().getUi()?.getElicitation() before calling UI methods.");
11241124
}
@@ -1201,10 +1201,10 @@ public CompletableFuture<String> input(String message, InputOptions options) {
12011201
field.put("title", options.getTitle());
12021202
if (options.getDescription() != null)
12031203
field.put("description", options.getDescription());
1204-
if (options.getMinLength() != null)
1205-
field.put("minLength", options.getMinLength());
1206-
if (options.getMaxLength() != null)
1207-
field.put("maxLength", options.getMaxLength());
1204+
if (options.getMinLength().isPresent())
1205+
field.put("minLength", options.getMinLength().getAsInt());
1206+
if (options.getMaxLength().isPresent())
1207+
field.put("maxLength", options.getMaxLength().getAsInt());
12081208
if (options.getFormat() != null)
12091209
field.put("format", options.getFormat());
12101210
if (options.getDefaultValue() != null)
@@ -1695,7 +1695,8 @@ public CompletableFuture<Void> setModel(String model, String reasoningEffort,
16951695
ModelCapabilitiesOverrideSupports supports = null;
16961696
if (modelCapabilities.getSupports() != null) {
16971697
var s = modelCapabilities.getSupports();
1698-
supports = new ModelCapabilitiesOverrideSupports(s.getVision(), s.getReasoningEffort());
1698+
supports = new ModelCapabilitiesOverrideSupports(s.getVision().orElse(null),
1699+
s.getReasoningEffort().orElse(null));
16991700
}
17001701
ModelCapabilitiesOverrideLimits limits = null;
17011702
if (modelCapabilities.getLimits() != null) {

src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,18 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess
111111
request.setAvailableTools(config.getAvailableTools());
112112
request.setExcludedTools(config.getExcludedTools());
113113
request.setProvider(config.getProvider());
114-
request.setEnableSessionTelemetry(config.getEnableSessionTelemetry());
115-
request.setRequestUserInput(config.getOnUserInputRequest() != null ? true : null);
116-
request.setHooks(config.getHooks() != null && config.getHooks().hasHooks() ? true : null);
114+
config.getEnableSessionTelemetry().ifPresent(request::setEnableSessionTelemetry);
115+
if (config.getOnUserInputRequest() != null) {
116+
request.setRequestUserInput(true);
117+
}
118+
if (config.getHooks() != null && config.getHooks().hasHooks()) {
119+
request.setHooks(true);
120+
}
117121
request.setWorkingDirectory(config.getWorkingDirectory());
118-
request.setStreaming(config.isStreaming() ? true : null);
119-
request.setIncludeSubAgentStreamingEvents(config.getIncludeSubAgentStreamingEvents());
122+
if (config.isStreaming()) {
123+
request.setStreaming(true);
124+
}
125+
config.getIncludeSubAgentStreamingEvents().ifPresent(request::setIncludeSubAgentStreamingEvents);
120126
request.setMcpServers(config.getMcpServers());
121127
request.setCustomAgents(config.getCustomAgents());
122128
request.setDefaultAgent(config.getDefaultAgent());
@@ -126,7 +132,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess
126132
request.setInstructionDirectories(config.getInstructionDirectories());
127133
request.setDisabledSkills(config.getDisabledSkills());
128134
request.setConfigDir(config.getConfigDir());
129-
request.setEnableConfigDiscovery(config.getEnableConfigDiscovery());
135+
config.getEnableConfigDiscovery().ifPresent(request::setEnableConfigDiscovery);
130136
request.setModelCapabilities(config.getModelCapabilities());
131137

132138
if (config.getCommands() != null && !config.getCommands().isEmpty()) {
@@ -194,15 +200,23 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo
194200
request.setAvailableTools(config.getAvailableTools());
195201
request.setExcludedTools(config.getExcludedTools());
196202
request.setProvider(config.getProvider());
197-
request.setEnableSessionTelemetry(config.getEnableSessionTelemetry());
198-
request.setRequestUserInput(config.getOnUserInputRequest() != null ? true : null);
199-
request.setHooks(config.getHooks() != null && config.getHooks().hasHooks() ? true : null);
203+
config.getEnableSessionTelemetry().ifPresent(request::setEnableSessionTelemetry);
204+
if (config.getOnUserInputRequest() != null) {
205+
request.setRequestUserInput(true);
206+
}
207+
if (config.getHooks() != null && config.getHooks().hasHooks()) {
208+
request.setHooks(true);
209+
}
200210
request.setWorkingDirectory(config.getWorkingDirectory());
201211
request.setConfigDir(config.getConfigDir());
202-
request.setEnableConfigDiscovery(config.getEnableConfigDiscovery());
203-
request.setDisableResume(config.isDisableResume() ? true : null);
204-
request.setStreaming(config.isStreaming() ? true : null);
205-
request.setIncludeSubAgentStreamingEvents(config.getIncludeSubAgentStreamingEvents());
212+
config.getEnableConfigDiscovery().ifPresent(request::setEnableConfigDiscovery);
213+
if (config.isDisableResume()) {
214+
request.setDisableResume(true);
215+
}
216+
if (config.isStreaming()) {
217+
request.setStreaming(true);
218+
}
219+
config.getIncludeSubAgentStreamingEvents().ifPresent(request::setIncludeSubAgentStreamingEvents);
206220
request.setMcpServers(config.getMcpServers());
207221
request.setCustomAgents(config.getCustomAgents());
208222
request.setDefaultAgent(config.getDefaultAgent());

src/main/java/com/github/copilot/sdk/json/CopilotClientOptions.java

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
import java.util.function.Supplier;
1515

1616
import com.fasterxml.jackson.annotation.JsonInclude;
17+
import com.fasterxml.jackson.annotation.JsonIgnore;
18+
import java.util.Optional;
19+
import java.util.OptionalInt;
1720

1821
/**
1922
* Configuration options for creating a
@@ -499,34 +502,46 @@ public CopilotClientOptions setTelemetry(TelemetryConfig telemetry) {
499502
/**
500503
* Gets the server-wide idle timeout for sessions in seconds.
501504
*
502-
* @return the session idle timeout in seconds, or {@code null} to disable
503-
* (sessions live indefinitely)
505+
* @return an {@link OptionalInt} containing the session idle timeout in
506+
* seconds, or empty to disable (sessions live indefinitely)
504507
* @since 1.3.0
505508
*/
506-
public Integer getSessionIdleTimeoutSeconds() {
507-
return sessionIdleTimeoutSeconds;
509+
@JsonIgnore
510+
public OptionalInt getSessionIdleTimeoutSeconds() {
511+
return sessionIdleTimeoutSeconds == null ? OptionalInt.empty() : OptionalInt.of(sessionIdleTimeoutSeconds);
508512
}
509513

510514
/**
511515
* Sets the server-wide idle timeout for sessions in seconds.
512516
* <p>
513517
* Sessions without activity for this duration are automatically cleaned up. Set
514-
* to {@code 0} or leave as {@code null} to disable (sessions live
515-
* indefinitely).
518+
* to {@code 0} to disable (sessions live indefinitely). Use
519+
* {@link #clearSessionIdleTimeoutSeconds()} to revert to the default.
516520
* <p>
517521
* This option is only used when the SDK spawns the CLI process; it is ignored
518522
* when connecting to an external server via {@link #setCliUrl(String)}.
519523
*
520524
* @param sessionIdleTimeoutSeconds
521-
* the idle timeout in seconds, or {@code null} to disable
525+
* the idle timeout in seconds
522526
* @return this options instance for method chaining
523527
* @since 1.3.0
524528
*/
525-
public CopilotClientOptions setSessionIdleTimeoutSeconds(Integer sessionIdleTimeoutSeconds) {
529+
public CopilotClientOptions setSessionIdleTimeoutSeconds(int sessionIdleTimeoutSeconds) {
526530
this.sessionIdleTimeoutSeconds = sessionIdleTimeoutSeconds;
527531
return this;
528532
}
529533

534+
/**
535+
* Clears the sessionIdleTimeoutSeconds setting, reverting to the default
536+
* behavior.
537+
*
538+
* @return this instance for method chaining
539+
*/
540+
public CopilotClientOptions clearSessionIdleTimeoutSeconds() {
541+
this.sessionIdleTimeoutSeconds = null;
542+
return this;
543+
}
544+
530545
/**
531546
* Gets the connection token for the headless CLI server (TCP only).
532547
*
@@ -555,11 +570,11 @@ public CopilotClientOptions setTcpConnectionToken(String tcpConnectionToken) {
555570
/**
556571
* Returns whether to use the logged-in user for authentication.
557572
*
558-
* @return {@code true} to use logged-in user auth, {@code false} to use only
559-
* explicit tokens, or {@code null} to use default behavior
573+
* @return an {@link Optional} containing the boolean value, or empty if not set
560574
*/
561-
public Boolean getUseLoggedInUser() {
562-
return useLoggedInUser;
575+
@JsonIgnore
576+
public Optional<Boolean> getUseLoggedInUser() {
577+
return Optional.ofNullable(useLoggedInUser);
563578
}
564579

565580
/**
@@ -569,15 +584,23 @@ public Boolean getUseLoggedInUser() {
569584
* auth. When false, only explicit tokens (gitHubToken or environment variables)
570585
* are used. Default: true (but defaults to false when gitHubToken is provided).
571586
* <p>
572-
* Passing {@code null} is equivalent to passing {@link Boolean#FALSE}.
573587
*
574588
* @param useLoggedInUser
575-
* {@code true} to use logged-in user auth, {@code false} or
576-
* {@code null} otherwise
589+
* {@code true} to use logged-in user auth, {@code false} otherwise
577590
* @return this options instance for method chaining
578591
*/
579-
public CopilotClientOptions setUseLoggedInUser(Boolean useLoggedInUser) {
580-
this.useLoggedInUser = useLoggedInUser != null ? useLoggedInUser : Boolean.FALSE;
592+
public CopilotClientOptions setUseLoggedInUser(boolean useLoggedInUser) {
593+
this.useLoggedInUser = useLoggedInUser;
594+
return this;
595+
}
596+
597+
/**
598+
* Clears the useLoggedInUser setting, reverting to the default behavior.
599+
*
600+
* @return this instance for method chaining
601+
*/
602+
public CopilotClientOptions clearUseLoggedInUser() {
603+
this.useLoggedInUser = null;
581604
return this;
582605
}
583606

0 commit comments

Comments
 (0)