| 1 | using System.Reflection; |
| 2 | using System.Runtime.Loader; |
| 3 | |
| 4 | namespace AgentForge.Plugins; |
| 5 | |
| 6 | /// <summary>Collectible ALC for one plugin assembly (FORGE-ADR-0023 HR3).</summary> |
| 7 | internal sealed class ForgePluginLoadContext(string pluginId, string pluginAssemblyPath) : AssemblyLoadContext($"forge-plugin-{pluginId}", isCollectible: true) |
| 8 | { |
| 9 | private readonly AssemblyDependencyResolver _resolver = new(pluginAssemblyPath); |
| 10 | |
| 11 | internal string PluginId { get; } = pluginId; |
| 12 | |
| 13 | internal string AssemblyPath { get; } = pluginAssemblyPath; |
| 14 | |
| 15 | protected override Assembly? Load(AssemblyName assemblyName) |
| 16 | { |
| 17 | var name = assemblyName.Name; |
| 18 | if (name is not null) |
| 19 | { |
| 20 | var inDefault = Default.Assemblies.FirstOrDefault(a => |
| 21 | string.Equals(a.GetName().Name, name, StringComparison.Ordinal)); |
| 22 | if (inDefault is not null) |
| 23 | return inDefault; |
| 24 | |
| 25 | if (IsSharedWithHost(name)) |
| 26 | { |
| 27 | var hostPath = Path.Combine(AppContext.BaseDirectory, $"{name}.dll"); |
| 28 | if (File.Exists(hostPath)) |
| 29 | return Default.LoadFromAssemblyPath(hostPath); |
| 30 | |
| 31 | return Default.LoadFromAssemblyName(assemblyName); |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | var path = _resolver.ResolveAssemblyToPath(assemblyName); |
| 36 | if (path is null && name is not null) |
| 37 | { |
| 38 | var sibling = Path.Combine(Path.GetDirectoryName(AssemblyPath)!, $"{name}.dll"); |
| 39 | if (File.Exists(sibling)) |
| 40 | path = sibling; |
| 41 | } |
| 42 | |
| 43 | return path is not null ? LoadFromAssemblyPath(path) : null; |
| 44 | } |
| 45 | |
| 46 | protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) |
| 47 | { |
| 48 | var path = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); |
| 49 | return path is not null ? LoadUnmanagedDllFromPath(path) : IntPtr.Zero; |
| 50 | } |
| 51 | |
| 52 | private static bool IsSharedWithHost(string assemblyName) => |
| 53 | assemblyName.StartsWith("AgentForge.Abstractions", StringComparison.Ordinal) |
| 54 | || assemblyName.StartsWith("AgentForge.Plugin.Sdk", StringComparison.Ordinal) |
| 55 | || assemblyName.StartsWith("AgentForge.Mcp.Core", StringComparison.Ordinal) |
| 56 | || assemblyName.StartsWith("AgentForge.Plugin.ViewShell", StringComparison.Ordinal) |
| 57 | || assemblyName.StartsWith("AgentForge.Plugin.View", StringComparison.Ordinal) |
| 58 | || assemblyName.StartsWith("AgentForge.Plugin.RepositoryImport.GitHub.Core", StringComparison.Ordinal) |
| 59 | || assemblyName.StartsWith("Microsoft.", StringComparison.Ordinal) |
| 60 | || assemblyName.StartsWith("System.", StringComparison.Ordinal) |
| 61 | || assemblyName.StartsWith("netstandard", StringComparison.Ordinal); |
| 62 | } |
| 63 | |