diff --git a/extensions/discord/contract-api.ts b/extensions/discord/contract-api.ts index 8659ae168fd..ddaace1ca57 100644 --- a/extensions/discord/contract-api.ts +++ b/extensions/discord/contract-api.ts @@ -13,7 +13,6 @@ export { unsupportedSecretRefSurfacePatterns, collectUnsupportedSecretRefConfigCandidates, } from "./src/security-contract.js"; -export { deriveLegacySessionChatType } from "./src/session-contract.js"; export type { DiscordInteractiveHandlerContext, DiscordInteractiveHandlerRegistration, diff --git a/extensions/discord/src/session-contract.ts b/extensions/discord/src/session-contract.ts deleted file mode 100644 index 00b66226902..00000000000 --- a/extensions/discord/src/session-contract.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function deriveLegacySessionChatType(sessionKey: string): "channel" | undefined { - return /^discord:(?:[^:]+:)?guild-[^:]+:channel-[^:]+$/.test(sessionKey) ? "channel" : undefined; -} diff --git a/extensions/discord/src/shared.ts b/extensions/discord/src/shared.ts index acdb136137f..2f7860dae5d 100644 --- a/extensions/discord/src/shared.ts +++ b/extensions/discord/src/shared.ts @@ -29,7 +29,6 @@ import { unsupportedSecretRefSurfacePatterns, } from "./security-contract.js"; import { discordSecurityAdapter } from "./security.js"; -import { deriveLegacySessionChatType } from "./session-contract.js"; const DISCORD_CHANNEL = "discord" as const; @@ -160,9 +159,6 @@ export function createDiscordPluginBase(params: { }, }), }, - messaging: { - deriveLegacySessionChatType, - }, security: discordSecurityAdapter, secrets: { secretTargetRegistryEntries, diff --git a/extensions/whatsapp/contract-api.ts b/extensions/whatsapp/contract-api.ts index 7ba0e12d044..66859646afe 100644 --- a/extensions/whatsapp/contract-api.ts +++ b/extensions/whatsapp/contract-api.ts @@ -1,5 +1,5 @@ import { whatsappCommandPolicy as whatsappCommandPolicyImpl } from "./src/command-policy.js"; -import { resolveLegacyGroupSessionKey as resolveLegacyGroupSessionKeyImpl } from "./src/group-session-contract.js"; +import { resolveGroupSessionKey as resolveGroupSessionKeyImpl } from "./src/group-session-contract.js"; import { __testing as whatsappAccessControlTestingImpl } from "./src/inbound/access-control.js"; import { isWhatsAppGroupJid as isWhatsAppGroupJidImpl, @@ -10,20 +10,14 @@ export { listWhatsAppDirectoryPeersFromConfig, } from "./src/directory-config.js"; import { resolveWhatsAppRuntimeGroupPolicy as resolveWhatsAppRuntimeGroupPolicyImpl } from "./src/runtime-group-policy.js"; -import { - canonicalizeLegacySessionKey as canonicalizeLegacySessionKeyImpl, - isLegacyGroupSessionKey as isLegacyGroupSessionKeyImpl, -} from "./src/session-contract.js"; export { collectUnsupportedSecretRefConfigCandidates, unsupportedSecretRefSurfacePatterns, } from "./src/security-contract.js"; -export const canonicalizeLegacySessionKey = canonicalizeLegacySessionKeyImpl; -export const isLegacyGroupSessionKey = isLegacyGroupSessionKeyImpl; export const isWhatsAppGroupJid = isWhatsAppGroupJidImpl; export const normalizeWhatsAppTarget = normalizeWhatsAppTargetImpl; -export const resolveLegacyGroupSessionKey = resolveLegacyGroupSessionKeyImpl; +export const resolveGroupSessionKey = resolveGroupSessionKeyImpl; export const resolveWhatsAppRuntimeGroupPolicy = resolveWhatsAppRuntimeGroupPolicyImpl; export const whatsappAccessControlTesting = whatsappAccessControlTestingImpl; export const whatsappCommandPolicy = whatsappCommandPolicyImpl; diff --git a/extensions/whatsapp/src/group-session-contract.ts b/extensions/whatsapp/src/group-session-contract.ts index 00c9cf5f5f0..d24a3e4fab0 100644 --- a/extensions/whatsapp/src/group-session-contract.ts +++ b/extensions/whatsapp/src/group-session-contract.ts @@ -1,6 +1,6 @@ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime"; -export function resolveLegacyGroupSessionKey(ctx: { From?: string }): { +export function resolveGroupSessionKey(ctx: { From?: string }): { key: string; channel: string; id: string; diff --git a/extensions/whatsapp/src/session-contract.test.ts b/extensions/whatsapp/src/session-contract.test.ts index 03fe2be8675..f600a7c18cb 100644 --- a/extensions/whatsapp/src/session-contract.test.ts +++ b/extensions/whatsapp/src/session-contract.test.ts @@ -1,9 +1,5 @@ import { describe, expect, it } from "vitest"; -import { - canonicalizeLegacySessionKey, - deriveLegacySessionChatType, - isLegacyGroupSessionKey, -} from "./session-contract.js"; +import { canonicalizeLegacySessionKey, isLegacyGroupSessionKey } from "./session-contract.js"; describe("whatsapp legacy session contract", () => { it("canonicalizes legacy WhatsApp group keys to channel-qualified agent keys", () => { @@ -20,12 +16,11 @@ describe("whatsapp legacy session contract", () => { it("does not claim generic non-WhatsApp group keys", () => { expect(isLegacyGroupSessionKey("group:abc")).toBe(false); - expect(deriveLegacySessionChatType("group:abc")).toBeUndefined(); expect(canonicalizeLegacySessionKey({ key: "group:abc", agentId: "main" })).toBeNull(); }); - it("derives chat type for legacy WhatsApp group keys", () => { - expect(deriveLegacySessionChatType("123@g.us")).toBe("group"); - expect(deriveLegacySessionChatType("whatsapp:123@g.us")).toBe("group"); + it("identifies legacy WhatsApp group keys for doctor migration", () => { + expect(isLegacyGroupSessionKey("123@g.us")).toBe(true); + expect(isLegacyGroupSessionKey("whatsapp:123@g.us")).toBe(true); }); }); diff --git a/extensions/whatsapp/src/session-contract.ts b/extensions/whatsapp/src/session-contract.ts index e5c3d4fc751..5e7f456f33f 100644 --- a/extensions/whatsapp/src/session-contract.ts +++ b/extensions/whatsapp/src/session-contract.ts @@ -28,10 +28,6 @@ export function isLegacyGroupSessionKey(key: string): boolean { return extractLegacyWhatsAppGroupId(key) !== null; } -export function deriveLegacySessionChatType(key: string): "group" | undefined { - return isLegacyGroupSessionKey(key) ? "group" : undefined; -} - export function canonicalizeLegacySessionKey(params: { key: string; agentId: string; diff --git a/extensions/whatsapp/src/shared.ts b/extensions/whatsapp/src/shared.ts index b3ff58ddbc1..556423b9e1d 100644 --- a/extensions/whatsapp/src/shared.ts +++ b/extensions/whatsapp/src/shared.ts @@ -26,17 +26,12 @@ import { import { formatWhatsAppConfigAllowFromEntries } from "./config-accessors.js"; import { WhatsAppChannelConfigSchema } from "./config-schema.js"; import { whatsappDoctor } from "./doctor.js"; -import { resolveLegacyGroupSessionKey } from "./group-session-contract.js"; +import { resolveGroupSessionKey } from "./group-session-contract.js"; import { collectUnsupportedSecretRefConfigCandidates, unsupportedSecretRefSurfacePatterns, } from "./security-contract.js"; import { applyWhatsAppSecurityConfigFixes } from "./security-fix.js"; -import { - canonicalizeLegacySessionKey, - deriveLegacySessionChatType, - isLegacyGroupSessionKey, -} from "./session-contract.js"; const WHATSAPP_CHANNEL = "whatsapp" as const; @@ -259,11 +254,7 @@ export function createWhatsAppPluginBase(params: { config: base.config!, messaging: { defaultMarkdownTableMode: "bullets", - deriveLegacySessionChatType, - resolveLegacyGroupSessionKey, - isLegacyGroupSessionKey, - canonicalizeLegacySessionKey: (params) => - canonicalizeLegacySessionKey({ key: params.key, agentId: params.agentId }), + resolveGroupSessionKey, }, secrets: { unsupportedSecretRefSurfacePatterns, diff --git a/src/channels/plugins/bundled.ts b/src/channels/plugins/bundled.ts index 4d7f3a10447..8fa8e613524 100644 --- a/src/channels/plugins/bundled.ts +++ b/src/channels/plugins/bundled.ts @@ -729,11 +729,7 @@ export function listBundledChannelLegacySessionSurfaces( if (surface) { return [surface]; } - if (!hasSetupEntryFeature(setupEntry, "legacySessionSurfaces")) { - return []; - } - const plugin = getBundledChannelSetupPluginForRoot(id, rootScope, loadContext); - return plugin?.messaging ? [plugin.messaging] : []; + return []; }); } diff --git a/src/channels/plugins/types.core.ts b/src/channels/plugins/types.core.ts index 30631d2729d..b5851f22c96 100644 --- a/src/channels/plugins/types.core.ts +++ b/src/channels/plugins/types.core.ts @@ -515,13 +515,7 @@ export type ChannelMessagingAdapter = { sessionKey: string; ctx: MsgContext; }) => string | undefined; - deriveLegacySessionChatType?: (sessionKey: string) => "direct" | "group" | "channel" | undefined; - isLegacyGroupSessionKey?: (key: string) => boolean; - canonicalizeLegacySessionKey?: (params: { - key: string; - agentId: string; - }) => string | null | undefined; - resolveLegacyGroupSessionKey?: (ctx: MsgContext) => { + resolveGroupSessionKey?: (ctx: MsgContext) => { key: string; channel: string; id: string; diff --git a/src/config/sessions/group.ts b/src/config/sessions/group.ts index d8d52c0fd86..b3091e96f32 100644 --- a/src/config/sessions/group.ts +++ b/src/config/sessions/group.ts @@ -11,15 +11,15 @@ import type { GroupKeyResolution } from "./types.js"; const getGroupSurfaces = () => new Set([...listDeliverableMessageChannels(), "webchat"]); -type LegacyGroupSessionSurface = { - resolveLegacyGroupSessionKey?: (ctx: MsgContext) => GroupKeyResolution | null; +type GroupSessionSurface = { + resolveGroupSessionKey?: (ctx: MsgContext) => GroupKeyResolution | null; }; -function resolveLegacyGroupSessionKey(ctx: MsgContext): GroupKeyResolution | null { +function resolvePluginGroupSessionKey(ctx: MsgContext): GroupKeyResolution | null { for (const plugin of listChannelPlugins()) { const resolved = ( - plugin.messaging as LegacyGroupSessionSurface | undefined - )?.resolveLegacyGroupSessionKey?.(ctx); + plugin.messaging as GroupSessionSurface | undefined + )?.resolveGroupSessionKey?.(ctx); if (resolved) { return resolved; } @@ -107,13 +107,13 @@ export function resolveGroupSessionKey(ctx: MsgContext): GroupKeyResolution | nu const normalizedChatType = chatType === "channel" ? "channel" : chatType === "group" ? "group" : undefined; - const legacyResolution = resolveLegacyGroupSessionKey(ctx); + const pluginResolution = resolvePluginGroupSessionKey(ctx); const looksLikeGroup = normalizedChatType === "group" || normalizedChatType === "channel" || from.includes(":group:") || from.includes(":channel:") || - legacyResolution !== null; + pluginResolution !== null; if (!looksLikeGroup) { return null; } @@ -124,11 +124,11 @@ export function resolveGroupSessionKey(ctx: MsgContext): GroupKeyResolution | nu const head = normalizeLowercaseStringOrEmpty(parts[0]); const headIsSurface = head ? getGroupSurfaces().has(head) : false; - if (!headIsSurface && !providerHint && legacyResolution) { - return legacyResolution; + if (!headIsSurface && !providerHint && pluginResolution) { + return pluginResolution; } - const provider = headIsSurface ? head : (providerHint ?? legacyResolution?.channel); + const provider = headIsSurface ? head : (providerHint ?? pluginResolution?.channel); if (!provider) { return null; } diff --git a/src/sessions/session-chat-type-shared.ts b/src/sessions/session-chat-type-shared.ts index 8c232f1f516..81e20725373 100644 --- a/src/sessions/session-chat-type-shared.ts +++ b/src/sessions/session-chat-type-shared.ts @@ -3,21 +3,7 @@ import { parseAgentSessionKey } from "./session-key-utils.js"; export type SessionKeyChatType = "direct" | "group" | "channel" | "unknown"; -function deriveBuiltInLegacySessionChatType( - scopedSessionKey: string, -): SessionKeyChatType | undefined { - if (/^group:[^:]+$/.test(scopedSessionKey)) { - return "group"; - } - return undefined; -} - -export function deriveSessionChatTypeFromScopedKey( - scopedSessionKey: string, - deriveLegacySessionChatTypes: Array< - (scopedSessionKey: string) => SessionKeyChatType | undefined - > = [], -): SessionKeyChatType { +export function deriveSessionChatTypeFromScopedKey(scopedSessionKey: string): SessionKeyChatType { const tokens = new Set(scopedSessionKey.split(":").filter(Boolean)); if (tokens.has("group")) { return "group"; @@ -28,32 +14,17 @@ export function deriveSessionChatTypeFromScopedKey( if (tokens.has("direct") || tokens.has("dm")) { return "direct"; } - const builtInLegacy = deriveBuiltInLegacySessionChatType(scopedSessionKey); - if (builtInLegacy) { - return builtInLegacy; - } - for (const deriveLegacySessionChatType of deriveLegacySessionChatTypes) { - const derived = deriveLegacySessionChatType(scopedSessionKey); - if (derived) { - return derived; - } - } return "unknown"; } -/** - * Best-effort chat-type extraction from session keys across canonical and legacy formats. - */ +/** Best-effort chat-type extraction from canonical session keys. */ export function deriveSessionChatTypeFromKey( sessionKey: string | undefined | null, - deriveLegacySessionChatTypes: Array< - (scopedSessionKey: string) => SessionKeyChatType | undefined - > = [], ): SessionKeyChatType { const raw = normalizeLowercaseStringOrEmpty(sessionKey); if (!raw) { return "unknown"; } const scoped = parseAgentSessionKey(raw)?.rest ?? raw; - return deriveSessionChatTypeFromScopedKey(scoped, deriveLegacySessionChatTypes); + return deriveSessionChatTypeFromScopedKey(scoped); } diff --git a/src/sessions/session-chat-type.ts b/src/sessions/session-chat-type.ts index a90ed8ab01d..2cd987cbcfa 100644 --- a/src/sessions/session-chat-type.ts +++ b/src/sessions/session-chat-type.ts @@ -1,65 +1,13 @@ -import { getBootstrapChannelPlugin } from "../channels/plugins/bootstrap-registry.js"; -import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; import { deriveSessionChatTypeFromKey, type SessionKeyChatType, } from "./session-chat-type-shared.js"; -import { parseAgentSessionKey } from "./session-key-utils.js"; export { deriveSessionChatTypeFromKey, type SessionKeyChatType, } from "./session-chat-type-shared.js"; -type LegacySessionChatTypeDeriver = NonNullable< - NonNullable>["messaging"] ->["deriveLegacySessionChatType"]; - -function resolveScopedSessionKey(sessionKey: string | undefined | null): string { - const raw = normalizeLowercaseStringOrEmpty(sessionKey); - if (!raw) { - return ""; - } - return parseAgentSessionKey(raw)?.rest ?? raw; -} - -function collectLegacyChatTypeCandidatePluginIds(scopedSessionKey: string): string[] { - const ids = new Set(); - const firstToken = scopedSessionKey.split(":").find(Boolean); - if (firstToken) { - ids.add(firstToken); - } - if (scopedSessionKey.includes("@g.us")) { - ids.add("whatsapp"); - } - return Array.from(ids); -} - -function derivePluginLegacySessionChatType( - scopedSessionKey: string, - deriveLegacySessionChatType: LegacySessionChatTypeDeriver, -): SessionKeyChatType | undefined { - if (!deriveLegacySessionChatType) { - return undefined; - } - return deriveLegacySessionChatType(scopedSessionKey); -} - export function deriveSessionChatType(sessionKey: string | undefined | null): SessionKeyChatType { - const builtInType = deriveSessionChatTypeFromKey(sessionKey); - if (builtInType !== "unknown") { - return builtInType; - } - - const scopedSessionKey = resolveScopedSessionKey(sessionKey); - for (const pluginId of collectLegacyChatTypeCandidatePluginIds(scopedSessionKey)) { - const derived = derivePluginLegacySessionChatType( - scopedSessionKey, - getBootstrapChannelPlugin(pluginId)?.messaging?.deriveLegacySessionChatType, - ); - if (derived) { - return derived; - } - } - return "unknown"; + return deriveSessionChatTypeFromKey(sessionKey); }