TypeBox
Treat TypeBox schemas as the single source of truth (SSOT) for Gateway protocol.
Última actualización: 2026-01-10
TypeBox es una librería de esquemas TypeScript-first. La usamos para definir el **protocolo
WebSocket del Gateway** (handshake, petición/respuesta, eventos del servidor). Esos esquemas
impulsan validación en runtime, exportación de esquema JSON, y codegen Swift para la
aplicación macOS. Una única fuente de verdad; todo lo demás se genera.
Si quieres un contexto de protocolo de más alto nivel, empieza desde
Modelo Mental (30 segundos)
Cada mensaje WS del gateway es uno de tres frames:
- ''Petición'': ''{ type: "req", id, method, params }''
- ''Respuesta'': ''{ type: "res", id, ok, payload | error }''
- ''Evento'': ''{ type: "event", event, payload, seq?, stateVersion? }''
El primer frame ''debe'' ser una petición ''connect''. Después, los clientes pueden llamar
métodos (ej. ''health'', ''send'', ''chat.send'') y suscribirse a eventos (ej.
''presence'', ''tick'', ''agent'').
Flujo de conexión (mínimo):
Client Gateway |- res:hello-ok --| |<-| |->| |<-|
Métodos + eventos comunes:
| Categoría | Ejemplos | Notas |
-
| Core | ''connect'', ''health'', ''status'' | ''connect'' debe ser primero |
| Mensajes | ''send'', ''poll'', ''agent'', ''agent.wait'' | Efectos secundarios requieren ''idempotencyKey'' |
| Chat | ''chat.history'', ''chat.send'', ''chat.abort'', ''chat.inject'' | WebChat usa estos |
| Sesiones | ''sessions.list'', ''sessions.patch'', ''sessions.delete'' | Gestión de sesiones |
| Nodos | ''node.list'', ''node.invoke'', ''node.pair.*'' | Gateway WS + operaciones de nodo |
| Eventos | ''tick'', ''presence'', ''agent'', ''chat'', ''health'', ''shutdown'' | Push del servidor |
La lista autoritativa vive en ''src/gateway/server.ts'' (''METHODS'', ''EVENTS'').
Donde Viven los Esquemas
- Fuente: ''src/gateway/protocol/schema.ts''
- Validador runtime (AJV): ''src/gateway/protocol/index.ts''
- Handshake del servidor + despacho de métodos: ''src/gateway/server.ts''
- Cliente Node: ''src/gateway/client.ts''
- Esquema JSON generado: ''dist/protocol.schema.json''
- Modelos Swift generados: ''apps/macos/Sources/OpenClawProtocol/GatewayModels.swift''
Pipeline Actual
- ''pnpm protocol:gen''
- Escribe esquema JSON (draft 07) a ''dist/protocol.schema.json''
- ''pnpm protocol:gen:swift''
- Genera modelos Swift del gateway
- ''pnpm protocol:check''
- Ejecuta ambos generadores y verifica que la salida esté commiteada
Cómo el Runtime Usa los Esquemas
- Lado servidor: Cada frame entrante se valida via AJV. Solo el handshake
acepta una petición ''connect'' con params que coinciden con ''ConnectParams''.
- Cliente: El cliente JS valida frames de evento y respuesta antes de
usarlos.
- ''Superficie de métodos'': El gateway anuncia ''methods'' y
''events'' soportados en ''hello-ok''.
Framework de Ejemplo
Conexión (primer mensaje):
{
"type": "req",
"id": "c1",
"method": "connect",
"params": {
"minProtocol": 2,
"maxProtocol": 2,
"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": 2,
"server": { "version": "dev", "connId": "ws-1" },
"features": { "methods": ["health"], "events": ["tick"] },
"snapshot": {
"presence": [],
"health": {},
"stateVersion": { "presence": 0, "health": 0 },
"uptimeMs": 0
},
"policy": { "maxPayload": 1048576, "maxBufferedBytes": 1048576, "tickIntervalMs": 30000 }
}
}Petición + 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 mínimo útil: connect + health.
import { WebSocket } from "ws";
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: 3,
maxProtocol: 3,
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: Agregar un Método End-to-End
Ejemplo: Agregar una nueva petición ''system.echo'' que retorna ''{ ok: true, text }''.
1. Esquema (fuente de verdad)
Agregar a ''src/gateway/protocol/schema.ts'':
export const SystemEchoParamsSchema = Type.Object(
{ text: NonEmptyString },
{ additionalProperties: false },
);
export const SystemEchoResultSchema = Type.Object(
{ ok: Type.Boolean(), text: NonEmptyString },
{ additionalProperties: false },
);Agregar ambos a ''ProtocolSchemas'' y exportar tipos:
SystemEchoParams: SystemEchoParamsSchema, SystemEchoResult: SystemEchoResultSchema,
export type SystemEchoParams = Static<typeof SystemEchoParamsSchema>; export type SystemEchoResult = Static<typeof SystemEchoResultSchema>;
2. Validación
En ''src/gateway/protocol/index.ts'', exportar validadores AJV:
export const validateSystemEchoParams = ajv.compile<SystemEchoParams>(SystemEchoParamsSchema);
3. Comportamiento del servidor
Agregar un handler 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 });
},
};Registrarlo en ''src/gateway/server-methods.ts'' (''systemHandlers'' merged),
luego agregar ''"system.echo"'' a ''METHODS'' en ''src/gateway/server.ts''.
4. Regeneración
pnpm protocol:check
5. Test + Docs
Agregar tests del servidor en ''src/gateway/server.*.test.ts'' y notar el método en docs.
Swift Code Generation Behavior
Swift generator emits:
- ''GatewayFrame'' enum with ''req'', ''res'', ''event'', and ''unknown'' cases
- Strongly-typed payload structs/enums
- ''ErrorCode'' values and ''GATEWAY_PROTOCOL_VERSION''
Unknown frame types are preserved as raw payloads for forward compatibility.
Versioning + Compatibility
- ''PROTOCOL_VERSION'' lives in ''src/gateway/protocol/schema.ts''.
- Client sends ''minProtocol'' + ''maxProtocol''; server rejects mismatches.
- Swift models preserve unknown frame types to avoid breaking older clients.
Schema Patterns and Conventions
- Most objects use ''additionalProperties: false'' for strict payloads.
- ''NonEmptyString'' is the default for IDs and method/event names.
- Top-level ''GatewayFrame'' uses a ''discriminator'' on ''type''.
- Methods with side effects typically require ''idempotencyKey'' in params
(e.g.: ''send'', ''poll'', ''agent'', ''chat.send'').
Live Schema JSON
El esquema JSON generado está en el repositorio en ''dist/protocol.schema.json''. El
published raw file is typically available at:
- https://raw.githubusercontent.com/openclaw/openclaw/main/dist/protocol.schema.json
When You Change a Schema
1. Update TypeBox schema.
2. Run ''pnpm protocol:check''.
3. Commit regenerated schema + Swift models.