diff --git a/docs/tools/plugin.md b/docs/tools/plugin.md index 9aa0936da13..0cc98187550 100644 --- a/docs/tools/plugin.md +++ b/docs/tools/plugin.md @@ -1140,6 +1140,54 @@ authoring plugins: `openclaw/plugin-sdk/twitch`, `openclaw/plugin-sdk/voice-call`, `openclaw/plugin-sdk/zalo`, and `openclaw/plugin-sdk/zalouser`. +## Channel target resolution + +Channel plugins should own channel-specific target semantics. Keep the shared +outbound host generic and use the messaging adapter surface for provider rules: + +- `messaging.inferTargetChatType({ to })` decides whether a normalized target + should be treated as `direct`, `group`, or `channel` before directory lookup. +- `messaging.targetResolver.looksLikeId(raw, normalized)` tells core whether an + input should skip straight to id-like resolution instead of directory search. +- `messaging.targetResolver.resolveTarget(...)` is the plugin fallback when + core needs a final provider-owned resolution after normalization or after a + directory miss. +- `messaging.resolveOutboundSessionRoute(...)` owns provider-specific session + route construction once a target is resolved. + +Recommended split: + +- Use `inferTargetChatType` for category decisions that should happen before + searching peers/groups. +- Use `looksLikeId` for “treat this as an explicit/native target id” checks. +- Use `resolveTarget` for provider-specific normalization fallback, not for + broad directory search. +- Keep provider-native ids like chat ids, thread ids, JIDs, handles, and room + ids inside `target` values or provider-specific params, not in generic SDK + fields. + +## Config-backed directories + +Plugins that derive directory entries from config should keep that logic in the +plugin and reuse the shared helpers from +`openclaw/plugin-sdk/directory-runtime`. + +Use this when a channel needs config-backed peers/groups such as: + +- allowlist-driven DM peers +- configured channel/group maps +- account-scoped static directory fallbacks + +The shared helpers in `directory-runtime` only handle generic operations: + +- query filtering +- limit application +- deduping/normalization helpers +- building `ChannelDirectoryEntry[]` + +Channel-specific account inspection and id normalization should stay in the +plugin implementation. + ## Provider catalogs Provider plugins can define model catalogs for inference with diff --git a/src/channels/plugins/types.core.ts b/src/channels/plugins/types.core.ts index 668a47c750b..ed6191ce1c4 100644 --- a/src/channels/plugins/types.core.ts +++ b/src/channels/plugins/types.core.ts @@ -392,6 +392,10 @@ export type ChannelMessagingAdapter = { threadId?: string | number; chatType?: ChatType; } | null; + /** + * Lightweight chat-type inference used before directory lookup so plugins can + * steer peer-vs-group resolution without reimplementing host search flow. + */ inferTargetChatType?: (params: { to: string }) => ChatType | undefined; buildCrossContextComponents?: ChannelCrossContextComponentsFactory; enableInteractiveReplies?: (params: { @@ -402,6 +406,10 @@ export type ChannelMessagingAdapter = { targetResolver?: { looksLikeId?: (raw: string, normalized?: string) => boolean; hint?: string; + /** + * Plugin-owned fallback for explicit/native targets or post-directory-miss + * resolution. This should complement directory lookup, not duplicate it. + */ resolveTarget?: (params: { cfg: OpenClawConfig; accountId?: string | null; @@ -420,6 +428,10 @@ export type ChannelMessagingAdapter = { display?: string; kind?: ChannelDirectoryEntryKind; }) => string; + /** + * Provider-specific session-route builder used after target resolution. + * Keep session-key orchestration in core and channel-native routing rules here. + */ resolveOutboundSessionRoute?: (params: { cfg: OpenClawConfig; agentId: string;