@openclaw/imessage 2026.8.1-beta.3 → 2026.8.2

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 (156) hide show
  1. package/dist/{accounts-D00Njxg2.js → accounts-CCirBEIm.js} +4 -5
  2. package/dist/{actions.runtime-B_xJiBXF.js → actions.runtime-UDLBuS_8.js} +5 -5
  3. package/dist/api.js +6 -5
  4. package/dist/{approval-handler.runtime-BKku8aUf.js → approval-handler.runtime-ZaQQ2c4l.js} +9 -8
  5. package/dist/{approval-reactions-BME5eA6T.js → approval-reactions-B2-_97DN.js} +7 -11
  6. package/dist/{channel-BFQTpvrG.js → channel-B0wzN0uJ.js} +18 -16
  7. package/dist/channel-config-api.js +1 -1
  8. package/dist/channel-plugin-api.js +1 -1
  9. package/dist/{channel.runtime-BjkXwX9_.js → channel.runtime-lJfW7FBd.js} +60 -13
  10. package/dist/{config-schema-DV0KP5nC.js → config-schema-DuGbA8Kp.js} +8 -7
  11. package/dist/contract-api.js +1 -1
  12. package/dist/doctor-contract-api.js +8 -281
  13. package/dist/{group-policy-Ca3c5KES.js → group-policy-fYokgrgz.js} +25 -23
  14. package/dist/media-contract-api.js +1 -1
  15. package/dist/{media-contract-BEgdvYhW.js → media-contract-pns3TSS5.js} +3 -2
  16. package/dist/{message-tool-api-TZiNYA17.js → message-tool-api-CLWaomvw.js} +9 -7
  17. package/dist/message-tool-api.js +1 -1
  18. package/dist/{monitor-Uv5RaRTk.js → monitor-CSTZ-GFU.js} +304 -40
  19. package/dist/{question-reactions-to0i8vxE.js → question-reactions-viP7ZcyG.js} +1 -1
  20. package/dist/{reaction-context-DaNrZqYo.js → reaction-context-BAYI7pz0.js} +2 -2
  21. package/dist/{remote-file-CDuk0joc.js → remote-file-CmdycSRk.js} +9 -8
  22. package/dist/runtime-Cza4CY5T.js +8 -0
  23. package/dist/runtime-api.js +10 -9
  24. package/dist/{sanitize-outbound-DaLmdkbf.js → sanitize-outbound-DKRlFQoD.js} +371 -80
  25. package/dist/{send-Dake4Awx.js → send-DxhFcMfW.js} +146 -24
  26. package/dist/state-contract-DvahEY4X.js +56 -0
  27. package/dist/state-migrations-BhY4RwjU.js +277 -0
  28. package/node_modules/typebox/build/format/date.d.mts +1 -1
  29. package/node_modules/typebox/build/format/date.mjs +1 -1
  30. package/node_modules/typebox/build/format/date_time.d.mts +2 -2
  31. package/node_modules/typebox/build/format/date_time.mjs +3 -3
  32. package/node_modules/typebox/build/format/email.d.mts +1 -1
  33. package/node_modules/typebox/build/format/email.mjs +2 -2
  34. package/node_modules/typebox/build/format/hostname.mjs +2 -10
  35. package/node_modules/typebox/build/format/idn_email.d.mts +1 -1
  36. package/node_modules/typebox/build/format/idn_email.mjs +3 -3
  37. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -0
  38. package/node_modules/typebox/build/format/idn_hostname.mjs +3 -11
  39. package/node_modules/typebox/build/format/idna/format/bidi.d.mts +3 -0
  40. package/node_modules/typebox/build/format/idna/format/bidi.mjs +98 -0
  41. package/node_modules/typebox/build/format/idna/format/puny.d.mts +3 -0
  42. package/node_modules/typebox/build/format/idna/format/puny.mjs +144 -0
  43. package/node_modules/typebox/build/format/idna/hostname.d.mts +1 -0
  44. package/node_modules/typebox/build/format/idna/hostname.mjs +16 -0
  45. package/node_modules/typebox/build/format/idna/idn-hostname.d.mts +1 -0
  46. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +41 -0
  47. package/node_modules/typebox/build/format/idna/index.d.mts +2 -0
  48. package/node_modules/typebox/build/format/idna/index.mjs +2 -0
  49. package/node_modules/typebox/build/format/idna/label/ascii.d.mts +1 -0
  50. package/node_modules/typebox/build/format/idna/label/ascii.mjs +9 -0
  51. package/node_modules/typebox/build/format/idna/label/puny.d.mts +1 -0
  52. package/node_modules/typebox/build/format/idna/label/puny.mjs +28 -0
  53. package/node_modules/typebox/build/format/idna/label/unicode.d.mts +1 -0
  54. package/node_modules/typebox/build/format/idna/label/unicode.mjs +106 -0
  55. package/node_modules/typebox/build/format/idna/pattern/pattern.d.mts +30 -0
  56. package/node_modules/typebox/build/format/idna/pattern/pattern.mjs +70 -0
  57. package/node_modules/typebox/build/format/ipv4.d.mts +0 -1
  58. package/node_modules/typebox/build/format/ipv4.mjs +2 -32
  59. package/node_modules/typebox/build/format/ipv6.mjs +2 -61
  60. package/node_modules/typebox/build/format/iri.d.mts +2 -2
  61. package/node_modules/typebox/build/format/iri.mjs +26 -8
  62. package/node_modules/typebox/build/format/iri_reference.d.mts +2 -2
  63. package/node_modules/typebox/build/format/iri_reference.mjs +9 -55
  64. package/node_modules/typebox/build/format/regex.d.mts +1 -2
  65. package/node_modules/typebox/build/format/regex.mjs +2 -6
  66. package/node_modules/typebox/build/format/time.d.mts +2 -2
  67. package/node_modules/typebox/build/format/time.mjs +19 -14
  68. package/node_modules/typebox/build/format/uri.d.mts +1 -1
  69. package/node_modules/typebox/build/format/uri.mjs +3 -130
  70. package/node_modules/typebox/build/format/uri_reference.d.mts +1 -1
  71. package/node_modules/typebox/build/format/uri_reference.mjs +2 -3
  72. package/node_modules/typebox/build/format/uri_template.d.mts +2 -3
  73. package/node_modules/typebox/build/format/uri_template.mjs +4 -5
  74. package/node_modules/typebox/build/format/url.d.mts +3 -3
  75. package/node_modules/typebox/build/format/url.mjs +4 -5
  76. package/node_modules/typebox/build/guard/emit.d.mts +3 -1
  77. package/node_modules/typebox/build/guard/emit.mjs +12 -5
  78. package/node_modules/typebox/build/guard/guard.d.mts +8 -2
  79. package/node_modules/typebox/build/guard/guard.mjs +25 -4
  80. package/node_modules/typebox/build/guard/string.d.mts +2 -2
  81. package/node_modules/typebox/build/guard/string.mjs +7 -7
  82. package/node_modules/typebox/build/schema/engine/_context.mjs +3 -3
  83. package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -2
  84. package/node_modules/typebox/build/schema/engine/_regexp.d.mts +2 -0
  85. package/node_modules/typebox/build/schema/engine/_regexp.mjs +4 -0
  86. package/node_modules/typebox/build/schema/engine/_stack.mjs +12 -13
  87. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +17 -7
  88. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +4 -3
  89. package/node_modules/typebox/build/schema/engine/contains.mjs +18 -6
  90. package/node_modules/typebox/build/schema/engine/enum.mjs +1 -1
  91. package/node_modules/typebox/build/schema/engine/items.mjs +26 -6
  92. package/node_modules/typebox/build/schema/engine/maxContains.mjs +3 -3
  93. package/node_modules/typebox/build/schema/engine/minContains.mjs +14 -4
  94. package/node_modules/typebox/build/schema/engine/not.mjs +4 -2
  95. package/node_modules/typebox/build/schema/engine/oneOf.mjs +7 -6
  96. package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -2
  97. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +4 -3
  98. package/node_modules/typebox/build/schema/engine/ref.mjs +1 -7
  99. package/node_modules/typebox/build/schema/engine/schema.mjs +13 -4
  100. package/node_modules/typebox/build/schema/engine/type.mjs +1 -1
  101. package/node_modules/typebox/build/schema/resolve/resolve.d.mts +4 -1
  102. package/node_modules/typebox/build/schema/resolve/resolve.mjs +202 -1
  103. package/node_modules/typebox/build/schema/static/propertyNames.d.mts +5 -0
  104. package/node_modules/typebox/build/schema/static/propertyNames.mjs +3 -0
  105. package/node_modules/typebox/build/schema/static/schema.d.mts +3 -0
  106. package/node_modules/typebox/build/schema/types/index.d.mts +0 -2
  107. package/node_modules/typebox/build/schema/types/index.mjs +0 -2
  108. package/node_modules/typebox/build/system/memory/clone.mjs +14 -11
  109. package/node_modules/typebox/build/system/memory/discard.mjs +6 -5
  110. package/node_modules/typebox/build/system/settings/settings.d.mts +9 -0
  111. package/node_modules/typebox/build/system/settings/settings.mjs +2 -0
  112. package/node_modules/typebox/build/type/engine/call/distribute_arguments.d.mts +6 -1
  113. package/node_modules/typebox/build/type/engine/call/distribute_arguments.mjs +16 -3
  114. package/node_modules/typebox/build/type/engine/call/instantiate.mjs +34 -4
  115. package/node_modules/typebox/build/type/engine/evaluate/broaden.d.mts +5 -5
  116. package/node_modules/typebox/build/type/engine/evaluate/broaden.mjs +20 -26
  117. package/node_modules/typebox/build/type/engine/evaluate/compare.d.mts +6 -10
  118. package/node_modules/typebox/build/type/engine/evaluate/compare.mjs +9 -13
  119. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +7 -4
  120. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +12 -19
  121. package/node_modules/typebox/build/type/engine/evaluate/distribute.d.mts +4 -20
  122. package/node_modules/typebox/build/type/engine/evaluate/distribute.mjs +8 -20
  123. package/node_modules/typebox/build/type/engine/evaluate/evaluate.d.mts +4 -4
  124. package/node_modules/typebox/build/type/engine/evaluate/evaluate.mjs +11 -8
  125. package/node_modules/typebox/build/type/engine/evaluate/flatten.d.mts +1 -1
  126. package/node_modules/typebox/build/type/engine/evaluate/flatten.mjs +3 -4
  127. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +17 -2
  128. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +26 -6
  129. package/node_modules/typebox/build/type/engine/exclude/operation.mjs +3 -4
  130. package/node_modules/typebox/build/type/engine/extract/operation.d.mts +1 -1
  131. package/node_modules/typebox/build/type/engine/extract/operation.mjs +3 -4
  132. package/node_modules/typebox/build/type/engine/priority/priority.d.mts +2 -2
  133. package/node_modules/typebox/build/type/engine/priority/priority.mjs +3 -4
  134. package/node_modules/typebox/build/type/script/mapping.d.mts +32 -29
  135. package/node_modules/typebox/build/type/script/mapping.mjs +18 -25
  136. package/node_modules/typebox/build/type/script/parser.d.mts +19 -25
  137. package/node_modules/typebox/build/type/script/parser.mjs +27 -23
  138. package/node_modules/typebox/build/value/errors/errors.d.mts +2 -2
  139. package/node_modules/typebox/build/value/errors/errors.mjs +1 -1
  140. package/node_modules/typebox/package.json +27 -27
  141. package/node_modules/typebox/readme.md +79 -82
  142. package/openclaw.plugin.json +0 -6
  143. package/package.json +5 -5
  144. package/dist/catchup-Dh-Rv4Ur.js +0 -294
  145. package/dist/monitor-reply-cache-BdeUQaHO.js +0 -353
  146. package/dist/persisted-echo-cache-DGxy-J4t.js +0 -143
  147. package/node_modules/typebox/build/format/_idna.d.mts +0 -2
  148. package/node_modules/typebox/build/format/_idna.mjs +0 -246
  149. package/node_modules/typebox/build/format/_puny.d.mts +0 -1
  150. package/node_modules/typebox/build/format/_puny.mjs +0 -77
  151. package/node_modules/typebox/build/schema/resolve/ref.d.mts +0 -3
  152. package/node_modules/typebox/build/schema/resolve/ref.mjs +0 -146
  153. package/node_modules/typebox/build/schema/types/contentEncoding.d.mts +0 -9
  154. package/node_modules/typebox/build/schema/types/contentEncoding.mjs +0 -13
  155. package/node_modules/typebox/build/schema/types/contentMediaType.d.mts +0 -9
  156. package/node_modules/typebox/build/schema/types/contentMediaType.mjs +0 -13
@@ -1,15 +1,14 @@
1
- import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount } from "./accounts-D00Njxg2.js";
2
- import { b as imessageRpcSupportsMethod, c as parseIMessageAllowTarget, i as isAllowedIMessageReplyContextSender, l as parseIMessageTarget, n as formatIMessageChatTarget, s as normalizeIMessageHandle, y as getCachedIMessagePrivateApiStatus } from "./message-tool-api-TZiNYA17.js";
3
- import { c as rememberIMessageReplyCache, f as getIMessageRuntime, s as isKnownFromMeIMessageMessageId, v as resolveIMessageDirectChatService } from "./monitor-reply-cache-BdeUQaHO.js";
4
- import { o as createIMessageRpcClient, r as sanitizeOutboundText } from "./sanitize-outbound-DaLmdkbf.js";
5
- import { r as resolveIMessageInboundConversationId, y as probeIMessage } from "./group-policy-Ca3c5KES.js";
6
- import { n as resolveIMessageAttachmentRoots, r as resolveIMessageRemoteAttachmentRoots } from "./media-contract-BEgdvYhW.js";
7
- import { a as resolveCatchupConfig, i as performIMessageCatchup, n as advanceIMessageCatchupCursor } from "./catchup-Dh-Rv4Ur.js";
8
- import { c as stripLeadingEchoTextCorruptionMarkers, i as hasPersistedIMessageEcho, o as resolveIMessageEchoMediaKey } from "./persisted-echo-cache-DGxy-J4t.js";
9
- import { i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction, s as buildIMessageApprovalConversationKeyForInbound } from "./approval-reactions-BME5eA6T.js";
10
- import { a as maybeResolveIMessageApprovalPollVote, o as iMessageApprovalControlBindings, t as sendMessageIMessage } from "./send-Dake4Awx.js";
11
- import { t as resolveIMessageReactionContext } from "./reaction-context-DaNrZqYo.js";
12
- import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-to0i8vxE.js";
1
+ import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount } from "./accounts-CCirBEIm.js";
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-CLWaomvw.js";
3
+ import { l as isKnownFromMeIMessageMessageId, o as createIMessageRpcClient, r as sanitizeOutboundText, u as rememberIMessageReplyCache, v as resolveIMessageDirectChatService } from "./sanitize-outbound-DKRlFQoD.js";
4
+ import { f as probeIMessage, r as resolveIMessageInboundConversationId } from "./group-policy-fYokgrgz.js";
5
+ import { t as getIMessageRuntime } from "./runtime-Cza4CY5T.js";
6
+ import { d as resolveIMessageEchoMediaKey, l as capFailureRetriesMap, t as IMESSAGE_CATCHUP_CURSOR_NAMESPACE, u as resolveIMessageCatchupCursorKey } from "./state-contract-DvahEY4X.js";
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-B2-_97DN.js";
9
+ import { c as iMessageApprovalControlBindings, n as hasPersistedIMessageEcho, r as stripLeadingEchoTextCorruptionMarkers, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-DxhFcMfW.js";
10
+ import { t as resolveIMessageReactionContext } from "./reaction-context-BAYI7pz0.js";
11
+ import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-viP7ZcyG.js";
13
12
  import { bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
14
13
  import { chunkMarkdownTextWithMode as chunkTextWithMode, resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
15
14
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
@@ -18,17 +17,15 @@ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
18
17
  import { asSafeIntegerInRange, hasNonEmptyString, isRecord, normalizeOptionalString, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
19
18
  import fs from "node:fs/promises";
20
19
  import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, warn } from "openclaw/plugin-sdk/runtime-env";
21
- import { asDateTimestampMs, asPositiveFiniteNumber, parseDateStringTimestampMs, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
20
+ import { isInboundPathAllowed, kindFromMime } from "openclaw/plugin-sdk/media-runtime";
21
+ import { asDateTimestampMs, asPositiveFiniteNumber, parseDateStringTimestampMs, resolveIntegerOption, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
22
22
  import { createHash } from "node:crypto";
23
23
  import { 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 { isInboundPathAllowed, kindFromMime } from "openclaw/plugin-sdk/media-runtime";
27
- import { convertMarkdownTables as convertMarkdownTables$1, findCodeRegions, isInsideCode, sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
28
26
  import { buildChannelGroupsScopeTree, resolveChannelGroupPolicy, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
27
+ import { convertMarkdownTables as convertMarkdownTables$1, findCodeRegions, isInsideCode, sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
29
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";
30
- import { openLocalFileSafely, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
31
- import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
32
29
  import { resolveAgentConfig, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
33
30
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-runtime";
34
31
  import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
@@ -40,12 +37,14 @@ import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
40
37
  import { redactIdentifier } from "openclaw/plugin-sdk/logging-core";
41
38
  import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
42
39
  import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
40
+ import { openLocalFileSafely, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
43
41
  import { getSessionEntry, readSessionUpdatedAt, resolveSendPolicy, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
44
42
  import { openNodeSqliteDatabase } from "openclaw/plugin-sdk/sqlite-runtime";
45
43
  import { waitForTransportReady } from "openclaw/plugin-sdk/transport-ready-runtime";
46
44
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
47
45
  import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
48
46
  import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
47
+ import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
49
48
  import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
50
49
  import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
51
50
  import { hasControlCommand } from "openclaw/plugin-sdk/command-auth-native";
@@ -264,6 +263,269 @@ function attachIMessageMonitorAbortHandler(params) {
264
263
  return () => abort.removeEventListener("abort", onAbort);
265
264
  }
266
265
  //#endregion
266
+ //#region extensions/imessage/src/monitor/catchup.ts
267
+ const DEFAULT_MAX_AGE_MINUTES = 120;
268
+ const MAX_MAX_AGE_MINUTES = 720;
269
+ const DEFAULT_PER_RUN_LIMIT = 50;
270
+ const MAX_PER_RUN_LIMIT = 500;
271
+ const DEFAULT_FIRST_RUN_LOOKBACK_MINUTES = 30;
272
+ const DEFAULT_MAX_FAILURE_RETRIES = 10;
273
+ const MAX_MAX_FAILURE_RETRIES = 1e3;
274
+ const cursorWriteQueue = new KeyedAsyncQueue();
275
+ function openCatchupCursorStore() {
276
+ return getIMessageRuntime().state.openSyncKeyedStore({
277
+ namespace: IMESSAGE_CATCHUP_CURSOR_NAMESPACE,
278
+ maxEntries: 256
279
+ });
280
+ }
281
+ function updateCatchupCursorStore(key, updateValue) {
282
+ const store = openCatchupCursorStore();
283
+ if (!store.update) throw new Error("iMessage catchup cursor persistence requires atomic plugin-state update.");
284
+ return store.update(key, updateValue);
285
+ }
286
+ function enqueueCursorWrite(accountId, fn) {
287
+ const key = resolveIMessageCatchupCursorKey(accountId);
288
+ return cursorWriteQueue.enqueue(key, fn);
289
+ }
290
+ function sanitizeFailureRetriesInput(raw) {
291
+ if (!raw || typeof raw !== "object") return {};
292
+ const out = {};
293
+ for (const [guid, count] of Object.entries(raw)) {
294
+ if (!guid || typeof guid !== "string") continue;
295
+ if (typeof count !== "number" || !Number.isFinite(count) || count <= 0) continue;
296
+ out[guid] = Math.floor(count);
297
+ }
298
+ return out;
299
+ }
300
+ function normalizeIMessageCatchupCursor(value) {
301
+ if (!value || typeof value !== "object") return null;
302
+ const raw = value;
303
+ if (typeof raw.lastSeenMs !== "number" || !Number.isFinite(raw.lastSeenMs)) return null;
304
+ if (typeof raw.lastSeenRowid !== "number" || !Number.isFinite(raw.lastSeenRowid)) return null;
305
+ const failureRetries = sanitizeFailureRetriesInput(raw.failureRetries);
306
+ const hasRetries = Object.keys(failureRetries).length > 0;
307
+ return {
308
+ lastSeenMs: raw.lastSeenMs,
309
+ lastSeenRowid: raw.lastSeenRowid,
310
+ updatedAt: typeof raw.updatedAt === "number" ? raw.updatedAt : 0,
311
+ ...hasRetries ? { failureRetries } : {}
312
+ };
313
+ }
314
+ function readIMessageCatchupCursor(accountId) {
315
+ return normalizeIMessageCatchupCursor(openCatchupCursorStore().lookup(resolveIMessageCatchupCursorKey(accountId)));
316
+ }
317
+ async function loadIMessageCatchupCursor(accountId) {
318
+ return readIMessageCatchupCursor(accountId);
319
+ }
320
+ function buildIMessageCatchupCursor(next) {
321
+ const sanitized = sanitizeFailureRetriesInput(next.failureRetries);
322
+ const hasRetries = Object.keys(sanitized).length > 0;
323
+ return {
324
+ lastSeenMs: next.lastSeenMs,
325
+ lastSeenRowid: next.lastSeenRowid,
326
+ updatedAt: Date.now(),
327
+ ...hasRetries ? { failureRetries: sanitized } : {}
328
+ };
329
+ }
330
+ async function saveIMessageCatchupCursor(accountId, next, options = {}) {
331
+ const cursor = buildIMessageCatchupCursor(next);
332
+ updateCatchupCursorStore(resolveIMessageCatchupCursorKey(accountId), (existingValue) => {
333
+ const existing = normalizeIMessageCatchupCursor(existingValue);
334
+ if (existing && cursor.lastSeenRowid < existing.lastSeenRowid) {
335
+ if (!options.allowCursorRewindForRetries) return;
336
+ return buildIMessageCatchupCursor({
337
+ lastSeenMs: cursor.lastSeenMs,
338
+ lastSeenRowid: cursor.lastSeenRowid,
339
+ failureRetries: {
340
+ ...existing.failureRetries,
341
+ ...cursor.failureRetries
342
+ }
343
+ });
344
+ }
345
+ return cursor;
346
+ });
347
+ }
348
+ function clampInt(value, min, max, fallback) {
349
+ return resolveIntegerOption(value, fallback, {
350
+ min,
351
+ max
352
+ });
353
+ }
354
+ function resolveCatchupConfig(raw) {
355
+ return {
356
+ enabled: Boolean(raw?.enabled),
357
+ maxAgeMinutes: clampInt(raw?.maxAgeMinutes, 1, MAX_MAX_AGE_MINUTES, DEFAULT_MAX_AGE_MINUTES),
358
+ perRunLimit: clampInt(raw?.perRunLimit, 1, MAX_PER_RUN_LIMIT, DEFAULT_PER_RUN_LIMIT),
359
+ firstRunLookbackMinutes: clampInt(raw?.firstRunLookbackMinutes, 1, MAX_MAX_AGE_MINUTES, DEFAULT_FIRST_RUN_LOOKBACK_MINUTES),
360
+ maxFailureRetries: clampInt(raw?.maxFailureRetries, 1, MAX_MAX_FAILURE_RETRIES, DEFAULT_MAX_FAILURE_RETRIES)
361
+ };
362
+ }
363
+ async function advanceIMessageCatchupCursor(accountId, next, config) {
364
+ if (!Number.isFinite(next.lastSeenMs) || !Number.isFinite(next.lastSeenRowid)) return false;
365
+ return await enqueueCursorWrite(accountId, async () => {
366
+ let advanced = false;
367
+ updateCatchupCursorStore(resolveIMessageCatchupCursorKey(accountId), (existingValue) => {
368
+ const cursor = normalizeIMessageCatchupCursor(existingValue);
369
+ if (cursor && next.lastSeenRowid <= cursor.lastSeenRowid) return;
370
+ if (Object.values(cursor?.failureRetries ?? {}).some((count) => count < config.maxFailureRetries)) return;
371
+ advanced = true;
372
+ return buildIMessageCatchupCursor({
373
+ lastSeenMs: Math.max(cursor?.lastSeenMs ?? next.lastSeenMs, next.lastSeenMs),
374
+ lastSeenRowid: next.lastSeenRowid,
375
+ failureRetries: cursor?.failureRetries
376
+ });
377
+ });
378
+ return advanced;
379
+ });
380
+ }
381
+ /**
382
+ * One catchup pass. Loads the cursor, fetches `messages.history`, replays
383
+ * each row through `dispatch`, advances the cursor on success / give-up,
384
+ * persists the cursor, returns a summary.
385
+ *
386
+ * The fetch and dispatch functions are injected so this loop is unit-testable
387
+ * without standing up an `imsg` daemon. The wiring in `monitor-provider.ts`
388
+ * passes the live `client.request("messages.history", ...)` adapter as
389
+ * `fetch` and the `evaluateIMessageInbound` + `runChannelInboundEvent`
390
+ * pipeline as `dispatch`.
391
+ */
392
+ async function performIMessageCatchup(params) {
393
+ const now = params.now ?? Date.now();
394
+ const cfg = params.config;
395
+ const cursor = await loadIMessageCatchupCursor(params.accountId);
396
+ const lookbackMs = cursor === null ? cfg.firstRunLookbackMinutes * 6e4 : cfg.maxAgeMinutes * 6e4;
397
+ const ageBoundMs = now - cfg.maxAgeMinutes * 6e4;
398
+ const windowStartMs = Math.max(cursor?.lastSeenMs ?? now - lookbackMs, ageBoundMs);
399
+ const windowEndMs = now;
400
+ const sinceRowid = cursor?.lastSeenRowid ?? 0;
401
+ const summary = {
402
+ querySucceeded: false,
403
+ fullyCaughtUp: false,
404
+ fetchedCount: 0,
405
+ replayed: 0,
406
+ skippedFromMe: 0,
407
+ skippedPreCursor: 0,
408
+ skippedGivenUp: 0,
409
+ failed: 0,
410
+ givenUp: 0,
411
+ cursorBefore: cursor ? {
412
+ lastSeenMs: cursor.lastSeenMs,
413
+ lastSeenRowid: cursor.lastSeenRowid
414
+ } : null,
415
+ cursorAfter: {
416
+ lastSeenMs: cursor?.lastSeenMs ?? windowStartMs,
417
+ lastSeenRowid: cursor?.lastSeenRowid ?? 0
418
+ },
419
+ windowStartMs,
420
+ windowEndMs
421
+ };
422
+ let fetchResult;
423
+ try {
424
+ fetchResult = await params.fetch({
425
+ sinceMs: windowStartMs,
426
+ sinceRowid,
427
+ limit: cfg.perRunLimit
428
+ });
429
+ } catch (err) {
430
+ params.warn?.(`imessage catchup: fetch failed: ${String(err)}`);
431
+ return summary;
432
+ }
433
+ if (!fetchResult.resolved) {
434
+ params.warn?.(`imessage catchup: fetch returned unresolved result`);
435
+ return summary;
436
+ }
437
+ summary.querySucceeded = true;
438
+ summary.fullyCaughtUp = fetchResult.fullyCaughtUp !== false;
439
+ summary.fetchedCount = fetchResult.rows.length;
440
+ const rows = fetchResult.rows.toSorted((a, b) => a.rowid - b.rowid);
441
+ const failureRetries = { ...cursor?.failureRetries };
442
+ const cursorBeforeMs = cursor?.lastSeenMs ?? windowStartMs;
443
+ const cursorBeforeRowid = cursor?.lastSeenRowid ?? 0;
444
+ let highWatermarkMs = cursorBeforeMs;
445
+ let highWatermarkRowid = cursorBeforeRowid;
446
+ let earliestHeldFailureRow = null;
447
+ for (const row of rows) {
448
+ if (row.rowid <= sinceRowid) {
449
+ summary.skippedPreCursor += 1;
450
+ continue;
451
+ }
452
+ if (row.date < ageBoundMs) {
453
+ summary.skippedPreCursor += 1;
454
+ highWatermarkMs = Math.max(highWatermarkMs, row.date);
455
+ highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
456
+ continue;
457
+ }
458
+ if (row.isFromMe) {
459
+ try {
460
+ await params.observeSkippedFromMe?.(row);
461
+ } catch (err) {
462
+ params.warn?.(`imessage catchup: from-me observer failed for guid=${row.guid}: ${String(err)}`);
463
+ }
464
+ summary.skippedFromMe += 1;
465
+ highWatermarkMs = Math.max(highWatermarkMs, row.date);
466
+ highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
467
+ continue;
468
+ }
469
+ const priorCount = failureRetries[row.guid] ?? 0;
470
+ if (priorCount >= cfg.maxFailureRetries) {
471
+ summary.skippedGivenUp += 1;
472
+ highWatermarkMs = Math.max(highWatermarkMs, row.date);
473
+ highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
474
+ continue;
475
+ }
476
+ let dispatched;
477
+ try {
478
+ dispatched = await params.dispatch(row);
479
+ } catch (err) {
480
+ params.warn?.(`imessage catchup: dispatch threw for guid=${row.guid}: ${String(err)}`);
481
+ dispatched = { ok: false };
482
+ }
483
+ if (dispatched.ok) {
484
+ summary.replayed += 1;
485
+ delete failureRetries[row.guid];
486
+ highWatermarkMs = Math.max(highWatermarkMs, row.date);
487
+ highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
488
+ continue;
489
+ }
490
+ const nextCount = priorCount + 1;
491
+ failureRetries[row.guid] = nextCount;
492
+ summary.failed += 1;
493
+ if (nextCount >= cfg.maxFailureRetries) {
494
+ summary.givenUp += 1;
495
+ params.warn?.(`imessage catchup: giving up on guid=${row.guid} after ${nextCount} failures; advancing cursor past it`);
496
+ highWatermarkMs = Math.max(highWatermarkMs, row.date);
497
+ highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
498
+ continue;
499
+ }
500
+ if (earliestHeldFailureRow === null || row.rowid < earliestHeldFailureRow.rowid) earliestHeldFailureRow = row;
501
+ }
502
+ if (earliestHeldFailureRow === null) {
503
+ if (typeof fetchResult.highWatermarkMs === "number") highWatermarkMs = Math.max(highWatermarkMs, fetchResult.highWatermarkMs);
504
+ if (typeof fetchResult.highWatermarkRowid === "number") highWatermarkRowid = Math.max(highWatermarkRowid, fetchResult.highWatermarkRowid);
505
+ }
506
+ let lastSeenMs;
507
+ let lastSeenRowid;
508
+ if (earliestHeldFailureRow !== null) {
509
+ lastSeenMs = Math.max(cursorBeforeMs, earliestHeldFailureRow.date - 1);
510
+ lastSeenRowid = Math.max(cursorBeforeRowid, earliestHeldFailureRow.rowid - 1);
511
+ } else {
512
+ lastSeenMs = highWatermarkMs;
513
+ lastSeenRowid = highWatermarkRowid;
514
+ }
515
+ const capped = capFailureRetriesMap(failureRetries);
516
+ summary.cursorAfter = {
517
+ lastSeenMs,
518
+ lastSeenRowid
519
+ };
520
+ await saveIMessageCatchupCursor(params.accountId, {
521
+ lastSeenMs,
522
+ lastSeenRowid,
523
+ failureRetries: capped
524
+ }, { allowCursorRewindForRetries: earliestHeldFailureRow !== null });
525
+ if (summary.replayed > 0 || summary.failed > 0 || summary.givenUp > 0) params.log?.(`imessage catchup: replayed=${summary.replayed} skippedFromMe=${summary.skippedFromMe} skippedGivenUp=${summary.skippedGivenUp} failed=${summary.failed} givenUp=${summary.givenUp} fetchedCount=${summary.fetchedCount}`);
526
+ return summary;
527
+ }
528
+ //#endregion
267
529
  //#region extensions/imessage/src/monitor/strip-imsg-length-prefixed-text.ts
268
530
  const utf8Decoder = new TextDecoder();
269
531
  function readVarint(buf, start) {
@@ -304,9 +566,6 @@ function stripImessageLengthPrefixedUtf8Text(text) {
304
566
  function isOptionalString(value) {
305
567
  return value === void 0 || value === null || typeof value === "string";
306
568
  }
307
- function isOptionalStringOrNumber(value) {
308
- return value === void 0 || value === null || typeof value === "string" || typeof value === "number";
309
- }
310
569
  function isOptionalNumber(value) {
311
570
  return value === void 0 || value === null || typeof value === "number";
312
571
  }
@@ -329,7 +588,7 @@ function parseIMessageNotification(raw) {
329
588
  const maybeMessage = raw.message;
330
589
  if (!isRecord(maybeMessage)) return null;
331
590
  const message = maybeMessage;
332
- if (!isOptionalNumber(message.id) || !isOptionalString(message.guid) || !isOptionalNumber(message.chat_id) || !isOptionalString(message.sender) || !isOptionalString(message.destination_caller_id) || !isOptionalBoolean(message.is_from_me) || !isOptionalString(message.text) || !isOptionalStringOrNumber(message.reply_to_id) || !isOptionalString(message.reply_to_guid) || !isOptionalString(message.reply_to_text) || !isOptionalString(message.reply_to_sender) || !isOptionalString(message.created_at) || !isOptionalBoolean(message.is_reaction) || !isOptionalBoolean(message.is_tapback) || !isOptionalString(message.associated_message_guid) || !isOptionalNumber(message.associated_message_type) || !isOptionalString(message.reaction_type) || !isOptionalString(message.reaction_emoji) || !isOptionalBoolean(message.is_reaction_add) || !isOptionalString(message.reacted_to_guid) || !isOptionalAttachments(message.attachments) || !isOptionalString(message.chat_identifier) || !isOptionalString(message.chat_guid) || !isOptionalString(message.chat_name) || !isOptionalStringArray(message.participants) || !isOptionalBoolean(message.is_group)) return null;
591
+ if (!isOptionalNumber(message.id) || !isOptionalString(message.guid) || !isOptionalNumber(message.chat_id) || !isOptionalString(message.sender) || !isOptionalString(message.destination_caller_id) || !isOptionalBoolean(message.is_from_me) || !isOptionalString(message.text) || !isOptionalString(message.thread_originator_guid) || !isOptionalString(message.reply_to_guid) || !isOptionalString(message.reply_to_text) || !isOptionalString(message.reply_to_sender) || !isOptionalString(message.created_at) || !isOptionalBoolean(message.is_reaction) || !isOptionalBoolean(message.is_tapback) || !isOptionalString(message.associated_message_guid) || !isOptionalNumber(message.associated_message_type) || !isOptionalString(message.reaction_type) || !isOptionalString(message.reaction_emoji) || !isOptionalBoolean(message.is_reaction_add) || !isOptionalString(message.reacted_to_guid) || !isOptionalAttachments(message.attachments) || !isOptionalString(message.chat_identifier) || !isOptionalString(message.chat_guid) || !isOptionalString(message.chat_name) || !isOptionalStringArray(message.participants) || !isOptionalBoolean(message.is_group)) return null;
333
592
  return {
334
593
  ...message,
335
594
  text: typeof message.text === "string" ? stripImessageLengthPrefixedUtf8Text(message.text) : message.text,
@@ -518,7 +777,7 @@ function combineIMessagePayloads(payloads) {
518
777
  const first = expectDefined(payloads[0], "first iMessage payload to coalesce");
519
778
  if (payloads.length === 1) return first;
520
779
  const last = expectDefined(payloads.at(-1), "last iMessage payload to coalesce");
521
- const boundedPayloads = payloads.length > MAX_COALESCED_ENTRIES ? [...payloads.slice(0, MAX_COALESCED_ENTRIES - 1), last] : payloads;
780
+ const boundedPayloads = payloads.length > MAX_COALESCED_ENTRIES ? [...payloads.slice(0, 9), last] : payloads;
522
781
  const seenTexts = /* @__PURE__ */ new Set();
523
782
  const textParts = [];
524
783
  for (const payload of boundedPayloads) {
@@ -549,15 +808,16 @@ function combineIMessagePayloads(payloads) {
549
808
  seenGuids.add(guid);
550
809
  coalescedMessageGuids.push(guid);
551
810
  }
552
- const entryWithReply = payloads.find((p) => p.reply_to_id != null);
811
+ const reply = payloads.find((payload) => payload.thread_originator_guid != null || payload.reply_to_guid != null) ?? first;
553
812
  return {
554
813
  ...first,
555
814
  text: combinedText,
556
815
  attachments: allAttachments.length > 0 ? allAttachments : null,
557
816
  created_at: latestCreatedAt,
558
- reply_to_id: entryWithReply?.reply_to_id ?? first.reply_to_id ?? null,
559
- reply_to_text: entryWithReply?.reply_to_text ?? first.reply_to_text ?? null,
560
- reply_to_sender: entryWithReply?.reply_to_sender ?? first.reply_to_sender ?? null,
817
+ thread_originator_guid: reply.thread_originator_guid ?? null,
818
+ reply_to_guid: reply.reply_to_guid ?? null,
819
+ reply_to_text: reply.reply_to_text ?? null,
820
+ reply_to_sender: reply.reply_to_sender ?? null,
561
821
  coalescedMessageGuids: coalescedMessageGuids.length > 0 ? coalescedMessageGuids : void 0,
562
822
  coalescedCatchupCursor: Number.isFinite(maxRowid) && Number.isFinite(maxDateMs) ? {
563
823
  lastSeenMs: maxDateMs,
@@ -700,7 +960,8 @@ async function deliverIMessageReply(params) {
700
960
  accountId
701
961
  });
702
962
  const chunkMode = resolveChunkMode(cfg, "imessage", accountId);
703
- const reply = resolveSendableOutboundReplyParts(payload, { text: convertMarkdownTables$1(sanitizeOutboundText(payload.text ?? ""), tableMode) });
963
+ const rawText = sanitizeOutboundText(payload.text ?? "");
964
+ const reply = resolveSendableOutboundReplyParts(payload, { text: convertMarkdownTables$1(rawText, tableMode) });
704
965
  const accepted = [];
705
966
  const sendAccepted = async (text, mediaUrl) => {
706
967
  const sent = await sendMessageIMessage(target, text, {
@@ -762,7 +1023,8 @@ async function deliverIMessageReply(params) {
762
1023
  }
763
1024
  function createIMessageEchoCachingSend(params) {
764
1025
  return async (target, text, opts) => {
765
- const sent = await sendMessageIMessage(target, sanitizeOutboundText(text), opts);
1026
+ const sanitizedText = sanitizeOutboundText(text);
1027
+ const sent = await sendMessageIMessage(target, sanitizedText, opts);
766
1028
  const scope = `${params.accountId ?? opts.accountId ?? ""}:${target}`;
767
1029
  const echoText = sent.echoText ?? (sent.sentText || void 0);
768
1030
  params.sentMessageCache?.remember(scope, {
@@ -977,8 +1239,8 @@ function warnGroupAllowlistDropPerChatOnce(params) {
977
1239
  }
978
1240
  //#endregion
979
1241
  //#region extensions/imessage/src/monitor/inbound-dedupe.ts
980
- const IMESSAGE_STALE_INBOUND_THRESHOLD_MS = 900 * 1e3;
981
- const IMESSAGE_RECOVERY_MAX_AGE_MS = 7200 * 1e3;
1242
+ const IMESSAGE_STALE_INBOUND_THRESHOLD_MS = 9e5;
1243
+ const IMESSAGE_RECOVERY_MAX_AGE_MS = 72e5;
982
1244
  /**
983
1245
  * Age fence: true when the message's own send date is materially older than
984
1246
  * now, i.e. stale backlog rather than a live message. Fails open (returns
@@ -1177,7 +1439,7 @@ function describeReplyContext(message) {
1177
1439
  if (!body) return null;
1178
1440
  return {
1179
1441
  body,
1180
- id: normalizeReplyField(message.reply_to_id),
1442
+ id: normalizeReplyField(message.thread_originator_guid) ?? normalizeReplyField(message.reply_to_guid),
1181
1443
  sender: normalizeReplyField(message.reply_to_sender)
1182
1444
  };
1183
1445
  }
@@ -1722,6 +1984,7 @@ async function buildIMessageInboundContext(params) {
1722
1984
  const directService = resolveIMessageDirectChatService(params.accountService, decision.chatGuid) ?? "auto";
1723
1985
  const imessageTo = decision.isGroup ? chatTarget || `imessage:${decision.sender}` : `${directService}:${decision.sender}`;
1724
1986
  const imessageFrom = decision.isGroup ? `imessage:group:${chatId ?? "unknown"}` : imessageTo;
1987
+ const replyTarget = decision.isGroup ? imessageTo : chatId != null ? `chat_id:${chatId}` : decision.chatGuid ? `chat_guid:${decision.chatGuid}` : imessageTo;
1725
1988
  const inboundHistory = !decision.isGroup && params.dmHistory?.inboundHistory ? params.dmHistory.inboundHistory : decision.isGroup && decision.historyKey && params.historyLimit > 0 ? createChannelHistoryWindow({ historyMap: params.groupHistories }).buildInboundHistory({
1726
1989
  historyKey: decision.historyKey,
1727
1990
  limit: params.historyLimit
@@ -1749,7 +2012,8 @@ async function buildIMessageInboundContext(params) {
1749
2012
  from: imessageFrom,
1750
2013
  sender: {
1751
2014
  id: decision.sender,
1752
- name: decision.senderNormalized
2015
+ name: decision.senderNormalized,
2016
+ isSelf: params.message.is_from_me === true
1753
2017
  },
1754
2018
  conversation: {
1755
2019
  kind: decision.isGroup ? "group" : "direct",
@@ -1766,7 +2030,7 @@ async function buildIMessageInboundContext(params) {
1766
2030
  accountId: decision.route.accountId,
1767
2031
  routeSessionKey: decision.route.sessionKey
1768
2032
  },
1769
- reply: { to: imessageTo },
2033
+ reply: { to: replyTarget },
1770
2034
  message: {
1771
2035
  body: combinedBody,
1772
2036
  bodyForAgent: decision.agentBodyText ?? decision.bodyText,
@@ -1924,7 +2188,7 @@ function createIMessageDurableIngress(options) {
1924
2188
  },
1925
2189
  pollIntervalMs: IMESSAGE_INGRESS_DRAIN_INTERVAL_MS,
1926
2190
  retention: {
1927
- completedTtlMs: 14400 * 1e3,
2191
+ completedTtlMs: 144e5,
1928
2192
  completedMaxEntries: 1e4,
1929
2193
  failedMaxEntries: 1e3
1930
2194
  },
@@ -2051,18 +2315,18 @@ async function readPinnedAttachmentBytes(handle, maxBytes) {
2051
2315
  let totalBytes = 0;
2052
2316
  while (totalBytes <= maxBytes) {
2053
2317
  const remaining = maxBytes + 1 - totalBytes;
2054
- const chunk = Buffer.allocUnsafe(Math.min(64 * 1024, remaining));
2318
+ const chunk = Buffer.allocUnsafe(Math.min(65536, remaining));
2055
2319
  const { bytesRead } = await handle.read(chunk, 0, chunk.length, null);
2056
2320
  if (bytesRead === 0) return Buffer.concat(chunks, totalBytes);
2057
2321
  chunks.push(chunk.subarray(0, bytesRead));
2058
2322
  totalBytes += bytesRead;
2059
2323
  }
2060
- throw new Error(`attachment exceeds ${Math.round(maxBytes / (1024 * 1024))}MB limit`);
2324
+ throw new Error(`attachment exceeds ${Math.round(maxBytes / 1048576)}MB limit`);
2061
2325
  }
2062
2326
  async function readAttachmentBuffer(params) {
2063
2327
  const opened = await (params.deps.openLocalFileSafely ?? openLocalFileSafely)({ filePath: params.attachmentPath });
2064
2328
  try {
2065
- if (opened.stat.size > params.maxBytes) throw new Error(`attachment exceeds ${Math.round(params.maxBytes / (1024 * 1024))}MB limit`);
2329
+ if (opened.stat.size > params.maxBytes) throw new Error(`attachment exceeds ${Math.round(params.maxBytes / 1048576)}MB limit`);
2066
2330
  await assertAllowedCanonicalAttachmentPath({
2067
2331
  canonicalPath: opened.realPath,
2068
2332
  allowedRoots: params.allowedRoots
@@ -2442,7 +2706,7 @@ function sanitizeIMessageWatchErrorPayload(payload) {
2442
2706
  if (typeof payload.code === "number" && Number.isFinite(payload.code)) safe.code = payload.code;
2443
2707
  if (typeof payload.message === "string") {
2444
2708
  const sanitizedMessage = sanitizeTerminalText(payload.message);
2445
- if (sanitizedMessage) safe.message = sanitizedMessage.length > MAX_WATCH_ERROR_MESSAGE_CHARS ? `${truncateUtf16Safe(sanitizedMessage, MAX_WATCH_ERROR_MESSAGE_CHARS - 1)}…` : sanitizedMessage;
2709
+ if (sanitizedMessage) safe.message = sanitizedMessage.length > MAX_WATCH_ERROR_MESSAGE_CHARS ? `${truncateUtf16Safe(sanitizedMessage, 199)}…` : sanitizedMessage;
2446
2710
  }
2447
2711
  return safe;
2448
2712
  }
@@ -2454,7 +2718,7 @@ const CHANNEL_APPROVAL_GATEWAY_RUNTIME_CONTEXT_CAPABILITY = "approval.gateway";
2454
2718
  const APPROVAL_REACTION_POLL_INTERVAL_MS = 2e3;
2455
2719
  const APPROVAL_REACTION_DISCOVERY_INTERVAL_MS = 6e4;
2456
2720
  const IMESSAGE_TYPING_KEEPALIVE_INTERVAL_MS = 8e3;
2457
- const IMESSAGE_TYPING_KEEPALIVE_MAX_DURATION_MS = 10 * 6e4;
2721
+ const IMESSAGE_TYPING_KEEPALIVE_MAX_DURATION_MS = 6e5;
2458
2722
  function resolveConfiguredIMessageTypingMode(cfg, agentId) {
2459
2723
  return resolveAgentConfig(cfg, agentId)?.typingMode ?? cfg.agents?.defaults?.typingMode;
2460
2724
  }
@@ -2838,7 +3102,7 @@ async function monitorIMessageProvider(opts = {}) {
2838
3102
  const conversationKey = chatId != null ? `group:${chatId}` : `dm:${senderForKey}`;
2839
3103
  const rateLimitKey = `${accountInfo.accountId}:${conversationKey}`;
2840
3104
  if (decision.kind === "drop") {
2841
- if (decision.reason === "echo" || decision.reason === "self-chat echo" || decision.reason === "reflected assistant content") loopRateLimiter.record(rateLimitKey);
3105
+ if (decision.reason === "echo" || decision.reason === "reflected assistant content") loopRateLimiter.record(rateLimitKey);
2842
3106
  const diagnostic = describeIMessageInboundDropDiagnostic({
2843
3107
  accountId: accountInfo.accountId,
2844
3108
  reason: decision.reason,
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { t as resolveIMessageReactionContext } from "./reaction-context-DaNrZqYo.js";
2
+ import { t as resolveIMessageReactionContext } from "./reaction-context-BAYI7pz0.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({
@@ -80,11 +80,11 @@ function resolveTapbackTextContext(bodyText) {
80
80
  for (const pattern of TAPBACK_TEXT_PATTERNS) {
81
81
  if (!lower.startsWith(pattern.prefix)) continue;
82
82
  const afterPrefix = bodyText.slice(pattern.prefix.length).trim();
83
- if (!/^["“]/u.test(afterPrefix)) continue;
83
+ if (!/^(?:"[\s\S]*"|“[\s\S]*”)$/u.test(afterPrefix)) continue;
84
84
  return {
85
85
  action: pattern.action,
86
86
  emoji: pattern.emoji,
87
- targetText: afterPrefix.replace(/^["“]/u, "").replace(/["”]$/u, "").trim()
87
+ targetText: afterPrefix.slice(1, -1).trim()
88
88
  };
89
89
  }
90
90
  return null;
@@ -1,5 +1,5 @@
1
- import { p as resolveLocalIMessageChatDbPath, u as expandIMessageUserPath } from "./accounts-D00Njxg2.js";
2
- import { g as isIMessageEmailChatIdentifier, h as chatContextFromIMessageTarget, l as resolveIMessageCachedResourceBinding, u as resolveIMessageMessageId } from "./monitor-reply-cache-BdeUQaHO.js";
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-DKRlFQoD.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";
@@ -9,8 +9,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
11
  //#region extensions/imessage/src/cli-output.ts
12
- const IMESSAGE_CLI_STDOUT_MAX_BYTES = 8 * 1024 * 1024;
13
- const IMESSAGE_CLI_STDERR_TAIL_BYTES = 64 * 1024;
12
+ const IMESSAGE_CLI_STDOUT_MAX_BYTES = 8388608;
13
+ const IMESSAGE_CLI_STDERR_TAIL_BYTES = 65536;
14
14
  function parseLastJsonObject(stdout) {
15
15
  const last = stdout.split(/\r?\n/u).findLast((line) => line.trim().length > 0)?.trim();
16
16
  if (!last) return null;
@@ -23,12 +23,13 @@ function parseLastJsonObject(stdout) {
23
23
  }
24
24
  async function runIMessageCliJsonCommand(params) {
25
25
  const dbPath = params.dbPath?.trim();
26
- const result = await runCommandWithTimeout([
26
+ const argv = [
27
27
  expandIMessageUserPath(params.cliPath),
28
28
  ...params.args,
29
29
  ...dbPath ? ["--db", dbPath] : [],
30
30
  "--json"
31
- ], {
31
+ ];
32
+ const result = await runCommandWithTimeout(argv, {
32
33
  killProcessTree: true,
33
34
  maxOutputBytes: {
34
35
  stdout: IMESSAGE_CLI_STDOUT_MAX_BYTES,
@@ -213,8 +214,8 @@ async function runChecked(run, label, argv, options) {
213
214
  const result = await run(argv, {
214
215
  killProcessTree: true,
215
216
  maxOutputBytes: {
216
- stdout: 4 * 1024,
217
- stderr: 64 * 1024
217
+ stdout: 4096,
218
+ stderr: 65536
218
219
  },
219
220
  outputCapture: {
220
221
  stdout: "head",
@@ -0,0 +1,8 @@
1
+ import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
2
+ //#region extensions/imessage/src/runtime.ts
3
+ const { getRuntime: getIMessageRuntime, setRuntime: setIMessageRuntime, tryGetRuntime: getOptionalIMessageRuntime } = createPluginRuntimeStore({
4
+ pluginId: "imessage",
5
+ errorMessage: "iMessage runtime not initialized"
6
+ });
7
+ //#endregion
8
+ export { getOptionalIMessageRuntime as n, setIMessageRuntime as r, getIMessageRuntime as t };
@@ -1,15 +1,16 @@
1
- import { o as resolveIMessageAccount } from "./accounts-D00Njxg2.js";
2
- import { m as setIMessageRuntime } from "./monitor-reply-cache-BdeUQaHO.js";
3
- import { h as normalizeIMessageMessagingTarget, l as chunkTextForOutbound, m as looksLikeIMessageTargetId, n as resolveIMessageGroupToolPolicy, t as resolveIMessageGroupRequireMention, v as imessageMessageActions, y as probeIMessage } from "./group-policy-Ca3c5KES.js";
4
- import { n as IMessageConfigSchema } from "./config-schema-DV0KP5nC.js";
5
- import { t as sendMessageIMessage } from "./send-Dake4Awx.js";
6
- import { t as monitorIMessageProvider } from "./monitor-Uv5RaRTk.js";
7
- import { buildComputedAccountStatusSnapshot, collectStatusIssuesFromLastError } from "openclaw/plugin-sdk/status-helpers";
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-fYokgrgz.js";
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-DxhFcMfW.js";
6
+ import { t as monitorIMessageProvider } from "./monitor-CSTZ-GFU.js";
8
7
  import { formatTrimmedAllowFromEntries } from "openclaw/plugin-sdk/channel-config-helpers";
9
- import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
8
+ import { buildComputedAccountStatusSnapshot, collectStatusIssuesFromLastError } from "openclaw/plugin-sdk/status-helpers";
9
+ import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/account-helpers";
10
+ import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
10
11
  import { DEFAULT_ACCOUNT_ID, getChatChannelMeta } from "openclaw/plugin-sdk/core";
11
- import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/media-runtime";
12
12
  import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
13
+ import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
13
14
  //#region extensions/imessage/src/config-accessors.ts
14
15
  function resolveIMessageConfigAllowFrom(params) {
15
16
  return (resolveIMessageAccount(params).config.allowFrom ?? []).map((entry) => String(entry));