Langsung ke konten utama
Untuk model kapabilitas publik, bentuk Plugin, dan kontrak kepemilikan/eksekusi, lihat Arsitektur Plugin. Halaman ini adalah referensi untuk mekanika internal: alur pemuatan, registri, kait waktu jalan, rute HTTP Gateway, jalur impor, dan tabel skema.

Alur pemuatan

Saat mulai berjalan, OpenClaw secara garis besar melakukan ini:
  1. menemukan akar Plugin kandidat
  2. membaca manifes bundel native atau kompatibel dan metadata paket
  3. menolak kandidat yang tidak aman
  4. menormalkan konfigurasi Plugin (plugins.enabled, allow, deny, entries, slots, load.paths)
  5. menentukan pengaktifan untuk setiap kandidat
  6. memuat modul native yang diaktifkan: modul bundel bawaan menggunakan pemuat native; sumber lokal TypeScript pihak ketiga menggunakan mekanisme cadangan darurat Jiti
  7. memanggil kait native register(api) dan mengumpulkan registrasi ke registri Plugin
  8. mengekspos registri ke permukaan perintah/waktu jalan
activate adalah alias lama untuk register — pemuat menyelesaikan mana pun yang tersedia (def.register ?? def.activate) dan memanggilnya pada titik yang sama. Semua Plugin bundel menggunakan register; pilih register untuk Plugin baru.
Gerbang keamanan terjadi sebelum eksekusi waktu jalan. Kandidat diblokir ketika entri keluar dari akar Plugin, jalurnya dapat ditulis oleh semua pengguna, atau kepemilikan jalur terlihat mencurigakan untuk Plugin non-bundel. Kandidat yang diblokir tetap terikat ke id Plugin-nya untuk diagnostik. Jika konfigurasi masih mereferensikan id itu, validasi melaporkan Plugin sebagai ada tetapi diblokir dan menunjuk kembali ke peringatan keamanan jalur alih-alih memperlakukan entri konfigurasi sebagai usang.

Perilaku mengutamakan manifes

Manifes adalah sumber kebenaran bidang kontrol. OpenClaw menggunakannya untuk:
  • mengidentifikasi Plugin
  • menemukan kanal/Skills/skema konfigurasi yang dideklarasikan atau kapabilitas bundel
  • memvalidasi plugins.entries.<id>.config
  • memperkaya label/placeholder Control UI
  • menampilkan metadata instalasi/katalog
  • mempertahankan deskriptor aktivasi dan penyiapan yang murah tanpa memuat waktu jalan Plugin
Untuk Plugin native, modul waktu jalan adalah bagian bidang data. Modul ini mendaftarkan perilaku aktual seperti kait, alat, perintah, atau alur penyedia. Blok manifes opsional activation dan setup tetap berada di bidang kontrol. Keduanya adalah deskriptor khusus metadata untuk perencanaan aktivasi dan penemuan penyiapan; keduanya tidak menggantikan registrasi waktu jalan, register(...), atau setupEntry. Konsumen aktivasi langsung pertama kini menggunakan petunjuk perintah, kanal, dan penyedia dari manifes untuk mempersempit pemuatan Plugin sebelum materialisasi registri yang lebih luas:
  • pemuatan CLI dipersempit ke Plugin yang memiliki perintah utama yang diminta
  • penyiapan kanal/resolusi Plugin dipersempit ke Plugin yang memiliki id kanal yang diminta
  • penyiapan/resolusi waktu jalan penyedia eksplisit dipersempit ke Plugin yang memiliki id penyedia yang diminta
  • perencanaan startup Gateway menggunakan activation.onStartup untuk impor startup eksplisit dan pengecualian startup; Plugin tanpa metadata startup hanya dimuat melalui pemicu aktivasi yang lebih sempit
Prapemuatan waktu jalan pada saat permintaan yang meminta cakupan all yang luas tetap menurunkan kumpulan id Plugin efektif eksplisit dari konfigurasi, perencanaan startup, kanal yang dikonfigurasi, slot, dan aturan pengaktifan otomatis. Jika kumpulan turunan itu kosong, OpenClaw memuat registri waktu jalan kosong alih-alih memperluas ke setiap Plugin yang dapat ditemukan. Perencana aktivasi mengekspos API khusus id untuk pemanggil yang sudah ada dan API rencana untuk diagnostik baru. Entri rencana melaporkan mengapa sebuah Plugin dipilih, memisahkan petunjuk perencana activation.* eksplisit dari cadangan kepemilikan manifes seperti providers, channels, commandAliases, setup.providers, contracts.tools, dan kait. Pemisahan alasan itu adalah batas kompatibilitas: metadata Plugin yang sudah ada tetap berfungsi, sementara kode baru dapat mendeteksi petunjuk luas atau perilaku cadangan tanpa mengubah semantik pemuatan waktu jalan. Penemuan penyiapan kini lebih memilih id yang dimiliki deskriptor seperti setup.providers dan setup.cliBackends untuk mempersempit Plugin kandidat sebelum kembali ke setup-api untuk Plugin yang masih membutuhkan kait waktu jalan pada saat penyiapan. Daftar penyiapan penyedia menggunakan providerAuthChoices manifes, pilihan penyiapan turunan deskriptor, dan metadata katalog instalasi tanpa memuat waktu jalan penyedia. setup.requiresRuntime: false eksplisit adalah batas khusus deskriptor; requiresRuntime yang dihilangkan mempertahankan cadangan setup-api lama untuk kompatibilitas. Jika lebih dari satu Plugin yang ditemukan mengklaim id penyedia penyiapan atau backend CLI ternormalisasi yang sama, pencarian penyiapan menolak pemilik yang ambigu alih-alih mengandalkan urutan penemuan. Ketika waktu jalan penyiapan memang dieksekusi, diagnostik registri melaporkan penyimpangan antara setup.providers / setup.cliBackends dan penyedia atau backend CLI yang didaftarkan oleh setup-api tanpa memblokir Plugin lama.

Batas cache Plugin

OpenClaw tidak menyimpan cache hasil penemuan Plugin atau data registri manifes langsung di balik jendela berbasis jam dinding. Instalasi, penyuntingan manifes, dan perubahan jalur pemuatan harus terlihat pada pembacaan metadata eksplisit berikutnya atau pembangunan ulang snapshot berikutnya. Parser file manifes boleh menyimpan cache tanda tangan file terbatas yang dikunci oleh jalur manifes yang dibuka, inode, ukuran, dan timestamp; cache itu hanya menghindari pemarsingan ulang byte yang tidak berubah dan tidak boleh menyimpan jawaban penemuan, registri, pemilik, atau kebijakan. Jalur cepat metadata yang aman adalah kepemilikan objek eksplisit, bukan cache tersembunyi. Jalur panas startup Gateway harus meneruskan PluginMetadataSnapshot saat ini, PluginLookUpTable turunan, atau registri manifes eksplisit melalui rantai panggilan. Validasi konfigurasi, pengaktifan otomatis startup, bootstrap Plugin, dan pemilihan penyedia dapat menggunakan ulang objek-objek itu selama mereka merepresentasikan konfigurasi dan inventaris Plugin saat ini. Pencarian penyiapan tetap merekonstruksi metadata manifes sesuai permintaan kecuali jalur penyiapan spesifik menerima registri manifes eksplisit; pertahankan itu sebagai cadangan jalur dingin alih-alih menambahkan cache pencarian tersembunyi. Ketika input berubah, bangun ulang dan ganti snapshot alih-alih memutasinya atau menyimpan salinan historis. Tampilan atas registri Plugin aktif dan pembantu bootstrap kanal bundel harus dihitung ulang dari registri/akar saat ini. Peta berumur pendek boleh digunakan di dalam satu panggilan untuk menghapus duplikasi pekerjaan atau menjaga reentri; peta tersebut tidak boleh menjadi cache metadata proses. Untuk pemuatan Plugin, lapisan cache persisten adalah pemuatan waktu jalan. Lapisan ini boleh menggunakan ulang status pemuat ketika kode atau artefak terpasang benar-benar dimuat, seperti:
  • PluginLoaderCacheState dan registri waktu jalan aktif yang kompatibel
  • cache jiti/modul dan cache pemuat permukaan publik yang digunakan untuk menghindari impor permukaan waktu jalan yang sama berulang kali
  • cache sistem file untuk artefak Plugin terpasang
  • peta per panggilan berumur pendek untuk normalisasi jalur atau resolusi duplikat
Cache tersebut adalah detail implementasi bidang data. Cache tersebut tidak boleh menjawab pertanyaan bidang kontrol seperti “Plugin mana yang memiliki penyedia ini?” kecuali pemanggil memang sengaja meminta pemuatan waktu jalan. Jangan tambahkan cache persisten atau berbasis jam dinding untuk:
  • hasil penemuan
  • registri manifes langsung
  • registri manifes yang direkonstruksi dari indeks Plugin terpasang
  • pencarian pemilik penyedia, penekanan model, kebijakan penyedia, atau metadata artefak publik
  • jawaban turunan manifes lainnya yang seharusnya membuat manifes yang berubah, indeks terpasang, atau jalur pemuatan terlihat pada pembacaan metadata berikutnya
Pemanggil yang membangun ulang metadata manifes dari indeks Plugin terpasang yang dipersistenkan merekonstruksi registri itu sesuai permintaan. Indeks terpasang adalah status bidang sumber yang tahan lama; itu bukan cache metadata dalam proses yang tersembunyi.

Model registri

Plugin yang dimuat tidak secara langsung memutasi global inti acak. Mereka mendaftar ke registri Plugin pusat. Registri melacak:
  • catatan Plugin (identitas, sumber, asal, status, diagnostik)
  • alat
  • kait lama dan kait bertipe
  • kanal
  • penyedia
  • handler RPC Gateway
  • rute HTTP
  • registrar CLI
  • layanan latar belakang
  • perintah yang dimiliki Plugin
Fitur inti kemudian membaca dari registri itu alih-alih berbicara langsung ke modul Plugin. Ini membuat pemuatan satu arah:
  • modul Plugin -> registrasi registri
  • waktu jalan inti -> konsumsi registri
Pemisahan itu penting untuk pemeliharaan. Artinya sebagian besar permukaan inti hanya membutuhkan satu titik integrasi: “baca registri”, bukan “perlakukan setiap modul Plugin secara khusus”.

Callback pengikatan percakapan

Plugin yang mengikat percakapan dapat bereaksi ketika sebuah persetujuan diselesaikan. Gunakan api.onConversationBindingResolved(...) untuk menerima callback setelah permintaan pengikatan disetujui atau ditolak:
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);
    });
  },
};
Field payload callback:
  • status: "approved" atau "denied"
  • decision: "allow-once", "allow-always", atau "deny"
  • binding: pengikatan yang diselesaikan untuk permintaan yang disetujui
  • request: ringkasan permintaan asli, petunjuk pelepasan, id pengirim, dan metadata percakapan
Callback ini hanya notifikasi. Callback ini tidak mengubah siapa yang diizinkan mengikat percakapan, dan berjalan setelah penanganan persetujuan inti selesai.

Kait waktu jalan penyedia

Plugin penyedia memiliki tiga lapisan:
  • Metadata manifes untuk pencarian murah sebelum waktu jalan: setup.providers[].envVars, kompatibilitas usang providerAuthEnvVars, providerAuthAliases, providerAuthChoices, dan channelEnvVars.
  • Kait waktu konfigurasi: catalog (discovery lama) ditambah applyConfigDefaults.
  • Kait waktu jalan: 40+ kait opsional yang mencakup autentikasi, resolusi model, pembungkusan stream, level berpikir, kebijakan replay, dan endpoint penggunaan. Lihat daftar lengkap di Urutan dan penggunaan kait.
OpenClaw tetap memiliki loop agen generik, failover, penanganan transkrip, dan kebijakan alat. Kait ini adalah permukaan ekstensi untuk perilaku spesifik penyedia tanpa memerlukan transport inferensi kustom utuh. Gunakan manifes setup.providers[].envVars ketika penyedia memiliki kredensial berbasis env yang harus dilihat jalur autentikasi/status/pemilih-model generik tanpa memuat waktu jalan Plugin. providerAuthEnvVars yang usang masih dibaca oleh adapter kompatibilitas selama jendela deprekasi, dan Plugin non-bundel yang menggunakannya menerima diagnostik manifes. Gunakan manifes providerAuthAliases ketika satu id penyedia harus menggunakan ulang env var, profil autentikasi, autentikasi berbasis konfigurasi, dan pilihan onboarding kunci API milik id penyedia lain. Gunakan manifes providerAuthChoices ketika permukaan CLI onboarding/pilihan-autentikasi harus mengetahui id pilihan penyedia, label grup, dan pengabelan autentikasi satu-flag sederhana tanpa memuat waktu jalan penyedia. Pertahankan envVars waktu jalan penyedia untuk petunjuk yang menghadap operator seperti label onboarding atau variabel penyiapan client-id/client-secret OAuth. Gunakan manifes channelEnvVars ketika sebuah kanal memiliki autentikasi atau penyiapan berbasis env yang harus dilihat oleh cadangan shell-env generik, pemeriksaan konfigurasi/status, atau prompt penyiapan tanpa memuat waktu jalan kanal.

Urutan dan penggunaan kait

Untuk Plugin model/penyedia, OpenClaw memanggil kait dalam urutan kasar ini. Kolom “Kapan digunakan” adalah panduan keputusan cepat. Field penyedia khusus kompatibilitas yang tidak lagi dipanggil OpenClaw, seperti ProviderPlugin.capabilities dan suppressBuiltInModel, sengaja tidak dicantumkan di sini.
#KaitFungsinyaKapan digunakan
1catalogMenerbitkan konfigurasi penyedia ke models.providers selama pembuatan models.jsonPenyedia memiliki katalog atau default URL dasar
2applyConfigDefaultsMenerapkan default konfigurasi global milik penyedia selama materialisasi konfigurasiDefault bergantung pada mode auth, env, atau semantik keluarga model penyedia
(pencarian model bawaan)OpenClaw mencoba jalur registry/katalog normal terlebih dahulu(bukan kait plugin)
3normalizeModelIdMenormalkan alias ID model legacy atau pratinjau sebelum pencarianPenyedia memiliki pembersihan alias sebelum resolusi model kanonis
4normalizeTransportMenormalkan api / baseUrl keluarga penyedia sebelum perakitan model generikPenyedia memiliki pembersihan transport untuk ID penyedia kustom dalam keluarga transport yang sama
5normalizeConfigMenormalkan models.providers.<id> sebelum resolusi runtime/penyediaPenyedia memerlukan pembersihan konfigurasi yang seharusnya berada di plugin; helper keluarga Google bawaan juga menopang entri konfigurasi Google yang didukung
6applyNativeStreamingUsageCompatMenerapkan penulisan ulang kompat native streaming-usage ke penyedia konfigurasiPenyedia memerlukan perbaikan metadata penggunaan streaming native berbasis endpoint
7resolveConfigApiKeyMenyelesaikan auth penanda env untuk penyedia konfigurasi sebelum pemuatan auth runtimePenyedia mengekspos kait resolusi kunci API penanda env milik sendiri
8resolveSyntheticAuthMemunculkan auth lokal/self-hosted atau berbasis konfigurasi tanpa menyimpan plaintextPenyedia dapat beroperasi dengan penanda kredensial sintetis/lokal
9resolveExternalAuthProfilesMelapisi profil auth eksternal milik penyedia; default persistence adalah runtime-only untuk kredensial milik CLI/aplikasiPenyedia menggunakan kembali kredensial auth eksternal tanpa menyimpan refresh token yang disalin; deklarasikan contracts.externalAuthProviders di manifes
10shouldDeferSyntheticProfileAuthMenurunkan prioritas placeholder profil sintetis tersimpan di belakang auth berbasis env/konfigurasiPenyedia menyimpan profil placeholder sintetis yang tidak boleh menang prioritas
11resolveDynamicModelFallback sinkron untuk ID model milik penyedia yang belum ada di registry lokalPenyedia menerima ID model upstream arbitrer
12prepareDynamicModelPemanasan asinkron, lalu resolveDynamicModel berjalan lagiPenyedia memerlukan metadata jaringan sebelum menyelesaikan ID yang tidak dikenal
13normalizeResolvedModelPenulisan ulang final sebelum runner tertanam menggunakan model yang sudah diselesaikanPenyedia memerlukan penulisan ulang transport tetapi tetap menggunakan transport inti
14normalizeToolSchemasMenormalkan skema tool sebelum runner tertanam melihatnyaPenyedia memerlukan pembersihan skema keluarga transport
15inspectToolSchemasMemunculkan diagnostik skema milik penyedia setelah normalisasiPenyedia menginginkan peringatan kata kunci tanpa mengajarkan aturan khusus penyedia ke inti
16resolveReasoningOutputModeMemilih kontrak output reasoning native vs bertagPenyedia memerlukan reasoning/output final bertag alih-alih field native
17prepareExtraParamsNormalisasi parameter request sebelum wrapper opsi stream generikPenyedia memerlukan parameter request default atau pembersihan parameter per penyedia
18createStreamFnSepenuhnya mengganti jalur stream normal dengan transport kustomPenyedia memerlukan protokol wire kustom, bukan hanya wrapper
20wrapStreamFnWrapper stream setelah wrapper generik diterapkanPenyedia memerlukan wrapper kompat header/body/model request tanpa transport kustom
21resolveTransportTurnStateMelampirkan header atau metadata transport native per giliranPenyedia ingin transport generik mengirim identitas giliran native penyedia
22resolveWebSocketSessionPolicyMelampirkan header WebSocket native atau kebijakan cool-down sesiPenyedia ingin transport WS generik menyesuaikan header sesi atau kebijakan fallback
23formatApiKeyPemformat profil auth: profil tersimpan menjadi string apiKey runtimePenyedia menyimpan metadata auth tambahan dan memerlukan bentuk token runtime kustom
24refreshOAuthOverride refresh OAuth untuk endpoint refresh kustom atau kebijakan kegagalan refreshPenyedia tidak cocok dengan refresher OpenClaw bersama
25buildAuthDoctorHintPetunjuk perbaikan yang ditambahkan saat refresh OAuth gagalPenyedia memerlukan panduan perbaikan auth milik penyedia setelah kegagalan refresh
26matchesContextOverflowErrorPencocok overflow context-window milik penyediaPenyedia memiliki error overflow mentah yang akan terlewat oleh heuristik generik
27classifyFailoverReasonKlasifikasi alasan failover milik penyediaPenyedia dapat memetakan error API/transport mentah ke rate-limit/overload/dll
28isCacheTtlEligibleKebijakan prompt-cache untuk penyedia proxy/backhaulPenyedia memerlukan gating TTL cache khusus proxy
29buildMissingAuthMessagePengganti pesan pemulihan auth hilang generikPenyedia memerlukan petunjuk pemulihan auth hilang khusus penyedia
30augmentModelCatalogBaris katalog sintetis/final yang ditambahkan setelah discoveryPenyedia memerlukan baris forward-compat sintetis di models list dan pemilih
31resolveThinkingProfileKumpulan level /think khusus model, label tampilan, dan defaultPenyedia mengekspos tangga thinking kustom atau label biner untuk model terpilih
32isBinaryThinkingKait kompatibilitas toggle reasoning hidup/matiPenyedia hanya mengekspos thinking biner hidup/mati
33supportsXHighThinkingKait kompatibilitas dukungan reasoning xhighPenyedia menginginkan xhigh hanya pada sebagian model
34resolveDefaultThinkingLevelKait kompatibilitas level /think defaultPenyedia memiliki kebijakan /think default untuk sebuah keluarga model
35isModernModelRefPencocok model modern untuk filter profil live dan pemilihan smokePenyedia memiliki pencocokan model pilihan live/smoke
36prepareRuntimeAuthMenukar kredensial terkonfigurasi menjadi token/kunci runtime aktual tepat sebelum inferensiPenyedia memerlukan pertukaran token atau kredensial request berumur pendek
37resolveUsageAuthMenyelesaikan kredensial penggunaan/billing untuk /usage dan permukaan status terkaitPenyedia memerlukan parsing token penggunaan/kuota kustom atau kredensial penggunaan yang berbeda
38fetchUsageSnapshotMengambil dan menormalkan snapshot penggunaan/kuota khusus provider setelah auth diselesaikanProvider memerlukan endpoint penggunaan khusus provider atau parser payload
39createEmbeddingProviderMembangun adapter embedding milik provider untuk memori/pencarianPerilaku embedding memori menjadi milik Plugin provider
40buildReplayPolicyMengembalikan kebijakan replay yang mengontrol penanganan transkrip untuk providerProvider memerlukan kebijakan transkrip khusus (misalnya, penghapusan thinking-block)
41sanitizeReplayHistoryMenulis ulang riwayat replay setelah pembersihan transkrip generikProvider memerlukan penulisan ulang replay khusus provider di luar helper compaction bersama
42validateReplayTurnsValidasi akhir replay-turn atau pembentukan ulang sebelum runner tertanamTransport provider memerlukan validasi giliran yang lebih ketat setelah sanitasi generik
43onModelSelectedMenjalankan efek samping pasca-pemilihan milik providerProvider memerlukan telemetri atau state milik provider saat model menjadi aktif
normalizeModelId, normalizeTransport, dan normalizeConfig pertama-tama memeriksa Plugin penyedia yang cocok, lalu meneruskan ke Plugin penyedia lain yang mendukung hook hingga ada yang benar-benar mengubah id model atau transport/config. Ini menjaga shim penyedia alias/kompat tetap berfungsi tanpa mengharuskan pemanggil mengetahui Plugin bawaan mana yang memiliki penulisan ulang tersebut. Jika tidak ada hook penyedia yang menulis ulang entri config keluarga Google yang didukung, normalizer config Google bawaan tetap menerapkan pembersihan kompatibilitas tersebut. Jika penyedia membutuhkan protokol wire yang sepenuhnya khusus atau eksekutor permintaan khusus, itu adalah kelas ekstensi yang berbeda. Hook ini ditujukan untuk perilaku penyedia yang masih berjalan pada loop inferensi normal OpenClaw. resolveUsageAuth memutuskan apakah OpenClaw harus memanggil fetchUsageSnapshot atau kembali ke resolusi kredensial generik untuk permukaan penggunaan/status. Kembalikan { token, accountId? } ketika penyedia memiliki kredensial penggunaan, kembalikan { handled: true } ketika auth penggunaan milik penyedia telah menangani permintaan dan harus menekan fallback API-key/OAuth generik, dan kembalikan null atau undefined ketika penyedia tidak menangani auth penggunaan.

Contoh penyedia

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

Contoh bawaan

Plugin penyedia bawaan mengombinasikan hook di atas agar sesuai dengan kebutuhan catalog, auth, pemikiran, replay, dan penggunaan tiap vendor. Kumpulan hook otoritatif berada bersama setiap Plugin di bawah extensions/; halaman ini mengilustrasikan bentuknya, bukan mencerminkan daftar tersebut.
OpenRouter, Kilocode, Z.AI, xAI mendaftarkan catalog plus resolveDynamicModel / prepareDynamicModel agar mereka dapat menampilkan id model upstream sebelum catalog statis OpenClaw.
GitHub Copilot, Gemini CLI, ChatGPT Codex, MiniMax, Xiaomi, z.ai memasangkan prepareRuntimeAuth atau formatApiKey dengan resolveUsageAuth + fetchUsageSnapshot untuk memiliki pertukaran token dan integrasi /usage.
Keluarga bernama bersama (google-gemini, passthrough-gemini, anthropic-by-model, hybrid-anthropic-openai) memungkinkan penyedia ikut serta dalam kebijakan transkrip melalui buildReplayPolicy, alih-alih setiap Plugin mengimplementasikan ulang pembersihan.
byteplus, cloudflare-ai-gateway, huggingface, kimi-coding, nvidia, qianfan, synthetic, together, venice, vercel-ai-gateway, dan volcengine hanya mendaftarkan catalog dan menggunakan loop inferensi bersama.
Header beta, /fast / serviceTier, dan context1m berada di dalam seam publik Plugin Anthropic api.ts / contract-api.ts (wrapAnthropicProviderStream, resolveAnthropicBetas, resolveAnthropicFastMode, resolveAnthropicServiceTier), bukan di dalam SDK generik.

Helper runtime

Plugin dapat mengakses helper inti tertentu melalui api.runtime. Untuk 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,
});
Catatan:
  • textToSpeech mengembalikan payload output TTS inti normal untuk permukaan file/catatan suara.
  • Menggunakan konfigurasi messages.tts inti dan pemilihan penyedia.
  • Mengembalikan buffer audio PCM + laju sampel. Plugin harus melakukan resample/encode untuk penyedia.
  • listVoices bersifat opsional per penyedia. Gunakan untuk pemilih suara milik vendor atau alur penyiapan.
  • Daftar suara dapat menyertakan metadata yang lebih kaya seperti locale, gender, dan tag kepribadian untuk pemilih sadar penyedia.
  • OpenAI dan ElevenLabs mendukung telephony saat ini. Microsoft tidak.
Plugin juga dapat mendaftarkan penyedia speech melalui 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,
    };
  },
});
Catatan:
  • Pertahankan kebijakan TTS, fallback, dan pengiriman balasan di inti.
  • Gunakan penyedia speech untuk perilaku sintesis milik vendor.
  • Input Microsoft lama edge dinormalisasi ke id penyedia microsoft.
  • Model kepemilikan yang disukai berorientasi perusahaan: satu Plugin vendor dapat memiliki penyedia teks, speech, gambar, dan media masa depan saat OpenClaw menambahkan kontrak kapabilitas tersebut.
Untuk pemahaman gambar/audio/video, Plugin mendaftarkan satu penyedia pemahaman-media bertipe, bukan kantong key/value generik:
api.registerMediaUnderstandingProvider({
  id: "google",
  capabilities: ["image", "audio", "video"],
  describeImage: async (req) => ({ text: "..." }),
  transcribeAudio: async (req) => ({ text: "..." }),
  describeVideo: async (req) => ({ text: "..." }),
});
Catatan:
  • Pertahankan orkestrasi, fallback, config, dan wiring channel di inti.
  • Pertahankan perilaku vendor di Plugin penyedia.
  • Ekspansi aditif harus tetap bertipe: metode opsional baru, field hasil opsional baru, kapabilitas opsional baru.
  • Pembuatan video sudah mengikuti pola yang sama:
    • inti memiliki kontrak kapabilitas dan helper runtime
    • Plugin vendor mendaftarkan api.registerVideoGenerationProvider(...)
    • Plugin fitur/channel mengonsumsi api.runtime.videoGeneration.*
Untuk helper runtime pemahaman-media, Plugin dapat memanggil:
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,
});
Untuk transkripsi audio, Plugin dapat menggunakan runtime pemahaman-media atau alias STT yang lebih lama:
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",
});
Catatan:
  • api.runtime.mediaUnderstanding.* adalah permukaan bersama yang disukai untuk pemahaman gambar/audio/video.
  • extractStructuredWithModel(...) adalah seam yang menghadap Plugin untuk ekstraksi milik penyedia yang terbatas dan mengutamakan gambar. Sertakan setidaknya satu input gambar; input teks adalah konteks tambahan. Plugin produk memiliki route dan skemanya, sementara OpenClaw memiliki batas penyedia/runtime.
  • Menggunakan konfigurasi audio pemahaman-media inti (tools.media.audio) dan urutan fallback penyedia.
  • Mengembalikan { text: undefined } ketika tidak ada output transkripsi yang dihasilkan (misalnya input dilewati/tidak didukung).
  • api.runtime.stt.transcribeAudioFile(...) tetap sebagai alias kompatibilitas.
Plugin juga dapat meluncurkan run subagent latar belakang melalui 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,
});
Catatan:
  • provider dan model adalah override per-run opsional, bukan perubahan sesi persisten.
  • OpenClaw hanya menghormati field override tersebut untuk pemanggil tepercaya.
  • Untuk run fallback milik Plugin, operator harus ikut serta dengan plugins.entries.<id>.subagent.allowModelOverride: true.
  • Gunakan plugins.entries.<id>.subagent.allowedModels untuk membatasi Plugin tepercaya ke target kanonis provider/model tertentu, atau "*" untuk mengizinkan target apa pun secara eksplisit.
  • Run subagent Plugin yang tidak tepercaya tetap berfungsi, tetapi permintaan override ditolak alih-alih diam-diam melakukan fallback.
  • Sesi subagent yang dibuat Plugin ditandai dengan id Plugin pembuat. Fallback api.runtime.subagent.deleteSession(...) hanya dapat menghapus sesi yang dimiliki tersebut; penghapusan sesi arbitrer tetap membutuhkan permintaan Gateway berscope admin.
Untuk pencarian web, Plugin dapat mengonsumsi helper runtime bersama alih-alih masuk ke wiring tool agent:
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 juga dapat mendaftarkan penyedia pencarian web melalui api.registerWebSearchProvider(...). Catatan:
  • Pertahankan pemilihan penyedia, resolusi kredensial, dan semantik permintaan bersama di inti.
  • Gunakan penyedia pencarian web untuk transport pencarian khusus vendor.
  • api.runtime.webSearch.* adalah permukaan bersama yang disukai untuk Plugin fitur/channel yang membutuhkan perilaku pencarian tanpa bergantung pada wrapper tool agent.

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(...): hasilkan gambar menggunakan rantai penyedia pembuatan-gambar yang dikonfigurasi.
  • listProviders(...): cantumkan penyedia pembuatan-gambar yang tersedia dan kapabilitasnya.

Route HTTP Gateway

Plugin dapat mengekspos endpoint HTTP dengan api.registerHttpRoute(...).
api.registerHttpRoute({
  path: "/acme/webhook",
  auth: "plugin",
  match: "exact",
  handler: async (_req, res) => {
    res.statusCode = 200;
    res.end("ok");
    return true;
  },
});
Field route:
  • path: path rute di bawah server HTTP gateway.
  • auth: wajib. Gunakan "gateway" untuk mewajibkan autentikasi gateway normal, atau "plugin" untuk autentikasi/verifikasi webhook yang dikelola plugin.
  • match: opsional. "exact" (bawaan) atau "prefix".
  • replaceExisting: opsional. Memungkinkan plugin yang sama mengganti pendaftaran rute miliknya yang sudah ada.
  • handler: kembalikan true ketika rute menangani permintaan.
Catatan:
  • api.registerHttpHandler(...) telah dihapus dan akan menyebabkan kesalahan pemuatan plugin. Gunakan api.registerHttpRoute(...) sebagai gantinya.
  • Rute plugin harus mendeklarasikan auth secara eksplisit.
  • Konflik path + match yang persis ditolak kecuali replaceExisting: true, dan satu plugin tidak dapat mengganti rute plugin lain.
  • Rute yang bertumpang tindih dengan tingkat auth berbeda ditolak. Pertahankan rantai fallthrough exact/prefix hanya pada tingkat auth yang sama.
  • Rute auth: "plugin" tidak menerima cakupan runtime operator secara otomatis. Rute ini untuk webhook/verifikasi tanda tangan yang dikelola plugin, bukan panggilan pembantu Gateway yang memiliki hak istimewa.
  • Rute auth: "gateway" berjalan di dalam cakupan runtime permintaan Gateway, tetapi cakupan tersebut sengaja konservatif:
    • autentikasi bearer rahasia bersama (gateway.auth.mode = "token" / "password") menjaga cakupan runtime rute plugin tetap tersemat ke operator.write, meskipun pemanggil mengirim x-openclaw-scopes
    • mode HTTP tepercaya yang membawa identitas (misalnya trusted-proxy atau gateway.auth.mode = "none" pada ingress privat) menghormati x-openclaw-scopes hanya ketika header tersebut hadir secara eksplisit
    • jika x-openclaw-scopes tidak ada pada permintaan rute plugin yang membawa identitas tersebut, cakupan runtime kembali ke operator.write
  • Aturan praktis: jangan menganggap rute plugin dengan auth gateway sebagai permukaan admin implisit. Jika rute Anda membutuhkan perilaku khusus admin, wajibkan mode auth yang membawa identitas dan dokumentasikan kontrak header eksplisit x-openclaw-scopes.

Path impor Plugin SDK

Gunakan subpath SDK yang sempit alih-alih barrel root monolitik openclaw/plugin-sdk saat menulis plugin baru. Subpath inti:
SubpathTujuan
openclaw/plugin-sdk/plugin-entryPrimitif pendaftaran plugin
openclaw/plugin-sdk/channel-corePembantu entri/build channel
openclaw/plugin-sdk/corePembantu bersama generik dan kontrak payung
openclaw/plugin-sdk/config-schemaSkema Zod root openclaw.json (OpenClawSchema)
Plugin channel memilih dari keluarga seam yang sempit — channel-setup, setup-runtime, setup-tools, channel-pairing, channel-contract, channel-feedback, channel-inbound, channel-outbound, command-auth, secret-input, webhook-ingress, channel-targets, dan channel-actions. Perilaku persetujuan sebaiknya dikonsolidasikan pada satu kontrak approvalCapability, bukan dicampur di berbagai field plugin yang tidak terkait. Lihat Plugin channel. Pembantu runtime dan config berada di bawah subpath *-runtime terfokus yang cocok (approval-runtime, agent-runtime, lazy-runtime, directory-runtime, text-runtime, runtime-store, system-event-runtime, heartbeat-runtime, channel-activity-runtime, dll.). Utamakan config-contracts, plugin-config-runtime, runtime-config-snapshot, dan config-mutation alih-alih barrel kompatibilitas config-runtime yang luas.
openclaw/plugin-sdk/channel-runtime, openclaw/plugin-sdk/channel-lifecycle, facade pembantu channel kecil, openclaw/plugin-sdk/outbound-runtime, openclaw/plugin-sdk/outbound-send-deps, openclaw/plugin-sdk/config-runtime, dan openclaw/plugin-sdk/infra-runtime adalah shim kompatibilitas yang tidak digunakan lagi untuk plugin lama. Kode baru sebaiknya mengimpor primitif generik yang lebih sempit sebagai gantinya.
Titik masuk internal repo (per root paket plugin yang dibundel):
  • index.js — entri plugin yang dibundel
  • api.js — barrel pembantu/tipe
  • runtime-api.js — barrel khusus runtime
  • setup-entry.js — entri plugin setup
Plugin eksternal sebaiknya hanya mengimpor subpath openclaw/plugin-sdk/*. Jangan pernah mengimpor src/* milik paket plugin lain dari core atau dari plugin lain. Titik masuk yang dimuat facade mengutamakan snapshot config runtime aktif ketika ada, lalu fallback ke file config yang telah di-resolve di disk. Subpath khusus kapabilitas seperti image-generation, media-understanding, dan speech ada karena plugin yang dibundel menggunakannya saat ini. Subpath tersebut tidak otomatis menjadi kontrak eksternal jangka panjang yang dibekukan — periksa halaman referensi SDK yang relevan saat mengandalkannya.

Skema alat pesan

Plugin sebaiknya memiliki kontribusi skema describeMessageTool(...) khusus channel untuk primitif non-pesan seperti reaksi, tanda baca, dan jajak pendapat. Presentasi kirim bersama sebaiknya menggunakan kontrak MessagePresentation generik alih-alih field tombol, komponen, blok, atau kartu native penyedia. Lihat Presentasi Pesan untuk kontrak, aturan fallback, pemetaan penyedia, dan checklist penulis plugin. Plugin yang mampu mengirim mendeklarasikan apa yang dapat mereka render melalui kapabilitas pesan:
  • presentation untuk blok presentasi semantik (text, context, divider, buttons, select)
  • delivery-pin untuk permintaan pengiriman yang disematkan
Core memutuskan apakah akan merender presentasi secara native atau menurunkannya menjadi teks. Jangan mengekspos escape hatch UI native penyedia dari alat pesan generik. Pembantu SDK yang tidak digunakan lagi untuk skema native legacy tetap diekspor untuk plugin pihak ketiga yang ada, tetapi plugin baru sebaiknya tidak menggunakannya.

Resolusi target channel

Plugin channel sebaiknya memiliki semantik target khusus channel. Jaga host outbound bersama tetap generik dan gunakan permukaan adapter messaging untuk aturan penyedia:
  • messaging.inferTargetChatType({ to }) memutuskan apakah target yang dinormalisasi harus diperlakukan sebagai direct, group, atau channel sebelum lookup direktori.
  • messaging.targetResolver.looksLikeId(raw, normalized) memberi tahu core apakah input harus langsung melewati ke resolusi mirip id alih-alih pencarian direktori.
  • messaging.targetResolver.reservedLiterals mencantumkan kata polos yang merupakan referensi channel/sesi untuk penyedia tersebut. Resolusi mempertahankan entri direktori yang dikonfigurasi sebelum menolak literal cadangan, lalu gagal tertutup pada lookup direktori yang luput.
  • messaging.targetResolver.resolveTarget(...) adalah fallback plugin ketika core membutuhkan resolusi akhir milik penyedia setelah normalisasi atau setelah lookup direktori yang luput.
  • messaging.resolveOutboundSessionRoute(...) memiliki konstruksi rute sesi khusus penyedia setelah target di-resolve.
Pembagian yang direkomendasikan:
  • Gunakan inferTargetChatType untuk keputusan kategori yang sebaiknya terjadi sebelum mencari peer/grup.
  • Gunakan looksLikeId untuk pemeriksaan “perlakukan ini sebagai id target eksplisit/native”.
  • Gunakan resolveTarget untuk fallback normalisasi khusus penyedia, bukan untuk pencarian direktori yang luas.
  • Simpan id native penyedia seperti id chat, id thread, JID, handle, dan id room di dalam nilai target atau params khusus penyedia, bukan di field SDK generik.

Direktori berbasis config

Plugin yang menurunkan entri direktori dari config sebaiknya menjaga logika tersebut di dalam plugin dan menggunakan kembali pembantu bersama dari openclaw/plugin-sdk/directory-runtime. Gunakan ini ketika channel membutuhkan peer/grup berbasis config seperti:
  • peer DM yang digerakkan allowlist
  • peta channel/grup yang dikonfigurasi
  • fallback direktori statis bercakupan akun
Helper bersama di directory-runtime hanya menangani operasi generik:
  • pemfilteran kueri
  • penerapan batas
  • helper deduplikasi/normalisasi
  • pembuatan ChannelDirectoryEntry[]
Inspeksi akun spesifik channel dan normalisasi id harus tetap berada di implementasi Plugin.

Katalog penyedia

Plugin penyedia dapat menentukan katalog model untuk inferensi dengan registerProvider({ catalog: { run(...) { ... } } }). catalog.run(...) mengembalikan bentuk yang sama dengan yang ditulis OpenClaw ke models.providers:
  • { provider } untuk satu entri penyedia
  • { providers } untuk beberapa entri penyedia
Gunakan catalog saat Plugin memiliki id model spesifik penyedia, default URL dasar, atau metadata model yang dibatasi autentikasi. catalog.order mengontrol kapan katalog Plugin digabungkan relatif terhadap penyedia implisit bawaan OpenClaw:
  • simple: penyedia API-key biasa atau berbasis env
  • profile: penyedia yang muncul saat profil autentikasi ada
  • paired: penyedia yang menyintesis beberapa entri penyedia terkait
  • late: lintasan terakhir, setelah penyedia implisit lainnya
Penyedia yang lebih akhir menang saat terjadi tabrakan kunci, sehingga Plugin dapat sengaja menimpa entri penyedia bawaan dengan id penyedia yang sama. Plugin juga dapat menerbitkan baris model baca-saja melalui api.registerModelCatalogProvider({ provider, kinds, staticCatalog, liveCatalog }). Ini adalah jalur ke depan untuk permukaan daftar/bantuan/pemilih dan mendukung baris text, image_generation, video_generation, dan music_generation. Plugin penyedia tetap memiliki panggilan endpoint live, pertukaran token, dan pemetaan respons vendor; core memiliki bentuk baris umum, label sumber, dan pemformatan bantuan alat media. Registrasi penyedia pembuatan media menyintesis baris katalog statis secara otomatis dari defaultModel, models, dan capabilities. Kompatibilitas:
  • discovery masih berfungsi sebagai alias lama, tetapi mengeluarkan peringatan penghentian
  • jika catalog dan discovery sama-sama terdaftar, OpenClaw menggunakan catalog
  • augmentModelCatalog tidak lagi disarankan; penyedia bawaan sebaiknya menerbitkan baris tambahan melalui registerModelCatalogProvider

Inspeksi channel baca-saja

Jika Plugin Anda mendaftarkan channel, sebaiknya implementasikan plugin.config.inspectAccount(cfg, accountId) bersama resolveAccount(...). Mengapa:
  • resolveAccount(...) adalah jalur runtime. Jalur ini boleh mengasumsikan kredensial sudah sepenuhnya tersedia dan dapat gagal cepat saat rahasia wajib tidak ada.
  • Jalur perintah baca-saja seperti openclaw status, openclaw status --all, openclaw channels status, openclaw channels resolve, dan alur doctor/perbaikan konfigurasi tidak seharusnya perlu menyiapkan kredensial runtime hanya untuk mendeskripsikan konfigurasi.
Perilaku inspectAccount(...) yang disarankan:
  • Hanya kembalikan status akun deskriptif.
  • Pertahankan enabled dan configured.
  • Sertakan kolom sumber/status kredensial bila relevan, seperti:
    • tokenSource, tokenStatus
    • botTokenSource, botTokenStatus
    • appTokenSource, appTokenStatus
    • signingSecretSource, signingSecretStatus
  • Anda tidak perlu mengembalikan nilai token mentah hanya untuk melaporkan ketersediaan baca-saja. Mengembalikan tokenStatus: "available" (dan kolom sumber yang sesuai) sudah cukup untuk perintah bergaya status.
  • Gunakan configured_unavailable saat kredensial dikonfigurasi melalui SecretRef tetapi tidak tersedia di jalur perintah saat ini.
Ini memungkinkan perintah baca-saja melaporkan “dikonfigurasi tetapi tidak tersedia di jalur perintah ini” alih-alih crash atau salah melaporkan akun sebagai belum dikonfigurasi.

Paket package

Direktori Plugin dapat menyertakan package.json dengan openclaw.extensions:
{
  "name": "my-pack",
  "openclaw": {
    "extensions": ["./src/safety.ts", "./src/tools.ts"],
    "setupEntry": "./src/setup-entry.ts"
  }
}
Setiap entri menjadi Plugin. Jika pack mencantumkan beberapa ekstensi, id Plugin menjadi name/<fileBase>. Jika Plugin Anda mengimpor dependensi npm, instal dependensi tersebut di direktori itu agar node_modules tersedia (npm install / pnpm install). Batasan keamanan: setiap entri openclaw.extensions harus tetap berada di dalam direktori Plugin setelah resolusi symlink. Entri yang keluar dari direktori package akan ditolak. Catatan keamanan: openclaw plugins install menginstal dependensi Plugin dengan npm install --omit=dev --ignore-scripts lokal proyek (tanpa skrip siklus hidup, tanpa dependensi dev saat runtime), mengabaikan pengaturan npm install global yang diwarisi. Jaga pohon dependensi Plugin tetap “JS/TS murni” dan hindari paket yang memerlukan build postinstall. Opsional: openclaw.setupEntry dapat menunjuk ke modul ringan khusus penyiapan. Ketika OpenClaw memerlukan permukaan penyiapan untuk Plugin kanal yang dinonaktifkan, atau ketika Plugin kanal diaktifkan tetapi masih belum dikonfigurasi, OpenClaw memuat setupEntry alih-alih entri Plugin penuh. Ini membuat startup dan penyiapan lebih ringan ketika entri Plugin utama Anda juga merangkai alat, hook, atau kode lain yang hanya digunakan saat runtime. Opsional: openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen dapat mengikutsertakan Plugin kanal ke jalur setupEntry yang sama selama fase startup pra-dengar Gateway, bahkan ketika kanal sudah dikonfigurasi. Gunakan ini hanya ketika setupEntry sepenuhnya mencakup permukaan startup yang harus ada sebelum Gateway mulai mendengarkan. Dalam praktiknya, ini berarti entri penyiapan harus mendaftarkan setiap kapabilitas milik kanal yang bergantung pada startup, seperti:
  • pendaftaran kanal itu sendiri
  • rute HTTP apa pun yang harus tersedia sebelum Gateway mulai mendengarkan
  • metode, alat, atau layanan Gateway apa pun yang harus ada selama jendela yang sama
Jika entri penuh Anda masih memiliki kapabilitas startup wajib apa pun, jangan aktifkan flag ini. Pertahankan Plugin pada perilaku default dan biarkan OpenClaw memuat entri penuh selama startup. Kanal yang dibundel juga dapat menerbitkan helper permukaan kontrak khusus penyiapan yang dapat dikonsultasikan inti sebelum runtime kanal penuh dimuat. Permukaan promosi penyiapan saat ini adalah:
  • singleAccountKeysToMove
  • namedAccountPromotionKeys
  • resolveSingleAccountPromotionTarget(...)
Inti menggunakan permukaan itu ketika perlu mempromosikan konfigurasi kanal akun tunggal lama ke channels.<id>.accounts.* tanpa memuat entri Plugin penuh. Matrix adalah contoh bundel saat ini: Matrix hanya memindahkan kunci auth/bootstrap ke akun bernama yang dipromosikan ketika akun bernama sudah ada, dan dapat mempertahankan kunci akun default non-kanonis yang dikonfigurasi alih-alih selalu membuat accounts.default. Adapter patch penyiapan tersebut menjaga penemuan permukaan kontrak bundel tetap malas. Waktu impor tetap ringan; permukaan promosi dimuat hanya saat pertama kali digunakan, alih-alih memasuki ulang startup kanal bundel saat impor modul. Ketika permukaan startup tersebut mencakup metode RPC Gateway, pertahankan dengan prefiks khusus Plugin. Namespace admin inti (config.*, exec.approvals.*, wizard.*, update.*) tetap dicadangkan dan selalu diselesaikan ke operator.admin, bahkan jika Plugin meminta cakupan yang lebih sempit. Contoh:
{
  "name": "@scope/my-channel",
  "openclaw": {
    "extensions": ["./index.ts"],
    "setupEntry": "./setup-entry.ts",
    "startup": {
      "deferConfiguredChannelFullLoadUntilAfterListen": true
    }
  }
}

Metadata katalog kanal

Plugin kanal dapat mengiklankan metadata penyiapan/penemuan melalui openclaw.channel dan petunjuk instal melalui openclaw.install. Ini menjaga data katalog inti tetap kosong. Contoh:
{
  "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"
    }
  }
}
Field openclaw.channel yang berguna di luar contoh minimal:
  • detailLabel: label sekunder untuk permukaan katalog/status yang lebih kaya
  • docsLabel: timpa teks tautan untuk tautan docs
  • preferOver: id Plugin/kanal berprioritas lebih rendah yang harus dikalahkan entri katalog ini
  • selectionDocsPrefix, selectionDocsOmitLabel, selectionExtras: kontrol salinan permukaan pemilihan
  • markdownCapable: menandai kanal sebagai mampu markdown untuk keputusan pemformatan keluar
  • exposure.configured: sembunyikan kanal dari permukaan daftar kanal yang dikonfigurasi ketika disetel ke false
  • exposure.setup: sembunyikan kanal dari pemilih penyiapan/konfigurasi interaktif ketika disetel ke false
  • exposure.docs: tandai kanal sebagai internal/privat untuk permukaan navigasi docs
  • showConfigured / showInSetup: alias lama yang masih diterima untuk kompatibilitas; utamakan exposure
  • quickstartAllowFrom: ikutkan kanal ke alur quickstart standar allowFrom
  • forceAccountBinding: wajibkan pengikatan akun eksplisit bahkan ketika hanya ada satu akun
  • preferSessionLookupForAnnounceTarget: utamakan pencarian sesi saat menyelesaikan target pengumuman
OpenClaw juga dapat menggabungkan katalog kanal eksternal (misalnya, ekspor registry MPM). Letakkan file JSON di salah satu dari:
  • ~/.openclaw/mpm/plugins.json
  • ~/.openclaw/mpm/catalog.json
  • ~/.openclaw/plugins/catalog.json
Atau arahkan OPENCLAW_PLUGIN_CATALOG_PATHS (atau OPENCLAW_MPM_CATALOG_PATHS) ke satu atau beberapa file JSON (dipisahkan koma/titik koma/PATH). Setiap file harus berisi { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }. Parser juga menerima "packages" atau "plugins" sebagai alias lama untuk kunci "entries". Entri katalog kanal yang dihasilkan dan entri katalog instal penyedia mengekspos fakta sumber instal yang dinormalisasi di samping blok mentah openclaw.install. Fakta yang dinormalisasi mengidentifikasi apakah spesifikasi npm adalah versi persis atau selektor mengambang, apakah metadata integritas yang diharapkan tersedia, dan apakah jalur sumber lokal juga tersedia. Ketika identitas katalog/paket diketahui, fakta yang dinormalisasi memperingatkan jika nama paket npm yang diurai menyimpang dari identitas tersebut. Fakta tersebut juga memperingatkan ketika defaultChoice tidak valid atau menunjuk ke sumber yang tidak tersedia, dan ketika metadata integritas npm tersedia tanpa sumber npm yang valid. Konsumen harus memperlakukan installSource sebagai field opsional aditif sehingga entri buatan tangan dan shim katalog tidak harus menyintesisnya. Ini memungkinkan onboarding dan diagnostik menjelaskan status bidang sumber tanpa mengimpor runtime Plugin. Entri npm eksternal resmi sebaiknya mengutamakan npmSpec persis plus expectedIntegrity. Nama paket polos dan dist-tag masih berfungsi untuk kompatibilitas, tetapi memunculkan peringatan bidang sumber sehingga katalog dapat bergerak menuju instal yang dipin dan diperiksa integritasnya tanpa merusak Plugin yang ada. Ketika onboarding menginstal dari jalur katalog lokal, onboarding mencatat entri indeks Plugin terkelola dengan source: "path" dan sourcePath relatif workspace bila memungkinkan. Jalur pemuatan operasional absolut tetap berada di plugins.load.paths; catatan instal menghindari duplikasi jalur workstation lokal ke dalam konfigurasi berumur panjang. Ini membuat instal pengembangan lokal tetap terlihat oleh diagnostik bidang sumber tanpa menambahkan permukaan pengungkapan jalur sistem file mentah kedua. Baris SQLite installed_plugin_index yang dipersisten adalah sumber kebenaran instal dan dapat disegarkan tanpa memuat modul runtime Plugin. Map installRecords miliknya tahan lama bahkan ketika manifes Plugin hilang atau tidak valid; payload plugins miliknya adalah tampilan manifes yang dapat dibangun ulang.

Plugin mesin konteks

Plugin mesin konteks memiliki orkestrasi konteks sesi untuk ingest, perakitan, dan Compaction. Daftarkan dari Plugin Anda dengan api.registerContextEngine(id, factory), lalu pilih mesin aktif dengan plugins.slots.contextEngine. Gunakan ini ketika Plugin Anda perlu mengganti atau memperluas pipeline konteks default, bukan sekadar menambahkan pencarian memori atau hook.
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 mengekspos nilai opsional config, agentDir, dan workspaceDir untuk inisialisasi waktu konstruksi. assemble() dapat mengembalikan contextProjection ketika harness aktif memiliki thread backend persisten. Hilangkan untuk proyeksi per giliran lama. Kembalikan { mode: "thread_bootstrap", epoch } ketika konteks yang dirakit harus disuntikkan sekali ke thread backend dan digunakan ulang sampai epoch berubah. Ubah epoch setelah konteks semantik mesin berubah, seperti setelah pass Compaction milik mesin. Host dapat mempertahankan metadata panggilan alat, bentuk input, dan hasil alat yang telah direda dalam proyeksi thread-bootstrap agar thread backend baru mempertahankan kontinuitas alat tanpa menyalin payload mentah yang mengandung rahasia. Jika mesin Anda tidak memiliki algoritma Compaction, tetap implementasikan compact() dan delegasikan secara eksplisit:
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);
    },
  }));
}

Menambahkan kapabilitas baru

Ketika Plugin memerlukan perilaku yang tidak cocok dengan API saat ini, jangan melewati sistem Plugin dengan akses privat ke dalam. Tambahkan kapabilitas yang hilang. Urutan yang direkomendasikan:
  1. definisikan kontrak inti Tentukan perilaku bersama apa yang harus dimiliki inti: kebijakan, fallback, penggabungan konfigurasi, siklus hidup, semantik yang berhadapan dengan kanal, dan bentuk helper runtime.
  2. tambahkan permukaan pendaftaran/runtime Plugin bertipe Perluas OpenClawPluginApi dan/atau api.runtime dengan permukaan kapabilitas bertipe paling kecil yang berguna.
  3. rangkai konsumen inti + kanal/fitur Kanal dan Plugin fitur harus mengonsumsi kapabilitas baru melalui inti, bukan dengan mengimpor implementasi vendor secara langsung.
  4. daftarkan implementasi vendor Plugin vendor kemudian mendaftarkan backend mereka terhadap kapabilitas tersebut.
  5. tambahkan cakupan kontrak Tambahkan tes agar kepemilikan dan bentuk pendaftaran tetap eksplisit seiring waktu.
Beginilah cara OpenClaw tetap berpendirian tanpa menjadi hardcoded ke pandangan dunia satu penyedia. Lihat Buku Masak Kapabilitas untuk checklist file konkret dan contoh yang dikerjakan.

Checklist kapabilitas

Ketika Anda menambahkan kapabilitas baru, implementasinya biasanya harus menyentuh permukaan berikut secara bersamaan:
  • tipe kontrak inti di src/<capability>/types.ts
  • helper runner/runtime inti di src/<capability>/runtime.ts
  • permukaan pendaftaran API Plugin di src/plugins/types.ts
  • perangkaian registry Plugin di src/plugins/registry.ts
  • eksposur runtime Plugin di src/plugins/runtime/* ketika Plugin fitur/kanal perlu mengonsumsinya
  • helper capture/test di src/test-utils/plugin-registration.ts
  • asersi kepemilikan/kontrak di src/plugins/contracts/registry.ts
  • docs operator/Plugin di docs/
Jika salah satu permukaan tersebut hilang, itu biasanya tanda bahwa kapabilitas belum sepenuhnya terintegrasi.

Templat kapabilitas

Pola minimal:
// 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,
});
Pola pengujian kontrak:
expect(findVideoGenerationProviderIdsForPlugin("openai")).toEqual(["openai"]);
Itu menjaga aturannya tetap sederhana:
  • core memiliki kontrak kapabilitas + orkestrasi
  • Plugin vendor memiliki implementasi vendor
  • Plugin fitur/channel menggunakan helper runtime
  • pengujian kontrak menjaga kepemilikan tetap eksplisit

Terkait