diff --git a/dotnet/samples/02-agents/DevUI/DevUI_Step01_BasicUsage/README.md b/dotnet/samples/02-agents/DevUI/DevUI_Step01_BasicUsage/README.md
index 48deaad94e..d2ce2137c5 100644
--- a/dotnet/samples/02-agents/DevUI/DevUI_Step01_BasicUsage/README.md
+++ b/dotnet/samples/02-agents/DevUI/DevUI_Step01_BasicUsage/README.md
@@ -63,6 +63,12 @@ To add DevUI to your ASP.NET Core application:
.AddAsAIAgent();
```
+ `AddAsAIAgent()` includes the workflow's final output in the hosted agent response. This is
+ required when the workflow is exposed through `MapOpenAIResponses()` or
+ `MapOpenAIConversations()`; otherwise the output can be visible in streaming workflow events
+ while the completed response has no output items. If you create an agent directly from a
+ `Workflow`, opt in with `workflow.AsAIAgent(includeWorkflowOutputsInResponse: true)`.
+
3. Add OpenAI services and map the endpoints for OpenAI and DevUI:
```csharp
// Register services for OpenAI responses and conversations (also required for DevUI)
diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting/HostedWorkflowBuilderExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting/HostedWorkflowBuilderExtensions.cs
index abee1cb566..8dc684721b 100644
--- a/dotnet/src/Microsoft.Agents.AI.Hosting/HostedWorkflowBuilderExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Hosting/HostedWorkflowBuilderExtensions.cs
@@ -15,6 +15,7 @@ public static class HostedWorkflowBuilderExtensions
///
/// The instance to extend.
/// The DI service lifetime for the agent registration. Defaults to .
+ /// Workflow outputs are included in the hosted agent response.
/// An that can be used to further configure the agent.
public static IHostedAgentBuilder AddAsAIAgent(this IHostedWorkflowBuilder builder, ServiceLifetime lifetime = ServiceLifetime.Singleton)
=> builder.AddAsAIAgent(name: null, lifetime: lifetime);
@@ -25,6 +26,7 @@ public static IHostedAgentBuilder AddAsAIAgent(this IHostedWorkflowBuilder build
/// The instance to extend.
/// The optional name for the AI agent. If not specified, the workflow name is used.
/// The DI service lifetime for the agent registration. Defaults to .
+ /// Workflow outputs are included in the hosted agent response.
/// An that can be used to further configure the agent.
public static IHostedAgentBuilder AddAsAIAgent(this IHostedWorkflowBuilder builder, string? name, ServiceLifetime lifetime = ServiceLifetime.Singleton)
{
@@ -32,6 +34,8 @@ public static IHostedAgentBuilder AddAsAIAgent(this IHostedWorkflowBuilder build
var agentName = name ?? workflowName;
return builder.HostApplicationBuilder.AddAIAgent(agentName, (sp, key) =>
- sp.GetRequiredKeyedService(workflowName).AsAIAgent(name: key), lifetime);
+ sp.GetRequiredKeyedService(workflowName).AsAIAgent(
+ name: key,
+ includeWorkflowOutputsInResponse: true), lifetime);
}
}
diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/ChatMessageOutputWorkflow.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/ChatMessageOutputWorkflow.cs
new file mode 100644
index 0000000000..64d16beefe
--- /dev/null
+++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/ChatMessageOutputWorkflow.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Agents.AI.Workflows;
+using Microsoft.Extensions.AI;
+
+namespace Microsoft.Agents.AI.Hosting.UnitTests;
+
+internal static class ChatMessageOutputWorkflow
+{
+ internal static Workflow Build(string name)
+ {
+ var output = new OutputExecutor("output");
+ return new WorkflowBuilder(output)
+ .WithName(name)
+ .WithOutputFrom(output)
+ .Build();
+ }
+
+ private sealed class OutputExecutor(string id) : ChatProtocolExecutor(id)
+ {
+ protected override ValueTask TakeTurnAsync(
+ List messages,
+ IWorkflowContext context,
+ bool? emitEvents,
+ CancellationToken cancellationToken = default)
+ => context.AddEventAsync(
+ new WorkflowOutputEvent(new ChatMessage(ChatRole.Assistant, "workflow output"), this.Id),
+ cancellationToken);
+ }
+}
diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/HostApplicationBuilderWorkflowExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/HostApplicationBuilderWorkflowExtensionsTests.cs
index c17655bd29..03045b292d 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/HostApplicationBuilderWorkflowExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/HostApplicationBuilderWorkflowExtensionsTests.cs
@@ -2,7 +2,9 @@
using System;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.Agents.AI.Workflows;
+using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Moq;
@@ -186,6 +188,27 @@ public void AddAsAIAgent_WithoutName_UsesWorkflowName()
Assert.NotNull(agentDescriptor);
}
+ ///
+ /// Verifies that a workflow registered as an AI agent includes its chat-message output in the response.
+ ///
+ [Fact]
+ public async Task AddAsAIAgent_IncludesWorkflowOutputInResponseAsync()
+ {
+ // Arrange
+ var builder = new HostApplicationBuilder();
+ const string WorkflowName = "outputWorkflow";
+ builder.AddWorkflow(WorkflowName, (sp, key) => ChatMessageOutputWorkflow.Build(key))
+ .AddAsAIAgent();
+ using var host = builder.Build();
+ AIAgent agent = host.Services.GetRequiredKeyedService(WorkflowName);
+
+ // Act
+ AgentResponse response = await agent.RunAsync(new ChatMessage(ChatRole.User, "hello"));
+
+ // Assert
+ Assert.Equal("workflow output", response.Text);
+ }
+
///
/// Verifies that AddAsAIAgent with a name parameter uses that name instead of the workflow name.
///