Concept internals
TypeBox
TypeBox es una biblioteca de esquemas diseñada principalmente para TypeScript. OpenClaw la utiliza para definir el protocolo WebSocket del Gateway (negociación inicial, solicitud/respuesta y eventos del servidor). Esos esquemas controlan la validación en tiempo de ejecución (AJV), la exportación de JSON Schema y la generación de código Swift para la aplicación de macOS. Una única fuente de verdad; todo lo demás se genera.
Para conocer el contexto de más alto nivel del protocolo, comience por la arquitectura del Gateway.
Modelo mental (30 segundos)
Cada mensaje WS del Gateway es uno de estos tres tipos de trama:
- Solicitud:
{ type: "req", id, method, params } - Respuesta:
{ type: "res", id, ok, payload | error } - Evento:
{ type: "event", event, payload, seq?, stateVersion? }
La primera trama debe ser una solicitud connect. Después, los clientes llaman a métodos (por ejemplo, health, send, chat.send) y se suscriben a eventos (por ejemplo, presence, tick, agent).
Flujo de conexión (mínimo):
Cliente Gateway |---- sol.:connect ------->| |<---- resp.:hello-ok ------| |<---- evento:tick ---------| |---- sol.:health --------->| |<---- resp.:health --------|Métodos y eventos habituales:
| Categoría | Ejemplos | Notas |
|---|---|---|
| Núcleo | connect, health, status |
connect debe ser el primero |
| Mensajería | send, agent, agent.wait, system-event, logs.tail |
los métodos con efectos secundarios necesitan idempotencyKey |
| Chat | chat.history, chat.send, chat.abort |
WebChat utiliza estos |
| Sesiones | sessions.list, sessions.patch, sessions.delete |
administración de sesiones |
| Automatización | wake, cron.list, cron.run, cron.runs |
control de activación y cron |
| Nodos | node.list, node.invoke, node.pair.* |
WS del Gateway y acciones de nodo |
| Eventos | tick, presence, agent, chat, health, shutdown |
envío desde el servidor |
El inventario autoritativo de detección anunciado se encuentra en src/gateway/server-methods-list.ts (listGatewayMethods, GATEWAY_EVENTS).
Ubicación de los esquemas
- Módulo de exportación de origen:
packages/gateway-protocol/src/schema.tsvuelve a exportar los módulos de dominio depackages/gateway-protocol/src/schema/*.ts(frames.tspara los envoltorios de nivel superior y la negociación inicial, yagent.ts,sessions.ts,cron.ts, etc., para cada área funcional).protocol-schemas.tses el registro centralProtocolSchemasque asigna los nombres de esquema a sus definiciones de TypeBox. - Validadores en tiempo de ejecución (AJV):
packages/gateway-protocol/src/index.ts - Registro anunciado de funciones y detección:
src/gateway/server-methods-list.ts - Negociación inicial del servidor y despacho de métodos:
src/gateway/server.impl.ts - Cliente de nodo:
src/gateway/client.ts - JSON Schema generado:
dist/protocol.schema.json(salida de compilación, no incluida en los commits) - Modelos Swift generados:
apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift
Pipeline actual
pnpm protocol:genescribe JSON Schema (borrador 07) endist/protocol.schema.json.pnpm protocol:gen:swiftgenera los modelos Swift del Gateway.pnpm protocol:checkejecuta ambos generadores y verifica que la salida de Swift esté incluida en los commits (la salida de JSON Schema es un artefacto de compilación ignorado por Git).
Uso de los esquemas en tiempo de ejecución
- En el servidor: cada trama entrante se valida con AJV. La negociación inicial solo acepta una solicitud
connectcuyos parámetros coincidan conConnectParams. - En el cliente: el cliente JS valida las tramas de eventos y respuestas antes de utilizarlas.
- Detección de funciones: el Gateway envía listas conservadoras
features.methodsyfeatures.eventsenhello-ok, provenientes delistGatewayMethods()yGATEWAY_EVENTS. - Esa lista de detección no es un volcado generado de todas las funciones auxiliares invocables de
coreGatewayHandlers; algunos RPC auxiliares están implementados ensrc/gateway/server-methods/*.tssin estar enumerados en la lista de funciones anunciadas.
Tramas de ejemplo
Conexión (primer mensaje):
{ "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" } }}Respuesta 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 } }}Solicitud y respuesta:
{ "type": "req", "id": "r1", "method": "health" }{ "type": "res", "id": "r1", "ok": true, "payload": { "ok": true } }Evento:
{ "type": "event", "event": "tick", "payload": { "ts": 1730000000 }, "seq": 12 }Cliente mínimo (Node.js)
Flujo útil más sencillo: conexión + estado.
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(); }});Ejemplo práctico: añadir un método de extremo a extremo
Ejemplo: añadir una nueva solicitud system.echo que devuelva { ok: true, text }.
- Esquema (fuente de verdad)
Añádalo a packages/gateway-protocol/src/schema/system.ts (o al módulo funcional que mejor corresponda):
export const SystemEchoParamsSchema = Type.Object( { text: NonEmptyString }, { additionalProperties: false },); export const SystemEchoResultSchema = Type.Object( { ok: Type.Boolean(), text: NonEmptyString }, { additionalProperties: false },);Importe ambos en packages/gateway-protocol/src/schema/protocol-schemas.ts, añádalos al registro ProtocolSchemas y exporte los tipos derivados:
SystemEchoParams: SystemEchoParamsSchema, SystemEchoResult: SystemEchoResultSchema,export type SystemEchoParams = Static<typeof SystemEchoParamsSchema>;export type SystemEchoResult = Static<typeof SystemEchoResultSchema>;- Validación
En packages/gateway-protocol/src/index.ts, exporte un validador AJV:
export const validateSystemEchoParams = ajv.compile<SystemEchoParams>(SystemEchoParamsSchema);- Comportamiento del servidor
Añada un controlador en src/gateway/server-methods/system.ts:
export const systemHandlers: GatewayRequestHandlers = { "system.echo": ({ params, respond }) => { const text = String(params.text ?? ""); respond(true, { ok: true, text }); },};Regístrelo en src/gateway/server-methods.ts (que ya combina systemHandlers) y después añada "system.echo" a la entrada listGatewayMethods de src/gateway/server-methods-list.ts.
Si el método puede ser invocado por clientes operadores o nodos, clasifíquelo también en src/gateway/method-scopes.ts para que la aplicación de ámbitos y el anuncio de funciones hello-ok permanezcan alineados.
- Regeneración
pnpm protocol:check- Pruebas y documentación
Añada una prueba del servidor en src/gateway/server.*.test.ts y mencione el método en la documentación.
Comportamiento de la generación de código Swift
El generador de Swift emite:
- una enumeración
GatewayFramecon los casosreq,res,eventyunknown - estructuras y enumeraciones de carga útil con tipado fuerte
- valores
ErrorCode,GATEWAY_PROTOCOL_VERSIONyGATEWAY_MIN_PROTOCOL_VERSION
Los tipos de trama desconocidos se conservan como cargas útiles sin procesar para garantizar la compatibilidad futura.
Control de versiones y compatibilidad
PROTOCOL_VERSIONse encuentra enpackages/gateway-protocol/src/version.ts(valor actual:4).- Los clientes envían
minProtocolymaxProtocol; el servidor rechaza los intervalos que no incluyen su protocolo actual. - Los modelos Swift conservan los tipos de trama desconocidos para evitar que los clientes antiguos dejen de funcionar.
Patrones y convenciones de los esquemas
- La mayoría de los objetos utilizan
additionalProperties: falsepara cargas útiles estrictas. NonEmptyString(Type.String({ minLength: 1 })) es el valor predeterminado para los identificadores y los nombres de métodos y eventos.- El
GatewayFramede nivel superior utiliza un discriminador entype. - Los métodos con efectos secundarios suelen requerir un
idempotencyKeyen sus parámetros (ejemplo:send,poll,agent,chat.send). agentacepta el parámetro opcionalinternalEventspara el contexto de orquestación generado en tiempo de ejecución (por ejemplo, la entrega al finalizar una tarea de subagente o cron); debe tratarse como una superficie de API interna.
JSON del esquema en directo
El JSON Schema generado es un artefacto de compilación y no se incluye en los commits del repositorio. El archivo sin procesar publicado suele estar disponible en:
Al modificar los esquemas
- Actualice los esquemas de TypeBox en el módulo
packages/gateway-protocol/src/schema/*.tspropietario y regístrelos enprotocol-schemas.ts. - Registre el método o evento en
src/gateway/server-methods-list.ts. - Actualice
src/gateway/method-scopes.tscuando el nuevo RPC necesite una clasificación de ámbito de operador o nodo. - Ejecute
pnpm protocol:check. - Incluya los modelos Swift regenerados en el commit.