mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-09 05:08:44 +02:00
fix: auto-allow infrastructure tool hooks
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -7,9 +7,19 @@ namespace Aryx.AgentHost.Services;
|
|||||||
|
|
||||||
internal static class CopilotSessionHooks
|
internal static class CopilotSessionHooks
|
||||||
{
|
{
|
||||||
|
private const string AskUserToolName = "ask_user";
|
||||||
private const string AllowDecision = "allow";
|
private const string AllowDecision = "allow";
|
||||||
private const string AskDecision = "ask";
|
private const string AskDecision = "ask";
|
||||||
private const string DenyDecision = "deny";
|
private const string DenyDecision = "deny";
|
||||||
|
private const string HandoffToolPrefix = "handoff_to_";
|
||||||
|
private const string ReportIntentToolName = "report_intent";
|
||||||
|
private const string TaskCompleteToolName = "task_complete";
|
||||||
|
private static readonly HashSet<string> AlwaysAllowedToolNames = new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
AskUserToolName,
|
||||||
|
ReportIntentToolName,
|
||||||
|
TaskCompleteToolName,
|
||||||
|
};
|
||||||
private static readonly JsonSerializerOptions HookJsonOptions = new(JsonSerializerDefaults.Web)
|
private static readonly JsonSerializerOptions HookJsonOptions = new(JsonSerializerDefaults.Web)
|
||||||
{
|
{
|
||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
@@ -216,11 +226,20 @@ internal static class CopilotSessionHooks
|
|||||||
PatternAgentDefinitionDto agentDefinition,
|
PatternAgentDefinitionDto agentDefinition,
|
||||||
PreToolUseHookInput input)
|
PreToolUseHookInput input)
|
||||||
{
|
{
|
||||||
|
string? toolName = Normalize(input.ToolName);
|
||||||
|
if (IsAlwaysAllowedTool(toolName))
|
||||||
|
{
|
||||||
|
return new PreToolUseHookOutput
|
||||||
|
{
|
||||||
|
PermissionDecision = AllowDecision,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
bool requiresApproval = CopilotApprovalCoordinator.RequiresToolCallApproval(
|
bool requiresApproval = CopilotApprovalCoordinator.RequiresToolCallApproval(
|
||||||
command.Pattern.ApprovalPolicy,
|
command.Pattern.ApprovalPolicy,
|
||||||
agentDefinition.Id,
|
agentDefinition.Id,
|
||||||
Normalize(input.ToolName),
|
toolName,
|
||||||
Normalize(input.ToolName));
|
toolName);
|
||||||
|
|
||||||
return new PreToolUseHookOutput
|
return new PreToolUseHookOutput
|
||||||
{
|
{
|
||||||
@@ -262,6 +281,14 @@ internal static class CopilotSessionHooks
|
|||||||
private static string SerializeHookValue(object? value)
|
private static string SerializeHookValue(object? value)
|
||||||
=> JsonSerializer.Serialize(value, HookJsonOptions);
|
=> JsonSerializer.Serialize(value, HookJsonOptions);
|
||||||
|
|
||||||
|
private static bool IsAlwaysAllowedTool(string? toolName)
|
||||||
|
{
|
||||||
|
string? normalizedToolName = Normalize(toolName);
|
||||||
|
return normalizedToolName is not null
|
||||||
|
&& (AlwaysAllowedToolNames.Contains(normalizedToolName)
|
||||||
|
|| normalizedToolName.StartsWith(HandoffToolPrefix, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
private static string? Normalize(string? value)
|
private static string? Normalize(string? value)
|
||||||
=> string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
=> string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,27 @@ public sealed class CopilotSessionHooksTests
|
|||||||
Assert.Single(runner.Invocations);
|
Assert.Single(runner.Invocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("ask_user")]
|
||||||
|
[InlineData("report_intent")]
|
||||||
|
[InlineData("task_complete")]
|
||||||
|
[InlineData("handoff_to_2")]
|
||||||
|
[InlineData("handoff_to_specialist")]
|
||||||
|
public async Task Create_PreToolUseAutoAllowsInfrastructureTools(string toolName)
|
||||||
|
{
|
||||||
|
RunTurnCommandDto command = CreateCommandWithToolApproval();
|
||||||
|
SessionHooks hooks = CopilotSessionHooks.Create(command, command.Pattern.Agents[0], ResolvedHookSet.Empty, new RecordingHookCommandRunner());
|
||||||
|
|
||||||
|
PreToolUseHookOutput? decision = await hooks.OnPreToolUse!(
|
||||||
|
new PreToolUseHookInput
|
||||||
|
{
|
||||||
|
ToolName = toolName,
|
||||||
|
},
|
||||||
|
null!);
|
||||||
|
|
||||||
|
Assert.Equal("allow", decision?.PermissionDecision);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Create_RunsConfiguredNonPreToolHooks()
|
public async Task Create_RunsConfiguredNonPreToolHooks()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user