openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,505 lines • 61.5 kB
JavaScript
import { s as normalizeSortedUniqueStringEntries } from "./string-normalization-WNUDCpXX.js";
import { f as safeRealpathSync, i as isPathInside } from "./path-BlG8lhgR.js";
import { t as discoverOpenClawPlugins } from "./discovery-BDSfxwT6.js";
import { o as resolveCompatibilityHostVersion } from "./version-Crcn9X9T.js";
import { r as hasKind } from "./slots-kpL659LX.js";
import { t as loadPluginManifestRegistry } from "./manifest-registry-BywZIeAq.js";
import "./path-safety-4zNHq1Ot.js";
import { n as loadInstalledPluginIndexInstallRecordsSync } from "./installed-plugin-index-record-reader-CPHayOZI.js";
import { c as resolveEffectiveEnableState, l as resolveEffectivePluginActivationState, s as normalizePluginsConfig } from "./config-state-CikNNz7T.js";
import { t as isPluginEnabledByDefaultForPlatform } from "./default-enablement-CEIbpabL.js";
import { t as describePluginInstallSource } from "./install-source-info-D9acrZr3.js";
import fs from "node:fs";
import path from "node:path";
import crypto from "node:crypto";
//#region src/plugins/compat/registry.ts
const CHANNEL_RUNTIME_SDK_SURFACE = ["openclaw/plugin-sdk/channel", "runtime"].join("-");
const LEGACY_CONFIG_MIGRATE_TEST_PATH = ["src/commands/doctor/shared/legacy-config", "migrate.test.ts"].join("-");
const PLUGIN_COMPAT_RECORDS = [
{
code: "legacy-before-agent-start",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-24",
deprecated: "2026-04-24",
warningStarts: "2026-04-24",
removeAfter: "2026-07-24",
replacement: "`before_model_resolve` and `before_prompt_build` hooks",
docsPath: "/plugins/sdk-migration",
surfaces: [
"plugin hooks",
"plugins inspect",
"status diagnostics"
],
diagnostics: ["plugin compatibility notice"],
tests: ["src/plugins/status.test.ts", "src/plugins/contracts/shape.contract.test.ts"],
releaseNote: "Legacy `before_agent_start` hook compatibility remains wired while plugins migrate to modern hook stages."
},
{
code: "legacy-deactivate-hook-alias",
status: "deprecated",
owner: "sdk",
introduced: "2026-05-16",
deprecated: "2026-05-16",
warningStarts: "2026-05-16",
removeAfter: "2026-08-16",
replacement: "`gateway_stop` hook",
docsPath: "/plugins/hooks#upcoming-deprecations",
surfaces: ["api.on(\"deactivate\", ...)", "plugin typed hook registration"],
diagnostics: ["plugin runtime compatibility warning"],
tests: ["src/plugins/loader.test.ts"],
releaseNote: "`api.on(\"deactivate\", ...)` remains wired as a deprecated compatibility alias while plugins migrate to `gateway_stop`."
},
{
code: "legacy-subagent-spawning-hook",
status: "deprecated",
owner: "sdk",
introduced: "2026-05-30",
deprecated: "2026-05-30",
warningStarts: "2026-05-30",
removeAfter: "2026-08-30",
replacement: "`subagent_spawned` for post-launch observation; core session-binding adapters for thread routing",
docsPath: "/plugins/hooks#upcoming-deprecations",
surfaces: [
"api.on(\"subagent_spawning\", ...)",
"PluginHookSubagentSpawningEvent",
"PluginHookSubagentSpawningResult",
"SubagentLifecycleHookRunner.runSubagentSpawning"
],
diagnostics: ["plugin runtime compatibility warning"],
tests: ["src/plugins/loader.test.ts", "src/plugins/compat/registry.test.ts"],
releaseNote: "`api.on(\"subagent_spawning\", ...)` remains wired only for older plugins; core now owns thread-bound subagent routing."
},
{
code: "hook-only-plugin-shape",
status: "active",
owner: "sdk",
introduced: "2026-04-24",
replacement: "explicit capability registration",
docsPath: "/plugins/sdk-migration",
surfaces: [
"plugin shape inspection",
"plugins inspect",
"status diagnostics"
],
diagnostics: ["plugin compatibility notice"],
tests: ["src/plugins/status.test.ts", "src/plugins/contracts/shape.contract.test.ts"]
},
{
code: "deprecated-memory-embedding-provider-api",
status: "deprecated",
owner: "sdk",
introduced: "2026-05-21",
deprecated: "2026-05-21",
warningStarts: "2026-05-21",
removeAfter: "2026-08-21",
replacement: "`api.registerEmbeddingProvider(...)` and `contracts.embeddingProviders`",
docsPath: "/plugins/sdk-migration#memory-embedding-provider-api",
surfaces: [
"api.registerMemoryEmbeddingProvider(...)",
"contracts.memoryEmbeddingProviders",
"openclaw/plugin-sdk/memory-core-host-engine-embeddings registerMemoryEmbeddingProvider",
"plugins inspect compatibility notices"
],
diagnostics: ["plugin compatibility notice", "plugin SDK package guardrail"],
tests: [
"src/plugins/status.test.ts",
"src/plugins/compat/registry.test.ts",
"src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts"
],
releaseNote: "Memory-specific embedding provider registration remains wired as a deprecated compatibility path while providers migrate to the generic embedding provider contract."
},
{
code: "legacy-root-sdk-import",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-24",
deprecated: "2026-04-24",
warningStarts: "2026-04-24",
removeAfter: "2026-07-24",
replacement: "focused `openclaw/plugin-sdk/<subpath>` imports",
docsPath: "/plugins/sdk-migration",
surfaces: ["openclaw/plugin-sdk", "openclaw/plugin-sdk/compat"],
diagnostics: ["OPENCLAW_PLUGIN_SDK_COMPAT_DEPRECATED"],
tests: [
"src/plugins/contracts/plugin-sdk-index.test.ts",
"src/plugins/contracts/plugin-sdk-root-alias.test.ts",
"src/plugins/contracts/plugin-sdk-subpaths.test.ts"
]
},
{
code: "hook.before_tool_call.terminal-block-approval",
status: "active",
owner: "agent-runtime",
introduced: "2026-04-29",
docsPath: "/plugins/hooks",
surfaces: ["before_tool_call block result", "before_tool_call approval result"],
diagnostics: ["hook runner contract probe"],
tests: ["src/plugins/hooks.security.test.ts", "src/agents/agent-tools.before-tool-call.e2e.test.ts"]
},
{
code: "hook.llm-observer.privacy-payload",
status: "active",
owner: "agent-runtime",
introduced: "2026-04-29",
docsPath: "/plugins/hooks",
surfaces: [
"llm_input",
"llm_output",
"agent_end",
"allowConversationAccess"
],
diagnostics: ["conversation access hook contract probe"],
tests: ["src/agents/cli-runner.reliability.test.ts", "src/config/schema.help.quality.test.ts"]
},
{
code: "api.capture.runtime-registrars",
status: "active",
owner: "plugin-execution",
introduced: "2026-04-29",
docsPath: "/plugins/architecture-internals",
surfaces: [
"createCapturedPluginRegistration",
"capturePluginRegistration",
"OpenClawPluginApi"
],
diagnostics: ["runtime registration capture contract probe"],
tests: ["src/plugins/captured-registration.test.ts"]
},
{
code: "channel.runtime.envelope-config-metadata",
status: "active",
owner: "channel",
introduced: "2026-04-29",
docsPath: "/plugins/sdk-channel-plugins",
surfaces: [
"api.registerChannel",
"channel setup metadata",
"channel message envelope"
],
diagnostics: ["channel runtime contract probe"],
tests: ["src/plugin-sdk/channel-entry-contract.test.ts", "src/plugins/captured-registration.test.ts"]
},
{
code: "bundled-channel-sdk-compat-facades",
status: "active",
owner: "sdk",
introduced: "2026-04-28",
replacement: "generic channel SDK subpaths or plugin-local `api.ts` / `runtime-api.ts` barrels for new plugins",
docsPath: "/plugins/sdk-overview",
surfaces: ["openclaw/plugin-sdk/discord component message helpers", "openclaw/plugin-sdk/telegram-account resolveTelegramAccount"],
diagnostics: ["plugin SDK compatibility registry"],
tests: [
"src/plugin-sdk/discord.test.ts",
"src/plugin-sdk/telegram-account.test.ts",
"src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts"
]
},
{
code: "bundled-channel-config-schema-legacy",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-28",
deprecated: "2026-04-28",
warningStarts: "2026-04-28",
removeAfter: "2026-07-28",
replacement: "`openclaw/plugin-sdk/bundled-channel-config-schema` for maintained bundled plugins; plugin-local schemas for third-party plugins",
docsPath: "/plugins/sdk-migration",
surfaces: ["openclaw/plugin-sdk/channel-config-schema-legacy"],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugins/contracts/config-footprint-guardrails.test.ts", "test/extension-test-boundary.test.ts"]
},
{
code: "plugin-sdk-testing-barrel",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-28",
deprecated: "2026-04-28",
warningStarts: "2026-04-28",
removeAfter: "2026-07-28",
replacement: "focused `openclaw/plugin-sdk/*` test subpaths such as `plugin-test-runtime`, `channel-test-helpers`, `test-env`, and `test-fixtures`",
docsPath: "/plugins/sdk-migration",
surfaces: ["openclaw/plugin-sdk/testing"],
diagnostics: ["plugin SDK compatibility warning"],
tests: [
"src/plugins/compat/registry.test.ts",
"scripts/check-no-extension-test-core-imports.ts",
"test/extension-test-boundary.test.ts"
]
},
{
code: "channel-route-key-aliases",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-28",
deprecated: "2026-04-28",
warningStarts: "2026-04-28",
removeAfter: "2026-07-28",
replacement: "`channelRouteDedupeKey` and `channelRouteCompactKey`",
docsPath: "/plugins/sdk-migration",
surfaces: ["openclaw/plugin-sdk/channel-route channelRouteIdentityKey", "openclaw/plugin-sdk/channel-route channelRouteKey"],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugin-sdk/channel-route.test.ts", "src/plugins/contracts/plugin-sdk-subpaths.test.ts"]
},
{
code: "channel-target-comparable-aliases",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-28",
deprecated: "2026-04-28",
warningStarts: "2026-04-28",
removeAfter: "2026-07-28",
replacement: "`ChannelRouteParsedTarget`, `channelRouteTargetsMatchExact`, `channelRouteTargetsShareConversation`, and `messaging.resolveOutboundSessionRoute` for provider-specific target grammar",
docsPath: "/plugins/sdk-migration",
surfaces: [
"src/channels/plugins/target-parsing-loaded ComparableChannelTarget",
"src/channels/plugins/target-parsing-loaded resolveComparableTargetForLoadedChannel",
"src/channels/plugins/target-parsing-loaded comparableChannelTargetsMatch",
"src/channels/plugins/target-parsing-loaded comparableChannelTargetsShareRoute"
],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/channels/plugins/target-parsing.test.ts", "src/plugins/contracts/plugin-sdk-subpaths.test.ts"]
},
{
code: "channel-explicit-target-parser",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-28",
deprecated: "2026-05-23",
warningStarts: "2026-05-23",
removeAfter: "2026-08-23",
replacement: "`messaging.targetResolver` for target normalization and `messaging.resolveOutboundSessionRoute` for session/thread identity",
docsPath: "/plugins/sdk-migration",
surfaces: [
"ChannelMessagingAdapter.parseExplicitTarget",
"openclaw/plugin-sdk/channel-route ChannelRouteExplicitTarget",
"openclaw/plugin-sdk/channel-route ChannelRouteExplicitTargetParser",
"openclaw/plugin-sdk/channel-route resolveChannelRouteTargetWithParser",
"src/channels/plugins/target-parsing-loaded ParsedChannelExplicitTarget",
"src/channels/plugins/target-parsing-loaded parseExplicitTargetForLoadedChannel",
"src/channels/plugins/target-parsing-loaded resolveRouteTargetForLoadedChannel"
],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/channels/plugins/contracts/test-helpers/surface-contract-suite.ts", "src/plugins/compat/registry.test.ts"]
},
{
code: "channel-messaging-targets-subpath",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-28",
deprecated: "2026-05-23",
warningStarts: "2026-05-23",
removeAfter: "2026-08-23",
replacement: "`openclaw/plugin-sdk/channel-targets`",
docsPath: "/plugins/sdk-migration",
surfaces: ["openclaw/plugin-sdk/messaging-targets"],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugins/compat/registry.test.ts", "src/plugins/contracts/plugin-sdk-subpaths.test.ts"]
},
{
code: "bundled-plugin-allowlist",
status: "active",
owner: "config",
introduced: "2026-04-24",
replacement: "manifest-owned plugin enablement and scoped load plans",
docsPath: "/plugins/architecture",
surfaces: [
"plugins.allow",
"bundled provider startup",
"plugins status"
],
diagnostics: ["plugin status report"],
tests: ["src/plugins/status.test.ts", "src/plugins/config-state.test.ts"]
},
{
code: "bundled-plugin-enablement",
status: "active",
owner: "config",
introduced: "2026-04-24",
replacement: "manifest-owned plugin defaults and scoped load plans",
docsPath: "/plugins/architecture",
surfaces: [
"plugins.entries",
"bundled provider startup",
"plugins status"
],
diagnostics: ["plugin status report"],
tests: ["src/plugins/status.test.ts", "src/plugins/config-state.test.ts"]
},
{
code: "bundled-plugin-vitest-defaults",
status: "active",
owner: "config",
introduced: "2026-04-24",
replacement: "explicit test plugin config fixtures",
docsPath: "/plugins/architecture",
surfaces: ["Vitest plugin defaults", "bundled provider tests"],
diagnostics: ["test-only compatibility path"],
tests: ["src/plugins/config-state.test.ts"]
},
{
code: "provider-auth-env-vars",
status: "deprecated",
owner: "setup",
introduced: "2026-04-24",
deprecated: "2026-04-24",
warningStarts: "2026-04-24",
removeAfter: "2026-07-24",
replacement: "`setup.providers[].envVars` and `providerAuthChoices`",
docsPath: "/plugins/manifest",
surfaces: ["openclaw.plugin.json providerAuthEnvVars", "provider setup"],
diagnostics: ["manifest compatibility diagnostic"],
tests: ["src/plugins/setup-registry.test.ts", "src/plugins/provider-auth-choices.test.ts"]
},
{
code: "channel-env-vars",
status: "deprecated",
owner: "channel",
introduced: "2026-04-24",
deprecated: "2026-04-24",
warningStarts: "2026-04-24",
removeAfter: "2026-07-24",
replacement: "`channelConfigs.<id>.schema` and setup descriptors",
docsPath: "/plugins/manifest",
surfaces: ["openclaw.plugin.json channelEnvVars", "channel setup"],
diagnostics: ["manifest compatibility diagnostic"],
tests: ["src/plugins/setup-registry.test.ts", "src/channels/plugins/setup-group-access.test.ts"]
},
{
code: "activation-agent-harness-hint",
status: "active",
owner: "plugin-execution",
introduced: "2026-04-24",
replacement: "top-level `cliBackends[]` for CLI aliases and future `agentRuntime` ownership metadata",
docsPath: "/plugins/manifest",
surfaces: ["activation.onAgentHarnesses", "activation planner"],
diagnostics: ["activation plan compat reason"],
tests: ["src/plugins/activation-planner.test.ts"]
},
{
code: "activation-provider-hint",
status: "active",
owner: "plugin-execution",
introduced: "2026-04-24",
replacement: "`providers[]` manifest ownership",
docsPath: "/plugins/manifest",
surfaces: ["activation.onProviders", "activation planner"],
diagnostics: ["activation plan compat reason"],
tests: ["src/plugins/activation-planner.test.ts"]
},
{
code: "activation-channel-hint",
status: "active",
owner: "plugin-execution",
introduced: "2026-04-24",
replacement: "`channels[]` manifest ownership",
docsPath: "/plugins/manifest",
surfaces: ["activation.onChannels", "activation planner"],
diagnostics: ["activation plan compat reason"],
tests: ["src/plugins/activation-planner.test.ts"]
},
{
code: "activation-command-hint",
status: "active",
owner: "plugin-execution",
introduced: "2026-04-24",
replacement: "`commandAliases` or command contribution metadata",
docsPath: "/plugins/manifest",
surfaces: ["activation.onCommands", "activation planner"],
diagnostics: ["activation plan compat reason"],
tests: ["src/plugins/activation-planner.test.ts"]
},
{
code: "activation-route-hint",
status: "active",
owner: "plugin-execution",
introduced: "2026-04-24",
replacement: "HTTP route contribution metadata",
docsPath: "/plugins/manifest",
surfaces: ["activation.onRoutes", "activation planner"],
diagnostics: ["activation plan compat reason"],
tests: ["src/plugins/activation-planner.test.ts"]
},
{
code: "activation-config-path-hint",
status: "active",
owner: "plugin-execution",
introduced: "2026-04-27",
replacement: "manifest contribution ownership for root config surfaces",
docsPath: "/plugins/manifest",
surfaces: ["activation.onConfigPaths", "startup plugin selection"],
diagnostics: ["activation plan compat reason"],
tests: ["src/plugins/channel-plugin-ids.test.ts"]
},
{
code: "activation-capability-hint",
status: "active",
owner: "plugin-execution",
introduced: "2026-04-24",
replacement: "manifest contribution ownership",
docsPath: "/plugins/manifest",
surfaces: ["activation.onCapabilities", "activation planner"],
diagnostics: ["activation plan compat reason"],
tests: ["src/plugins/activation-planner.test.ts"]
},
{
code: "embedded-harness-config-alias",
status: "deprecated",
owner: "agent-runtime",
introduced: "2026-04-24",
deprecated: "2026-04-25",
warningStarts: "2026-04-25",
removeAfter: "2026-07-25",
replacement: "`agentRuntime` config naming",
docsPath: "/plugins/sdk-agent-harness",
surfaces: ["agents.defaults.embeddedHarness", "model/provider runtime selection"],
diagnostics: ["agent runtime config compatibility"],
tests: [LEGACY_CONFIG_MIGRATE_TEST_PATH]
},
{
code: "agent-harness-sdk-alias",
status: "deprecated",
owner: "agent-runtime",
introduced: "2026-04-24",
deprecated: "2026-04-25",
warningStarts: "2026-04-25",
removeAfter: "2026-07-25",
replacement: "`openclaw/plugin-sdk/agent-runtime`",
docsPath: "/plugins/sdk-agent-harness",
surfaces: ["openclaw/plugin-sdk/agent-harness", "openclaw/plugin-sdk/agent-harness-runtime"],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugins/contracts/plugin-sdk-subpaths.test.ts"]
},
{
code: "embedded-pi-agent-sdk-aliases",
status: "deprecated",
owner: "agent-runtime",
introduced: "2026-05-21",
deprecated: "2026-05-21",
warningStarts: "2026-05-21",
removeAfter: "2026-08-21",
replacement: "`runEmbeddedAgent` and `EmbeddedAgent*` SDK/runtime names",
docsPath: "/plugins/sdk-runtime",
surfaces: [
"api.runtime.agent.runEmbeddedPiAgent",
"openclaw/extension-api runEmbeddedPiAgent",
"openclaw/plugin-sdk/agent-harness-runtime EmbeddedPi* aliases"
],
diagnostics: ["plugin SDK compatibility registry"],
tests: ["src/plugins/runtime/index.test.ts", "src/plugins/contracts/plugin-sdk-subpaths.test.ts"],
releaseNote: "Legacy `runEmbeddedPiAgent` and `EmbeddedPi*` plugin aliases remain as deprecated SDK compatibility only."
},
{
code: "agent-harness-id-alias",
status: "deprecated",
owner: "agent-runtime",
introduced: "2026-04-24",
deprecated: "2026-04-25",
warningStarts: "2026-04-25",
removeAfter: "2026-07-25",
replacement: "`agentRuntime` ids and policy metadata",
docsPath: "/plugins/sdk-agent-harness",
surfaces: ["manifest/catalog execution policy", "runtime selection"],
diagnostics: ["agent runtime compatibility warning"],
tests: ["src/plugins/provider-runtime.test.ts", "packages/web-content-core/src/provider-runtime-shared.test.ts"]
},
{
code: "generated-bundled-channel-config-fallback",
status: "active",
owner: "channel",
introduced: "2026-04-24",
replacement: "manifest registry `channelConfigs` metadata",
docsPath: "/plugins/manifest",
surfaces: ["generated bundled channel config metadata", "channel config validation"],
diagnostics: ["channel config metadata fallback"],
tests: ["src/plugins/contracts/config-footprint-guardrails.test.ts"]
},
{
code: "disable-persisted-plugin-registry-env",
status: "deprecated",
owner: "config",
introduced: "2026-04-25",
deprecated: "2026-04-25",
warningStarts: "2026-04-25",
removeAfter: "2026-07-25",
replacement: "`openclaw plugins registry --refresh` and `openclaw doctor --fix`",
docsPath: "/cli/plugins#registry",
surfaces: ["OPENCLAW_DISABLE_PERSISTED_PLUGIN_REGISTRY", "plugin registry reads"],
diagnostics: ["persisted-registry-disabled"],
tests: ["src/plugins/plugin-registry.test.ts"]
},
{
code: "plugin-registry-install-migration-env",
status: "deprecated",
owner: "config",
introduced: "2026-04-25",
deprecated: "2026-04-25",
warningStarts: "2026-04-25",
removeAfter: "2026-07-25",
replacement: "`openclaw plugins registry --refresh` and `openclaw doctor --fix`",
docsPath: "/cli/plugins#registry",
surfaces: [
"OPENCLAW_DISABLE_PLUGIN_REGISTRY_MIGRATION",
"OPENCLAW_FORCE_PLUGIN_REGISTRY_MIGRATION",
"package postinstall plugin registry migration"
],
diagnostics: ["postinstall migration skip", "postinstall migration force deprecation warning"],
tests: ["src/commands/doctor/shared/plugin-registry-migration.test.ts"]
},
{
code: "plugin-install-config-ledger",
status: "deprecated",
owner: "config",
introduced: "2026-04-25",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "shared SQLite `installed_plugin_index` install ledger",
docsPath: "/cli/plugins#registry",
surfaces: ["plugins.installs authored config", "plugin install/update migration"],
diagnostics: ["config write migration warning", "doctor registry migration"],
tests: ["src/config/io.write-config.test.ts", "src/commands/doctor/shared/plugin-registry-migration.test.ts"]
},
{
code: "bundled-plugin-load-path-aliases",
status: "deprecated",
owner: "config",
introduced: "2026-04-25",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "packaged bundled plugins resolved through the persisted plugin registry",
docsPath: "/cli/plugins#registry",
surfaces: ["plugins.load.paths entries pointing at bundled plugin source/dist paths"],
diagnostics: ["doctor bundled plugin load-path warning"],
tests: ["src/commands/doctor/shared/bundled-plugin-load-paths.test.ts"]
},
{
code: "plugin-owned-web-search-config",
status: "deprecated",
owner: "provider",
introduced: "2026-04-26",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`plugins.entries.<plugin>.config.webSearch`",
docsPath: "/tools/web",
surfaces: ["tools.web.search.apiKey", "tools.web.search.<provider>"],
diagnostics: ["doctor legacy web-search config migration"],
tests: ["src/commands/doctor/shared/legacy-web-search-migrate.test.ts"]
},
{
code: "plugin-owned-web-fetch-config",
status: "deprecated",
owner: "provider",
introduced: "2026-04-26",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`plugins.entries.firecrawl.config.webFetch`",
docsPath: "/tools/web-fetch",
surfaces: ["tools.web.fetch.firecrawl"],
diagnostics: ["doctor legacy web-fetch config migration"],
tests: ["src/commands/doctor/shared/legacy-web-fetch-migrate.test.ts"]
},
{
code: "plugin-owned-x-search-config",
status: "deprecated",
owner: "provider",
introduced: "2026-04-26",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`plugins.entries.xai.config.webSearch.apiKey`",
docsPath: "/tools/grok-search",
surfaces: ["tools.web.x_search.apiKey"],
diagnostics: ["doctor legacy x_search config migration"],
tests: ["src/commands/doctor/shared/legacy-x-search-migrate.test.ts", LEGACY_CONFIG_MIGRATE_TEST_PATH]
},
{
code: "plugin-activate-entrypoint-alias",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`register(api)` plugin entrypoint",
docsPath: "/plugins/sdk-entrypoints",
surfaces: ["plugin module `activate(api)`", "plugin loader registration"],
diagnostics: ["loader compatibility path"],
tests: ["src/plugins/loader.test.ts"]
},
{
code: "setup-runtime-fallback",
status: "active",
owner: "setup",
introduced: "2026-04-24",
replacement: "`setup.requiresRuntime: false` with complete setup descriptors",
docsPath: "/plugins/manifest#setup-reference",
surfaces: ["setup-api runtime fallback", "setup.requiresRuntime omitted"],
diagnostics: ["setup registry runtime diagnostic"],
tests: ["src/plugins/setup-registry.test.ts", "src/plugins/setup-registry.runtime.test.ts"]
},
{
code: "provider-discovery-hook-alias",
status: "deprecated",
owner: "provider",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`catalog.run(...)` provider catalog hook",
docsPath: "/plugins/sdk-migration",
surfaces: ["provider plugin `discovery` hook", "provider catalog resolution"],
diagnostics: ["provider validation warning when catalog and discovery both register"],
tests: ["src/plugins/provider-discovery.test.ts", "src/plugins/provider-validation.test.ts"]
},
{
code: "channel-exposure-legacy-aliases",
status: "deprecated",
owner: "channel",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`openclaw.channel.exposure` metadata",
docsPath: "/plugins/sdk-setup",
surfaces: ["openclaw.channel.showConfigured", "openclaw.channel.showInSetup"],
diagnostics: ["channel exposure compatibility path"],
tests: ["src/commands/channel-setup/discovery.test.ts"]
},
{
code: "channel-runtime-sdk-alias",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "focused channel SDK subpaths, especially `openclaw/plugin-sdk/channel-runtime-context`",
docsPath: "/plugins/sdk-migration",
surfaces: [CHANNEL_RUNTIME_SDK_SURFACE],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugins/contracts/plugin-sdk-subpaths.test.ts"]
},
{
code: "command-auth-status-builders",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`openclaw/plugin-sdk/command-status`",
docsPath: "/plugins/sdk-migration",
surfaces: [
"openclaw/plugin-sdk/command-auth buildCommandsMessage",
"openclaw/plugin-sdk/command-auth buildCommandsMessagePaginated",
"openclaw/plugin-sdk/command-auth buildHelpMessage"
],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugin-sdk/command-auth.test.ts"]
},
{
code: "clawdbot-config-type-alias",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`OpenClawConfig`",
docsPath: "/plugins/sdk-migration",
surfaces: ["openclaw/plugin-sdk `ClawdbotConfig` type export"],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugins/contracts/plugin-sdk-index.test.ts"]
},
{
code: "openclaw-schema-type-alias",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-26",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`OpenClawConfig` from `openclaw/plugin-sdk/config-schema`",
docsPath: "/plugins/sdk-migration",
surfaces: ["openclaw/plugin-sdk `OpenClawSchemaType` type export"],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugins/contracts/plugin-sdk-index.test.ts"]
},
{
code: "legacy-extension-api-import",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "injected `api.runtime.*` helpers or focused `openclaw/plugin-sdk/<subpath>` imports",
docsPath: "/plugins/sdk-migration",
surfaces: ["openclaw/extension-api"],
diagnostics: ["OPENCLAW_EXTENSION_API_DEPRECATED"],
tests: ["src/plugins/sdk-alias.test.ts", "src/index.test.ts"]
},
{
code: "memory-split-registration",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`api.registerMemoryCapability({ promptBuilder, flushPlanResolver, runtime })`",
docsPath: "/plugins/sdk-migration",
surfaces: [
"api.registerMemoryPromptSection",
"api.registerMemoryFlushPlan",
"api.registerMemoryRuntime",
"src/plugins/memory-state split registration helpers"
],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugins/memory-state.test.ts", "src/plugins/loader.test.ts"]
},
{
code: "provider-static-capabilities-bag",
status: "deprecated",
owner: "provider",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "explicit provider hooks such as `buildReplayPolicy`, `normalizeToolSchemas`, and `wrapStreamFn`",
docsPath: "/plugins/sdk-provider-plugins",
surfaces: ["ProviderPlugin.capabilities", "ProviderCapabilities"],
diagnostics: ["provider validation warning"],
tests: ["src/plugins/provider-runtime.test.ts", "src/plugins/contracts/provider-family-plugin-tests.test.ts"]
},
{
code: "provider-discovery-type-aliases",
status: "deprecated",
owner: "provider",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`ProviderCatalogOrder`, `ProviderCatalogContext`, `ProviderCatalogResult`, and `ProviderPluginCatalog`",
docsPath: "/plugins/sdk-migration",
surfaces: [
"ProviderDiscoveryOrder",
"ProviderDiscoveryContext",
"ProviderDiscoveryResult",
"ProviderPluginDiscovery"
],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugins/contracts/plugin-sdk-index.test.ts"]
},
{
code: "provider-thinking-policy-hooks",
status: "deprecated",
owner: "provider",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`resolveThinkingProfile`",
docsPath: "/plugins/sdk-provider-plugins",
surfaces: [
"ProviderPlugin.isBinaryThinking",
"ProviderPlugin.supportsXHighThinking",
"ProviderPlugin.resolveDefaultThinkingLevel"
],
diagnostics: ["provider runtime compatibility warning"],
tests: ["src/plugins/provider-runtime.test.ts"]
},
{
code: "provider-external-oauth-profiles-hook",
status: "deprecated",
owner: "provider",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`contracts.externalAuthProviders` plus `resolveExternalAuthProfiles`",
docsPath: "/plugins/sdk-provider-plugins",
surfaces: ["ProviderPlugin.resolveExternalOAuthProfiles"],
diagnostics: ["provider external auth fallback warning"],
tests: ["src/plugins/provider-runtime.test.ts"]
},
{
code: "agent-tool-result-harness-alias",
status: "deprecated",
owner: "agent-runtime",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`runtime` and `runtimes` agent tool-result middleware fields",
docsPath: "/plugins/sdk-agent-harness",
surfaces: [
"AgentToolResultMiddlewareHarness",
"AgentToolResultMiddlewareContext.harness",
"AgentToolResultMiddlewareOptions.harnesses",
"normalizeAgentToolResultMiddlewareHarnesses"
],
diagnostics: ["agent runtime compatibility warning"],
tests: ["src/plugins/captured-registration.test.ts", "src/agents/codex-app-server.extensions.test.ts"]
},
{
code: "runtime-config-load-write",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-27",
deprecated: "2026-04-27",
warningStarts: "2026-04-27",
removeAfter: "2026-07-27",
replacement: "`api.runtime.config.current()`, passed config values, `mutateConfigFile(...)`, or `replaceConfigFile(...)`",
docsPath: "/plugins/sdk-runtime#config-loading-and-writes",
surfaces: ["api.runtime.config.loadConfig", "api.runtime.config.writeConfigFile"],
diagnostics: [
"plugin runtime compatibility warning",
"deprecated API usage guard",
"runtime channel config boundary guard"
],
tests: [
"src/plugins/runtime/runtime-config.test.ts",
"src/plugins/contracts/deprecated-internal-config-api.test.ts",
"src/plugins/contracts/config-boundary-guard.test.ts"
]
},
{
code: "runtime-taskflow-legacy-alias",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`api.runtime.tasks.managedFlows` for managed mutations or `api.runtime.tasks.flows` for DTO reads",
docsPath: "/plugins/sdk-runtime",
surfaces: ["api.runtime.taskFlow", "api.runtime.tasks.flow"],
diagnostics: ["plugin runtime compatibility warning"],
tests: ["src/plugins/runtime/index.test.ts", "src/plugins/runtime/runtime-tasks.test.ts"]
},
{
code: "runtime-subagent-get-session-alias",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`api.runtime.subagent.getSessionMessages`",
docsPath: "/plugins/sdk-runtime",
surfaces: ["api.runtime.subagent.getSession"],
diagnostics: ["plugin runtime compatibility warning"],
tests: ["src/plugins/runtime/index.test.ts"]
},
{
code: "runtime-stt-alias",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`api.runtime.mediaUnderstanding.transcribeAudioFile`",
docsPath: "/plugins/sdk-runtime",
surfaces: ["api.runtime.stt.transcribeAudioFile"],
diagnostics: ["plugin runtime compatibility warning"],
tests: ["src/plugins/runtime/index.test.ts"]
},
{
code: "runtime-inbound-envelope-alias",
status: "deprecated",
owner: "channel",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`BodyForAgent` plus structured user-context blocks",
docsPath: "/plugins/sdk-runtime",
surfaces: ["api.runtime.channel.reply.formatInboundEnvelope"],
diagnostics: ["channel runtime compatibility warning"],
tests: ["src/plugins/runtime/index.test.ts"]
},
{
code: "channel-native-message-schema-helpers",
status: "deprecated",
owner: "channel",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "semantic `presentation` capabilities",
docsPath: "/plugins/sdk-migration",
surfaces: ["openclaw/plugin-sdk/channel-actions createMessageToolButtonsSchema", "openclaw/plugin-sdk/channel-actions createMessageToolCardSchema"],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugins/contracts/plugin-sdk-subpaths.test.ts"]
},
{
code: "channel-mention-gating-legacy-helpers",
status: "deprecated",
owner: "channel",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "`resolveInboundMentionDecision({ facts, policy })`",
docsPath: "/plugins/sdk-migration",
surfaces: [
"openclaw/plugin-sdk/channel-inbound resolveMentionGating",
"openclaw/plugin-sdk/channel-inbound resolveMentionGatingWithBypass",
"openclaw/plugin-sdk/channel-mention-gating resolveMentionGating",
"openclaw/plugin-sdk/channel-mention-gating resolveMentionGatingWithBypass"
],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugins/contracts/plugin-sdk-subpaths.test.ts"]
},
{
code: "provider-web-search-core-wrapper",
status: "deprecated",
owner: "provider",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "provider-owned `createTool(...)` on the returned `WebSearchProviderPlugin`",
docsPath: "/plugins/sdk-provider-plugins",
surfaces: ["openclaw/plugin-sdk/provider-web-search createPluginBackedWebSearchProvider"],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugins/contracts/plugin-sdk-subpaths.test.ts"]
},
{
code: "approval-capability-approvals-alias",
status: "deprecated",
owner: "channel",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "top-level `delivery`, `nativeRuntime`, `render`, and `native` approval capability fields",
docsPath: "/plugins/sdk-channel-plugins",
surfaces: ["createChannelApprovalCapability({ approvals })"],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugin-sdk/approval-delivery-helpers.test.ts"]
},
{
code: "plugin-sdk-test-utils-alias",
status: "deprecated",
owner: "sdk",
introduced: "2026-04-24",
deprecated: "2026-04-26",
warningStarts: "2026-04-26",
removeAfter: "2026-07-26",
replacement: "focused `openclaw/plugin-sdk/*` test subpaths",
docsPath: "/plugins/sdk-migration",
surfaces: ["openclaw/plugin-sdk/test-utils"],
diagnostics: ["plugin SDK compatibility warning"],
tests: ["src/plugins/compat/registry.test.ts", "src/plugins/contracts/plugin-sdk-subpaths.test.ts"]
}
];
const pluginCompatRecordByCode = new Map(PLUGIN_COMPAT_RECORDS.map((record) => [record.code, record]));
function listPluginCompatRecords() {
return PLUGIN_COMPAT_RECORDS;
}
function getPluginCompatRecord(code) {
const record = pluginCompatRecordByCode.get(code);
if (!record) throw new Error(`Unknown plugin compatibility code: ${code}`);
return record;
}
//#endregion
//#region src/plugins/installed-plugin-index-hash.ts
function hashString(value) {
return crypto.createHash("sha256").update(value).digest("hex");
}
/** Hashes JSON-serializable data with SHA-256. */
function hashJson(value) {
return hashString(JSON.stringify(value));
}
/** Safely hashes a file, optionally recording required-file diagnostics. */
function safeHashFile(params) {
try {
return crypto.createHash("sha256").update(fs.readFileSync(params.filePath)).digest("hex");
} catch (err) {
if (params.required) params.diagnostics.push({
level: "warn",
...params.pluginId ? { pluginId: params.pluginId } : {},
source: params.filePath,
message: `installed plugin index could not hash ${params.filePath}: ${err instanceof Error ? err.message : String(err)}`
});
return;
}
}
/** Reads a safe file signature for installed plugin index freshness checks. */
function safeFileSignature(filePath) {
try {
const stat = fs.statSync(filePath);
if (!stat.isFile()) return;
return {
size: stat.size,
mtimeMs: stat.mtimeMs,
ctimeMs: stat.ctimeMs
};
} catch {
return;
}
}
/** Compares current file metadata with a stored installed-plugin file signature. */
function fileSignatureMatches(filePath, signature) {
if (!signature) return;
if (typeof signature.ctimeMs !== "number") return;
const current = safeFileSignature(filePath);
if (!current) return false;
return current.size === signature.size && current.mtimeMs === signature.mtimeMs && current.ctimeMs === signature.ctimeMs;
}
//#endregion
//#region src/plugins/installed-plugin-index-policy.ts
/** Hashes plugin compat registry state that can affect installed index validity. */
function resolveCompatRegistryVersion() {
return hashJson(listPluginCompatRecords().map((record) => ({
code: record.code,
status: record.status,
deprecated: record.deprecated,
warningStarts: record.warningStarts,
removeAfter: record.removeAfter,
replacement: record.replacement
})));
}
/** Hashes config policy inputs that can change installed plugin activation. */
function resolveInstalledPluginIndexPolicyHash(config) {
const normalized = normalizePluginsConfig(config?.plugins);
const channelPolicy = {};
const channels = config?.channels;
if (channels && typeof channels === "object" && !Array.isArray(channels)) {
for (const [channelId, value] of Object.entries(channels)) if (value && typeof value === "object" && !Array.isArray(value)) {
const enabled = value.enabled;
if (typeof enabled === "boolean") channelPolicy[channelId] = enabled;
}
}
return hashJson({
plugins: {
enabled: normalized.enabled,
allow: normalized.allow,
deny: normalized.deny,
slots: normalized.slots,
entries: Object.fromEntries(Object.entries(normalized.entries).flatMap(([pluginId, entry]) => typeof entry.enabled === "boolean" ? [[pluginId, entry.enabled]] : []).toSorted(([left], [right]) => left.localeCompare(right)))
},
channels: Object.fromEntries(Object.entries(channelPolicy).toSorted(([left], [right]) => left.localeCompare(right)))
});
}
//#endregion
//#region src/plugins/installed-plugin-index-install-records.ts
function setInstallStringField(target, key, value) {
if (typeof value !== "string") return;
const normalized = value.trim();
if (normalized) target[key] = normalized;
}
function setInstallNumberField(target, key, value) {
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) target[key] = value;
}
function normalizeInstallRecord(record) {
if (!record) return;
const normalized = { source: record.source };
setInstallStringField(normalized, "spec", record.spec);
setInstallStringField(normalized, "sourcePath", record.sourcePath);
setInstallStringField(normalized, "installPath", record.installPath);
setInstallStringField(normalized, "version", record.version);
setInstallStringField(normalized, "resolvedName", record.resolvedName);
setInstallStringField(normalized, "resolvedVersion", record.resolvedVersion);
setInstallStringField(normalized, "resolvedSpec", record.resolvedSpec);
setInstallStringField(normalized, "integrity", record.integrity);
setInstallStringField(normalized, "shasum", record.shasum);
setInstallStringField(normalized, "resolvedAt", record.resolvedAt);
setInstallStringField(normalized, "installedAt", record.installedAt);
setInstallStringField(normalized, "clawhubUrl", record.clawhubUrl);
setInstallStringField(normalized, "clawhubPackage", record.clawhubPackage);
setInstallStringField(normalized, "clawhubFamily", record.clawhubFamily);
setInstallStringField(normalized, "clawhubChannel", record.clawhubChannel);
setInstallStringField(normalized, "artifactKind", record.artifactKind);
setInstallStringField(normalized, "artifactFormat", record.artifactFormat);
setInstallStringField(normalized, "npmIntegrity", record.npmIntegrity);
setInstallStringField(normalized, "npmShasum", record.npmShasum);
setInstallStringField(normalized, "npmTarballName", record.npmTarballName);
setInstallStringField(normalized, "clawpackSha256", record.clawpackSha256);
setInstallNumberField(normalized, "clawpackSpecVersion", record.clawpackSpecVersion);
setInstallStringField(normalized, "clawpackManifestSha256", record.clawpackManifestSha256);
setInstallNumberField(normalized, "clawpackSize", record.clawpackSize);
setInstallStringField(normalized, "gitUrl", record.gitUrl);
setInstallStringField(normalized, "gitRef", record.gitRef);
setInstallStringField(normalized, "gitCommit", record.gitCommit);
setInstallStringField(normalized, "marketplaceName", record.marketplaceName);
setInstallStringField(normalized, "marketplaceSource", record.marketplaceSource);
setInstallStringField(normalized, "marketplacePlugin", record.marketplacePlugin);
return normalized;
}
function restoreInstallRecord(record) {
if (!record?.source) return;
return structuredClone(record);
}
/** Normalizes raw plugin install records into index-safe install record metadata. */
function normalizeInstallRecordMap(records) {
const normalized = {};
for (const [pluginId, record] of Object.entries(records ?? {}).toSorted(([left], [right]) => left.localeCompare(right))) {
const installRecord = normalizeInstallRecord(record);
if (installRecord) normalized[pluginId] = installRecord;
}
return normalized;
}
function restoreInstallRecordMap(records) {
const restored = {};
for (const [pluginId, record] of Object.entries(records ?? {}).toSorted(([left], [right]) => left.localeCompare(right))) {
const installRecord = restoreInstallRecord(record);
if (installRecord) restored[pluginId] = installRecord;
}
return restored;
}
/** Extracts raw plugin install records from either current or legacy installed-index shapes. */
function extractPluginInstallRecordsFromInstalledPluginIndex(index) {
if (index && Object.hasOwn(index, "installRecords")) return restoreInstallRecordMap(index.installRecords);
const records = {};
for (const plugin of index?.plugins ?? []) {
const record = restoreInstallRecord(plugin.installRecord);
if (record) records[plugin.pluginId] = record;
}
return records;
}
//#endregion
//#region src/plugins/installed-plugin-index-manifest.ts
/** True when a Claude bundle record omits its optional manifest file. */
function hasOptionalMissingPluginManifestFile(record) {
return record.format === "bundle" && record.bundleFormat === "claude" && !fs.existsSync(record.manifestPath);
}
//#endregion
//#region src/plugins/installed-plugin-index-record-builder.ts
/** Builds installed-index records from normalized plugin manifest registry entries. */
function buildStartupInfo(record) {
return {
sidecar: record.activation?.onStartup === true,
memory: hasKind(record.kind, "memory"),
deferConfiguredChannelFullLoadUntilAfterListen: record.startupDeferConfiguredChannelFullLoadUntilAfterListen === true,
agentHarnesses: normalizeSortedUniqueStringEntries([...record.activation?.onAgentHarnesses ?? [], ...record.cliBackends ?? []]),
configPaths: normalizeSortedUniqueStringEntries(record.activation?.onConfigPaths)
};
}
function buildContributionInfo(record) {
const contracts = Object.fromEntries(Object.entries(record.contracts ?? {}).map(([key, values]) => [key, normalizeSortedUniqueStringEntries(values)]));
return {
channels: normalizeSortedUniqueStringEntries(record.channels),
channelConfigs: normalizeSortedUniqueStringEntries(Object.keys(record.channelConfigs ?? {})),
providers: normalizeSortedUniqueStringEntries(record.providers),
modelCatalogProviders: normalizeSortedUniqueStringEntries([
...Object.keys(record.modelCatalog?.providers ?? {}),
...Object.keys(record.modelCatalog?.aliases ?? {}),
...(record.modelCatalog?.suppressions ?? []).map((entry) => entry.provider)
]),
modelSupportPrefixes: normalizeSortedUniqueStringEntries(record.modelSupport?.modelPrefixes),
modelSupportPatterns: normalizeSortedUniqueStringEntries(record.modelSupport?.modelPatterns),
autoEnableProviderIds: normalizeSortedUniqueStringEntries(record.autoEnableWhenConfiguredProviders),
commandAliases: normalizeSortedUniqueStringEntries(record.commandAliases?.map((alias) => alias.name)),
contracts
};
}
/** Collects compatibility codes implied by a manifest's legacy or activation surfaces. */
function collectPluginManifestCompatCodes(record) {
const codes = [];
if (record.providerAuthEnvVars && Object.keys(record.providerAuthEnvVars).length > 0) codes.push("provider-auth-env-vars");
if (record.channelEnvVars && Object.keys(record.channelEnvVars).length > 0) codes.push("channel-env-vars");
if (record.activation?.onProviders?.length) codes.push("activation-provider-hint");
if (record.activation?.onAgentHarnesses?.length) codes.push("activation-agent-harness-hint");
if (record.activation?.onChannels?.length) codes.push("activation-channel-hint");
if (record.activation?.onCommands?.length) codes.push("activation-command-hint");
if (record.activation?.onRoutes?.length) codes.push("activation-route-hint");
if (record.activation?.onConfigPaths?.length) codes.push("activation-config-path-hint");
if (record.activation?.onCapabilities?.length) codes.push("activation-capability-hint");
return normalizeSortedUniqueStringEntries(codes);
}
function resolvePackageJsonPath(candidate, realpathCache) {
if (!candidate?.packageDir) return;
const packageDir = safeRealpathSync(candidate.packageDir, realpathCache) ?? path.resolve(candidate.packageDir);
const packageJsonPath = path.join(packageDir, "package.json");
const rootDir = candidate.rootDir === candidate.packageDir ? packageDir : safeRealpathSync(candidate.rootDir, realpathCache) ?? path.resolve(candidate.rootDir);
const packageJsonRealPath = safeRealpathSync(packageJsonPath, realpathCache);
return packageJsonRealPath && isPathInside(rootDir, packageJsonRealPath) ? packageJsonPath : void 0;
}
function resolvePackageJsonRelativePath(rootDir, packageJsonPath, realpathCache) {
const resolvedRootDir = rootDir === path.dirname(packageJsonPath) ? path.dirname(packageJsonPath) : safeRealpathSync(rootDir, realpathCache) ?? path.resolve(rootDir);
return (path.relative(resolvedRootDir, packageJsonPath) || "package.json").split(path.sep).join("/");
}
function resolvePackageJsonRecord(params) {
if (!params.candidate?.packageDir || !params.packageJsonPath) return;
const hash = safeHashFile({
filePath: params.packageJsonPath,
pluginId: params.pluginId,
diagnostics: params.diagnostics,
required: false
});
if (!hash) return;
const fileSignature = safeFileSignature(params.packageJsonPath);
return {
path: resolvePackageJsonRelativePath(params.candidate.rootDir, params.packageJsonPath, params.realpathCache),
hash,
...fileSignature ? { fileSignature } : {}
};
}
function describePackageInstallSource(candidate) {
const install = candidate?.packageManifest?.install;
if (!install) return;
return describePluginInstallSource(install, { expectedPackageName: candidate?.packageName });
}
function normalizeStringField(value) {
if (typeof value !== "string") return;
const normalized = value.trim();
return normalized ? normalized : void 0;
}
function normalizePackageChannel(channel) {
const id = normalizeStringField(channel?.id);
if (!id) return;
return {
...structuredClone(channel),
id
};
}
function hashManifestlessBundleRecord(record) {
return hashJson({
id: record.id,
name: record.name,
description: record.description,
version: record.version,
format: record.format,
bundleFormat: record.bundleFormat,
bundleCapabilities: record.bundleCapabilities ?? [],
skills: record.skills ?? [],
settingsFiles: record.settingsFiles ?? [],
hooks: record.hooks ?? []
});
}
function resolveManifestHash(params) {
if (hasOptionalMissingPluginManifestFile(params.record)) return hashManifestlessBundleRecord(params.record);
const hash = safeHashFile({
filePath: params.record.manifestPath,
pluginId: params.record.id,
diagnostics: params.diagnostics,
required: true
});
if (hash) return hash;
return "";
}
function buildCandidateLookup(candidates) {
const byRootDir = /* @__PURE__ */ new Map();
for (const candidate of candidates) byRootDir.set(candidate.rootDir, candidate);
return byRootDir;
}
function buildInstalledPluginIndexRecords(params) {
const candidateByRootDir = buildCandidateLookup(params.candidates);
const normalizedConfig = normalizePluginsConfig(params.config?.plugins);
const realpathCache = /* @__PURE__ */ new Map();
return params.registry.plugins.map((record) => {
const candidate = candidateByRootDir.get(record.rootDir);
const packageJsonPath = resolvePackageJsonPath(candidate, realpathCache);
const installRecord = params.installRecords[record.id];
const packageInstall = describePackageInstallSource(candidate);
const packageChannel = normalizePackageChannel(record.packageChannel ?? candidate?.packageManifest?.channel);
const manifestHash = resolveManifestHash({
record,
diagnostics: params.diagnostics
});
const manifestFile = hasOptionalMissingPluginManifestFile(record) ? void 0 : safeFileSignature(record.manifestPath);
const packageJson = resolvePackageJsonRecord({
candidate,
packageJsonPath,
diagnostics: params.diagnostics,
pluginId: record.id,
realpathCache
});
const enabled = resolveEffectiveEnableState({
id: record.id,
origin: record.origin,
config: normalizedConfig,
rootConfig: params.config,
enabledByDefault: isPluginEnabledByDefaultForPlatform(record)
}).enabled;
const indexRecord = {
pluginId: record.id,
manifestPath: record.manifestPath,
manifestHash,
...manifestFile ? { manifestFile } : {},
source: record.source,
rootDir: record.rootDir,
origin: record.origin,
enabled,
startup: buildStartupInfo(record),
contributions: buildContributionInfo(record),
compat: collectPluginManifestCompatCodes(record)
};
if (record.format && record.format !== "openclaw") indexRecord.format = record.format;
if (record.bundleFormat) indexRecord.bundleFormat = record.bundleFormat;
if (record.enabledByDefault === true) indexRecord.enabledByDefault = true;
if (record.enabledByDefaultOnPlatforms?.length) indexRecord.enabledByDefaultOnPlatforms = [...record.enabledByDefaultOnPlatforms];
if (record.syntheticAuthRefs?.length) indexRecord.syntheticAuthRefs = [...record.syntheticAuthRefs];
if (record.setupSource) indexRecord.setupSource = record.setupSource;
if (candidate?.packageName) indexRecord.packageName = candidate.packageName;
if (candidate?.packageVersion) indexRecord.packageVersion = candidate.packageVersion;
if (installRecord) indexRecord.installRecordHash = hashJson(installRecord);
if (packageInstall) indexRecord.packageInstall = packageInstall;
if (packageChannel) indexRecord.packageChannel = packageChannel;
if (packageJson) indexRecord.packageJson = packageJson;
return indexRecord;
});
}
//#endregion
//#region src/plugins/installed-plugin-index-registry.ts
/** Resolves discovery candidates and manifest registry for installed plugin index loading. */
function resolveInstalledPluginIndexRegistry(params) {
if (params.candidates) return {
candidates: params.candidates,
registry: loadPluginManifestRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
candidates: params.candidates,
diagnostics: params.diagnostics,
installRecords: params.installRecords
})
};
const normalized = normalizePluginsConfig(params.config?.plugins);
const installRecords = params.installRecords ?? loadInstalledPluginIndexInstallRecordsSync({ env: params.env });
const discovery = params.discovery ?? discoverOpenClawPlugins({
workspaceDir: params.workspaceDir,
extraPaths: normalized.loadPaths,
env: params.env,
installRecords
});
return {
candidates: discovery.candidates,
discovery,
registry: loadPluginManifestRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
candidates: discovery.candidates,
diagnostics: discovery.diagnostics,
installRecords
})
};
}
//#endregion
//#region src/plugins/installed-plugin-index-types.ts
const INSTALLED_PLUGIN_INDEX_WARNING = "DO NOT EDIT. This file is generated by OpenClaw from plugin manifests, install records, and config policy. Use `openclaw plugins registry --refresh`, `openclaw plugins install/update/uninstall`, or `openclaw plugins enable/disable` instead.";
//#endregion
//#region src/plugins/installed-plugin-index-config-path-scope.ts
/** Compat code marking install records that need config-path activation metadata. */
const CONFIG_PATH_ACTIVATION_COMPAT_CODE = "activation-config-path-hint";
function recordUsesConfigPathActivation(plugin) {
return plugin.compat.includes(CONFIG_PATH_ACTIVATION_COMPAT_CODE);
}
/** True when an index still has config-path activation records missing startup metadata. */
function hasMissingConfigPathActivationMetadata(index) {
return index.plugins.some((plugin) => recordUsesConfigPathActivation(plugin) && plugin.startup.configPaths === void 0);
}
/** True when a record migrated config-path activation startup metadata. */
function hasConfigPathActivationMetadataMigration(params) {
return recordUsesConfigPathActivation(params.previous) && params.previous.startup.configPaths === void 0 && params.current.startup.configPaths !== void 0;
}
//#endregion
//#region src/plugins/installed-plugin-index-invalidation.ts
function diffInstalledPluginIndexInvalidationReasons(previous, current) {
const reasons = /* @__PURE__ */ new Set();
if (previous.version !== current.version) reasons.add("missing");
if (previous.hostContractVersion !== current.hostContractVersion) reasons.add("host-contract-changed");
if (previous.compatRegistryVersion !== current.compatRegistryVersion) reasons.add("compat-registry-changed");
if (previous.migrationVersion !== current.migrationVersion) reasons.add("migration");
if (previous.policyHash !== current.policyHash) reasons.add("policy-changed");
if (hashJson(previous.installRecords ?? {}) !== hashJson(current.installRecords ?? {})) reasons.add("source-changed");
const previousByPluginId = new Map(previous.plugins.map((plugin) => [plugin.pluginId, plugin]));
const currentByPluginId = new Map(current.plugins.map((plugin) => [plugin.pluginId, plugin]));
for (const [pluginId, previousPlugin] of previousByPluginId) {
const currentPlugin = currentByPluginId.get(pluginId);
if (!currentPlugin) {
reasons.add("source-changed");
continue;
}
if (previousPlugin.rootDir !== currentPlugin.rootDir || previousPlugin.manifestPath !== currentPlugin.manifestPath || previousPlugin.installRecordHash !== currentPlugin.installRecordHash) reasons.add("source-changed");
if (previousPlugin.enabled !== currentPlugin.enabled) reasons.add("policy-changed");
if (hasConfigPathActivationMetadataMigration({
previous: previousPlugin,
current: currentPlugin
})) reasons.add("migration");
if (previousPlugin.manifestHash !== currentPlugin.manifestHash) reasons.add("stale-manifest");
if (previousPlugin.packageVersion !== currentPlugin.packageVersion || previousPlugin.packageJson?.path !== currentPlugin.packageJson?.path || previousPlugin.packageJson?.hash !== currentPlugin.packageJson?.hash) reasons.add("stale-package");
}
for (const pluginId of currentByPluginId.keys()) if (!previousByPluginId.has(pluginId)) {
if (currentByPluginId.get(pluginId)?.enabled === false) continue;
reasons.add("source-changed");
}
return Array.from(reasons).toSorted((left, right) => left.localeCompare(right));
}
//#endregion
//#region src/plugins/installed-plugin-index.ts
function buildInstalledPluginIndex(params) {
const env = params.env ?? process.env;
const { candidates, registry, discovery } = resolveInstalledPluginIndexRegistry(params);
const diagnostics = [...registry.diagnostics ?? []];
const generatedAtMs = (params.now?.() ?? /* @__PURE__ */ new Date()).getTime();
const installRecords = normalizeInstallRecordMap(params.installRecords ?? loadInstalledPluginIndexInstallRecordsSync({
env,
...params.stateDir ? { stateDir: params.stateDir } : {},
...params.pluginIndexFilePath ? { filePath: params.pluginIndexFilePath } : {}
}));
const plugins = buildInstalledPluginIndexRecords({
candidates,
registry,
config: params.config,
diagnostics,
installRecords
});
return {
index: {
version: 1,
warning: INSTALLED_PLUGIN_INDEX_WARNING,
hostContractVersion: resolveCompatibilityHostVersion(env),
compatRegistryVersion: resolveCompatRegistryVersion(),
migrationVersion: 1,
policyHash: resolveInstalledPluginIndexPolicyHash(params.config),
generatedAtMs,
...params.refreshReason ? { refreshReason: params.refreshReason } : {},
installRecords,
plugins,
diagnostics
},
discovery
};
}
function loadInstalledPluginIndex(params = {}) {
return buildInstalledPluginIndex(params).index;
}
function loadInstalledPluginIndexWithDiscovery(params = {}) {
return buildInstalledPluginIndex(params);
}
function refreshInstalledPluginIndex(params) {
return buildInstalledPluginIndex({
...params,
refreshReason: params.reason
}).index;
}
function getInstalledPluginRecord(index, pluginId) {
return index.plugins.find((plugin) => plugin.pluginId === pluginId);
}
function isInstalledPluginEnabled(index, pluginId, config) {
const record = getInstalledPluginRecord(index, pluginId);
if (!record) return false;
if (!config) return record.enabled;
const normalizedConfig = normalizePluginsConfig(config?.plugins);
const state = resolveEffectivePluginActivationState({
id: record.pluginId,
origin: record.origin,
config: normalizedConfig,
rootConfig: config,
enabledByDefault: isPluginEnabledByDefaultForPlatform(record)
});
return state.enabled && (record.enabled || state.explicitlyEnabled);
}
//#endregion
export { getPluginCompatRecord as _, refreshInstalledPluginIndex as a, hasMissingConfigPathActivationMetadata as c, hasOptionalMissingPluginManifestFile as d, extractPluginInstallRecordsFromInstalledPluginIndex as f, hashJson as g, fileSignatureMatches as h, loadInstalledPluginIndexWithDiscovery as i, INSTALLED_PLUGIN_INDEX_WARNING as l, resolveInstalledPluginIndexPolicyHash as m, isInstalledPluginEnabled as n, diffInstalledPluginIndexInvalidationReasons as o, resolveCompatRegistryVersion as p, loadInstalledPluginIndex as r, CONFIG_PATH_ACTIVATION_COMPAT_CODE as s, getInstalledPluginRecord as t, collectPluginManifestCompatCodes as u };