Przejdź do głównej treści
Publiczny model 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 runtime, tras HTTP Gateway, ścieżek importu i tabel schematów.

Potok ładowania

Podczas uruchamiania OpenClaw wykonuje w przybliżeniu to:
  1. wykrywa katalogi główne kandydatów Plugin
  2. odczytuje natywne lub zgodne manifesty pakietów oraz metadane pakietów
  3. odrzuca niebezpiecznych kandydatów
  4. normalizuje konfigurację Plugin (plugins.enabled, allow, deny, entries, slots, load.paths)
  5. decyduje o włączeniu każdego kandydata
  6. ładuje włączone moduły natywne: zbudowane moduły pakietowane używają natywnego loadera; lokalne źródła TypeScript firm trzecich używają awaryjnego fallbacku Jiti
  7. wywołuje natywne hooki register(api) i zbiera rejestracje w rejestrze Plugin
  8. udostępnia rejestr poleceniom i powierzchniom runtime
activate jest starszym aliasem register — loader rozwiązuje dostępny wariant (def.register ?? def.activate) i wywołuje go w tym samym miejscu. Wszystkie pakietowane Plugin używają register; dla nowych Plugin preferuj register.
Bramki bezpieczeństwa działają przed wykonaniem runtime. Kandydaci są blokowani, gdy entry wychodzi poza katalog główny Plugin, ścieżka jest zapisywalna dla wszystkich albo własność ścieżki wygląda podejrzanie w przypadku niepakietowanych Plugin. 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 wskazuje ostrzeżenie 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 do:
  • identyfikowania Plugin
  • wykrywania zadeklarowanych kanałów/Skills/schematu konfiguracji lub możliwości pakietu
  • walidowania plugins.entries.<id>.config
  • uzupełniania etykiet/placeholderów Control UI
  • pokazywania metadanych instalacji/katalogu
  • zachowywania tanich deskryptorów aktywacji i konfiguracji bez ładowania runtime Plugin
W przypadku natywnych Plugin moduł runtime jest częścią płaszczyzny danych. Rejestruje rzeczywiste zachowanie, takie jak hooki, narzędzia, polecenia lub przepływy provideró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 runtime, register(...) ani setupEntry. Pierwsi aktywni konsumenci aktywacji używają teraz podpowiedzi manifestu dotyczących poleceń, kanałów i providerów, aby zawęzić ładowanie Plugin przed szerszą materializacją rejestru:
  • ładowanie CLI zawęża zakres do Plugin, które są właścicielami żądanego polecenia głównego
  • konfiguracja kanału/rozwiązywanie Plugin zawęża zakres do Plugin, które są właścicielami żądanego identyfikatora kanału
  • jawna konfiguracja providera/rozwiązywanie runtime zawęża zakres do Plugin, które są właścicielami żądanego identyfikatora providera
  • planowanie uruchamiania Gateway używa activation.onStartup dla jawnych importów startowych i rezygnacji ze startu; Plugin bez metadanych startowych ładują się tylko przez węższe wyzwalacze aktywacji
Preloady runtime w czasie żądania, które proszą o szeroki zakres all, nadal wyprowadzają 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 runtime zamiast rozszerzać zakres na każdy wykrywalny Plugin. Planer aktywacji udostępnia zarówno API zawierające tylko identyfikatory dla istniejących wywołań, jak i API planu dla nowej diagnostyki. Wpisy planu raportują, dlaczego Plugin został wybrany, oddzielając jawne podpowiedzi planera activation.* od fallbacku własności manifestu, takiego jak providers, channels, commandAliases, setup.providers, contracts.tools i hooki. Ten podział przyczyn jest granicą zgodności: istniejące metadane Plugin nadal działają, a nowy kod może wykrywać szerokie podpowiedzi lub zachowanie fallbacku bez zmiany semantyki ładowania runtime. Wykrywanie konfiguracji preferuje teraz identyfikatory należące do deskryptora, takie jak setup.providers i setup.cliBackends, aby zawęzić kandydatów Plugin, zanim wróci do setup-api dla Plugin, które nadal potrzebują hooków runtime w czasie konfiguracji. Listy konfiguracji providerów używają manifestu providerAuthChoices, wyborów konfiguracji wyprowadzonych z deskryptorów oraz metadanych katalogu instalacji bez ładowania runtime providera. Jawne setup.requiresRuntime: false jest odcięciem wyłącznie deskryptorowym; pominięte requiresRuntime zachowuje starszy fallback setup-api dla zgodności. Jeśli więcej niż jeden wykryty Plugin deklaruje ten sam znormalizowany identyfikator providera konfiguracji lub backendu CLI, wyszukiwanie konfiguracji odmawia niejednoznacznego właściciela zamiast polegać na kolejności wykrywania. Gdy runtime konfiguracji faktycznie się wykonuje, diagnostyka rejestru raportuje rozbieżność między setup.providers / setup.cliBackends a providerami lub backendami CLI zarejestrowanymi przez setup-api, bez blokowania starszych Plugin.

Granica cache Plugin

OpenClaw nie cache’uje wyników wykrywania Plugin ani bezpośrednich danych rejestru manifestów za oknami czasu zegarowego. Instalacje, edycje manifestów i zmiany ścieżek ładowania muszą być widoczne przy następnym jawnym odczycie metadanych lub odbudowie snapshotu. Parser pliku manifestu może utrzymywać ograniczony cache sygnatur plików oparty na otwartej ścieżce manifestu, inode, rozmiarze i znacznikach czasu; ten cache tylko unika ponownego parsowania niezmienionych bajtów i nie może cache’ować odpowiedzi dotyczących wykrywania, rejestru, właściciela ani polityki. Bezpieczna szybka ścieżka metadanych to jawna własność obiektu, a nie ukryty cache. Gorące ścieżki uruchamiania Gateway powinny przekazywać aktualny PluginMetadataSnapshot, wyprowadzoną PluginLookUpTable albo jawny rejestr manifestów przez łańcuch wywołań. Walidacja konfiguracji, automatyczne włączanie przy starcie, bootstrap Plugin i wybór providera mogą ponownie używać tych obiektów, dopóki reprezentują aktualną konfigurację i inwentarz Plugin. Wyszukiwanie konfiguracji nadal rekonstruuje metadane manifestu na żądanie, chyba że konkretna ścieżka konfiguracji otrzyma jawny rejestr manifestów; traktuj to jako fallback zimnej ścieżki zamiast dodawać ukryte cache wyszukiwania. Gdy dane wejściowe się zmienią, odbuduj i zastąp snapshot zamiast mutować go lub trzymać kopie historyczne. Widoki aktywnego rejestru Plugin i helpery bootstrapu pakietowanych kanałów powinny być ponownie obliczane z aktualnego 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ę cache’ami metadanych procesu. W przypadku ładowania Plugin trwałą warstwą cache jest ładowanie runtime. Może ponownie używać stanu loadera, gdy kod lub zainstalowane artefakty są faktycznie ładowane, na przykład:
  • PluginLoaderCacheState i zgodne aktywne rejestry runtime
  • cache jiti/modułów oraz cache loadera powierzchni publicznych używane, aby unikać wielokrotnego importowania tej samej powierzchni runtime
  • cache systemu plików dla zainstalowanych artefaktów Plugin
  • krótkotrwałe mapy per wywołanie do normalizacji ścieżek lub rozwiązywania duplikatów
Te cache są szczegółami implementacyjnymi płaszczyzny danych. Nie mogą odpowiadać na pytania płaszczyzny sterowania, takie jak „który Plugin jest właścicielem tego providera?”, chyba że wywołujący celowo poprosił o ładowanie runtime. Nie dodawaj trwałych ani zegarowych cache dla:
  • wyników wykrywania
  • bezpośrednich rejestrów manifestów
  • rejestrów manifestów rekonstruowanych z indeksu zainstalowanych Plugin
  • wyszukiwania właściciela providera, tłumienia modeli, polityki providera lub metadanych publicznych artefaktów
  • jakiejkolwiek innej odpowiedzi wyprowadzonej z manifestu, gdzie zmieniony manifest, zainstalowany indeks lub ścieżka ładowania powinny być widoczne przy następnym odczycie metadanych
Wywołujący, którzy odbudowują metadane manifestu z utrwalonego indeksu zainstalowanych Plugin, rekonstruują ten rejestr na żądanie. Zainstalowany indeks jest trwałym stanem płaszczyzny źródłowej; nie jest ukrytym cache metadanych w procesie.

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
  • providerów
  • handlery RPC Gateway
  • trasy HTTP
  • rejestratory CLI
  • usługi w tle
  • polecenia należące do Plugin
Funkcje rdzenia odczytują potem z tego rejestru zamiast komunikować się bezpośrednio z modułami Plugin. Dzięki temu ładowanie pozostaje jednokierunkowe:
  • moduł Plugin -> rejestracja w rejestrze
  • runtime rdzenia -> użycie 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 powiązania rozmowy

Plugin, które wiążą rozmowę, mogą reagować, gdy zatwierdzenie zostanie rozstrzygnięte. 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" lub "denied"
  • decision: "allow-once", "allow-always" lub "deny"
  • binding: rozwiązane powiązanie dla zatwierdzonych żądań
  • request: oryginalne podsumowanie żądania, podpowiedź odłączenia, identyfikator nadawcy i metadane rozmowy
Ten callback służy wyłącznie do powiadamiania. Nie zmienia tego, komu wolno powiązać rozmowę, i uruchamia się po zakończeniu obsługi zatwierdzania przez rdzeń.

Hooki runtime providera

Plugin providerów mają trzy warstwy:
  • Metadane manifestu do taniego wyszukiwania przed runtime: setup.providers[].envVars, przestarzała zgodność providerAuthEnvVars, providerAuthAliases, providerAuthChoices i channelEnvVars.
  • Hooki czasu konfiguracji: catalog (starsze discovery) oraz applyConfigDefaults.
  • Hooki runtime: ponad 40 opcjonalnych hooków obejmujących auth, rozwiązywanie modeli, opakowywanie strumieni, poziomy myślenia, politykę replay oraz endpointy użycia. Zobacz pełną listę w sekcji Kolejność i użycie hooków.
OpenClaw nadal jest właścicielem ogólnej pętli agenta, failoveru, obsługi transkrypcji i polityki narzędzi. Te hooki są powierzchnią rozszerzeń dla zachowania specyficznego dla providera bez potrzeby tworzenia całego niestandardowego transportu inferencji. Użyj manifestu setup.providers[].envVars, gdy provider ma poświadczenia oparte na env, które ogólne ścieżki auth/status/wyboru modelu powinny widzieć bez ładowania runtime Plugin. Przestarzałe providerAuthEnvVars jest nadal odczytywane przez adapter zgodności w okresie deprecjacji, a niepakietowane Plugin używające go otrzymują diagnostykę manifestu. Użyj manifestu providerAuthAliases, gdy jeden identyfikator providera powinien ponownie używać zmiennych env, profili auth, auth opartego na konfiguracji i wyboru onboardingu klucza API innego identyfikatora providera. Użyj manifestu providerAuthChoices, gdy powierzchnie CLI onboardingu/wyboru auth powinny znać identyfikator wyboru providera, etykiety grup i proste jednoflagowe okablowanie auth bez ładowania runtime providera. Zachowaj runtime providera envVars dla wskazówek skierowanych do operatora, takich jak etykiety onboardingu lub zmienne konfiguracji client-id/client-secret OAuth. Użyj manifestu channelEnvVars, gdy kanał ma auth lub konfigurację sterowaną env, które ogólny fallback shell-env, kontrole konfiguracji/statusu lub prompty konfiguracji powinny widzieć bez ładowania runtime kanału.

Kolejność i użycie hooków

Dla Plugin modeli/providerów OpenClaw wywołuje hooki mniej więcej w tej kolejności. Kolumna „Kiedy używać” jest szybkim przewodnikiem decyzyjnym. Pola providerów wyłącznie zgodnościowe, których OpenClaw już nie wywołuje, takie jak ProviderPlugin.capabilities i suppressBuiltInModel, celowo nie są tu wymienione.
#HookCo robiKiedy używać
1catalogPublikuje konfigurację dostawcy do models.providers podczas generowania models.jsonDostawca jest właścicielem katalogu lub domyślnych wartości bazowego adresu URL
2applyConfigDefaultsStosuje globalne wartości domyślne konfiguracji należące do dostawcy podczas materializacji konfiguracjiWartości domyślne zależą od trybu uwierzytelniania, środowiska lub semantyki rodziny modeli dostawcy
(wbudowane wyszukiwanie modelu)OpenClaw najpierw próbuje użyć normalnej ścieżki rejestru/katalogu(nie jest to hook Plugin)
3normalizeModelIdNormalizuje starsze aliasy lub aliasy podglądowych identyfikatorów modeli przed wyszukiwaniemDostawca odpowiada za oczyszczanie aliasów przed kanonicznym rozwiązywaniem modelu
4normalizeTransportNormalizuje api / baseUrl rodziny dostawcy przed ogólnym składaniem modeluDostawca odpowiada za oczyszczanie transportu dla niestandardowych identyfikatorów dostawców w tej samej rodzinie transportu
5normalizeConfigNormalizuje models.providers.<id> przed rozwiązywaniem środowiska uruchomieniowego/dostawcyDostawca potrzebuje oczyszczania konfiguracji, które powinno należeć do pluginu; dołączone pomocniki rodziny Google zabezpieczają też obsługiwane wpisy konfiguracji Google
6applyNativeStreamingUsageCompatStosuje poprawki zgodności natywnego użycia strumieniowania do dostawców konfiguracjiDostawca potrzebuje poprawek metadanych natywnego użycia strumieniowania sterowanych punktem końcowym
7resolveConfigApiKeyRozwiązuje uwierzytelnianie znacznikiem środowiskowym dla dostawców konfiguracji przed ładowaniem uwierzytelniania środowiska uruchomieniowegoDostawcy udostępniają własne hooki rozwiązywania klucza API ze znacznika środowiskowego
8resolveSyntheticAuthUdostępnia lokalne/samodzielnie hostowane lub oparte na konfiguracji uwierzytelnianie bez utrwalania tekstu jawnegoDostawca może działać z syntetycznym/lokalnym znacznikiem poświadczeń
9resolveExternalAuthProfilesNakłada zewnętrzne profile uwierzytelniania należące do dostawcy; domyślna wartość persistence to runtime-only dla poświadczeń należących do CLI/aplikacjiDostawca ponownie używa zewnętrznych poświadczeń uwierzytelniania bez utrwalania skopiowanych tokenów odświeżania; zadeklaruj contracts.externalAuthProviders w manifeście
10shouldDeferSyntheticProfileAuthObniża priorytet zapisanych syntetycznych symboli zastępczych profilu za uwierzytelnianiem opartym na środowisku/konfiguracjiDostawca przechowuje syntetyczne profile zastępcze, które nie powinny mieć pierwszeństwa
11resolveDynamicModelSynchroniczny mechanizm awaryjny dla identyfikatorów modeli należących do dostawcy, których nie ma jeszcze w lokalnym rejestrzeDostawca akceptuje dowolne identyfikatory modeli z usługi źródłowej
12prepareDynamicModelAsynchroniczne przygotowanie, po którym resolveDynamicModel uruchamia się ponownieDostawca potrzebuje metadanych z sieci przed rozwiązywaniem nieznanych identyfikatorów
13normalizeResolvedModelKońcowe przepisanie, zanim osadzony mechanizm uruchamiania użyje rozwiązanego modeluDostawca potrzebuje przepisań transportu, ale nadal używa transportu z rdzenia
14normalizeToolSchemasNormalizuje schematy narzędzi, zanim zobaczy je osadzony mechanizm uruchamianiaDostawca potrzebuje oczyszczania schematów rodziny transportu
15inspectToolSchemasUdostępnia diagnostykę schematów należącą do dostawcy po normalizacjiDostawca chce ostrzeżeń o słowach kluczowych bez uczenia rdzenia reguł specyficznych dla dostawcy
16resolveReasoningOutputModeWybiera natywny lub tagowany kontrakt wyjścia rozumowaniaDostawca potrzebuje tagowanego rozumowania/końcowego wyjścia zamiast pól natywnych
17prepareExtraParamsNormalizacja parametrów żądania przed ogólnymi opakowaniami opcji strumieniaDostawca potrzebuje domyślnych parametrów żądania lub oczyszczania parametrów dla konkretnego dostawcy
18createStreamFnW pełni zastępuje normalną ścieżkę strumienia niestandardowym transportemDostawca potrzebuje niestandardowego protokołu przewodowego, nie tylko opakowania
20wrapStreamFnOpakowanie strumienia po zastosowaniu ogólnych opakowańDostawca potrzebuje opakowań zgodności nagłówków/treści/modelu żądania bez niestandardowego transportu
21resolveTransportTurnStateDołącza natywne nagłówki transportu lub metadane dla pojedynczej turyDostawca chce, aby ogólne transporty wysyłały natywną dla dostawcy tożsamość tury
22resolveWebSocketSessionPolicyDołącza natywne nagłówki WebSocket lub zasady wyciszenia sesjiDostawca chce, aby ogólne transporty WS dostrajały nagłówki sesji lub zasady awaryjne
23formatApiKeyFormater profilu uwierzytelniania: zapisany profil staje się ciągiem apiKey środowiska uruchomieniowegoDostawca przechowuje dodatkowe metadane uwierzytelniania i potrzebuje niestandardowego kształtu tokenu środowiska uruchomieniowego
24refreshOAuthNadpisanie odświeżania OAuth dla niestandardowych punktów końcowych odświeżania lub zasad błędu odświeżaniaDostawca nie pasuje do współdzielonych mechanizmów odświeżania OpenClaw
25buildAuthDoctorHintWskazówka naprawy dodawana, gdy odświeżanie OAuth się nie powiedzieDostawca potrzebuje własnych wskazówek naprawy uwierzytelniania po błędzie odświeżania
26matchesContextOverflowErrorMatcher przepełnienia okna kontekstu należący do dostawcyDostawca ma surowe błędy przepełnienia, których ogólne heurystyki by nie wykryły
27classifyFailoverReasonKlasyfikacja przyczyny przełączenia awaryjnego należąca do dostawcyDostawca może mapować surowe błędy API/transportu na limit szybkości/przeciążenie itd.
28isCacheTtlEligibleZasady pamięci podręcznej promptów dla dostawców proxy/backhaulDostawca potrzebuje bramkowania TTL pamięci podręcznej specyficznego dla proxy
29buildMissingAuthMessageZamiennik ogólnego komunikatu odzyskiwania brakującego uwierzytelnianiaDostawca potrzebuje wskazówki odzyskiwania brakującego uwierzytelniania specyficznej dla dostawcy
30augmentModelCatalogSyntetyczne/końcowe wiersze katalogu dodawane po wykrywaniuDostawca potrzebuje syntetycznych wierszy zgodności w przód w models list i selektorach
31resolveThinkingProfileZestaw poziomów /think specyficzny dla modelu, etykiety wyświetlania i wartość domyślnaDostawca udostępnia niestandardową drabinę myślenia lub etykietę binarną dla wybranych modeli
32isBinaryThinkingHook zgodności przełącznika rozumowania włącz/wyłączDostawca udostępnia tylko binarne włączanie/wyłączanie myślenia
33supportsXHighThinkingHook zgodności obsługi rozumowania xhighDostawca chce xhigh tylko dla podzbioru modeli
34resolveDefaultThinkingLevelHook zgodności domyślnego poziomu /thinkDostawca jest właścicielem domyślnych zasad /think dla rodziny modeli
35isModernModelRefMatcher nowoczesnego modelu dla filtrów profilu live i wyboru testu smokeDostawca jest właścicielem dopasowania preferowanego modelu live/smoke
36prepareRuntimeAuthWymienia skonfigurowane poświadczenie na rzeczywisty token/klucz środowiska uruchomieniowego tuż przed inferencjąDostawca potrzebuje wymiany tokenu lub krótkotrwałego poświadczenia żądania
37resolveUsageAuthRozwiązuje poświadczenia użycia/rozliczeń dla /usage i powiązanych powierzchni statusuDostawca potrzebuje niestandardowego parsowania tokenu użycia/limitu lub innego poświadczenia użycia
38fetchUsageSnapshotPobierz i znormalizuj migawki użycia/limitu specyficzne dla dostawcy po rozstrzygnięciu uwierzytelnianiaDostawca potrzebuje specyficznego dla dostawcy punktu końcowego użycia lub parsera ładunku
39createEmbeddingProviderZbuduj należący do dostawcy adapter osadzania dla pamięci/wyszukiwaniaZachowanie osadzania pamięci należy do Plugin dostawcy
40buildReplayPolicyZwróć politykę replay kontrolującą obsługę transkryptu dla dostawcyDostawca potrzebuje niestandardowej polityki transkryptu (na przykład usuwania bloków myślenia)
41sanitizeReplayHistoryPrzepisz historię replay po ogólnym czyszczeniu transkryptuDostawca potrzebuje specyficznych dla dostawcy przekształceń replay wykraczających poza współdzielone pomocniki kompaktowania
42validateReplayTurnsKońcowa walidacja lub przekształcenie tur replay przed osadzonym runneremTransport dostawcy wymaga ściślejszej walidacji tur po ogólnym czyszczeniu
43onModelSelectedUruchom należące do dostawcy efekty uboczne po wyborzeDostawca potrzebuje telemetrii lub 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 albo transport/config. Dzięki temu warstwy aliasów/zgodności dostawców nadal działają bez wymagania, aby wywołujący wiedział, który wbudowany Plugin jest właścicielem przepisywania. Jeśli żaden hook dostawcy nie przepisze obsługiwanego wpisu konfiguracji z rodziny Google, wbudowany normalizator konfiguracji Google nadal zastosuje to czyszczenie zgodności. Jeśli dostawca potrzebuje w pełni niestandardowego protokołu przewodowego albo 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. resolveUsageAuth decyduje, czy OpenClaw powinien wywołać fetchUsageSnapshot, czy wrócić do ogólnego rozwiązywania poświadczeń dla powierzchni użycia/statusu. Zwróć { token, accountId? }, gdy dostawca ma poświadczenie użycia, zwróć { handled: true }, gdy obsługa uwierzytelniania użycia należąca do dostawcy obsłużyła żądanie i musi wyłączyć ogólny fallback klucza API/OAuth, oraz zwróć null albo undefined, gdy dostawca nie obsłużył uwierzytelniania użycia.

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

Wbudowane Pluginy dostawców łączą powyższe hooki, aby dopasować się do katalogu, uwierzytelniania, rozumowania, odtwarzania i potrzeb użycia poszczególnych vendorów. Autorytatywny zestaw hooków znajduje się przy każdym Pluginie w extensions/; ta strona ilustruje kształty zamiast odzwierciedlać listę.
OpenRouter, Kilocode, Z.AI, xAI rejestrują catalog oraz resolveDynamicModel / prepareDynamicModel, aby mogły pokazywać nadrzędne identyfikatory modeli przed statycznym katalogiem OpenClaw.
GitHub Copilot, Gemini CLI, ChatGPT Codex, MiniMax, Xiaomi, z.ai łączą prepareRuntimeAuth albo formatApiKey z resolveUsageAuth + fetchUsageSnapshot, aby posiadać wymianę tokenów i integrację /usage.
Współdzielone nazwane rodziny (google-gemini, passthrough-gemini, anthropic-by-model, hybrid-anthropic-openai) pozwalają dostawcom włączać politykę transkryptu przez buildReplayPolicy, zamiast aby każdy Plugin ponownie implementował czyszczenie.
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.
Nagłówki beta, /fast / serviceTier oraz context1m znajdują się w publicznym punkcie styku api.ts / contract-api.ts Pluginu Anthropic (wrapAnthropicProviderStream, resolveAnthropicBetas, resolveAnthropicFastMode, resolveAnthropicServiceTier), a nie w ogólnym SDK.

Pomocniki 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:
  • textToSpeech zwraca normalny ładunek wyjściowy TTS rdzenia dla powierzchni plików/notatek głosowych.
  • Używa konfiguracji messages.tts rdzenia i wyboru dostawcy.
  • Zwraca bufor audio PCM + częstotliwość próbkowania. Pluginy muszą ponownie próbkować/kodować dla dostawców.
  • listVoices jest opcjonalne dla każdego dostawcy. Używaj go dla należących do vendora selektorów głosów albo przepływów konfiguracji.
  • Listy głosów mogą zawierać bogatsze metadane, takie jak locale, 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 vendora.
  • Starsze wejście Microsoft edge jest normalizowane do identyfikatora dostawcy microsoft.
  • Preferowany model własności jest zorientowany na firmę: jeden Plugin vendora może posiadać dostawców tekstu, mowy, obrazu i przyszłych mediów, gdy OpenClaw doda te kontrakty funkcjonalności.
Dla rozumienia obrazów/audio/wideo Pluginy rejestrują jednego typowanego dostawcę rozumienia mediów zamiast ogólnego 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 vendora w Pluginie dostawcy.
  • Rozszerzanie addytywne powinno pozostać typowane: nowe opcjonalne metody, nowe opcjonalne pola wyników, nowe opcjonalne funkcjonalności.
  • Generowanie wideo już stosuje ten sam wzorzec:
    • rdzeń posiada kontrakt funkcjonalności i pomocnik runtime
    • Pluginy vendorów rejestrują api.registerVideoGenerationProvider(...)
    • Pluginy funkcji/kanałów korzystają z 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,
});

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,
});
Dla transkrypcji audio Pluginy mogą użyć 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.
  • extractStructuredWithModel(...) jest punktem styku dla Pluginów do ograniczonej ekstrakcji należącej do dostawcy i zaczynającej od obrazu. Dołącz co najmniej jedno wejście obrazu; wejścia tekstowe są kontekstem uzupełniającym. Pluginy produktowe posiadają swoje trasy i schematy, podczas gdy OpenClaw posiada granicę dostawcy/runtime.
  • Używa konfiguracji audio rozumienia mediów rdzenia (tools.media.audio) i kolejności fallbacku dostawców.
  • Zwraca { text: undefined }, gdy nie powstanie żadne wyjście transkrypcji (na przykład pominięte/nieobsługiwane wejście).
  • api.runtime.stt.transcribeAudioFile(...) pozostaje aliasem zgodności.
Pluginy mogą także uruchamiać działające w tle przebiegi podagentów 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:
  • provider i model są opcjonalnymi nadpisaniami dla pojedynczego przebiegu, a nie trwałymi zmianami sesji.
  • OpenClaw honoruje te pola nadpisania tylko dla zaufanych wywołujących.
  • Dla należących do Pluginu przebiegów fallbacku 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ów provider/model, albo "*", aby jawnie zezwolić na dowolny cel.
  • Niezaufane przebiegi podagentów Pluginów nadal działają, ale żądania nadpisania są odrzucane zamiast cicho wracać do fallbacku.
  • Sesje podagentów utworzone przez Plugin są tagowane identyfikatorem tworzącego Pluginu. Fallback api.runtime.subagent.deleteSession(...) może usuwać tylko te posiadane sesje; dowolne usuwanie sesji nadal wymaga żądania Gateway o zakresie administracyjnym.
Dla wyszukiwania w sieci Pluginy mogą używać współdzielonego pomocnika runtime zamiast sięgać do okablowania narzędzi 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 specyficznych dla vendora transportów wyszukiwania.
  • 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ędzi 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 za pomocą skonfigurowanego łańcucha dostawców generowania obrazów.
  • listProviders(...): wyświetl dostępnych dostawców generowania obrazów i ich funkcjonalności.

Trasy HTTP Gateway

Pluginy mogą udostępniać punkty końcowe 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ć zwykłego uwierzytelniania gateway, albo "plugin" dla uwierzytelniania/weryfikacji Webhook 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(...) zostało usunięte i spowoduje błąd ładowania pluginu. Zamiast tego użyj api.registerHttpRoute(...).
  • Trasy pluginów muszą jawnie deklarować auth.
  • Dokładne konflikty path + match są odrzucane, chyba że ustawiono replaceExisting: true, a jeden plugin nie może zastąpić trasy innego pluginu.
  • Nakładające się trasy z różnymi poziomami auth są odrzucane. Łańcuchy przekazywania exact/prefix utrzymuj wyłącznie na tym samym poziomie uwierzytelniania.
  • Trasy auth: "plugin" nie otrzymują automatycznie zakresów runtime operatora. Służą do Webhooków/weryfikacji podpisów zarządzanych przez plugin, a nie do uprzywilejowanych wywołań pomocniczych Gateway.
  • Trasy auth: "gateway" działają wewnątrz zakresu runtime żądania Gateway, ale ten zakres jest celowo zachowawczy:
    • uwierzytelnianie bearer ze współdzielonym sekretem (gateway.auth.mode = "token" / "password") utrzymuje zakresy runtime tras pluginów przypięte do operator.write, nawet jeśli wywołujący wysyła x-openclaw-scopes
    • zaufane tryby HTTP niosące tożsamość (na przykład trusted-proxy albo gateway.auth.mode = "none" na prywatnym wejściu) honorują x-openclaw-scopes tylko wtedy, gdy nagłówek jest jawnie obecny
    • jeśli x-openclaw-scopes nie ma w tych żądaniach tras pluginów niosących tożsamość, zakres runtime wraca do operator.write
  • Praktyczna zasada: nie zakładaj, że trasa pluginu uwierzytelniana przez gateway jest niejawną powierzchnią administracyjną. Jeśli trasa wymaga zachowania dostępnego tylko dla administratora, wymagaj trybu uwierzytelniania niosącego tożsamość i udokumentuj jawny kontrakt nagłówka x-openclaw-scopes.

Ścieżki importu Plugin SDK

Podczas tworzenia nowych pluginów używaj wąskich podścieżek SDK zamiast monolitycznej głównej beczki openclaw/plugin-sdk. Podstawowe podścieżki:
PodścieżkaCel
openclaw/plugin-sdk/plugin-entryPrymitywy rejestracji pluginu
openclaw/plugin-sdk/channel-corePomocniki wejścia/budowania kanału
openclaw/plugin-sdk/coreOgólne współdzielone pomocniki i kontrakt parasolowy
openclaw/plugin-sdk/config-schemaSchemat Zod głównego openclaw.json (OpenClawSchema)
Pluginy kanałów wybierają z rodziny wąskich połączeń — channel-setup, setup-runtime, setup-tools, channel-pairing, channel-contract, channel-feedback, channel-inbound, channel-outbound, command-auth, secret-input, webhook-ingress, channel-targets i channel-actions. Zachowanie zatwierdzania powinno konsolidować się na jednym kontrakcie approvalCapability, zamiast mieszać niepowiązane pola pluginu. Zobacz Pluginy kanałów. Pomocniki runtime i konfiguracji znajdują się pod odpowiadającymi im, wyspecjalizowanymi podścieżkami *-runtime (approval-runtime, agent-runtime, lazy-runtime, directory-runtime, text-runtime, runtime-store, system-event-runtime, heartbeat-runtime, channel-activity-runtime itd.). Preferuj config-contracts, plugin-config-runtime, runtime-config-snapshot i config-mutation zamiast szerokiej beczki zgodności config-runtime.
openclaw/plugin-sdk/channel-runtime, openclaw/plugin-sdk/channel-lifecycle, małe fasady pomocników kanału, openclaw/plugin-sdk/outbound-runtime, openclaw/plugin-sdk/outbound-send-deps, openclaw/plugin-sdk/config-runtime i openclaw/plugin-sdk/infra-runtime to przestarzałe warstwy zgodności dla starszych pluginów. Nowy kod powinien zamiast tego importować węższe, ogólne prymitywy.
Wewnętrzne punkty wejścia repozytorium (dla katalogu głównego każdego pakietu pluginu wbudowanego):
  • index.js — wejście pluginu wbudowanego
  • api.js — beczka pomocników/typów
  • runtime-api.js — beczka tylko dla runtime
  • setup-entry.js — wejście pluginu konfiguracji
Pluginy zewnętrzne powinny importować wyłącznie podścieżki openclaw/plugin-sdk/*. Nigdy nie importuj src/* pakietu innego 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ązanego pliku konfiguracji na dysku. Podścieżki specyficzne dla możliwości, takie jak image-generation, media-understanding i speech, istnieją, ponieważ wbudowane 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 być właścicielami specyficznych dla kanału wkładów schematu describeMessageTool(...) dla prymitywów innych niż wiadomości, takich jak reakcje, odczyty i ankiety. Współdzielona 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:
  • presentation dla semantycznych bloków prezentacji (text, context, divider, buttons, select)
  • delivery-pin dla żądań przypiętego dostarczenia
Core decyduje, czy renderować prezentację natywnie, czy degradować ją do tekstu. Nie ujawniaj natywnych dla dostawcy awaryjnych wyjść UI z ogólnego narzędzia wiadomości. Przestarzałe pomocniki SDK dla starszych natywnych schematów pozostają eksportowane dla istniejących pluginów firm trzecich, ale nowe pluginy nie powinny ich używać.

Rozwiązywanie celów kanału

Pluginy kanałów powinny być właścicielami semantyki celów specyficznej dla kanału. Utrzymuj współdzielony host wychodzący jako ogólny i używaj powierzchni adaptera wiadomości dla reguł dostawcy:
  • messaging.inferTargetChatType({ to }) decyduje, czy znormalizowany cel powinien być traktowany jako direct, group albo channel przed 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.reservedLiterals wymienia gołe słowa, które są referencjami kanału/sesji dla tego dostawcy. Rozwiązywanie zachowuje skonfigurowane wpisy katalogu przed odrzuceniem zarezerwowanych literałów, a następnie zamyka się błędem przy braku trafienia w katalogu.
  • messaging.targetResolver.resolveTarget(...) jest fallbackiem pluginu, gdy core potrzebuje końcowego, należącego do dostawcy rozstrzygnięcia po normalizacji albo po braku trafienia w katalogu.
  • messaging.resolveOutboundSessionRoute(...) odpowiada za budowę trasy sesji specyficznej dla dostawcy po rozwiązaniu celu.
Zalecany podział:
  • Używaj inferTargetChatType do decyzji kategorii, które powinny nastąpić przed wyszukiwaniem peerów/grup.
  • Używaj looksLikeId do sprawdzeń typu „traktuj to jako jawny/natywny identyfikator celu”.
  • Używaj resolveTarget jako fallbacku normalizacji specyficznej dla dostawcy, a nie do szerokiego wyszukiwania w katalogu.
  • Natywne identyfikatory dostawcy, takie jak identyfikatory czatów, identyfikatory wątków, JID, handle i identyfikatory pokojów, trzymaj wewnątrz wartości target albo parametrów 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żyj tego, gdy kanał potrzebuje peerów/grup opartych na konfiguracji, takich jak:
  • peery DM sterowane listą dozwolonych
  • skonfigurowane mapy kanałów/grup
  • statyczne fallbacki katalogu ograniczone do konta
Współdzielone pomocniki w directory-runtime obsługują tylko ogólne operacje:
  • filtrowanie zapytań
  • stosowanie limitu
  • pomocniki deduplikacji/normalizacji
  • budowanie ChannelDirectoryEntry[]
Inspekcja konta i normalizacja identyfikatorów specyficzna dla kanału 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 jest właścicielem specyficznych dla dostawcy identyfikatorów modeli, domyślnych wartości bazowego URL albo metadanych modeli chronionych uwierzytelnianiem. catalog.order kontroluje, kiedy katalog pluginu scala się względem wbudowanych, niejawnych dostawców OpenClaw:
  • simple: zwykli dostawcy sterowani kluczem API lub env
  • profile: dostawcy pojawiający się, gdy istnieją profile uwierzytelniania
  • paired: dostawcy syntetyzujący wiele powiązanych wpisów dostawców
  • late: 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. Pluginy mogą też publikować wiersze modeli tylko do odczytu przez api.registerModelCatalogProvider({ provider, kinds, staticCatalog, liveCatalog }). To przyszła ścieżka dla powierzchni list/pomocy/wybieraka i obsługuje wiersze text, image_generation, video_generation oraz music_generation. Pluginy dostawców nadal są właścicielami wywołań endpointów na żywo, wymiany tokenów i mapowania odpowiedzi dostawcy; core jest właścicielem wspólnego kształtu wiersza, etykiet źródeł i formatowania pomocy narzędzi medialnych. Rejestracje dostawców generowania mediów syntetyzują statyczne wiersze katalogu automatycznie z defaultModel, models i capabilities. Zgodność:
  • discovery nadal działa jako starszy alias, ale emituje ostrzeżenie o wycofaniu
  • jeśli zarejestrowane są zarówno catalog, jak i discovery, OpenClaw używa catalog
  • augmentModelCatalog jest przestarzałe; wbudowani dostawcy powinni publikować wiersze uzupełniające przez registerModelCatalogProvider

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 szybko koń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 resolve oraz przepływy naprawy doctor/config nie powinny musieć materializować poświadczeń runtime tylko po to, by opisać konfigurację.
Zalecane zachowanie inspectAccount(...):
  • Zwracaj wyłącznie opisowy stan konta.
  • Zachowuj enabled i configured.
  • Uwzględniaj pola źródła/statusu poświadczeń, gdy są istotne, takie jak:
    • tokenSource, tokenStatus
    • botTokenSource, botTokenStatus
    • appTokenSource, appTokenStatus
    • signingSecretSource, signingSecretStatus
  • Nie musisz zwracać surowych wartości tokenów tylko po to, aby zgłosić dostępność tylko do odczytu. Zwrócenie tokenStatus: "available" (i odpowiadającego pola źródła) wystarczy dla poleceń typu status.
  • 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ą zgłaszać „skonfigurowane, ale niedostępne w tej ścieżce polecenia” zamiast ulegać awarii albo błędnie raportować konto jako nieskonfigurowane.

Pakiety pakietó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 dowiązań symbolicznych. Wpisy wychodzące poza katalog pakietu są odrzucane. Uwaga dotycząca bezpieczeństwa: openclaw plugins install instaluje zależności Plugin za pomocą lokalnego dla projektu npm install --omit=dev --ignore-scripts (bez skryptów cyklu życia, bez zależności deweloperskich w czasie działania), ignorując odziedziczone globalne ustawienia instalacji npm. Utrzymuj drzewa zależności Plugin jako „pure JS/TS” i unikaj pakietów wymagających kompilacji postinstall. Opcjonalnie: openclaw.setupEntry może wskazywać lekki moduł przeznaczony tylko do konfiguracji. Gdy OpenClaw potrzebuje powierzchni konfiguracyjnych dla wyłączonego kanałowego pluginu albo gdy kanałowy plugin jest włączony, ale nadal nieskonfigurowany, ładuje setupEntry zamiast pełnego punktu wejścia pluginu. Dzięki temu uruchamianie i konfiguracja są lżejsze, gdy główny punkt wejścia pluginu podłącza także narzędzia, hooki lub inny kod używany tylko w czasie działania. Opcjonalnie: openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen może włączyć dla kanałowego pluginu tę samą ścieżkę setupEntry podczas fazy uruchamiania Gateway przed rozpoczęciem nasłuchiwania, nawet gdy kanał jest już skonfigurowany. Używaj tego tylko wtedy, gdy setupEntry w pełni pokrywa powierzchnię startową, która musi istnieć zanim Gateway zacznie nasłuchiwać. W praktyce oznacza to, że wpis konfiguracji musi rejestrować każdą funkcjonalność należącą do kanału, od której zależy uruchamianie, taką jak:
  • sama rejestracja kanału
  • wszystkie trasy HTTP, które muszą być dostępne, zanim Gateway zacznie nasłuchiwać
  • wszystkie metody, narzędzia lub usługi Gateway, które muszą istnieć w tym samym oknie czasowym
Jeśli pełny punkt wejścia nadal posiada jakąkolwiek wymaganą funkcjonalność startową, nie włączaj tej flagi. Pozostaw Plugin przy domyślnym zachowaniu i pozwól OpenClaw załadować pełny punkt wejścia podczas uruchamiania. Kanały dołączone do pakietu mogą także publikować pomocniki powierzchni kontraktu przeznaczone tylko do konfiguracji, które core może sprawdzić przed załadowaniem pełnego środowiska uruchomieniowego kanału. Obecna powierzchnia promocji konfiguracji to:
  • singleAccountKeysToMove
  • namedAccountPromotionKeys
  • resolveSingleAccountPromotionTarget(...)
Core używa tej powierzchni, gdy musi promować starszą konfigurację kanału z jednym kontem do channels.<id>.accounts.* bez ładowania pełnego punktu wejścia pluginu. Matrix jest obecnym przykładem dołączonym do pakietu: przenosi tylko klucze auth/bootstrap 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 powierzchni kontraktu dołączonej do pakietu. Czas importu pozostaje krótki; powierzchnia promocji jest ładowana dopiero przy pierwszym użyciu zamiast ponownie wchodzić w start kanału dołączonego do pakietu podczas importu modułu. Gdy te powierzchnie startowe obejmują metody RPC Gateway, utrzymuj je pod prefiksem specyficznym dla pluginu. Przestrzenie nazw administracyjnych core (config.*, exec.approvals.*, wizard.*, update.*) pozostają zarezerwowane i zawsze rozwiązują się 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

Pluginy kanałów mogą ogłaszać metadane konfiguracji/wykrywania przez openclaw.channel oraz wskazówki instalacji przez openclaw.install. Dzięki temu dane katalogu nie trafiają do core. 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/statusu
  • docsLabel: zastępuje tekst linku do dokumentacji
  • preferOver: identyfikatory pluginów/kanałów o niższym priorytecie, które ten wpis katalogu powinien wyprzedzać
  • selectionDocsPrefix, selectionDocsOmitLabel, selectionExtras: kontrolki tekstu powierzchni wyboru
  • markdownCapable: oznacza kanał jako obsługujący markdown dla decyzji o formatowaniu wychodzącym
  • exposure.configured: ukrywa kanał z powierzchni list skonfigurowanych kanałów, gdy ustawione na false
  • exposure.setup: ukrywa kanał z interaktywnych selektorów konfiguracji, gdy ustawione na false
  • exposure.docs: oznacza kanał jako wewnętrzny/prywatny dla powierzchni nawigacji dokumentacji
  • showConfigured / showInSetup: starsze aliasy nadal akceptowane dla zgodności; preferuj exposure
  • quickstartAllowFrom: włącza kanał do standardowego przepływu szybkiego startu allowFrom
  • forceAccountBinding: wymaga jawnego powiązania konta, nawet gdy istnieje tylko jedno konto
  • preferSessionLookupForAnnounceTarget: preferuje wyszukiwanie sesji przy rozwiązywaniu celów ogłoszeń
OpenClaw może także 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 przecinkami/średnikami/PATH). Każdy plik powinien zawierać { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }. Parser akceptuje także "packages" lub "plugins" jako starsze aliasy klucza "entries". Wygenerowane wpisy katalogu kanałów i wpisy katalogu instalacji dostawców udostępniają znormalizowane fakty o źródle instalacji obok surowego bloku openclaw.install. Te znormalizowane fakty określają, czy specyfikacja npm jest dokładną wersją, czy zmiennym selektorem, czy oczekiwane metadane integralności są obecne oraz czy dostępna jest także lokalna ścieżka źródłowa. Gdy tożsamość katalogu/pakietu jest znana, znormalizowane fakty ostrzegają, jeśli sparsowana nazwa pakietu npm odbiega od tej tożsamości. Ostrzegają także, gdy defaultChoice jest nieprawidłowy lub wskazuje źródło, które nie jest dostępne, oraz gdy metadane integralności npm są obecne bez prawidłowego źródła npm. Konsumenci powinni traktować installSource jako addytywne pole opcjonalne, aby ręcznie budowane wpisy i shim katalogu nie musiały go syntetyzować. Dzięki temu onboarding i diagnostyka mogą wyjaśniać stan płaszczyzny źródłowej bez importowania środowiska uruchomieniowego pluginu. Oficjalne zewnętrzne wpisy npm powinny preferować dokładne npmSpec plus expectedIntegrity. Same nazwy pakietów i dist-tagi nadal działają dla zgodności, ale ujawniają ostrzeżenia płaszczyzny źródłowej, aby katalog mógł przechodzić w stronę przypiętych instalacji ze sprawdzoną integralnością bez psucia istniejących pluginów. Gdy onboarding instaluje z lokalnej ścieżki katalogu, zapisuje zarządzany wpis indeksu Plugin z source: "path" oraz względnym wobec workspace 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 do długotrwałej konfiguracji. Dzięki temu lokalne instalacje deweloperskie są widoczne dla diagnostyki płaszczyzny źródłowej bez dodawania drugiej surowej powierzchni ujawniania ścieżek systemu plików. Utrwalony wiersz SQLite installed_plugin_index jest źródłem prawdy dla instalacji i można go odświeżyć bez ładowania modułów środowiska uruchomieniowego pluginu. Jego mapa installRecords jest trwała nawet wtedy, gdy manifest pluginu jest brakujący lub nieprawidłowy; jego ładunek plugins jest odtwarzalnym widokiem manifestu.

Pluginy silnika kontekstu

Pluginy silnika kontekstu odpowiadają za orkiestrację kontekstu sesji dla ingestii, składania i Compaction. Zarejestruj je ze swojego pluginu za pomocą api.registerContextEngine(id, factory), a następnie wybierz aktywny silnik przez plugins.slots.contextEngine. Użyj tego, gdy Twój Plugin musi zastąpić lub rozszerzyć domyślny potok kontekstu, a nie tylko dodać wyszukiwanie pamięci lub hooki.
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 };
    },
  }));
}
Fabryka ctx udostępnia opcjonalne wartości config, agentDir i workspaceDir do inicjalizacji w czasie konstrukcji. assemble() może zwrócić contextProjection, gdy aktywny harness ma trwały wątek backendu. Pomiń go dla starszej projekcji per tura. Zwróć { mode: "thread_bootstrap", epoch }, gdy złożony kontekst powinien zostać wstrzyknięty raz do wątku backendu i używany ponownie do czasu zmiany epoki. Zmień epokę po zmianie semantycznego kontekstu silnika, na przykład po przebiegu Compaction należącym do silnika. Hosty mogą zachować metadane wywołań narzędzi, kształt wejścia oraz zredagowane wyniki narzędzi w projekcji thread-bootstrap, aby świeże wątki backendu zachowały ciągłość narzędzi bez kopiowania surowych ładunków zawierających sekrety. Jeśli Twój silnik nie posiada algorytmu Compaction, pozostaw compact() zaimplementowane i deleguj go jawnie:
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);
    },
  }));
}

Dodawanie nowej funkcjonalności

Gdy Plugin potrzebuje zachowania, które nie mieści się w obecnym API, nie obchodź systemu pluginów prywatnym sięganiem do środka. Dodaj brakującą funkcjonalność. Zalecana sekwencja:
  1. zdefiniuj kontrakt core Zdecyduj, jakie współdzielone zachowanie powinien posiadać core: politykę, fallback, scalanie konfiguracji, cykl życia, semantykę skierowaną do kanałów i kształt pomocników środowiska uruchomieniowego.
  2. dodaj typowane powierzchnie rejestracji/środowiska uruchomieniowego pluginu Rozszerz OpenClawPluginApi i/lub api.runtime o najmniejszą użyteczną typowaną powierzchnię funkcjonalności.
  3. podłącz core oraz konsumentów kanału/funkcji Kanały i pluginy funkcji powinny konsumować nową funkcjonalność przez core, a nie przez bezpośredni import implementacji dostawcy.
  4. zarejestruj implementacje dostawców Pluginy dostawców rejestrują następnie swoje backendy względem funkcjonalności.
  5. dodaj pokrycie kontraktu Dodaj testy, aby własność i kształt rejestracji pozostały jawne w czasie.
W ten sposób OpenClaw pozostaje opiniotwórczy, nie stając się na stałe powiązany ze światopoglądem jednego dostawcy. Zobacz Capability Cookbook, aby znaleźć konkretną listę kontrolną plików i opracowany przykład.

Lista kontrolna funkcjonalności

Gdy dodajesz nową funkcjonalność, implementacja zwykle powinna obejmować razem te powierzchnie:
  • typy kontraktu core w src/<capability>/types.ts
  • pomocnik runnera/środowiska uruchomieniowego core w src/<capability>/runtime.ts
  • powierzchnię rejestracji API pluginu w src/plugins/types.ts
  • podłączenie rejestru pluginów w src/plugins/registry.ts
  • ekspozycję środowiska uruchomieniowego pluginu w src/plugins/runtime/*, gdy pluginy funkcji/kanałów muszą ją konsumować
  • pomocniki przechwytywania/testowe w src/test-utils/plugin-registration.ts
  • asercje własności/kontraktu w src/plugins/contracts/registry.ts
  • dokumentację operatora/pluginu w docs/
Jeśli brakuje jednej z tych powierzchni, zwykle oznacza to, że funkcjonalność nie jest jeszcze w pełni zintegrowana.

Szablon funkcjonalnoś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:
  • core jest właścicielem kontraktu capability i orkiestracji
  • pluginy dostawców są właścicielami implementacji dostawców
  • pluginy funkcji/kanałów używają helperów runtime
  • testy kontraktu utrzymują jawne ownership

Powiązane