diff --git a/sidecar/src/Aryx.AgentHost/Services/CopilotWorkflowRunner.cs b/sidecar/src/Aryx.AgentHost/Services/CopilotWorkflowRunner.cs index 9f81852..87c3e33 100644 --- a/sidecar/src/Aryx.AgentHost/Services/CopilotWorkflowRunner.cs +++ b/sidecar/src/Aryx.AgentHost/Services/CopilotWorkflowRunner.cs @@ -353,6 +353,14 @@ public sealed class CopilotWorkflowRunner : ITurnWorkflowRunner updateAgent = resolvedUpdateAgent; authorName = resolvedUpdateAgent.AgentName; } + else if (state.ActiveAgent is AgentIdentity activeAgent) + { + updateAgent = activeAgent; + authorName = activeAgent.AgentName; + TraceHandoff( + command, + $"Agent response update fell back to active agent {activeAgent.AgentName} ({activeAgent.AgentId}) for executor '{update.ExecutorId}' and message '{update.Update.MessageId ?? ""}'."); + } if (updateAgent.HasValue) { diff --git a/sidecar/tests/Aryx.AgentHost.Tests/CopilotWorkflowRunnerTests.cs b/sidecar/tests/Aryx.AgentHost.Tests/CopilotWorkflowRunnerTests.cs index 3828c74..958d3be 100644 --- a/sidecar/tests/Aryx.AgentHost.Tests/CopilotWorkflowRunnerTests.cs +++ b/sidecar/tests/Aryx.AgentHost.Tests/CopilotWorkflowRunnerTests.cs @@ -799,6 +799,61 @@ public sealed class CopilotWorkflowRunnerTests }); } + [Fact] + public async Task HandleWorkflowEventAsync_FallsBackToActiveAgentForUnresolvedStreamingUpdates() + { + RunTurnCommandDto command = CreateHandoffCommand(); + CopilotTurnExecutionState state = new(command); + state.ObserveSessionEvent( + CreateAgent("agent-handoff-ux", "UX Specialist"), + SessionEvent.FromJson( + """ + { + "type": "assistant.reasoning_delta", + "data": { + "reasoningId": "reasoning-1", + "deltaContent": "Polishing the UI." + }, + "id": "77777777-7777-7777-7777-777777777777", + "timestamp": "2026-03-27T00:00:00Z" + } + """)); + _ = state.DrainPendingEvents(); + List deltas = []; + + MethodInfo handleWorkflowEvent = typeof(CopilotWorkflowRunner).GetMethod( + "HandleWorkflowEventAsync", + BindingFlags.NonPublic | BindingFlags.Static)!; + Task handleTask = (Task)handleWorkflowEvent.Invoke( + null, + [ + command, + new AgentResponseUpdateEvent( + "copilot-executor-ux", + new AgentResponseUpdate(ChatRole.Assistant, "The button is ready.") + { + MessageId = "msg-ux-1", + }), + Array.Empty(), + state, + (Func)(delta => + { + deltas.Add(delta); + return Task.CompletedTask; + }), + (Func)(_ => Task.CompletedTask), + ])!; + + bool shouldEndTurn = await handleTask; + + Assert.False(shouldEndTurn); + TurnDeltaEventDto delta = Assert.Single(deltas); + Assert.Equal("msg-ux-1", delta.MessageId); + Assert.Equal("UX Specialist", delta.AuthorName); + Assert.Equal("The button is ready.", delta.ContentDelta); + Assert.Equal("The button is ready.", delta.Content); + } + [Fact] public async Task HandleWorkflowEventAsync_EmitsWorkflowCheckpointSavedEvent() {