Skip to content

Commit 764900f

Browse files
Copilotedburns
andauthored
Port copilotHome, tcpConnectionToken, instructionDirectories, continuePendingWork, and MCP OAuth fields from reference implementation
- Add copilotHome option to CopilotClientOptions (sets COPILOT_HOME env var) - Add tcpConnectionToken option to CopilotClientOptions (sets COPILOT_CONNECTION_TOKEN env var) - Add instructionDirectories to SessionConfig and ResumeSessionConfig - Add continuePendingWork to ResumeSessionConfig - Add oauthClientId, oauthPublicClient, oauthGrantType to McpHttpServerConfig - Update SessionRequestBuilder to pass new fields in RPC requests - Add unit tests for new request builder fields - Add E2E tests for instructionDirectories on create and resume - Update .lastmerge to e8dabaf9d9734c92f4d1541dd8c4169cedbfb688 Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
1 parent 130e541 commit 764900f

11 files changed

Lines changed: 415 additions & 1 deletion

.lastmerge

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
e42b726ca42bd1b2e099a956c9287ba9435ba3e5
1+
e8dabaf9d9734c92f4d1541dd8c4169cedbfb688

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,16 @@ ProcessInfo startCliServer() throws IOException, InterruptedException {
115115
pb.environment().put("COPILOT_SDK_AUTH_TOKEN", options.getGitHubToken());
116116
}
117117

118+
// Set connection token in environment if provided
119+
if (options.getTcpConnectionToken() != null && !options.getTcpConnectionToken().isEmpty()) {
120+
pb.environment().put("COPILOT_CONNECTION_TOKEN", options.getTcpConnectionToken());
121+
}
122+
123+
// Set copilot home directory in environment if provided
124+
if (options.getCopilotHome() != null && !options.getCopilotHome().isEmpty()) {
125+
pb.environment().put("COPILOT_HOME", options.getCopilotHome());
126+
}
127+
118128
// Set telemetry environment variables if configured
119129
if (options.getTelemetry() != null) {
120130
var telemetry = options.getTelemetry();

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess
122122
request.setAgent(config.getAgent());
123123
request.setInfiniteSessions(config.getInfiniteSessions());
124124
request.setSkillDirectories(config.getSkillDirectories());
125+
request.setInstructionDirectories(config.getInstructionDirectories());
125126
request.setDisabledSkills(config.getDisabledSkills());
126127
request.setConfigDir(config.getConfigDir());
127128
request.setEnableConfigDiscovery(config.getEnableConfigDiscovery());
@@ -199,8 +200,10 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo
199200
request.setDefaultAgent(config.getDefaultAgent());
200201
request.setAgent(config.getAgent());
201202
request.setSkillDirectories(config.getSkillDirectories());
203+
request.setInstructionDirectories(config.getInstructionDirectories());
202204
request.setDisabledSkills(config.getDisabledSkills());
203205
request.setInfiniteSessions(config.getInfiniteSessions());
206+
request.setContinuePendingWork(config.getContinuePendingWork());
204207
request.setModelCapabilities(config.getModelCapabilities());
205208

206209
if (config.getCommands() != null && !config.getCommands().isEmpty()) {

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,15 @@ public class CopilotClientOptions {
4444
private String[] cliArgs;
4545
private String cliPath;
4646
private String cliUrl;
47+
private String copilotHome;
4748
private String cwd;
4849
private Map<String, String> environment;
4950
private Executor executor;
5051
private String gitHubToken;
5152
private String logLevel = "info";
5253
private Supplier<CompletableFuture<List<ModelInfo>>> onListModels;
5354
private int port;
55+
private String tcpConnectionToken;
5456
private TelemetryConfig telemetry;
5557
private Integer sessionIdleTimeoutSeconds;
5658
private Boolean useLoggedInUser;
@@ -214,6 +216,36 @@ public CopilotClientOptions setCwd(String cwd) {
214216
return this;
215217
}
216218

219+
/**
220+
* Gets the base directory for Copilot data (session state, config, etc.).
221+
*
222+
* @return the copilot home directory path, or {@code null} to use the CLI
223+
* default ({@code ~/.copilot})
224+
* @since 1.4.0
225+
*/
226+
public String getCopilotHome() {
227+
return copilotHome;
228+
}
229+
230+
/**
231+
* Sets the base directory for Copilot data (session state, config, etc.).
232+
* <p>
233+
* Sets the {@code COPILOT_HOME} environment variable on the spawned CLI
234+
* process. When {@code null}, the CLI defaults to {@code ~/.copilot}.
235+
* <p>
236+
* This option is only used when the SDK spawns the CLI process; it is ignored
237+
* when connecting to an external server via {@link #setCliUrl(String)}.
238+
*
239+
* @param copilotHome
240+
* the directory path for Copilot data
241+
* @return this options instance for method chaining
242+
* @since 1.4.0
243+
*/
244+
public CopilotClientOptions setCopilotHome(String copilotHome) {
245+
this.copilotHome = copilotHome;
246+
return this;
247+
}
248+
217249
/**
218250
* Gets the environment variables for the CLI process.
219251
* <p>
@@ -462,6 +494,33 @@ public CopilotClientOptions setSessionIdleTimeoutSeconds(Integer sessionIdleTime
462494
return this;
463495
}
464496

497+
/**
498+
* Gets the connection token for the headless CLI server (TCP only).
499+
*
500+
* @return the connection token, or {@code null} if not set
501+
* @since 1.4.0
502+
*/
503+
public String getTcpConnectionToken() {
504+
return tcpConnectionToken;
505+
}
506+
507+
/**
508+
* Sets the connection token for the headless CLI server (TCP only).
509+
* <p>
510+
* When the SDK spawns its own CLI in TCP mode and this is omitted, a UUID is
511+
* generated automatically so the loopback listener is safe by default. Cannot
512+
* be combined with {@link #setUseStdio(boolean)} set to {@code true}.
513+
*
514+
* @param tcpConnectionToken
515+
* the connection token (must be non-empty if provided)
516+
* @return this options instance for method chaining
517+
* @since 1.4.0
518+
*/
519+
public CopilotClientOptions setTcpConnectionToken(String tcpConnectionToken) {
520+
this.tcpConnectionToken = tcpConnectionToken;
521+
return this;
522+
}
523+
465524
/**
466525
* Returns whether to use the logged-in user for authentication.
467526
*
@@ -533,6 +592,7 @@ public CopilotClientOptions clone() {
533592
copy.cliArgs = this.cliArgs != null ? this.cliArgs.clone() : null;
534593
copy.cliPath = this.cliPath;
535594
copy.cliUrl = this.cliUrl;
595+
copy.copilotHome = this.copilotHome;
536596
copy.cwd = this.cwd;
537597
copy.environment = this.environment != null ? new java.util.HashMap<>(this.environment) : null;
538598
copy.executor = this.executor;
@@ -541,6 +601,7 @@ public CopilotClientOptions clone() {
541601
copy.onListModels = this.onListModels;
542602
copy.port = this.port;
543603
copy.sessionIdleTimeoutSeconds = this.sessionIdleTimeoutSeconds;
604+
copy.tcpConnectionToken = this.tcpConnectionToken;
544605
copy.telemetry = this.telemetry;
545606
copy.useLoggedInUser = this.useLoggedInUser;
546607
copy.useStdio = this.useStdio;

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ public final class CreateSessionRequest {
9191
@JsonProperty("skillDirectories")
9292
private List<String> skillDirectories;
9393

94+
@JsonProperty("instructionDirectories")
95+
private List<String> instructionDirectories;
96+
9497
@JsonProperty("disabledSkills")
9598
private List<String> disabledSkills;
9699

@@ -326,6 +329,18 @@ public void setSkillDirectories(List<String> skillDirectories) {
326329
this.skillDirectories = skillDirectories;
327330
}
328331

332+
/** Gets instruction directories. @return the instruction directories */
333+
public List<String> getInstructionDirectories() {
334+
return instructionDirectories == null ? null : Collections.unmodifiableList(instructionDirectories);
335+
}
336+
337+
/**
338+
* Sets instruction directories. @param instructionDirectories the directories
339+
*/
340+
public void setInstructionDirectories(List<String> instructionDirectories) {
341+
this.instructionDirectories = instructionDirectories;
342+
}
343+
329344
/** Gets disabled skills. @return the disabled skill names */
330345
public List<String> getDisabledSkills() {
331346
return disabledSkills == null ? null : Collections.unmodifiableList(disabledSkills);

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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ public final class McpHttpServerConfig extends McpServerConfig {
4141
@JsonProperty("headers")
4242
private Map<String, String> headers;
4343

44+
@JsonProperty("oauthClientId")
45+
private String oauthClientId;
46+
47+
@JsonProperty("oauthPublicClient")
48+
private Boolean oauthPublicClient;
49+
50+
@JsonProperty("oauthGrantType")
51+
private String oauthGrantType;
52+
4453
/**
4554
* Gets the server type discriminator.
4655
*
@@ -92,6 +101,77 @@ public McpHttpServerConfig setHeaders(Map<String, String> headers) {
92101
return this;
93102
}
94103

104+
/**
105+
* Gets the optional OAuth client ID for the remote server.
106+
*
107+
* @return the OAuth client ID, or {@code null}
108+
* @since 1.4.0
109+
*/
110+
public String getOauthClientId() {
111+
return oauthClientId;
112+
}
113+
114+
/**
115+
* Sets the optional OAuth client ID for the remote server.
116+
*
117+
* @param oauthClientId
118+
* the OAuth client ID
119+
* @return this config for method chaining
120+
* @since 1.4.0
121+
*/
122+
public McpHttpServerConfig setOauthClientId(String oauthClientId) {
123+
this.oauthClientId = oauthClientId;
124+
return this;
125+
}
126+
127+
/**
128+
* Gets whether this is a public OAuth client.
129+
*
130+
* @return {@code true} if public OAuth client, or {@code null}
131+
* @since 1.4.0
132+
*/
133+
public Boolean getOauthPublicClient() {
134+
return oauthPublicClient;
135+
}
136+
137+
/**
138+
* Sets whether this is a public OAuth client.
139+
*
140+
* @param oauthPublicClient
141+
* whether this is a public OAuth client
142+
* @return this config for method chaining
143+
* @since 1.4.0
144+
*/
145+
public McpHttpServerConfig setOauthPublicClient(Boolean oauthPublicClient) {
146+
this.oauthPublicClient = oauthPublicClient;
147+
return this;
148+
}
149+
150+
/**
151+
* Gets the optional OAuth grant type for the remote server.
152+
*
153+
* @return the OAuth grant type (e.g., "authorization_code" or
154+
* "client_credentials"), or {@code null}
155+
* @since 1.4.0
156+
*/
157+
public String getOauthGrantType() {
158+
return oauthGrantType;
159+
}
160+
161+
/**
162+
* Sets the optional OAuth grant type for the remote server.
163+
*
164+
* @param oauthGrantType
165+
* the OAuth grant type (e.g., "authorization_code" or
166+
* "client_credentials")
167+
* @return this config for method chaining
168+
* @since 1.4.0
169+
*/
170+
public McpHttpServerConfig setOauthGrantType(String oauthGrantType) {
171+
this.oauthGrantType = oauthGrantType;
172+
return this;
173+
}
174+
95175
@Override
96176
public McpHttpServerConfig setTools(List<String> tools) {
97177
super.setTools(tools);

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,10 @@ public class ResumeSessionConfig {
5959
private DefaultAgentConfig defaultAgent;
6060
private String agent;
6161
private List<String> skillDirectories;
62+
private List<String> instructionDirectories;
6263
private List<String> disabledSkills;
6364
private InfiniteSessionConfig infiniteSessions;
65+
private Boolean continuePendingWork;
6466
private Consumer<SessionEvent> onEvent;
6567
private List<CommandDefinition> commands;
6668
private ElicitationHandler onElicitationRequest;
@@ -591,6 +593,29 @@ public ResumeSessionConfig setSkillDirectories(List<String> skillDirectories) {
591593
return this;
592594
}
593595

596+
/**
597+
* Gets the instruction directories.
598+
*
599+
* @return the list of instruction directory paths
600+
* @since 1.4.0
601+
*/
602+
public List<String> getInstructionDirectories() {
603+
return instructionDirectories == null ? null : Collections.unmodifiableList(instructionDirectories);
604+
}
605+
606+
/**
607+
* Sets additional directories to search for custom instruction files.
608+
*
609+
* @param instructionDirectories
610+
* the list of instruction directory paths
611+
* @return this config for method chaining
612+
* @since 1.4.0
613+
*/
614+
public ResumeSessionConfig setInstructionDirectories(List<String> instructionDirectories) {
615+
this.instructionDirectories = instructionDirectories;
616+
return this;
617+
}
618+
594619
/**
595620
* Gets the disabled skills.
596621
*
@@ -635,6 +660,37 @@ public ResumeSessionConfig setInfiniteSessions(InfiniteSessionConfig infiniteSes
635660
return this;
636661
}
637662

663+
/**
664+
* Gets whether to continue pending work on resume.
665+
*
666+
* @return {@code true} to continue pending work, {@code false} or {@code null}
667+
* to treat pending work as interrupted
668+
* @since 1.4.0
669+
*/
670+
public Boolean getContinuePendingWork() {
671+
return continuePendingWork;
672+
}
673+
674+
/**
675+
* Sets whether to continue any tool calls or permission prompts that were still
676+
* pending when the session was last suspended.
677+
* <p>
678+
* When {@code true}, the runtime continues pending work on resume. When
679+
* {@code false} (the default), the runtime treats pending work as interrupted.
680+
* For permission requests, the runtime re-emits {@code permission.requested} so
681+
* the registered handler can re-prompt; for external tool calls, the consumer
682+
* is expected to supply the result via the corresponding low-level RPC method.
683+
*
684+
* @param continuePendingWork
685+
* whether to continue pending work
686+
* @return this config for method chaining
687+
* @since 1.4.0
688+
*/
689+
public ResumeSessionConfig setContinuePendingWork(Boolean continuePendingWork) {
690+
this.continuePendingWork = continuePendingWork;
691+
return this;
692+
}
693+
638694
/**
639695
* Gets the event handler registered before the session.resume RPC is issued.
640696
*
@@ -775,8 +831,12 @@ public ResumeSessionConfig clone() {
775831
copy.defaultAgent = this.defaultAgent;
776832
copy.agent = this.agent;
777833
copy.skillDirectories = this.skillDirectories != null ? new ArrayList<>(this.skillDirectories) : null;
834+
copy.instructionDirectories = this.instructionDirectories != null
835+
? new ArrayList<>(this.instructionDirectories)
836+
: null;
778837
copy.disabledSkills = this.disabledSkills != null ? new ArrayList<>(this.disabledSkills) : null;
779838
copy.infiniteSessions = this.infiniteSessions;
839+
copy.continuePendingWork = this.continuePendingWork;
780840
copy.onEvent = this.onEvent;
781841
copy.commands = this.commands != null ? new ArrayList<>(this.commands) : null;
782842
copy.onElicitationRequest = this.onElicitationRequest;

0 commit comments

Comments
 (0)