Files
gott/Services/MemoryService.cs
T
Meleki 5d090e160b feat: Windows-Kompatibilität
- WorkspaceHelper: Zentrale Hilfsmethoden (IsWindows, TempDir, IsSubPathOf,
  GetRelativePath, ShellExecutable, ShellArguments)
- ToolService: execute nutzt cmd.exe auf Windows, GetRelativePath mit
  Forward-Slashes, IsSubPathOf für case-insensitive Pfadprüfung
- SessionEndpoint: /tmp → Path.GetTempPath(), bash → cmd.exe,
  Fire-and-forget via .bat statt systemd-run auf Windows
- ChatService: /tmp → WorkspaceHelper.TempDir
- MemoryService: IsSubPathOf + GetRelativePath
- WorkspaceEndpoint: HandleBrowse zeigt Laufwerke auf Windows,
  IsSubPathOf statt StartsWith
- InfoEndpoint: gott.exe auf Windows, isWindows-Flag in API-Response
- Frontend: File-Explorer mit Laufwerks-Navigation auf Windows
2026-03-09 11:35:04 +01:00

156 lines
5.0 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Gott.Services;
public class MemoryService
{
private readonly string _dataDir;
private readonly string _templateDir;
public string DataDir => _dataDir;
public MemoryService(string dataDir)
{
_dataDir = dataDir;
_templateDir = Path.Combine(AppContext.BaseDirectory, "Templates");
}
/// <summary>
/// Liest ein Template aus dem Templates-Ordner. Gibt null zurück wenn nicht vorhanden.
/// </summary>
private string ReadTemplate(string fileName)
{
var path = Path.Combine(_templateDir, fileName);
return File.Exists(path) ? File.ReadAllText(path) : null;
}
/// <summary>
/// Gibt den Memory-Ordner für einen Agent zurück. Erstellt ihn bei Bedarf.
/// </summary>
public string GetAgentMemoryDir(string agentId)
{
var dir = Path.Combine(_dataDir, "agents", agentId);
Directory.CreateDirectory(dir);
return dir;
}
/// <summary>
/// Liest eine Memory-Datei. Gibt null zurück wenn nicht vorhanden.
/// </summary>
public string ReadMemoryFile(string agentId, string fileName)
{
var path = ResolvePath(agentId, fileName);
if (path == null || !File.Exists(path)) return null;
return File.ReadAllText(path);
}
/// <summary>
/// Schreibt/überschreibt eine Memory-Datei.
/// </summary>
public void WriteMemoryFile(string agentId, string fileName, string content)
{
var path = ResolvePath(agentId, fileName);
if (path == null) throw new Exception("Ungültiger Dateipfad");
var dir = Path.GetDirectoryName(path);
if (dir != null) Directory.CreateDirectory(dir);
File.WriteAllText(path, content);
}
/// <summary>
/// Hängt Inhalt an eine Memory-Datei an.
/// </summary>
public void AppendMemoryFile(string agentId, string fileName, string content)
{
var path = ResolvePath(agentId, fileName);
if (path == null) throw new Exception("Ungültiger Dateipfad");
var dir = Path.GetDirectoryName(path);
if (dir != null) Directory.CreateDirectory(dir);
File.AppendAllText(path, content);
}
/// <summary>
/// Listet alle Memory-Dateien eines Agents auf.
/// </summary>
public List<string> ListMemoryFiles(string agentId, string subPath = null)
{
var baseDir = GetAgentMemoryDir(agentId);
var targetDir = string.IsNullOrEmpty(subPath)
? baseDir
: Path.GetFullPath(Path.Combine(baseDir, subPath));
if (!WorkspaceHelper.IsSubPathOf(targetDir, baseDir))
throw new Exception("Zugriff außerhalb des Memory-Verzeichnisses nicht erlaubt");
if (!Directory.Exists(targetDir))
return new List<string>();
var entries = new List<string>();
foreach (var dir in Directory.GetDirectories(targetDir))
entries.Add(WorkspaceHelper.GetRelativePath(baseDir, dir) + "/");
foreach (var file in Directory.GetFiles(targetDir))
entries.Add(WorkspaceHelper.GetRelativePath(baseDir, file));
return entries;
}
/// <summary>
/// Erstellt die Standard-Files (IDENTITY.md, SOUL.md) für einen Agent.
/// Überschreibt NICHT, falls bereits vorhanden.
/// </summary>
public void InitializeAgentFiles(string agentId, string agentName)
{
var dir = GetAgentMemoryDir(agentId);
var identityPath = Path.Combine(dir, "IDENTITY.md");
if (!File.Exists(identityPath))
{
var template = ReadTemplate("IDENTITY.md") ?? "# IDENTITY.md\n";
File.WriteAllText(identityPath, template.Replace("{NAME}", agentName));
}
var soulPath = Path.Combine(dir, "SOUL.md");
if (!File.Exists(soulPath))
{
var template = ReadTemplate("SOUL.md") ?? "# SOUL.md\n";
File.WriteAllText(soulPath, template);
}
InitializeUserFile();
}
/// <summary>
/// Erstellt die globale USER.md im Data-Ordner, falls nicht vorhanden.
/// </summary>
public void InitializeUserFile()
{
var userPath = Path.Combine(_dataDir, "USER.md");
if (!File.Exists(userPath))
{
var template = ReadTemplate("USER.md") ?? "# USER.md\n";
File.WriteAllText(userPath, template);
}
}
/// <summary>
/// Liest MEMORY.md (das Haupt-Memory-File). Gibt leeren String zurück wenn nicht vorhanden.
/// </summary>
public string GetMainMemory(string agentId)
{
return ReadMemoryFile(agentId, "MEMORY.md") ?? "";
}
/// <summary>
/// Löst einen Pfad relativ zum Agent-Memory-Dir auf und prüft Sandbox.
/// </summary>
private string ResolvePath(string agentId, string fileName)
{
var baseDir = GetAgentMemoryDir(agentId);
var full = Path.GetFullPath(Path.Combine(baseDir, fileName));
if (!WorkspaceHelper.IsSubPathOf(full, baseDir))
return null;
return full;
}
}