From 2fc03454c4d7e48d157bca8f722c39a91a22f86d Mon Sep 17 00:00:00 2001 From: David Kaya Date: Tue, 24 Mar 2026 23:51:38 +0100 Subject: [PATCH] fix: block tools for handoff triage\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Services/CopilotAgentBundle.cs | 21 ++++- .../CopilotAgentBundleTests.cs | 90 +++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 sidecar/tests/Eryx.AgentHost.Tests/CopilotAgentBundleTests.cs diff --git a/sidecar/src/Eryx.AgentHost/Services/CopilotAgentBundle.cs b/sidecar/src/Eryx.AgentHost/Services/CopilotAgentBundle.cs index b555597..8f75294 100644 --- a/sidecar/src/Eryx.AgentHost/Services/CopilotAgentBundle.cs +++ b/sidecar/src/Eryx.AgentHost/Services/CopilotAgentBundle.cs @@ -56,7 +56,7 @@ internal sealed class CopilotAgentBundle : IAsyncDisposable Streaming = true, }; - if (isScratchpad) + if (ShouldDisableSessionTools(command.Pattern, definition, command.WorkspaceKind)) { sessionConfig.AvailableTools = []; } @@ -90,6 +90,25 @@ internal sealed class CopilotAgentBundle : IAsyncDisposable return bundle; } + internal static bool ShouldDisableSessionTools( + PatternDefinitionDto pattern, + PatternAgentDefinitionDto definition, + string workspaceKind) + { + if (string.Equals(workspaceKind, "scratchpad", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + if (!string.Equals(pattern.Mode, "handoff", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + PatternHandoffTopology topology = PatternGraphResolver.ResolveHandoff(pattern); + return string.Equals(definition.Id, topology.EntryAgentId, StringComparison.OrdinalIgnoreCase); + } + public Workflow BuildWorkflow(PatternDefinitionDto pattern) { return pattern.Mode switch diff --git a/sidecar/tests/Eryx.AgentHost.Tests/CopilotAgentBundleTests.cs b/sidecar/tests/Eryx.AgentHost.Tests/CopilotAgentBundleTests.cs new file mode 100644 index 0000000..2758381 --- /dev/null +++ b/sidecar/tests/Eryx.AgentHost.Tests/CopilotAgentBundleTests.cs @@ -0,0 +1,90 @@ +using Eryx.AgentHost.Contracts; +using Eryx.AgentHost.Services; + +namespace Eryx.AgentHost.Tests; + +public sealed class CopilotAgentBundleTests +{ + [Fact] + public void ShouldDisableSessionTools_DisablesScratchpadAgents() + { + PatternDefinitionDto pattern = CreatePattern( + mode: "sequential", + CreateAgent("agent-primary", "Primary")); + + bool disabled = CopilotAgentBundle.ShouldDisableSessionTools( + pattern, + pattern.Agents[0], + workspaceKind: "scratchpad"); + + Assert.True(disabled); + } + + [Fact] + public void ShouldDisableSessionTools_DisablesOnlyHandoffEntryAgent() + { + PatternDefinitionDto pattern = CreatePattern( + mode: "handoff", + CreateAgent("agent-handoff-triage", "Triage"), + CreateAgent("agent-handoff-runtime", "Runtime Specialist")); + + bool triageDisabled = CopilotAgentBundle.ShouldDisableSessionTools( + pattern, + pattern.Agents[0], + workspaceKind: "project"); + bool specialistDisabled = CopilotAgentBundle.ShouldDisableSessionTools( + pattern, + pattern.Agents[1], + workspaceKind: "project"); + + Assert.True(triageDisabled); + Assert.False(specialistDisabled); + } + + [Fact] + public void ShouldDisableSessionTools_LeavesNonHandoffProjectAgentsEnabled() + { + PatternDefinitionDto pattern = CreatePattern( + mode: "sequential", + CreateAgent("agent-analyst", "Analyst"), + CreateAgent("agent-builder", "Builder")); + + bool disabled = CopilotAgentBundle.ShouldDisableSessionTools( + pattern, + pattern.Agents[0], + workspaceKind: "project"); + + Assert.False(disabled); + } + + private static PatternDefinitionDto CreatePattern(string mode, params PatternAgentDefinitionDto[] agents) + { + return new PatternDefinitionDto + { + Id = $"pattern-{mode}", + Name = mode, + Mode = mode, + Availability = "available", + Agents = agents, + Graph = PatternGraphResolver.CreateDefault(new PatternDefinitionDto + { + Id = $"pattern-{mode}", + Name = mode, + Mode = mode, + Availability = "available", + Agents = agents, + }), + }; + } + + private static PatternAgentDefinitionDto CreateAgent(string id, string name) + { + return new PatternAgentDefinitionDto + { + Id = id, + Name = name, + Instructions = $"You are {name}.", + Model = "gpt-5.4", + }; + } +}