プロトコルの信頼できる情報源としてのTypeBox

最終更新:2026-01-10

TypeBoxはTypeScriptファーストのスキーマライブラリです。これを使用してGateway WebSocketプロトコル(ハンドシェイク、リクエスト/レスポンス、サーバーイベント)を定義します。これらのスキーマはランタイム検証JSON Schemaエクスポート、およびmacOSアプリ用のSwiftコード生成を駆動します。唯一の信頼できる情報源。他のすべては生成されます。

より高レベルのプロトコルコンテキストが必要な場合は、Gatewayアーキテクチャから始めてください。

メンタルモデル(30秒)

すべてのGateway WSメッセージは3つのフレームのいずれかです:

  • Request: { type: "req", id, method, params }
  • Response: { type: "res", id, ok, payload | error }
  • Event: { type: "event", event, payload, seq?, stateVersion? }

最初のフレームはconnectリクエストでなければなりません。その後、クライアントはメソッド(例:healthsendchat.send)を呼び出し、イベント(例:presencetickagent)にサブスクライブできます。

接続フロー(最小限):

Client                    Gateway
  |---- req:connect -------->|
  |<---- res:hello-ok --------|
  |<---- event:tick ----------|
  |---- req:health ---------->|
  |<---- res:health ----------|

一般的なメソッド + イベント:

カテゴリ注意
Coreconnect, health, statusconnectが最初でなければならない
Messagingsend, poll, agent, agent.wait副作用にはidempotencyKeyが必要
Chatchat.history, chat.send, chat.abort, chat.injectWebChatがこれらを使用
Sessionssessions.list, sessions.patch, sessions.deleteセッション管理
Nodesnode.list, node.invoke, node.pair.*Gateway WS + ノードアクション
Eventstick, presence, agent, chat, health, shutdownサーバープッシュ

権威あるリストはsrc/gateway/server.ts (METHODS, EVENTS)にあります。

スキーマの場所

  • ソース: src/gateway/protocol/schema.ts
  • ランタイムバリデーター(AJV): src/gateway/protocol/index.ts
  • サーバーハンドシェイク + メソッドディスパッチ: src/gateway/server.ts
  • ノードクライアント: src/gateway/client.ts
  • 生成されたJSON Schema: dist/protocol.schema.json
  • 生成されたSwiftモデル: apps/macos/Sources/OpenClawProtocol/GatewayModels.swift

現在のパイプライン

  • pnpm protocol:gen
    • JSON Schema(draft‑07)をdist/protocol.schema.jsonに書き込み
  • pnpm protocol:gen:swift
    • Swiftゲートウェイモデルを生成
  • pnpm protocol:check
    • 両方のジェネレーターを実行し、出力がコミットされていることを確認

ランタイムでのスキーマの使用方法

  • サーバー側: すべての受信フレームはAJVで検証されます。ハンドシェイクは、パラメータがConnectParamsと一致するconnectリクエストのみを受け入れます。
  • クライアント側: JSクライアントはイベントとレスポンスフレームを使用する前に検証します。
  • メソッドサーフェス: Gatewayはhello-okでサポートされているmethodseventsをアドバタイズします。

フレームの例

接続(最初のメッセージ):

{
  "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"
    }
  }
}

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 }
  }
}

リクエスト + レスポンス:

{ "type": "req", "id": "r1", "method": "health" }
{ "type": "res", "id": "r1", "ok": true, "payload": { "ok": true } }

イベント:

{ "type": "event", "event": "tick", "payload": { "ts": 1730000000 }, "seq": 12 }

最小クライアント(Node.js)

最小限の有用なフロー:接続 + ヘルス。

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();
  }
});

実例:エンドツーエンドでメソッドを追加

例:{ ok: true, text }を返す新しいsystem.echoリクエストを追加。

  1. スキーマ(信頼できる情報源)

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 },
);

両方をProtocolSchemasに追加し、型をエクスポート:

  SystemEchoParams: SystemEchoParamsSchema,
  SystemEchoResult: SystemEchoResultSchema,
export type SystemEchoParams = Static<typeof SystemEchoParamsSchema>;
export type SystemEchoResult = Static<typeof SystemEchoResultSchema>;
  1. 検証

src/gateway/protocol/index.tsでAJVバリデーターをエクスポート:

export const validateSystemEchoParams =
  ajv.compile<SystemEchoParams>(SystemEchoParamsSchema);
  1. サーバー動作

src/gateway/server-methods/system.tsにハンドラーを追加:

export const systemHandlers: GatewayRequestHandlers = {
  "system.echo": ({ params, respond }) => {
    const text = String(params.text ?? "");
    respond(true, { ok: true, text });
  },
};

src/gateway/server-methods.tsに登録(すでにsystemHandlersをマージ)し、src/gateway/server.tsMETHODS"system.echo"を追加します。

  1. 再生成
pnpm protocol:check
  1. テスト + ドキュメント

src/gateway/server.*.test.tsにサーバーテストを追加し、ドキュメントにメソッドを記載します。

Swiftコード生成動作

Swiftジェネレーターは次を出力します:

  • reqreseventunknownケースを持つGatewayFrame列挙型
  • 強く型付けされたペイロード構造体/列挙型
  • ErrorCode値とGATEWAY_PROTOCOL_VERSION

未知のフレームタイプは、前方互換性のために生のペイロードとして保持されます。

バージョニング + 互換性

  • PROTOCOL_VERSIONsrc/gateway/protocol/schema.tsにあります。
  • クライアントはminProtocol + maxProtocolを送信します。サーバーは不一致を拒否します。
  • Swiftモデルは、古いクライアントの破損を避けるために未知のフレームタイプを保持します。

スキーマパターンと慣習

  • ほとんどのオブジェクトは、厳密なペイロードのためにadditionalProperties: falseを使用します。
  • NonEmptyStringはIDおよびメソッド/イベント名のデフォルトです。
  • トップレベルのGatewayFrametypediscriminatorを使用します。
  • 副作用を持つメソッドは通常、パラメータにidempotencyKeyが必要です(例:sendpollagentchat.send)。

ライブスキーマJSON

生成されたJSON Schemaはdist/protocol.schema.jsonのリポジトリにあります。公開されている生ファイルは通常、次の場所で利用できます:

スキーマを変更する場合

  1. TypeBoxスキーマを更新します。
  2. pnpm protocol:checkを実行します。
  3. 再生成されたスキーマ + Swiftモデルをコミットします。