Tools/WorkspaceTools/WorkspaceTools.cscsharp

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.

using System;using System.IO;using System.Text.Json;using Agent.Core.Logging;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 project/document file to the workspace output folder. Do NOT use for user facts — use store_memory with paths like user/name instead.",            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" }            },            ToolCategory.Self        ));        registry.Register(new AgentTool(            "read_workspace_file",            "Reads a project/document file from the workspace (output, then config). Do NOT use for user facts — use retrieve_memory or get_memory_at_path.",            args => ReadWorkspaceFile(args),            new            {                type = "object",                properties = new                {                    path = new { type = "string", description = "Relative path of the file to read." }                },                required = new[] { "path" }            },            ToolCategory.Self        ));        registry.Register(new AgentTool(            "list_workspace_files",            "Lists all files and subdirectories located inside both the config and output workspace folders.",            _ => ListWorkspaceFiles(),            null,            ToolCategory.Self        ));    }    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.";            }            if (IsMemoryNamespacePath(relativePath))            {                return StoreViaMemoryAsync(relativePath, content);            }            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.";            }            if (IsMemoryNamespacePath(relativePath))            {                return ReadViaMemory(relativePath);            }            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 string StoreViaMemoryAsync(string path, string content)    {        if (_client?.Memory is null)        {            return "Error: Memory system is not initialized.";        }        try        {            Logger.WriteLine(                "Memory",                $"Storing to memory path '{path}' (redirected from write_workspace_file)...",                LogColors.Cyan);            _client.Memory.StoreAsync(path, content).GetAwaiter().GetResult();            return $"Success: Stored memory under path '{path}'.";        }        catch (Exception ex)        {            return $"Error: {ex.Message}";        }    }    private string ReadViaMemory(string path)    {        if (_client?.Memory is null)        {            return "Error: Memory system is not initialized.";        }        string? exact = _client.Memory.QueryPath(path);        if (exact is not null)        {            return exact;        }        try        {            var matches = _client.Memory.RetrieveAsync(path, topN: 1).GetAwaiter().GetResult();            if (matches.Count > 0)            {                return matches[0].Value;            }        }        catch (Exception ex)        {            return $"Error: {ex.Message}";        }        return $"No memory found at path '{path}'.";    }    private static bool IsMemoryNamespacePath(string relativePath)    {        string normalized = relativePath.Replace('\\', '/').TrimStart('/');        return normalized.StartsWith("user/", StringComparison.OrdinalIgnoreCase)            || string.Equals(normalized, "user", StringComparison.OrdinalIgnoreCase);    }    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";    }}