mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-25 20:33:58 +02:00
fix: always use system copilot cli
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="GitHub.Copilot.SDK" Version="0.2.0" />
|
||||||
<PackageReference Include="Microsoft.Agents.AI" Version="1.0.0-rc4" />
|
<PackageReference Include="Microsoft.Agents.AI" Version="1.0.0-rc4" />
|
||||||
<PackageReference Include="Microsoft.Agents.AI.GitHub.Copilot" Version="1.0.0-preview.260311.1" />
|
<PackageReference Include="Microsoft.Agents.AI.GitHub.Copilot" Version="1.0.0-preview.260311.1" />
|
||||||
<PackageReference Include="Microsoft.Agents.AI.Workflows" Version="1.0.0-rc4" />
|
<PackageReference Include="Microsoft.Agents.AI.Workflows" Version="1.0.0-rc4" />
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
using GitHub.Copilot.SDK;
|
using GitHub.Copilot.SDK;
|
||||||
|
|
||||||
namespace Kopaya.AgentHost.Services;
|
namespace Kopaya.AgentHost.Services;
|
||||||
@@ -8,62 +7,34 @@ internal static class CopilotCliPathResolver
|
|||||||
private const string CopilotCommandName = "copilot";
|
private const string CopilotCommandName = "copilot";
|
||||||
private const string DefaultWindowsPathExtensions = ".COM;.EXE;.BAT;.CMD";
|
private const string DefaultWindowsPathExtensions = ".COM;.EXE;.BAT;.CMD";
|
||||||
|
|
||||||
public static CopilotClientOptions? CreateClientOptions()
|
public static CopilotClientOptions CreateClientOptions()
|
||||||
{
|
{
|
||||||
CopilotCliResolution resolution = Resolve(
|
string? cliPath = Resolve(
|
||||||
Environment.ProcessPath,
|
|
||||||
Environment.GetEnvironmentVariable("PATH"),
|
Environment.GetEnvironmentVariable("PATH"),
|
||||||
Environment.GetEnvironmentVariable("PATHEXT"),
|
Environment.GetEnvironmentVariable("PATHEXT"),
|
||||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows),
|
OperatingSystem.IsWindows(),
|
||||||
File.Exists);
|
File.Exists);
|
||||||
|
|
||||||
if (!resolution.ShouldOverrideCliPath)
|
if (string.IsNullOrWhiteSpace(cliPath))
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(resolution.CliPath))
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
"Development sidecar could not find the system-installed 'copilot' command on PATH. Install the GitHub Copilot CLI or provide an explicit CliPath.");
|
"Kopaya requires the system-installed 'copilot' command on PATH. Install the GitHub Copilot CLI and ensure it is available in the current environment.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CopilotClientOptions
|
return new CopilotClientOptions
|
||||||
{
|
{
|
||||||
CliPath = resolution.CliPath,
|
CliPath = cliPath,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static CopilotCliResolution Resolve(
|
internal static string? Resolve(
|
||||||
string? processPath,
|
|
||||||
string? pathValue,
|
string? pathValue,
|
||||||
string? pathExtValue,
|
string? pathExtValue,
|
||||||
bool isWindows,
|
bool isWindows,
|
||||||
Func<string, bool> fileExists)
|
Func<string, bool> fileExists)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(fileExists);
|
ArgumentNullException.ThrowIfNull(fileExists);
|
||||||
|
return ResolveCliPath(pathValue, pathExtValue, isWindows, fileExists);
|
||||||
if (!IsDevelopmentHost(processPath))
|
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CopilotCliResolution(
|
|
||||||
ShouldOverrideCliPath: true,
|
|
||||||
CliPath: ResolveCliPath(pathValue, pathExtValue, isWindows, fileExists));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsDevelopmentHost(string? processPath)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(processPath))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Equals(
|
|
||||||
Path.GetFileNameWithoutExtension(processPath),
|
|
||||||
"dotnet",
|
|
||||||
StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string? ResolveCliPath(
|
private static string? ResolveCliPath(
|
||||||
@@ -128,5 +99,3 @@ internal static class CopilotCliPathResolver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly record struct CopilotCliResolution(bool ShouldOverrideCliPath, string? CliPath);
|
|
||||||
|
|||||||
@@ -170,11 +170,11 @@ public sealed class CopilotWorkflowRunner
|
|||||||
{
|
{
|
||||||
List<IAsyncDisposable> disposables = [];
|
List<IAsyncDisposable> disposables = [];
|
||||||
List<AIAgent> agents = [];
|
List<AIAgent> agents = [];
|
||||||
CopilotClientOptions? clientOptions = CopilotCliPathResolver.CreateClientOptions();
|
CopilotClientOptions clientOptions = CopilotCliPathResolver.CreateClientOptions();
|
||||||
|
|
||||||
foreach (PatternAgentDefinitionDto definition in pattern.Agents)
|
foreach (PatternAgentDefinitionDto definition in pattern.Agents)
|
||||||
{
|
{
|
||||||
CopilotClient client = clientOptions is null ? new() : new(clientOptions);
|
CopilotClient client = new(clientOptions);
|
||||||
await client.StartAsync(cancellationToken).ConfigureAwait(false);
|
await client.StartAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
SessionConfig sessionConfig = new()
|
SessionConfig sessionConfig = new()
|
||||||
@@ -263,7 +263,7 @@ public sealed class CopilotWorkflowRunner
|
|||||||
{
|
{
|
||||||
return Task.FromResult(new PermissionRequestResult
|
return Task.FromResult(new PermissionRequestResult
|
||||||
{
|
{
|
||||||
Kind = "approved",
|
Kind = PermissionRequestResultKind.Approved,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Kopaya.AgentHost.Tests;
|
|||||||
public sealed class CopilotCliPathResolverTests
|
public sealed class CopilotCliPathResolverTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Resolve_UsesCopilotFromPathDuringDevelopment()
|
public void Resolve_UsesCopilotFromPath()
|
||||||
{
|
{
|
||||||
string copilotDirectory = @"C:\tools\copilot";
|
string copilotDirectory = @"C:\tools\copilot";
|
||||||
HashSet<string> existingFiles = new(StringComparer.OrdinalIgnoreCase)
|
HashSet<string> existingFiles = new(StringComparer.OrdinalIgnoreCase)
|
||||||
@@ -13,42 +13,42 @@ public sealed class CopilotCliPathResolverTests
|
|||||||
Path.Combine(copilotDirectory, "copilot.exe"),
|
Path.Combine(copilotDirectory, "copilot.exe"),
|
||||||
};
|
};
|
||||||
|
|
||||||
CopilotCliResolution resolution = CopilotCliPathResolver.Resolve(
|
string? cliPath = CopilotCliPathResolver.Resolve(
|
||||||
processPath: @"C:\Program Files\dotnet\dotnet.exe",
|
|
||||||
pathValue: $"C:\\other;\"{copilotDirectory}\"",
|
pathValue: $"C:\\other;\"{copilotDirectory}\"",
|
||||||
pathExtValue: ".COM;.EXE;.BAT;.CMD",
|
pathExtValue: ".COM;.EXE;.BAT;.CMD",
|
||||||
isWindows: true,
|
isWindows: true,
|
||||||
fileExists: existingFiles.Contains);
|
fileExists: existingFiles.Contains);
|
||||||
|
|
||||||
Assert.True(resolution.ShouldOverrideCliPath);
|
Assert.Equal(Path.Combine(copilotDirectory, "copilot.exe"), cliPath, ignoreCase: true);
|
||||||
Assert.Equal(Path.Combine(copilotDirectory, "copilot.exe"), resolution.CliPath, ignoreCase: true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Resolve_LeavesPackagedRuntimeOnBundledCli()
|
public void Resolve_UsesDefaultWindowsExtensionsWhenPathExtMissing()
|
||||||
{
|
{
|
||||||
CopilotCliResolution resolution = CopilotCliPathResolver.Resolve(
|
string copilotDirectory = @"C:\tools\copilot";
|
||||||
processPath: @"C:\Program Files\Kopaya\Kopaya.AgentHost.exe",
|
HashSet<string> existingFiles = new(StringComparer.OrdinalIgnoreCase)
|
||||||
pathValue: @"C:\tools",
|
{
|
||||||
pathExtValue: ".COM;.EXE;.BAT;.CMD",
|
Path.Combine(copilotDirectory, "copilot.cmd"),
|
||||||
|
};
|
||||||
|
|
||||||
|
string? cliPath = CopilotCliPathResolver.Resolve(
|
||||||
|
pathValue: $"C:\\other;\"{copilotDirectory}\"",
|
||||||
|
pathExtValue: null,
|
||||||
isWindows: true,
|
isWindows: true,
|
||||||
fileExists: _ => true);
|
fileExists: existingFiles.Contains);
|
||||||
|
|
||||||
Assert.False(resolution.ShouldOverrideCliPath);
|
Assert.Equal(Path.Combine(copilotDirectory, "copilot.cmd"), cliPath, ignoreCase: true);
|
||||||
Assert.Null(resolution.CliPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Resolve_ReportsMissingCopilotWhenDevelopmentPathDoesNotContainIt()
|
public void Resolve_ReturnsNullWhenPathDoesNotContainCopilot()
|
||||||
{
|
{
|
||||||
CopilotCliResolution resolution = CopilotCliPathResolver.Resolve(
|
string? cliPath = CopilotCliPathResolver.Resolve(
|
||||||
processPath: @"C:\Program Files\dotnet\dotnet.exe",
|
|
||||||
pathValue: @"C:\tools;C:\other",
|
pathValue: @"C:\tools;C:\other",
|
||||||
pathExtValue: ".COM;.EXE;.BAT;.CMD",
|
pathExtValue: ".COM;.EXE;.BAT;.CMD",
|
||||||
isWindows: true,
|
isWindows: true,
|
||||||
fileExists: _ => false);
|
fileExists: _ => false);
|
||||||
|
|
||||||
Assert.True(resolution.ShouldOverrideCliPath);
|
Assert.Null(cliPath);
|
||||||
Assert.Null(resolution.CliPath);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user