diff --git a/.github/workflows/dotnet-format.yml b/.github/workflows/dotnet-format.yml
index 8d7c9febb7..8bdaeba8a3 100644
--- a/.github/workflows/dotnet-format.yml
+++ b/.github/workflows/dotnet-format.yml
@@ -86,11 +86,10 @@ jobs:
run: docker pull mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }}
# This step will run dotnet format on each of the unique csproj files and fail if any changes are made
- # exclude-diagnostics should be removed after fixes for IL2026 and IL3050 are out: https://github.com/dotnet/sdk/issues/51136
- name: Run dotnet format
if: steps.find-csproj.outputs.csproj_files != ''
run: |
for csproj in ${{ steps.find-csproj.outputs.csproj_files }}; do
echo "Running dotnet format on $csproj"
- docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "dotnet format $csproj --verify-no-changes --verbosity diagnostic --exclude-diagnostics IL2026 IL3050"
+ docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:${{ matrix.dotnet }} /bin/sh -c "dotnet format $csproj --verify-no-changes --verbosity diagnostic"
done
diff --git a/dotnet/global.json b/dotnet/global.json
index 482aa6b8d3..42bb8863a3 100644
--- a/dotnet/global.json
+++ b/dotnet/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "10.0.100",
+ "version": "10.0.200",
"rollForward": "minor",
"allowPrerelease": false
},
diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs
index 2393f59202..9d98857e9b 100644
--- a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs
+++ b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs
@@ -127,6 +127,7 @@ public sealed class A2AAgent : AIAgent
{
AgentId = this.Id,
ResponseId = message.MessageId,
+ FinishReason = ChatFinishReason.Stop,
RawRepresentation = message,
Messages = [message.ToChatMessage()],
AdditionalProperties = message.Metadata?.ToAdditionalProperties(),
@@ -141,6 +142,7 @@ public sealed class A2AAgent : AIAgent
{
AgentId = this.Id,
ResponseId = agentTask.Id,
+ FinishReason = MapTaskStateToFinishReason(agentTask.Status.State),
RawRepresentation = agentTask,
Messages = agentTask.ToChatMessages() ?? [],
ContinuationToken = CreateContinuationToken(agentTask.Id, agentTask.Status.State),
@@ -328,6 +330,7 @@ public sealed class A2AAgent : AIAgent
{
AgentId = this.Id,
ResponseId = message.MessageId,
+ FinishReason = ChatFinishReason.Stop,
RawRepresentation = message,
Role = ChatRole.Assistant,
MessageId = message.MessageId,
@@ -342,6 +345,7 @@ public sealed class A2AAgent : AIAgent
{
AgentId = this.Id,
ResponseId = task.Id,
+ FinishReason = MapTaskStateToFinishReason(task.Status.State),
RawRepresentation = task,
Role = ChatRole.Assistant,
Contents = task.ToAIContents(),
@@ -365,7 +369,16 @@ public sealed class A2AAgent : AIAgent
responseUpdate.Contents = artifactUpdateEvent.Artifact.ToAIContents();
responseUpdate.RawRepresentation = artifactUpdateEvent;
}
+ else if (taskUpdateEvent is TaskStatusUpdateEvent statusUpdateEvent)
+ {
+ responseUpdate.FinishReason = MapTaskStateToFinishReason(statusUpdateEvent.Status.State);
+ }
return responseUpdate;
}
+
+ private static ChatFinishReason? MapTaskStateToFinishReason(TaskState state)
+ {
+ return state == TaskState.Completed ? ChatFinishReason.Stop : null;
+ }
}
diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponse.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponse.cs
index 313c64350b..081e054efc 100644
--- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponse.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponse.cs
@@ -61,6 +61,7 @@ public class AgentResponse
this.AdditionalProperties = response.AdditionalProperties;
this.CreatedAt = response.CreatedAt;
+ this.FinishReason = response.FinishReason;
this.Messages = response.Messages;
this.RawRepresentation = response;
this.ResponseId = response.ResponseId;
@@ -84,6 +85,7 @@ public class AgentResponse
this.AdditionalProperties = response.AdditionalProperties;
this.CreatedAt = response.CreatedAt;
+ this.FinishReason = response.FinishReason;
this.Messages = response.Messages;
this.RawRepresentation = response;
this.ResponseId = response.ResponseId;
@@ -190,6 +192,21 @@ public class AgentResponse
///
public DateTimeOffset? CreatedAt { get; set; }
+ ///
+ /// Gets or sets the reason for the agent response finishing.
+ ///
+ ///
+ /// A value indicating why the response finished (e.g., stop, length, content filter, tool calls),
+ /// or if the finish reason is not available.
+ ///
+ ///
+ ///
+ /// This property is particularly useful for detecting non-normal completions, such as content filtering
+ /// or token limit truncation, which may require special handling by the caller.
+ ///
+ ///
+ public ChatFinishReason? FinishReason { get; set; }
+
///
/// Gets or sets the resource usage information for generating this response.
///
@@ -276,6 +293,7 @@ public class AgentResponse
RawRepresentation = message.RawRepresentation,
Role = message.Role,
+ FinishReason = this.FinishReason,
AgentId = this.AgentId,
ResponseId = this.ResponseId,
MessageId = message.MessageId,
diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponseExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponseExtensions.cs
index 75ff6fb359..52edccea1c 100644
--- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponseExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponseExtensions.cs
@@ -38,6 +38,7 @@ public static class AgentResponseExtensions
{
AdditionalProperties = response.AdditionalProperties,
CreatedAt = response.CreatedAt,
+ FinishReason = response.FinishReason,
Messages = response.Messages,
RawRepresentation = response,
ResponseId = response.ResponseId,
@@ -71,6 +72,7 @@ public static class AgentResponseExtensions
AuthorName = responseUpdate.AuthorName,
Contents = responseUpdate.Contents,
CreatedAt = responseUpdate.CreatedAt,
+ FinishReason = responseUpdate.FinishReason,
MessageId = responseUpdate.MessageId,
RawRepresentation = responseUpdate,
ResponseId = responseUpdate.ResponseId,
diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponseUpdate.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponseUpdate.cs
index 3dbe1ada8d..3610c36cdf 100644
--- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponseUpdate.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentResponseUpdate.cs
@@ -70,6 +70,7 @@ public class AgentResponseUpdate
this.AuthorName = chatResponseUpdate.AuthorName;
this.Contents = chatResponseUpdate.Contents;
this.CreatedAt = chatResponseUpdate.CreatedAt;
+ this.FinishReason = chatResponseUpdate.FinishReason;
this.MessageId = chatResponseUpdate.MessageId;
this.RawRepresentation = chatResponseUpdate;
this.ResponseId = chatResponseUpdate.ResponseId;
@@ -153,6 +154,15 @@ public class AgentResponseUpdate
///
public ResponseContinuationToken? ContinuationToken { get; set; }
+ ///
+ /// Gets or sets the reason for the agent response finishing.
+ ///
+ ///
+ /// A value indicating why the response finished (e.g., stop, length, content filter, tool calls),
+ /// or if the finish reason is not available or not yet determined (mid-stream).
+ ///
+ public ChatFinishReason? FinishReason { get; set; }
+
///
public override string ToString() => this.Text;
diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/ChatCompletions/AIAgentChatCompletionsProcessor.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/ChatCompletions/AIAgentChatCompletionsProcessor.cs
index 42443dc2ca..f0c9286c68 100644
--- a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/ChatCompletions/AIAgentChatCompletionsProcessor.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/ChatCompletions/AIAgentChatCompletionsProcessor.cs
@@ -72,9 +72,7 @@ internal static class AIAgentChatCompletionsProcessor
await foreach (var agentResponseUpdate in agent.RunStreamingAsync(chatMessages, options: options, cancellationToken: cancellationToken).WithCancellation(cancellationToken))
{
- var finishReason = (agentResponseUpdate.RawRepresentation is ChatResponseUpdate { FinishReason: not null } chatResponseUpdate)
- ? chatResponseUpdate.FinishReason.ToString()
- : "stop";
+ var finishReason = agentResponseUpdate.FinishReason?.ToString() ?? "stop";
var choiceChunks = new List();
CompletionUsage? usageDetails = null;
diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/ChatCompletions/AgentResponseExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/ChatCompletions/AgentResponseExtensions.cs
index 95d7df0231..823f0e7fef 100644
--- a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/ChatCompletions/AgentResponseExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/ChatCompletions/AgentResponseExtensions.cs
@@ -34,9 +34,7 @@ internal static class AgentResponseExtensions
var chatCompletionChoices = new List();
var index = 0;
- var finishReason = (agentResponse.RawRepresentation is ChatResponse { FinishReason: not null } chatResponse)
- ? chatResponse.FinishReason.ToString()
- : "stop"; // "stop" is a natural stop point; returning this by-default
+ var finishReason = agentResponse.FinishReason?.ToString() ?? ChatFinishReason.Stop.Value; // "stop" is a natural stop point; returning this by-default
foreach (var message in agentResponse.Messages)
{
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/MessageMerger.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/MessageMerger.cs
index de4a8b89f7..4b702034ce 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/MessageMerger.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/MessageMerger.cs
@@ -124,6 +124,7 @@ internal sealed class MessageMerger
List messages = [];
Dictionary responses = [];
HashSet agentIds = [];
+ HashSet finishReasons = [];
foreach (string responseId in this._mergeStates.Keys)
{
@@ -156,6 +157,11 @@ internal sealed class MessageMerger
createdTimes.Add(response.CreatedAt.Value);
}
+ if (response.FinishReason.HasValue)
+ {
+ finishReasons.Add(response.FinishReason.Value);
+ }
+
usage = MergeUsage(usage, response.Usage);
additionalProperties = MergeProperties(additionalProperties, response.AdditionalProperties);
}
@@ -182,6 +188,7 @@ internal sealed class MessageMerger
AgentId = primaryAgentId
?? primaryAgentName
?? (agentIds.Count == 1 ? agentIds.First() : null),
+ FinishReason = finishReasons.Count == 1 ? finishReasons.First() : null,
CreatedAt = DateTimeOffset.UtcNow,
Usage = usage,
AdditionalProperties = additionalProperties
@@ -207,6 +214,7 @@ internal sealed class MessageMerger
AgentId = incoming.AgentId ?? current.AgentId,
AdditionalProperties = MergeProperties(current.AdditionalProperties, incoming.AdditionalProperties),
CreatedAt = incoming.CreatedAt ?? current.CreatedAt,
+ FinishReason = incoming.FinishReason ?? current.FinishReason,
Messages = current.Messages.Concat(incoming.Messages).ToList(),
ResponseId = current.ResponseId,
RawRepresentation = rawRepresentation,
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
index 50d83c140d..514922dd26 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
@@ -126,6 +126,7 @@ public sealed class A2AAgentTests : IDisposable
Assert.Single(result.Messages);
Assert.Equal(ChatRole.Assistant, result.Messages[0].Role);
Assert.Equal("Hello! How can I help you today?", result.Messages[0].Text);
+ Assert.Equal(ChatFinishReason.Stop, result.FinishReason);
}
[Fact]
@@ -249,8 +250,7 @@ public sealed class A2AAgentTests : IDisposable
Assert.Equal("stream-1", updates[0].MessageId);
Assert.Equal(this._agent.Id, updates[0].AgentId);
Assert.Equal("stream-1", updates[0].ResponseId);
-
- Assert.NotNull(updates[0].RawRepresentation);
+ Assert.Equal(ChatFinishReason.Stop, updates[0].FinishReason);
Assert.IsType(updates[0].RawRepresentation);
Assert.Equal("stream-1", ((AgentMessage)updates[0].RawRepresentation!).MessageId);
}
@@ -501,8 +501,7 @@ public sealed class A2AAgentTests : IDisposable
Assert.NotNull(result);
Assert.Equal(this._agent.Id, result.AgentId);
Assert.Equal("task-789", result.ResponseId);
-
- Assert.NotNull(result.RawRepresentation);
+ Assert.Null(result.FinishReason);
Assert.IsType(result.RawRepresentation);
Assert.Equal("task-789", ((AgentTask)result.RawRepresentation).Id);
@@ -552,6 +551,15 @@ public sealed class A2AAgentTests : IDisposable
{
Assert.Null(result.ContinuationToken);
}
+
+ if (taskState is TaskState.Completed)
+ {
+ Assert.Equal(ChatFinishReason.Stop, result.FinishReason);
+ }
+ else
+ {
+ Assert.Null(result.FinishReason);
+ }
}
[Fact]
@@ -661,6 +669,7 @@ public sealed class A2AAgentTests : IDisposable
Assert.Equal(MessageId, update0.ResponseId);
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);
}
@@ -702,6 +711,7 @@ public sealed class A2AAgentTests : IDisposable
Assert.Equal(ChatRole.Assistant, update0.Role);
Assert.Equal(TaskId, update0.ResponseId);
Assert.Equal(this._agent.Id, update0.AgentId);
+ Assert.Null(update0.FinishReason);
Assert.IsType(update0.RawRepresentation);
Assert.Equal(TaskId, ((AgentTask)update0.RawRepresentation!).Id);
@@ -741,6 +751,7 @@ public sealed class A2AAgentTests : IDisposable
Assert.Equal(ChatRole.Assistant, update0.Role);
Assert.Equal(TaskId, update0.ResponseId);
Assert.Equal(this._agent.Id, update0.AgentId);
+ Assert.Null(update0.FinishReason);
Assert.IsType(update0.RawRepresentation);
// Assert - session should be updated with context and task IDs
@@ -784,6 +795,7 @@ public sealed class A2AAgentTests : IDisposable
Assert.Equal(ChatRole.Assistant, update0.Role);
Assert.Equal(TaskId, update0.ResponseId);
Assert.Equal(this._agent.Id, update0.AgentId);
+ Assert.Null(update0.FinishReason);
Assert.IsType(update0.RawRepresentation);
// Assert - artifact content should be in the update
diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseTests.cs
index e1425b3144..6d24c821bc 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseTests.cs
@@ -53,6 +53,7 @@ public class AgentResponseTests
{
AdditionalProperties = [],
CreatedAt = new DateTimeOffset(2022, 1, 1, 0, 0, 0, TimeSpan.Zero),
+ FinishReason = ChatFinishReason.ContentFilter,
Messages = [new(ChatRole.Assistant, "This is a test message.")],
RawRepresentation = new object(),
ResponseId = "responseId",
@@ -63,6 +64,7 @@ public class AgentResponseTests
AgentResponse response = new(chatResponse);
Assert.Same(chatResponse.AdditionalProperties, response.AdditionalProperties);
Assert.Equal(chatResponse.CreatedAt, response.CreatedAt);
+ Assert.Equal(chatResponse.FinishReason, response.FinishReason);
Assert.Same(chatResponse.Messages, response.Messages);
Assert.Equal(chatResponse.ResponseId, response.ResponseId);
Assert.Same(chatResponse, response.RawRepresentation as ChatResponse);
@@ -105,6 +107,10 @@ public class AgentResponseTests
Assert.Null(response.ContinuationToken);
response.ContinuationToken = ResponseContinuationToken.FromBytes(new byte[] { 1, 2, 3 });
Assert.Equivalent(ResponseContinuationToken.FromBytes(new byte[] { 1, 2, 3 }), response.ContinuationToken);
+
+ Assert.Null(response.FinishReason);
+ response.FinishReason = ChatFinishReason.Length;
+ Assert.Equal(ChatFinishReason.Length, response.FinishReason);
}
[Fact]
@@ -188,6 +194,7 @@ public class AgentResponseTests
ResponseId = "12345",
CreatedAt = new DateTimeOffset(2024, 11, 10, 9, 20, 0, TimeSpan.Zero),
AdditionalProperties = new() { ["key1"] = "value1", ["key2"] = 42 },
+ FinishReason = ChatFinishReason.ContentFilter,
Usage = new UsageDetails
{
TotalTokenCount = 100
@@ -205,6 +212,7 @@ public class AgentResponseTests
Assert.Equal(new DateTimeOffset(2024, 11, 10, 9, 20, 0, TimeSpan.Zero), update0.CreatedAt);
Assert.Equal("customRole", update0.Role?.Value);
Assert.Equal("Text", update0.Text);
+ Assert.Equal(ChatFinishReason.ContentFilter, update0.FinishReason);
AgentResponseUpdate update1 = updates[1];
Assert.Equal("value1", update1.AdditionalProperties?["key1"]);
diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateExtensionsTests.cs
index 790298ddf9..89cff04de8 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateExtensionsTests.cs
@@ -334,6 +334,7 @@ public class AgentResponseUpdateExtensionsTests
{
ResponseId = "test-response-id",
CreatedAt = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero),
+ FinishReason = ChatFinishReason.ContentFilter,
Usage = new UsageDetails { TotalTokenCount = 50 },
AdditionalProperties = new() { ["key"] = "value" },
ContinuationToken = ResponseContinuationToken.FromBytes(new byte[] { 1, 2, 3 }),
@@ -346,6 +347,7 @@ public class AgentResponseUpdateExtensionsTests
Assert.NotNull(result);
Assert.Equal("test-response-id", result.ResponseId);
Assert.Equal(new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero), result.CreatedAt);
+ Assert.Equal(ChatFinishReason.ContentFilter, result.FinishReason);
Assert.Same(agentResponse.Messages, result.Messages);
Assert.Same(agentResponse, result.RawRepresentation);
Assert.Same(agentResponse.Usage, result.Usage);
@@ -392,6 +394,7 @@ public class AgentResponseUpdateExtensionsTests
ResponseId = "update-id",
MessageId = "message-id",
CreatedAt = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero),
+ FinishReason = ChatFinishReason.ToolCalls,
AdditionalProperties = new() { ["key"] = "value" },
ContinuationToken = ResponseContinuationToken.FromBytes(new byte[] { 1, 2, 3 }),
};
@@ -405,6 +408,7 @@ public class AgentResponseUpdateExtensionsTests
Assert.Equal("update-id", result.ResponseId);
Assert.Equal("message-id", result.MessageId);
Assert.Equal(new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero), result.CreatedAt);
+ Assert.Equal(ChatFinishReason.ToolCalls, result.FinishReason);
Assert.Equal(ChatRole.Assistant, result.Role);
Assert.Same(agentResponseUpdate.Contents, result.Contents);
Assert.Same(agentResponseUpdate, result.RawRepresentation);
diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateTests.cs
index 7fda5f680b..b563661b61 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentResponseUpdateTests.cs
@@ -24,6 +24,7 @@ public class AgentResponseUpdateTests
Assert.Null(update.CreatedAt);
Assert.Equal(string.Empty, update.ToString());
Assert.Null(update.ContinuationToken);
+ Assert.Null(update.FinishReason);
}
[Fact]
@@ -50,6 +51,7 @@ public class AgentResponseUpdateTests
Assert.Equal(chatResponseUpdate.AuthorName, response.AuthorName);
Assert.Same(chatResponseUpdate.Contents, response.Contents);
Assert.Equal(chatResponseUpdate.CreatedAt, response.CreatedAt);
+ Assert.Equal(chatResponseUpdate.FinishReason, response.FinishReason);
Assert.Equal(chatResponseUpdate.MessageId, response.MessageId);
Assert.Same(chatResponseUpdate, response.RawRepresentation as ChatResponseUpdate);
Assert.Equal(chatResponseUpdate.ResponseId, response.ResponseId);
@@ -109,6 +111,10 @@ public class AgentResponseUpdateTests
Assert.Null(update.ContinuationToken);
update.ContinuationToken = ResponseContinuationToken.FromBytes(new byte[] { 1, 2, 3 });
Assert.Equivalent(ResponseContinuationToken.FromBytes(new byte[] { 1, 2, 3 }), update.ContinuationToken);
+
+ Assert.Null(update.FinishReason);
+ update.FinishReason = ChatFinishReason.ToolCalls;
+ Assert.Equal(ChatFinishReason.ToolCalls, update.FinishReason);
}
[Fact]
diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/MessageMergerTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/MessageMergerTests.cs
index 93448aa327..704e25b14a 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/MessageMergerTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/MessageMergerTests.cs
@@ -37,5 +37,36 @@ public class MessageMergerTests
response.CreatedAt.Should().NotBe(creationTime);
response.Messages[0].CreatedAt.Should().Be(creationTime);
response.Messages[0].Contents.Should().HaveCount(1);
+ response.FinishReason.Should().BeNull();
+ }
+
+ [Fact]
+ public void Test_MessageMerger_PropagatesFinishReasonFromUpdates()
+ {
+ // Arrange
+ string responseId = Guid.NewGuid().ToString("N");
+ string messageId = Guid.NewGuid().ToString("N");
+
+ MessageMerger merger = new();
+
+ foreach (AgentResponseUpdate update in "Hello".ToAgentRunStream(agentId: TestAgentId1, messageId: messageId, responseId: responseId))
+ {
+ merger.AddUpdate(update);
+ }
+
+ // Add a final update with FinishReason set
+ merger.AddUpdate(new AgentResponseUpdate
+ {
+ ResponseId = responseId,
+ MessageId = messageId,
+ FinishReason = ChatFinishReason.ContentFilter,
+ Role = ChatRole.Assistant,
+ });
+
+ // Act
+ AgentResponse response = merger.ComputeMerged(responseId);
+
+ // Assert - FinishReason from the update should propagate through
+ response.FinishReason.Should().Be(ChatFinishReason.ContentFilter);
}
}