> ## Documentation Index
> Fetch the complete documentation index at: https://docs2.openclaw.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 頻道傳入 API

Channel 接收路徑遵循單一流程：

```text theme={"theme":{"light":"min-light","dark":"min-dark"}}
平台事件 -> 入站事實／情境 -> 代理程式回覆 -> 訊息傳遞
```

使用 `openclaw/plugin-sdk/channel-inbound` 處理入站事件正規化、
格式化、根目錄與協調作業。使用
`openclaw/plugin-sdk/channel-outbound` 處理原生傳送、收據、持久化
傳遞與即時預覽行為。

## 核心輔助函式

```ts theme={"theme":{"light":"min-light","dark":"min-dark"}}
import {
  buildChannelInboundEventContext,
  runChannelInboundEvent,
  dispatchChannelInboundReply,
} from "openclaw/plugin-sdk/channel-inbound";
```

* `buildChannelInboundEventContext(...)`：將正規化的 Channel 事實
  投影至提示詞／工作階段情境。透過 `channelContext` 傳遞
  Channel 所擁有的傳送者／聊天中繼資料，外掛鉤子會將其視為 `ctx.channelContext`。
  從此子路徑擴充 `PluginHookChannelSenderContext` 或 `PluginHookChannelChatContext`
  以加入 Channel 特定欄位。
* `runChannelInboundEvent(...)`：針對一個入站平台事件執行擷取、分類、預檢、解析、
  記錄、分派及完成作業。
* `dispatchChannelInboundReply(...)`：使用傳遞配接器記錄並分派已
  組裝完成的入站回覆。

對於僅含媒體的入站事件，請將訊息本文與命令文字留空，並
為每個原生附件傳入一項 `ChannelInboundMediaInput` 事實。當環境
歷史記錄行或其他純文字載體必須描述這些事實時，請使用
`formatMediaPlaceholderText(media)`。它會依序根據 `kind`、MIME
類型，再根據路徑或 URL 副檔名分類每項事實；尚未下載的原生附件仍應
各自提供一項僅含類型的事實。請勿使用格式化工具合成
主要入站本文。

使用 `toInboundMediaFacts(...)` 正規化外掛所擁有的附件記錄，然後
透過情境的 `media` 欄位傳入產生的有序陣列：

```ts theme={"theme":{"light":"min-light","dark":"min-dark"}}
const media = toInboundMediaFacts([
  { path: saved.path, url: nativeUrl, contentType: saved.contentType, messageId },
]);

const ctx = finalizeInboundContext({ Body: caption, media });
```

陣列位置就是附件身分。每項事實的 `transcribed`、`messageId` 和
`workspaceDir` 會取代舊版平行索引／工作區欄位。
`MediaPath`、`MediaPaths`、`MediaUrl`、`MediaUrls`、`MediaType`、`MediaTypes`、
`MediaTranscribedIndexes`、`MediaWorkspaceDir` 和 `MediaStaged` 情境欄位，
以及 `buildChannelInboundMediaPayload(...)`，僅作為已棄用的
相容性功能保留。新的外掛不應建構或讀取這些欄位。

已接收注入之外掛執行階段物件的內建／原生 Channel，
可以改為呼叫 `runtime.channel.inbound.*` 下的相同輔助函式，而不必
直接匯入此子路徑：

```ts theme={"theme":{"light":"min-light","dark":"min-dark"}}
await runtime.channel.inbound.run({
  channel: "demo",
  accountId,
  raw: platformEvent,
  adapter: {
    ingest: normalizePlatformEvent,
    resolveTurn: resolveInboundReply,
  },
});
```

為將平台傳遞保留在傳遞配接器中的相容性
分派器組裝 `dispatchChannelInboundReply(...)` 輸入。新的傳送
路徑應改用 `channel-outbound` 中的訊息配接器與持久化訊息輔助函式。

## 傳遞結算合約

`ChannelInboundTurnPlan.delivery` 負責每個邏輯回覆
承載內容的原生傳送。核心負責出站鉤子的執行順序，以及在配接器選擇加入時，
終端 `message_sent` 觀察。請將這些責任分開，以免
一項承載內容產生重複的終端事件。

傳遞結果欄位具有下列含義：

| 欄位                       | 合約                                                                              |
| ------------------------ | ------------------------------------------------------------------------------- |
| `content`                | 邏輯承載內容經原生格式化或完成處理後，提供者所接受的可見文字。省略此欄位時，終端觀察會使用已準備的承載內容文字。僅媒體傳送可省略此欄位。            |
| `messageIds` / `receipt` | 可見傳送的實際提供者身分。優先使用 `MessageReceipt`；核心會使用其主要提供者 ID 作為 `message_sent`。            |
| `visibleReplySent`       | 僅當提供者未產生任何可見預覽或最終訊息時，才設為 `false`。核心不會為該結果發出成功的 `message_sent`。                  |
| `finalization`           | 同一邏輯承載內容延遲完成原生結算的 Promise，例如關閉或編輯原地串流資訊卡。其解析後的欄位會在終端觀察與 `onDelivered` 之前覆寫立即結果。 |

當核心應為此配接器的非持久化傳送發出標準外掛及內部
`message_sent` 事件時，請將傳遞配接器的 `observeMessageSent` 選項設為 `true`。
請勿從 `deliver` 傳回此選項，也不要同時在
外掛中發出這些事件。持久化傳送已由共用出站擁有者發出，
不會重複發出。

每個邏輯承載內容傳回一項結果。`finalization` 並非第二次傳送，
不得重新執行 `reply_payload_sending` 或 `message_sending`。一旦
`deliver` 傳回，核心就會觀察完成處理 Promise 的拒絕，
以免它成為未處理的拒絕；在回覆分派完成後，核心仍會等待原始 Promise。
接著，它會使用完成處理後的內容與提供者 ID，為每項承載內容發出至多一次終端觀察。
若有 `onDelivered`，它會在該觀察之後接收
已結算的結果。

原生傳遞失敗時，拒絕 `deliver` 或 `finalization`。若未嘗試任何提供者
傳送，請從 `openclaw/plugin-sdk/error-runtime` 擲回 `PlatformMessageNotDispatchedError`；
核心會抑制錯誤的 `message_sent`
事件。若原生傳送在後續作業失敗前已經可見，
請在錯誤中保留可見的子集：

```ts theme={"theme":{"light":"min-light","dark":"min-dark"}}
import { createChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";

throw createChannelPartialDeliveryError(cause, {
  visibleReplySent: true,
  content: finalizedVisibleText,
  receipt,
});
```

核心會使用該提供者可見的內容與
身分發出失敗的終端觀察，並維持傳遞失敗狀態，避免呼叫端將部分
成功誤認為完整傳送。任何預覽、草稿、附件或最終訊息
變得可見後，都不得回報 `visibleReplySent: false`。

註冊 `reply_payload_sending` 或 `message_sending` 時，這些鉤子
必須在建立任何提供者可見內容之前完成，因為任一鉤子
都可能改寫或取消邏輯承載內容。過早顯示原生預覽會洩漏
改寫前的內容，或遺留已取消的草稿。請緩衝預覽內容，
直到接受的承載內容抵達 `deliver`；若任一鉤子已註冊，
較早啟動預覽的相容性分派器必須抑制該過早預覽。新的預覽路徑請使用
[Channel 出站 API](/zh-TW/plugins/sdk-channel-outbound) 中可完成處理的即時預覽輔助函式。

## 移轉

`runtime.channel.turn.*` 執行階段別名已移除。請使用：

* `runtime.channel.inbound.run(...)` 用於原始入站事件。
* `runtime.channel.inbound.dispatchReply(...)` 用於已組裝的回覆情境。
* `runtime.channel.inbound.buildContext(...)` 用於入站情境承載內容。
* `runtime.channel.inbound.runPreparedReply(...)` 已棄用，僅適用於
  已自行組裝分派閉包、由 Channel 所擁有的已準備分派路徑。

新的外掛程式碼不應引入以 `turn` 命名的 Channel API。模型或
代理程式回合詞彙應保留在代理程式／提供者程式碼內；Channel 外掛應使用入站、
訊息、傳遞與回覆等詞彙。
