Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dotnet/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.InMemory" Version="1.67.0-preview" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="1.67.0-preview" />
<!-- Agent SDKs -->
<PackageVersion Include="GitHub.Copilot.SDK" Version="1.0.0-beta.2" />
<PackageVersion Include="GitHub.Copilot.SDK" Version="1.0.0" />
<PackageVersion Include="Microsoft.Agents.CopilotStudio.Client" Version="1.3.171-beta" />
<!-- M365 Agents SDK -->
<PackageVersion Include="AdaptiveCards" Version="3.1.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoWarn>$(NoWarn);GHCP001</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@

// This sample shows how to create a GitHub Copilot agent with shell command permissions.

using GitHub.Copilot.SDK;
using GitHub.Copilot;
using GitHub.Copilot.Rpc;
using Microsoft.Agents.AI;

// Permission handler that prompts the user for approval
static Task<PermissionRequestResult> PromptPermission(PermissionRequest request, PermissionInvocation invocation)
static Task<PermissionDecision> PromptPermission(PermissionRequest request, PermissionInvocation invocation)
{
Console.WriteLine($"\n[Permission Request: {request.Kind}]");
Console.Write("Approve? (y/n): ");

string? input = Console.ReadLine()?.Trim().ToUpperInvariant();
PermissionRequestResultKind kind = input is "Y" or "YES"
? PermissionRequestResultKind.Approved
: PermissionRequestResultKind.Rejected;
PermissionDecision decision = input is "Y" or "YES"
? PermissionDecision.ApproveOnce()
: PermissionDecision.Reject();

return Task.FromResult(new PermissionRequestResult { Kind = kind });
return Task.FromResult(decision);
}

// Create and start a Copilot client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ dotnet run
You can customize the agent by providing additional configuration:

```csharp
using GitHub.Copilot.SDK;
using GitHub.Copilot;
using Microsoft.Agents.AI;

// Create and start a Copilot client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using Microsoft.Extensions.AI;
using Microsoft.Shared.Diagnostics;

namespace GitHub.Copilot.SDK;
namespace GitHub.Copilot;

/// <summary>
/// Provides extension methods for <see cref="CopilotClient"/>
Expand Down
57 changes: 18 additions & 39 deletions dotnet/src/Microsoft.Agents.AI.GitHub.Copilot/GitHubCopilotAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using GitHub.Copilot.SDK;
using GitHub.Copilot;
using Microsoft.Extensions.AI;
using Microsoft.Shared.Diagnostics;

Expand Down Expand Up @@ -169,7 +169,7 @@ protected override async IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingA
Channel<AgentResponseUpdate> channel = Channel.CreateUnbounded<AgentResponseUpdate>();

// Subscribe to session events
using IDisposable subscription = copilotSession.On(evt =>
using IDisposable subscription = copilotSession.On<SessionEvent>(evt =>
{
switch (evt)
{
Expand Down Expand Up @@ -210,7 +210,7 @@ protected override async IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingA
string prompt = string.Join("\n", messages.Select(m => m.Text));

// Handle DataContent as attachments
(List<UserMessageAttachmentFile>? attachments, tempDir) = await ProcessDataContentAttachmentsAsync(
(List<AttachmentFile>? attachments, tempDir) = await ProcessDataContentAttachmentsAsync(
messages,
cancellationToken).ConfigureAwait(false);

Expand Down Expand Up @@ -262,10 +262,7 @@ public async ValueTask DisposeAsync()

private async Task EnsureClientStartedAsync(CancellationToken cancellationToken)
{
if (this._copilotClient.State != ConnectionState.Connected)
{
await this._copilotClient.StartAsync(cancellationToken).ConfigureAwait(false);
}
await this._copilotClient.StartAsync(cancellationToken).ConfigureAwait(false);
}

private ResumeSessionConfig CreateResumeConfig()
Expand All @@ -275,36 +272,18 @@ private ResumeSessionConfig CreateResumeConfig()

/// <summary>
/// Copies all supported properties from a source <see cref="SessionConfig"/> into a new instance
/// with <see cref="SessionConfig.Streaming"/> set to <c>true</c>.
/// with <see cref="SessionConfigBase.Streaming"/> set to <c>true</c>.
/// </summary>
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
};
SessionConfig copy = source.Clone();
copy.Streaming = true;
return copy;
}

/// <summary>
/// Copies all supported properties from a source <see cref="SessionConfig"/> into a new
/// <see cref="ResumeSessionConfig"/> with <see cref="ResumeSessionConfig.Streaming"/> set to <c>true</c>.
/// <see cref="ResumeSessionConfig"/> with <see cref="SessionConfigBase.Streaming"/> set to <c>true</c>.
/// </summary>
internal static ResumeSessionConfig CopyResumeSessionConfig(SessionConfig? source)
{
Expand All @@ -321,7 +300,7 @@ internal static ResumeSessionConfig CopyResumeSessionConfig(SessionConfig? sourc
OnUserInputRequest = source?.OnUserInputRequest,
Hooks = source?.Hooks,
WorkingDirectory = source?.WorkingDirectory,
ConfigDir = source?.ConfigDir,
ConfigDirectory = source?.ConfigDirectory,
McpServers = source?.McpServers,
CustomAgents = source?.CustomAgents,
SkillDirectories = source?.SkillDirectories,
Expand Down Expand Up @@ -394,10 +373,10 @@ private AgentResponseUpdate ConvertToAgentResponseUpdate(AssistantUsageEvent usa

AdditionalPropertiesDictionary<long>? additionalCounts = null;

if (usageEvent.Data.CacheWriteTokens is double cacheWriteTokens)
if (usageEvent.Data.CacheWriteTokens is long cacheWriteTokens)
{
additionalCounts ??= [];
additionalCounts[nameof(AssistantUsageData.CacheWriteTokens)] = (long)cacheWriteTokens;
additionalCounts[nameof(AssistantUsageData.CacheWriteTokens)] = cacheWriteTokens;
}

if (usageEvent.Data.Cost is double cost)
Expand All @@ -406,10 +385,10 @@ private AgentResponseUpdate ConvertToAgentResponseUpdate(AssistantUsageEvent usa
additionalCounts[nameof(AssistantUsageData.Cost)] = (long)cost;
}

if (usageEvent.Data.Duration is double duration)
if (usageEvent.Data.Duration is TimeSpan duration)
{
additionalCounts ??= [];
additionalCounts[nameof(AssistantUsageData.Duration)] = (long)duration;
additionalCounts[nameof(AssistantUsageData.Duration)] = (long)duration.TotalMilliseconds;
}

return additionalCounts;
Expand All @@ -432,7 +411,7 @@ private AgentResponseUpdate ConvertToAgentResponseUpdate(SessionEvent sessionEve

private static SessionConfig? GetSessionConfig(IList<AITool>? tools, string? instructions)
{
List<AIFunction>? mappedTools = tools is { Count: > 0 } ? tools.OfType<AIFunction>().ToList() : null;
List<AIFunctionDeclaration>? mappedTools = tools is { Count: > 0 } ? tools.OfType<AIFunctionDeclaration>().ToList() : null;
SystemMessageConfig? systemMessage = instructions is not null ? new SystemMessageConfig { Mode = SystemMessageMode.Append, Content = instructions } : null;

if (mappedTools is null && systemMessage is null)
Expand All @@ -443,11 +422,11 @@ private AgentResponseUpdate ConvertToAgentResponseUpdate(SessionEvent sessionEve
return new SessionConfig { Tools = mappedTools, SystemMessage = systemMessage };
}

private static async Task<(List<UserMessageAttachmentFile>? Attachments, string? TempDir)> ProcessDataContentAttachmentsAsync(
private static async Task<(List<AttachmentFile>? Attachments, string? TempDir)> ProcessDataContentAttachmentsAsync(
IEnumerable<ChatMessage> messages,
CancellationToken cancellationToken)
{
List<UserMessageAttachmentFile>? attachments = null;
List<AttachmentFile>? attachments = null;
string? tempDir = null;
foreach (ChatMessage message in messages)
{
Expand All @@ -461,7 +440,7 @@ private AgentResponseUpdate ConvertToAgentResponseUpdate(SessionEvent sessionEve
string tempFilePath = await dataContent.SaveToAsync(tempDir, cancellationToken).ConfigureAwait(false);

attachments ??= [];
attachments.Add(new UserMessageAttachmentFile
attachments.Add(new AttachmentFile
{
Path = tempFilePath,
DisplayName = Path.GetFileName(tempFilePath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<VersionSuffix>preview</VersionSuffix>
<!-- GitHub.Copilot.SDK only supports .NET 8.0+ -->
<TargetFrameworks>$(TargetFrameworksCore)</TargetFrameworks>
<NoWarn>$(NoWarn);GHCP001</NoWarn>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ private void AssertNoError(string line)
}
}

[RetryFact(2, 5000)]
[RetryFact(2, 5000, Skip = "KeyNotFoundException in workflow execution. See https://github.com/microsoft/agent-framework/issues/6404")]
public async Task WorkflowEventsSampleValidationAsync()
{
using CancellationTokenSource testTimeoutCts = this.CreateTestTimeoutCts(s_testTimeout);
Expand Down Expand Up @@ -278,7 +278,7 @@ await this.RunSampleTestAsync(samplePath, async (process, logs) =>
});
}

[RetryFact(2, 5000)]
[RetryFact(2, 5000, Skip = "KeyNotFoundException in workflow execution. See https://github.com/microsoft/agent-framework/issues/6404")]
public async Task WorkflowSharedStateSampleValidationAsync()
{
using CancellationTokenSource testTimeoutCts = this.CreateTestTimeoutCts(s_testTimeout);
Expand Down Expand Up @@ -376,7 +376,7 @@ await this.RunSampleTestAsync(samplePath, async (process, logs) =>
});
}

[RetryFact(2, 5000)]
[RetryFact(2, 5000, Skip = "KeyNotFoundException in workflow execution. See https://github.com/microsoft/agent-framework/issues/6404")]
public async Task SubWorkflowsSampleValidationAsync()
{
using CancellationTokenSource testTimeoutCts = this.CreateTestTimeoutCts(s_testTimeout);
Expand Down Expand Up @@ -452,7 +452,7 @@ await this.RunSampleTestAsync(samplePath, async (process, logs) =>
});
}

[RetryFact(2, 5000)]
[RetryFact(2, 5000, Skip = "KeyNotFoundException in workflow execution. See https://github.com/microsoft/agent-framework/issues/6404")]
public async Task WorkflowHITLSampleValidationAsync()
{
using CancellationTokenSource testTimeoutCts = this.CreateTestTimeoutCts(s_testTimeout);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GitHub.Copilot.SDK;
using GitHub.Copilot;
using GitHub.Copilot.Rpc;
using Microsoft.Extensions.AI;

namespace Microsoft.Agents.AI.GitHub.Copilot.IntegrationTests;
Expand All @@ -13,8 +14,8 @@ public class GitHubCopilotAgentTests
{
private const string SkipReason = "Integration tests require GitHub Copilot CLI installed. For local execution only.";

private static Task<PermissionRequestResult> OnPermissionRequestAsync(PermissionRequest request, PermissionInvocation invocation)
=> Task.FromResult(new PermissionRequestResult { Kind = PermissionRequestResultKind.Approved });
private static Task<PermissionDecision> OnPermissionRequestAsync(PermissionRequest request, PermissionInvocation invocation)
=> Task.FromResult(PermissionDecision.ApproveOnce());

[Fact(Skip = SkipReason)]
public async Task RunAsync_WithSimplePrompt_ReturnsResponseAsync()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<!-- GitHub.Copilot.SDK only supports .NET 8.0+ -->
<TargetFrameworks>$(TargetFrameworksCore)</TargetFrameworks>
<NoWarn>$(NoWarn);GHCP001</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

using System;
using System.Collections.Generic;
using GitHub.Copilot.SDK;
using GitHub.Copilot;
using Microsoft.Extensions.AI;

namespace Microsoft.Agents.AI.GitHub.Copilot.UnitTests;
Expand All @@ -16,7 +16,7 @@ public sealed class CopilotClientExtensionsTests
public void AsAIAgent_WithAllParameters_ReturnsGitHubCopilotAgentWithSpecifiedProperties()
{
// Arrange
CopilotClient copilotClient = new(new CopilotClientOptions { AutoStart = false });
CopilotClient copilotClient = new(new CopilotClientOptions());

const string TestId = "test-agent-id";
const string TestName = "Test Agent";
Expand All @@ -37,7 +37,7 @@ public void AsAIAgent_WithAllParameters_ReturnsGitHubCopilotAgentWithSpecifiedPr
public void AsAIAgent_WithMinimalParameters_ReturnsGitHubCopilotAgent()
{
// Arrange
CopilotClient copilotClient = new(new CopilotClientOptions { AutoStart = false });
CopilotClient copilotClient = new(new CopilotClientOptions());

// Act
var agent = copilotClient.AsAIAgent(ownsClient: false, tools: null);
Expand All @@ -61,7 +61,7 @@ public void AsAIAgent_WithNullClient_ThrowsArgumentNullException()
public void AsAIAgent_WithOwnsClient_ReturnsAgentThatOwnsClient()
{
// Arrange
CopilotClient copilotClient = new(new CopilotClientOptions { AutoStart = false });
CopilotClient copilotClient = new(new CopilotClientOptions());

// Act
var agent = copilotClient.AsAIAgent(ownsClient: true, tools: null);
Expand All @@ -75,7 +75,7 @@ public void AsAIAgent_WithOwnsClient_ReturnsAgentThatOwnsClient()
public void AsAIAgent_WithTools_ReturnsAgentWithTools()
{
// Arrange
CopilotClient copilotClient = new(new CopilotClientOptions { AutoStart = false });
CopilotClient copilotClient = new(new CopilotClientOptions());
List<AITool> tools = [AIFunctionFactory.Create(() => "test", "TestFunc", "Test function")];

// Act
Expand Down
Loading
Loading