From 43a73d6a31221952051dedbefd119e6c5005adc4 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 28 Apr 2026 02:31:03 +0100 Subject: [PATCH] refactor: separate bundled channel schema surface --- CHANGELOG.md | 1 + docs/plugins/architecture.md | 2 +- docs/plugins/sdk-migration.md | 3 +- docs/plugins/sdk-overview.md | 8 +-- docs/plugins/sdk-subpaths.md | 3 +- extensions/discord/config-api.ts | 2 +- extensions/googlechat/runtime-api.ts | 2 +- extensions/imessage/config-api.ts | 2 +- extensions/msteams/config-api.ts | 2 +- extensions/signal/config-api.ts | 2 +- extensions/slack/config-api.ts | 2 +- extensions/telegram/config-api.ts | 2 +- extensions/whatsapp/config-api.ts | 2 +- package.json | 4 ++ scripts/lib/plugin-sdk-entrypoints.json | 1 + .../bundled-channel-config-schema.ts | 34 ++++++++++++ .../channel-config-schema-legacy.ts | 36 +++---------- src/plugins/compat/registry.test.ts | 10 ++++ src/plugins/compat/registry.ts | 36 +++++++++++++ .../config-footprint-guardrails.test.ts | 12 +++-- ...in-sdk-package-contract-guardrails.test.ts | 53 ++++++++++++++++--- .../plugin-sdk-runtime-api-guardrails.test.ts | 2 +- test/extension-test-boundary.test.ts | 11 ++++ 23 files changed, 177 insertions(+), 55 deletions(-) create mode 100644 src/plugin-sdk/bundled-channel-config-schema.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 9be56b00d81..f42facde42b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Docs: https://docs.openclaw.ai - Gateway/runtime: reuse the current plugin metadata snapshot for provider discovery so repeated model-provider discovery avoids rebuilding plugin manifest metadata. Thanks @shakkernerd. - Gateway/startup: pass the plugin metadata snapshot from config validation into plugin bootstrap so startup reuses one manifest product instead of rebuilding plugin metadata. Thanks @shakkernerd. - Plugin SDK/testing: move core-only channel contract fixtures under the channel contract test tree and retire the old `test/helpers/channels` bridge directory so plugin tests stay on focused SDK surfaces. Thanks @vincentkoc. +- Plugin SDK: move maintained bundled channels off the deprecated `channel-config-schema-legacy` subpath, add an explicit bundled-channel schema SDK surface, and track both remaining legacy test/config compatibility barrels with dated removal windows. Thanks @vincentkoc. - Plugin SDK/testing: promote bundled plugin/provider/channel contract helpers to focused SDK test subpaths and retire the repo-only `test/helpers/plugins` TypeScript bridge. Thanks @vincentkoc. - Plugin SDK/testing: expose generic channel action, setup, status, and directory contract helpers through `plugin-sdk/channel-test-helpers` so bundled extension tests no longer import repo-only channel helper bridges. Thanks @vincentkoc. - Plugin SDK/testing: add `plugin-sdk/channel-target-testing` for shared channel target-resolution cases, document channel reaction helpers on `plugin-sdk/channel-feedback`, and keep the old `plugin-sdk/test-utils` alias as compatibility-only. Thanks @vincentkoc. diff --git a/docs/plugins/architecture.md b/docs/plugins/architecture.md index e9cb2b76853..b37738ad775 100644 --- a/docs/plugins/architecture.md +++ b/docs/plugins/architecture.md @@ -473,7 +473,7 @@ Keep capability registration public. Trim non-contract helper exports: - vendor-specific convenience helpers - setup/onboarding helpers that are implementation details -Some bundled-plugin helper subpaths still remain in the generated SDK export map for compatibility and bundled-plugin maintenance. Current examples include `plugin-sdk/feishu`, `plugin-sdk/feishu-setup`, `plugin-sdk/zalo`, `plugin-sdk/zalo-setup`, `plugin-sdk/channel-config-schema-legacy`, and several `plugin-sdk/matrix*` seams. Treat those as deprecated reserved exports, not as the recommended SDK pattern for new third-party plugins. +Some bundled-plugin helper subpaths still remain in the generated SDK export map for compatibility and bundled-plugin maintenance. Current examples include `plugin-sdk/feishu`, `plugin-sdk/feishu-setup`, `plugin-sdk/zalo`, `plugin-sdk/zalo-setup`, `plugin-sdk/bundled-channel-config-schema`, `plugin-sdk/channel-config-schema-legacy`, and several `plugin-sdk/matrix*` seams. Treat those as deprecated or reserved exports, not as the recommended SDK pattern for new third-party plugins. ## Internals and reference diff --git a/docs/plugins/sdk-migration.md b/docs/plugins/sdk-migration.md index 7094e865998..32749a857a5 100644 --- a/docs/plugins/sdk-migration.md +++ b/docs/plugins/sdk-migration.md @@ -377,7 +377,8 @@ releases. | `plugin-sdk/channel-reply-pipeline` | Reply prefix + typing wiring | `createChannelReplyPipeline` | | `plugin-sdk/channel-config-helpers` | Config adapter factories | `createHybridChannelConfigAdapter` | | `plugin-sdk/channel-config-schema` | Config schema builders | Shared channel config schema primitives and the generic builder only | - | `plugin-sdk/channel-config-schema-legacy` | Deprecated bundled config schemas | Bundled compatibility only; new plugins must define plugin-local schemas | + | `plugin-sdk/bundled-channel-config-schema` | Bundled config schemas | OpenClaw-maintained bundled plugins only; new plugins must define plugin-local schemas | + | `plugin-sdk/channel-config-schema-legacy` | Deprecated bundled config schemas | Compatibility alias only; use `plugin-sdk/bundled-channel-config-schema` for maintained bundled plugins | | `plugin-sdk/telegram-command-config` | Telegram command config helpers | Command-name normalization, description trimming, duplicate/conflict validation | | `plugin-sdk/channel-policy` | Group/DM policy resolution | `resolveChannelGroupRequireMention` | | `plugin-sdk/channel-lifecycle` | Account status and draft stream lifecycle helpers | `createAccountStatusSink`, draft preview finalization helpers | diff --git a/docs/plugins/sdk-overview.md b/docs/plugins/sdk-overview.md index 714d6f268fd..ca14b904158 100644 --- a/docs/plugins/sdk-overview.md +++ b/docs/plugins/sdk-overview.md @@ -37,9 +37,11 @@ the broader umbrella surface and shared helpers such as For channel config, publish the channel-owned JSON Schema through `openclaw.plugin.json#channelConfigs`. The `plugin-sdk/channel-config-schema` -subpath is for shared schema primitives and the generic builder. Deprecated -bundled-channel schema exports live on `plugin-sdk/channel-config-schema-legacy` -for bundled compatibility only; they are not a pattern for new plugins. +subpath is for shared schema primitives and the generic builder. OpenClaw's +bundled plugins use `plugin-sdk/bundled-channel-config-schema` for retained +bundled-channel schemas. Deprecated compatibility exports remain on +`plugin-sdk/channel-config-schema-legacy`; neither bundled schema subpath is a +pattern for new plugins. Do not import provider- or channel-branded convenience seams (for example diff --git a/docs/plugins/sdk-subpaths.md b/docs/plugins/sdk-subpaths.md index 944cfaa6582..de7b0767705 100644 --- a/docs/plugins/sdk-subpaths.md +++ b/docs/plugins/sdk-subpaths.md @@ -53,7 +53,8 @@ For the plugin authoring guide, see [Plugin SDK overview](/plugins/sdk-overview) | `plugin-sdk/channel-reply-pipeline` | `createChannelReplyPipeline` | | `plugin-sdk/channel-config-helpers` | `createHybridChannelConfigAdapter` | | `plugin-sdk/channel-config-schema` | Shared channel config schema primitives and generic builder | - | `plugin-sdk/channel-config-schema-legacy` | Deprecated bundled-channel config schemas for bundled compatibility only | + | `plugin-sdk/bundled-channel-config-schema` | Bundled OpenClaw channel config schemas for maintained bundled plugins only | + | `plugin-sdk/channel-config-schema-legacy` | Deprecated compatibility alias for bundled-channel config schemas | | `plugin-sdk/telegram-command-config` | Telegram custom-command normalization/validation helpers with bundled-contract fallback | | `plugin-sdk/command-gating` | Narrow command authorization gate helpers | | `plugin-sdk/channel-policy` | `resolveChannelGroupRequireMention` | diff --git a/extensions/discord/config-api.ts b/extensions/discord/config-api.ts index 67bd2a9e7c2..721c804dea8 100644 --- a/extensions/discord/config-api.ts +++ b/extensions/discord/config-api.ts @@ -1,4 +1,4 @@ export { buildChannelConfigSchema, DiscordConfigSchema, -} from "openclaw/plugin-sdk/channel-config-schema-legacy"; +} from "openclaw/plugin-sdk/bundled-channel-config-schema"; diff --git a/extensions/googlechat/runtime-api.ts b/extensions/googlechat/runtime-api.ts index 5ab7cef0eb8..30c389b766b 100644 --- a/extensions/googlechat/runtime-api.ts +++ b/extensions/googlechat/runtime-api.ts @@ -30,7 +30,7 @@ export { export { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status"; export { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking"; export type { OpenClawConfig } from "openclaw/plugin-sdk/config-types"; -export { GoogleChatConfigSchema } from "openclaw/plugin-sdk/channel-config-schema-legacy"; +export { GoogleChatConfigSchema } from "openclaw/plugin-sdk/bundled-channel-config-schema"; export { GROUP_POLICY_BLOCKED_LABEL, resolveAllowlistProviderRuntimeGroupPolicy, diff --git a/extensions/imessage/config-api.ts b/extensions/imessage/config-api.ts index 0fae904952c..0ba74cc2592 100644 --- a/extensions/imessage/config-api.ts +++ b/extensions/imessage/config-api.ts @@ -1,4 +1,4 @@ export { buildChannelConfigSchema, IMessageConfigSchema, -} from "openclaw/plugin-sdk/channel-config-schema-legacy"; +} from "openclaw/plugin-sdk/bundled-channel-config-schema"; diff --git a/extensions/msteams/config-api.ts b/extensions/msteams/config-api.ts index 618418db768..277ec5b4938 100644 --- a/extensions/msteams/config-api.ts +++ b/extensions/msteams/config-api.ts @@ -1,4 +1,4 @@ export { buildChannelConfigSchema, MSTeamsConfigSchema, -} from "openclaw/plugin-sdk/channel-config-schema-legacy"; +} from "openclaw/plugin-sdk/bundled-channel-config-schema"; diff --git a/extensions/signal/config-api.ts b/extensions/signal/config-api.ts index cff779ae393..1fe3b520cab 100644 --- a/extensions/signal/config-api.ts +++ b/extensions/signal/config-api.ts @@ -1,4 +1,4 @@ export { buildChannelConfigSchema, SignalConfigSchema, -} from "openclaw/plugin-sdk/channel-config-schema-legacy"; +} from "openclaw/plugin-sdk/bundled-channel-config-schema"; diff --git a/extensions/slack/config-api.ts b/extensions/slack/config-api.ts index a7662885d05..99c4efe904c 100644 --- a/extensions/slack/config-api.ts +++ b/extensions/slack/config-api.ts @@ -1,4 +1,4 @@ export { buildChannelConfigSchema, SlackConfigSchema, -} from "openclaw/plugin-sdk/channel-config-schema-legacy"; +} from "openclaw/plugin-sdk/bundled-channel-config-schema"; diff --git a/extensions/telegram/config-api.ts b/extensions/telegram/config-api.ts index bba1c3e8036..8e91a33f495 100644 --- a/extensions/telegram/config-api.ts +++ b/extensions/telegram/config-api.ts @@ -1,7 +1,7 @@ export { buildChannelConfigSchema, TelegramConfigSchema, -} from "openclaw/plugin-sdk/channel-config-schema-legacy"; +} from "openclaw/plugin-sdk/bundled-channel-config-schema"; export { normalizeTelegramCommandDescription, normalizeTelegramCommandName, diff --git a/extensions/whatsapp/config-api.ts b/extensions/whatsapp/config-api.ts index fa8a52e7fc6..c41d5c08584 100644 --- a/extensions/whatsapp/config-api.ts +++ b/extensions/whatsapp/config-api.ts @@ -1,4 +1,4 @@ export { buildChannelConfigSchema, WhatsAppConfigSchema, -} from "openclaw/plugin-sdk/channel-config-schema-legacy"; +} from "openclaw/plugin-sdk/bundled-channel-config-schema"; diff --git a/package.json b/package.json index 0e2d62594b2..c2c5f5ff6d3 100644 --- a/package.json +++ b/package.json @@ -746,6 +746,10 @@ "types": "./dist/plugin-sdk/channel-config-schema.d.ts", "default": "./dist/plugin-sdk/channel-config-schema.js" }, + "./plugin-sdk/bundled-channel-config-schema": { + "types": "./dist/plugin-sdk/bundled-channel-config-schema.d.ts", + "default": "./dist/plugin-sdk/bundled-channel-config-schema.js" + }, "./plugin-sdk/channel-config-schema-legacy": { "types": "./dist/plugin-sdk/channel-config-schema-legacy.d.ts", "default": "./dist/plugin-sdk/channel-config-schema-legacy.js" diff --git a/scripts/lib/plugin-sdk-entrypoints.json b/scripts/lib/plugin-sdk-entrypoints.json index a1f57d3efdc..6be7821d2cb 100644 --- a/scripts/lib/plugin-sdk-entrypoints.json +++ b/scripts/lib/plugin-sdk-entrypoints.json @@ -170,6 +170,7 @@ "channel-config-writes", "channel-config-primitives", "channel-config-schema", + "bundled-channel-config-schema", "channel-config-schema-legacy", "channel-actions", "channel-plugin-common", diff --git a/src/plugin-sdk/bundled-channel-config-schema.ts b/src/plugin-sdk/bundled-channel-config-schema.ts new file mode 100644 index 00000000000..2e6a86077e2 --- /dev/null +++ b/src/plugin-sdk/bundled-channel-config-schema.ts @@ -0,0 +1,34 @@ +/** + * Bundled-channel config schemas for OpenClaw-maintained plugins. + * + * Third-party plugins should define plugin-local schemas and import primitives + * from openclaw/plugin-sdk/channel-config-schema instead of depending on these + * bundled channel schemas. + */ +export { + AllowFromListSchema, + buildChannelConfigSchema, + buildCatchallMultiAccountChannelSchema, + buildNestedDmConfigSchema, +} from "../channels/plugins/config-schema.js"; +export { + BlockStreamingCoalesceSchema, + ContextVisibilityModeSchema, + DmConfigSchema, + DmPolicySchema, + GroupPolicySchema, + MarkdownConfigSchema, + ReplyRuntimeConfigSchemaShape, + requireOpenAllowFrom, +} from "../config/zod-schema.core.js"; +export { ToolPolicySchema } from "../config/zod-schema.agent-runtime.js"; +export { + DiscordConfigSchema, + GoogleChatConfigSchema, + IMessageConfigSchema, + MSTeamsConfigSchema, + SignalConfigSchema, + SlackConfigSchema, + TelegramConfigSchema, +} from "../config/zod-schema.providers-core.js"; +export { WhatsAppConfigSchema } from "../config/zod-schema.providers-whatsapp.js"; diff --git a/src/plugin-sdk/channel-config-schema-legacy.ts b/src/plugin-sdk/channel-config-schema-legacy.ts index 3fe6b5ff304..890e4e491f7 100644 --- a/src/plugin-sdk/channel-config-schema-legacy.ts +++ b/src/plugin-sdk/channel-config-schema-legacy.ts @@ -1,34 +1,10 @@ /** * Deprecated bundled-channel compatibility surface. * - * New plugins should define plugin-local schemas and import primitives from - * openclaw/plugin-sdk/channel-config-schema instead of depending on these - * bundled channel schemas. + * OpenClaw-maintained bundled plugins should import + * openclaw/plugin-sdk/bundled-channel-config-schema. Third-party plugins should + * define plugin-local schemas and import primitives from + * openclaw/plugin-sdk/channel-config-schema instead of depending on bundled + * channel schemas. */ -export { - AllowFromListSchema, - buildChannelConfigSchema, - buildCatchallMultiAccountChannelSchema, - buildNestedDmConfigSchema, -} from "../channels/plugins/config-schema.js"; -export { - BlockStreamingCoalesceSchema, - ContextVisibilityModeSchema, - DmConfigSchema, - DmPolicySchema, - GroupPolicySchema, - MarkdownConfigSchema, - ReplyRuntimeConfigSchemaShape, - requireOpenAllowFrom, -} from "../config/zod-schema.core.js"; -export { ToolPolicySchema } from "../config/zod-schema.agent-runtime.js"; -export { - DiscordConfigSchema, - GoogleChatConfigSchema, - IMessageConfigSchema, - MSTeamsConfigSchema, - SignalConfigSchema, - SlackConfigSchema, - TelegramConfigSchema, -} from "../config/zod-schema.providers-core.js"; -export { WhatsAppConfigSchema } from "../config/zod-schema.providers-whatsapp.js"; +export * from "./bundled-channel-config-schema.js"; diff --git a/src/plugins/compat/registry.test.ts b/src/plugins/compat/registry.test.ts index 99390e97ec3..2977020732c 100644 --- a/src/plugins/compat/registry.test.ts +++ b/src/plugins/compat/registry.test.ts @@ -120,6 +120,16 @@ const knownDeprecatedSurfaceMarkers = [ file: "src/commands/doctor/shared/legacy-x-search-migrate.ts", marker: "tools.web.x_search", }, + { + code: "bundled-channel-config-schema-legacy", + file: "src/plugin-sdk/channel-config-schema-legacy.ts", + marker: "Deprecated bundled-channel compatibility surface", + }, + { + code: "plugin-sdk-testing-barrel", + file: "src/plugin-sdk/testing.ts", + marker: "Broad legacy compatibility barrel for older plugin tests", + }, { code: "channel-route-key-aliases", file: "src/plugin-sdk/channel-route.ts", diff --git a/src/plugins/compat/registry.ts b/src/plugins/compat/registry.ts index 4ef516615ac..85899f34e9f 100644 --- a/src/plugins/compat/registry.ts +++ b/src/plugins/compat/registry.ts @@ -51,6 +51,42 @@ export const PLUGIN_COMPAT_RECORDS = [ "src/plugins/contracts/plugin-sdk-subpaths.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: [ + "scripts/check-no-extension-test-core-imports.ts", + "test/extension-test-boundary.test.ts", + ], + }, { code: "channel-route-key-aliases", status: "deprecated", diff --git a/src/plugins/contracts/config-footprint-guardrails.test.ts b/src/plugins/contracts/config-footprint-guardrails.test.ts index f70849e5b95..373ba688ab4 100644 --- a/src/plugins/contracts/config-footprint-guardrails.test.ts +++ b/src/plugins/contracts/config-footprint-guardrails.test.ts @@ -164,12 +164,13 @@ describe("config footprint guardrails", () => { it("keeps bundled channel schemas out of the generic channel config SDK surface", () => { const source = readSource("src/plugin-sdk/channel-config-schema.ts"); + const bundledSource = readSource("src/plugin-sdk/bundled-channel-config-schema.ts"); const legacySource = readSource("src/plugin-sdk/channel-config-schema-legacy.ts"); - const legacySection = legacySource.slice( - legacySource.indexOf("Deprecated bundled-channel compatibility surface"), + const bundledSection = bundledSource.slice( + bundledSource.indexOf("Bundled-channel config schemas"), ); const bundledSchemaExportBlocks = Array.from( - legacySection.matchAll( + bundledSection.matchAll( /export \{(?[^}]*)\} from "\.\.\/config\/zod-schema\.providers-(?:core|whatsapp)\.js";/g, ), ) @@ -196,7 +197,10 @@ describe("config footprint guardrails", () => { for (const schemaName of exportedSchemaNames) { expect(source).not.toContain(schemaName); } + expect(bundledSource).toContain("Bundled-channel config schemas"); + expect(bundledSource).toContain("openclaw/plugin-sdk/channel-config-schema"); expect(legacySource).toContain("Deprecated bundled-channel compatibility surface"); - expect(legacySource).toContain("openclaw/plugin-sdk/channel-config-schema"); + expect(legacySource).toContain("openclaw/plugin-sdk/bundled-channel-config-schema"); + expect(legacySource).toContain('export * from "./bundled-channel-config-schema.js";'); }); }); diff --git a/src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts b/src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts index 0ea2faafcfc..e209195f9c7 100644 --- a/src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts +++ b/src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts @@ -196,17 +196,25 @@ function collectExtensionFiles(dir: string): string[] { return files; } +function isExtensionTestOrSupportPath(repoRelativePath: string): boolean { + return ( + /(?:^|\/)(?:__tests__|tests|test-support)(?:\/|$)/.test(repoRelativePath) || + /(?:^|\/)test-support\.[cm]?tsx?$/.test(repoRelativePath) || + /(?:^|\/)test-helpers\.[cm]?tsx?$/.test(repoRelativePath) || + /(?:^|\/)test-harness\.[cm]?tsx?$/.test(repoRelativePath) || + /\.test-support\.[cm]?tsx?$/.test(repoRelativePath) || + /\.test-helpers\.[cm]?tsx?$/.test(repoRelativePath) || + /\.test-harness\.[cm]?tsx?$/.test(repoRelativePath) || + /\.test\.[cm]?tsx?$/.test(repoRelativePath) + ); +} + function collectExtensionCoreImportLeaks(): Array<{ file: string; specifier: string }> { const leaks: Array<{ file: string; specifier: string }> = []; const importPattern = /\b(?:import|export)\b[\s\S]*?\bfrom\s*["']((?:\.\.\/)+src\/[^"']+)["']/g; for (const file of collectExtensionFiles(resolve(REPO_ROOT, "extensions"))) { const repoRelativePath = relative(REPO_ROOT, file).replaceAll("\\", "/"); - if ( - /(?:^|\/)(?:__tests__|tests|test-support)(?:\/|$)/.test(repoRelativePath) || - /(?:^|\/)test-support\.[cm]?tsx?$/.test(repoRelativePath) || - /\.test-support\.[cm]?tsx?$/.test(repoRelativePath) || - /\.test\.[cm]?tsx?$/.test(repoRelativePath) - ) { + if (isExtensionTestOrSupportPath(repoRelativePath)) { continue; } const extensionRootMatch = /^(.*?\/extensions\/[^/]+)/.exec(file.replaceAll("\\", "/")); @@ -230,6 +238,35 @@ function collectExtensionCoreImportLeaks(): Array<{ file: string; specifier: str return leaks; } +function collectExtensionTestHelperImportLeaks(): Array<{ file: string; specifier: string }> { + const leaks: Array<{ file: string; specifier: string }> = []; + const importPatterns = [ + /\b(?:import|export)\b[\s\S]*?\bfrom\s*["']((?:\.\.\/)+test\/helpers\/[^"']+)["']/g, + /\bimport\s*\(\s*["']((?:\.\.\/)+test\/helpers\/[^"']+)["']\s*\)/g, + /\bvi\.(?:mock|doMock)\s*\(\s*["']((?:\.\.\/)+test\/helpers\/[^"']+)["']/g, + ]; + for (const file of collectExtensionFiles(resolve(REPO_ROOT, "extensions"))) { + const repoRelativePath = relative(REPO_ROOT, file).replaceAll("\\", "/"); + if (isExtensionTestOrSupportPath(repoRelativePath)) { + continue; + } + const source = readFileSync(file, "utf8"); + for (const importPattern of importPatterns) { + for (const match of source.matchAll(importPattern)) { + const specifier = match[1]; + if (!specifier) { + continue; + } + leaks.push({ + file: repoRelativePath, + specifier, + }); + } + } + } + return leaks; +} + function collectCrossOwnerReservedSdkImports(): Array<{ file: string; specifier: string; @@ -389,6 +426,10 @@ describe("plugin-sdk package contract guardrails", () => { expect(collectExtensionCoreImportLeaks()).toEqual([]); }); + it("keeps extension production sources off repo test helpers", () => { + expect(collectExtensionTestHelperImportLeaks()).toEqual([]); + }); + it("keeps reserved SDK compatibility subpaths inside their owning bundled plugins", () => { expect(collectCrossOwnerReservedSdkImports()).toEqual([]); }); diff --git a/src/plugins/contracts/plugin-sdk-runtime-api-guardrails.test.ts b/src/plugins/contracts/plugin-sdk-runtime-api-guardrails.test.ts index cbe932b4d0e..a667f41b6fb 100644 --- a/src/plugins/contracts/plugin-sdk-runtime-api-guardrails.test.ts +++ b/src/plugins/contracts/plugin-sdk-runtime-api-guardrails.test.ts @@ -66,7 +66,7 @@ const RUNTIME_API_EXPORT_GUARDS: Record = { 'export { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";', 'export { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";', 'export type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";', - 'export { GoogleChatConfigSchema } from "openclaw/plugin-sdk/channel-config-schema-legacy";', + 'export { GoogleChatConfigSchema } from "openclaw/plugin-sdk/bundled-channel-config-schema";', 'export { GROUP_POLICY_BLOCKED_LABEL, resolveAllowlistProviderRuntimeGroupPolicy, resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";', 'export { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";', 'export { fetchRemoteMedia, resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/media-runtime";', diff --git a/test/extension-test-boundary.test.ts b/test/extension-test-boundary.test.ts index d2a290203da..71dc5679cdb 100644 --- a/test/extension-test-boundary.test.ts +++ b/test/extension-test-boundary.test.ts @@ -227,4 +227,15 @@ describe("non-extension test boundaries", () => { expect(offenders).toEqual([]); }); + + it("keeps bundled extension sources off deprecated channel config schema aliases", () => { + const files = walkCode(path.join(repoRoot, "extensions")); + + const offenders = files.filter((file) => { + const source = fs.readFileSync(path.join(repoRoot, file), "utf8"); + return source.includes("openclaw/plugin-sdk/channel-config-schema-legacy"); + }); + + expect(offenders).toEqual([]); + }); });