mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-27 13:23:57 +02:00
fix: emit completed activity for sequential agents
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -54,6 +54,11 @@ internal sealed class CopilotTurnExecutionState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void QueueCompletedActivity(AgentIdentity agent)
|
||||||
|
{
|
||||||
|
_pendingEvents.Enqueue(CreateCompletedActivity(agent));
|
||||||
|
}
|
||||||
|
|
||||||
public void ApplyEvent(SidecarEventDto evt)
|
public void ApplyEvent(SidecarEventDto evt)
|
||||||
{
|
{
|
||||||
if (evt is AgentActivityEventDto activity
|
if (evt is AgentActivityEventDto activity
|
||||||
@@ -310,6 +315,19 @@ internal sealed class CopilotTurnExecutionState
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AgentActivityEventDto CreateCompletedActivity(AgentIdentity agent)
|
||||||
|
{
|
||||||
|
return new AgentActivityEventDto
|
||||||
|
{
|
||||||
|
Type = "agent-activity",
|
||||||
|
RequestId = _command.RequestId,
|
||||||
|
SessionId = _command.SessionId,
|
||||||
|
ActivityType = "completed",
|
||||||
|
AgentId = agent.AgentId,
|
||||||
|
AgentName = agent.AgentName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private MessageReclassifiedEventDto CreateMessageReclassifiedEvent(string messageId)
|
private MessageReclassifiedEventDto CreateMessageReclassifiedEvent(string messageId)
|
||||||
{
|
{
|
||||||
return new MessageReclassifiedEventDto
|
return new MessageReclassifiedEventDto
|
||||||
|
|||||||
@@ -375,6 +375,7 @@ public sealed class CopilotWorkflowRunner : ITurnWorkflowRunner
|
|||||||
out AgentIdentity completedAgent))
|
out AgentIdentity completedAgent))
|
||||||
{
|
{
|
||||||
TraceHandoff(command, $"Executor completed: {completed.ExecutorId} -> {completedAgent.AgentName} ({completedAgent.AgentId}).");
|
TraceHandoff(command, $"Executor completed: {completed.ExecutorId} -> {completedAgent.AgentName} ({completedAgent.AgentId}).");
|
||||||
|
state.QueueCompletedActivity(completedAgent);
|
||||||
state.ClearActiveAgentIfMatching(completedAgent);
|
state.ClearActiveAgentIfMatching(completedAgent);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -94,6 +94,22 @@ public sealed class CopilotTurnExecutionStateTests
|
|||||||
Assert.Equal("handoff_to_specialist", toolName);
|
Assert.Equal("handoff_to_specialist", toolName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void QueueCompletedActivity_QueuesCompletedAgentActivity()
|
||||||
|
{
|
||||||
|
RunTurnCommandDto command = CreateCommand();
|
||||||
|
CopilotTurnExecutionState state = new(command);
|
||||||
|
|
||||||
|
state.QueueCompletedActivity(new AgentIdentity("agent-1", "Primary"));
|
||||||
|
|
||||||
|
AgentActivityEventDto activity = Assert.Single(state.DrainPendingEvents().OfType<AgentActivityEventDto>());
|
||||||
|
Assert.Equal("completed", activity.ActivityType);
|
||||||
|
Assert.Equal("agent-1", activity.AgentId);
|
||||||
|
Assert.Equal("Primary", activity.AgentName);
|
||||||
|
Assert.Equal(command.RequestId, activity.RequestId);
|
||||||
|
Assert.Equal(command.SessionId, activity.SessionId);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ObserveSessionEvent_AssistantMessageWithToolRequests_QueuesMessageReclassifiedEvent()
|
public void ObserveSessionEvent_AssistantMessageWithToolRequests_QueuesMessageReclassifiedEvent()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -945,6 +945,54 @@ public sealed class CopilotWorkflowRunnerTests
|
|||||||
Assert.Equal("InvalidOperationException", diagnostic.ExceptionType);
|
Assert.Equal("InvalidOperationException", diagnostic.ExceptionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task HandleWorkflowEventAsync_QueuesCompletedActivityForCompletedExecutor()
|
||||||
|
{
|
||||||
|
RunTurnCommandDto command = CreateApprovalCommand();
|
||||||
|
CopilotTurnExecutionState state = new(command);
|
||||||
|
WorkflowNodeDto primaryAgent = command.Workflow.GetAgentNodes()[0];
|
||||||
|
|
||||||
|
state.ObserveSessionEvent(
|
||||||
|
primaryAgent,
|
||||||
|
SessionEvent.FromJson(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"type": "assistant.message_delta",
|
||||||
|
"data": {
|
||||||
|
"messageId": "msg-1",
|
||||||
|
"deltaContent": "Working"
|
||||||
|
},
|
||||||
|
"id": "11111111-1111-1111-1111-111111111111",
|
||||||
|
"timestamp": "2026-03-27T00:00:00Z"
|
||||||
|
}
|
||||||
|
"""));
|
||||||
|
_ = state.DrainPendingEvents();
|
||||||
|
|
||||||
|
MethodInfo handleWorkflowEvent = typeof(CopilotWorkflowRunner).GetMethod(
|
||||||
|
"HandleWorkflowEventAsync",
|
||||||
|
BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
Task<bool> handleTask = (Task<bool>)handleWorkflowEvent.Invoke(
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
command,
|
||||||
|
new ExecutorCompletedEvent("agent-1", null),
|
||||||
|
Array.Empty<ChatMessage>(),
|
||||||
|
state,
|
||||||
|
(Func<TurnDeltaEventDto, Task>)(_ => Task.CompletedTask),
|
||||||
|
(Func<SidecarEventDto, Task>)(_ => Task.CompletedTask),
|
||||||
|
])!;
|
||||||
|
|
||||||
|
bool shouldEndTurn = await handleTask;
|
||||||
|
|
||||||
|
Assert.False(shouldEndTurn);
|
||||||
|
Assert.Null(state.ActiveAgent);
|
||||||
|
|
||||||
|
AgentActivityEventDto completed = Assert.Single(state.DrainPendingEvents().OfType<AgentActivityEventDto>());
|
||||||
|
Assert.Equal("completed", completed.ActivityType);
|
||||||
|
Assert.Equal("agent-1", completed.AgentId);
|
||||||
|
Assert.Equal("Primary", completed.AgentName);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task HandleWorkflowEventAsync_EmitsWorkflowWarningDiagnostic()
|
public async Task HandleWorkflowEventAsync_EmitsWorkflowWarningDiagnostic()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user