diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/Magentic/MagenticOrchestrator.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/Magentic/MagenticOrchestrator.cs index b863c44f1c..0334c48e0f 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/Magentic/MagenticOrchestrator.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/Magentic/MagenticOrchestrator.cs @@ -110,7 +110,7 @@ internal class MagenticOrchestrator(AIAgent managerAgent, List team, Ta out this._planReviewPort); } - private ValueTask SubmitPlanReviewRequestAsync(MagenticTaskContext taskContext, IWorkflowContext workflowContext) + private ValueTask SubmitPlanReviewRequestAsync(MagenticTaskContext taskContext, IWorkflowContext workflowContext, bool isStalled = false) { MagenticProgressLedger? progressLedger = taskContext.ProgressLedger; if (progressLedger?.IsStarted is not true) @@ -118,7 +118,7 @@ internal class MagenticOrchestrator(AIAgent managerAgent, List team, Ta progressLedger = null; } - MagenticPlanReviewRequest request = new(taskContext.TaskLedger!.CurrentPlan, progressLedger, taskContext.IsStalled); + MagenticPlanReviewRequest request = new(taskContext.TaskLedger!.CurrentPlan, progressLedger, isStalled); return this._planReviewPort!.PostRequestAsync(request); } @@ -162,7 +162,7 @@ internal class MagenticOrchestrator(AIAgent managerAgent, List team, Ta } } - private async ValueTask UpdatePlanAndDelegateAsync(MagenticTaskContext taskContext, IWorkflowContext context, CancellationToken cancellationToken) + private async ValueTask UpdatePlanAndDelegateAsync(MagenticTaskContext taskContext, IWorkflowContext context, CancellationToken cancellationToken, bool isStalled = false) { bool isReplan = taskContext.TaskLedger != null; @@ -178,7 +178,7 @@ internal class MagenticOrchestrator(AIAgent managerAgent, List team, Ta if (requirePlanSignoff) { - await this.SubmitPlanReviewRequestAsync(taskContext, context).ConfigureAwait(false); + await this.SubmitPlanReviewRequestAsync(taskContext, context, isStalled).ConfigureAwait(false); } else { @@ -289,10 +289,11 @@ internal class MagenticOrchestrator(AIAgent managerAgent, List team, Ta private async ValueTask ResetAndReplanAsync(MagenticTaskContext taskContext, IWorkflowContext context, CancellationToken cancellationToken) { + bool wasStalled = taskContext.IsStalled; taskContext.Reset(); await context.SendMessageAsync(new ResetChatSignal(), cancellationToken: cancellationToken).ConfigureAwait(false); - await this.UpdatePlanAndDelegateAsync(taskContext, context, cancellationToken).ConfigureAwait(false); + await this.UpdatePlanAndDelegateAsync(taskContext, context, cancellationToken, isStalled: wasStalled).ConfigureAwait(false); } private async ValueTask PrepareFinalAnswerAsync(MagenticTaskContext taskContext, IWorkflowContext context, CancellationToken cancellationToken) diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/MagenticOrchestrationTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/MagenticOrchestrationTests.cs index db3eec5b59..1632fc6059 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/MagenticOrchestrationTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/MagenticOrchestrationTests.cs @@ -641,6 +641,7 @@ public class MagenticOrchestrationTests MagenticPlanReviewRequest? reviewRequest1 = request1.Data.As(); reviewRequest1.Should().NotBeNull(); reviewRequest1!.Plan.Text.Should().Contain("Initial plan"); + reviewRequest1.IsStalled.Should().BeFalse("the initial plan review is not stall-triggered"); // Act 2: Approve initial plan → stall occurs → reset → replan → new plan review MagenticPlanReviewResponse approval1 = reviewRequest1.Approve(); @@ -658,6 +659,7 @@ public class MagenticOrchestrationTests MagenticPlanReviewRequest? reviewRequest2 = request2.Data.As(); reviewRequest2.Should().NotBeNull(); reviewRequest2!.Plan.Text.Should().Contain("Fresh plan after stall reset"); + reviewRequest2.IsStalled.Should().BeTrue("the replan was triggered by a stall"); // Act 3: Approve the revised plan → satisfied → final answer MagenticPlanReviewResponse approval2 = reviewRequest2.Approve();