66} from "openclaw/plugin-sdk/channel-message" ;
77import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts" ;
88import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime" ;
9- import { resolveOutboundAttachmentFromUrl } from "openclaw/plugin-sdk/media-runtime" ;
9+ import { kindFromMime , resolveOutboundAttachmentFromUrl } from "openclaw/plugin-sdk/media-runtime" ;
1010import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime" ;
1111import { convertMarkdownTables } from "openclaw/plugin-sdk/text-chunking" ;
1212import { stripInlineDirectiveTagsForDelivery } from "openclaw/plugin-sdk/text-chunking" ;
@@ -47,6 +47,7 @@ type IMessageSendOpts = {
4747type IMessageSendResult = {
4848 messageId : string ;
4949 sentText : string ;
50+ echoText ?: string ;
5051 receipt : MessageReceipt ;
5152} ;
5253
@@ -93,6 +94,17 @@ function resolveMessageId(result: Record<string, unknown> | null | undefined): s
9394 return raw ? raw . trim ( ) : null ;
9495}
9596
97+ function resolveOutboundEchoText ( text : string , mediaContentType ?: string ) : string | undefined {
98+ if ( text . trim ( ) ) {
99+ return text ;
100+ }
101+ const kind = kindFromMime ( mediaContentType ?? undefined ) ;
102+ if ( ! kind ) {
103+ return undefined ;
104+ }
105+ return kind === "image" ? "<media:image>" : `<media:${ kind } >` ;
106+ }
107+
96108function createIMessageSendReceipt ( params : {
97109 messageId : string ;
98110 target : ReturnType < typeof parseIMessageTarget > ;
@@ -175,6 +187,7 @@ export async function sendMessageIMessage(
175187 : 16 * 1024 * 1024 ;
176188 let message = text ?? "" ;
177189 let filePath : string | undefined ;
190+ let mediaContentType : string | undefined ;
178191
179192 if ( opts . mediaUrl ?. trim ( ) ) {
180193 const resolveAttachmentFn = opts . resolveAttachmentImpl ?? resolveOutboundAttachmentFromUrl ;
@@ -183,6 +196,7 @@ export async function sendMessageIMessage(
183196 readFile : opts . mediaReadFile ,
184197 } ) ;
185198 filePath = resolved . path ;
199+ mediaContentType = resolved . contentType ?? undefined ;
186200 }
187201
188202 if ( ! message . trim ( ) && ! filePath ) {
@@ -211,6 +225,7 @@ export async function sendMessageIMessage(
211225 if ( ! message . trim ( ) && ! filePath ) {
212226 throw new Error ( "iMessage send requires text or media" ) ;
213227 }
228+ const echoText = resolveOutboundEchoText ( message , filePath ? mediaContentType : undefined ) ;
214229 const resolvedReplyToId = sanitizeReplyToId ( opts . replyToId ) ;
215230 const params : Record < string , unknown > = {
216231 text : message ,
@@ -253,7 +268,7 @@ export async function sendMessageIMessage(
253268 if ( echoScope ) {
254269 rememberPersistedIMessageEcho ( {
255270 scope : echoScope ,
256- text : message ,
271+ text : echoText ,
257272 messageId : resolvedId ?? undefined ,
258273 } ) ;
259274 }
@@ -280,6 +295,7 @@ export async function sendMessageIMessage(
280295 return {
281296 messageId,
282297 sentText : message ,
298+ ...( echoText ? { echoText } : { } ) ,
283299 receipt : createIMessageSendReceipt ( {
284300 messageId,
285301 target,
0 commit comments