fix: Ensure Workflow outputs persisted in chat history when hosted AsAgent

This commit is contained in:
Jacob Alber
2026-03-17 10:41:46 -04:00
Unverified
parent 4f5143cc7e
commit e11ca24913
3 changed files with 43 additions and 4 deletions
@@ -62,6 +62,12 @@ internal sealed class WorkflowChatHistoryProvider : ChatHistoryProvider
}
}
public IEnumerable<ChatMessage> GetAllMessages(AgentSession session)
{
var state = this._sessionState.GetOrInitializeState(session);
return state.Messages;
}
public void UpdateBookmark(AgentSession session)
{
var state = this._sessionState.GetOrInitializeState(session);
@@ -119,13 +119,17 @@ internal sealed class WorkflowHostAgent : AIAgent
MessageMerger merger = new();
await foreach (AgentResponseUpdate update in workflowSession.InvokeStageAsync(cancellationToken)
.ConfigureAwait(false)
.WithCancellation(cancellationToken))
.ConfigureAwait(false)
.WithCancellation(cancellationToken))
{
merger.AddUpdate(update);
}
return merger.ComputeMerged(workflowSession.LastResponseId!, this.Id, this.Name);
AgentResponse response = merger.ComputeMerged(workflowSession.LastResponseId!, this.Id, this.Name);
workflowSession.ChatHistoryProvider.AddMessages(workflowSession, response.Messages);
workflowSession.ChatHistoryProvider.UpdateBookmark(workflowSession);
return response;
}
protected override async
@@ -138,11 +142,18 @@ internal sealed class WorkflowHostAgent : AIAgent
await this.ValidateWorkflowAsync().ConfigureAwait(false);
WorkflowSession workflowSession = await this.UpdateSessionAsync(messages, session, cancellationToken).ConfigureAwait(false);
MessageMerger merger = new();
await foreach (AgentResponseUpdate update in workflowSession.InvokeStageAsync(cancellationToken)
.ConfigureAwait(false)
.WithCancellation(cancellationToken))
{
merger.AddUpdate(update);
yield return update;
}
AgentResponse response = merger.ComputeMerged(workflowSession.LastResponseId!, this.Id, this.Name);
workflowSession.ChatHistoryProvider.AddMessages(workflowSession, response.Messages);
workflowSession.ChatHistoryProvider.UpdateBookmark(workflowSession);
}
}
@@ -206,7 +206,7 @@ internal sealed class TurnTrackingStartExecutor : ChatProtocolExecutor
}
}
public class WorkflowHostSmokeTests
public class WorkflowHostSmokeTests : AIAgentHostingExecutorTestsBase
{
private sealed class AlwaysFailsAIAgent(bool failByThrowing) : AIAgent
{
@@ -731,4 +731,26 @@ public class WorkflowHostSmokeTests
.Should()
.BeEmpty();
}
[Fact]
public async Task Test_AsAgent_OutgoingMessagesInHistoryAsync()
{
// Arrange
TestReplayAgent agent = new(TestMessages, TestAgentId, TestAgentName);
Workflow handoffWorkflow = new HandoffsWorkflowBuilder(agent).Build();
AIAgent workflowAgent = handoffWorkflow.AsAIAgent();
// Act
AgentSession session = await workflowAgent.CreateSessionAsync();
AgentResponse response = await workflowAgent.RunAsync(session);
// Assert
WorkflowSession workflowSession = session.Should().BeOfType<WorkflowSession>().Subject;
ChatMessage[] responseMessages = response.Messages.ToArray();
ChatMessage[] sessionMessages = workflowSession.ChatHistoryProvider.GetAllMessages(workflowSession).ToArray();
// Since we never sent an incoming message, the expectation is that there should be nothing in the session
responseMessages.Should().BeEquivalentTo(sessionMessages);
}
}