using System;using System.IO;using System.Text.Json;using Agent.Core.ToolCalling;namespace Lilith.Agent;public partial class Lilith{ public void RegisterWorkspaceTools(ToolRegistry registry) { registry.Register(new AgentTool( "write_workspace_file", "Writes a text file to the output directory of the workspace.", args => WriteWorkspaceFile(args), new { type = "object", properties = new { path = new { type = "string", description = "Relative path/filename of the file to write in the output folder." }, content = new { type = "string", description = "The text content to write into the file." } }, required = new[] { "path", "content" } } )); registry.Register(new AgentTool( "read_workspace_file", "Reads a file from the workspace. It checks the output folder first, then the config folder.", args => ReadWorkspaceFile(args), new { type = "object", properties = new { path = new { type = "string", description = "Relative path of the file to read." } }, required = new[] { "path" } } )); registry.Register(new AgentTool( "list_workspace_files", "Lists all files and subdirectories located inside both the config and output workspace folders.", _ => ListWorkspaceFiles(), null )); } private string WriteWorkspaceFile(string argumentsJson) { if (Workspace is null) return "Error: Workspace is not initialized."; try { using var doc = JsonDocument.Parse(argumentsJson); var root = doc.RootElement; if (!root.TryGetProperty("path", out var pathProp) || !root.TryGetProperty("content", out var contentProp)) { return "Error: Missing path or content properties."; } string relativePath = pathProp.GetString() ?? ""; string content = contentProp.GetString() ?? ""; if (string.IsNullOrWhiteSpace(relativePath)) { return "Error: Path cannot be empty."; } string? safePath = GetSafePath(Workspace.OutputFolder, relativePath); if (safePath is null) { return "Error: Access denied. Path is outside of output workspace directory."; } string? dir = Path.GetDirectoryName(safePath); if (dir is not null && !Directory.Exists(dir)) { Directory.CreateDirectory(dir); } File.WriteAllText(safePath, content, System.Text.Encoding.UTF8); return $"Success: Successfully wrote {content.Length} characters to file '{relativePath}'."; } catch (Exception ex) { return $"Error writing file: {ex.Message}"; } } private string ReadWorkspaceFile(string argumentsJson) { if (Workspace is null) return "Error: Workspace is not initialized."; try { using var doc = JsonDocument.Parse(argumentsJson); var root = doc.RootElement; if (!root.TryGetProperty("path", out var pathProp)) { return "Error: Missing path property."; } string relativePath = pathProp.GetString() ?? ""; if (string.IsNullOrWhiteSpace(relativePath)) { return "Error: Path cannot be empty."; } string? safeOutputPath = GetSafePath(Workspace.OutputFolder, relativePath); if (safeOutputPath is not null && File.Exists(safeOutputPath)) { return File.ReadAllText(safeOutputPath, System.Text.Encoding.UTF8); } string? safeConfigPath = GetSafePath(Workspace.ConfigFolder, relativePath); if (safeConfigPath is not null && File.Exists(safeConfigPath)) { return File.ReadAllText(safeConfigPath, System.Text.Encoding.UTF8); } return $"Error: File '{relativePath}' not found in output or config folders."; } catch (Exception ex) { return $"Error reading file: {ex.Message}"; } } private string ListWorkspaceFiles() { if (Workspace is null) return "Error: Workspace is not initialized."; var sb = new System.Text.StringBuilder(); sb.AppendLine("Workspace Files & Directories:"); sb.AppendLine(); sb.AppendLine("1. Setup & Configuration Folder (Config):"); if (Directory.Exists(Workspace.ConfigFolder)) { var files = Directory.GetFiles(Workspace.ConfigFolder, "*", SearchOption.AllDirectories); if (files.Length == 0) { sb.AppendLine(" (Empty)"); } foreach (var file in files) { var rel = Path.GetRelativePath(Workspace.ConfigFolder, file).Replace('\\', '/'); var size = new FileInfo(file).Length; sb.AppendLine($" - {rel} ({FormatSize(size)})"); } } else { sb.AppendLine(" (Directory does not exist)"); } sb.AppendLine(); sb.AppendLine("2. Output & Creative Sandbox Folder (Output):"); if (Directory.Exists(Workspace.OutputFolder)) { var files = Directory.GetFiles(Workspace.OutputFolder, "*", SearchOption.AllDirectories); if (files.Length == 0) { sb.AppendLine(" (Empty)"); } foreach (var file in files) { var rel = Path.GetRelativePath(Workspace.OutputFolder, file).Replace('\\', '/'); var size = new FileInfo(file).Length; sb.AppendLine($" - {rel} ({FormatSize(size)})"); } } else { sb.AppendLine(" (Directory does not exist)"); } return sb.ToString(); } private static string? GetSafePath(string baseFolder, string relativePath) { if (string.IsNullOrWhiteSpace(baseFolder)) return null; string fullBase = Path.GetFullPath(baseFolder); string combined = Path.GetFullPath(Path.Combine(fullBase, relativePath)); if (combined.StartsWith(fullBase, StringComparison.OrdinalIgnoreCase)) { return combined; } return null; } private static string FormatSize(long bytes) { if (bytes >= 1024 * 1024) return $"{bytes / 1024.0 / 1024.0:F2} MB"; if (bytes >= 1024) return $"{bytes / 1024.0:F2} KB"; return $"{bytes} bytes"; }}
Documentation
Lilith (Version 3)
Version 3 Lilith agent library. Entry point: Lilith.cs with partials under Boot/, Chat/, Ollama/, and SystemPrompt/.
Build the solution or run python ../Build-v3.py from this tree.