diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index 0270f0e38b..1d3da75c86 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -32,18 +32,18 @@
-
+
-
+
-
-
+
+
-
-
+
+
@@ -64,7 +64,7 @@
-
+
@@ -77,11 +77,11 @@
-
+
-
+
@@ -102,8 +102,8 @@
-
-
+
+
diff --git a/dotnet/samples/04-hosting/A2A/A2AAgent_PollingForTaskCompletion/A2AAgent_PollingForTaskCompletion.csproj b/dotnet/samples/04-hosting/A2A/A2AAgent_PollingForTaskCompletion/A2AAgent_PollingForTaskCompletion.csproj
index 1bccc99d4f..d91b20e34b 100644
--- a/dotnet/samples/04-hosting/A2A/A2AAgent_PollingForTaskCompletion/A2AAgent_PollingForTaskCompletion.csproj
+++ b/dotnet/samples/04-hosting/A2A/A2AAgent_PollingForTaskCompletion/A2AAgent_PollingForTaskCompletion.csproj
@@ -2,7 +2,7 @@
Exe
- net10.0
+ net10.0
enable
enable
@@ -13,7 +13,6 @@
-
diff --git a/dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/Program.cs b/dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/Program.cs
index 3624acd981..2175e13e71 100644
--- a/dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/Program.cs
+++ b/dotnet/samples/05-end-to-end/A2AClientServer/A2AClient/Program.cs
@@ -62,12 +62,10 @@ private static async Task HandleCommandsAsync(CancellationToken cancellationToke
}
var agentResponse = await hostAgent.Agent!.RunAsync(message, session, cancellationToken: cancellationToken);
- foreach (var chatMessage in agentResponse.Messages)
- {
- Console.ForegroundColor = ConsoleColor.Cyan;
- Console.WriteLine($"\nAgent: {chatMessage.Text}");
- Console.ResetColor();
- }
+
+ Console.ForegroundColor = ConsoleColor.Cyan;
+ Console.WriteLine($"\nAgent: {agentResponse.Text}");
+ Console.ResetColor();
}
}
catch (Exception ex)
diff --git a/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/HostAgentFactory.cs b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/HostAgentFactory.cs
index d5f1c9a88d..28e0c5fe5e 100644
--- a/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/HostAgentFactory.cs
+++ b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/HostAgentFactory.cs
@@ -13,7 +13,7 @@ namespace A2AServer;
internal static class HostAgentFactory
{
- internal static async Task<(AIAgent, AgentCard)> CreateFoundryHostAgentAsync(string agentType, string model, string endpoint, string agentName, IList? tools = null)
+ internal static async Task<(AIAgent, AgentCard)> CreateFoundryHostAgentAsync(string agentType, string model, string endpoint, string agentName, string[] agentUrls, IList? tools = null)
{
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
@@ -25,16 +25,16 @@ internal static class HostAgentFactory
AgentCard agentCard = agentType.ToUpperInvariant() switch
{
- "INVOICE" => GetInvoiceAgentCard(),
- "POLICY" => GetPolicyAgentCard(),
- "LOGISTICS" => GetLogisticsAgentCard(),
+ "INVOICE" => GetInvoiceAgentCard(agentUrls),
+ "POLICY" => GetPolicyAgentCard(agentUrls),
+ "LOGISTICS" => GetLogisticsAgentCard(agentUrls),
_ => throw new ArgumentException($"Unsupported agent type: {agentType}"),
};
return new(agent, agentCard);
}
- internal static async Task<(AIAgent, AgentCard)> CreateChatCompletionHostAgentAsync(string agentType, string model, string apiKey, string name, string instructions, IList? tools = null)
+ internal static async Task<(AIAgent, AgentCard)> CreateChatCompletionHostAgentAsync(string agentType, string model, string apiKey, string name, string instructions, string[] agentUrls, IList? tools = null)
{
AIAgent agent = new OpenAIClient(apiKey)
.GetChatClient(model)
@@ -42,9 +42,9 @@ internal static class HostAgentFactory
AgentCard agentCard = agentType.ToUpperInvariant() switch
{
- "INVOICE" => GetInvoiceAgentCard(),
- "POLICY" => GetPolicyAgentCard(),
- "LOGISTICS" => GetLogisticsAgentCard(),
+ "INVOICE" => GetInvoiceAgentCard(agentUrls),
+ "POLICY" => GetPolicyAgentCard(agentUrls),
+ "LOGISTICS" => GetLogisticsAgentCard(agentUrls),
_ => throw new ArgumentException($"Unsupported agent type: {agentType}"),
};
@@ -52,7 +52,7 @@ internal static class HostAgentFactory
}
#region private
- private static AgentCard GetInvoiceAgentCard()
+ private static AgentCard GetInvoiceAgentCard(string[] agentUrls)
{
var capabilities = new AgentCapabilities()
{
@@ -81,10 +81,11 @@ private static AgentCard GetInvoiceAgentCard()
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [invoiceQuery],
+ SupportedInterfaces = CreateAgentInterfaces(agentUrls)
};
}
- private static AgentCard GetPolicyAgentCard()
+ private static AgentCard GetPolicyAgentCard(string[] agentUrls)
{
var capabilities = new AgentCapabilities()
{
@@ -113,10 +114,11 @@ private static AgentCard GetPolicyAgentCard()
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [policyQuery],
+ SupportedInterfaces = CreateAgentInterfaces(agentUrls)
};
}
- private static AgentCard GetLogisticsAgentCard()
+ private static AgentCard GetLogisticsAgentCard(string[] agentUrls)
{
var capabilities = new AgentCapabilities()
{
@@ -145,7 +147,18 @@ private static AgentCard GetLogisticsAgentCard()
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [logisticsQuery],
+ SupportedInterfaces = CreateAgentInterfaces(agentUrls)
};
}
+
+ private static List CreateAgentInterfaces(string[] agentUrls)
+ {
+ return agentUrls.Select(url => new AgentInterface
+ {
+ Url = url,
+ ProtocolBinding = "JSONRPC",
+ ProtocolVersion = "1.0",
+ }).ToList();
+ }
#endregion
}
diff --git a/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/Program.cs b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/Program.cs
index 8dcb3d1a34..773d57e9fc 100644
--- a/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/Program.cs
+++ b/dotnet/samples/05-end-to-end/A2AClientServer/A2AServer/Program.cs
@@ -38,14 +38,15 @@
string? apiKey = configuration["OPENAI_API_KEY"];
string model = configuration["OPENAI_CHAT_MODEL_NAME"] ?? "gpt-5.4-mini";
string? endpoint = configuration["AZURE_AI_PROJECT_ENDPOINT"];
+string[] agentUrls = (app.Configuration["urls"] ?? "http://localhost:5000").Split(';');
var invoiceQueryPlugin = new InvoiceQuery();
IList tools =
- [
+[
AIFunctionFactory.Create(invoiceQueryPlugin.QueryInvoices),
AIFunctionFactory.Create(invoiceQueryPlugin.QueryByTransactionId),
AIFunctionFactory.Create(invoiceQueryPlugin.QueryByInvoiceId)
- ];
+];
AIAgent hostA2AAgent;
AgentCard hostA2AAgentCard;
@@ -54,9 +55,9 @@
{
(hostA2AAgent, hostA2AAgentCard) = agentType.ToUpperInvariant() switch
{
- "INVOICE" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, model, endpoint, agentName, tools),
- "POLICY" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, model, endpoint, agentName),
- "LOGISTICS" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, model, endpoint, agentName),
+ "INVOICE" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, model, endpoint, agentName, agentUrls, tools),
+ "POLICY" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, model, endpoint, agentName, agentUrls),
+ "LOGISTICS" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, model, endpoint, agentName, agentUrls),
_ => throw new ArgumentException($"Unsupported agent type: {agentType}"),
};
}
@@ -68,7 +69,7 @@
agentType, model, apiKey, "InvoiceAgent",
"""
You specialize in handling queries related to invoices.
- """, tools),
+ """, agentUrls, tools),
"POLICY" => await HostAgentFactory.CreateChatCompletionHostAgentAsync(
agentType, model, apiKey, "PolicyAgent",
"""
@@ -84,7 +85,7 @@ You specialize in handling queries related to policies and customer communicatio
resolution in SAP CRM and notify the customer via email within 2 business days, referencing the
original invoice and the credit memo number. Use the 'Formal Credit Notification' email
template."
- """),
+ """, agentUrls),
"LOGISTICS" => await HostAgentFactory.CreateChatCompletionHostAgentAsync(
agentType, model, apiKey, "LogisticsAgent",
"""
@@ -95,7 +96,7 @@ You specialize in handling queries related to logistics.
Shipment number: SHPMT-SAP-001
Item: TSHIRT-RED-L
Quantity: 900
- """),
+ """, agentUrls),
_ => throw new ArgumentException($"Unsupported agent type: {agentType}"),
};
}
@@ -104,10 +105,9 @@ You specialize in handling queries related to logistics.
throw new ArgumentException("Either A2AServer:ApiKey or A2AServer:ConnectionString & agentName must be provided");
}
-var a2aTaskManager = app.MapA2A(
+app.MapA2A(
hostA2AAgent,
path: "/",
- agentCard: hostA2AAgentCard,
- taskManager => app.MapWellKnownAgentCard(taskManager, "/"));
+ agentCard: hostA2AAgentCard);
await app.RunAsync();
diff --git a/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs
index f790ec0daa..d2c67d0ca5 100644
--- a/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs
+++ b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/A2AAgentClient.cs
@@ -43,20 +43,21 @@ public override async IAsyncEnumerable RunStreamingAsync(
{
// Convert all messages to A2A parts and create a single message
var parts = messages.ToParts();
- var a2aMessage = new AgentMessage
+ var a2aMessage = new Message
{
MessageId = Guid.NewGuid().ToString("N"),
ContextId = contextId,
- Role = MessageRole.User,
+ Role = Role.User,
Parts = parts
};
- var messageSendParams = new MessageSendParams { Message = a2aMessage };
+ var messageSendParams = new SendMessageRequest { Message = a2aMessage };
var a2aResponse = await a2aClient.SendMessageAsync(messageSendParams, cancellationToken);
// Handle different response types
- if (a2aResponse is AgentMessage message)
+ if (a2aResponse.PayloadCase == SendMessageResponseCase.Message)
{
+ var message = a2aResponse.Message!;
var responseMessage = message.ToChatMessage();
if (responseMessage is { Contents.Count: > 0 })
{
@@ -67,9 +68,10 @@ public override async IAsyncEnumerable RunStreamingAsync(
});
}
}
- else if (a2aResponse is AgentTask agentTask)
+ else if (a2aResponse.PayloadCase == SendMessageResponseCase.Task)
{
// Manually convert AgentTask artifacts to ChatMessages since the extension method is internal
+ var agentTask = a2aResponse.Task!;
if (agentTask.Artifacts is not null)
{
foreach (var artifact in agentTask.Artifacts)
diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs
index 9d98857e9b..1e3ce3a273 100644
--- a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs
+++ b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Net.ServerSentEvents;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Threading;
@@ -100,64 +99,47 @@ protected override async Task RunCoreAsync(IEnumerable 0 } taskMessages)
- {
- response.Messages = taskMessages;
- }
+ UpdateSession(typedSession, agentTask.ContextId, agentTask.Id);
- return response;
+ return this.ConvertToAgentResponse(agentTask);
}
- throw new NotSupportedException($"Only Message and AgentTask responses are supported from A2A agents. Received: {a2aResponse.GetType().FullName ?? "null"}");
+ throw new NotSupportedException($"Only Message and AgentTask responses are supported from A2A agents. Received: {a2aResponse.PayloadCase}");
}
///
@@ -169,59 +151,61 @@ protected override async IAsyncEnumerable RunCoreStreamingA
this._logger.LogA2AAgentInvokingAgent(nameof(RunStreamingAsync), this.Id, this.Name);
- ConfiguredCancelableAsyncEnumerable> a2aSseEvents;
+ ConfiguredCancelableAsyncEnumerable streamEvents;
- if (options?.ContinuationToken is not null)
+ if (GetContinuationToken(messages, options) is { } token)
{
- // Task stream resumption is not well defined in the A2A v2.* specification, leaving it to the agent implementations.
- // The v3.0 specification improves this by defining task stream reconnection that allows obtaining the same stream
- // from the beginning, but it does not define stream resumption from a specific point in the stream.
- // Therefore, the code should be updated once the A2A .NET library supports the A2A v3.0 specification,
- // and AF has the necessary model to allow consumers to know whether they need to resume the stream and add new updates to
- // the existing ones or reconnect the stream and obtain all updates again.
- // For more details, see the following issue: https://github.com/microsoft/agent-framework/issues/1764
- throw new InvalidOperationException("Reconnecting to task streams using continuation tokens is not supported yet.");
- // a2aSseEvents = this._a2aClient.SubscribeToTaskAsync(token.TaskId, cancellationToken).ConfigureAwait(false);
+ streamEvents = this._a2aClient.SubscribeToTaskAsync(new SubscribeToTaskRequest { Id = token.TaskId }, cancellationToken).ConfigureAwait(false);
}
-
- MessageSendParams sendParams = new()
+ else
{
- Message = CreateA2AMessage(typedSession, messages),
- Metadata = options?.AdditionalProperties?.ToA2AMetadata()
- };
+ SendMessageRequest sendParams = new()
+ {
+ Message = CreateA2AMessage(typedSession, messages),
+ Metadata = options?.AdditionalProperties?.ToA2AMetadata()
+ };
- a2aSseEvents = this._a2aClient.SendMessageStreamingAsync(sendParams, cancellationToken).ConfigureAwait(false);
+ streamEvents = this._a2aClient.SendStreamingMessageAsync(sendParams, cancellationToken).ConfigureAwait(false);
+ }
this._logger.LogAgentChatClientInvokedAgent(nameof(RunStreamingAsync), this.Id, this.Name);
string? contextId = null;
string? taskId = null;
- await foreach (var sseEvent in a2aSseEvents)
+ await foreach (var streamResponse in streamEvents)
{
- if (sseEvent.Data is AgentMessage message)
- {
- contextId = message.ContextId;
-
- yield return this.ConvertToAgentResponseUpdate(message);
- }
- else if (sseEvent.Data is AgentTask task)
+ switch (streamResponse.PayloadCase)
{
- contextId = task.ContextId;
- taskId = task.Id;
-
- yield return this.ConvertToAgentResponseUpdate(task);
- }
- else if (sseEvent.Data is TaskUpdateEvent taskUpdateEvent)
- {
- contextId = taskUpdateEvent.ContextId;
- taskId = taskUpdateEvent.TaskId;
-
- yield return this.ConvertToAgentResponseUpdate(taskUpdateEvent);
- }
- else
- {
- throw new NotSupportedException($"Only message, task, task update events are supported from A2A agents. Received: {sseEvent.Data.GetType().FullName ?? "null"}");
+ case StreamResponseCase.Message:
+ var message = streamResponse.Message!;
+ contextId = message.ContextId;
+ yield return this.ConvertToAgentResponseUpdate(message);
+ break;
+
+ case StreamResponseCase.Task:
+ var task = streamResponse.Task!;
+ contextId = task.ContextId;
+ taskId = task.Id;
+ yield return this.ConvertToAgentResponseUpdate(task);
+ break;
+
+ case StreamResponseCase.StatusUpdate:
+ var statusUpdate = streamResponse.StatusUpdate!;
+ contextId = statusUpdate.ContextId;
+ taskId = statusUpdate.TaskId;
+ yield return this.ConvertToAgentResponseUpdate(statusUpdate);
+ break;
+
+ case StreamResponseCase.ArtifactUpdate:
+ var artifactUpdate = streamResponse.ArtifactUpdate!;
+ contextId = artifactUpdate.ContextId;
+ taskId = artifactUpdate.TaskId;
+ yield return this.ConvertToAgentResponseUpdate(artifactUpdate);
+ break;
+
+ default:
+ throw new NotSupportedException($"Only message, task, task update events are supported from A2A agents. Received: {streamResponse.PayloadCase}");
}
}
@@ -284,7 +268,7 @@ private static void UpdateSession(A2AAgentSession? session, string? contextId, s
session.TaskId = taskId;
}
- private static AgentMessage CreateA2AMessage(A2AAgentSession typedSession, IEnumerable messages)
+ private static Message CreateA2AMessage(A2AAgentSession typedSession, IEnumerable messages)
{
var a2aMessage = messages.ToA2AMessage();
@@ -324,7 +308,34 @@ private static AgentMessage CreateA2AMessage(A2AAgentSession typedSession, IEnum
return null;
}
- private AgentResponseUpdate ConvertToAgentResponseUpdate(AgentMessage message)
+ private AgentResponse ConvertToAgentResponse(Message message)
+ {
+ return new AgentResponse
+ {
+ AgentId = this.Id,
+ ResponseId = message.MessageId,
+ FinishReason = ChatFinishReason.Stop,
+ RawRepresentation = message,
+ Messages = [message.ToChatMessage()],
+ AdditionalProperties = message.Metadata?.ToAdditionalProperties(),
+ };
+ }
+
+ private AgentResponse ConvertToAgentResponse(AgentTask agentTask)
+ {
+ return new AgentResponse
+ {
+ AgentId = this.Id,
+ ResponseId = agentTask.Id,
+ FinishReason = MapTaskStateToFinishReason(agentTask.Status.State),
+ RawRepresentation = agentTask,
+ Messages = agentTask.ToChatMessages() ?? [],
+ ContinuationToken = CreateContinuationToken(agentTask.Id, agentTask.Status.State),
+ AdditionalProperties = agentTask.Metadata?.ToAdditionalProperties(),
+ };
+ }
+
+ private AgentResponseUpdate ConvertToAgentResponseUpdate(Message message)
{
return new AgentResponseUpdate
{
@@ -353,28 +364,30 @@ private AgentResponseUpdate ConvertToAgentResponseUpdate(AgentTask task)
};
}
- private AgentResponseUpdate ConvertToAgentResponseUpdate(TaskUpdateEvent taskUpdateEvent)
+ private AgentResponseUpdate ConvertToAgentResponseUpdate(TaskStatusUpdateEvent statusUpdateEvent)
{
- AgentResponseUpdate responseUpdate = new()
+ return new AgentResponseUpdate
{
AgentId = this.Id,
- ResponseId = taskUpdateEvent.TaskId,
- RawRepresentation = taskUpdateEvent,
+ ResponseId = statusUpdateEvent.TaskId,
+ RawRepresentation = statusUpdateEvent,
Role = ChatRole.Assistant,
- AdditionalProperties = taskUpdateEvent.Metadata?.ToAdditionalProperties() ?? [],
+ FinishReason = MapTaskStateToFinishReason(statusUpdateEvent.Status.State),
+ AdditionalProperties = statusUpdateEvent.Metadata?.ToAdditionalProperties() ?? [],
};
+ }
- if (taskUpdateEvent is TaskArtifactUpdateEvent artifactUpdateEvent)
- {
- responseUpdate.Contents = artifactUpdateEvent.Artifact.ToAIContents();
- responseUpdate.RawRepresentation = artifactUpdateEvent;
- }
- else if (taskUpdateEvent is TaskStatusUpdateEvent statusUpdateEvent)
+ private AgentResponseUpdate ConvertToAgentResponseUpdate(TaskArtifactUpdateEvent artifactUpdateEvent)
+ {
+ return new AgentResponseUpdate
{
- responseUpdate.FinishReason = MapTaskStateToFinishReason(statusUpdateEvent.Status.State);
- }
-
- return responseUpdate;
+ AgentId = this.Id,
+ ResponseId = artifactUpdateEvent.TaskId,
+ RawRepresentation = artifactUpdateEvent,
+ Role = ChatRole.Assistant,
+ Contents = artifactUpdateEvent.Artifact.ToAIContents(),
+ AdditionalProperties = artifactUpdateEvent.Metadata?.ToAdditionalProperties() ?? [],
+ };
}
private static ChatFinishReason? MapTaskStateToFinishReason(TaskState state)
diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AAgentCardExtensions.cs b/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AAgentCardExtensions.cs
index 1998d020b5..897349f666 100644
--- a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AAgentCardExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/A2AAgentCardExtensions.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
+using System.Linq;
using System.Net.Http;
using Microsoft.Agents.AI;
using Microsoft.Extensions.Logging;
@@ -29,8 +30,12 @@ public static class A2AAgentCardExtensions
/// An instance backed by the A2A agent.
public static AIAgent AsAIAgent(this AgentCard card, HttpClient? httpClient = null, ILoggerFactory? loggerFactory = null)
{
+ // TODO: Refactor to support interface selection from card.SupportedInterfaces.
+ var url = card.SupportedInterfaces?.FirstOrDefault()?.Url
+ ?? throw new InvalidOperationException("The AgentCard does not have any SupportedInterfaces with a URL.");
+
// Create the A2A client using the agent URL from the card.
- var a2aClient = new A2AClient(new Uri(card.Url), httpClient);
+ var a2aClient = new A2AClient(new Uri(url), httpClient);
return a2aClient.AsAIAgent(name: card.Name, description: card.Description, loggerFactory: loggerFactory);
}
diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/ChatMessageExtensions.cs b/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/ChatMessageExtensions.cs
index b1f1bd643a..dd0749ecc9 100644
--- a/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/ChatMessageExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.A2A/Extensions/ChatMessageExtensions.cs
@@ -11,7 +11,7 @@ namespace Microsoft.Extensions.AI;
///
internal static class ChatMessageExtensions
{
- internal static AgentMessage ToA2AMessage(this IEnumerable messages)
+ internal static Message ToA2AMessage(this IEnumerable messages)
{
List allParts = [];
@@ -23,10 +23,10 @@ internal static AgentMessage ToA2AMessage(this IEnumerable messages
}
}
- return new AgentMessage
+ return new Message
{
MessageId = Guid.NewGuid().ToString("N"),
- Role = MessageRole.User,
+ Role = Role.User,
Parts = allParts,
};
}
diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/Microsoft.Agents.AI.A2A.csproj b/dotnet/src/Microsoft.Agents.AI.A2A/Microsoft.Agents.AI.A2A.csproj
index b1b9ba7671..4e92826f56 100644
--- a/dotnet/src/Microsoft.Agents.AI.A2A/Microsoft.Agents.AI.A2A.csproj
+++ b/dotnet/src/Microsoft.Agents.AI.A2A/Microsoft.Agents.AI.A2A.csproj
@@ -1,6 +1,7 @@
+ $(TargetFrameworksCore)
preview
$(NoWarn);MEAI001
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
index 514922dd26..f6a4722699 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
@@ -6,9 +6,7 @@
using System.Linq;
using System.Net;
using System.Net.Http;
-using System.Net.ServerSentEvents;
using System.Text;
-using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
@@ -89,14 +87,17 @@ public async Task RunAsync_AllowsNonUserRoleMessagesAsync()
public async Task RunAsync_WithValidUserMessage_RunsSuccessfullyAsync()
{
// Arrange
- this._handler.ResponseToReturn = new AgentMessage
+ this._handler.ResponseToReturn = new SendMessageResponse
{
- MessageId = "response-123",
- Role = MessageRole.Agent,
- Parts =
- [
- new TextPart { Text = "Hello! How can I help you today?" }
- ]
+ Message = new Message
+ {
+ MessageId = "response-123",
+ Role = Role.Agent,
+ Parts =
+ [
+ new Part { Text = "Hello! How can I help you today?" }
+ ]
+ }
};
var inputMessages = new List
@@ -108,11 +109,11 @@ public async Task RunAsync_WithValidUserMessage_RunsSuccessfullyAsync()
var result = await this._agent.RunAsync(inputMessages);
// Assert input message sent to A2AClient
- var inputMessage = this._handler.CapturedMessageSendParams?.Message;
+ var inputMessage = this._handler.CapturedSendMessageRequest?.Message;
Assert.NotNull(inputMessage);
Assert.Single(inputMessage.Parts);
- Assert.Equal(MessageRole.User, inputMessage.Role);
- Assert.Equal("Hello, world!", ((TextPart)inputMessage.Parts[0]).Text);
+ Assert.Equal(Role.User, inputMessage.Role);
+ Assert.Equal("Hello, world!", inputMessage.Parts[0].Text);
// Assert response from A2AClient is converted correctly
Assert.NotNull(result);
@@ -120,8 +121,8 @@ public async Task RunAsync_WithValidUserMessage_RunsSuccessfullyAsync()
Assert.Equal("response-123", result.ResponseId);
Assert.NotNull(result.RawRepresentation);
- Assert.IsType(result.RawRepresentation);
- Assert.Equal("response-123", ((AgentMessage)result.RawRepresentation).MessageId);
+ Assert.IsType(result.RawRepresentation);
+ Assert.Equal("response-123", ((Message)result.RawRepresentation).MessageId);
Assert.Single(result.Messages);
Assert.Equal(ChatRole.Assistant, result.Messages[0].Role);
@@ -133,15 +134,18 @@ public async Task RunAsync_WithValidUserMessage_RunsSuccessfullyAsync()
public async Task RunAsync_WithNewSession_UpdatesSessionConversationIdAsync()
{
// Arrange
- this._handler.ResponseToReturn = new AgentMessage
+ this._handler.ResponseToReturn = new SendMessageResponse
{
- MessageId = "response-123",
- Role = MessageRole.Agent,
- Parts =
- [
- new TextPart { Text = "Response" }
- ],
- ContextId = "new-context-id"
+ Message = new Message
+ {
+ MessageId = "response-123",
+ Role = Role.Agent,
+ Parts =
+ [
+ new Part { Text = "Response" }
+ ],
+ ContextId = "new-context-id"
+ }
};
var inputMessages = new List
@@ -177,7 +181,7 @@ public async Task RunAsync_WithExistingSession_SetConversationIdToMessageAsync()
await this._agent.RunAsync(inputMessages, session);
// Assert
- var message = this._handler.CapturedMessageSendParams?.Message;
+ var message = this._handler.CapturedSendMessageRequest?.Message;
Assert.NotNull(message);
Assert.Equal("existing-context-id", message.ContextId);
}
@@ -191,15 +195,18 @@ public async Task RunAsync_WithSessionHavingDifferentContextId_ThrowsInvalidOper
new(ChatRole.User, "Test message")
};
- this._handler.ResponseToReturn = new AgentMessage
+ this._handler.ResponseToReturn = new SendMessageResponse
{
- MessageId = "response-123",
- Role = MessageRole.Agent,
- Parts =
- [
- new TextPart { Text = "Response" }
- ],
- ContextId = "different-context"
+ Message = new Message
+ {
+ MessageId = "response-123",
+ Role = Role.Agent,
+ Parts =
+ [
+ new Part { Text = "Response" }
+ ],
+ ContextId = "different-context"
+ }
};
var session = await this._agent.CreateSessionAsync();
@@ -219,12 +226,15 @@ public async Task RunStreamingAsync_WithValidUserMessage_YieldsAgentResponseUpda
new(ChatRole.User, "Hello, streaming!")
};
- this._handler.StreamingResponseToReturn = new AgentMessage()
+ this._handler.StreamingResponseToReturn = new StreamResponse
{
- MessageId = "stream-1",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Hello" }],
- ContextId = "stream-context"
+ Message = new Message
+ {
+ MessageId = "stream-1",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Hello" }],
+ ContextId = "stream-context"
+ }
};
// Act
@@ -238,11 +248,11 @@ public async Task RunStreamingAsync_WithValidUserMessage_YieldsAgentResponseUpda
Assert.Single(updates);
// Assert input message sent to A2AClient
- var inputMessage = this._handler.CapturedMessageSendParams?.Message;
+ var inputMessage = this._handler.CapturedSendMessageRequest?.Message;
Assert.NotNull(inputMessage);
Assert.Single(inputMessage.Parts);
- Assert.Equal(MessageRole.User, inputMessage.Role);
- Assert.Equal("Hello, streaming!", ((TextPart)inputMessage.Parts[0]).Text);
+ Assert.Equal(Role.User, inputMessage.Role);
+ Assert.Equal("Hello, streaming!", inputMessage.Parts[0].Text);
// Assert response from A2AClient is converted correctly
Assert.Equal(ChatRole.Assistant, updates[0].Role);
@@ -251,8 +261,8 @@ public async Task RunStreamingAsync_WithValidUserMessage_YieldsAgentResponseUpda
Assert.Equal(this._agent.Id, updates[0].AgentId);
Assert.Equal("stream-1", updates[0].ResponseId);
Assert.Equal(ChatFinishReason.Stop, updates[0].FinishReason);
- Assert.IsType(updates[0].RawRepresentation);
- Assert.Equal("stream-1", ((AgentMessage)updates[0].RawRepresentation!).MessageId);
+ Assert.IsType(updates[0].RawRepresentation);
+ Assert.Equal("stream-1", ((Message)updates[0].RawRepresentation!).MessageId);
}
[Fact]
@@ -264,12 +274,15 @@ public async Task RunStreamingAsync_WithSession_UpdatesSessionConversationIdAsyn
new(ChatRole.User, "Test streaming")
};
- this._handler.StreamingResponseToReturn = new AgentMessage()
+ this._handler.StreamingResponseToReturn = new StreamResponse
{
- MessageId = "stream-1",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Response" }],
- ContextId = "new-stream-context"
+ Message = new Message
+ {
+ MessageId = "stream-1",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Response" }],
+ ContextId = "new-stream-context"
+ }
};
var session = await this._agent.CreateSessionAsync();
@@ -294,7 +307,7 @@ public async Task RunStreamingAsync_WithExistingSession_SetConversationIdToMessa
new(ChatRole.User, "Test streaming")
};
- this._handler.StreamingResponseToReturn = new AgentMessage();
+ this._handler.StreamingResponseToReturn = new StreamResponse { Message = new Message() };
var session = await this._agent.CreateSessionAsync();
var a2aSession = (A2AAgentSession)session;
@@ -307,7 +320,7 @@ public async Task RunStreamingAsync_WithExistingSession_SetConversationIdToMessa
}
// Assert
- var message = this._handler.CapturedMessageSendParams?.Message;
+ var message = this._handler.CapturedSendMessageRequest?.Message;
Assert.NotNull(message);
Assert.Equal("existing-context-id", message.ContextId);
}
@@ -325,12 +338,15 @@ public async Task RunStreamingAsync_WithSessionHavingDifferentContextId_ThrowsIn
new(ChatRole.User, "Test streaming")
};
- this._handler.StreamingResponseToReturn = new AgentMessage()
+ this._handler.StreamingResponseToReturn = new StreamResponse
{
- MessageId = "stream-1",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Response" }],
- ContextId = "different-context"
+ Message = new Message
+ {
+ MessageId = "stream-1",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Response" }],
+ ContextId = "different-context"
+ }
};
// Act
@@ -346,12 +362,15 @@ await Assert.ThrowsAsync(async () =>
public async Task RunStreamingAsync_AllowsNonUserRoleMessagesAsync()
{
// Arrange
- this._handler.StreamingResponseToReturn = new AgentMessage()
+ this._handler.StreamingResponseToReturn = new StreamResponse
{
- MessageId = "stream-1",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Response" }],
- ContextId = "new-stream-context"
+ Message = new Message
+ {
+ MessageId = "stream-1",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Response" }],
+ ContextId = "new-stream-context"
+ }
};
var inputMessages = new List
@@ -385,13 +404,13 @@ public async Task RunAsync_WithHostedFileContent_ConvertsToFilePartAsync()
await this._agent.RunAsync(inputMessages);
// Assert
- var message = this._handler.CapturedMessageSendParams?.Message;
+ var message = this._handler.CapturedSendMessageRequest?.Message;
Assert.NotNull(message);
Assert.Equal(2, message.Parts.Count);
- Assert.IsType(message.Parts[0]);
- Assert.Equal("Check this file:", ((TextPart)message.Parts[0]).Text);
- Assert.IsType(message.Parts[1]);
- Assert.Equal("https://example.com/file.pdf", ((FilePart)message.Parts[1]).File.Uri?.ToString());
+ Assert.Equal(PartContentCase.Text, message.Parts[0].ContentCase);
+ Assert.Equal("Check this file:", message.Parts[0].Text);
+ Assert.Equal(PartContentCase.Url, message.Parts[1].ContentCase);
+ Assert.Equal("https://example.com/file.pdf", message.Parts[1].Url);
}
[Fact]
@@ -413,10 +432,11 @@ public async Task RunAsync_WithContinuationTokenAndMessages_ThrowsInvalidOperati
public async Task RunAsync_WithContinuationToken_CallsGetTaskAsyncAsync()
{
// Arrange
- this._handler.ResponseToReturn = new AgentTask
+ this._handler.AgentTaskToReturn = new AgentTask
{
Id = "task-123",
- ContextId = "context-123"
+ ContextId = "context-123",
+ Status = new() { State = TaskState.Submitted }
};
var options = new AgentRunOptions { ContinuationToken = new A2AContinuationToken("task-123") };
@@ -425,19 +445,22 @@ public async Task RunAsync_WithContinuationToken_CallsGetTaskAsyncAsync()
await this._agent.RunAsync([], options: options);
// Assert
- Assert.Equal("tasks/get", this._handler.CapturedJsonRpcRequest?.Method);
- Assert.Equal("task-123", this._handler.CapturedTaskIdParams?.Id);
+ Assert.Equal("GetTask", this._handler.CapturedJsonRpcRequest?.Method);
+ Assert.Equal("task-123", this._handler.CapturedGetTaskRequest?.Id);
}
[Fact]
public async Task RunAsync_WithTaskInSessionAndMessage_AddTaskAsReferencesToMessageAsync()
{
// Arrange
- this._handler.ResponseToReturn = new AgentMessage
+ this._handler.ResponseToReturn = new SendMessageResponse
{
- MessageId = "response-123",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Response to task" }]
+ Message = new Message
+ {
+ MessageId = "response-123",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Response to task" }]
+ }
};
var session = (A2AAgentSession)await this._agent.CreateSessionAsync();
@@ -449,7 +472,7 @@ public async Task RunAsync_WithTaskInSessionAndMessage_AddTaskAsReferencesToMess
await this._agent.RunAsync(inputMessage, session);
// Assert
- var message = this._handler.CapturedMessageSendParams?.Message;
+ var message = this._handler.CapturedSendMessageRequest?.Message;
Assert.Null(message?.TaskId);
Assert.NotNull(message?.ReferenceTaskIds);
Assert.Contains("task-123", message.ReferenceTaskIds);
@@ -459,11 +482,14 @@ public async Task RunAsync_WithTaskInSessionAndMessage_AddTaskAsReferencesToMess
public async Task RunAsync_WithAgentTask_UpdatesSessionTaskIdAsync()
{
// Arrange
- this._handler.ResponseToReturn = new AgentTask
+ this._handler.ResponseToReturn = new SendMessageResponse
{
- Id = "task-456",
- ContextId = "context-789",
- Status = new() { State = TaskState.Submitted }
+ Task = new AgentTask
+ {
+ Id = "task-456",
+ ContextId = "context-789",
+ Status = new() { State = TaskState.Submitted }
+ }
};
var session = await this._agent.CreateSessionAsync();
@@ -480,16 +506,19 @@ public async Task RunAsync_WithAgentTask_UpdatesSessionTaskIdAsync()
public async Task RunAsync_WithAgentTaskResponse_ReturnsTaskResponseCorrectlyAsync()
{
// Arrange
- this._handler.ResponseToReturn = new AgentTask
+ this._handler.ResponseToReturn = new SendMessageResponse
{
- Id = "task-789",
- ContextId = "context-456",
- Status = new() { State = TaskState.Submitted },
- Metadata = new Dictionary
+ Task = new AgentTask
+ {
+ Id = "task-789",
+ ContextId = "context-456",
+ Status = new() { State = TaskState.Submitted },
+ Metadata = new Dictionary
{
{ "key1", JsonSerializer.SerializeToElement("value1") },
{ "count", JsonSerializer.SerializeToElement(42) }
}
+ }
};
var session = await this._agent.CreateSessionAsync();
@@ -532,11 +561,14 @@ public async Task RunAsync_WithAgentTaskResponse_ReturnsTaskResponseCorrectlyAsy
public async Task RunAsync_WithVariousTaskStates_ReturnsCorrectTokenAsync(TaskState taskState)
{
// Arrange
- this._handler.ResponseToReturn = new AgentTask
+ this._handler.ResponseToReturn = new SendMessageResponse
{
- Id = "task-123",
- ContextId = "context-123",
- Status = new() { State = taskState }
+ Task = new AgentTask
+ {
+ Id = "task-123",
+ ContextId = "context-123",
+ Status = new() { State = taskState }
+ }
};
// Act
@@ -583,15 +615,76 @@ await Assert.ThrowsAsync(async () =>
});
}
+ [Fact]
+ public async Task RunStreamingAsync_WithContinuationToken_UsesSubscribeToTaskMethodAsync()
+ {
+ // Arrange
+ this._handler.StreamingResponseToReturn = new StreamResponse
+ {
+ Message = new Message
+ {
+ MessageId = "response-123",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Continuation response" }]
+ }
+ };
+
+ var options = new AgentRunOptions { ContinuationToken = new A2AContinuationToken("task-456") };
+
+ // Act
+ await foreach (var _ in this._agent.RunStreamingAsync([], null, options))
+ {
+ // Just iterate through to trigger the logic
+ }
+
+ // Assert - verify SubscribeToTask was called (not SendStreamingMessage)
+ Assert.Single(this._handler.CapturedJsonRpcRequests);
+ Assert.Equal("SubscribeToTask", this._handler.CapturedJsonRpcRequests[0].Method);
+ }
+
+ [Fact]
+ public async Task RunStreamingAsync_WithContinuationToken_PassesCorrectTaskIdAsync()
+ {
+ // Arrange
+ this._handler.StreamingResponseToReturn = new StreamResponse
+ {
+ Message = new Message
+ {
+ MessageId = "response-123",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Continuation response" }]
+ }
+ };
+
+ const string ExpectedTaskId = "my-task-789";
+ var options = new AgentRunOptions { ContinuationToken = new A2AContinuationToken(ExpectedTaskId) };
+
+ // Act
+ await foreach (var _ in this._agent.RunStreamingAsync([], null, options))
+ {
+ // Just iterate through to trigger the logic
+ }
+
+ // Assert - verify the task ID was passed correctly
+ Assert.NotEmpty(this._handler.CapturedJsonRpcRequests);
+ var subscribeRequest = this._handler.CapturedJsonRpcRequests[0];
+ var subscribeParams = subscribeRequest.Params?.Deserialize(A2AJsonUtilities.DefaultOptions);
+ Assert.NotNull(subscribeParams);
+ Assert.Equal(ExpectedTaskId, subscribeParams.Id);
+ }
+
[Fact]
public async Task RunStreamingAsync_WithTaskInSessionAndMessage_AddTaskAsReferencesToMessageAsync()
{
// Arrange
- this._handler.StreamingResponseToReturn = new AgentMessage
+ this._handler.StreamingResponseToReturn = new StreamResponse
{
- MessageId = "response-123",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Response to task" }]
+ Message = new Message
+ {
+ MessageId = "response-123",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Response to task" }]
+ }
};
var session = (A2AAgentSession)await this._agent.CreateSessionAsync();
@@ -604,7 +697,7 @@ public async Task RunStreamingAsync_WithTaskInSessionAndMessage_AddTaskAsReferen
}
// Assert
- var message = this._handler.CapturedMessageSendParams?.Message;
+ var message = this._handler.CapturedSendMessageRequest?.Message;
Assert.Null(message?.TaskId);
Assert.NotNull(message?.ReferenceTaskIds);
Assert.Contains("task-123", message.ReferenceTaskIds);
@@ -614,11 +707,14 @@ public async Task RunStreamingAsync_WithTaskInSessionAndMessage_AddTaskAsReferen
public async Task RunStreamingAsync_WithAgentTask_UpdatesSessionTaskIdAsync()
{
// Arrange
- this._handler.StreamingResponseToReturn = new AgentTask
+ this._handler.StreamingResponseToReturn = new StreamResponse
{
- Id = "task-456",
- ContextId = "context-789",
- Status = new() { State = TaskState.Submitted }
+ Task = new AgentTask
+ {
+ Id = "task-456",
+ ContextId = "context-789",
+ Status = new() { State = TaskState.Submitted }
+ }
};
var session = await this._agent.CreateSessionAsync();
@@ -642,15 +738,18 @@ public async Task RunStreamingAsync_WithAgentMessage_YieldsResponseUpdateAsync()
const string ContextId = "ctx-456";
const string MessageText = "Hello from agent!";
- this._handler.StreamingResponseToReturn = new AgentMessage
+ this._handler.StreamingResponseToReturn = new StreamResponse
{
- MessageId = MessageId,
- Role = MessageRole.Agent,
- ContextId = ContextId,
- Parts =
- [
- new TextPart { Text = MessageText }
- ]
+ Message = new Message
+ {
+ MessageId = MessageId,
+ Role = Role.Agent,
+ ContextId = ContextId,
+ Parts =
+ [
+ new Part { Text = MessageText }
+ ]
+ }
};
// Act
@@ -670,8 +769,8 @@ public async Task RunStreamingAsync_WithAgentMessage_YieldsResponseUpdateAsync()
Assert.Equal(this._agent.Id, update0.AgentId);
Assert.Equal(MessageText, update0.Text);
Assert.Equal(ChatFinishReason.Stop, update0.FinishReason);
- Assert.IsType(update0.RawRepresentation);
- Assert.Equal(MessageId, ((AgentMessage)update0.RawRepresentation!).MessageId);
+ Assert.IsType(update0.RawRepresentation);
+ Assert.Equal(MessageId, ((Message)update0.RawRepresentation!).MessageId);
}
[Fact]
@@ -681,18 +780,21 @@ public async Task RunStreamingAsync_WithAgentTask_YieldsResponseUpdateAsync()
const string TaskId = "task-789";
const string ContextId = "ctx-012";
- this._handler.StreamingResponseToReturn = new AgentTask
+ this._handler.StreamingResponseToReturn = new StreamResponse
{
- Id = TaskId,
- ContextId = ContextId,
- Status = new() { State = TaskState.Submitted },
- Artifacts = [
+ Task = new AgentTask
+ {
+ Id = TaskId,
+ ContextId = ContextId,
+ Status = new() { State = TaskState.Submitted },
+ Artifacts = [
new()
{
ArtifactId = "art-123",
- Parts = [new TextPart { Text = "Task artifact content" }]
+ Parts = [new Part { Text = "Task artifact content" }]
}
]
+ }
};
var session = await this._agent.CreateSessionAsync();
@@ -728,11 +830,14 @@ public async Task RunStreamingAsync_WithTaskStatusUpdateEvent_YieldsResponseUpda
const string TaskId = "task-status-123";
const string ContextId = "ctx-status-456";
- this._handler.StreamingResponseToReturn = new TaskStatusUpdateEvent
+ this._handler.StreamingResponseToReturn = new StreamResponse
{
- TaskId = TaskId,
- ContextId = ContextId,
- Status = new() { State = TaskState.Working }
+ StatusUpdate = new TaskStatusUpdateEvent
+ {
+ TaskId = TaskId,
+ ContextId = ContextId,
+ Status = new() { State = TaskState.Working }
+ }
};
var session = await this._agent.CreateSessionAsync();
@@ -768,14 +873,17 @@ public async Task RunStreamingAsync_WithTaskArtifactUpdateEvent_YieldsResponseUp
const string ContextId = "ctx-artifact-456";
const string ArtifactContent = "Task artifact data";
- this._handler.StreamingResponseToReturn = new TaskArtifactUpdateEvent
+ this._handler.StreamingResponseToReturn = new StreamResponse
{
- TaskId = TaskId,
- ContextId = ContextId,
- Artifact = new()
+ ArtifactUpdate = new TaskArtifactUpdateEvent
{
- ArtifactId = "artifact-789",
- Parts = [new TextPart { Text = ArtifactContent }]
+ TaskId = TaskId,
+ ContextId = ContextId,
+ Artifact = new()
+ {
+ ArtifactId = "artifact-789",
+ Parts = [new Part { Text = ArtifactContent }]
+ }
}
};
@@ -848,15 +956,18 @@ await Assert.ThrowsAsync(async () =>
public async Task RunAsync_WithAgentMessageResponseMetadata_ReturnsMetadataAsAdditionalPropertiesAsync()
{
// Arrange
- this._handler.ResponseToReturn = new AgentMessage
+ this._handler.ResponseToReturn = new SendMessageResponse
{
- MessageId = "response-123",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Response with metadata" }],
- Metadata = new Dictionary
+ Message = new Message
{
- { "responseKey1", JsonSerializer.SerializeToElement("responseValue1") },
- { "responseCount", JsonSerializer.SerializeToElement(99) }
+ MessageId = "response-123",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Response with metadata" }],
+ Metadata = new Dictionary
+ {
+ { "responseKey1", JsonSerializer.SerializeToElement("responseValue1") },
+ { "responseCount", JsonSerializer.SerializeToElement(99) }
+ }
}
};
@@ -877,14 +988,17 @@ public async Task RunAsync_WithAgentMessageResponseMetadata_ReturnsMetadataAsAdd
}
[Fact]
- public async Task RunAsync_WithAdditionalProperties_PropagatesThemAsMetadataToMessageSendParamsAsync()
+ public async Task RunAsync_WithAdditionalProperties_PropagatesThemAsMetadataToSendMessageRequestAsync()
{
// Arrange
- this._handler.ResponseToReturn = new AgentMessage
+ this._handler.ResponseToReturn = new SendMessageResponse
{
- MessageId = "response-123",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Response" }]
+ Message = new Message
+ {
+ MessageId = "response-123",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Response" }]
+ }
};
var inputMessages = new List
@@ -906,22 +1020,25 @@ public async Task RunAsync_WithAdditionalProperties_PropagatesThemAsMetadataToMe
await this._agent.RunAsync(inputMessages, null, options);
// Assert
- Assert.NotNull(this._handler.CapturedMessageSendParams);
- Assert.NotNull(this._handler.CapturedMessageSendParams.Metadata);
- Assert.Equal("value1", this._handler.CapturedMessageSendParams.Metadata["key1"].GetString());
- Assert.Equal(42, this._handler.CapturedMessageSendParams.Metadata["key2"].GetInt32());
- Assert.True(this._handler.CapturedMessageSendParams.Metadata["key3"].GetBoolean());
+ Assert.NotNull(this._handler.CapturedSendMessageRequest);
+ Assert.NotNull(this._handler.CapturedSendMessageRequest.Metadata);
+ Assert.Equal("value1", this._handler.CapturedSendMessageRequest.Metadata["key1"].GetString());
+ Assert.Equal(42, this._handler.CapturedSendMessageRequest.Metadata["key2"].GetInt32());
+ Assert.True(this._handler.CapturedSendMessageRequest.Metadata["key3"].GetBoolean());
}
[Fact]
public async Task RunAsync_WithNullAdditionalProperties_DoesNotSetMetadataAsync()
{
// Arrange
- this._handler.ResponseToReturn = new AgentMessage
+ this._handler.ResponseToReturn = new SendMessageResponse
{
- MessageId = "response-123",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Response" }]
+ Message = new Message
+ {
+ MessageId = "response-123",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Response" }]
+ }
};
var inputMessages = new List
@@ -938,19 +1055,22 @@ public async Task RunAsync_WithNullAdditionalProperties_DoesNotSetMetadataAsync(
await this._agent.RunAsync(inputMessages, null, options);
// Assert
- Assert.NotNull(this._handler.CapturedMessageSendParams);
- Assert.Null(this._handler.CapturedMessageSendParams.Metadata);
+ Assert.NotNull(this._handler.CapturedSendMessageRequest);
+ Assert.Null(this._handler.CapturedSendMessageRequest.Metadata);
}
[Fact]
- public async Task RunStreamingAsync_WithAdditionalProperties_PropagatesThemAsMetadataToMessageSendParamsAsync()
+ public async Task RunStreamingAsync_WithAdditionalProperties_PropagatesThemAsMetadataToSendMessageRequestAsync()
{
// Arrange
- this._handler.StreamingResponseToReturn = new AgentMessage
+ this._handler.StreamingResponseToReturn = new StreamResponse
{
- MessageId = "stream-123",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Streaming response" }]
+ Message = new Message
+ {
+ MessageId = "stream-123",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Streaming response" }]
+ }
};
var inputMessages = new List
@@ -974,22 +1094,25 @@ public async Task RunStreamingAsync_WithAdditionalProperties_PropagatesThemAsMet
}
// Assert
- Assert.NotNull(this._handler.CapturedMessageSendParams);
- Assert.NotNull(this._handler.CapturedMessageSendParams.Metadata);
- Assert.Equal("streamValue1", this._handler.CapturedMessageSendParams.Metadata["streamKey1"].GetString());
- Assert.Equal(100, this._handler.CapturedMessageSendParams.Metadata["streamKey2"].GetInt32());
- Assert.False(this._handler.CapturedMessageSendParams.Metadata["streamKey3"].GetBoolean());
+ Assert.NotNull(this._handler.CapturedSendMessageRequest);
+ Assert.NotNull(this._handler.CapturedSendMessageRequest.Metadata);
+ Assert.Equal("streamValue1", this._handler.CapturedSendMessageRequest.Metadata["streamKey1"].GetString());
+ Assert.Equal(100, this._handler.CapturedSendMessageRequest.Metadata["streamKey2"].GetInt32());
+ Assert.False(this._handler.CapturedSendMessageRequest.Metadata["streamKey3"].GetBoolean());
}
[Fact]
public async Task RunStreamingAsync_WithNullAdditionalProperties_DoesNotSetMetadataAsync()
{
// Arrange
- this._handler.StreamingResponseToReturn = new AgentMessage
+ this._handler.StreamingResponseToReturn = new StreamResponse
{
- MessageId = "stream-123",
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Streaming response" }]
+ Message = new Message
+ {
+ MessageId = "stream-123",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Streaming response" }]
+ }
};
var inputMessages = new List
@@ -1008,8 +1131,115 @@ public async Task RunStreamingAsync_WithNullAdditionalProperties_DoesNotSetMetad
}
// Assert
- Assert.NotNull(this._handler.CapturedMessageSendParams);
- Assert.Null(this._handler.CapturedMessageSendParams.Metadata);
+ Assert.NotNull(this._handler.CapturedSendMessageRequest);
+ Assert.Null(this._handler.CapturedSendMessageRequest.Metadata);
+ }
+
+ [Fact]
+ public async Task RunAsync_WithDefaultOptions_SetsBlockingToTrueAsync()
+ {
+ // Arrange
+ var inputMessages = new List
+ {
+ new(ChatRole.User, "Test message")
+ };
+
+ // Act
+ await this._agent.RunAsync(inputMessages);
+
+ // Assert
+ Assert.NotNull(this._handler.CapturedSendMessageRequest);
+ Assert.NotNull(this._handler.CapturedSendMessageRequest.Configuration);
+ Assert.False(this._handler.CapturedSendMessageRequest.Configuration.ReturnImmediately);
+ }
+
+ [Fact]
+ public async Task RunAsync_WithAllowBackgroundResponsesTrue_SetsReturnImmediatelyToTrueAsync()
+ {
+ // Arrange
+ var inputMessages = new List
+ {
+ new(ChatRole.User, "Test message")
+ };
+
+ var session = await this._agent.CreateSessionAsync();
+ var options = new AgentRunOptions { AllowBackgroundResponses = true };
+
+ // Act
+ await this._agent.RunAsync(inputMessages, session, options);
+
+ // Assert
+ Assert.NotNull(this._handler.CapturedSendMessageRequest);
+ Assert.NotNull(this._handler.CapturedSendMessageRequest.Configuration);
+ Assert.True(this._handler.CapturedSendMessageRequest.Configuration.ReturnImmediately);
+ }
+
+ [Fact]
+ public async Task RunAsync_WithAllowBackgroundResponsesFalse_SetsReturnImmediatelyToFalseAsync()
+ {
+ // Arrange
+ var inputMessages = new List
+ {
+ new(ChatRole.User, "Test message")
+ };
+
+ var options = new AgentRunOptions { AllowBackgroundResponses = false };
+
+ // Act
+ await this._agent.RunAsync(inputMessages, null, options);
+
+ // Assert
+ Assert.NotNull(this._handler.CapturedSendMessageRequest);
+ Assert.NotNull(this._handler.CapturedSendMessageRequest.Configuration);
+ Assert.False(this._handler.CapturedSendMessageRequest.Configuration.ReturnImmediately);
+ }
+
+ [Fact]
+ public async Task RunAsync_WithNullOptions_SetsReturnImmediatelyToFalseAsync()
+ {
+ // Arrange
+ var inputMessages = new List
+ {
+ new(ChatRole.User, "Test message")
+ };
+
+ // Act
+ await this._agent.RunAsync(inputMessages, null, null);
+
+ // Assert
+ Assert.NotNull(this._handler.CapturedSendMessageRequest);
+ Assert.NotNull(this._handler.CapturedSendMessageRequest.Configuration);
+ Assert.False(this._handler.CapturedSendMessageRequest.Configuration.ReturnImmediately);
+ }
+
+ [Fact]
+ public async Task RunStreamingAsync_SendMessageRequest_DoesNotSetReturnImmediatelyConfigurationAsync()
+ {
+ // Arrange
+ this._handler.StreamingResponseToReturn = new StreamResponse
+ {
+ Message = new Message
+ {
+ MessageId = "response-123",
+ Role = Role.Agent,
+ Parts = [new Part { Text = "Streaming response" }]
+ }
+ };
+
+ var inputMessages = new List
+ {
+ new(ChatRole.User, "Test message")
+ };
+
+ // Act
+ await foreach (var _ in this._agent.RunStreamingAsync(inputMessages))
+ {
+ // Just iterate through to trigger the logic
+ }
+
+ // Assert
+ Assert.NotNull(this._handler.CapturedSendMessageRequest);
+ Assert.Null(this._handler.CapturedSendMessageRequest.Configuration);
}
[Fact]
@@ -1256,6 +1486,7 @@ await Assert.ThrowsAnyAsync(async () =>
public void Dispose()
{
+ this._a2aClient.Dispose();
this._handler.Dispose();
this._httpClient.Dispose();
}
@@ -1269,13 +1500,17 @@ internal sealed class A2AClientHttpMessageHandlerStub : HttpMessageHandler
{
public JsonRpcRequest? CapturedJsonRpcRequest { get; set; }
- public MessageSendParams? CapturedMessageSendParams { get; set; }
+ public List CapturedJsonRpcRequests { get; } = [];
+
+ public SendMessageRequest? CapturedSendMessageRequest { get; set; }
- public TaskIdParams? CapturedTaskIdParams { get; set; }
+ public GetTaskRequest? CapturedGetTaskRequest { get; set; }
- public A2AEvent? ResponseToReturn { get; set; }
+ public SendMessageResponse? ResponseToReturn { get; set; }
- public A2AEvent? StreamingResponseToReturn { get; set; }
+ public AgentTask? AgentTaskToReturn { get; set; }
+
+ public StreamResponse? StreamingResponseToReturn { get; set; }
protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
@@ -1286,22 +1521,46 @@ protected override async Task SendAsync(HttpRequestMessage
this.CapturedJsonRpcRequest = JsonSerializer.Deserialize(content);
+ if (this.CapturedJsonRpcRequest is not null)
+ {
+ this.CapturedJsonRpcRequests.Add(this.CapturedJsonRpcRequest);
+ }
+
try
{
- this.CapturedMessageSendParams = this.CapturedJsonRpcRequest?.Params?.Deserialize();
+ this.CapturedSendMessageRequest = this.CapturedJsonRpcRequest?.Params?.Deserialize(A2AJsonUtilities.DefaultOptions);
}
- catch { /* Ignore deserialization errors for non-MessageSendParams requests */ }
+ catch { /* Ignore deserialization errors for non-SendMessageRequest requests */ }
try
{
- this.CapturedTaskIdParams = this.CapturedJsonRpcRequest?.Params?.Deserialize();
+ this.CapturedGetTaskRequest = this.CapturedJsonRpcRequest?.Params?.Deserialize(A2AJsonUtilities.DefaultOptions);
+ }
+ catch { /* Ignore deserialization errors for non-GetTaskRequest requests */ }
+
+ // Return the pre-configured AgentTask response (for tasks/get)
+ if (this.AgentTaskToReturn is not null && this.CapturedJsonRpcRequest?.Method == "GetTask")
+ {
+ var jsonRpcResponse = new JsonRpcResponse
+ {
+ Id = "response-id",
+ Result = JsonSerializer.SerializeToNode(this.AgentTaskToReturn, A2AJsonUtilities.DefaultOptions)
+ };
+
+ return new HttpResponseMessage(HttpStatusCode.OK)
+ {
+ Content = new StringContent(JsonSerializer.Serialize(jsonRpcResponse), Encoding.UTF8, "application/json")
+ };
}
- catch { /* Ignore deserialization errors for non-TaskIdParams requests */ }
// Return the pre-configured non-streaming response
if (this.ResponseToReturn is not null)
{
- var jsonRpcResponse = JsonRpcResponse.CreateJsonRpcResponse("response-id", this.ResponseToReturn);
+ var jsonRpcResponse = new JsonRpcResponse
+ {
+ Id = "response-id",
+ Result = JsonSerializer.SerializeToNode(this.ResponseToReturn, A2AJsonUtilities.DefaultOptions)
+ };
return new HttpResponseMessage(HttpStatusCode.OK)
{
@@ -1311,22 +1570,18 @@ protected override async Task SendAsync(HttpRequestMessage
// Return the pre-configured streaming response
else if (this.StreamingResponseToReturn is not null)
{
- var stream = new MemoryStream();
-
- await SseFormatter.WriteAsync(
- new SseItem[]
- {
- new(JsonRpcResponse.CreateJsonRpcResponse("response-id", this.StreamingResponseToReturn!))
- }.ToAsyncEnumerable(),
- stream,
- (item, writer) =>
- {
- using Utf8JsonWriter json = new(writer, new() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
- JsonSerializer.Serialize(json, item.Data);
- },
- cancellationToken
- );
+ var jsonRpcResponse = new JsonRpcResponse
+ {
+ Id = "response-id",
+ Result = JsonSerializer.SerializeToNode(this.StreamingResponseToReturn, A2AJsonUtilities.DefaultOptions)
+ };
+ var stream = new MemoryStream();
+ var writer = new StreamWriter(stream);
+ await writer.WriteAsync($"data: {JsonSerializer.Serialize(jsonRpcResponse, A2AJsonUtilities.DefaultOptions)}\n\n");
+#pragma warning disable CA2016 // Forward the 'CancellationToken' parameter to methods; overload doesn't exist downlevel
+ await writer.FlushAsync();
+#pragma warning restore CA2016
stream.Position = 0;
return new HttpResponseMessage(HttpStatusCode.OK)
@@ -1339,7 +1594,11 @@ await SseFormatter.WriteAsync(
}
else
{
- var jsonRpcResponse = JsonRpcResponse.CreateJsonRpcResponse("response-id", new AgentMessage());
+ var jsonRpcResponse = new JsonRpcResponse
+ {
+ Id = "response-id",
+ Result = JsonSerializer.SerializeToNode(new SendMessageResponse { Message = new Message() }, A2AJsonUtilities.DefaultOptions)
+ };
return new HttpResponseMessage(HttpStatusCode.OK)
{
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAIContentExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAIContentExtensionsTests.cs
index 358bdfb152..c2e704833a 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAIContentExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAIContentExtensionsTests.cs
@@ -42,14 +42,14 @@ public void ToA2AParts_WithMultipleContents_ReturnsListWithAllParts()
Assert.NotNull(result);
Assert.Equal(3, result.Count);
- var firstTextPart = Assert.IsType(result[0]);
- Assert.Equal("First text", firstTextPart.Text);
+ Assert.Equal(PartContentCase.Text, result[0].ContentCase);
+ Assert.Equal("First text", result[0].Text);
- var filePart = Assert.IsType(result[1]);
- Assert.Equal("https://example.com/file1.txt", filePart.File.Uri?.ToString());
+ Assert.Equal(PartContentCase.Url, result[1].ContentCase);
+ Assert.Equal("https://example.com/file1.txt", result[1].Url);
- var secondTextPart = Assert.IsType(result[2]);
- Assert.Equal("Second text", secondTextPart.Text);
+ Assert.Equal(PartContentCase.Text, result[2].ContentCase);
+ Assert.Equal("Second text", result[2].Text);
}
[Fact]
@@ -72,14 +72,14 @@ public void ToA2AParts_WithMixedSupportedAndUnsupportedContent_IgnoresUnsupporte
Assert.NotNull(result);
Assert.Equal(3, result.Count);
- var firstTextPart = Assert.IsType(result[0]);
- Assert.Equal("First text", firstTextPart.Text);
+ Assert.Equal(PartContentCase.Text, result[0].ContentCase);
+ Assert.Equal("First text", result[0].Text);
- var filePart = Assert.IsType(result[1]);
- Assert.Equal("https://example.com/file.txt", filePart.File.Uri?.ToString());
+ Assert.Equal(PartContentCase.Url, result[1].ContentCase);
+ Assert.Equal("https://example.com/file.txt", result[1].Url);
- var secondTextPart = Assert.IsType(result[2]);
- Assert.Equal("Second text", secondTextPart.Text);
+ Assert.Equal(PartContentCase.Text, result[2].ContentCase);
+ Assert.Equal("Second text", result[2].Text);
}
// Mock class for testing unsupported scenarios
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAgentCardExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAgentCardExtensionsTests.cs
index f644109b38..b45c381bd2 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAgentCardExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAgentCardExtensionsTests.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
@@ -26,7 +27,7 @@ public A2AAgentCardExtensionsTests()
{
Name = "Test Agent",
Description = "A test agent for unit testing",
- Url = "http://test-endpoint/agent"
+ SupportedInterfaces = [new AgentInterface { Url = "http://test-endpoint/agent" }]
};
}
@@ -50,10 +51,10 @@ public async Task RunIAgentAsync_SendsRequestToTheUrlSpecifiedInAgentCardAsync()
using var handler = new HttpMessageHandlerStub();
using var httpClient = new HttpClient(handler, false);
- handler.ResponsesToReturn.Enqueue(new AgentMessage
+ handler.ResponsesToReturn.Enqueue(new Message
{
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Response" }],
+ Role = Role.Agent,
+ Parts = [Part.FromText("Response")],
});
var agent = this._agentCard.AsAIAgent(httpClient);
@@ -66,6 +67,41 @@ public async Task RunIAgentAsync_SendsRequestToTheUrlSpecifiedInAgentCardAsync()
Assert.Equal(new Uri("http://test-endpoint/agent"), handler.CapturedUris[0]);
}
+ [Fact]
+ public async Task AsAIAgent_WithMultipleInterfaces_UsesFirstInterfaceAsync()
+ {
+ // Arrange
+ var card = new AgentCard
+ {
+ Name = "Multi-Interface Agent",
+ Description = "An agent with multiple interfaces",
+ SupportedInterfaces =
+ [
+ new AgentInterface { Url = "http://first/agent" },
+ new AgentInterface { Url = "http://second/agent", ProtocolBinding = "grpc" },
+ new AgentInterface { Url = "http://third/agent", ProtocolBinding = "http" },
+ ]
+ };
+
+ using var handler = new HttpMessageHandlerStub();
+ using var httpClient = new HttpClient(handler, false);
+
+ handler.ResponsesToReturn.Enqueue(new Message
+ {
+ Role = Role.Agent,
+ Parts = [Part.FromText("Response")],
+ });
+
+ var agent = card.AsAIAgent(httpClient);
+
+ // Act
+ await agent.RunAsync("Test input");
+
+ // Assert
+ Assert.Single(handler.CapturedUris);
+ Assert.Equal(new Uri("http://first/agent"), handler.CapturedUris[0]);
+ }
+
internal sealed class HttpMessageHandlerStub : HttpMessageHandler
{
public Queue ResponsesToReturn { get; } = new();
@@ -86,13 +122,18 @@ protected override async Task SendAsync(HttpRequestMessage
Content = new StringContent(json, Encoding.UTF8, "application/json")
};
}
- else if (response is AgentMessage message)
+ else if (response is Message message)
{
- var jsonRpcResponse = JsonRpcResponse.CreateJsonRpcResponse("response-id", message);
+ var sendMessageResponse = new SendMessageResponse { Message = message };
+ var jsonRpcResponse = new JsonRpcResponse
+ {
+ Id = "response-id",
+ Result = JsonSerializer.SerializeToNode(sendMessageResponse, A2AJsonUtilities.DefaultOptions)
+ };
return new HttpResponseMessage(HttpStatusCode.OK)
{
- Content = new StringContent(JsonSerializer.Serialize(jsonRpcResponse), Encoding.UTF8, "application/json")
+ Content = new StringContent(JsonSerializer.Serialize(jsonRpcResponse, A2AJsonUtilities.DefaultOptions), Encoding.UTF8, "application/json")
};
}
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAgentTaskExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAgentTaskExtensionsTests.cs
index 97c9ca7c05..5fdfb1ff89 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAgentTaskExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AAgentTaskExtensionsTests.cs
@@ -40,7 +40,7 @@ public void ToChatMessages_WithEmptyArtifactsAndNoUserInputRequests_ReturnsNull(
{
Id = "task1",
Artifacts = [],
- Status = new AgentTaskStatus { State = TaskState.Completed },
+ Status = new TaskStatus { State = TaskState.Completed },
};
// Act
@@ -58,7 +58,7 @@ public void ToChatMessages_WithNullArtifactsAndNoUserInputRequests_ReturnsNull()
{
Id = "task1",
Artifacts = null,
- Status = new AgentTaskStatus { State = TaskState.Completed },
+ Status = new TaskStatus { State = TaskState.Completed },
};
// Act
@@ -76,7 +76,7 @@ public void ToAIContents_WithEmptyArtifactsAndNoUserInputRequests_ReturnsNull()
{
Id = "task1",
Artifacts = [],
- Status = new AgentTaskStatus { State = TaskState.Completed },
+ Status = new TaskStatus { State = TaskState.Completed },
};
// Act
@@ -94,7 +94,7 @@ public void ToAIContents_WithNullArtifactsAndNoUserInputRequests_ReturnsNull()
{
Id = "task1",
Artifacts = null,
- Status = new AgentTaskStatus { State = TaskState.Completed },
+ Status = new TaskStatus { State = TaskState.Completed },
};
// Act
@@ -110,14 +110,14 @@ public void ToChatMessages_WithValidArtifact_ReturnsChatMessages()
// Arrange
var artifact = new Artifact
{
- Parts = [new TextPart { Text = "response" }],
+ Parts = [Part.FromText("response")],
};
var agentTask = new AgentTask
{
Id = "task1",
Artifacts = [artifact],
- Status = new AgentTaskStatus { State = TaskState.Completed },
+ Status = new TaskStatus { State = TaskState.Completed },
};
// Act
@@ -136,15 +136,15 @@ public void ToAIContents_WithMultipleArtifacts_FlattenAllContents()
// Arrange
var artifact1 = new Artifact
{
- Parts = [new TextPart { Text = "content1" }],
+ Parts = [Part.FromText("content1")],
};
var artifact2 = new Artifact
{
Parts =
[
- new TextPart { Text = "content2" },
- new TextPart { Text = "content3" }
+ Part.FromText("content2"),
+ Part.FromText("content3")
],
};
@@ -152,7 +152,7 @@ public void ToAIContents_WithMultipleArtifacts_FlattenAllContents()
{
Id = "task1",
Artifacts = [artifact1, artifact2],
- Status = new AgentTaskStatus { State = TaskState.Completed },
+ Status = new TaskStatus { State = TaskState.Completed },
};
// Act
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AArtifactExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AArtifactExtensionsTests.cs
index b18abd4485..1f6cfa65f0 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AArtifactExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2AArtifactExtensionsTests.cs
@@ -22,9 +22,9 @@ public void ToChatMessage_WithMultiplePartsMetadataAndRawRepresentation_ReturnsC
Name = "comprehensive-artifact",
Parts =
[
- new TextPart { Text = "First part" },
- new TextPart { Text = "Second part" },
- new TextPart { Text = "Third part" }
+ Part.FromText("First part"),
+ Part.FromText("Second part"),
+ Part.FromText("Third part")
],
Metadata = new Dictionary
{
@@ -66,9 +66,9 @@ public void ToAIContents_WithMultipleParts_ReturnsCorrectList()
Name = "test",
Parts =
[
- new TextPart { Text = "Part 1" },
- new TextPart { Text = "Part 2" },
- new TextPart { Text = "Part 3" }
+ Part.FromText("Part 1"),
+ Part.FromText("Part 2"),
+ Part.FromText("Part 3")
],
Metadata = null
};
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2ACardResolverExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2ACardResolverExtensionsTests.cs
index dcc45e8fce..95cb2a67d2 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2ACardResolverExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/A2ACardResolverExtensionsTests.cs
@@ -37,7 +37,7 @@ public async Task GetAIAgentAsync_WithValidAgentCard_ReturnsAIAgentAsync()
{
Name = "Test Agent",
Description = "A test agent for unit testing",
- Url = "http://test-endpoint/agent"
+ SupportedInterfaces = [new AgentInterface { Url = "http://test-endpoint/agent" }]
});
// Act
@@ -60,12 +60,12 @@ public async Task RunIAgentAsync_WithUrlFromAgentCard_SendsRequestToTheUrlAsync(
// Arrange
this._handler.ResponsesToReturn.Enqueue(new AgentCard
{
- Url = "http://test-endpoint/agent"
+ SupportedInterfaces = [new AgentInterface { Url = "http://test-endpoint/agent" }]
});
- this._handler.ResponsesToReturn.Enqueue(new AgentMessage
+ this._handler.ResponsesToReturn.Enqueue(new Message
{
- Role = MessageRole.Agent,
- Parts = [new TextPart { Text = "Response" }],
+ Role = Role.Agent,
+ Parts = [Part.FromText("Response")],
});
var agent = await this._resolver.GetAIAgentAsync(this._httpClient);
@@ -104,13 +104,18 @@ protected override async Task SendAsync(HttpRequestMessage
Content = new StringContent(json, Encoding.UTF8, "application/json")
};
}
- else if (response is AgentMessage message)
+ else if (response is Message message)
{
- var jsonRpcResponse = JsonRpcResponse.CreateJsonRpcResponse("response-id", message);
+ var sendMessageResponse = new SendMessageResponse { Message = message };
+ var jsonRpcResponse = new JsonRpcResponse
+ {
+ Id = "response-id",
+ Result = JsonSerializer.SerializeToNode(sendMessageResponse, A2AJsonUtilities.DefaultOptions)
+ };
return new HttpResponseMessage(HttpStatusCode.OK)
{
- Content = new StringContent(JsonSerializer.Serialize(jsonRpcResponse), Encoding.UTF8, "application/json")
+ Content = new StringContent(JsonSerializer.Serialize(jsonRpcResponse, A2AJsonUtilities.DefaultOptions), Encoding.UTF8, "application/json")
};
}
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/ChatMessageExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/ChatMessageExtensionsTests.cs
index 8d771c679c..bb502bbea0 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/ChatMessageExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Extensions/ChatMessageExtensionsTests.cs
@@ -32,20 +32,19 @@ public void ToA2AMessage_WithMessageContainingMultipleContents_AddsAllContentsAs
Assert.NotNull(a2aMessage.MessageId);
Assert.NotEmpty(a2aMessage.MessageId);
- Assert.Equal(MessageRole.User, a2aMessage.Role);
+ Assert.Equal(Role.User, a2aMessage.Role);
Assert.NotNull(a2aMessage.Parts);
Assert.Equal(3, a2aMessage.Parts.Count);
- var filePart = Assert.IsType(a2aMessage.Parts[0]);
- Assert.NotNull(filePart.File);
- Assert.Equal("https://example.com/report.pdf", filePart.File.Uri?.ToString());
+ Assert.Equal(PartContentCase.Url, a2aMessage.Parts[0].ContentCase);
+ Assert.Equal("https://example.com/report.pdf", a2aMessage.Parts[0].Url);
- var secondTextPart = Assert.IsType(a2aMessage.Parts[1]);
- Assert.Equal("please summarize the file content", secondTextPart.Text);
+ Assert.Equal(PartContentCase.Text, a2aMessage.Parts[1].ContentCase);
+ Assert.Equal("please summarize the file content", a2aMessage.Parts[1].Text);
- var thirdTextPart = Assert.IsType(a2aMessage.Parts[2]);
- Assert.Equal("and send it to me over email", thirdTextPart.Text);
+ Assert.Equal(PartContentCase.Text, a2aMessage.Parts[2].ContentCase);
+ Assert.Equal("and send it to me over email", a2aMessage.Parts[2].Text);
}
[Fact]
@@ -71,19 +70,18 @@ public void ToA2AMessage_WithMixedMessages_AddsAllContentsAsParts()
Assert.NotNull(a2aMessage.MessageId);
Assert.NotEmpty(a2aMessage.MessageId);
- Assert.Equal(MessageRole.User, a2aMessage.Role);
+ Assert.Equal(Role.User, a2aMessage.Role);
Assert.NotNull(a2aMessage.Parts);
Assert.Equal(3, a2aMessage.Parts.Count);
- var filePart = Assert.IsType(a2aMessage.Parts[0]);
- Assert.NotNull(filePart.File);
- Assert.Equal("https://example.com/report.pdf", filePart.File.Uri?.ToString());
+ Assert.Equal(PartContentCase.Url, a2aMessage.Parts[0].ContentCase);
+ Assert.Equal("https://example.com/report.pdf", a2aMessage.Parts[0].Url);
- var secondTextPart = Assert.IsType(a2aMessage.Parts[1]);
- Assert.Equal("please summarize the file content", secondTextPart.Text);
+ Assert.Equal(PartContentCase.Text, a2aMessage.Parts[1].ContentCase);
+ Assert.Equal("please summarize the file content", a2aMessage.Parts[1].Text);
- var thirdTextPart = Assert.IsType(a2aMessage.Parts[2]);
- Assert.Equal("and send it to me over email", thirdTextPart.Text);
+ Assert.Equal(PartContentCase.Text, a2aMessage.Parts[2].ContentCase);
+ Assert.Equal("and send it to me over email", a2aMessage.Parts[2].Text);
}
}
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Microsoft.Agents.AI.A2A.UnitTests.csproj b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Microsoft.Agents.AI.A2A.UnitTests.csproj
index d33de0613b..97541f6a94 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Microsoft.Agents.AI.A2A.UnitTests.csproj
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/Microsoft.Agents.AI.A2A.UnitTests.csproj
@@ -1,5 +1,9 @@
+
+ $(TargetFrameworksCore)
+
+