---
read_when:
    - Gateway WS クライアントの実装または更新
    - プロトコル不一致または接続失敗のデバッグ
    - プロトコルのスキーマ/モデルを再生成しています
summary: 'Gateway WebSocket プロトコル: ハンドシェイク、フレーム、バージョニング'
title: Gateway プロトコル
x-i18n:
    generated_at: "2026-07-04T17:48:55Z"
    model: gpt-5.5
    postprocess_version: locale-links-v1
    provider: openai
    source_hash: 763dd5cba2f1aa0de95243a4996b4da1b4aa32c5c1a4b5b6c112d605e677bd70
    source_path: gateway/protocol.md
    workflow: 16
---

Gateway WSプロトコルは、OpenClaw の**単一のコントロールプレーン + ノードトランスポート**です。すべてのクライアント（CLI、Web UI、macOSアプリ、iOS/Androidノード、ヘッドレスノード）は WebSocket 経由で接続し、ハンドシェイク時に**ロール** + **スコープ**を宣言します。

## トランスポート

- WebSocket、JSONペイロードを持つテキストフレーム。
- 最初のフレームは**必ず** `connect` リクエストである必要があります。
- 接続前フレームは 64 KiB に制限されます。ハンドシェイクが成功した後、クライアントは `hello-ok.policy.maxPayload` と `hello-ok.policy.maxBufferedBytes` の制限に従う必要があります。診断が有効な場合、過大な受信フレームと遅い送信バッファは、Gateway が影響を受けるフレームを閉じるか破棄する前に `payload.large` イベントを発行します。これらのイベントはサイズ、制限、サーフェス、安全な理由コードを保持します。メッセージ本文、添付ファイルの内容、生フレーム本文、トークン、Cookie、シークレット値は保持しません。

## ハンドシェイク（connect）

Gateway → クライアント（接続前チャレンジ）:

```json
{
  "type": "event",
  "event": "connect.challenge",
  "payload": { "nonce": "…", "ts": 1737264000000 }
}
```

クライアント → Gateway:

```json
{
  "type": "req",
  "id": "…",
  "method": "connect",
  "params": {
    "minProtocol": 3,
    "maxProtocol": 4,
    "client": {
      "id": "cli",
      "version": "1.2.3",
      "platform": "macos",
      "mode": "operator"
    },
    "role": "operator",
    "scopes": ["operator.read", "operator.write"],
    "caps": [],
    "commands": [],
    "permissions": {},
    "auth": { "token": "…" },
    "locale": "en-US",
    "userAgent": "openclaw-cli/1.2.3",
    "device": {
      "id": "device_fingerprint",
      "publicKey": "…",
      "signature": "…",
      "signedAt": 1737264000000,
      "nonce": "…"
    }
  }
}
```

Gateway → クライアント:

```json
{
  "type": "res",
  "id": "…",
  "ok": true,
  "payload": {
    "type": "hello-ok",
    "protocol": 4,
    "server": { "version": "…", "connId": "…" },
    "features": { "methods": ["…"], "events": ["…"] },
    "snapshot": { "…": "…" },
    "auth": {
      "role": "operator",
      "scopes": ["operator.read", "operator.write"]
    },
    "policy": {
      "maxPayload": 26214400,
      "maxBufferedBytes": 52428800,
      "tickIntervalMs": 15000
    }
  }
}
```

Gateway がまだ起動時サイドカーの完了処理中の場合、`connect` リクエストは `details.reason` が `"startup-sidecars"` に設定され、`retryAfterMs` を含む、再試行可能な `UNAVAILABLE` エラーを返すことがあります。クライアントは、その応答を最終的なハンドシェイク失敗として表面化するのではなく、全体の接続予算内で再試行する必要があります。

`server`、`features`、`snapshot`、`policy` はすべてスキーマ（`packages/gateway-protocol/src/schema/frames.ts`）で必須です。`auth` も必須で、ネゴシエートされたロール/スコープを報告します。`pluginSurfaceUrls` は任意で、`canvas` などのPluginサーフェス名を、スコープ付きホストURLにマップします。

スコープ付きPluginサーフェスURLは期限切れになる場合があります。ノードは `node.pluginSurface.refresh` を `{ "surface": "canvas" }` とともに呼び出して、`pluginSurfaceUrls` の新しいエントリを受け取れます。実験的な Canvas Plugin リファクタリングは、非推奨の `canvasHostUrl`、`canvasCapability`、または `node.canvas.capability.refresh` 互換パスをサポートしません。現在のネイティブクライアントとGatewayはPluginサーフェスを使用する必要があります。

デバイストークンが発行されない場合、`hello-ok.auth` はトークンフィールドなしで、ネゴシエートされた権限を報告します。

```json
{
  "auth": {
    "role": "operator",
    "scopes": ["operator.read", "operator.write"]
  }
}
```

信頼された同一プロセスのバックエンドクライアント（`client.id: "gateway-client"`、`client.mode: "backend"`）は、共有Gatewayトークン/パスワードで認証する場合、直接ループバック接続で `device` を省略できます。このパスは内部コントロールプレーンRPC専用で、古いCLI/デバイスペアリングのベースラインが、サブエージェントセッション更新などのローカルバックエンド作業をブロックしないようにします。リモートクライアント、ブラウザーオリジンクライアント、ノードクライアント、明示的なデバイストークン/デバイスIDクライアントは、引き続き通常のペアリングとスコープアップグレードチェックを使用します。

デバイストークンが発行される場合、`hello-ok` には次も含まれます。

```json
{
  "auth": {
    "deviceToken": "…",
    "role": "operator",
    "scopes": ["operator.read", "operator.write"]
  }
}
```

組み込みのQR/セットアップコードのブートストラップは、新しいモバイル引き継ぎパスです。ベースラインのセットアップコード接続が成功すると、プライマリノードトークンに加えて、範囲が限定されたオペレータートークンが1つ返されます。

```json
{
  "auth": {
    "deviceToken": "…",
    "role": "node",
    "scopes": [],
    "deviceTokens": [
      {
        "deviceToken": "…",
        "role": "operator",
        "scopes": ["operator.approvals", "operator.read", "operator.talk.secrets", "operator.write"]
      }
    ]
  }
}
```

オペレーター引き継ぎは意図的に範囲が限定されており、QRオンボーディングでモバイルオペレーターループを開始し、ペアリング変更スコープや `operator.admin` を付与せずにネイティブセットアップを完了できます。ブートストラップ後にネイティブクライアントが必要な Talk 設定を読み取れるよう、`operator.talk.secrets` が含まれます。より広いペアリング権限と管理者アクセスには、別途承認済みのオペレーターペアリングまたはトークンフローが必要です。クライアントは、接続が `wss://` やループバック/local pairing などの信頼されたトランスポート上でブートストラップ認証を使用した場合にのみ、`hello-ok.auth.deviceTokens` を永続化する必要があります。

### ノードの例

```json
{
  "type": "req",
  "id": "…",
  "method": "connect",
  "params": {
    "minProtocol": 3,
    "maxProtocol": 4,
    "client": {
      "id": "ios-node",
      "version": "1.2.3",
      "platform": "ios",
      "mode": "node"
    },
    "role": "node",
    "scopes": [],
    "caps": ["camera", "canvas", "screen", "location", "voice"],
    "commands": ["camera.snap", "canvas.navigate", "screen.record", "location.get"],
    "permissions": { "camera.capture": true, "screen.record": false },
    "auth": { "token": "…" },
    "locale": "en-US",
    "userAgent": "openclaw-ios/1.2.3",
    "device": {
      "id": "device_fingerprint",
      "publicKey": "…",
      "signature": "…",
      "signedAt": 1737264000000,
      "nonce": "…"
    }
  }
}
```

## フレーミング

- **リクエスト**: `{type:"req", id, method, params}`
- **レスポンス**: `{type:"res", id, ok, payload|error}`
- **イベント**: `{type:"event", event, payload, seq?, stateVersion?}`

副作用のあるメソッドには**冪等性キー**が必要です（スキーマを参照）。

## ロール + スコープ

完全なオペレータースコープモデル、承認時チェック、共有シークレットのセマンティクスについては、[オペレータースコープ](/ja-JP/gateway/operator-scopes) を参照してください。

### ロール

- `operator` = コントロールプレーンクライアント（CLI/UI/自動化）。
- `node` = 機能ホスト（camera/screen/canvas/system.run）。

### スコープ（operator）

一般的なスコープ:

- `operator.read`
- `operator.write`
- `operator.admin`
- `operator.approvals`
- `operator.pairing`
- `operator.talk.secrets`

`includeSecrets: true` を指定した `talk.config` には `operator.talk.secrets`（または `operator.admin`）が必要です。
シークレットが含まれる場合、クライアントはアクティブな Talk プロバイダーの認証情報を `talk.resolved.config.apiKey` から読み取る必要があります。`talk.providers.<id>.apiKey` はソース形状のままで、SecretRef オブジェクトまたは秘匿化された文字列である場合があります。

Plugin登録のGateway RPCメソッドは独自のオペレータースコープを要求できますが、予約済みのコア管理プレフィックス（`config.*`、`exec.approvals.*`、`wizard.*`、`update.*`）は常に `operator.admin` に解決されます。

メソッドスコープは最初のゲートにすぎません。`chat.send` 経由で到達する一部のスラッシュコマンドには、さらに厳格なコマンドレベルのチェックが適用されます。たとえば、永続的な `/config set` と `/config unset` の書き込みには `operator.admin` が必要です。

`node.pair.approve` には、基本メソッドスコープに加えて、追加の承認時スコープチェックもあります。

- コマンドなしリクエスト: `operator.pairing`
- exec 以外のノードコマンドを含むリクエスト: `operator.pairing` + `operator.write`
- `system.run`、`system.run.prepare`、または `system.which` を含むリクエスト:
  `operator.pairing` + `operator.admin`

### 機能/コマンド/権限（node）

ノードは接続時に機能クレームを宣言します。

- `caps`: `camera`、`canvas`、`screen`、`location`、`voice`、`talk` などの高レベルな機能カテゴリ。
- `commands`: invoke 用のコマンド許可リスト。
- `permissions`: 粒度の細かいトグル（例: `screen.record`、`camera.capture`）。

Gateway はこれらを**クレーム**として扱い、サーバー側の許可リストを強制します。

## プレゼンス

- `system-presence` はデバイスIDをキーにしたエントリを返します。
- プレゼンスエントリには `deviceId`、`roles`、`scopes` が含まれるため、UIは、同じデバイスが**オペレーター**と**ノード**の両方として接続している場合でも、デバイスごとに1行で表示できます。
- `node.list` には任意の `lastSeenAtMs` と `lastSeenReason` フィールドが含まれます。接続中のノードは、現在の接続時刻を `lastSeenAtMs` として、理由 `connect` とともに報告します。ペアリング済みノードは、信頼されたノードイベントがペアリングメタデータを更新したときに、永続的なバックグラウンドプレゼンスも報告できます。

### ノードのバックグラウンド生存イベント

ノードは `event: "node.presence.alive"` を指定して `node.event` を呼び出し、ペアリング済みノードがバックグラウンドウェイク中に生存していたことを、接続済みとしてマークせずに記録できます。

```json
{
  "event": "node.presence.alive",
  "payloadJSON": "{\"trigger\":\"silent_push\",\"sentAtMs\":1737264000000,\"displayName\":\"Peter's iPhone\",\"version\":\"2026.4.28\",\"platform\":\"iOS 18.4.0\",\"deviceFamily\":\"iPhone\",\"modelIdentifier\":\"iPhone17,1\",\"pushTransport\":\"relay\"}"
}
```

`trigger` は閉じた列挙型です: `background`、`silent_push`、`bg_app_refresh`、`significant_location`、`manual`、または `connect`。未知のトリガー文字列は、永続化前にGatewayによって `background` に正規化されます。このイベントは、認証済みのノードデバイスセッションについてのみ永続化されます。デバイスなしまたは未ペアリングのセッションは `handled: false` を返します。

成功したGatewayは構造化された結果を返します。

```json
{
  "ok": true,
  "event": "node.presence.alive",
  "handled": true,
  "reason": "persisted"
}
```

古いGatewayは `node.event` に対してまだ `{ "ok": true }` を返す場合があります。クライアントはそれを、永続的なプレゼンスの永続化ではなく、確認済みRPCとして扱う必要があります。

## ブロードキャストイベントのスコープ制御

サーバーからプッシュされる WebSocket ブロードキャストイベントはスコープでゲートされるため、ペアリングスコープのセッションやノード専用セッションがセッション内容を受動的に受信することはありません。

- **チャット、エージェント、ツール結果フレーム**（ストリーミングされた `agent` イベントとツール呼び出し結果を含む）には、少なくとも `operator.read` が必要です。`operator.read` を持たないセッションは、これらのフレームを完全にスキップします。
- **Plugin定義の `plugin.*` ブロードキャスト**は、Plugin が登録した方法に応じて、`operator.write` または `operator.admin` にゲートされます。
- **ステータスとトランスポートイベント**（`heartbeat`、`presence`、`tick`、接続/切断ライフサイクルなど）は制限されないままなので、トランスポートの健全性はすべての認証済みセッションから観測可能です。
- **未知のブロードキャストイベントファミリー**は、登録済みハンドラーが明示的に緩和しない限り、デフォルトでスコープゲートされます（fail-closed）。

各クライアント接続は独自のクライアントごとのシーケンス番号を保持するため、異なるクライアントがイベントストリームのスコープでフィルタリングされた異なるサブセットを見る場合でも、ブロードキャストはそのソケット上で単調な順序を維持します。

## 一般的なRPCメソッドファミリー

公開WSサーフェスは、上記のハンドシェイク/認証の例よりも広範です。これは生成されたダンプではありません。`hello-ok.features.methods` は、`src/gateway/server-methods-list.ts` と読み込まれたPlugin/チャンネルメソッドのエクスポートから構築された保守的なディスカバリーリストです。`src/gateway/server-methods/*.ts` の完全な列挙ではなく、機能ディスカバリーとして扱ってください。

  <AccordionGroup>
  <Accordion title="システムとアイデンティティ">
    - `health` は、キャッシュ済みまたは新しくプローブされた Gateway ヘルススナップショットを返します。
    - `diagnostics.stability` は、最近の有界診断安定性レコーダーを返します。イベント名、カウント、バイトサイズ、メモリ読み取り値、キュー/セッション状態、チャンネル/Plugin 名、セッション ID などの運用メタデータを保持します。チャットテキスト、Webhook 本文、ツール出力、生のリクエストまたはレスポンス本文、トークン、Cookie、秘密値は保持しません。オペレーターの読み取りスコープが必要です。
    - `status` は、`/status` 形式の Gateway サマリーを返します。機密フィールドは、管理者スコープのオペレータークライアントにのみ含まれます。
    - `gateway.identity.get` は、リレーとペアリングフローで使用される Gateway デバイスアイデンティティを返します。
    - `system-presence` は、接続済みオペレーター/Node デバイスの現在のプレゼンススナップショットを返します。
    - `system-event` は、システムイベントを追加し、プレゼンスコンテキストを更新/ブロードキャストできます。
    - `last-heartbeat` は、最新の永続化済み Heartbeat イベントを返します。
    - `set-heartbeats` は、Gateway 上の Heartbeat 処理を切り替えます。

  </Accordion>

  <Accordion title="モデルと使用状況">
    - `models.list` は、ランタイムで許可されているモデルカタログを返します。ピッカー向けサイズの設定済みモデル（まず `agents.defaults.models`、次に `models.providers.*.models`）には `{ "view": "configured" }` を、完全なカタログには `{ "view": "all" }` を渡します。
    - `usage.status` は、プロバイダー使用状況ウィンドウ/残りクォータのサマリーを返します。
    - `usage.cost` は、日付範囲の集計済みコスト使用状況サマリーを返します。
      1 つのエージェントには `agentId` を渡し、設定済みエージェントを集計するには `agentScope: "all"` を渡します。
    - `doctor.memory.status` は、アクティブなデフォルトエージェントワークスペースのベクトルメモリ/キャッシュ済み埋め込みの準備状態を返します。呼び出し元がライブの埋め込みプロバイダー ping を明示的に求める場合にのみ、`{ "probe": true }` または `{ "deep": true }` を渡します。Dreaming 対応クライアントは、Dreaming ストア統計を選択したエージェントワークスペースにスコープするために `{ "agentId": "agent-id" }` も渡せます。`agentId` を省略すると、デフォルトエージェントへのフォールバックを維持し、設定済み Dreaming ワークスペースを集計します。
    - `doctor.memory.dreamDiary`、`doctor.memory.backfillDreamDiary`、`doctor.memory.resetDreamDiary`、`doctor.memory.resetGroundedShortTerm`、`doctor.memory.repairDreamingArtifacts`、`doctor.memory.dedupeDreamDiary` は、選択エージェントの Dreaming ビュー/アクション向けに任意の `{ "agentId": "agent-id" }` パラメーターを受け付けます。`agentId` が省略されると、設定済みのデフォルトエージェントワークスペースで動作します。
    - `doctor.memory.remHarness` は、リモートコントロールプレーンクライアント向けに、有界で読み取り専用の REM ハーネスプレビューを返します。ワークスペースパス、メモリスニペット、レンダリング済みの grounded markdown、深い昇格候補を含めることができるため、呼び出し元には `operator.read` が必要です。
    - `sessions.usage` は、セッションごとの使用状況サマリーを返します。1 つの
      エージェントには `agentId` を渡し、設定済みエージェントをまとめて一覧表示するには `agentScope: "all"` を渡します。
    - `sessions.usage.timeseries` は、1 つのセッションの時系列使用状況を返します。
    - `sessions.usage.logs` は、1 つのセッションの使用状況ログエントリを返します。

  </Accordion>

  <Accordion title="チャンネルとログインヘルパー">
    - `channels.status` は、組み込み + バンドル済みチャンネル/Plugin の状態サマリーを返します。
    - `channels.logout` は、チャンネルがログアウトをサポートしている特定のチャンネル/アカウントからログアウトします。
    - `web.login.start` は、現在の QR 対応 Web チャンネルプロバイダーの QR/Web ログインフローを開始します。
    - `web.login.wait` は、その QR/Web ログインフローが完了するまで待機し、成功時にチャンネルを開始します。
    - `push.test` は、登録済み iOS Node にテスト APNs プッシュを送信します。
    - `voicewake.get` は、保存済みのウェイクワードトリガーを返します。
    - `voicewake.set` は、ウェイクワードトリガーを更新し、変更をブロードキャストします。

  </Accordion>

  <Accordion title="メッセージングとログ">
    - `send` は、チャットランナー外でのチャンネル/アカウント/スレッド対象送信向けの直接アウトバウンド配信 RPC です。
    - `logs.tail` は、設定済み Gateway ファイルログの末尾を、カーソル/制限および最大バイト制御付きで返します。

  </Accordion>

  <Accordion title="Talk と TTS">
    - `talk.catalog` は、音声、ストリーミング文字起こし、リアルタイム音声向けの読み取り専用 Talk プロバイダーカタログを返します。正規プロバイダー ID、レジストリエイリアス、ラベル、設定状態、任意のグループレベル `ready` 結果、公開モデル/音声 ID、正規モード、トランスポート、ブレイン戦略、リアルタイム音声/ケイパビリティフラグを含み、プロバイダーシークレットを返したりグローバル設定を変更したりしません。現在の Gateway は、ランタイムプロバイダー選択を適用した後に `ready` を設定します。クライアントは、古い Gateway との互換性のため、それが存在しない場合は未検証として扱う必要があります。
    - `talk.config` は、有効な Talk 設定ペイロードを返します。`includeSecrets` には `operator.talk.secrets`（または `operator.admin`）が必要です。
    - `talk.session.create` は、`realtime/gateway-relay`、`transcription/gateway-relay`、または `stt-tts/managed-room` 向けに Gateway 所有の Talk セッションを作成します。`stt-tts/managed-room` では、`sessionKey` を渡す `operator.write` 呼び出し元は、スコープ付きセッションキー可視性のために `spawnedBy` も渡す必要があります。スコープなしの `sessionKey` 作成と `brain: "direct-tools"` には `operator.admin` が必要です。
    - `talk.session.join` は、管理ルームセッショントークンを検証し、必要に応じて `session.ready` または `session.replaced` イベントを発行し、平文トークンや保存済みトークンハッシュなしでルーム/セッションメタデータと最近の Talk イベントを返します。
    - `talk.session.appendAudio` は、base64 PCM 入力音声を Gateway 所有のリアルタイムリレーおよび文字起こしセッションに追加します。
    - `talk.session.startTurn`、`talk.session.endTurn`、`talk.session.cancelTurn` は、状態がクリアされる前に古いターンを拒否しつつ、管理ルームのターンライフサイクルを駆動します。
    - `talk.session.cancelOutput` は、主に Gateway リレーセッションでの VAD ゲート付き割り込みのために、アシスタント音声出力を停止します。
    - `talk.session.submitToolResult` は、Gateway 所有のリアルタイムリレーセッションによって発行されたプロバイダーツール呼び出しを完了します。最終結果が後続する中間ツール出力には `options: { willContinue: true }` を渡し、別のリアルタイムアシスタントレスポンスを開始せずにツール結果でプロバイダー呼び出しを満たす必要がある場合は `options: { suppressResponse: true }` を渡します。
    - `talk.session.steer` は、Gateway 所有のエージェント支援 Talk セッションにアクティブ実行の音声制御を送信します。`{ sessionId, text, mode? }` を受け付け、`mode` は `status`、`steer`、`cancel`、または `followup` です。省略されたモードは、発話テキストから分類されます。
    - `talk.session.close` は、Gateway 所有のリレー、文字起こし、または管理ルームセッションを閉じ、終端 Talk イベントを発行します。
    - `talk.mode` は、WebChat/Control UI クライアント向けに現在の Talk モード状態を設定/ブロードキャストします。
    - `talk.client.create` は、Gateway が設定、認証情報、指示、ツールポリシーを所有したまま、`webrtc` または `provider-websocket` を使用してクライアント所有のリアルタイムプロバイダーセッションを作成します。
    - `talk.client.toolCall` は、クライアント所有のリアルタイムトランスポートがプロバイダーツール呼び出しを Gateway ポリシーへ転送できるようにします。最初にサポートされるツールは `openclaw_agent_consult` です。クライアントは実行 ID を受け取り、プロバイダー固有のツール結果を送信する前に通常のチャットライフサイクルイベントを待ちます。
    - `talk.client.steer` は、クライアント所有のリアルタイムトランスポート向けにアクティブ実行の音声制御を送信します。Gateway は `sessionKey` からアクティブな埋め込み実行を解決し、ステアリングを黙って破棄するのではなく、構造化された受理/拒否結果を返します。
    - `talk.event` は、リアルタイム、文字起こし、STT/TTS、管理ルーム、テレフォニー、会議アダプター向けの単一の Talk イベントチャンネルです。
    - `talk.speak` は、アクティブな Talk 音声プロバイダーを通じて音声を合成します。
    - `tts.status` は、TTS 有効状態、アクティブプロバイダー、フォールバックプロバイダー、プロバイダー設定状態を返します。
    - `tts.providers` は、表示可能な TTS プロバイダーインベントリを返します。
    - `tts.enable` と `tts.disable` は、TTS 設定状態を切り替えます。
    - `tts.setProvider` は、優先 TTS プロバイダーを更新します。
    - `tts.convert` は、1 回限りのテキスト読み上げ変換を実行します。

  </Accordion>

  <Accordion title="シークレット、設定、更新、ウィザード">
    - `secrets.reload` は、アクティブな SecretRefs を再解決し、完全に成功した場合にのみランタイムのシークレット状態を入れ替えます。
    - `secrets.resolve` は、特定のコマンド/ターゲットセット向けに、コマンド対象のシークレット割り当てを解決します。
    - `config.get` は、現在の設定スナップショットとハッシュを返します。
    - `config.set` は、検証済みの設定ペイロードを書き込みます。
    - `config.patch` は、部分的な設定更新をマージします。破壊的な配列
      置換には、影響を受けるパスを `replacePaths` に含める必要があります。配列エントリ配下のネストされた配列には、`agents.list[].skills` のような `[]` パスを使用します。
    - `config.apply` は、完全な設定ペイロードを検証して置き換えます。
    - `config.schema` は、Control UI と CLI ツールで使用されるライブ設定スキーマペイロードを返します。スキーマ、`uiHints`、バージョン、生成メタデータを含み、ランタイムが読み込める場合は Plugin + チャンネルスキーマメタデータも含みます。このスキーマには、UI で使用される同じラベルとヘルプテキストから派生したフィールド `title` / `description` メタデータが含まれます。一致するフィールドドキュメントが存在する場合は、ネストされたオブジェクト、ワイルドカード、配列項目、`anyOf` / `oneOf` / `allOf` 合成ブランチも含まれます。
    - `config.schema.lookup` は、1 つの設定パスについて、パススコープのルックアップペイロードを返します。正規化パス、浅いスキーマノード、一致したヒント + `hintPath`、任意の `reloadKind`、UI/CLI ドリルダウン向けの直接の子サマリーを含みます。`reloadKind` は `restart`、`hot`、`none` のいずれかで、要求されたパスに対する Gateway 設定リロードプランナーを反映します。ルックアップスキーマノードは、ユーザー向けドキュメントと共通の検証フィールド（`title`、`description`、`type`、`enum`、`const`、`format`、`pattern`、数値/文字列/配列/オブジェクトの境界、`additionalProperties`、`deprecated`、`readOnly`、`writeOnly` などのフラグ）を保持します。子サマリーは、`key`、正規化済み `path`、`type`、`required`、`hasChildren`、任意の `reloadKind`、さらに一致した `hint` / `hintPath` を公開します。
    - `update.run` は Gateway 更新フローを実行し、更新自体が成功した場合にのみ再起動をスケジュールします。セッションを持つ呼び出し元は `continuationMessage` を含めることができるため、起動時に再起動継続キューを通じて 1 回の後続エージェントターンが再開されます。コントロールプレーンからのパッケージマネージャー更新と監視付き git チェックアウト更新では、稼働中の Gateway 内でパッケージツリーを置き換えたりチェックアウト/ビルド出力を変更したりする代わりに、分離された管理サービスへのハンドオフを使用します。開始されたハンドオフは、`result.reason: "managed-service-handoff-started"` と `handoff.status: "started"` を伴う `ok: true` を返します。利用不可または失敗したハンドオフは、`managed-service-handoff-unavailable` または `managed-service-handoff-failed` を伴う `ok: false` を返し、手動シェル更新が必要な場合は `handoff.command` も返します。利用不可のハンドオフは、OpenClaw に安全なスーパーバイザー境界または永続的なサービスアイデンティティがないことを意味します。たとえば systemd の `OPENCLAW_SYSTEMD_UNIT` などです。開始されたハンドオフ中、再起動センチネルは一時的に `stats.reason: "restart-health-pending"` を報告する場合があります。継続は、CLI が再起動後の Gateway を検証し、最終的な `ok` センチネルを書き込むまで遅延されます。
    - `update.status` は、最新の更新再起動センチネルを更新して返します。利用可能な場合は、再起動後に稼働しているバージョンも含みます。
    - `wizard.start`、`wizard.next`、`wizard.status`、`wizard.cancel` は、WS RPC 経由でオンボーディングウィザードを公開します。

  </Accordion>

  <Accordion title="Agent and workspace helpers">
    - `agents.list` は、実効モデルとランタイムメタデータを含む、設定済みエージェントエントリを返します。
    - `agents.create`、`agents.update`、`agents.delete` は、エージェントレコードとワークスペースの接続を管理します。
    - `agents.files.list`、`agents.files.get`、`agents.files.set` は、エージェントに公開されるブートストラップワークスペースファイルを管理します。
    - `tasks.list`、`tasks.get`、`tasks.cancel` は、Gateway タスク台帳を SDK とオペレータークライアントに公開します。
    - `artifacts.list`、`artifacts.get`、`artifacts.download` は、明示的な `sessionKey`、`runId`、または `taskId` スコープに対して、トランスクリプト由来のアーティファクト要約とダウンロードを公開します。実行クエリとタスククエリは、所有するセッションをサーバー側で解決し、由来が一致するトランスクリプトメディアだけを返します。安全でない URL ソースやローカル URL ソースは、サーバー側で取得する代わりに、未対応のダウンロードを返します。
    - `environments.list` と `environments.status` は、SDK クライアント向けに読み取り専用の Gateway ローカル環境とノード環境の検出を公開します。
    - `agent.identity.get` は、エージェントまたはセッションの実効アシスタント ID を返します。
    - `agent.wait` は実行の完了を待ち、利用可能な場合は終端スナップショットを返します。

  </Accordion>

  <Accordion title="Session control">
    - `sessions.list` は、エージェントランタイムバックエンドが設定されている場合に行ごとの `agentRuntime` メタデータを含む、現在のセッションインデックスを返します。
    - `sessions.subscribe` と `sessions.unsubscribe` は、現在の WS クライアントのセッション変更イベント購読を切り替えます。
    - `sessions.messages.subscribe` と `sessions.messages.unsubscribe` は、1 つのセッションのトランスクリプト/メッセージイベント購読を切り替えます。
    - `sessions.preview` は、特定のセッションキーについて範囲制限されたトランスクリプトプレビューを返します。
    - `sessions.describe` は、正確なセッションキーに対応する 1 つの Gateway セッション行を返します。
    - `sessions.resolve` は、セッションターゲットを解決または正規化します。
    - `sessions.create` は、新しいセッションエントリを作成します。
    - `sessions.send` は、既存のセッションにメッセージを送信します。
    - `sessions.steer` は、アクティブなセッション向けの中断して誘導するバリアントです。
    - `sessions.abort` は、セッションのアクティブな作業を中止します。呼び出し元は `key` と任意の `runId` を渡すか、Gateway がセッションへ解決できるアクティブな実行については `runId` だけを渡せます。
    - `sessions.patch` は、セッションメタデータ/オーバーライドを更新し、解決済みの正規モデルと実効 `agentRuntime` を報告します。
    - `sessions.reset`、`sessions.delete`、`sessions.compact` は、セッションのメンテナンスを実行します。
    - `sessions.get` は、保存されている完全なセッション行を返します。
    - チャット実行は引き続き `chat.history`、`chat.send`、`chat.abort`、`chat.inject` を使用します。`chat.history` は UI クライアント向けに表示正規化されます。インラインディレクティブタグは表示テキストから取り除かれ、プレーンテキストのツール呼び出し XML ペイロード（`<tool_call>...</tool_call>`、`<function_call>...</function_call>`、`<tool_calls>...</tool_calls>`、`<function_calls>...</function_calls>`、および切り詰められたツール呼び出しブロックを含む）と漏えいした ASCII/全角のモデル制御トークンは取り除かれ、正確な `NO_REPLY` / `no_reply` のような純粋なサイレントトークンのアシスタント行は省略され、過大な行はプレースホルダーに置き換えられる場合があります。
    - `chat.message.get` は、単一の可視トランスクリプトエントリ向けに追加された、範囲制限付きの完全メッセージリーダーです。クライアントは `sessionKey`、セッション選択がエージェントスコープの場合は任意の `agentId`、さらに以前に `chat.history` を通じて公開されたトランスクリプト `messageId` を渡します。保存済みエントリがまだ利用可能で過大でない場合、Gateway は軽量履歴の切り詰め上限なしで、同じ表示正規化済みプロジェクションを返します。
    - `chat.send` は、1 ターンの `fastMode: "auto"` を受け入れ、自動カットオフ前に開始されたモデル呼び出しには fast mode を使い、その後のリトライ、フォールバック、ツール結果、または継続呼び出しは fast mode なしで開始します。カットオフのデフォルトは 60 秒で、`agents.defaults.models["<provider>/<model>"].params.fastAutoOnSeconds` によりモデルごとに設定できます。`chat.send` の呼び出し元は、そのリクエストのカットオフを上書きするために 1 ターンの `fastAutoOnSeconds` を渡せます。

  </Accordion>

  <Accordion title="Device pairing and device tokens">
    - `device.pair.list` は、保留中および承認済みのペアリング済みデバイスを返します。
    - `device.pair.setupCode` は、モバイルセットアップコードと、デフォルトでは PNG QR データ URL を作成します。`operator.admin` が必要で、広告される検出からは意図的に省略されています。結果には `setupCode`、任意の `qrDataUrl`、`gatewayUrl`、秘密ではない `auth` ラベル、`urlSource` が含まれます。
    - `device.pair.approve`、`device.pair.reject`、`device.pair.remove` は、デバイスペアリングレコードを管理します。
    - `device.token.rotate` は、承認済みロールと呼び出し元スコープの範囲内で、ペアリング済みデバイストークンをローテーションします。
    - `device.token.revoke` は、承認済みロールと呼び出し元スコープの範囲内で、ペアリング済みデバイストークンを取り消します。

    セットアップコードには、有効期間の短いブートストラップ認証情報が埋め込まれます。クライアントは、ペアリングフローを超えてそれをログに記録したり永続化したりしてはなりません。

  </Accordion>

  <Accordion title="Node pairing, invoke, and pending work">
    - `node.pair.request`、`node.pair.list`、`node.pair.approve`、`node.pair.reject`、`node.pair.remove`、`node.pair.verify` は、ノードペアリングとブートストラップ検証を扱います。
    - `node.list` と `node.describe` は、既知/接続済みノードの状態を返します。
    - `node.rename` は、ペアリング済みノードのラベルを更新します。
    - `node.invoke` は、接続済みノードにコマンドを転送します。
    - `node.invoke.result` は、invoke リクエストの結果を返します。
    - `node.event` は、ノード発のイベントを Gateway に戻します。
    - `node.pending.pull` と `node.pending.ack` は、接続済みノードキュー API です。
    - `node.pending.enqueue` と `node.pending.drain` は、オフライン/切断済みノード向けの永続的な保留作業を管理します。

  </Accordion>

  <Accordion title="Approval families">
    - `exec.approval.request`、`exec.approval.get`、`exec.approval.list`、`exec.approval.resolve` は、単発の exec 承認リクエストと保留中の承認の検索/再生を扱います。
    - `exec.approval.waitDecision` は、1 つの保留中 exec 承認を待機し、最終判断（タイムアウト時は `null`）を返します。
    - `exec.approvals.get` と `exec.approvals.set` は、Gateway exec 承認ポリシースナップショットを管理します。
    - `exec.approvals.node.get` と `exec.approvals.node.set` は、ノードリレーコマンドを通じてノードローカルの exec 承認ポリシーを管理します。
    - `plugin.approval.request`、`plugin.approval.list`、`plugin.approval.waitDecision`、`plugin.approval.resolve` は、Plugin 定義の承認フローを扱います。

  </Accordion>

  <Accordion title="Automation, skills, and tools">
    - 自動化: `wake` は即時または次回 Heartbeat での wake テキスト挿入をスケジュールします。`cron.get`、`cron.list`、`cron.status`、`cron.add`、`cron.update`、`cron.remove`、`cron.run`、`cron.runs` は、スケジュール済み作業を管理します。
    - `cron.run` は、手動実行向けの enqueue 形式 RPC のままです。完了セマンティクスが必要なクライアントは、返された `runId` を読み取り、`cron.runs` をポーリングする必要があります。
    - `cron.runs` は、任意の空でない `runId` フィルターを受け入れるため、クライアントは同じジョブの他の履歴エントリと競合することなく、キューに入った 1 つの手動実行を追跡できます。
    - Skills とツール: `commands.list`、`skills.*`、`tools.catalog`、`tools.effective`、`tools.invoke`。

  </Accordion>
</AccordionGroup>

### 共通イベントファミリー

- `chat`: `chat.inject` などの UI チャット更新、およびその他のトランスクリプト専用チャットイベント。プロトコル v4 では、デルタペイロードは `deltaText` を運びます。`message` は累積されたアシスタントスナップショットのままです。非プレフィックス置換は `replace=true` を設定し、`deltaText` を置換テキストとして使用します。
- `session.message`、`session.operation`、`session.tool`: 購読中セッション向けのトランスクリプト、実行中セッション操作、イベントストリーム更新。
- `sessions.changed`: セッションインデックスまたはメタデータが変更されました。
- `presence`: システムプレゼンススナップショット更新。
- `tick`: 定期的な keepalive / liveness イベント。
- `health`: Gateway ヘルススナップショット更新。
- `heartbeat`: Heartbeat イベントストリーム更新。
- `cron`: cron 実行/ジョブ変更イベント。
- `shutdown`: Gateway シャットダウン通知。
- `node.pair.requested` / `node.pair.resolved`: ノードペアリングのライフサイクル。
- `node.invoke.request`: ノード invoke リクエストのブロードキャスト。
- `device.pair.requested` / `device.pair.resolved`: ペアリング済みデバイスのライフサイクル。
- `voicewake.changed`: ウェイクワードトリガー設定が変更されました。
- `exec.approval.requested` / `exec.approval.resolved`: exec 承認ライフサイクル。
- `plugin.approval.requested` / `plugin.approval.resolved`: Plugin 承認ライフサイクル。

### ノードヘルパーメソッド

- ノードは、自動許可チェック用に現在の skill 実行可能ファイル一覧を取得するため、`skills.bins` を呼び出せます。

### タスク台帳 RPC

オペレータークライアントは、タスク台帳 RPC を通じて Gateway バックグラウンドタスクレコードを検査およびキャンセルできます。これらのメソッドは、生のランタイム状態ではなく、サニタイズ済みタスク要約を返します。

- `tasks.list` には `operator.read` が必要です。
  - パラメーター: 任意の `status`（`"queued"`、`"running"`、`"completed"`、`"failed"`、`"cancelled"`、または `"timed_out"`）またはそれらのステータスの配列、任意の `agentId`、任意の `sessionKey`、`1` から `500` までの任意の `limit`、任意の文字列 `cursor`。
  - 結果: `{ "tasks": TaskSummary[], "nextCursor"?: string }`。
- `tasks.get` には `operator.read` が必要です。
  - パラメーター: `{ "taskId": string }`。
  - 結果: `{ "task": TaskSummary }`。
  - 見つからないタスク ID は、Gateway の not-found エラー形状を返します。
- `tasks.cancel` には `operator.write` が必要です。
  - パラメーター: `{ "taskId": string, "reason"?: string }`。
  - 結果:
    `{ "found": boolean, "cancelled": boolean, "reason"?: string, "task"?: TaskSummary }`。
  - `found` は、台帳に一致するタスクがあったかどうかを報告します。`cancelled` は、ランタイムがキャンセルを受け入れたか、または記録したかどうかを報告します。

`TaskSummary` には、`id`、`status`、および `kind`、`runtime`、`title`、`agentId`、`sessionKey`、`childSessionKey`、`ownerKey`、`runId`、`taskId`、`flowId`、`parentTaskId`、`sourceId`、タイムスタンプ、進捗、終端要約、サニタイズ済みエラーテキストなどの任意のメタデータが含まれます。`agentId` は、タスクを実行しているエージェントを識別します。`sessionKey` と `ownerKey` は、リクエスターと制御コンテキストを保持します。

### オペレーターヘルパーメソッド

- オペレーターは `commands.list` (`operator.read`) を呼び出して、エージェントのランタイム
  コマンドインベントリを取得できます。
  - `agentId` は任意です。省略するとデフォルトのエージェントワークスペースを読み取ります。
  - `scope` は、プライマリ `name` が対象にするサーフェスを制御します:
    - `text` は、先頭の `/` を除いたプライマリテキストコマンドトークンを返します
    - `native` とデフォルトの `both` パスは、利用可能な場合にプロバイダーを考慮したネイティブ名を返します
  - `textAliases` は `/model` や `/m` などの正確なスラッシュエイリアスを保持します。
  - `nativeName` は、存在する場合にプロバイダーを考慮したネイティブコマンド名を保持します。
  - `provider` は任意で、ネイティブ名とネイティブプラグイン
    コマンドの可用性にのみ影響します。
  - `includeArgs=false` は、シリアライズされた引数メタデータをレスポンスから省略します。
- オペレーターは `tools.catalog` (`operator.read`) を呼び出して、エージェントのランタイムツールカタログを取得できます。レスポンスには、グループ化されたツールと来歴メタデータが含まれます:
  - `source`: `core` または `plugin`
  - `pluginId`: `source="plugin"` の場合のプラグイン所有者
  - `optional`: プラグインツールが任意かどうか
- オペレーターは `tools.effective` (`operator.read`) を呼び出して、セッションでランタイム上有効なツール
  インベントリを取得できます。
  - `sessionKey` は必須です。
  - Gateway は、呼び出し元が指定した認証または配信コンテキストを受け入れる代わりに、セッションからサーバー側で信頼済みランタイムコンテキストを導出します。
  - レスポンスは、アクティブなインベントリのセッションスコープのサーバー導出プロジェクションであり、
    core、プラグイン、チャネル、すでに検出済みの MCP サーバーツールを含みます。
  - `tools.effective` は MCP に対して読み取り専用です。ウォーム状態のセッション MCP カタログを最終ツールポリシーに通して投影する場合がありますが、MCP ランタイムの作成、トランスポートの接続、または
    `tools/list` の発行は行いません。一致するウォームカタログが存在しない場合、レスポンスには
    `mcp-not-yet-connected`、`mcp-not-yet-listed`、または `mcp-stale-catalog` のような通知が含まれる場合があります。
  - 有効なツールエントリは `source="core"`、`source="plugin"`、`source="channel"`、または
    `source="mcp"` を使用します。
- オペレーターは `tools.invoke` (`operator.write`) を呼び出して、
  `/tools/invoke` と同じ Gateway ポリシーパスを通じて、利用可能なツールを 1 つ呼び出せます。
  - `name` は必須です。`args`、`sessionKey`、`agentId`、`confirm`、および
    `idempotencyKey` は任意です。
  - `sessionKey` と `agentId` の両方が存在する場合、解決されたセッションエージェントは
    `agentId` と一致する必要があります。
  - `cron`、`gateway`、`nodes` など所有者専用の core ラッパーは、
    `tools.invoke` メソッド自体は `operator.write` ですが、
    所有者/管理者 ID (`operator.admin`) が必要です。
  - レスポンスは、`ok`、`toolName`、任意の `output`、および型付きの
    `error` フィールドを持つ SDK 向けエンベロープです。承認またはポリシーによる拒否は、Gateway ツールポリシーパイプラインを迂回するのではなく、ペイロード内で `ok:false` を返します。
- オペレーターは `skills.status` (`operator.read`) を呼び出して、エージェントに表示される
  skill インベントリを取得できます。
  - `agentId` は任意です。省略するとデフォルトのエージェントワークスペースを読み取ります。
  - レスポンスには、適格性、不足している要件、設定チェック、および
    生のシークレット値を公開しないサニタイズ済みインストールオプションが含まれます。
- オペレーターは `skills.search` と `skills.detail` (`operator.read`) を呼び出して、
  ClawHub 検出メタデータを取得できます。
- オペレーターは `skills.upload.begin`、`skills.upload.chunk`、および
  `skills.upload.commit` (`operator.admin`) を呼び出して、インストール前に非公開 skill アーカイブをステージングできます。これは信頼済みクライアント用の別個の管理者アップロードパスであり、
  通常の ClawHub skill インストールフローではありません。また、
  `skills.install.allowUploadedArchives` が有効でない限りデフォルトで無効です。
  - `skills.upload.begin({ kind: "skill-archive", slug, sizeBytes, sha256?, force?, idempotencyKey? })`
    は、その slug と force 値にバインドされたアップロードを作成します。
  - `skills.upload.chunk({ uploadId, offset, dataBase64 })` は、正確なデコード済みオフセットにバイトを追加します。
  - `skills.upload.commit({ uploadId, sha256? })` は、最終サイズと
    SHA-256 を検証します。コミットはアップロードを確定するだけで、skill はインストールしません。
  - アップロードされた skill アーカイブは、ルートに `SKILL.md` を含む zip アーカイブです。アーカイブ内部のディレクトリ名がインストール先を選択することはありません。
- オペレーターは `skills.install` (`operator.admin`) を 3 つのモードで呼び出せます:
  - ClawHub モード: `{ source: "clawhub", slug, version?, force? }` は、skill フォルダーをデフォルトのエージェントワークスペースの `skills/` ディレクトリにインストールします。
  - アップロードモード: `{ source: "upload", uploadId, slug, force?, sha256?, timeoutMs? }`
    は、コミット済みアップロードをデフォルトのエージェントワークスペースの `skills/<slug>`
    ディレクトリにインストールします。slug と force 値は、元の
    `skills.upload.begin` リクエストと一致する必要があります。このモードは
    `skills.install.allowUploadedArchives` が有効でない限り拒否されます。この設定は
    ClawHub インストールには影響しません。
  - Gateway インストーラーモード: `{ name, installId, timeoutMs? }`
    は、Gateway ホスト上で宣言済みの `metadata.openclaw.install` アクションを実行します。
    古いクライアントは引き続き `dangerouslyForceUnsafeInstall` を送信する場合があります。このフィールドは非推奨で、プロトコル互換性のためにのみ受け入れられ、無視されます。
    オペレーター所有のインストール判断には `security.installPolicy` を使用してください。
- オペレーターは `skills.update` (`operator.admin`) を 2 つのモードで呼び出せます:
  - ClawHub モードは、デフォルトのエージェントワークスペース内の、追跡対象の slug 1 つ、または追跡対象のすべての ClawHub インストールを更新します。
  - 設定モードは、`enabled`、
    `apiKey`、`env` などの `skills.entries.<skillKey>` 値にパッチを適用します。

### `models.list` ビュー

`models.list` は任意の `view` パラメーターを受け付けます:

- 省略または `"default"`: 現在のランタイム動作。`agents.defaults.models` が設定されている場合、レスポンスは許可済みカタログになり、`provider/*` エントリに対して動的に検出されたモデルも含まれます。それ以外の場合、レスポンスは完全な Gateway カタログです。
- `"configured"`: ピッカー向けサイズの動作。`agents.defaults.models` が設定されている場合は、引き続きそれが優先され、`provider/*` エントリに対するプロバイダースコープの検出も含まれます。許可リストがない場合、レスポンスは明示的な `models.providers.*.models` エントリを使用し、設定済みモデル行が存在しない場合にのみ完全なカタログへフォールバックします。
- `"all"`: 完全な Gateway カタログで、`agents.defaults.models` を迂回します。通常のモデルピッカーではなく、診断と検出 UI に使用してください。

## Exec 承認

- exec リクエストが承認を必要とする場合、Gateway は `exec.approval.requested` をブロードキャストします。
- オペレータークライアントは `exec.approval.resolve` を呼び出して解決します (`operator.approvals` スコープが必要)。
- `host=node` の場合、`exec.approval.request` には `systemRunPlan` (正規の `argv`/`cwd`/`rawCommand`/セッションメタデータ) を含める必要があります。`systemRunPlan` がないリクエストは拒否されます。
- 承認後、転送される `node.invoke system.run` 呼び出しは、その正規の
  `systemRunPlan` を権威ある command/cwd/セッションコンテキストとして再利用します。
- 呼び出し元が prepare から最終承認済み `system.run` 転送までの間に `command`、`rawCommand`、`cwd`、`agentId`、または
  `sessionKey` を変更した場合、Gateway は変更されたペイロードを信頼せずに実行を拒否します。

## エージェント配信フォールバック

- `agent` リクエストには、アウトバウンド配信をリクエストするために `deliver=true` を含められます。
- `bestEffortDeliver=false` は厳密な動作を維持します。未解決または内部専用の配信ターゲットは `INVALID_REQUEST` を返します。
- `bestEffortDeliver=true` は、外部に配信可能なルートを解決できない場合に、セッションのみの実行へのフォールバックを許可します (たとえば、内部/webchat セッションや曖昧なマルチチャネル設定)。
- 最終的な `agent` 結果には、配信がリクエストされた場合に
  `result.deliveryStatus` が含まれる場合があり、[`openclaw agent --json --deliver`](/ja-JP/cli/agent#json-delivery-status) で文書化されているものと同じ `sent`、`suppressed`、`partial_failed`、および `failed`
  ステータスを使用します。

## バージョニング

- `PROTOCOL_VERSION` は `packages/gateway-protocol/src/version.ts` にあります。
- クライアントは `minProtocol` + `maxProtocol` を送信します。サーバーは、
  現在のプロトコルを含まない範囲を拒否します。現在のクライアントとサーバーは
  プロトコル v4 を必要とします。
- スキーマ + モデルは TypeBox 定義から生成されます:
  - `pnpm protocol:gen`
  - `pnpm protocol:gen:swift`
  - `pnpm protocol:check`

### クライアント定数

`src/gateway/client.ts` のリファレンスクライアントは、これらのデフォルトを使用します。値は
プロトコル v4 全体で安定しており、サードパーティクライアントの想定ベースラインです。

| 定数                                      | デフォルト                                            | ソース                                                                                     |
| ----------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `PROTOCOL_VERSION`                        | `4`                                                   | `packages/gateway-protocol/src/version.ts`                                                 |
| `MIN_CLIENT_PROTOCOL_VERSION`             | `4`                                                   | `packages/gateway-protocol/src/version.ts`                                                 |
| リクエストタイムアウト (RPC ごと)         | `30_000` ms                                           | `src/gateway/client.ts` (`requestTimeoutMs`)                                               |
| 事前認証 / 接続チャレンジのタイムアウト   | `15_000` ms                                           | `src/gateway/handshake-timeouts.ts` (config/env can raise the paired server/client budget) |
| 初期再接続バックオフ                      | `1_000` ms                                            | `src/gateway/client.ts` (`backoffMs`)                                                      |
| 最大再接続バックオフ                      | `30_000` ms                                           | `src/gateway/client.ts` (`scheduleReconnect`)                                              |
| device-token クローズ後の高速リトライ制限 | `250` ms                                              | `src/gateway/client.ts`                                                                    |
| `terminate()` 前の強制停止猶予            | `250` ms                                              | `FORCE_STOP_TERMINATE_GRACE_MS`                                                            |
| `stopAndWait()` デフォルトタイムアウト     | `1_000` ms                                            | `STOP_AND_WAIT_TIMEOUT_MS`                                                                 |
| デフォルト tick 間隔 (`hello-ok` 前)       | `30_000` ms                                           | `src/gateway/client.ts`                                                                    |
| tick タイムアウトクローズ                 | 無音が `tickIntervalMs * 2` を超えた場合は code `4000` | `src/gateway/client.ts`                                                                    |
| `MAX_PAYLOAD_BYTES`                       | `25 * 1024 * 1024` (25 MB)                            | `src/gateway/server-constants.ts`                                                          |

サーバーは、有効な `policy.tickIntervalMs`、`policy.maxPayload`、
および `policy.maxBufferedBytes` を `hello-ok` で通知します。クライアントは、ハンドシェイク前のデフォルトではなく、それらの値を尊重する必要があります。

## 認証

- 共有シークレットの Gateway 認証は、設定された認証モードに応じて、`connect.params.auth.token` または
  `connect.params.auth.password` を使用します。
- Tailscale Serve
  (`gateway.auth.allowTailscale: true`) や非ループバックの
  `gateway.auth.mode: "trusted-proxy"` など、ID を伴うモードは、
  `connect.params.auth.*` の代わりにリクエストヘッダーから connect 認証チェックを満たします。
- プライベートイングレスの `gateway.auth.mode: "none"` は、共有シークレットの connect 認証を
  完全にスキップします。このモードをパブリックまたは信頼できないイングレスに公開しないでください。
- ペアリング後、Gateway は接続ロール + スコープに限定された **デバイストークン** を発行します。
  これは `hello-ok.auth.deviceToken` で返され、今後の接続のためにクライアントが永続化する必要があります。
- クライアントは、成功した connect の後に必ず主要な `hello-ok.auth.deviceToken` を永続化する必要があります。
- その **保存済み** デバイストークンで再接続する場合、そのトークンに対して保存済みの承認済みスコープセットも再利用する必要があります。
  これにより、すでに付与された読み取り/プローブ/ステータスアクセスが保持され、再接続が暗黙の管理者専用スコープへ
  気づかないうちに狭められることを避けられます。
- クライアント側の connect 認証組み立て (`src/gateway/client.ts` の `selectConnectAuth`):
  - `auth.password` は独立しており、設定されている場合は常に転送されます。
  - `auth.token` は優先順で設定されます。明示的な共有トークンが最優先で、
    次に明示的な `deviceToken`、その次に保存済みのデバイス単位トークン (`deviceId` + `role` でキー付け) です。
  - `auth.bootstrapToken` は、上記のどれも `auth.token` を解決しなかった場合にのみ送信されます。
    共有トークンまたは解決済みのデバイストークンがある場合、これは抑制されます。
  - 1 回限りの `AUTH_TOKEN_MISMATCH` 再試行で保存済みデバイストークンを自動昇格する処理は、
    **信頼済みエンドポイントのみ** に制限されます。つまり、ループバック、または `tlsFingerprint` がピン留めされた
    `wss://` です。ピン留めのないパブリック `wss://` は該当しません。
- 組み込みセットアップコードのブートストラップは、主要な Node
  `hello-ok.auth.deviceToken` に加え、信頼済みモバイル引き渡し用の制限付きオペレータートークンを
  `hello-ok.auth.deviceTokens` で返します。オペレータートークンには、ネイティブ Talk 設定の読み取り用に
  `operator.talk.secrets` が含まれますが、ペアリング変更スコープと `operator.admin` は除外されます。
- 非ベースラインのセットアップコードブートストラップが承認待ちの間、`PAIRING_REQUIRED`
  の詳細には `recommendedNextStep: "wait_then_retry"`、`retryable: true`、
  `pauseReconnect: false` が含まれます。クライアントは、リクエストが承認されるかトークンが無効になるまで、
  同じブートストラップトークンで再接続を続ける必要があります。
- `hello-ok.auth.deviceTokens` は、connect が `wss://` やループバック/local ペアリングなどの信頼済みトランスポート上で
  ブートストラップ認証を使用した場合にのみ永続化してください。
- クライアントが **明示的な** `deviceToken` または明示的な `scopes` を提供した場合、その呼び出し元が要求した
  スコープセットが引き続き正となります。キャッシュ済みスコープは、クライアントが保存済みのデバイス単位トークンを
  再利用している場合にのみ再利用されます。
- デバイストークンは `device.token.rotate` と
  `device.token.revoke` でローテーション/失効できます (`operator.pairing` スコープが必要)。Node または他の非オペレーターロールを
  ローテーションまたは失効する場合は、`operator.admin` も必要です。
- `device.token.rotate` はローテーションメタデータを返します。置換用のベアラートークンは、
  そのデバイストークンですでに認証済みの同一デバイス呼び出しにのみエコーされます。そのため、トークンのみのクライアントは
  再接続前に置換トークンを永続化できます。共有/管理者ローテーションではベアラートークンはエコーされません。
- トークンの発行、ローテーション、失効は、そのデバイスのペアリングエントリに記録された承認済みロールセットに制限されます。
  トークン変更によって、ペアリング承認で付与されていないデバイスロールへ拡張したり、対象にしたりすることはできません。
- ペアリング済みデバイストークンのセッションでは、呼び出し元が `operator.admin` も持っていない限り、
  デバイス管理は自己スコープに制限されます。非管理者の呼び出し元が管理できるのは、**自分自身の** デバイスエントリの
  オペレータートークンのみです。Node や他の非オペレータートークンの管理は、呼び出し元自身のデバイスであっても管理者専用です。
- `device.token.rotate` と `device.token.revoke` は、対象のオペレータートークンのスコープセットを、
  呼び出し元の現在のセッションスコープとも照合します。非管理者の呼び出し元は、自分がすでに保持しているものより広い
  オペレータートークンをローテーションまたは失効できません。
- 認証失敗には、`error.details.code` と復旧ヒントが含まれます:
  - `error.details.canRetryWithDeviceToken` (boolean)
  - `error.details.recommendedNextStep` (`retry_with_device_token`, `update_auth_configuration`, `update_auth_credentials`, `wait_then_retry`, `review_auth_configuration`)
- `AUTH_TOKEN_MISMATCH` に対するクライアントの動作:
  - 信頼済みクライアントは、キャッシュ済みのデバイス単位トークンで 1 回だけ制限付き再試行を試みることができます。
  - その再試行が失敗した場合、クライアントは自動再接続ループを停止し、オペレーターの対応案内を表示する必要があります。
- `AUTH_SCOPE_MISMATCH` は、デバイストークンは認識されたものの、要求されたロール/スコープをカバーしていないことを意味します。
  クライアントはこれを不正なトークンとして提示しないでください。オペレーターに、再ペアリングするか、より狭い/広いスコープ契約を
  承認するよう促してください。

## デバイス ID + ペアリング

- Node は、鍵ペアのフィンガープリントから派生した安定したデバイス ID (`device.id`) を含める必要があります。
- Gateway はデバイス + ロールごとにトークンを発行します。
- local 自動承認が有効でない限り、新しいデバイス ID にはペアリング承認が必要です。
- ペアリングの自動承認は、直接の local loopback 接続を中心にしています。
- OpenClaw には、信頼済み共有シークレットヘルパーフロー用に、狭いバックエンド/コンテナローカルの自己接続パスもあります。
- 同一ホストの tailnet または LAN 接続も、ペアリングでは引き続きリモートとして扱われ、承認が必要です。
- WS クライアントは通常、`connect` 中に `device` ID を含めます (オペレーター +
  Node)。デバイスなしのオペレーター例外は、明示的な信頼パスのみです:
  - localhost 専用の安全でない HTTP 互換性のための `gateway.controlUi.allowInsecureAuth=true`。
  - 成功した `gateway.auth.mode: "trusted-proxy"` オペレーター Control UI 認証。
  - `gateway.controlUi.dangerouslyDisableDeviceAuth=true` (非常用、重大なセキュリティ低下)。
  - 予約済み内部ヘルパーパス上の direct-loopback `gateway-client` バックエンド RPC。
- デバイス ID を省略するとスコープ上の影響があります。デバイスなしのオペレーター接続が明示的な信頼パスで許可された場合でも、
  OpenClaw は、そのパスに名前付きのスコープ保持例外がない限り、自己申告されたスコープを空セットにクリアします。
  その後、スコープで制限されたメソッドは `missing scope` で失敗します。
- `gateway.controlUi.dangerouslyDisableDeviceAuth=true` は、Control UI の非常用スコープ保持パスです。
  任意のカスタムバックエンドや CLI 形式の WebSocket クライアントにスコープを付与するものではありません。
- 予約済みの direct-loopback `gateway-client` バックエンドヘルパーパスは、
  内部 local コントロールプレーン RPC に対してのみスコープを保持します。カスタムバックエンド ID はこの例外を受けません。
- すべての接続は、サーバーが提供する `connect.challenge` nonce に署名する必要があります。

### デバイス認証移行診断

チャレンジ前の署名動作をまだ使用しているレガシークライアントに対して、`connect` は現在、
安定した `error.details.reason` とともに、`error.details.code` 配下で
`DEVICE_AUTH_*` 詳細コードを返します。

一般的な移行失敗:

| メッセージ                     | details.code                     | details.reason           | 意味                                            |
| --------------------------- | -------------------------------- | ------------------------ | -------------------------------------------------- |
| `device nonce required`     | `DEVICE_AUTH_NONCE_REQUIRED`     | `device-nonce-missing`   | クライアントが `device.nonce` を省略しました (または空で送信しました)。     |
| `device nonce mismatch`     | `DEVICE_AUTH_NONCE_MISMATCH`     | `device-nonce-mismatch`  | クライアントが古い/誤った nonce で署名しました。            |
| `device signature invalid`  | `DEVICE_AUTH_SIGNATURE_INVALID`  | `device-signature`       | 署名ペイロードが v2 ペイロードと一致しません。       |
| `device signature expired`  | `DEVICE_AUTH_SIGNATURE_EXPIRED`  | `device-signature-stale` | 署名済みタイムスタンプが許容スキューの範囲外です。          |
| `device identity mismatch`  | `DEVICE_AUTH_DEVICE_ID_MISMATCH` | `device-id-mismatch`     | `device.id` が公開鍵フィンガープリントと一致しません。 |
| `device public key invalid` | `DEVICE_AUTH_PUBLIC_KEY_INVALID` | `device-public-key`      | 公開鍵の形式/正規化に失敗しました。         |

移行先:

- 必ず `connect.challenge` を待ちます。
- サーバー nonce を含む v2 ペイロードに署名します。
- 同じ nonce を `connect.params.device.nonce` で送信します。
- 推奨される署名ペイロードは `v3` です。これは device/client/role/scopes/token/nonce フィールドに加えて、
  `platform` と `deviceFamily` を結び付けます。
- レガシー `v2` 署名は互換性のため引き続き受け入れられますが、再接続時のコマンドポリシーは
  ペアリング済みデバイスのメタデータピン留めによって引き続き制御されます。

## TLS + ピン留め

- TLS は WS 接続でサポートされています。
- クライアントは任意で Gateway 証明書フィンガープリントをピン留めできます (`gateway.tls`
  設定に加え、`gateway.remote.tlsFingerprint` または CLI `--tls-fingerprint` を参照)。

## スコープ

このプロトコルは **完全な Gateway API** (ステータス、チャンネル、モデル、チャット、
エージェント、セッション、Node、承認など) を公開します。正確なサーフェスは
`packages/gateway-protocol/src/schema.ts` の TypeBox スキーマで定義されています。

## 関連

- [ブリッジプロトコル](/ja-JP/gateway/bridge-protocol)
- [Gateway ランブック](/ja-JP/gateway)
