Sicherungspunkt vor dem Aufraeumen. Buendelt die Arbeit, die seit dem Abschluss der Avalonia-Portierung im Arbeitsverzeichnis lag. Oberflaeche - Entwurf aus Mockup/ umgesetzt: Theme.axaml (Farben je Thema, Barlow als mitgelieferte Schrift), Icons.axaml (Symbolgeometrien), Shell.axaml (eigene ControlThemes statt Fluent umzufaerben). - Neue Steuerelemente StrokeIcon und BlueprintFrame, Seiten fuer Token-Verbrauch und Agenten-Chats, Werkzeug-Einstellungen als Seite statt eigenem Fenster, Texteditor-Fenster. - ThemeManager mit hellem und dunklem Thema; die beiden Pinsel-Konverter entfallen, weil ein fester Farbwert den Themenwechsel nicht ueberlebt. Rocket.Chat - Neues Tool-Projekt (Client, Konfiguration, Workspace-Dateien) nach der Bauform des Telegram-Tools: rocketchat_poll als Tool-Job, geweckt wird nur, wenn wirklich etwas anliegt. - send_file ist freigabepflichtig, send_message bewusst nicht: Der Raum ist Arbeitsraum, der Schutz sitzt an der Raum-Allowlist. - Konzept-Doc um die Messung gegen die echte Instanz 8.7 ergaenzt; drei Annahmen waren falsch und sind korrigiert. Deploymentcenter - DC6 (Update anwenden) und DC7 (Erstinstallation ueber setup.json) erledigt, DC3 fuer win-x64/dev; deploy/publish.py als Release-Strecke. - AppHost.DisposeAsync gegen doppeltes Herunterfahren gesperrt - sonst ueberschreibt eine zweite Abmeldung den Wartungszustand am Watchdog. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
759 lines
39 KiB
Markdown
759 lines
39 KiB
Markdown
# Rocket.Chat und Nextcloud — Konzept
|
||
|
||
Zwei neue Tools, ein gemeinsamer Zweck: **Rocket.Chat** wird der Ort, an dem wir mit den
|
||
Agenten reden; **Nextcloud** wird der Ort, an dem die Agenten uns Ergebnisse hinlegen.
|
||
Der typische Ablauf ist die Kombination aus beidem — „schreib mir die Auswertung und leg
|
||
sie in die Cloud" im Chat, Datei in Nextcloud, Link zurück in den Chat.
|
||
|
||
Dieses Dokument prüft die Machbarkeit, legt den Schnitt fest und benennt die Punkte, die
|
||
vor der Umsetzung entschieden werden müssen. **Es ist noch keine Umsetzungsfreigabe.**
|
||
|
||
Verwandt: [Taskboard-Konzept](Taskboard-Konzept.md) (Scanner/Wake), [Staging-Konzept](Staging-Konzept.md)
|
||
(Freigaben), [Audit-Konzept](Audit-Konzept.md), [Roadmap](Roadmap.md) (A5 — siehe Konflikt unten).
|
||
|
||
---
|
||
|
||
## 0 — Kurzfassung des Befunds
|
||
|
||
| Frage | Antwort |
|
||
|---|---|
|
||
| Ist es umsetzbar? | Ja, beides. Ohne neue Architektur — die vorhandenen Bausteine tragen. |
|
||
| Braucht es Änderungen am Core? | Für Phase 1: **nein**, nur zwei neue Tool-Projekte + Staging-Defaults. Für den automatischen Rückweg (Antwort landet ohne Zutun des Modells im Raum) und den Notfallkanal: ja, zwei kleine Core-Ergänzungen. |
|
||
| Größtes technisches Risiko | Nicht die API — sondern **Antwort-Schleifen zwischen Agenten** und **Kosten durch zu häufiges Wecken**. |
|
||
| Größte Konzeptkollision | Roadmap **A5** sieht Matrix/Element für genau diesen Zweck vor. Rocket.Chat ersetzt A5, oder wir haben zwei Chat-Wege. Muss entschieden werden. |
|
||
| „Agent erstellt Dokument direkt über die Nextcloud-API" | So nicht. Nextcloud hat keine API, die Inhalte *erzeugt*. Der Weg ist: Datei lokal im Workspace erzeugen → hochladen. Für PDF/XLSX kann **Collabora als Konverter** dienen — das ist der elegante Teil, siehe 5.4. |
|
||
|
||
---
|
||
|
||
## 1 — Was schon da ist (und deshalb nicht neu gebaut wird)
|
||
|
||
Der Rückkanal von außen nach innen existiert vollständig:
|
||
|
||
```
|
||
TaskScanner (60-s-Takt)
|
||
└─ Task vom Typ tool_job
|
||
└─ EngineTaskDispatcher.DispatchToolJobAsync
|
||
└─ IToolJobProvider.ExecuteJobAsync ← kein LLM, kostenlos
|
||
└─ ToolJobResult.Wake(text) ← nur wenn wirklich etwas da ist
|
||
└─ AgentEngine.ChatAsync ← hier erst kostet es Tokens
|
||
```
|
||
|
||
Das Telegram-Tool nutzt genau das (`telegram_poll`). **Rocket.Chat bekommt dieselbe
|
||
Bauform** — `rocketchat_poll`. Damit gilt automatisch:
|
||
|
||
- Zustand (letzter gesehener Zeitpunkt) über `IStateStore`, überlebt Neustarts.
|
||
- Ein Takt ohne neue Nachricht kostet nichts.
|
||
- Kein eigener Thread, kein eigener Scheduler, keine Sonderbehandlung beim Start.
|
||
- Jeder Tool-Aufruf läuft ohnehin durch `StagingGate` (A2) und Audit (A3).
|
||
|
||
Ebenso vorhanden und wiederverwendbar:
|
||
|
||
- **Pro-Agent-Konfiguration** (`AgentConfig.Tools["RocketChat"]`) — jeder Agent bekommt
|
||
seine eigenen Zugangsdaten, ohne dass ein Agent die eines anderen sehen kann.
|
||
- **`ConfigSecrets`** verschlüsselt Felder nach Namen (`token`, `password`, `apikey` …) —
|
||
ein Feld namens `authToken` bzw. `appPassword` ist automatisch geschützt.
|
||
- **Workspace-Prefixe** `personal:` / `shared:` samt Path-Traversal-Prüfung — aus dem
|
||
FTP-Tool wortgleich übernehmbar für Nextcloud-Uploads.
|
||
|
||
---
|
||
|
||
## 2 — Rocket.Chat: Machbarkeit
|
||
|
||
Geprüft gegen die REST- und Realtime-API von Rocket.Chat. Alles Folgende ist
|
||
Standardfunktion einer selbstgehosteten Instanz, kein Enterprise-Feature.
|
||
|
||
### 2.0 Prüfung gegen die echte Instanz (Rocket.Chat 8.7, August 2026)
|
||
|
||
Alles unten Stehende wurde gegen die Testinstanz gemessen, nicht aus der Dokumentation
|
||
übernommen. **Drei Annahmen waren falsch** — sie sind hier korrigiert.
|
||
|
||
**Bestätigt:**
|
||
|
||
| Prüfung | Ergebnis |
|
||
|---|---|
|
||
| `subscriptions.get` als Sammelabruf | liefert je Raum `rid`, `t`, `name`, `unread`, `userMentions`, `groupMentions`, `alert` — genau der Vorfilter, auf dem der Poll steht |
|
||
| `chat.postMessage`, auch mit `tmid` (Thread) | funktioniert |
|
||
| `channels.history` / `groups.history` / `im.history` mit `oldest` | funktioniert; Raumart bestimmt den Endpunkt |
|
||
| `subscriptions.read` | funktioniert; danach steht `ls` und `unread` fällt auf 0 |
|
||
| `im.create`, `groups.create`, Senden in privaten Gruppen | funktioniert |
|
||
| Erwähnungen | kommen als **`mentions[]`-Feld mit Benutzernamen** — der Erwähnungsfilter braucht kein Textparsen |
|
||
| Unbekannter Raum | `400 [invalid-channel]` |
|
||
| `users.create` mit Rolle `bot` | funktioniert (Admin) |
|
||
|
||
**Korrekturen:**
|
||
|
||
1. **Berechtigungsnamen.** Sie heißen `create-personal-access-tokens` (Rollen: `admin`,
|
||
`user`) und `user-generate-access-token` (Rolle: `admin`) — nicht wie zuvor notiert.
|
||
2. **Ein Admin kann *kein* Token für einen fremden Benutzer prägen.**
|
||
`users.generatePersonalAccessToken` lehnt `userId` ab („must NOT have additional
|
||
properties") — der Endpunkt gilt nur für den aufrufenden Benutzer.
|
||
`users.createToken` verlangt ein `secret`, für das es in dieser Instanz keine
|
||
Einstellung gibt. Beide Wege sind zu.
|
||
3. **Systemnachrichten.** Die Historie liefert auch Ereignisse wie „Benutzer beigetreten"
|
||
(Feld `t`, z. B. `uj`). Ohne Filter antwortet ein Agent auf einen Raumbeitritt. Im
|
||
Tool umgesetzt und geprüft.
|
||
|
||
**Offen geblieben:** Das Ratenlimit (`API_Enable_Rate_Limiter` = an, 10 Aufrufe/60 s)
|
||
griff bei 14 schnellen Aufrufen **nicht** — Administratoren umgehen es. Für einen
|
||
Benutzer mit reiner `bot`-Rolle ist es damit **nicht** gemessen. Der Entwurf bleibt mit
|
||
einem Sammelabruf je Takt weit darunter; nachzumessen, sobald ein Agenten-Benutzer
|
||
nutzbar ist.
|
||
|
||
### 2.0.1 Der Stolperstein: 2FA verhindert die automatische Bereitstellung
|
||
|
||
Ein frisch per API angelegter Benutzer **kann sich nicht anmelden**: Rocket.Chat antwortet
|
||
mit `totp-required` und schickt einen Code per E-Mail. Ursache ist
|
||
`Accounts_TwoFactorAuthentication_By_Email_Auto_Opt_In` (in der Testinstanz aktiv) — jeder
|
||
neue Benutzer bekommt E-Mail-2FA automatisch.
|
||
|
||
Geprüft und ausgeschlossen: `users.update` kennt kein Feld dafür („must NOT have
|
||
additional properties"), `users.resetTOTP` betrifft nur App-basiertes TOTP.
|
||
**Es gibt keinen Weg über die Admin-API, das E-Mail-2FA eines einzelnen Benutzers
|
||
abzuschalten.**
|
||
|
||
Damit stehen drei Wege offen — die Entscheidung gehört dir, weil sie eine
|
||
Sicherheitseinstellung berührt:
|
||
|
||
| Weg | Ablauf | Preis |
|
||
|---|---|---|
|
||
| **A (empfohlen)** | `Auto_Opt_In` global auf **aus**, dann Benutzer anlegen (Rollen `bot` + `user`), als dieser anmelden, PAT erzeugen, Passwort verwerfen | Neue **menschliche** Benutzer bekommen E-Mail-2FA dann nicht mehr automatisch. Bestehende Konten und TOTP bleiben unberührt |
|
||
| **B** | Setting bleibt; jeder Agent braucht ein echtes Postfach, ClawdDotNet holt den 2FA-Code per IMAP (das Mail-Tool kann das) | Funktioniert, ist aber ein zerbrechlicher Umweg |
|
||
| **C** | Halbautomatisch: API legt den Benutzer an, ein Mensch erzeugt das PAT einmalig in der Oberfläche | Kein „HR-Agent" möglich — bei jedem neuen Agenten Handarbeit |
|
||
|
||
**Weg A ist umgesetzt und gemessen** (August 2026): Nach dem Abschalten von `Auto_Opt_In`
|
||
läuft die Kette vollständig durch —
|
||
|
||
```
|
||
users.create (Rollen bot + user) → login als dieser Benutzer →
|
||
users.generatePersonalAccessToken → PAT
|
||
```
|
||
|
||
Damit ist ein „HR-Agent", der einen neuen Agenten samt Chat-Konto einrichtet, technisch
|
||
möglich. Die Rolle `user` ist dabei nötig, **nicht** nur `bot`: Nur sie bringt die
|
||
Berechtigung `create-personal-access-tokens` mit.
|
||
|
||
Ein Benutzer, der **vor** der Umstellung angelegt wurde, behält sein 2FA-Flag dauerhaft —
|
||
er lässt sich nicht nachträglich retten und muss neu angelegt werden.
|
||
|
||
Ob sich `Auto_Opt_In` nach der Bereitstellung wieder einschalten lässt, ohne die
|
||
bestehenden Agenten zu verlieren, ist plausibel (das Flag wird beim Anlegen gesetzt), aber
|
||
weiterhin **nicht gemessen**.
|
||
|
||
Für das Tool selbst ist die Frage folgenlos: Es nimmt `userId` und `authToken` aus der
|
||
Konfiguration entgegen, gleich woher sie stammen.
|
||
|
||
### 2.0.2 Zwei Fehler, die erst der Live-Test zeigte
|
||
|
||
Beide wären in keinem Schreibtischtest aufgefallen und sind behoben:
|
||
|
||
1. **`unread` zählt in dieser Instanz nur Erwähnungen.** Eine gewöhnliche Nachricht setzt
|
||
allein `alert=true`. Schwerer wiegt: `userMentions` ist ein Zähler über *ungelesene*
|
||
Erwähnungen und bleibt stehen, solange nichts gelesen wurde. Ein einziger alter,
|
||
ungelesener Ruf machte den Raum damit dauerhaft „heiß" — und anschließend wurde **jede**
|
||
weitere Nachricht ausgeliefert, auch ohne Erwähnung.
|
||
*Behoben:* Die Erwähnungsprüfung sitzt jetzt an der einzelnen Nachricht; der
|
||
Raum-Zähler ist nur noch ein billiger Vorfilter. Zusätzlich wird jeder geprüfte Raum als
|
||
gelesen markiert, auch wenn nichts zu wecken war — sonst veralten die Zähler.
|
||
2. **Die Startmarke verschluckte die erste echte Nachricht.** Ein Raum wird erst dann zum
|
||
Kandidaten, wenn Verkehr da ist — genau dann setzte der alte Code aber „Marke auf jetzt,
|
||
nichts wecken". Die auslösende Nachricht ging verloren.
|
||
*Behoben:* Beim ersten Abruf eines Raums wird begrenzt zurückgeschaut
|
||
(`initialLookbackMinutes`, Standard 5) statt zu überspringen.
|
||
|
||
### 2.1 Identität — ein echter Benutzer je Agent
|
||
|
||
Die Anforderung „jeder Agent mit eigenem Benutzer, in Gruppen und im Direktkontakt" ist
|
||
der richtige Ansatz und wird von Rocket.Chat direkt unterstützt.
|
||
|
||
- Admin legt je Agent einen Benutzer an: `POST /api/v1/users.create`
|
||
(`{ name, username, email, password, roles: ["bot"] }`).
|
||
- Die Rolle **`bot`** ist wichtig: Sie markiert den Benutzer als Maschine (relevant für
|
||
Schleifenschutz, siehe 2.5) und wird in neueren Versionen bei der Sitzplatzzählung
|
||
nicht als normaler Nutzer gewertet. *Gegen die eigene Version zu prüfen.*
|
||
- Für jeden Agenten wird ein **Personal Access Token** erzeugt
|
||
(`POST /api/v1/users.generatePersonalAccessToken`, oder im Konto des Benutzers).
|
||
Dauerhaft gültig, einzeln widerrufbar — deutlich besser als Login mit Passwort, weil
|
||
kein Session-Ablauf und keine gespeicherten Passwörter im Spiel sind.
|
||
- Authentifiziert wird jeder Aufruf über zwei Header: `X-Auth-Token` und `X-User-Id`.
|
||
|
||
**Entscheidung, die ich empfehle:** Das Anlegen der Benutzer ist **kein Agenten-Tool**.
|
||
Es ist eine einmalige Einrichtungsfunktion in der WinForms-Oberfläche
|
||
(Instanz-Einstellungen → Rocket.Chat → „Agenten-Benutzer anlegen"). Sonst müsste ein
|
||
Agent ein Admin-Token halten — und ein Admin-Token in Reichweite einer Prompt-Injection
|
||
ist genau das, was A2 verhindern soll. Der Admin-Token liegt in der **Instanz**-Konfiguration,
|
||
nicht in einer Agenten-Tool-Konfiguration.
|
||
|
||
### 2.2 Ausgang — Nachrichten senden
|
||
|
||
| Zweck | Endpunkt |
|
||
|---|---|
|
||
| In Kanal/Gruppe/DM schreiben | `POST /api/v1/chat.postMessage` (`roomId` oder `channel`) |
|
||
| Auf eine Nachricht antworten (Thread) | dasselbe, mit `tmid` |
|
||
| Datei anhängen | `POST /api/v1/rooms.upload/{roomId}` (multipart) |
|
||
| Reaktion setzen | `POST /api/v1/chat.react` |
|
||
|
||
Gesendet wird als der Agenten-Benutzer — Direktnachrichten funktionieren dadurch echt und
|
||
nicht als „Bot mit Alias".
|
||
|
||
### 2.3 Eingang — der Poll-Weg (Phase 1)
|
||
|
||
Der sparsame Weg, ohne jede neue Infrastruktur:
|
||
|
||
1. `GET /api/v1/subscriptions.get?updatedSince=<zeitstempel>` — **ein einziger Aufruf**
|
||
liefert für diesen Agenten alle Räume mit Ungelesen-Zähler, Erwähnungs-Zähler und
|
||
„zuletzt gesehen"-Marke. Auch bei 50 Räumen bleibt es ein Aufruf.
|
||
2. Nur für Räume mit relevanten Neuigkeiten wird die Historie geholt:
|
||
`channels.history` (öffentlich) / `groups.history` (privat) / `im.history` (DM),
|
||
jeweils mit `oldest=<letzte gesehene Zeit>`.
|
||
3. `POST /api/v1/subscriptions.read` markiert gelesen — der Zähler geht zurück auf null.
|
||
|
||
Zustand im `IStateStore`: `rocketchat:{agentId}:lastCheck` sowie je Raum die zuletzt
|
||
verarbeitete Nachrichtenzeit.
|
||
|
||
**Rate-Limits:** Rocket.Chat begrenzt REST-Aufrufe (Standard in der Größenordnung von
|
||
10 Aufrufen je Minute und Endpunkt). Bei einem Takt von 30–60 Sekunden und einem
|
||
Sammelaufruf pro Takt ist das unkritisch — es ist aber der Grund, warum der Entwurf über
|
||
`subscriptions.get` sammelt statt jeden Raum einzeln zu pollen.
|
||
|
||
**Latenz:** Bei 60-Sekunden-Takt antwortet ein Agent im Mittel nach ~30 s plus Laufzeit.
|
||
Für Gespräche mit Agenten ist das spürbar, aber tragbar. Der Takt lässt sich pro Job
|
||
setzen (`*/1 * * * *` ist das Minimum des Cron-Modells; feiner ginge nur über die
|
||
Realtime-API).
|
||
|
||
### 2.4 Eingang — die Realtime-Variante (Phase 3, optional)
|
||
|
||
Rocket.Chat bietet eine WebSocket-/DDP-Schnittstelle (`wss://host/websocket`): nach
|
||
`login` mit dem Token abonniert man `stream-notify-user/{userId}/notification` und
|
||
bekommt DMs und Erwähnungen **sofort** gepusht, ohne Polling.
|
||
|
||
Das ist die richtige Endstufe (Antwortzeit ~1 s statt ~30 s), aber es ist eine dauerhafte
|
||
Verbindung je Agent mit Wiederverbindungs-Logik — also eine echte Komponente, keine
|
||
Ergänzung eines Tools. Vorschlag: **erst nachrüsten, wenn Phase 1 im Alltag steht** und
|
||
sich die Verzögerung tatsächlich stört.
|
||
|
||
Eine dritte Möglichkeit — Rocket.Chats *Outgoing Webhook* auf unsere vorhandene
|
||
`ClawdDotNetApi` (Port 5082) — wäre die einfachste Push-Lösung, setzt aber voraus, dass
|
||
der Rocket.Chat-Server den Windows-Rechner über das Netz erreicht. Das ist eine Frage
|
||
deiner Netztopologie und keine der Software. Falls erreichbar: der kürzeste Weg zu
|
||
niedriger Latenz.
|
||
|
||
### 2.5 Die zwei echten Fallen
|
||
|
||
Diese beiden Punkte sind wichtiger als jede API-Frage.
|
||
|
||
**(a) Mehrere Agenten im selben Raum.** Wenn drei Agenten denselben Gruppenchat pollen,
|
||
antworten drei Agenten auf jede Nachricht. Regel im Entwurf:
|
||
|
||
> Ein Agent wird nur geweckt bei (1) Direktnachrichten an ihn oder (2) Nachrichten, die
|
||
> ihn per `@name` erwähnen. Alles andere liest er nicht einmal.
|
||
|
||
Ein Raum kann per Konfiguration auf `respondToAll: true` gestellt werden — das ist die
|
||
bewusste Ausnahme für einen Raum mit genau einem Agenten.
|
||
|
||
**(b) Agenten-Schleifen.** Agent A schreibt, Agent B wird geweckt, antwortet, weckt A —
|
||
und das läuft, bis das Tagesbudget greift. Der `LoopGuard` schützt nur *innerhalb* eines
|
||
Laufs, nicht über Agenten hinweg. Regel im Entwurf:
|
||
|
||
> Nachrichten von Benutzern mit der Rolle `bot` werden **ignoriert**, außer der Agent ist
|
||
> namentlich erwähnt. Zusätzlich eine Drossel: höchstens N Weckvorgänge je Raum und
|
||
> Stunde (Zähler im `IStateStore`), danach schweigt der Agent in diesem Raum bis zur
|
||
> nächsten Stunde und protokolliert das.
|
||
|
||
Das Tagesbudget (K5) ist das letzte Netz, nicht das erste.
|
||
|
||
### 2.6 Der Rückweg der Antwort
|
||
|
||
Der Wake-Mechanismus liefert die Nachricht *in* den Agenten. Seine Antwort geht heute in
|
||
den Chat-Verlauf, nicht zurück nach Rocket.Chat. Zwei Wege:
|
||
|
||
- **(a) Der Agent antwortet selbst** — die Weck-Nachricht enthält die `roomId` und die
|
||
Anweisung, mit `RocketChat.send_message` zu antworten. Kein Core-Eingriff, funktioniert
|
||
sofort. Schwäche: Es hängt daran, dass das Modell es tut. Erfahrungsgemäß klappt das
|
||
gut, aber nicht in 100 % der Fälle.
|
||
- **(b) Automatischer Rückweg** — der Tool-Job merkt sich „Antwort gehört nach Raum X",
|
||
und der Dispatcher schickt die Abschlussnachricht des Laufs dorthin. Zuverlässig, aber
|
||
es braucht einen kleinen Haken in `ToolJobResult`/`EngineTaskDispatcher`
|
||
(etwa ein `ReplyTo`-Feld, das der Dispatcher nach dem Lauf an dasselbe Tool zurückgibt).
|
||
|
||
**Empfehlung:** (a) in Phase 1, (b) in Phase 2 nachziehen — denn (b) ist der Unterschied
|
||
zwischen „meistens antwortet er" und „er antwortet". Für die Hauptkommunikationsschiene
|
||
ist das am Ende nicht optional.
|
||
|
||
### 2.7 Sicherheit
|
||
|
||
- **Nachrichten aus Rocket.Chat sind fremder Text.** Sie müssen als
|
||
`<untrusted_content>` gerahmt in den Kontext (Roadmap K2-Rest). Bei Telegram fehlt das
|
||
bis heute; hier sollte es von Anfang an drin sein, weil Gruppenchats mehrere Absender
|
||
haben.
|
||
- **Raum-Allowlist** je Agent (`allowedRooms`), analog `allowedChatIds` beim Telegram-Tool.
|
||
- **Staging-Vorschlag** (siehe 6): Senden in erlaubte Räume `auto`, alles darüber hinaus
|
||
`approve`.
|
||
- Zugangsdaten heißen im Konfigurationsfeld `authToken` → `ConfigSecrets` verschlüsselt sie
|
||
automatisch. Der Admin-Token der Instanz muss in `ConfigSecrets.Apply(InstanceConfig)`
|
||
ergänzt werden.
|
||
- **TLS** ist Pflicht; selbstsignierte Zertifikate ausdrücklich konfigurieren müssen statt
|
||
Validierung generell abschalten.
|
||
|
||
### 2.8 Tool-Zuschnitt
|
||
|
||
**Umgesetzt** (`src/ClawdDotNet.Tools.RocketChat`, gegen die Testinstanz geprüft):
|
||
|
||
```
|
||
Tool: RocketChat
|
||
Aktionen: send_message | reply | send_file | list_rooms | read_room | mark_read
|
||
Job: rocketchat_poll
|
||
```
|
||
|
||
Noch nicht umgesetzt: `search`.
|
||
|
||
**Dateiversand.** `send_file` schickt eine Datei aus dem Workspace direkt in einen Raum
|
||
oder als Direktnachricht — der kurze Weg für „stell mir das zusammen und schick es rüber",
|
||
ohne Umweg über die Cloud. Pfade tragen dieselben Prefixe wie bei FileRW und FTP
|
||
(`personal:` / `shared:` / ohne Prefix), samt Prüfung gegen einen Ausbruch aus dem
|
||
Verzeichnis. Grenze über `maxUploadMb` (Standard 25, Serverseite erlaubt 100).
|
||
|
||
Dabei zeigte sich die **dritte Doku-Korrektur**: Der Ein-Schritt-Endpunkt `rooms.upload`
|
||
existiert in 8.7 nicht mehr — er antwortet mit einem nackten HTML-404. Aktuell ist ein
|
||
zweistufiger Ablauf:
|
||
|
||
1. `rooms.media/:rid` nimmt die Datei als `multipart/form-data` und liefert eine Datei-Id;
|
||
sichtbar ist damit noch **nichts**.
|
||
2. `rooms.mediaConfirm/:rid/:fileId` veröffentlicht sie als Nachricht.
|
||
|
||
Ohne den zweiten Schritt liegt die Datei hochgeladen, aber unsichtbar auf dem Server.
|
||
Voraussetzung ist damit Rocket.Chat 6.x oder neuer; einen Rückfall auf `rooms.upload` für
|
||
ältere Instanzen gibt es bewusst nicht, weil er sich hier nicht prüfen ließe.
|
||
|
||
Anders als `send_message` steht `RocketChat.send_file` in der Staging-Policy auf
|
||
**`approve`**: Eine Nachricht formuliert der Agent, eine Datei verlässt den Workspace als
|
||
Ganzes. Wer das im Alltag als zu hinderlich empfindet, streicht die eine Zeile in
|
||
`StagingPolicy.DefaultRules` — dann schützt weiterhin die Raum-Allowlist.
|
||
|
||
Geprüft gegen die Testinstanz (8 von 8): Markdown aus dem persönlichen Workspace · CSV aus
|
||
dem geteilten mit abweichendem Dateinamen · Datei per Direktnachricht · Ausbruchsversuch
|
||
`../` abgewiesen · absoluter Pfad abgewiesen · fehlende Datei · fehlender `localPath` ·
|
||
nicht freigegebener Raum.
|
||
|
||
### 2.9 Eingehende Dateien und Links
|
||
|
||
Was hereinkommt, ist genauso wichtig wie das, was hinausgeht — und stand anfangs nicht im
|
||
Entwurf.
|
||
|
||
**Dateien.** Eine Dateisendung trägt den Begleittext in `msg`, die Datei selbst aber
|
||
daneben in `files[]`. Wer nur `msg` liest, sieht bei einer reinen Dateisendung einen
|
||
**leeren Beitrag**. Weckmeldung und `read_room` führen Anhänge deshalb eigens auf:
|
||
|
||
```
|
||
[20:06] @richard (messageId: rD68…): schau dir die Zahlen bitte an.
|
||
Datei: auswertung.csv (text/csv, 77 B) — fileId: 6a821828ea0ad1bcab878f74
|
||
```
|
||
|
||
Mit dieser `fileId` holt `download_file` die Datei in den Workspace. Der Abruf geht gegen
|
||
`/file-upload/{fileId}/download` mit denselben Kopfzeilen wie die API — ohne sie antwortet
|
||
der Server mit 403 (`FileUpload_ProtectFiles` ist aktiv).
|
||
|
||
Schutzmaßnahmen, weil Name und Inhalt vom Absender bestimmt sind:
|
||
|
||
- Der Zielname wird auf den reinen Dateinamen reduziert; Pfadangaben darin verfallen.
|
||
Nur das Verzeichnis (`personal:` / `shared:`) darf der Agent wählen.
|
||
- Ausführbare Endungen (`.exe`, `.ps1`, `.bat`, `.jar`, …) werden abgelehnt, sofern nicht
|
||
`allowDangerousDownloads` gesetzt ist.
|
||
- Größengrenze `maxDownloadMb` (Standard 25) — geprüft an `Content-Length` **und**
|
||
während des Schreibens, da die Angabe fehlen darf.
|
||
- Geschrieben wird über eine `.part`-Nebendatei; bricht der Abruf ab, bleibt keine halbe
|
||
Datei liegen, die der Agent für vollständig hält.
|
||
|
||
Dabei fiel ein Windows-Fehler auf, der leicht zu übersehen ist: `Path.GetFileName` behält
|
||
`"shared:kopie.csv"` unverändert bei, weil `:` dort kein Pfadtrenner ist, sondern ein
|
||
**NTFS-Alternativdatenstrom** eingeleitet wird. Die Datei landete als Datenstrom am
|
||
Verzeichnis statt als eigene Datei. Der Präfix wird jetzt vor der Namensbereinigung
|
||
abgetrennt, und `WorkspaceFile` weist Doppelpunkte grundsätzlich ab.
|
||
|
||
**Links.** Rocket.Chat entpackt Links selbst und legt in `urls[].meta` Titel und
|
||
Beschreibung der Zielseite ab. Der Agent bekommt das mitgeliefert:
|
||
|
||
```
|
||
Link: https://www.rocket.chat/ — Rocket.Chat | Secure CommsOS™ …
|
||
```
|
||
|
||
Das erspart oft einen eigenen Seitenabruf. **Wichtig:** Diese Vorschau ist Text der
|
||
verlinkten Seite, also fremdbestimmt — sie steht deshalb wie alles andere innerhalb der
|
||
`<untrusted_content>`-Rahmung. Ein tatsächlicher Abruf der Seite bleibt Sache des
|
||
WebFetch-Tools und damit eine bewusste Entscheidung des Agenten, keine Nebenwirkung des
|
||
Empfangens.
|
||
|
||
Geprüft (9 von 9): Datei in der Weckmeldung samt `fileId` · Link mit Titel · Download in
|
||
den persönlichen und in den geteilten Workspace · Inhalt stimmt · Ausbruch über den
|
||
Dateinamen entschärft · gefährliche Endung abgelehnt · unbekannte `fileId` · fehlende
|
||
`fileId`.
|
||
|
||
Der Weckpfad ist gegen die Testinstanz durchgespielt (6 von 6 Erwartungen):
|
||
ruhiger Takt weckt nicht · Nachricht ohne Erwähnung weckt nicht · Erwähnung weckt ·
|
||
Direktnachricht weckt auch ohne Erwähnung · eigene Nachricht weckt nicht ·
|
||
private Gruppe weckt.
|
||
|
||
Konfiguration je Agent:
|
||
|
||
```json
|
||
"RocketChat": {
|
||
"baseUrl": "https://chat.example.org",
|
||
"userId": "aBcD…",
|
||
"authToken": "…", // von ConfigSecrets geschützt (Schlüssel "authtoken")
|
||
"username": "agent-hermes", // für den Erwähnungsfilter
|
||
"allowedRooms": ["GENERAL", "finanz-team", "richard"],
|
||
"defaultRoom": "finanz-team",
|
||
"mentionOnly": true,
|
||
"agentUsernames": ["agent-hermes", "agent-atlas"],
|
||
"maxWakesPerRoomPerHour": 12,
|
||
"maxMessagesPerRoom": 20,
|
||
"initialLookbackMinutes": 5,
|
||
"maxUploadMb": 25
|
||
}
|
||
```
|
||
|
||
**`allowedRooms` gilt auch für Direktnachrichten.** Ein DM-Raum trägt den Namen des
|
||
Gegenübers — wer per DM erreichbar sein soll, steht dort mit seinem **Benutzernamen**
|
||
(oben `"richard"`). Ohne Eintrag ignoriert der Agent die Direktnachricht. Eine leere Liste
|
||
erlaubt alles; das Tool erfindet keine Allowlist.
|
||
|
||
Mit `"@benutzername"` als `room` beginnt der Agent auch ein **neues** Gespräch (`im.create`)
|
||
— nur bei ausdrücklicher `@`-Schreibweise, damit ein vertippter Kanalname nicht
|
||
stillschweigend zur Direktnachricht wird.
|
||
|
||
Zum Verhalten des Abrufs:
|
||
|
||
- **Erster Takt je Raum setzt nur die Marke** und weckt nicht — sonst käme beim Einrichten
|
||
die gesamte Raumgeschichte auf einmal in den Kontext.
|
||
- Die Marke wandert auf die jüngste **gesehene** Nachricht, auch auf gefilterte. Sonst
|
||
würde eine ignorierte Agentennachricht bei jedem Takt erneut geprüft.
|
||
- **Gelesen-Markierung erst nach dem Einsammeln** — bricht der Takt vorher ab, bleibt der
|
||
Zähler stehen und nichts geht verloren.
|
||
- Der Schleifenschutz vergleicht gegen `agentUsernames`, nicht gegen ein Server-Flag: Wir
|
||
wissen selbst am besten, welche Konten unsere Agenten sind.
|
||
- **Kein Eintrag in der Staging-Policy** — `RocketChat.send_message` bleibt bewusst `auto`
|
||
(Standard). Der Schutz sitzt an `allowedRooms`, nicht an einer Einzelfreigabe.
|
||
|
||
---
|
||
|
||
## 3 — Redundanz: was passiert, wenn Rocket.Chat ausfällt
|
||
|
||
Das ist die Anforderung, die die Architektur bestimmt — nicht der Chat selbst. Der Kern:
|
||
**Rocket.Chat darf ein Kanal sein, nicht der Kanal.**
|
||
|
||
### 3.1 Was heute schon unabhängig funktioniert
|
||
|
||
| Kanal | Unabhängig von Rocket.Chat? | Richtung |
|
||
|---|---|---|
|
||
| WinForms-Chat (`frm_chat`) | vollständig — läuft in der App selbst | beide |
|
||
| Telegram-Bot-Tool | ja — fremde Infrastruktur | beide |
|
||
| Mail-Tool | ja, sofern der Mailserver anderswo läuft | beide |
|
||
| Web-Chat / `ClawdDotNetApi` | ja, aber nur im lokalen Netz | beide |
|
||
|
||
Wir sind also nicht bei null. Was fehlt, ist die **Umschaltung** — heute muss ein Mensch
|
||
merken, dass nichts mehr ankommt.
|
||
|
||
### 3.2 Vorschlag: `ChannelRouter` im Core
|
||
|
||
Eine kleine Komponente im Core (kein neues Tool, keine Tool-zu-Tool-Abhängigkeit —
|
||
sie löst Tools über die vorhandene `ToolRegistry` nach Namen auf, wie es der Dispatcher
|
||
schon tut):
|
||
|
||
- Je Agent eine **geordnete Kanalliste**, z. B. `["RocketChat", "Telegram", "Mail"]`.
|
||
- Eine Methode „stelle dem Menschen diese Nachricht zu": versucht der Reihe nach, bis
|
||
einer erfolgreich ist, und protokolliert im Audit-Log, **über welchen Kanal** zugestellt
|
||
wurde — inklusive des Hinweises „Primärkanal war nicht erreichbar".
|
||
- Genutzt von: Agenten (`notify_user`), aber vor allem von **systemseitigen** Meldungen,
|
||
die heute keinen Weg nach außen haben: Staging-Vorschlag wartet auf Freigabe, Budget
|
||
überschritten, Watchdog-Alarm, Task blockiert.
|
||
|
||
Der zweite Teil ist der wichtigere: Gerade wenn etwas kaputt ist, ist die Meldung darüber
|
||
diejenige, die ankommen muss.
|
||
|
||
### 3.3 Gesundheitsprüfung und Eskalation
|
||
|
||
Ein Tool-Job `rocketchat_health` (Takt ~5 Minuten, `GET /api/info`):
|
||
|
||
- Nach **drei** aufeinanderfolgenden Fehlschlägen: einmalige Meldung über den nächsten
|
||
Kanal der Liste — „Rocket.Chat ist seit HH:MM nicht erreichbar, ich melde mich hier."
|
||
Einmalig, nicht je Takt.
|
||
- Bei Rückkehr: „Rocket.Chat ist wieder da", und der Zustand wird zurückgesetzt.
|
||
- Nachrichten, die während des Ausfalls nicht gesendet werden konnten, werden **nicht**
|
||
in einer eigenen Warteschlange gehalten — sie gehen über den Ersatzkanal raus. Eine
|
||
zweite Zustellwarteschlange wäre eine zweite Fehlerquelle.
|
||
|
||
**Eingehend während des Ausfalls:** Der Telegram-Poll-Job bleibt dauerhaft aktiv, nur mit
|
||
langsamem Takt (z. B. alle 5 Minuten). Er kostet nichts, wenn nichts kommt — und ist im
|
||
Ernstfall der Weg, auf dem *du* die Agenten erreichst. Der WinForms-Chat ist ohnehin immer
|
||
da, solange die App läuft.
|
||
|
||
**Entschieden (August 2026): Telegram ist der Notfallkanal.** Die Kanalliste lautet damit
|
||
`["RocketChat", "Telegram"]`. Konsequenzen:
|
||
|
||
- Das Telegram-Tool wird **nicht** abgebaut und geht nicht in Rocket.Chat auf. Es behält
|
||
seine Rolle, verliert aber die Rolle als Alltagskanal.
|
||
- `Telegram.send_message` bleibt in der Staging-Policy auf `approve` — mit einer Ausnahme:
|
||
Meldungen, die der `ChannelRouter` selbst erzeugt (Ausfall, Budget, Watchdog, offene
|
||
Freigabe), laufen **ohne** Freigabe. Sonst bliebe die Warnung, dass eine Freigabe
|
||
aussteht, selbst in der Freigabewarteschlange hängen — ein Ringschluss, der genau im
|
||
Ernstfall zuschlägt.
|
||
- Der Telegram-Poll bleibt dauerhaft eingerichtet, aber mit langsamem Takt. Ein Kanal, der
|
||
erst im Notfall eingeschaltet wird, ist im Notfall ungetestet.
|
||
- Mail bleibt außen vor. Zwei Ersatzkanäle zu pflegen lohnt nicht; das Mail-Tool behält
|
||
seinen fachlichen Zweck.
|
||
|
||
### 3.4 Was das für die Prompts heißt
|
||
|
||
Ein Agent soll seinen Kanal nicht selbst wählen. Er sagt „ich möchte dem Nutzer das hier
|
||
mitteilen", der Router entscheidet. Sonst muss das Modell im Fehlerfall improvisieren —
|
||
und genau dann ist Improvisation das Letzte, was man will.
|
||
|
||
---
|
||
|
||
## 4 — Konflikt mit Roadmap A5 (Matrix)
|
||
|
||
Roadmap-Punkt **A5** legt fest: „Die Kommunikation (Benachrichtigungen, Berichte, Chat mit
|
||
Agenten) wird auf Element/Matrix umgestellt", und der gestrichene Tool-Kandidat *Notify*
|
||
geht darin auf.
|
||
|
||
Rocket.Chat besetzt exakt dieselbe Rolle. Drei mögliche Auflösungen:
|
||
|
||
1. **Rocket.Chat ersetzt A5.** A5 wird umgeschrieben, Matrix entfällt. Vorteil: eine
|
||
Schiene, ein Betriebsaufwand, die Instanz läuft bereits.
|
||
2. **A5 bleibt, Rocket.Chat ist nur ein weiteres Tool.** Dann bauen wir zweimal dasselbe.
|
||
Schwer zu begründen.
|
||
3. **Rocket.Chat primär, Matrix als späterer Zweitkanal.** Passt formal zur
|
||
Redundanz-Anforderung, verdoppelt aber den Wartungsaufwand für einen Fall, den
|
||
Telegram schon abdeckt.
|
||
|
||
**Meine Empfehlung: (1).** Der `ChannelRouter` aus 3.2 ist ohnehin die Verallgemeinerung,
|
||
die A5 gebraucht hätte — mit ihm ist ein späterer Matrix-Kanal ein zusätzlicher Eintrag in
|
||
der Liste, keine Migration. Das ist eine Entscheidung für dich, keine technische Sachfrage.
|
||
|
||
---
|
||
|
||
## 5 — Nextcloud: Machbarkeit
|
||
|
||
### 5.1 Der Zugriffsweg
|
||
|
||
Nextcloud hat zwei Schnittstellen, beide brauchen wir:
|
||
|
||
| Zweck | Schnittstelle |
|
||
|---|---|
|
||
| Dateien lesen/schreiben/auflisten/verschieben | **WebDAV**: `/remote.php/dav/files/{benutzer}/{pfad}` |
|
||
| Öffentlichen Link erzeugen | **OCS**: `/ocs/v2.php/apps/files_sharing/api/v1/shares` |
|
||
|
||
Authentifiziert wird mit **App-Passwörtern** (Nextcloud → Einstellungen → Sicherheit →
|
||
„Neues App-Passwort erstellen") per Basic-Auth. Ein App-Passwort ist einzeln widerrufbar
|
||
und lässt das eigentliche Kontopasswort unangetastet — dieselbe Logik wie das Personal
|
||
Access Token bei Rocket.Chat.
|
||
|
||
WebDAV braucht keine Bibliothek: `HttpClient` mit den Methoden `PUT`, `GET`, `MKCOL`,
|
||
`PROPFIND`, `MOVE`, `DELETE`. Nur `PROPFIND` liefert XML (Multistatus), das geparst werden
|
||
muss — überschaubar, und es erspart uns eine weitere Abhängigkeit.
|
||
|
||
### 5.2 Ein Benutzer je Agent — oder ein Sammelkonto?
|
||
|
||
Zwei Modelle:
|
||
|
||
- **Je Agent ein Nextcloud-Benutzer.** Sauber nachvollziehbar („wer hat das abgelegt"),
|
||
passt zum Rocket.Chat-Modell, kostet je nach Lizenzmodell Nutzer.
|
||
- **Ein Dienstkonto `clawd-agents` mit Unterordnern je Agent.** Einfacher zu verwalten,
|
||
Herkunft steht dann im Pfad statt im Konto.
|
||
|
||
**Empfehlung:** Ein Dienstkonto mit Ordnerstruktur `/ClawdDotNet/{Agent}/…`, **plus** einen
|
||
mit dir geteilten Ordner `/ClawdDotNet/Berichte/`. Begründung: Bei Rocket.Chat ist die
|
||
eigene Identität funktional zwingend (DMs, Erwähnungen), bei Dateien ist sie es nicht —
|
||
und ein Ordnerbaum ist leichter aufzuräumen als zehn Konten. Falls du die Trennung dennoch
|
||
willst, ändert das am Tool nichts, nur an der Konfiguration.
|
||
|
||
Die Ordnerdurchsetzung gehört ins Tool: eine konfigurierte `rootPath`, aus der der Agent
|
||
nicht ausbrechen kann — dieselbe Prüfung wie in `FTPTool.ResolveLocalPath`.
|
||
|
||
### 5.3 Die ehrliche Antwort zu „direkt über die API erstellen"
|
||
|
||
Nextcloud hat **keine** API, die Dokumenteninhalte erzeugt. Es ist ein Dateiablage- und
|
||
Freigabesystem; Collabora ist ein *Editor* im Browser (über WOPI angebunden), kein
|
||
Generator, den man von außen mit „erstelle eine Tabelle mit diesen Zahlen" beauftragen
|
||
kann.
|
||
|
||
Der tatsächliche Weg ist deshalb der, den du selbst schon beschrieben hast:
|
||
|
||
```
|
||
Agent erzeugt die Datei im eigenen Workspace (FileRW-Tool, schon vorhanden)
|
||
→ Nextcloud.upload (WebDAV PUT)
|
||
→ Nextcloud.share (optional) (OCS, liefert Link)
|
||
→ RocketChat.send_message mit dem Link
|
||
```
|
||
|
||
Das ist kein Umweg, sondern die richtige Aufteilung: Der Agent kann seine Datei lokal
|
||
prüfen und korrigieren, bevor sie irgendwo landet.
|
||
|
||
### 5.4 Formate — und wo Collabora doch nützlich wird
|
||
|
||
Was ein Agent von sich aus gut schreiben kann: **Markdown** (Nextcloud rendert `.md`
|
||
direkt in der Weboberfläche — für Berichte oft die beste Wahl), **CSV**, **HTML**, JSON.
|
||
|
||
Was er nicht von sich aus schreiben kann: `.xlsx`, `.docx`, `.pdf`.
|
||
|
||
Hier gibt es einen eleganten Weg, weil du Collabora ohnehin betreibst: Collabora Online
|
||
bringt einen **Konvertierungs-Endpunkt** mit (`POST /cool/convert-to/{format}`, multipart).
|
||
Damit gilt:
|
||
|
||
| Ziel | Weg |
|
||
|---|---|
|
||
| PDF | Agent schreibt HTML oder ODT → Collabora → PDF |
|
||
| XLSX | Agent schreibt CSV → Collabora → XLSX |
|
||
| DOCX | Agent schreibt HTML/ODT → Collabora → DOCX |
|
||
|
||
Vorteil: **keine zusätzliche PDF- oder Excel-Bibliothek** im Projekt (und keine
|
||
Lizenzfrage, die wir uns damit einhandeln — mehrere verbreitete .NET-Bibliotheken für
|
||
XLSX und PDF sind für kommerzielle Nutzung nicht frei).
|
||
|
||
Zu prüfen, bevor wir darauf bauen:
|
||
- Ist der Endpunkt in deiner Collabora-Installation erreichbar? Er muss in `coolwsd.xml`
|
||
für die IP des ClawdDotNet-Rechners freigegeben sein (`net`/`post_allow`-Allowlist).
|
||
Standardmäßig ist das eng gefasst.
|
||
- Der Pfad heißt je nach Version `/cool/convert-to/…` (neu) oder `/lool/convert-to/…` (alt).
|
||
|
||
Falls der Endpunkt nicht freigegeben werden soll: Rückfallebene ist Markdown/CSV — für
|
||
den Alltag völlig ausreichend, PDF wäre dann ein späterer eigener Punkt.
|
||
|
||
### 5.5 Freigabe-Links
|
||
|
||
`POST /ocs/v2.php/apps/files_sharing/api/v1/shares` (Header `OCS-APIRequest: true`),
|
||
`shareType=3` = öffentlicher Link. Optional `password`, `expireDate`, `permissions=1`
|
||
(nur lesen). Die Antwort enthält die fertige URL.
|
||
|
||
Zwei Hinweise:
|
||
- Manche Instanzen erzwingen Passwortschutz für öffentliche Links — dann muss das Tool ein
|
||
Passwort mitgeben und zurückliefern.
|
||
- Ein öffentlicher Link ist **irreversibel im Sinne von A2**: Einmal geteilt, kann er
|
||
weitergegeben worden sein, auch wenn man ihn danach löscht. Deshalb steht er unten in
|
||
der Staging-Tabelle auf `approve`.
|
||
|
||
Innerhalb der eigenen Instanz ist die freundlichere Variante `shareType=0` (an einen
|
||
konkreten Nextcloud-Benutzer) — kein öffentlicher Link nötig, wenn du ohnehin ein Konto
|
||
hast. Das sollte der **Standard** sein, öffentlich die Ausnahme.
|
||
|
||
### 5.6 Fallstricke
|
||
|
||
- **Dateisperren (HTTP 423).** Wenn du eine Datei gerade in Collabora offen hast, kann ein
|
||
Upload auf dieselbe Datei scheitern. Das Tool muss 423 sauber melden statt kryptisch zu
|
||
scheitern — und beim Überschreiben eines Berichts lieber einen neuen Dateinamen mit
|
||
Zeitstempel vergeben.
|
||
- **Überschreiben ist nicht destruktiv**, solange die Versionierung aktiv ist (Nextcloud
|
||
legt automatisch eine Vorversion an). Das ist der Grund, warum `Nextcloud.upload` unten
|
||
auf `auto` steht, `FTP.upload` aber auf `approve`.
|
||
- **Größenbegrenzung.** Ein einfaches `PUT` reicht für Berichte problemlos; erst bei sehr
|
||
großen Dateien bräuchte es den Chunked-Upload (`/remote.php/dav/uploads/…`). Für den
|
||
angedachten Zweck (Berichte, Tabellen, PDFs) nicht nötig — und wenn doch, meldet der
|
||
Server einen klaren Fehler.
|
||
- **Quota.** Ein Agent, der stündlich Berichte ablegt, füllt das Konto. Ein Aufräum-Task
|
||
(„Berichte älter als 90 Tage") gehört mittelfristig ins Taskboard.
|
||
|
||
### 5.7 Tool-Zuschnitt
|
||
|
||
```
|
||
Tool: Nextcloud
|
||
Aktionen: upload | download | list | mkdir | move | delete
|
||
| share | unshare | convert (convert nur falls Collabora freigegeben)
|
||
```
|
||
|
||
Konfiguration je Agent:
|
||
|
||
```json
|
||
"Nextcloud": {
|
||
"baseUrl": "https://cloud.example.org",
|
||
"username": "clawd-agents",
|
||
"appPassword": "…", // von ConfigSecrets geschützt
|
||
"rootPath": "/ClawdDotNet/Hermes",
|
||
"allowPublicShares": false,
|
||
"collaboraUrl": "https://collabora.example.org"
|
||
}
|
||
```
|
||
|
||
`appPassword` muss der Schlüsselliste in `ConfigSecrets` hinzugefügt werden — `password`
|
||
allein greift nicht, weil dort auf ganze Feldnamen verglichen wird.
|
||
|
||
---
|
||
|
||
## 6 — Verzahnung mit Staging (A2) und Audit (A3)
|
||
|
||
Vorschlag für die `StagingPolicy.DefaultRules`:
|
||
|
||
| Aktion | Standard | Begründung |
|
||
|---|---|---|
|
||
| `RocketChat.send_message` (erlaubter Raum) | **auto** | Sonst ist Chat unbenutzbar — jede Antwort bräuchte einen Klick |
|
||
| `RocketChat.send_message` (Raum nicht in `allowedRooms`) | **deny** | Wird vom Tool selbst abgewiesen, gar nicht erst vorgelegt |
|
||
| `RocketChat.send_file` | **approve** | Dateiabfluss in einen Chatraum |
|
||
| `Nextcloud.upload`, `mkdir`, `move` | **auto** | Versioniert, im eigenen Ordner, umkehrbar |
|
||
| `Nextcloud.delete` | **approve** | wie `FileRW.delete` |
|
||
| `Nextcloud.share` (an Benutzer) | **auto** | bleibt innerhalb der Instanz |
|
||
| `Nextcloud.share` (öffentlicher Link) | **approve** | nicht zurückholbar |
|
||
|
||
Der Unterschied zu `Telegram.send_message` (heute `approve`) ist Absicht: Telegram ist ein
|
||
Benachrichtigungskanal nach außen, Rocket.Chat ist der Arbeitsraum. Ein Arbeitsraum, in
|
||
dem jede Antwort eine Freigabe braucht, ist kein Arbeitsraum. Der Schutz sitzt hier an der
|
||
Raum-Allowlist statt an der Einzelfreigabe.
|
||
|
||
Für das Audit-Log entstehen keine Sonderfälle — die Tool-Aufrufe laufen ohnehin durch.
|
||
|
||
---
|
||
|
||
## 7 — Was dieses Konzept **nicht** vorsieht
|
||
|
||
Damit der Zuschnitt klar ist:
|
||
|
||
- Keine Rocket.Chat-**App** (Apps-Engine, TypeScript im Server) — wir bleiben Client.
|
||
- Keine Verwaltung von Rocket.Chat durch Agenten (Benutzer anlegen, Räume erstellen,
|
||
Rechte vergeben). Das ist Admin-Arbeit in der WinForms-Oberfläche.
|
||
- Keine Sprach-/Videofunktionen, keine Nextcloud Talk-Anbindung.
|
||
- Kein Ersatz für den WinForms-Chat — der bleibt und ist die unterste Rückfallebene.
|
||
- Keine Ende-zu-Ende-Verschlüsselung. Rocket.Chat kann das, aber verschlüsselte Räume sind
|
||
über die REST-API nicht lesbar. Agenten arbeiten in unverschlüsselten Räumen — das ist
|
||
eine bewusste Einschränkung, die du kennen solltest.
|
||
|
||
---
|
||
|
||
## 8 — Vorschlag für den Schnitt
|
||
|
||
| Phase | Inhalt | Ergebnis |
|
||
|---|---|---|
|
||
| **1** | `Nextcloud`-Tool: upload/download/list/mkdir/move/delete/share | Agent kann Berichte ablegen und einen Link liefern |
|
||
| **2** | `RocketChat`-Tool: senden, lesen, `rocketchat_poll`-Job, Raum-Allowlist, Erwähnungsfilter, Schleifendrossel | Gespräch mit Agenten über Rocket.Chat, Antwort per Prompt |
|
||
| **3** | Automatischer Rückweg (`ReplyTo` in `ToolJobResult`) | Antwort landet zuverlässig im richtigen Raum/Thread |
|
||
| **4** | `ChannelRouter` + `rocketchat_health` + Eskalation | Der Notfallkanal — Ausfall wird erkannt und umschifft |
|
||
| **5** | Collabora-Konvertierung (PDF/XLSX) | Berichte in Büroformaten |
|
||
| **6** *(optional)* | Realtime/DDP statt Polling | Antwortzeit ~1 s statt ~30 s |
|
||
|
||
Nextcloud zuerst, weil es das kleinere, in sich abgeschlossene Stück ist und sofort Nutzen
|
||
bringt — und weil es sich unabhängig vom Ausgang der A5-Entscheidung lohnt.
|
||
|
||
Phase 4 ist **kein Nice-to-have**: Ohne sie ist Rocket.Chat ein Einzelpunkt, dessen Ausfall
|
||
niemand meldet. Sie sollte nicht hinter Phase 5 rutschen.
|
||
|
||
Zur Modell-Einstufung im Sinne der Roadmap: Phasen 1, 2 und 5 sind klar spezifizierbare
|
||
Tool-Arbeit (4.6-tauglich). Phase 3 und 4 fassen Engine bzw. Zustellwege an und sollten
|
||
mit vorheriger Festlegung der Invarianten und mit Tests gebaut werden.
|
||
|
||
---
|
||
|
||
## 9 — Offene Punkte für die Diskussion
|
||
|
||
1. **A5/Matrix** — ersetzt Rocket.Chat den Punkt, oder bleibt Matrix als Ziel bestehen?
|
||
(Abschnitt 4; das entscheidet, ob der `ChannelRouter` Pflicht oder Kür ist.)
|
||
2. **Nextcloud-Identität** — ein Dienstkonto mit Ordnern je Agent (mein Vorschlag) oder
|
||
je Agent ein eigener Nextcloud-Benutzer?
|
||
3. **Rückweg der Antwort** — reicht Phase 2 (Agent antwortet selbst) für den Anfang, oder
|
||
soll Phase 3 direkt mitgebaut werden?
|
||
4. ~~**Notfallkanal** — Telegram oder Mail?~~ **Entschieden: Telegram** (siehe 3.3).
|
||
Offen bleibt nur die Kleinigkeit, ob die Reihenfolge instanzweit gilt (mein Vorschlag)
|
||
oder pro Agent einstellbar sein soll.
|
||
5. **Collabora-Konvertierung** — ist der `convert-to`-Endpunkt für den ClawdDotNet-Rechner
|
||
freigebbar? Falls nein, bleibt es bei Markdown/CSV.
|
||
6. **Versionen** — welche Rocket.Chat- und welche Nextcloud-Version läuft bei dir?
|
||
Einzelne Endpunkte und Rollennamen sind versionsabhängig; das prüfe ich vor der
|
||
Umsetzung gegen deine Instanz statt gegen die Dokumentation.
|
||
7. **Öffentliche Links** — grundsätzlich erlauben (mit Freigabe) oder ganz sperren
|
||
(`allowPublicShares: false` als harte Voreinstellung)?
|