@openclaw/imessage 2026.8.1-beta.2 → 2026.8.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 (157) hide show
  1. package/dist/{accounts-D00Njxg2.js → accounts-CCirBEIm.js} +4 -5
  2. package/dist/{actions.runtime-DTFKDN5p.js → actions.runtime-CAl3WcqD.js} +2 -3
  3. package/dist/api.js +6 -5
  4. package/dist/{approval-handler.runtime-BtLs4gho.js → approval-handler.runtime-UBd1j2Sg.js} +15 -9
  5. package/dist/approval-reactions-LL5yn1Rc.js +562 -0
  6. package/dist/{channel-lppmI51h.js → channel-DNI9DKGN.js} +26 -21
  7. package/dist/channel-config-api.js +1 -1
  8. package/dist/channel-plugin-api.js +1 -1
  9. package/dist/{channel.runtime-5wpCKQb7.js → channel.runtime-HSkrmWfW.js} +58 -11
  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-DF9xSdik.js → group-policy-Dvc-BcWo.js} +19 -651
  14. package/dist/{media-contract-BEgdvYhW.js → media-contract-D-FhGE98.js} +1 -1
  15. package/dist/media-contract-api.js +1 -1
  16. package/dist/{message-tool-api-CPDfJJoE.js → message-tool-api-CvIgJpOo.js} +7 -4
  17. package/dist/message-tool-api.js +1 -1
  18. package/dist/{monitor-DnxJ8pW9.js → monitor-BywJn1QW.js} +343 -111
  19. package/dist/{question-reactions-DVe50UhB.js → question-reactions-viP7ZcyG.js} +2 -1
  20. package/dist/reaction-context-BAYI7pz0.js +105 -0
  21. package/dist/{remote-file-DLwO9ved.js → remote-file-6mCj2ZBy.js} +7 -3
  22. package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
  23. package/dist/runtime-Cza4CY5T.js +8 -0
  24. package/dist/runtime-api.js +10 -9
  25. package/dist/{sanitize-outbound-CoTZLXMw.js → sanitize-outbound-Du-r1lNQ.js} +345 -58
  26. package/dist/{send-BUXW76tw.js → send-mgWPKqSV.js} +147 -26
  27. package/dist/state-contract-4TK_cDDb.js +56 -0
  28. package/dist/state-migrations-SFdKp3GQ.js +277 -0
  29. package/node_modules/typebox/build/format/date.d.mts +1 -1
  30. package/node_modules/typebox/build/format/date.mjs +1 -1
  31. package/node_modules/typebox/build/format/date_time.d.mts +2 -2
  32. package/node_modules/typebox/build/format/date_time.mjs +3 -3
  33. package/node_modules/typebox/build/format/email.d.mts +1 -1
  34. package/node_modules/typebox/build/format/email.mjs +2 -2
  35. package/node_modules/typebox/build/format/hostname.mjs +2 -10
  36. package/node_modules/typebox/build/format/idn_email.d.mts +1 -1
  37. package/node_modules/typebox/build/format/idn_email.mjs +3 -3
  38. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -0
  39. package/node_modules/typebox/build/format/idn_hostname.mjs +3 -11
  40. package/node_modules/typebox/build/format/idna/format/bidi.d.mts +3 -0
  41. package/node_modules/typebox/build/format/idna/format/bidi.mjs +98 -0
  42. package/node_modules/typebox/build/format/idna/format/puny.d.mts +3 -0
  43. package/node_modules/typebox/build/format/idna/format/puny.mjs +144 -0
  44. package/node_modules/typebox/build/format/idna/hostname.d.mts +1 -0
  45. package/node_modules/typebox/build/format/idna/hostname.mjs +16 -0
  46. package/node_modules/typebox/build/format/idna/idn-hostname.d.mts +1 -0
  47. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +23 -0
  48. package/node_modules/typebox/build/format/idna/index.d.mts +2 -0
  49. package/node_modules/typebox/build/format/idna/index.mjs +2 -0
  50. package/node_modules/typebox/build/format/idna/label/ascii.d.mts +1 -0
  51. package/node_modules/typebox/build/format/idna/label/ascii.mjs +9 -0
  52. package/node_modules/typebox/build/format/idna/label/puny.d.mts +1 -0
  53. package/node_modules/typebox/build/format/idna/label/puny.mjs +28 -0
  54. package/node_modules/typebox/build/format/idna/label/unicode.d.mts +1 -0
  55. package/node_modules/typebox/build/format/idna/label/unicode.mjs +106 -0
  56. package/node_modules/typebox/build/format/idna/pattern/pattern.d.mts +30 -0
  57. package/node_modules/typebox/build/format/idna/pattern/pattern.mjs +70 -0
  58. package/node_modules/typebox/build/format/ipv4.d.mts +0 -1
  59. package/node_modules/typebox/build/format/ipv4.mjs +2 -32
  60. package/node_modules/typebox/build/format/ipv6.mjs +2 -61
  61. package/node_modules/typebox/build/format/iri.d.mts +2 -2
  62. package/node_modules/typebox/build/format/iri.mjs +26 -8
  63. package/node_modules/typebox/build/format/iri_reference.d.mts +2 -2
  64. package/node_modules/typebox/build/format/iri_reference.mjs +9 -55
  65. package/node_modules/typebox/build/format/regex.d.mts +1 -2
  66. package/node_modules/typebox/build/format/regex.mjs +2 -6
  67. package/node_modules/typebox/build/format/time.d.mts +2 -2
  68. package/node_modules/typebox/build/format/time.mjs +19 -14
  69. package/node_modules/typebox/build/format/uri.d.mts +1 -1
  70. package/node_modules/typebox/build/format/uri.mjs +3 -130
  71. package/node_modules/typebox/build/format/uri_reference.d.mts +1 -1
  72. package/node_modules/typebox/build/format/uri_reference.mjs +2 -3
  73. package/node_modules/typebox/build/format/uri_template.d.mts +2 -3
  74. package/node_modules/typebox/build/format/uri_template.mjs +4 -5
  75. package/node_modules/typebox/build/format/url.d.mts +3 -3
  76. package/node_modules/typebox/build/format/url.mjs +4 -5
  77. package/node_modules/typebox/build/guard/emit.d.mts +3 -1
  78. package/node_modules/typebox/build/guard/emit.mjs +12 -5
  79. package/node_modules/typebox/build/guard/guard.d.mts +8 -2
  80. package/node_modules/typebox/build/guard/guard.mjs +25 -4
  81. package/node_modules/typebox/build/guard/string.d.mts +2 -2
  82. package/node_modules/typebox/build/guard/string.mjs +7 -7
  83. package/node_modules/typebox/build/schema/engine/_context.mjs +3 -3
  84. package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -2
  85. package/node_modules/typebox/build/schema/engine/_regexp.d.mts +2 -0
  86. package/node_modules/typebox/build/schema/engine/_regexp.mjs +4 -0
  87. package/node_modules/typebox/build/schema/engine/_stack.mjs +12 -13
  88. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +17 -7
  89. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +4 -3
  90. package/node_modules/typebox/build/schema/engine/contains.mjs +18 -6
  91. package/node_modules/typebox/build/schema/engine/enum.mjs +1 -1
  92. package/node_modules/typebox/build/schema/engine/items.mjs +26 -6
  93. package/node_modules/typebox/build/schema/engine/maxContains.mjs +3 -3
  94. package/node_modules/typebox/build/schema/engine/minContains.mjs +14 -4
  95. package/node_modules/typebox/build/schema/engine/not.mjs +4 -2
  96. package/node_modules/typebox/build/schema/engine/oneOf.mjs +7 -6
  97. package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -2
  98. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +4 -3
  99. package/node_modules/typebox/build/schema/engine/ref.mjs +1 -7
  100. package/node_modules/typebox/build/schema/engine/schema.mjs +13 -4
  101. package/node_modules/typebox/build/schema/engine/type.mjs +1 -1
  102. package/node_modules/typebox/build/schema/resolve/resolve.d.mts +4 -1
  103. package/node_modules/typebox/build/schema/resolve/resolve.mjs +202 -1
  104. package/node_modules/typebox/build/schema/static/propertyNames.d.mts +5 -0
  105. package/node_modules/typebox/build/schema/static/propertyNames.mjs +3 -0
  106. package/node_modules/typebox/build/schema/static/schema.d.mts +3 -0
  107. package/node_modules/typebox/build/schema/types/index.d.mts +0 -2
  108. package/node_modules/typebox/build/schema/types/index.mjs +0 -2
  109. package/node_modules/typebox/build/system/memory/clone.mjs +14 -11
  110. package/node_modules/typebox/build/system/memory/discard.mjs +6 -5
  111. package/node_modules/typebox/build/system/settings/settings.d.mts +9 -0
  112. package/node_modules/typebox/build/system/settings/settings.mjs +2 -0
  113. package/node_modules/typebox/build/type/engine/call/distribute_arguments.d.mts +6 -1
  114. package/node_modules/typebox/build/type/engine/call/distribute_arguments.mjs +16 -3
  115. package/node_modules/typebox/build/type/engine/call/instantiate.mjs +34 -4
  116. package/node_modules/typebox/build/type/engine/evaluate/broaden.d.mts +5 -5
  117. package/node_modules/typebox/build/type/engine/evaluate/broaden.mjs +20 -26
  118. package/node_modules/typebox/build/type/engine/evaluate/compare.d.mts +6 -10
  119. package/node_modules/typebox/build/type/engine/evaluate/compare.mjs +9 -13
  120. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +7 -4
  121. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +12 -19
  122. package/node_modules/typebox/build/type/engine/evaluate/distribute.d.mts +4 -20
  123. package/node_modules/typebox/build/type/engine/evaluate/distribute.mjs +8 -20
  124. package/node_modules/typebox/build/type/engine/evaluate/evaluate.d.mts +4 -4
  125. package/node_modules/typebox/build/type/engine/evaluate/evaluate.mjs +11 -8
  126. package/node_modules/typebox/build/type/engine/evaluate/flatten.d.mts +1 -1
  127. package/node_modules/typebox/build/type/engine/evaluate/flatten.mjs +3 -4
  128. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +17 -2
  129. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +26 -6
  130. package/node_modules/typebox/build/type/engine/exclude/operation.mjs +3 -4
  131. package/node_modules/typebox/build/type/engine/extract/operation.d.mts +1 -1
  132. package/node_modules/typebox/build/type/engine/extract/operation.mjs +3 -4
  133. package/node_modules/typebox/build/type/engine/priority/priority.d.mts +2 -2
  134. package/node_modules/typebox/build/type/engine/priority/priority.mjs +3 -4
  135. package/node_modules/typebox/build/type/script/mapping.d.mts +32 -29
  136. package/node_modules/typebox/build/type/script/mapping.mjs +18 -25
  137. package/node_modules/typebox/build/type/script/parser.d.mts +19 -25
  138. package/node_modules/typebox/build/type/script/parser.mjs +27 -23
  139. package/node_modules/typebox/build/value/errors/errors.d.mts +2 -2
  140. package/node_modules/typebox/build/value/errors/errors.mjs +1 -1
  141. package/node_modules/typebox/package.json +27 -27
  142. package/node_modules/typebox/readme.md +79 -82
  143. package/openclaw.plugin.json +0 -6
  144. package/package.json +5 -5
  145. package/dist/catchup-D32oYNxR.js +0 -294
  146. package/dist/monitor-reply-cache-DKlYGd5x.js +0 -348
  147. package/dist/persisted-echo-cache-BPWGUK-L.js +0 -143
  148. package/node_modules/typebox/build/format/_idna.d.mts +0 -2
  149. package/node_modules/typebox/build/format/_idna.mjs +0 -246
  150. package/node_modules/typebox/build/format/_puny.d.mts +0 -1
  151. package/node_modules/typebox/build/format/_puny.mjs +0 -77
  152. package/node_modules/typebox/build/schema/resolve/ref.d.mts +0 -3
  153. package/node_modules/typebox/build/schema/resolve/ref.mjs +0 -146
  154. package/node_modules/typebox/build/schema/types/contentEncoding.d.mts +0 -9
  155. package/node_modules/typebox/build/schema/types/contentEncoding.mjs +0 -13
  156. package/node_modules/typebox/build/schema/types/contentMediaType.d.mts +0 -9
  157. package/node_modules/typebox/build/schema/types/contentMediaType.mjs +0 -13
@@ -1,13 +1,14 @@
1
- import { C as registerIMessageApprovalReactionTarget, D as buildIMessageApprovalConversationKeyForInbound, E as resolveIMessageReactionContext, I as probeIMessage, S as maybeResolveIMessageApprovalReaction, b as handleIMessageApprovalReaction, r as resolveIMessageInboundConversationId, x as listPendingIMessageApprovalReactionPollTargets, y as extractIMessageApprovalPromptBinding } from "./group-policy-DF9xSdik.js";
2
- import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount } from "./accounts-D00Njxg2.js";
3
- import { c as parseIMessageAllowTarget, i as isAllowedIMessageReplyContextSender, l as parseIMessageTarget, n as formatIMessageChatTarget, s as normalizeIMessageHandle, v as getCachedIMessagePrivateApiStatus, y as imessageRpcSupportsMethod } from "./message-tool-api-CPDfJJoE.js";
4
- import { c as rememberIMessageReplyCache, f as getIMessageRuntime, s as isKnownFromMeIMessageMessageId } from "./monitor-reply-cache-DKlYGd5x.js";
5
- import { o as createIMessageRpcClient, r as sanitizeOutboundText } from "./sanitize-outbound-CoTZLXMw.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-D32oYNxR.js";
8
- import { c as stripLeadingEchoTextCorruptionMarkers, i as hasPersistedIMessageEcho, o as resolveIMessageEchoMediaKey } from "./persisted-echo-cache-BPWGUK-L.js";
9
- import { a as maybeResolveIMessageApprovalPollVote, o as iMessageApprovalControlBindings, t as sendMessageIMessage } from "./send-BUXW76tw.js";
10
- import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-DVe50UhB.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-CvIgJpOo.js";
3
+ import { l as isKnownFromMeIMessageMessageId, o as createIMessageRpcClient, r as sanitizeOutboundText, u as rememberIMessageReplyCache, v as resolveIMessageDirectChatService } from "./sanitize-outbound-Du-r1lNQ.js";
4
+ import { f as probeIMessage, r as resolveIMessageInboundConversationId } from "./group-policy-Dvc-BcWo.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-4TK_cDDb.js";
7
+ import { n as resolveIMessageAttachmentRoots, r as resolveIMessageRemoteAttachmentRoots } from "./media-contract-D-FhGE98.js";
8
+ import { i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction, s as buildIMessageApprovalConversationKeyForInbound } from "./approval-reactions-LL5yn1Rc.js";
9
+ import { c as iMessageApprovalControlBindings, n as hasPersistedIMessageEcho, r as stripLeadingEchoTextCorruptionMarkers, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-mgWPKqSV.js";
10
+ import { t as resolveIMessageReactionContext } from "./reaction-context-BAYI7pz0.js";
11
+ import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-viP7ZcyG.js";
11
12
  import { bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
12
13
  import { chunkMarkdownTextWithMode as chunkTextWithMode, resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
13
14
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
@@ -16,34 +17,34 @@ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
16
17
  import { asSafeIntegerInRange, hasNonEmptyString, isRecord, normalizeOptionalString, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
17
18
  import fs from "node:fs/promises";
18
19
  import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, warn } from "openclaw/plugin-sdk/runtime-env";
19
- import { asDateTimestampMs, asPositiveFiniteNumber, parseDateStringTimestampMs, resolveExpiresAtMsFromDurationMs, 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";
20
22
  import { createHash } from "node:crypto";
21
23
  import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
22
24
  import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
23
25
  import { collectErrorGraphCandidates, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
24
- import { isInboundPathAllowed, kindFromMime } from "openclaw/plugin-sdk/media-runtime";
25
- import { convertMarkdownTables as convertMarkdownTables$1, findCodeRegions, isInsideCode, sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
26
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";
27
28
  import { buildChannelInboundEventContext, buildMentionRegexes, createChannelInboundDebouncer, createChannelPartialDeliveryError, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundFromLabel, formatInboundMediaUnavailableText, formatMediaPlaceholderText, isChannelPartialDeliveryError, logInboundDrop, matchesMentionPatterns, resolveEnvelopeFormatOptions, resolveInboundMentionDecision, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, shouldDebounceTextInbound, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
28
- import { openLocalFileSafely, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
29
- import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
30
29
  import { resolveAgentConfig, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
31
30
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-runtime";
32
31
  import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
33
32
  import { createChannelIngressResolver, defineStableChannelIngressIdentity, fanInChannelIngressLifecycles } from "openclaw/plugin-sdk/channel-ingress-runtime";
34
33
  import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
35
34
  import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
36
- import { readChannelAllowFromStore, resolveConfiguredBindingRoute, resolveRuntimeConversationBindingRoute, upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
35
+ import { ensureConfiguredBindingRouteReady, readChannelAllowFromStore, resolveConfiguredBindingRoute, resolveRuntimeConversationBindingRoute, upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
37
36
  import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
38
37
  import { redactIdentifier } from "openclaw/plugin-sdk/logging-core";
39
38
  import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
40
39
  import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
40
+ import { openLocalFileSafely, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
41
41
  import { getSessionEntry, readSessionUpdatedAt, resolveSendPolicy, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
42
42
  import { openNodeSqliteDatabase } from "openclaw/plugin-sdk/sqlite-runtime";
43
43
  import { waitForTransportReady } from "openclaw/plugin-sdk/transport-ready-runtime";
44
44
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
45
45
  import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
46
46
  import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
47
+ import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
47
48
  import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
48
49
  import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
49
50
  import { hasControlCommand } from "openclaw/plugin-sdk/command-auth-native";
@@ -55,8 +56,6 @@ import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
55
56
  //#region extensions/imessage/src/approval-reaction-poller.ts
56
57
  const RECENT_CHAT_LIMIT = 50;
57
58
  const PER_CHAT_HISTORY_LIMIT = 30;
58
- const OBSERVED_APPROVAL_PROMPT_TARGET_TTL_MS = 300 * 1e3;
59
- const accountIdsWithCompletedNoTargetDiscovery = /* @__PURE__ */ new Set();
60
59
  function normalizeChatId(value) {
61
60
  return asPositiveFiniteNumber(value) ?? null;
62
61
  }
@@ -150,42 +149,13 @@ function bindObservedConversation(params) {
150
149
  ttlMs
151
150
  });
152
151
  }
153
- function bindObservedApprovalPrompt(params) {
154
- if (params.message.is_from_me !== true) return null;
155
- const messageId = params.message.guid?.trim();
156
- if (!messageId) return null;
157
- const binding = extractIMessageApprovalPromptBinding(params.message.text ?? "");
158
- if (!binding) return null;
159
- const conversation = buildConversationKeyFromMessage(params.message);
160
- const expiresAtMs = resolveExpiresAtMsFromDurationMs(OBSERVED_APPROVAL_PROMPT_TARGET_TTL_MS);
161
- if (expiresAtMs === void 0) return null;
162
- const target = {
163
- accountId: params.accountId,
164
- conversation,
165
- messageId,
166
- approvalId: binding.approvalId,
167
- approvalKind: binding.approvalKind,
168
- allowedDecisions: binding.allowedDecisions,
169
- expiresAtMs
170
- };
171
- bindObservedConversation({
172
- target,
173
- message: params.message
174
- });
175
- return target;
176
- }
177
152
  async function pollPendingIMessageApprovalReactions(params) {
178
- const targets = listPendingIMessageApprovalReactionPollTargets({ accountId: params.accountId });
179
- const shouldAttemptNoTargetDiscovery = targets.length === 0 && params.allowRecentChatDiscovery === true && !accountIdsWithCompletedNoTargetDiscovery.has(params.accountId);
180
- if (targets.length === 0 && !shouldAttemptNoTargetDiscovery) return;
153
+ const targets = await listPendingIMessageApprovalReactionPollTargets({ accountId: params.accountId });
154
+ if (targets.length === 0) return;
181
155
  const pendingByMessageId = buildPendingTargetsByMessageId(targets);
182
156
  const explicitChatIds = listTargetChatIds(targets);
183
- const chatIds = params.allowRecentChatDiscovery === true && (targets.length === 0 || hasUnscopedTarget(targets)) ? uniqueChatIds([...explicitChatIds, ...await listRecentChatIds(params.client)]) : explicitChatIds;
184
- if (chatIds.length === 0) {
185
- if (shouldAttemptNoTargetDiscovery) accountIdsWithCompletedNoTargetDiscovery.add(params.accountId);
186
- return;
187
- }
188
- let hadHistoryFetchError = false;
157
+ const chatIds = params.allowRecentChatDiscovery === true && targets.length > 0 && hasUnscopedTarget(targets) ? uniqueChatIds([...explicitChatIds, ...await listRecentChatIds(params.client)]) : explicitChatIds;
158
+ if (chatIds.length === 0) return;
189
159
  for (const chatId of chatIds) {
190
160
  let messages;
191
161
  try {
@@ -194,17 +164,13 @@ async function pollPendingIMessageApprovalReactions(params) {
194
164
  chatId
195
165
  });
196
166
  } catch (err) {
197
- hadHistoryFetchError = true;
198
167
  params.logVerboseMessage?.(`imessage: approval reaction poll skipped chat_id=${chatId}: ${String(err)}`);
199
168
  continue;
200
169
  }
201
170
  for (const message of messages) {
202
171
  const targetGuid = message.guid?.trim();
203
172
  if (!targetGuid) continue;
204
- const target = pendingByMessageId.get(targetGuid) ?? pendingByMessageId.get(normalizeMessageGuid(targetGuid)) ?? bindObservedApprovalPrompt({
205
- accountId: params.accountId,
206
- message
207
- });
173
+ const target = pendingByMessageId.get(targetGuid) ?? pendingByMessageId.get(normalizeMessageGuid(targetGuid));
208
174
  if (!target) continue;
209
175
  bindObservedConversation({
210
176
  target,
@@ -216,22 +182,17 @@ async function pollPendingIMessageApprovalReactions(params) {
216
182
  reaction
217
183
  });
218
184
  if (!reactionPayload) continue;
219
- const handled = await handleIMessageApprovalReaction({
185
+ if ((await handleIMessageApprovalReaction({
220
186
  cfg: params.cfg,
221
187
  accountId: params.accountId,
222
188
  message: reactionPayload,
223
189
  bodyText: reactionPayload.text ?? "",
224
190
  gatewayRuntime: params.gatewayRuntime,
225
191
  logVerboseMessage: params.logVerboseMessage
226
- });
227
- if (handled.stopPolling) {
228
- if (shouldAttemptNoTargetDiscovery && handled.stopPollingReason !== "resolver-error") break;
229
- return;
230
- }
192
+ })).stopPolling) return;
231
193
  }
232
194
  }
233
195
  }
234
- if (shouldAttemptNoTargetDiscovery && !hadHistoryFetchError) accountIdsWithCompletedNoTargetDiscovery.add(params.accountId);
235
196
  }
236
197
  //#endregion
237
198
  //#region extensions/imessage/src/chat.ts
@@ -302,6 +263,269 @@ function attachIMessageMonitorAbortHandler(params) {
302
263
  return () => abort.removeEventListener("abort", onAbort);
303
264
  }
304
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
305
529
  //#region extensions/imessage/src/monitor/strip-imsg-length-prefixed-text.ts
306
530
  const utf8Decoder = new TextDecoder();
307
531
  function readVarint(buf, start) {
@@ -342,9 +566,6 @@ function stripImessageLengthPrefixedUtf8Text(text) {
342
566
  function isOptionalString(value) {
343
567
  return value === void 0 || value === null || typeof value === "string";
344
568
  }
345
- function isOptionalStringOrNumber(value) {
346
- return value === void 0 || value === null || typeof value === "string" || typeof value === "number";
347
- }
348
569
  function isOptionalNumber(value) {
349
570
  return value === void 0 || value === null || typeof value === "number";
350
571
  }
@@ -367,7 +588,7 @@ function parseIMessageNotification(raw) {
367
588
  const maybeMessage = raw.message;
368
589
  if (!isRecord(maybeMessage)) return null;
369
590
  const message = maybeMessage;
370
- 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;
371
592
  return {
372
593
  ...message,
373
594
  text: typeof message.text === "string" ? stripImessageLengthPrefixedUtf8Text(message.text) : message.text,
@@ -587,15 +808,16 @@ function combineIMessagePayloads(payloads) {
587
808
  seenGuids.add(guid);
588
809
  coalescedMessageGuids.push(guid);
589
810
  }
590
- 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;
591
812
  return {
592
813
  ...first,
593
814
  text: combinedText,
594
815
  attachments: allAttachments.length > 0 ? allAttachments : null,
595
816
  created_at: latestCreatedAt,
596
- reply_to_id: entryWithReply?.reply_to_id ?? first.reply_to_id ?? null,
597
- reply_to_text: entryWithReply?.reply_to_text ?? first.reply_to_text ?? null,
598
- 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,
599
821
  coalescedMessageGuids: coalescedMessageGuids.length > 0 ? coalescedMessageGuids : void 0,
600
822
  coalescedCatchupCursor: Number.isFinite(maxRowid) && Number.isFinite(maxDateMs) ? {
601
823
  lastSeenMs: maxDateMs,
@@ -1033,7 +1255,7 @@ function isStaleIMessageBacklog(message, nowMs, thresholdMs = IMESSAGE_STALE_INB
1033
1255
  //#endregion
1034
1256
  //#region extensions/imessage/src/conversation-route.ts
1035
1257
  function resolveIMessageConversationRoute(params) {
1036
- let route = resolveAgentRoute({
1258
+ const route = resolveAgentRoute({
1037
1259
  cfg: params.cfg,
1038
1260
  channel: "imessage",
1039
1261
  accountId: params.accountId,
@@ -1047,28 +1269,30 @@ function resolveIMessageConversationRoute(params) {
1047
1269
  sender: params.sender,
1048
1270
  chatId: params.chatId
1049
1271
  });
1050
- if (!conversationId) return route;
1051
- route = resolveConfiguredBindingRoute({
1272
+ if (!conversationId) return {
1273
+ route,
1274
+ bindingResolution: null
1275
+ };
1276
+ const conversation = {
1277
+ channel: "imessage",
1278
+ accountId: params.accountId,
1279
+ conversationId
1280
+ };
1281
+ const configuredRoute = resolveConfiguredBindingRoute({
1052
1282
  cfg: params.cfg,
1053
1283
  route,
1054
- conversation: {
1055
- channel: "imessage",
1056
- accountId: params.accountId,
1057
- conversationId
1058
- }
1059
- }).route;
1284
+ conversation
1285
+ });
1060
1286
  const runtimeRoute = resolveRuntimeConversationBindingRoute({
1061
- route,
1062
- conversation: {
1063
- channel: "imessage",
1064
- accountId: params.accountId,
1065
- conversationId
1066
- }
1287
+ route: configuredRoute.route,
1288
+ conversation
1067
1289
  });
1068
- route = runtimeRoute.route;
1069
1290
  if (runtimeRoute.bindingRecord && !runtimeRoute.boundSessionKey) logVerbose(`imessage: plugin-bound conversation ${conversationId}`);
1070
1291
  else if (runtimeRoute.boundSessionKey) logVerbose(`imessage: routed via bound conversation ${conversationId} -> ${runtimeRoute.boundSessionKey}`);
1071
- return route;
1292
+ return {
1293
+ route: runtimeRoute.route,
1294
+ bindingResolution: runtimeRoute.bindingRecord ? null : configuredRoute.bindingResolution
1295
+ };
1072
1296
  }
1073
1297
  //#endregion
1074
1298
  //#region extensions/imessage/src/monitor/reflection-guard.ts
@@ -1213,7 +1437,7 @@ function describeReplyContext(message) {
1213
1437
  if (!body) return null;
1214
1438
  return {
1215
1439
  body,
1216
- id: normalizeReplyField(message.reply_to_id),
1440
+ id: normalizeReplyField(message.thread_originator_guid) ?? normalizeReplyField(message.reply_to_guid),
1217
1441
  sender: normalizeReplyField(message.reply_to_sender)
1218
1442
  };
1219
1443
  }
@@ -1391,7 +1615,7 @@ async function resolveIMessageInboundDecision(params) {
1391
1615
  const groupId = isGroup ? groupIdCandidate : void 0;
1392
1616
  const hasControlCommandInMessage = hasControlCommand(messageText, params.cfg);
1393
1617
  const groupAllowFromForAccess = isGroup ? groupAllowFromWithLegacyChatTargets : params.groupAllowFrom;
1394
- const route = resolveIMessageConversationRoute({
1618
+ const { route, bindingResolution } = resolveIMessageConversationRoute({
1395
1619
  cfg: params.cfg,
1396
1620
  accountId: params.accountId,
1397
1621
  isGroup,
@@ -1689,6 +1913,7 @@ async function resolveIMessageInboundDecision(params) {
1689
1913
  sender,
1690
1914
  senderNormalized,
1691
1915
  route,
1916
+ bindingResolution,
1692
1917
  bodyText,
1693
1918
  createdAt,
1694
1919
  replyContext: filteredReplyContext,
@@ -1754,11 +1979,8 @@ async function buildIMessageInboundContext(params) {
1754
1979
  envelope: envelopeOptions
1755
1980
  })
1756
1981
  });
1757
- const imessageTo = decision.isGroup ? chatTarget || `imessage:${decision.sender}` : buildDirectIMessageReplyTarget({
1758
- cfg: params.cfg,
1759
- accountId: decision.route.accountId,
1760
- sender: decision.sender
1761
- });
1982
+ const directService = resolveIMessageDirectChatService(params.accountService, decision.chatGuid) ?? "auto";
1983
+ const imessageTo = decision.isGroup ? chatTarget || `imessage:${decision.sender}` : `${directService}:${decision.sender}`;
1762
1984
  const imessageFrom = decision.isGroup ? `imessage:group:${chatId ?? "unknown"}` : imessageTo;
1763
1985
  const inboundHistory = !decision.isGroup && params.dmHistory?.inboundHistory ? params.dmHistory.inboundHistory : decision.isGroup && decision.historyKey && params.historyLimit > 0 ? createChannelHistoryWindow({ historyMap: params.groupHistories }).buildInboundHistory({
1764
1986
  historyKey: decision.historyKey,
@@ -1787,11 +2009,16 @@ async function buildIMessageInboundContext(params) {
1787
2009
  from: imessageFrom,
1788
2010
  sender: {
1789
2011
  id: decision.sender,
1790
- name: decision.senderNormalized
2012
+ name: decision.senderNormalized,
2013
+ isSelf: params.message.is_from_me === true
1791
2014
  },
1792
2015
  conversation: {
1793
2016
  kind: decision.isGroup ? "group" : "direct",
1794
2017
  id: chatId != null ? String(chatId) : decision.sender,
2018
+ ...decision.isGroup && chatId == null ? {} : { routePeer: {
2019
+ kind: decision.isGroup ? "group" : "direct",
2020
+ id: decision.isGroup ? String(chatId) : decision.senderNormalized
2021
+ } },
1795
2022
  label: fromLabel
1796
2023
  },
1797
2024
  route: {
@@ -1840,13 +2067,6 @@ function buildIMessageEchoScope(params) {
1840
2067
  if (params.chatIdentifier) scopes.push(`${params.accountId}:chat_identifier:${params.chatIdentifier}`);
1841
2068
  return scopes;
1842
2069
  }
1843
- function buildDirectIMessageReplyTarget(params) {
1844
- if (resolveIMessageAccount({
1845
- cfg: params.cfg,
1846
- accountId: params.accountId
1847
- }).config.service === "sms") return `sms:${params.sender}`;
1848
- return `imessage:${params.sender}`;
1849
- }
1850
2070
  function describeIMessageEchoDropLog(params) {
1851
2071
  const preview = truncateUtf16Safe(params.messageText, 50);
1852
2072
  return `imessage: skipping echo message${params.messageId ? ` id=${params.messageId}` : ""}: "${preview}"`;
@@ -2879,7 +3099,7 @@ async function monitorIMessageProvider(opts = {}) {
2879
3099
  const conversationKey = chatId != null ? `group:${chatId}` : `dm:${senderForKey}`;
2880
3100
  const rateLimitKey = `${accountInfo.accountId}:${conversationKey}`;
2881
3101
  if (decision.kind === "drop") {
2882
- if (decision.reason === "echo" || decision.reason === "self-chat echo" || decision.reason === "reflected assistant content") loopRateLimiter.record(rateLimitKey);
3102
+ if (decision.reason === "echo" || decision.reason === "reflected assistant content") loopRateLimiter.record(rateLimitKey);
2883
3103
  const diagnostic = describeIMessageInboundDropDiagnostic({
2884
3104
  accountId: accountInfo.accountId,
2885
3105
  reason: decision.reason,
@@ -2956,6 +3176,16 @@ async function monitorIMessageProvider(opts = {}) {
2956
3176
  });
2957
3177
  return;
2958
3178
  }
3179
+ if (decision.bindingResolution) {
3180
+ const readiness = await ensureConfiguredBindingRouteReady({
3181
+ cfg,
3182
+ bindingResolution: decision.bindingResolution
3183
+ });
3184
+ if (!readiness.ok) {
3185
+ runtime.error?.(`imessage: dropped inbound message; configured ACP binding unavailable for ${decision.bindingResolution.record.conversation.conversationId}: ${readiness.error}`);
3186
+ return;
3187
+ }
3188
+ }
2959
3189
  const storePath = resolveStorePath(cfg.session?.store, { agentId: decision.route.agentId });
2960
3190
  const privateApiStatus = getCachedIMessagePrivateApiStatus(cliPath);
2961
3191
  const supportsTyping = imessageRpcSupportsMethod(privateApiStatus, "typing");
@@ -2975,11 +3205,9 @@ async function monitorIMessageProvider(opts = {}) {
2975
3205
  chatType: decision.isGroup ? "group" : "direct"
2976
3206
  });
2977
3207
  const shouldUseDirectToolTypingOptions = !decision.isGroup && sendPolicy !== "deny" && (configuredTypingMode === void 0 || configuredTypingMode === "instant");
2978
- const earlyDirectTypingTarget = supportsTyping && shouldUseDirectToolTypingOptions ? buildDirectIMessageReplyTarget({
2979
- cfg,
2980
- accountId: decision.route.accountId,
2981
- sender: decision.sender
2982
- }) : void 0;
3208
+ const shouldStartDirectTyping = supportsTyping && shouldUseDirectToolTypingOptions;
3209
+ const earlyDirectTypingService = resolveIMessageDirectChatService(imessageCfg.service, decision.chatGuid) ?? "auto";
3210
+ const earlyDirectTypingTarget = shouldStartDirectTyping ? `${earlyDirectTypingService}:${decision.sender}` : void 0;
2983
3211
  let stopEarlyDirectTyping;
2984
3212
  if (earlyDirectTypingTarget) {
2985
3213
  const earlyDirectTypingStarted = sendIMessageTyping(earlyDirectTypingTarget, true, {
@@ -3056,8 +3284,10 @@ async function monitorIMessageProvider(opts = {}) {
3056
3284
  envelopeOptions: resolveEnvelopeFormatOptions(cfg),
3057
3285
  logVerbose
3058
3286
  }) : void 0;
3287
+ const pluginChannelRuntime = opts.channelRuntime;
3059
3288
  const { ctxPayload, chatTarget, imessageTo } = await buildIMessageInboundContext({
3060
3289
  cfg,
3290
+ accountService: imessageCfg.service,
3061
3291
  decision: contextDecision,
3062
3292
  message,
3063
3293
  previousTimestamp,
@@ -3065,7 +3295,7 @@ async function monitorIMessageProvider(opts = {}) {
3065
3295
  historyLimit,
3066
3296
  groupHistories,
3067
3297
  dmHistory,
3068
- buildContext: opts.channelRuntime?.inbound.buildContext,
3298
+ buildContext: pluginChannelRuntime?.inbound.buildContext,
3069
3299
  media: { facts: mediaAttachments }
3070
3300
  });
3071
3301
  const updateTarget = chatTarget || imessageTo;
@@ -3080,7 +3310,8 @@ async function monitorIMessageProvider(opts = {}) {
3080
3310
  }
3081
3311
  const sendReadReceipts = imessageCfg.sendReadReceipts !== false;
3082
3312
  const typingTarget = ctxPayload.To;
3083
- if (supportsRead && sendReadReceipts && typingTarget) markIMessageChatRead(typingTarget, {
3313
+ const readTarget = !decision.isGroup && decision.chatGuid ? `chat_guid:${decision.chatGuid}` : typingTarget;
3314
+ if (supportsRead && sendReadReceipts && readTarget) markIMessageChatRead(readTarget, {
3084
3315
  cfg,
3085
3316
  accountId: accountInfo.accountId,
3086
3317
  cliPath,
@@ -3217,6 +3448,7 @@ async function monitorIMessageProvider(opts = {}) {
3217
3448
  sessionKey: decision.route.sessionKey
3218
3449
  },
3219
3450
  ctxPayload,
3451
+ dispatchReplyFromConfig: pluginChannelRuntime?.reply?.dispatchReplyFromConfig,
3220
3452
  record: {
3221
3453
  updateLastRoute: !decision.isGroup && updateTarget ? {
3222
3454
  sessionKey: inboundLastRouteSessionKey,