diff --git a/CHANGELOG.md b/CHANGELOG.md index 72db989413a..cbdcad54882 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Plugins/compat: add missing dated compatibility records for legacy extension-api, memory registration, provider hook/type aliases, runtime aliases, channel SDK helpers, and approval/test utility shims. Thanks @vincentkoc. - Plugins/CLI: make plugin install and uninstall config writes conflict-aware, clear stale denylist entries on explicit reinstall/removal, and delete managed plugin files only after config/index commit succeeds. Thanks @codex. - Plugins: fail `plugins update` when tracked plugin or hook updates error, keep bundled runtime-dependency repair behind restrictive allowlists, and reject package installs with unloadable extension entries. Thanks @codex. - Gateway/chat: keep duplicate attachment-backed `chat.send` retries with the same idempotency key on the documented in-flight path so aborts still target the real active run. Fixes #70139. Thanks @Feelw00. diff --git a/docs/plugins/compatibility.md b/docs/plugins/compatibility.md index 7e6a641f54a..804216d7c1a 100644 --- a/docs/plugins/compatibility.md +++ b/docs/plugins/compatibility.md @@ -84,11 +84,20 @@ Current compatibility records include: - 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 +- legacy SDK aliases such as `openclaw/extension-api`, + `openclaw/plugin-sdk/channel-runtime`, `openclaw/plugin-sdk/command-auth` + status builders, `openclaw/plugin-sdk/test-utils`, and the `ClawdbotConfig` + type alias - bundled plugin allowlist and enablement behavior - legacy provider/channel env-var manifest metadata +- legacy provider plugin hooks and type aliases while providers move to + explicit catalog, auth, thinking, replay, and transport hooks +- legacy runtime aliases such as `api.runtime.taskFlow`, + `api.runtime.subagent.getSession`, and `api.runtime.stt` +- legacy memory-plugin split registration while memory plugins move to + `registerMemoryCapability` +- legacy channel SDK helpers for native message schemas, mention gating, + inbound envelope formatting, and approval capability nesting - activation hints that are being replaced by manifest contribution ownership - `setup-api` runtime fallback while setup descriptors move to cold `setup.requiresRuntime: false` metadata diff --git a/src/plugins/compat/registry.test.ts b/src/plugins/compat/registry.test.ts index b308f0491a4..2aa2aa95254 100644 --- a/src/plugins/compat/registry.test.ts +++ b/src/plugins/compat/registry.test.ts @@ -9,6 +9,89 @@ import { const datePattern = /^\d{4}-\d{2}-\d{2}$/u; +const knownDeprecatedSurfaceMarkers = [ + { + code: "legacy-extension-api-import", + file: "src/extensionAPI.ts", + marker: "openclaw/extension-api is deprecated", + }, + { + code: "memory-split-registration", + file: "src/plugins/memory-state.ts", + marker: "registerMemoryPromptSection", + }, + { + code: "provider-static-capabilities-bag", + file: "src/plugins/types.ts", + marker: "Legacy static provider capability bag", + }, + { + code: "provider-discovery-type-aliases", + file: "src/plugins/types.ts", + marker: "ProviderPluginDiscovery = ProviderPluginCatalog", + }, + { + code: "provider-thinking-policy-hooks", + file: "src/plugins/types.ts", + marker: "Prefer `resolveThinkingProfile`", + }, + { + code: "provider-external-oauth-profiles-hook", + file: "src/plugins/types.ts", + marker: "resolveExternalOAuthProfiles", + }, + { + code: "agent-tool-result-harness-alias", + file: "src/plugins/agent-tool-result-middleware-types.ts", + marker: "AgentToolResultMiddlewareHarness", + }, + { + code: "runtime-taskflow-legacy-alias", + file: "src/plugins/runtime/types-core.ts", + marker: "taskFlow", + }, + { + code: "runtime-subagent-get-session-alias", + file: "src/plugins/runtime/types.ts", + marker: "getSessionMessages", + }, + { + code: "runtime-stt-alias", + file: "src/plugins/runtime/types-core.ts", + marker: "stt", + }, + { + code: "runtime-inbound-envelope-alias", + file: "src/plugins/runtime/types-channel.ts", + marker: "formatInboundEnvelope", + }, + { + code: "channel-native-message-schema-helpers", + file: "src/plugin-sdk/channel-actions.ts", + marker: "createMessageToolButtonsSchema", + }, + { + code: "channel-mention-gating-legacy-helpers", + file: "src/plugin-sdk/channel-inbound.ts", + marker: "resolveMentionGatingWithBypass", + }, + { + code: "provider-web-search-core-wrapper", + file: "src/plugin-sdk/provider-web-search.ts", + marker: "createPluginBackedWebSearchProvider", + }, + { + code: "approval-capability-approvals-alias", + file: "src/plugin-sdk/approval-delivery-helpers.ts", + marker: "approvals?: Partial", + }, + { + code: "plugin-sdk-test-utils-alias", + file: "src/plugin-sdk/test-utils.ts", + marker: "Deprecated compatibility alias", + }, +] as const; + function parseDate(date: string): Date { return new Date(`${date}T00:00:00Z`); } @@ -58,4 +141,11 @@ describe("plugin compatibility registry", () => { } } }); + + it("tracks known plugin-facing deprecated surfaces", () => { + for (const surface of knownDeprecatedSurfaceMarkers) { + expect(isPluginCompatCode(surface.code), surface.code).toBe(true); + expect(fs.readFileSync(surface.file, "utf8"), surface.file).toContain(surface.marker); + } + }); }); diff --git a/src/plugins/compat/registry.ts b/src/plugins/compat/registry.ts index 3b2ea953b55..10ad91934ef 100644 --- a/src/plugins/compat/registry.ts +++ b/src/plugins/compat/registry.ts @@ -361,6 +361,267 @@ export const PLUGIN_COMPAT_RECORDS = [ 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/` 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-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.flows`", + 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: "`openclaw/plugin-sdk/testing`", + docsPath: "/plugins/sdk-migration", + surfaces: ["openclaw/plugin-sdk/test-utils"], + diagnostics: ["plugin SDK compatibility warning"], + tests: ["src/plugins/contracts/plugin-sdk-subpaths.test.ts"], + }, ] as const satisfies readonly PluginCompatRecord[]; export type PluginCompatCode = (typeof PLUGIN_COMPAT_RECORDS)[number]["code"];