diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs
index e4491970ad..887e780104 100644
--- a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs
+++ b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs
@@ -75,11 +75,7 @@ internal sealed class A2AAgent : AIAgent
{
_ = Throw.IfNull(messages);
- thread ??= this.GetNewThread();
- if (thread is not A2AAgentThread typedThread)
- {
- throw new InvalidOperationException("The provided thread is not compatible with the agent. Only threads created by the agent can be used.");
- }
+ A2AAgentThread typedThread = this.GetA2AThread(thread, options);
this._logger.LogA2AAgentInvokingAgent(nameof(RunAsync), this.Id, this.Name);
@@ -142,11 +138,7 @@ internal sealed class A2AAgent : AIAgent
{
_ = Throw.IfNull(messages);
- thread ??= this.GetNewThread();
- if (thread is not A2AAgentThread typedThread)
- {
- throw new InvalidOperationException("The provided thread is not compatible with the agent. Only threads created by the agent can be used.");
- }
+ A2AAgentThread typedThread = this.GetA2AThread(thread, options);
this._logger.LogA2AAgentInvokingAgent(nameof(RunStreamingAsync), this.Id, this.Name);
@@ -217,6 +209,26 @@ internal sealed class A2AAgent : AIAgent
///
public override string? Description => this._description ?? base.Description;
+ private A2AAgentThread GetA2AThread(AgentThread? thread, AgentRunOptions? options)
+ {
+ // Aligning with other agent implementations that support background responses, where
+ // a thread is required for background responses to prevent inconsistent experience
+ // for callers if they forget to provide the thread for initial or follow-up runs.
+ if (options?.AllowBackgroundResponses is true && thread is null)
+ {
+ throw new InvalidOperationException("A thread must be provided when AllowBackgroundResponses is enabled.");
+ }
+
+ thread ??= this.GetNewThread();
+
+ if (thread is not A2AAgentThread typedThread)
+ {
+ throw new InvalidOperationException($"The provided thread type {thread.GetType()} is not compatible with the agent. Only A2A agent created threads are supported.");
+ }
+
+ return typedThread;
+ }
+
private static void UpdateThread(A2AAgentThread? thread, string? contextId, string? taskId = null)
{
if (thread is null)
diff --git a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
index 05c7f5ba08..f079fc5ed8 100644
--- a/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.A2A.UnitTests/A2AAgentTests.cs
@@ -799,12 +799,80 @@ public sealed class A2AAgentTests : IDisposable
Assert.Equal(TaskId, a2aThread.TaskId);
}
+ [Fact]
+ public async Task RunAsync_WithAllowBackgroundResponsesAndNoThread_ThrowsInvalidOperationExceptionAsync()
+ {
+ // Arrange
+ var inputMessages = new List
+ {
+ new(ChatRole.User, "Test message")
+ };
+
+ var options = new AgentRunOptions { AllowBackgroundResponses = true };
+
+ // Act & Assert
+ await Assert.ThrowsAsync(() => this._agent.RunAsync(inputMessages, null, options));
+ }
+
+ [Fact]
+ public async Task RunStreamingAsync_WithAllowBackgroundResponsesAndNoThread_ThrowsInvalidOperationExceptionAsync()
+ {
+ // Arrange
+ var inputMessages = new List
+ {
+ new(ChatRole.User, "Test message")
+ };
+
+ var options = new AgentRunOptions { AllowBackgroundResponses = true };
+
+ // Act & Assert
+ await Assert.ThrowsAsync(async () =>
+ {
+ await foreach (var _ in this._agent.RunStreamingAsync(inputMessages, null, options))
+ {
+ // Just iterate through to trigger the exception
+ }
+ });
+ }
+
+ [Fact]
+ public async Task RunAsync_WithInvalidThreadType_ThrowsInvalidOperationExceptionAsync()
+ {
+ // Arrange
+ // Create a thread from a different agent type
+ var invalidThread = new CustomAgentThread();
+
+ // Act & Assert
+ await Assert.ThrowsAsync(() => this._agent.RunAsync(invalidThread));
+ }
+
+ [Fact]
+ public async Task RunStreamingAsync_WithInvalidThreadType_ThrowsInvalidOperationExceptionAsync()
+ {
+ // Arrange
+ var inputMessages = new List
+ {
+ new(ChatRole.User, "Test message")
+ };
+
+ // Create a thread from a different agent type
+ var invalidThread = new CustomAgentThread();
+
+ // Act & Assert
+ await Assert.ThrowsAsync(async () => await this._agent.RunStreamingAsync(inputMessages, invalidThread).ToListAsync());
+ }
+
public void Dispose()
{
this._handler.Dispose();
this._httpClient.Dispose();
}
+ ///
+ /// Custom agent thread class for testing invalid thread type scenario.
+ ///
+ private sealed class CustomAgentThread : AgentThread;
+
internal sealed class A2AClientHttpMessageHandlerStub : HttpMessageHandler
{
public JsonRpcRequest? CapturedJsonRpcRequest { get; set; }