feat(ui): complete Avalonia UI port with 7 main pages, tool settings & top MenuBar

This commit is contained in:
Richard
2026-08-10 10:48:34 +02:00
parent a0e18d2a57
commit b5bf97ae74
187 changed files with 20054 additions and 882 deletions
+108
View File
@@ -0,0 +1,108 @@
# Staging-Freigabe für irreversible Aktionen
Setzt A2 aus der [Roadmap](Roadmap.md) um (F-A1 + S4). Irreversible Aktionen werden
**gestaged statt ausgeführt**: Vorschlag → Review im Hauptfenster → Freigabe/Ablehnung.
Aufbauend auf dem Taskboard (A1, Fortsetzung nach Freigabe) und dem Audit-Log (A3,
Approval-Records).
Sicherheitswirkung: Eine Prompt-Injection (K2) kann dann nur noch einen **Vorschlag**
erzeugen, keine Ausführung.
## Das Gate wird zum Durchsetzungspunkt (S4)
Das bisher wirkungslose `PermissionGate` (es prüfte nur, ob ein Tool zugewiesen ist)
wird zur zentralen Stelle: Policy-Prüfung, Staging-Entscheidung und Audit-Hook an
**einer** Stelle (`AgentEngine.ExecuteToolCallAsync`), statt ad-hoc in jedem Tool. S4
geht hier auf.
## Policy: `auto | approve | deny` pro Tool/Aktion
Je (Tool, Aktion) eine Entscheidung:
- **`auto`** — läuft wie bisher.
- **`approve`** — wird gestaged; die Ausführung wartet auf eine menschliche Freigabe.
- **`deny`** — wird gar nicht erst vorgeschlagen, sondern abgelehnt.
Auflösung vom Speziellen zum Allgemeinen: `Tool.Aktion``Tool` → Standard (`auto`).
Die „Aktion" ist das `action`-Argument des Aufrufs (die meisten Tools haben es).
**Eingebaute Standardregeln** (nach Sichtung der Tools, überschreibbar per Konfiguration)
— genau die in der Roadmap genannten irreversiblen Aktionen:
| Tool.Aktion | Standard |
|---|---|
| `Mail.send` | approve |
| `Telegram.send_message` | approve |
| `Database.insert`, `Database.upsert` | approve |
| `FileRW.delete` | approve |
| `FTP.upload`, `FTP.delete` | approve |
| alles andere | auto |
Lesende Aktionen (`Mail.read_inbox`, `FileRW.read`, `Database.query`, …) bleiben `auto`
Staging soll schützen, nicht lähmen.
## Plan-Freeze
Freigegeben wird ein **eingefrorener, konkreter Aufruf**: Tool, Aktion und die **exakten
Argumente** zum Zeitpunkt des Stagings. Ausgeführt wird genau das Eingefrorene (der
gespeicherte Argument-JSON), nie eine nachträglich veränderte Fassung. Jede Änderung
wäre eine neue Freigabe.
Das ist auch der Grund, warum der Agent den Aufruf **nicht** nach der Freigabe erneut
formuliert (er könnte etwas anderes bauen) — der eingefrorene JSON wird direkt an das
Tool gegeben.
## Fortsetzung nach Freigabe — kein pausierter Lauf
Die tragende Architektur-Vorgabe (aus der Roadmap-Einstufung): **Kein pausierter, im
Speicher gehaltener Lauf.** Der Ablauf:
1. **Vorschlag.** Der Agent ruft eine `approve`-Aktion auf. Das Gate führt sie nicht aus,
sondern legt einen **Pending**-Datensatz an (eingefrorener Aufruf) und gibt dem Agenten
„Zur Freigabe vorgelegt (#id)" als Tool-Ergebnis zurück. Der Lauf endet **regulär**
der Agent schließt ab, nichts hängt im Speicher.
2. **Review.** Ein Mensch sieht die offenen Vorschläge im Hauptfenster und entscheidet.
3a. **Freigabe.** Der eingefrorene Aufruf wird **direkt** ausgeführt (standalone, nicht
über einen Chat-Lauf). Das Ergebnis wird festgehalten, und ein **Folge-Task** (A1)
weckt den Agenten: „Deine Aktion X wurde freigegeben und ausgeführt, Ergebnis: Y —
mach weiter." Der Scanner stellt ihn zu (`ChatAsync`, bestehender Kontext).
3b. **Ablehnung.** Ein Folge-Task weckt den Agenten mit der Ablehnung (samt Grund).
Echtes Suspend/Resume eines laufenden `ChatAsync` wäre Fable-Terrain — und ist mit dieser
Vereinfachung unnötig.
## Approval-Records (A3)
Jede Entscheidung — Freigabe wie Ablehnung — wird an zwei Stellen verankert:
- Im **Staging-Datensatz** selbst: Status, `DecidedBy`, `DecidedAt`, Ergebnis-Verweis
bzw. Ablehnungsgrund.
- Als **Audit-Eintrag** (A3): Tool, Ausgang, `source: approval`, „Freigegeben von …" bzw.
„Abgelehnt von …". Die Herkunft stempelt auch hier das System, nicht der Agent.
Der Vorschlag selbst wird beim Anlegen als Audit-Eintrag mit Status **`Staged`** notiert —
so ist die ganze Kette (Vorschlag → Entscheidung → Ausführung) im Log nachvollziehbar.
## Nebenläufigkeit
Zwei Reviewer dürfen nicht denselben Vorschlag doppelt freigeben. Der Übergang
`pending → approved/rejected` ist ein **atomares, bedingtes `UPDATE`** (dieselbe
Claim-Technik wie beim Taskboard): Genau einer gewinnt, der zweite Klick läuft ins Leere.
Erst nach gewonnenem Übergang wird der eingefrorene Aufruf ausgeführt.
## Verdrahtung
- `StagingGate` (Policy + Anlegen des Vorschlags) hängt optional an der Engine — ohne es
läuft alles wie bisher (`auto`).
- Der eingefrorene Aufruf wird über `IFrozenCallExecutor` (von der Engine implementiert)
ausgeführt: gültiger Tool-Kontext, aber ohne LLM-Schleife.
- `StagingService` (Freigabe/Ablehnung) nutzt Executor, Audit und das Taskboard für den
Folge-Task. Es ist die API, die die Review-Oberfläche aufruft.
## Offen
- **Review-Oberfläche** im Hauptfenster (Liste der offenen Vorschläge, Freigeben/Ablehnen)
— die Dienst-API steht bereit; die WinForms-Ansicht ist die verbleibende Integration.
- **Output-Scrubbing** greift auch hier auf den gespeicherten Argument-JSON, sobald es
steht (eigener Roadmap-Punkt).
- **Orders** (Handelsaufträge) reihen sich später als weitere `approve`-Aktionen ein.