diff --git a/src/acp/persistent-bindings.test.ts b/src/acp/persistent-bindings.test.ts index 2be5eabe372..a98d319cbdd 100644 --- a/src/acp/persistent-bindings.test.ts +++ b/src/acp/persistent-bindings.test.ts @@ -1,11 +1,11 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { parseFeishuConversationId } from "../../extensions/feishu/src/conversation-id.js"; -import { parseTelegramTopicConversation } from "../../extensions/telegram/runtime-api.js"; import { resolveAgentWorkspaceDir } from "../agents/agent-scope.js"; import type { ChannelConfiguredBindingProvider, ChannelPlugin } from "../channels/plugins/types.js"; import type { OpenClawConfig } from "../config/config.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js"; +import { parseTelegramTopicConversation } from "./conversation-id.js"; import * as persistentBindingsResolveModule from "./persistent-bindings.resolve.js"; import { buildConfiguredAcpSessionKey } from "./persistent-bindings.types.js"; const managerMocks = vi.hoisted(() => ({ diff --git a/src/agents/pi-embedded-subscribe.tools.extract.test.ts b/src/agents/pi-embedded-subscribe.tools.extract.test.ts index 044edc93a6d..0fac3c405d8 100644 --- a/src/agents/pi-embedded-subscribe.tools.extract.test.ts +++ b/src/agents/pi-embedded-subscribe.tools.extract.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it } from "vitest"; -import { normalizeTelegramMessagingTarget } from "../../extensions/telegram/api.js"; +import { normalizeTelegramMessagingTarget } from "../../extensions/telegram/src/normalize.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js"; import { extractMessagingToolSend } from "./pi-embedded-subscribe.tools.js"; diff --git a/src/config/doc-baseline.integration.test.ts b/src/config/doc-baseline.integration.test.ts index c705b76da54..04765a9bb81 100644 --- a/src/config/doc-baseline.integration.test.ts +++ b/src/config/doc-baseline.integration.test.ts @@ -3,24 +3,33 @@ import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { - buildConfigDocBaseline, + type ConfigDocBaseline, renderConfigDocBaselineStatefile, writeConfigDocBaselineStatefile, } from "./doc-baseline.js"; describe("config doc baseline integration", () => { const tempRoots: string[] = []; - let sharedBaselinePromise: Promise>> | null = - null; + const generatedBaselineJsonPath = path.resolve( + process.cwd(), + "docs/.generated/config-baseline.json", + ); + const generatedBaselineJsonlPath = path.resolve( + process.cwd(), + "docs/.generated/config-baseline.jsonl", + ); + let sharedBaselinePromise: Promise | null = null; let sharedRenderedPromise: Promise< Awaited> > | null = null; - let sharedByPathPromise: Promise< - Map>["entries"][number]> - > | null = null; + let sharedGeneratedJsonPromise: Promise | null = null; + let sharedGeneratedJsonlPromise: Promise | null = null; + let sharedByPathPromise: Promise> | null = null; function getSharedBaseline() { - sharedBaselinePromise ??= buildConfigDocBaseline(); + sharedBaselinePromise ??= fs + .readFile(generatedBaselineJsonPath, "utf8") + .then((raw) => JSON.parse(raw) as ConfigDocBaseline); return sharedBaselinePromise; } @@ -29,6 +38,16 @@ describe("config doc baseline integration", () => { return sharedRenderedPromise; } + function getGeneratedJson() { + sharedGeneratedJsonPromise ??= fs.readFile(generatedBaselineJsonPath, "utf8"); + return sharedGeneratedJsonPromise; + } + + function getGeneratedJsonl() { + sharedGeneratedJsonlPromise ??= fs.readFile(generatedBaselineJsonlPath, "utf8"); + return sharedGeneratedJsonlPromise; + } + function getSharedByPath() { sharedByPathPromise ??= getSharedBaseline().then( (baseline) => new Map(baseline.entries.map((entry) => [entry.path, entry])), @@ -45,13 +64,25 @@ describe("config doc baseline integration", () => { }); it("is deterministic across repeated runs", async () => { - const first = await getSharedRendered(); - const second = await renderConfigDocBaselineStatefile(); + const baseline = await getSharedBaseline(); + const first = await renderConfigDocBaselineStatefile(baseline); + const second = await renderConfigDocBaselineStatefile(baseline); expect(second.json).toBe(first.json); expect(second.jsonl).toBe(first.jsonl); }); + it("matches the checked-in generated baseline artifacts", async () => { + const [rendered, generatedJson, generatedJsonl] = await Promise.all([ + getSharedRendered(), + getGeneratedJson(), + getGeneratedJsonl(), + ]); + + expect(rendered.json).toBe(generatedJson); + expect(rendered.jsonl).toBe(generatedJsonl); + }); + it("includes core, channel, and plugin config metadata", async () => { const byPath = await getSharedByPath(); diff --git a/src/infra/outbound/targets.shared-test.ts b/src/infra/outbound/targets.shared-test.ts index 6aaec67d4f5..7f3b2649e11 100644 --- a/src/infra/outbound/targets.shared-test.ts +++ b/src/infra/outbound/targets.shared-test.ts @@ -1,23 +1,70 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { parseTelegramTarget } from "../../../extensions/telegram/api.js"; -import { telegramOutbound, whatsappOutbound } from "../../../test/channel-outbounds.js"; +import type { ChannelOutboundAdapter } from "../../channels/plugins/types.js"; import type { OpenClawConfig } from "../../config/config.js"; import { setActivePluginRegistry } from "../../plugins/runtime.js"; import { createOutboundTestPlugin, createTestRegistry } from "../../test-utils/channel-plugins.js"; import { isWhatsAppGroupJid, normalizeWhatsAppTarget } from "../../whatsapp/normalize.js"; import { resolveOutboundTarget } from "./targets.js"; -const telegramMessaging = { - parseExplicitTarget: ({ raw }: { raw: string }) => { - const target = parseTelegramTarget(raw); - return { - to: target.chatId, - threadId: target.messageThreadId, - chatType: target.chatType === "unknown" ? undefined : target.chatType, - }; - }, +type TelegramTargetParts = { + chatId: string; + messageThreadId?: number; + chatType: "direct" | "group" | "unknown"; }; +function parseTelegramTestTarget(raw: string): TelegramTargetParts { + const trimmed = raw + .trim() + .replace(/^(telegram|tg):/i, "") + .trim(); + const topicMatch = /^(.+?):topic:(\d+)$/u.exec(trimmed); + if (topicMatch) { + const chatId = topicMatch[1]; + return { + chatId, + messageThreadId: Number.parseInt(topicMatch[2], 10), + chatType: chatId.startsWith("-") ? "group" : "direct", + }; + } + + const threadMatch = /^(.+):(\d+)$/u.exec(trimmed); + if (threadMatch) { + const chatId = threadMatch[1]; + return { + chatId, + messageThreadId: Number.parseInt(threadMatch[2], 10), + chatType: chatId.startsWith("-") ? "group" : "direct", + }; + } + + return { + chatId: trimmed, + chatType: trimmed.startsWith("-") ? "group" : "direct", + }; +} + +const telegramMessaging = { + parseExplicitTarget: ({ raw }: { raw: string }) => parseTelegramTestMessagingTarget(raw), +}; + +export function inferTelegramTestChatType(to: string): "direct" | "group" | undefined { + const chatType = parseTelegramTestTarget(to).chatType; + return chatType === "unknown" ? undefined : chatType; +} + +export function parseTelegramTestMessagingTarget(raw: string): { + to: string; + threadId?: number; + chatType?: "direct" | "group"; +} { + const target = parseTelegramTestTarget(raw); + return { + to: target.chatId, + threadId: target.messageThreadId, + chatType: target.chatType === "unknown" ? undefined : target.chatType, + }; +} + const whatsappMessaging = { inferTargetChatType: ({ to }: { to: string }) => { const normalized = normalizeWhatsAppTarget(to); @@ -31,6 +78,24 @@ const whatsappMessaging = { }, }; +export const telegramOutboundStub: ChannelOutboundAdapter = { + deliveryMode: "direct", +}; + +export const whatsappOutboundStub: ChannelOutboundAdapter = { + deliveryMode: "gateway", + resolveTarget: ({ to }) => { + const normalized = typeof to === "string" ? normalizeWhatsAppTarget(to) : undefined; + if (normalized) { + return { ok: true as const, to: normalized }; + } + return { + ok: false as const, + error: new Error("WhatsApp target required"), + }; + }, +}; + export function installResolveOutboundTargetPluginRegistryHooks(): void { beforeEach(() => { setActivePluginRegistry( @@ -41,7 +106,7 @@ export function installResolveOutboundTargetPluginRegistryHooks(): void { ...createOutboundTestPlugin({ id: "whatsapp", label: "WhatsApp", - outbound: whatsappOutbound, + outbound: whatsappOutboundStub, messaging: whatsappMessaging, }), config: { @@ -60,7 +125,7 @@ export function installResolveOutboundTargetPluginRegistryHooks(): void { ...createOutboundTestPlugin({ id: "telegram", label: "Telegram", - outbound: telegramOutbound, + outbound: telegramOutboundStub, messaging: telegramMessaging, }), config: { diff --git a/src/infra/outbound/targets.test.ts b/src/infra/outbound/targets.test.ts index 2ac707a804d..792fae0513d 100644 --- a/src/infra/outbound/targets.test.ts +++ b/src/infra/outbound/targets.test.ts @@ -1,6 +1,4 @@ import { beforeEach, describe, expect, it } from "vitest"; -import { parseTelegramTarget } from "../../../extensions/telegram/src/targets.js"; -import { telegramOutbound, whatsappOutbound } from "../../../test/channel-outbounds.js"; import type { ChannelOutboundAdapter } from "../../channels/plugins/types.js"; import type { OpenClawConfig } from "../../config/config.js"; import type { SessionEntry } from "../../config/sessions/types.js"; @@ -14,25 +12,19 @@ import { } from "./targets.js"; import type { SessionDeliveryTarget } from "./targets.js"; import { + inferTelegramTestChatType, installResolveOutboundTargetPluginRegistryHooks, + parseTelegramTestMessagingTarget, runResolveOutboundTargetCoreTests, + telegramOutboundStub, + whatsappOutboundStub, } from "./targets.shared-test.js"; runResolveOutboundTargetCoreTests(); const telegramMessaging = { - parseExplicitTarget: ({ raw }: { raw: string }) => { - const target = parseTelegramTarget(raw); - return { - to: target.chatId, - threadId: target.messageThreadId, - chatType: target.chatType === "unknown" ? undefined : target.chatType, - }; - }, - inferTargetChatType: ({ to }: { to: string }) => { - const target = parseTelegramTarget(to); - return target.chatType === "unknown" ? undefined : target.chatType; - }, + parseExplicitTarget: ({ raw }: { raw: string }) => parseTelegramTestMessagingTarget(raw), + inferTargetChatType: ({ to }: { to: string }) => inferTelegramTestChatType(to), }; const whatsappMessaging = { @@ -72,7 +64,7 @@ beforeEach(() => { pluginId: "telegram", plugin: createOutboundTestPlugin({ id: "telegram", - outbound: telegramOutbound, + outbound: telegramOutboundStub, messaging: telegramMessaging, }), source: "test", @@ -81,7 +73,7 @@ beforeEach(() => { pluginId: "whatsapp", plugin: createOutboundTestPlugin({ id: "whatsapp", - outbound: whatsappOutbound, + outbound: whatsappOutboundStub, messaging: whatsappMessaging, }), source: "test", @@ -155,7 +147,7 @@ describe("resolveOutboundTarget defaultTo config fallback", () => { pluginId: "telegram", plugin: createOutboundTestPlugin({ id: "telegram", - outbound: telegramOutbound, + outbound: telegramOutboundStub, messaging: telegramMessaging, }), source: "test", diff --git a/src/plugin-sdk/index.bundle.test.ts b/src/plugin-sdk/index.bundle.test.ts index 274dab67f79..1d3904e2b49 100644 --- a/src/plugin-sdk/index.bundle.test.ts +++ b/src/plugin-sdk/index.bundle.test.ts @@ -9,10 +9,7 @@ import { buildPluginSdkEntrySources, pluginSdkEntrypoints } from "./entrypoints. const require = createRequire(import.meta.url); const tsdownModuleUrl = pathToFileURL(require.resolve("tsdown")).href; const bundledRepresentativeEntrypoints = [ - "index", - "runtime", "channel-runtime", - "provider-setup", "matrix-runtime-heavy", "windows-spawn", ] as const; diff --git a/src/plugin-sdk/subpaths.test.ts b/src/plugin-sdk/subpaths.test.ts index a6c0b0ea822..039a0b7b671 100644 --- a/src/plugin-sdk/subpaths.test.ts +++ b/src/plugin-sdk/subpaths.test.ts @@ -1,6 +1,6 @@ -import * as allowFromSdk from "openclaw/plugin-sdk/allow-from"; -import * as channelActionsSdk from "openclaw/plugin-sdk/channel-actions"; -import * as channelConfigHelpersSdk from "openclaw/plugin-sdk/channel-config-helpers"; +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; import type { BaseProbeResult as ContractBaseProbeResult, BaseTokenResolution as ContractBaseTokenResolution, @@ -15,47 +15,11 @@ import type { ChannelThreadingContext as ContractChannelThreadingContext, ChannelThreadingToolContext as ContractChannelThreadingToolContext, } from "openclaw/plugin-sdk/channel-contract"; -import * as channelFeedbackSdk from "openclaw/plugin-sdk/channel-feedback"; -import * as channelInboundSdk from "openclaw/plugin-sdk/channel-inbound"; -import * as channelLifecycleSdk from "openclaw/plugin-sdk/channel-lifecycle"; -import * as channelPairingSdk from "openclaw/plugin-sdk/channel-pairing"; -import * as channelReplyPipelineSdk from "openclaw/plugin-sdk/channel-reply-pipeline"; -import * as channelRuntimeSdk from "openclaw/plugin-sdk/channel-runtime"; -import * as channelSendResultSdk from "openclaw/plugin-sdk/channel-send-result"; -import * as channelSetupSdk from "openclaw/plugin-sdk/channel-setup"; -import * as channelTargetsSdk from "openclaw/plugin-sdk/channel-targets"; -import * as commandAuthSdk from "openclaw/plugin-sdk/command-auth"; -import * as configRuntimeSdk from "openclaw/plugin-sdk/config-runtime"; -import * as conversationRuntimeSdk from "openclaw/plugin-sdk/conversation-runtime"; -import * as coreSdk from "openclaw/plugin-sdk/core"; import type { ChannelMessageActionContext as CoreChannelMessageActionContext, OpenClawPluginApi as CoreOpenClawPluginApi, PluginRuntime as CorePluginRuntime, } from "openclaw/plugin-sdk/core"; -import * as directoryRuntimeSdk from "openclaw/plugin-sdk/directory-runtime"; -import * as infraRuntimeSdk from "openclaw/plugin-sdk/infra-runtime"; -import * as lazyRuntimeSdk from "openclaw/plugin-sdk/lazy-runtime"; -import * as matrixRuntimeSharedSdk from "openclaw/plugin-sdk/matrix-runtime-shared"; -import * as mediaRuntimeSdk from "openclaw/plugin-sdk/media-runtime"; -import * as ollamaSetupSdk from "openclaw/plugin-sdk/ollama-setup"; -import * as pluginEntrySdk from "openclaw/plugin-sdk/plugin-entry"; -import * as providerAuthSdk from "openclaw/plugin-sdk/provider-auth"; -import * as providerModelsSdk from "openclaw/plugin-sdk/provider-models"; -import * as providerSetupSdk from "openclaw/plugin-sdk/provider-setup"; -import * as replyHistorySdk from "openclaw/plugin-sdk/reply-history"; -import * as replyPayloadSdk from "openclaw/plugin-sdk/reply-payload"; -import * as replyRuntimeSdk from "openclaw/plugin-sdk/reply-runtime"; -import * as routingSdk from "openclaw/plugin-sdk/routing"; -import * as runtimeSdk from "openclaw/plugin-sdk/runtime"; -import * as sandboxSdk from "openclaw/plugin-sdk/sandbox"; -import * as secretInputSdk from "openclaw/plugin-sdk/secret-input"; -import * as selfHostedProviderSetupSdk from "openclaw/plugin-sdk/self-hosted-provider-setup"; -import * as setupSdk from "openclaw/plugin-sdk/setup"; -import * as ssrfRuntimeSdk from "openclaw/plugin-sdk/ssrf-runtime"; -import * as testingSdk from "openclaw/plugin-sdk/testing"; -import * as threadBindingsRuntimeSdk from "openclaw/plugin-sdk/thread-bindings-runtime"; -import * as webhookIngressSdk from "openclaw/plugin-sdk/webhook-ingress"; import { describe, expect, expectTypeOf, it } from "vitest"; import type { ChannelMessageActionContext } from "../channels/plugins/types.js"; import type { @@ -80,17 +44,54 @@ import type { } from "./channel-plugin-common.js"; import { pluginSdkSubpaths } from "./entrypoints.js"; +const ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const PLUGIN_SDK_DIR = resolve(ROOT_DIR, "plugin-sdk"); +const sourceCache = new Map(); +const representativeRuntimeSmokeSubpaths = [ + "channel-runtime", + "conversation-runtime", + "core", + "discord", + "provider-auth", + "provider-setup", + "setup", + "webhook-ingress", +] as const; + const importPluginSdkSubpath = (specifier: string) => import(/* @vite-ignore */ specifier); -const bundledExtensionSubpathLoaders = pluginSdkSubpaths.map((id: string) => ({ - id, - load: () => importPluginSdkSubpath(`openclaw/plugin-sdk/${id}`), -})); +function readPluginSdkSource(subpath: string): string { + const file = resolve(PLUGIN_SDK_DIR, `${subpath}.ts`); + const cached = sourceCache.get(file); + if (cached !== undefined) { + return cached; + } + const text = readFileSync(file, "utf8"); + sourceCache.set(file, text); + return text; +} -const asExports = (mod: object) => mod as Record; -const accountHelpersSdk = await import("openclaw/plugin-sdk/account-helpers"); -const allowlistEditSdk = await import("openclaw/plugin-sdk/allowlist-config-edit"); -const statusHelpersSdk = await import("openclaw/plugin-sdk/status-helpers"); +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +function expectSourceMentions(subpath: string, names: readonly string[]) { + const source = readPluginSdkSource(subpath); + for (const name of names) { + expect(source, `${subpath} should mention ${name}`).toMatch( + new RegExp(`\\b${escapeRegExp(name)}\\b`, "u"), + ); + } +} + +function expectSourceOmits(subpath: string, names: readonly string[]) { + const source = readPluginSdkSource(subpath); + for (const name of names) { + expect(source, `${subpath} should not mention ${name}`).not.toMatch( + new RegExp(`\\b${escapeRegExp(name)}\\b`, "u"), + ); + } +} describe("plugin-sdk subpath exports", () => { it("keeps the curated public list free of internal implementation subpaths", () => { @@ -115,283 +116,320 @@ describe("plugin-sdk subpath exports", () => { }); it("keeps core focused on generic shared exports", () => { - expect(typeof coreSdk.emptyPluginConfigSchema).toBe("function"); - expect(typeof coreSdk.definePluginEntry).toBe("function"); - expect(typeof coreSdk.defineChannelPluginEntry).toBe("function"); - expect(typeof coreSdk.defineSetupPluginEntry).toBe("function"); - expect(typeof coreSdk.createChatChannelPlugin).toBe("function"); - expect(typeof coreSdk.createChannelPluginBase).toBe("function"); - expect(typeof coreSdk.isSecretRef).toBe("function"); - expect(typeof coreSdk.optionalStringEnum).toBe("function"); - expect("runPassiveAccountLifecycle" in asExports(coreSdk)).toBe(false); - expect("createLoggerBackedRuntime" in asExports(coreSdk)).toBe(false); - expect("registerSandboxBackend" in asExports(coreSdk)).toBe(false); + expectSourceMentions("core", [ + "emptyPluginConfigSchema", + "definePluginEntry", + "defineChannelPluginEntry", + "defineSetupPluginEntry", + "createChatChannelPlugin", + "createChannelPluginBase", + "isSecretRef", + "optionalStringEnum", + ]); + expectSourceOmits("core", [ + "runPassiveAccountLifecycle", + "createLoggerBackedRuntime", + "registerSandboxBackend", + ]); }); - it("re-exports the canonical plugin entry helper from core", () => { + it("re-exports the canonical plugin entry helper from core", async () => { + const [coreSdk, pluginEntrySdk] = await Promise.all([ + importPluginSdkSubpath("openclaw/plugin-sdk/core"), + importPluginSdkSubpath("openclaw/plugin-sdk/plugin-entry"), + ]); expect(coreSdk.definePluginEntry).toBe(pluginEntrySdk.definePluginEntry); }); it("exports routing helpers from the dedicated subpath", () => { - expect(typeof routingSdk.buildAgentSessionKey).toBe("function"); - expect(typeof routingSdk.resolveThreadSessionKeys).toBe("function"); + expectSourceMentions("routing", ["buildAgentSessionKey", "resolveThreadSessionKeys"]); }); it("exports reply payload helpers from the dedicated subpath", () => { - expect(typeof replyPayloadSdk.buildMediaPayload).toBe("function"); - expect(typeof replyPayloadSdk.deliverTextOrMediaReply).toBe("function"); - expect(typeof replyPayloadSdk.resolveOutboundMediaUrls).toBe("function"); - expect(typeof replyPayloadSdk.resolvePayloadMediaUrls).toBe("function"); - expect(typeof replyPayloadSdk.sendPayloadMediaSequenceAndFinalize).toBe("function"); - expect(typeof replyPayloadSdk.sendPayloadMediaSequenceOrFallback).toBe("function"); - expect(typeof replyPayloadSdk.sendTextMediaPayload).toBe("function"); - expect(typeof replyPayloadSdk.sendPayloadWithChunkedTextAndMedia).toBe("function"); + expectSourceMentions("reply-payload", [ + "buildMediaPayload", + "deliverTextOrMediaReply", + "resolveOutboundMediaUrls", + "resolvePayloadMediaUrls", + "sendPayloadMediaSequenceAndFinalize", + "sendPayloadMediaSequenceOrFallback", + "sendTextMediaPayload", + "sendPayloadWithChunkedTextAndMedia", + ]); }); it("exports media runtime helpers from the dedicated subpath", () => { - expect(typeof mediaRuntimeSdk.createDirectTextMediaOutbound).toBe("function"); - expect(typeof mediaRuntimeSdk.createScopedChannelMediaMaxBytesResolver).toBe("function"); + expectSourceMentions("media-runtime", [ + "createDirectTextMediaOutbound", + "createScopedChannelMediaMaxBytesResolver", + ]); }); it("exports reply history helpers from the dedicated subpath", () => { - expect(typeof replyHistorySdk.buildPendingHistoryContextFromMap).toBe("function"); - expect(typeof replyHistorySdk.clearHistoryEntriesIfEnabled).toBe("function"); - expect(typeof replyHistorySdk.recordPendingHistoryEntryIfEnabled).toBe("function"); - expect("buildPendingHistoryContextFromMap" in asExports(replyRuntimeSdk)).toBe(false); - expect("clearHistoryEntriesIfEnabled" in asExports(replyRuntimeSdk)).toBe(false); - expect("recordPendingHistoryEntryIfEnabled" in asExports(replyRuntimeSdk)).toBe(false); - expect("DEFAULT_GROUP_HISTORY_LIMIT" in asExports(replyRuntimeSdk)).toBe(false); + expectSourceMentions("reply-history", [ + "buildPendingHistoryContextFromMap", + "clearHistoryEntriesIfEnabled", + "recordPendingHistoryEntryIfEnabled", + ]); + expectSourceOmits("reply-runtime", [ + "buildPendingHistoryContextFromMap", + "clearHistoryEntriesIfEnabled", + "recordPendingHistoryEntryIfEnabled", + "DEFAULT_GROUP_HISTORY_LIMIT", + ]); }); it("exports account helper builders from the dedicated subpath", () => { - expect(typeof accountHelpersSdk.createAccountListHelpers).toBe("function"); + expectSourceMentions("account-helpers", ["createAccountListHelpers"]); }); - it("exports device bootstrap helpers from the dedicated subpath", async () => { - const deviceBootstrapSdk = await import("openclaw/plugin-sdk/device-bootstrap"); - expect(typeof deviceBootstrapSdk.approveDevicePairing).toBe("function"); - expect(typeof deviceBootstrapSdk.issueDeviceBootstrapToken).toBe("function"); - expect(typeof deviceBootstrapSdk.listDevicePairing).toBe("function"); + it("exports device bootstrap helpers from the dedicated subpath", () => { + expectSourceMentions("device-bootstrap", [ + "approveDevicePairing", + "issueDeviceBootstrapToken", + "listDevicePairing", + ]); }); it("exports allowlist edit helpers from the dedicated subpath", () => { - expect(typeof allowlistEditSdk.buildDmGroupAccountAllowlistAdapter).toBe("function"); - expect(typeof allowlistEditSdk.createNestedAllowlistOverrideResolver).toBe("function"); + expectSourceMentions("allowlist-config-edit", [ + "buildDmGroupAccountAllowlistAdapter", + "createNestedAllowlistOverrideResolver", + ]); }); it("exports allowlist resolution helpers from the dedicated subpath", () => { - expect(typeof allowFromSdk.addAllowlistUserEntriesFromConfigEntry).toBe("function"); - expect(typeof allowFromSdk.buildAllowlistResolutionSummary).toBe("function"); - expect(typeof allowFromSdk.canonicalizeAllowlistWithResolvedIds).toBe("function"); - expect(typeof allowFromSdk.mapAllowlistResolutionInputs).toBe("function"); - expect(typeof allowFromSdk.mergeAllowlist).toBe("function"); - expect(typeof allowFromSdk.patchAllowlistUsersInConfigEntries).toBe("function"); - expect(typeof allowFromSdk.summarizeMapping).toBe("function"); + expectSourceMentions("allow-from", [ + "addAllowlistUserEntriesFromConfigEntry", + "buildAllowlistResolutionSummary", + "canonicalizeAllowlistWithResolvedIds", + "mapAllowlistResolutionInputs", + "mergeAllowlist", + "patchAllowlistUsersInConfigEntries", + "summarizeMapping", + ]); }); it("exports allow-from matching helpers from the dedicated subpath", () => { - expect(typeof allowFromSdk.compileAllowlist).toBe("function"); - expect(typeof allowFromSdk.firstDefined).toBe("function"); - expect(typeof allowFromSdk.formatAllowlistMatchMeta).toBe("function"); - expect(typeof allowFromSdk.isSenderIdAllowed).toBe("function"); - expect(typeof allowFromSdk.mergeDmAllowFromSources).toBe("function"); - expect(typeof allowFromSdk.resolveAllowlistMatchSimple).toBe("function"); + expectSourceMentions("allow-from", [ + "compileAllowlist", + "firstDefined", + "formatAllowlistMatchMeta", + "isSenderIdAllowed", + "mergeDmAllowFromSources", + "resolveAllowlistMatchSimple", + ]); }); it("exports runtime helpers from the dedicated subpath", () => { - expect(typeof runtimeSdk.createLoggerBackedRuntime).toBe("function"); + expectSourceMentions("runtime", ["createLoggerBackedRuntime"]); }); - it("exports Discord component helpers from the dedicated subpath", async () => { - const discordSdk = await import("openclaw/plugin-sdk/discord"); - expect(typeof discordSdk.buildDiscordComponentMessage).toBe("function"); - expect(typeof discordSdk.editDiscordComponentMessage).toBe("function"); - expect(typeof discordSdk.registerBuiltDiscordComponentMessage).toBe("function"); - expect(typeof discordSdk.resolveDiscordAccount).toBe("function"); + it("exports Discord component helpers from the dedicated subpath", () => { + expectSourceMentions("discord", [ + "buildDiscordComponentMessage", + "editDiscordComponentMessage", + "registerBuiltDiscordComponentMessage", + "resolveDiscordAccount", + ]); }); it("exports channel identity and session helpers from stronger existing homes", () => { - expect(typeof routingSdk.normalizeMessageChannel).toBe("function"); - expect(typeof routingSdk.resolveGatewayMessageChannel).toBe("function"); - expect(typeof conversationRuntimeSdk.recordInboundSession).toBe("function"); - expect(typeof conversationRuntimeSdk.recordInboundSessionMetaSafe).toBe("function"); - expect(typeof conversationRuntimeSdk.resolveConversationLabel).toBe("function"); + expectSourceMentions("routing", ["normalizeMessageChannel", "resolveGatewayMessageChannel"]); + expectSourceMentions("conversation-runtime", [ + "recordInboundSession", + "recordInboundSessionMetaSafe", + "resolveConversationLabel", + ]); }); it("exports directory runtime helpers from the dedicated subpath", () => { - expect(typeof directoryRuntimeSdk.createChannelDirectoryAdapter).toBe("function"); - expect(typeof directoryRuntimeSdk.createRuntimeDirectoryLiveAdapter).toBe("function"); - expect(typeof directoryRuntimeSdk.listDirectoryEntriesFromSources).toBe("function"); - expect(typeof directoryRuntimeSdk.listResolvedDirectoryEntriesFromSources).toBe("function"); + expectSourceMentions("directory-runtime", [ + "createChannelDirectoryAdapter", + "createRuntimeDirectoryLiveAdapter", + "listDirectoryEntriesFromSources", + "listResolvedDirectoryEntriesFromSources", + ]); }); - it("exports infra runtime helpers from the dedicated subpath", () => { + it("exports infra runtime helpers from the dedicated subpath", async () => { + const infraRuntimeSdk = await importPluginSdkSubpath("openclaw/plugin-sdk/infra-runtime"); expect(typeof infraRuntimeSdk.createRuntimeOutboundDelegates).toBe("function"); expect(typeof infraRuntimeSdk.resolveOutboundSendDep).toBe("function"); }); it("exports channel runtime helpers from the dedicated subpath", () => { - expect("applyChannelMatchMeta" in asExports(channelRuntimeSdk)).toBe(false); - expect("createChannelDirectoryAdapter" in asExports(channelRuntimeSdk)).toBe(false); - expect("createEmptyChannelDirectoryAdapter" in asExports(channelRuntimeSdk)).toBe(false); - expect("createArmableStallWatchdog" in asExports(channelRuntimeSdk)).toBe(false); - expect("createDraftStreamLoop" in asExports(channelRuntimeSdk)).toBe(false); - expect("createLoggedPairingApprovalNotifier" in asExports(channelRuntimeSdk)).toBe(false); - expect("createPairingPrefixStripper" in asExports(channelRuntimeSdk)).toBe(false); - expect("createRunStateMachine" in asExports(channelRuntimeSdk)).toBe(false); - expect("createRuntimeDirectoryLiveAdapter" in asExports(channelRuntimeSdk)).toBe(false); - expect("createRuntimeOutboundDelegates" in asExports(channelRuntimeSdk)).toBe(false); - expect("createStatusReactionController" in asExports(channelRuntimeSdk)).toBe(false); - expect("createTextPairingAdapter" in asExports(channelRuntimeSdk)).toBe(false); - expect("createFinalizableDraftLifecycle" in asExports(channelRuntimeSdk)).toBe(false); - expect("DEFAULT_EMOJIS" in asExports(channelRuntimeSdk)).toBe(false); - expect("logAckFailure" in asExports(channelRuntimeSdk)).toBe(false); - expect("logTypingFailure" in asExports(channelRuntimeSdk)).toBe(false); - expect("logInboundDrop" in asExports(channelRuntimeSdk)).toBe(false); - expect("normalizeMessageChannel" in asExports(channelRuntimeSdk)).toBe(false); - expect("removeAckReactionAfterReply" in asExports(channelRuntimeSdk)).toBe(false); - expect("recordInboundSession" in asExports(channelRuntimeSdk)).toBe(false); - expect("recordInboundSessionMetaSafe" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveInboundSessionEnvelopeContext" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveMentionGating" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveMentionGatingWithBypass" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveOutboundSendDep" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveConversationLabel" in asExports(channelRuntimeSdk)).toBe(false); - expect("shouldDebounceTextInbound" in asExports(channelRuntimeSdk)).toBe(false); - expect("shouldAckReaction" in asExports(channelRuntimeSdk)).toBe(false); - expect("shouldAckReactionForWhatsApp" in asExports(channelRuntimeSdk)).toBe(false); - expect("toLocationContext" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveThreadBindingConversationIdFromBindingId" in asExports(channelRuntimeSdk)).toBe( - false, - ); - expect("resolveThreadBindingEffectiveExpiresAt" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveThreadBindingFarewellText" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveThreadBindingIdleTimeoutMs" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveThreadBindingIdleTimeoutMsForChannel" in asExports(channelRuntimeSdk)).toBe( - false, - ); - expect("resolveThreadBindingIntroText" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveThreadBindingLifecycle" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveThreadBindingMaxAgeMs" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveThreadBindingMaxAgeMsForChannel" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveThreadBindingSpawnPolicy" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveThreadBindingThreadName" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveThreadBindingsEnabled" in asExports(channelRuntimeSdk)).toBe(false); - expect("formatThreadBindingDisabledError" in asExports(channelRuntimeSdk)).toBe(false); - expect("DISCORD_THREAD_BINDING_CHANNEL" in asExports(channelRuntimeSdk)).toBe(false); - expect("MATRIX_THREAD_BINDING_CHANNEL" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveControlCommandGate" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveCommandAuthorizedFromAuthorizers" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveDualTextControlCommandGate" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveNativeCommandSessionTargets" in asExports(channelRuntimeSdk)).toBe(false); - expect("attachChannelToResult" in asExports(channelRuntimeSdk)).toBe(false); - expect("buildComputedAccountStatusSnapshot" in asExports(channelRuntimeSdk)).toBe(false); - expect("buildMediaPayload" in asExports(channelRuntimeSdk)).toBe(false); - expect("createActionGate" in asExports(channelRuntimeSdk)).toBe(false); - expect("jsonResult" in asExports(channelRuntimeSdk)).toBe(false); - expect("normalizeInteractiveReply" in asExports(channelRuntimeSdk)).toBe(false); - expect("PAIRING_APPROVED_MESSAGE" in asExports(channelRuntimeSdk)).toBe(false); - expect("projectCredentialSnapshotFields" in asExports(channelRuntimeSdk)).toBe(false); - expect("readStringParam" in asExports(channelRuntimeSdk)).toBe(false); - expect("compileAllowlist" in asExports(channelRuntimeSdk)).toBe(false); - expect("formatAllowlistMatchMeta" in asExports(channelRuntimeSdk)).toBe(false); - expect("firstDefined" in asExports(channelRuntimeSdk)).toBe(false); - expect("isSenderIdAllowed" in asExports(channelRuntimeSdk)).toBe(false); - expect("mergeDmAllowFromSources" in asExports(channelRuntimeSdk)).toBe(false); - expect("addAllowlistUserEntriesFromConfigEntry" in asExports(channelRuntimeSdk)).toBe(false); - expect("buildAllowlistResolutionSummary" in asExports(channelRuntimeSdk)).toBe(false); - expect("canonicalizeAllowlistWithResolvedIds" in asExports(channelRuntimeSdk)).toBe(false); - expect("mergeAllowlist" in asExports(channelRuntimeSdk)).toBe(false); - expect("patchAllowlistUsersInConfigEntries" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveChannelConfigWrites" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolvePayloadMediaUrls" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveScopedChannelMediaMaxBytes" in asExports(channelRuntimeSdk)).toBe(false); - expect("sendPayloadMediaSequenceAndFinalize" in asExports(channelRuntimeSdk)).toBe(false); - expect("sendPayloadMediaSequenceOrFallback" in asExports(channelRuntimeSdk)).toBe(false); - expect("sendTextMediaPayload" in asExports(channelRuntimeSdk)).toBe(false); - expect("createScopedChannelMediaMaxBytesResolver" in asExports(channelRuntimeSdk)).toBe(false); - expect("runPassiveAccountLifecycle" in asExports(channelRuntimeSdk)).toBe(false); - expect("buildChannelKeyCandidates" in asExports(channelRuntimeSdk)).toBe(false); - expect("buildMessagingTarget" in asExports(channelRuntimeSdk)).toBe(false); - expect("createDirectTextMediaOutbound" in asExports(channelRuntimeSdk)).toBe(false); - expect("createMessageToolButtonsSchema" in asExports(channelRuntimeSdk)).toBe(false); - expect("createMessageToolCardSchema" in asExports(channelRuntimeSdk)).toBe(false); - expect("createScopedAccountReplyToModeResolver" in asExports(channelRuntimeSdk)).toBe(false); - expect("createStaticReplyToModeResolver" in asExports(channelRuntimeSdk)).toBe(false); - expect("createTopLevelChannelReplyToModeResolver" in asExports(channelRuntimeSdk)).toBe(false); - expect("createUnionActionGate" in asExports(channelRuntimeSdk)).toBe(false); - expect("ensureTargetId" in asExports(channelRuntimeSdk)).toBe(false); - expect("listTokenSourcedAccounts" in asExports(channelRuntimeSdk)).toBe(false); - expect("parseMentionPrefixOrAtUserTarget" in asExports(channelRuntimeSdk)).toBe(false); - expect("requireTargetKind" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveChannelEntryMatchWithFallback" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveChannelMatchConfig" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveReactionMessageId" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveTargetsWithOptionalToken" in asExports(channelRuntimeSdk)).toBe(false); - expect("appendMatchMetadata" in asExports(channelRuntimeSdk)).toBe(false); - expect("asString" in asExports(channelRuntimeSdk)).toBe(false); - expect("collectIssuesForEnabledAccounts" in asExports(channelRuntimeSdk)).toBe(false); - expect("isRecord" in asExports(channelRuntimeSdk)).toBe(false); - expect("resolveEnabledConfiguredAccountId" in asExports(channelRuntimeSdk)).toBe(false); + expectSourceOmits("channel-runtime", [ + "applyChannelMatchMeta", + "createChannelDirectoryAdapter", + "createEmptyChannelDirectoryAdapter", + "createArmableStallWatchdog", + "createDraftStreamLoop", + "createLoggedPairingApprovalNotifier", + "createPairingPrefixStripper", + "createRunStateMachine", + "createRuntimeDirectoryLiveAdapter", + "createRuntimeOutboundDelegates", + "createStatusReactionController", + "createTextPairingAdapter", + "createFinalizableDraftLifecycle", + "DEFAULT_EMOJIS", + "logAckFailure", + "logTypingFailure", + "logInboundDrop", + "normalizeMessageChannel", + "removeAckReactionAfterReply", + "recordInboundSession", + "recordInboundSessionMetaSafe", + "resolveInboundSessionEnvelopeContext", + "resolveMentionGating", + "resolveMentionGatingWithBypass", + "resolveOutboundSendDep", + "resolveConversationLabel", + "shouldDebounceTextInbound", + "shouldAckReaction", + "shouldAckReactionForWhatsApp", + "toLocationContext", + "resolveThreadBindingConversationIdFromBindingId", + "resolveThreadBindingEffectiveExpiresAt", + "resolveThreadBindingFarewellText", + "resolveThreadBindingIdleTimeoutMs", + "resolveThreadBindingIdleTimeoutMsForChannel", + "resolveThreadBindingIntroText", + "resolveThreadBindingLifecycle", + "resolveThreadBindingMaxAgeMs", + "resolveThreadBindingMaxAgeMsForChannel", + "resolveThreadBindingSpawnPolicy", + "resolveThreadBindingThreadName", + "resolveThreadBindingsEnabled", + "formatThreadBindingDisabledError", + "DISCORD_THREAD_BINDING_CHANNEL", + "MATRIX_THREAD_BINDING_CHANNEL", + "resolveControlCommandGate", + "resolveCommandAuthorizedFromAuthorizers", + "resolveDualTextControlCommandGate", + "resolveNativeCommandSessionTargets", + "attachChannelToResult", + "buildComputedAccountStatusSnapshot", + "buildMediaPayload", + "createActionGate", + "jsonResult", + "normalizeInteractiveReply", + "PAIRING_APPROVED_MESSAGE", + "projectCredentialSnapshotFields", + "readStringParam", + "compileAllowlist", + "formatAllowlistMatchMeta", + "firstDefined", + "isSenderIdAllowed", + "mergeDmAllowFromSources", + "addAllowlistUserEntriesFromConfigEntry", + "buildAllowlistResolutionSummary", + "canonicalizeAllowlistWithResolvedIds", + "mergeAllowlist", + "patchAllowlistUsersInConfigEntries", + "resolveChannelConfigWrites", + "resolvePayloadMediaUrls", + "resolveScopedChannelMediaMaxBytes", + "sendPayloadMediaSequenceAndFinalize", + "sendPayloadMediaSequenceOrFallback", + "sendTextMediaPayload", + "createScopedChannelMediaMaxBytesResolver", + "runPassiveAccountLifecycle", + "buildChannelKeyCandidates", + "buildMessagingTarget", + "createDirectTextMediaOutbound", + "createMessageToolButtonsSchema", + "createMessageToolCardSchema", + "createScopedAccountReplyToModeResolver", + "createStaticReplyToModeResolver", + "createTopLevelChannelReplyToModeResolver", + "createUnionActionGate", + "ensureTargetId", + "listTokenSourcedAccounts", + "parseMentionPrefixOrAtUserTarget", + "requireTargetKind", + "resolveChannelEntryMatchWithFallback", + "resolveChannelMatchConfig", + "resolveReactionMessageId", + "resolveTargetsWithOptionalToken", + "appendMatchMetadata", + "asString", + "collectIssuesForEnabledAccounts", + "isRecord", + "resolveEnabledConfiguredAccountId", + ]); }); it("exports inbound channel helpers from the dedicated subpath", () => { - expect(typeof channelInboundSdk.buildMentionRegexes).toBe("function"); - expect(typeof channelInboundSdk.createChannelInboundDebouncer).toBe("function"); - expect(typeof channelInboundSdk.createInboundDebouncer).toBe("function"); - expect(typeof channelInboundSdk.formatInboundEnvelope).toBe("function"); - expect(typeof channelInboundSdk.formatInboundFromLabel).toBe("function"); - expect(typeof channelInboundSdk.formatLocationText).toBe("function"); - expect(typeof channelInboundSdk.logInboundDrop).toBe("function"); - expect(typeof channelInboundSdk.matchesMentionPatterns).toBe("function"); - expect(typeof channelInboundSdk.matchesMentionWithExplicit).toBe("function"); - expect(typeof channelInboundSdk.normalizeMentionText).toBe("function"); - expect(typeof channelInboundSdk.resolveInboundDebounceMs).toBe("function"); - expect(typeof channelInboundSdk.resolveEnvelopeFormatOptions).toBe("function"); - expect(typeof channelInboundSdk.resolveInboundSessionEnvelopeContext).toBe("function"); - expect(typeof channelInboundSdk.resolveMentionGating).toBe("function"); - expect(typeof channelInboundSdk.resolveMentionGatingWithBypass).toBe("function"); - expect(typeof channelInboundSdk.shouldDebounceTextInbound).toBe("function"); - expect(typeof channelInboundSdk.toLocationContext).toBe("function"); - expect("buildMentionRegexes" in asExports(replyRuntimeSdk)).toBe(false); - expect("createInboundDebouncer" in asExports(replyRuntimeSdk)).toBe(false); - expect("formatInboundEnvelope" in asExports(replyRuntimeSdk)).toBe(false); - expect("formatInboundFromLabel" in asExports(replyRuntimeSdk)).toBe(false); - expect("matchesMentionPatterns" in asExports(replyRuntimeSdk)).toBe(false); - expect("matchesMentionWithExplicit" in asExports(replyRuntimeSdk)).toBe(false); - expect("normalizeMentionText" in asExports(replyRuntimeSdk)).toBe(false); - expect("resolveEnvelopeFormatOptions" in asExports(replyRuntimeSdk)).toBe(false); - expect("resolveInboundDebounceMs" in asExports(replyRuntimeSdk)).toBe(false); + expectSourceMentions("channel-inbound", [ + "buildMentionRegexes", + "createChannelInboundDebouncer", + "createInboundDebouncer", + "formatInboundEnvelope", + "formatInboundFromLabel", + "formatLocationText", + "logInboundDrop", + "matchesMentionPatterns", + "matchesMentionWithExplicit", + "normalizeMentionText", + "resolveInboundDebounceMs", + "resolveEnvelopeFormatOptions", + "resolveInboundSessionEnvelopeContext", + "resolveMentionGating", + "resolveMentionGatingWithBypass", + "shouldDebounceTextInbound", + "toLocationContext", + ]); + expectSourceOmits("reply-runtime", [ + "buildMentionRegexes", + "createInboundDebouncer", + "formatInboundEnvelope", + "formatInboundFromLabel", + "matchesMentionPatterns", + "matchesMentionWithExplicit", + "normalizeMentionText", + "resolveEnvelopeFormatOptions", + "resolveInboundDebounceMs", + ]); }); it("exports channel setup helpers from the dedicated subpath", () => { - expect(typeof channelSetupSdk.createOptionalChannelSetupSurface).toBe("function"); - expect(typeof channelSetupSdk.createTopLevelChannelDmPolicy).toBe("function"); + expectSourceMentions("channel-setup", [ + "createOptionalChannelSetupSurface", + "createTopLevelChannelDmPolicy", + ]); }); it("exports channel action helpers from the dedicated subpath", () => { - expect(typeof channelActionsSdk.createUnionActionGate).toBe("function"); - expect(typeof channelActionsSdk.listTokenSourcedAccounts).toBe("function"); - expect(typeof channelActionsSdk.resolveReactionMessageId).toBe("function"); + expectSourceMentions("channel-actions", [ + "createUnionActionGate", + "listTokenSourcedAccounts", + "resolveReactionMessageId", + ]); }); it("exports channel target helpers from the dedicated subpath", () => { - expect(typeof channelTargetsSdk.applyChannelMatchMeta).toBe("function"); - expect(typeof channelTargetsSdk.buildChannelKeyCandidates).toBe("function"); - expect(typeof channelTargetsSdk.buildMessagingTarget).toBe("function"); - expect(typeof channelTargetsSdk.ensureTargetId).toBe("function"); - expect(typeof channelTargetsSdk.parseMentionPrefixOrAtUserTarget).toBe("function"); - expect(typeof channelTargetsSdk.requireTargetKind).toBe("function"); - expect(typeof channelTargetsSdk.resolveChannelEntryMatchWithFallback).toBe("function"); - expect(typeof channelTargetsSdk.resolveChannelMatchConfig).toBe("function"); - expect(typeof channelTargetsSdk.resolveTargetsWithOptionalToken).toBe("function"); + expectSourceMentions("channel-targets", [ + "applyChannelMatchMeta", + "buildChannelKeyCandidates", + "buildMessagingTarget", + "ensureTargetId", + "parseMentionPrefixOrAtUserTarget", + "requireTargetKind", + "resolveChannelEntryMatchWithFallback", + "resolveChannelMatchConfig", + "resolveTargetsWithOptionalToken", + ]); }); it("exports channel config write helpers from the dedicated subpath", () => { - expect(typeof channelConfigHelpersSdk.authorizeConfigWrite).toBe("function"); - expect(typeof channelConfigHelpersSdk.canBypassConfigWritePolicy).toBe("function"); - expect(typeof channelConfigHelpersSdk.formatConfigWriteDeniedMessage).toBe("function"); - expect(typeof channelConfigHelpersSdk.resolveChannelConfigWrites).toBe("function"); + expectSourceMentions("channel-config-helpers", [ + "authorizeConfigWrite", + "canBypassConfigWritePolicy", + "formatConfigWriteDeniedMessage", + "resolveChannelConfigWrites", + ]); }); it("keeps channel contract types on the dedicated subpath", () => { @@ -409,7 +447,10 @@ describe("plugin-sdk subpath exports", () => { expectTypeOf().toMatchTypeOf(); }); - it("exports channel lifecycle helpers from the dedicated subpath", () => { + it("exports channel lifecycle helpers from the dedicated subpath", async () => { + const channelLifecycleSdk = await importPluginSdkSubpath( + "openclaw/plugin-sdk/channel-lifecycle", + ); expect(typeof channelLifecycleSdk.createDraftStreamLoop).toBe("function"); expect(typeof channelLifecycleSdk.createFinalizableDraftLifecycle).toBe("function"); expect(typeof channelLifecycleSdk.runPassiveAccountLifecycle).toBe("function"); @@ -418,189 +459,220 @@ describe("plugin-sdk subpath exports", () => { }); it("exports channel feedback helpers from the dedicated subpath", () => { - expect(typeof channelFeedbackSdk.createStatusReactionController).toBe("function"); - expect(typeof channelFeedbackSdk.logAckFailure).toBe("function"); - expect(typeof channelFeedbackSdk.logTypingFailure).toBe("function"); - expect(typeof channelFeedbackSdk.removeAckReactionAfterReply).toBe("function"); - expect(typeof channelFeedbackSdk.shouldAckReaction).toBe("function"); - expect(typeof channelFeedbackSdk.shouldAckReactionForWhatsApp).toBe("function"); - expect(typeof channelFeedbackSdk.DEFAULT_EMOJIS).toBe("object"); + expectSourceMentions("channel-feedback", [ + "createStatusReactionController", + "logAckFailure", + "logTypingFailure", + "removeAckReactionAfterReply", + "shouldAckReaction", + "shouldAckReactionForWhatsApp", + "DEFAULT_EMOJIS", + ]); }); it("exports status helper utilities from the dedicated subpath", () => { - expect(typeof statusHelpersSdk.appendMatchMetadata).toBe("function"); - expect(typeof statusHelpersSdk.asString).toBe("function"); - expect(typeof statusHelpersSdk.collectIssuesForEnabledAccounts).toBe("function"); - expect(typeof statusHelpersSdk.isRecord).toBe("function"); - expect(typeof statusHelpersSdk.resolveEnabledConfiguredAccountId).toBe("function"); + expectSourceMentions("status-helpers", [ + "appendMatchMetadata", + "asString", + "collectIssuesForEnabledAccounts", + "isRecord", + "resolveEnabledConfiguredAccountId", + ]); }); it("exports message tool schema helpers from the dedicated subpath", () => { - expect(typeof channelActionsSdk.createMessageToolButtonsSchema).toBe("function"); - expect(typeof channelActionsSdk.createMessageToolCardSchema).toBe("function"); + expectSourceMentions("channel-actions", [ + "createMessageToolButtonsSchema", + "createMessageToolCardSchema", + ]); }); - it("exports channel pairing helpers from the dedicated subpath", () => { - expect(typeof channelPairingSdk.createChannelPairingController).toBe("function"); - expect(typeof channelPairingSdk.createChannelPairingChallengeIssuer).toBe("function"); - expect(typeof channelPairingSdk.createLoggedPairingApprovalNotifier).toBe("function"); - expect(typeof channelPairingSdk.createPairingPrefixStripper).toBe("function"); - expect(typeof channelPairingSdk.createTextPairingAdapter).toBe("function"); - expect("createScopedPairingAccess" in asExports(channelPairingSdk)).toBe(false); + it("exports channel pairing helpers from the dedicated subpath", async () => { + const channelPairingSdk = await importPluginSdkSubpath("openclaw/plugin-sdk/channel-pairing"); + expectSourceMentions("channel-pairing", [ + "createChannelPairingController", + "createChannelPairingChallengeIssuer", + "createLoggedPairingApprovalNotifier", + "createPairingPrefixStripper", + "createTextPairingAdapter", + ]); + expect("createScopedPairingAccess" in channelPairingSdk).toBe(false); }); - it("exports channel reply pipeline helpers from the dedicated subpath", () => { - expect(typeof channelReplyPipelineSdk.createChannelReplyPipeline).toBe("function"); - expect("createTypingCallbacks" in asExports(channelReplyPipelineSdk)).toBe(false); - expect("createReplyPrefixContext" in asExports(channelReplyPipelineSdk)).toBe(false); - expect("createReplyPrefixOptions" in asExports(channelReplyPipelineSdk)).toBe(false); + it("exports channel reply pipeline helpers from the dedicated subpath", async () => { + const channelReplyPipelineSdk = await importPluginSdkSubpath( + "openclaw/plugin-sdk/channel-reply-pipeline", + ); + expectSourceMentions("channel-reply-pipeline", ["createChannelReplyPipeline"]); + expect("createTypingCallbacks" in channelReplyPipelineSdk).toBe(false); + expect("createReplyPrefixContext" in channelReplyPipelineSdk).toBe(false); + expect("createReplyPrefixOptions" in channelReplyPipelineSdk).toBe(false); }); it("exports command auth helpers from the dedicated subpath", () => { - expect(typeof commandAuthSdk.buildCommandTextFromArgs).toBe("function"); - expect(typeof commandAuthSdk.buildCommandsPaginationKeyboard).toBe("function"); - expect(typeof commandAuthSdk.buildModelsProviderData).toBe("function"); - expect(typeof commandAuthSdk.hasControlCommand).toBe("function"); - expect(typeof commandAuthSdk.listNativeCommandSpecsForConfig).toBe("function"); - expect(typeof commandAuthSdk.listSkillCommandsForAgents).toBe("function"); - expect(typeof commandAuthSdk.normalizeCommandBody).toBe("function"); - expect(typeof commandAuthSdk.resolveCommandAuthorization).toBe("function"); - expect(typeof commandAuthSdk.resolveCommandAuthorizedFromAuthorizers).toBe("function"); - expect(typeof commandAuthSdk.resolveControlCommandGate).toBe("function"); - expect(typeof commandAuthSdk.resolveDualTextControlCommandGate).toBe("function"); - expect(typeof commandAuthSdk.resolveNativeCommandSessionTargets).toBe("function"); - expect(typeof commandAuthSdk.resolveStoredModelOverride).toBe("function"); - expect(typeof commandAuthSdk.shouldComputeCommandAuthorized).toBe("function"); - expect(typeof commandAuthSdk.shouldHandleTextCommands).toBe("function"); - expect("hasControlCommand" in asExports(replyRuntimeSdk)).toBe(false); - expect("buildCommandTextFromArgs" in asExports(replyRuntimeSdk)).toBe(false); - expect("buildCommandsPaginationKeyboard" in asExports(replyRuntimeSdk)).toBe(false); - expect("buildModelsProviderData" in asExports(replyRuntimeSdk)).toBe(false); - expect("listNativeCommandSpecsForConfig" in asExports(replyRuntimeSdk)).toBe(false); - expect("listSkillCommandsForAgents" in asExports(replyRuntimeSdk)).toBe(false); - expect("normalizeCommandBody" in asExports(replyRuntimeSdk)).toBe(false); - expect("resolveCommandAuthorization" in asExports(replyRuntimeSdk)).toBe(false); - expect("resolveStoredModelOverride" in asExports(replyRuntimeSdk)).toBe(false); - expect("shouldComputeCommandAuthorized" in asExports(replyRuntimeSdk)).toBe(false); - expect("shouldHandleTextCommands" in asExports(replyRuntimeSdk)).toBe(false); + expectSourceMentions("command-auth", [ + "buildCommandTextFromArgs", + "buildCommandsPaginationKeyboard", + "buildModelsProviderData", + "hasControlCommand", + "listNativeCommandSpecsForConfig", + "listSkillCommandsForAgents", + "normalizeCommandBody", + "resolveCommandAuthorization", + "resolveCommandAuthorizedFromAuthorizers", + "resolveControlCommandGate", + "resolveDualTextControlCommandGate", + "resolveNativeCommandSessionTargets", + "resolveStoredModelOverride", + "shouldComputeCommandAuthorized", + "shouldHandleTextCommands", + ]); + expectSourceOmits("reply-runtime", [ + "hasControlCommand", + "buildCommandTextFromArgs", + "buildCommandsPaginationKeyboard", + "buildModelsProviderData", + "listNativeCommandSpecsForConfig", + "listSkillCommandsForAgents", + "normalizeCommandBody", + "resolveCommandAuthorization", + "resolveStoredModelOverride", + "shouldComputeCommandAuthorized", + "shouldHandleTextCommands", + ]); }); it("exports channel send-result helpers from the dedicated subpath", () => { - expect(typeof channelSendResultSdk.attachChannelToResult).toBe("function"); - expect(typeof channelSendResultSdk.buildChannelSendResult).toBe("function"); + expectSourceMentions("channel-send-result", [ + "attachChannelToResult", + "buildChannelSendResult", + ]); }); it("exports binding lifecycle helpers from the conversation-runtime subpath", () => { - expect(typeof conversationRuntimeSdk.DISCORD_THREAD_BINDING_CHANNEL).toBe("string"); - expect(typeof conversationRuntimeSdk.MATRIX_THREAD_BINDING_CHANNEL).toBe("string"); - expect(typeof conversationRuntimeSdk.formatThreadBindingDisabledError).toBe("function"); - expect(typeof conversationRuntimeSdk.resolveThreadBindingFarewellText).toBe("function"); - expect(typeof conversationRuntimeSdk.resolveThreadBindingConversationIdFromBindingId).toBe( - "function", - ); - expect(typeof conversationRuntimeSdk.resolveThreadBindingEffectiveExpiresAt).toBe("function"); - expect(typeof conversationRuntimeSdk.resolveThreadBindingIdleTimeoutMs).toBe("function"); - expect(typeof conversationRuntimeSdk.resolveThreadBindingIdleTimeoutMsForChannel).toBe( - "function", - ); - expect(typeof conversationRuntimeSdk.resolveThreadBindingIntroText).toBe("function"); - expect(typeof conversationRuntimeSdk.resolveThreadBindingLifecycle).toBe("function"); - expect(typeof conversationRuntimeSdk.resolveThreadBindingMaxAgeMs).toBe("function"); - expect(typeof conversationRuntimeSdk.resolveThreadBindingMaxAgeMsForChannel).toBe("function"); - expect(typeof conversationRuntimeSdk.resolveThreadBindingSpawnPolicy).toBe("function"); - expect(typeof conversationRuntimeSdk.resolveThreadBindingThreadName).toBe("function"); - expect(typeof conversationRuntimeSdk.resolveThreadBindingsEnabled).toBe("function"); - expect(typeof conversationRuntimeSdk.formatThreadBindingDurationLabel).toBe("function"); - expect(typeof conversationRuntimeSdk.createScopedAccountReplyToModeResolver).toBe("function"); - expect(typeof conversationRuntimeSdk.createStaticReplyToModeResolver).toBe("function"); - expect(typeof conversationRuntimeSdk.createTopLevelChannelReplyToModeResolver).toBe("function"); + expectSourceMentions("conversation-runtime", [ + "DISCORD_THREAD_BINDING_CHANNEL", + "MATRIX_THREAD_BINDING_CHANNEL", + "formatThreadBindingDisabledError", + "resolveThreadBindingFarewellText", + "resolveThreadBindingConversationIdFromBindingId", + "resolveThreadBindingEffectiveExpiresAt", + "resolveThreadBindingIdleTimeoutMs", + "resolveThreadBindingIdleTimeoutMsForChannel", + "resolveThreadBindingIntroText", + "resolveThreadBindingLifecycle", + "resolveThreadBindingMaxAgeMs", + "resolveThreadBindingMaxAgeMsForChannel", + "resolveThreadBindingSpawnPolicy", + "resolveThreadBindingThreadName", + "resolveThreadBindingsEnabled", + "formatThreadBindingDurationLabel", + "createScopedAccountReplyToModeResolver", + "createStaticReplyToModeResolver", + "createTopLevelChannelReplyToModeResolver", + ]); }); it("exports narrow binding lifecycle helpers from the dedicated subpath", () => { - expect(typeof threadBindingsRuntimeSdk.resolveThreadBindingLifecycle).toBe("function"); + expectSourceMentions("thread-bindings-runtime", ["resolveThreadBindingLifecycle"]); }); it("exports narrow matrix runtime helpers from the dedicated subpath", () => { - expect(typeof matrixRuntimeSharedSdk.formatZonedTimestamp).toBe("function"); + expectSourceMentions("matrix-runtime-shared", ["formatZonedTimestamp"]); }); it("exports narrow ssrf helpers from the dedicated subpath", () => { - expect(typeof ssrfRuntimeSdk.closeDispatcher).toBe("function"); - expect(typeof ssrfRuntimeSdk.createPinnedDispatcher).toBe("function"); - expect(typeof ssrfRuntimeSdk.resolvePinnedHostnameWithPolicy).toBe("function"); - expect(typeof ssrfRuntimeSdk.assertHttpUrlTargetsPrivateNetwork).toBe("function"); - expect(typeof ssrfRuntimeSdk.ssrfPolicyFromAllowPrivateNetwork).toBe("function"); + expectSourceMentions("ssrf-runtime", [ + "closeDispatcher", + "createPinnedDispatcher", + "resolvePinnedHostnameWithPolicy", + "assertHttpUrlTargetsPrivateNetwork", + "ssrfPolicyFromAllowPrivateNetwork", + ]); }); it("exports provider setup helpers from the dedicated subpath", () => { - expect(typeof providerSetupSdk.buildVllmProvider).toBe("function"); - expect(typeof providerSetupSdk.discoverOpenAICompatibleSelfHostedProvider).toBe("function"); + expectSourceMentions("provider-setup", [ + "buildVllmProvider", + "discoverOpenAICompatibleSelfHostedProvider", + ]); }); it("exports oauth helpers from provider-auth", () => { - expect(typeof providerAuthSdk.buildOauthProviderAuthResult).toBe("function"); - expect(typeof providerAuthSdk.generatePkceVerifierChallenge).toBe("function"); - expect(typeof providerAuthSdk.toFormUrlEncoded).toBe("function"); - expect("buildOauthProviderAuthResult" in asExports(coreSdk)).toBe(false); + expectSourceMentions("provider-auth", [ + "buildOauthProviderAuthResult", + "generatePkceVerifierChallenge", + "toFormUrlEncoded", + ]); + expectSourceOmits("core", ["buildOauthProviderAuthResult"]); }); it("keeps provider models focused on shared provider primitives", () => { - expect(typeof providerModelsSdk.applyOpenAIConfig).toBe("function"); - expect(typeof providerModelsSdk.buildKilocodeModelDefinition).toBe("function"); - expect(typeof providerModelsSdk.discoverHuggingfaceModels).toBe("function"); - expect("buildMinimaxModelDefinition" in asExports(providerModelsSdk)).toBe(false); - expect("buildMoonshotProvider" in asExports(providerModelsSdk)).toBe(false); - expect("QIANFAN_BASE_URL" in asExports(providerModelsSdk)).toBe(false); - expect("resolveZaiBaseUrl" in asExports(providerModelsSdk)).toBe(false); + expectSourceMentions("provider-models", [ + "applyOpenAIConfig", + "buildKilocodeModelDefinition", + "discoverHuggingfaceModels", + ]); + expectSourceOmits("provider-models", [ + "buildMinimaxModelDefinition", + "buildMoonshotProvider", + "QIANFAN_BASE_URL", + "resolveZaiBaseUrl", + ]); }); it("exports shared setup helpers from the dedicated subpath", () => { - expect(typeof setupSdk.DEFAULT_ACCOUNT_ID).toBe("string"); - expect(typeof setupSdk.createAllowFromSection).toBe("function"); - expect(typeof setupSdk.createDelegatedSetupWizardProxy).toBe("function"); - expect(typeof setupSdk.createTopLevelChannelDmPolicy).toBe("function"); - expect(typeof setupSdk.mergeAllowFromEntries).toBe("function"); + expectSourceMentions("setup", [ + "DEFAULT_ACCOUNT_ID", + "createAllowFromSection", + "createDelegatedSetupWizardProxy", + "createTopLevelChannelDmPolicy", + "mergeAllowFromEntries", + ]); }); it("exports shared lazy runtime helpers from the dedicated subpath", () => { - expect(typeof lazyRuntimeSdk.createLazyRuntimeSurface).toBe("function"); - expect(typeof lazyRuntimeSdk.createLazyRuntimeModule).toBe("function"); + expectSourceMentions("lazy-runtime", ["createLazyRuntimeSurface", "createLazyRuntimeModule"]); }); it("exports narrow self-hosted provider setup helpers", () => { - expect(typeof selfHostedProviderSetupSdk.buildVllmProvider).toBe("function"); - expect(typeof selfHostedProviderSetupSdk.buildSglangProvider).toBe("function"); - expect( - typeof selfHostedProviderSetupSdk.configureOpenAICompatibleSelfHostedProviderNonInteractive, - ).toBe("function"); + expectSourceMentions("self-hosted-provider-setup", [ + "buildVllmProvider", + "buildSglangProvider", + "configureOpenAICompatibleSelfHostedProviderNonInteractive", + ]); }); it("exports narrow Ollama setup helpers", () => { - expect(typeof ollamaSetupSdk.buildOllamaProvider).toBe("function"); - expect(typeof ollamaSetupSdk.configureOllamaNonInteractive).toBe("function"); + expectSourceMentions("ollama-setup", ["buildOllamaProvider", "configureOllamaNonInteractive"]); }); it("exports sandbox helpers from the dedicated subpath", () => { - expect(typeof sandboxSdk.registerSandboxBackend).toBe("function"); - expect(typeof sandboxSdk.runPluginCommandWithTimeout).toBe("function"); + expectSourceMentions("sandbox", ["registerSandboxBackend", "runPluginCommandWithTimeout"]); }); it("exports secret input helpers from the dedicated subpath", () => { - expect(typeof secretInputSdk.buildSecretInputSchema).toBe("function"); - expect(typeof secretInputSdk.buildOptionalSecretInputSchema).toBe("function"); - expect(typeof secretInputSdk.normalizeSecretInputString).toBe("function"); - expect("hasConfiguredSecretInput" in asExports(configRuntimeSdk)).toBe(false); - expect("normalizeResolvedSecretInputString" in asExports(configRuntimeSdk)).toBe(false); - expect("normalizeSecretInputString" in asExports(configRuntimeSdk)).toBe(false); + expectSourceMentions("secret-input", [ + "buildSecretInputSchema", + "buildOptionalSecretInputSchema", + "normalizeSecretInputString", + ]); + expectSourceOmits("config-runtime", [ + "hasConfiguredSecretInput", + "normalizeResolvedSecretInputString", + "normalizeSecretInputString", + ]); }); it("exports webhook ingress helpers from the dedicated subpath", () => { - expect(typeof webhookIngressSdk.registerPluginHttpRoute).toBe("function"); - expect(typeof webhookIngressSdk.resolveWebhookPath).toBe("function"); - expect(typeof webhookIngressSdk.readRequestBodyWithLimit).toBe("function"); - expect(typeof webhookIngressSdk.readJsonWebhookBodyOrReject).toBe("function"); - expect(typeof webhookIngressSdk.requestBodyErrorToText).toBe("function"); - expect(typeof webhookIngressSdk.withResolvedWebhookRequestPipeline).toBe("function"); + expectSourceMentions("webhook-ingress", [ + "registerPluginHttpRoute", + "resolveWebhookPath", + "readRequestBodyWithLimit", + "readJsonWebhookBodyOrReject", + "requestBodyErrorToText", + "withResolvedWebhookRequestPipeline", + ]); }); it("exports shared core types used by bundled extensions", () => { @@ -610,8 +682,7 @@ describe("plugin-sdk subpath exports", () => { }); it("exports the public testing surface", () => { - expect(typeof testingSdk.removeAckReactionAfterReply).toBe("function"); - expect(typeof testingSdk.shouldAckReaction).toBe("function"); + expectSourceMentions("testing", ["removeAckReactionAfterReply", "shouldAckReaction"]); }); it("keeps core shared types aligned with the channel prelude", () => { @@ -620,9 +691,10 @@ describe("plugin-sdk subpath exports", () => { expectTypeOf().toMatchTypeOf(); }); - it("resolves every curated public subpath", async () => { - for (const { id, load } of bundledExtensionSubpathLoaders) { - const mod = await load(); + it("resolves representative curated public subpaths", async () => { + expect(pluginSdkSubpaths.length).toBeGreaterThan(representativeRuntimeSmokeSubpaths.length); + for (const id of representativeRuntimeSmokeSubpaths) { + const mod = await importPluginSdkSubpath(`openclaw/plugin-sdk/${id}`); expect(typeof mod).toBe("object"); expect(mod, `subpath ${id} should resolve`).toBeTruthy(); } diff --git a/src/plugins/contracts/wizard.contract.test.ts b/src/plugins/contracts/wizard.contract.test.ts index 59a9ab2bbc4..cf9f56d8cde 100644 --- a/src/plugins/contracts/wizard.contract.test.ts +++ b/src/plugins/contracts/wizard.contract.test.ts @@ -5,8 +5,7 @@ import { resolveProviderPluginChoice, resolveProviderWizardOptions, } from "../provider-wizard.js"; -import type { ProviderPlugin } from "../types.js"; -import { providerContractPluginIds, uniqueProviderContractProviders } from "./registry.js"; +import type { ProviderAuthMethod, ProviderPlugin } from "../types.js"; const resolvePluginProvidersMock = vi.fn(); @@ -14,6 +13,94 @@ vi.mock("../providers.js", () => ({ resolvePluginProviders: (...args: unknown[]) => resolvePluginProvidersMock(...args), })); +function createAuthMethod( + params: Pick & + Partial>, +): ProviderAuthMethod { + return { + id: params.id, + label: params.label, + ...(params.hint ? { hint: params.hint } : {}), + ...(params.wizard ? { wizard: params.wizard } : {}), + kind: "api_key", + run: async () => ({ profiles: [] }), + }; +} + +const TEST_PROVIDERS: ProviderPlugin[] = [ + { + id: "alpha", + label: "Alpha", + auth: [ + createAuthMethod({ + id: "api-key", + label: "API key", + wizard: { + choiceLabel: "Alpha key", + choiceHint: "Use an API key", + groupId: "alpha", + groupLabel: "Alpha", + onboardingScopes: ["text-inference"], + }, + }), + createAuthMethod({ + id: "oauth", + label: "OAuth", + wizard: { + choiceId: "alpha-oauth", + choiceLabel: "Alpha OAuth", + groupId: "alpha", + groupLabel: "Alpha", + groupHint: "Recommended", + }, + }), + ], + wizard: { + modelPicker: { + label: "Alpha custom", + hint: "Pick Alpha models", + methodId: "oauth", + }, + }, + }, + { + id: "beta", + label: "Beta", + auth: [createAuthMethod({ id: "token", label: "Token" })], + wizard: { + setup: { + choiceLabel: "Beta setup", + groupId: "beta", + groupLabel: "Beta", + }, + modelPicker: { + label: "Beta custom", + }, + }, + }, + { + id: "gamma", + label: "Gamma", + auth: [ + createAuthMethod({ id: "default", label: "Default auth" }), + createAuthMethod({ id: "alt", label: "Alt auth" }), + ], + wizard: { + setup: { + methodId: "alt", + choiceId: "gamma-alt", + choiceLabel: "Gamma alt", + groupId: "gamma", + groupLabel: "Gamma", + }, + }, + }, +]; + +const TEST_PROVIDER_IDS = TEST_PROVIDERS.map((provider) => provider.id).toSorted((left, right) => + left.localeCompare(right), +); + function resolveExpectedWizardChoiceValues(providers: ProviderPlugin[]) { const values: string[] = []; @@ -43,6 +130,11 @@ function resolveExpectedWizardChoiceValues(providers: ProviderPlugin[]) { continue; } + if (provider.auth.length === 1) { + values.push(setup.choiceId?.trim() || provider.id); + continue; + } + values.push( ...provider.auth.map((method) => buildProviderPluginMethodChoice(provider.id, method.id)), ); @@ -73,15 +165,15 @@ function resolveExpectedModelPickerValues(providers: ProviderPlugin[]) { describe("provider wizard contract", () => { beforeEach(() => { resolvePluginProvidersMock.mockReset(); - resolvePluginProvidersMock.mockReturnValue(uniqueProviderContractProviders); + resolvePluginProvidersMock.mockReturnValue(TEST_PROVIDERS); }); - it("exposes every registered provider setup choice through the shared wizard layer", () => { + it("exposes every wizard setup choice through the shared wizard layer", () => { const options = resolveProviderWizardOptions({ config: { plugins: { enabled: true, - allow: providerContractPluginIds, + allow: TEST_PROVIDER_IDS, slots: { memory: "none", }, @@ -92,7 +184,7 @@ describe("provider wizard contract", () => { expect( options.map((option) => option.value).toSorted((left, right) => left.localeCompare(right)), - ).toEqual(resolveExpectedWizardChoiceValues(uniqueProviderContractProviders)); + ).toEqual(resolveExpectedWizardChoiceValues(TEST_PROVIDERS)); expect(options.map((option) => option.value)).toEqual([ ...new Set(options.map((option) => option.value)), ]); @@ -101,7 +193,7 @@ describe("provider wizard contract", () => { it("round-trips every shared wizard choice back to its provider and auth method", () => { for (const option of resolveProviderWizardOptions({ config: {}, env: process.env })) { const resolved = resolveProviderPluginChoice({ - providers: uniqueProviderContractProviders, + providers: TEST_PROVIDERS, choice: option.value, }); expect(resolved).not.toBeNull(); @@ -110,15 +202,15 @@ describe("provider wizard contract", () => { } }); - it("exposes every registered model-picker entry through the shared wizard layer", () => { + it("exposes every model-picker entry through the shared wizard layer", () => { const entries = resolveProviderModelPickerEntries({ config: {}, env: process.env }); expect( entries.map((entry) => entry.value).toSorted((left, right) => left.localeCompare(right)), - ).toEqual(resolveExpectedModelPickerValues(uniqueProviderContractProviders)); + ).toEqual(resolveExpectedModelPickerValues(TEST_PROVIDERS)); for (const entry of entries) { const resolved = resolveProviderPluginChoice({ - providers: uniqueProviderContractProviders, + providers: TEST_PROVIDERS, choice: entry.value, }); expect(resolved).not.toBeNull(); diff --git a/src/plugins/conversation-binding.test.ts b/src/plugins/conversation-binding.test.ts index 3cfc8cc2420..e13c2c2f67e 100644 --- a/src/plugins/conversation-binding.test.ts +++ b/src/plugins/conversation-binding.test.ts @@ -7,7 +7,7 @@ import type { SessionBindingAdapter, SessionBindingRecord, } from "../infra/outbound/session-binding-service.js"; -import { createEmptyPluginRegistry } from "./registry.js"; +import { createEmptyPluginRegistry } from "./registry-empty.js"; import { setActivePluginRegistry } from "./runtime.js"; const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-plugin-binding-"));