diff --git a/docs/de/concepts/active-memory.md b/docs/de/concepts/active-memory.md index 9f7d130fe..05c084a62 100644 --- a/docs/de/concepts/active-memory.md +++ b/docs/de/concepts/active-memory.md @@ -1,36 +1,36 @@ --- read_when: - - Sie möchten verstehen, wofür Active Memory gedacht ist - - Sie möchten Active Memory für einen Konversationsagenten aktivieren - - Sie möchten das Verhalten von Active Memory anpassen, ohne es überall zu aktivieren + - Sie möchten verstehen, wofür Active Memory gedacht ist. + - Sie möchten Active Memory für einen Konversationsagenten aktivieren. + - Sie möchten das Verhalten von Active Memory anpassen, ohne es überall zu aktivieren. summary: Ein Plugin-eigener blockierender Speicher-Sub-Agent, der relevante Erinnerungen in interaktive Chat-Sitzungen einspeist title: Active Memory x-i18n: - generated_at: "2026-04-14T02:08:44Z" + generated_at: "2026-04-16T06:22:27Z" model: gpt-5.4 provider: openai - source_hash: b151e9eded7fc5c37e00da72d95b24c1dc94be22e855c8875f850538392b0637 + source_hash: ab36c5fea1578348cc2258ea3b344cc7bdc814f337d659cdb790512b3ea45473 source_path: concepts/active-memory.md workflow: 15 --- # Active Memory -Active Memory ist ein optionaler Plugin-eigener blockierender Speicher-Sub-Agent, der +Active Memory ist ein optionaler, Plugin-eigener blockierender Speicher-Sub-Agent, der vor der Hauptantwort für geeignete Konversationssitzungen ausgeführt wird. Er existiert, weil die meisten Speichersysteme leistungsfähig, aber reaktiv sind. Sie verlassen sich darauf, -dass der Haupt-Agent entscheidet, wann der Speicher durchsucht werden soll, oder darauf, dass der Benutzer Dinge sagt -wie „merke dir das“ oder „durchsuche den Speicher“. Bis dahin ist der Moment, in dem der Speicher -die Antwort natürlich hätte wirken lassen, bereits verstrichen. +dass der Hauptagent entscheidet, wann der Speicher durchsucht werden soll, oder darauf, dass der Benutzer Dinge sagt +wie „Merke dir das“ oder „Durchsuche den Speicher“. Zu diesem Zeitpunkt ist der Moment, in dem der Speicher +die Antwort natürlich hätte wirken lassen, bereits vorbei. Active Memory gibt dem System eine begrenzte Gelegenheit, relevanten Speicherinhalt anzuzeigen, bevor die Hauptantwort generiert wird. ## Fügen Sie dies in Ihren Agenten ein -Fügen Sie dies in Ihren Agenten ein, wenn Sie Active Memory mit einer -eigenständigen, standardmäßig sicheren Konfiguration aktivieren möchten: +Fügen Sie dies in Ihren Agenten ein, wenn Sie möchten, dass er Active Memory mit einer +eigenständigen Konfiguration mit sicheren Standardwerten aktiviert: ```json5 { @@ -56,9 +56,9 @@ eigenständigen, standardmäßig sicheren Konfiguration aktivieren möchten: } ``` -Dadurch wird das Plugin für den Agenten `main` aktiviert, standardmäßig auf Sitzungen im Stil von Direktnachrichten -beschränkt, lässt es zuerst das aktuelle Sitzungsmodell erben und verwendet -das konfigurierte Fallback-Modell nur dann, wenn kein explizites oder geerbtes Modell verfügbar ist. +Dadurch wird das Plugin für den Agenten `main` aktiviert, standardmäßig auf Sitzungen +im Stil von Direktnachrichten beschränkt, es kann zunächst das aktuelle Sitzungsmodell übernehmen und +verwendet das konfigurierte Fallback-Modell nur, wenn kein explizites oder übernommenes Modell verfügbar ist. Starten Sie danach das Gateway neu: @@ -66,7 +66,7 @@ Starten Sie danach das Gateway neu: openclaw gateway ``` -So prüfen Sie es live in einer Konversation: +Um es live in einer Unterhaltung zu prüfen: ```text /verbose on @@ -78,8 +78,8 @@ So prüfen Sie es live in einer Konversation: Die sicherste Einrichtung ist: 1. das Plugin aktivieren -2. einen Konversationsagenten festlegen -3. die Protokollierung nur während der Feinabstimmung aktiviert lassen +2. einen Konversationsagenten als Ziel festlegen +3. Logging nur während der Anpassung aktiviert lassen Beginnen Sie mit Folgendem in `openclaw.json`: @@ -116,17 +116,102 @@ Das bedeutet: - `plugins.entries.active-memory.enabled: true` aktiviert das Plugin - `config.agents: ["main"]` aktiviert Active Memory nur für den Agenten `main` -- `config.allowedChatTypes: ["direct"]` sorgt standardmäßig dafür, dass Active Memory nur für Sitzungen im Stil von Direktnachrichten aktiv ist -- wenn `config.model` nicht gesetzt ist, erbt Active Memory zuerst das aktuelle Sitzungsmodell -- `config.modelFallback` stellt optional Ihren eigenen Fallback-Anbieter bzw. Ihr eigenes Fallback-Modell für die Erinnerung bereit +- `config.allowedChatTypes: ["direct"]` hält Active Memory standardmäßig nur für Sitzungen im Stil von Direktnachrichten aktiv +- wenn `config.model` nicht gesetzt ist, übernimmt Active Memory zunächst das aktuelle Sitzungsmodell +- `config.modelFallback` stellt optional Ihr eigenes Fallback-Anbieter-/Modell für den Abruf bereit - `config.promptStyle: "balanced"` verwendet den allgemeinen Standard-Prompt-Stil für den Modus `recent` -- Active Memory wird weiterhin nur für geeignete interaktive persistente Chat-Sitzungen ausgeführt +- Active Memory wird weiterhin nur in geeigneten interaktiven persistenten Chat-Sitzungen ausgeführt -## So sehen Sie es +## Empfehlungen zur Geschwindigkeit -Active Memory injiziert ein verborgenes, nicht vertrauenswürdiges Prompt-Präfix für das Modell. Es +Die einfachste Einrichtung besteht darin, `config.model` nicht zu setzen und Active Memory +dasselbe Modell verwenden zu lassen, das Sie bereits für normale Antworten verwenden. Das ist der sicherste Standard, +weil dabei Ihre vorhandenen Anbieter-, Authentifizierungs- und Modellpräferenzen übernommen werden. + +Wenn Sie möchten, dass sich Active Memory schneller anfühlt, verwenden Sie ein dediziertes Inferenzmodell, +anstatt das Haupt-Chat-Modell zu übernehmen. + +Beispiel für eine Einrichtung mit schnellem Anbieter: + +```json5 +models: { + providers: { + cerebras: { + baseUrl: "https://api.cerebras.ai/v1", + apiKey: "${CEREBRAS_API_KEY}", + api: "openai-completions", + models: [{ id: "gpt-oss-120b", name: "GPT OSS 120B (Cerebras)" }], + }, + }, +}, +plugins: { + entries: { + "active-memory": { + enabled: true, + config: { + model: "cerebras/gpt-oss-120b", + }, + }, + }, +} +``` + +Schnelle Modelloptionen, die in Betracht gezogen werden sollten: + +- `cerebras/gpt-oss-120b` für ein schnelles dediziertes Abrufmodell mit einer schmalen Tool-Oberfläche +- Ihr normales Sitzungsmodell, indem Sie `config.model` nicht setzen +- ein Fallback-Modell mit geringer Latenz wie `google/gemini-3-flash`, wenn Sie ein separates Abrufmodell möchten, ohne Ihr primäres Chat-Modell zu ändern + +Warum Cerebras eine starke geschwindigkeitsorientierte Option für Active Memory ist: + +- die Tool-Oberfläche von Active Memory ist schmal: Es ruft nur `memory_search` und `memory_get` auf +- die Qualität des Abrufs ist wichtig, aber die Latenz ist wichtiger als beim Hauptantwortpfad +- ein dedizierter schneller Anbieter vermeidet es, die Latenz des Speicherabrufs an Ihren primären Chat-Anbieter zu koppeln + +Wenn Sie kein separates geschwindigkeitsoptimiertes Modell möchten, lassen Sie `config.model` ungesetzt +und lassen Sie Active Memory das aktuelle Sitzungsmodell übernehmen. + +### Cerebras-Einrichtung + +Fügen Sie einen Anbietereintrag wie diesen hinzu: + +```json5 +models: { + providers: { + cerebras: { + baseUrl: "https://api.cerebras.ai/v1", + apiKey: "${CEREBRAS_API_KEY}", + api: "openai-completions", + models: [{ id: "gpt-oss-120b", name: "GPT OSS 120B (Cerebras)" }], + }, + }, +} +``` + +Richten Sie Active Memory dann darauf aus: + +```json5 +plugins: { + entries: { + "active-memory": { + enabled: true, + config: { + model: "cerebras/gpt-oss-120b", + }, + }, + }, +} +``` + +Hinweis: + +- stellen Sie sicher, dass der Cerebras-API-Schlüssel tatsächlich Modellzugriff für das von Ihnen gewählte Modell hat, da die Sichtbarkeit von `/v1/models` allein keinen Zugriff auf `chat/completions` garantiert + +## Wie man es sieht + +Active Memory fügt für das Modell ein verborgenes nicht vertrauenswürdiges Prompt-Präfix ein. Es zeigt keine rohen `...`-Tags in der -normalen, für den Client sichtbaren Antwort an. +normalen für den Client sichtbaren Antwort an. ## Sitzungsumschaltung @@ -139,8 +224,8 @@ aktuelle Chat-Sitzung pausieren oder fortsetzen möchten, ohne die Konfiguration /active-memory on ``` -Dies gilt nur für die Sitzung. Es ändert nicht -`plugins.entries.active-memory.enabled`, die Agentenauswahl oder andere globale +Dies gilt für die Sitzung. Es ändert nicht +`plugins.entries.active-memory.enabled`, die Agentenzuordnung oder andere globale Konfigurationen. Wenn der Befehl in die Konfiguration schreiben und Active Memory für @@ -153,11 +238,11 @@ alle Sitzungen pausieren oder fortsetzen soll, verwenden Sie die explizite globa ``` Die globale Form schreibt `plugins.entries.active-memory.config.enabled`. Dabei bleibt -`plugins.entries.active-memory.enabled` aktiviert, damit der Befehl verfügbar bleibt, um -Active Memory später wieder zu aktivieren. +`plugins.entries.active-memory.enabled` aktiviert, sodass der Befehl verfügbar bleibt, um +Active Memory später wieder einzuschalten. Wenn Sie sehen möchten, was Active Memory in einer Live-Sitzung macht, aktivieren Sie die -Sitzungsumschaltungen, die der gewünschten Ausgabe entsprechen: +Sitzungsumschalter, die zu der gewünschten Ausgabe passen: ```text /verbose on @@ -170,12 +255,12 @@ Wenn diese aktiviert sind, kann OpenClaw Folgendes anzeigen: - eine lesbare Debug-Zusammenfassung wie `Active Memory Debug: Lemon pepper wings with blue cheese.`, wenn `/trace on` Diese Zeilen stammen aus demselben Active-Memory-Durchlauf, der das verborgene -Prompt-Präfix speist, sind aber für Menschen formatiert, anstatt rohe Prompt-Markup-Strukturen offenzulegen. -Sie werden als diagnostische Folgenachricht nach der normalen -Assistentenantwort gesendet, sodass Kanal-Clients wie Telegram keine separate -Diagnoseblase vor der Antwort anzeigen. +Prompt-Präfix speist, sind aber für Menschen formatiert, anstatt rohe Prompt- +Auszeichnung anzuzeigen. Sie werden als Diagnose-Folgenachricht nach der normalen +Assistentenantwort gesendet, damit Kanal-Clients wie Telegram keine separate +Diagnose-Sprechblase vor der Antwort aufblinken lassen. -Wenn Sie zusätzlich `/trace raw` aktivieren, zeigt der nachverfolgte Block `Model Input (User Role)` +Wenn Sie zusätzlich `/trace raw` aktivieren, zeigt der verfolgte Block `Model Input (User Role)` das verborgene Active-Memory-Präfix wie folgt an: ```text @@ -207,16 +292,16 @@ Erwartete sichtbare Antwortform: ## Wann es ausgeführt wird -Active Memory verwendet zwei Schranken: +Active Memory verwendet zwei Gate-Prüfungen: 1. **Konfigurations-Opt-in** Das Plugin muss aktiviert sein, und die aktuelle Agenten-ID muss in `plugins.entries.active-memory.config.agents` enthalten sein. -2. **Strenge Laufzeit-Eignung** - Selbst wenn es aktiviert und als Ziel festgelegt ist, wird Active Memory nur für geeignete +2. **Strikte Laufzeitberechtigung** + Auch wenn es aktiviert ist und als Ziel festgelegt wurde, wird Active Memory nur für geeignete interaktive persistente Chat-Sitzungen ausgeführt. -Die tatsächliche Regel ist: +Die tatsächliche Regel lautet: ```text plugin enabled @@ -234,8 +319,8 @@ Wenn eine dieser Bedingungen fehlschlägt, wird Active Memory nicht ausgeführt. ## Sitzungstypen -`config.allowedChatTypes` steuert, welche Arten von Konversationen Active -Memory überhaupt ausführen dürfen. +`config.allowedChatTypes` steuert, in welchen Arten von Unterhaltungen Active +Memory überhaupt ausgeführt werden darf. Der Standardwert ist: @@ -243,8 +328,8 @@ Der Standardwert ist: allowedChatTypes: ["direct"] ``` -Das bedeutet, dass Active Memory standardmäßig in Sitzungen im Stil von Direktnachrichten ausgeführt wird, -aber nicht in Gruppen- oder Kanalsitzungen, sofern Sie diese nicht ausdrücklich aktivieren. +Das bedeutet, dass Active Memory standardmäßig in Sitzungen im Stil von Direktnachrichten ausgeführt wird, aber +nicht in Gruppen- oder Kanalsitzungen, es sei denn, Sie aktivieren diese ausdrücklich. Beispiele: @@ -262,23 +347,23 @@ allowedChatTypes: ["direct", "group", "channel"] ## Wo es ausgeführt wird -Active Memory ist eine Funktion zur Anreicherung von Konversationen, keine -plattformweite Inferenzfunktion. +Active Memory ist eine Funktion zur Anreicherung von Konversationen, keine plattformweite +Inferenzfunktion. -| Oberfläche | Führt Active Memory aus? | -| ------------------------------------------------------------------- | ------------------------------------------------------- | -| Control UI / persistente Web-Chat-Sitzungen | Ja, wenn das Plugin aktiviert ist und der Agent ausgewählt ist | -| Andere interaktive Kanal-Sitzungen auf demselben persistenten Chat-Pfad | Ja, wenn das Plugin aktiviert ist und der Agent ausgewählt ist | -| Headless-Einmalläufe | Nein | -| Heartbeat-/Hintergrundläufe | Nein | -| Generische interne `agent-command`-Pfade | Nein | -| Sub-Agent-/interne Hilfsausführung | Nein | +| Surface | Active Memory wird ausgeführt? | +| ------------------------------------------------------------------- | -------------------------------------------------------- | +| Persistente Sitzungen in der Control UI / im Web-Chat | Ja, wenn das Plugin aktiviert ist und der Agent als Ziel festgelegt ist | +| Andere interaktive Kanalsitzungen auf demselben persistenten Chat-Pfad | Ja, wenn das Plugin aktiviert ist und der Agent als Ziel festgelegt ist | +| Headless-Einmalläufe | Nein | +| Heartbeat-/Hintergrundläufe | Nein | +| Generische interne `agent-command`-Pfade | Nein | +| Ausführung von Sub-Agenten/internen Hilfen | Nein | -## Warum Sie es verwenden sollten +## Warum es verwenden Verwenden Sie Active Memory, wenn: -- die Sitzung persistent und benutzerseitig ist +- die Sitzung persistent und benutzergerichtet ist - der Agent über sinnvollen Langzeitspeicher verfügt, der durchsucht werden kann - Kontinuität und Personalisierung wichtiger sind als reine Prompt-Deterministik @@ -286,16 +371,16 @@ Es funktioniert besonders gut für: - stabile Präferenzen - wiederkehrende Gewohnheiten -- langfristigen Benutzerkontext, der natürlich auftauchen sollte +- langfristigen Benutzerkontext, der natürlich angezeigt werden soll Es ist ungeeignet für: - Automatisierung - interne Worker - einmalige API-Aufgaben -- Orte, an denen verborgene Personalisierung überraschend wäre +- Stellen, an denen verborgene Personalisierung überraschend wäre -## Funktionsweise +## Wie es funktioniert Die Laufzeitform ist: @@ -317,7 +402,7 @@ Wenn die Verbindung schwach ist, sollte er `NONE` zurückgeben. ## Abfragemodi -`config.queryMode` steuert, wie viel der Konversation der blockierende Speicher-Sub-Agent sieht. +`config.queryMode` steuert, wie viel Unterhaltung der blockierende Speicher-Sub-Agent sieht. ## Prompt-Stile @@ -327,9 +412,9 @@ wenn er entscheidet, ob Speicherinhalt zurückgegeben werden soll. Verfügbare Stile: - `balanced`: allgemeiner Standard für den Modus `recent` -- `strict`: am wenigsten bereitwillig; am besten geeignet, wenn Sie sehr wenig Übertragung aus nahem Kontext möchten -- `contextual`: am freundlichsten für Kontinuität; am besten geeignet, wenn der Konversationsverlauf wichtiger sein soll -- `recall-heavy`: eher bereit, Speicher auch bei schwächeren, aber noch plausiblen Übereinstimmungen anzuzeigen +- `strict`: am wenigsten bereitwillig; am besten, wenn Sie sehr wenig Übergreifen aus nahem Kontext möchten +- `contextual`: am stärksten auf Kontinuität ausgerichtet; am besten, wenn der Unterhaltungsverlauf stärker ins Gewicht fallen soll +- `recall-heavy`: eher bereit, Speicherinhalt auch bei schwächeren, aber dennoch plausiblen Übereinstimmungen anzuzeigen - `precision-heavy`: bevorzugt aggressiv `NONE`, sofern die Übereinstimmung nicht offensichtlich ist - `preference-only`: optimiert für Favoriten, Gewohnheiten, Routinen, Geschmack und wiederkehrende persönliche Fakten @@ -349,7 +434,7 @@ Beispiel: promptStyle: "preference-only" ``` -## Richtlinie für Modell-Fallbacks +## Modell-Fallback-Richtlinie Wenn `config.model` nicht gesetzt ist, versucht Active Memory, ein Modell in dieser Reihenfolge aufzulösen: @@ -368,52 +453,52 @@ Optionales benutzerdefiniertes Fallback: modelFallback: "google/gemini-3-flash" ``` -Wenn kein explizites, geerbtes oder konfiguriertes Fallback-Modell aufgelöst werden kann, überspringt Active Memory -die Erinnerung für diesen Zug. +Wenn kein explizites, übernommenes oder konfiguriertes Fallback-Modell aufgelöst werden kann, überspringt Active Memory +den Abruf für diesen Zug. `config.modelFallbackPolicy` wird nur noch als veraltetes Kompatibilitätsfeld -für ältere Konfigurationen beibehalten. Es ändert das Laufzeitverhalten nicht mehr. +für ältere Konfigurationen beibehalten. Es verändert das Laufzeitverhalten nicht mehr. -## Erweiterte Ausweichmöglichkeiten +## Erweiterte Escape-Hatches Diese Optionen sind absichtlich nicht Teil der empfohlenen Einrichtung. -`config.thinking` kann die Thinking-Stufe des blockierenden Speicher-Sub-Agenten überschreiben: +`config.thinking` kann die Denkstufe des blockierenden Speicher-Sub-Agenten überschreiben: ```json5 thinking: "medium" ``` -Standardwert: +Standard: ```json5 thinking: "off" ``` Aktivieren Sie dies nicht standardmäßig. Active Memory läuft im Antwortpfad, daher erhöht zusätzliche -Thinking-Zeit direkt die für Benutzer sichtbare Latenz. +Denkzeit direkt die für Benutzer sichtbare Latenz. -`config.promptAppend` fügt zusätzliche Operator-Anweisungen nach dem standardmäßigen Active- -Memory-Prompt und vor dem Konversationskontext hinzu: +`config.promptAppend` fügt nach dem Standard-Prompt von Active Memory und vor dem Unterhaltungskontext +zusätzliche Operator-Anweisungen hinzu: ```json5 -promptAppend: "Prefer stable long-term preferences over one-off events." +promptAppend: "Bevorzuge stabile langfristige Präferenzen gegenüber einmaligen Ereignissen." ``` -`config.promptOverride` ersetzt den standardmäßigen Active-Memory-Prompt. OpenClaw -hängt danach weiterhin den Konversationskontext an: +`config.promptOverride` ersetzt den Standard-Prompt von Active Memory. OpenClaw +hängt den Unterhaltungskontext danach weiterhin an: ```json5 promptOverride: "You are a memory search agent. Return NONE or one compact user fact." ``` -Eine Prompt-Anpassung wird nicht empfohlen, es sei denn, Sie testen bewusst einen -anderen Erinnerungsvertrag. Der Standard-Prompt ist darauf abgestimmt, entweder `NONE` -oder kompakten Benutzerfakt-Kontext für das Hauptmodell zurückzugeben. +Eine Anpassung des Prompts wird nicht empfohlen, es sei denn, Sie testen bewusst einen +anderen Abrufvertrag. Der Standard-Prompt ist darauf abgestimmt, entweder `NONE` +oder kompakten Benutzerfakten-Kontext für das Hauptmodell zurückzugeben. ### `message` -Es wird nur die neueste Benutzernachricht gesendet. +Es wird nur die letzte Benutzernachricht gesendet. ```text Latest user message only @@ -422,16 +507,16 @@ Latest user message only Verwenden Sie dies, wenn: - Sie das schnellste Verhalten möchten -- Sie die stärkste Ausrichtung auf die Erinnerung stabiler Präferenzen möchten -- Folgezüge keinen Konversationskontext benötigen +- Sie die stärkste Ausrichtung auf den Abruf stabiler Präferenzen möchten +- Folgezüge keinen Unterhaltungskontext benötigen -Empfohlener Timeout: +Empfohlenes Timeout: - beginnen Sie bei etwa `3000` bis `5000` ms ### `recent` -Die neueste Benutzernachricht plus ein kleiner aktueller Konversationsverlauf werden gesendet. +Die letzte Benutzernachricht plus ein kleiner aktueller Unterhaltungsausschnitt wird gesendet. ```text Recent conversation tail: @@ -445,16 +530,16 @@ Latest user message: Verwenden Sie dies, wenn: -- Sie ein besseres Gleichgewicht zwischen Geschwindigkeit und konversationeller Verankerung möchten -- Rückfragen häufig von den letzten wenigen Zügen abhängen +- Sie ein besseres Gleichgewicht zwischen Geschwindigkeit und Gesprächsverankerung möchten +- Folgefragen oft von den letzten wenigen Zügen abhängen -Empfohlener Timeout: +Empfohlenes Timeout: - beginnen Sie bei etwa `15000` ms ### `full` -Die vollständige Konversation wird an den blockierenden Speicher-Sub-Agenten gesendet. +Die vollständige Unterhaltung wird an den blockierenden Speicher-Sub-Agenten gesendet. ```text Full conversation context: @@ -466,15 +551,15 @@ user: ... Verwenden Sie dies, wenn: -- die bestmögliche Erinnerungsqualität wichtiger ist als Latenz -- die Konversation wichtige Einrichtungsschritte weit oben im Verlauf enthält +- die bestmögliche Abrufqualität wichtiger ist als die Latenz +- die Unterhaltung wichtige Vorbereitung weit oben im Thread enthält -Empfohlener Timeout: +Empfohlenes Timeout: -- erhöhen Sie ihn im Vergleich zu `message` oder `recent` deutlich -- beginnen Sie bei etwa `15000` ms oder höher, je nach Größe des Threads +- erhöhen Sie es deutlich im Vergleich zu `message` oder `recent` +- beginnen Sie bei etwa `15000` ms oder höher, abhängig von der Thread-Größe -Im Allgemeinen sollte der Timeout mit der Kontextgröße steigen: +Im Allgemeinen sollte das Timeout mit der Kontextgröße zunehmen: ```text message < recent < full @@ -482,9 +567,8 @@ message < recent < full ## Transkriptpersistenz -Läufe des blockierenden Speicher-Sub-Agenten von Active Memory erzeugen während des -Aufrufs des blockierenden Speicher-Sub-Agenten ein echtes `session.jsonl`- -Transkript. +Durchläufe des blockierenden Speicher-Sub-Agenten von Active Memory erzeugen während des Aufrufs des blockierenden Speicher-Sub-Agenten +ein echtes `session.jsonl`-Transkript. Standardmäßig ist dieses Transkript temporär: @@ -492,8 +576,8 @@ Standardmäßig ist dieses Transkript temporär: - es wird nur für den Lauf des blockierenden Speicher-Sub-Agenten verwendet - es wird sofort gelöscht, nachdem der Lauf abgeschlossen ist -Wenn Sie diese Transkripte des blockierenden Speicher-Sub-Agenten zur Fehlerdiagnose oder -Inspektion auf dem Datenträger behalten möchten, aktivieren Sie die Persistenz ausdrücklich: +Wenn Sie diese Transkripte des blockierenden Speicher-Sub-Agenten zur Fehlersuche oder +Prüfung auf der Festplatte behalten möchten, aktivieren Sie die Persistenz ausdrücklich: ```json5 { @@ -513,9 +597,9 @@ Inspektion auf dem Datenträger behalten möchten, aktivieren Sie die Persistenz ``` Wenn aktiviert, speichert Active Memory Transkripte in einem separaten Verzeichnis unter dem -Sitzungsordner des Ziel-Agenten, nicht im Haupttranskriptpfad der Benutzerkonversation. +Sitzungsordner des Zielagenten, nicht im Haupttranskriptpfad der Benutzerunterhaltung. -Das Standardlayout sieht konzeptionell so aus: +Das Standardlayout ist konzeptionell: ```text agents//sessions/active-memory/.jsonl @@ -523,10 +607,10 @@ agents//sessions/active-memory/.jso Sie können das relative Unterverzeichnis mit `config.transcriptDir` ändern. -Verwenden Sie dies mit Bedacht: +Verwenden Sie dies mit Vorsicht: -- Transkripte des blockierenden Speicher-Sub-Agenten können sich in ausgelasteten Sitzungen schnell ansammeln -- der Abfragemodus `full` kann viel Konversationskontext duplizieren +- Transkripte des blockierenden Speicher-Sub-Agenten können sich bei ausgelasteten Sitzungen schnell ansammeln +- der Abfragemodus `full` kann viel Unterhaltungskontext duplizieren - diese Transkripte enthalten verborgenen Prompt-Kontext und abgerufene Erinnerungen ## Konfiguration @@ -539,32 +623,32 @@ plugins.entries.active-memory Die wichtigsten Felder sind: -| Schlüssel | Typ | Bedeutung | -| --------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | -| `enabled` | `boolean` | Aktiviert das Plugin selbst | -| `config.agents` | `string[]` | Agenten-IDs, die Active Memory verwenden dürfen | -| `config.model` | `string` | Optionale Modellreferenz für den blockierenden Speicher-Sub-Agenten; wenn nicht gesetzt, verwendet Active Memory das aktuelle Sitzungsmodell | -| `config.queryMode` | `"message" \| "recent" \| "full"` | Steuert, wie viel der Konversation der blockierende Speicher-Sub-Agent sieht | -| `config.promptStyle` | `"balanced" \| "strict" \| "contextual" \| "recall-heavy" \| "precision-heavy" \| "preference-only"` | Steuert, wie bereitwillig oder streng der blockierende Speicher-Sub-Agent ist, wenn er entscheidet, ob Speicher zurückgegeben werden soll | +| Key | Type | Bedeutung | +| --------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| `enabled` | `boolean` | Aktiviert das Plugin selbst | +| `config.agents` | `string[]` | Agent-IDs, die Active Memory verwenden dürfen | +| `config.model` | `string` | Optionaler Modellverweis für den blockierenden Speicher-Sub-Agenten; wenn nicht gesetzt, verwendet Active Memory das aktuelle Sitzungsmodell | +| `config.queryMode` | `"message" \| "recent" \| "full"` | Steuert, wie viel Unterhaltung der blockierende Speicher-Sub-Agent sieht | +| `config.promptStyle` | `"balanced" \| "strict" \| "contextual" \| "recall-heavy" \| "precision-heavy" \| "preference-only"` | Steuert, wie bereitwillig oder streng der blockierende Speicher-Sub-Agent bei der Entscheidung über die Rückgabe von Speicherinhalt ist | | `config.thinking` | `"off" \| "minimal" \| "low" \| "medium" \| "high" \| "xhigh" \| "adaptive"` | Erweiterte Thinking-Überschreibung für den blockierenden Speicher-Sub-Agenten; Standard `off` für Geschwindigkeit | -| `config.promptOverride` | `string` | Erweiterter vollständiger Prompt-Ersatz; für die normale Nutzung nicht empfohlen | -| `config.promptAppend` | `string` | Erweiterte zusätzliche Anweisungen, die an den Standard- oder überschriebenen Prompt angehängt werden | -| `config.timeoutMs` | `number` | Harter Timeout für den blockierenden Speicher-Sub-Agenten | -| `config.maxSummaryChars` | `number` | Maximal zulässige Gesamtanzahl von Zeichen in der Active-Memory-Zusammenfassung | -| `config.logging` | `boolean` | Gibt während der Feinabstimmung Active-Memory-Protokolle aus | -| `config.persistTranscripts` | `boolean` | Behält Transkripte des blockierenden Speicher-Sub-Agenten auf dem Datenträger, statt temporäre Dateien zu löschen | -| `config.transcriptDir` | `string` | Relatives Transkriptverzeichnis des blockierenden Speicher-Sub-Agenten unter dem Sitzungsordner des Agenten | +| `config.promptOverride` | `string` | Erweiterter vollständiger Prompt-Ersatz; für normale Verwendung nicht empfohlen | +| `config.promptAppend` | `string` | Erweiterte zusätzliche Anweisungen, die an den Standard- oder überschriebenen Prompt angehängt werden | +| `config.timeoutMs` | `number` | Hartes Timeout für den blockierenden Speicher-Sub-Agenten | +| `config.maxSummaryChars` | `number` | Maximal zulässige Gesamtzahl von Zeichen in der Active-Memory-Zusammenfassung | +| `config.logging` | `boolean` | Gibt Active-Memory-Logs während der Anpassung aus | +| `config.persistTranscripts` | `boolean` | Behält Transkripte des blockierenden Speicher-Sub-Agenten auf der Festplatte, anstatt temporäre Dateien zu löschen | +| `config.transcriptDir` | `string` | Relatives Transkriptverzeichnis des blockierenden Speicher-Sub-Agenten unter dem Sitzungsordner des Agenten | -Nützliche Felder zur Feinabstimmung: +Nützliche Felder zur Anpassung: -| Schlüssel | Typ | Bedeutung | -| ----------------------------- | -------- | ------------------------------------------------------------------- | -| `config.maxSummaryChars` | `number` | Maximal zulässige Gesamtanzahl von Zeichen in der Active-Memory-Zusammenfassung | -| `config.recentUserTurns` | `number` | Frühere Benutzerzüge, die einbezogen werden, wenn `queryMode` `recent` ist | -| `config.recentAssistantTurns` | `number` | Frühere Assistentenzüge, die einbezogen werden, wenn `queryMode` `recent` ist | -| `config.recentUserChars` | `number` | Maximale Zeichen pro aktuellem Benutzerzug | -| `config.recentAssistantChars` | `number` | Maximale Zeichen pro aktuellem Assistentenzug | -| `config.cacheTtlMs` | `number` | Cache-Wiederverwendung für wiederholte identische Abfragen | +| Key | Type | Bedeutung | +| ----------------------------- | -------- | ------------------------------------------------------------ | +| `config.maxSummaryChars` | `number` | Maximal zulässige Gesamtzahl von Zeichen in der Active-Memory-Zusammenfassung | +| `config.recentUserTurns` | `number` | Vorherige Benutzerzüge, die einbezogen werden, wenn `queryMode` `recent` ist | +| `config.recentAssistantTurns` | `number` | Vorherige Assistentenzüge, die einbezogen werden, wenn `queryMode` `recent` ist | +| `config.recentUserChars` | `number` | Maximale Zeichenanzahl pro aktuellem Benutzerzug | +| `config.recentAssistantChars` | `number` | Maximale Zeichenanzahl pro aktuellem Assistentenzug | +| `config.cacheTtlMs` | `number` | Cache-Wiederverwendung für wiederholte identische Abfragen | ## Empfohlene Einrichtung @@ -590,8 +674,8 @@ Beginnen Sie mit `recent`. } ``` -Wenn Sie das Live-Verhalten während der Feinabstimmung prüfen möchten, verwenden Sie `/verbose on` für die -normale Statuszeile und `/trace on` für die Active-Memory-Debug-Zusammenfassung statt +Wenn Sie das Live-Verhalten während der Anpassung prüfen möchten, verwenden Sie `/verbose on` für die +normale Statuszeile und `/trace on` für die Active-Memory-Debug-Zusammenfassung, anstatt nach einem separaten Active-Memory-Debug-Befehl zu suchen. In Chat-Kanälen werden diese Diagnosezeilen nach der Hauptantwort des Assistenten gesendet und nicht davor. @@ -607,76 +691,74 @@ Wenn Active Memory nicht dort angezeigt wird, wo Sie es erwarten: 1. Bestätigen Sie, dass das Plugin unter `plugins.entries.active-memory.enabled` aktiviert ist. 2. Bestätigen Sie, dass die aktuelle Agenten-ID in `config.agents` aufgeführt ist. 3. Bestätigen Sie, dass Sie über eine interaktive persistente Chat-Sitzung testen. -4. Aktivieren Sie `config.logging: true` und beobachten Sie die Gateway-Protokolle. -5. Verifizieren Sie mit `openclaw memory status --deep`, dass die Speichersuche selbst funktioniert. +4. Aktivieren Sie `config.logging: true` und beobachten Sie die Gateway-Logs. +5. Überprüfen Sie mit `openclaw memory status --deep`, dass die Speichersuche selbst funktioniert. -Wenn Speicher-Treffer zu verrauscht sind, verschärfen Sie: +Wenn Speicher-Treffer zu ungenau sind, verschärfen Sie: - `maxSummaryChars` Wenn Active Memory zu langsam ist: -- `queryMode` reduzieren -- `timeoutMs` reduzieren -- die Anzahl aktueller Züge reduzieren -- die Zeichenobergrenzen pro Zug reduzieren +- verringern Sie `queryMode` +- verringern Sie `timeoutMs` +- reduzieren Sie die Anzahl aktueller Züge +- reduzieren Sie die Zeichenobergrenzen pro Zug ## Häufige Probleme -### Embedding-Anbieter wurde unerwartet geändert +### Embedding-Anbieter hat sich unerwartet geändert Active Memory verwendet die normale `memory_search`-Pipeline unter `agents.defaults.memorySearch`. Das bedeutet, dass die Einrichtung des Embedding-Anbieters nur dann -erforderlich ist, wenn Ihre `memorySearch`-Einrichtung Embeddings für das gewünschte Verhalten -benötigt. +erforderlich ist, wenn Ihre `memorySearch`-Einrichtung Embeddings für das gewünschte Verhalten benötigt. In der Praxis gilt: -- eine explizite Anbieter-Einrichtung ist **erforderlich**, wenn Sie einen Anbieter möchten, der nicht - automatisch erkannt wird, etwa `ollama` -- eine explizite Anbieter-Einrichtung ist **erforderlich**, wenn die automatische Erkennung - keinen verwendbaren Embedding-Anbieter für Ihre Umgebung auflösen kann -- eine explizite Anbieter-Einrichtung ist **dringend empfohlen**, wenn Sie eine deterministische - Anbieterauswahl statt „der erste verfügbare gewinnt“ möchten -- eine explizite Anbieter-Einrichtung ist in der Regel **nicht erforderlich**, wenn die automatische Erkennung bereits - den gewünschten Anbieter auflöst und dieser in Ihrer Bereitstellung stabil ist +- eine explizite Anbietereinrichtung ist **erforderlich**, wenn Sie einen Anbieter möchten, der nicht + automatisch erkannt wird, z. B. `ollama` +- eine explizite Anbietereinrichtung ist **erforderlich**, wenn die automatische Erkennung + keinen verwendbaren Embedding-Anbieter für Ihre Umgebung auflöst +- eine explizite Anbietereinrichtung ist **dringend empfohlen**, wenn Sie eine deterministische + Anbieterauswahl statt „erstes verfügbares gewinnt“ möchten +- eine explizite Anbietereinrichtung ist normalerweise **nicht erforderlich**, wenn die automatische Erkennung bereits + den gewünschten Anbieter auflöst und dieser Anbieter in Ihrer Bereitstellung stabil ist Wenn `memorySearch.provider` nicht gesetzt ist, erkennt OpenClaw automatisch den ersten verfügbaren Embedding-Anbieter. Das kann in realen Bereitstellungen verwirrend sein: -- ein neu verfügbarer API-Schlüssel kann ändern, welcher Anbieter für die Speichersuche verwendet wird +- ein neu verfügbarer API-Schlüssel kann ändern, welchen Anbieter die Speichersuche verwendet - ein Befehl oder eine Diagnoseoberfläche kann den ausgewählten Anbieter anders erscheinen lassen als den Pfad, den Sie tatsächlich bei der Live-Speichersynchronisierung oder beim - Bootstrap der Suche verwenden -- gehostete Anbieter können mit Kontingent- oder Rate-Limit-Fehlern scheitern, die erst sichtbar werden, - wenn Active Memory vor jeder Antwort Erinnerungsabfragen ausführt + Such-Bootstrap verwenden +- gehostete Anbieter können mit Kontingent- oder Ratenbegrenzungsfehlern scheitern, die erst sichtbar werden, + sobald Active Memory vor jeder Antwort Abrufsuchen ausführt -Active Memory kann auch ohne Embeddings ausgeführt werden, wenn `memory_search` im -degradierten rein lexikalischen Modus arbeiten kann, was typischerweise geschieht, wenn kein Embedding- -Anbieter aufgelöst werden kann. +Active Memory kann auch ohne Embeddings laufen, wenn `memory_search` im degradierten rein lexikalischen Modus +arbeiten kann, was typischerweise geschieht, wenn kein Embedding-Anbieter aufgelöst werden kann. -Gehen Sie nicht davon aus, dass derselbe Fallback bei Laufzeitfehlern des Anbieters gilt, etwa bei ausgeschöpftem Kontingent, -Rate-Limits, Netzwerk-/Anbieterfehlern oder fehlenden lokalen/Remote-Modellen, nachdem bereits -ein Anbieter ausgewählt wurde. +Gehen Sie nicht davon aus, dass derselbe Fallback auch bei Laufzeitfehlern des Anbieters funktioniert, etwa bei +Kontingenterschöpfung, Ratenbegrenzungen, Netzwerk-/Anbieterfehlern oder fehlenden lokalen/entfernten +Modellen, nachdem bereits ein Anbieter ausgewählt wurde. -In der Praxis gilt: +In der Praxis: -- wenn kein Embedding-Anbieter aufgelöst werden kann, kann `memory_search` auf - rein lexikalen Abruf degradieren +- wenn kein Embedding-Anbieter aufgelöst werden kann, kann `memory_search` zu + rein lexikalischem Abruf degradieren - wenn ein Embedding-Anbieter aufgelöst wird und dann zur Laufzeit fehlschlägt, garantiert OpenClaw - derzeit keinen lexikalen Fallback für diese Anfrage -- wenn Sie eine deterministische Anbieterauswahl benötigen, setzen Sie - `agents.defaults.memorySearch.provider` fest + derzeit keinen lexikalischen Fallback für diese Anfrage +- wenn Sie eine deterministische Anbieterauswahl benötigen, pinnen Sie + `agents.defaults.memorySearch.provider` - wenn Sie Anbieter-Failover bei Laufzeitfehlern benötigen, konfigurieren Sie `agents.defaults.memorySearch.fallback` explizit -Wenn Sie auf embedding-gestützten Abruf, multimodale Indizierung oder einen bestimmten -lokalen/Remote-Anbieter angewiesen sind, setzen Sie den Anbieter explizit fest, statt sich auf -die automatische Erkennung zu verlassen. +Wenn Sie auf embeddinggestützten Abruf, multimodale Indizierung oder einen bestimmten +lokalen/entfernten Anbieter angewiesen sind, pinnen Sie den Anbieter explizit, anstatt sich auf +automatische Erkennung zu verlassen. -Häufige Beispiele für das Festlegen: +Häufige Beispiele für das Pinning: OpenAI: @@ -723,8 +805,8 @@ Ollama: } ``` -Wenn Sie Anbieter-Failover bei Laufzeitfehlern wie ausgeschöpftem Kontingent erwarten, -reicht das Festlegen eines Anbieters allein nicht aus. Konfigurieren Sie zusätzlich einen expliziten Fallback: +Wenn Sie Anbieter-Failover bei Laufzeitfehlern wie Kontingenterschöpfung erwarten, +ist das Pinning eines Anbieters allein nicht ausreichend. Konfigurieren Sie zusätzlich einen expliziten Fallback: ```json5 { @@ -741,30 +823,30 @@ reicht das Festlegen eines Anbieters allein nicht aus. Konfigurieren Sie zusätz ### Fehlerbehebung bei Anbieterproblemen -Wenn Active Memory langsam ist, leer bleibt oder Anbieter unerwartet zu wechseln scheint: +Wenn Active Memory langsam ist, leer bleibt oder scheinbar unerwartet den Anbieter wechselt: -- beobachten Sie die Gateway-Protokolle, während Sie das Problem reproduzieren; suchen Sie nach Zeilen wie +- beobachten Sie die Gateway-Logs, während Sie das Problem reproduzieren; achten Sie auf Zeilen wie `active-memory: ... start|done`, `memory sync failed (search-bootstrap)` oder - anbieterbezogenen Embedding-Fehlern + anbieterspezifische Embedding-Fehler - aktivieren Sie `/trace on`, um die Plugin-eigene Active-Memory-Debug-Zusammenfassung in der Sitzung anzuzeigen -- aktivieren Sie `/verbose on`, wenn Sie zusätzlich die normale `🧩 Active Memory: ...` - Statuszeile nach jeder Antwort möchten -- führen Sie `openclaw memory status --deep` aus, um das aktuelle Backend der Speichersuche - und den Zustand des Index zu prüfen -- prüfen Sie `agents.defaults.memorySearch.provider` und die zugehörige Authentifizierung/Konfiguration, um sicherzustellen, - dass der Anbieter, den Sie erwarten, tatsächlich zur Laufzeit aufgelöst werden kann -- wenn Sie `ollama` verwenden, verifizieren Sie, dass das konfigurierte Embedding-Modell installiert ist, zum +- aktivieren Sie `/verbose on`, wenn Sie zusätzlich die normale Statuszeile + `🧩 Active Memory: ...` nach jeder Antwort sehen möchten +- führen Sie `openclaw memory status --deep` aus, um das aktuelle Memory-Search- + Backend und den Zustand des Index zu prüfen +- prüfen Sie `agents.defaults.memorySearch.provider` und die zugehörige Authentifizierung/Konfiguration, um + sicherzustellen, dass der Anbieter, den Sie erwarten, tatsächlich derjenige ist, der zur Laufzeit aufgelöst werden kann +- wenn Sie `ollama` verwenden, prüfen Sie, ob das konfigurierte Embedding-Modell installiert ist, zum Beispiel mit `ollama list` -Beispiel für eine Debugging-Schleife: +Beispiel für einen Debugging-Ablauf: ```text -1. Starten Sie das Gateway und beobachten Sie seine Protokolle -2. Führen Sie in der Chat-Sitzung /trace on aus -3. Senden Sie eine Nachricht, die Active Memory auslösen sollte -4. Vergleichen Sie die im Chat sichtbare Debug-Zeile mit den Gateway-Protokollzeilen -5. Wenn die Anbieterwahl unklar ist, setzen Sie agents.defaults.memorySearch.provider explizit fest +1. Gateway starten und seine Logs beobachten +2. In der Chat-Sitzung /trace on ausführen +3. Eine Nachricht senden, die Active Memory auslösen sollte +4. Die im Chat sichtbare Debug-Zeile mit den Gateway-Logzeilen vergleichen +5. Wenn die Anbieterauswahl uneindeutig ist, agents.defaults.memorySearch.provider explizit pinnen ``` Beispiel: @@ -796,8 +878,8 @@ Oder, wenn Sie Gemini-Embeddings möchten: } ``` -Nach dem Ändern des Anbieters starten Sie das Gateway neu und führen einen frischen Test mit -`/trace on` aus, damit die Active-Memory-Debug-Zeile den neuen Embedding-Pfad widerspiegelt. +Nachdem Sie den Anbieter geändert haben, starten Sie das Gateway neu und führen Sie mit +`/trace on` einen neuen Test aus, damit die Active-Memory-Debug-Zeile den neuen Embedding-Pfad widerspiegelt. ## Verwandte Seiten diff --git a/docs/de/gateway/protocol.md b/docs/de/gateway/protocol.md index c35f75724..19e314d11 100644 --- a/docs/de/gateway/protocol.md +++ b/docs/de/gateway/protocol.md @@ -1,32 +1,32 @@ --- read_when: - - Implementieren oder Aktualisieren von Gateway-WS-Clients - - Debuggen von Protokollabweichungen oder Verbindungsfehlern + - Implementierung oder Aktualisierung von Gateway-WS-Clients + - Fehlersuche bei Protokollabweichungen oder Verbindungsfehlern - Erneutes Generieren von Protokollschema/-modellen summary: 'Gateway-WebSocket-Protokoll: Handshake, Frames, Versionierung' title: Gateway-Protokoll x-i18n: - generated_at: "2026-04-11T02:44:51Z" + generated_at: "2026-04-16T06:22:25Z" model: gpt-5.4 provider: openai - source_hash: 83c820c46d4803d571c770468fd6782619eaa1dca253e156e8087dec735c127f + source_hash: 683e61ebe993a2d739bc34860060b0e3eda36b5c57267a2bcc03d177ec612fb3 source_path: gateway/protocol.md workflow: 15 --- # Gateway-Protokoll (WebSocket) -Das Gateway-WS-Protokoll ist die **einzige Steuerungsebene + der Knotentransport** für -OpenClaw. Alle Clients (CLI, Web-UI, macOS-App, iOS-/Android-Knoten, headless -Knoten) verbinden sich über WebSocket und deklarieren ihre **Rolle** + ihren **Scope** zum -Zeitpunkt des Handshakes. +Das Gateway-WS-Protokoll ist die **einzige Control Plane + der einzige Node-Transport** für +OpenClaw. Alle Clients (CLI, Web-UI, macOS-App, iOS-/Android-Nodes, Headless- +Nodes) verbinden sich über WebSocket und deklarieren ihre **Rolle** + ihren **Scope** beim +Handshake. ## Transport - WebSocket, Text-Frames mit JSON-Payloads. - Der erste Frame **muss** eine `connect`-Anfrage sein. -## Handshake (connect) +## Handshake (`connect`) Gateway → Client (Pre-Connect-Challenge): @@ -80,11 +80,25 @@ Gateway → Client: "type": "res", "id": "…", "ok": true, - "payload": { "type": "hello-ok", "protocol": 3, "policy": { "tickIntervalMs": 15000 } } + "payload": { + "type": "hello-ok", + "protocol": 3, + "server": { "version": "…", "connId": "…" }, + "features": { "methods": ["…"], "events": ["…"] }, + "snapshot": { "…": "…" }, + "policy": { + "maxPayload": 26214400, + "maxBufferedBytes": 52428800, + "tickIntervalMs": 15000 + } + } } ``` -Wenn ein Gerätetoken ausgestellt wird, enthält `hello-ok` außerdem: +`server`, `features`, `snapshot` und `policy` sind laut Schema alle erforderlich +(`src/gateway/protocol/schema/frames.ts`). `auth` und `canvasHostUrl` sind optional. + +Wenn ein Device-Token ausgestellt wird, enthält `hello-ok` außerdem: ```json { @@ -116,14 +130,14 @@ gebundene Rolleneinträge in `deviceTokens` enthalten: } ``` -Für den integrierten Bootstrap-Flow für Knoten/Operatoren bleibt das primäre Knotentoken bei -`scopes: []`, und jedes übergebene Operatortoken bleibt auf die Bootstrap- -Allowlist für Operatoren beschränkt (`operator.approvals`, `operator.read`, -`operator.talk.secrets`, `operator.write`). Prüfungen des Bootstrap-Scopes bleiben -rollenpräfixbasiert: Operatoreinträge erfüllen nur Operatoranfragen, und Nicht-Operator- +Für den integrierten Bootstrap-Ablauf für Node/Operator bleibt das primäre Node-Token bei +`scopes: []`, und jedes übergebene Operator-Token bleibt auf die Bootstrap- +Operator-Allowlist beschränkt (`operator.approvals`, `operator.read`, +`operator.talk.secrets`, `operator.write`). Bootstrap-Scope-Prüfungen bleiben +rollenpräfixiert: Operator-Einträge erfüllen nur Operator-Anfragen, und Nicht-Operator- Rollen benötigen weiterhin Scopes unter ihrem eigenen Rollenpräfix. -### Knotenbeispiel +### Node-Beispiel ```json { @@ -160,20 +174,20 @@ Rollen benötigen weiterhin Scopes unter ihrem eigenen Rollenpräfix. ## Framing -- **Anfrage**: `{type:"req", id, method, params}` -- **Antwort**: `{type:"res", id, ok, payload|error}` -- **Ereignis**: `{type:"event", event, payload, seq?, stateVersion?}` +- **Request**: `{type:"req", id, method, params}` +- **Response**: `{type:"res", id, ok, payload|error}` +- **Event**: `{type:"event", event, payload, seq?, stateVersion?}` -Methoden mit Nebeneffekten erfordern **Idempotenzschlüssel** (siehe Schema). +Methoden mit Nebenwirkungen erfordern **Idempotenzschlüssel** (siehe Schema). ## Rollen + Scopes ### Rollen -- `operator` = Client der Steuerungsebene (CLI/UI/Automatisierung). -- `node` = Fähigkeits-Host (camera/screen/canvas/system.run). +- `operator` = Control-Plane-Client (CLI/UI/Automatisierung). +- `node` = Capability-Host (camera/screen/canvas/system.run). -### Scopes (operator) +### Scopes (`operator`) Häufige Scopes: @@ -187,283 +201,286 @@ Häufige Scopes: `talk.config` mit `includeSecrets: true` erfordert `operator.talk.secrets` (oder `operator.admin`). -Von Plugins registrierte Gateway-RPC-Methoden können ihren eigenen Operator-Scope anfordern, aber +Plugin-registrierte Gateway-RPC-Methoden können ihren eigenen Operator-Scope anfordern, aber reservierte Core-Admin-Präfixe (`config.*`, `exec.approvals.*`, `wizard.*`, `update.*`) werden immer zu `operator.admin` aufgelöst. Der Methodenscope ist nur die erste Hürde. Einige Slash-Befehle, die über -`chat.send` erreicht werden, wenden zusätzlich strengere Prüfungen auf Befehlsebene an. Zum Beispiel erfordern persistente -Schreibvorgänge mit `/config set` und `/config unset` `operator.admin`. +`chat.send` erreicht werden, wenden zusätzlich strengere Prüfungen auf +Befehlsebene an. Zum Beispiel erfordern persistente Schreibvorgänge mit +`/config set` und `/config unset` `operator.admin`. -`node.pair.approve` hat zusätzlich zur -grundlegenden Methodenscope-Prüfung eine weitere Scope-Prüfung zum Zeitpunkt der Genehmigung: +`node.pair.approve` hat zusätzlich zur Basis-Methodenscope außerdem eine weitere +Scope-Prüfung zum Zeitpunkt der Freigabe: -- anfragelose Requests: `operator.pairing` -- Requests mit Nicht-Exec-Knotenbefehlen: `operator.pairing` + `operator.write` -- Requests, die `system.run`, `system.run.prepare` oder `system.which` enthalten: +- Anfragen ohne Befehle: `operator.pairing` +- Anfragen mit Nicht-Exec-Node-Befehlen: `operator.pairing` + `operator.write` +- Anfragen, die `system.run`, `system.run.prepare` oder `system.which` enthalten: `operator.pairing` + `operator.admin` -### Caps/commands/permissions (node) +### Caps/commands/permissions (`node`) -Knoten deklarieren ihre Fähigkeitsansprüche zum Zeitpunkt von `connect`: +Nodes deklarieren ihre Capability-Claims beim Verbinden: -- `caps`: übergeordnete Fähigkeitskategorien. -- `commands`: Allowlist von Befehlen für `invoke`. -- `permissions`: granulare Umschalter (z. B. `screen.record`, `camera.capture`). +- `caps`: allgemeine Capability-Kategorien. +- `commands`: Befehls-Allowlist für `invoke`. +- `permissions`: granulare Schalter (z. B. `screen.record`, `camera.capture`). -Das Gateway behandelt diese als **Ansprüche** und erzwingt serverseitige Allowlists. +Das Gateway behandelt diese als **Claims** und erzwingt serverseitige Allowlists. -## Präsenz +## Presence -- `system-presence` gibt Einträge zurück, die nach Geräteidentität gruppiert sind. -- Präsenzeinträge enthalten `deviceId`, `roles` und `scopes`, damit UIs eine einzelne Zeile pro Gerät anzeigen können, +- `system-presence` gibt Einträge zurück, die nach Geräteidentität indiziert sind. +- Presence-Einträge enthalten `deviceId`, `roles` und `scopes`, damit UIs eine einzelne Zeile pro Gerät anzeigen können, selbst wenn es sowohl als **operator** als auch als **node** verbunden ist. ## Häufige RPC-Methodenfamilien -Diese Seite ist kein vollständig generierter Dump, aber die öffentliche WS-Oberfläche ist breiter -als die oben gezeigten Handshake-/Auth-Beispiele. Dies sind die wichtigsten Methodenfamilien, die das -Gateway heute bereitstellt. +Diese Seite ist kein generierter vollständiger Dump, aber die öffentliche WS-Oberfläche ist umfassender +als die Handshake-/Auth-Beispiele oben. Dies sind die wichtigsten Methodenfamilien, die das +Gateway derzeit bereitstellt. `hello-ok.features.methods` ist eine konservative Discovery-Liste, die aus -`src/gateway/server-methods-list.ts` plus geladenen Plugin-/Kanal-Methodenexporten erstellt wird. -Behandeln Sie sie als Feature-Erkennung, nicht als generierten Dump jeder aufrufbaren Hilfsfunktion, -die in `src/gateway/server-methods/*.ts` implementiert ist. +`src/gateway/server-methods-list.ts` sowie geladenen Plugin-/Channel-Methodenexports aufgebaut wird. +Behandle sie als Feature Discovery, nicht als generierten Dump aller aufrufbaren Helfer, +die in `src/gateway/server-methods/*.ts` implementiert sind. ### System und Identität - `health` gibt den gecachten oder frisch geprüften Gateway-Health-Snapshot zurück. -- `status` gibt die Gateway-Zusammenfassung im Stil von `/status` zurück; sensible Felder sind - nur für Operator-Clients mit Admin-Scope enthalten. +- `status` gibt die Gateway-Zusammenfassung im Stil von `/status` zurück; sensible Felder werden + nur für Operator-Clients mit Admin-Scope einbezogen. - `gateway.identity.get` gibt die Gateway-Geräteidentität zurück, die von Relay- und - Pairing-Flows verwendet wird. -- `system-presence` gibt den aktuellen Präsenz-Snapshot für verbundene - Operator-/Knotengeräte zurück. -- `system-event` hängt ein Systemereignis an und kann den Präsenzkontext - aktualisieren/übertragen. + Pairing-Abläufen verwendet wird. +- `system-presence` gibt den aktuellen Presence-Snapshot für verbundene + Operator-/Node-Geräte zurück. +- `system-event` hängt ein Systemereignis an und kann den Presence- + Kontext aktualisieren/übertragen. - `last-heartbeat` gibt das zuletzt persistierte Heartbeat-Ereignis zurück. - `set-heartbeats` schaltet die Heartbeat-Verarbeitung auf dem Gateway um. ### Modelle und Nutzung - `models.list` gibt den zur Laufzeit erlaubten Modellkatalog zurück. -- `usage.status` gibt Zusammenfassungen von Provider-Nutzungsfenstern/verbleibender Quote zurück. -- `usage.cost` gibt aggregierte Kosten-Nutzungszusammenfassungen für einen Datumsbereich zurück. -- `doctor.memory.status` gibt die Bereitschaft von Vektorspeicher/Embeddings für den +- `usage.status` gibt Provider-Nutzungsfenster/Zusammenfassungen des verbleibenden Kontingents zurück. +- `usage.cost` gibt aggregierte Zusammenfassungen der Kostennutzung für einen Datumsbereich zurück. +- `doctor.memory.status` gibt den Bereitschaftsstatus für Vector Memory / Embeddings für den aktiven Standard-Agent-Workspace zurück. - `sessions.usage` gibt Nutzungszusammenfassungen pro Sitzung zurück. -- `sessions.usage.timeseries` gibt Zeitreihen-Nutzungsdaten für eine Sitzung zurück. -- `sessions.usage.logs` gibt Nutzungseinträge für eine Sitzung zurück. +- `sessions.usage.timeseries` gibt Zeitreihennutzung für eine Sitzung zurück. +- `sessions.usage.logs` gibt Nutzungslogeinträge für eine Sitzung zurück. -### Kanäle und Login-Helfer +### Channels und Login-Helfer -- `channels.status` gibt Statuszusammenfassungen für integrierte und gebündelte Kanäle/Plugins zurück. -- `channels.logout` meldet einen bestimmten Kanal/ein bestimmtes Konto ab, wenn der Kanal +- `channels.status` gibt Statuszusammenfassungen für integrierte und gebündelte Channels/Plugins zurück. +- `channels.logout` meldet einen bestimmten Channel/Account ab, sofern der Channel Logout unterstützt. -- `web.login.start` startet einen QR-/Web-Login-Flow für den aktuellen QR-fähigen Web- - Kanal-Provider. -- `web.login.wait` wartet auf den Abschluss dieses QR-/Web-Login-Flows und startet bei Erfolg den - Kanal. -- `push.test` sendet einen Test-APNs-Push an einen registrierten iOS-Knoten. +- `web.login.start` startet einen QR-/Web-Login-Ablauf für den aktuell QR-fähigen Web- + Channel-Provider. +- `web.login.wait` wartet darauf, dass dieser QR-/Web-Login-Ablauf abgeschlossen wird, und startet bei Erfolg den + Channel. +- `push.test` sendet einen Test-APNs-Push an einen registrierten iOS-Node. - `voicewake.get` gibt die gespeicherten Wake-Word-Trigger zurück. - `voicewake.set` aktualisiert Wake-Word-Trigger und überträgt die Änderung. ### Messaging und Logs -- `send` ist die direkte RPC für ausgehende Zustellung für auf Kanal/Konto/Thread ausgerichtete - Sendungen außerhalb des Chat-Runners. -- `logs.tail` gibt den konfigurierten Gateway-Dateilog-Tail mit Cursor-/Limit- und +- `send` ist die direkte RPC für ausgehende Zustellung an Channel/Account/Thread-Ziele + außerhalb des Chat-Runners. +- `logs.tail` gibt den konfigurierten Gateway-Dateilog-Tail mit Cursor/Limit und Max-Byte-Steuerung zurück. ### Talk und TTS - `talk.config` gibt die effektive Talk-Konfigurations-Payload zurück; `includeSecrets` erfordert `operator.talk.secrets` (oder `operator.admin`). -- `talk.mode` setzt/überträgt den aktuellen Zustand des Talk-Modus für WebChat-/Control-UI- +- `talk.mode` setzt/überträgt den aktuellen Talk-Modusstatus für WebChat-/Control-UI- Clients. -- `talk.speak` synthetisiert Sprache über den aktiven Talk-Sprachprovider. +- `talk.speak` synthetisiert Sprache über den aktiven Talk-Speech-Provider. - `tts.status` gibt den aktivierten TTS-Status, den aktiven Provider, Fallback-Provider - und den Zustand der Provider-Konfiguration zurück. -- `tts.providers` gibt das sichtbare Inventar der TTS-Provider zurück. -- `tts.enable` und `tts.disable` schalten den TTS-Präferenzstatus um. + und den Provider-Konfigurationsstatus zurück. +- `tts.providers` gibt das sichtbare TTS-Provider-Inventar zurück. +- `tts.enable` und `tts.disable` schalten den TTS-Status der Voreinstellungen um. - `tts.setProvider` aktualisiert den bevorzugten TTS-Provider. - `tts.convert` führt eine einmalige Text-zu-Sprache-Konvertierung aus. ### Secrets, Konfiguration, Update und Wizard -- `secrets.reload` löst aktive SecretRefs erneut auf und tauscht den Laufzeit-Secret-Status +- `secrets.reload` löst aktive SecretRefs erneut auf und tauscht den Laufzeitstatus für Secrets nur bei vollständigem Erfolg aus. -- `secrets.resolve` löst zielgerichtete Secret-Zuweisungen für einen bestimmten - Befehl/Zielsatz auf. +- `secrets.resolve` löst Secret-Zuweisungen für ein bestimmtes + Befehls-/Ziel-Set auf. - `config.get` gibt den aktuellen Konfigurations-Snapshot und Hash zurück. - `config.set` schreibt eine validierte Konfigurations-Payload. -- `config.patch` führt eine teilweise Konfigurationsaktualisierung zusammen. +- `config.patch` führt eine partielle Konfigurationsaktualisierung zusammen. - `config.apply` validiert und ersetzt die vollständige Konfigurations-Payload. - `config.schema` gibt die Live-Konfigurationsschema-Payload zurück, die von Control UI und CLI-Tooling verwendet wird: Schema, `uiHints`, Version und Generierungsmetadaten, einschließlich - Plugin- und Kanalschema-Metadaten, wenn die Laufzeit sie laden kann. Das Schema - enthält die Feldmetadaten `title` / `description`, die aus denselben Labels - und Hilfetexten abgeleitet sind, die von der UI verwendet werden, einschließlich verschachtelter Objekt-, - Wildcard-, Array-Item- und `anyOf` / `oneOf` / `allOf`-Kompositionszweige, wenn passende Felddokumentation vorhanden ist. + Plugin- und Channel-Schema-Metadaten, wenn die Laufzeit sie laden kann. Das Schema + enthält Feldmetadaten `title` / `description`, die aus denselben Labels + und Hilfetexten abgeleitet werden, die von der UI verwendet werden, einschließlich verschachtelter Objekte, + Wildcards, Array-Items und `anyOf`- / `oneOf`- / `allOf`-Kompositionszweigen, wenn passende + Felddokumentation existiert. - `config.schema.lookup` gibt eine pfadbezogene Lookup-Payload für einen Konfigurationspfad zurück: - normalisierter Pfad, ein flacher Schemaknoten, passender Hint + `hintPath` sowie - Zusammenfassungen der unmittelbaren Kinder für UI-/CLI-Drill-down. - - Lookup-Schemaknoten behalten die benutzerseitige Dokumentation und allgemeine Validierungsfelder: + normalisierter Pfad, ein flacher Schemaknoten, passender Hint + `hintPath` und + Zusammenfassungen der direkten Kindknoten für UI-/CLI-Drill-down. + - Lookup-Schemaknoten behalten die nutzerseitige Dokumentation und häufige Validierungsfelder: `title`, `description`, `type`, `enum`, `const`, `format`, `pattern`, - numerische/String-/Array-/Objekt-Grenzen sowie boolesche Flags wie + numerische/String-/Array-/Objekt-Grenzen und boolesche Flags wie `additionalProperties`, `deprecated`, `readOnly`, `writeOnly`. - - Kindzusammenfassungen stellen `key`, normalisierten `path`, `type`, `required`, + - Zusammenfassungen der Kindknoten stellen `key`, normalisierten `path`, `type`, `required`, `hasChildren` sowie den passenden `hint` / `hintPath` bereit. -- `update.run` führt den Gateway-Update-Flow aus und plant einen Neustart nur dann, +- `update.run` führt den Gateway-Update-Ablauf aus und plant einen Neustart nur dann, wenn das Update selbst erfolgreich war. - `wizard.start`, `wizard.next`, `wizard.status` und `wizard.cancel` stellen den Onboarding-Wizard über WS RPC bereit. -### Bestehende große Familien +### Vorhandene große Familien -#### Agenten- und Workspace-Helfer +#### Agent- und Workspace-Helfer -- `agents.list` gibt konfigurierte Agenteneinträge zurück. -- `agents.create`, `agents.update` und `agents.delete` verwalten Agentendatensätze und +- `agents.list` gibt konfigurierte Agent-Einträge zurück. +- `agents.create`, `agents.update` und `agents.delete` verwalten Agent-Datensätze und Workspace-Verdrahtung. - `agents.files.list`, `agents.files.get` und `agents.files.set` verwalten die exponierten Bootstrap-Workspace-Dateien eines Agenten. - `agent.identity.get` gibt die effektive Assistentenidentität für einen Agenten oder eine Sitzung zurück. -- `agent.wait` wartet darauf, dass ein Lauf abgeschlossen wird, und gibt den terminalen Snapshot zurück, - wenn verfügbar. +- `agent.wait` wartet darauf, dass ein Lauf abgeschlossen wird, und gibt den terminalen Snapshot zurück, wenn + verfügbar. #### Sitzungssteuerung - `sessions.list` gibt den aktuellen Sitzungsindex zurück. - `sessions.subscribe` und `sessions.unsubscribe` schalten Abonnements für Sitzungsänderungsereignisse - für den aktuellen WS-Client ein bzw. aus. + für den aktuellen WS-Client um. - `sessions.messages.subscribe` und `sessions.messages.unsubscribe` schalten - Protokoll-/Nachrichtenereignis-Abonnements für eine Sitzung ein bzw. aus. -- `sessions.preview` gibt begrenzte Protokollvorschauen für bestimmte Sitzungsschlüssel - zurück. + Abonnements für Transkript-/Nachrichtenereignisse für eine Sitzung um. +- `sessions.preview` gibt begrenzte Transkriptvorschauen für bestimmte Sitzungs- + Schlüssel zurück. - `sessions.resolve` löst ein Sitzungsziel auf oder kanonisiert es. - `sessions.create` erstellt einen neuen Sitzungseintrag. - `sessions.send` sendet eine Nachricht in eine bestehende Sitzung. -- `sessions.steer` ist die Interrupt-und-Steer-Variante für eine aktive Sitzung. +- `sessions.steer` ist die Variante zum Unterbrechen und Neu-Steuern für eine aktive Sitzung. - `sessions.abort` bricht aktive Arbeit für eine Sitzung ab. -- `sessions.patch` aktualisiert Sitzungsmetadaten/-Überschreibungen. +- `sessions.patch` aktualisiert Sitzungsmetadaten/-Overrides. - `sessions.reset`, `sessions.delete` und `sessions.compact` führen Sitzungs- - Wartungsaufgaben aus. + Wartung durch. - `sessions.get` gibt die vollständige gespeicherte Sitzungszeile zurück. - Die Chat-Ausführung verwendet weiterhin `chat.history`, `chat.send`, `chat.abort` und `chat.inject`. -- `chat.history` ist für UI-Clients anzeige-normalisiert: Inline-Direktiv-Tags werden +- `chat.history` ist für UI-Clients anzeige-normalisiert: Inline-Direktiventags werden aus sichtbarem Text entfernt, XML-Payloads von Tool-Aufrufen im Klartext (einschließlich `...`, `...`, `...`, `...` und abgeschnittener Tool-Call-Blöcke) sowie durchgesickerte ASCII-/Full-Width- - Modellsteuerungstoken werden entfernt, reine stille-Token-Assistentenzeilen wie exakt `NO_REPLY` / - `no_reply` werden ausgelassen, und übergroße Zeilen können durch Platzhalter ersetzt werden. + Modellsteuerungstoken werden entfernt, reine Assistant-Zeilen mit stillen Tokens wie exaktem `NO_REPLY` / + `no_reply` werden weggelassen, und übergroße Zeilen können durch Platzhalter ersetzt werden. -#### Geräte-Pairing und Gerätetoken +#### Geräte-Pairing und Device-Tokens -- `device.pair.list` gibt ausstehende und genehmigte gepaarte Geräte zurück. +- `device.pair.list` gibt ausstehende und genehmigte gekoppelte Geräte zurück. - `device.pair.approve`, `device.pair.reject` und `device.pair.remove` verwalten Geräte-Pairing-Einträge. -- `device.token.rotate` rotiert ein gepaartes Gerätetoken innerhalb seiner genehmigten Rollen- +- `device.token.rotate` rotiert ein Token für ein gekoppeltes Gerät innerhalb seiner genehmigten Rollen- und Scope-Grenzen. -- `device.token.revoke` widerruft ein gepaartes Gerätetoken. +- `device.token.revoke` widerruft ein Token für ein gekoppeltes Gerät. -#### Knoten-Pairing, Invoke und ausstehende Arbeit +#### Node-Pairing, Invoke und ausstehende Arbeit - `node.pair.request`, `node.pair.list`, `node.pair.approve`, - `node.pair.reject` und `node.pair.verify` decken Knoten-Pairing und Bootstrap- + `node.pair.reject` und `node.pair.verify` decken Node-Pairing und Bootstrap- Verifizierung ab. -- `node.list` und `node.describe` geben den bekannten/verbundenen Knotenzustand zurück. -- `node.rename` aktualisiert eine Bezeichnung eines gepaarten Knotens. -- `node.invoke` leitet einen Befehl an einen verbundenen Knoten weiter. +- `node.list` und `node.describe` geben bekannte/verbundene Node-Zustände zurück. +- `node.rename` aktualisiert ein Label für einen gekoppelten Node. +- `node.invoke` leitet einen Befehl an einen verbundenen Node weiter. - `node.invoke.result` gibt das Ergebnis für eine Invoke-Anfrage zurück. -- `node.event` überträgt von Knoten stammende Ereignisse zurück in das Gateway. -- `node.canvas.capability.refresh` aktualisiert bereichsbezogene Canvas-Fähigkeitstoken. -- `node.pending.pull` und `node.pending.ack` sind die Queue-APIs für verbundene Knoten. +- `node.event` überträgt von Nodes stammende Ereignisse zurück ins Gateway. +- `node.canvas.capability.refresh` aktualisiert bereichsbezogene Canvas-Capability-Tokens. +- `node.pending.pull` und `node.pending.ack` sind die Queue-APIs für verbundene Nodes. - `node.pending.enqueue` und `node.pending.drain` verwalten dauerhafte ausstehende Arbeit - für offline/getrennte Knoten. + für offline/getrennte Nodes. #### Genehmigungsfamilien - `exec.approval.request`, `exec.approval.get`, `exec.approval.list` und - `exec.approval.resolve` decken einmalige Exec-Genehmigungsanfragen plus Nachschlagen/Wiederholen + `exec.approval.resolve` decken einmalige Exec-Genehmigungsanfragen sowie Lookup/Replay ausstehender Genehmigungen ab. - `exec.approval.waitDecision` wartet auf eine ausstehende Exec-Genehmigung und gibt - die endgültige Entscheidung zurück (oder `null` bei Zeitüberschreitung). -- `exec.approvals.get` und `exec.approvals.set` verwalten Gateway-Exec-Genehmigungs- - Richtlinien-Snapshots. -- `exec.approvals.node.get` und `exec.approvals.node.set` verwalten knotenspezifische Exec- - Genehmigungsrichtlinien über Knoten-Relay-Befehle. + die finale Entscheidung zurück (oder `null` bei Timeout). +- `exec.approvals.get` und `exec.approvals.set` verwalten Snapshots der Gateway-Exec- + Genehmigungsrichtlinie. +- `exec.approvals.node.get` und `exec.approvals.node.set` verwalten Node-lokale Exec- + Genehmigungsrichtlinien über Node-Relay-Befehle. - `plugin.approval.request`, `plugin.approval.list`, `plugin.approval.waitDecision` und `plugin.approval.resolve` decken - plugin-definierte Genehmigungs-Flows ab. + Plugin-definierte Genehmigungsabläufe ab. -#### Andere große Familien +#### Weitere große Familien - Automatisierung: - - `wake` plant eine sofortige oder bei einem nächsten Heartbeat erfolgende Wake-Text-Injektion + - `wake` plant eine sofortige oder beim nächsten Heartbeat erfolgende Wake-Textinjektion - `cron.list`, `cron.status`, `cron.add`, `cron.update`, `cron.remove`, `cron.run`, `cron.runs` - Skills/Tools: `commands.list`, `skills.*`, `tools.catalog`, `tools.effective` ### Häufige Ereignisfamilien -- `chat`: UI-Chat-Updates wie `chat.inject` und andere reine Protokoll-Chat- - Ereignisse. -- `session.message` und `session.tool`: Protokoll-/Ereignisstream-Updates für eine +- `chat`: UI-Chat-Updates wie `chat.inject` und andere Chat- + Ereignisse nur für das Transkript. +- `session.message` und `session.tool`: Transkript-/Ereignisstream-Updates für eine abonnierte Sitzung. -- `sessions.changed`: Sitzungsindex oder Metadaten haben sich geändert. -- `presence`: Snapshot-Aktualisierungen der Systempräsenz. +- `sessions.changed`: Sitzungsindex oder -metadaten wurden geändert. +- `presence`: Updates des System-Presence-Snapshots. - `tick`: periodisches Keepalive-/Liveness-Ereignis. -- `health`: Gateway-Health-Snapshot-Aktualisierung. -- `heartbeat`: Aktualisierung des Heartbeat-Ereignisstreams. -- `cron`: Änderungsereignis für Cron-Lauf/-Job. -- `shutdown`: Gateway-Abschaltbenachrichtigung. -- `node.pair.requested` / `node.pair.resolved`: Lebenszyklus des Knoten-Pairings. -- `node.invoke.request`: Broadcast einer Knoten-Invoke-Anfrage. -- `device.pair.requested` / `device.pair.resolved`: Lebenszyklus gepaarter Geräte. -- `voicewake.changed`: Konfiguration von Wake-Word-Triggern wurde geändert. +- `health`: Update des Gateway-Health-Snapshots. +- `heartbeat`: Update des Heartbeat-Ereignisstreams. +- `cron`: Ereignis für Änderung von Cron-Lauf/Job. +- `shutdown`: Benachrichtigung über das Herunterfahren des Gateways. +- `node.pair.requested` / `node.pair.resolved`: Lebenszyklus des Node-Pairings. +- `node.invoke.request`: Broadcast einer Node-Invoke-Anfrage. +- `device.pair.requested` / `device.pair.resolved`: Lebenszyklus gekoppelter Geräte. +- `voicewake.changed`: Konfiguration des Wake-Word-Triggers wurde geändert. - `exec.approval.requested` / `exec.approval.resolved`: Lebenszyklus der Exec- Genehmigung. -- `plugin.approval.requested` / `plugin.approval.resolved`: Lebenszyklus der Plugin-Genehmigung. +- `plugin.approval.requested` / `plugin.approval.resolved`: Lebenszyklus der Plugin- + Genehmigung. -### Hilfsmethoden für Knoten +### Node-Helfermethoden -- Knoten können `skills.bins` aufrufen, um die aktuelle Liste ausführbarer Skills +- Nodes können `skills.bins` aufrufen, um die aktuelle Liste ausführbarer Skill-Dateien für Auto-Allow-Prüfungen abzurufen. -### Hilfsmethoden für Operatoren +### Operator-Helfermethoden - Operatoren können `commands.list` (`operator.read`) aufrufen, um das Laufzeit- Befehlsinventar für einen Agenten abzurufen. - - `agentId` ist optional; lassen Sie es weg, um den Standard-Agent-Workspace zu lesen. - - `scope` steuert, welche Oberfläche vom primären `name` angesprochen wird: + - `agentId` ist optional; lasse es weg, um den Standard-Agent-Workspace zu lesen. + - `scope` steuert, auf welche Oberfläche sich `name` primär bezieht: - `text` gibt das primäre Textbefehlstoken ohne führendes `/` zurück - `native` und der Standardpfad `both` geben providerbewusste native Namen zurück, wenn verfügbar - `textAliases` enthält exakte Slash-Aliasse wie `/model` und `/m`. - - `nativeName` enthält den providerbewussten nativen Befehlsnamen, wenn einer existiert. - - `provider` ist optional und beeinflusst nur native Benennung sowie die Verfügbarkeit - nativer Plugin-Befehle. + - `nativeName` enthält den providerbewussten nativen Befehlsnamen, wenn ein solcher existiert. + - `provider` ist optional und beeinflusst nur die native Benennung sowie die Verfügbarkeit nativer Plugin- + Befehle. - `includeArgs=false` lässt serialisierte Argumentmetadaten in der Antwort weg. - Operatoren können `tools.catalog` (`operator.read`) aufrufen, um den Laufzeit-Toolkatalog für einen Agenten abzurufen. Die Antwort enthält gruppierte Tools und Herkunftsmetadaten: - `source`: `core` oder `plugin` - `pluginId`: Plugin-Eigentümer, wenn `source="plugin"` - `optional`: ob ein Plugin-Tool optional ist -- Operatoren können `tools.effective` (`operator.read`) aufrufen, um das zur Laufzeit wirksame Tool- +- Operatoren können `tools.effective` (`operator.read`) aufrufen, um das zur Laufzeit effektive Tool- Inventar für eine Sitzung abzurufen. - `sessionKey` ist erforderlich. - - Das Gateway leitet vertrauenswürdigen Laufzeitkontext serverseitig aus der Sitzung ab, statt - vom Aufrufer gelieferten Auth- oder Zustellungskontext zu akzeptieren. - - Die Antwort ist sitzungsbezogen und spiegelt wider, was die aktive Unterhaltung derzeit nutzen kann, - einschließlich Core-, Plugin- und Kanal-Tools. + - Das Gateway leitet den vertrauenswürdigen Laufzeitkontext serverseitig aus der Sitzung ab, statt + vom Aufrufer bereitgestellten Auth- oder Zustellungskontext zu akzeptieren. + - Die Antwort ist sitzungsbezogen und spiegelt wider, was die aktive Unterhaltung derzeit verwenden kann, + einschließlich Core-, Plugin- und Channel-Tools. - Operatoren können `skills.status` (`operator.read`) aufrufen, um das sichtbare - Skill-Inventar für einen Agenten abzurufen. - - `agentId` ist optional; lassen Sie es weg, um den Standard-Agent-Workspace zu lesen. - - Die Antwort enthält Eignung, fehlende Anforderungen, Konfigurationsprüfungen und + Skills-Inventar für einen Agenten abzurufen. + - `agentId` ist optional; lasse es weg, um den Standard-Agent-Workspace zu lesen. + - Die Antwort enthält Eignung, fehlende Voraussetzungen, Konfigurationsprüfungen und bereinigte Installationsoptionen, ohne rohe Secret-Werte offenzulegen. - Operatoren können `skills.search` und `skills.detail` (`operator.read`) für ClawHub-Discovery-Metadaten aufrufen. @@ -473,134 +490,166 @@ die in `src/gateway/server-methods/*.ts` implementiert ist. - Gateway-Installer-Modus: `{ name, installId, dangerouslyForceUnsafeInstall?, timeoutMs? }` führt eine deklarierte `metadata.openclaw.install`-Aktion auf dem Gateway-Host aus. - Operatoren können `skills.update` (`operator.admin`) in zwei Modi aufrufen: - - Der ClawHub-Modus aktualisiert einen verfolgten Slug oder alle verfolgten ClawHub-Installationen im - Standard-Agent-Workspace. - - Der Konfigurationsmodus patcht `skills.entries.`-Werte wie `enabled`, - `apiKey` und `env`. + - Im ClawHub-Modus wird ein verfolgter Slug oder werden alle verfolgten ClawHub-Installationen im + Standard-Agent-Workspace aktualisiert. + - Im Konfigurationsmodus werden Werte in `skills.entries.` wie `enabled`, + `apiKey` und `env` gepatcht. ## Exec-Genehmigungen -- Wenn eine Exec-Anfrage eine Genehmigung benötigt, überträgt das Gateway `exec.approval.requested`. -- Operator-Clients lösen dies durch Aufruf von `exec.approval.resolve` auf (erfordert Scope `operator.approvals`). -- Für `host=node` muss `exec.approval.request` `systemRunPlan` enthalten (kanonische `argv`/`cwd`/`rawCommand`/Sitzungsmetadaten). Anfragen ohne `systemRunPlan` werden abgelehnt. -- Nach der Genehmigung verwenden weitergeleitete `node.invoke system.run`-Aufrufe dieses kanonische - `systemRunPlan` als maßgeblichen Befehls-/cwd-/Sitzungskontext. +- Wenn eine Exec-Anfrage eine Genehmigung benötigt, sendet das Gateway `exec.approval.requested`. +- Operator-Clients lösen dies durch Aufruf von `exec.approval.resolve` auf (erfordert den Scope `operator.approvals`). +- Für `host=node` muss `exec.approval.request` `systemRunPlan` enthalten (kanonische `argv`-/`cwd`-/`rawCommand`-/Sitzungsmetadaten). Anfragen ohne `systemRunPlan` werden abgelehnt. +- Nach der Genehmigung verwenden weitergeleitete `node.invoke system.run`-Aufrufe denselben kanonischen + `systemRunPlan` als maßgeblichen Befehls-/`cwd`-/Sitzungskontext. - Wenn ein Aufrufer `command`, `rawCommand`, `cwd`, `agentId` oder - `sessionKey` zwischen der Vorbereitung und der endgültigen genehmigten Weiterleitung von `system.run` verändert, lehnt das - Gateway den Lauf ab, statt der veränderten Payload zu vertrauen. + `sessionKey` zwischen `prepare` und dem finalen weitergeleiteten genehmigten `system.run` ändert, lehnt das + Gateway den Lauf ab, statt der geänderten Payload zu vertrauen. -## Agenten-Zustellungs-Fallback +## Agent-Zustellungs-Fallback - `agent`-Anfragen können `deliver=true` enthalten, um ausgehende Zustellung anzufordern. -- `bestEffortDeliver=false` behält striktes Verhalten bei: nicht auflösbare oder nur intern verfügbare Zustellungsziele geben `INVALID_REQUEST` zurück. -- `bestEffortDeliver=true` erlaubt Fallback auf reine Sitzungsausführung, wenn keine extern zustellbare Route aufgelöst werden kann (zum Beispiel interne/WebChat-Sitzungen oder mehrdeutige Multi-Kanal-Konfigurationen). +- `bestEffortDeliver=false` behält striktes Verhalten bei: Nicht auflösbare oder nur intern verfügbare Zustellungsziele geben `INVALID_REQUEST` zurück. +- `bestEffortDeliver=true` erlaubt Fallback auf reine Sitzungsausführung, wenn keine extern zustellbare Route aufgelöst werden kann (zum Beispiel bei internen/WebChat-Sitzungen oder mehrdeutigen Multi-Channel-Konfigurationen). ## Versionierung -- `PROTOCOL_VERSION` befindet sich in `src/gateway/protocol/schema.ts`. +- `PROTOCOL_VERSION` befindet sich in `src/gateway/protocol/schema/protocol-schemas.ts`. - Clients senden `minProtocol` + `maxProtocol`; der Server lehnt Abweichungen ab. - Schemas + Modelle werden aus TypeBox-Definitionen generiert: - `pnpm protocol:gen` - `pnpm protocol:gen:swift` - `pnpm protocol:check` +### Client-Konstanten + +Der Referenzclient in `src/gateway/client.ts` verwendet diese Standardwerte. Die Werte sind +über Protokoll v3 hinweg stabil und bilden die erwartete Basis für Drittanbieter-Clients. + +| Konstante | Standardwert | Quelle | +| ----------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------- | +| `PROTOCOL_VERSION` | `3` | `src/gateway/protocol/schema/protocol-schemas.ts` | +| Request-Timeout (pro RPC) | `30_000` ms | `src/gateway/client.ts` (`requestTimeoutMs`) | +| Preauth- / Connect-Challenge-Timeout | `10_000` ms | `src/gateway/handshake-timeouts.ts` (Clamp `250`–`10_000`) | +| Initialer Reconnect-Backoff | `1_000` ms | `src/gateway/client.ts` (`backoffMs`) | +| Maximaler Reconnect-Backoff | `30_000` ms | `src/gateway/client.ts` (`scheduleReconnect`) | +| Fast-Retry-Clamp nach Device-Token-Close | `250` ms | `src/gateway/client.ts` | +| Force-Stop-Schonfrist vor `terminate()` | `250` ms | `FORCE_STOP_TERMINATE_GRACE_MS` | +| Standard-Timeout von `stopAndWait()` | `1_000` ms | `STOP_AND_WAIT_TIMEOUT_MS` | +| Standard-Tick-Intervall (vor `hello-ok`) | `30_000` ms | `src/gateway/client.ts` | +| Tick-Timeout-Close | Code `4000`, wenn Stille `tickIntervalMs * 2` überschreitet | `src/gateway/client.ts` | +| `MAX_PAYLOAD_BYTES` | `25 * 1024 * 1024` (25 MB) | `src/gateway/server-constants.ts` | + +Der Server kündigt das effektive `policy.tickIntervalMs`, `policy.maxPayload` +und `policy.maxBufferedBytes` in `hello-ok` an; Clients sollten diese Werte +statt der Standardwerte vor dem Handshake beachten. + ## Auth -- Gateway-Auth mit gemeinsamem Secret verwendet `connect.params.auth.token` oder +- Authentifizierung des Gateways per Shared Secret verwendet `connect.params.auth.token` oder `connect.params.auth.password`, abhängig vom konfigurierten Auth-Modus. - Identitätstragende Modi wie Tailscale Serve - (`gateway.auth.allowTailscale: true`) oder Nicht-Loopback- + (`gateway.auth.allowTailscale: true`) oder nicht über Loopback laufendes `gateway.auth.mode: "trusted-proxy"` erfüllen die Connect-Auth-Prüfung über Request-Header statt über `connect.params.auth.*`. -- `gateway.auth.mode: "none"` für private Ingress überspringt die Connect-Auth mit gemeinsamem Secret - vollständig; setzen Sie diesen Modus nicht an öffentlichem/nicht vertrauenswürdigem Ingress ein. -- Nach dem Pairing stellt das Gateway ein **Gerätetoken** aus, das auf die Verbindungs- - Rolle + Scopes begrenzt ist. Es wird in `hello-ok.auth.deviceToken` zurückgegeben und sollte +- `gateway.auth.mode: "none"` für privaten Ingress überspringt die Connect-Auth per Shared Secret + vollständig; diesen Modus nicht auf öffentlichem/nicht vertrauenswürdigem Ingress bereitstellen. +- Nach dem Pairing stellt das Gateway ein **Device-Token** aus, das auf die Rollen + Scopes + der Verbindung begrenzt ist. Es wird in `hello-ok.auth.deviceToken` zurückgegeben und sollte vom Client für zukünftige Verbindungen persistiert werden. - Clients sollten das primäre `hello-ok.auth.deviceToken` nach jeder erfolgreichen Verbindung persistieren. -- Eine erneute Verbindung mit diesem **gespeicherten** Gerätetoken sollte außerdem den gespeicherten - genehmigten Scope-Satz für dieses Token wiederverwenden. Dadurch bleibt bereits gewährter - Lese-/Probe-/Status-Zugriff erhalten und es wird vermieden, dass Reconnects stillschweigend auf einen - engeren impliziten Admin-Only-Scope zurückfallen. -- Die normale Priorität bei Connect-Auth ist zuerst explizites gemeinsames Token/Passwort, dann - explizites `deviceToken`, dann gespeichertes gerätespezifisches Token, dann Bootstrap-Token. -- Zusätzliche Einträge in `hello-ok.auth.deviceTokens` sind Bootstrap-Übergabetoken. - Persistieren Sie diese nur, wenn die Verbindung Bootstrap-Auth auf einem vertrauenswürdigen Transport - wie `wss://` oder Loopback/lokal verwendet hat. +- Beim erneuten Verbinden mit diesem **gespeicherten** Device-Token sollte auch der gespeicherte + genehmigte Scope-Satz für dieses Token wiederverwendet werden. Dadurch bleibt bereits gewährter + Zugriff für Lesen/Probe/Status erhalten und es wird vermieden, dass Reconnects stillschweigend auf einen + engeren impliziten Admin-only-Scope zusammenfallen. +- Clientseitiger Aufbau der Connect-Auth (`selectConnectAuth` in + `src/gateway/client.ts`): + - `auth.password` ist orthogonal und wird immer weitergeleitet, wenn gesetzt. + - `auth.token` wird in dieser Prioritätsreihenfolge befüllt: zuerst explizites Shared Token, + dann ein explizites `deviceToken`, dann ein gespeichertes gerätespezifisches Token (indiziert nach + `deviceId` + `role`). + - `auth.bootstrapToken` wird nur gesendet, wenn keines der oben genannten ein + `auth.token` aufgelöst hat. Ein Shared Token oder ein aufgelöstes Device-Token unterdrückt es. + - Automatische Promotion eines gespeicherten Device-Tokens beim einmaligen + Retry bei `AUTH_TOKEN_MISMATCH` ist **nur für vertrauenswürdige Endpunkte** aktiviert — + Loopback oder `wss://` mit angeheftetem `tlsFingerprint`. Öffentliches `wss://` + ohne Pinning qualifiziert sich nicht. +- Zusätzliche Einträge in `hello-ok.auth.deviceTokens` sind Bootstrap-Handoff-Tokens. + Persistiere sie nur, wenn die Verbindung Bootstrap-Auth über einen vertrauenswürdigen Transport + wie `wss://` oder Loopback/lokales Pairing verwendet hat. - Wenn ein Client ein **explizites** `deviceToken` oder explizite `scopes` angibt, bleibt dieser - vom Aufrufer angeforderte Scope-Satz maßgeblich; gecachte Scopes werden nur - wiederverwendet, wenn der Client das gespeicherte gerätespezifische Token wiederverwendet. -- Gerätetoken können über `device.token.rotate` und - `device.token.revoke` rotiert/widerrufen werden (erfordert Scope `operator.pairing`). -- Die Ausgabe/Rotation von Token bleibt auf den genehmigten Rollensatz begrenzt, der im - Pairing-Eintrag dieses Geräts gespeichert ist; durch das Rotieren eines Tokens kann das Gerät nicht auf eine - Rolle erweitert werden, die durch die Pairing-Genehmigung nie erteilt wurde. -- Für Sitzungen mit gepaarten Gerätetokens ist die Geräteverwaltung auf das eigene Gerät beschränkt, sofern der + vom Aufrufer angeforderte Scope-Satz maßgeblich; gecachte Scopes werden nur wiederverwendet, wenn + der Client das gespeicherte gerätespezifische Token wiederverwendet. +- Device-Tokens können über `device.token.rotate` und + `device.token.revoke` rotiert/widerrufen werden (erfordert den Scope `operator.pairing`). +- Ausstellung/Rotation von Tokens bleibt auf den genehmigten Rollensatz beschränkt, der im + Pairing-Eintrag dieses Geräts aufgezeichnet ist; durch die Rotation eines Tokens kann ein Gerät nicht + auf eine Rolle erweitert werden, die durch die Pairing-Genehmigung nie gewährt wurde. +- Für Sitzungen mit Tokens gekoppelter Geräte ist die Geräteverwaltung selbstbegrenzt, sofern der Aufrufer nicht zusätzlich `operator.admin` hat: Nicht-Admin-Aufrufer können nur ihren **eigenen** Geräteeintrag entfernen/widerrufen/rotieren. - `device.token.rotate` prüft außerdem den angeforderten Operator-Scope-Satz gegen die aktuellen Sitzungsscopes des Aufrufers. Nicht-Admin-Aufrufer können ein Token nicht in einen - breiteren Operator-Scope-Satz rotieren, als sie bereits besitzen. -- Auth-Fehler enthalten `error.details.code` plus Wiederherstellungshinweise: - - `error.details.canRetryWithDeviceToken` (boolean) + umfassenderen Operator-Scope-Satz rotieren, als sie bereits besitzen. +- Auth-Fehler enthalten `error.details.code` sowie Wiederherstellungshinweise: + - `error.details.canRetryWithDeviceToken` (boolesch) - `error.details.recommendedNextStep` (`retry_with_device_token`, `update_auth_configuration`, `update_auth_credentials`, `wait_then_retry`, `review_auth_configuration`) -- Client-Verhalten für `AUTH_TOKEN_MISMATCH`: - - Vertrauenswürdige Clients können einen begrenzten Wiederholungsversuch mit einem gecachten gerätespezifischen Token unternehmen. - - Wenn dieser Wiederholungsversuch fehlschlägt, sollten Clients automatische Reconnect-Schleifen beenden und Hinweise für erforderliche Operatoraktionen anzeigen. +- Client-Verhalten bei `AUTH_TOKEN_MISMATCH`: + - Vertrauenswürdige Clients können einen begrenzten Retry mit einem gecachten gerätespezifischen Token versuchen. + - Wenn dieser Retry fehlschlägt, sollten Clients automatische Reconnect-Schleifen stoppen und Hinweise für Operator-Aktionen anzeigen. ## Geräteidentität + Pairing -- Knoten sollten eine stabile Geräteidentität (`device.id`) angeben, die von einem +- Nodes sollten eine stabile Geräteidentität (`device.id`) angeben, die aus einem Schlüsselpaar-Fingerprint abgeleitet ist. -- Gateways stellen Token pro Gerät + Rolle aus. -- Pairing-Genehmigungen sind für neue Geräte-IDs erforderlich, sofern keine lokale automatische Genehmigung - aktiviert ist. -- Die automatische Pairing-Genehmigung konzentriert sich auf direkte lokale local loopback-Verbindungen. -- OpenClaw hat außerdem einen engen backend-/containerlokalen Self-Connect-Pfad für - vertrauenswürdige Helper-Flows mit gemeinsamem Secret. -- Same-Host-Tailnet- oder LAN-Verbindungen werden für das Pairing weiterhin als remote behandelt und +- Gateways stellen Tokens pro Gerät + Rolle aus. +- Pairing-Genehmigungen sind für neue Geräte-IDs erforderlich, sofern lokale automatische Genehmigung + nicht aktiviert ist. +- Die automatische Pairing-Genehmigung ist auf direkte lokale Loopback-Verbindungen ausgerichtet. +- OpenClaw hat außerdem einen engen lokalen Self-Connect-Pfad für Backend-/Container- + Kontexte für vertrauenswürdige Shared-Secret-Helferabläufe. +- Verbindungen über dasselbe Host-Tailnet oder LAN werden für Pairing weiterhin als remote behandelt und erfordern eine Genehmigung. -- Alle WS-Clients müssen während `connect` eine `device`-Identität angeben (operator + node). +- Alle WS-Clients müssen während `connect` die Geräteidentität `device` angeben (`operator` + `node`). Control UI kann sie nur in diesen Modi weglassen: - - `gateway.controlUi.allowInsecureAuth=true` für localhost-only inkompatibilitätsbedingte unsichere HTTP-Unterstützung. - - erfolgreiche `gateway.auth.mode: "trusted-proxy"`-Authentifizierung für operator Control UI. - - `gateway.controlUi.dangerouslyDisableDeviceAuth=true` (Break-Glass, schwerwiegende Sicherheitsverschlechterung). -- Alle Verbindungen müssen den vom Server bereitgestellten `connect.challenge`-Nonce signieren. + - `gateway.controlUi.allowInsecureAuth=true` für localhost-only-Kompatibilität mit unsicherem HTTP. + - erfolgreiche `gateway.auth.mode: "trusted-proxy"`-Authentifizierung der Operator-Control-UI. + - `gateway.controlUi.dangerouslyDisableDeviceAuth=true` (Break-Glass, erhebliche Sicherheitsabsenkung). +- Alle Verbindungen müssen die vom Server bereitgestellte Nonce `connect.challenge` signieren. -### Diagnostik für die Migration der Geräteauthentifizierung +### Migrationsdiagnostik für Geräte-Auth Für Legacy-Clients, die noch das Signaturverhalten vor der Challenge verwenden, gibt `connect` jetzt -`DEVICE_AUTH_*`-Detailcodes unter `error.details.code` mit einem stabilen `error.details.reason` zurück. +Detailcodes `DEVICE_AUTH_*` unter `error.details.code` mit einem stabilen `error.details.reason` zurück. Häufige Migrationsfehler: -| Nachricht | details.code | details.reason | Bedeutung | -| ---------------------------- | -------------------------------- | ------------------------ | --------------------------------------------------- | -| `device nonce required` | `DEVICE_AUTH_NONCE_REQUIRED` | `device-nonce-missing` | Client hat `device.nonce` weggelassen (oder leer gesendet). | -| `device nonce mismatch` | `DEVICE_AUTH_NONCE_MISMATCH` | `device-nonce-mismatch` | Client hat mit einem veralteten/falschen Nonce signiert. | -| `device signature invalid` | `DEVICE_AUTH_SIGNATURE_INVALID` | `device-signature` | Die Signatur-Payload stimmt nicht mit der v2-Payload überein. | -| `device signature expired` | `DEVICE_AUTH_SIGNATURE_EXPIRED` | `device-signature-stale` | Der signierte Zeitstempel liegt außerhalb der zulässigen Abweichung. | -| `device identity mismatch` | `DEVICE_AUTH_DEVICE_ID_MISMATCH` | `device-id-mismatch` | `device.id` stimmt nicht mit dem Public-Key-Fingerprint überein. | -| `device public key invalid` | `DEVICE_AUTH_PUBLIC_KEY_INVALID` | `device-public-key` | Format/Kanonisierung des Public Key ist fehlgeschlagen. | +| Meldung | details.code | details.reason | Bedeutung | +| --------------------------- | -------------------------------- | ------------------------ | ----------------------------------------------------------- | +| `device nonce required` | `DEVICE_AUTH_NONCE_REQUIRED` | `device-nonce-missing` | Client hat `device.nonce` weggelassen (oder leer gesendet). | +| `device nonce mismatch` | `DEVICE_AUTH_NONCE_MISMATCH` | `device-nonce-mismatch` | Client hat mit einer veralteten/falschen Nonce signiert. | +| `device signature invalid` | `DEVICE_AUTH_SIGNATURE_INVALID` | `device-signature` | Signatur-Payload entspricht nicht der v2-Payload. | +| `device signature expired` | `DEVICE_AUTH_SIGNATURE_EXPIRED` | `device-signature-stale` | Signierter Zeitstempel liegt außerhalb des erlaubten Skews. | +| `device identity mismatch` | `DEVICE_AUTH_DEVICE_ID_MISMATCH` | `device-id-mismatch` | `device.id` entspricht nicht dem Public-Key-Fingerprint. | +| `device public key invalid` | `DEVICE_AUTH_PUBLIC_KEY_INVALID` | `device-public-key` | Public-Key-Format/Kanonisierung fehlgeschlagen. | Migrationsziel: -- Warten Sie immer auf `connect.challenge`. -- Signieren Sie die v2-Payload, die den Server-Nonce enthält. -- Senden Sie denselben Nonce in `connect.params.device.nonce`. +- Immer auf `connect.challenge` warten. +- Die v2-Payload signieren, die die Server-Nonce enthält. +- Dieselbe Nonce in `connect.params.device.nonce` senden. - Die bevorzugte Signatur-Payload ist `v3`, die zusätzlich zu den Feldern für device/client/role/scopes/token/nonce auch `platform` und `deviceFamily` bindet. -- Legacy-`v2`-Signaturen bleiben aus Kompatibilitätsgründen akzeptiert, aber das Pinning der gepaarten Geräte- - Metadaten steuert weiterhin die Befehlsrichtlinie beim Wiederverbinden. +- Legacy-Signaturen vom Typ `v2` bleiben aus Kompatibilitätsgründen akzeptiert, aber das Anheften von Metadaten gekoppelter Geräte steuert weiterhin die Befehlsrichtlinie beim erneuten Verbinden. ## TLS + Pinning - TLS wird für WS-Verbindungen unterstützt. -- Clients können optional den Gateway-Zertifikat-Fingerprint pinnen (siehe `gateway.tls`- - Konfiguration plus `gateway.remote.tlsFingerprint` oder CLI `--tls-fingerprint`). +- Clients können optional den Fingerprint des Gateway-Zertifikats anheften (siehe Konfiguration `gateway.tls` + sowie `gateway.remote.tlsFingerprint` oder CLI `--tls-fingerprint`). -## Scope +## Umfang -Dieses Protokoll stellt die **vollständige Gateway-API** bereit (status, channels, models, chat, -agent, sessions, nodes, approvals usw.). Die genaue Oberfläche ist durch die +Dieses Protokoll stellt die **vollständige Gateway-API** bereit (Status, Channels, Modelle, Chat, +Agent, Sitzungen, Nodes, Genehmigungen usw.). Die genaue Oberfläche wird durch die TypeBox-Schemas in `src/gateway/protocol/schema.ts` definiert. diff --git a/docs/de/refactor/async-exec-duplicate-completion-investigation.md b/docs/de/refactor/async-exec-duplicate-completion-investigation.md new file mode 100644 index 000000000..8835bfe60 --- /dev/null +++ b/docs/de/refactor/async-exec-duplicate-completion-investigation.md @@ -0,0 +1,132 @@ +--- +x-i18n: + generated_at: "2026-04-16T06:22:19Z" + model: gpt-5.4 + provider: openai + source_hash: 95e56c5411204363676f002059c942201503e2359515d1a4b409882cc2e04920 + source_path: refactor/async-exec-duplicate-completion-investigation.md + workflow: 15 +--- + +# Untersuchung zu doppeltem Abschluss bei Async Exec + +## Umfang + +- Sitzung: `agent:main:telegram:group:-1003774691294:topic:1` +- Symptom: Derselbe Async-Exec-Abschluss für Sitzung/Run `keen-nexus` wurde in LCM zweimal als Nutzer-Turn erfasst. +- Ziel: Ermitteln, ob dies höchstwahrscheinlich eine doppelte Sitzungsinjektion oder ein einfacher Retry bei der ausgehenden Zustellung ist. + +## Schlussfolgerung + +Am wahrscheinlichsten ist dies eine **doppelte Sitzungsinjektion**, nicht ein reiner Retry bei der ausgehenden Zustellung. + +Die stärkste Lücke auf Gateway-Seite liegt im **Pfad für Node-Exec-Abschlüsse**: + +1. Ein Exec-Abschluss auf Node-Seite sendet `exec.finished` mit der vollständigen `runId`. +2. Gateway `server-node-events` wandelt das in ein System-Event um und fordert einen Heartbeat an. +3. Der Heartbeat-Run injiziert den geleerten System-Event-Block in den Agent-Prompt. +4. Der eingebettete Runner persistiert diesen Prompt als neuen Nutzer-Turn im Sitzungs-Transkript. + +Wenn dasselbe `exec.finished` aus irgendeinem Grund (Replay, erneute Verbindung mit Duplikat, erneutes Senden Upstream, duplizierter Producer) zweimal mit derselben `runId` beim Gateway ankommt, hat OpenClaw auf diesem Pfad derzeit **keine Idempotenzprüfung, die nach `runId`/`contextKey` schlüsselt**. Die zweite Kopie wird zu einer zweiten Nutzernachricht mit identischem Inhalt. + +## Exakter Code-Pfad + +### 1. Producer: Event für Node-Exec-Abschluss + +- `src/node-host/invoke.ts:340-360` + - `sendExecFinishedEvent(...)` sendet `node.event` mit dem Event `exec.finished`. + - Die Payload enthält `sessionKey` und die vollständige `runId`. + +### 2. Gateway-Event-Ingestion + +- `src/gateway/server-node-events.ts:574-640` + - Verarbeitet `exec.finished`. + - Baut Text auf: + - `Exec finished (node=..., id=, code ...)` + - Stellt ihn in die Queue über: + - `enqueueSystemEvent(text, { sessionKey, contextKey: runId ? \`exec:${runId}\` : "exec", trusted: false })` + - Fordert sofort ein Wake an: + - `requestHeartbeatNow(scopedHeartbeatWakeOptions(sessionKey, { reason: "exec-event" }))` + +### 3. Schwäche bei der Deduplizierung von System-Events + +- `src/infra/system-events.ts:90-115` + - `enqueueSystemEvent(...)` unterdrückt nur **aufeinanderfolgende doppelte Texte**: + - `if (entry.lastText === cleaned) return false` + - Es speichert `contextKey`, verwendet `contextKey` jedoch **nicht** für Idempotenz. + - Nach dem Drain wird die Duplikatunterdrückung zurückgesetzt. + +Das bedeutet: Ein erneut gesendetes `exec.finished` mit derselben `runId` kann später erneut akzeptiert werden, obwohl der Code bereits einen stabilen Kandidaten für Idempotenz hatte (`exec:`). + +### 4. Wake-Handling ist nicht der primäre Verursacher von Duplikaten + +- `src/infra/heartbeat-wake.ts:79-117` + - Wakes werden nach `(agentId, sessionKey)` zusammengefasst. + - Doppelte Wake-Anfragen für dasselbe Ziel werden zu einem ausstehenden Wake-Eintrag zusammengeführt. + +Dadurch ist **doppeltes Wake-Handling allein** eine schwächere Erklärung als doppelte Event-Ingestion. + +### 5. Heartbeat verarbeitet das Event und macht daraus Prompt-Eingabe + +- `src/infra/heartbeat-runner.ts:535-574` + - Preflight prüft ausstehende System-Events vorab und klassifiziert Runs mit Exec-Events. +- `src/auto-reply/reply/session-system-events.ts:86-90` + - `drainFormattedSystemEvents(...)` leert die Queue für die Sitzung. +- `src/auto-reply/reply/get-reply-run.ts:400-427` + - Der geleerte System-Event-Block wird dem Agent-Prompt-Body vorangestellt. + +### 6. Injektionspunkt ins Transkript + +- `src/agents/pi-embedded-runner/run/attempt.ts:2000-2017` + - `activeSession.prompt(effectivePrompt)` übergibt den vollständigen Prompt an die eingebettete PI-Sitzung. + - Das ist der Punkt, an dem der vom Abschluss abgeleitete Prompt als persistierter Nutzer-Turn gespeichert wird. + +Sobald also dasselbe System-Event zweimal in den Prompt eingebaut wird, sind doppelte LCM-Nutzernachrichten zu erwarten. + +## Warum ein einfacher Retry bei der ausgehenden Zustellung weniger wahrscheinlich ist + +Es gibt einen realen Fehlerpfad für ausgehende Zustellung im Heartbeat-Runner: + +- `src/infra/heartbeat-runner.ts:1194-1242` + - Die Antwort wird zuerst generiert. + - Die ausgehende Zustellung erfolgt später über `deliverOutboundPayloads(...)`. + - Ein Fehler dort liefert `{ status: "failed" }` zurück. + +Für denselben System-Event-Queue-Eintrag reicht das allein jedoch **nicht aus**, um die doppelten Nutzer-Turns zu erklären: + +- `src/auto-reply/reply/session-system-events.ts:86-90` + - Die System-Event-Queue ist bereits geleert, bevor die ausgehende Zustellung erfolgt. + +Ein Retry beim Kanalversand allein würde das exakt gleiche Queue-Event also nicht erneut erzeugen. Das könnte fehlende/fehlgeschlagene externe Zustellung erklären, aber für sich genommen nicht eine zweite identische Nutzer-Nachricht in der Sitzung. + +## Sekundäre Möglichkeit mit geringerer Sicherheit + +Es gibt eine Retry-Schleife für vollständige Runs im Agent-Runner: + +- `src/auto-reply/reply/agent-runner-execution.ts:741-1473` + - Bestimmte transiente Fehler können den gesamten Run erneut versuchen und denselben `commandBody` erneut absenden. + +Das kann einen persistierten Nutzer-Prompt **innerhalb derselben Antwortausführung** duplizieren, wenn der Prompt bereits angehängt wurde, bevor die Retry-Bedingung ausgelöst hat. + +Ich stufe das niedriger ein als doppelte `exec.finished`-Ingestion, weil: + +- der beobachtete Abstand bei etwa 51 Sekunden lag, was eher wie ein zweiter Wake/Turn aussieht als wie ein In-Process-Retry; +- im Bericht bereits wiederholte Fehler beim Nachrichtensenden erwähnt werden, was eher auf einen separaten späteren Turn hindeutet als auf einen unmittelbaren Modell-/Runtime-Retry. + +## Hypothese zur Grundursache + +Hypothese mit der höchsten Sicherheit: + +- Der Abschluss von `keen-nexus` kam über den **Node-Exec-Event-Pfad**. +- Dasselbe `exec.finished` wurde zweimal an `server-node-events` zugestellt. +- Das Gateway akzeptierte beide, weil `enqueueSystemEvent(...)` nicht nach `contextKey` / `runId` dedupliziert. +- Jedes akzeptierte Event löste einen Heartbeat aus und wurde als Nutzer-Turn in das PI-Transkript injiziert. + +## Vorgeschlagener kleiner, gezielter Fix + +Falls ein Fix gewünscht ist, ist die kleinste Änderung mit hohem Nutzen: + +- dafür zu sorgen, dass die Idempotenz von Exec-/System-Events `contextKey` für einen kurzen Zeitraum berücksichtigt, zumindest für exakte Wiederholungen von `(sessionKey, contextKey, text)`; +- oder eine dedizierte Deduplizierung in `server-node-events` für `exec.finished` hinzuzufügen, geschlüsselt nach `(sessionKey, runId, event kind)`. + +Das würde erneut gesendete `exec.finished`-Duplikate direkt blockieren, bevor sie zu Sitzungsturns werden.