SelfImprovement/SandboxToolInvoker.cscsharp

Documentation

Lilith self-improvement (Version 7+)

Lilith can extend herself only by creating tools in three categories:

| Category | Location | Examples | |----------|----------|----------| | Core | Agent-Core | Memory, time (rare; system-level) | | Addon | Agent-Addons | Weather, C# projects, apps | | Self | Lilith | Workspace files, self-improvement |

Layout

  • ShippedSource/ — copy of src/Version7 next to the built app (MSBuild CopyShippedSource)
  • {workspace}/output/self-improvement/live — current editable source
  • {workspace}/output/self-improvement/sandbox — clone for edits and builds
  • {workspace}/output/self-improvement/backups/{timestamp} — backups before promote

Tools

1. self_improve_get_source_layout 2. self_improve_generate_tool 3. self_improve_backup_live 4. self_improve_create_sandbox 5. self_improve_build_sandbox 6. self_improve_verify_sandbox_tool 7. self_improve_promote_sandbox — copies sandbox → live, builds, restarts

Set GENESIS_REPO_ROOT to the repo root when developing from source instead of ShippedSource.

using System.Reflection;using System.Runtime.Loader;using System.Text;namespace Lilith.Agent.SelfImprovement;/// <summary>Loads the built sandbox Lilith assembly and invokes a generated tool by name.</summary>internal static class SandboxToolInvoker{    public static string Invoke(SelfImprovementPaths paths, string toolName, string argumentsJson = "{}")    {        if (string.IsNullOrWhiteSpace(toolName))            return "Error: tool_name is required.";        string binDir = Path.Combine(paths.Sandbox, "Lilith", "bin", "Release", "net8.0");        string lilithDll = Path.Combine(binDir, "Lilith.dll");        string agentCoreDll = Path.Combine(binDir, "Agent-Core.dll");        if (!File.Exists(lilithDll))            return $"Error: sandbox Lilith.dll not found at {lilithDll}. Call self_improve_build_sandbox first.";        if (!File.Exists(agentCoreDll))            return $"Error: sandbox Agent-Core.dll not found at {agentCoreDll}.";        try        {            var loadContext = new SandboxAssemblyLoadContext(binDir);            Assembly agentCoreAsm = loadContext.LoadFromAssemblyPath(agentCoreDll);            Assembly lilithAsm = loadContext.LoadFromAssemblyPath(lilithDll);            string className = ToPascalCase(toolName) + "Tool";            string fullName = $"Lilith.Agent.Tools.Generated.{className}";            Type? toolType = lilithAsm.GetType(fullName, throwOnError: false, ignoreCase: false);            if (toolType is null)                return $"Error: generated type {fullName} was not found in sandbox assembly.";            Type registryType = agentCoreAsm.GetType("Agent.Core.ToolCalling.ToolRegistry", throwOnError: true)!;            object registry = Activator.CreateInstance(registryType)!;            MethodInfo? register = toolType.GetMethod("Register", BindingFlags.Public | BindingFlags.Static);            if (register is null)                return $"Error: {className}.Register was not found.";            register.Invoke(null, [registry]);            MethodInfo tryInvoke = registryType.GetMethod("TryInvoke", BindingFlags.Public | BindingFlags.Instance)!;            object?[] invokeArgs = [toolName, argumentsJson, null];            bool ok = (bool)tryInvoke.Invoke(registry, invokeArgs)!;            string result = (string)invokeArgs[2]!;            return ok ? result : result;        }        catch (Exception ex)        {            return $"Error invoking sandbox tool '{toolName}': {ex.Message}";        }    }    private sealed class SandboxAssemblyLoadContext(string binDir) : AssemblyLoadContext(isCollectible: true)    {        protected override Assembly? Load(AssemblyName assemblyName)        {            string? path = ResolveAssemblyPath(assemblyName.Name);            return path is not null ? LoadFromAssemblyPath(path) : null;        }        private string? ResolveAssemblyPath(string? simpleName)        {            if (string.IsNullOrWhiteSpace(simpleName))                return null;            string candidate = Path.Combine(binDir, simpleName + ".dll");            return File.Exists(candidate) ? candidate : null;        }    }    private static string ToPascalCase(string snake)    {        var parts = snake.Split('_', StringSplitOptions.RemoveEmptyEntries);        var sb = new StringBuilder();        foreach (string part in parts)        {            if (part.Length == 0) continue;            sb.Append(char.ToUpperInvariant(part[0]));            if (part.Length > 1)                sb.Append(part[1..]);        }        return sb.ToString();    }}