+ * Called internally when creating or resuming a session with an exit-plan-mode + * handler. + * + * @param handler + * the handler to invoke when an exit-plan-mode request is received + */ + void registerExitPlanModeHandler(ExitPlanModeHandler handler) { + exitPlanModeHandler.set(handler); + } + + /** + * Registers an auto-mode-switch handler for this session. + *
+ * Called internally when creating or resuming a session with an + * auto-mode-switch handler. + * + * @param handler + * the handler to invoke when an auto-mode-switch request is received + */ + void registerAutoModeSwitchHandler(AutoModeSwitchHandler handler) { + autoModeSwitchHandler.set(handler); + } + /** * Sets the capabilities reported by the host for this session. *
@@ -1356,6 +1392,60 @@ CompletableFuture
+ * Called internally when the server requests to exit plan mode.
+ *
+ * @param request
+ * the exit-plan-mode request
+ * @return a future that resolves with the user's decision
+ */
+ CompletableFuture
+ * Called internally when the server requests to switch to auto mode.
+ *
+ * @param request
+ * the auto-mode-switch request
+ * @return a future that resolves with the user's decision
+ */
+ CompletableFuture
@@ -1850,6 +1940,8 @@ public void close() {
permissionHandler.set(null);
userInputHandler.set(null);
elicitationHandler.set(null);
+ exitPlanModeHandler.set(null);
+ autoModeSwitchHandler.set(null);
hooksHandler.set(null);
}
diff --git a/src/main/java/com/github/copilot/sdk/RpcHandlerDispatcher.java b/src/main/java/com/github/copilot/sdk/RpcHandlerDispatcher.java
index d085f7fce..2906a928f 100644
--- a/src/main/java/com/github/copilot/sdk/RpcHandlerDispatcher.java
+++ b/src/main/java/com/github/copilot/sdk/RpcHandlerDispatcher.java
@@ -17,6 +17,8 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.copilot.sdk.generated.SessionEvent;
+import com.github.copilot.sdk.json.AutoModeSwitchRequest;
+import com.github.copilot.sdk.json.ExitPlanModeRequest;
import com.github.copilot.sdk.json.PermissionRequestResult;
import com.github.copilot.sdk.json.PermissionRequestResultKind;
import com.github.copilot.sdk.json.SessionLifecycleEvent;
@@ -79,6 +81,10 @@ void registerHandlers(JsonRpcClient rpc) {
(requestId, params) -> handlePermissionRequest(rpc, requestId, params));
rpc.registerMethodHandler("userInput.request",
(requestId, params) -> handleUserInputRequest(rpc, requestId, params));
+ rpc.registerMethodHandler("exitPlanMode.request",
+ (requestId, params) -> handleExitPlanModeRequest(rpc, requestId, params));
+ rpc.registerMethodHandler("autoModeSwitch.request",
+ (requestId, params) -> handleAutoModeSwitchRequest(rpc, requestId, params));
rpc.registerMethodHandler("hooks.invoke", (requestId, params) -> handleHooksInvoke(rpc, requestId, params));
rpc.registerMethodHandler("systemMessage.transform",
(requestId, params) -> handleSystemMessageTransform(rpc, requestId, params));
@@ -283,6 +289,96 @@ private void handleUserInputRequest(JsonRpcClient rpc, String requestId, JsonNod
});
}
+ private void handleExitPlanModeRequest(JsonRpcClient rpc, String requestId, JsonNode params) {
+ runAsync(() -> {
+ try {
+ String sessionId = params.get("sessionId").asText();
+ String summary = params.has("summary") ? params.get("summary").asText() : "";
+ String planContent = params.has("planContent") && !params.get("planContent").isNull()
+ ? params.get("planContent").asText()
+ : null;
+ String recommendedAction = params.has("recommendedAction")
+ ? params.get("recommendedAction").asText()
+ : "autopilot";
+
+ var actions = new ArrayList
+ * Implement this interface to handle requests from the agent to switch to an
+ * alternative model after a rate limit is encountered.
+ *
+ *
+ * This is sent when the agent encounters a rate limit and wants to switch to an
+ * alternative model automatically.
+ *
+ * @since 1.4.0
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AutoModeSwitchRequest {
+
+ @JsonProperty("errorCode")
+ private String errorCode;
+
+ @JsonProperty("retryAfterSeconds")
+ private Double retryAfterSeconds;
+
+ /**
+ * Gets the rate-limit error code that triggered the request.
+ *
+ * @return the error code, or {@code null}
+ */
+ public String getErrorCode() {
+ return errorCode;
+ }
+
+ /**
+ * Sets the rate-limit error code.
+ *
+ * @param errorCode
+ * the error code
+ * @return this instance for method chaining
+ */
+ public AutoModeSwitchRequest setErrorCode(String errorCode) {
+ this.errorCode = errorCode;
+ return this;
+ }
+
+ /**
+ * Gets the seconds until the rate limit resets, when known.
+ *
+ * @return the retry-after seconds, or {@code null}
+ */
+ public Double getRetryAfterSeconds() {
+ return retryAfterSeconds;
+ }
+
+ /**
+ * Sets the retry-after seconds.
+ *
+ * @param retryAfterSeconds
+ * the retry-after seconds
+ * @return this instance for method chaining
+ */
+ public AutoModeSwitchRequest setRetryAfterSeconds(Double retryAfterSeconds) {
+ this.retryAfterSeconds = retryAfterSeconds;
+ return this;
+ }
+}
diff --git a/src/main/java/com/github/copilot/sdk/json/AutoModeSwitchResponse.java b/src/main/java/com/github/copilot/sdk/json/AutoModeSwitchResponse.java
new file mode 100644
index 000000000..12c272426
--- /dev/null
+++ b/src/main/java/com/github/copilot/sdk/json/AutoModeSwitchResponse.java
@@ -0,0 +1,60 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk.json;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Response to an auto-mode-switch request.
+ *
+ * @since 1.4.0
+ */
+public enum AutoModeSwitchResponse {
+
+ /** Approve the switch for this rate-limit cycle. */
+ YES("yes"),
+
+ /** Approve and remember the choice for this session. */
+ YES_ALWAYS("yes_always"),
+
+ /** Decline the switch. */
+ NO("no");
+
+ private final String value;
+
+ AutoModeSwitchResponse(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Gets the JSON string value of this response.
+ *
+ * @return the JSON value
+ */
+ @JsonValue
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Returns the enum constant for the given JSON value.
+ *
+ * @param value
+ * the JSON string value
+ * @return the matching enum constant
+ * @throws IllegalArgumentException
+ * if the value does not match any constant
+ */
+ @JsonCreator
+ public static AutoModeSwitchResponse fromValue(String value) {
+ for (AutoModeSwitchResponse response : values()) {
+ if (response.value.equals(value)) {
+ return response;
+ }
+ }
+ throw new IllegalArgumentException("Unknown AutoModeSwitchResponse value: " + value);
+ }
+}
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 3a0b90f19..12bab4154 100644
--- a/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java
+++ b/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java
@@ -112,6 +112,12 @@ public final class CreateSessionRequest {
@JsonProperty("requestElicitation")
private Boolean requestElicitation;
+ @JsonProperty("requestExitPlanMode")
+ private Boolean requestExitPlanMode;
+
+ @JsonProperty("requestAutoModeSwitch")
+ private Boolean requestAutoModeSwitch;
+
@JsonProperty("modelCapabilities")
private ModelCapabilitiesOverride modelCapabilities;
@@ -419,6 +425,28 @@ public void setRequestElicitation(Boolean requestElicitation) {
this.requestElicitation = requestElicitation;
}
+ /** Gets the requestExitPlanMode flag. @return the flag */
+ public Boolean getRequestExitPlanMode() {
+ return requestExitPlanMode;
+ }
+
+ /** Sets the requestExitPlanMode flag. @param requestExitPlanMode the flag */
+ public void setRequestExitPlanMode(Boolean requestExitPlanMode) {
+ this.requestExitPlanMode = requestExitPlanMode;
+ }
+
+ /** Gets the requestAutoModeSwitch flag. @return the flag */
+ public Boolean getRequestAutoModeSwitch() {
+ return requestAutoModeSwitch;
+ }
+
+ /**
+ * Sets the requestAutoModeSwitch flag. @param requestAutoModeSwitch the flag
+ */
+ public void setRequestAutoModeSwitch(Boolean requestAutoModeSwitch) {
+ this.requestAutoModeSwitch = requestAutoModeSwitch;
+ }
+
/** Gets the model capabilities override. @return the override */
public ModelCapabilitiesOverride getModelCapabilities() {
return modelCapabilities;
diff --git a/src/main/java/com/github/copilot/sdk/json/ExitPlanModeHandler.java b/src/main/java/com/github/copilot/sdk/json/ExitPlanModeHandler.java
new file mode 100644
index 000000000..0af806104
--- /dev/null
+++ b/src/main/java/com/github/copilot/sdk/json/ExitPlanModeHandler.java
@@ -0,0 +1,43 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk.json;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Handler for exit-plan-mode requests from the agent.
+ *
+ * Implement this interface to handle requests from the agent to exit plan mode.
+ * The handler receives the plan summary and available actions, and returns the
+ * user's decision.
+ *
+ *
+ * This is sent when the agent requests to exit plan mode, providing a summary
+ * of the plan and available actions for the user to choose from.
+ *
+ * @since 1.4.0
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ExitPlanModeRequest {
+
+ @JsonProperty("summary")
+ private String summary = "";
+
+ @JsonProperty("planContent")
+ private String planContent;
+
+ @JsonProperty("actions")
+ private List
+ * When provided, the server will route {@code exitPlanMode.request} callbacks
+ * to this handler.
+ *
+ * @param onExitPlanMode
+ * the exit-plan-mode handler
+ * @return this config for method chaining
+ * @see ExitPlanModeHandler
+ * @since 1.4.0
+ */
+ public ResumeSessionConfig setOnExitPlanMode(ExitPlanModeHandler onExitPlanMode) {
+ this.onExitPlanMode = onExitPlanMode;
+ return this;
+ }
+
+ /**
+ * Gets the auto-mode-switch handler.
+ *
+ * @return the handler, or {@code null}
+ * @since 1.4.0
+ */
+ public AutoModeSwitchHandler getOnAutoModeSwitch() {
+ return onAutoModeSwitch;
+ }
+
+ /**
+ * Sets a handler for auto-mode-switch requests from the server.
+ *
+ * When provided, the server will route {@code autoModeSwitch.request} callbacks
+ * to this handler.
+ *
+ * @param onAutoModeSwitch
+ * the auto-mode-switch handler
+ * @return this config for method chaining
+ * @see AutoModeSwitchHandler
+ * @since 1.4.0
+ */
+ public ResumeSessionConfig setOnAutoModeSwitch(AutoModeSwitchHandler onAutoModeSwitch) {
+ this.onAutoModeSwitch = onAutoModeSwitch;
+ return this;
+ }
+
/**
* Gets the GitHub token for per-session authentication.
*
@@ -839,6 +895,8 @@ public ResumeSessionConfig clone() {
copy.onEvent = this.onEvent;
copy.commands = this.commands != null ? new ArrayList<>(this.commands) : null;
copy.onElicitationRequest = this.onElicitationRequest;
+ copy.onExitPlanMode = this.onExitPlanMode;
+ copy.onAutoModeSwitch = this.onAutoModeSwitch;
copy.gitHubToken = this.gitHubToken;
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 9b2c17f1a..a1af26970 100644
--- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java
+++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java
@@ -116,6 +116,12 @@ public final class ResumeSessionRequest {
@JsonProperty("requestElicitation")
private Boolean requestElicitation;
+ @JsonProperty("requestExitPlanMode")
+ private Boolean requestExitPlanMode;
+
+ @JsonProperty("requestAutoModeSwitch")
+ private Boolean requestAutoModeSwitch;
+
@JsonProperty("modelCapabilities")
private ModelCapabilitiesOverride modelCapabilities;
@@ -439,6 +445,28 @@ public void setRequestElicitation(Boolean requestElicitation) {
this.requestElicitation = requestElicitation;
}
+ /** Gets the requestExitPlanMode flag. @return the flag */
+ public Boolean getRequestExitPlanMode() {
+ return requestExitPlanMode;
+ }
+
+ /** Sets the requestExitPlanMode flag. @param requestExitPlanMode the flag */
+ public void setRequestExitPlanMode(Boolean requestExitPlanMode) {
+ this.requestExitPlanMode = requestExitPlanMode;
+ }
+
+ /** Gets the requestAutoModeSwitch flag. @return the flag */
+ public Boolean getRequestAutoModeSwitch() {
+ return requestAutoModeSwitch;
+ }
+
+ /**
+ * Sets the requestAutoModeSwitch flag. @param requestAutoModeSwitch the flag
+ */
+ public void setRequestAutoModeSwitch(Boolean requestAutoModeSwitch) {
+ this.requestAutoModeSwitch = requestAutoModeSwitch;
+ }
+
/** Gets the model capabilities override. @return the override */
public ModelCapabilitiesOverride getModelCapabilities() {
return modelCapabilities;
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 a4b2769b7..d28f2a79e 100644
--- a/src/main/java/com/github/copilot/sdk/json/SessionConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/SessionConfig.java
@@ -66,6 +66,8 @@ public class SessionConfig {
private Consumer
+ * When provided, the server will route {@code exitPlanMode.request} callbacks
+ * to this handler.
+ *
+ * @param onExitPlanMode
+ * the exit-plan-mode handler
+ * @return this config instance for method chaining
+ * @see ExitPlanModeHandler
+ * @since 1.4.0
+ */
+ public SessionConfig setOnExitPlanMode(ExitPlanModeHandler onExitPlanMode) {
+ this.onExitPlanMode = onExitPlanMode;
+ return this;
+ }
+
+ /**
+ * Gets the auto-mode-switch handler.
+ *
+ * @return the handler, or {@code null}
+ * @since 1.4.0
+ */
+ public AutoModeSwitchHandler getOnAutoModeSwitch() {
+ return onAutoModeSwitch;
+ }
+
+ /**
+ * Sets a handler for auto-mode-switch requests from the server.
+ *
+ * When provided, the server will route {@code autoModeSwitch.request} callbacks
+ * to this handler.
+ *
+ * @param onAutoModeSwitch
+ * the auto-mode-switch handler
+ * @return this config instance for method chaining
+ * @see AutoModeSwitchHandler
+ * @since 1.4.0
+ */
+ public SessionConfig setOnAutoModeSwitch(AutoModeSwitchHandler onAutoModeSwitch) {
+ this.onAutoModeSwitch = onAutoModeSwitch;
+ return this;
+ }
+
/**
* Gets the GitHub token for per-session authentication.
*
@@ -891,6 +947,8 @@ public SessionConfig clone() {
copy.onEvent = this.onEvent;
copy.commands = this.commands != null ? new ArrayList<>(this.commands) : null;
copy.onElicitationRequest = this.onElicitationRequest;
+ copy.onExitPlanMode = this.onExitPlanMode;
+ copy.onAutoModeSwitch = this.onAutoModeSwitch;
copy.gitHubToken = this.gitHubToken;
return copy;
}
diff --git a/src/site/markdown/advanced.md b/src/site/markdown/advanced.md
index ccf386640..e9a26579b 100644
--- a/src/site/markdown/advanced.md
+++ b/src/site/markdown/advanced.md
@@ -53,6 +53,9 @@ This guide covers advanced scenarios for extending and customizing your Copilot
- [Incoming Elicitation Handler](#Incoming_Elicitation_Handler)
- [Session Capabilities](#Session_Capabilities)
- [Outgoing Elicitation via session.getUi()](#Outgoing_Elicitation_via_session.getUi)
+- [Mode Handlers](#Mode_Handlers)
+ - [Exit Plan Mode Handler](#Exit_Plan_Mode_Handler)
+ - [Auto Mode Switch Handler](#Auto_Mode_Switch_Handler)
- [Getting Session Metadata by ID](#Getting_Session_Metadata_by_ID)
---
@@ -1267,6 +1270,59 @@ All `getUi()` methods throw `IllegalStateException` if the host does not support
---
+## Mode Handlers
+
+Mode handlers let the host respond to runtime requests for switching between agent modes (plan, interactive, autopilot).
+
+### Exit Plan Mode Handler
+
+Register an `ExitPlanModeHandler` to handle requests from the agent to exit plan mode. The handler receives a summary of the plan, available actions, and a recommended action. If no handler is registered, exit-plan-mode requests are auto-approved.
+
+```java
+var session = client.createSession(
+ new SessionConfig()
+ .setOnExitPlanMode((request, invocation) -> {
+ System.out.println("Plan summary: " + request.getSummary());
+ System.out.println("Available actions: " + request.getActions());
+ // Approve and select an action
+ return CompletableFuture.completedFuture(
+ new ExitPlanModeResult()
+ .setApproved(true)
+ .setSelectedAction("autopilot"));
+ })
+).get();
+```
+
+See [ExitPlanModeHandler](apidocs/com/github/copilot/sdk/json/ExitPlanModeHandler.html) Javadoc for more details.
+
+### Auto Mode Switch Handler
+
+Register an `AutoModeSwitchHandler` to handle requests to switch to an alternative model when a rate limit is encountered. If no handler is registered, auto-mode-switch requests are declined by default.
+
+```java
+var session = client.createSession(
+ new SessionConfig()
+ .setOnAutoModeSwitch((request, invocation) -> {
+ System.out.println("Rate limited: " + request.getErrorCode());
+ System.out.println("Retry after: " + request.getRetryAfterSeconds() + "s");
+ // Approve the switch for this rate-limit cycle
+ return CompletableFuture.completedFuture(AutoModeSwitchResponse.YES);
+ })
+).get();
+```
+
+Available responses:
+
+| Response | Description |
+|---|---|
+| `AutoModeSwitchResponse.YES` | Approve the switch for this rate-limit cycle |
+| `AutoModeSwitchResponse.YES_ALWAYS` | Approve and remember the choice for this session |
+| `AutoModeSwitchResponse.NO` | Decline the switch |
+
+See [AutoModeSwitchHandler](apidocs/com/github/copilot/sdk/json/AutoModeSwitchHandler.html) Javadoc for more details.
+
+---
+
## Getting Session Metadata by ID
Retrieve metadata for a specific session without listing all sessions:
diff --git a/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java b/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java
index f7ce3aa4d..e034e008e 100644
--- a/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java
+++ b/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java
@@ -26,6 +26,9 @@
import com.github.copilot.sdk.json.SystemMessageConfig;
import com.github.copilot.sdk.json.TelemetryConfig;
+import com.github.copilot.sdk.json.AutoModeSwitchResponse;
+import com.github.copilot.sdk.json.ExitPlanModeResult;
+
class ConfigCloneTest {
@Test
@@ -375,4 +378,32 @@ void copilotClientOptionsSessionIdleTimeoutCloned() {
assertEquals(600, cloned.getSessionIdleTimeoutSeconds());
}
+
+ @Test
+ void sessionConfigModeSwitchHandlersCloned() {
+ SessionConfig original = new SessionConfig();
+ original.setOnExitPlanMode(
+ (request, invocation) -> CompletableFuture.completedFuture(new ExitPlanModeResult()));
+ original.setOnAutoModeSwitch(
+ (request, invocation) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.NO));
+
+ SessionConfig cloned = original.clone();
+
+ assertSame(original.getOnExitPlanMode(), cloned.getOnExitPlanMode());
+ assertSame(original.getOnAutoModeSwitch(), cloned.getOnAutoModeSwitch());
+ }
+
+ @Test
+ void resumeSessionConfigModeSwitchHandlersCloned() {
+ ResumeSessionConfig original = new ResumeSessionConfig();
+ original.setOnExitPlanMode(
+ (request, invocation) -> CompletableFuture.completedFuture(new ExitPlanModeResult()));
+ original.setOnAutoModeSwitch(
+ (request, invocation) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.NO));
+
+ ResumeSessionConfig cloned = original.clone();
+
+ assertSame(original.getOnExitPlanMode(), cloned.getOnExitPlanMode());
+ assertSame(original.getOnAutoModeSwitch(), cloned.getOnAutoModeSwitch());
+ }
}
diff --git a/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java b/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java
index 0d13576eb..c4b8c4b37 100644
--- a/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java
+++ b/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java
@@ -12,11 +12,15 @@
import org.junit.jupiter.api.Test;
+import com.github.copilot.sdk.json.AutoModeSwitchHandler;
+import com.github.copilot.sdk.json.AutoModeSwitchResponse;
import com.github.copilot.sdk.json.CreateSessionRequest;
import com.github.copilot.sdk.json.DefaultAgentConfig;
import com.github.copilot.sdk.json.ElicitationHandler;
import com.github.copilot.sdk.json.ElicitationResult;
import com.github.copilot.sdk.json.ElicitationResultAction;
+import com.github.copilot.sdk.json.ExitPlanModeHandler;
+import com.github.copilot.sdk.json.ExitPlanModeResult;
import com.github.copilot.sdk.json.ResumeSessionConfig;
import com.github.copilot.sdk.json.ResumeSessionRequest;
import com.github.copilot.sdk.json.SessionConfig;
@@ -535,4 +539,136 @@ void testResumeRequestOmitsEnableSessionTelemetryWhenNull() throws Exception {
var json = mapper.writeValueAsString(request);
assertFalse(json.contains("enableSessionTelemetry"), "enableSessionTelemetry should be omitted when null");
}
+
+ @Test
+ void buildCreateRequest_setsRequestExitPlanModeWhenHandlerPresent() {
+ var config = new SessionConfig().setOnExitPlanMode(
+ (request, invocation) -> CompletableFuture.completedFuture(new ExitPlanModeResult()));
+
+ CreateSessionRequest result = SessionRequestBuilder.buildCreateRequest(config, "sess-epm");
+
+ assertEquals(true, result.getRequestExitPlanMode());
+ }
+
+ @Test
+ void buildCreateRequest_omitsRequestExitPlanModeWhenNoHandler() {
+ var config = new SessionConfig();
+
+ CreateSessionRequest result = SessionRequestBuilder.buildCreateRequest(config, "sess-epm-none");
+
+ assertNull(result.getRequestExitPlanMode());
+ }
+
+ @Test
+ void buildCreateRequest_setsRequestAutoModeSwitchWhenHandlerPresent() {
+ var config = new SessionConfig().setOnAutoModeSwitch(
+ (request, invocation) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.NO));
+
+ CreateSessionRequest result = SessionRequestBuilder.buildCreateRequest(config, "sess-ams");
+
+ assertEquals(true, result.getRequestAutoModeSwitch());
+ }
+
+ @Test
+ void buildCreateRequest_omitsRequestAutoModeSwitchWhenNoHandler() {
+ var config = new SessionConfig();
+
+ CreateSessionRequest result = SessionRequestBuilder.buildCreateRequest(config, "sess-ams-none");
+
+ assertNull(result.getRequestAutoModeSwitch());
+ }
+
+ @Test
+ void buildResumeRequest_setsRequestExitPlanModeWhenHandlerPresent() {
+ var config = new ResumeSessionConfig().setOnExitPlanMode(
+ (request, invocation) -> CompletableFuture.completedFuture(new ExitPlanModeResult()));
+
+ ResumeSessionRequest result = SessionRequestBuilder.buildResumeRequest("sess-epm", config);
+
+ assertEquals(true, result.getRequestExitPlanMode());
+ }
+
+ @Test
+ void buildResumeRequest_setsRequestAutoModeSwitchWhenHandlerPresent() {
+ var config = new ResumeSessionConfig().setOnAutoModeSwitch(
+ (request, invocation) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.YES));
+
+ ResumeSessionRequest result = SessionRequestBuilder.buildResumeRequest("sess-ams", config);
+
+ assertEquals(true, result.getRequestAutoModeSwitch());
+ }
+
+ @Test
+ void configureSessionWithExitPlanModeHandler_registersHandler() {
+ CopilotSession session = new CopilotSession("session-1", null);
+
+ ExitPlanModeHandler handler = (request, invocation) -> CompletableFuture
+ .completedFuture(new ExitPlanModeResult());
+ var config = new SessionConfig().setOnExitPlanMode(handler);
+
+ SessionRequestBuilder.configureSession(session, config);
+ }
+
+ @Test
+ void configureSessionWithAutoModeSwitchHandler_registersHandler() {
+ CopilotSession session = new CopilotSession("session-1", null);
+
+ AutoModeSwitchHandler handler = (request, invocation) -> CompletableFuture
+ .completedFuture(AutoModeSwitchResponse.NO);
+ var config = new SessionConfig().setOnAutoModeSwitch(handler);
+
+ SessionRequestBuilder.configureSession(session, config);
+ }
+
+ @Test
+ void configureResumedSessionWithExitPlanModeHandler_registersHandler() {
+ CopilotSession session = new CopilotSession("session-1", null);
+
+ ExitPlanModeHandler handler = (request, invocation) -> CompletableFuture
+ .completedFuture(new ExitPlanModeResult());
+ var config = new ResumeSessionConfig().setOnExitPlanMode(handler);
+
+ SessionRequestBuilder.configureSession(session, config);
+ }
+
+ @Test
+ void configureResumedSessionWithAutoModeSwitchHandler_registersHandler() {
+ CopilotSession session = new CopilotSession("session-1", null);
+
+ AutoModeSwitchHandler handler = (request, invocation) -> CompletableFuture
+ .completedFuture(AutoModeSwitchResponse.YES);
+ var config = new ResumeSessionConfig().setOnAutoModeSwitch(handler);
+
+ SessionRequestBuilder.configureSession(session, config);
+ }
+
+ @Test
+ void buildCreateRequest_serializesExitPlanModeAndAutoModeSwitchFlags() throws Exception {
+ var config = new SessionConfig()
+ .setOnExitPlanMode((request, invocation) -> CompletableFuture.completedFuture(new ExitPlanModeResult()))
+ .setOnAutoModeSwitch(
+ (request, invocation) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.NO));
+
+ CreateSessionRequest result = SessionRequestBuilder.buildCreateRequest(config, "sess-flags");
+
+ var mapper = JsonRpcClient.getObjectMapper();
+ var json = mapper.writeValueAsString(result);
+ assertTrue(json.contains("\"requestExitPlanMode\":true"));
+ assertTrue(json.contains("\"requestAutoModeSwitch\":true"));
+ }
+
+ @Test
+ void buildResumeRequest_serializesExitPlanModeAndAutoModeSwitchFlags() throws Exception {
+ var config = new ResumeSessionConfig()
+ .setOnExitPlanMode((request, invocation) -> CompletableFuture.completedFuture(new ExitPlanModeResult()))
+ .setOnAutoModeSwitch(
+ (request, invocation) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.YES));
+
+ ResumeSessionRequest result = SessionRequestBuilder.buildResumeRequest("sess-flags", config);
+
+ var mapper = JsonRpcClient.getObjectMapper();
+ var json = mapper.writeValueAsString(result);
+ assertTrue(json.contains("\"requestExitPlanMode\":true"));
+ assertTrue(json.contains("\"requestAutoModeSwitch\":true"));
+ }
}
Example Usage
+ *
+ * {@code
+ * AutoModeSwitchHandler handler = (request, invocation) -> {
+ * System.out.println("Rate limited: " + request.getErrorCode());
+ * return CompletableFuture.completedFuture(AutoModeSwitchResponse.YES);
+ * };
+ *
+ * var session = client.createSession(new SessionConfig().setOnAutoModeSwitch(handler)).get();
+ * }
+ *
+ * @since 1.4.0
+ */
+@FunctionalInterface
+public interface AutoModeSwitchHandler {
+
+ /**
+ * Handles an auto-mode-switch request from the agent.
+ *
+ * @param request
+ * the auto-mode-switch request containing the error code and retry
+ * information
+ * @param invocation
+ * context information about the invocation
+ * @return a future that resolves with the user's decision
+ */
+ CompletableFutureExample Usage
+ *
+ * {@code
+ * ExitPlanModeHandler handler = (request, invocation) -> {
+ * System.out.println("Plan summary: " + request.getSummary());
+ * return CompletableFuture
+ * .completedFuture(new ExitPlanModeResult().setApproved(true).setSelectedAction("autopilot"));
+ * };
+ *
+ * var session = client.createSession(new SessionConfig().setOnExitPlanMode(handler)).get();
+ * }
+ *
+ * @since 1.4.0
+ */
+@FunctionalInterface
+public interface ExitPlanModeHandler {
+
+ /**
+ * Handles an exit-plan-mode request from the agent.
+ *
+ * @param request
+ * the exit-plan-mode request containing the summary and actions
+ * @param invocation
+ * context information about the invocation
+ * @return a future that resolves with the user's decision
+ */
+ CompletableFuture