diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/AgentWorkflowBuilder.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/AgentWorkflowBuilder.cs
index 501c7df230..22ee1b48eb 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/AgentWorkflowBuilder.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/AgentWorkflowBuilder.cs
@@ -145,7 +145,7 @@ public static partial class AgentWorkflowBuilder
return builder.Build();
}
- /// Creates a new using as the starting agent in the workflow.
+ /// Creates a new using as the starting agent in the workflow.
/// The agent that will receive inputs provided to the workflow.
/// The builder for creating a workflow based on handoffs.
///
@@ -154,7 +154,7 @@ public static partial class AgentWorkflowBuilder
/// The must be capable of understanding those provided. If the agent
/// ignores the tools or is otherwise unable to advertize them to the underlying provider, handoffs will not occur.
///
- public static HandoffsWorkflowBuilder CreateHandoffBuilderWith(AIAgent initialAgent)
+ public static HandoffWorkflowBuilder CreateHandoffBuilderWith(AIAgent initialAgent)
{
Throw.IfNull(initialAgent);
return new(initialAgent);
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/HandoffsCurrentAgentTracker.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/HandoffsCurrentAgentTracker.cs
deleted file mode 100644
index 51e3fd9475..0000000000
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/HandoffsCurrentAgentTracker.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-namespace Microsoft.Agents.AI.Workflows.Specialized;
-
-/// Tracks the current agent ID across turns when return-to-previous routing is enabled.
-internal sealed class HandoffsCurrentAgentTracker
-{
- public string? CurrentAgentId { get; set; }
-}
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/HandoffsEndExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/HandoffsEndExecutor.cs
index c892112453..4a43c00a72 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/HandoffsEndExecutor.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/HandoffsEndExecutor.cs
@@ -8,7 +8,7 @@ using Microsoft.Extensions.AI;
namespace Microsoft.Agents.AI.Workflows.Specialized;
/// Executor used at the end of a handoff workflow to raise a final completed event.
-internal sealed class HandoffsEndExecutor(HandoffsCurrentAgentTracker? tracker = null) : Executor(ExecutorId, declareCrossRunShareable: true), IResettableExecutor
+internal sealed class HandoffsEndExecutor(bool returnToPrevious) : Executor(ExecutorId, declareCrossRunShareable: true), IResettableExecutor
{
public const string ExecutorId = "HandoffEnd";
@@ -19,9 +19,13 @@ internal sealed class HandoffsEndExecutor(HandoffsCurrentAgentTracker? tracker =
private async ValueTask HandleAsync(HandoffState handoff, IWorkflowContext context, CancellationToken cancellationToken)
{
- if (tracker is not null && handoff.CurrentAgentId is not null)
+ if (returnToPrevious)
{
- tracker.CurrentAgentId = handoff.CurrentAgentId;
+ await context.QueueStateUpdateAsync(HandoffConstants.CurrentAgentTrackerKey,
+ handoff.CurrentAgentId,
+ HandoffConstants.CurrentAgentTrackerScope,
+ cancellationToken)
+ .ConfigureAwait(false);
}
await context.YieldOutputAsync(handoff.Messages, cancellationToken).ConfigureAwait(false);
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/HandoffsStartExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/HandoffsStartExecutor.cs
index 119a0a778d..87c3b4566b 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/HandoffsStartExecutor.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/HandoffsStartExecutor.cs
@@ -7,8 +7,14 @@ using Microsoft.Extensions.AI;
namespace Microsoft.Agents.AI.Workflows.Specialized;
+internal static class HandoffConstants
+{
+ internal const string CurrentAgentTrackerKey = "LastAgentId";
+ internal const string CurrentAgentTrackerScope = "HandoffOrchestration";
+}
+
/// Executor used at the start of a handoffs workflow to accumulate messages and emit them as HandoffState upon receiving a turn token.
-internal sealed class HandoffsStartExecutor(HandoffsCurrentAgentTracker? tracker = null) : ChatProtocolExecutor(ExecutorId, DefaultOptions, declareCrossRunShareable: true), IResettableExecutor
+internal sealed class HandoffsStartExecutor(bool returnToPrevious) : ChatProtocolExecutor(ExecutorId, DefaultOptions, declareCrossRunShareable: true), IResettableExecutor
{
internal const string ExecutorId = "HandoffStart";
@@ -22,7 +28,25 @@ internal sealed class HandoffsStartExecutor(HandoffsCurrentAgentTracker? tracker
base.ConfigureProtocol(protocolBuilder).SendsMessage();
protected override ValueTask TakeTurnAsync(List messages, IWorkflowContext context, bool? emitEvents, CancellationToken cancellationToken = default)
- => context.SendMessageAsync(new HandoffState(new(emitEvents), null, messages, tracker?.CurrentAgentId), cancellationToken: cancellationToken);
+ {
+ if (returnToPrevious)
+ {
+ return context.InvokeWithStateAsync(
+ async (string? currentAgentId, IWorkflowContext context, CancellationToken cancellationToken) =>
+ {
+ HandoffState handoffState = new(new(emitEvents), null, messages, currentAgentId);
+ await context.SendMessageAsync(handoffState, cancellationToken).ConfigureAwait(false);
+
+ return currentAgentId;
+ },
+ HandoffConstants.CurrentAgentTrackerKey,
+ HandoffConstants.CurrentAgentTrackerScope,
+ cancellationToken);
+ }
+
+ HandoffState handoff = new(new(emitEvents), null, messages);
+ return context.SendMessageAsync(handoff, cancellationToken);
+ }
public new ValueTask ResetAsync() => base.ResetAsync();
}
diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/12_HandOff_HostAsAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/12_HandOff_HostAsAgent.cs
index 3d88ed22ab..993a6d462b 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/12_HandOff_HostAsAgent.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/12_HandOff_HostAsAgent.cs
@@ -20,7 +20,7 @@ internal sealed class HandoffTestEchoAgent(string id, string name, string prefix
{
IEnumerable? handoffs = chatClientOptions.ChatOptions
.Tools?
- .Where(tool => tool.Name?.StartsWith(HandoffsWorkflowBuilder.FunctionPrefix,
+ .Where(tool => tool.Name?.StartsWith(HandoffWorkflowBuilder.FunctionPrefix,
StringComparison.OrdinalIgnoreCase) is true);
if (handoffs != null)
@@ -58,7 +58,7 @@ internal static class Step12EntryPoint
.Select(i => new HandoffTestEchoAgent($"{EchoAgentIdPrefix}{i}", $"{EchoAgentNamePrefix}{i}", EchoPrefixForAgent(i)))
.ToArray();
- return new HandoffsWorkflowBuilder(echoAgents[0])
+ return new HandoffWorkflowBuilder(echoAgents[0])
.WithHandoff(echoAgents[0], echoAgents[1])
.Build();
}