Files
ClawdDotNet/docs/umsetzungsplaene/UMSETZUNGSPLAN-FileRW-Papierkorb-Cleanup.md
T
RichardandClaude Opus 5 61a93ffa58 Umsetzungsplaene fuer vier neue Agenten-Tools angelegt
Aus Richards Ideensammlung, nach Sichtung des vorhandenen Codes:

- FileRW-Papierkorb + Cleanup-Job: delete verschiebt nach .trash statt
  endgueltig zu loeschen, Aufraeumen ueber IToolJobProvider/ToolJobScheduler.
  Macht Loeschen reversibel und erlaubt damit, FileRW.delete im Staging von
  Approve auf Auto herunterzustufen.
- AgentInspector: lesende Aufsicht ueber andere Agenten. Beleg-basiert
  (Audit/Receipts/Taskboard) statt datei-basiert; harte Allowlist, damit
  AgentSettings.json mit den API-Keys nicht in einen LLM-Kontext geraet.
- AgentEditor-Haertung: Identity/Soul-Aenderungen ueber die vorhandene
  StagingPolicy freigabepflichtig machen, Selbstbearbeitung sperren,
  Audit + restore ergaenzen. Das Tool selbst existiert bereits.
- WebSearch: Suche als eigenes Tool, Lesen bleibt bei WebFetch hinter der
  Domain-Whitelist. Kein agent-reach (Klartext-Cookies, Fremdprozess).
  Trennung Rechercheagent / handelnder Agent gegen Prompt Injection.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 13:13:09 +02:00

8.9 KiB

Umsetzungsplan: FileRW-Papierkorb + Cleanup-Job

Stand: 2026-08-05 Ziel: FileRW.delete löscht nicht mehr endgültig, sondern verschiebt in einen Papierkorb je Workspace. Ein Cron-Job räumt den Papierkorb nach X Tagen auf. Reihenfolge: Zuerst umsetzen — kleinster Eingriff, entschärft ein reales Risiko und ist Voraussetzung dafür, FileRW.delete im Staging von Approve auf Auto herunterzustufen (siehe Abschnitt 6).


1. Ausgangslage

HandleDeleteAsync in src/ClawdDotNet.Tools.FileRW/FileRWTool.cs:318 löscht sofort und endgültig:

if (File.Exists(path))      { File.Delete(path); ... }
else if (Directory.Exists(path)) { Directory.Delete(path, true); ... }

Ein einziger Tool-Call mit path: "." räumt damit den kompletten Workspace eines Agenten ab — ohne Rückweg. Gleichzeitig fehlt dem Agenten jede Möglichkeit, gefahrlos aufzuräumen: Jede Aufräumaktion ist irreversibel.

Vorhandene Schutzmechanismen, die erhalten bleiben müssen:

Mechanismus Ort Verhalten
Workspace-Einsperrung WorkspacePath.Resolve absolute Pfade, .., ADS (:) verboten
Zugriffsstufen IsActionAllowed delete im Shared nur bei sharedAccessLevel = Admin
Geschützte Pfade IsPathProtected protectedPaths sind append-only, kein Löschen
Datei-Locks GetFileLock pro Pfad ein SemaphoreSlim

2. Zielverhalten

delete      → verschiebt nach .trash/<yyyyMMdd_HHmmss>/<originalpfad>
list_trash  → listet Papierkorb-Einträge mit Originalpfad und Löschzeitpunkt
restore     → holt einen Eintrag an seinen Originalpfad zurück
purge       → löscht endgültig (nur Admin, ausdrücklicher Opt-in)

Papierkorb-Layout (im jeweiligen Workspace-Root):

.trash/
├── 20260805_142233/
│   ├── _meta.json          ← Originalpfad, Zeitpunkt, Agent, Typ
│   └── notizen.md          ← der gelöschte Inhalt (Datei oder Verzeichnis)
└── 20260805_151002/
    ├── _meta.json
    └── alte-recherche/

_meta.json:

{
  "originalPath": "recherche/notizen.md",
  "workspace": "personal",
  "deletedAt": "2026-08-05T14:22:33Z",
  "deletedBy": "senior_developer",
  "type": "file"
}

Der Zeitstempel ist der Ordnername — damit braucht der Cleanup-Job keine Metadatei zu lesen, um das Alter zu bestimmen (_meta.json ist Komfort, keine Voraussetzung). Bei Kollision wird _1, _2 … angehängt, analog HandleStockAddAsync.


3. Sicherheitsanforderungen

Diese Punkte sind nicht optional — ohne sie öffnet der Papierkorb neue Lücken:

  1. .trash ist für alle direkten Aktionen gesperrt. read, write, append, delete, copy mit einem Pfad in .trash/ werden abgelehnt. Sonst wäre der Papierkorb ein Ablageort, über den die Endungsprüfung (personalAllowedExtensions) umgangen werden kann: eine .exe „löschen" und aus dem Papierkorb an beliebiger Stelle wieder herausholen. Zugriff ausschließlich über list_trash / restore / purge.

  2. list blendet .trash aus. Sonst verschmutzt der Papierkorb jede Verzeichnisauflistung und damit den Kontext des Agenten.

  3. Geschützte Pfade bleiben unantastbar. Die bestehende Prüfung in HandleDeleteAsync greift vor dem Verschieben — ein protectedPath wandert auch nicht in den Papierkorb.

  4. restore prüft das Ziel erneut vollständig: WorkspacePath.Resolve auf den Originalpfad, Endungsprüfung, IsPathProtected, IsActionAllowed. Der Originalpfad aus _meta.json ist Eingabedatum, keine Wahrheit — eine manipulierte Metadatei darf keinen Ausbruch ermöglichen.

  5. Shared Workspace: ein Papierkorb je Agent.trash/<agentId>/<zeitstempel>/. Sonst sieht und restauriert Agent A die gelöschten Dateien von Agent B, und purge eines Agenten trifft alle. Im Personal Workspace entfällt die Ebene (dort ist nur ein Agent).

  6. purge nur bei sharedAccessLevel = Admin (Shared) bzw. explizit im Personal Workspace. Der Regelweg zum endgültigen Löschen ist der Cleanup-Job, nicht der Agent.

  7. Kein Verschieben über Laufwerksgrenzen annehmen. Directory.Move scheitert, wenn Workspace und .trash auf verschiedenen Volumes lägen — das ist per Konstruktion nicht der Fall (.trash liegt im Workspace-Root), aber der Fehlerfall wird sauber gemeldet statt zu einer Teilkopie zu führen.


4. Cleanup-Job

FileRWTool implementiert zusätzlich IToolJobProvider (src/ClawdDotNet.Core/Tools/IToolJobProvider.cs). Die Infrastruktur ist vollständig vorhanden — ToolJobScheduler kennt Cron, RunOnStart und manuelles Auslösen; es braucht keinen neuen Scheduler.

public IReadOnlyList<ToolJobDefinition> GetJobDefinitions() =>
[
    new("filerw_trash_cleanup", "Papierkorb aufräumen",
        "Löscht Papierkorb-Einträge, die älter als retentionDays sind")
];

ExecuteJobAsync bekommt workspacePath und agentId bereits übergeben (siehe Signatur des Interfaces). Der Job:

  1. liest retentionDays aus toolConfig (Default 14, Minimum 1),
  2. durchläuft .trash/* in Personal- und Shared-Workspace,
  3. löscht Ordner, deren Zeitstempel älter als die Aufbewahrungsfrist ist,
  4. gibt immer ToolJobResult.NoAction(logSummary) zurück — der Agent wird nie geweckt. Aufräumen ist kein Ereignis, das ein LLM-Run wert wäre (und kein Token kosten soll).

Der Shared-Papierkorb wird nur bereinigt, wenn der Job-Agent dort Admin-Rechte hat; andernfalls nur der eigene Unterordner. Damit räumt nicht jeder Agent bei jedem Tick fremde Einträge ab.

Beispielkonfiguration im Agenten:

"toolJobs": [
  {
    "jobId": "trash-daily",
    "toolName": "FileRW",
    "jobTypeId": "filerw_trash_cleanup",
    "cron": "0 3 * * *",
    "enabled": true,
    "runOnStart": false
  }
]

Achtung Zeitzone: ToolJobScheduler.RunToolJobAsync rechnet mit DateTime.Now (lokal), die Papierkorb-Zeitstempel oben sind UTC. Das Altersvergleich im Job daher konsequent in UTC durchführen (DateTime.UtcNow), nicht mischen. Dieselbe Falle ist bereits aus der Watchdog-Integration bekannt.


5. Umsetzungsschritte

Slice 1 — Papierkorb im Tool (Kern)

  • TrashPath-Helfer analog WorkspacePath: baut und validiert .trash-Pfade, kapselt die Agent-Ebene im Shared Workspace
  • HandleDeleteAsync auf Verschieben umstellen (Datei und Verzeichnis)
  • .trash-Sperre in GetAndValidatePath (greift für alle direkten Aktionen)
  • .trash aus HandleListAsync filtern
  • _meta.json schreiben (über AtomicFile, wie beim Stock-Index)
  • Tests: tests/ClawdDotNet.Tools.Tests/FileRW/TrashTests.cs

Slice 2 — list_trash / restore / purge

  • Drei Aktionen im InputSchema und im switch ergänzen
  • restore mit vollständiger Zielprüfung (siehe 3.4)
  • purge mit Admin-Prüfung
  • Description des Tools ergänzen, damit das LLM den Papierkorb kennt
  • Tests: Restore an geschützten Pfad, Restore mit manipulierter _meta.json, Restore mit inzwischen belegtem Zielpfad

Slice 3 — Cleanup-Job

  • IToolJobProvider an FileRWTool
  • retentionDays in FileRWToolSettings (Models/ToolSettingsViewModels.cs:68) + Designer-Property
  • Job-Definition in der UI auswählbar (läuft über die bestehende Job-Verwaltung, kein neuer Dialog)
  • Tests mit FakeTimeProvider (tests/ClawdDotNet.Core.Tests/Infrastructure/FakeTimeProvider.cs)

Slice 4 — Dokumentation

  • docs/ToolDevelopmentGuide.md: FileRW-Abschnitt (Zeile ~221) auf die neuen Aktionen aktualisieren
  • Prompt-Hinweis für Agenten: „Löschen ist reversibel, der Papierkorb wird nach X Tagen geleert" — sonst bleibt der Agent unnötig vorsichtig

6. Folgeentscheidung: Staging herunterstufen

StagingPolicy.DefaultRules (src/ClawdDotNet.Core/Staging/StagingPolicy.cs) führt FileRW.delete heute als Approve — jede Aufräumaktion braucht eine menschliche Freigabe. Das ist richtig, solange Löschen endgültig ist.

Mit dem Papierkorb ist es das nicht mehr. Empfehlung nach Slice 2:

["FileRW.delete"] = StagingDecision.Auto,   // reversibel über .trash
["FileRW.purge"]  = StagingDecision.Approve // endgültig → Freigabe

Damit kann der Agent selbstständig aufräumen (genau der Wunsch aus der Ideensammlung), ohne dass irreversible Aktionen ungefragt durchgehen.


7. Abnahmekriterien

  • Ein gelöschter Ordner liegt vollständig im Papierkorb und ist per restore wiederherstellbar.
  • read/write auf einen .trash-Pfad wird abgelehnt.
  • restore einer .exe in einen Workspace mit allowedExtensions ohne .exe wird abgelehnt.
  • Ein protectedPath lässt sich weiterhin nicht löschen.
  • Agent B sieht im Shared-Papierkorb nicht die Einträge von Agent A.
  • Nach Ablauf von retentionDays ist der Eintrag beim nächsten Job-Tick weg, ohne dass ein LLM-Run stattgefunden hat.