fix: preserve handoff final authorship

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-25 00:15:30 +01:00
co-authored by Copilot
parent a6b3b0ff83
commit f2c5b2cf1a
2 changed files with 78 additions and 0 deletions
@@ -159,6 +159,24 @@ internal static class WorkflowTranscriptProjector
}
}
if (remainingMessageCount == 1)
{
if (fallbackAgent.HasValue
&& AgentIdentityResolver.IsGenericAssistantIdentifier(message.AuthorName)
&& TryFindLastSegment(
remainingSegments,
segment => string.Equals(
AgentIdentityResolver.ResolveDisplayAuthorName(pattern, segment.AuthorName),
fallbackAgent.Value.AgentName,
StringComparison.Ordinal),
out (string MessageId, string AuthorName, string Content) fallbackMatchedSegment))
{
return fallbackMatchedSegment;
}
return remainingSegments[^1];
}
return remainingSegments.Count == remainingMessageCount
? remainingSegments[0]
: null;
@@ -182,6 +200,25 @@ internal static class WorkflowTranscriptProjector
return false;
}
private static bool TryFindLastSegment(
IReadOnlyList<(string MessageId, string AuthorName, string Content)> segments,
Func<(string MessageId, string AuthorName, string Content), bool> predicate,
out (string MessageId, string AuthorName, string Content) matchedSegment)
{
for (int index = segments.Count - 1; index >= 0; index--)
{
(string MessageId, string AuthorName, string Content) segment = segments[index];
if (predicate(segment))
{
matchedSegment = segment;
return true;
}
}
matchedSegment = default;
return false;
}
public static List<ChatMessage> SelectNewOutputMessages(
IReadOnlyList<ChatMessage> outputMessages,
IReadOnlyList<ChatMessage> inputMessages)
@@ -494,6 +494,47 @@ public sealed class CopilotWorkflowRunnerTests
Assert.Equal("Done — GoogleClient.cs now contains the requested class.", message.Content);
}
[Fact]
public void ProjectCompletedMessages_UsesFallbackAgentForSingleGenericAssistantOutputWithMultipleSegments()
{
RunTurnCommandDto command = new()
{
RequestId = "turn-1",
SessionId = "session-1",
Pattern = new PatternDefinitionDto
{
Id = "pattern-handoff",
Name = "Handoff Support Flow",
Mode = "handoff",
Availability = "available",
Agents =
[
CreateAgent(id: "agent-handoff-triage", name: "Triage"),
CreateAgent(id: "agent-handoff-runtime", name: "Runtime Specialist"),
],
},
};
IReadOnlyList<ChatMessageDto> messages = WorkflowTranscriptProjector.ProjectCompletedMessages(
command,
[
new ChatMessage(ChatRole.Assistant, "Done — GoogleClient.cs now contains the requested class with cleaned formatting.")
{
AuthorName = "assistant",
},
],
[
("msg-1", "Triage", "I'll hand this to a coding specialist."),
("msg-2", "Runtime Specialist", "Done — GoogleClient.cs now contains the requested class with draft formatting."),
],
new AgentIdentity("agent-handoff-runtime", "Runtime Specialist"));
ChatMessageDto message = Assert.Single(messages);
Assert.Equal("msg-2", message.Id);
Assert.Equal("Runtime Specialist", message.AuthorName);
Assert.Equal("Done — GoogleClient.cs now contains the requested class with cleaned formatting.", message.Content);
}
[Fact]
public void ProjectCompletedMessages_DropsBlankAssistantOutputMessages()
{