diff --git a/sidecar/src/Aryx.AgentHost/Services/CopilotAgentBundle.cs b/sidecar/src/Aryx.AgentHost/Services/CopilotAgentBundle.cs index 083dc7d..8a61dea 100644 --- a/sidecar/src/Aryx.AgentHost/Services/CopilotAgentBundle.cs +++ b/sidecar/src/Aryx.AgentHost/Services/CopilotAgentBundle.cs @@ -45,32 +45,13 @@ internal sealed class CopilotAgentBundle : IAsyncDisposable CopilotClient client = new(clientOptions); await client.StartAsync(cancellationToken).ConfigureAwait(false); - SessionConfig sessionConfig = new() - { - SessionId = CopilotManagedSessionIds.Build(command.SessionId, definition.Id), - Model = definition.Model, - ReasoningEffort = definition.ReasoningEffort, - SystemMessage = new SystemMessageConfig - { - Content = AgentInstructionComposer.Compose( - command.Pattern, - definition, - agentIndex, - command.WorkspaceKind, - command.Mode), - }, - WorkingDirectory = command.ProjectPath, - OnPermissionRequest = (request, invocation) => onPermissionRequest(definition, request, invocation), - OnUserInputRequest = (request, invocation) => onUserInputRequest(definition, request, invocation), - Hooks = CopilotSessionHooks.Create(command, definition), - OnEvent = evt => onSessionEvent?.Invoke(definition, evt), - Streaming = true, - CustomAgents = CreateCustomAgents(definition.Copilot?.CustomAgents), - Agent = NormalizeOptionalString(definition.Copilot?.Agent), - SkillDirectories = CreateStringList(definition.Copilot?.SkillDirectories), - DisabledSkills = CreateStringList(definition.Copilot?.DisabledSkills), - InfiniteSessions = CreateInfiniteSessions(definition.Copilot?.InfiniteSessions), - }; + SessionConfig sessionConfig = CreateSessionConfig( + command, + definition, + agentIndex, + (request, invocation) => onPermissionRequest(definition, request, invocation), + (request, invocation) => onUserInputRequest(definition, request, invocation), + evt => onSessionEvent?.Invoke(definition, evt)); ApplySessionTooling(sessionConfig, toolingBundle?.McpServers, toolingBundle?.Tools); @@ -91,6 +72,43 @@ internal sealed class CopilotAgentBundle : IAsyncDisposable return bundle; } + internal static SessionConfig CreateSessionConfig( + RunTurnCommandDto command, + PatternAgentDefinitionDto definition, + int agentIndex, + PermissionRequestHandler? onPermissionRequest = null, + UserInputHandler? onUserInputRequest = null, + SessionEventHandler? onSessionEvent = null) + { + // Let the Copilot SDK allocate session IDs. Explicit custom SessionId values currently + // cause turns to complete without assistant output, even for simple single-agent prompts. + return new SessionConfig + { + Model = definition.Model, + ReasoningEffort = definition.ReasoningEffort, + SystemMessage = new SystemMessageConfig + { + Content = AgentInstructionComposer.Compose( + command.Pattern, + definition, + agentIndex, + command.WorkspaceKind, + command.Mode), + }, + WorkingDirectory = command.ProjectPath, + OnPermissionRequest = onPermissionRequest, + OnUserInputRequest = onUserInputRequest, + Hooks = CopilotSessionHooks.Create(command, definition), + OnEvent = onSessionEvent, + Streaming = true, + CustomAgents = CreateCustomAgents(definition.Copilot?.CustomAgents), + Agent = NormalizeOptionalString(definition.Copilot?.Agent), + SkillDirectories = CreateStringList(definition.Copilot?.SkillDirectories), + DisabledSkills = CreateStringList(definition.Copilot?.DisabledSkills), + InfiniteSessions = CreateInfiniteSessions(definition.Copilot?.InfiniteSessions), + }; + } + internal static void ApplySessionTooling( SessionConfig sessionConfig, Dictionary? mcpServers, diff --git a/sidecar/tests/Aryx.AgentHost.Tests/CopilotAgentBundleTests.cs b/sidecar/tests/Aryx.AgentHost.Tests/CopilotAgentBundleTests.cs index 8f20c54..0406d07 100644 --- a/sidecar/tests/Aryx.AgentHost.Tests/CopilotAgentBundleTests.cs +++ b/sidecar/tests/Aryx.AgentHost.Tests/CopilotAgentBundleTests.cs @@ -202,6 +202,45 @@ public sealed class CopilotAgentBundleTests Assert.Equal(0.9, config.BufferExhaustionThreshold); } + [Fact] + public void CreateSessionConfig_DoesNotForceSessionId() + { + RunTurnCommandDto command = new() + { + SessionId = "session-1", + ProjectPath = @"C:\workspace\project", + WorkspaceKind = "project", + Mode = "interactive", + Pattern = new PatternDefinitionDto + { + Id = "pattern-1", + Name = "Pattern", + Mode = "single", + Availability = "available", + Agents = + [ + new PatternAgentDefinitionDto + { + Id = "agent-1", + Name = "Primary", + Model = "gpt-5.4", + Instructions = "Help.", + }, + ], + }, + }; + + SessionConfig sessionConfig = CopilotAgentBundle.CreateSessionConfig( + command, + command.Pattern.Agents[0], + agentIndex: 0); + + Assert.Null(sessionConfig.SessionId); + Assert.Equal(@"C:\workspace\project", sessionConfig.WorkingDirectory); + Assert.True(sessionConfig.Streaming); + Assert.NotNull(sessionConfig.Hooks); + } + [Fact] public async Task CopilotSessionHooks_Create_UsesApprovalPolicyForPreToolUse() {