>> onListModels;
private int port;
+ private String tcpConnectionToken;
private TelemetryConfig telemetry;
private Integer sessionIdleTimeoutSeconds;
private Boolean useLoggedInUser;
@@ -214,6 +216,36 @@ public CopilotClientOptions setCwd(String cwd) {
return this;
}
+ /**
+ * Gets the base directory for Copilot data (session state, config, etc.).
+ *
+ * @return the Copilot home directory path, or {@code null} to use the CLI
+ * default ({@code ~/.copilot})
+ * @since 1.4.0
+ */
+ public String getCopilotHome() {
+ return copilotHome;
+ }
+
+ /**
+ * Sets the base directory for Copilot data (session state, config, etc.).
+ *
+ * Sets the {@code COPILOT_HOME} environment variable on the spawned CLI
+ * process. When {@code null}, the CLI defaults to {@code ~/.copilot}.
+ *
+ * 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 copilotHome
+ * the Copilot home directory path (must not be {@code null})
+ * @return this options instance for method chaining
+ * @since 1.4.0
+ */
+ public CopilotClientOptions setCopilotHome(String copilotHome) {
+ this.copilotHome = Objects.requireNonNull(copilotHome, "copilotHome must not be null");
+ return this;
+ }
+
/**
* Gets the environment variables for the CLI process.
*
@@ -405,6 +437,33 @@ public CopilotClientOptions setPort(int port) {
return this;
}
+ /**
+ * Gets the connection token for the headless CLI server (TCP only).
+ *
+ * @return the connection token, or {@code null} if not set
+ * @since 1.4.0
+ */
+ public String getTcpConnectionToken() {
+ return tcpConnectionToken;
+ }
+
+ /**
+ * Sets the connection token for the headless CLI server (TCP only).
+ *
+ * When the SDK spawns its own CLI in TCP mode and this is omitted, a UUID is
+ * generated automatically so the loopback listener is safe by default. Cannot
+ * be combined with {@link #setUseStdio(boolean)} set to {@code true}.
+ *
+ * @param tcpConnectionToken
+ * the connection token (must not be {@code null} or empty)
+ * @return this options instance for method chaining
+ * @since 1.4.0
+ */
+ public CopilotClientOptions setTcpConnectionToken(String tcpConnectionToken) {
+ this.tcpConnectionToken = Objects.requireNonNull(tcpConnectionToken, "tcpConnectionToken must not be null");
+ return this;
+ }
+
/**
* Gets the OpenTelemetry configuration for the CLI server.
*
@@ -533,6 +592,7 @@ public CopilotClientOptions clone() {
copy.cliArgs = this.cliArgs != null ? this.cliArgs.clone() : null;
copy.cliPath = this.cliPath;
copy.cliUrl = this.cliUrl;
+ copy.copilotHome = this.copilotHome;
copy.cwd = this.cwd;
copy.environment = this.environment != null ? new java.util.HashMap<>(this.environment) : null;
copy.executor = this.executor;
@@ -541,6 +601,7 @@ public CopilotClientOptions clone() {
copy.onListModels = this.onListModels;
copy.port = this.port;
copy.sessionIdleTimeoutSeconds = this.sessionIdleTimeoutSeconds;
+ copy.tcpConnectionToken = this.tcpConnectionToken;
copy.telemetry = this.telemetry;
copy.useLoggedInUser = this.useLoggedInUser;
copy.useStdio = this.useStdio;
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 ef8d5fda2..b94608caf 100644
--- a/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java
+++ b/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java
@@ -91,6 +91,9 @@ public final class CreateSessionRequest {
@JsonProperty("skillDirectories")
private List skillDirectories;
+ @JsonProperty("instructionDirectories")
+ private List instructionDirectories;
+
@JsonProperty("disabledSkills")
private List disabledSkills;
@@ -326,6 +329,18 @@ public void setSkillDirectories(List skillDirectories) {
this.skillDirectories = skillDirectories;
}
+ /** Gets instruction directories. @return the instruction directory paths */
+ public List getInstructionDirectories() {
+ return instructionDirectories == null ? null : Collections.unmodifiableList(instructionDirectories);
+ }
+
+ /**
+ * Sets instruction directories. @param instructionDirectories the directories
+ */
+ public void setInstructionDirectories(List instructionDirectories) {
+ this.instructionDirectories = instructionDirectories;
+ }
+
/** Gets disabled skills. @return the disabled skill names */
public List getDisabledSkills() {
return disabledSkills == null ? null : Collections.unmodifiableList(disabledSkills);
diff --git a/src/main/java/com/github/copilot/sdk/json/McpHttpServerConfig.java b/src/main/java/com/github/copilot/sdk/json/McpHttpServerConfig.java
index 7017db3d2..904dabf15 100644
--- a/src/main/java/com/github/copilot/sdk/json/McpHttpServerConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/McpHttpServerConfig.java
@@ -41,6 +41,15 @@ public final class McpHttpServerConfig extends McpServerConfig {
@JsonProperty("headers")
private Map headers;
+ @JsonProperty("oauthClientId")
+ private String oauthClientId;
+
+ @JsonProperty("oauthPublicClient")
+ private Boolean oauthPublicClient;
+
+ @JsonProperty("oauthGrantType")
+ private String oauthGrantType;
+
/**
* Gets the server type discriminator.
*
@@ -92,6 +101,79 @@ public McpHttpServerConfig setHeaders(Map headers) {
return this;
}
+ /**
+ * Gets the optional OAuth client ID for the remote server.
+ *
+ * @return the OAuth client ID, or {@code null}
+ * @since 1.4.0
+ */
+ public String getOauthClientId() {
+ return oauthClientId;
+ }
+
+ /**
+ * Sets the optional OAuth client ID for the remote server.
+ *
+ * @param oauthClientId
+ * the OAuth client ID
+ * @return this config for method chaining
+ * @since 1.4.0
+ */
+ public McpHttpServerConfig setOauthClientId(String oauthClientId) {
+ this.oauthClientId = oauthClientId;
+ return this;
+ }
+
+ /**
+ * Gets whether this is a public OAuth client.
+ *
+ * @return {@code true} if public, {@code null} if not set
+ * @since 1.4.0
+ */
+ public Boolean getOauthPublicClient() {
+ return oauthPublicClient;
+ }
+
+ /**
+ * Sets whether this is a public OAuth client.
+ *
+ * @param oauthPublicClient
+ * {@code true} if public
+ * @return this config for method chaining
+ * @since 1.4.0
+ */
+ public McpHttpServerConfig setOauthPublicClient(Boolean oauthPublicClient) {
+ this.oauthPublicClient = oauthPublicClient;
+ return this;
+ }
+
+ /**
+ * Gets the optional OAuth grant type for the remote server.
+ *
+ * Valid values: {@code "authorization_code"}, {@code "client_credentials"}.
+ *
+ * @return the OAuth grant type, or {@code null}
+ * @since 1.4.0
+ */
+ public String getOauthGrantType() {
+ return oauthGrantType;
+ }
+
+ /**
+ * Sets the optional OAuth grant type for the remote server.
+ *
+ * Valid values: {@code "authorization_code"}, {@code "client_credentials"}.
+ *
+ * @param oauthGrantType
+ * the OAuth grant type
+ * @return this config for method chaining
+ * @since 1.4.0
+ */
+ public McpHttpServerConfig setOauthGrantType(String oauthGrantType) {
+ this.oauthGrantType = oauthGrantType;
+ return this;
+ }
+
@Override
public McpHttpServerConfig setTools(List tools) {
super.setTools(tools);
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 b4dacf370..f0a5f6b49 100644
--- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java
@@ -51,6 +51,7 @@ public class ResumeSessionConfig {
private String workingDirectory;
private String configDir;
private Boolean enableConfigDiscovery;
+ private Boolean continuePendingWork;
private boolean disableResume;
private boolean streaming;
private Boolean includeSubAgentStreamingEvents;
@@ -59,6 +60,7 @@ public class ResumeSessionConfig {
private DefaultAgentConfig defaultAgent;
private String agent;
private List skillDirectories;
+ private List instructionDirectories;
private List disabledSkills;
private InfiniteSessionConfig infiniteSessions;
private Consumer onEvent;
@@ -458,6 +460,40 @@ public ResumeSessionConfig setDisableResume(boolean disableResume) {
return this;
}
+ /**
+ * Returns whether to continue pending work on resume.
+ *
+ * When {@code true}, instructs the runtime to continue any tool calls or
+ * permission prompts that were still pending when the session was last
+ * suspended. When {@code false} (the default), the runtime treats pending work
+ * as interrupted on resume.
+ *
+ * @return {@code true} to continue pending work, {@code false} or {@code null}
+ * to treat as interrupted
+ * @since 1.4.0
+ */
+ public Boolean getContinuePendingWork() {
+ return continuePendingWork;
+ }
+
+ /**
+ * Sets whether to continue pending work on resume.
+ *
+ * When {@code true}, instructs the runtime to continue any tool calls or
+ * permission prompts that were still pending when the session was last
+ * suspended. When {@code false} (the default), the runtime treats pending work
+ * as interrupted on resume.
+ *
+ * @param continuePendingWork
+ * {@code true} to continue pending work
+ * @return this config for method chaining
+ * @since 1.4.0
+ */
+ public ResumeSessionConfig setContinuePendingWork(Boolean continuePendingWork) {
+ this.continuePendingWork = continuePendingWork;
+ return this;
+ }
+
/**
* Returns whether streaming is enabled.
*
@@ -591,6 +627,29 @@ public ResumeSessionConfig setSkillDirectories(List skillDirectories) {
return this;
}
+ /**
+ * Gets the additional directories to search for custom instruction files.
+ *
+ * @return the list of instruction directory paths
+ * @since 1.4.0
+ */
+ public List getInstructionDirectories() {
+ return instructionDirectories == null ? null : Collections.unmodifiableList(instructionDirectories);
+ }
+
+ /**
+ * Sets additional directories to search for custom instruction files.
+ *
+ * @param instructionDirectories
+ * the list of instruction directory paths
+ * @return this config for method chaining
+ * @since 1.4.0
+ */
+ public ResumeSessionConfig setInstructionDirectories(List instructionDirectories) {
+ this.instructionDirectories = instructionDirectories;
+ return this;
+ }
+
/**
* Gets the disabled skills.
*
@@ -767,6 +826,7 @@ public ResumeSessionConfig clone() {
copy.workingDirectory = this.workingDirectory;
copy.configDir = this.configDir;
copy.enableConfigDiscovery = this.enableConfigDiscovery;
+ copy.continuePendingWork = this.continuePendingWork;
copy.disableResume = this.disableResume;
copy.streaming = this.streaming;
copy.includeSubAgentStreamingEvents = this.includeSubAgentStreamingEvents;
@@ -775,6 +835,9 @@ public ResumeSessionConfig clone() {
copy.defaultAgent = this.defaultAgent;
copy.agent = this.agent;
copy.skillDirectories = this.skillDirectories != null ? new ArrayList<>(this.skillDirectories) : null;
+ copy.instructionDirectories = this.instructionDirectories != null
+ ? new ArrayList<>(this.instructionDirectories)
+ : null;
copy.disabledSkills = this.disabledSkills != null ? new ArrayList<>(this.disabledSkills) : null;
copy.infiniteSessions = this.infiniteSessions;
copy.onEvent = this.onEvent;
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 e36e90b67..460f4cd90 100644
--- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java
+++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java
@@ -74,6 +74,9 @@ public final class ResumeSessionRequest {
@JsonProperty("disableResume")
private Boolean disableResume;
+ @JsonProperty("continuePendingWork")
+ private Boolean continuePendingWork;
+
@JsonProperty("streaming")
private Boolean streaming;
@@ -98,6 +101,9 @@ public final class ResumeSessionRequest {
@JsonProperty("skillDirectories")
private List skillDirectories;
+ @JsonProperty("instructionDirectories")
+ private List instructionDirectories;
+
@JsonProperty("disabledSkills")
private List disabledSkills;
@@ -281,6 +287,16 @@ public void setDisableResume(Boolean disableResume) {
this.disableResume = disableResume;
}
+ /** Gets continue pending work flag. @return the flag */
+ public Boolean getContinuePendingWork() {
+ return continuePendingWork;
+ }
+
+ /** Sets continue pending work flag. @param continuePendingWork the flag */
+ public void setContinuePendingWork(Boolean continuePendingWork) {
+ this.continuePendingWork = continuePendingWork;
+ }
+
/** Gets streaming flag. @return the flag */
public Boolean getStreaming() {
return streaming;
@@ -366,6 +382,18 @@ public void setSkillDirectories(List skillDirectories) {
this.skillDirectories = skillDirectories;
}
+ /** Gets instruction directories. @return the instruction directory paths */
+ public List getInstructionDirectories() {
+ return instructionDirectories == null ? null : Collections.unmodifiableList(instructionDirectories);
+ }
+
+ /**
+ * Sets instruction directories. @param instructionDirectories the directories
+ */
+ public void setInstructionDirectories(List instructionDirectories) {
+ this.instructionDirectories = instructionDirectories;
+ }
+
/** Gets disabled skills. @return the disabled skill names */
public List getDisabledSkills() {
return disabledSkills == null ? null : Collections.unmodifiableList(disabledSkills);
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 09661346a..578bb92fd 100644
--- a/src/main/java/com/github/copilot/sdk/json/SessionConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/SessionConfig.java
@@ -57,6 +57,7 @@ public class SessionConfig {
private String agent;
private InfiniteSessionConfig infiniteSessions;
private List skillDirectories;
+ private List instructionDirectories;
private List disabledSkills;
private String configDir;
private Boolean enableConfigDiscovery;
@@ -550,6 +551,29 @@ public SessionConfig setSkillDirectories(List skillDirectories) {
return this;
}
+ /**
+ * Gets the additional directories to search for custom instruction files.
+ *
+ * @return the list of instruction directory paths
+ * @since 1.4.0
+ */
+ public List getInstructionDirectories() {
+ return instructionDirectories == null ? null : Collections.unmodifiableList(instructionDirectories);
+ }
+
+ /**
+ * Sets additional directories to search for custom instruction files.
+ *
+ * @param instructionDirectories
+ * the list of instruction directory paths
+ * @return this config instance for method chaining
+ * @since 1.4.0
+ */
+ public SessionConfig setInstructionDirectories(List instructionDirectories) {
+ this.instructionDirectories = instructionDirectories;
+ return this;
+ }
+
/**
* Gets the disabled skill names.
*
@@ -825,6 +849,9 @@ public SessionConfig clone() {
copy.agent = this.agent;
copy.infiniteSessions = this.infiniteSessions;
copy.skillDirectories = this.skillDirectories != null ? new ArrayList<>(this.skillDirectories) : null;
+ copy.instructionDirectories = this.instructionDirectories != null
+ ? new ArrayList<>(this.instructionDirectories)
+ : null;
copy.disabledSkills = this.disabledSkills != null ? new ArrayList<>(this.disabledSkills) : null;
copy.configDir = this.configDir;
copy.enableConfigDiscovery = this.enableConfigDiscovery;
diff --git a/src/site/markdown/advanced.md b/src/site/markdown/advanced.md
index a4c4d830a..bd2b5e9f3 100644
--- a/src/site/markdown/advanced.md
+++ b/src/site/markdown/advanced.md
@@ -25,6 +25,7 @@ This guide covers advanced scenarios for extending and customizing your Copilot
- [Skills Configuration](#Skills_Configuration)
- [Loading Skills](#Loading_Skills)
- [Disabling Skills](#Disabling_Skills)
+- [Instruction Directories](#Instruction_Directories)
- [Custom Configuration Directory](#Custom_Configuration_Directory)
- [Session Logging](#Session_Logging)
- [Early Event Registration](#Early_Event_Registration)
@@ -32,6 +33,8 @@ This guide covers advanced scenarios for extending and customizing your Copilot
- [Permission Handling](#Permission_Handling)
- [Session Hooks](#Session_Hooks)
- [Manual Server Control](#Manual_Server_Control)
+- [Copilot Home Directory](#Copilot_Home_Directory)
+- [TCP Connection Token](#TCP_Connection_Token)
- [Session Context and Filtering](#Session_Context_and_Filtering)
- [Listing Sessions with Context](#Listing_Sessions_with_Context)
- [Filtering Sessions by Context](#Filtering_Sessions_by_Context)
@@ -626,6 +629,28 @@ var session = client.createSession(
---
+## Instruction Directories
+
+Specify additional directories to search for custom instruction files. Instruction files (`.instructions.md`) placed in `.github/instructions/` subdirectories of the specified paths will be included in the session's system message:
+
+```java
+var session = client.createSession(
+ new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setInstructionDirectories(List.of("/path/to/extra/instructions"))
+).get();
+```
+
+This works with both `createSession` and `resumeSession`:
+
+```java
+var resumed = client.resumeSession(sessionId,
+ new ResumeSessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setInstructionDirectories(List.of("/path/to/instructions"))
+).get();
+```
+
+---
+
## Custom Configuration Directory
Use a custom configuration directory for session settings:
@@ -830,6 +855,33 @@ client.forceStop().get();
---
+## Copilot Home Directory
+
+Configure a custom base directory for Copilot data (session state, config, etc.). This sets the `COPILOT_HOME` environment variable on the spawned CLI process:
+
+```java
+var options = new CopilotClientOptions()
+ .setCopilotHome("/custom/data/dir");
+```
+
+When not set, the CLI defaults to `~/.copilot`. This option is only used when the SDK spawns the CLI process; it is ignored when connecting to an external server via `setCliUrl()`.
+
+---
+
+## TCP Connection Token
+
+When the SDK spawns the CLI in TCP mode, a connection token is generated automatically to secure the loopback listener. You can provide your own token:
+
+```java
+var options = new CopilotClientOptions()
+ .setUseStdio(false)
+ .setTcpConnectionToken("my-secret-token");
+```
+
+This cannot be combined with `setUseStdio(true)`. When omitted in TCP mode, the SDK auto-generates a UUID token.
+
+---
+
## Session Context and Filtering
Track and filter sessions by their working directory context including the current directory, git repository, and branch information.
diff --git a/src/site/markdown/documentation.md b/src/site/markdown/documentation.md
index 7b0c958e3..595d8815e 100644
--- a/src/site/markdown/documentation.md
+++ b/src/site/markdown/documentation.md
@@ -644,11 +644,13 @@ When resuming a session, you can optionally reconfigure many settings. This is u
| `customAgents` | Configure custom agents |
| `agent` | Pre-select a custom agent at session start |
| `skillDirectories` | Directories to load skills from |
+| `instructionDirectories` | Additional directories to search for custom instruction files |
| `disabledSkills` | Skills to disable |
| `infiniteSessions` | Configure infinite session behavior |
| `commands` | Slash command definitions for the resumed session |
| `onElicitationRequest` | Handler for incoming elicitation requests |
| `disableResume` | When `true`, resumes without emitting a `session.resume` event |
+| `continuePendingWork` | When `true`, continues pending tool calls/permissions from prior suspend |
| `onEvent` | Event handler registered before session resumption |
**Example: Changing Model on Resume**
diff --git a/src/test/java/com/github/copilot/sdk/CopilotClientTest.java b/src/test/java/com/github/copilot/sdk/CopilotClientTest.java
index 1cc067587..611799792 100644
--- a/src/test/java/com/github/copilot/sdk/CopilotClientTest.java
+++ b/src/test/java/com/github/copilot/sdk/CopilotClientTest.java
@@ -81,6 +81,22 @@ void testCliUrlMutualExclusionWithCliPath() {
assertThrows(IllegalArgumentException.class, () -> new CopilotClient(options));
}
+ @Test
+ void testTcpConnectionTokenWithStdioThrows() {
+ var options = new CopilotClientOptions().setTcpConnectionToken("my-token").setUseStdio(true);
+
+ assertThrows(IllegalArgumentException.class, () -> new CopilotClient(options));
+ }
+
+ @Test
+ void testTcpConnectionTokenWithTcpMode() {
+ var options = new CopilotClientOptions().setTcpConnectionToken("my-token").setUseStdio(false);
+
+ // Should NOT throw
+ var client = new CopilotClient(options);
+ client.close();
+ }
+
@Test
void testStartAndConnectUsingStdio() throws Exception {
assertNotNull(cliPath, "Copilot CLI not found in PATH or COPILOT_CLI_PATH");