diff --git a/CHANGELOG.md b/CHANGELOG.md index c0ddc492567..8ae84756193 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ Docs: https://docs.openclaw.ai - CLI/configure: keep web-search configure prompts on cold plugin registry metadata until the user chooses managed search setup. Thanks @vincentkoc. - Plugins/chat commands: refresh the persisted plugin registry after `/plugins enable` and `/plugins disable`, matching the CLI mutation path. Thanks @vincentkoc. - Plugins/compat: mark `OPENCLAW_DISABLE_PERSISTED_PLUGIN_REGISTRY` as a deprecated break-glass switch and point operators at registry repair instead. Thanks @vincentkoc. +- Plugins/compat: expand the central compatibility registry with dated owners, replacements, and removal targets for legacy SDK, manifest, setup, registry-migration, and agent-runtime surfaces. Thanks @vincentkoc. - Plugins/registry: ignore stale persisted registry reads when plugin policy no longer matches current config, and stamp generated registry files with a do-not-edit warning. Thanks @vincentkoc. - Diagnostics/OTEL: surface provider request identifiers as bounded hashes on model-call diagnostics and span events, without exporting raw request IDs or metric labels. Thanks @Lidang-Jiang and @vincentkoc. - Plugins/diagnostics: add metadata-only `model_call_started` and `model_call_ended` hooks for provider/model call telemetry without exposing prompts, responses, headers, request bodies, or raw provider request IDs. Thanks @vincentkoc. diff --git a/docs/plugins/compatibility.md b/docs/plugins/compatibility.md index 8e99e711514..23f974fbd51 100644 --- a/docs/plugins/compatibility.md +++ b/docs/plugins/compatibility.md @@ -71,7 +71,9 @@ The migration sequence is: 7. Remove only with explicit breaking-release approval. Deprecated records must include a warning start date, replacement, docs link, -and target removal date when known. +and target removal date. Do not add a deprecated compatibility path with an +open-ended removal window unless maintainers explicitly decide it is permanent +compatibility and mark it `active` instead. ## Current compatibility areas @@ -79,15 +81,27 @@ Current compatibility records include: - legacy broad SDK imports such as `openclaw/plugin-sdk/compat` - legacy hook-only plugin shapes and `before_agent_start` +- legacy `activate(api)` plugin entrypoints while plugins migrate to + `register(api)` +- legacy SDK aliases such as `openclaw/plugin-sdk/channel-runtime`, + `openclaw/plugin-sdk/command-auth` status builders, and the + `ClawdbotConfig` type alias - bundled plugin allowlist and enablement behavior - legacy provider/channel env-var manifest metadata - activation hints that are being replaced by manifest contribution ownership +- `setup-api` runtime fallback while setup descriptors move to cold + `setup.requiresRuntime: false` metadata +- provider `discovery` hooks while provider catalog hooks move to + `catalog.run(...)` +- channel `showConfigured` / `showInSetup` metadata while channel packages move + to `openclaw.channel.exposure` - legacy runtime-policy config keys while doctor migrates operators to `agentRuntime` - generated bundled channel config metadata fallback while registry-first `channelConfigs` metadata lands -- the persisted plugin registry disable env while repair flows migrate operators - to `openclaw plugins registry --refresh` and `openclaw doctor --fix` +- persisted plugin registry disable and install-migration env flags while + repair flows migrate operators to `openclaw plugins registry --refresh` and + `openclaw doctor --fix` New plugin code should prefer the replacement listed in the registry and in the specific migration guide. Existing plugins can keep using a compatibility path diff --git a/src/plugins/compat/registry.test.ts b/src/plugins/compat/registry.test.ts index f90737161fc..88b8112f128 100644 --- a/src/plugins/compat/registry.test.ts +++ b/src/plugins/compat/registry.test.ts @@ -1,3 +1,4 @@ +import fs from "node:fs"; import { describe, expect, it } from "vitest"; import { getPluginCompatRecord, @@ -23,6 +24,7 @@ describe("plugin compatibility registry", () => { for (const record of listDeprecatedPluginCompatRecords()) { expect(record.deprecated, record.code).toMatch(datePattern); expect(record.warningStarts, record.code).toMatch(datePattern); + expect(record.removeAfter, record.code).toMatch(datePattern); expect(record.replacement, record.code).toBeTruthy(); expect(record.docsPath, record.code).toMatch(/^\//u); } @@ -35,6 +37,9 @@ describe("plugin compatibility registry", () => { expect(record.surfaces.length, record.code).toBeGreaterThan(0); expect(record.diagnostics.length, record.code).toBeGreaterThan(0); expect(record.tests.length, record.code).toBeGreaterThan(0); + for (const testPath of record.tests) { + expect(fs.existsSync(testPath), `${record.code}: ${testPath}`).toBe(true); + } } }); }); diff --git a/src/plugins/compat/registry.ts b/src/plugins/compat/registry.ts index 282da5406fc..1f356f5dc1b 100644 --- a/src/plugins/compat/registry.ts +++ b/src/plugins/compat/registry.ts @@ -8,6 +8,7 @@ export const PLUGIN_COMPAT_RECORDS = [ introduced: "2026-04-24", deprecated: "2026-04-24", warningStarts: "2026-04-24", + removeAfter: "2026-07-01", replacement: "`before_model_resolve` and `before_prompt_build` hooks", docsPath: "/plugins/sdk-migration", surfaces: ["plugin hooks", "plugins inspect", "status diagnostics"], @@ -34,6 +35,7 @@ export const PLUGIN_COMPAT_RECORDS = [ introduced: "2026-04-24", deprecated: "2026-04-24", warningStarts: "2026-04-24", + removeAfter: "2026-07-01", replacement: "focused `openclaw/plugin-sdk/` imports", docsPath: "/plugins/sdk-migration", surfaces: ["openclaw/plugin-sdk", "openclaw/plugin-sdk/compat"], @@ -83,6 +85,7 @@ export const PLUGIN_COMPAT_RECORDS = [ introduced: "2026-04-24", deprecated: "2026-04-24", warningStarts: "2026-04-24", + removeAfter: "2026-07-01", replacement: "`setup.providers[].envVars` and `providerAuthChoices`", docsPath: "/plugins/manifest", surfaces: ["openclaw.plugin.json providerAuthEnvVars", "provider setup"], @@ -96,6 +99,7 @@ export const PLUGIN_COMPAT_RECORDS = [ introduced: "2026-04-24", deprecated: "2026-04-24", warningStarts: "2026-04-24", + removeAfter: "2026-07-01", replacement: "`channelConfigs..schema` and setup descriptors", docsPath: "/plugins/manifest", surfaces: ["openclaw.plugin.json channelEnvVars", "channel setup"], @@ -105,6 +109,18 @@ export const PLUGIN_COMPAT_RECORDS = [ "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", @@ -167,11 +183,12 @@ export const PLUGIN_COMPAT_RECORDS = [ introduced: "2026-04-24", deprecated: "2026-04-25", warningStarts: "2026-04-25", + removeAfter: "2026-08-01", replacement: "`agentRuntime` config naming", docsPath: "/plugins/sdk-agent-harness", surfaces: ["agents.defaults.embeddedHarness", "model/provider runtime selection"], diagnostics: ["agent runtime config compatibility"], - tests: ["src/agents/config.test.ts", "src/agents/runtime-selection.test.ts"], + tests: ["src/commands/doctor/shared/legacy-config-migrate.test.ts"], }, { code: "agent-harness-sdk-alias", @@ -180,6 +197,7 @@ export const PLUGIN_COMPAT_RECORDS = [ introduced: "2026-04-24", deprecated: "2026-04-25", warningStarts: "2026-04-25", + removeAfter: "2026-08-01", replacement: "`openclaw/plugin-sdk/agent-runtime`", docsPath: "/plugins/sdk-agent-harness", surfaces: ["openclaw/plugin-sdk/agent-harness", "openclaw/plugin-sdk/agent-harness-runtime"], @@ -193,6 +211,7 @@ export const PLUGIN_COMPAT_RECORDS = [ introduced: "2026-04-24", deprecated: "2026-04-25", warningStarts: "2026-04-25", + removeAfter: "2026-08-01", replacement: "`agentRuntime` ids and policy metadata", docsPath: "/plugins/sdk-agent-harness", surfaces: ["manifest/catalog execution policy", "runtime selection"], @@ -217,12 +236,131 @@ export const PLUGIN_COMPAT_RECORDS = [ introduced: "2026-04-25", deprecated: "2026-04-25", warningStarts: "2026-04-25", + removeAfter: "2026-07-15", 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-15", + 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-activate-entrypoint-alias", + status: "deprecated", + owner: "sdk", + introduced: "2026-04-24", + deprecated: "2026-04-26", + warningStarts: "2026-04-26", + removeAfter: "2026-08-01", + 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-08-01", + 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-08-01", + 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-08-01", + replacement: + "focused channel SDK subpaths, especially `openclaw/plugin-sdk/channel-runtime-context`", + docsPath: "/plugins/sdk-migration", + surfaces: ["openclaw/plugin-sdk/channel-runtime"], + 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-08-01", + 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-08-01", + 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"], + }, ] as const satisfies readonly PluginCompatRecord[]; export type PluginCompatCode = (typeof PLUGIN_COMPAT_RECORDS)[number]["code"]; diff --git a/src/plugins/installed-plugin-index.test.ts b/src/plugins/installed-plugin-index.test.ts index 14d06879758..119bc8bea3c 100644 --- a/src/plugins/installed-plugin-index.test.ts +++ b/src/plugins/installed-plugin-index.test.ts @@ -127,6 +127,7 @@ function createRichPluginFixture(params: { packageVersion?: string } = {}) { "demo-chat": ["DEMO_CHAT_TOKEN"], }, activation: { + onAgentHarnesses: ["codex"], onProviders: ["demo"], onChannels: ["demo-chat"], }, @@ -205,6 +206,7 @@ describe("installed plugin index", () => { }, }, compat: [ + "activation-agent-harness-hint", "activation-channel-hint", "activation-provider-hint", "channel-env-vars", diff --git a/src/plugins/installed-plugin-index.ts b/src/plugins/installed-plugin-index.ts index a040375b35e..4c280e8a9cb 100644 --- a/src/plugins/installed-plugin-index.ts +++ b/src/plugins/installed-plugin-index.ts @@ -223,6 +223,9 @@ function collectCompatCodes(record: PluginManifestRecord): readonly PluginCompat 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"); }