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."; }}
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 ofsrc/Version7next to the built app (MSBuildCopyShippedSource){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.