refactor: pin workflow host options explicitly

Explicitly configure AIAgentHostOptions for the workflow modes that
host agents directly instead of relying on Agent Framework defaults.

- add a shared host-options factory that preserves Aryx's current
  behavior
- use custom sequential, concurrent, and round-robin group-chat
  builders so all host options are set intentionally
- add workflow-level tests asserting the configured host options

Keep EmitAgentResponseEvents disabled because Aryx still projects
streaming transcript state itself and enabling response events would
require a separate reconciliation change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-04-01 19:04:44 +02:00
co-authored by Copilot
parent d7004ec2a9
commit 235ddf7e56
3 changed files with 323 additions and 11 deletions
@@ -130,6 +130,36 @@ public sealed class CopilotAgentBundleTests
Assert.Equal(HandoffWorkflowGuidance.CreateWorkflowInstructions(), builder.HandoffInstructions);
}
[Theory]
[InlineData("single", 1)]
[InlineData("sequential", 2)]
[InlineData("concurrent", 2)]
[InlineData("group-chat", 2)]
public void BuildWorkflow_ExplicitlyConfiguresAgentHostOptions(string mode, int agentCount)
{
CopilotAgentBundle bundle = new(CreateAgents(agentCount), hasConfiguredHooks: false);
PatternDefinitionDto pattern = CreatePattern(mode, agentCount);
Workflow workflow = bundle.BuildWorkflow(pattern);
AIAgentBinding[] bindings = workflow.ReflectExecutors().Values
.OfType<AIAgentBinding>()
.ToArray();
Assert.Equal(agentCount, bindings.Length);
foreach (AIAgentBinding binding in bindings)
{
AIAgentHostOptions options = Assert.IsType<AIAgentHostOptions>(binding.Options);
Assert.Null(options.EmitAgentUpdateEvents);
Assert.False(options.EmitAgentResponseEvents);
Assert.False(options.InterceptUserInputRequests);
Assert.False(options.InterceptUnterminatedFunctionCalls);
Assert.True(options.ReassignOtherAgentsAsUsers);
Assert.True(options.ForwardIncomingMessages);
}
}
[Fact]
public void ConvertToolRequestsToFunctionCalls_MapsCallIdsNamesAndArguments()
{
@@ -388,6 +418,33 @@ public sealed class CopilotAgentBundleTests
CreateTool().JsonSchema);
}
private static IReadOnlyList<AIAgent> CreateAgents(int count)
=> Enumerable.Range(1, count)
.Select(index => (AIAgent)CreateChatClientAgent($"agent-{index}", $"Agent {index}"))
.ToArray();
private static PatternDefinitionDto CreatePattern(string mode, int agentCount)
{
return new PatternDefinitionDto
{
Id = $"pattern-{mode}",
Name = $"Pattern {mode}",
Mode = mode,
Availability = "available",
Agents =
[
.. Enumerable.Range(1, agentCount).Select(index => new PatternAgentDefinitionDto
{
Id = $"agent-{index}",
Name = $"Agent {index}",
Description = $"Agent {index} description.",
Instructions = $"Agent {index} instructions.",
Model = "gpt-5.4",
}),
],
};
}
private static ChatClientAgent CreateChatClientAgent(string id, string name)
{
return new ChatClientAgent(