Plugins
Wewnętrzne mechanizmy architektury Plugin
Model publicznych możliwości, kształty Plugin oraz kontrakty własności/wykonania opisuje Architektura Plugin. Ta strona jest odniesieniem dla mechaniki wewnętrznej: potoku ładowania, rejestru, hooków środowiska uruchomieniowego, tras HTTP Gateway, ścieżek importu i tabel schematów.
Potok ładowania
Podczas uruchamiania OpenClaw wykonuje w przybliżeniu następujące kroki:
- wykrywa katalogi główne kandydatów na Plugin
- odczytuje natywne lub zgodne manifesty pakietów oraz metadane pakietów
- odrzuca niebezpiecznych kandydatów
- normalizuje konfigurację Plugin (
plugins.enabled,allow,deny,entries,slots,load.paths) - decyduje o włączeniu każdego kandydata
- ładuje włączone moduły natywne: zbudowane moduły pakietowe używają natywnego loadera; lokalny kod źródłowy TypeScript firm trzecich używa awaryjnego mechanizmu zapasowego Jiti
- wywołuje natywne hooki
register(api)i zbiera rejestracje w rejestrze Plugin - udostępnia rejestr poleceniom i powierzchniom środowiska uruchomieniowego
Bramki bezpieczeństwa działają przed wykonaniem w środowisku uruchomieniowym. Kandydaci są blokowani, gdy punkt wejścia wychodzi poza katalog główny Plugin, ścieżka jest zapisywalna dla wszystkich albo własność ścieżki wygląda podejrzanie w przypadku Plugin spoza pakietu.
Zablokowani kandydaci pozostają powiązani ze swoim identyfikatorem Plugin na potrzeby diagnostyki. Jeśli konfiguracja nadal odwołuje się do tego identyfikatora, walidacja zgłasza Plugin jako obecny, ale zablokowany, i odsyła do ostrzeżenia o bezpieczeństwie ścieżki zamiast traktować wpis konfiguracji jako nieaktualny.
Zachowanie oparte najpierw na manifeście
Manifest jest źródłem prawdy płaszczyzny sterowania. OpenClaw używa go, aby:
- identyfikować Plugin
- wykrywać zadeklarowane kanały/skills/schemat konfiguracji lub możliwości pakietu
- walidować
plugins.entries.<id>.config - uzupełniać etykiety i placeholdery Control UI
- pokazywać metadane instalacji/katalogu
- zachować tanie deskryptory aktywacji i konfiguracji bez ładowania środowiska uruchomieniowego Plugin
W przypadku natywnych Plugin moduł środowiska uruchomieniowego jest częścią płaszczyzny danych. Rejestruje rzeczywiste zachowania, takie jak hooki, narzędzia, polecenia lub przepływy dostawców.
Opcjonalne bloki manifestu activation i setup pozostają w płaszczyźnie sterowania. Są wyłącznie metadanymi opisującymi planowanie aktywacji i wykrywanie konfiguracji; nie zastępują rejestracji środowiska uruchomieniowego, register(...) ani setupEntry. Pierwsi aktywni konsumenci aktywacji używają teraz podpowiedzi manifestu dotyczących poleceń, kanałów i dostawców, aby zawęzić ładowanie Plugin przed szerszą materializacją rejestru:
- ładowanie CLI zawęża się do Plugin, które są właścicielami żądanego polecenia głównego
- rozwiązywanie konfiguracji kanału/Plugin zawęża się do Plugin, które są właścicielami żądanego identyfikatora kanału
- jawne rozwiązywanie konfiguracji/środowiska uruchomieniowego dostawcy zawęża się do Plugin, które są właścicielami żądanego identyfikatora dostawcy
- planowanie startu Gateway używa
activation.onStartupdla jawnych importów startowych i rezygnacji ze startu; Plugin bez metadanych startowych ładują się tylko przez węższe wyzwalacze aktywacji
Wstępne ładowanie środowiska uruchomieniowego w czasie żądania, które prosi o szeroki zakres all, nadal wyprowadza jawny efektywny zestaw identyfikatorów Plugin z konfiguracji, planowania startu, skonfigurowanych kanałów, slotów i reguł automatycznego włączania. Jeśli wyprowadzony zestaw jest pusty, OpenClaw ładuje pusty rejestr środowiska uruchomieniowego zamiast rozszerzać zakres na każdy wykrywalny Plugin.
Planer aktywacji udostępnia zarówno API tylko z identyfikatorami dla istniejących wywołujących, jak i API planu dla nowej diagnostyki. Wpisy planu raportują, dlaczego Plugin został wybrany, rozdzielając jawne podpowiedzi planera activation.* od zapasowego ustalania własności z manifestu, takiego jak providers, channels, commandAliases, setup.providers, contracts.tools i hooki. Ten podział powodów jest granicą zgodności: istniejące metadane Plugin nadal działają, a nowy kod może wykrywać szerokie podpowiedzi lub zachowanie zapasowe bez zmiany semantyki ładowania środowiska uruchomieniowego.
Wykrywanie konfiguracji preferuje teraz identyfikatory należące do deskryptora, takie jak setup.providers i setup.cliBackends, aby zawęzić kandydatów Plugin, zanim cofnie się do setup-api dla Plugin, które nadal wymagają hooków środowiska uruchomieniowego w czasie konfiguracji. Listy konfiguracji dostawców używają manifestu providerAuthChoices, wyborów konfiguracji wyprowadzonych z deskryptora oraz metadanych katalogu instalacji bez ładowania środowiska uruchomieniowego dostawcy. Jawne setup.requiresRuntime: false jest wyłącznie odcięciem deskryptorowym; pominięte requiresRuntime zachowuje starszy mechanizm zapasowy setup-api dla zgodności. Jeśli więcej niż jeden wykryty Plugin zgłasza ten sam znormalizowany identyfikator dostawcy konfiguracji lub backendu CLI, wyszukiwanie konfiguracji odrzuca niejednoznacznego właściciela zamiast polegać na kolejności wykrywania. Gdy środowisko uruchomieniowe konfiguracji rzeczywiście się wykonuje, diagnostyka rejestru raportuje rozbieżności między setup.providers / setup.cliBackends a dostawcami lub backendami CLI zarejestrowanymi przez setup-api, bez blokowania starszych Plugin.
Granica pamięci podręcznej Plugin
OpenClaw nie buforuje wyników wykrywania Plugin ani bezpośrednich danych rejestru manifestów za oknami czasu zegarowego. Instalacje, edycje manifestu i zmiany ścieżek ładowania muszą być widoczne przy następnym jawnym odczycie metadanych lub przebudowie migawki. Parser pliku manifestu może utrzymywać ograniczoną pamięć podręczną sygnatur plików, kluczowaną otwartą ścieżką manifestu, i-węzłem, rozmiarem i znacznikami czasu; ta pamięć podręczna tylko unika ponownego parsowania niezmienionych bajtów i nie może buforować odpowiedzi dotyczących wykrywania, rejestru, właściciela ani polityki.
Bezpieczna szybka ścieżka metadanych to jawna własność obiektu, a nie ukryta pamięć podręczna. Gorące ścieżki startowe Gateway powinny przekazywać bieżący PluginMetadataSnapshot, wyprowadzony PluginLookUpTable albo jawny rejestr manifestu przez łańcuch wywołań. Walidacja konfiguracji, automatyczne włączanie przy starcie, bootstrap Plugin i wybór dostawcy mogą ponownie używać tych obiektów, dopóki reprezentują bieżącą konfigurację i inwentarz Plugin. Wyszukiwanie konfiguracji nadal rekonstruuje metadane manifestu na żądanie, chyba że konkretna ścieżka konfiguracji otrzyma jawny rejestr manifestu; zachowaj to jako zapasową ścieżkę zimną zamiast dodawać ukryte pamięci podręczne wyszukiwania. Gdy dane wejściowe się zmienią, przebuduj i zastąp migawkę zamiast ją mutować lub przechowywać historyczne kopie.
Widoki aktywnego rejestru Plugin i pomocnicze funkcje bootstrapu pakietowych kanałów powinny być ponownie obliczane z bieżącego rejestru/katalogu głównego. Krótkotrwałe mapy są w porządku w ramach jednego wywołania, aby deduplikować pracę lub chronić ponowne wejście; nie mogą stać się procesowymi pamięciami podręcznymi metadanych.
Dla ładowania Plugin trwałą warstwą pamięci podręcznej jest ładowanie środowiska uruchomieniowego. Może ponownie używać stanu loadera, gdy kod lub zainstalowane artefakty są rzeczywiście ładowane, na przykład:
PluginLoaderCacheStatei zgodnych aktywnych rejestrów środowiska uruchomieniowego- pamięci podręcznych jiti/modułów oraz pamięci podręcznych loadera powierzchni publicznej używanych, aby unikać wielokrotnego importowania tej samej powierzchni środowiska uruchomieniowego
- pamięci podręcznych systemu plików dla zainstalowanych artefaktów Plugin
- krótkotrwałych map na wywołanie do normalizacji ścieżek lub rozwiązywania duplikatów
Te pamięci podręczne są szczegółami implementacji płaszczyzny danych. Nie mogą odpowiadać na pytania płaszczyzny sterowania, takie jak „który Plugin jest właścicielem tego dostawcy?”, chyba że wywołujący celowo poprosił o ładowanie środowiska uruchomieniowego.
Nie dodawaj trwałych ani zegarowych pamięci podręcznych dla:
- wyników wykrywania
- bezpośrednich rejestrów manifestów
- rejestrów manifestów rekonstruowanych z indeksu zainstalowanych Plugin
- wyszukiwania właściciela dostawcy, wyciszania modeli, polityki dostawcy ani metadanych artefaktów publicznych
- żadnej innej odpowiedzi wyprowadzonej z manifestu, w której zmieniony manifest, zainstalowany indeks lub ścieżka ładowania powinny być widoczne przy następnym odczycie metadanych
Wywołujący, którzy przebudowują metadane manifestu z utrwalonego indeksu zainstalowanych Plugin, rekonstruują ten rejestr na żądanie. Zainstalowany indeks jest trwałym stanem płaszczyzny źródła; nie jest ukrytą wewnątrzprocesową pamięcią podręczną metadanych.
Model rejestru
Załadowane Plugin nie mutują bezpośrednio losowych globali rdzenia. Rejestrują się w centralnym rejestrze Plugin.
Rejestr śledzi:
- rekordy Plugin (tożsamość, źródło, pochodzenie, status, diagnostyka)
- narzędzia
- starsze hooki i typowane hooki
- kanały
- dostawców
- handlery RPC Gateway
- trasy HTTP
- rejestratory CLI
- usługi w tle
- polecenia należące do Plugin
Funkcje rdzenia czytają następnie z tego rejestru zamiast komunikować się bezpośrednio z modułami Plugin. Utrzymuje to ładowanie jednokierunkowe:
- moduł Plugin -> rejestracja w rejestrze
- środowisko uruchomieniowe rdzenia -> konsumpcja rejestru
To rozdzielenie ma znaczenie dla utrzymywalności. Oznacza, że większość powierzchni rdzenia potrzebuje tylko jednego punktu integracji: „odczytaj rejestr”, a nie „obsłuż specjalnie każdy moduł Plugin”.
Callbacki wiązania konwersacji
Plugin, które wiążą konwersację, mogą zareagować po rozstrzygnięciu zatwierdzenia.
Użyj api.onConversationBindingResolved(...), aby otrzymać callback po zatwierdzeniu lub odrzuceniu żądania powiązania:
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);
});
},
};
Pola payloadu callbacku:
status:"approved"albo"denied"decision:"allow-once","allow-always"albo"deny"binding: rozwiązane wiązanie dla zatwierdzonych żądańrequest: oryginalne podsumowanie żądania, wskazówka odłączenia, identyfikator nadawcy i metadane konwersacji
Ten callback służy wyłącznie do powiadamiania. Nie zmienia tego, kto może wiązać konwersację, i uruchamia się po zakończeniu obsługi zatwierdzenia przez rdzeń.
Hooki środowiska uruchomieniowego dostawcy
Plugin dostawców mają trzy warstwy:
- Metadane manifestu do taniego wyszukiwania przed środowiskiem uruchomieniowym:
setup.providers[].envVars, przestarzałą zgodnościową opcjęproviderAuthEnvVars,providerAuthAliases,providerAuthChoicesorazchannelEnvVars. - Hooki czasu konfiguracji:
catalog(starszediscovery) orazapplyConfigDefaults. - Hooki środowiska uruchomieniowego: ponad 40 opcjonalnych hooków obejmujących uwierzytelnianie, rozwiązywanie modeli, opakowywanie strumieni, poziomy myślenia, politykę odtwarzania i endpointy użycia. Zobacz pełną listę w sekcji Kolejność hooków i użycie.
OpenClaw nadal jest właścicielem ogólnej pętli agenta, przełączania awaryjnego, obsługi transkryptu i polityki narzędzi. Te hooki są powierzchnią rozszerzeń dla zachowania specyficznego dla dostawcy, bez potrzeby tworzenia całego niestandardowego transportu inferencji.
Używaj manifestu setup.providers[].envVars, gdy dostawca ma poświadczenia oparte na zmiennych środowiskowych, które ogólne ścieżki uwierzytelniania/statusu/wyboru modelu powinny widzieć bez ładowania środowiska uruchomieniowego Plugin. Przestarzałe providerAuthEnvVars jest nadal odczytywane przez adapter zgodności w okresie wycofywania, a Plugin spoza pakietu, które go używają, otrzymują diagnostykę manifestu. Używaj manifestu providerAuthAliases, gdy jeden identyfikator dostawcy powinien ponownie używać zmiennych środowiskowych, profili uwierzytelniania, uwierzytelniania opartego na konfiguracji i wyboru onboardingu klucza API innego identyfikatora dostawcy. Używaj manifestu providerAuthChoices, gdy powierzchnie CLI onboardingu/wyboru uwierzytelniania powinny znać identyfikator wyboru dostawcy, etykiety grup i proste okablowanie uwierzytelniania jedną flagą bez ładowania środowiska uruchomieniowego dostawcy. Zachowaj envVars środowiska uruchomieniowego dostawcy dla wskazówek dla operatora, takich jak etykiety onboardingu albo zmienne konfiguracji client-id/client-secret OAuth.
Używaj manifestu channelEnvVars, gdy kanał ma uwierzytelnianie lub konfigurację sterowaną zmiennymi środowiskowymi, które ogólny fallback środowiska powłoki, sprawdzenia konfiguracji/statusu lub prompty konfiguracji powinny widzieć bez ładowania środowiska uruchomieniowego kanału.
Kolejność hooków i użycie
Dla Plugin modeli/dostawców OpenClaw wywołuje hooki w przybliżeniu w tej kolejności.
Kolumna „Kiedy używać” jest krótkim przewodnikiem decyzyjnym.
Pola dostawcy tylko dla zgodności, których OpenClaw już nie wywołuje, takie jak
ProviderPlugin.capabilities i suppressBuiltInModel, celowo nie są tutaj wymienione.
| # | Punkt zaczepienia | Co robi | Kiedy używać |
|---|---|---|---|
| 1 | catalog |
Publikuje konfigurację dostawcy do models.providers podczas generowania models.json |
Dostawca ma własny katalog lub domyślne wartości bazowego adresu URL |
| 2 | applyConfigDefaults |
Stosuje należące do dostawcy globalne wartości domyślne konfiguracji podczas materializacji konfiguracji | Wartości domyślne zależą od trybu uwierzytelniania, środowiska lub semantyki rodziny modeli dostawcy |
| -- | (wbudowane wyszukiwanie modelu) | OpenClaw najpierw próbuje zwykłej ścieżki rejestru/katalogu | (nie jest to punkt zaczepienia Plugin) |
| 3 | normalizeModelId |
Normalizuje starsze aliasy lub aliasy identyfikatorów modeli w wersji zapoznawczej przed wyszukiwaniem | Dostawca odpowiada za porządkowanie aliasów przed kanonicznym rozwiązywaniem modelu |
| 4 | normalizeTransport |
Normalizuje api / baseUrl rodziny dostawcy przed ogólnym składaniem modelu |
Dostawca odpowiada za porządkowanie transportu dla niestandardowych identyfikatorów dostawców w tej samej rodzinie transportu |
| 5 | normalizeConfig |
Normalizuje models.providers.<id> przed rozwiązywaniem środowiska uruchomieniowego/dostawcy |
Dostawca potrzebuje porządkowania konfiguracji, które powinno należeć do Plugin; dołączone pomocniki rodziny Google także zabezpieczają obsługiwane wpisy konfiguracji Google |
| 6 | applyNativeStreamingUsageCompat |
Stosuje przepisywania zgodności natywnego użycia streamingu do dostawców konfiguracji | Dostawca potrzebuje poprawek metadanych natywnego użycia streamingu zależnych od punktu końcowego |
| 7 | resolveConfigApiKey |
Rozwiązuje uwierzytelnianie znacznikami środowiskowymi dla dostawców konfiguracji przed ładowaniem uwierzytelniania środowiska uruchomieniowego | Dostawca ma własne rozwiązywanie klucza API przez znaczniki środowiskowe; amazon-bedrock ma tu także wbudowany resolver znaczników środowiskowych AWS |
| 8 | resolveSyntheticAuth |
Udostępnia lokalne/samodzielnie hostowane lub oparte na konfiguracji uwierzytelnianie bez utrwalania tekstu jawnego | Dostawca może działać z syntetycznym/lokalnym znacznikiem poświadczeń |
| 9 | resolveExternalAuthProfiles |
Nakłada zewnętrzne profile uwierzytelniania należące do dostawcy; domyślna wartość persistence to runtime-only dla poświadczeń należących do CLI/aplikacji |
Dostawca ponownie używa zewnętrznych poświadczeń uwierzytelniania bez utrwalania skopiowanych tokenów odświeżania; zadeklaruj contracts.externalAuthProviders w manifeście |
| 10 | shouldDeferSyntheticProfileAuth |
Obniża priorytet zapisanych syntetycznych symboli zastępczych profilu względem uwierzytelniania opartego na środowisku/konfiguracji | Dostawca przechowuje syntetyczne profile zastępcze, które nie powinny mieć pierwszeństwa |
| 11 | resolveDynamicModel |
Synchroniczna ścieżka awaryjna dla należących do dostawcy identyfikatorów modeli, których nie ma jeszcze w lokalnym rejestrze | Dostawca akceptuje dowolne identyfikatory modeli z upstreamu |
| 12 | prepareDynamicModel |
Asynchroniczna rozgrzewka, po której resolveDynamicModel uruchamia się ponownie |
Dostawca potrzebuje metadanych sieciowych przed rozwiązywaniem nieznanych identyfikatorów |
| 13 | normalizeResolvedModel |
Końcowe przepisywanie, zanim osadzony runner użyje rozwiązanego modelu | Dostawca potrzebuje przepisań transportu, ale nadal używa transportu rdzenia |
| 14 | contributeResolvedModelCompat |
Dostarcza flagi zgodności dla modeli dostawcy za innym zgodnym transportem | Dostawca rozpoznaje własne modele na transportach proxy bez przejmowania dostawcy |
| 15 | normalizeToolSchemas |
Normalizuje schematy narzędzi, zanim zobaczy je osadzony runner | Dostawca potrzebuje porządkowania schematów rodziny transportu |
| 16 | inspectToolSchemas |
Udostępnia należącą do dostawcy diagnostykę schematów po normalizacji | Dostawca chce ostrzeżeń o słowach kluczowych bez uczenia rdzenia reguł specyficznych dla dostawcy |
| 17 | resolveReasoningOutputMode |
Wybiera kontrakt wyjścia rozumowania: natywny albo tagowany | Dostawca potrzebuje tagowanego rozumowania/końcowego wyjścia zamiast natywnych pól |
| 18 | prepareExtraParams |
Normalizacja parametrów żądania przed ogólnymi wrapperami opcji streamingu | Dostawca potrzebuje domyślnych parametrów żądania lub porządkowania parametrów dla danego dostawcy |
| 19 | createStreamFn |
Całkowicie zastępuje zwykłą ścieżkę streamingu transportem niestandardowym | Dostawca potrzebuje niestandardowego protokołu przewodowego, a nie tylko wrappera |
| 20 | wrapStreamFn |
Wrapper streamingu po zastosowaniu ogólnych wrapperów | Dostawca potrzebuje wrapperów zgodności nagłówków/treści/modelu żądania bez niestandardowego transportu |
| 21 | resolveTransportTurnState |
Dołącza natywne nagłówki transportu lub metadane dla tury | Dostawca chce, aby ogólne transporty wysyłały natywną dla dostawcy tożsamość tury |
| 22 | resolveWebSocketSessionPolicy |
Dołącza natywne nagłówki WebSocket lub politykę schładzania sesji | Dostawca chce, aby ogólne transporty WS dostrajały nagłówki sesji lub politykę awaryjną |
| 23 | formatApiKey |
Formater profilu uwierzytelniania: zapisany profil staje się runtime’owym ciągiem apiKey |
Dostawca przechowuje dodatkowe metadane uwierzytelniania i potrzebuje niestandardowego kształtu tokena środowiska uruchomieniowego |
| 24 | refreshOAuth |
Nadpisanie odświeżania OAuth dla niestandardowych punktów końcowych odświeżania lub polityki błędów odświeżania | Dostawca nie pasuje do współdzielonych odświeżaczy pi-ai |
| 25 | buildAuthDoctorHint |
Wskazówka naprawcza dołączana po niepowodzeniu odświeżania OAuth | Dostawca potrzebuje należących do dostawcy wskazówek naprawy uwierzytelniania po niepowodzeniu odświeżania |
| 26 | matchesContextOverflowError |
Należący do dostawcy matcher przepełnienia okna kontekstu | Dostawca ma surowe błędy przepełnienia, których ogólne heurystyki by nie wychwyciły |
| 27 | classifyFailoverReason |
Należąca do dostawcy klasyfikacja przyczyny przełączenia awaryjnego | Dostawca może mapować surowe błędy API/transportu na limit szybkości/przeciążenie/itd. |
| 28 | isCacheTtlEligible |
Polityka pamięci podręcznej promptów dla dostawców proxy/backhaul | Dostawca potrzebuje bramkowania TTL pamięci podręcznej specyficznego dla proxy |
| 29 | buildMissingAuthMessage |
Zamiennik ogólnego komunikatu odzyskiwania po braku uwierzytelniania | Dostawca potrzebuje wskazówki odzyskiwania po braku uwierzytelniania specyficznej dla dostawcy |
| 30 | augmentModelCatalog |
Syntetyczne/końcowe wiersze katalogu dołączane po wykrywaniu | Dostawca potrzebuje syntetycznych wierszy zgodności w przód w models list i selektorach |
| 31 | resolveThinkingProfile |
Specyficzny dla modelu zestaw poziomów /think, etykiety wyświetlania i wartość domyślna |
Dostawca udostępnia niestandardową drabinę myślenia lub etykietę binarną dla wybranych modeli |
| 32 | isBinaryThinking |
Punkt zaczepienia zgodności przełącznika rozumowania wł./wył. | Dostawca udostępnia tylko binarne myślenie wł./wył. |
| 33 | supportsXHighThinking |
Punkt zaczepienia zgodności obsługi rozumowania xhigh |
Dostawca chce xhigh tylko dla podzbioru modeli |
| 34 | resolveDefaultThinkingLevel |
Punkt zaczepienia zgodności domyślnego poziomu /think |
Dostawca odpowiada za domyślną politykę /think dla rodziny modeli |
| 35 | isModernModelRef |
Matcher nowoczesnego modelu dla filtrów profilu live i wyboru smoke | Dostawca odpowiada za dopasowywanie preferowanego modelu live/smoke |
| 36 | prepareRuntimeAuth |
Wymienia skonfigurowane poświadczenie na rzeczywisty token/klucz środowiska uruchomieniowego tuż przed inferencją | Dostawca potrzebuje wymiany tokena lub krótkotrwałego poświadczenia żądania |
| 37 | resolveUsageAuth |
Rozwiąż dane uwierzytelniające użycia/rozliczeń dla /usage i powiązanych powierzchni statusu |
Dostawca potrzebuje niestandardowego parsowania tokenów użycia/limitu albo innych danych uwierzytelniających użycie |
| 38 | fetchUsageSnapshot |
Pobierz i znormalizuj specyficzne dla dostawcy migawki użycia/limitu po rozwiązaniu uwierzytelnienia | Dostawca potrzebuje specyficznego dla dostawcy endpointu użycia albo parsera payloadu |
| 39 | createEmbeddingProvider |
Zbuduj należący do dostawcy adapter osadzania dla pamięci/wyszukiwania | Zachowanie osadzania pamięci należy do pluginu dostawcy |
| 40 | buildReplayPolicy |
Zwróć politykę odtwarzania kontrolującą obsługę transkryptu dla dostawcy | Dostawca potrzebuje niestandardowej polityki transkryptu (na przykład usuwania bloków rozumowania) |
| 41 | sanitizeReplayHistory |
Przepisz historię odtwarzania po ogólnym czyszczeniu transkryptu | Dostawca potrzebuje specyficznych dla dostawcy przekształceń odtwarzania wykraczających poza współdzielone helpery Compaction |
| 42 | validateReplayTurns |
Końcowa walidacja lub przekształcenie tur odtwarzania przed osadzonym uruchamiaczem | Transport dostawcy potrzebuje ściślejszej walidacji tur po ogólnym oczyszczaniu |
| 43 | onModelSelected |
Uruchom należące do dostawcy efekty uboczne po wyborze | Dostawca potrzebuje telemetrii albo należącego do dostawcy stanu, gdy model staje się aktywny |
normalizeModelId, normalizeTransport i normalizeConfig najpierw sprawdzają
dopasowany plugin dostawcy, a następnie przechodzą przez inne pluginy dostawców
obsługujące hooki, aż któryś faktycznie zmieni identyfikator modelu lub
transport/konfigurację. Dzięki temu aliasy i zgodnościowe nakładki dostawców
działają bez wymagania, aby wywołujący wiedział, który dołączony plugin jest
właścicielem przepisania. Jeśli żaden hook dostawcy nie przepisze obsługiwanego
wpisu konfiguracji z rodziny Google, dołączony normalizator konfiguracji Google
nadal zastosuje to zgodnościowe czyszczenie.
Jeśli dostawca potrzebuje w pełni niestandardowego protokołu przewodowego lub niestandardowego wykonawcy żądań, jest to inna klasa rozszerzenia. Te hooki są przeznaczone dla zachowania dostawcy, które nadal działa w normalnej pętli inferencji OpenClaw.
Przykład dostawcy
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);
},
});
Wbudowane przykłady
Dołączone pluginy dostawców łączą powyższe hooki, aby dopasować się do potrzeb
każdego dostawcy dotyczących katalogu, uwierzytelniania, myślenia, odtwarzania i
użycia. Autorytatywny zestaw hooków znajduje się przy każdym pluginie w
extensions/; ta strona ilustruje kształty zamiast odzwierciedlać listę.
Dostawcy katalogu przekazującego
OpenRouter, Kilocode, Z.AI, xAI rejestrują catalog oraz
resolveDynamicModel / prepareDynamicModel, aby mogli udostępniać
identyfikatory modeli upstream przed statycznym katalogiem OpenClaw.
Dostawcy OAuth i endpointów użycia
GitHub Copilot, Gemini CLI, ChatGPT Codex, MiniMax, Xiaomi, z.ai łączą
prepareRuntimeAuth lub formatApiKey z resolveUsageAuth +
fetchUsageSnapshot, aby zarządzać wymianą tokenów i integracją /usage.
Rodziny odtwarzania i czyszczenia transkrypcji
Współdzielone nazwane rodziny (google-gemini, passthrough-gemini,
anthropic-by-model, hybrid-anthropic-openai) pozwalają dostawcom włączyć
się do polityki transkrypcji przez buildReplayPolicy, zamiast aby każdy
plugin ponownie implementował czyszczenie.
Dostawcy tylko katalogu
byteplus, cloudflare-ai-gateway, huggingface, kimi-coding, nvidia,
qianfan, synthetic, together, venice, vercel-ai-gateway i
volcengine rejestrują tylko catalog i korzystają ze współdzielonej pętli inferencji.
Pomocnicy strumieni specyficzni dla Anthropic
Nagłówki beta, /fast / serviceTier i context1m znajdują się w publicznym
przejściu api.ts / contract-api.ts pluginu Anthropic
(wrapAnthropicProviderStream, resolveAnthropicBetas,
resolveAnthropicFastMode, resolveAnthropicServiceTier), a nie w
generycznym SDK.
Pomocnicy runtime
Pluginy mogą uzyskiwać dostęp do wybranych pomocników rdzenia przez api.runtime. Dla 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,
});
Uwagi:
textToSpeechzwraca normalny ładunek wyjściowy TTS rdzenia dla powierzchni plików/notatek głosowych.- Używa konfiguracji rdzenia
messages.ttsi wyboru dostawcy. - Zwraca bufor audio PCM + częstotliwość próbkowania. Pluginy muszą ponownie próbkować/kodować dla dostawców.
listVoicesjest opcjonalne dla każdego dostawcy. Używaj go dla selektorów głosu lub przepływów konfiguracji należących do dostawcy.- Listy głosów mogą zawierać bogatsze metadane, takie jak język, płeć i tagi osobowości dla selektorów świadomych dostawcy.
- OpenAI i ElevenLabs obsługują dziś telefonię. Microsoft nie.
Pluginy mogą także rejestrować dostawców mowy przez 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,
};
},
});
Uwagi:
- Zachowaj politykę TTS, fallback i dostarczanie odpowiedzi w rdzeniu.
- Używaj dostawców mowy dla zachowania syntezy należącego do dostawcy.
- Starsze wejście Microsoft
edgejest normalizowane do identyfikatora dostawcymicrosoft. - Preferowany model własności jest zorientowany na firmę: jeden plugin dostawcy może posiadać dostawców tekstu, mowy, obrazu i przyszłych mediów, gdy OpenClaw dodaje te kontrakty możliwości.
Dla rozumienia obrazów/audio/wideo pluginy rejestrują jednego typowanego dostawcę rozumienia mediów zamiast generycznego worka klucz/wartość:
api.registerMediaUnderstandingProvider({
id: "google",
capabilities: ["image", "audio", "video"],
describeImage: async (req) => ({ text: "..." }),
transcribeAudio: async (req) => ({ text: "..." }),
describeVideo: async (req) => ({ text: "..." }),
});
Uwagi:
- Zachowaj orkiestrację, fallback, konfigurację i okablowanie kanałów w rdzeniu.
- Zachowaj zachowanie dostawcy w pluginie dostawcy.
- Rozszerzanie addytywne powinno pozostać typowane: nowe opcjonalne metody, nowe opcjonalne pola wyników, nowe opcjonalne możliwości.
- Generowanie wideo już stosuje ten sam wzorzec:
- rdzeń posiada kontrakt możliwości i pomocnik runtime
- pluginy dostawców rejestrują
api.registerVideoGenerationProvider(...) - pluginy funkcji/kanałów używają
api.runtime.videoGeneration.*
Dla pomocników runtime rozumienia mediów pluginy mogą wywoływać:
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,
});
Dla transkrypcji audio pluginy mogą używać runtime rozumienia mediów albo starszego aliasu 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",
});
Uwagi:
api.runtime.mediaUnderstanding.*jest preferowaną współdzieloną powierzchnią dla rozumienia obrazów/audio/wideo.- Używa konfiguracji audio rozumienia mediów rdzenia (
tools.media.audio) i kolejności fallbacku dostawców. - Zwraca
{ text: undefined }, gdy nie powstanie wyjście transkrypcji (na przykład pominięte/nieobsługiwane wejście). api.runtime.stt.transcribeAudioFile(...)pozostaje aliasem zgodnościowym.
Pluginy mogą także uruchamiać przebiegi subagentów w tle przez 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,
});
Uwagi:
providerimodelsą opcjonalnymi nadpisaniami dla pojedynczego przebiegu, a nie trwałymi zmianami sesji.- OpenClaw honoruje te pola nadpisania tylko dla zaufanych wywołujących.
- Dla przebiegów fallback należących do pluginu operatorzy muszą wyrazić zgodę przez
plugins.entries.<id>.subagent.allowModelOverride: true. - Użyj
plugins.entries.<id>.subagent.allowedModels, aby ograniczyć zaufane pluginy do konkretnych kanonicznych celówprovider/model, albo"*", aby jawnie zezwolić na dowolny cel. - Przebiegi subagentów niezaufanych pluginów nadal działają, ale żądania nadpisania są odrzucane zamiast cicho wracać do ustawień domyślnych.
- Sesje subagentów utworzone przez plugin są oznaczane identyfikatorem tworzącego pluginu. Fallback
api.runtime.subagent.deleteSession(...)może usuwać tylko te posiadane sesje; arbitralne usuwanie sesji nadal wymaga żądania Gateway o zakresie administratora.
Dla wyszukiwania w sieci pluginy mogą używać współdzielonego pomocnika runtime zamiast sięgać do okablowania narzędzia agenta:
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,
},
});
Pluginy mogą także rejestrować dostawców wyszukiwania w sieci przez
api.registerWebSearchProvider(...).
Uwagi:
- Zachowaj wybór dostawcy, rozwiązywanie poświadczeń i współdzieloną semantykę żądań w rdzeniu.
- Używaj dostawców wyszukiwania w sieci dla transportów wyszukiwania specyficznych dla dostawcy.
api.runtime.webSearch.*jest preferowaną współdzieloną powierzchnią dla pluginów funkcji/kanałów, które potrzebują zachowania wyszukiwania bez zależności od wrappera narzędzia agenta.
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(...): wygeneruj obraz z użyciem skonfigurowanego łańcucha dostawców generowania obrazów.listProviders(...): wypisz dostępnych dostawców generowania obrazów i ich możliwości.
Trasy HTTP Gateway
Pluginy mogą udostępniać endpointy HTTP za pomocą api.registerHttpRoute(...).
api.registerHttpRoute({
path: "/acme/webhook",
auth: "plugin",
match: "exact",
handler: async (_req, res) => {
res.statusCode = 200;
res.end("ok");
return true;
},
});
Pola trasy:
path: ścieżka trasy pod serwerem HTTP gateway.auth: wymagane. Użyj"gateway", aby wymagać normalnego uwierzytelniania gateway, albo"plugin"dla uwierzytelniania/weryfikacji webhooka zarządzanych przez plugin.match: opcjonalne."exact"(domyślnie) albo"prefix".replaceExisting: opcjonalne. Pozwala temu samemu pluginowi zastąpić własną istniejącą rejestrację trasy.handler: zwróćtrue, gdy trasa obsłużyła żądanie.
Uwagi:
api.registerHttpHandler(...)usunięto i spowoduje błąd ładowania pluginu. Zamiast tego użyjapi.registerHttpRoute(...).- Trasy pluginów muszą jawnie deklarować
auth. - Dokładne konflikty
path + matchsą odrzucane, chyba że ustawionoreplaceExisting: true, a jeden plugin nie może zastąpić trasy innego pluginu. - Nakładające się trasy z różnymi poziomami
authsą odrzucane. Łańcuchy przechodzeniaexact/prefixutrzymuj wyłącznie na tym samym poziomie auth. - Trasy
auth: "plugin"nie otrzymują automatycznie zakresów runtime operatora. Służą do zarządzanych przez plugin webhooków/weryfikacji podpisów, a nie do uprzywilejowanych wywołań pomocniczych Gateway. - Trasy
auth: "gateway"działają w zakresie runtime żądania Gateway, ale ten zakres jest celowo konserwatywny:- uwierzytelnianie bearer współdzielonym sekretem (
gateway.auth.mode = "token"/"password") utrzymuje zakresy runtime tras pluginów przypięte dooperator.write, nawet jeśli wywołujący wyślex-openclaw-scopes - zaufane tryby HTTP przenoszące tożsamość (na przykład
trusted-proxylubgateway.auth.mode = "none"na prywatnym wejściu) respektująx-openclaw-scopestylko wtedy, gdy nagłówek jest jawnie obecny - jeśli
x-openclaw-scopesjest nieobecny w takich żądaniach tras pluginów przenoszących tożsamość, zakres runtime wraca dooperator.write
- uwierzytelnianie bearer współdzielonym sekretem (
- Zasada praktyczna: nie zakładaj, że trasa pluginu z gateway-auth jest niejawną powierzchnią administracyjną. Jeśli trasa wymaga zachowania wyłącznie dla administratora, wymagaj trybu auth przenoszącego tożsamość i udokumentuj jawny kontrakt nagłówka
x-openclaw-scopes.
Ścieżki importu SDK pluginów
Podczas tworzenia nowych pluginów używaj wąskich podścieżek SDK zamiast monolitycznego głównego
barrela openclaw/plugin-sdk. Główne podścieżki:
| Podścieżka | Cel |
|---|---|
openclaw/plugin-sdk/plugin-entry |
Prymitywy rejestracji pluginu |
openclaw/plugin-sdk/channel-core |
Pomocniki wejścia/budowania kanału |
openclaw/plugin-sdk/core |
Ogólne współdzielone pomocniki i kontrakt zbiorczy |
openclaw/plugin-sdk/config-schema |
Schemat Zod głównego openclaw.json (OpenClawSchema) |
Pluginy kanałów wybierają z rodziny wąskich styków — channel-setup,
setup-runtime, setup-adapter-runtime, setup-tools, channel-pairing,
channel-contract, channel-feedback, channel-inbound, channel-lifecycle,
channel-reply-pipeline, command-auth, secret-input, webhook-ingress,
channel-targets i channel-actions. Zachowanie zatwierdzania powinno konsolidować się
na jednym kontrakcie approvalCapability, zamiast mieszać je między niepowiązanymi
polami pluginów. Zobacz Pluginy kanałów.
Pomocniki runtime i konfiguracji znajdują się w dopasowanych, wyspecjalizowanych podścieżkach *-runtime
(approval-runtime, agent-runtime, lazy-runtime, directory-runtime,
text-runtime, runtime-store, system-event-runtime, heartbeat-runtime,
channel-activity-runtime itd.). Preferuj config-types,
plugin-config-runtime, runtime-config-snapshot i config-mutation
zamiast szerokiego barrela zgodności config-runtime.
Wewnętrzne punkty wejścia repozytorium (dla głównego katalogu każdego dołączonego pakietu pluginu):
index.js— wejście dołączonego pluginuapi.js— barrel pomocników/typówruntime-api.js— barrel wyłącznie runtimesetup-entry.js— wejście pluginu konfiguracji
Zewnętrzne pluginy powinny importować tylko podścieżki openclaw/plugin-sdk/*. Nigdy
nie importuj src/* innego pakietu pluginu z core ani z innego pluginu.
Punkty wejścia ładowane przez fasadę preferują aktywny snapshot konfiguracji runtime, gdy taki
istnieje, a następnie wracają do rozwiązania pliku konfiguracji na dysku.
Podścieżki specyficzne dla możliwości, takie jak image-generation, media-understanding
i speech, istnieją, ponieważ dołączone pluginy używają ich obecnie. Nie są one
automatycznie długoterminowo zamrożonymi kontraktami zewnętrznymi — sprawdź odpowiednią stronę
referencyjną SDK, gdy na nich polegasz.
Schematy narzędzi wiadomości
Pluginy powinny posiadać specyficzne dla kanału wkłady schematu describeMessageTool(...)
dla prymitywów innych niż wiadomości, takich jak reakcje, odczyty i ankiety.
Wspólna prezentacja wysyłania powinna używać ogólnego kontraktu MessagePresentation
zamiast natywnych dla dostawcy pól przycisków, komponentów, bloków lub kart.
Zobacz Prezentacja wiadomości, aby poznać kontrakt,
reguły fallbacku, mapowanie dostawców i listę kontrolną autora pluginu.
Pluginy zdolne do wysyłania deklarują, co potrafią renderować, przez możliwości wiadomości:
presentationdla semantycznych bloków prezentacji (text,context,divider,buttons,select)delivery-pindla żądań przypiętego dostarczania
Core decyduje, czy renderować prezentację natywnie, czy zdegradować ją do tekstu. Nie udostępniaj natywnych dla dostawcy obejść UI z ogólnego narzędzia wiadomości. Przestarzałe pomocniki SDK dla starszych natywnych schematów pozostają eksportowane dla istniejących zewnętrznych pluginów, ale nowe pluginy nie powinny ich używać.
Rozwiązywanie celu kanału
Pluginy kanałów powinny posiadać specyficzną dla kanału semantykę celów. Wspólnego hosta wychodzącego utrzymuj jako ogólny i używaj powierzchni adaptera wiadomości dla reguł dostawcy:
messaging.inferTargetChatType({ to })decyduje, czy znormalizowany cel powinien być traktowany jakodirect,groupczychannelprzed wyszukiwaniem w katalogu.messaging.targetResolver.looksLikeId(raw, normalized)mówi core, czy wejście powinno od razu przejść do rozwiązywania podobnego do identyfikatora zamiast wyszukiwania w katalogu.messaging.targetResolver.resolveTarget(...)jest fallbackiem pluginu, gdy core potrzebuje ostatecznego rozwiązywania należącego do dostawcy po normalizacji lub po nietrafieniu w katalogu.messaging.resolveOutboundSessionRoute(...)odpowiada za specyficzne dla dostawcy budowanie trasy sesji po rozwiązaniu celu.
Zalecany podział:
- Używaj
inferTargetChatTypedo decyzji kategorii, które powinny nastąpić przed wyszukiwaniem peerów/grup. - Używaj
looksLikeIddo sprawdzeń typu „traktuj to jako jawny/natywny identyfikator celu”. - Używaj
resolveTargetdo specyficznego dla dostawcy fallbacku normalizacji, a nie do szerokiego wyszukiwania w katalogu. - Natywne identyfikatory dostawcy, takie jak identyfikatory czatów, identyfikatory wątków, JID, uchwyty i identyfikatory pokoi
trzymaj w wartościach
targetlub parametrach specyficznych dla dostawcy, a nie w ogólnych polach SDK.
Katalogi oparte na konfiguracji
Pluginy, które wyprowadzają wpisy katalogu z konfiguracji, powinny utrzymywać tę logikę w
pluginie i ponownie używać współdzielonych pomocników z
openclaw/plugin-sdk/directory-runtime.
Używaj tego, gdy kanał potrzebuje peerów/grup opartych na konfiguracji, takich jak:
- peery DM sterowane allowlistą
- skonfigurowane mapy kanałów/grup
- statyczne fallbacki katalogu w zakresie konta
Współdzielone pomocniki w directory-runtime obsługują tylko operacje ogólne:
- filtrowanie zapytań
- stosowanie limitu
- pomocniki deduplikacji/normalizacji
- budowanie
ChannelDirectoryEntry[]
Specyficzna dla kanału inspekcja konta i normalizacja identyfikatorów powinny pozostać w implementacji pluginu.
Katalogi dostawców
Pluginy dostawców mogą definiować katalogi modeli do inferencji za pomocą
registerProvider({ catalog: { run(...) { ... } } }).
catalog.run(...) zwraca ten sam kształt, który OpenClaw zapisuje w
models.providers:
{ provider }dla jednego wpisu dostawcy{ providers }dla wielu wpisów dostawców
Używaj catalog, gdy plugin posiada specyficzne dla dostawcy identyfikatory modeli, domyślne
adresy URL bazowe lub metadane modeli bramkowane auth.
catalog.order kontroluje, kiedy katalog pluginu scala się względem
wbudowanych niejawnych dostawców OpenClaw:
simple: zwykli dostawcy sterowani kluczem API lub envprofile: dostawcy pojawiający się, gdy istnieją profile authpaired: dostawcy syntetyzujący wiele powiązanych wpisów dostawcylate: ostatni przebieg, po innych niejawnych dostawcach
Późniejsi dostawcy wygrywają przy kolizji kluczy, więc pluginy mogą celowo zastąpić wbudowany wpis dostawcy tym samym identyfikatorem dostawcy.
Zgodność:
discoverynadal działa jako starszy alias- jeśli zarejestrowane są jednocześnie
catalogidiscovery, OpenClaw używacatalog
Inspekcja kanału tylko do odczytu
Jeśli twój plugin rejestruje kanał, preferuj implementację
plugin.config.inspectAccount(cfg, accountId) obok resolveAccount(...).
Dlaczego:
resolveAccount(...)jest ścieżką runtime. Może zakładać, że poświadczenia są w pełni zmaterializowane, i może szybko zakończyć się błędem, gdy brakuje wymaganych sekretów.- Ścieżki poleceń tylko do odczytu, takie jak
openclaw status,openclaw status --all,openclaw channels status,openclaw channels resolveoraz przepływy naprawy doctor/config nie powinny musieć materializować poświadczeń runtime tylko po to, aby opisać konfigurację.
Zalecane zachowanie inspectAccount(...):
- Zwracaj tylko opisowy stan konta.
- Zachowaj
enablediconfigured. - Uwzględniaj pola źródła/statusu poświadczeń, gdy są istotne, takie jak:
tokenSource,tokenStatusbotTokenSource,botTokenStatusappTokenSource,appTokenStatussigningSecretSource,signingSecretStatus
- Nie musisz zwracać surowych wartości tokenów tylko po to, aby raportować dostępność
tylko do odczytu. Zwrócenie
tokenStatus: "available"(i odpowiadającego pola źródła) wystarcza dla poleceń w stylu statusu. - Używaj
configured_unavailable, gdy poświadczenie jest skonfigurowane przez SecretRef, ale niedostępne w bieżącej ścieżce polecenia.
Dzięki temu polecenia tylko do odczytu mogą raportować „skonfigurowane, ale niedostępne w tej ścieżce polecenia” zamiast zawieszać się lub błędnie zgłaszać konto jako nieskonfigurowane.
Pakiety pluginów
Katalog pluginu może zawierać package.json z openclaw.extensions:
{
"name": "my-pack",
"openclaw": {
"extensions": ["./src/safety.ts", "./src/tools.ts"],
"setupEntry": "./src/setup-entry.ts"
}
}
Każdy wpis staje się pluginem. Jeśli pakiet wymienia wiele rozszerzeń, identyfikator pluginu
staje się name/<fileBase>.
Jeśli twój plugin importuje zależności npm, zainstaluj je w tym katalogu, aby
node_modules było dostępne (npm install / pnpm install).
Zabezpieczenie bezpieczeństwa: każdy wpis openclaw.extensions musi pozostać wewnątrz katalogu pluginu
po rozwiązaniu symlinków. Wpisy wychodzące poza katalog pakietu są
odrzucane.
Uwaga dotycząca bezpieczeństwa: openclaw plugins install instaluje zależności pluginu za pomocą
lokalnego dla projektu npm install --omit=dev --ignore-scripts (bez skryptów lifecycle,
bez zależności dev w runtime), ignorując odziedziczone globalne ustawienia instalacji npm.
Utrzymuj drzewa zależności pluginów jako „czysty JS/TS” i unikaj pakietów, które wymagają
buildów postinstall.
Opcjonalnie: openclaw.setupEntry może wskazywać lekki moduł tylko do konfiguracji.
Gdy OpenClaw potrzebuje powierzchni konfiguracji dla wyłączonego pluginu kanału albo
gdy plugin kanału jest włączony, ale nadal nieskonfigurowany, ładuje setupEntry
zamiast pełnego wejścia pluginu. Dzięki temu uruchamianie i konfiguracja są lżejsze,
gdy główne wejście pluginu podłącza także narzędzia, hooki lub inny kod wyłącznie runtime.
Opcjonalnie: openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen
może włączyć plugin kanału do tej samej ścieżki setupEntry podczas fazy uruchamiania gatewaya
przed nasłuchiwaniem, nawet gdy kanał jest już skonfigurowany.
Używaj tego tylko wtedy, gdy setupEntry w pełni obejmuje powierzchnię uruchomieniową, która musi istnieć,
zanim gateway zacznie nasłuchiwać. W praktyce oznacza to, że wpis konfiguracji
musi zarejestrować każdą należącą do kanału możliwość, od której zależy uruchomienie, taką jak:
- sama rejestracja kanału
- wszelkie trasy HTTP, które muszą być dostępne, zanim gateway zacznie nasłuchiwać
- wszelkie metody, narzędzia lub usługi gatewaya, które muszą istnieć w tym samym oknie
Jeśli pełne wejście nadal posiada jakąkolwiek wymaganą możliwość uruchomieniową, nie włączaj tej flagi. Pozostaw plugin przy zachowaniu domyślnym i pozwól OpenClaw załadować pełne wejście podczas uruchamiania.
Dołączone kanały mogą również publikować pomocniki powierzchni kontraktu tylko do konfiguracji, z których core może korzystać, zanim pełny runtime kanału zostanie załadowany. Obecna powierzchnia promocji konfiguracji to:
singleAccountKeysToMovenamedAccountPromotionKeysresolveSingleAccountPromotionTarget(...)
Rdzeń używa tej powierzchni, gdy musi promować starszą konfigurację kanału z jednym kontem do channels.<id>.accounts.* bez ładowania pełnego wpisu Plugin. Matrix jest obecnym dołączonym przykładem: przenosi tylko klucze uwierzytelniania/rozruchu do nazwanego promowanego konta, gdy nazwane konta już istnieją, i może zachować skonfigurowany niekanoniczny klucz konta domyślnego zamiast zawsze tworzyć accounts.default.
Te adaptery poprawek konfiguracji utrzymują leniwe wykrywanie dołączonej powierzchni kontraktu. Czas importu pozostaje lekki; powierzchnia promocji jest ładowana dopiero przy pierwszym użyciu zamiast ponownie wchodzić w uruchamianie dołączonego kanału podczas importu modułu.
Gdy te powierzchnie startowe obejmują metody RPC Gateway, zachowaj je pod prefiksem specyficznym dla Plugin. Przestrzenie nazw administracyjnych rdzenia (config.*, exec.approvals.*, wizard.*, update.*) pozostają zarezerwowane i zawsze są rozwiązywane do operator.admin, nawet jeśli Plugin żąda węższego zakresu.
Przykład:
{
"name": "@scope/my-channel",
"openclaw": {
"extensions": ["./index.ts"],
"setupEntry": "./setup-entry.ts",
"startup": {
"deferConfiguredChannelFullLoadUntilAfterListen": true
}
}
}
Metadane katalogu kanałów
Plugin kanałów mogą ogłaszać metadane konfiguracji/wykrywania przez openclaw.channel oraz wskazówki instalacji przez openclaw.install. Dzięki temu główny katalog pozostaje wolny od danych.
Przykład:
{
"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"
}
}
}
Przydatne pola openclaw.channel poza minimalnym przykładem:
detailLabel: etykieta pomocnicza dla bogatszych powierzchni katalogu/statusudocsLabel: zastępuje tekst linku do dokumentacjipreferOver: identyfikatory Plugin/kanałów o niższym priorytecie, które ten wpis katalogu powinien wyprzedzaćselectionDocsPrefix,selectionDocsOmitLabel,selectionExtras: kontrolki treści powierzchni wyborumarkdownCapable: oznacza kanał jako obsługujący Markdown na potrzeby decyzji o formatowaniu wiadomości wychodzącychexposure.configured: ukrywa kanał z powierzchni list skonfigurowanych kanałów, gdy ustawione nafalseexposure.setup: ukrywa kanał z interaktywnych list wyboru konfiguracji/konfigurowania, gdy ustawione nafalseexposure.docs: oznacza kanał jako wewnętrzny/prywatny dla powierzchni nawigacji dokumentacjishowConfigured/showInSetup: starsze aliasy nadal akceptowane dla zgodności; preferujexposurequickstartAllowFrom: włącza kanał do standardowego przepływu szybkiego startuallowFromforceAccountBinding: wymaga jawnego powiązania konta nawet wtedy, gdy istnieje tylko jedno kontopreferSessionLookupForAnnounceTarget: preferuje wyszukiwanie sesji podczas rozwiązywania celów ogłoszeń
OpenClaw może też scalać zewnętrzne katalogi kanałów (na przykład eksport rejestru MPM). Umieść plik JSON w jednej z lokalizacji:
~/.openclaw/mpm/plugins.json~/.openclaw/mpm/catalog.json~/.openclaw/plugins/catalog.json
Albo wskaż OPENCLAW_PLUGIN_CATALOG_PATHS (lub OPENCLAW_MPM_CATALOG_PATHS) na jeden lub więcej plików JSON (rozdzielonych przecinkiem/średnikiem/PATH). Każdy plik powinien zawierać { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }. Parser akceptuje też "packages" lub "plugins" jako starsze aliasy klucza "entries".
Wygenerowane wpisy katalogu kanałów oraz wpisy katalogu instalacji dostawców udostępniają znormalizowane fakty o źródle instalacji obok surowego bloku openclaw.install. Znormalizowane fakty określają, czy specyfikacja npm jest dokładną wersją czy pływającym selektorem, czy obecne są oczekiwane metadane integralności oraz czy dostępna jest też lokalna ścieżka źródłowa. Gdy znana jest tożsamość katalogu/pakietu, znormalizowane fakty ostrzegają, jeśli przeanalizowana nazwa pakietu npm odbiega od tej tożsamości. Ostrzegają też, gdy defaultChoice jest nieprawidłowe lub wskazuje na niedostępne źródło, oraz gdy metadane integralności npm są obecne bez prawidłowego źródła npm. Konsumenci powinni traktować installSource jako dodatkowe pole opcjonalne, aby ręcznie tworzone wpisy i adaptery katalogów nie musiały go syntetyzować. Dzięki temu onboarding i diagnostyka mogą wyjaśniać stan płaszczyzny źródeł bez importowania środowiska wykonawczego Plugin.
Oficjalne zewnętrzne wpisy npm powinny preferować dokładne npmSpec wraz z expectedIntegrity. Same nazwy pakietów i tagi dystrybucyjne nadal działają dla zgodności, ale pokazują ostrzeżenia płaszczyzny źródeł, aby katalog mógł przejść w kierunku przypiętych instalacji ze sprawdzaną integralnością bez psucia istniejących Plugin. Gdy onboarding instaluje z lokalnej ścieżki katalogu, zapisuje zarządzany wpis indeksu Plugin z source: "path" oraz względnym wobec obszaru roboczego sourcePath, gdy to możliwe. Bezwzględna operacyjna ścieżka ładowania pozostaje w plugins.load.paths; rekord instalacji unika duplikowania lokalnych ścieżek stacji roboczej w długotrwałej konfiguracji. Dzięki temu lokalne instalacje deweloperskie są widoczne dla diagnostyki płaszczyzny źródeł bez dodawania drugiej surowej powierzchni ujawniania ścieżek systemu plików. Utrwalony indeks Plugin plugins/installs.json jest źródłem prawdy instalacji i może być odświeżany bez ładowania modułów środowiska wykonawczego Plugin. Jego mapa installRecords jest trwała nawet wtedy, gdy manifest Plugin jest brakujący lub nieprawidłowy; jego tablica plugins jest odtwarzalnym widokiem manifestów.
Plugin silnika kontekstu
Plugin silnika kontekstu są właścicielami orkiestracji kontekstu sesji dla pozyskiwania, składania i Compaction. Zarejestruj je z Plugin za pomocą api.registerContextEngine(id, factory), a następnie wybierz aktywny silnik przez plugins.slots.contextEngine.
Użyj tego, gdy Plugin musi zastąpić lub rozszerzyć domyślny potok kontekstu, a nie tylko dodać wyszukiwanie pamięci lub hooki.
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 };
},
}));
}
Fabryka ctx udostępnia opcjonalne wartości config, agentDir i workspaceDir do inicjalizacji w czasie konstrukcji.
Jeśli Twój silnik nie jest właścicielem algorytmu Compaction, pozostaw compact() zaimplementowane i deleguj je jawnie:
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);
},
}));
}
Dodawanie nowej możliwości
Gdy Plugin potrzebuje zachowania, które nie pasuje do obecnego API, nie obchodź systemu Plugin prywatnym sięgnięciem do wnętrza. Dodaj brakującą możliwość.
Zalecana kolejność:
- zdefiniuj kontrakt rdzenia Zdecyduj, jakie współdzielone zachowanie powinien posiadać rdzeń: politykę, fallback, scalanie konfiguracji, cykl życia, semantykę widoczną dla kanału i kształt pomocnika środowiska wykonawczego.
- dodaj typowane powierzchnie rejestracji/środowiska wykonawczego Plugin
Rozszerz
OpenClawPluginApii/lubapi.runtimeo najmniejszą użyteczną typowaną powierzchnię możliwości. - połącz rdzeń i konsumentów kanału/funkcji Kanały i Plugin funkcji powinny korzystać z nowej możliwości przez rdzeń, a nie przez bezpośredni import implementacji dostawcy.
- zarejestruj implementacje dostawców Plugin dostawców rejestrują następnie swoje backendy względem możliwości.
- dodaj pokrycie kontraktu Dodaj testy, aby własność i kształt rejestracji pozostawały jawne z czasem.
W ten sposób OpenClaw pozostaje opiniotwórczy, nie stając się zakodowanym na stałe pod światopogląd jednego dostawcy. Zobacz Capability Cookbook, aby uzyskać konkretną listę kontrolną plików i opracowany przykład.
Lista kontrolna możliwości
Gdy dodajesz nową możliwość, implementacja zwykle powinna dotykać tych powierzchni razem:
- typy kontraktu rdzenia w
src/<capability>/types.ts - pomocnik uruchamiający/środowiska wykonawczego rdzenia w
src/<capability>/runtime.ts - powierzchnia rejestracji API Plugin w
src/plugins/types.ts - okablowanie rejestru Plugin w
src/plugins/registry.ts - ekspozycja środowiska wykonawczego Plugin w
src/plugins/runtime/*, gdy Plugin funkcji/kanałów muszą z niej korzystać - pomocniki przechwytywania/testów w
src/test-utils/plugin-registration.ts - asercje własności/kontraktu w
src/plugins/contracts/registry.ts - dokumentacja operatora/Plugin w
docs/
Jeśli brakuje jednej z tych powierzchni, zwykle jest to znak, że możliwość nie jest jeszcze w pełni zintegrowana.
Szablon możliwości
Minimalny wzorzec:
// 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,
});
Wzorzec testu kontraktu:
expect(findVideoGenerationProviderIdsForPlugin("openai")).toEqual(["openai"]);
Dzięki temu reguła pozostaje prosta:
- rdzeń posiada kontrakt możliwości i orkiestrację
- Plugin dostawców posiadają implementacje dostawców
- Plugin funkcji/kanałów korzystają z pomocników środowiska wykonawczego
- testy kontraktu utrzymują jawną własność
Powiązane
- Architektura Plugin — publiczny model i kształty możliwości
- Podścieżki Plugin SDK
- Konfiguracja Plugin SDK
- Budowanie Plugin