メインコンテンツへスキップ

Documentation Index

Fetch the complete documentation index at: https://docs.openclaw.ai/llms.txt

Use this file to discover all available pages before exploring further.

チャネルターンカーネルは、正規化されたプラットフォームイベントをエージェントターンに変換する共有インバウンド状態マシンです。チャネルPluginはプラットフォームの事実情報と配信コールバックを提供します。コアはオーケストレーション、つまり取り込み、分類、プリフライト、解決、認可、組み立て、記録、ディスパッチ、完了処理を所有します。 Pluginがインバウンドメッセージのホットパス上にある場合にこれを使用してください。メッセージ以外のイベント(スラッシュコマンド、モーダル、ボタン操作、ライフサイクルイベント、リアクション、音声状態)は、Pluginローカルに保ってください。カーネルが所有するのは、エージェントのテキストターンになる可能性があるイベントだけです。
カーネルには、注入されたPluginランタイムを通じて runtime.channel.turn.* として到達します。Pluginランタイム型は openclaw/plugin-sdk/core からエクスポートされるため、サードパーティのネイティブPluginは、バンドルされたチャネルPluginと同じ方法でこれらのエントリーポイントを使用できます。

共有カーネルが必要な理由

チャネルPluginは同じインバウンドフローを繰り返します。正規化、ルーティング、ゲート、コンテキストの構築、セッションメタデータの記録、エージェントターンのディスパッチ、配信状態の完了処理です。共有カーネルがない場合、メンションゲート、ツール専用の可視返信、セッションメタデータ、保留履歴、ディスパッチ完了処理への変更をチャネルごとに適用する必要があります。 カーネルは4つの概念を意図的に分離します。
  • ConversationFacts: メッセージの送信元
  • RouteFacts: どのエージェントとセッションが処理すべきか
  • ReplyPlanFacts: 可視返信をどこへ送るべきか
  • MessageFacts: エージェントが見るべき本文と補足コンテキスト
Slack DM、Telegramトピック、Matrixスレッド、Feishuトピックセッションは、実際にはすべてこれらを区別します。これらを1つの識別子として扱うと、時間の経過とともにずれが生じます。

ステージライフサイクル

カーネルは、チャネルに関係なく同じ固定パイプラインを実行します。
  1. ingest — アダプターが生のプラットフォームイベントを NormalizedTurnInput に変換する
  2. classify — アダプターがこのイベントでエージェントターンを開始できるかを宣言する
  3. preflight — アダプターが重複排除、自己エコー、ハイドレーション、デバウンス、復号、部分的な事実情報の事前入力を行う
  4. resolve — アダプターが完全に組み立てられたターン(ルート、返信計画、メッセージ、配信)を返す
  5. authorize — 組み立て済みの事実情報にDM、グループ、メンション、コマンドポリシーを適用する
  6. assemblebuildContext を介して事実情報から FinalizedMsgContext を構築する
  7. record — インバウンドセッションメタデータと最後のルートを永続化する
  8. dispatch — バッファリングされたブロックディスパッチャーを通じてエージェントターンを実行する
  9. finalize — ディスパッチエラー時でもアダプターの onFinalize が実行される
log コールバックが提供されると、各ステージは構造化ログイベントを出力します。可観測性を参照してください。

受け入れ種別

ターンがゲートされた場合でも、カーネルは例外をスローしません。ChannelTurnAdmission を返します。
種別条件
dispatchターンが受け入れられます。エージェントターンが実行され、可視返信パスが使用されます。
observeOnlyターンはエンドツーエンドで実行されますが、配信アダプターは可視のものを何も送信しません。ブロードキャスト監視エージェントやその他の受動的なマルチエージェントフローに使用します。
handledプラットフォームイベントがローカルで消費されました(ライフサイクル、リアクション、ボタン、モーダル)。カーネルはディスパッチをスキップします。
dropスキップパスです。任意で recordHistory: true にすると、将来のメンションがコンテキストを持てるように、メッセージを保留中のグループ履歴に保持します。
受け入れは、classify(イベントクラスがターンを開始できないと言った場合)、preflight(重複排除、自己エコー、履歴記録を伴うメンション欠落)、または resolveTurn 自体から発生します。

エントリーポイント

ランタイムは、アダプターがチャネルに合ったレベルでオプトインできるように、3つの推奨エントリーポイントを公開します。
runtime.channel.turn.run(...)             // adapter-driven full pipeline
runtime.channel.turn.runPrepared(...)     // channel owns dispatch; kernel runs record + finalize
runtime.channel.turn.buildContext(...)    // pure facts to FinalizedMsgContext mapping
Plugin SDK互換性のため、2つの古いランタイムヘルパーも引き続き利用できます。
runtime.channel.turn.runResolved(...)      // deprecated compatibility alias; prefer run
runtime.channel.turn.dispatchAssembled(...) // deprecated compatibility alias; prefer run or runPrepared

run

チャネルがインバウンドフローを ChannelTurnAdapter<TRaw> として表現できる場合に使用します。アダプターには、ingest、任意の classify、任意の preflight、必須の resolveTurn、任意の onFinalize の各コールバックがあります。
await runtime.channel.turn.run({
  channel: "tlon",
  accountId,
  raw: platformEvent,
  adapter: {
    ingest(raw) {
      return {
        id: raw.messageId,
        timestamp: raw.timestamp,
        rawText: raw.body,
        textForAgent: raw.body,
      };
    },
    classify(input) {
      return { kind: "message", canStartAgentTurn: input.rawText.length > 0 };
    },
    async preflight(input, eventClass) {
      if (await isDuplicate(input.id)) {
        return { admission: { kind: "drop", reason: "dedupe" } };
      }
      return {};
    },
    resolveTurn(input) {
      return buildAssembledTurn(input);
    },
    onFinalize(result) {
      clearPendingGroupHistory(result);
    },
  },
});
run は、チャネルのアダプターロジックが小さく、フックを通じてライフサイクルを所有するメリットがある場合に適した形です。

runPrepared

プレビュー、再試行、編集、スレッドのブートストラップを含む複雑なローカルディスパッチャーがチャネルにあり、それをチャネル所有のままにする必要がある場合に使用します。カーネルは引き続き、ディスパッチ前にインバウンドセッションを記録し、統一された DispatchedChannelTurnResult を公開します。
const { dispatchResult } = await runtime.channel.turn.runPrepared({
  channel: "matrix",
  accountId,
  routeSessionKey,
  storePath,
  ctxPayload,
  recordInboundSession,
  record: {
    onRecordError,
    updateLastRoute,
  },
  onPreDispatchFailure: async (err) => {
    await stopStatusReactions();
  },
  runDispatch: async () => {
    return await runMatrixOwnedDispatcher();
  },
});
リッチなチャネル(Matrix、Mattermost、Microsoft Teams、Feishu、QQ Bot)は runPrepared を使用します。ディスパッチャーが、カーネルが知るべきではないプラットフォーム固有の動作をオーケストレーションするためです。

buildContext

事実情報の束を FinalizedMsgContext にマッピングする純粋関数です。チャネルがパイプラインの一部を手作業で実装しつつ、一貫したコンテキスト形状を求める場合に使用します。
const ctxPayload = runtime.channel.turn.buildContext({
  channel: "googlechat",
  accountId,
  messageId,
  timestamp,
  from,
  sender,
  conversation,
  route,
  reply,
  message,
  access,
  media,
  supplemental,
});
buildContext は、run のターンを組み立てる際に resolveTurn コールバック内でも有用です。
dispatchInboundReplyWithBase などの非推奨SDKヘルパーは、現在も組み立て済みターンヘルパーを介して橋渡しされます。新しいPluginコードでは run または runPrepared を使用してください。

事実情報の型

カーネルがアダプターから消費する事実情報は、プラットフォーム非依存です。プラットフォームオブジェクトをカーネルへ渡す前に、これらの形に変換してください。

NormalizedTurnInput

フィールド目的
id重複排除とログに使用される安定したメッセージID
timestamp任意のエポックミリ秒
rawTextプラットフォームから受信した本文
textForAgentエージェント向けの任意のクリーン済み本文(メンション削除、入力トリム)
textForCommands/command 解析に使用される任意の本文
raw元の値が必要なアダプターコールバック向けの任意のパススルー参照

ChannelEventClass

フィールド目的
kindmessagecommandinteractionreactionlifecycleunknown
canStartAgentTurnfalseの場合、カーネルは { kind: "handled" } を返す
requiresImmediateAckディスパッチ前にACKが必要なアダプター向けのヒント

SenderFacts

フィールド目的
id安定したプラットフォーム送信者ID
name表示名
usernamename と異なる場合のハンドル
tagDiscordスタイルの識別子またはプラットフォームタグ
rolesメンバーロール許可リスト照合に使用されるロールID
isBot送信者が既知のボットの場合はtrue(カーネルはドロップに使用)
isSelf送信者が設定済みのエージェント自身の場合はtrue
displayLabelエンベロープテキスト用の事前レンダリング済みラベル

ConversationFacts

フィールド目的
kinddirectgroup、または channel
idルーティングに使用される会話ID
labelエンベロープ用の人間向けラベル
spaceId任意の外側のスペース識別子(Slackワークスペース、Matrixホームサーバー)
parentIdこれがスレッドの場合の外側の会話ID
threadIdこのメッセージがスレッド内にある場合のスレッドID
nativeChannelIdルーティングIDと異なる場合のプラットフォームネイティブのチャネルID
routePeerresolveAgentRoute ルックアップに使用されるピア

RouteFacts

フィールド目的
agentIdこのターンを処理すべきエージェント
accountId任意の上書き(マルチアカウントチャネル)
routeSessionKeyルーティングに使用されるセッションキー
dispatchSessionKeyルートキーと異なる場合にディスパッチで使用されるセッションキー
persistedSessionKey永続化されたセッションメタデータに書き込まれるセッションキー
parentSessionKey分岐セッションまたはスレッドセッションの親
modelParentSessionKey分岐セッションのモデル側の親
mainSessionKey直接会話用のメインDM所有者ピン
createIfMissing記録ステップが存在しないセッション行を作成できるようにする

ReplyPlanFacts

フィールド目的
toコンテキスト To に書き込まれる論理返信先
originatingTo元のコンテキスト対象 (OriginatingTo)
nativeChannelId配信用のプラットフォームネイティブなチャネル ID
replyTargetto と異なる場合の最終的な可視返信先
deliveryTarget低レベルの配信オーバーライド
replyToId引用/アンカーされたメッセージ ID
replyToIdFullプラットフォームが両方を持つ場合の完全形式の引用 ID
messageThreadId配信時点のスレッド ID
threadParentIdスレッドの親メッセージ ID
sourceReplyDeliveryModethreadreplychanneldirect、または none

AccessFacts

AccessFacts は authorize 段階が必要とする真偽値を保持します。ID 照合はチャネル内にとどまり、カーネルはその結果だけを使用します。
フィールド目的
dmDM の許可/ペアリング/拒否判定と allowFrom リスト
groupグループポリシー、ルート許可、送信者許可、許可リスト、メンション要件
commands構成済み authorizer 全体でのコマンド認可
mentionsメンション検出が可能か、およびエージェントがメンションされたか

MessageFacts

フィールド目的
body最終的なエンベロープ本文 (整形済み)
rawBody生の受信本文
bodyForAgentエージェントが見る本文
commandBodyコマンド解析に使用される本文
envelopeFromエンベロープ用に事前レンダリングされた送信者ラベル
senderLabelレンダリングされる送信者の任意のオーバーライド
previewログ用の短い伏せ字プレビュー
inboundHistoryチャネルがバッファを保持する場合の最近の受信履歴エントリ

SupplementalContextFacts

補足コンテキストは、引用、転送、スレッド初期化コンテキストを扱います。カーネルは構成済みの contextVisibility ポリシーを適用します。チャネルアダプターはファクトと senderAllowed フラグだけを提供するため、チャネル横断ポリシーの一貫性が保たれます。

InboundMediaFacts

メディアはファクト形式です。プラットフォームのダウンロード、認証、SSRF ポリシー、CDN ルール、復号はチャネルローカルにとどまります。カーネルはファクトを MediaPathMediaUrlMediaTypeMediaPathsMediaUrlsMediaTypesMediaTranscribedIndexes にマップします。

アダプター契約

完全な run の場合、アダプターの形は次のとおりです。
type ChannelTurnAdapter<TRaw> = {
  ingest(raw: TRaw): Promise<NormalizedTurnInput | null> | NormalizedTurnInput | null;
  classify?(input: NormalizedTurnInput): Promise<ChannelEventClass> | ChannelEventClass;
  preflight?(
    input: NormalizedTurnInput,
    eventClass: ChannelEventClass,
  ): Promise<PreflightFacts | ChannelTurnAdmission | null | undefined>;
  resolveTurn(
    input: NormalizedTurnInput,
    eventClass: ChannelEventClass,
    preflight: PreflightFacts,
  ): Promise<ChannelTurnResolved> | ChannelTurnResolved;
  onFinalize?(result: ChannelTurnResult): Promise<void> | void;
};
resolveTurnChannelTurnResolved を返します。これは任意の admission kind を持つ AssembledChannelTurn です。{ admission: { kind: "observeOnly" } } を返すと、可視出力を生成せずにターンを実行します。アダプターは引き続き配信コールバックを所有しますが、そのターンでは no-op になります。 onFinalize は、dispatch エラーを含むすべての結果で実行されます。保留中のグループ履歴のクリア、ack リアクションの削除、ステータスインジケーターの停止、ローカル状態のフラッシュに使用します。

配信アダプター

カーネルはプラットフォームを直接呼び出しません。チャネルは ChannelTurnDeliveryAdapter をカーネルに渡します。
type ChannelTurnDeliveryAdapter = {
  deliver(payload: ReplyPayload, info: ChannelDeliveryInfo): Promise<ChannelDeliveryResult | void>;
  onError?(err: unknown, info: { kind: string }): void;
};

type ChannelDeliveryResult = {
  messageIds?: string[];
  threadId?: string;
  replyToId?: string;
  visibleReplySent?: boolean;
};
deliver はバッファされた返信チャンクごとに 1 回呼び出されます。チャネルがプラットフォームメッセージ ID を持っている場合は返してください。これにより dispatcher はスレッドアンカーを保持し、後続チャンクを編集できます。observe-only ターンでは、{ visibleReplySent: false } を返すか、createNoopChannelTurnDeliveryAdapter() を使用します。

記録オプション

record 段階は recordInboundSession をラップします。ほとんどのチャネルはデフォルトを使用できます。record でオーバーライドします。
record: {
  groupResolution,
  createIfMissing: true,
  updateLastRoute,
  onRecordError: (err) => log.warn("record failed", err),
  trackSessionMetaTask: (task) => pendingTasks.push(task),
}
dispatcher は record 段階を待ちます。record が throw した場合、カーネルは (runPrepared に提供されていれば) onPreDispatchFailure を実行し、再 throw します。

可観測性

log コールバックが提供されている場合、各段階は構造化イベントを発行します。
await runtime.channel.turn.run({
  channel: "twitch",
  accountId,
  raw,
  adapter,
  log: (event) => {
    runtime.log?.debug?.(`turn.${event.stage}:${event.event}`, {
      channel: event.channel,
      accountId: event.accountId,
      messageId: event.messageId,
      sessionKey: event.sessionKey,
      admission: event.admission,
      reason: event.reason,
    });
  },
});
ログ出力される段階: ingestclassifypreflightresolveauthorizeassemblerecorddispatchfinalize。生の本文のログ出力は避け、短い伏せ字プレビューには MessageFacts.preview を使用してください。

チャネルローカルにとどまるもの

カーネルはオーケストレーションを所有します。チャネルは引き続き次を所有します。
  • プラットフォームトランスポート (gateway、REST、websocket、polling、webhooks)
  • ID 解決と表示名の照合
  • ネイティブコマンド、スラッシュコマンド、オートコンプリート、モーダル、ボタン、音声状態
  • カード、モーダル、adaptive-card のレンダリング
  • メディア認証、CDN ルール、暗号化メディア、文字起こし
  • 編集、リアクション、伏せ字化、プレゼンス API
  • backfill とプラットフォーム側の履歴取得
  • プラットフォーム固有の検証を必要とするペアリングフロー
これらのいずれかについて 2 つのチャネルが同じヘルパーを必要とし始めた場合は、カーネルに押し込むのではなく、共有 SDK ヘルパーとして抽出してください。

安定性

runtime.channel.turn.* は公開 Plugin ランタイムサーフェスの一部です。ファクト型 (SenderFactsConversationFactsRouteFactsReplyPlanFactsAccessFactsMessageFactsSupplementalContextFactsInboundMediaFacts) と admission 形状 (ChannelTurnAdmissionChannelEventClass) は、openclaw/plugin-sdk/core から PluginRuntime 経由で到達できます。 後方互換性ルールが適用されます。新しいファクトフィールドは追加的であり、admission kind はリネームされず、エントリポイント名は安定したままです。非追加的な変更を必要とする新しいチャネル要件は、plugin SDK の移行プロセスを経る必要があります。

関連