chore(i18n): refresh de translations

This commit is contained in:
openclaw-docs-i18n[bot] 2026-04-16 06:24:00 +00:00
parent 9bb381e0f4
commit e1734433e0
3 changed files with 682 additions and 419 deletions

View File

@ -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 `<active_memory_plugin>...</active_memory_plugin>`-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/<agent>/sessions/active-memory/<blocking-memory-sub-agent-session-id>.jsonl
@ -523,10 +607,10 @@ agents/<agent>/sessions/active-memory/<blocking-memory-sub-agent-session-id>.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

View File

@ -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
`<tool_call>...</tool_call>`, `<function_call>...</function_call>`,
`<tool_calls>...</tool_calls>`, `<function_calls>...</function_calls>` 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.<skillKey>`-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.<skillKey>` 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.

View File

@ -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=<runId>, 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:<runId>`).
### 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.