Versionierung der Arbeitsbereiche: Verlauf je Datei mit Agent, Lauf und Vorfassung (#719)

Eigene inhaltsadressierte Ablage (Agents/.history/objects, SHA-256) statt Git,
Journal FileHistory in state.db. Jeder Schreibweg - FileRW write/append/copy/
delete/stock_add, FTP- und RocketChat-Downloads - legt die alte Fassung ab und
verbucht die Aenderung mit RunId. Externe Aenderungen findet ein Abgleich beim
Start und stuendlich. Aufbewahrung nach Tagen und Fassungen, verwaiste Objekte
werden entfernt. WebUI: Dateiverlauf mit Filter, Diff und Wiederherstellen nur
durch den Menschen mit Grund.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-09-28 11:12:13 +02:00
co-authored by Claude Opus 5.5
parent 621a83de43
commit e9c39c3c96
21 changed files with 1663 additions and 2 deletions
@@ -265,9 +265,15 @@ public sealed class RocketChatTool : IAgentTool, IToolJobProvider
Directory.CreateDirectory(Path.GetDirectoryName(file.FullPath)!);
// #719: the download is a change like a write — kept and journaled.
var historyWorkspace = file.Label == WorkspaceLayout.Personal ? $"{WorkspaceLayout.Personal}:{context.AgentId}" : file.Label;
var change = context.History is { } history ? await history.BeforeAsync(file.FullPath, historyWorkspace, ct) : null;
var bytes = await client.DownloadToFileAsync(fileId, file.FullPath, config.MaxDownloadBytes, ct);
if (guarded)
context.Files!.NoteReplace(context.AgentId, context.RunId, file.FullPath, Core.Files.FileLedger.HashOf(file.FullPath));
if (change is not null)
await context.History!.RecordAsync(change, Core.Files.FileAction.Replace, context.AgentId, context.RunId, ct, "RocketChat download");
return ToolResult.Ok(JsonSerializer.Serialize(new
{