/** * Inbound — TIM push message → standard OpenClaw MsgContext conversion * * This module converts the TIM SDK's RawPushMessage into the standard * MsgContext shape expected by channelRuntime.reply.dispatchReplyFromConfig(). * * v3.4.0: Added BodyForAgent, InboundHistory, GroupSystemPrompt, GroupSubject, * ConversationLabel, Surface, SenderId, RawBody, CommandBody to match * the official plugin contract (Signal, LINE, Discord). * See: docs/audit/008-inbound-context-gap.md * * Reference: docs/reference/plugins/openclaw-weixin/src/messaging/inbound.ts */ import type { RawPushMessage } from '../tim/client.js'; // ── Types ── /** History entry for InboundHistory field. */ export interface InboundHistoryEntry { sender: string; body: string; timestamp: number; } /** Extra context not available from the raw TIM message alone. */ export interface InboundExtras { /** Group display name (fetched from TIM group profile) */ groupName?: string; /** Group-level system prompt (from channel skill / notification field) */ groupSystemPrompt?: string; /** Recent message history for this channel */ inboundHistory?: InboundHistoryEntry[]; } /** * Standard inbound context for the OpenClaw pipeline. * Field names MUST match the OpenClaw MsgContext contract. */ export interface ClawlinkMsgContext { /** Message text body (may include envelope in future) */ Body: string; /** Current message original text — LLM prompt main body */ BodyForAgent: string; /** Raw message body for command detection */ RawBody: string; /** Command detection text */ CommandBody: string; /** Sender user ID */ From: string; /** Destination (channel ID for group chats) */ To: string; /** Account ID this message belongs to */ AccountId: string; /** Sender machine ID */ SenderId: string; /** Sender display name */ SenderName: string; /** Human-readable conversation label */ ConversationLabel: string; /** Group name / subject */ GroupSubject: string; /** Channel plugin identifier */ OriginatingChannel: 'clawlink'; /** Original destination identifier */ OriginatingTo: string; /** Unique message ID */ MessageSid: string; /** Message timestamp (ms) */ Timestamp?: number; /** Provider identifier */ Provider: 'clawlink'; /** Platform surface identifier */ Surface: 'clawlink'; /** Chat type: ClawLink is group-only */ ChatType: 'group'; /** Whether the message @mentioned our agent */ WasMentioned?: boolean; /** Session key override (set after resolveAgentRoute) */ SessionKey?: string; /** Whether sender is authorized for slash commands */ CommandAuthorized?: boolean; /** Group-level system prompt (channel skill / rules) */ GroupSystemPrompt?: string; /** Recent message history for this channel */ InboundHistory?: InboundHistoryEntry[]; } // ── Conversion ── /** * Convert a TIM RawPushMessage to the standard MsgContext. * * Mapping: * raw.text → Body, BodyForAgent, RawBody, CommandBody * raw.from → From, SenderId * raw.channelId → To, OriginatingTo * raw.nick → SenderName * raw.id → MessageSid * raw.time → Timestamp * raw.mentionsMe → WasMentioned * extras.groupName → GroupSubject, ConversationLabel * extras.groupSystemPrompt → GroupSystemPrompt * extras.inboundHistory → InboundHistory */ export function timMessageToMsgContext( raw: RawPushMessage, accountId: string, extras?: InboundExtras, ): ClawlinkMsgContext { const senderName = raw.nick || raw.from; const groupName = extras?.groupName || raw.channelId; return { // ── Message body (4 variants per official contract) ── Body: raw.text, BodyForAgent: raw.text, RawBody: raw.text, CommandBody: raw.text, // ── Routing ── From: raw.from, To: raw.channelId, AccountId: accountId, // ── Sender info ── SenderId: raw.from, SenderName: senderName, // ── Group info ── ConversationLabel: groupName, GroupSubject: groupName, // ── Channel metadata ── OriginatingChannel: 'clawlink', OriginatingTo: raw.channelId, MessageSid: raw.id, Timestamp: raw.time, Provider: 'clawlink', Surface: 'clawlink', ChatType: 'group', // ── Flags ── WasMentioned: raw.mentionsMe, // ── Enriched context (from extras) ── GroupSystemPrompt: extras?.groupSystemPrompt || undefined, InboundHistory: extras?.inboundHistory, }; }