@openclaw/imessage 2026.9.7 → 2026.10.1-beta.1

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 (33) hide show
  1. package/dist/.setup/{accounts-nzcU0cq2.mjs → accounts-evTpvJMv.mjs} +6 -12
  2. package/dist/.setup/{group-policy-90pw4tyG.mjs → actions-DJqbUmbN.mjs} +103 -269
  3. package/dist/.setup/{actions.runtime-DdBN29zj.mjs → actions.runtime-DL5i-Y4J.mjs} +49 -84
  4. package/dist/.setup/{api-B9Qx21G-.mjs → api-SAVMfMoY.mjs} +5 -5
  5. package/dist/.setup/{api-D2S6FhGw.mjs → api-oq_MryN7.mjs} +6 -6
  6. package/dist/.setup/{approval-handler.runtime-DKSq0DWq.mjs → approval-handler.runtime-Bj2NGNXF.mjs} +26 -62
  7. package/dist/.setup/{approval-reactions-Bp33xc83.mjs → approval-reactions-L9Ul7PAu.mjs} +3 -2
  8. package/dist/.setup/{channel-9fyvuN_p.mjs → channel-CL73qdMG.mjs} +134 -60
  9. package/dist/.setup/{monitor-fv9nRuTH.mjs → channel.runtime-DY7JJ0_9.mjs} +202 -206
  10. package/dist/.setup/{sanitize-outbound-3MLljgtY.mjs → client-12apu4Ip.mjs} +26 -635
  11. package/dist/.setup/{config-schema-4Q3_EqYt.mjs → config-schema-DXwa_3V5.mjs} +1 -1
  12. package/dist/.setup/{media-contract-CagwSMvo.mjs → media-contract-DjQGZlfl.mjs} +1 -1
  13. package/dist/.setup/{message-tool-api-D032l82H.mjs → message-tool-api-CqfRxLlT.mjs} +2 -2
  14. package/dist/.setup/{question-reactions-Cy-dmU_j.mjs → question-reactions-DJV-TnA8.mjs} +2 -1
  15. package/dist/.setup/{reaction-context-Br-vAJFp.mjs → reaction-context-By7kUMBC.mjs} +2 -7
  16. package/dist/.setup/{remote-file-DRPCaVmg.mjs → remote-file-BU7pA-Zi.mjs} +7 -11
  17. package/dist/.setup/sanitize-outbound-C0x4BPky.mjs +564 -0
  18. package/dist/.setup/{send-Cu9nl757.mjs → send-pkYwL6Rt.mjs} +56 -103
  19. package/dist/.setup/{setup-core-8MDdYHj5.mjs → setup-core-DqOBe-n0.mjs} +1 -1
  20. package/dist/.setup/{setup-surface-tGXFDdWh.mjs → setup-surface-zKhIinwY.mjs} +2 -2
  21. package/dist/api.js +6 -6
  22. package/dist/channel-config-api.js +1 -1
  23. package/dist/channel-plugin-api.js +1 -1
  24. package/dist/contract-api.js +1 -1
  25. package/dist/doctor-contract-api.js +29 -21
  26. package/dist/media-contract-api.js +1 -1
  27. package/dist/message-tool-api.js +1 -1
  28. package/dist/runtime-api.js +2 -24
  29. package/dist/setup-entry.js +1 -1
  30. package/openclaw.plugin.json +1 -1
  31. package/package.json +4 -4
  32. package/dist/.setup/channel.runtime-n-MPKL8k.mjs +0 -92
  33. package/dist/.setup/state-migrations-B-Q7bO0Y.mjs +0 -47
@@ -1,24 +1,30 @@
1
1
  import { c as parseIMessageTarget, o as normalizeIMessageHandle, r as isAllowedIMessageReplyContextSender, s as parseIMessageAllowTarget, t as formatIMessageChatTarget } from "./targets-BicBiYsM.mjs";
2
- import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount } from "./accounts-nzcU0cq2.mjs";
3
- import { r as imessageRpcSupportsMethod } from "./message-tool-api-D032l82H.mjs";
4
- import { D as resolveIMessageDirectChatService, b as resolveIMessageEchoMediaKey, d as isKnownFromMeIMessageTarget, f as rememberIMessageReplyCache, h as IMESSAGE_CATCHUP_CURSOR_NAMESPACE, o as createIMessageRpcClient, r as sanitizeOutboundText, v as capFailureRetriesMap, y as resolveIMessageCatchupCursorKey } from "./sanitize-outbound-3MLljgtY.mjs";
5
- import { d as probeIMessage, f as probeIMessagePrivateApi, i as resolveIMessageInboundConversationId, n as resolveIMessageGroupSystemPrompt } from "./group-policy-90pw4tyG.mjs";
2
+ import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount, r as listEnabledIMessageAccounts, s as resolveIMessageDuplicateSourceOwner } from "./accounts-evTpvJMv.mjs";
3
+ import { r as imessageRpcSupportsMethod } from "./message-tool-api-CqfRxLlT.mjs";
4
+ import { C as resolveIMessageDirectChatService, c as rememberIMessageReplyCache, d as IMESSAGE_CATCHUP_CURSOR_NAMESPACE, g as resolveIMessageEchoMediaKey, h as resolveIMessageCatchupCursorKey, m as capFailureRetriesMap, n as createIMessageRpcClient, s as isKnownFromMeIMessageTarget } from "./client-12apu4Ip.mjs";
5
+ import { a as normalizeIMessageMessageId, i as normalizeIMessageGuid, n as probeIMessage, r as probeIMessagePrivateApi } from "./actions-DJqbUmbN.mjs";
6
6
  import { t as getIMessageRuntime } from "./runtime-Cza4CY5T.mjs";
7
- import { n as resolveIMessageAttachmentRoots, r as resolveIMessageRemoteAttachmentRoots } from "./media-contract-CagwSMvo.mjs";
8
- import { i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction, s as buildIMessageApprovalConversationKeyForInbound } from "./approval-reactions-Bp33xc83.mjs";
9
- import { c as iMessageApprovalControlBindings, n as hasPersistedIMessageEcho, r as normalizeIMessageEchoText, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-Cu9nl757.mjs";
10
- import { n as normalizeIMessageGuid, t as resolveIMessageReactionContext } from "./reaction-context-Br-vAJFp.mjs";
11
- import { i as resolveIMessageStartupRowidWatermark } from "./remote-file-DRPCaVmg.mjs";
12
- import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-Cy-dmU_j.mjs";
7
+ import { a as resolveIMessageGroupSystemPrompt, s as resolveIMessageInboundConversationId } from "./channel-CL73qdMG.mjs";
8
+ import { r as sanitizeOutboundText } from "./sanitize-outbound-C0x4BPky.mjs";
9
+ import { n as resolveIMessageAttachmentRoots, r as resolveIMessageRemoteAttachmentRoots } from "./media-contract-DjQGZlfl.mjs";
10
+ import { t as IMESSAGE_LEGACY_OUTBOUND_SEND_DEP_KEYS } from "./outbound-send-deps-B-QEsLSM.mjs";
11
+ import { i as resolveIMessageStartupRowidWatermark } from "./remote-file-BU7pA-Zi.mjs";
12
+ import { i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction, s as buildIMessageApprovalConversationKeyForInbound } from "./approval-reactions-L9Ul7PAu.mjs";
13
+ import { c as iMessageApprovalControlBindings, n as hasPersistedIMessageEcho, r as normalizeIMessageEchoText, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-pkYwL6Rt.mjs";
14
+ import { t as resolveIMessageReactionContext } from "./reaction-context-By7kUMBC.mjs";
15
+ import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-DJV-TnA8.mjs";
16
+ import { imessageSetupWizard } from "./setup-surface-zKhIinwY.mjs";
13
17
  import { asSafeIntegerInRange, hasNonEmptyString, isRecord, normalizeOptionalString, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
14
- import { bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
18
+ import { normalizeChannelDmPolicy } from "openclaw/plugin-sdk/channel-config-helpers";
19
+ import { bindIngressLifecycleToReplyOptions, createAccountStatusSink, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, resolveChannelStreamingBlockEnabled, resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-outbound";
15
20
  import { resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
16
21
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
17
22
  import path from "node:path";
23
+ import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/account-helpers";
18
24
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
19
25
  import fs from "node:fs/promises";
20
26
  import { isInboundPathAllowed, kindFromMime } from "openclaw/plugin-sdk/media-runtime";
21
- import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, warn } from "openclaw/plugin-sdk/runtime-env";
27
+ import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, sleepWithAbort, waitForAbortSignal, warn } from "openclaw/plugin-sdk/runtime-env";
22
28
  import { asDateTimestampMs, asPositiveFiniteNumber, parseDateStringTimestampMs, resolveIntegerOption, resolvePromptHistoryLimit, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
23
29
  import { createHash } from "node:crypto";
24
30
  import { createRuntimeConfigReader, getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
@@ -27,8 +33,12 @@ import { collectErrorGraphCandidates, formatErrorMessage } from "openclaw/plugin
27
33
  import { resolveChannelGroupPolicy, resolveChannelGroups, resolveChannelGroupsConfigPath, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
28
34
  import { convertMarkdownTables, findCodeRegions, isInsideCode, sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
29
35
  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";
30
- import { resolveAgentConfig, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
36
+ import { FsSafeError, openLocalFileSafely, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
37
+ import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
31
38
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-runtime";
39
+ import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
40
+ import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
41
+ import { resolveAgentConfig, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
32
42
  import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
33
43
  import { defineStableChannelIngressIdentity, fanInChannelIngressLifecycles } from "openclaw/plugin-sdk/channel-ingress-runtime";
34
44
  import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
@@ -37,12 +47,8 @@ import { ensureConfiguredBindingRouteReady, readChannelAllowFromStore, upsertCha
37
47
  import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
38
48
  import { redactIdentifier } from "openclaw/plugin-sdk/logging-core";
39
49
  import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
40
- import { FsSafeError, openLocalFileSafely, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
41
50
  import { getSessionEntry, readSessionUpdatedAt, resolveSendPolicy, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
42
51
  import { waitForTransportReady } from "openclaw/plugin-sdk/transport-ready-runtime";
43
- import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
44
- import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
45
- import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
46
52
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
47
53
  import { chunkMarkdownTextWithMode, resolveChunkMode } from "openclaw/plugin-sdk/reply-chunking";
48
54
  import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
@@ -148,12 +154,12 @@ async function bindObservedConversation(params) {
148
154
  }
149
155
  async function pollPendingIMessageApprovalReactions(params) {
150
156
  const targets = await listPendingIMessageApprovalReactionPollTargets({ accountId: params.accountId });
151
- if (targets.length === 0) return;
157
+ if (params.signal?.aborted || targets.length === 0) return;
152
158
  const pendingByMessageId = buildPendingTargetsByMessageId(targets);
153
159
  const explicitChatIds = listTargetChatIds(targets);
154
160
  const chatIds = params.allowRecentChatDiscovery === true && hasUnscopedTarget(targets) ? [.../* @__PURE__ */ new Set([...explicitChatIds, ...await listRecentChatIds(params.client)])] : explicitChatIds;
155
- if (chatIds.length === 0) return;
156
161
  for (const chatId of chatIds) {
162
+ if (params.signal?.aborted) return;
157
163
  let messages;
158
164
  try {
159
165
  messages = await fetchRecentHistory({
@@ -165,6 +171,7 @@ async function pollPendingIMessageApprovalReactions(params) {
165
171
  continue;
166
172
  }
167
173
  for (const message of messages) {
174
+ if (params.signal?.aborted) return;
168
175
  const targetGuid = message.guid?.trim();
169
176
  if (!targetGuid) continue;
170
177
  const target = pendingByMessageId.get(targetGuid) ?? pendingByMessageId.get(normalizeIMessageGuid(targetGuid));
@@ -174,6 +181,7 @@ async function pollPendingIMessageApprovalReactions(params) {
174
181
  message
175
182
  });
176
183
  for (const reaction of message.reactions ?? []) {
184
+ if (params.signal?.aborted) return;
177
185
  const reactionPayload = buildReactionPayload({
178
186
  targetMessage: message,
179
187
  reaction
@@ -607,21 +615,7 @@ function parseIMessageNotification(raw) {
607
615
  const PER_CHAT_HISTORY_LIMIT = 500;
608
616
  const CATCHUP_CHATS_LIST_LIMIT = 200;
609
617
  const CATCHUP_RPC_TIMEOUT_MS = 3e4;
610
- /**
611
- * Wire `performIMessageCatchup` against the live `imsg` JSON-RPC client.
612
- *
613
- * Catchup recovers messages that landed in `chat.db` while the gateway was
614
- * offline (crash, restart, mac sleep) by:
615
- * 1. listing recently-active chats via `chats.list`,
616
- * 2. fetching per-chat history since the cursor via `messages.history`,
617
- * 3. sorting cross-chat by `rowid`, capping at `perRunLimit`,
618
- * 4. admitting each row through the same durable GUID queue used by live
619
- * notifications, so replay, coalescing, echo, and receipt behavior match.
620
- *
621
- * Runs at most once per `monitorIMessageProvider` invocation, between
622
- * `watch.subscribe` and the live dispatch loop. Anything that arrives during
623
- * catchup itself flows through live admission; queue tombstones reject overlap.
624
- */
618
+ /** Replay oldest-first through durable admission; tombstones reject live/watch overlap. */
625
619
  async function runIMessageCatchup(params) {
626
620
  const { client, accountId, config, includeAttachments, dispatchPayload, runtime } = params;
627
621
  const log = (msg) => runtime?.log?.(msg);
@@ -699,22 +693,13 @@ async function runIMessageCatchup(params) {
699
693
  const sorted = collected.toSorted((a, b) => a.rowid - b.rowid);
700
694
  const capped = sorted.slice(0, limit);
701
695
  const isCapTruncated = capped.length < sorted.length;
702
- if (isCapTruncated) {
703
- warnLog(`imessage catchup: fetched ${sorted.length} rows across chats, capped to perRunLimit=${limit} (oldest first); next startup picks up the rest`);
704
- const keep = new Set(capped.map((row) => row.guid));
705
- for (const guid of payloadByGuid.keys()) if (!keep.has(guid)) payloadByGuid.delete(guid);
706
- }
696
+ if (isCapTruncated) warnLog(`imessage catchup: fetched ${sorted.length} rows across chats, capped to perRunLimit=${limit} (oldest first); next startup picks up the rest`);
707
697
  let effectiveWatermarkRowid = rawWatermarkRowid;
708
698
  let effectiveWatermarkMs = rawWatermarkMs;
709
- if (isCapTruncated && capped.length > 0) {
699
+ if (isCapTruncated) {
710
700
  const last = capped.at(-1);
711
- if (last) {
712
- effectiveWatermarkRowid = Math.min(effectiveWatermarkRowid, last.rowid);
713
- effectiveWatermarkMs = Math.min(effectiveWatermarkMs, last.date);
714
- }
715
- } else if (isCapTruncated && capped.length === 0) {
716
- effectiveWatermarkRowid = NaN;
717
- effectiveWatermarkMs = NaN;
701
+ effectiveWatermarkRowid = last ? Math.min(rawWatermarkRowid, last.rowid) : NaN;
702
+ effectiveWatermarkMs = last ? Math.min(rawWatermarkMs, last.date) : NaN;
718
703
  }
719
704
  return {
720
705
  resolved: true,
@@ -828,7 +813,7 @@ function hasUsableConversationAnchor(projection) {
828
813
  return hasPositiveChatId(projection.chat_id) || hasNonEmptyString(projection.chat_guid) || hasNonEmptyString(projection.chat_identifier);
829
814
  }
830
815
  function isIMessageAnchorless(message) {
831
- if (hasPositiveChatId(message.chat_id) || hasNonEmptyString(message.chat_guid) || hasNonEmptyString(message.chat_identifier)) return false;
816
+ if (hasUsableConversationAnchor(message)) return false;
832
817
  return message.chat_id === null || typeof message.chat_id === "number" && (!Number.isFinite(message.chat_id) || message.chat_id <= 0) || isExplicitEmptyString(message.chat_guid) || isExplicitEmptyString(message.chat_identifier);
833
818
  }
834
819
  function extractAuthoritativeRecoveryProjection(entry) {
@@ -1085,12 +1070,6 @@ function createIMessageThrottledDropDiagnosticCache() {
1085
1070
  //#region extensions/imessage/src/monitor/echo-cache.ts
1086
1071
  const SENT_MESSAGE_TEXT_TTL_MS = 4e3;
1087
1072
  const SENT_MESSAGE_ID_TTL_MS = 6e4;
1088
- function normalizeEchoMessageIdKey(messageId) {
1089
- if (!messageId) return null;
1090
- const normalized = messageId.trim();
1091
- if (!normalized || normalized === "ok" || normalized === "unknown") return null;
1092
- return normalized;
1093
- }
1094
1073
  var DefaultSentMessageCache = class {
1095
1074
  constructor() {
1096
1075
  this.textCache = /* @__PURE__ */ new Map();
@@ -1104,7 +1083,7 @@ var DefaultSentMessageCache = class {
1104
1083
  if (textKey) this.textCache.set(`${scope}:${textKey}`, Date.now());
1105
1084
  const mediaKey = resolveIMessageEchoMediaKey(lookup.media);
1106
1085
  if (mediaKey) this.mediaCache.set(`${scope}:${mediaKey}`, Date.now());
1107
- const messageIdKey = normalizeEchoMessageIdKey(lookup.messageId);
1086
+ const messageIdKey = normalizeIMessageMessageId(lookup.messageId);
1108
1087
  if (messageIdKey) {
1109
1088
  this.messageIdCache.set(`${scope}:${messageIdKey}`, Date.now());
1110
1089
  if (textKey) this.textBackedByIdCache.set(`${scope}:${textKey}`, Date.now());
@@ -1125,7 +1104,7 @@ var DefaultSentMessageCache = class {
1125
1104
  })) return true;
1126
1105
  const textKey = normalizeIMessageEchoText(lookup.text);
1127
1106
  const mediaKey = resolveIMessageEchoMediaKey(lookup.media);
1128
- const messageIdKey = normalizeEchoMessageIdKey(lookup.messageId);
1107
+ const messageIdKey = normalizeIMessageMessageId(lookup.messageId);
1129
1108
  let canUseMediaFallback = !messageIdKey;
1130
1109
  if (messageIdKey) {
1131
1110
  const idTimestamp = this.messageIdCache.get(`${scope}:${messageIdKey}`);
@@ -1450,18 +1429,11 @@ function normalizeIMessageChatIdentifierEntry(entry) {
1450
1429
  const parsed = parseIMessageAllowTarget(entry.trim());
1451
1430
  return parsed.kind === "chat_identifier" ? parsed.chatIdentifier.trim() || null : null;
1452
1431
  }
1453
- function normalizeDmPolicy(policy) {
1454
- return policy === "open" || policy === "allowlist" || policy === "disabled" ? policy : "pairing";
1455
- }
1456
1432
  function normalizeGroupPolicy(policy) {
1457
1433
  return policy === "open" || policy === "disabled" ? policy : "allowlist";
1458
1434
  }
1459
1435
  function normalizeReplyField(value) {
1460
- if (typeof value === "string") {
1461
- const trimmed = value.trim();
1462
- return trimmed ? trimmed : void 0;
1463
- }
1464
- if (typeof value === "number") return String(value);
1436
+ return typeof value === "number" ? String(value) : normalizeOptionalString(value);
1465
1437
  }
1466
1438
  function describeReplyContext(message) {
1467
1439
  const body = normalizeReplyField(message.reply_to_text);
@@ -1573,6 +1545,14 @@ async function resolveIMessageInboundDecision(params) {
1573
1545
  const inboundMessageIds = resolveInboundEchoMessageIds(params.message);
1574
1546
  const inboundMessageId = inboundMessageIds[0];
1575
1547
  const hasInboundGuid = Boolean(normalizeReplyField(params.message.guid));
1548
+ const echoScope = buildIMessageEchoScope({
1549
+ accountId: params.accountId,
1550
+ isGroup,
1551
+ chatId,
1552
+ chatGuid,
1553
+ chatIdentifier,
1554
+ sender
1555
+ });
1576
1556
  if (params.message.is_from_me) {
1577
1557
  if (isAmbiguousSelfThread) params.selfChatCache?.remember(selfChatLookup);
1578
1558
  if (isSelfChat) {
@@ -1580,14 +1560,6 @@ async function resolveIMessageInboundDecision(params) {
1580
1560
  ...selfChatLookup,
1581
1561
  allowCreatedAtSkew: true
1582
1562
  });
1583
- const echoScope = buildIMessageEchoScope({
1584
- accountId: params.accountId,
1585
- isGroup,
1586
- chatId,
1587
- chatGuid,
1588
- chatIdentifier,
1589
- sender
1590
- });
1591
1563
  if (params.echoCache && (bodyText || inboundMessageId || mediaFacts.length > 0) && await hasIMessageEchoMatch({
1592
1564
  echoCache: params.echoCache,
1593
1565
  scope: echoScope,
@@ -1642,7 +1614,7 @@ async function resolveIMessageInboundDecision(params) {
1642
1614
  id: chatId != null ? String(chatId) : sender
1643
1615
  },
1644
1616
  contextBinding,
1645
- dmPolicy: normalizeDmPolicy(params.dmPolicy),
1617
+ dmPolicy: normalizeChannelDmPolicy(params.dmPolicy) ?? "pairing",
1646
1618
  groupPolicy: normalizeGroupPolicy(params.groupPolicy),
1647
1619
  policy: { groupAllowFromFallbackToAllowFrom: false },
1648
1620
  allowFrom: params.allowFrom,
@@ -1715,14 +1687,7 @@ async function resolveIMessageInboundDecision(params) {
1715
1687
  const targetGuids = reactionContext.targetGuids ?? (targetGuid ? [targetGuid] : []);
1716
1688
  const targetIsOwn = Boolean(targetGuid && (params.echoCache && await hasIMessageEchoMatch({
1717
1689
  echoCache: params.echoCache,
1718
- scope: buildIMessageEchoScope({
1719
- accountId: params.accountId,
1720
- isGroup,
1721
- chatId,
1722
- chatGuid,
1723
- chatIdentifier,
1724
- sender
1725
- }),
1690
+ scope: echoScope,
1726
1691
  messageIds: targetGuids
1727
1692
  }) || await isKnownFromMeIMessageTarget({
1728
1693
  messageIds: targetGuids,
@@ -1776,14 +1741,6 @@ async function resolveIMessageInboundDecision(params) {
1776
1741
  };
1777
1742
  }
1778
1743
  if (params.echoCache && (messageText || inboundMessageId || mediaFacts.length > 0)) {
1779
- const echoScope = buildIMessageEchoScope({
1780
- accountId: params.accountId,
1781
- isGroup,
1782
- chatId,
1783
- chatGuid,
1784
- chatIdentifier,
1785
- sender
1786
- });
1787
1744
  if (await hasIMessageEchoMatch({
1788
1745
  echoCache: params.echoCache,
1789
1746
  scope: echoScope,
@@ -1816,7 +1773,7 @@ async function resolveIMessageInboundDecision(params) {
1816
1773
  channel: "imessage",
1817
1774
  accountId: params.accountId
1818
1775
  });
1819
- const replyContextAllowFrom = Array.from(/* @__PURE__ */ new Set([...groupAllowFromForAccess, ...effectiveGroupAllowFrom]));
1776
+ const replyContextAllowFrom = uniqueStrings([...groupAllowFromForAccess, ...effectiveGroupAllowFrom]);
1820
1777
  const replySenderAllowed = resolveInboundSupplementalSenderAllowed({
1821
1778
  isGroup,
1822
1779
  groupPolicy: replyContextAllowFrom.length === 0 ? "open" : "allowlist",
@@ -2247,29 +2204,29 @@ function createLoopRateLimiter(opts) {
2247
2204
  const now = Date.now();
2248
2205
  if (now - lastCleanup < CLEANUP_INTERVAL_MS) return;
2249
2206
  lastCleanup = now;
2250
- for (const [key, win] of conversations.entries()) {
2251
- const recent = win.timestamps.filter((ts) => now - ts <= windowMs);
2207
+ for (const [key, timestamps] of conversations.entries()) {
2208
+ const recent = timestamps.filter((ts) => now - ts <= windowMs);
2252
2209
  if (recent.length === 0) conversations.delete(key);
2253
- else win.timestamps = recent;
2210
+ else conversations.set(key, recent);
2254
2211
  }
2255
2212
  }
2256
2213
  return {
2257
2214
  record(conversationKey) {
2258
2215
  cleanup();
2259
- let win = conversations.get(conversationKey);
2260
- if (!win) {
2261
- win = { timestamps: [] };
2262
- conversations.set(conversationKey, win);
2216
+ let timestamps = conversations.get(conversationKey);
2217
+ if (!timestamps) {
2218
+ timestamps = [];
2219
+ conversations.set(conversationKey, timestamps);
2263
2220
  }
2264
- win.timestamps.push(Date.now());
2221
+ timestamps.push(Date.now());
2265
2222
  },
2266
2223
  isRateLimited(conversationKey) {
2267
2224
  cleanup();
2268
- const win = conversations.get(conversationKey);
2269
- if (!win) return false;
2225
+ const timestamps = conversations.get(conversationKey);
2226
+ if (!timestamps) return false;
2270
2227
  const now = Date.now();
2271
- const recent = win.timestamps.filter((ts) => now - ts <= windowMs);
2272
- win.timestamps = recent;
2228
+ const recent = timestamps.filter((ts) => now - ts <= windowMs);
2229
+ conversations.set(conversationKey, recent);
2273
2230
  return recent.length >= maxHits;
2274
2231
  }
2275
2232
  };
@@ -2640,9 +2597,7 @@ function buildScope(parts) {
2640
2597
  var DefaultSelfChatCache = class {
2641
2598
  constructor() {
2642
2599
  this.cache = /* @__PURE__ */ new Map();
2643
- this.insertionOrder = [];
2644
- this.insertionOrderOffset = 0;
2645
- this.entryCount = 0;
2600
+ this.insertionOrder = /* @__PURE__ */ new Map();
2646
2601
  this.lastCleanupAt = 0;
2647
2602
  this.nextEntryId = 1;
2648
2603
  }
@@ -2656,19 +2611,14 @@ var DefaultSelfChatCache = class {
2656
2611
  if (!key || !isUsableTimestamp(lookup.createdAt)) return;
2657
2612
  const entries = this.cache.get(key) ?? /* @__PURE__ */ new Map();
2658
2613
  const entry = {
2659
- id: this.nextEntryId,
2660
2614
  createdAt: lookup.createdAt,
2661
2615
  createdAtSkewToleranceMs: lookup.allowCreatedAtSkew ? SELF_CHAT_CREATED_AT_TOLERANCE_MS : 0,
2662
2616
  rememberedAt: Date.now()
2663
2617
  };
2664
- this.nextEntryId += 1;
2665
- entries.set(entry.id, entry);
2618
+ const id = this.nextEntryId++;
2619
+ entries.set(id, entry);
2666
2620
  this.cache.set(key, entries);
2667
- this.insertionOrder.push({
2668
- key,
2669
- id: entry.id
2670
- });
2671
- this.entryCount += 1;
2621
+ this.insertionOrder.set(id, key);
2672
2622
  this.maybeCleanup();
2673
2623
  }
2674
2624
  has(lookup) {
@@ -2691,25 +2641,18 @@ var DefaultSelfChatCache = class {
2691
2641
  for (const [key, entries] of this.cache.entries()) {
2692
2642
  for (const [id, entry] of entries.entries()) if (now - entry.rememberedAt > SELF_CHAT_TTL_MS) {
2693
2643
  entries.delete(id);
2694
- this.entryCount -= 1;
2644
+ this.insertionOrder.delete(id);
2695
2645
  }
2696
2646
  if (entries.size === 0) this.cache.delete(key);
2697
2647
  }
2698
- while (this.entryCount > MAX_SELF_CHAT_CACHE_ENTRIES && this.insertionOrderOffset < this.insertionOrder.length) {
2699
- const oldest = expectDefined(this.insertionOrder[this.insertionOrderOffset], "oldest iMessage self-chat cache entry");
2700
- this.insertionOrderOffset += 1;
2701
- const entries = this.cache.get(oldest.key);
2648
+ while (this.insertionOrder.size > MAX_SELF_CHAT_CACHE_ENTRIES) {
2649
+ const [id, key] = expectDefined(this.insertionOrder.entries().next().value, "oldest iMessage self-chat cache entry");
2650
+ this.insertionOrder.delete(id);
2651
+ const entries = this.cache.get(key);
2702
2652
  if (!entries) continue;
2703
- if (!entries.delete(oldest.id)) continue;
2704
- this.entryCount -= 1;
2705
- if (entries.size === 0) this.cache.delete(oldest.key);
2653
+ entries.delete(id);
2654
+ if (entries.size === 0) this.cache.delete(key);
2706
2655
  }
2707
- this.compactInsertionOrder();
2708
- }
2709
- compactInsertionOrder() {
2710
- if (this.insertionOrderOffset <= 1024 && this.insertionOrder.length <= this.entryCount + 1024) return;
2711
- this.insertionOrder = this.insertionOrder.slice(this.insertionOrderOffset).filter((entry) => this.cache.get(entry.key)?.has(entry.id));
2712
- this.insertionOrderOffset = 0;
2713
2656
  }
2714
2657
  };
2715
2658
  function createSelfChatCache() {
@@ -2815,22 +2758,8 @@ function describeIMessageWatchSubscribeStartupFailure(params) {
2815
2758
  const retry = params.retryDelayMs !== void 0 ? ` retry_in_ms=${params.retryDelayMs}` : "";
2816
2759
  return `imessage: watch.subscribe startup failed attempt=${params.attempt}/${params.maxAttempts} account=${params.accountId} cliPath=${params.cliPath} dbPath=${params.dbPath ? "configured" : "default"} remoteHost=${params.remoteHost ? "configured" : "none"} timeoutMs=${params.probeTimeoutMs} since_rowid=${params.watchSinceRowid ?? "none"} attachments=${params.includeAttachments} include_reactions=true${retry}: ${String(params.error)}`;
2817
2760
  }
2818
- async function waitForWatchSubscribeRetryDelay(params) {
2819
- if (params.ms <= 0) return;
2820
- await new Promise((resolve) => {
2821
- const timer = setTimeout(() => {
2822
- params.abortSignal?.removeEventListener("abort", onAbort);
2823
- resolve();
2824
- }, params.ms);
2825
- const onAbort = () => {
2826
- clearTimeout(timer);
2827
- params.abortSignal?.removeEventListener("abort", onAbort);
2828
- resolve();
2829
- };
2830
- params.abortSignal?.addEventListener("abort", onAbort, { once: true });
2831
- });
2832
- }
2833
- async function monitorIMessageProvider(opts = {}) {
2761
+ async function monitorIMessageProvider(opts) {
2762
+ const { scheduler } = opts;
2834
2763
  const runtime = opts.runtime ?? createNonExitingRuntime();
2835
2764
  const cfg = opts.config ?? getRuntimeConfig();
2836
2765
  const readConfig = createRuntimeConfigReader(cfg);
@@ -2995,13 +2924,8 @@ async function monitorIMessageProvider(opts = {}) {
2995
2924
  await abandon();
2996
2925
  return;
2997
2926
  }
2998
- if (entries.length === 1) {
2999
- await handleMessageNow(expectDefined(entries[0], "single iMessage dispatch entry").message, admissionLifecycle);
3000
- await settle();
3001
- return;
3002
- }
3003
- const combined = combineIMessagePayloads(entries.map((entry) => entry.message));
3004
- if (shouldLogVerbose()) {
2927
+ const combined = entries.length === 1 ? expectDefined(entries[0], "single iMessage dispatch entry").message : combineIMessagePayloads(entries.map((entry) => entry.message));
2928
+ if (entries.length > 1 && shouldLogVerbose()) {
3005
2929
  const text = combined.text ?? "";
3006
2930
  const preview = sliceUtf16Safe(text, 0, 50);
3007
2931
  const ellipsis = text.length > 50 ? "..." : "";
@@ -3378,6 +3302,10 @@ async function monitorIMessageProvider(opts = {}) {
3378
3302
  }
3379
3303
  };
3380
3304
  let directTypingController;
3305
+ const startDirectToolTyping = async () => {
3306
+ await directTypingController?.startTypingLoop();
3307
+ return false;
3308
+ };
3381
3309
  const directToolTypingOptions = shouldUseDirectToolTypingOptions ? {
3382
3310
  suppressDefaultToolProgressMessages: true,
3383
3311
  allowToolLifecycleWhenProgressHidden: true,
@@ -3385,14 +3313,8 @@ async function monitorIMessageProvider(opts = {}) {
3385
3313
  onTypingController: (typing) => {
3386
3314
  directTypingController = typing;
3387
3315
  },
3388
- onToolResult: async () => {
3389
- await directTypingController?.startTypingLoop();
3390
- return false;
3391
- },
3392
- ...supportsTyping ? { onToolStart: async () => {
3393
- await directTypingController?.startTypingLoop();
3394
- return false;
3395
- } } : {}
3316
+ onToolResult: startDirectToolTyping,
3317
+ ...supportsTyping ? { onToolStart: startDirectToolTyping } : {}
3396
3318
  } : {};
3397
3319
  const configuredBlockStreaming = resolveChannelStreamingBlockEnabled(accountInfo.config);
3398
3320
  const inboundLastRouteSessionKey = resolveInboundLastRouteSessionKey({
@@ -3608,25 +3530,27 @@ async function monitorIMessageProvider(opts = {}) {
3608
3530
  } catch (err) {
3609
3531
  if (abort?.aborted) return;
3610
3532
  const retriable = isRetriableWatchSubscribeStartupError(err);
3611
- if (!(attempt < WATCH_SUBSCRIBE_MAX_ATTEMPTS && retriable)) {
3533
+ const shouldRetry = attempt < WATCH_SUBSCRIBE_MAX_ATTEMPTS && retriable;
3534
+ const failureParams = {
3535
+ accountId: accountInfo.accountId,
3536
+ attempt,
3537
+ maxAttempts: WATCH_SUBSCRIBE_MAX_ATTEMPTS,
3538
+ cliPath,
3539
+ dbPath,
3540
+ remoteHost,
3541
+ includeAttachments,
3542
+ probeTimeoutMs,
3543
+ watchSinceRowid,
3544
+ error: err
3545
+ };
3546
+ if (!shouldRetry) {
3612
3547
  opts.statusSink?.({
3613
3548
  connected: false,
3614
3549
  lifecycle: retriable ? "recovering" : "blocked",
3615
3550
  terminalDisconnect: retriable ? void 0 : true,
3616
3551
  lastError: String(err)
3617
3552
  });
3618
- runtime.error?.(danger(`imessage: monitor failed: ${describeIMessageWatchSubscribeStartupFailure({
3619
- accountId: accountInfo.accountId,
3620
- attempt,
3621
- maxAttempts: WATCH_SUBSCRIBE_MAX_ATTEMPTS,
3622
- cliPath,
3623
- dbPath,
3624
- remoteHost,
3625
- includeAttachments,
3626
- probeTimeoutMs,
3627
- watchSinceRowid,
3628
- error: err
3629
- })}`));
3553
+ runtime.error?.(danger(`imessage: monitor failed: ${describeIMessageWatchSubscribeStartupFailure(failureParams)}`));
3630
3554
  throw err;
3631
3555
  }
3632
3556
  opts.statusSink?.({
@@ -3635,25 +3559,15 @@ async function monitorIMessageProvider(opts = {}) {
3635
3559
  lastError: String(err)
3636
3560
  });
3637
3561
  runtime.log?.(warn(describeIMessageWatchSubscribeStartupFailure({
3638
- accountId: accountInfo.accountId,
3639
- attempt,
3640
- maxAttempts: WATCH_SUBSCRIBE_MAX_ATTEMPTS,
3641
- cliPath,
3642
- dbPath,
3643
- remoteHost,
3644
- includeAttachments,
3645
- probeTimeoutMs,
3646
- watchSinceRowid,
3647
- error: err,
3562
+ ...failureParams,
3648
3563
  retryDelayMs: WATCH_SUBSCRIBE_RETRY_DELAY_MS
3649
3564
  })));
3650
3565
  attemptDetachAbortHandler();
3651
3566
  attemptDetachAbortHandler = () => {};
3652
3567
  await attemptClient?.stop();
3653
3568
  attemptClient = void 0;
3654
- await waitForWatchSubscribeRetryDelay({
3655
- ms: WATCH_SUBSCRIBE_RETRY_DELAY_MS,
3656
- abortSignal: abort
3569
+ await sleepWithAbort(WATCH_SUBSCRIBE_RETRY_DELAY_MS, abort).catch((error) => {
3570
+ if (!abort?.aborted) throw error;
3657
3571
  });
3658
3572
  if (abort?.aborted) return;
3659
3573
  } finally {
@@ -3664,7 +3578,11 @@ async function monitorIMessageProvider(opts = {}) {
3664
3578
  }
3665
3579
  }
3666
3580
  const activeClient = client;
3667
- if (!activeClient) return;
3581
+ if (!activeClient || scheduler.signal.aborted) {
3582
+ detachAbortHandler();
3583
+ await activeClient?.stop();
3584
+ return;
3585
+ }
3668
3586
  ingress.start();
3669
3587
  const approvalContextLease = opts.channelRuntime ? registerChannelRuntimeContext({
3670
3588
  channelRuntime: opts.channelRuntime,
@@ -3674,32 +3592,29 @@ async function monitorIMessageProvider(opts = {}) {
3674
3592
  context: { accountId: accountInfo.accountId },
3675
3593
  abortSignal: abort
3676
3594
  }) : void 0;
3677
- let approvalReactionPollInFlight = false;
3678
- const pollApprovalReactions = async (allowRecentChatDiscovery = false) => {
3679
- if (approvalReactionPollInFlight) return;
3680
- approvalReactionPollInFlight = true;
3681
- try {
3682
- await pollPendingIMessageApprovalReactions({
3595
+ const approvalScheduler = scheduler.scope();
3596
+ let nextDiscoveryAt = 0;
3597
+ approvalScheduler.schedule({
3598
+ id: "approval-reactions",
3599
+ delayMs: 0,
3600
+ everyMs: APPROVAL_REACTION_POLL_INTERVAL_MS,
3601
+ run: () => {
3602
+ const now = approvalScheduler.now();
3603
+ const allowRecentChatDiscovery = now >= nextDiscoveryAt;
3604
+ if (allowRecentChatDiscovery) nextDiscoveryAt = now + APPROVAL_REACTION_DISCOVERY_INTERVAL_MS;
3605
+ return pollPendingIMessageApprovalReactions({
3606
+ signal: approvalScheduler.signal,
3683
3607
  client: activeClient,
3684
3608
  cfg,
3685
3609
  accountId: accountInfo.accountId,
3686
3610
  allowRecentChatDiscovery,
3687
3611
  gatewayRuntime: approvalGatewayRuntime,
3688
3612
  logVerboseMessage: logVerbose
3613
+ }).catch((err) => {
3614
+ logVerbose(`imessage: approval reaction poll failed: ${String(err)}`);
3689
3615
  });
3690
- } catch (err) {
3691
- logVerbose(`imessage: approval reaction poll failed: ${String(err)}`);
3692
- } finally {
3693
- approvalReactionPollInFlight = false;
3694
3616
  }
3695
- };
3696
- const approvalReactionPollTimer = setInterval(() => {
3697
- pollApprovalReactions();
3698
- }, APPROVAL_REACTION_POLL_INTERVAL_MS);
3699
- const approvalReactionDiscoveryTimer = setInterval(() => {
3700
- pollApprovalReactions(true);
3701
- }, APPROVAL_REACTION_DISCOVERY_INTERVAL_MS);
3702
- pollApprovalReactions(true);
3617
+ });
3703
3618
  if (catchupCfg.enabled && !abort?.aborted) {
3704
3619
  startupCatchupInProgress = true;
3705
3620
  try {
@@ -3739,13 +3654,94 @@ async function monitorIMessageProvider(opts = {}) {
3739
3654
  runtime.error?.(danger(`imessage: monitor failed: ${String(err)}`));
3740
3655
  throw err;
3741
3656
  } finally {
3742
- clearInterval(approvalReactionPollTimer);
3743
- clearInterval(approvalReactionDiscoveryTimer);
3657
+ approvalScheduler.beginClose();
3744
3658
  approvalContextLease?.dispose();
3745
3659
  detachAbortHandler();
3746
3660
  await activeClient.stop();
3661
+ await approvalScheduler.stop();
3747
3662
  await ingress.stop();
3748
3663
  }
3749
3664
  }
3750
3665
  //#endregion
3751
- export { monitorIMessageProvider as t };
3666
+ //#region extensions/imessage/src/channel.runtime.ts
3667
+ async function sendIMessageOutbound(params) {
3668
+ const send = resolveOutboundSendDep(params.deps, "imessage", { legacyKeys: IMESSAGE_LEGACY_OUTBOUND_SEND_DEP_KEYS }) ?? sendMessageIMessage;
3669
+ const maxBytes = resolveChannelMediaMaxBytes({
3670
+ cfg: params.cfg,
3671
+ resolveChannelLimitMb: () => resolveIMessageAccount(params).config.mediaMaxMb
3672
+ });
3673
+ const result = await send(params.to, params.text, {
3674
+ config: params.cfg,
3675
+ ...params.mediaUrl ? { mediaUrl: params.mediaUrl } : {},
3676
+ ...params.mediaAccess ? { mediaAccess: params.mediaAccess } : {},
3677
+ ...params.mediaLocalRoots?.length ? { mediaLocalRoots: params.mediaLocalRoots } : {},
3678
+ ...params.mediaReadFile ? { mediaReadFile: params.mediaReadFile } : {},
3679
+ ...params.audioAsVoice ? { audioAsVoice: true } : {},
3680
+ maxBytes,
3681
+ accountId: params.accountId ?? void 0,
3682
+ replyToId: params.replyToId ?? void 0,
3683
+ conversationReadOrigin: params.conversationReadOrigin,
3684
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff,
3685
+ onPlatformSendDispatch: params.onPlatformSendDispatch,
3686
+ ...params.onDeliveryResult ? { onDeliveryResult: params.onDeliveryResult } : {}
3687
+ });
3688
+ const meta = {
3689
+ ...result.meta,
3690
+ ...result.guid ? { imessageMessageGuid: result.guid } : {},
3691
+ ...result.sentText ? { imessageVisibleText: result.sentText } : {}
3692
+ };
3693
+ return Object.keys(meta).length > 0 ? {
3694
+ ...result,
3695
+ meta
3696
+ } : result;
3697
+ }
3698
+ async function probeIMessageAccount(params) {
3699
+ return await probeIMessage(params?.timeoutMs, {
3700
+ cliPath: params?.cliPath,
3701
+ dbPath: params?.dbPath,
3702
+ remoteHost: params?.remoteHost,
3703
+ forceRefresh: true
3704
+ });
3705
+ }
3706
+ async function startIMessageGatewayAccount(ctx) {
3707
+ const account = ctx.account;
3708
+ const cliPath = account.config.cliPath?.trim() || "imsg";
3709
+ const dbPath = account.config.dbPath?.trim();
3710
+ ctx.setStatus({
3711
+ accountId: account.accountId,
3712
+ cliPath,
3713
+ dbPath: dbPath ?? null
3714
+ });
3715
+ await Promise.all(listEnabledIMessageAccounts(ctx.cfg).map(async (candidate) => {
3716
+ await resolveIMessageRemoteHost({
3717
+ cliPath: candidate.config.cliPath?.trim() || "imsg",
3718
+ remoteHost: candidate.config.remoteHost
3719
+ });
3720
+ }));
3721
+ const ownerAccountId = resolveIMessageDuplicateSourceOwner({
3722
+ cfg: ctx.cfg,
3723
+ account
3724
+ });
3725
+ if (ownerAccountId) {
3726
+ ctx.log?.info?.(`[${account.accountId}] skipping watcher: duplicate iMessage source; using account "${ownerAccountId}"`);
3727
+ await waitForAbortSignal(ctx.abortSignal);
3728
+ return;
3729
+ }
3730
+ const statusSink = createAccountStatusSink({
3731
+ accountId: account.accountId,
3732
+ setStatus: ctx.setStatus
3733
+ });
3734
+ statusSink({ lifecycle: "starting" });
3735
+ ctx.log?.info?.(`[${account.accountId}] starting provider (${cliPath}${dbPath ? ` db=${dbPath}` : ""})`);
3736
+ return await monitorIMessageProvider({
3737
+ scheduler: ctx.scheduler,
3738
+ accountId: account.accountId,
3739
+ config: ctx.cfg,
3740
+ runtime: ctx.runtime,
3741
+ abortSignal: ctx.abortSignal,
3742
+ channelRuntime: ctx.channelRuntime,
3743
+ statusSink
3744
+ });
3745
+ }
3746
+ //#endregion
3747
+ export { imessageSetupWizard, probeIMessageAccount, sendIMessageOutbound, startIMessageGatewayAccount };