diff --git a/.lastmerge b/.lastmerge index 2e1ed67d3..e8f667ef7 100644 --- a/.lastmerge +++ b/.lastmerge @@ -1 +1 @@ -5016587a62652f3d184b3c6958dfc63359921aa8 +c263dfc69055f9f28ee2d4b121cf617fca5a42dc diff --git a/CHANGELOG.md b/CHANGELOG.md index 649255c77..66d72554f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] -> **Upstream sync:** [`github/copilot-sdk@e40d57c`](https://github.com/github/copilot-sdk/commit/e40d57c86e18b495722adbf42045288c03924342) +> **Upstream sync:** [`github/copilot-sdk@c263dfc`](https://github.com/github/copilot-sdk/commit/c263dfc69055f9f28ee2d4b121cf617fca5a42dc) + +### Added + +- `SessionConfig.setClientName(String)` / `getClientName()` — identifies the application using the SDK; included in the User-Agent header for API requests (upstream: [`397ef66`](https://github.com/github/copilot-sdk/commit/397ef66)) +- `ResumeSessionConfig.setClientName(String)` / `getClientName()` — same for resumed sessions +- `PermissionHandler.APPROVE_ALL` — pre-built handler that approves all permission requests (upstream: [`3e2d2b2`](https://github.com/github/copilot-sdk/commit/3e2d2b2)) + +### Changed + +- **Breaking:** permissions are now denied by default when no `OnPermissionRequest` handler is provided. The `requestPermission` flag is always sent as `true` so the server calls back for every permission request; the SDK returns a deny result when no handler is registered (upstream: [`3e2d2b2`](https://github.com/github/copilot-sdk/commit/3e2d2b2)) ## [1.0.9] - 2026-02-16 diff --git a/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java b/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java index 61c06e7f0..90f3c71d8 100644 --- a/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java +++ b/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java @@ -31,25 +31,28 @@ private SessionRequestBuilder() { */ static CreateSessionRequest buildCreateRequest(SessionConfig config) { var request = new CreateSessionRequest(); + // Always request permission callbacks to enable deny-by-default behavior + request.setRequestPermission(true); + // Always send envValueMode=direct for MCP servers + request.setEnvValueMode("direct"); if (config == null) { return request; } request.setModel(config.getModel()); request.setSessionId(config.getSessionId()); + request.setClientName(config.getClientName()); request.setReasoningEffort(config.getReasoningEffort()); request.setTools(config.getTools()); request.setSystemMessage(config.getSystemMessage()); request.setAvailableTools(config.getAvailableTools()); request.setExcludedTools(config.getExcludedTools()); request.setProvider(config.getProvider()); - request.setRequestPermission(config.getOnPermissionRequest() != null ? true : null); request.setRequestUserInput(config.getOnUserInputRequest() != null ? true : null); request.setHooks(config.getHooks() != null && config.getHooks().hasHooks() ? true : null); request.setWorkingDirectory(config.getWorkingDirectory()); request.setStreaming(config.isStreaming() ? true : null); request.setMcpServers(config.getMcpServers()); - request.setEnvValueMode("direct"); request.setCustomAgents(config.getCustomAgents()); request.setInfiniteSessions(config.getInfiniteSessions()); request.setSkillDirectories(config.getSkillDirectories()); @@ -71,19 +74,23 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config) { static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionConfig config) { var request = new ResumeSessionRequest(); request.setSessionId(sessionId); + // Always request permission callbacks to enable deny-by-default behavior + request.setRequestPermission(true); + // Always send envValueMode=direct for MCP servers + request.setEnvValueMode("direct"); if (config == null) { return request; } request.setModel(config.getModel()); + request.setClientName(config.getClientName()); request.setReasoningEffort(config.getReasoningEffort()); request.setTools(config.getTools()); request.setSystemMessage(config.getSystemMessage()); request.setAvailableTools(config.getAvailableTools()); request.setExcludedTools(config.getExcludedTools()); request.setProvider(config.getProvider()); - request.setRequestPermission(config.getOnPermissionRequest() != null ? true : null); request.setRequestUserInput(config.getOnUserInputRequest() != null ? true : null); request.setHooks(config.getHooks() != null && config.getHooks().hasHooks() ? true : null); request.setWorkingDirectory(config.getWorkingDirectory()); @@ -91,7 +98,6 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setDisableResume(config.isDisableResume() ? true : null); request.setStreaming(config.isStreaming() ? true : null); request.setMcpServers(config.getMcpServers()); - request.setEnvValueMode("direct"); request.setCustomAgents(config.getCustomAgents()); request.setSkillDirectories(config.getSkillDirectories()); request.setDisabledSkills(config.getDisabledSkills()); 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 522aabd32..d73d82e6a 100644 --- a/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java +++ b/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java @@ -31,6 +31,9 @@ public final class CreateSessionRequest { @JsonProperty("sessionId") private String sessionId; + @JsonProperty("clientName") + private String clientName; + @JsonProperty("reasoningEffort") private String reasoningEffort; @@ -105,6 +108,16 @@ public void setSessionId(String sessionId) { this.sessionId = sessionId; } + /** Gets the client name. @return the client name */ + public String getClientName() { + return clientName; + } + + /** Sets the client name. @param clientName the client name */ + public void setClientName(String clientName) { + this.clientName = clientName; + } + /** Gets the reasoning effort. @return the reasoning effort level */ public String getReasoningEffort() { return reasoningEffort; diff --git a/src/main/java/com/github/copilot/sdk/json/PermissionHandler.java b/src/main/java/com/github/copilot/sdk/json/PermissionHandler.java index 259c058f3..e987e41ae 100644 --- a/src/main/java/com/github/copilot/sdk/json/PermissionHandler.java +++ b/src/main/java/com/github/copilot/sdk/json/PermissionHandler.java @@ -28,6 +28,10 @@ * }; * } * + *

+ * A pre-built handler that approves all requests is available as + * {@link #APPROVE_ALL}. + * * @see SessionConfig#setOnPermissionRequest(PermissionHandler) * @see PermissionRequest * @see PermissionRequestResult @@ -36,6 +40,14 @@ @FunctionalInterface public interface PermissionHandler { + /** + * A pre-built handler that approves all permission requests. + * + * @since 1.0.11 + */ + PermissionHandler APPROVE_ALL = (request, invocation) -> CompletableFuture + .completedFuture(new PermissionRequestResult().setKind("approved")); + /** * Handles a permission request from the assistant. *

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 fc790258a..0682699bc 100644 --- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java +++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java @@ -33,6 +33,7 @@ @JsonInclude(JsonInclude.Include.NON_NULL) public class ResumeSessionConfig { + private String clientName; private String model; private List tools; private SystemMessageConfig systemMessage; @@ -76,6 +77,29 @@ public ResumeSessionConfig setModel(String model) { return this; } + /** + * Gets the client name used to identify the application using the SDK. + * + * @return the client name, or {@code null} if not set + */ + public String getClientName() { + return clientName; + } + + /** + * Sets the client name to identify the application using the SDK. + *

+ * This value is included in the User-Agent header for API requests. + * + * @param clientName + * the client name + * @return this config for method chaining + */ + public ResumeSessionConfig setClientName(String clientName) { + this.clientName = clientName; + return this; + } + /** * Gets the custom tools for this session. * @@ -491,6 +515,7 @@ public ResumeSessionConfig setInfiniteSessions(InfiniteSessionConfig infiniteSes @Override public ResumeSessionConfig clone() { ResumeSessionConfig copy = new ResumeSessionConfig(); + copy.clientName = this.clientName; copy.model = this.model; copy.tools = this.tools != null ? new ArrayList<>(this.tools) : null; copy.systemMessage = this.systemMessage; 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 5d5228a16..4216e5eef 100644 --- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java +++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java @@ -29,6 +29,9 @@ public final class ResumeSessionRequest { @JsonProperty("sessionId") private String sessionId; + @JsonProperty("clientName") + private String clientName; + @JsonProperty("model") private String model; @@ -99,6 +102,16 @@ public void setSessionId(String sessionId) { this.sessionId = sessionId; } + /** Gets the client name. @return the client name */ + public String getClientName() { + return clientName; + } + + /** Sets the client name. @param clientName the client name */ + public void setClientName(String clientName) { + this.clientName = clientName; + } + /** Gets the model name. @return the model */ public String getModel() { return model; 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 064fee9f3..bfed0608e 100644 --- a/src/main/java/com/github/copilot/sdk/json/SessionConfig.java +++ b/src/main/java/com/github/copilot/sdk/json/SessionConfig.java @@ -34,6 +34,7 @@ public class SessionConfig { private String sessionId; + private String clientName; private String model; private String reasoningEffort; private List tools; @@ -76,6 +77,29 @@ public SessionConfig setSessionId(String sessionId) { return this; } + /** + * Gets the client name used to identify the application using the SDK. + * + * @return the client name, or {@code null} if not set + */ + public String getClientName() { + return clientName; + } + + /** + * Sets the client name to identify the application using the SDK. + *

+ * This value is included in the User-Agent header for API requests. + * + * @param clientName + * the client name + * @return this config instance for method chaining + */ + public SessionConfig setClientName(String clientName) { + this.clientName = clientName; + return this; + } + /** * Gets the AI model to use. * @@ -529,6 +553,7 @@ public SessionConfig setConfigDir(String configDir) { public SessionConfig clone() { SessionConfig copy = new SessionConfig(); copy.sessionId = this.sessionId; + copy.clientName = this.clientName; copy.model = this.model; copy.reasoningEffort = this.reasoningEffort; copy.tools = this.tools != null ? new ArrayList<>(this.tools) : null; diff --git a/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java b/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java index 3bd1b2344..1c5ca6e09 100644 --- a/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java +++ b/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java @@ -68,12 +68,14 @@ void copilotClientOptionsEnvironmentIndependence() { void sessionConfigCloneBasic() { SessionConfig original = new SessionConfig(); original.setSessionId("my-session"); + original.setClientName("my-app"); original.setModel("gpt-4o"); original.setStreaming(true); SessionConfig cloned = original.clone(); assertEquals(original.getSessionId(), cloned.getSessionId()); + assertEquals(original.getClientName(), cloned.getClientName()); assertEquals(original.getModel(), cloned.getModel()); assertEquals(original.isStreaming(), cloned.isStreaming()); } diff --git a/src/test/java/com/github/copilot/sdk/CopilotSessionTest.java b/src/test/java/com/github/copilot/sdk/CopilotSessionTest.java index d4357e111..6ac77fdb6 100644 --- a/src/test/java/com/github/copilot/sdk/CopilotSessionTest.java +++ b/src/test/java/com/github/copilot/sdk/CopilotSessionTest.java @@ -30,6 +30,7 @@ import com.github.copilot.sdk.events.ToolExecutionStartEvent; import com.github.copilot.sdk.events.UserMessageEvent; import com.github.copilot.sdk.json.MessageOptions; +import com.github.copilot.sdk.json.PermissionHandler; import com.github.copilot.sdk.json.SessionConfig; import com.github.copilot.sdk.json.SystemMessageConfig; import com.github.copilot.sdk.json.ToolDefinition; @@ -180,7 +181,8 @@ void testSendReturnsImmediatelyWhileEventsStreamInBackground() throws Exception ctx.configureForTest("session", "send_returns_immediately_while_events_stream_in_background"); try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client.createSession().get(); + CopilotSession session = client + .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); var events = new ArrayList(); var lastMessage = new AtomicReference(); diff --git a/src/test/java/com/github/copilot/sdk/HooksTest.java b/src/test/java/com/github/copilot/sdk/HooksTest.java index c728b9b99..05cd3292b 100644 --- a/src/test/java/com/github/copilot/sdk/HooksTest.java +++ b/src/test/java/com/github/copilot/sdk/HooksTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import com.github.copilot.sdk.json.MessageOptions; +import com.github.copilot.sdk.json.PermissionHandler; import com.github.copilot.sdk.json.PostToolUseHookInput; import com.github.copilot.sdk.json.PreToolUseHookInput; import com.github.copilot.sdk.json.PreToolUseHookOutput; @@ -67,11 +68,12 @@ void testInvokePreToolUseHookWhenModelRunsATool() throws Exception { var preToolUseInputs = new ArrayList(); final String[] sessionIdHolder = new String[1]; - var config = new SessionConfig().setHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> { - preToolUseInputs.add(input); - assertEquals(sessionIdHolder[0], invocation.getSessionId()); - return CompletableFuture.completedFuture(PreToolUseHookOutput.allow()); - })); + var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> { + preToolUseInputs.add(input); + assertEquals(sessionIdHolder[0], invocation.getSessionId()); + return CompletableFuture.completedFuture(PreToolUseHookOutput.allow()); + })); try (CopilotClient client = ctx.createClient()) { CopilotSession session = client.createSession(config).get(); @@ -106,11 +108,12 @@ void testInvokePostToolUseHookAfterModelRunsATool() throws Exception { var postToolUseInputs = new ArrayList(); final String[] sessionIdHolder = new String[1]; - var config = new SessionConfig().setHooks(new SessionHooks().setOnPostToolUse((input, invocation) -> { - postToolUseInputs.add(input); - assertEquals(sessionIdHolder[0], invocation.getSessionId()); - return CompletableFuture.completedFuture(null); - })); + var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setHooks(new SessionHooks().setOnPostToolUse((input, invocation) -> { + postToolUseInputs.add(input); + assertEquals(sessionIdHolder[0], invocation.getSessionId()); + return CompletableFuture.completedFuture(null); + })); try (CopilotClient client = ctx.createClient()) { CopilotSession session = client.createSession(config).get(); @@ -147,13 +150,14 @@ void testInvokeBothHooksForSingleToolCall() throws Exception { var preToolUseInputs = new ArrayList(); var postToolUseInputs = new ArrayList(); - var config = new SessionConfig().setHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> { - preToolUseInputs.add(input); - return CompletableFuture.completedFuture(PreToolUseHookOutput.allow()); - }).setOnPostToolUse((input, invocation) -> { - postToolUseInputs.add(input); - return CompletableFuture.completedFuture(null); - })); + var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> { + preToolUseInputs.add(input); + return CompletableFuture.completedFuture(PreToolUseHookOutput.allow()); + }).setOnPostToolUse((input, invocation) -> { + postToolUseInputs.add(input); + return CompletableFuture.completedFuture(null); + })); try (CopilotClient client = ctx.createClient()) { CopilotSession session = client.createSession(config).get(); @@ -192,11 +196,12 @@ void testDenyToolExecutionWhenPreToolUseReturnsDeny() throws Exception { var preToolUseInputs = new ArrayList(); - var config = new SessionConfig().setHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> { - preToolUseInputs.add(input); - // Deny all tool calls - return CompletableFuture.completedFuture(PreToolUseHookOutput.deny()); - })); + var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> { + preToolUseInputs.add(input); + // Deny all tool calls + return CompletableFuture.completedFuture(PreToolUseHookOutput.deny()); + })); try (CopilotClient client = ctx.createClient()) { CopilotSession session = client.createSession(config).get(); diff --git a/src/test/java/com/github/copilot/sdk/PermissionsTest.java b/src/test/java/com/github/copilot/sdk/PermissionsTest.java index d07b7c500..b2a1cfe66 100644 --- a/src/test/java/com/github/copilot/sdk/PermissionsTest.java +++ b/src/test/java/com/github/copilot/sdk/PermissionsTest.java @@ -18,6 +18,8 @@ import org.junit.jupiter.api.TestInfo; import com.github.copilot.sdk.events.AssistantMessageEvent; +import com.github.copilot.sdk.events.ToolExecutionCompleteEvent; +import com.github.copilot.sdk.json.PermissionHandler; import com.github.copilot.sdk.json.PermissionRequest; import com.github.copilot.sdk.json.PermissionRequestResult; import com.github.copilot.sdk.json.SessionConfig; @@ -286,4 +288,70 @@ void testShouldHandlePermissionHandlerErrorsGracefully(TestInfo testInfo) throws session.close(); } } + + /** + * Verifies that tool operations are denied by default when no handler is + * provided. + * + * @see Snapshot: + * permissions/should_deny_tool_operations_by_default_when_no_handler_is_provided + */ + @Test + void testShouldDenyToolOperationsByDefaultWhenNoHandlerIsProvided(TestInfo testInfo) throws Exception { + ctx.configureForTest("permissions", "should_deny_tool_operations_by_default_when_no_handler_is_provided"); + + try (CopilotClient client = ctx.createClient()) { + CopilotSession session = client.createSession().get(); + + final boolean[] permissionDenied = {false}; + session.on(ToolExecutionCompleteEvent.class, evt -> { + if (!evt.getData().success() && evt.getData().error() != null && evt.getData().error().message() != null + && evt.getData().error().message().contains("Permission denied")) { + permissionDenied[0] = true; + } + }); + + session.sendAndWait(new MessageOptions().setPrompt("Run 'node --version'")).get(60, TimeUnit.SECONDS); + + assertTrue(permissionDenied[0], "Expected a tool.execution_complete event with Permission denied result"); + + session.close(); + } + } + + /** + * Verifies that tool operations are denied by default when no handler is + * provided after resuming a session. + * + * @see Snapshot: + * permissions/should_deny_tool_operations_by_default_when_no_handler_is_provided_after_resume + */ + @Test + void testShouldDenyToolOperationsByDefaultWhenNoHandlerIsProvidedAfterResume(TestInfo testInfo) throws Exception { + ctx.configureForTest("permissions", + "should_deny_tool_operations_by_default_when_no_handler_is_provided_after_resume"); + + try (CopilotClient client = ctx.createClient()) { + var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL); + CopilotSession session1 = client.createSession(config).get(); + String sessionId = session1.getSessionId(); + session1.sendAndWait(new MessageOptions().setPrompt("What is 1+1?")).get(60, TimeUnit.SECONDS); + + CopilotSession session2 = client.resumeSession(sessionId).get(); + + final boolean[] permissionDenied = {false}; + session2.on(ToolExecutionCompleteEvent.class, evt -> { + if (!evt.getData().success() && evt.getData().error() != null && evt.getData().error().message() != null + && evt.getData().error().message().contains("Permission denied")) { + permissionDenied[0] = true; + } + }); + + session2.sendAndWait(new MessageOptions().setPrompt("Run 'node --version'")).get(60, TimeUnit.SECONDS); + + assertTrue(permissionDenied[0], "Expected a tool.execution_complete event with Permission denied result"); + + session2.close(); + } + } } diff --git a/src/test/java/com/github/copilot/sdk/SessionEventsE2ETest.java b/src/test/java/com/github/copilot/sdk/SessionEventsE2ETest.java index 5af1b445b..758d57c20 100644 --- a/src/test/java/com/github/copilot/sdk/SessionEventsE2ETest.java +++ b/src/test/java/com/github/copilot/sdk/SessionEventsE2ETest.java @@ -26,6 +26,8 @@ import com.github.copilot.sdk.events.ToolExecutionStartEvent; import com.github.copilot.sdk.events.UserMessageEvent; import com.github.copilot.sdk.json.MessageOptions; +import com.github.copilot.sdk.json.PermissionHandler; +import com.github.copilot.sdk.json.SessionConfig; /** * E2E tests for session events to verify event lifecycle. @@ -130,7 +132,8 @@ void testInvokesBuiltInTools_toolExecutionCompleteEvent() throws Exception { var toolCompletes = new ArrayList(); try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client.createSession().get(); + CopilotSession session = client + .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); session.on(ToolExecutionStartEvent.class, toolStarts::add); session.on(ToolExecutionCompleteEvent.class, toolCompletes::add); @@ -235,7 +238,8 @@ void testInvokesBuiltInTools_eventOrderDuringToolExecution() throws Exception { var eventTypes = new ArrayList(); try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client.createSession().get(); + CopilotSession session = client + .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); session.on(event -> eventTypes.add(event.getType())); diff --git a/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java b/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java index b1737de55..7e9d5ee69 100644 --- a/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java +++ b/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java @@ -37,6 +37,8 @@ void testBuildCreateRequestNullConfig() { CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(null); assertNotNull(request); assertNull(request.getModel()); + assertTrue(request.getRequestPermission(), "requestPermission should be true even for null config"); + assertEquals("direct", request.getEnvValueMode(), "envValueMode should be 'direct' even for null config"); } @Test @@ -65,6 +67,21 @@ void testBuildCreateRequestSetsEnvValueModeToDirect() { assertEquals("direct", request.getEnvValueMode()); } + @Test + void testBuildCreateRequestAlwaysSetsRequestPermissionTrue() { + // No permission handler set - requestPermission should still be true + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(new SessionConfig()); + assertTrue(request.getRequestPermission(), + "requestPermission should always be true to enable deny-by-default behavior"); + } + + @Test + void testBuildCreateRequestSetsClientName() { + var config = new SessionConfig().setClientName("my-app"); + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); + assertEquals("my-app", request.getClientName()); + } + // ========================================================================= // buildResumeRequest // ========================================================================= @@ -74,6 +91,8 @@ void testBuildResumeRequestNullConfig() { ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", null); assertEquals("sid-1", request.getSessionId()); assertNull(request.getModel()); + assertTrue(request.getRequestPermission(), "requestPermission should be true even for null config"); + assertEquals("direct", request.getEnvValueMode(), "envValueMode should be 'direct' even for null config"); } @Test @@ -142,6 +161,21 @@ void testBuildResumeRequestSetsEnvValueModeToDirect() { assertEquals("direct", request.getEnvValueMode()); } + @Test + void testBuildResumeRequestAlwaysSetsRequestPermissionTrue() { + // No permission handler set - requestPermission should still be true + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-9", new ResumeSessionConfig()); + assertTrue(request.getRequestPermission(), + "requestPermission should always be true to enable deny-by-default behavior"); + } + + @Test + void testBuildResumeRequestSetsClientName() { + var config = new ResumeSessionConfig().setClientName("my-app"); + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-10", config); + assertEquals("my-app", request.getClientName()); + } + // ========================================================================= // configureSession (ResumeSessionConfig overload) // ========================================================================= diff --git a/src/test/java/com/github/copilot/sdk/ToolsTest.java b/src/test/java/com/github/copilot/sdk/ToolsTest.java index bc392d9f0..a1c66dc49 100644 --- a/src/test/java/com/github/copilot/sdk/ToolsTest.java +++ b/src/test/java/com/github/copilot/sdk/ToolsTest.java @@ -21,6 +21,7 @@ import com.github.copilot.sdk.events.AssistantMessageEvent; import com.github.copilot.sdk.json.MessageOptions; +import com.github.copilot.sdk.json.PermissionHandler; import com.github.copilot.sdk.json.SessionConfig; import com.github.copilot.sdk.json.ToolDefinition; @@ -62,7 +63,8 @@ void testInvokesBuiltInTools(TestInfo testInfo) throws Exception { Files.writeString(readmeFile, "# ELIZA, the only chatbot you'll ever need"); try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client.createSession().get(); + CopilotSession session = client + .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); AssistantMessageEvent response = session .sendAndWait(