From 0c67dbbce59fde5462f2a17ebe719dd0c93a886f Mon Sep 17 00:00:00 2001 From: Chandramouleswaran Date: Thu, 12 Feb 2026 10:31:20 -0800 Subject: [PATCH] =?UTF-8?q?.NET:=20Update=20GitHub.Copilot.SDK=20to=200.1.?= =?UTF-8?q?23=20and=20copy=20new=20session=20config=20prope=E2=80=A6=20(#3?= =?UTF-8?q?788)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update GitHub.Copilot.SDK to 0.1.23 and copy new session config properties - Bump GitHub.Copilot.SDK from 0.1.18 to 0.1.23 - Add new SessionConfig properties: ReasoningEffort, Hooks, OnUserInputRequest, WorkingDirectory, ConfigDir, InfiniteSessions - Add missing ResumeSessionConfig properties: Model, SystemMessage, AvailableTools, ExcludedTools, ReasoningEffort, Hooks, OnUserInputRequest, WorkingDirectory, ConfigDir, InfiniteSessions - Fix UserMessageDataAttachmentsItem -> UserMessageDataAttachmentsItemFile for new polymorphic attachment API - Add unit tests for new session config properties * Address PR review: centralize config mapping and improve test coverage - Extract CopySessionConfig/CopyResumeSessionConfig as internal static helpers to eliminate duplicated mapping logic between RunCoreStreamingAsync and CreateResumeConfig (addresses reviewer comment on drift risk) - Add InternalsVisibleTo for unit test project - Replace shallow constructor tests with comprehensive property-verification tests that validate every config property is correctly copied, including OnUserInputRequest (addresses reviewer comments on test coverage) * Remove accidentally committed git-lfs hooks --- dotnet/Directory.Packages.props | 2 +- .../GitHubCopilotAgent.cs | 81 ++++++++---- .../Microsoft.Agents.AI.GitHub.Copilot.csproj | 4 + .../GitHubCopilotAgentTests.cs | 121 ++++++++++++++++++ 4 files changed, 183 insertions(+), 25 deletions(-) diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index d0227c1dbe..de0efb2eb5 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -89,7 +89,7 @@ - + diff --git a/dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/GitHubCopilotAgent.cs b/dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/GitHubCopilotAgent.cs index ed1f1a32ac..8319832613 100644 --- a/dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/GitHubCopilotAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/GitHubCopilotAgent.cs @@ -147,21 +147,7 @@ public sealed class GitHubCopilotAgent : AIAgent, IAsyncDisposable // Create or resume a session with streaming enabled SessionConfig sessionConfig = this._sessionConfig != null - ? new SessionConfig - { - Model = this._sessionConfig.Model, - Tools = this._sessionConfig.Tools, - SystemMessage = this._sessionConfig.SystemMessage, - AvailableTools = this._sessionConfig.AvailableTools, - ExcludedTools = this._sessionConfig.ExcludedTools, - Provider = this._sessionConfig.Provider, - OnPermissionRequest = this._sessionConfig.OnPermissionRequest, - McpServers = this._sessionConfig.McpServers, - CustomAgents = this._sessionConfig.CustomAgents, - SkillDirectories = this._sessionConfig.SkillDirectories, - DisabledSkills = this._sessionConfig.DisabledSkills, - Streaming = true - } + ? CopySessionConfig(this._sessionConfig) : new SessionConfig { Streaming = true }; CopilotSession copilotSession; @@ -283,16 +269,64 @@ public sealed class GitHubCopilotAgent : AIAgent, IAsyncDisposable } private ResumeSessionConfig CreateResumeConfig() + { + return CopyResumeSessionConfig(this._sessionConfig); + } + + /// + /// Copies all supported properties from a source into a new instance + /// with set to true. + /// + internal static SessionConfig CopySessionConfig(SessionConfig source) + { + return new SessionConfig + { + Model = source.Model, + ReasoningEffort = source.ReasoningEffort, + Tools = source.Tools, + SystemMessage = source.SystemMessage, + AvailableTools = source.AvailableTools, + ExcludedTools = source.ExcludedTools, + Provider = source.Provider, + OnPermissionRequest = source.OnPermissionRequest, + OnUserInputRequest = source.OnUserInputRequest, + Hooks = source.Hooks, + WorkingDirectory = source.WorkingDirectory, + ConfigDir = source.ConfigDir, + McpServers = source.McpServers, + CustomAgents = source.CustomAgents, + SkillDirectories = source.SkillDirectories, + DisabledSkills = source.DisabledSkills, + InfiniteSessions = source.InfiniteSessions, + Streaming = true + }; + } + + /// + /// Copies all supported properties from a source into a new + /// with set to true. + /// + internal static ResumeSessionConfig CopyResumeSessionConfig(SessionConfig? source) { return new ResumeSessionConfig { - Tools = this._sessionConfig?.Tools, - Provider = this._sessionConfig?.Provider, - OnPermissionRequest = this._sessionConfig?.OnPermissionRequest, - McpServers = this._sessionConfig?.McpServers, - CustomAgents = this._sessionConfig?.CustomAgents, - SkillDirectories = this._sessionConfig?.SkillDirectories, - DisabledSkills = this._sessionConfig?.DisabledSkills, + Model = source?.Model, + ReasoningEffort = source?.ReasoningEffort, + Tools = source?.Tools, + SystemMessage = source?.SystemMessage, + AvailableTools = source?.AvailableTools, + ExcludedTools = source?.ExcludedTools, + Provider = source?.Provider, + OnPermissionRequest = source?.OnPermissionRequest, + OnUserInputRequest = source?.OnUserInputRequest, + Hooks = source?.Hooks, + WorkingDirectory = source?.WorkingDirectory, + ConfigDir = source?.ConfigDir, + McpServers = source?.McpServers, + CustomAgents = source?.CustomAgents, + SkillDirectories = source?.SkillDirectories, + DisabledSkills = source?.DisabledSkills, + InfiniteSessions = source?.InfiniteSessions, Streaming = true }; } @@ -427,9 +461,8 @@ public sealed class GitHubCopilotAgent : AIAgent, IAsyncDisposable string tempFilePath = await dataContent.SaveToAsync(tempDir, cancellationToken).ConfigureAwait(false); attachments ??= []; - attachments.Add(new UserMessageDataAttachmentsItem + attachments.Add(new UserMessageDataAttachmentsItemFile { - Type = UserMessageDataAttachmentsItemType.File, Path = tempFilePath, DisplayName = Path.GetFileName(tempFilePath) }); diff --git a/dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/Microsoft.Agents.AI.GitHub.Copilot.csproj b/dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/Microsoft.Agents.AI.GitHub.Copilot.csproj index f4f79c27bd..4c436263c1 100644 --- a/dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/Microsoft.Agents.AI.GitHub.Copilot.csproj +++ b/dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/Microsoft.Agents.AI.GitHub.Copilot.csproj @@ -17,6 +17,10 @@ + + + + diff --git a/dotnet/tests/Microsoft.Agents.AI.GitHub.Copilot.UnitTests/GitHubCopilotAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.GitHub.Copilot.UnitTests/GitHubCopilotAgentTests.cs index e80990de1b..8a4d3c1068 100644 --- a/dotnet/tests/Microsoft.Agents.AI.GitHub.Copilot.UnitTests/GitHubCopilotAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.GitHub.Copilot.UnitTests/GitHubCopilotAgentTests.cs @@ -100,4 +100,125 @@ public sealed class GitHubCopilotAgentTests Assert.NotNull(agent); Assert.NotNull(agent.Id); } + + [Fact] + public void CopySessionConfig_CopiesAllProperties() + { + // Arrange + List tools = [AIFunctionFactory.Create(() => "test", "TestFunc", "Test function")]; + var hooks = new SessionHooks(); + var infiniteSessions = new InfiniteSessionConfig(); + var systemMessage = new SystemMessageConfig { Mode = SystemMessageMode.Append, Content = "Be helpful" }; + PermissionHandler permissionHandler = (_, _) => Task.FromResult(new PermissionRequestResult()); + UserInputHandler userInputHandler = (_, _) => Task.FromResult(new UserInputResponse { Answer = "input" }); + var mcpServers = new Dictionary { ["server1"] = new McpLocalServerConfig() }; + + var source = new SessionConfig + { + Model = "gpt-4o", + ReasoningEffort = "high", + Tools = tools, + SystemMessage = systemMessage, + AvailableTools = ["tool1", "tool2"], + ExcludedTools = ["tool3"], + WorkingDirectory = "/workspace", + ConfigDir = "/config", + Hooks = hooks, + InfiniteSessions = infiniteSessions, + OnPermissionRequest = permissionHandler, + OnUserInputRequest = userInputHandler, + McpServers = mcpServers, + DisabledSkills = ["skill1"], + }; + + // Act + SessionConfig result = GitHubCopilotAgent.CopySessionConfig(source); + + // Assert + Assert.Equal("gpt-4o", result.Model); + Assert.Equal("high", result.ReasoningEffort); + Assert.Same(tools, result.Tools); + Assert.Same(systemMessage, result.SystemMessage); + Assert.Equal(new List { "tool1", "tool2" }, result.AvailableTools); + Assert.Equal(new List { "tool3" }, result.ExcludedTools); + Assert.Equal("/workspace", result.WorkingDirectory); + Assert.Equal("/config", result.ConfigDir); + Assert.Same(hooks, result.Hooks); + Assert.Same(infiniteSessions, result.InfiniteSessions); + Assert.Same(permissionHandler, result.OnPermissionRequest); + Assert.Same(userInputHandler, result.OnUserInputRequest); + Assert.Same(mcpServers, result.McpServers); + Assert.Equal(new List { "skill1" }, result.DisabledSkills); + Assert.True(result.Streaming); + } + + [Fact] + public void CopyResumeSessionConfig_CopiesAllProperties() + { + // Arrange + List tools = [AIFunctionFactory.Create(() => "test", "TestFunc", "Test function")]; + var hooks = new SessionHooks(); + var infiniteSessions = new InfiniteSessionConfig(); + var systemMessage = new SystemMessageConfig { Mode = SystemMessageMode.Append, Content = "Be helpful" }; + PermissionHandler permissionHandler = (_, _) => Task.FromResult(new PermissionRequestResult()); + UserInputHandler userInputHandler = (_, _) => Task.FromResult(new UserInputResponse { Answer = "input" }); + var mcpServers = new Dictionary { ["server1"] = new McpLocalServerConfig() }; + + var source = new SessionConfig + { + Model = "gpt-4o", + ReasoningEffort = "high", + Tools = tools, + SystemMessage = systemMessage, + AvailableTools = ["tool1", "tool2"], + ExcludedTools = ["tool3"], + WorkingDirectory = "/workspace", + ConfigDir = "/config", + Hooks = hooks, + InfiniteSessions = infiniteSessions, + OnPermissionRequest = permissionHandler, + OnUserInputRequest = userInputHandler, + McpServers = mcpServers, + DisabledSkills = ["skill1"], + }; + + // Act + ResumeSessionConfig result = GitHubCopilotAgent.CopyResumeSessionConfig(source); + + // Assert + Assert.Equal("gpt-4o", result.Model); + Assert.Equal("high", result.ReasoningEffort); + Assert.Same(tools, result.Tools); + Assert.Same(systemMessage, result.SystemMessage); + Assert.Equal(new List { "tool1", "tool2" }, result.AvailableTools); + Assert.Equal(new List { "tool3" }, result.ExcludedTools); + Assert.Equal("/workspace", result.WorkingDirectory); + Assert.Equal("/config", result.ConfigDir); + Assert.Same(hooks, result.Hooks); + Assert.Same(infiniteSessions, result.InfiniteSessions); + Assert.Same(permissionHandler, result.OnPermissionRequest); + Assert.Same(userInputHandler, result.OnUserInputRequest); + Assert.Same(mcpServers, result.McpServers); + Assert.Equal(new List { "skill1" }, result.DisabledSkills); + Assert.True(result.Streaming); + } + + [Fact] + public void CopyResumeSessionConfig_WithNullSource_ReturnsDefaults() + { + // Act + ResumeSessionConfig result = GitHubCopilotAgent.CopyResumeSessionConfig(null); + + // Assert + Assert.Null(result.Model); + Assert.Null(result.ReasoningEffort); + Assert.Null(result.Tools); + Assert.Null(result.SystemMessage); + Assert.Null(result.OnPermissionRequest); + Assert.Null(result.OnUserInputRequest); + Assert.Null(result.Hooks); + Assert.Null(result.WorkingDirectory); + Assert.Null(result.ConfigDir); + Assert.True(result.Streaming); + } }