Skip to content

Commit 639f2ee

Browse files
authored
Merge pull request #138 from copilot-community-sdk/copilot/sync-upstream-commits
Upstream sync: Add clone() methods to config classes (6 commits)
2 parents efdec38 + 6e2e991 commit 639f2ee

File tree

7 files changed

+267
-1
lines changed

7 files changed

+267
-1
lines changed

.lastmerge

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
304d812cd4c98755159da427c6701bfb7e0b7c32
1+
5016587a62652f3d184b3c6958dfc63359921aa8

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ ProcessInfo startCliServer() throws IOException, InterruptedException {
8585
var pb = new ProcessBuilder(command);
8686
pb.redirectErrorStream(false);
8787

88+
// Note: On Windows, console window visibility depends on how the parent Java
89+
// process was launched. GUI applications started with 'javaw' will not create
90+
// visible console windows for subprocesses. Console applications started with
91+
// 'java' will share their console with subprocesses. Java's ProcessBuilder
92+
// doesn't provide explicit CREATE_NO_WINDOW flags like native Windows APIs,
93+
// but the default behavior is appropriate for most use cases.
94+
8895
if (options.getCwd() != null) {
8996
pb.directory(new File(options.getCwd()));
9097
}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,4 +323,32 @@ public CopilotClientOptions setUseLoggedInUser(Boolean useLoggedInUser) {
323323
this.useLoggedInUser = useLoggedInUser;
324324
return this;
325325
}
326+
327+
/**
328+
* Creates a shallow clone of this {@code CopilotClientOptions} instance.
329+
* <p>
330+
* Array properties (like {@code cliArgs}) are copied into new arrays so that
331+
* modifications to the clone do not affect the original. The
332+
* {@code environment} map is also copied to a new map instance. Other
333+
* reference-type properties are shared between the original and clone.
334+
*
335+
* @return a clone of this options instance
336+
*/
337+
@Override
338+
public CopilotClientOptions clone() {
339+
CopilotClientOptions copy = new CopilotClientOptions();
340+
copy.cliPath = this.cliPath;
341+
copy.cliArgs = this.cliArgs != null ? this.cliArgs.clone() : null;
342+
copy.cwd = this.cwd;
343+
copy.port = this.port;
344+
copy.useStdio = this.useStdio;
345+
copy.cliUrl = this.cliUrl;
346+
copy.logLevel = this.logLevel;
347+
copy.autoStart = this.autoStart;
348+
copy.autoRestart = this.autoRestart;
349+
copy.environment = this.environment != null ? new java.util.HashMap<>(this.environment) : null;
350+
copy.githubToken = this.githubToken;
351+
copy.useLoggedInUser = this.useLoggedInUser;
352+
return copy;
353+
}
326354
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package com.github.copilot.sdk.json;
66

7+
import java.util.ArrayList;
78
import java.util.Collections;
89
import java.util.List;
910

@@ -107,4 +108,23 @@ public String getMode() {
107108
return mode;
108109
}
109110

111+
/**
112+
* Creates a shallow clone of this {@code MessageOptions} instance.
113+
* <p>
114+
* Mutable collection properties are copied into new collection instances so
115+
* that modifications to those collections on the clone do not affect the
116+
* original. Other reference-type properties (like attachment items) are not
117+
* deep-cloned; the original and the clone will share those objects.
118+
*
119+
* @return a clone of this options instance
120+
*/
121+
@Override
122+
public MessageOptions clone() {
123+
MessageOptions copy = new MessageOptions();
124+
copy.prompt = this.prompt;
125+
copy.attachments = this.attachments != null ? new ArrayList<>(this.attachments) : null;
126+
copy.mode = this.mode;
127+
return copy;
128+
}
129+
110130
}

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package com.github.copilot.sdk.json;
66

7+
import java.util.ArrayList;
78
import java.util.Collections;
89
import java.util.List;
910
import java.util.Map;
@@ -475,4 +476,40 @@ public ResumeSessionConfig setInfiniteSessions(InfiniteSessionConfig infiniteSes
475476
this.infiniteSessions = infiniteSessions;
476477
return this;
477478
}
479+
480+
/**
481+
* Creates a shallow clone of this {@code ResumeSessionConfig} instance.
482+
* <p>
483+
* Mutable collection properties are copied into new collection instances so
484+
* that modifications to those collections on the clone do not affect the
485+
* original. Other reference-type properties (like provider configuration,
486+
* system messages, hooks, infinite session configuration, and handlers) are not
487+
* deep-cloned; the original and the clone will share those objects.
488+
*
489+
* @return a clone of this config instance
490+
*/
491+
@Override
492+
public ResumeSessionConfig clone() {
493+
ResumeSessionConfig copy = new ResumeSessionConfig();
494+
copy.model = this.model;
495+
copy.tools = this.tools != null ? new ArrayList<>(this.tools) : null;
496+
copy.systemMessage = this.systemMessage;
497+
copy.availableTools = this.availableTools != null ? new ArrayList<>(this.availableTools) : null;
498+
copy.excludedTools = this.excludedTools != null ? new ArrayList<>(this.excludedTools) : null;
499+
copy.provider = this.provider;
500+
copy.reasoningEffort = this.reasoningEffort;
501+
copy.onPermissionRequest = this.onPermissionRequest;
502+
copy.onUserInputRequest = this.onUserInputRequest;
503+
copy.hooks = this.hooks;
504+
copy.workingDirectory = this.workingDirectory;
505+
copy.configDir = this.configDir;
506+
copy.disableResume = this.disableResume;
507+
copy.streaming = this.streaming;
508+
copy.mcpServers = this.mcpServers != null ? new java.util.HashMap<>(this.mcpServers) : null;
509+
copy.customAgents = this.customAgents != null ? new ArrayList<>(this.customAgents) : null;
510+
copy.skillDirectories = this.skillDirectories != null ? new ArrayList<>(this.skillDirectories) : null;
511+
copy.disabledSkills = this.disabledSkills != null ? new ArrayList<>(this.disabledSkills) : null;
512+
copy.infiniteSessions = this.infiniteSessions;
513+
return copy;
514+
}
478515
}

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package com.github.copilot.sdk.json;
66

7+
import java.util.ArrayList;
78
import java.util.Collections;
89
import java.util.List;
910
import java.util.Map;
@@ -512,4 +513,40 @@ public SessionConfig setConfigDir(String configDir) {
512513
this.configDir = configDir;
513514
return this;
514515
}
516+
517+
/**
518+
* Creates a shallow clone of this {@code SessionConfig} instance.
519+
* <p>
520+
* Mutable collection properties are copied into new collection instances so
521+
* that modifications to those collections on the clone do not affect the
522+
* original. Other reference-type properties (like provider configuration,
523+
* system messages, hooks, infinite session configuration, and handlers) are not
524+
* deep-cloned; the original and the clone will share those objects.
525+
*
526+
* @return a clone of this config instance
527+
*/
528+
@Override
529+
public SessionConfig clone() {
530+
SessionConfig copy = new SessionConfig();
531+
copy.sessionId = this.sessionId;
532+
copy.model = this.model;
533+
copy.reasoningEffort = this.reasoningEffort;
534+
copy.tools = this.tools != null ? new ArrayList<>(this.tools) : null;
535+
copy.systemMessage = this.systemMessage;
536+
copy.availableTools = this.availableTools != null ? new ArrayList<>(this.availableTools) : null;
537+
copy.excludedTools = this.excludedTools != null ? new ArrayList<>(this.excludedTools) : null;
538+
copy.provider = this.provider;
539+
copy.onPermissionRequest = this.onPermissionRequest;
540+
copy.onUserInputRequest = this.onUserInputRequest;
541+
copy.hooks = this.hooks;
542+
copy.workingDirectory = this.workingDirectory;
543+
copy.streaming = this.streaming;
544+
copy.mcpServers = this.mcpServers != null ? new java.util.HashMap<>(this.mcpServers) : null;
545+
copy.customAgents = this.customAgents != null ? new ArrayList<>(this.customAgents) : null;
546+
copy.infiniteSessions = this.infiniteSessions;
547+
copy.skillDirectories = this.skillDirectories != null ? new ArrayList<>(this.skillDirectories) : null;
548+
copy.disabledSkills = this.disabledSkills != null ? new ArrayList<>(this.disabledSkills) : null;
549+
copy.configDir = this.configDir;
550+
return copy;
551+
}
515552
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.sdk;
6+
7+
import static org.junit.jupiter.api.Assertions.*;
8+
9+
import java.util.ArrayList;
10+
import java.util.HashMap;
11+
import java.util.List;
12+
import java.util.Map;
13+
14+
import org.junit.jupiter.api.Test;
15+
16+
import com.github.copilot.sdk.json.CopilotClientOptions;
17+
import com.github.copilot.sdk.json.SessionConfig;
18+
import com.github.copilot.sdk.json.ResumeSessionConfig;
19+
import com.github.copilot.sdk.json.MessageOptions;
20+
21+
class ConfigCloneTest {
22+
23+
@Test
24+
void copilotClientOptionsCloneBasic() {
25+
CopilotClientOptions original = new CopilotClientOptions();
26+
original.setCliPath("/usr/local/bin/copilot");
27+
original.setLogLevel("debug");
28+
original.setPort(9000);
29+
30+
CopilotClientOptions cloned = original.clone();
31+
32+
assertEquals(original.getCliPath(), cloned.getCliPath());
33+
assertEquals(original.getLogLevel(), cloned.getLogLevel());
34+
assertEquals(original.getPort(), cloned.getPort());
35+
}
36+
37+
@Test
38+
void copilotClientOptionsArrayIndependence() {
39+
CopilotClientOptions original = new CopilotClientOptions();
40+
String[] args = {"--flag1", "--flag2"};
41+
original.setCliArgs(args);
42+
43+
CopilotClientOptions cloned = original.clone();
44+
cloned.getCliArgs()[0] = "--changed";
45+
46+
assertEquals("--flag1", original.getCliArgs()[0]);
47+
assertEquals("--changed", cloned.getCliArgs()[0]);
48+
}
49+
50+
@Test
51+
void copilotClientOptionsEnvironmentIndependence() {
52+
CopilotClientOptions original = new CopilotClientOptions();
53+
Map<String, String> env = new HashMap<>();
54+
env.put("KEY1", "value1");
55+
original.setEnvironment(env);
56+
57+
CopilotClientOptions cloned = original.clone();
58+
59+
// Mutate the original environment map to test independence
60+
env.put("KEY2", "value2");
61+
62+
// The cloned config should be unaffected by mutations to the original map
63+
assertEquals(1, cloned.getEnvironment().size());
64+
assertEquals(2, original.getEnvironment().size());
65+
}
66+
67+
@Test
68+
void sessionConfigCloneBasic() {
69+
SessionConfig original = new SessionConfig();
70+
original.setSessionId("my-session");
71+
original.setModel("gpt-4o");
72+
original.setStreaming(true);
73+
74+
SessionConfig cloned = original.clone();
75+
76+
assertEquals(original.getSessionId(), cloned.getSessionId());
77+
assertEquals(original.getModel(), cloned.getModel());
78+
assertEquals(original.isStreaming(), cloned.isStreaming());
79+
}
80+
81+
@Test
82+
void sessionConfigListIndependence() {
83+
SessionConfig original = new SessionConfig();
84+
List<String> toolList = new ArrayList<>();
85+
toolList.add("grep");
86+
toolList.add("bash");
87+
original.setAvailableTools(toolList);
88+
89+
SessionConfig cloned = original.clone();
90+
91+
// Mutate the original list directly to test independence
92+
toolList.add("web");
93+
94+
// The cloned config should be unaffected by mutations to the original list
95+
assertEquals(2, cloned.getAvailableTools().size());
96+
assertEquals(3, original.getAvailableTools().size());
97+
}
98+
99+
@Test
100+
void resumeSessionConfigCloneBasic() {
101+
ResumeSessionConfig original = new ResumeSessionConfig();
102+
original.setModel("o1");
103+
original.setStreaming(false);
104+
105+
ResumeSessionConfig cloned = original.clone();
106+
107+
assertEquals(original.getModel(), cloned.getModel());
108+
assertEquals(original.isStreaming(), cloned.isStreaming());
109+
}
110+
111+
@Test
112+
void messageOptionsCloneBasic() {
113+
MessageOptions original = new MessageOptions();
114+
original.setPrompt("What is 2+2?");
115+
original.setMode("immediate");
116+
117+
MessageOptions cloned = original.clone();
118+
119+
assertEquals(original.getPrompt(), cloned.getPrompt());
120+
assertEquals(original.getMode(), cloned.getMode());
121+
}
122+
123+
@Test
124+
void clonePreservesNullFields() {
125+
CopilotClientOptions opts = new CopilotClientOptions();
126+
CopilotClientOptions optsClone = opts.clone();
127+
assertNull(optsClone.getCliPath());
128+
129+
SessionConfig cfg = new SessionConfig();
130+
SessionConfig cfgClone = cfg.clone();
131+
assertNull(cfgClone.getModel());
132+
133+
MessageOptions msg = new MessageOptions();
134+
MessageOptions msgClone = msg.clone();
135+
assertNull(msgClone.getMode());
136+
}
137+
}

0 commit comments

Comments
 (0)