Concept internals
TypeBox
TypeBox est une bibliothèque de schémas conçue en priorité pour TypeScript. OpenClaw l’utilise pour définir le protocole WebSocket du Gateway (négociation initiale, requêtes/réponses, événements du serveur). Ces schémas pilotent la validation à l’exécution (AJV), l’export JSON Schema et la génération de code Swift pour l’application macOS. Une seule source de vérité ; tout le reste est généré.
Pour découvrir le contexte général du protocole, commencez par Architecture du Gateway.
Modèle mental (30 secondes)
Chaque message WS du Gateway correspond à l’une de ces trois trames :
- Requête :
{ type: "req", id, method, params } - Réponse :
{ type: "res", id, ok, payload | error } - Événement :
{ type: "event", event, payload, seq?, stateVersion? }
La première trame doit être une requête connect. Ensuite, les clients appellent des méthodes (par exemple health, send, chat.send) et s’abonnent à des événements (par exemple presence, tick, agent).
Flux de connexion (minimal) :
Client Gateway |---- req:connect -------->| |<---- res:hello-ok --------| |<---- event:tick ----------| |---- req:health ---------->| |<---- res:health ----------|Méthodes et événements courants :
| Catégorie | Exemples | Remarques |
|---|---|---|
| Noyau | connect, health, status |
connect doit être appelé en premier |
| Messagerie | send, agent, agent.wait, system-event, logs.tail |
les méthodes à effets de bord exigent idempotencyKey |
| Discussion | chat.history, chat.send, chat.abort |
WebChat les utilise |
| Sessions | sessions.list, sessions.patch, sessions.delete |
administration des sessions |
| Automatisation | wake, cron.list, cron.run, cron.runs |
contrôle du réveil et de Cron |
| Nodes | node.list, node.invoke, node.pair.* |
WS du Gateway et actions des Nodes |
| Événements | tick, presence, agent, chat, health, shutdown |
envoi spontané par le serveur |
L’inventaire de découverte de référence annoncé se trouve dans src/gateway/server-methods-list.ts (listGatewayMethods, GATEWAY_EVENTS).
Emplacement des schémas
- Point d’exportation source :
packages/gateway-protocol/src/schema.tsréexporte les modules de domaine souspackages/gateway-protocol/src/schema/*.ts(frames.tspour les enveloppes de premier niveau et la négociation initiale,agent.ts,sessions.ts,cron.ts, etc. selon le domaine fonctionnel).protocol-schemas.tsest le registre centralProtocolSchemasqui associe les noms de schémas à leurs définitions TypeBox. - Validateurs à l’exécution (AJV) :
packages/gateway-protocol/src/index.ts - Registre des fonctionnalités et de découverte annoncées :
src/gateway/server-methods-list.ts - Négociation initiale du serveur et répartition des méthodes :
src/gateway/server.impl.ts - Client Node :
src/gateway/client.ts - JSON Schema généré :
dist/protocol.schema.json(sortie de compilation, non suivie dans le dépôt) - Modèles Swift générés :
apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift
Pipeline actuel
pnpm protocol:genécrit le JSON Schema (draft-07) dansdist/protocol.schema.json.pnpm protocol:gen:swiftgénère les modèles Swift du Gateway.pnpm protocol:checkexécute les deux générateurs et vérifie que la sortie Swift est enregistrée dans le dépôt (la sortie JSON Schema est un artefact de compilation ignoré par Git).
Utilisation des schémas à l’exécution
- Côté serveur : chaque trame entrante est validée avec AJV. La négociation initiale n’accepte qu’une requête
connectdont les paramètres correspondent àConnectParams. - Côté client : le client JS valide les trames d’événement et de réponse avant de les utiliser.
- Découverte des fonctionnalités : le Gateway envoie dans
hello-okune liste prudentefeatures.methodsetfeatures.events, issue delistGatewayMethods()et deGATEWAY_EVENTS. - Cette liste de découverte n’est pas un inventaire généré de tous les assistants appelables dans
coreGatewayHandlers; certains RPC auxiliaires sont implémentés danssrc/gateway/server-methods/*.tssans figurer dans la liste des fonctionnalités annoncées.
Exemples de trames
Connexion (premier message) :
{ "type": "req", "id": "c1", "method": "connect", "params": { "minProtocol": 3, "maxProtocol": 4, "client": { "id": "openclaw-macos", "displayName": "macos", "version": "1.0.0", "platform": "macos 15.1", "mode": "ui", "instanceId": "A1B2" } }}Réponse Hello-ok :
{ "type": "res", "id": "c1", "ok": true, "payload": { "type": "hello-ok", "protocol": 4, "server": { "version": "dev", "connId": "ws-1" }, "features": { "methods": ["health"], "events": ["tick"] }, "snapshot": { "presence": [], "health": {}, "stateVersion": { "presence": 0, "health": 0 }, "uptimeMs": 0 }, "auth": { "role": "operator", "scopes": ["operator.read"] }, "policy": { "maxPayload": 1048576, "maxBufferedBytes": 1048576, "tickIntervalMs": 30000 } }}Requête et réponse :
{ "type": "req", "id": "r1", "method": "health" }{ "type": "res", "id": "r1", "ok": true, "payload": { "ok": true } }Événement :
{ "type": "event", "event": "tick", "payload": { "ts": 1730000000 }, "seq": 12 }Client minimal (Node.js)
Flux utile le plus simple : connexion + état de santé.
const ws = new WebSocket("ws://127.0.0.1:18789"); ws.on("open", () => { ws.send( JSON.stringify({ type: "req", id: "c1", method: "connect", params: { minProtocol: 4, maxProtocol: 4, client: { id: "cli", displayName: "example", version: "dev", platform: "node", mode: "cli", }, }, }), );}); ws.on("message", (data) => { const msg = JSON.parse(String(data)); if (msg.type === "res" && msg.id === "c1" && msg.ok) { ws.send(JSON.stringify({ type: "req", id: "h1", method: "health" })); } if (msg.type === "res" && msg.id === "h1") { console.log("health:", msg.payload); ws.close(); }});Exemple détaillé : ajouter une méthode de bout en bout
Exemple : ajouter une nouvelle requête system.echo qui renvoie { ok: true, text }.
- Schéma (source de vérité)
Ajoutez ceci à packages/gateway-protocol/src/schema/system.ts (ou au module fonctionnel correspondant le mieux) :
export const SystemEchoParamsSchema = Type.Object( { text: NonEmptyString }, { additionalProperties: false },); export const SystemEchoResultSchema = Type.Object( { ok: Type.Boolean(), text: NonEmptyString }, { additionalProperties: false },);Importez les deux dans packages/gateway-protocol/src/schema/protocol-schemas.ts, ajoutez-les au registre ProtocolSchemas et exportez les types dérivés :
SystemEchoParams: SystemEchoParamsSchema, SystemEchoResult: SystemEchoResultSchema,export type SystemEchoParams = Static<typeof SystemEchoParamsSchema>;export type SystemEchoResult = Static<typeof SystemEchoResultSchema>;- Validation
Dans packages/gateway-protocol/src/index.ts, exportez un validateur AJV :
export const validateSystemEchoParams = ajv.compile<SystemEchoParams>(SystemEchoParamsSchema);- Comportement du serveur
Ajoutez un gestionnaire dans src/gateway/server-methods/system.ts :
export const systemHandlers: GatewayRequestHandlers = { "system.echo": ({ params, respond }) => { const text = String(params.text ?? ""); respond(true, { ok: true, text }); },};Enregistrez-le dans src/gateway/server-methods.ts (qui fusionne déjà systemHandlers), puis ajoutez "system.echo" à l’entrée de listGatewayMethods dans src/gateway/server-methods-list.ts.
Si la méthode peut être appelée par des clients opérateur ou Node, classez-la également dans src/gateway/method-scopes.ts afin que l’application des portées et l’annonce des fonctionnalités dans hello-ok restent cohérentes.
- Régénération
pnpm protocol:check- Tests et documentation
Ajoutez un test du serveur dans src/gateway/server.*.test.ts et mentionnez la méthode dans la documentation.
Comportement de la génération de code Swift
Le générateur Swift produit :
- une énumération
GatewayFrameavec les casreq,res,eventetunknown - des structures et énumérations de charges utiles fortement typées
- les valeurs
ErrorCode,GATEWAY_PROTOCOL_VERSIONetGATEWAY_MIN_PROTOCOL_VERSION
Les types de trames inconnus sont conservés sous forme de charges utiles brutes pour assurer la compatibilité ascendante.
Gestion des versions et compatibilité
PROTOCOL_VERSIONse trouve danspackages/gateway-protocol/src/version.ts(valeur actuelle :4).- Les clients envoient
minProtocoletmaxProtocol; le serveur rejette les plages qui n’incluent pas son protocole actuel. - Les modèles Swift conservent les types de trames inconnus afin de ne pas rendre les anciens clients incompatibles.
Modèles et conventions des schémas
- La plupart des objets utilisent
additionalProperties: falsepour imposer des charges utiles strictes. NonEmptyString(Type.String({ minLength: 1 })) est utilisé par défaut pour les identifiants ainsi que les noms de méthodes et d’événements.- Le
GatewayFramede premier niveau utilise un discriminateur surtype. - Les méthodes ayant des effets de bord exigent généralement un
idempotencyKeydans leurs paramètres (exemple :send,poll,agent,chat.send). agentaccepte un paramètre facultatifinternalEventspour le contexte d’orchestration généré à l’exécution (par exemple, la transmission de l’achèvement d’une tâche d’un sous-agent ou de Cron) ; considérez-le comme une surface d’API interne.
JSON du schéma actif
Le JSON Schema généré est un artefact de compilation qui n’est pas enregistré dans le dépôt. Le fichier brut publié est généralement disponible à l’adresse suivante :
Lorsque vous modifiez les schémas
- Mettez à jour les schémas TypeBox dans le module propriétaire
packages/gateway-protocol/src/schema/*.tset enregistrez-les dansprotocol-schemas.ts. - Enregistrez la méthode ou l’événement dans
src/gateway/server-methods-list.ts. - Mettez à jour
src/gateway/method-scopes.tslorsque le nouveau RPC nécessite une classification de portée pour l’opérateur ou le Node. - Exécutez
pnpm protocol:check. - Enregistrez les modèles Swift régénérés dans le dépôt.