FileRW: Loeschen ueber den Dateiverlauf umkehrbar, ohne Freigabe; Ordner und Unaufhebbares bleiben beim Menschen (#195)

Der Dateiverlauf (#719) haelt die Vorfassung jeder geloeschten Datei - ein
eigener Papierkorb waere doppelt. delete einer Datei, die der Verlauf aufhebt,
laeuft ohne Freigabe; Ordner nur mit recursive:true (delete_folder, Freigabe);
zu grosse Dateien oder Verlauf aus (delete_unkept, Freigabe); die Wurzel nie.
Neue Aktionen deleted und restore mit voller Pfad-/Endungspruefung.
IAgentTool.StagingAktion bekommt eine Ueberladung mit Kontext.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-10-02 11:28:53 +02:00
co-authored by Claude Opus 5.5
parent 2a225129e9
commit 5de60c3b21
8 changed files with 308 additions and 6 deletions
+1 -1
View File
@@ -1655,7 +1655,7 @@ public sealed class AgentEngine : IAgentMessageRouter, Staging.IFrozenCallExecut
if (_stagingGate is not null)
{
// #641: Die Rechte stehen in der Werkzeugkonfiguration des Agenten.
var operation = tool.StagingAktion(input) ?? Staging.StagingGate.ExtractAction(arguments);
var operation = tool.StagingAktion(input, context) ?? Staging.StagingGate.ExtractAction(arguments);
var intercept = await _stagingGate.InterceptAsync(
agentConfig.AgentId, instanceId, runId, toolName, arguments, ct,
operation,
+18
View File
@@ -107,6 +107,24 @@ public sealed class FileHistory
public bool Covers(string fullPath)
=> !IsInside(Path.GetFullPath(fullPath), Path.Combine(_agentsDirectory, FolderName));
/// <summary>
/// #195: Whether a delete of this file can be undone — the history is on, covers the file
/// and keeps content of its size. Only then is deleting it no irreversible act.
/// </summary>
public bool CanKeep(string fullPath)
{
fullPath = Path.GetFullPath(fullPath);
return Enabled && Covers(fullPath) && File.Exists(fullPath)
&& new FileInfo(fullPath).Length <= (long)_settings().MaxObjectMegabytes * 1024 * 1024;
}
/// <summary>#195: Whether the content of a version is still kept.</summary>
public bool CanKeepContent(string? hash)
=> !string.IsNullOrWhiteSpace(hash) && hash.Length == 64 && hash.All(Uri.IsHexDigit) && File.Exists(ObjectPath(hash.ToLowerInvariant()));
/// <summary>#195: How long a deleted file's content stays restorable.</summary>
public int RetentionDays => Math.Max(1, _settings().RetentionDays);
// ─── Recording ───
/// <summary>Keeps the version the file has now, before a tool changes it.</summary>
@@ -58,6 +58,9 @@ public sealed class StagingPolicy
["Database.truncate"] = StagingDecision.Approve,
["FileRW.delete"] = StagingDecision.Approve,
// #195: FileRW names these itself — a folder at once, or a file the history cannot keep.
["FileRW.delete_folder"] = StagingDecision.Approve,
["FileRW.delete_unkept"] = StagingDecision.Approve,
["FTP.upload"] = StagingDecision.Approve,
["FTP.delete"] = StagingDecision.Approve,
+7
View File
@@ -77,6 +77,13 @@ public interface IAgentTool
/// </summary>
string? StagingAktion(JsonElement input) => null;
/// <summary>
/// #195: The staging key with the call's context — for a key that depends on what the call
/// meets (FileRW: a file the history keeps, a folder, a file too large to keep). Default:
/// <see cref="StagingAktion(JsonElement)"/>.
/// </summary>
string? StagingAktion(JsonElement input, AgentToolContext context) => StagingAktion(input);
/// <summary>
/// Die Operationen, die sich in der Werkzeugkonfiguration eines Agenten erlauben oder
/// verbieten lassen (#641) - dieselben Schluessel, die <see cref="StagingAktion"/> bzw. das