メインコンテンツへスキップ
公開 capability モデル、Plugin 形状、所有権/実行の契約については、Plugin アーキテクチャを参照してください。このページでは、内部メカニクスであるロードパイプライン、レジストリ、ランタイムフック、Gateway HTTP ルート、インポートパス、スキーマテーブルを扱います。

ロードパイプライン

起動時、OpenClaw はおおよそ次の処理を行います。
  1. 候補 Plugin ルートを検出する
  2. ネイティブまたは互換バンドルのマニフェストとパッケージメタデータを読み取る
  3. 安全でない候補を拒否する
  4. Plugin 設定(plugins.enabledallowdenyentries, slotsload.paths)を正規化する
  5. 各候補の有効化を判断する
  6. 有効化されたネイティブモジュールをロードする: ビルド済みのバンドルモジュールはネイティブローダーを使用し、 サードパーティのローカルソース TypeScript は緊急用の Jiti フォールバックを使用する
  7. ネイティブの register(api) フックを呼び出し、登録内容を Plugin レジストリに収集する
  8. コマンド/ランタイム面にレジストリを公開する
activateregister のレガシーエイリアスです。ローダーは存在する方(def.register ?? def.activate)を解決し、同じ時点で呼び出します。すべてのバンドル済み Plugin は register を使用します。新しい Plugin では register を優先してください。
安全性ゲートはランタイム実行の前に実行されます。検出は、次の場合に候補をブロックします。
  • 解決されたエントリが Plugin ルートの外へ出る
  • パス(またはそのルートディレクトリ)が world-writable である
  • 非バンドル Plugin で、パスの所有者が現在の uid(または root)と一致しない
world-writable なバンドルディレクトリには、ゲートの再チェック前にまずインプレースの chmod 修復を試みます(npm/global インストールではパッケージディレクトリが 0777 で出荷されることがあります)。所有者チェックはバンドル由来の場合は完全にスキップされます。 ブロックされた候補でも、Plugin id が分かっている場合(それ以外なら拒否されるディレクトリ内のマニフェストから解決された id を含む)は、出力される診断にその Plugin id が含まれます。そのため、その id を参照する設定では、無関係な「不明な Plugin」エラーではなく、パス安全性警告に紐づいたブロック済み Plugin として扱われます。

マニフェスト優先の動作

マニフェストは制御プレーンの信頼できる情報源です。OpenClaw はこれを使って次を行います。
  • Plugin を識別する
  • 宣言されたチャンネル/Skills/設定スキーマまたはバンドル capability を検出する
  • plugins.entries.<id>.config を検証する
  • Control UI のラベル/プレースホルダーを補強する
  • インストール/カタログメタデータを表示する
  • Plugin ランタイムをロードせずに、軽量な activation と setup 記述子を保持する
ネイティブ Plugin では、ランタイムモジュールがデータプレーン部分です。フック、ツール、コマンド、プロバイダーフローなどの実際の動作を登録します。 任意のマニフェスト activation ブロックと setup ブロックは制御プレーンに留まります。これらは activation 計画と setup 検出のためのメタデータ専用記述子です。ランタイム登録、register(...)setupEntry を置き換えるものではありません。ライブ activation の利用側は、マニフェストのコマンド、チャンネル、プロバイダーのヒントを使用して、より広いレジストリ具体化の前に Plugin ロードを絞り込みます。
  • CLI ロードは、要求されたプライマリコマンドを所有する Plugin に絞り込む
  • チャンネル setup/Plugin 解決は、要求されたチャンネル id を所有する Plugin に絞り込む
  • 明示的なプロバイダー setup/ランタイム解決は、要求されたプロバイダー id を所有する Plugin に絞り込む
  • Gateway 起動計画は、明示的な起動時インポートに activation.onStartup を使用する。起動メタデータのない Plugin は、より狭い activation トリガーを通じてのみロードされる
activation プランナーは、既存の呼び出し元向けの id のみの API と、診断向けの plan API の両方を公開します。plan エントリは Plugin が選択された理由を報告し、明示的な activation.* ヒントとマニフェスト所有権フォールバックを分離します。
理由(activation.* ヒント由来)理由(マニフェスト所有権由来)
activation-agent-harness-hint
activation-capability-hint
activation-channel-hintmanifest-channel-owner (channels)
activation-command-hintmanifest-command-alias (commandAliases)
activation-provider-hintmanifest-provider-owner (providers), manifest-setup-provider-owner (setup.providers)
activation-route-hint
—(フックトリガーにはヒントのバリアントがない)manifest-hook-owner (hooks), manifest-tool-contract (contracts.tools)
この理由の分割が互換性境界です。既存の Plugin メタデータは引き続き動作し、新しいコードはランタイムロードのセマンティクスを変更せずに、広いヒントやフォールバック動作を検出できます。 広い all スコープを要求するリクエスト時ランタイムプリロードでも、設定、起動計画、設定済みチャンネル、slots、自動有効化ルールから、明示的な有効 Plugin id セットを導出します(src/plugins/effective-plugin-ids.tsresolveEffectivePluginIds)。その導出されたセットが空の場合、OpenClaw は検出可能なすべての Plugin に広げるのではなく、スコープを空のままにします。 Setup 検出は、setup.providerssetup.cliBackends などの記述子所有 id を優先し、setup-api にフォールバックする前に候補 Plugin を絞り込みます。これは setup 時ランタイムフックをまだ必要とする Plugin 向けです。プロバイダー setup リストは、プロバイダーランタイムをロードせずに、マニフェストの providerAuthChoices、記述子から導出された setup choices、インストールカタログメタデータを使用します。明示的な setup.requiresRuntime: false は記述子のみの打ち切りです。requiresRuntime が省略されている場合は、互換性のためにレガシーの setup-api フォールバックを維持します。検出された複数の Plugin が同じ正規化済み setup プロバイダーまたは CLI backend id を主張する場合、setup lookup は検出順に依存せず、曖昧な所有者を拒否します。setup ランタイムが実行される場合、レジストリ診断は、setup.providers / setup.cliBackends と、setup-api によって実際に登録されたプロバイダーまたは CLI backend の不一致を報告しますが、レガシー Plugin はブロックしません。

Plugin キャッシュ境界

OpenClaw は、Plugin 検出結果や直接のマニフェストレジストリデータを、壁時計時間のウィンドウの背後にキャッシュしません。インストール、マニフェスト編集、ロードパス変更は、次の明示的なメタデータ読み取りまたはスナップショット再構築で見える必要があります。マニフェストファイルパーサーは、開かれたマニフェストパスに device/inode、size、mtime/ctime を加えたものをキーとする、有界のファイルシグネチャキャッシュを保持します。このキャッシュは変更されていないバイト列の再パースを避けるだけであり、検出、レジストリ、所有者、ポリシーの回答をキャッシュしてはなりません。 安全なメタデータ高速パスは、隠れたキャッシュではなく、明示的なオブジェクト所有権です。Gateway 起動のホットパスでは、現在の PluginMetadataSnapshot、導出された PluginLookUpTable、または明示的なマニフェストレジストリを呼び出しチェーンで渡すべきです。設定検証、起動時自動有効化、Plugin ブートストラップ、プロバイダー選択は、それらのオブジェクトが現在の設定と Plugin インベントリを表している間は再利用できます。Setup lookup は、特定の setup パスが明示的なマニフェストレジストリを受け取らない限り、依然として必要に応じてマニフェストメタデータを再構築します。隠れた lookup キャッシュを追加するのではなく、これをコールドパスフォールバックとして維持してください。入力が変わったら、スナップショットを変更したり履歴コピーを保持したりするのではなく、再構築して置き換えます。アクティブな Plugin レジストリ上のビューと、バンドル済みチャンネルブートストラップヘルパーは、現在のレジストリ/ルートから再計算すべきです。短命のマップは、1 回の呼び出し内で作業を重複排除したり再入を防いだりする用途なら問題ありません。ただし、プロセスメタデータキャッシュになってはなりません。 Plugin ロードにおいて、永続キャッシュ層はランタイムロードです。コードやインストール済みアーティファクトが実際にロードされる場合、次のようなローダー状態を再利用できます。
  • PluginLoaderCacheState と互換性のあるアクティブランタイムレジストリ
  • 同じランタイム面を繰り返しインポートするのを避けるために使われる jiti/module キャッシュと public-surface ローダーキャッシュ
  • インストール済み Plugin アーティファクト用のファイルシステムキャッシュ
  • パス正規化や重複解決用の短命な呼び出し単位マップ
これらのキャッシュはデータプレーンの実装詳細です。呼び出し元が意図的にランタイムロードを要求した場合を除き、「どの Plugin がこのプロバイダーを所有しているか」のような制御プレーンの質問に答えてはなりません。 次について、永続キャッシュや壁時計時間キャッシュを追加しないでください。
  • 検出結果
  • 直接のマニフェストレジストリ
  • インストール済み Plugin インデックスから再構築されたマニフェストレジストリ
  • プロバイダー所有者 lookup、モデル抑制、プロバイダーポリシー、public-artifact メタデータ
  • 変更されたマニフェスト、インストール済みインデックス、ロードパスが次のメタデータ読み取りで見えるべき、その他のマニフェスト由来の回答
永続化されたインストール済み Plugin インデックスからマニフェストメタデータを再構築する呼び出し元は、そのレジストリを必要に応じて再構築します。インストール済みインデックスは永続的な source-plane 状態であり、隠れたインプロセスメタデータキャッシュではありません。

レジストリモデル

ロード済み Plugin は、任意のコアグローバルを直接変更しません。中央の Plugin レジストリ(src/plugins/registry-types.tsPluginRegistry)に登録します。このレジストリは Plugin レコード(identity、source、origin、status、diagnostics)に加え、すべての capability 用の配列を追跡します。対象には、tools、レガシーフックと型付きフック、channels、providers、gateway RPC handlers、HTTP routes、CLI registrars、background services、Plugin 所有 commands、さらに多数の型付きプロバイダーファミリー(speech、embeddings、image/video/music generation、web fetch/search、agent harnesses、session actions など)が含まれます。 その後、コア機能は Plugin モジュールと直接やり取りするのではなく、そのレジストリから読み取ります。これによりロードは一方向に保たれます。
  • Plugin モジュール -> レジストリ登録
  • コアランタイム -> レジストリ消費
この分離は保守性にとって重要です。つまり、ほとんどのコア面で必要な統合点は 1 つだけです。「すべての Plugin モジュールを特別扱いする」ことではなく、「レジストリを読む」ことです。

会話バインディングコールバック

会話をバインドする Plugin は、承認が解決されたときに反応できます。 api.onConversationBindingResolved(...) を使用すると、バインドリクエストが承認または拒否された後にコールバックを受け取れます。
export default {
  id: "my-plugin",
  register(api) {
    api.onConversationBindingResolved(async (event) => {
      if (event.status === "approved") {
        // A binding now exists for this plugin + conversation.
        console.log(event.binding?.conversationId);
        return;
      }

      // The request was denied; clear any local pending state.
      console.log(event.request.conversation.conversationId);
    });
  },
};
コールバック payload フィールド:
  • status: "approved" または "denied"
  • decision: "allow-once""allow-always"、または "deny"
  • binding: 承認されたリクエストの解決済みバインディング
  • request: 元のリクエスト概要、detach hint、sender id、会話メタデータ
このコールバックは通知専用です。会話をバインドできる対象は変更せず、コアの承認処理が完了した後に実行されます。

プロバイダーランタイムフック

プロバイダー Plugin には 3 つのレイヤーがあります。
  • 安価なランタイム前 lookup のためのマニフェストメタデータ: setup.providers[].envVars、非推奨の互換性 providerAuthEnvVarsproviderAuthAliasesproviderAuthChoiceschannelEnvVars
  • 設定時フック: catalog(レガシー discovery)と applyConfigDefaults
  • ランタイムフック: 認証、モデル解決、ストリームラップ、thinking levels、replay policy、使用量エンドポイントをカバーする 40 個以上の任意フック。フックの順序と使い方を参照してください。
OpenClaw は引き続き、汎用 agent loop、failover、transcript handling、tool policy を所有します。これらのフックは、完全なカスタム inference transport を必要とせずに、プロバイダー固有の動作を拡張するための面です。 プロバイダーが環境変数ベースの認証情報を持ち、汎用の認証/状態/モデル選択パスが Plugin ランタイムを読み込まずに参照できるようにする必要がある場合は、マニフェストの setup.providers[].envVars を使用します。非推奨の providerAuthEnvVars は、非推奨期間中は互換性アダプターによって引き続き読み取られ、それを使用する非バンドル Plugin はマニフェスト診断を受け取ります。あるプロバイダー ID が別のプロバイダー ID の環境変数、認証プロファイル、設定ベースの認証、API キーのオンボーディング選択を再利用する必要がある場合は、マニフェストの providerAuthAliases を使用します。オンボーディング/認証選択 CLI サーフェスが、プロバイダーランタイムを読み込まずにプロバイダーの選択 ID、グループラベル、単純な単一フラグ認証の配線を知る必要がある場合は、マニフェストの providerAuthChoices を使用します。オンボーディングラベルや OAuth クライアント ID/クライアントシークレット設定変数など、運用者向けのヒントには、プロバイダーランタイムの envVars を維持します。 チャネルに環境変数駆動の認証またはセットアップがあり、汎用のシェル環境変数フォールバック、設定/状態チェック、またはセットアッププロンプトがチャネルランタイムを読み込まずに参照できるようにする必要がある場合は、マニフェストの channelEnvVars を使用します。

フックの順序と使い方

モデル/プロバイダー Plugin では、OpenClaw は概ね次の順序でフックを呼び出します。 「使用する場合」列は、素早く判断するためのガイドです。 ProviderPlugin.capabilitiessuppressBuiltInModel など、OpenClaw が現在は呼び出さない互換性専用のプロバイダーフィールドは、意図的にここには記載していません。
フックその役割使用する場面
catalogmodels.json 生成中にプロバイダー設定を models.providers へ公開するプロバイダーがカタログまたはベース URL のデフォルトを所有する場合
applyConfigDefaults設定の具体化中に、プロバイダー所有のグローバル設定デフォルトを適用するデフォルトが認証モード、env、またはプロバイダーモデルファミリーのセマンティクスに依存する場合
(組み込みモデル検索)OpenClaw はまず通常のレジストリ/カタログパスを試す(Plugin フックではない)
normalizeModelId検索前にレガシーまたはプレビューのモデル ID エイリアスを正規化するプロバイダーが正規モデル解決前のエイリアスクリーンアップを所有する場合
normalizeTransport汎用モデル組み立て前に、プロバイダーファミリーの api / baseUrl を正規化するプロバイダーが同じトランスポートファミリー内のカスタムプロバイダー ID 向けトランスポートクリーンアップを所有する場合
normalizeConfigランタイム/プロバイダー解決前に models.providers.<id> を正規化するプロバイダーに、Plugin と一緒に置くべき設定クリーンアップが必要な場合。バンドルされた Google ファミリーのヘルパーも、サポート対象の Google 設定エントリを補完する
applyNativeStreamingUsageCompat設定プロバイダーにネイティブストリーミング使用量互換の書き換えを適用するプロバイダーに、エンドポイント駆動のネイティブストリーミング使用量メタデータ修正が必要な場合
resolveConfigApiKeyランタイム認証の読み込み前に、設定プロバイダー向け env マーカー認証を解決するプロバイダーが独自の env マーカー API キー解決フックを公開する場合
resolveSyntheticAuth平文を永続化せずに、ローカル/セルフホストまたは設定ベースの認証を表面化するプロバイダーが合成/ローカル資格情報マーカーで動作できる場合
resolveExternalAuthProfilesプロバイダー所有の外部認証プロファイルを重ねる。CLI/アプリ所有の資格情報では、デフォルトの persistenceruntime-onlyプロバイダーが、コピーされたリフレッシュトークンを永続化せずに外部認証資格情報を再利用する場合。マニフェストで contracts.externalAuthProviders を宣言する
shouldDeferSyntheticProfileAuth保存済み合成プロファイルプレースホルダーを env/設定ベース認証の背後に下げるプロバイダーが、優先順位で勝つべきではない合成プレースホルダープロファイルを保存する場合
resolveDynamicModelローカルレジストリにまだない、プロバイダー所有モデル ID の同期フォールバックプロバイダーが任意の上流モデル ID を受け入れる場合
prepareDynamicModel非同期ウォームアップを行い、その後 resolveDynamicModel が再度実行される不明な ID を解決する前に、プロバイダーがネットワークメタデータを必要とする場合
normalizeResolvedModel埋め込みランナーが解決済みモデルを使用する前の最終書き換えプロバイダーがトランスポート書き換えを必要とするが、引き続きコアトランスポートを使用する場合
normalizeToolSchemas埋め込みランナーが参照する前にツールスキーマを正規化するプロバイダーがトランスポートファミリーのスキーマクリーンアップを必要とする場合
inspectToolSchemas正規化後に、プロバイダー所有のスキーマ診断を表面化するコアにプロバイダー固有ルールを教えずに、プロバイダーがキーワード警告を出したい場合
resolveReasoningOutputModeネイティブまたはタグ付き reasoning-output contract を選択するネイティブフィールドではなく、タグ付き reasoning/最終出力がプロバイダーに必要な場合
prepareExtraParams汎用ストリームオプションラッパー前にリクエストパラメーターを正規化するプロバイダーがデフォルトのリクエストパラメーターまたはプロバイダーごとのパラメータークリーンアップを必要とする場合
createStreamFn通常のストリームパスをカスタムトランスポートで完全に置き換えるプロバイダーが単なるラッパーではなく、カスタムのワイヤプロトコルを必要とする場合
wrapStreamFn汎用ラッパー適用後のストリームラッパープロバイダーがカスタムトランスポートなしで、リクエストヘッダー/ボディ/モデル互換ラッパーを必要とする場合
resolveTransportTurnStateネイティブのターンごとのトランスポートヘッダーまたはメタデータを付与する汎用トランスポートにプロバイダーネイティブなターン ID を送信させたい場合
resolveWebSocketSessionPolicyネイティブ WebSocket ヘッダーまたはセッションクールダウンポリシーを付与する汎用 WS トランスポートのセッションヘッダーまたはフォールバックポリシーを、プロバイダーが調整したい場合
formatApiKey認証プロファイルフォーマッター: 保存済みプロファイルがランタイムの apiKey 文字列になるプロバイダーが追加の認証メタデータを保存し、カスタムランタイムトークン形状を必要とする場合
refreshOAuthカスタムリフレッシュエンドポイントまたはリフレッシュ失敗ポリシー向けの OAuth リフレッシュ上書きプロバイダーが共有 OpenClaw リフレッシャーに適合しない場合
buildAuthDoctorHintOAuth リフレッシュ失敗時に追加される修復ヒントリフレッシュ失敗後に、プロバイダー所有の認証修復ガイダンスが必要な場合
matchesContextOverflowErrorプロバイダー所有のコンテキストウィンドウオーバーフローマッチャープロバイダーに、汎用ヒューリスティックでは見逃す生のオーバーフローエラーがある場合
classifyFailoverReasonプロバイダー所有のフェイルオーバー理由分類プロバイダーが生の API/トランスポートエラーをレート制限/過負荷などに対応付けられる場合
isCacheTtlEligibleプロキシ/バックホールプロバイダー向けプロンプトキャッシュポリシープロバイダーがプロキシ固有のキャッシュ TTL ゲーティングを必要とする場合
buildMissingAuthMessage汎用の認証欠落リカバリーメッセージの置き換えプロバイダー固有の認証欠落リカバリーヒントが必要な場合
augmentModelCatalog検出後に追加される合成/最終カタログ行 (非推奨。以下を参照)プロバイダーが models list とピッカーに、合成の前方互換行を必要とする場合
resolveThinkingProfileモデル固有の /think レベルセット、表示ラベル、デフォルトプロバイダーが、選択されたモデル向けにカスタム思考ラダーまたはバイナリラベルを公開する場合
isBinaryThinkingオン/オフ reasoning トグル互換フックプロバイダーがバイナリの思考オン/オフのみを公開する場合
supportsXHighThinkingxhigh reasoning サポート互換フックプロバイダーがモデルの一部でのみ xhigh を有効にしたい場合
resolveDefaultThinkingLevelデフォルト /think レベル互換フックプロバイダーがモデルファミリーのデフォルト /think ポリシーを所有する場合
isModernModelRefライブプロファイルフィルターとスモーク選択向けのモダンモデルマッチャープロバイダーがライブ/スモークの優先モデルマッチングを所有する場合
prepareRuntimeAuth推論直前に、設定済み資格情報を実際のランタイムトークン/キーへ交換するプロバイダーがトークン交換または短命のリクエスト資格情報を必要とする場合
resolveUsageAuth/usage と関連ステータスサーフェス向けの使用量/課金資格情報を解決するプロバイダーがカスタム使用量/クォータトークン解析または別の使用量資格情報を必要とする場合
fetchUsageSnapshot認証解決後に、プロバイダー固有の使用量/クォータスナップショットを取得して正規化するプロバイダーがプロバイダー固有の使用量エンドポイントまたはペイロードパーサーを必要とする場合
createEmbeddingProviderメモリ/検索用のプロバイダー所有の埋め込みアダプターを構築するメモリ埋め込みの動作はプロバイダー Plugin に属する
buildReplayPolicyプロバイダーのトランスクリプト処理を制御するリプレイポリシーを返すプロバイダーにはカスタムのトランスクリプトポリシーが必要(たとえば、thinking ブロックの除去)
sanitizeReplayHistory汎用トランスクリプトクリーンアップ後にリプレイ履歴を書き換えるプロバイダーには共有 Compaction ヘルパーを超えるプロバイダー固有のリプレイ書き換えが必要
validateReplayTurns埋め込みランナーの前に最終的なリプレイターン検証または整形を行うプロバイダートランスポートには汎用サニタイズ後のより厳格なターン検証が必要
onModelSelectedプロバイダー所有の選択後副作用を実行するモデルがアクティブになったとき、プロバイダーにはテレメトリまたはプロバイダー所有の状態が必要
normalizeModelIdnormalizeTransportnormalizeConfig はまず一致したプロバイダーPluginを確認し、その後、モデル ID またはトランスポート/設定を実際に変更するものが見つかるまで、フック対応の他のプロバイダーPluginにフォールスルーします。これにより、どのバンドルPluginが書き換えを所有しているかを呼び出し元が知る必要なく、エイリアス/互換プロバイダーのシムを機能させられます。対応している Google ファミリー設定エントリを書き換えるプロバイダーフックがない場合でも、バンドルされた Google 設定ノーマライザーがその互換性クリーンアップを適用します。 プロバイダーが完全にカスタムのワイヤプロトコルやカスタムリクエスト実行機構を必要とする場合、それは別種の拡張です。これらのフックは、OpenClaw の通常の推論ループ上で引き続き動作するプロバイダー挙動のためのものです。 resolveUsageAuth は、OpenClaw が fetchUsageSnapshot を呼び出すべきか、それとも使用量/ステータス表示向けに汎用の資格情報解決へフォールバックすべきかを決定します。プロバイダーに使用量用の資格情報がある場合は { token, accountId? } を返し、プロバイダー所有の使用量認証がリクエストを処理済みで、汎用の API キー/OAuth フォールバックを抑止する必要がある場合は { handled: true } を返し、プロバイダーが使用量認証を処理しなかった場合は null または undefined を返します。 組織または課金用の資格情報は、マニフェストの providerUsageAuthEnvVars で宣言します。これにより、汎用の検出やシークレットスクラブ処理の表示面が、それらを推論認証候補にすることなく認識できます。

プロバイダー例

api.registerProvider({
  id: "example-proxy",
  label: "Example Proxy",
  auth: [],
  catalog: {
    order: "simple",
    run: async (ctx) => {
      const apiKey = ctx.resolveProviderApiKey("example-proxy").apiKey;
      if (!apiKey) {
        return null;
      }
      return {
        provider: {
          baseUrl: "https://proxy.example.com/v1",
          apiKey,
          api: "openai-completions",
          models: [{ id: "auto", name: "Auto" }],
        },
      };
    },
  },
  resolveDynamicModel: (ctx) => ({
    id: ctx.modelId,
    name: ctx.modelId,
    provider: "example-proxy",
    api: "openai-completions",
    baseUrl: "https://proxy.example.com/v1",
    reasoning: false,
    input: ["text"],
    cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
    contextWindow: 128000,
    maxTokens: 8192,
  }),
  prepareRuntimeAuth: async (ctx) => {
    const exchanged = await exchangeToken(ctx.apiKey);
    return {
      apiKey: exchanged.token,
      baseUrl: exchanged.baseUrl,
      expiresAt: exchanged.expiresAt,
    };
  },
  resolveUsageAuth: async (ctx) => {
    const auth = await ctx.resolveOAuthToken();
    return auth ? { token: auth.token } : null;
  },
  fetchUsageSnapshot: async (ctx) => {
    return await fetchExampleProxyUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn);
  },
});

組み込み例

バンドルされたプロバイダーPluginは、各ベンダーのカタログ、認証、thinking、リプレイ、使用量の要件に合わせて、上記のフックを組み合わせます。信頼できるフックセットは extensions/ 配下の各Pluginにあります。このページは一覧をミラーするのではなく、形を示すものです。
OpenRouter、Kilocode、Z.AI、xAI は catalogresolveDynamicModel / prepareDynamicModel を登録し、OpenClaw の静的カタログに先行して上流のモデル ID を表示できるようにします。
GitHub Copilot、Gemini CLI、ChatGPT Codex、MiniMax、Xiaomi、z.ai は prepareRuntimeAuth または formatApiKeyresolveUsageAuth + fetchUsageSnapshot を組み合わせ、トークン交換と /usage 連携を所有します。
共有の名前付きファミリー(google-geminipassthrough-geminianthropic-by-modelhybrid-anthropic-openai)により、各Pluginがクリーンアップを再実装する代わりに、プロバイダーは buildReplayPolicy を通じてトランスクリプトポリシーへオプトインできます。
bytepluscloudflare-ai-gatewayhuggingfacekimi-codingnvidiaqianfansynthetictogethervenicevercel-ai-gatewayvolcenginecatalog だけを登録し、共有推論ループ上で動作します。
ベータヘッダー、/fast / serviceTiercontext1m は、汎用 SDK ではなく Anthropic Plugin の公開 api.ts / contract-api.ts 境界(wrapAnthropicProviderStreamresolveAnthropicBetasresolveAnthropicFastModeresolveAnthropicServiceTier)内にあります。

ランタイムヘルパー

Pluginは api.runtime を介して選択されたコアヘルパーにアクセスできます。TTS の場合:
const clip = await api.runtime.tts.textToSpeech({
  text: "Hello from OpenClaw",
  cfg: api.config,
});

const result = await api.runtime.tts.textToSpeechTelephony({
  text: "Hello from OpenClaw",
  cfg: api.config,
});

const voices = await api.runtime.tts.listVoices({
  provider: "elevenlabs",
  cfg: api.config,
});
注:
  • textToSpeech は、ファイル/ボイスメモ表示面向けの通常のコア TTS 出力ペイロードを返します。
  • コアの messages.tts 設定とプロバイダー選択を使用します。
  • PCM 音声バッファ + サンプルレートを返します。Pluginはプロバイダー向けにリサンプリング/エンコードする必要があります。
  • listVoices はプロバイダーごとに任意です。ベンダー所有の音声ピッカーやセットアップフローで使用します。
  • 音声一覧には、ロケール、性別、性格タグなど、プロバイダー対応ピッカー向けのより豊富なメタデータを含められます。
  • OpenAI と ElevenLabs は現在テレフォニーに対応しています。Microsoft は対応していません。
Pluginは api.registerSpeechProvider(...) を介して音声プロバイダーを登録することもできます。
api.registerSpeechProvider({
  id: "acme-speech",
  label: "Acme Speech",
  isConfigured: ({ config }) => Boolean(config.messages?.tts),
  synthesize: async (req) => {
    return {
      audioBuffer: Buffer.from([]),
      outputFormat: "mp3",
      fileExtension: ".mp3",
      voiceCompatible: false,
    };
  },
});
注:
  • TTS ポリシー、フォールバック、返信配信はコアに保持します。
  • ベンダー所有の合成挙動には音声プロバイダーを使用します。
  • レガシー Microsoft edge 入力は microsoft プロバイダー ID に正規化されます。
  • 推奨される所有モデルは企業指向です。OpenClaw がそれらの機能契約を追加するにつれ、1 つのベンダーPluginがテキスト、音声、画像、将来のメディアプロバイダーを所有できます。
画像/音声/動画理解については、Pluginは汎用のキー/値バッグではなく、型付きのメディア理解プロバイダーを 1 つ登録します。
api.registerMediaUnderstandingProvider({
  id: "google",
  capabilities: ["image", "audio", "video"],
  describeImage: async (req) => ({ text: "..." }),
  transcribeAudio: async (req) => ({ text: "..." }),
  describeVideo: async (req) => ({ text: "..." }),
});
注:
  • オーケストレーション、フォールバック、設定、チャネル配線はコアに保持します。
  • ベンダー挙動はプロバイダーPluginに保持します。
  • 追加的な拡張は型付きのままにします。新しい任意メソッド、新しい任意結果フィールド、新しい任意機能を使用します。
  • 動画生成もすでに同じパターンに従っています:
    • コアが機能契約とランタイムヘルパーを所有する
    • ベンダーPluginが api.registerVideoGenerationProvider(...) を登録する
    • 機能/チャネルPluginが api.runtime.videoGeneration.* を使用する
メディア理解ランタイムヘルパーについては、Pluginは次を呼び出せます。
const image = await api.runtime.mediaUnderstanding.describeImageFile({
  filePath: "/tmp/inbound-photo.jpg",
  cfg: api.config,
  agentDir: "/tmp/agent",
});

const video = await api.runtime.mediaUnderstanding.describeVideoFile({
  filePath: "/tmp/inbound-video.mp4",
  cfg: api.config,
});

const extraction = await api.runtime.mediaUnderstanding.extractStructuredWithModel({
  provider: "codex",
  model: "gpt-5.5",
  input: [
    {
      type: "image",
      buffer: receiptImageBuffer,
      fileName: "receipt.png",
      mime: "image/png",
    },
    { type: "text", text: "Use the printed fields as the source of truth." },
  ],
  instructions: "Return entities and searchable tags.",
  schemaName: "example.evidence",
  jsonSchema: {
    type: "object",
    properties: {
      entities: { type: "array", items: { type: "string" } },
      tags: { type: "array", items: { type: "string" } },
    },
  },
  cfg: api.config,
});
音声文字起こしについては、Pluginはメディア理解ランタイムまたは古い STT エイリアスのどちらかを使用できます。
const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({
  filePath: "/tmp/inbound-audio.ogg",
  cfg: api.config,
  // Optional when MIME cannot be inferred reliably:
  mime: "audio/ogg",
});
注:
  • api.runtime.mediaUnderstanding.* は、画像/音声/動画理解向けの推奨共有表示面です。
  • extractStructuredWithModel(...) は、境界付けられたプロバイダー所有の画像優先抽出に対するPlugin向け境界です。少なくとも 1 つの画像入力を含めます。テキスト入力は補足コンテキストです。プロダクトPluginがそれぞれのルートとスキーマを所有し、OpenClaw がプロバイダー/ランタイム境界を所有します。
  • コアのメディア理解音声設定(tools.media.audio)とプロバイダーフォールバック順序を使用します。
  • 文字起こし出力が生成されない場合(たとえばスキップ/非対応入力)は { text: undefined } を返します。
  • api.runtime.stt.transcribeAudioFile(...) は互換エイリアスとして残ります。
Pluginは api.runtime.subagent を通じてバックグラウンドのサブエージェント実行を起動することもできます。
const result = await api.runtime.subagent.run({
  sessionKey: "agent:main:subagent:search-helper",
  message: "Expand this query into focused follow-up searches.",
  provider: "openai",
  model: "gpt-4.1-mini",
  deliver: false,
});
注:
  • providermodel は実行ごとの任意オーバーライドであり、永続的なセッション変更ではありません。
  • OpenClaw は信頼済み呼び出し元に対してのみ、それらのオーバーライドフィールドを尊重します。
  • Plugin所有のフォールバック実行については、オペレーターが plugins.entries.<id>.subagent.allowModelOverride: true でオプトインする必要があります。
  • plugins.entries.<id>.subagent.allowedModels を使用して、信頼済みPluginを特定の正規 provider/model ターゲットに制限します。または、任意のターゲットを明示的に許可するには "*" を使用します。
  • 信頼されていないPluginのサブエージェント実行も動作しますが、オーバーライド要求はサイレントにフォールバックするのではなく拒否されます。
  • Pluginが作成したサブエージェントセッションには、作成元Plugin ID がタグ付けされます。フォールバックの api.runtime.subagent.deleteSession(...) は、それらの所有セッションのみを削除できます。任意のセッション削除には、引き続き管理者スコープの Gateway リクエストが必要です。
Web 検索については、Pluginはエージェントツール配線へ踏み込む代わりに、共有ランタイムヘルパーを使用できます。
const providers = api.runtime.webSearch.listProviders({
  config: api.config,
});

const result = await api.runtime.webSearch.search({
  config: api.config,
  args: {
    query: "OpenClaw plugin runtime helpers",
    count: 5,
  },
});
Pluginは api.registerWebSearchProvider(...) を介して Web 検索プロバイダーを登録することもできます。 注:
  • プロバイダー選択、資格情報解決、共有リクエストセマンティクスはコアに保持します。
  • ベンダー固有の検索トランスポートには Web 検索プロバイダーを使用します。
  • api.runtime.webSearch.* は、エージェントツールラッパーに依存せずに検索挙動を必要とする機能/チャネルPlugin向けの推奨共有表示面です。

api.runtime.imageGeneration

const result = await api.runtime.imageGeneration.generate({
  config: api.config,
  args: { prompt: "A friendly lobster mascot", size: "1024x1024" },
});

const providers = api.runtime.imageGeneration.listProviders({
  config: api.config,
});
  • generate(...): 設定された画像生成プロバイダーチェーンを使用して画像を生成します。
  • listProviders(...): 利用可能な画像生成プロバイダーとその機能を一覧表示します。

Gateway HTTP ルート

Pluginは api.registerHttpRoute(...) で HTTP エンドポイントを公開できます。
api.registerHttpRoute({
  path: "/acme/webhook",
  auth: "plugin",
  match: "exact",
  handler: async (_req, res) => {
    res.statusCode = 200;
    res.end("ok");
    return true;
  },
});
ルートフィールド:
  • path: Gateway HTTP サーバー配下のルートパス。
  • auth: 必須、"gateway" または "plugin"。通常の Gateway 認証を要求するには "gateway" を使用し、Plugin 管理の認証/Webhook 検証には "plugin" を使用します。
  • match: 任意。"exact"(デフォルト)または "prefix"
  • handleUpgrade: 同じルート上の WebSocket アップグレードリクエスト用の任意ハンドラー。
  • replaceExisting: 任意。同じ Plugin が自身の既存ルート登録を置き換えることを許可します。
  • handler: ルートがリクエストを処理した場合に true を返します。
注記:
  • api.registerHttpHandler(...) は削除され、Plugin 読み込みエラーの原因になります。代わりに api.registerHttpRoute(...) を使用してください。
  • Plugin ルートは auth を明示的に宣言する必要があります。
  • 完全一致する path + match の競合は、replaceExisting: true でない限り拒否されます。また、ある Plugin が別の Plugin のルートを置き換えることはできません。
  • 異なる auth レベルで重複するルートは拒否されます。exact/prefix のフォールスルーチェーンは、同じ認証レベル内のみにしてください。
  • auth: "plugin" ルートは、オペレーターランタイムスコープを自動的には受け取りません。これは Plugin 管理の Webhook/署名検証用であり、特権的な Gateway ヘルパー呼び出し用ではありません。
  • auth: "gateway" ルートは、Gateway リクエストランタイムスコープ内で実行されます。デフォルトのサーフェス(gatewayRuntimeScopeSurface: "write-default")は意図的に保守的です:
    • 共有シークレットの bearer 認証(gateway.auth.mode = "token" / "password")および信頼済みプロキシ以外の認証方式は、呼び出し元が x-openclaw-scopes を送信しても、単一の operator.write スコープを取得します
    • 明示的な x-openclaw-scopes ヘッダーのない trusted-proxy 呼び出し元も、従来の operator.write のみのサーフェスを維持します
    • x-openclaw-scopes を送信する trusted-proxy 呼び出し元は、代わりに宣言されたスコープを取得します
    • ルートは gatewayRuntimeScopeSurface: "trusted-operator" にオプトインすることで、ID を伴う認証モードでは常に x-openclaw-scopes を尊重できます(ヘッダーがない場合は完全な CLI デフォルトスコープセットにフォールバックします)
  • 実用上のルール: Gateway 認証の Plugin ルートを暗黙の管理者サーフェスと見なさないでください。ルートに管理者専用の動作が必要な場合は、trusted-operator スコープサーフェスにオプトインし、ID を伴う認証モードを要求し、明示的な x-openclaw-scopes ヘッダー契約を文書化してください。

Plugin SDK インポートパス

新しい Plugin を作成するときは、モノリシックな openclaw/plugin-sdk ルート barrel ではなく、狭い SDK サブパスを使用してください。コアサブパス:
サブパス目的
openclaw/plugin-sdk/plugin-entryPlugin 登録プリミティブ
openclaw/plugin-sdk/channel-coreチャネルエントリー/ビルドヘルパー
openclaw/plugin-sdk/core汎用共有ヘルパーと包括契約
openclaw/plugin-sdk/config-schemaルート openclaw.json Zod スキーマ(OpenClawSchema
チャネル Plugin は、狭い接点のファミリーから選択します — channel-setup, setup-runtime, setup-tools, channel-pairing, channel-contract, channel-feedback, channel-inbound, channel-outbound, command-auth, secret-input, webhook-ingress, channel-targets, channel-actions。承認動作は、無関係な Plugin フィールドをまたいで混在させるのではなく、1 つの approvalCapability 契約に統合するべきです。チャネル Plugin を参照してください。 ランタイムおよび設定ヘルパーは、対応する焦点を絞った *-runtime サブパス (approval-runtime, agent-runtime, lazy-runtime, directory-runtime, text-runtime, runtime-store, system-event-runtime, heartbeat-runtime, channel-activity-runtime など)配下にあります。広範な config-runtime 互換 barrel ではなく、config-contracts, plugin-config-runtime, runtime-config-snapshot, config-mutation を優先してください。
openclaw/plugin-sdk/channel-runtime, openclaw/plugin-sdk/channel-lifecycle, 小さなチャネルヘルパー facade、openclaw/plugin-sdk/outbound-runtime, openclaw/plugin-sdk/outbound-send-deps, openclaw/plugin-sdk/config-runtime, openclaw/plugin-sdk/infra-runtime は、古い Plugin 向けの非推奨互換 shim です。新しいコードでは、代わりにより狭い汎用プリミティブをインポートしてください。
リポジトリ内部のエントリーポイント(バンドル済み Plugin パッケージルートごと):
  • index.js — バンドル済み Plugin エントリー
  • api.js — ヘルパー/型 barrel
  • runtime-api.js — ランタイム専用 barrel
  • setup-entry.js — セットアップ Plugin エントリー
外部 Plugin は openclaw/plugin-sdk/* サブパスのみをインポートするべきです。コアまたは別の Plugin から、別の Plugin パッケージの src/* をインポートしてはいけません。 facade 読み込みされたエントリーポイントは、存在する場合はアクティブなランタイム設定スナップショットを優先し、その後ディスク上の解決済み設定ファイルにフォールバックします。 image-generationmedia-understandingspeech などの機能固有サブパスは、現在バンドル済み Plugin が使用しているため存在します。これらは自動的に長期固定の外部契約になるわけではありません — 依存する場合は関連する SDK リファレンスページを確認してください。

メッセージツールスキーマ

Plugin は、リアクション、既読、投票などの非メッセージプリミティブについて、チャネル固有の describeMessageTool(...) スキーマ コントリビューションを所有するべきです。 共有送信プレゼンテーションでは、プロバイダー固有のボタン、コンポーネント、ブロック、カードフィールドではなく、汎用 MessagePresentation 契約を使用するべきです。 契約、フォールバックルール、プロバイダーマッピング、Plugin 作者チェックリストについては、メッセージプレゼンテーション を参照してください。 送信可能な Plugin は、メッセージ機能を通じてレンダリング可能なものを宣言します:
  • セマンティックなプレゼンテーションブロック(text, context, divider, buttons, select)用の presentation
  • ピン留め配信リクエスト用の delivery-pin
コアは、プレゼンテーションをネイティブにレンダリングするか、テキストに劣化させるかを決定します。 汎用メッセージツールからプロバイダー固有 UI の抜け道を公開しないでください。 レガシーなネイティブスキーマ用の非推奨 SDK ヘルパーは既存のサードパーティ Plugin 向けにエクスポートされたままですが、新しい Plugin はそれらを使用するべきではありません。

チャネルターゲット解決

チャネル Plugin は、チャネル固有のターゲットセマンティクスを所有するべきです。共有アウトバウンドホストは汎用のままにし、プロバイダールールにはメッセージングアダプターサーフェスを使用してください:
  • messaging.inferTargetChatType({ to }) は、ディレクトリ検索の前に、正規化されたターゲットを directgroupchannel のどれとして扱うべきかを決定します。
  • messaging.targetResolver.looksLikeId(raw, normalized) は、入力がディレクトリ検索ではなく ID らしい解決へ直接進むべきかどうかをコアに伝えます。
  • messaging.targetResolver.reservedLiterals は、そのプロバイダーのチャネル/セッション参照である裸の単語を列挙します。解決では、予約リテラルを拒否する前に設定済みディレクトリエントリーを保持し、その後ディレクトリミスでは fail closed します。
  • messaging.targetResolver.resolveTarget(...) は、正規化後またはディレクトリミス後に、コアが最終的なプロバイダー所有の解決を必要とする場合の Plugin フォールバックです。
  • messaging.resolveOutboundSessionRoute(...) は、ターゲット解決後のプロバイダー固有セッションルート構築を所有します。
推奨される分割:
  • ピア/グループを検索する前に行うべきカテゴリ判断には inferTargetChatType を使用します。
  • 「これを明示的/ネイティブターゲット ID として扱う」チェックには looksLikeId を使用します。
  • 広範なディレクトリ検索ではなく、プロバイダー固有の正規化フォールバックには resolveTarget を使用します。
  • チャット ID、スレッド ID、JID、ハンドル、ルーム ID などのプロバイダーネイティブ ID は、汎用 SDK フィールドではなく、target 値またはプロバイダー固有 params 内に保持してください。

設定に基づくディレクトリ

設定からディレクトリエントリーを派生する Plugin は、そのロジックを Plugin 内に保持し、openclaw/plugin-sdk/directory-runtime の共有ヘルパーを再利用するべきです。 チャネルが次のような設定に基づくピア/グループを必要とする場合に使用します:
  • allowlist 駆動の DM ピア
  • 設定済みチャネル/グループマップ
  • アカウントスコープの静的ディレクトリフォールバック
directory-runtime の共有ヘルパーは、汎用操作のみを処理します:
  • クエリフィルタリング
  • 制限の適用
  • 重複排除/正規化ヘルパー
  • ChannelDirectoryEntry[] の構築
チャネル固有のアカウント検査と ID 正規化は、Plugin 実装内に留めるべきです。

プロバイダーカタログ

プロバイダー Plugin は、registerProvider({ catalog: { run(...) { ... } } }) で推論用のモデルカタログを定義できます。 catalog.run(...) は、OpenClaw が models.providers に書き込むものと同じ形を返します:
  • 1 つのプロバイダーエントリー用の { provider }
  • 複数のプロバイダーエントリー用の { providers }
Plugin がプロバイダー固有のモデル ID、ベース URL デフォルト、または認証で保護されたモデルメタデータを所有する場合は catalog を使用します。 catalog.order は、Plugin のカタログが OpenClaw の組み込み暗黙プロバイダーに対していつマージされるかを制御します:
  • simple: プレーンな API キーまたは env 駆動のプロバイダー
  • profile: 認証プロファイルが存在すると現れるプロバイダー
  • paired: 複数の関連プロバイダーエントリーを合成するプロバイダー
  • late: 他の暗黙プロバイダー後の最後のパス
キー衝突では後のプロバイダーが勝つため、Plugin は同じプロバイダー ID を持つ組み込みプロバイダーエントリーを意図的に上書きできます。 Plugin は、api.registerModelCatalogProvider({ provider, kinds, staticCatalog, liveCatalog }) を通じて読み取り専用モデル行を公開することもできます。これは list/help/picker サーフェス向けの今後の経路であり、textvoiceimage_generationvideo_generationmusic_generation 行をサポートします。プロバイダー Plugin は引き続きライブエンドポイント呼び出し、トークン交換、ベンダーレスポンスマッピングを所有し、コアは共通の行形状、ソースラベル、メディアツールヘルプ整形を所有します。メディア生成プロバイダー登録は、defaultModelmodelscapabilities から静的カタログ行を自動的に合成します。 互換性:
  • discovery はレガシーエイリアスとして引き続き動作しますが、非推奨警告を出します
  • catalogdiscovery の両方が登録されている場合、OpenClaw は catalog を使用し、警告を出します
  • augmentModelCatalog は非推奨です。バンドル済みプロバイダーは registerModelCatalogProvider を通じて補足行を公開するべきです

読み取り専用チャネル検査

Plugin がチャネルを登録する場合は、resolveAccount(...) と併せて plugin.config.inspectAccount(cfg, accountId) を実装することを優先してください。 理由:
  • resolveAccount(...) はランタイム経路です。資格情報が完全に具体化されていると仮定でき、必要なシークレットがない場合は即座に失敗できます。
  • openclaw statusopenclaw status --allopenclaw channels statusopenclaw channels resolve、doctor/設定 修復フローなどの読み取り専用コマンド経路は、設定を説明するだけのためにランタイム資格情報を具体化する必要があるべきではありません。
推奨される inspectAccount(...) の動作:
  • 説明的なアカウント状態のみを返します。
  • enabledconfigured を保持します。
  • 関連する場合は、次のような資格情報ソース/ステータスフィールドを含めます:
    • tokenSource, tokenStatus
    • botTokenSource, botTokenStatus
    • appTokenSource, appTokenStatus
    • signingSecretSource, signingSecretStatus
  • 読み取り専用の可用性を報告するだけなら、生のトークン値を返す必要はありません。ステータス系コマンドには、tokenStatus: "available"(および一致する source フィールド)を返せば十分です。
  • 資格情報が SecretRef 経由で設定されているものの、現在のコマンド経路で利用できない場合は configured_unavailable を使用します。
これにより、読み取り専用コマンドはクラッシュしたり、アカウントを未設定と誤報告したりする代わりに、「設定済みだがこのコマンド経路では利用不可」と報告できます。

パッケージパック

Plugin ディレクトリには、openclaw.extensions を含む package.json を含めることができます:
{
  "name": "my-pack",
  "openclaw": {
    "extensions": ["./src/safety.ts", "./src/tools.ts"],
    "setupEntry": "./src/setup-entry.ts"
  }
}
各エントリーは 1 つの Plugin になります。パックが複数の extensions を列挙している場合、Plugin id は <manifestOrPackageName>/<fileBase> になります(manifest id が存在する場合はそれが優先され、そうでなければスコープなしの package.json 名になります)。 Plugin が npm deps を import する場合は、そのディレクトリでインストールして node_modules を利用できるようにします(npm install / pnpm install)。 セキュリティガードレール: すべての openclaw.extensions エントリは、シンボリックリンク解決後も Plugin ディレクトリ内に留まる必要があります。パッケージディレクトリの外へ出るエントリは 拒否されます。 セキュリティ上の注意: openclaw plugins install は Plugin 依存関係を プロジェクトローカルの npm install --omit=dev --ignore-scripts でインストールします(ライフサイクルスクリプトなし、 実行時の dev 依存関係なし)。継承されたグローバル npm install 設定は無視されます。 Plugin の依存関係ツリーは「純粋な JS/TS」に保ち、 postinstall ビルドを必要とするパッケージは避けてください。 任意: openclaw.setupEntry は軽量なセットアップ専用モジュールを指すことができます。 OpenClaw が無効なチャンネル Plugin のセットアップサーフェスを必要とする場合、または チャンネル Plugin が有効でもまだ未設定の場合、完全な Plugin エントリではなく setupEntry を読み込みます。これにより、メインの Plugin エントリがツール、フック、その他の実行時専用コードも 結線している場合でも、起動とセットアップを軽量に保てます。 任意: openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen は、チャンネルがすでに設定済みの場合でも、Gateway の listen 前の起動フェーズ中にチャンネル Plugin を同じ setupEntry パスへオプトインできます。 これは、Gateway が listen を開始する前に存在している必要がある起動サーフェスを setupEntry が完全にカバーしている場合にのみ使用してください。実際には、セットアップエントリが 起動で依存するチャンネル所有のすべての capability を登録する必要があることを意味します。例:
  • チャンネル登録自体
  • Gateway が listen を開始する前に利用可能である必要がある HTTP ルート
  • 同じ期間中に存在している必要がある Gateway メソッド、ツール、またはサービス
完全なエントリが必要な起動 capability をまだ所有している場合は、このフラグを有効にしないでください。 Plugin はデフォルト動作のままにし、OpenClaw が起動中に 完全なエントリを読み込むようにしてください。 バンドル済みチャンネルは、完全なチャンネルランタイムが読み込まれる前に core が参照できる セットアップ専用のコントラクトサーフェスヘルパーも公開できます。現在のセットアップ 昇格サーフェスは次のとおりです。
  • singleAccountKeysToMove
  • namedAccountPromotionKeys
  • resolveSingleAccountPromotionTarget(...)
core は、完全な Plugin エントリを読み込まずにレガシーな単一アカウントのチャンネル 設定を channels.<id>.accounts.* に昇格する必要がある場合に、このサーフェスを使用します。 Matrix が現在のバンドル済みの例です。名前付きアカウントがすでに存在する場合は、auth/bootstrap キーのみを 名前付きの昇格済みアカウントへ移動し、常に accounts.default を作成するのではなく、設定済みの非正規 default-account キーを保持できます。 これらのセットアップパッチアダプターにより、バンドル済みコントラクトサーフェスの検出は遅延されたままになります。import 時は軽量に保たれます。昇格サーフェスは、モジュール import 時にバンドル済みチャンネル起動へ 再突入するのではなく、初回使用時にのみ読み込まれます。 これらの起動サーフェスに Gateway RPC メソッドが含まれる場合は、 Plugin 固有の prefix に置いてください。core 管理者名前空間(config.*, exec.approvals.*, wizard.*, update.*)は予約済みのままで、Plugin がより狭い scope を要求しても 常に operator.admin に解決されます。 例:
{
  "name": "@scope/my-channel",
  "openclaw": {
    "extensions": ["./index.ts"],
    "setupEntry": "./setup-entry.ts",
    "startup": {
      "deferConfiguredChannelFullLoadUntilAfterListen": true
    }
  }
}

チャンネルカタログメタデータ

チャンネル Plugin は openclaw.channel を通じてセットアップ/検出メタデータを、 openclaw.install を通じてインストールヒントを公開できます。これにより core カタログはデータを持たずに済みます。 例:
{
  "name": "@openclaw/nextcloud-talk",
  "openclaw": {
    "extensions": ["./index.ts"],
    "channel": {
      "id": "nextcloud-talk",
      "label": "Nextcloud Talk",
      "selectionLabel": "Nextcloud Talk (self-hosted)",
      "docsPath": "/channels/nextcloud-talk",
      "docsLabel": "nextcloud-talk",
      "blurb": "Self-hosted chat via Nextcloud Talk webhook bots.",
      "order": 65,
      "aliases": ["nc-talk", "nc"]
    },
    "install": {
      "npmSpec": "@openclaw/nextcloud-talk",
      "localPath": "<bundled-plugin-local-path>",
      "defaultChoice": "npm"
    }
  }
}
最小例以外で有用な openclaw.channel フィールド:
  • detailLabel: よりリッチなカタログ/ステータスサーフェス向けの副ラベル
  • docsLabel: docs リンクのリンクテキストを上書き
  • preferOver: このカタログエントリが上回るべき、より低優先度の Plugin/チャンネル id
  • selectionDocsPrefix, selectionDocsOmitLabel, selectionExtras: 選択サーフェスのコピー制御
  • markdownCapable: 送信フォーマット判断のために、チャンネルを markdown 対応としてマーク
  • exposure.configured: false に設定した場合、設定済みチャンネル一覧サーフェスからチャンネルを非表示
  • exposure.setup: false に設定した場合、対話型セットアップ/設定ピッカーからチャンネルを非表示
  • exposure.docs: docs ナビゲーションサーフェス向けにチャンネルを internal/private としてマーク
  • showConfigured / showInSetup: 互換性のためにまだ受け入れられるレガシーエイリアス。exposure を優先
  • quickstartAllowFrom: 標準クイックスタート allowFrom フローへチャンネルをオプトイン
  • forceAccountBinding: アカウントが 1 つしか存在しない場合でも、明示的なアカウント binding を要求
  • preferSessionLookupForAnnounceTarget: announce target の解決時に session lookup を優先
OpenClaw は 外部チャンネルカタログ(たとえば MPM registry export)もマージできます。JSON ファイルを次のいずれかに配置してください。
  • ~/.openclaw/mpm/plugins.json
  • ~/.openclaw/mpm/catalog.json
  • ~/.openclaw/plugins/catalog.json
または、OPENCLAW_PLUGIN_CATALOG_PATHS(または OPENCLAW_MPM_CATALOG_PATHS)で 1 つ以上の JSON ファイルを指します(カンマ/セミコロン/PATH 区切り)。各ファイルには { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] } を含める必要があります。parser は "entries" キーのレガシーエイリアスとして "packages" または "plugins" も受け入れます。 生成されたチャンネルカタログエントリと provider install catalog エントリは、 raw openclaw.install ブロックの横に正規化済み install-source facts を公開します。 正規化済み facts は、npm spec が exact version か floating selector か、期待される integrity メタデータが存在するか、ローカル source path も利用可能かを識別します。カタログ/パッケージ identity が判明している場合、 正規化済み facts は、解析された npm package name がその identity からずれていると警告します。 また、defaultChoice が無効な場合、または利用できない source を指す場合、 および有効な npm source なしで npm integrity メタデータが存在する場合にも警告します。 consumer は installSource を加算的な任意フィールドとして扱う必要があります。これにより、 手作業で作られたエントリや catalog shim がそれを合成する必要はありません。 これにより、オンボーディングと診断は Plugin runtime を import せずに source-plane state を説明できます。 公式の外部 npm エントリでは、exact npmSpecexpectedIntegrity を優先してください。bare package name と dist-tag は互換性のため引き続き動作しますが、 source-plane warning を表示するため、既存の Plugin を壊さずにカタログを pinned かつ integrity-checked な install へ移行できます。 オンボーディングがローカルカタログパスからインストールする場合、可能であれば source: "path" と workspace-relative な sourcePath を持つ managed plugin plugin index entry を記録します。絶対の operational load path は plugins.load.paths に残ります。install record は、ローカルワークステーション path を長寿命の config へ重複して入れることを避けます。これにより、local development install は source-plane 診断から見えるままにしつつ、2 つ目の raw filesystem-path disclosure surface を追加しません。永続化された installed_plugin_index SQLite テーブルは install source of truth であり、Plugin runtime module を読み込まずに refresh できます。 その installRecords map は、Plugin manifest が欠落または無効でも durable です。 その plugins payload は rebuild 可能な manifest view です。

コンテキストエンジン Plugin

コンテキストエンジン Plugin は、ingest、assembly、 compaction のための session context orchestration を所有します。Plugin から api.registerContextEngine(id, factory) で登録し、active engine を plugins.slots.contextEngine で選択します。 Plugin が memory search や hook を追加するだけでなく、デフォルトの context pipeline を置き換える、または拡張する必要がある場合に使用してください。
import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";

export default function (api) {
  api.registerContextEngine("lossless-claw", (ctx) => ({
    info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true },
    async ingest() {
      return { ingested: true };
    },
    async assemble({ messages, availableTools, citationsMode }) {
      return {
        messages,
        estimatedTokens: 0,
        systemPromptAddition: buildMemorySystemPromptAddition({
          availableTools: availableTools ?? new Set(),
          citationsMode,
        }),
      };
    },
    async compact() {
      return { ok: true, compacted: false };
    },
  }));
}
factory ctx は、構築時の初期化向けに任意の configagentDirworkspaceDir 値を公開します。 active harness が persistent backend thread を持つ場合、assemble()contextProjection を返すことができます。レガシーな per-turn projection では省略してください。 assembled context を backend thread に一度だけ注入し、epoch が変わるまで再利用する必要がある場合は { mode: "thread_bootstrap", epoch } を返します。 engine 所有の compaction pass 後など、engine の semantic context が変化したら epoch を変更してください。host は thread-bootstrap projection 内で tool-call metadata、input shape、redacted tool results を保持できるため、新しい backend thread は raw secret-bearing payload をコピーせずに tool continuity を維持できます。 engine が compaction algorithm を所有していない場合は、compact() を 実装したまま明示的に委譲してください。
import {
  buildMemorySystemPromptAddition,
  delegateCompactionToRuntime,
} from "openclaw/plugin-sdk/core";

export default function (api) {
  api.registerContextEngine("my-memory-engine", (ctx) => ({
    info: {
      id: "my-memory-engine",
      name: "My Memory Engine",
      ownsCompaction: false,
    },
    async ingest() {
      return { ingested: true };
    },
    async assemble({ messages, availableTools, citationsMode }) {
      return {
        messages,
        estimatedTokens: 0,
        systemPromptAddition: buildMemorySystemPromptAddition({
          availableTools: availableTools ?? new Set(),
          citationsMode,
        }),
      };
    },
    async compact(params) {
      return await delegateCompactionToRuntime(params);
    },
  }));
}

新しい capability の追加

Plugin が現在の API に合わない挙動を必要とする場合、private な reach-in で Plugin system を迂回しないでください。不足している capability を追加してください。 推奨される手順:
  1. core contract を定義します。 core が所有すべき shared behavior を決めます: policy、fallback、config merge、lifecycle、channel-facing semantics、 runtime helper shape。
  2. typed plugin registration/runtime surface を追加します。 OpenClawPluginApiapi.runtime を、最小限で有用な typed capability surface で拡張します。
  3. core + channel/feature consumer を結線します。 チャンネルと feature Plugin は、 vendor 実装を直接 import するのではなく、core 経由で新しい capability を consume するべきです。
  4. vendor implementation を登録します。 その後、vendor Plugin が capability に対して backend を登録します。
  5. contract coverage を追加します。 ownership と registration shape が 時間が経っても明示的なままになるよう、テストを追加します。
これにより、OpenClaw は特定の provider の世界観へ hardcode されずに、opinionated なままでいられます。具体的なファイルチェックリストと実例については、Capability Cookbook を参照してください。

capability チェックリスト

新しい capability を追加する場合、実装は通常、これらの surface をまとめて触る必要があります。
  • src/<capability>/types.ts のコア契約型
  • src/<capability>/runtime.ts のコアランナー/ランタイムヘルパー
  • src/plugins/types.ts の Plugin API 登録サーフェス
  • src/plugins/registry.ts の Plugin レジストリ接続
  • 機能/チャネル Plugin が利用する必要がある場合の src/plugins/runtime/* における Plugin ランタイム公開
  • src/test-utils/plugin-registration.ts のキャプチャ/テストヘルパー
  • src/plugins/contracts/registry.ts の所有権/契約アサーション
  • docs/ のオペレーター/Plugin ドキュメント
これらのサーフェスのいずれかが欠けている場合、通常はそのケイパビリティが まだ完全には統合されていないことを示します。

ケイパビリティテンプレート

最小パターン:
// core contract
export type VideoGenerationProviderPlugin = {
  id: string;
  label: string;
  generateVideo: (req: VideoGenerationRequest) => Promise<VideoGenerationResult>;
};

// plugin API
api.registerVideoGenerationProvider({
  id: "openai",
  label: "OpenAI",
  async generateVideo(req) {
    return await generateOpenAiVideo(req);
  },
});

// shared runtime helper for feature/channel plugins
const clip = await api.runtime.videoGeneration.generate({
  prompt: "Show the robot walking through the lab.",
  cfg,
});
契約テストパターン(src/plugins/contracts/registry.tsproviderContractPluginIds などの所有権ルックアップを公開します。テストでは、 Plugin の contracts.videoGenerationProviders リストが実際に登録する内容と一致することをアサートします):
expect(pluginManifest.contracts?.videoGenerationProviders).toEqual(["openai"]);
これによりルールはシンプルに保たれます:
  • コアはケイパビリティ契約 + オーケストレーションを所有する
  • ベンダー Plugin はベンダー実装を所有する
  • 機能/チャネル Plugin はランタイムヘルパーを利用する
  • 契約テストは所有権を明示的に保つ

関連