Concept internals
TypeBox
TypeBox es una biblioteca de esquemas orientada a TypeScript. OpenClaw la utiliza para definir el protocolo WebSocket del Gateway (negociación inicial, solicitud/respuesta y eventos del servidor). Esos esquemas impulsan 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 general del protocolo, comienza por 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):
Client Gateway |---- req:connect -------->| |<---- res:hello-ok --------| |<---- event:tick ----------| |---- req:health ---------->| |<---- res: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 más acciones del nodo |
| Eventos | tick, presence, agent, chat, health, shutdown |
envío iniciado por el servidor |
El inventario autoritativo de descubrimiento anunciado se encuentra en src/gateway/server-methods-list.ts (listGatewayMethods, GATEWAY_EVENTS).
Dónde se encuentran los esquemas
- Módulo de exportación de origen:
packages/gateway-protocol/src/schema.tsreexporta los módulos de dominio incluidos enpackages/gateway-protocol/src/schema/*.ts(frames.tspara los envoltorios de nivel superior y la negociación inicial,agent.ts,sessions.ts,cron.ts, etc., según el área funcional).protocol-schemas.tses el registro centralProtocolSchemasque asigna los nombres de los esquemas a sus definiciones de TypeBox. - Validadores en tiempo de ejecución (AJV):
packages/gateway-protocol/src/index.ts - Registro anunciado de funcionalidades y descubrimiento:
src/gateway/server-methods-list.ts - Negociación inicial del servidor y despacho de métodos:
src/gateway/server.impl.ts - Cliente del nodo:
src/gateway/client.ts - JSON Schema generado:
dist/protocol.schema.json(salida de compilación, no se incluye en el repositorio) - Modelos Swift generados:
apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift
Canalización actual
pnpm protocol:genescribe el 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 Swift esté incluida en el repositorio (la salida de JSON Schema es un artefacto de compilación ignorado por Git).
Cómo se utilizan los esquemas en tiempo de ejecución
- Servidor: cada trama entrante se valida con AJV. La negociación inicial solo acepta una solicitud
connectcuyos parámetros coincidan conConnectParams. - Cliente: el cliente de JS valida las tramas de eventos y respuestas antes de utilizarlas.
- Descubrimiento de funcionalidades: el Gateway envía una lista conservadora de
features.methodsyfeatures.eventsenhello-ok, obtenida delistGatewayMethods()yGATEWAY_EVENTS. - Esa lista de descubrimiento no es un volcado generado de todos los auxiliares invocables de
coreGatewayHandlers; algunos RPC auxiliares están implementados ensrc/gateway/server-methods/*.tssin estar enumerados en la lista de funcionalidades anunciada.
Ejemplos de tramas
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 pequeño: conexión + estado de salud.
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ñádelo a packages/gateway-protocol/src/schema/system.ts (o al módulo funcional correspondiente más cercano):
export const SystemEchoParamsSchema = Type.Object( { text: NonEmptyString }, { additionalProperties: false },); export const SystemEchoResultSchema = Type.Object( { ok: Type.Boolean(), text: NonEmptyString }, { additionalProperties: false },);Importa ambos en packages/gateway-protocol/src/schema/protocol-schemas.ts, añádelos al registro ProtocolSchemas y exporta 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, exporta un validador AJV:
export const validateSystemEchoParams = ajv.compile<SystemEchoParams>(SystemEchoParamsSchema);- Comportamiento del servidor
Añade 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ístralo en src/gateway/server-methods.ts (que ya combina systemHandlers) y después añade "system.echo" a la entrada de listGatewayMethods en src/gateway/server-methods-list.ts.
Si los clientes de operador o de nodo pueden invocar el método, clasifícalo también en src/gateway/method-scopes.ts para que la aplicación de ámbitos y el anuncio de funcionalidades de hello-ok permanezcan alineados.
- Regenerar
pnpm protocol:check- Pruebas y documentación
Añade una prueba del servidor en src/gateway/server.*.test.ts y menciona 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 cargas útiles con tipos estrictos
- valores de
ErrorCode,GATEWAY_PROTOCOL_VERSIONyGATEWAY_MIN_PROTOCOL_VERSION
Los tipos de trama desconocidos se conservan como cargas útiles sin procesar para mantener 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 incluyan 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 imponer 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 los parámetros (por 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 transferencia al completarse una tarea de subagente o Cron); considera esto una superficie de API interna.
JSON del esquema publicado
El JSON Schema generado es un artefacto de compilación y no se incluye en el repositorio. El archivo sin procesar publicado suele estar disponible en:
Cuando cambies los esquemas
- Actualiza los esquemas de TypeBox en el módulo propietario
packages/gateway-protocol/src/schema/*.tsy regístralos enprotocol-schemas.ts. - Registra el método o evento en
src/gateway/server-methods-list.ts. - Actualiza
src/gateway/method-scopes.tscuando el nuevo RPC necesite una clasificación de ámbito de operador o nodo. - Ejecuta
pnpm protocol:check. - Incluye en el repositorio los modelos Swift regenerados.