Skip to content

外掛架構內部機制

關於公開能力模型、插件形狀以及所有權/執行合約,請參閱 Plugin architecture。此頁面是內部機制的參考資料:載入管道、註冊表、執行時掛鉤、Gateway HTTP 路由、匯入路徑以及架構表。

啟動時,OpenClaw 大致執行以下操作:

  1. 探索候選外掛根目錄
  2. 讀取原生或相容套件清單和套件元資料
  3. 拒絕不安全的候選項
  4. 正規化外掛設定 (plugins.enabled, allow, deny, entries, slots, load.paths)
  5. 決定每個候選項的啟用狀態
  6. 載入已啟用的原生模組:建構的捆綁模組使用原生載入器; 第三方本機原始碼 TypeScript 使用緊急 Jiti 後備方案
  7. 呼叫原生 register(api) 掛鉤並將註冊收集到外掛註冊表中
  8. 將註冊表公開給指令/執行時介面

安全檢查門檻發生在執行時執行之前。當進入點超出外掛根目錄、路徑可供任何人寫入,或對於非套件外掛路徑所有權看起來可疑時,候選項會被封鎖。

被阻擋的候選項仍會綁定至其插件 ID 以便進行診斷。如果設定 仍參照該 ID,驗證會將插件回報為存在但被阻擋, 並指向路徑安全性警告,而不是將設定條目 視為過時。

清單是控制平面的真實來源。OpenClaw 使用它來:

  • 識別插件
  • 探索宣告的頻道/技能/設定架構或捆綁功能
  • 驗證 plugins.entries.<id>.config
  • 擴充控制 UI 標籤/佔位符
  • 顯示安裝/目錄元資料
  • 保留低成本的啟用和設定描述符,而不載入插件執行時

對於原生插件,執行時模組是資料平面部分。它會註冊 實際行為,例如掛鉤、工具、指令或提供者流程。

選用的清單 activationsetup 區塊停留在控制平面上。 它們是僅包含元資料的描述符,用於啟用規劃和設定探索; 它們不會取代執行時註冊、register(...)setupEntry。 第一批即時啟用消費者現在會使用清單指令、頻道和提供者提示 在更廣泛的註冊表具體化之前縮小插件載入範圍:

  • CLI 載入縮小範圍至擁有請求的主要指令的插件
  • 頻道設定/插件解析縮小範圍至擁有請求的 頻道 ID 的插件
  • 明確的提供者設定/執行時解析縮小範圍至擁有 請求的提供者 ID 的插件
  • Gateway 啟動規劃使用 activation.onStartup 進行明確啟動 匯入和啟動選出;沒有啟動元資料的插件僅透過 更狹窄的啟用觸發程式載入

請求時執行時期預載若請求廣泛的 all 範圍,仍會從設定、啟動規劃、設定的頻道、插槽和自動啟用規則中推導出明確的有效外掛程式 ID 集合。如果該推導集合為空,OpenClaw 會載入空的執行時期註冊表,而不是擴大為每個可探索的外掛程式。

啟動規劃器為現有呼叫者公開了僅限 ID 的 API,並為新的診斷公開了規劃 API。規劃項目會回報選擇外掛程式的原因,將明確的 activation.* 規劃器提示與清單所有權後援(例如 providerschannelscommandAliasessetup.providerscontracts.tools 和掛勾)分開來。這種原因區分是相容性邊界:現有的外掛程式中繼資料保持運作,而新程式碼可以在不改變執行時期載入語意的情況下,偵測廣泛提示或後援行為。

設定探索現在優先使用描述符擁有的 ID(例如 setup.providerssetup.cliBackends),在回退到 setup-api(針對仍需要設定時執行時期掛勾的外掛程式)之前縮小候選外掛程式的範圍。提供者設定清單使用清單 providerAuthChoices、衍生自描述符的設定選擇和安裝目錄中繼資料,而不載入提供者執行時期。明確的 setup.requiresRuntime: false 是僅限描述符的切斷點;省略 requiresRuntime 則為了相容性保留傳統的 setup-api 後援。如果發現多個外掛程式宣告同一個標準化的設定提供者或 CLI 後端 ID,設定查詢會拒絕不明確的擁有者,而不是依賴探索順序。當設定執行時期確實執行時,註冊表診斷會回報 setup.providers / setup.cliBackends 與由 setup-api 註冊的提供者或 CLI 後端之間的差異,而不會封鎖傳統外掛程式。

OpenClaw 不會根據時鐘視窗快取外掛程式探索結果或直接清單註冊表資料。安裝、清單編輯和載入路徑變更必須在下次明確的元資料讀取或快照重建時變得可見。清單檔案剖析器可能會維護一個有界的檔案簽章快取,該快取以開啟的清單路徑、inode、大小和時間戳記為鍵;該快取僅用於避免重新剖析未變更的位元組,且不得快取探索、註冊表、擁有者或原則答案。

安全的元資料快速路徑是明確的物件擁有權,而非隱藏的快取。Gateway 啟動熱路徑應該在呼叫鏈中傳遞當前的 PluginMetadataSnapshot、衍生的 PluginLookUpTable 或明確的清單註冊表。設定驗證、啟動自動啟用、外掛程式引導程式和提供者選擇可以在這些物件代表當前設定和外掛程式清單時重複使用它們。除非特定的設置路徑接收到明確的清單註冊表,否則設置查詢仍然會按需重建清單元資料;請將其作為冷路徑後備方案,而不是新增隱藏的查詢快取。當輸入變更時,請重建並替換快照,而不是對其進行變更或保留歷史副本。 對於作用中的外掛程式註冊表和配套的通道引導程式輔助程式的檢視,應該從當前的註冊表/根目錄重新計算。在單次呼叫內部,短暫存在的對映是可以的,用於去重工作或防範重入;但它們不得成為行程元資料快取。

對於外掛程式載入,持續性快取層是執行時期載入。當實際載入程式碼或已安裝的構件時,它可能會重複使用載入器狀態,例如:

  • PluginLoaderCacheState 和相容的作用中執行時期註冊表
  • jiti/module 快取和用於避免重複匯入相同執行時期介面的 public-surface 載入器快取
  • 用於已安裝外掛程式構件的檔案系統快取
  • 用於路徑正規化或重複解析的短暫性每次呼叫對映

這些快取是資料平面實作細節。除非呼叫者刻意要求執行時期載入,否則它們不得回答控制平面問題,例如「哪個外掛程式擁有此提供者?」。

請勿為以下內容新增持續性或時鐘快取:

  • 探索結果
  • 直接清單註冊表
  • 從已安裝的外掛程式索引重建的清單註冊表
  • 提供者擁有者查找、模型抑制、提供者策略或公開構件 元資料
  • 任何其他清單衍生的答案,其中變更的清單、已安裝索引 或載入路徑應在下次元資料讀取時可見

從持久化的已安裝外掛索引重建清單元資料的呼叫者會按需重建該註冊表。已安裝索引是持久化的 來源平面狀態;它不是隱藏的進程內元資料快取。

已載入的外掛不會直接變更隨機的核心全域變數。它們會註冊到一個 中央外掛註冊表。

註冊表追蹤:

  • 外掛記錄(身分、來源、來處、狀態、診斷)
  • 工具
  • 舊版 Hook 和類型化 Hook
  • 通道
  • 提供者
  • 閘道 RPC 處理程式
  • HTTP 路由
  • CLI 註冊員
  • 背景服務
  • 外掛擁有的指令

核心功能然後從該註冊表讀取,而不是直接與外掛模組 對話。這使得載入變成單向:

  • 外掛模組 -> 註冊表註冊
  • 核心執行時 -> 註冊表使用

那種分離對於可維護性很重要。這意味著大多數核心表面只需要 一個整合點:「讀取註冊表」,而不是「對每個外掛 模組進行特殊處理」。

綁定對話的外掛可以在批准解決時做出反應。

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

回呼負載欄位:

  • status"approved""denied"
  • decision"allow-once""allow-always""deny"
  • binding:已批准請求的已解決綁定
  • request:原始請求摘要、分離提示、發送者 ID 和 對話元資料

此回呼僅供通知。它不會改變允許誰綁定 對話,並且在核心批准處理完成後執行。

提供者外掛有三層:

  • Manifest metadata 用於低成本的運行前查找: setup.providers[].envVars、已棄用的兼容性 providerAuthEnvVarsproviderAuthAliasesproviderAuthChoiceschannelEnvVars
  • Config-time hookscatalog(舊版 discovery)加上 applyConfigDefaults
  • Runtime hooks:40 多個涵蓋驗證、模型解析、串流包裝、思考層級、重播原則和使用端點的選用掛鉤。請參閱 Hook order and usage 下的完整列表。

OpenClaw 仍然擁有通用代理循環、故障轉移、轉錄處理和 工具策略。這些 hooks 是供應商特定 行為的擴展介面,而無需完整的自訂推論傳輸。

當供應商具有基於環境變數的 憑證,且通用身份驗證/狀態/模型選擇器路徑應在 不加載插件運行時的情況下看到它們時,請使用 manifest setup.providers[].envVars。已棄用的 providerAuthEnvVars 在棄用過渡期內仍會被 兼容性適配器讀取,使用它的非捆綁插件 會收到 manifest 診斷。當一個供應商 ID 應該重複使用另一個供應商 ID 的環境變數、身份驗證設定檔、 配置支援的身份驗證以及 API 金鑰入門選擇時,請使用 manifest providerAuthAliases。 當入門/身份驗證選擇 CLI 介面應在 不加載供應商運行時的情況下知道供應商的選擇 ID、群組標籤和簡單的單標誌身份驗證連線時,請使用 manifest providerAuthChoices。保留供應商運行時 envVars 用於操作員面向的提示,例如入門標籤或 OAuth 客戶端 ID/客戶端密鑰設定變數。

當頻道具有由環境驅動的身份驗證或設定,且通用 shell-env 後備、配置/狀態檢查或設定提示應在 不加載頻道運行時的情況下看到它們時,請使用 manifest channelEnvVars

對於模型/供應商插件,OpenClaw 大致按此順序調用 hooks。 「使用時機」欄是快速決策指南。 OpenClaw 不再調用的僅兼容性供應商欄位,例如 ProviderPlugin.capabilitiessuppressBuiltInModel,故意未 在此列出。

#Hook作用何時使用
1catalogmodels.json 產生期間將提供者配置發佈到 models.providers提供者擁有目錄或基礎 URL 預設值
2applyConfigDefaults在配置具體化期間套用提供者擁有的全域配置預設值預設值取決於驗證模式、環境或提供者模型系列語意
(內建模型查找)OpenClaw 首先嘗試正常的註冊表/目錄路徑(不是外掛程式掛鉤)
3normalizeModelId在查找之前正規化舊版或預覽模型 ID 別名提供者在解析規範模型之前擁有別名清理權
4normalizeTransport在通用模型組裝之前正規化提供者系列 api / baseUrlProvider 擁有同一傳輸系列中自訂 provider ID 的傳輸清理工作
5normalizeConfig在 runtime/provider 解析前先正規化 models.providers.<id>Provider 需要組態清理,這應該隨附於外掛;內建的 Google 系列輔助函式也會支援支援的 Google 組態項目作為最後防線
6applyNativeStreamingUsageCompat對組態 provider 套用原生串流使用相容性重寫Provider 需要端點驅動的原生串流使用中繼資料修復
7resolveConfigApiKey在 runtime auth 載入之前,解析組態 provider 的 env-marker auth提供者會公開其自身的 env-marker API 金鑰解析攔截器
8resolveSyntheticAuth公開本地/自託管或基於組態的 auth,而不儲存純文字Provider 可以使用合成的/本地的憑證標記運作
9resolveExternalAuthProfiles覆寫提供者擁有的外部驗證設定檔;預設的 persistence 是 CLI/應用程式擁有認證的 runtime-only提供者重複使用外部驗證憑證而不會保存複製的重新整理權杖;請在清單中宣告 contracts.externalAuthProviders
10shouldDeferSyntheticProfileAuth降低環境變數/配置支援的驗證背後所儲存的合成設定檔佔位符優先級Provider 儲存不應獲得優先權的合成佔位符設定檔
11resolveDynamicModel針對本地註冊表中尚未存在的 Provider 擁有的模型 ID 進行同步回退Provider 接受任意的上游模型 ID
12prepareDynamicModel非同步預熱,然後 resolveDynamicModel 再次執行提供者在解析未知 ID 之前需要網路中繼資料
13normalizeResolvedModel嵌入式執行器使用解析的模型之前的最終重寫提供者需要傳輸重寫,但仍使用核心傳輸
14normalizeToolSchemas在嵌入式執行器看到工具綱要之前先進行標準化提供者需要傳輸家族 (transport-family) 的綱要清理
15inspectToolSchemas在標準化後顯示提供者擁有的綱要診斷資訊提供者想要關鍵字警告,而不需要教導核心提供者特定的規則
16resolveReasoningOutputMode選擇原生與標記的推理輸出合約提供者需要標記的推理/最終輸出,而不是原生欄位
17prepareExtraParams在一般串流選項包裝器之前的請求參數標準化提供者需要預設請求參數或針對每個提供者的參數清理
18createStreamFn完全使用自訂傳輸取代正常的串流路徑提供者需要自訂線路協定,而不僅僅是包裝器
20wrapStreamFn在套用一般包裝器之後的串流包裝器提供者需要請求標頭/內文/模型相容性包裝器,而不需要自訂傳輸
21resolveTransportTurnState附加原生的每輪次傳輸標頭或中繼資料提供者希望通用傳輸能夠發送提供者原生的輪次身份
22resolveWebSocketSessionPolicy附加原生 WebSocket 標頭或會話冷卻策略提供者希望通用 WS 傳輸能調整會話標頭或後備策略
23formatApiKeyAuth-profile 格式化器:儲存的設定檔變成執行時 apiKey 字串提供者儲存額外的驗證元資料,並需要自訂的執行時令牌形狀
24refreshOAuthOAuth 重新整理覆寫,用於自訂重新整理端點或重新整理失敗策略提供者不符合共享的 OpenClaw 重新整理器
25buildAuthDoctorHint當 OAuth 重新整理失敗時附加的修復提示提供者在重新整理失敗後需要提供者擁有的驗證修復指引
26matchesContextOverflowError提供者擁有的內容視窗溢出匹配器提供者具有通用啟發式演算法會錯過的原始溢出錯誤
27classifyFailoverReason提供者擁有的故障移轉原因分類提供者可以將原始 API/傳輸錯誤對應到速率限制/過載等
28isCacheTtlEligible代理/回程提供者的提示快取策略提供者需要特定於代理的快取 TTL 閘控
29buildMissingAuthMessage通用缺少驗證恢復訊息的替換內容提供者需要特定於提供者的缺少驗證恢復提示
30augmentModelCatalog在探索之後附加的合成/最終目錄列提供者需要在 models list 和選擇器中加入合成的向前相容列
31resolveThinkingProfile特定模型的 /think 層級設定、顯示標籤和預設值提供者為選定的模型公開自訂的思考梯階或二元標籤
32isBinaryThinking開/關推理切換相容性鉤子提供者僅公開二元思考開/關
33supportsXHighThinkingxhigh 推理支援相容性鉤子提供者僅希望在部分模型上使用 xhigh
34resolveDefaultThinkingLevel預設 /think 層級相容性鉤子Provider 擁有模型系列的預設 /think 政策
35isModernModelRef用於即時設定檔過濾器和選擇的現代模型匹配器Provider 擁有即時/選擇的偏好模型匹配
36prepareRuntimeAuth在推論之前將設定的憑證交換為實際的執行時期 token/keyProvider 需要 token 交換或短期請求憑證
37resolveUsageAuth解析 /usage 及相關狀態介面的使用/計費憑證Provider 需要自訂的使用/配額 token 解析或不同的使用憑證
38fetchUsageSnapshot在解析驗證後擷取並正規化 Provider 特定的使用/配額快照Provider 需要 Provider 特定的使用端點或 payload 解析器
39createEmbeddingProvider為記憶體/搜尋建構 Provider 擁有的嵌入適配器記憶體嵌入行為屬於 Provider 外掛
40buildReplayPolicy傳回控制 Provider 轉錄內容處理的重新播放政策Provider 需要自訂的轉錄內容政策(例如,思考區塊剝離)
41sanitizeReplayHistory在一般轉錄內容清理之後重寫重新播放歷程Provider 除了共享的壓縮輔助功能外,還需要 Provider 特定的重新播放重寫
42validateReplayTurns在嵌入式執行器之前的最終重新播放輪次驗證或重組在一般清理之後,Provider 傳輸需要更嚴格的輪次驗證
43onModelSelected執行 Provider 擁有的選擇後副作用當模型變為作用中時,Provider 需要遙測或 Provider 擁有的狀態

normalizeModelIdnormalizeTransportnormalizeConfig 首先檢查匹配的提供者外掛程式,然後回退到其他具備鉤子功能的提供者外掛程式,直到其中一個實際變更了模型 ID 或傳輸/設定。這樣可以讓別名/相容性提供者填充層正常運作,而無需呼叫者知道哪個內建外掛程式擁有該重寫邏輯。如果沒有提供者鉤子重寫支援的 Google 系列設定項目,內建的 Google 設定正規化器仍會套用該相容性清理。

如果提供者需要完全自訂的連線協定或自訂請求執行器,那是另一種類別的擴充功能。這些鉤子適用於仍然在 OpenClaw 正常推斷迴圈上執行的提供者行為。

resolveUsageAuth 決定 OpenClaw 是否應該呼叫 fetchUsageSnapshot 或 退回針對使用/狀態介面的通用憑證解析。當提供者具有使用憑證時傳回 { token, accountId? },當提供者擁有的使用驗證已處理請求且 必須抑制通用 API 金鑰/OAuth 退回時傳回 { handled: true },並在提供者未處理使用驗證時傳回 nullundefined

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

隨附的提供者外掛程式結合上述掛鉤以滿足各家供應商的目錄、驗證、思考、重播和使用需求。權威的掛鉤集位於 每個外掛程式的 extensions/ 下;本頁面說明這些形狀,而非鏡像該列表。

透傳目錄提供商

OpenRouter、Kilocode、Z.AI 和 xAI 註冊 catalog 加上 resolveDynamicModel / prepareDynamicModel,以便它們能在 OpenClaw 的靜態目錄之前優先顯示上游模型 ID。

OAuth 和使用量端點提供商

GitHub Copilot、Gemini CLI、ChatGPT Codex、MiniMax、Xiaomi 和 z.ai 將 prepareRuntimeAuthformatApiKeyresolveUsageAuth + fetchUsageSnapshot 配對,以掌控令牌交換和 /usage 整合。

重播與逐字稿清理系列

共用命名系列(google-geminipassthrough-geminianthropic-by-modelhybrid-anthropic-openai)讓提供者能透過 buildReplayPolicy 加入逐字稿策略, 而不需要每個外掛程式都重新實作清理功能。

僅目錄提供者

bytepluscloudflare-ai-gatewayhuggingfacekimi-codingnvidiaqianfansynthetictogethervenicevercel-ai-gatewayvolcengine 僅註冊 catalog 並使用共享推斷迴圈。

Anthropic 專用串流輔助程式

Beta 標頭、/fast / serviceTiercontext1m 存在於 Anthropic 外掛的公開 api.ts / contract-api.ts 接縫 (wrapAnthropicProviderStreamresolveAnthropicBetasresolveAnthropicFastModeresolveAnthropicServiceTier) 中,而非 通用 SDK 中。

外掛可以透過 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 音訊緩衝區 + 採樣率。外掛必須為提供者重新採樣/編碼。
  • listVoices 對於每個提供者來說是可選的。將其用於供應商擁有的語音選擇器或設定流程。
  • 語音列表可以包含更豐富的元資料,例如地區設定、性別和個性標籤,供提供者感知的選擇器使用。
  • OpenAI 和 ElevenLabs 目前支援電話。Microsoft 不支援。

外掛也可以透過 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 新增這些能力合約,一個供應商外掛可以擁有文字、語音、圖像和未來的媒體提供者。

對於圖片/音訊/影片理解,外掛程式會註冊一個具類型的 媒體理解提供者,而非通用的鍵值包:

api.registerMediaUnderstandingProvider({
id: "google",
capabilities: ["image", "audio", "video"],
describeImage: async (req) => ({ text: "..." }),
transcribeAudio: async (req) => ({ text: "..." }),
describeVideo: async (req) => ({ text: "..." }),
});

註記:

  • 將編排、後備、設定和通道連線保留在核心中。
  • 將供應商行為保留在提供者外掛程式中。
  • 增擴展應保持類型:新增可選方法、新增可選 結果欄位、新增可選功能。
  • 影片生成已遵循相同的模式:
    • 核心擁有功能合約和執行時期輔助程式
    • 供應商外掛程式註冊 api.registerVideoGenerationProvider(...)
    • 功能/通道外掛程式使用 api.runtime.videoGeneration.*

對於媒體理解執行時期輔助程式,外掛程式可以呼叫:

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

對於音訊轉錄,外掛程式可以使用媒體理解執行時期 或較舊的 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(...) 是外掛程式面向的接縫,用於有界的提供者擁有的優先影像擷取。請至少包含一個影像輸入;文字輸入是補充上下文。產品外掛程式擁有其路由和架構,而 OpenClaw 擁有提供者/執行時邊界。
  • 使用核心媒體理解音訊設定 (tools.media.audio) 和提供者後備順序。
  • 當未產生轉錄輸出(例如跳過/不支援的輸入)時,回傳 { text: undefined }
  • api.runtime.stt.transcribeAudioFile(...) 保留為相容性別名。

外掛程式也可以透過 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 僅對受信任的呼叫者遵守這些覆寫欄位。
  • 對於外掛擁有的後備執行,操作員必須使用 plugins.entries.<id>.subagent.allowModelOverride: true 選擇加入。
  • 使用 plugins.entries.<id>.subagent.allowedModels 將受信任的外掛限制在特定的正規 provider/model 目標,或使用 "*" 以明確允許任何目標。
  • 不受信任的外掛子代理執行仍然有效,但覆寫請求將被拒絕,而不是靜默回退。
  • 外掛建立的子代理會話會標記建立該會話的外掛 ID。後備 api.runtime.subagent.deleteSession(...) 只能刪除那些擁有的會話;任意刪除會話仍需要具有管理員範圍的 Gateway 請求。

對於網路搜尋,外掛可以使用共享的執行時輔助函式,而不是 深入到代理工具連線中:

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

外掛也可以透過 api.registerWebSearchProvider(...) 註冊網路搜尋提供者。

備註:

  • 將提供者選擇、憑證解析和共用請求語意保留在核心中。
  • 針對廠商特定的搜尋傳輸使用網頁搜尋提供者。
  • api.runtime.webSearch.* 是需要搜尋行為但依賴於代理程式工具包裝函式的功能/通道外掛的首選共用介面。
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(...):列出可用的影像生成提供者及其功能。

外掛可以使用 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" 來要求正常的 gateway auth,或 "plugin" 用於外掛程式管理的 auth/webhook 驗證。
  • match:選用。"exact"(預設)或 "prefix"
  • replaceExisting:選用。允許同一個外掛程式取代其現有的路由註冊。
  • handler:當路由處理了請求時,回傳 true

備註:

  • api.registerHttpHandler(...) 已被移除,將會導致外掛程式載入錯誤。請改用 api.registerHttpRoute(...)
  • 外掛程式路由必須明確宣告 auth
  • 除非符合 replaceExisting: true,否則精確的 path + match 衝突會被拒絕,且一個外掛程式無法取代另一個外掛程式的路由。
  • 具有不同 auth 層級的重疊路由會被拒絕。請將 exact/prefix 透傳鏈保持在同一個驗證層級上。
  • auth: "plugin" 路由不會自動接收操作員運行時範圍。它們是用於外掛程式管理的 Webhook/簽名驗證,而不是特權 Gateway 輔助呼叫。
  • auth: "gateway" 路由在 Gateway 請求運行時範圍內運行,但該範圍是有意保守的:
    • shared-secret bearer auth (gateway.auth.mode = "token" / "password") 會將 plugin-route 執行時範圍固定在 operator.write,即使呼叫端發送 x-openclaw-scopes
    • trusted identity-bearing HTTP 模式(例如私有入口上的 trusted-proxygateway.auth.mode = "none")僅在標頭明確存在時才接受 x-openclaw-scopes
    • 如果在這些承載身分的 plugin-route 請求中缺少 x-openclaw-scopes,執行時範圍會回退至 operator.write
  • 實用規則:不要假設 gateway-auth plugin route 是隱含的管理介面。如果您的路由需要僅限管理員的行為,請要求使用承載身分的驗證模式,並記錄明確的 x-openclaw-scopes 標頭合約。

在編寫新外掛程式時,請使用狹窄的 SDK 子路徑,而不是單一的 openclaw/plugin-sdk 根目錄匯入。核心子路徑:

子路徑用途
openclaw/plugin-sdk/plugin-entry外掛程式註冊原語
openclaw/plugin-sdk/channel-core通道條目/建置輔助函式
openclaw/plugin-sdk/core通用共享輔助函式和總覽合約
openclaw/plugin-sdk/config-schemaopenclaw.json Zod 結構描述 (OpenClawSchema)

通道插件從一系列狹窄的接縫中進行選擇 — channel-setupsetup-runtimesetup-toolschannel-pairingchannel-contractchannel-feedbackchannel-inboundchannel-outboundcommand-authsecret-inputwebhook-ingresschannel-targetschannel-actions。審批行為應整合到一個 approvalCapability 合約中,而不是混合在不相關的插件欄位中。請參閱 通道插件

執行階段和設定輔助程式位於匹配的專注 *-runtime 子路徑下 (approval-runtimeagent-runtimelazy-runtimedirectory-runtimetext-runtimeruntime-storesystem-event-runtimeheartbeat-runtimechannel-activity-runtime 等)。請優先使用 config-contractsplugin-config-runtimeruntime-config-snapshotconfig-mutation, 而不是廣泛的 config-runtime 相容性匯出桶。

儲存庫內部進入點(每個打包外掛套件根目錄):

  • index.js — 打包外掛進入點
  • api.js — 輔助程式/型別統整檔案
  • runtime-api.js — 僅執行時期統整檔案
  • setup-entry.js — 設定外掛進入點

外部插件應僅匯入 openclaw/plugin-sdk/* 子路徑。切勿 從核心或其他插件匯入另一個插件套件的 src/*。 Facade 載入的進入點在存在時會優先使用有效的執行時設定快照, 然後再回退到磁碟上解析出的設定檔。

存在特定功能的子路徑,例如 image-generationmedia-understandingspeech,這是因為打包的插件目前使用它們。它們並 非自動長期凍結的外部合約 — 在依賴它們時請檢查相關的 SDK 參考頁面。

外掛應擁有特定頻道 describeMessageTool(...) 結構描述貢獻,用於非訊息原語,例如反應、已讀和投票。共用的發送呈現應使用通用的 MessagePresentation 合約,而非提供者原生的按鈕、元件、區塊或卡片欄位。請參閱 Message Presentation 以了解合約、後援規則、提供者對應以及外掛作者檢查清單。

具備發送功能的外掛透過訊息功能宣告其可呈現的內容:

  • presentation 用於語意呈現區塊 (textcontextdividerbuttonsselect)
  • delivery-pin 用於固定傳遞要求

Core 決定是以原生方式呈現展示層還是將其降級為文字。 請勿從通用訊息工具中暴露提供者原生 UI 的逃生口。 針對舊版原生結構已棄用的 SDK 協助程式仍會為現有 第三方插件匯出,但新插件不應使用它們。

頻道插件應擁有特定於頻道的目標語意。保持共用 輸出主機的通用性,並使用訊息介面卡層來處理提供者規則:

  • messaging.inferTargetChatType({ to }) 決定在目錄查找之前,是否應將正規化目標 視為 directgroupchannel
  • messaging.targetResolver.looksLikeId(raw, normalized) 告訴 Core 輸入是否應跳過目錄搜尋,直接進 行類似 ID 的解析。
  • 當核心在正規化或目錄未命中後需要最終的提供者擁有權解析時,messaging.targetResolver.resolveTarget(...) 是外掛程式的後備方案。
  • 一旦解析出目標,messaging.resolveOutboundSessionRoute(...) 便負責提供者特定的會話路由建構。

建議的分割方式:

  • 針對應在搜尋同層/群組之前發生的分類決策,請使用 inferTargetChatType
  • 針對「將此視為明確/原生目標 ID」的檢查,請使用 looksLikeId
  • resolveTarget 用於提供者特定的正規化後備方案,而非廣泛的目錄搜尋。
  • 請將提供者原生的 ID(如聊天 ID、執行緒 ID、JID、代碼和房間 ID)保留在 target 值或提供者特定的參數中,不要放在通用 SDK 欄位中。

從設定衍生目錄條目的外掛程式應將該邏輯保留在外掛程式中,並重複使用 openclaw/plugin-sdk/directory-runtime 中的共享輔助程式。

當頻道需要由設定支援的對等群組/群組時,請使用此功能,例如:

  • 由允許清單驅動的直接訊息對等物件
  • 已設定的頻道/群組對應
  • 帳戶範圍的靜態目錄後援機制

directory-runtime 中的共享輔助程式僅處理一般作業:

  • 查詢篩選
  • 限制應用
  • 去重/正規化輔助程式
  • 建構 ChannelDirectoryEntry[]

特定頻道的帳戶檢查和 ID 正規化應保留在外掛程式實作中。

提供者外掛程式可以使用 registerProvider({ catalog: { run(...) { ... } } }) 定義用於推斷的模型目錄。

catalog.run(...) 會傳回與 OpenClaw 寫入 models.providers 相同的形狀:

  • 用於單一提供者項目的 { provider }
  • 用於多個提供者項目的 { providers }

當外掛擁有提供者特定的模型 ID、預設基礎 URL 或受認證保護的模型元數據時,請使用 catalog

catalog.order 控制外掛的目錄相對於 OpenClaw 內建隱式提供者的合併時機:

  • simple:純 API 金鑰或環境變數驅動的提供者
  • profile:當存在認證設定檔時出現的提供者
  • paired:綜合多個相關提供者項目的提供者
  • late:最後一輪,在其他隱式提供者之後

後續的提供者在金鑰衝突時獲勝,因此外掛可以故意使用相同的提供者 ID 覆蓋內建的提供者項目。

外掛程式也可以透過 api.registerModelCatalogProvider({ provider, kinds, staticCatalog, liveCatalog }) 發布唯讀模型目錄列。這是用於清單/說明/選擇器介面的前向路徑,並支援 textimage_generationvideo_generationmusic_generation 列。 供應商外掛程式仍然擁有即時端點呼叫、權杖交換和廠商回應映射;核心則擁有通用列形狀、來源標籤和媒體工具說明格式。媒體生成供應商註冊會自動從 defaultModelmodelscapabilities 合成靜態目錄列。

相容性:

  • discovery 仍可作為舊版別名使用,但會發出棄用警告
  • 如果同時註冊了 catalogdiscovery,OpenClaw 將使用 catalog
  • augmentModelCatalog 已被棄用;打包的提供者應透過 registerModelCatalogProvider 發布補充行

如果您的外掛註冊了通道,建議在 resolveAccount(...) 旁一併實作 plugin.config.inspectAccount(cfg, accountId)

原因:

  • resolveAccount(...) 是執行時期路徑。它可以假設憑證已完全具體化,並在缺少必要的密鑰時快速失敗。
  • 唯讀指令路徑(例如 openclaw statusopenclaw status --allopenclaw channels statusopenclaw channels resolve 以及 doctor/config 修復流程)應該不需要僅為了描述組態而具體化執行時期憑證。

建議的 inspectAccount(...) 行為:

  • 僅傳回描述性帳戶狀態。
  • 保留 enabledconfigured
  • 在相關時包含憑證來源/狀態欄位,例如:
    • tokenSourcetokenStatus
    • botTokenSourcebotTokenStatus
    • appTokenSourceappTokenStatus
    • signingSecretSourcesigningSecretStatus
  • 您不需要僅為了回報唯讀可用性而傳回原始權杖值。傳回 tokenStatus: "available"(以及對應的來源欄位)對於狀態風格的指令已足夠。
  • 當憑證透過 SecretRef 配置但在目前指令路徑中無法使用時,請使用 configured_unavailable

這讓唯讀指令可以回報「已配置但在本指令路徑中無法使用」,而不是崩潰或將帳戶錯誤回報為未配置。

外掛目錄可以包含帶有 openclaw.extensionspackage.json

{
"name": "my-pack",
"openclaw": {
"extensions": ["./src/safety.ts", "./src/tools.ts"],
"setupEntry": "./src/setup-entry.ts"
}
}

每個條目會變成一個外掛。如果套件包列出了多個擴充功能,外掛 ID 會變成 name/<fileBase>

如果您的外掛匯入了 npm 相依項,請在該目錄中安裝它們,以便 node_modules 可用(npm install / pnpm install)。

安全防護:在解析符號連結後,每個 openclaw.extensions 條目必須保留在外掛目錄內。逃離套件目錄的條目會被拒絕。

安全提示:openclaw plugins install 會使用專案本地的 npm install --omit=dev --ignore-scripts 安裝外掛相依項(無生命週期腳本、執行時無 dev 相依項),並忽略繼承的全域 npm 安裝設定。請保持外掛相依樹「純 JS/TS」,並避免需要 postinstall 建置的套件。

選用:openclaw.setupEntry 可以指向一個輕量級的僅設定模組。當 OpenClaw 需要針對已停用的通道外掛程式提供設定介面,或是當通道外掛程式已啟用但尚未設定時,它會載入 setupEntry 而非完整的外掛程式進入點。這能讓啟動與設定過程更加輕量化,特別是當您的主外掛程式進入點同時連結了工具、攔截器或其他僅限執行時期的程式碼時。

選用:openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen 可以讓通道外掛程式在閘道的預監聽啟動階段中,選擇採用相同的 setupEntry 路徑,即使該通道已經完成設定。

僅當 setupEntry 完整涵蓋閘道開始監聽前必須存在的啟動介面時,才使用此選項。實務上,這意味著設定進入點必須註冊啟動所依賴的每一個通道擁有的功能,例如:

  • 通道註冊本身
  • 任何在閘道開始監聽前必須可用的 HTTP 路由
  • 任何在同一時段必須存在的閘道方法、工具或服務

如果您的完整進入點仍擁有任何必需的啟動功能,請勿啟用此旗標。讓外掛程式保持預設行為,並讓 OpenClaw 在啟動期間載入完整進入點。

打包的通道也可以發布僅供設定使用的合約介面輔助程式,讓核心在載入完整通道執行時期之前進行諮詢。目前的設定升級介面為:

  • singleAccountKeysToMove
  • namedAccountPromotionKeys
  • resolveSingleAccountPromotionTarget(...)

核心在需要將舊版單一帳戶通道設定升級為 channels.<id>.accounts.* 且不載入完整外掛程式進入點時,會使用該介面。Matrix 是目前打包範例:當命名帳戶已存在時,它僅將驗證/啟動金鑰移至命名的升級帳戶,並且它可以保留已設定的非標準預設帳戶金鑰,而不是總是建立 accounts.default

這些設定修補配接器讓打包的合約介面探索保持延遲載入。匯入時間保持輕量;升級介面僅在首次使用時載入,而不是在模組匯入時重新進入打包通道的啟動流程。

當這些啟動層面包含閘道 RPC 方法時,請將它們保留在 外掛特定的前綴上。核心管理命名空間(config.*exec.approvals.*wizard.*update.*)仍然保留,且始終解析 為 operator.admin,即使外掛請求了更窄的範圍。

範例:

{
"name": "@scope/my-channel",
"openclaw": {
"extensions": ["./index.ts"],
"setupEntry": "./setup-entry.ts",
"startup": {
"deferConfiguredChannelFullLoadUntilAfterListen": true
}
}
}

通道外掛可以透過 openclaw.channel 宣傳設定/探索元數據,並 透過 openclaw.install 提供安裝提示。這讓核心目錄保持無數據狀態。

範例:

{
"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:覆寫文件連結的連結文字
  • preferOver:此目錄條目應排名高於的優先級較低的外掛/通道 ID
  • selectionDocsPrefixselectionDocsOmitLabelselectionExtras:選擇層面的複製控制
  • markdownCapable:將通道標記為支援 Markdown,以便進行輸出格式設定
  • exposure.configured:當設為 false 時,在已設定通道列表層面中隱藏該通道
  • exposure.setup:當設為 false 時,在互動式設定/配置選擇器中隱藏該通道
  • exposure.docs:將通道標記為內部/私有,用於文件導覽層面
  • showConfigured / showInSetup:為了相容性仍接受的舊版別名;建議優先使用 exposure
  • quickstartAllowFrom:讓通道加入標準快速入門 allowFrom 流程
  • forceAccountBinding:即使只存在一個帳戶,也要求明確的帳戶綁定
  • preferSessionLookupForAnnounceTarget:在解析通告目標時優先使用工作階段查詢

OpenClaw 也可以合併 外部通道目錄(例如 MPM 登錄檔匯出)。將 JSON 檔案放置於以下任一位置:

  • ~/.openclaw/mpm/plugins.json
  • ~/.openclaw/mpm/catalog.json
  • ~/.openclaw/plugins/catalog.json

或者將 OPENCLAW_PLUGIN_CATALOG_PATHS(或 OPENCLAW_MPM_CATALOG_PATHS)指向 一或多個 JSON 檔案(以逗號/分號/PATH 分隔)。每個檔案應 包含 { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }。解析器也接受 "packages""plugins" 作為 "entries" 鍵的傳統別名。

生成的頻道目錄條目和提供者安裝目錄條目會在原始 openclaw.install 區塊旁邊公開正規化的安裝來源資訊。正規化資訊會識別 npm 規格是精確版本還是浮動選擇器、是否存在預期的完整性元數據,以及本地來源路徑是否也可用。當目錄/套件身分已知時,如果解析出的 npm 套件名稱與該身分不符,正規化資訊會發出警告。當 defaultChoice 無效或指向不可用的來源時,以及當存在 npm 完整性元數據卻沒有有效的 npm 來源時,它們也會發出警告。消費者應將 installSource 視為一個附加的可選欄位,這樣手動建構的條目和目錄填充層就不必合成它。這使得上架和診斷功能可以在不匯入外掛程式執行期的情況下,解釋來源平面狀態。

官方外部 npm 條目應優先使用精確的 npmSpec 加上 expectedIntegrity。為了相容性,裸套件名稱和 dist-tags 仍然有效,但它們會顯示 source-plane 警告,以便目錄可以在不破壞現有插件的情況下轉向固定、經完整性檢查的安裝。 當從本地目錄路徑註冊安裝時,它會記錄一個帶有 source: "path" 和盡可能相對於工作區的 sourcePath 的受管插件索引條目。絕對操作加載路徑保留在 plugins.load.paths 中;安裝記錄避免將本地工作站路徑重複到長期配置中。這使得本地開發安裝對 source-plane 診斷可見,而不會增加第二個原始檔案系統路徑洩露面。持久化的 installed_plugin_index SQLite 行是安裝的 事實來源,並且可以在不加載插件運行時模組的情況下重新整理。 其 installRecords 映射是持久的,即使插件清單遺失或無效;其 plugins 有效載荷是可重建的清單視圖。

Context engine 插件擁有會話上下文協調,用於攝取、組裝 和壓縮。使用 api.registerContextEngine(id, factory) 從您的插件註冊它們,然後使用 plugins.slots.contextEngine 選擇活動引擎。

當您的插件需要替換或擴展預設上下文管道而不僅僅是添加記憶體搜尋或鉤子時,請使用此選項。

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

工廠 ctx 公開可選的 configagentDirworkspaceDir 值,用於構建時初始化。

當 active harness 具有持久化後端執行緒時,assemble() 可能會傳回 contextProjection。若是舊版每次輪換的 projection,請省略它。當組合後的 context 應該一次性注入到後端執行緒並重複使用直到 epoch 變更時,請傳回 { mode: "thread_bootstrap", epoch }。在引擎的語意 context 變更後(例如在引擎擁有的壓縮過程之後)變更 epoch。主機可能會在 thread-bootstrap projection 中保留 tool-call metadata、input shape 和已編輯的 tool results,以便新的後端執行緒在不複製原始含機密 payload 的情況下維持 tool 連續性。

如果您的引擎擁有壓縮演算法,請保持 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);
},
}));
}

當外掛程式需要不符合目前 API 的行為時,請勿透過私下存取來繞過外掛程式系統。請新增缺失的功能。

建議順序:

  1. 定義核心合約 決定核心應該擁有哪些共享行為:原則、後備機制、設定合併、 生命週期、面向通道的語意,以及執行時期輔助程式形狀。
  2. 新增具型別的外掛程式註冊/執行時期介面 使用最小且有用的具型別功能介面來擴充 OpenClawPluginApi 和/或 api.runtime
  3. 連接核心 + 通道/功能消費者 通道和功能外掛程式應透過核心來使用新功能, 而非直接匯入供應商實作。
  4. 註冊供應商實作 供應商外掛程式接著會針對該功能註冊其後端。
  5. 新增合約覆蓋率 新增測試,以便讓擁有權和註冊形狀隨時間推移保持明確。

這就是 OpenClaw 如何在不針對單一提供者的世界觀進行硬編碼的情況下,保持其主觀性。如需具體的檔案檢查清單和實作範例,請參閱功能指南

當您新增一個功能時,實作通常應該同時涉及以下層面:

  • src/<capability>/types.ts 中的核心合約類型
  • src/<capability>/runtime.ts 中的核心執行器/執行時期輔助函式
  • src/plugins/types.ts 中的外掛 API 註冊介面
  • src/plugins/registry.ts 中的外掛註冊表連線
  • 當功能/頻道外掛需要使用時,在 src/plugins/runtime/* 中公開的外掛執行時期
  • src/test-utils/plugin-registration.ts 中的截取/測試輔助函式
  • src/plugins/contracts/registry.ts 中的擁有權/合約斷言
  • docs/ 中的操作員/外掛文件

如果缺少其中任何一個介面,通常表示該功能尚未完全整合。

最小模式:

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

合約測試模式:

expect(findVideoGenerationProviderIdsForPlugin("openai")).toEqual(["openai"]);

這讓規則保持簡單:

  • core 擁有功能合約和編排
  • 供應商外掛擁有供應商實作
  • 功能/通道外掛使用執行時期輔助程式
  • 合約測試保持擁有權明確