SelfImprovement/SelfImprovementTools.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;using System.Diagnostics;using System.IO;using System.Linq;using System.Text.Json;using Agent.Core;using Agent.Core.ToolCalling;using Lilith.Agent.SelfImprovement;namespace Lilith.Agent;public partial class Lilith{    public void RegisterSelfImprovementTools(ToolRegistry registry)    {        registry.Register(new AgentTool(            "self_improve_set_tool_spec",            "Saves the tool-under-test spec to workspace config (step 1 of smoke test). Example: tool_name ping_self, description Returns pong, category self, invoke_body return \"pong\";",            InvokeSetToolSpec,            new            {                type = "object",                properties = new                {                    tool_name = new { type = "string", description = "Snake_case name, e.g. ping_self." },                    description = new { type = "string", description = "What the tool does." },                    category = new { type = "string", description = "core, addon, or self (default self)." },                    invoke_body = new { type = "string", description = "C# body, e.g. return \"pong\";" },                },                required = new[] { "tool_name", "description" },            },            ToolCategory.Self));        registry.Register(new AgentTool(            "self_improve_generate_tool",            "Generates tool scaffold in sandbox from saved spec (call set_tool_spec first) or from JSON args. Requires sandbox.",            InvokeGenerateTool,            new            {                type = "object",                properties = new                {                    tool_name = new { type = "string", description = "Optional if set_tool_spec was called." },                    description = new { type = "string", description = "Optional if set_tool_spec was called." },                    category = new { type = "string", description = "core, addon, or self." },                    invoke_body = new { type = "string", description = "Optional C# return expression." },                },            },            ToolCategory.Self));        registry.Register(new AgentTool(            "self_improve_backup_live",            "Copies the live source tree to backups/{timestamp}. Run before risky edits.",            _ => InvokeBackupLive(),            null,            ToolCategory.Self));        registry.Register(new AgentTool(            "self_improve_create_sandbox",            "Clones the live source tree into sandbox for isolated edits and builds.",            _ => InvokeCreateSandbox(),            null,            ToolCategory.Self));        registry.Register(new AgentTool(            "self_improve_build_sandbox",            "dotnet build Release for sandbox LilithConsole.",            _ => InvokeBuildSandbox(),            null,            ToolCategory.Self));        registry.Register(new AgentTool(            "self_improve_invoke_sandbox_tool",            "Invokes the generated tool from the built sandbox DLL and compares output to the saved spec expected result.",            InvokeInvokeSandboxTool,            new            {                type = "object",                properties = new                {                    tool_name = new { type = "string", description = "Optional if set_tool_spec was called." },                    arguments_json = new { type = "string", description = "Optional JSON string to pass to the tool (default {})." },                    expected_result = new { type = "string", description = "Optional expected result override (otherwise uses saved spec expected result)." },                },            },            ToolCategory.Self));        registry.Register(new AgentTool(            "self_improve_verify_sandbox_tool",            "Builds sandbox, runs design tests, checks tool exists in source, and invokes the tool (TOOL TEST PASSED). Uses saved spec if omitted.",            InvokeVerifySandbox,            new            {                type = "object",                properties = new                {                    tool_name = new { type = "string", description = "Optional if set_tool_spec was called." },                },            },            ToolCategory.Self));        registry.Register(new AgentTool(            "self_improve_promote_sandbox",            "Promotes verified sandbox over live, deploys built DLLs to this console, and restarts into the upgraded build. Call only after VERIFICATION PASSED.",            _ => InvokePromoteSandbox(),            null,            ToolCategory.Self));    }    private SelfImprovementPaths RequirePaths()    {        if (Workspace is null)            throw new InvalidOperationException("Workspace is required for self-improvement.");        var paths = new SelfImprovementPaths(Workspace);        SourceTreeService.SeedLiveIfEmpty(paths);        return paths;    }    private string InvokeGetLayout()    {        var paths = RequirePaths();        return SourceTreeService.DescribeLayout(paths);    }    private Workspace RequireWorkspace()    {        if (Workspace is null)            throw new InvalidOperationException("Workspace is required for self-improvement.");        return Workspace;    }    public string SaveSelfImproveToolSpec(string argumentsJson) => InvokeSetToolSpec(argumentsJson);    private string InvokeSetToolSpec(string argumentsJson)    {        var workspace = RequireWorkspace();        var spec = ToolSpecStore.ParseFromJson(argumentsJson);        if (!spec.IsComplete)        {            return "Error: self_improve_set_tool_spec requires tool_name and description (category defaults to self).";        }        spec.Save(workspace);        return $"Tool spec saved: {spec.ToolName} ({spec.Category}). Path: {ToolSpecStore.GetSpecPath(workspace)}";    }    private ToolSpecStore ResolveToolSpec(string argumentsJson)    {        var workspace = RequireWorkspace();        var fromArgs = ToolSpecStore.ParseFromJson(argumentsJson);        var saved = ToolSpecStore.Load(workspace);        fromArgs.MergeMissing(saved);        return fromArgs;    }    private string InvokeGenerateTool(string argumentsJson)    {        var paths = RequirePaths();        if (!Directory.Exists(paths.Sandbox))            return "Error: sandbox does not exist. Call self_improve_create_sandbox first.";        var spec = ResolveToolSpec(argumentsJson);        if (!spec.IsComplete)        {            return "Error: No tool spec. Call self_improve_set_tool_spec first (or pass tool_name, description, category).";        }        if (string.IsNullOrWhiteSpace(spec.InvokeBody))            spec.InvokeBody = "return \"OK\";";        string path = ToolScaffoldGenerator.Generate(            paths.Sandbox, spec.ToolName, spec.Description, spec.Category, spec.InvokeBody);        return $"Generated tool '{spec.ToolName}' ({spec.Category}) at {path}. Next: self_improve_build_sandbox, then self_improve_verify_sandbox_tool.";    }    private string InvokeBackupLive()    {        var paths = RequirePaths();        string backup = SourceTreeService.CreateBackup(paths);        return $"Live source backed up to {backup}";    }    private string InvokeCreateSandbox()    {        var paths = RequirePaths();        SourceTreeService.CopyTree(paths.Live, paths.Sandbox, overwriteDestination: true);        return $"Sandbox created at {paths.Sandbox}";    }    private string InvokeBuildSandbox()    {        var paths = RequirePaths();        if (!Directory.Exists(paths.Sandbox))            return "Error: sandbox does not exist. Call self_improve_create_sandbox first.";        return SandboxBuildService.BuildSandboxConsole(paths);    }    private string InvokeVerifySandbox(string argumentsJson)    {        var paths = RequirePaths();        if (!Directory.Exists(paths.Sandbox))            return "Error: sandbox does not exist.";        var spec = ResolveToolSpec(argumentsJson);        if (string.IsNullOrWhiteSpace(spec.ToolName))            return "Error: No tool_name. Call self_improve_set_tool_spec first or pass tool_name.";        return SandboxVerificationService.VerifyTool(paths, spec);    }    private string InvokeInvokeSandboxTool(string argumentsJson)    {        var paths = RequirePaths();        if (!Directory.Exists(paths.Sandbox))            return "Error: sandbox does not exist. Call self_improve_create_sandbox and build first.";        var spec = ResolveToolSpec(argumentsJson);        if (string.IsNullOrWhiteSpace(spec.ToolName))            return "Error: No tool_name. Call self_improve_set_tool_spec first or pass tool_name.";        using var doc = JsonDocument.Parse(string.IsNullOrWhiteSpace(argumentsJson) ? "{}" : argumentsJson);        var root = doc.RootElement;        string toolArgs = root.TryGetProperty("arguments_json", out var argsEl) ? (argsEl.GetString() ?? "{}") : "{}";        string expectedOverride = root.TryGetProperty("expected_result", out var expEl) ? (expEl.GetString() ?? "") : "";        string? expected = !string.IsNullOrWhiteSpace(expectedOverride)            ? expectedOverride.Trim()            : spec.ResolveExpectedResult();        string result = SandboxToolInvoker.Invoke(paths, spec.ToolName, toolArgs);        if (result.StartsWith("Error", StringComparison.OrdinalIgnoreCase))            return result;        if (expected is null)            return $"TOOL TEST PASSED: {spec.ToolName}() => '{result}'";        if (result.Trim().Equals(expected, StringComparison.OrdinalIgnoreCase))            return $"TOOL TEST PASSED: {spec.ToolName}() => '{result}' (expected '{expected}')";        return $"TOOL TEST FAILED: {spec.ToolName}() => '{result}' (expected '{expected}')";    }    private string InvokePromoteSandbox()    {        var paths = RequirePaths();        if (!Directory.Exists(paths.Sandbox))            return "Error: sandbox does not exist.";        SourceTreeService.CreateBackup(paths);        SourceTreeService.CopyTree(paths.Sandbox, paths.Live, overwriteDestination: true);        string deployLog = PromoteDeploymentService.DeploySandboxBuildToRunningConsole(paths);        PromoteDeploymentService.SyncLiveSourceToShippedSource(paths);        string promoteNote = Path.Combine(paths.Root, "last-promote.txt");        File.WriteAllText(promoteNote, $"Promoted at {DateTime.UtcNow:O}\n{deployLog}");        bool testMode = Environment.GetEnvironmentVariable("LILITH_TESTER") == "1";        if (testMode)        {            return $"PROMOTED — {deployLog} Test runner will restart Lilith into the upgraded build.";        }        string? runningExe = Environment.ProcessPath            ?? Process.GetCurrentProcess().MainModule?.FileName;        if (string.IsNullOrWhiteSpace(runningExe))            return $"PROMOTED — {deployLog} Restart Lilith manually to run the upgraded build.";        var startInfo = new ProcessStartInfo(runningExe)        {            UseShellExecute = false,            WorkingDirectory = Path.GetDirectoryName(runningExe)!,        };        foreach (System.Collections.DictionaryEntry entry in Environment.GetEnvironmentVariables())        {            if (entry.Key is string key && entry.Value is string value)                startInfo.Environment[key] = value;        }        startInfo.Environment["GENESIS_AFTER_PROMOTE"] = "1";        Process.Start(startInfo);        Environment.Exit(0);        return "PROMOTED — restarting upgraded Lilith.";    }}