From a2d91a1a9a6d3880c4bc26636df90d34882b80f9 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Thu, 23 Apr 2026 01:12:01 -0700 Subject: [PATCH] docs: trim gateway configuration and plugin architecture reference dumps --- docs/gateway/configuration.md | 138 +++++----------- docs/plugins/architecture.md | 287 ++++++++-------------------------- 2 files changed, 103 insertions(+), 322 deletions(-) diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index 5a0267b5d59..82d32363980 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -72,26 +72,12 @@ See the [full reference](/gateway/configuration-reference) for every available f OpenClaw only accepts configurations that fully match the schema. Unknown keys, malformed types, or invalid values cause the Gateway to **refuse to start**. The only root-level exception is `$schema` (string), so editors can attach JSON Schema metadata. -Schema tooling notes: - -- `openclaw config schema` prints the same JSON Schema family used by Control UI - and config validation. -- Treat that schema output as the canonical machine-readable contract for - `openclaw.json`; this overview and the configuration reference summarize it. -- Field `title` and `description` values are carried into the schema output for - editor and form tooling. -- Nested object, wildcard (`*`), and array-item (`[]`) entries inherit the same - docs metadata where matching field documentation exists. -- `anyOf` / `oneOf` / `allOf` composition branches inherit the same docs - metadata too, so union/intersection variants keep the same field help. -- `config.schema.lookup` returns one normalized config path with a shallow - schema node (`title`, `description`, `type`, `enum`, `const`, common bounds, - and similar validation fields), matched UI hint metadata, and immediate child - summaries for drill-down tooling. -- Runtime plugin/channel schemas are merged in when the gateway can load the - current manifest registry. -- `pnpm config:docs:check` detects drift between docs-facing config baseline - artifacts and the current schema surface. +`openclaw config schema` prints the canonical JSON Schema used by Control UI +and validation. `config.schema.lookup` fetches a single path-scoped node plus +child summaries for drill-down tooling. Field `title`/`description` docs metadata +carries through nested objects, wildcard (`*`), array-item (`[]`), and `anyOf`/ +`oneOf`/`allOf` branches. Runtime plugin and channel schemas merge in when the +manifest registry is loaded. When validation fails: @@ -100,23 +86,13 @@ When validation fails: - Run `openclaw doctor` to see exact issues - Run `openclaw doctor --fix` (or `--yes`) to apply repairs -The Gateway also keeps a trusted last-known-good copy after a successful startup. If -`openclaw.json` is later changed outside OpenClaw and no longer validates, startup -and hot reload preserve the broken file as a timestamped `.clobbered.*` snapshot, -restore the last-known-good copy, and log a loud warning with the recovery reason. -Startup read recovery also treats sharp size drops, missing config metadata, and a -missing `gateway.mode` as critical clobber signatures when the last-known-good -copy had those fields. -If a status/log line is accidentally prepended before an otherwise valid JSON -config, gateway startup and `openclaw doctor --fix` can strip the prefix, -preserve the polluted file as `.clobbered.*`, and continue with the recovered -JSON. -The next main-agent turn also receives a system-event warning telling it that the -config was restored and must not be blindly rewritten. Last-known-good promotion -is updated after validated startup and after accepted hot reloads, including -OpenClaw-owned config writes whose persisted file hash still matches the accepted -write. Promotion is skipped when the candidate contains redacted secret -placeholders such as `***` or shortened token values. +The Gateway keeps a trusted last-known-good copy after each successful startup. +If `openclaw.json` later fails validation (or drops `gateway.mode`, shrinks +sharply, or has a stray log line prepended), OpenClaw preserves the broken file +as `.clobbered.*`, restores the last-known-good copy, and logs the recovery +reason. The next agent turn also receives a system-event warning so the main +agent does not blindly rewrite the restored config. Promotion to last-known-good +is skipped when a candidate contains redacted secret placeholders such as `***`. ## Common tasks @@ -583,75 +559,35 @@ source layout is ambiguous. ## Config RPC (programmatic updates) +For tooling that writes config over the gateway API, prefer this flow: + +- `config.schema.lookup` to inspect one subtree (shallow schema node + child + summaries) +- `config.get` to fetch the current snapshot plus `hash` +- `config.patch` for partial updates (JSON merge patch: objects merge, `null` + deletes, arrays replace) +- `config.apply` only when you intend to replace the entire config +- `update.run` for explicit self-update plus restart + -Control-plane write RPCs (`config.apply`, `config.patch`, `update.run`) are rate-limited to **3 requests per 60 seconds** per `deviceId+clientIp`. When limited, the RPC returns `UNAVAILABLE` with `retryAfterMs`. +Control-plane writes (`config.apply`, `config.patch`, `update.run`) are +rate-limited to 3 requests per 60 seconds per `deviceId+clientIp`. Restart +requests coalesce and then enforce a 30-second cooldown between restart cycles. -Safe/default flow: +Example partial patch: -- `config.schema.lookup`: inspect one path-scoped config subtree with a shallow - schema node, matched hint metadata, and immediate child summaries -- `config.get`: fetch the current snapshot + hash -- `config.patch`: preferred partial update path -- `config.apply`: full-config replacement only -- `update.run`: explicit self-update + restart +```bash +openclaw gateway call config.get --params '{}' # capture payload.hash +openclaw gateway call config.patch --params '{ + "raw": "{ channels: { telegram: { groups: { \"*\": { requireMention: false } } } } }", + "baseHash": "" +}' +``` -When you are not replacing the entire config, prefer `config.schema.lookup` -then `config.patch`. - - - - Validates + writes the full config and restarts the Gateway in one step. - - - `config.apply` replaces the **entire config**. Use `config.patch` for partial updates, or `openclaw config set` for single keys. - - - Params: - - - `raw` (string) — JSON5 payload for the entire config - - `baseHash` (optional) — config hash from `config.get` (required when config exists) - - `sessionKey` (optional) — session key for the post-restart wake-up ping - - `note` (optional) — note for the restart sentinel - - `restartDelayMs` (optional) — delay before restart (default 2000) - - Restart requests are coalesced while one is already pending/in-flight, and a 30-second cooldown applies between restart cycles. - - ```bash - openclaw gateway call config.get --params '{}' # capture payload.hash - openclaw gateway call config.apply --params '{ - "raw": "{ agents: { defaults: { workspace: \"~/.openclaw/workspace\" } } }", - "baseHash": "", - "sessionKey": "agent:main:whatsapp:direct:+15555550123" - }' - ``` - - - - - Merges a partial update into the existing config (JSON merge patch semantics): - - - Objects merge recursively - - `null` deletes a key - - Arrays replace - - Params: - - - `raw` (string) — JSON5 with just the keys to change - - `baseHash` (required) — config hash from `config.get` - - `sessionKey`, `note`, `restartDelayMs` — same as `config.apply` - - Restart behavior matches `config.apply`: coalesced pending restarts plus a 30-second cooldown between restart cycles. - - ```bash - openclaw gateway call config.patch --params '{ - "raw": "{ channels: { telegram: { groups: { \"*\": { requireMention: false } } } } }", - "baseHash": "" - }' - ``` - - - +Both `config.apply` and `config.patch` accept `raw`, `baseHash`, `sessionKey`, +`note`, and `restartDelayMs`. `baseHash` is required for `config.patch` and +recommended for `config.apply` when a config already exists. ## Environment variables diff --git a/docs/plugins/architecture.md b/docs/plugins/architecture.md index b63ab25804c..a5995992f7e 100644 --- a/docs/plugins/architecture.md +++ b/docs/plugins/architecture.md @@ -809,115 +809,33 @@ api.registerProvider({ ### Built-in examples -- Anthropic uses `resolveDynamicModel`, `capabilities`, `buildAuthDoctorHint`, - `resolveUsageAuth`, `fetchUsageSnapshot`, `isCacheTtlEligible`, - `resolveThinkingProfile`, `applyConfigDefaults`, `isModernModelRef`, - and `wrapStreamFn` because it owns Claude 4.6 forward-compat, - provider-family hints, auth repair guidance, usage endpoint integration, - prompt-cache eligibility, auth-aware config defaults, Claude - default/adaptive thinking policy, and Anthropic-specific stream shaping for - beta headers, `/fast` / `serviceTier`, and `context1m`. -- Anthropic's Claude-specific stream helpers stay in the bundled plugin's own - public `api.ts` / `contract-api.ts` seam for now. That package surface - exports `wrapAnthropicProviderStream`, `resolveAnthropicBetas`, - `resolveAnthropicFastMode`, `resolveAnthropicServiceTier`, and the lower-level - Anthropic wrapper builders instead of widening the generic SDK around one - provider's beta-header rules. -- OpenAI uses `resolveDynamicModel`, `normalizeResolvedModel`, and - `capabilities` plus `buildMissingAuthMessage`, `suppressBuiltInModel`, - `augmentModelCatalog`, `resolveThinkingProfile`, and `isModernModelRef` - because it owns GPT-5.4 forward-compat, the direct OpenAI - `openai-completions` -> `openai-responses` normalization, Codex-aware auth - hints, Spark suppression, synthetic OpenAI list rows, and GPT-5 thinking / - live-model policy; the `openai-responses-defaults` stream family owns the - shared native OpenAI Responses wrappers for attribution headers, - `/fast`/`serviceTier`, text verbosity, native Codex web search, - reasoning-compat payload shaping, and Responses context management. -- OpenRouter uses `catalog` plus `resolveDynamicModel` and - `prepareDynamicModel` because the provider is pass-through and may expose new - model ids before OpenClaw's static catalog updates; it also uses - `capabilities`, `wrapStreamFn`, and `isCacheTtlEligible` to keep - provider-specific request headers, routing metadata, reasoning patches, and - prompt-cache policy out of core. Its replay policy comes from the - `passthrough-gemini` family, while the `openrouter-thinking` stream family - owns proxy reasoning injection and the unsupported-model / `auto` skips. -- GitHub Copilot uses `catalog`, `auth`, `resolveDynamicModel`, and - `capabilities` plus `prepareRuntimeAuth` and `fetchUsageSnapshot` because it - needs provider-owned device login, model fallback behavior, Claude transcript - quirks, a GitHub token -> Copilot token exchange, and a provider-owned usage - endpoint. -- OpenAI Codex uses `catalog`, `resolveDynamicModel`, - `normalizeResolvedModel`, `refreshOAuth`, and `augmentModelCatalog` plus - `prepareExtraParams`, `resolveUsageAuth`, and `fetchUsageSnapshot` because it - still runs on core OpenAI transports but owns its transport/base URL - normalization, OAuth refresh fallback policy, default transport choice, - synthetic Codex catalog rows, and ChatGPT usage endpoint integration; it - shares the same `openai-responses-defaults` stream family as direct OpenAI. -- Google AI Studio and Gemini CLI OAuth use `resolveDynamicModel`, - `buildReplayPolicy`, `sanitizeReplayHistory`, - `resolveReasoningOutputMode`, `wrapStreamFn`, and `isModernModelRef` because the - `google-gemini` replay family owns Gemini 3.1 forward-compat fallback, - native Gemini replay validation, bootstrap replay sanitation, tagged - reasoning-output mode, and modern-model matching, while the - `google-thinking` stream family owns Gemini thinking payload normalization; - Gemini CLI OAuth also uses `formatApiKey`, `resolveUsageAuth`, and - `fetchUsageSnapshot` for token formatting, token parsing, and quota endpoint - wiring. -- Anthropic Vertex uses `buildReplayPolicy` through the - `anthropic-by-model` replay family so Claude-specific replay cleanup stays - scoped to Claude ids instead of every `anthropic-messages` transport. -- Amazon Bedrock uses `buildReplayPolicy`, `matchesContextOverflowError`, - `classifyFailoverReason`, and `resolveThinkingProfile` because it owns - Bedrock-specific throttle/not-ready/context-overflow error classification - for Anthropic-on-Bedrock traffic; its replay policy still shares the same - Claude-only `anthropic-by-model` guard. -- OpenRouter, Kilocode, Opencode, and Opencode Go use `buildReplayPolicy` - through the `passthrough-gemini` replay family because they proxy Gemini - models through OpenAI-compatible transports and need Gemini - thought-signature sanitation without native Gemini replay validation or - bootstrap rewrites. -- MiniMax uses `buildReplayPolicy` through the - `hybrid-anthropic-openai` replay family because one provider owns both - Anthropic-message and OpenAI-compatible semantics; it keeps Claude-only - thinking-block dropping on the Anthropic side while overriding reasoning - output mode back to native, and the `minimax-fast-mode` stream family owns - fast-mode model rewrites on the shared stream path. -- Moonshot uses `catalog`, `resolveThinkingProfile`, and `wrapStreamFn` because it still uses the shared - OpenAI transport but needs provider-owned thinking payload normalization; the - `moonshot-thinking` stream family maps config plus `/think` state onto its - native binary thinking payload. -- Kilocode uses `catalog`, `capabilities`, `wrapStreamFn`, and - `isCacheTtlEligible` because it needs provider-owned request headers, - reasoning payload normalization, Gemini transcript hints, and Anthropic - cache-TTL gating; the `kilocode-thinking` stream family keeps Kilo thinking - injection on the shared proxy stream path while skipping `kilo/auto` and - other proxy model ids that do not support explicit reasoning payloads. -- Z.AI uses `resolveDynamicModel`, `prepareExtraParams`, `wrapStreamFn`, - `isCacheTtlEligible`, `resolveThinkingProfile`, `isModernModelRef`, - `resolveUsageAuth`, and `fetchUsageSnapshot` because it owns GLM-5 fallback, - `tool_stream` defaults, binary thinking UX, modern-model matching, and both - usage auth + quota fetching; the `tool-stream-default-on` stream family keeps - the default-on `tool_stream` wrapper out of per-provider handwritten glue. -- xAI uses `normalizeResolvedModel`, `normalizeTransport`, - `contributeResolvedModelCompat`, `prepareExtraParams`, `wrapStreamFn`, - `resolveSyntheticAuth`, `resolveDynamicModel`, and `isModernModelRef` - because it owns native xAI Responses transport normalization, Grok fast-mode - alias rewrites, default `tool_stream`, strict-tool / reasoning-payload - cleanup, fallback auth reuse for plugin-owned tools, forward-compat Grok - model resolution, and provider-owned compat patches such as xAI tool-schema - profile, unsupported schema keywords, native `web_search`, and HTML-entity - tool-call argument decoding. -- Mistral, OpenCode Zen, and OpenCode Go use `capabilities` only to keep - transcript/tooling quirks out of core. -- Catalog-only bundled providers such as `byteplus`, `cloudflare-ai-gateway`, - `huggingface`, `kimi-coding`, `nvidia`, `qianfan`, - `synthetic`, `together`, `venice`, `vercel-ai-gateway`, and `volcengine` use - `catalog` only. -- Qwen uses `catalog` for its text provider plus shared media-understanding and - video-generation registrations for its multimodal surfaces. -- MiniMax and Xiaomi use `catalog` plus usage hooks because their `/usage` - behavior is plugin-owned even though inference still runs through the shared - transports. +Bundled provider plugins use the hooks above in combinations tailored to each +vendor's catalog, auth, thinking, replay, and usage-tracking needs. The exact +hook set per provider lives with the plugin source under `extensions/`; treat +that as the authoritative list rather than mirroring it here. + +Illustrative patterns: + +- **Pass-through catalog providers** (OpenRouter, Kilocode, Z.AI, xAI) register + `catalog` plus `resolveDynamicModel`/`prepareDynamicModel` so they can surface + upstream model ids ahead of OpenClaw's static catalog. +- **OAuth + usage endpoint providers** (GitHub Copilot, Gemini CLI, ChatGPT + Codex, MiniMax, Xiaomi, z.ai) pair `prepareRuntimeAuth` or `formatApiKey` + with `resolveUsageAuth` + `fetchUsageSnapshot` to own token exchange and + `/usage` integration. +- **Replay / transcript cleanup** is shared through named families: + `google-gemini`, `passthrough-gemini`, `anthropic-by-model`, + `hybrid-anthropic-openai`. Providers opt in through `buildReplayPolicy` + instead of each implementing transcript cleanup. +- **Catalog-only** bundled providers (`byteplus`, `cloudflare-ai-gateway`, + `huggingface`, `kimi-coding`, `nvidia`, `qianfan`, `synthetic`, `together`, + `venice`, `vercel-ai-gateway`, `volcengine`) register just `catalog` and ride + the shared inference loop. +- **Anthropic-specific stream helpers** (beta headers, `/fast`/`serviceTier`, + `context1m`) live inside the Anthropic bundled plugin's public `api.ts` / + `contract-api.ts` seam (`wrapAnthropicProviderStream`, `resolveAnthropicBetas`, + `resolveAnthropicFastMode`, `resolveAnthropicServiceTier`) rather than in the + generic SDK. ## Runtime helpers @@ -1137,121 +1055,48 @@ Notes: ## Plugin SDK import paths -Use SDK subpaths instead of the monolithic `openclaw/plugin-sdk` import when -authoring plugins: +Use narrow SDK subpaths instead of the monolithic `openclaw/plugin-sdk` root +barrel when authoring new plugins. Core subpaths: -- `openclaw/plugin-sdk/plugin-entry` for plugin registration primitives. -- `openclaw/plugin-sdk/core` for the generic shared plugin-facing contract. -- `openclaw/plugin-sdk/config-schema` for the root `openclaw.json` Zod schema - export (`OpenClawSchema`). -- Stable channel primitives such as `openclaw/plugin-sdk/channel-setup`, - `openclaw/plugin-sdk/setup-runtime`, - `openclaw/plugin-sdk/setup-adapter-runtime`, - `openclaw/plugin-sdk/setup-tools`, - `openclaw/plugin-sdk/channel-pairing`, - `openclaw/plugin-sdk/channel-contract`, - `openclaw/plugin-sdk/channel-feedback`, - `openclaw/plugin-sdk/channel-inbound`, - `openclaw/plugin-sdk/channel-lifecycle`, - `openclaw/plugin-sdk/channel-reply-pipeline`, - `openclaw/plugin-sdk/command-auth`, - `openclaw/plugin-sdk/secret-input`, and - `openclaw/plugin-sdk/webhook-ingress` for shared setup/auth/reply/webhook - wiring. `channel-inbound` is the shared home for debounce, mention matching, - inbound mention-policy helpers, envelope formatting, and inbound envelope - context helpers. - `channel-setup` is the narrow optional-install setup seam. - `setup-runtime` is the runtime-safe setup surface used by `setupEntry` / - deferred startup, including the import-safe setup patch adapters. - `setup-adapter-runtime` is the env-aware account-setup adapter seam. - `setup-tools` is the small CLI/archive/docs helper seam (`formatCliCommand`, - `detectBinary`, `extractArchive`, `resolveBrewExecutable`, `formatDocsLink`, - `CONFIG_DIR`). -- Domain subpaths such as `openclaw/plugin-sdk/channel-config-helpers`, - `openclaw/plugin-sdk/allow-from`, - `openclaw/plugin-sdk/channel-config-schema`, - `openclaw/plugin-sdk/telegram-command-config`, - `openclaw/plugin-sdk/channel-policy`, - `openclaw/plugin-sdk/approval-gateway-runtime`, - `openclaw/plugin-sdk/approval-handler-adapter-runtime`, - `openclaw/plugin-sdk/approval-handler-runtime`, - `openclaw/plugin-sdk/approval-runtime`, - `openclaw/plugin-sdk/config-runtime`, - `openclaw/plugin-sdk/infra-runtime`, - `openclaw/plugin-sdk/agent-runtime`, - `openclaw/plugin-sdk/lazy-runtime`, - `openclaw/plugin-sdk/reply-history`, - `openclaw/plugin-sdk/routing`, - `openclaw/plugin-sdk/status-helpers`, - `openclaw/plugin-sdk/text-runtime`, - `openclaw/plugin-sdk/runtime-store`, and - `openclaw/plugin-sdk/directory-runtime` for shared runtime/config helpers. - `telegram-command-config` is the narrow public seam for Telegram custom - command normalization/validation and stays available even if the bundled - Telegram contract surface is temporarily unavailable. - `text-runtime` is the shared text/markdown/logging seam, including - assistant-visible-text stripping, markdown render/chunking helpers, redaction - helpers, directive-tag helpers, and safe-text utilities. -- Approval-specific channel seams should prefer one `approvalCapability` - contract on the plugin. Core then reads approval auth, delivery, render, - native-routing, and lazy native-handler behavior through that one capability - instead of mixing approval behavior into unrelated plugin fields. -- `openclaw/plugin-sdk/channel-runtime` is deprecated and remains only as a - compatibility shim for older plugins. New code should import the narrower - generic primitives instead, and repo code should not add new imports of the - shim. -- Bundled extension internals remain private. External plugins should use only - `openclaw/plugin-sdk/*` subpaths. OpenClaw core/test code may use the repo - public entry points under a plugin package root such as `index.js`, `api.js`, - `runtime-api.js`, `setup-entry.js`, and narrowly scoped files such as - `login-qr-api.js`. Never import a plugin package's `src/*` from core or from - another extension. -- Repo entry point split: - `/api.js` is the helper/types barrel, - `/runtime-api.js` is the runtime-only barrel, - `/index.js` is the bundled plugin entry, - and `/setup-entry.js` is the setup plugin entry. -- Current bundled provider examples: - - Anthropic uses `api.js` / `contract-api.js` for Claude stream helpers such - as `wrapAnthropicProviderStream`, beta-header helpers, and `service_tier` - parsing. - - OpenAI uses `api.js` for provider builders, default-model helpers, and - realtime provider builders. - - OpenRouter uses `api.js` for its provider builder plus onboarding/config - helpers, while `register.runtime.js` can still re-export generic - `plugin-sdk/provider-stream` helpers for repo-local use. -- Facade-loaded public entry points prefer the active runtime config snapshot - when one exists, then fall back to the resolved config file on disk when - OpenClaw is not yet serving a runtime snapshot. -- Generic shared primitives remain the preferred public SDK contract. A small - reserved compatibility set of bundled channel-branded helper seams still - exists. Treat those as bundled-maintenance/compatibility seams, not new - third-party import targets; new cross-channel contracts should still land on - generic `plugin-sdk/*` subpaths or the plugin-local `api.js` / - `runtime-api.js` barrels. +| Subpath | Purpose | +| ----------------------------------- | -------------------------------------------------- | +| `openclaw/plugin-sdk/plugin-entry` | Plugin registration primitives | +| `openclaw/plugin-sdk/core` | Generic shared plugin-facing contract | +| `openclaw/plugin-sdk/config-schema` | Root `openclaw.json` Zod schema (`OpenClawSchema`) | -Compatibility note: +Channel plugins pick from a family of narrow seams — `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`, and `channel-actions`. Approval behavior should consolidate +on one `approvalCapability` contract rather than mixing across unrelated +plugin fields. See [Channel plugins](/plugins/sdk-channel-plugins). -- Avoid the root `openclaw/plugin-sdk` barrel for new code. -- Prefer the narrow stable primitives first. The newer setup/pairing/reply/ - feedback/contract/inbound/threading/command/secret-input/webhook/infra/ - allowlist/status/message-tool subpaths are the intended contract for new - bundled and external plugin work. - Target parsing/matching belongs on `openclaw/plugin-sdk/channel-targets`. - Message action gates and reaction message-id helpers belong on - `openclaw/plugin-sdk/channel-actions`. -- Bundled extension-specific helper barrels are not stable by default. If a - helper is only needed by a bundled extension, keep it behind the extension's - local `api.js` or `runtime-api.js` seam instead of promoting it into - `openclaw/plugin-sdk/`. -- New shared helper seams should be generic, not channel-branded. Shared target - parsing belongs on `openclaw/plugin-sdk/channel-targets`; channel-specific - internals stay behind the owning plugin's local `api.js` or `runtime-api.js` - seam. -- Capability-specific subpaths such as `image-generation`, - `media-understanding`, and `speech` exist because bundled/native plugins use - them today. Their presence does not by itself mean every exported helper is a - long-term frozen external contract. +Runtime and config helpers live under matching `*-runtime` subpaths +(`approval-runtime`, `config-runtime`, `infra-runtime`, `agent-runtime`, +`lazy-runtime`, `directory-runtime`, `text-runtime`, `runtime-store`, etc.). + + +`openclaw/plugin-sdk/channel-runtime` is deprecated — a compatibility shim for +older plugins. New code should import narrower generic primitives instead. + + +Repo-internal entry points (per bundled plugin package root): + +- `index.js` — bundled plugin entry +- `api.js` — helper/types barrel +- `runtime-api.js` — runtime-only barrel +- `setup-entry.js` — setup plugin entry + +External plugins should only import `openclaw/plugin-sdk/*` subpaths. Never +import another plugin package's `src/*` from core or from another plugin. +Facade-loaded entry points prefer the active runtime config snapshot when one +exists, then fall back to the resolved config file on disk. + +Capability-specific subpaths such as `image-generation`, `media-understanding`, +and `speech` exist because bundled plugins use them today. They are not +automatically long-term frozen external contracts — check the relevant SDK +reference page when relying on them. ## Message tool schemas