mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-06 19:58:43 +02:00
fix: use runtime tools for approval auto-approval
- load runtime tools dynamically from Copilot CLI capabilities via tools.list - merge runtime tools with configured MCP and LSP tools in the approval catalog - keep a fallback builtin runtime tool list when capabilities are unavailable - move approval-tool pruning to the app service so dynamic tools are not dropped on load - update approval UI and docs to use the corrected runtime-tool model Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -69,6 +69,13 @@ public sealed class SidecarModelCapabilityDto
|
||||
public string? DefaultReasoningEffort { get; init; }
|
||||
}
|
||||
|
||||
public sealed class SidecarRuntimeToolDto
|
||||
{
|
||||
public string Id { get; init; } = string.Empty;
|
||||
public string Label { get; init; } = string.Empty;
|
||||
public string? Description { get; init; }
|
||||
}
|
||||
|
||||
public sealed class SidecarConnectionDiagnosticsDto
|
||||
{
|
||||
public string Status { get; init; } = "copilot-error";
|
||||
@@ -103,6 +110,7 @@ public sealed class SidecarCapabilitiesDto
|
||||
public string Runtime { get; init; } = "dotnet-maf";
|
||||
public Dictionary<string, SidecarModeCapabilityDto> Modes { get; init; } = new(StringComparer.OrdinalIgnoreCase);
|
||||
public IReadOnlyList<SidecarModelCapabilityDto> Models { get; init; } = [];
|
||||
public IReadOnlyList<SidecarRuntimeToolDto> RuntimeTools { get; init; } = [];
|
||||
public SidecarConnectionDiagnosticsDto Connection { get; init; } = new();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Concurrent;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using GitHub.Copilot.SDK;
|
||||
using GitHub.Copilot.SDK.Rpc;
|
||||
using Eryx.AgentHost.Contracts;
|
||||
|
||||
namespace Eryx.AgentHost.Services;
|
||||
@@ -187,6 +188,7 @@ public sealed class SidecarProtocolHost
|
||||
private static async Task<SidecarCapabilitiesDto> BuildCapabilitiesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
IReadOnlyList<SidecarModelCapabilityDto> models = [];
|
||||
IReadOnlyList<SidecarRuntimeToolDto> runtimeTools = [];
|
||||
CopilotCliContext cliContext;
|
||||
SidecarConnectionDiagnosticsDto connection;
|
||||
SidecarCopilotCliVersionDiagnosticsDto? cliVersion = null;
|
||||
@@ -227,6 +229,14 @@ public sealed class SidecarProtocolHost
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
models = await ListAvailableModelsAsync(client, cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
runtimeTools = await ListAvailableRuntimeToolsAsync(client, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.Error.WriteLine($"[eryx sidecar] Failed to list available Copilot runtime tools: {exception.Message}");
|
||||
}
|
||||
cliVersion = await cliVersionTask.ConfigureAwait(false);
|
||||
connection = CreateReadyConnectionDiagnostics(cliContext.CliPath, models.Count, cliVersion, account);
|
||||
}
|
||||
@@ -241,6 +251,7 @@ public sealed class SidecarProtocolHost
|
||||
{
|
||||
Modes = BuildModeCapabilities(),
|
||||
Models = models,
|
||||
RuntimeTools = runtimeTools,
|
||||
Connection = connection,
|
||||
};
|
||||
}
|
||||
@@ -284,6 +295,24 @@ public sealed class SidecarProtocolHost
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static async Task<IReadOnlyList<SidecarRuntimeToolDto>> ListAvailableRuntimeToolsAsync(
|
||||
CopilotClient client,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ToolsListResult result = await client.Rpc.Tools.ListAsync(null!, cancellationToken).ConfigureAwait(false);
|
||||
return result.Tools
|
||||
.Where(tool => !string.IsNullOrWhiteSpace(tool.Name))
|
||||
.Select(tool => new SidecarRuntimeToolDto
|
||||
{
|
||||
Id = tool.Name.Trim(),
|
||||
Label = tool.Name.Trim(),
|
||||
Description = string.IsNullOrWhiteSpace(tool.Description) ? null : tool.Description.Trim(),
|
||||
})
|
||||
.DistinctBy(tool => tool.Id, StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(tool => tool.Label, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static bool IsReasoningEffort(string? value)
|
||||
{
|
||||
return value is "low" or "medium" or "high" or "xhigh";
|
||||
|
||||
@@ -37,6 +37,10 @@ public sealed class SidecarProtocolHostTests
|
||||
JsonElement model = Assert.Single(models);
|
||||
Assert.Equal("gpt-5.4", model.GetProperty("id").GetString());
|
||||
Assert.Equal("medium", model.GetProperty("defaultReasoningEffort").GetString());
|
||||
JsonElement[] runtimeTools = capabilities.GetProperty("runtimeTools").EnumerateArray().ToArray();
|
||||
JsonElement runtimeTool = Assert.Single(runtimeTools);
|
||||
Assert.Equal("web_fetch", runtimeTool.GetProperty("id").GetString());
|
||||
Assert.Equal("web_fetch", runtimeTool.GetProperty("label").GetString());
|
||||
JsonElement connection = capabilities.GetProperty("connection");
|
||||
Assert.Equal("ready", connection.GetProperty("status").GetString());
|
||||
Assert.Equal(@"C:\tools\copilot\copilot.exe", connection.GetProperty("copilotCliPath").GetString());
|
||||
@@ -403,6 +407,15 @@ public sealed class SidecarProtocolHostTests
|
||||
DefaultReasoningEffort = "medium",
|
||||
},
|
||||
],
|
||||
RuntimeTools =
|
||||
[
|
||||
new SidecarRuntimeToolDto
|
||||
{
|
||||
Id = "web_fetch",
|
||||
Label = "web_fetch",
|
||||
Description = "Fetch content from the web.",
|
||||
},
|
||||
],
|
||||
Connection = new SidecarConnectionDiagnosticsDto
|
||||
{
|
||||
Status = "ready",
|
||||
|
||||
Reference in New Issue
Block a user