@openclaw/imessage 2026.9.1 → 2026.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/{actions.runtime-DCjNT81u.js → actions.runtime-CzvR20qT.js} +2 -2
  2. package/dist/api.js +3 -3
  3. package/dist/{approval-handler.runtime-BReeID1z.js → approval-handler.runtime-B6JGnxQf.js} +5 -5
  4. package/dist/{approval-reactions-DcLRPx7O.js → approval-reactions-BM_AwHPq.js} +46 -85
  5. package/dist/{channel-zDmMTHWD.js → channel-B6qtBfCj.js} +21 -14
  6. package/dist/channel-config-api.js +1 -1
  7. package/dist/channel-plugin-api.js +1 -1
  8. package/dist/{channel.runtime-qQBHCle8.js → channel.runtime-C2lcFXWn.js} +4 -8
  9. package/dist/{config-schema-DuGbA8Kp.js → config-schema-CCbTcJEV.js} +10 -37
  10. package/dist/{group-policy-BWqG2esy.js → group-policy-Duro87b4.js} +2 -2
  11. package/dist/{monitor-AmSj6QIH.js → monitor-Dj3TAimI.js} +41 -43
  12. package/dist/{question-reactions-viP7ZcyG.js → question-reactions-9tBYB1_F.js} +4 -7
  13. package/dist/{reaction-context-BAYI7pz0.js → reaction-context-Br-vAJFp.js} +9 -3
  14. package/dist/{remote-file-COTN7196.js → remote-file-5n5LznCa.js} +3 -52
  15. package/dist/runtime-api.js +5 -5
  16. package/dist/{sanitize-outbound-CN3Hxbjy.js → sanitize-outbound-C6UF2cvr.js} +124 -51
  17. package/dist/{send-v0blZfaI.js → send-CIHiLtcY.js} +5 -4
  18. package/node_modules/typebox/build/format/json_pointer.d.mts +2 -2
  19. package/node_modules/typebox/build/format/json_pointer.mjs +2 -2
  20. package/node_modules/typebox/build/format/json_pointer_uri_fragment.d.mts +2 -2
  21. package/node_modules/typebox/build/format/json_pointer_uri_fragment.mjs +2 -2
  22. package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
  23. package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
  24. package/node_modules/typebox/build/system/memory/assign.mjs +2 -1
  25. package/node_modules/typebox/build/system/memory/create.mjs +3 -4
  26. package/node_modules/typebox/build/system/memory/discard.mjs +2 -2
  27. package/node_modules/typebox/build/system/memory/freeze.d.mts +4 -0
  28. package/node_modules/typebox/build/system/memory/freeze.mjs +6 -0
  29. package/node_modules/typebox/build/system/memory/update.mjs +3 -2
  30. package/node_modules/typebox/package.json +1 -1
  31. package/openclaw.plugin.json +7 -2
  32. package/package.json +5 -5
@@ -1,15 +1,15 @@
1
1
  import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount } from "./accounts-CCirBEIm.js";
2
2
  import { c as parseIMessageAllowTarget, f as getCachedIMessagePrivateApiStatus, i as isAllowedIMessageReplyContextSender, l as parseIMessageTarget, n as formatIMessageChatTarget, p as imessageRpcSupportsMethod, s as normalizeIMessageHandle } from "./message-tool-api-CUoFESHj.js";
3
- import { l as isKnownFromMeIMessageMessageId, o as createIMessageRpcClient, r as sanitizeOutboundText, u as rememberIMessageReplyCache, v as resolveIMessageDirectChatService } from "./sanitize-outbound-CN3Hxbjy.js";
4
- import { f as probeIMessage, r as resolveIMessageInboundConversationId } from "./group-policy-BWqG2esy.js";
3
+ import { d as rememberIMessageReplyCache, o as createIMessageRpcClient, r as sanitizeOutboundText, u as isKnownFromMeIMessageMessageId, y as resolveIMessageDirectChatService } from "./sanitize-outbound-C6UF2cvr.js";
4
+ import { f as probeIMessage, r as resolveIMessageInboundConversationId } from "./group-policy-Duro87b4.js";
5
5
  import { t as getIMessageRuntime } from "./runtime-Cza4CY5T.js";
6
6
  import { d as resolveIMessageEchoMediaKey, l as capFailureRetriesMap, t as IMESSAGE_CATCHUP_CURSOR_NAMESPACE, u as resolveIMessageCatchupCursorKey } from "./state-contract-DvahEY4X.js";
7
7
  import { n as resolveIMessageAttachmentRoots, r as resolveIMessageRemoteAttachmentRoots } from "./media-contract-pns3TSS5.js";
8
- import { i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction, s as buildIMessageApprovalConversationKeyForInbound } from "./approval-reactions-DcLRPx7O.js";
9
- import { c as iMessageApprovalControlBindings, n as hasPersistedIMessageEcho, r as stripLeadingEchoTextCorruptionMarkers, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-v0blZfaI.js";
10
- import { t as resolveIMessageReactionContext } from "./reaction-context-BAYI7pz0.js";
11
- import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-viP7ZcyG.js";
12
- import { bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
8
+ import { i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction, s as buildIMessageApprovalConversationKeyForInbound } from "./approval-reactions-BM_AwHPq.js";
9
+ import { c as iMessageApprovalControlBindings, n as hasPersistedIMessageEcho, r as stripLeadingEchoTextCorruptionMarkers, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-CIHiLtcY.js";
10
+ import { n as normalizeIMessageGuid, t as resolveIMessageReactionContext } from "./reaction-context-Br-vAJFp.js";
11
+ import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-9tBYB1_F.js";
12
+ import { bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
13
13
  import { chunkMarkdownTextWithMode as chunkTextWithMode, resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
14
14
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
15
15
  import path from "node:path";
@@ -20,12 +20,12 @@ import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, warn } f
20
20
  import { isInboundPathAllowed, kindFromMime } from "openclaw/plugin-sdk/media-runtime";
21
21
  import { asDateTimestampMs, asPositiveFiniteNumber, parseDateStringTimestampMs, resolveIntegerOption, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
22
22
  import { createHash } from "node:crypto";
23
- import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
23
+ import { createRuntimeConfigReader, getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
24
24
  import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
25
25
  import { collectErrorGraphCandidates, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
26
- import { buildChannelGroupsScopeTree, resolveChannelGroupPolicy, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
26
+ import { buildChannelGroupsScopeTree, resolveChannelGroupPolicy, resolveChannelGroups, resolveChannelGroupsConfigPath, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
27
27
  import { convertMarkdownTables as convertMarkdownTables$1, findCodeRegions, isInsideCode, sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
28
- import { buildChannelInboundEventContext, buildMentionRegexes, createChannelInboundDebouncer, createChannelPartialDeliveryError, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundFromLabel, formatInboundMediaUnavailableText, formatMediaPlaceholderText, isChannelPartialDeliveryError, logInboundDrop, matchesMentionPatterns, resolveEnvelopeFormatOptions, resolveInboundMentionDecision, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, shouldDebounceTextInbound, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
28
+ import { buildChannelInboundEventContext, buildMentionRegexes, createAcceptedChannelDeliveryResult, createChannelInboundDebouncer, createChannelPartialDeliveryError, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundFromLabel, formatInboundMediaUnavailableText, formatMediaPlaceholderText, isChannelPartialDeliveryError, logInboundDrop, matchesMentionPatterns, resolveEnvelopeFormatOptions, resolveInboundDebounceMs, resolveInboundMentionDecision, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, shouldDebounceTextInbound, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
29
29
  import { resolveAgentConfig, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
30
30
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-runtime";
31
31
  import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
@@ -73,13 +73,10 @@ function hasUnscopedTarget(targets) {
73
73
  function uniqueChatIds(chatIds) {
74
74
  return [...new Set(chatIds)];
75
75
  }
76
- function normalizeMessageGuid(value) {
77
- return value.trim().replace(/^p:\d+\//iu, "");
78
- }
79
76
  function enumerateMessageGuidCandidates(value) {
80
77
  const trimmed = value.trim();
81
78
  if (!trimmed) return [];
82
- return [trimmed, normalizeMessageGuid(trimmed)].filter((candidate, index, candidates) => candidate.length > 0 && candidates.indexOf(candidate) === index);
79
+ return [trimmed, normalizeIMessageGuid(trimmed)].filter((candidate, index, candidates) => candidate.length > 0 && candidates.indexOf(candidate) === index);
83
80
  }
84
81
  function buildPendingTargetsByMessageId(targets) {
85
82
  const pendingByMessageId = /* @__PURE__ */ new Map();
@@ -170,7 +167,7 @@ async function pollPendingIMessageApprovalReactions(params) {
170
167
  for (const message of messages) {
171
168
  const targetGuid = message.guid?.trim();
172
169
  if (!targetGuid) continue;
173
- const target = pendingByMessageId.get(targetGuid) ?? pendingByMessageId.get(normalizeMessageGuid(targetGuid));
170
+ const target = pendingByMessageId.get(targetGuid) ?? pendingByMessageId.get(normalizeIMessageGuid(targetGuid));
174
171
  if (!target) continue;
175
172
  bindObservedConversation({
176
173
  target,
@@ -994,32 +991,24 @@ async function deliverIMessageReply(params) {
994
991
  } catch (error) {
995
992
  const partial = isChannelPartialDeliveryError(error) ? error.deliveryResult : void 0;
996
993
  if (accepted.length === 0 && partial?.visibleReplySent !== true) throw error;
997
- const receipt = createMessageReceiptFromOutboundResults({
998
- results: [...accepted.map((result) => ({ receipt: result.receipt })), ...partial?.receipt ? [{ receipt: partial.receipt }] : (partial?.messageIds ?? []).map((messageId) => ({ messageId }))],
999
- kind: reply.mediaUrls.length > 0 ? "media" : "text"
1000
- });
1001
- throw createChannelPartialDeliveryError(error, {
1002
- messageIds: listMessageReceiptPlatformIds(receipt),
1003
- receipt,
1004
- visibleReplySent: true,
994
+ throw createChannelPartialDeliveryError(error, createAcceptedChannelDeliveryResult({
995
+ results: accepted.map((result) => ({ receipt: result.receipt })),
996
+ deliveryResults: partial ? [partial] : [],
997
+ kind: reply.mediaUrls.length > 0 ? "media" : "text",
1005
998
  content: [...accepted.map((result) => result.sentText), partial?.content].filter(Boolean).join("\n")
1006
- });
999
+ }));
1007
1000
  }
1008
1001
  if (delivered === "empty") return {
1009
1002
  visibleReplySent: false,
1010
1003
  suppression: { reason: "no_visible_result" }
1011
1004
  };
1012
- const receipt = createMessageReceiptFromOutboundResults({
1005
+ const deliveryResult = createAcceptedChannelDeliveryResult({
1013
1006
  results: accepted.map((result) => ({ receipt: result.receipt })),
1014
- kind: delivered
1007
+ kind: delivered,
1008
+ content: accepted.map((result) => result.sentText).filter(Boolean).join("\n")
1015
1009
  });
1016
1010
  runtime.log?.(`imessage: delivered reply to ${target}`);
1017
- return {
1018
- messageIds: listMessageReceiptPlatformIds(receipt),
1019
- receipt,
1020
- visibleReplySent: true,
1021
- content: accepted.map((result) => result.sentText).filter(Boolean).join("\n")
1022
- };
1011
+ return deliveryResult;
1023
1012
  }
1024
1013
  function createIMessageEchoCachingSend(params) {
1025
1014
  return async (target, text, opts) => {
@@ -2799,17 +2788,16 @@ const IMESSAGE_DIAGNOSTIC_DROP_REASONS = /* @__PURE__ */ new Set([
2799
2788
  "agent echo in self-chat",
2800
2789
  "echo",
2801
2790
  "from me",
2791
+ "no mention",
2802
2792
  "reflected assistant content",
2803
2793
  "self-chat echo"
2804
2794
  ]);
2805
- const IMESSAGE_THROTTLED_DIAGNOSTIC_DROP_REASONS = /* @__PURE__ */ new Set(["from me"]);
2806
- function shouldThrottleIMessageInboundDropDiagnostic(reason) {
2807
- return IMESSAGE_THROTTLED_DIAGNOSTIC_DROP_REASONS.has(reason);
2808
- }
2795
+ const IMESSAGE_THROTTLED_DIAGNOSTIC_DROP_REASONS = /* @__PURE__ */ new Set(["from me", "no mention"]);
2809
2796
  function describeIMessageInboundDropDiagnostic(params) {
2810
2797
  if (!IMESSAGE_DIAGNOSTIC_DROP_REASONS.has(params.reason)) return null;
2811
2798
  const messageId = typeof params.message.id === "number" || typeof params.message.id === "string" ? String(params.message.id) : "unknown";
2812
- return `imessage: dropped inbound message account=${params.accountId} reason=${JSON.stringify(params.reason)} chat_id=${params.message.chat_id ?? "unknown"} group=${params.message.is_group === true} message_id=${messageId} guid=${params.message.guid ? "present" : "missing"} created_at=${params.message.created_at ?? "unknown"}`;
2799
+ const mentionHint = params.reason === "no mention" ? ` Mention the agent (default patterns come from its identity name/emoji), or set ${params.groupsConfigPath}["${params.message.chat_id}"].requireMention=false. Preserve existing groups entries; when adding the first groups map, include "*": {} to keep other chats admitted.` : "";
2800
+ return `imessage: dropped inbound message account=${params.accountId} reason=${JSON.stringify(params.reason)} chat_id=${params.message.chat_id ?? "unknown"} group=${params.message.is_group === true} message_id=${messageId} guid=${params.message.guid ? "present" : "missing"} created_at=${params.message.created_at ?? "unknown"}${mentionHint}`;
2813
2801
  }
2814
2802
  function describeIMessageWatchSubscribeStartupFailure(params) {
2815
2803
  const retry = params.retryDelayMs !== void 0 ? ` retry_in_ms=${params.retryDelayMs}` : "";
@@ -2833,10 +2821,17 @@ async function waitForWatchSubscribeRetryDelay(params) {
2833
2821
  async function monitorIMessageProvider(opts = {}) {
2834
2822
  const runtime = resolveRuntime(opts);
2835
2823
  const cfg = opts.config ?? getRuntimeConfig();
2824
+ const readConfig = createRuntimeConfigReader(cfg);
2836
2825
  const accountInfo = resolveIMessageAccount({
2837
2826
  cfg,
2838
2827
  accountId: opts.accountId
2839
2828
  });
2829
+ const groupsConfigPath = resolveChannelGroupsConfigPath({
2830
+ cfg,
2831
+ channel: "imessage",
2832
+ accountId: accountInfo.accountId,
2833
+ groups: resolveChannelGroups(cfg, "imessage", accountInfo.accountId)
2834
+ });
2840
2835
  const approvalGatewayRuntime = opts.channelRuntime?.runtimeContexts.get({
2841
2836
  channelId: "imessage",
2842
2837
  accountId: accountInfo.accountId,
@@ -2941,6 +2936,10 @@ async function monitorIMessageProvider(opts = {}) {
2941
2936
  const { debouncer: inboundDebouncer } = createChannelInboundDebouncer({
2942
2937
  cfg,
2943
2938
  channel: "imessage",
2939
+ resolveDebounceMs: () => resolveInboundDebounceMs({
2940
+ cfg: readConfig(),
2941
+ channel: "imessage"
2942
+ }),
2944
2943
  buildKey: (entry) => {
2945
2944
  const msg = entry.message;
2946
2945
  const sender = msg.sender?.trim();
@@ -3041,9 +3040,6 @@ async function monitorIMessageProvider(opts = {}) {
3041
3040
  runtime.error?.(`imessage catchup: failed to advance pending live cursor: ${String(err)}`);
3042
3041
  }
3043
3042
  }
3044
- async function handleMessageNow(message, ingressLifecycle) {
3045
- await handleMessageNowInner(message, ingressLifecycle);
3046
- }
3047
3043
  const pollCommentFolder = createPollCommentFolder();
3048
3044
  function resolveIMessageInboundBodyText(message) {
3049
3045
  const messageText = ((message.poll ? renderIMessagePollBody(message.poll, message.sender, { preferOptionId: Boolean(remoteHost) }) : null) ?? message.text ?? "").trim();
@@ -3060,7 +3056,7 @@ async function monitorIMessageProvider(opts = {}) {
3060
3056
  effectiveAttachmentRoots
3061
3057
  };
3062
3058
  }
3063
- async function handleMessageNowInner(rawMessage, ingressLifecycle) {
3059
+ async function handleMessageNow(rawMessage, ingressLifecycle) {
3064
3060
  const message = await repairMessageConversationAnchor(rawMessage);
3065
3061
  if (!message) return;
3066
3062
  const pollFoldAtMs = message.created_at ? Date.parse(message.created_at) : NaN;
@@ -3105,12 +3101,13 @@ async function monitorIMessageProvider(opts = {}) {
3105
3101
  if (decision.reason === "echo" || decision.reason === "reflected assistant content") loopRateLimiter.record(rateLimitKey);
3106
3102
  const diagnostic = describeIMessageInboundDropDiagnostic({
3107
3103
  accountId: accountInfo.accountId,
3104
+ groupsConfigPath,
3108
3105
  reason: decision.reason,
3109
3106
  message
3110
3107
  });
3111
3108
  if (diagnostic) {
3112
3109
  const throttleKey = `${rateLimitKey}:${decision.reason}`;
3113
- if (!shouldThrottleIMessageInboundDropDiagnostic(decision.reason) || !loggedThrottledDropDiagnostics.check(throttleKey)) runtime.log?.(warn(diagnostic));
3110
+ if (!IMESSAGE_THROTTLED_DIAGNOSTIC_DROP_REASONS.has(decision.reason) || !loggedThrottledDropDiagnostics.check(throttleKey)) runtime.log?.(warn(diagnostic));
3114
3111
  }
3115
3112
  if (decision.reason === "group id not in allowlist") warnGroupAllowlistDropPerChatOnce({
3116
3113
  accountId: accountInfo.accountId,
@@ -3411,6 +3408,7 @@ async function monitorIMessageProvider(opts = {}) {
3411
3408
  let directTypingController;
3412
3409
  const directToolTypingOptions = shouldUseDirectToolTypingOptions ? {
3413
3410
  suppressDefaultToolProgressMessages: true,
3411
+ allowToolLifecycleWhenProgressHidden: true,
3414
3412
  allowProgressCallbacksWhenSourceDeliverySuppressed: true,
3415
3413
  onTypingController: (typing) => {
3416
3414
  directTypingController = typing;
@@ -3539,7 +3537,7 @@ async function monitorIMessageProvider(opts = {}) {
3539
3537
  const repairedMessage = await repairMessageConversationAnchor(message);
3540
3538
  if (!repairedMessage) return { kind: "completed" };
3541
3539
  if (isApprovalCommand) {
3542
- await handleMessageNowInner(repairedMessage);
3540
+ await handleMessageNow(repairedMessage);
3543
3541
  return { kind: "completed" };
3544
3542
  }
3545
3543
  const conversation = resolveApprovalControlConversation(repairedMessage);
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { t as resolveIMessageReactionContext } from "./reaction-context-BAYI7pz0.js";
2
+ import { n as normalizeIMessageGuid, t as resolveIMessageReactionContext } from "./reaction-context-Br-vAJFp.js";
3
3
  import { createQuestionReactionTargetStore, questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
4
4
  //#region extensions/imessage/src/question-reactions.ts
5
5
  var question_reactions_exports = /* @__PURE__ */ __exportAll({
@@ -7,12 +7,9 @@ var question_reactions_exports = /* @__PURE__ */ __exportAll({
7
7
  maybeResolveIMessageQuestionReaction: () => maybeResolveIMessageQuestionReaction,
8
8
  registerIMessageQuestionReactionTargetForDeliveredPayload: () => registerIMessageQuestionReactionTargetForDeliveredPayload
9
9
  });
10
- function normalizeGuid(value) {
11
- return value.trim().replace(/^p:\d+\//iu, "");
12
- }
13
10
  function buildKey(identity) {
14
11
  const account = identity.accountId.trim();
15
- const guid = normalizeGuid(identity.messageGuid);
12
+ const guid = normalizeIMessageGuid(identity.messageGuid);
16
13
  return account && guid ? `${account}:${guid}` : null;
17
14
  }
18
15
  const questionReactionTargets = createQuestionReactionTargetStore({
@@ -25,7 +22,7 @@ const questionReactionTargets = createQuestionReactionTargetStore({
25
22
  function reactionCandidates(message, bodyText) {
26
23
  const reaction = resolveIMessageReactionContext(message, bodyText);
27
24
  if (!reaction) return null;
28
- const guids = Array.from(new Set([...reaction.targetGuids ?? [], reaction.targetGuid ?? ""].map(normalizeGuid).filter(Boolean)));
25
+ const guids = Array.from(new Set([...reaction.targetGuids ?? [], reaction.targetGuid ?? ""].map(normalizeIMessageGuid).filter(Boolean)));
29
26
  return guids.length > 0 ? {
30
27
  action: reaction.action,
31
28
  emoji: reaction.emoji,
@@ -39,7 +36,7 @@ function registerIMessageQuestionReactionTargetForDeliveredPayload(params) {
39
36
  for (const result of params.results) {
40
37
  if (result.channel !== "imessage") continue;
41
38
  const guid = typeof result.meta?.imessageMessageGuid === "string" ? result.meta.imessageMessageGuid : result.messageId;
42
- if (/^\d+$/u.test(normalizeGuid(guid))) continue;
39
+ if (/^\d+$/u.test(normalizeIMessageGuid(guid))) continue;
43
40
  registered = questionReactionTargets.register(binding, {
44
41
  accountId: params.accountId,
45
42
  messageGuid: guid
@@ -1,3 +1,9 @@
1
+ //#region extensions/imessage/src/message-guid.ts
2
+ /** Strip the `p:<n>/` part prefix Messages puts on some GUIDs so keys match. */
3
+ function normalizeIMessageGuid(value) {
4
+ return value.trim().replace(/^p:\d+\//iu, "");
5
+ }
6
+ //#endregion
1
7
  //#region extensions/imessage/src/monitor/reaction-context.ts
2
8
  const TAPBACK_TEXT_PATTERNS = [
3
9
  {
@@ -62,7 +68,7 @@ const TAPBACK_TEXT_PATTERNS = [
62
68
  }
63
69
  ];
64
70
  function normalizeReactionValue(value) {
65
- return typeof value === "string" ? value.trim().replace(/^p:\d+\//iu, "") || void 0 : void 0;
71
+ return typeof value === "string" ? normalizeIMessageGuid(value) || void 0 : void 0;
66
72
  }
67
73
  function resolveReactionTargetGuidCandidates(...values) {
68
74
  const candidates = [];
@@ -70,7 +76,7 @@ function resolveReactionTargetGuidCandidates(...values) {
70
76
  if (typeof value !== "string") continue;
71
77
  const raw = value.trim();
72
78
  if (!raw) continue;
73
- const normalized = raw.replace(/^p:\d+\//iu, "");
79
+ const normalized = normalizeIMessageGuid(raw);
74
80
  for (const candidate of [normalized, raw]) if (candidate && !candidates.includes(candidate)) candidates.push(candidate);
75
81
  }
76
82
  return candidates;
@@ -102,4 +108,4 @@ function resolveIMessageReactionContext(message, bodyText) {
102
108
  return resolveTapbackTextContext(bodyText);
103
109
  }
104
110
  //#endregion
105
- export { resolveIMessageReactionContext as t };
111
+ export { normalizeIMessageGuid as n, resolveIMessageReactionContext as t };
@@ -1,5 +1,5 @@
1
- import { p as resolveLocalIMessageChatDbPath, u as expandIMessageUserPath } from "./accounts-CCirBEIm.js";
2
- import { d as resolveIMessageCachedResourceBinding, f as resolveIMessageMessageId, g as isIMessageEmailChatIdentifier, h as chatContextFromIMessageTarget } from "./sanitize-outbound-CN3Hxbjy.js";
1
+ import { p as resolveLocalIMessageChatDbPath } from "./accounts-CCirBEIm.js";
2
+ import { _ as isIMessageEmailChatIdentifier, f as resolveIMessageCachedResourceBinding, g as chatContextFromIMessageTarget, p as resolveIMessageMessageId } from "./sanitize-outbound-C6UF2cvr.js";
3
3
  import path from "node:path";
4
4
  import { normalizeScpRemoteHost } from "openclaw/plugin-sdk/host-runtime";
5
5
  import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";
@@ -8,55 +8,6 @@ import { randomUUID } from "node:crypto";
8
8
  import { runCommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
9
9
  import { openNodeSqliteDatabase } from "openclaw/plugin-sdk/sqlite-runtime";
10
10
  import { sanitizeTempFileName } from "openclaw/plugin-sdk/temp-path";
11
- //#region extensions/imessage/src/cli-output.ts
12
- const IMESSAGE_CLI_STDOUT_MAX_BYTES = 8388608;
13
- const IMESSAGE_CLI_STDERR_TAIL_BYTES = 65536;
14
- function parseLastJsonObject(stdout) {
15
- const last = stdout.split(/\r?\n/u).findLast((line) => line.trim().length > 0)?.trim();
16
- if (!last) return null;
17
- try {
18
- const value = JSON.parse(last);
19
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
20
- } catch {
21
- return null;
22
- }
23
- }
24
- async function runIMessageCliJsonCommand(params) {
25
- const dbPath = params.dbPath?.trim();
26
- const argv = [
27
- expandIMessageUserPath(params.cliPath),
28
- ...params.args,
29
- ...dbPath ? ["--db", dbPath] : [],
30
- "--json"
31
- ];
32
- const result = await runCommandWithTimeout(argv, {
33
- killProcessTree: true,
34
- maxOutputBytes: {
35
- stdout: IMESSAGE_CLI_STDOUT_MAX_BYTES,
36
- stderr: IMESSAGE_CLI_STDERR_TAIL_BYTES
37
- },
38
- outputCapture: {
39
- stdout: "head",
40
- stderr: "tail"
41
- },
42
- terminateOnOutputLimit: { stdout: true },
43
- timeoutMs: params.timeoutMs
44
- });
45
- if (result.termination === "timeout") throw new Error(`iMessage action timed out after ${params.timeoutMs}ms`);
46
- if (result.outputLimitExceeded || result.stdoutTruncatedBytes) throw new Error(`imsg stdout exceeded ${IMESSAGE_CLI_STDOUT_MAX_BYTES} bytes`);
47
- const parsed = parseLastJsonObject(result.stdout);
48
- if (result.code !== 0) {
49
- const detail = typeof parsed?.error === "string" && parsed.error.trim() || result.stderr.trim() || result.stdout.trim() || `imsg exited with code ${result.code}`;
50
- throw new Error(detail);
51
- }
52
- if (!parsed) throw new Error(`imsg returned non-JSON output: ${result.stdout.trim() || result.stderr.trim()}`);
53
- if (parsed.success === false) {
54
- const detail = typeof parsed.error === "string" && parsed.error.trim() ? parsed.error.trim() : "iMessage action failed";
55
- throw new Error(detail);
56
- }
57
- return parsed;
58
- }
59
- //#endregion
60
11
  //#region extensions/imessage/src/message-resource-db.ts
61
12
  function normalizeIMessageMessageGuidForLookup(messageId) {
62
13
  const trimmed = messageId.trim();
@@ -291,4 +242,4 @@ rm -rf -- ${remoteDir}
291
242
  }
292
243
  }
293
244
  //#endregion
294
- export { runIMessageCliJsonCommand as i, authorizeIMessageResourceReference as n, resolveAuthorizedIMessageReplyReference as r, withIMessageRemoteFile as t };
245
+ export { authorizeIMessageResourceReference as n, resolveAuthorizedIMessageReplyReference as r, withIMessageRemoteFile as t };
@@ -1,16 +1,16 @@
1
1
  import { o as resolveIMessageAccount } from "./accounts-CCirBEIm.js";
2
- import { c as normalizeIMessageMessagingTarget, d as imessageMessageActions, f as probeIMessage, n as resolveIMessageGroupToolPolicy, s as looksLikeIMessageTargetId, t as resolveIMessageGroupRequireMention } from "./group-policy-BWqG2esy.js";
2
+ import { c as normalizeIMessageMessagingTarget, d as imessageMessageActions, f as probeIMessage, n as resolveIMessageGroupToolPolicy, s as looksLikeIMessageTargetId, t as resolveIMessageGroupRequireMention } from "./group-policy-Duro87b4.js";
3
3
  import { r as setIMessageRuntime } from "./runtime-Cza4CY5T.js";
4
- import { n as IMessageConfigSchema } from "./config-schema-DuGbA8Kp.js";
5
- import { t as sendMessageIMessage } from "./send-v0blZfaI.js";
6
- import { t as monitorIMessageProvider } from "./monitor-AmSj6QIH.js";
4
+ import { n as IMessageConfigSchema } from "./config-schema-CCbTcJEV.js";
5
+ import { t as sendMessageIMessage } from "./send-CIHiLtcY.js";
6
+ import { t as monitorIMessageProvider } from "./monitor-Dj3TAimI.js";
7
7
  import { formatTrimmedAllowFromEntries } from "openclaw/plugin-sdk/channel-config-helpers";
8
+ import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
8
9
  import { buildComputedAccountStatusSnapshot, collectStatusIssuesFromLastError } from "openclaw/plugin-sdk/status-helpers";
9
10
  import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/account-helpers";
10
11
  import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
11
12
  import { DEFAULT_ACCOUNT_ID, getChatChannelMeta } from "openclaw/plugin-sdk/core";
12
13
  import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
13
- import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
14
14
  //#region extensions/imessage/src/config-accessors.ts
15
15
  function resolveIMessageConfigAllowFrom(params) {
16
16
  return (resolveIMessageAccount(params).config.allowFrom ?? []).map((entry) => String(entry));
@@ -5,6 +5,7 @@ import { a as IMESSAGE_REPLY_CACHE_NAMESPACE, f as resolveIMessageReplyCacheEntr
5
5
  import { normalizeE164 } from "openclaw/plugin-sdk/account-resolution";
6
6
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
7
  import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
8
+ import { runCommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
8
9
  import { resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
9
10
  import { spawn } from "node:child_process";
10
11
  import { StringDecoder } from "node:string_decoder";
@@ -348,6 +349,77 @@ function isIMessageCurrentMessageInChat(params) {
348
349
  }) === "match";
349
350
  }
350
351
  //#endregion
352
+ //#region extensions/imessage/src/cli-output.ts
353
+ const IMESSAGE_CLI_STDOUT_MAX_BYTES = 8388608;
354
+ const IMESSAGE_CLI_STDERR_TAIL_BYTES = 65536;
355
+ function parseLastJsonObject(stdout) {
356
+ const last = stdout.split(/\r?\n/u).findLast((line) => line.trim().length > 0)?.trim();
357
+ if (!last) return null;
358
+ try {
359
+ const value = JSON.parse(last);
360
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
361
+ } catch {
362
+ return null;
363
+ }
364
+ }
365
+ async function runIMessageCliJsonCommand(params) {
366
+ const dbPath = params.dbPath?.trim();
367
+ const argv = [
368
+ expandIMessageUserPath(params.cliPath),
369
+ ...params.args,
370
+ ...dbPath ? ["--db", dbPath] : [],
371
+ "--json"
372
+ ];
373
+ const result = await runCommandWithTimeout(argv, {
374
+ killProcessTree: true,
375
+ maxOutputBytes: {
376
+ stdout: IMESSAGE_CLI_STDOUT_MAX_BYTES,
377
+ stderr: IMESSAGE_CLI_STDERR_TAIL_BYTES
378
+ },
379
+ outputCapture: {
380
+ stdout: "head",
381
+ stderr: "tail"
382
+ },
383
+ terminateOnOutputLimit: { stdout: true },
384
+ timeoutMs: params.timeoutMs
385
+ });
386
+ if (result.termination === "timeout") throw new Error(`iMessage action timed out after ${params.timeoutMs}ms`);
387
+ if (result.outputLimitExceeded || result.stdoutTruncatedBytes) throw new Error(`imsg stdout exceeded ${IMESSAGE_CLI_STDOUT_MAX_BYTES} bytes`);
388
+ const parsed = parseLastJsonObject(result.stdout);
389
+ if (result.code !== 0) {
390
+ const detail = typeof parsed?.error === "string" && parsed.error.trim() || result.stderr.trim() || result.stdout.trim() || `imsg exited with code ${result.code}`;
391
+ throw new Error(detail);
392
+ }
393
+ if (!parsed) throw new Error(`imsg returned non-JSON output: ${result.stdout.trim() || result.stderr.trim()}`);
394
+ if (parsed.success === false) {
395
+ const detail = typeof parsed.error === "string" && parsed.error.trim() ? parsed.error.trim() : "iMessage action failed";
396
+ throw new Error(detail);
397
+ }
398
+ return parsed;
399
+ }
400
+ //#endregion
401
+ //#region extensions/imessage/src/bridge-recovery.ts
402
+ const BRIDGE_RECOVERY_TIMEOUT_MS = 3e4;
403
+ const recoveries = /* @__PURE__ */ new Map();
404
+ /**
405
+ * Re-inject the private bridge after imsg has positively identified it as
406
+ * unresponsive. This deliberately does not retry the failed mutation: imsg may
407
+ * still reconcile a published send, so replaying it could duplicate a message.
408
+ */
409
+ function recoverIMessageBridge(cliPath) {
410
+ const existing = recoveries.get(cliPath);
411
+ if (existing) return existing;
412
+ const recovery = runIMessageCliJsonCommand({
413
+ cliPath,
414
+ args: ["launch"],
415
+ timeoutMs: BRIDGE_RECOVERY_TIMEOUT_MS
416
+ }).then(() => void 0).finally(() => {
417
+ recoveries.delete(cliPath);
418
+ });
419
+ recoveries.set(cliPath, recovery);
420
+ return recovery;
421
+ }
422
+ //#endregion
351
423
  //#region extensions/imessage/src/client.ts
352
424
  var IMessageRpcRequestError = class extends Error {
353
425
  constructor(message, code, data) {
@@ -378,6 +450,29 @@ function normalizeIMessageFullDiskAccessError(message) {
378
450
  if (!normalized.includes("full disk access") || !normalized.includes("chat.db")) return;
379
451
  return PUBLIC_IMESSAGE_FULL_DISK_ACCESS_ERROR;
380
452
  }
453
+ function createLfLineFramer(onLine) {
454
+ const decoder = new StringDecoder("utf8");
455
+ let buffer = "";
456
+ return {
457
+ write(chunk) {
458
+ buffer += typeof chunk === "string" ? chunk : decoder.write(chunk);
459
+ let newlineIndex = buffer.indexOf("\n");
460
+ while (newlineIndex !== -1) {
461
+ const line = buffer.slice(0, newlineIndex);
462
+ buffer = buffer.slice(newlineIndex + 1);
463
+ onLine(line);
464
+ newlineIndex = buffer.indexOf("\n");
465
+ }
466
+ },
467
+ flush() {
468
+ buffer += decoder.end();
469
+ if (!buffer) return;
470
+ const line = buffer;
471
+ buffer = "";
472
+ onLine(line);
473
+ }
474
+ };
475
+ }
381
476
  var IMessageRpcClient = class {
382
477
  constructor(opts = {}) {
383
478
  this.pending = /* @__PURE__ */ new Map();
@@ -386,10 +481,8 @@ var IMessageRpcClient = class {
386
481
  this.isReaped = false;
387
482
  this.child = null;
388
483
  this.stopPromise = null;
389
- this.stdoutBuffer = "";
390
- this.stdoutDecoder = new StringDecoder("utf8");
391
- this.stderrBuffer = "";
392
- this.stderrDecoder = new StringDecoder("utf8");
484
+ this.stdoutFramer = createLfLineFramer((line) => this.handleStdoutLine(line));
485
+ this.stderrFramer = createLfLineFramer((line) => this.handleStderrLine(line));
393
486
  this.nextId = 1;
394
487
  this.publicProcessError = null;
395
488
  this.configuredCliPath = opts.cliPath?.trim() || "imsg";
@@ -418,21 +511,29 @@ var IMessageRpcClient = class {
418
511
  this.child = child;
419
512
  child.stdout.on("data", (chunk) => {
420
513
  if (this.child !== child) return;
421
- this.handleStdoutChunk(chunk);
514
+ this.stdoutFramer.write(chunk);
422
515
  });
423
516
  child.stderr?.on("data", (chunk) => {
424
517
  if (this.child !== child) return;
425
- this.handleStderrChunk(chunk);
518
+ this.stderrFramer.write(chunk);
426
519
  });
427
520
  const failFromProcessError = (err) => this.failTransport(err, child);
428
521
  child.on("error", failFromProcessError);
429
- child.stdin.on("error", failFromProcessError);
430
- child.stdout.on("error", failFromProcessError);
431
- child.stderr.on("error", failFromProcessError);
522
+ for (const stream of [
523
+ child.stdin,
524
+ child.stdout,
525
+ child.stderr
526
+ ]) {
527
+ stream.on("error", failFromProcessError);
528
+ stream.once("close", () => {
529
+ const error = stream.errored;
530
+ if (error) failFromProcessError(error);
531
+ });
532
+ }
432
533
  child.on("close", (code, signal) => {
433
534
  if (this.child === child) {
434
- this.flushStdoutBuffer();
435
- this.flushStderrBuffer();
535
+ this.stdoutFramer.flush();
536
+ this.stderrFramer.flush();
436
537
  this.child = null;
437
538
  }
438
539
  this.finish(this.buildCloseError(code, signal));
@@ -483,6 +584,11 @@ var IMessageRpcClient = class {
483
584
  } catch (err) {
484
585
  if (isIMessageBridgeStall(err)) {
485
586
  invalidateCachedIMessagePrivateApiStatus(this.configuredCliPath);
587
+ try {
588
+ await recoverIMessageBridge(this.configuredCliPath);
589
+ } catch (recoveryError) {
590
+ this.runtime?.error?.(`imessage: automatic bridge recovery failed: ${formatErrorMessage(recoveryError)}`);
591
+ }
486
592
  throw describeIMessageBridgeStall(err);
487
593
  }
488
594
  throw err;
@@ -520,48 +626,11 @@ var IMessageRpcClient = class {
520
626
  if (!this.finish(err instanceof Error ? err : new Error(String(err)))) return;
521
627
  if (child) this.signalChild(child, "SIGTERM");
522
628
  }
523
- handleStdoutChunk(chunk) {
524
- const text = typeof chunk === "string" ? chunk : this.stdoutDecoder.write(chunk);
525
- this.stdoutBuffer += text;
526
- let newlineIndex = this.stdoutBuffer.indexOf("\n");
527
- while (newlineIndex !== -1) {
528
- const line = this.stdoutBuffer.slice(0, newlineIndex);
529
- this.stdoutBuffer = this.stdoutBuffer.slice(newlineIndex + 1);
530
- this.handleStdoutLine(line);
531
- newlineIndex = this.stdoutBuffer.indexOf("\n");
532
- }
533
- }
534
- flushStdoutBuffer() {
535
- const tail = this.stdoutDecoder.end();
536
- if (tail) this.stdoutBuffer += tail;
537
- if (!this.stdoutBuffer) return;
538
- const line = this.stdoutBuffer;
539
- this.stdoutBuffer = "";
540
- this.handleStdoutLine(line);
541
- }
542
629
  handleStdoutLine(line) {
543
630
  const trimmed = line.trim();
544
631
  if (!trimmed) return;
545
632
  this.handleLine(trimmed);
546
633
  }
547
- handleStderrChunk(chunk) {
548
- const text = typeof chunk === "string" ? chunk : this.stderrDecoder.write(chunk);
549
- this.stderrBuffer += text;
550
- let newlineIndex = this.stderrBuffer.indexOf("\n");
551
- while (newlineIndex !== -1) {
552
- const line = this.stderrBuffer.slice(0, newlineIndex);
553
- this.stderrBuffer = this.stderrBuffer.slice(newlineIndex + 1);
554
- this.handleStderrLine(line);
555
- newlineIndex = this.stderrBuffer.indexOf("\n");
556
- }
557
- }
558
- flushStderrBuffer() {
559
- this.stderrBuffer += this.stderrDecoder.end();
560
- if (!this.stderrBuffer) return;
561
- const line = this.stderrBuffer;
562
- this.stderrBuffer = "";
563
- this.handleStderrLine(line);
564
- }
565
634
  handleStderrLine(line) {
566
635
  const trimmed = line.trim();
567
636
  if (!trimmed) return;
@@ -687,6 +756,10 @@ function codeDelimiter(content) {
687
756
  return "`".repeat(longestRun + 1);
688
757
  }
689
758
  function restoreCodeMarkers(text, ranges, codeRanges) {
759
+ if (codeRanges.length === 0) return {
760
+ text,
761
+ ranges
762
+ };
690
763
  let rendered = "";
691
764
  let cursor = 0;
692
765
  const edits = codeRanges.map((range) => {
@@ -735,12 +808,12 @@ function extractMarkdownFormatRuns(input) {
735
808
  preserveSourceBlockSpacing: true
736
809
  });
737
810
  const rendered = renderMarkdownWithAttributedRanges(ir, { styleMap: IMESSAGE_STYLE_MAP }, IMESSAGE_FORMAT_PROFILE);
738
- const code = renderMarkdownWithAttributedRanges(ir, { styleMap: { code: "code" } }, IMESSAGE_CODE_PROFILE);
811
+ const codeRanges = ir.styles.some((span) => span.style === "code") ? renderMarkdownWithAttributedRanges(ir, { styleMap: { code: "code" } }, IMESSAGE_CODE_PROFILE).ranges : [];
739
812
  return restoreCodeMarkers(rendered.text, rendered.ranges.map(({ start, length, style }) => ({
740
813
  start,
741
814
  length,
742
815
  styles: [style]
743
- })), code.ranges);
816
+ })), codeRanges);
744
817
  }
745
818
  //#endregion
746
819
  //#region extensions/imessage/src/monitor/sanitize-outbound.ts
@@ -1204,4 +1277,4 @@ function sanitizeOutboundText(text) {
1204
1277
  return cleaned;
1205
1278
  }
1206
1279
  //#endregion
1207
- export { normalizeDirectChatIdentifier as _, IMessageRpcRequestError as a, isIMessageCurrentMessageInChat as c, resolveIMessageCachedResourceBinding as d, resolveIMessageMessageId as f, isIMessageEmailChatIdentifier as g, chatContextFromIMessageTarget as h, extractMarkdownFormatRuns as i, isKnownFromMeIMessageMessageId as l, DEFAULT_IMESSAGE_SEND_TIMEOUT_MS as m, sanitizeIMessageFinalOutboundText as n, createIMessageRpcClient as o, DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS as p, sanitizeOutboundText as r, findLatestIMessageEntryForChat as s, protectIMessageFencedRoleMarkers as t, rememberIMessageReplyCache as u, resolveIMessageDirectChatService as v };
1280
+ export { isIMessageEmailChatIdentifier as _, IMessageRpcRequestError as a, findLatestIMessageEntryForChat as c, rememberIMessageReplyCache as d, resolveIMessageCachedResourceBinding as f, chatContextFromIMessageTarget as g, DEFAULT_IMESSAGE_SEND_TIMEOUT_MS as h, extractMarkdownFormatRuns as i, isIMessageCurrentMessageInChat as l, DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS as m, sanitizeIMessageFinalOutboundText as n, createIMessageRpcClient as o, resolveIMessageMessageId as p, sanitizeOutboundText as r, runIMessageCliJsonCommand as s, protectIMessageFencedRoleMarkers as t, isKnownFromMeIMessageMessageId as u, normalizeDirectChatIdentifier as v, resolveIMessageDirectChatService as y };
@@ -1,11 +1,12 @@
1
1
  import { d as resolveIMessageChatDbLookupPath, l as resolveIMessageRemoteHost, n as hasExclusiveIMessageLocalDatabase, o as resolveIMessageAccount } from "./accounts-CCirBEIm.js";
2
2
  import { l as parseIMessageTarget, n as formatIMessageChatTarget, s as normalizeIMessageHandle } from "./message-tool-api-CUoFESHj.js";
3
- import { a as IMessageRpcRequestError, h as chatContextFromIMessageTarget, n as sanitizeIMessageFinalOutboundText, o as createIMessageRpcClient, t as protectIMessageFencedRoleMarkers, u as rememberIMessageReplyCache, v as resolveIMessageDirectChatService } from "./sanitize-outbound-CN3Hxbjy.js";
4
- import { l as getIMessageApprovalApprovers, u as imessageApprovalAuth } from "./group-policy-BWqG2esy.js";
3
+ import { a as IMessageRpcRequestError, d as rememberIMessageReplyCache, g as chatContextFromIMessageTarget, n as sanitizeIMessageFinalOutboundText, o as createIMessageRpcClient, s as runIMessageCliJsonCommand, t as protectIMessageFencedRoleMarkers, y as resolveIMessageDirectChatService } from "./sanitize-outbound-C6UF2cvr.js";
4
+ import { l as getIMessageApprovalApprovers, u as imessageApprovalAuth } from "./group-policy-Duro87b4.js";
5
5
  import { n as getOptionalIMessageRuntime, t as getIMessageRuntime } from "./runtime-Cza4CY5T.js";
6
6
  import { c as IMESSAGE_SENT_ECHOES_TTL_MS, d as resolveIMessageEchoMediaKey, p as resolveIMessageSentEchoEntryKey, s as IMESSAGE_SENT_ECHOES_NAMESPACE } from "./state-contract-DvahEY4X.js";
7
- import { d as normalizeConversationKey, f as normalizeIMessageGuid, i as registerIMessageApprovalReactionTarget, l as enumerateApprovalTargetKeys, s as buildIMessageApprovalConversationKeyForInbound, u as enumerateConversationKeyForms } from "./approval-reactions-DcLRPx7O.js";
8
- import { i as runIMessageCliJsonCommand, r as resolveAuthorizedIMessageReplyReference, t as withIMessageRemoteFile } from "./remote-file-COTN7196.js";
7
+ import { d as normalizeConversationKey, i as registerIMessageApprovalReactionTarget, l as enumerateApprovalTargetKeys, s as buildIMessageApprovalConversationKeyForInbound, u as enumerateConversationKeyForms } from "./approval-reactions-BM_AwHPq.js";
8
+ import { n as normalizeIMessageGuid } from "./reaction-context-Br-vAJFp.js";
9
+ import { r as resolveAuthorizedIMessageReplyReference, t as withIMessageRemoteFile } from "./remote-file-5n5LznCa.js";
9
10
  import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
10
11
  import { createLazyRuntimeSurface } from "openclaw/plugin-sdk/lazy-runtime";
11
12
  import { accessSync, constants } from "node:fs";