@openclaw/imessage 2026.10.10 → 2026.11.5-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/.setup/{accounts-BH0coL46.mjs → accounts-C1Ypv3Wn.mjs} +6 -7
  2. package/dist/.setup/{actions-CW7XYYF4.mjs → actions-qOuKy8To.mjs} +360 -108
  3. package/dist/.setup/{actions.runtime-B2ZHQ3TJ.mjs → actions.runtime-Ccg9CARL.mjs} +10 -56
  4. package/dist/.setup/{api-6xi-NBrt.mjs → api-BXXuQQCS.mjs} +7 -7
  5. package/dist/.setup/{api-B0ih8Iof.mjs → api-DTbxSOPc.mjs} +8 -8
  6. package/dist/.setup/{approval-handler.runtime-BKmqba3L.mjs → approval-handler.runtime-B6vrFfZx.mjs} +112 -131
  7. package/dist/.setup/{approval-reactions-B9CJS0NT.mjs → approval-reactions-DBBsqbRC.mjs} +152 -72
  8. package/dist/.setup/{channel-6kcKpSRw.mjs → channel-CLaN4mQn.mjs} +142 -119
  9. package/dist/.setup/{channel.runtime-BW4A0m4h.mjs → channel.runtime-XJGx1MIH.mjs} +205 -363
  10. package/dist/.setup/chat-context-fTfJVqAi.mjs +77 -0
  11. package/dist/.setup/chat-lookup-xtPsOBkl.mjs +50 -0
  12. package/dist/.setup/client-D_BaG8sC.mjs +367 -0
  13. package/dist/.setup/{config-schema-DRkdRC21.mjs → config-schema-BjkQV5P5.mjs} +1 -2
  14. package/dist/.setup/{conversation-bindings-vZFn6sN2.mjs → conversation-bindings-8DjxoIYU.mjs} +9 -6
  15. package/dist/.setup/{media-contract-CoixpXRX.mjs → media-contract-BErFBKV9.mjs} +1 -1
  16. package/dist/.setup/{message-tool-api-CxSY_rh7.mjs → message-tool-api-DtZ1wKbT.mjs} +3 -6
  17. package/dist/.setup/{remote-file-CCgMYZ-u.mjs → remote-file-DpK3GQFD.mjs} +8 -22
  18. package/dist/.setup/{sanitize-outbound-C02fJrn6.mjs → sanitize-outbound-Dhjhz0a-.mjs} +23 -27
  19. package/dist/.setup/{send-D98hQFdv.mjs → send-DhEW0gfe.mjs} +150 -226
  20. package/dist/.setup/{setup-core-BWnW0czE.mjs → setup-core-B1_P_t4T.mjs} +2 -2
  21. package/dist/.setup/{setup-surface-aRZhAzzQ.mjs → setup-surface-CD28mXWK.mjs} +11 -25
  22. package/dist/.setup/{targets-CIx4BKqL.mjs → targets-C7CIWJ0k.mjs} +12 -3
  23. package/dist/.source-module-urls/929c518fa2986b0f85f66bceefead22868db0ab452dee914dc6a2bb86b2e60ad.json +1 -1
  24. package/dist/api.js +8 -8
  25. package/dist/channel-config-api.js +1 -1
  26. package/dist/channel-plugin-api.js +1 -1
  27. package/dist/contract-api.js +1 -1
  28. package/dist/media-contract-api.js +1 -1
  29. package/dist/message-tool-api.js +1 -1
  30. package/dist/runtime-api.js +1 -3
  31. package/dist/session-binding-contract-api.js +1 -1
  32. package/dist/setup-entry.js +1 -1
  33. package/dist/src/chat-db.worker.js +1 -1
  34. package/node_modules/typebox/build/format/duration.d.mts +2 -2
  35. package/node_modules/typebox/build/format/duration.mjs +2 -2
  36. package/node_modules/typebox/build/format/idn_email.d.mts +1 -0
  37. package/node_modules/typebox/build/format/idn_email.mjs +1 -0
  38. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -1
  39. package/node_modules/typebox/build/format/idn_hostname.mjs +1 -1
  40. package/node_modules/typebox/build/format/ipv4.d.mts +2 -2
  41. package/node_modules/typebox/build/format/ipv4.mjs +2 -2
  42. package/node_modules/typebox/build/format/ipv6.d.mts +1 -1
  43. package/node_modules/typebox/build/format/ipv6.mjs +1 -1
  44. package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
  45. package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
  46. package/node_modules/typebox/build/schema/build.mjs +1 -0
  47. package/node_modules/typebox/build/schema/engine/_unique.d.mts +4 -2
  48. package/node_modules/typebox/build/schema/engine/_unique.mjs +7 -3
  49. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +3 -3
  50. package/node_modules/typebox/build/schema/engine/additionalProperties.d.mts +0 -3
  51. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +15 -17
  52. package/node_modules/typebox/build/schema/engine/contains.mjs +3 -3
  53. package/node_modules/typebox/build/schema/engine/index.d.mts +1 -0
  54. package/node_modules/typebox/build/schema/engine/index.mjs +1 -0
  55. package/node_modules/typebox/build/schema/engine/items.mjs +7 -4
  56. package/node_modules/typebox/build/schema/engine/maxContains.mjs +2 -2
  57. package/node_modules/typebox/build/schema/engine/minContains.mjs +3 -3
  58. package/node_modules/typebox/build/schema/engine/oneOf.mjs +2 -2
  59. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +2 -2
  60. package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -2
  61. package/node_modules/typebox/build/schema/engine/unevaluatedItems.mjs +2 -2
  62. package/node_modules/typebox/build/schema/engine/unevaluatedProperties.mjs +2 -2
  63. package/node_modules/typebox/build/system/hashing/hash.mjs +63 -34
  64. package/node_modules/typebox/build/system/locale/en_US.mjs +2 -1
  65. package/node_modules/typebox/build/system/memory/assign.d.mts +2 -8
  66. package/node_modules/typebox/build/system/memory/assign.mjs +13 -6
  67. package/node_modules/typebox/build/system/memory/clone.d.mts +4 -5
  68. package/node_modules/typebox/build/system/memory/clone.mjs +8 -96
  69. package/node_modules/typebox/build/system/memory/create.d.mts +1 -5
  70. package/node_modules/typebox/build/system/memory/create.mjs +18 -13
  71. package/node_modules/typebox/build/system/memory/deep.d.mts +2 -0
  72. package/node_modules/typebox/build/system/memory/deep.mjs +108 -0
  73. package/node_modules/typebox/build/system/memory/discard.d.mts +1 -1
  74. package/node_modules/typebox/build/system/memory/discard.mjs +9 -8
  75. package/node_modules/typebox/build/system/memory/finalize.d.mts +4 -0
  76. package/node_modules/typebox/build/system/memory/finalize.mjs +30 -0
  77. package/node_modules/typebox/build/system/memory/keys.d.mts +2 -0
  78. package/node_modules/typebox/build/system/memory/keys.mjs +8 -0
  79. package/node_modules/typebox/build/system/memory/update.d.mts +1 -4
  80. package/node_modules/typebox/build/system/memory/update.mjs +43 -16
  81. package/node_modules/typebox/build/type/engine/call/index.d.mts +1 -0
  82. package/node_modules/typebox/build/type/engine/call/index.mjs +1 -0
  83. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +2 -24
  84. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +2 -29
  85. package/node_modules/typebox/build/type/engine/evaluate/index.d.mts +1 -0
  86. package/node_modules/typebox/build/type/engine/evaluate/index.mjs +1 -0
  87. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +5 -1
  88. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +6 -1
  89. package/node_modules/typebox/build/type/engine/evaluate/property.d.mts +30 -0
  90. package/node_modules/typebox/build/type/engine/evaluate/property.mjs +29 -0
  91. package/node_modules/typebox/build/type/engine/immutable/instantiate_add.d.mts +1 -3
  92. package/node_modules/typebox/build/type/engine/immutable/instantiate_add.mjs +1 -4
  93. package/node_modules/typebox/build/type/engine/object/from_intersect.d.mts +2 -2
  94. package/node_modules/typebox/build/type/engine/object/from_intersect.mjs +2 -2
  95. package/node_modules/typebox/build/type/engine/object/from_union.d.mts +2 -2
  96. package/node_modules/typebox/build/type/engine/object/from_union.mjs +2 -2
  97. package/node_modules/typebox/build/type/engine/optional/instantiate_add.d.mts +1 -3
  98. package/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs +1 -4
  99. package/node_modules/typebox/build/type/engine/readonly/instantiate_add.d.mts +1 -3
  100. package/node_modules/typebox/build/type/engine/readonly/instantiate_add.mjs +1 -4
  101. package/node_modules/typebox/build/type/engine/this/index.d.mts +1 -0
  102. package/node_modules/typebox/build/type/engine/this/index.mjs +1 -0
  103. package/node_modules/typebox/build/value/clone/clone.d.mts +4 -3
  104. package/node_modules/typebox/build/value/clone/clone.mjs +5 -7
  105. package/node_modules/typebox/build/value/repair/from_cyclic.d.mts +2 -0
  106. package/node_modules/typebox/build/value/repair/from_cyclic.mjs +6 -0
  107. package/node_modules/typebox/build/value/shared/union_priority_sort.mjs +25 -14
  108. package/node_modules/typebox/package.json +1 -1
  109. package/node_modules/typebox/readme.md +1 -1
  110. package/node_modules/zod/package.json +1 -1
  111. package/node_modules/zod/src/v4/classic/tests/array.test.ts +27 -0
  112. package/node_modules/zod/src/v4/classic/tests/map.test.ts +7 -0
  113. package/node_modules/zod/src/v4/classic/tests/set.test.ts +6 -0
  114. package/node_modules/zod/src/v4/core/compile.ts +9 -0
  115. package/node_modules/zod/src/v4/core/schemas.ts +35 -1
  116. package/node_modules/zod/src/v4/core/util.ts +13 -0
  117. package/node_modules/zod/src/v4/core/versions.ts +1 -1
  118. package/node_modules/zod/v4/core/compile.cjs +8 -0
  119. package/node_modules/zod/v4/core/compile.js +8 -0
  120. package/node_modules/zod/v4/core/schemas.cjs +30 -0
  121. package/node_modules/zod/v4/core/schemas.d.cts +3 -1
  122. package/node_modules/zod/v4/core/schemas.d.ts +3 -1
  123. package/node_modules/zod/v4/core/schemas.js +30 -0
  124. package/node_modules/zod/v4/core/util.cjs +15 -0
  125. package/node_modules/zod/v4/core/util.d.cts +2 -0
  126. package/node_modules/zod/v4/core/util.d.ts +2 -0
  127. package/node_modules/zod/v4/core/util.js +14 -0
  128. package/node_modules/zod/v4/core/versions.cjs +1 -1
  129. package/node_modules/zod/v4/core/versions.js +1 -1
  130. package/openclaw.plugin.json +0 -30
  131. package/package.json +6 -6
  132. package/dist/.setup/client-C3e7ZLqA.mjs +0 -739
  133. package/dist/.setup/question-reactions-DTj-VH3N.mjs +0 -67
  134. package/dist/.setup/reaction-context-Bw00FkmU.mjs +0 -105
  135. package/dist/.setup/runtime-BRQCPuAD.mjs +0 -7
  136. package/node_modules/typebox/build/system/memory/freeze.d.mts +0 -4
  137. package/node_modules/typebox/build/system/memory/freeze.mjs +0 -6
@@ -1,22 +1,20 @@
1
- import { c as parseIMessageTarget, o as normalizeIMessageHandle, r as isAllowedIMessageReplyContextSender, s as parseIMessageAllowTarget, t as formatIMessageChatTarget } from "./targets-CIx4BKqL.mjs";
2
- import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount, r as listEnabledIMessageAccounts, s as resolveIMessageDuplicateSourceOwner } from "./accounts-BH0coL46.mjs";
3
- import { r as imessageRpcSupportsMethod } from "./message-tool-api-CxSY_rh7.mjs";
4
- import { S as resolveIMessageDirectChatService, h as resolveIMessageEchoMediaKey, m as resolveIMessageCatchupCursorKey, n as createIMessageRpcClient, o as isKnownFromMeIMessageTarget, p as capFailureRetriesMap, s as rememberIMessageReplyCache, u as IMESSAGE_CATCHUP_CURSOR_NAMESPACE } from "./client-C3e7ZLqA.mjs";
5
- import { a as normalizeIMessageMessageId, i as normalizeIMessageGuid, n as probeIMessage, r as probeIMessagePrivateApi } from "./actions-CW7XYYF4.mjs";
6
- import { t as getIMessageRuntime } from "./runtime-BRQCPuAD.mjs";
7
- import { a as resolveIMessageGroupSystemPrompt, s as resolveIMessageInboundConversationId } from "./channel-6kcKpSRw.mjs";
8
- import { r as sanitizeOutboundText } from "./sanitize-outbound-C02fJrn6.mjs";
9
- import { n as resolveIMessageAttachmentRoots, r as resolveIMessageRemoteAttachmentRoots } from "./media-contract-CoixpXRX.mjs";
1
+ import { c as parseIMessageAllowTarget, i as isAllowedIMessageReplyContextSender, l as parseIMessageTarget, n as iMessageTargetRpcParams, s as normalizeIMessageHandle, t as formatIMessageChatTarget } from "./targets-C7CIWJ0k.mjs";
2
+ import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount, r as listEnabledIMessageAccounts, s as resolveIMessageDuplicateSourceOwner } from "./accounts-C1Ypv3Wn.mjs";
3
+ import { r as imessageRpcSupportsMethod } from "./message-tool-api-DtZ1wKbT.mjs";
4
+ import { a as resolveIMessageDirectChatService } from "./chat-context-fTfJVqAi.mjs";
5
+ import { n as createIMessageRpcClient, r as normalizeIMessageFullDiskAccessError } from "./client-D_BaG8sC.mjs";
6
+ import { a as rememberIMessageReplyCache, b as normalizeIMessageMessageId, c as IMESSAGE_CATCHUP_CURSOR_NAMESPACE, d as capFailureRetriesMap, f as resolveIMessageCatchupCursorKey, h as getIMessageRuntime, i as isKnownFromMeIMessageTarget, n as probeIMessage, p as resolveIMessageEchoMediaKey, r as probeIMessagePrivateApi, y as normalizeIMessageGuid } from "./actions-qOuKy8To.mjs";
7
+ import { a as resolveIMessageGroupSystemPrompt, s as resolveIMessageInboundConversationId } from "./channel-CLaN4mQn.mjs";
8
+ import { r as sanitizeOutboundText } from "./sanitize-outbound-Dhjhz0a-.mjs";
9
+ import { n as resolveIMessageAttachmentRoots, r as resolveIMessageRemoteAttachmentRoots } from "./media-contract-BErFBKV9.mjs";
10
10
  import { t as IMESSAGE_LEGACY_OUTBOUND_SEND_DEP_KEYS } from "./outbound-send-deps-_da8jqeo.mjs";
11
- import { i as resolveIMessageStartupRowidWatermark } from "./remote-file-CCgMYZ-u.mjs";
12
- import { i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction } from "./approval-reactions-B9CJS0NT.mjs";
13
- import { n as hasPersistedIMessageEcho, r as normalizeIMessageEchoText, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-D98hQFdv.mjs";
14
- import { t as resolveIMessageReactionContext } from "./reaction-context-Bw00FkmU.mjs";
15
- import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-DTj-VH3N.mjs";
16
- import { imessageSetupWizard } from "./setup-surface-aRZhAzzQ.mjs";
11
+ import { i as resolveIMessageStartupRowidWatermark } from "./remote-file-DpK3GQFD.mjs";
12
+ import { a as resolveIMessageReactionContext, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as registerIMessageApprovalReactionTarget } from "./approval-reactions-DBBsqbRC.mjs";
13
+ import { n as hasPersistedIMessageEcho, r as normalizeIMessageEchoText, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-DhEW0gfe.mjs";
17
14
  import { asSafeIntegerInRange, hasNonEmptyString, isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
18
15
  import { normalizeChannelDmPolicy } from "openclaw/plugin-sdk/channel-config-helpers";
19
16
  import { bindIngressLifecycleToReplyOptions, createAccountStatusSink, createChannelDeliveryAccumulator, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, resolveChannelStreamingBlockEnabled, resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-outbound";
17
+ import { resolveChannelGroupPolicy, resolveChannelGroups, resolveChannelGroupsConfigPath, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
20
18
  import { resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
21
19
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
22
20
  import path from "node:path";
@@ -24,17 +22,16 @@ import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/account-helpers
24
22
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
25
23
  import fs from "node:fs/promises";
26
24
  import { isInboundPathAllowed, kindFromMime } from "openclaw/plugin-sdk/media-runtime";
27
- import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, sleepWithAbort, waitForAbortSignal, warn } from "openclaw/plugin-sdk/runtime-env";
28
- import { asDateTimestampMs, asPositiveFiniteNumber, parseDateStringTimestampMs, resolveIntegerOption, resolvePromptHistoryLimit, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
25
+ import { createNonExitingRuntime, danger, logVerbose, sleepWithAbort, waitForAbortSignal, warn } from "openclaw/plugin-sdk/runtime-env";
29
26
  import { createHash } from "node:crypto";
30
27
  import { captureRuntimeConfigAsyncReader, createRuntimeConfigReader } from "openclaw/plugin-sdk/runtime-config-snapshot";
31
28
  import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
32
29
  import { collectErrorGraphCandidates, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
33
- import { resolveChannelGroupPolicy, resolveChannelGroups, resolveChannelGroupsConfigPath, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
34
- import { convertMarkdownTables, findCodeRegions, isInsideCode, sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
30
+ import { convertMarkdownTables, findCodeRegions, isInsideCode } from "openclaw/plugin-sdk/text-chunking";
35
31
  import { buildChannelInboundEventContext, buildMentionRegexes, createChannelInboundDebouncer, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundFromLabel, formatInboundMediaUnavailableText, formatMediaPlaceholderText, logInboundDrop, matchesMentionPatterns, resolveEnvelopeFormatOptions, resolveInboundDebounceMs, resolveInboundMentionDecision, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, shouldDebounceTextInbound, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
36
32
  import { FsSafeError, openLocalFileSafely, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
37
33
  import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
34
+ import { asDateTimestampMs, asPositiveFiniteNumber, parseDateStringTimestampMs, resolveIntegerOption, resolvePromptHistoryLimit, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
38
35
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-runtime";
39
36
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
40
37
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
@@ -44,15 +41,16 @@ import { defineStableChannelIngressIdentity, fanInChannelIngressLifecycles } fro
44
41
  import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
45
42
  import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
46
43
  import { ensureConfiguredBindingRouteReady, readChannelAllowFromStore, upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
44
+ import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
47
45
  import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
48
46
  import { redactIdentifier } from "openclaw/plugin-sdk/logging-core";
49
47
  import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
50
48
  import { getSessionEntryAsync, readSessionUpdatedAtAsync, resolveSendPolicy, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
51
49
  import { waitForTransportReady } from "openclaw/plugin-sdk/transport-ready-runtime";
52
50
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
51
+ import { isStringArray } from "openclaw/plugin-sdk/collection-input-runtime";
53
52
  import { chunkMarkdownTextWithMode, resolveChunkMode } from "openclaw/plugin-sdk/reply-chunking";
54
53
  import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
55
- import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
56
54
  import { hasControlCommand } from "openclaw/plugin-sdk/command-auth-native";
57
55
  import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
58
56
  import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
@@ -62,112 +60,43 @@ import { readFileHandleBounded } from "openclaw/plugin-sdk/file-access-runtime";
62
60
  import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
63
61
  import { loadWebMedia } from "openclaw/plugin-sdk/web-media";
64
62
  import { enqueueRoutedSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
63
+ import { sha256Hex } from "openclaw/plugin-sdk/node-crypto-runtime";
65
64
  // extensions/imessage/src/approval-reaction-poller.ts
66
65
  const RECENT_CHAT_LIMIT = 50;
67
66
  const PER_CHAT_HISTORY_LIMIT$1 = 30;
68
67
  function normalizeChatId(value) {
69
68
  return asPositiveFiniteNumber(value) ?? null;
70
69
  }
71
- function listTargetChatIds(targets) {
72
- const chatIds = /* @__PURE__ */ new Set();
73
- for (const target of targets) {
74
- const chatId = normalizeChatId(target.conversation.chatId);
75
- if (chatId !== null) chatIds.add(chatId);
76
- }
77
- return [...chatIds];
78
- }
79
- function hasUnscopedTarget(targets) {
80
- return targets.some((target) => normalizeChatId(target.conversation.chatId) === null);
81
- }
82
70
  function enumerateMessageGuidCandidates(value) {
83
71
  const trimmed = value.trim();
84
72
  if (!trimmed) return [];
85
73
  return [trimmed, normalizeIMessageGuid(trimmed)].filter((candidate, index, candidates) => candidate.length > 0 && candidates.indexOf(candidate) === index);
86
74
  }
87
- function buildPendingTargetsByMessageId(targets) {
88
- const pendingByMessageId = /* @__PURE__ */ new Map();
89
- for (const target of targets) for (const candidate of enumerateMessageGuidCandidates(target.messageId)) pendingByMessageId.set(candidate, target);
90
- return pendingByMessageId;
91
- }
92
75
  async function listRecentChatIds(client) {
93
76
  return ((await client.request("chats.list", { limit: RECENT_CHAT_LIMIT }, { timeoutMs: 1e4 })).chats ?? []).map((chat) => normalizeChatId(chat.id)).filter((chatId) => chatId !== null);
94
77
  }
95
- async function fetchRecentHistory(params) {
96
- return ((await params.client.request("messages.history", {
97
- chat_id: params.chatId,
98
- limit: PER_CHAT_HISTORY_LIMIT$1
99
- }, { timeoutMs: 1e4 })).messages ?? []).filter((message) => Boolean(message && typeof message === "object"));
100
- }
101
- function buildReactionPayload(params) {
102
- const emoji = params.reaction.emoji?.trim();
103
- const sender = params.reaction.sender?.trim();
104
- const targetGuid = params.targetMessage.guid?.trim();
105
- if (!emoji || !sender || !targetGuid) return null;
106
- const reactionId = normalizeChatId(params.reaction.id);
107
- return {
108
- ...reactionId !== null ? { id: reactionId } : {},
109
- guid: `reaction:${targetGuid}:${sender}:${emoji}:${params.reaction.created_at ?? ""}`,
110
- chat_id: params.targetMessage.chat_id,
111
- chat_guid: params.targetMessage.chat_guid,
112
- chat_identifier: params.targetMessage.chat_identifier,
113
- chat_name: params.targetMessage.chat_name,
114
- participants: params.targetMessage.participants,
115
- is_group: params.targetMessage.is_group,
116
- sender,
117
- destination_caller_id: params.targetMessage.destination_caller_id,
118
- is_from_me: params.reaction.is_from_me,
119
- text: `${params.reaction.type ?? "reaction"} "${params.targetMessage.text ?? ""}"`,
120
- created_at: params.reaction.created_at,
121
- is_reaction: true,
122
- is_tapback: true,
123
- associated_message_guid: targetGuid,
124
- associated_message_type: 2e3,
125
- reaction_type: params.reaction.type ?? void 0,
126
- reaction_emoji: emoji,
127
- is_reaction_add: true,
128
- reacted_to_guid: targetGuid
129
- };
130
- }
131
- function buildConversationKeyFromMessage(message) {
132
- return {
133
- ...message.chat_guid?.trim() ? { chatGuid: message.chat_guid.trim() } : {},
134
- ...message.chat_identifier?.trim() ? { chatIdentifier: message.chat_identifier.trim() } : {},
135
- ...normalizeChatId(message.chat_id) !== null ? { chatId: message.chat_id } : {}
136
- };
137
- }
138
- async function bindObservedConversation(params) {
139
- const nowMs = asDateTimestampMs(Date.now());
140
- const expiresAtMs = asDateTimestampMs(params.target.expiresAtMs);
141
- if (nowMs === void 0 || expiresAtMs === void 0 || expiresAtMs <= nowMs) return;
142
- const ttlMs = expiresAtMs - nowMs;
143
- const conversation = buildConversationKeyFromMessage(params.message);
144
- const messageIds = /* @__PURE__ */ new Set([...enumerateMessageGuidCandidates(params.target.messageId), ...enumerateMessageGuidCandidates(params.message.guid ?? "")]);
145
- await Promise.all([...messageIds].map((messageId) => registerIMessageApprovalReactionTarget({
146
- accountId: params.target.accountId,
147
- conversation,
148
- messageId,
149
- approvalId: params.target.approvalId,
150
- approvalKind: params.target.approvalKind,
151
- allowedDecisions: params.target.allowedDecisions,
152
- ttlMs
153
- })));
154
- }
155
78
  async function pollPendingIMessageApprovalReactions(params) {
156
79
  const targets = await listPendingIMessageApprovalReactionPollTargets({ accountId: params.accountId });
157
80
  if (params.signal?.aborted || targets.length === 0) return;
158
- const pendingByMessageId = buildPendingTargetsByMessageId(targets);
159
- const explicitChatIds = listTargetChatIds(targets);
160
- const chatIds = params.allowRecentChatDiscovery === true && hasUnscopedTarget(targets) ? [.../* @__PURE__ */ new Set([...explicitChatIds, ...await listRecentChatIds(params.client)])] : explicitChatIds;
81
+ const pendingByMessageId = /* @__PURE__ */ new Map();
82
+ for (const target of targets) for (const candidate of enumerateMessageGuidCandidates(target.messageId)) pendingByMessageId.set(candidate, target);
83
+ const targetChatIds = /* @__PURE__ */ new Set();
84
+ for (const target of targets) {
85
+ const chatId = normalizeChatId(target.conversation.chatId);
86
+ if (chatId !== null) targetChatIds.add(chatId);
87
+ }
88
+ const explicitChatIds = [...targetChatIds];
89
+ const chatIds = params.allowRecentChatDiscovery === true && targets.some((target) => normalizeChatId(target.conversation.chatId) === null) ? [.../* @__PURE__ */ new Set([...explicitChatIds, ...await listRecentChatIds(params.client)])] : explicitChatIds;
161
90
  for (const chatId of chatIds) {
162
91
  if (params.signal?.aborted) return;
163
92
  let messages;
164
93
  try {
165
- messages = await fetchRecentHistory({
166
- client: params.client,
167
- chatId
168
- });
94
+ messages = ((await params.client.request("messages.history", {
95
+ chat_id: chatId,
96
+ limit: PER_CHAT_HISTORY_LIMIT$1
97
+ }, { timeoutMs: 1e4 })).messages ?? []).filter((message) => Boolean(message && typeof message === "object"));
169
98
  } catch (err) {
170
- params.logVerboseMessage?.(`imessage: approval reaction poll skipped chat_id=${chatId}: ${String(err)}`);
99
+ logVerbose(`imessage: approval reaction poll skipped chat_id=${chatId}: ${String(err)}`);
171
100
  continue;
172
101
  }
173
102
  for (const message of messages) {
@@ -176,24 +105,66 @@ async function pollPendingIMessageApprovalReactions(params) {
176
105
  if (!targetGuid) continue;
177
106
  const target = pendingByMessageId.get(targetGuid) ?? pendingByMessageId.get(normalizeIMessageGuid(targetGuid));
178
107
  if (!target) continue;
179
- await bindObservedConversation({
180
- target,
181
- message
182
- });
108
+ await (async () => {
109
+ const nowMs = asDateTimestampMs(Date.now());
110
+ const expiresAtMs = asDateTimestampMs(target.expiresAtMs);
111
+ if (nowMs === void 0 || expiresAtMs === void 0 || expiresAtMs <= nowMs) return;
112
+ const ttlMs = expiresAtMs - nowMs;
113
+ const conversation = {
114
+ ...message.chat_guid?.trim() ? { chatGuid: message.chat_guid.trim() } : {},
115
+ ...message.chat_identifier?.trim() ? { chatIdentifier: message.chat_identifier.trim() } : {},
116
+ ...normalizeChatId(message.chat_id) !== null ? { chatId: message.chat_id } : {}
117
+ };
118
+ const messageIds = /* @__PURE__ */ new Set([...enumerateMessageGuidCandidates(target.messageId), ...enumerateMessageGuidCandidates(message.guid ?? "")]);
119
+ await Promise.all([...messageIds].map((messageId) => registerIMessageApprovalReactionTarget({
120
+ accountId: target.accountId,
121
+ conversation,
122
+ messageId,
123
+ approvalId: target.approvalId,
124
+ approvalKind: target.approvalKind,
125
+ allowedDecisions: target.allowedDecisions,
126
+ ttlMs
127
+ })));
128
+ })();
183
129
  for (const reaction of message.reactions ?? []) {
184
130
  if (params.signal?.aborted) return;
185
- const reactionPayload = buildReactionPayload({
186
- targetMessage: message,
187
- reaction
188
- });
131
+ const reactionPayload = (() => {
132
+ const emoji = reaction.emoji?.trim();
133
+ const sender = reaction.sender?.trim();
134
+ const reactionTargetGuid = message.guid?.trim();
135
+ if (!emoji || !sender || !reactionTargetGuid) return null;
136
+ const reactionId = normalizeChatId(reaction.id);
137
+ return {
138
+ ...reactionId !== null ? { id: reactionId } : {},
139
+ guid: `reaction:${reactionTargetGuid}:${sender}:${emoji}:${reaction.created_at ?? ""}`,
140
+ chat_id: message.chat_id,
141
+ chat_guid: message.chat_guid,
142
+ chat_identifier: message.chat_identifier,
143
+ chat_name: message.chat_name,
144
+ participants: message.participants,
145
+ is_group: message.is_group,
146
+ sender,
147
+ destination_caller_id: message.destination_caller_id,
148
+ is_from_me: reaction.is_from_me,
149
+ text: `${reaction.type ?? "reaction"} "${message.text ?? ""}"`,
150
+ created_at: reaction.created_at,
151
+ is_reaction: true,
152
+ is_tapback: true,
153
+ associated_message_guid: reactionTargetGuid,
154
+ associated_message_type: 2e3,
155
+ reaction_type: reaction.type ?? void 0,
156
+ reaction_emoji: emoji,
157
+ is_reaction_add: true,
158
+ reacted_to_guid: reactionTargetGuid
159
+ };
160
+ })();
189
161
  if (!reactionPayload) continue;
190
162
  if ((await handleIMessageApprovalReaction({
191
163
  cfg: params.cfg,
192
164
  accountId: params.accountId,
193
165
  message: reactionPayload,
194
166
  bodyText: reactionPayload.text ?? "",
195
- gatewayRuntime: params.gatewayRuntime,
196
- logVerboseMessage: params.logVerboseMessage
167
+ gatewayRuntime: params.gatewayRuntime
197
168
  })).stopPolling) return;
198
169
  }
199
170
  }
@@ -202,19 +173,15 @@ async function pollPendingIMessageApprovalReactions(params) {
202
173
  // extensions/imessage/src/chat.ts
203
174
  async function runChatAction(method, to, opts, isTyping) {
204
175
  const cfg = requireRuntimeConfig(opts.cfg, "iMessage chat action");
205
- const account = opts.account ?? resolveIMessageAccount({
176
+ const account = resolveIMessageAccount({
206
177
  cfg,
207
178
  accountId: opts.accountId
208
179
  });
209
- const target = parseIMessageTarget(opts.chatId ? formatIMessageChatTarget(opts.chatId) : to);
210
- const params = {};
211
- if (target.kind === "chat_id") params.chat_id = target.chatId;
212
- else if (target.kind === "chat_guid") params.chat_guid = target.chatGuid;
213
- else if (target.kind === "chat_identifier") params.chat_identifier = target.chatIdentifier;
214
- else params.to = target.to;
180
+ const target = parseIMessageTarget(to);
181
+ const params = iMessageTargetRpcParams(target);
215
182
  if (method === "typing") {
216
183
  params.typing = isTyping;
217
- const service = opts.service ?? (target.kind === "handle" ? target.service : void 0) ?? account.config.service;
184
+ const service = (target.kind === "handle" ? target.service : void 0) ?? account.config.service;
218
185
  if (service) params.service = service;
219
186
  }
220
187
  const cliPath = opts.cliPath?.trim() || account.config.cliPath?.trim() || "imsg";
@@ -230,7 +197,7 @@ async function runChatAction(method, to, opts, isTyping) {
230
197
  });
231
198
  const shouldClose = !opts.client;
232
199
  try {
233
- await client.request(method, params, { timeoutMs: opts.timeoutMs });
200
+ await client.request(method, params);
234
201
  } finally {
235
202
  if (shouldClose) await client.stop();
236
203
  }
@@ -358,13 +325,12 @@ async function advanceIMessageCatchupCursor(accountId, next, config) {
358
325
  return await updateIMessageCatchupCursor(accountId, (existing) => decideLiveCatchupCursorAdvance(existing, cursor, config.maxFailureRetries));
359
326
  }
360
327
  async function performIMessageCatchup(params) {
361
- const now = params.now ?? Date.now();
328
+ const now = Date.now();
362
329
  const cfg = params.config;
363
330
  const cursor = await loadIMessageCatchupCursor(params.accountId);
364
331
  const lookbackMs = cursor === null ? cfg.firstRunLookbackMinutes * 6e4 : cfg.maxAgeMinutes * 6e4;
365
332
  const ageBoundMs = now - cfg.maxAgeMinutes * 6e4;
366
333
  const windowStartMs = Math.max(cursor?.lastSeenMs ?? now - lookbackMs, ageBoundMs);
367
- const windowEndMs = now;
368
334
  const sinceRowid = cursor?.lastSeenRowid ?? 0;
369
335
  const summary = {
370
336
  querySucceeded: false,
@@ -372,20 +338,9 @@ async function performIMessageCatchup(params) {
372
338
  fetchedCount: 0,
373
339
  replayed: 0,
374
340
  skippedFromMe: 0,
375
- skippedPreCursor: 0,
376
341
  skippedGivenUp: 0,
377
342
  failed: 0,
378
- givenUp: 0,
379
- cursorBefore: cursor ? {
380
- lastSeenMs: cursor.lastSeenMs,
381
- lastSeenRowid: cursor.lastSeenRowid
382
- } : null,
383
- cursorAfter: {
384
- lastSeenMs: cursor?.lastSeenMs ?? windowStartMs,
385
- lastSeenRowid: cursor?.lastSeenRowid ?? 0
386
- },
387
- windowStartMs,
388
- windowEndMs
343
+ givenUp: 0
389
344
  };
390
345
  let fetchResult;
391
346
  try {
@@ -413,16 +368,10 @@ async function performIMessageCatchup(params) {
413
368
  let highWatermarkRowid = cursorBeforeRowid;
414
369
  let earliestHeldFailureRow = null;
415
370
  for (const row of rows) {
416
- if (row.rowid <= sinceRowid) {
417
- summary.skippedPreCursor += 1;
418
- continue;
419
- }
371
+ if (row.rowid <= sinceRowid) continue;
420
372
  highWatermarkMs = Math.max(highWatermarkMs, row.date);
421
373
  highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
422
- if (row.date < ageBoundMs) {
423
- summary.skippedPreCursor += 1;
424
- continue;
425
- }
374
+ if (row.date < ageBoundMs) continue;
426
375
  if (row.isFromMe) {
427
376
  try {
428
377
  await params.observeSkippedFromMe?.(row);
@@ -473,10 +422,6 @@ async function performIMessageCatchup(params) {
473
422
  lastSeenRowid = highWatermarkRowid;
474
423
  }
475
424
  const capped = capFailureRetriesMap(failureRetries);
476
- summary.cursorAfter = {
477
- lastSeenMs,
478
- lastSeenRowid
479
- };
480
425
  const next = buildIMessageCatchupCursor({
481
426
  lastSeenMs,
482
427
  lastSeenRowid,
@@ -532,7 +477,7 @@ function isOptionalBoolean(value) {
532
477
  return value === void 0 || value === null || typeof value === "boolean";
533
478
  }
534
479
  function isOptionalStringArray(value) {
535
- return value === void 0 || value === null || Array.isArray(value) && value.every((entry) => typeof entry === "string");
480
+ return value === void 0 || value === null || isStringArray(value);
536
481
  }
537
482
  function isOptionalAttachments(value) {
538
483
  if (value === void 0 || value === null) return true;
@@ -675,8 +620,7 @@ async function runIMessageCatchup(params) {
675
620
  await params.observeSkippedFromMePayload?.(entry.message);
676
621
  },
677
622
  log,
678
- warn: warnLog,
679
- ...params.now ? { now: params.now() } : {}
623
+ warn: warnLog
680
624
  });
681
625
  }
682
626
  // extensions/imessage/src/monitor/coalesce.ts
@@ -707,7 +651,7 @@ function combineIMessagePayloads(payloads) {
707
651
  const keepContent = payloadIndex < 9 || payloadIndex === payloads.length - 1;
708
652
  payloadIndex += 1;
709
653
  const createdAt = payload.created_at;
710
- if (typeof createdAt === "string" && createdAt.length > 0 && (latestCreatedAt === void 0 || createdAt > latestCreatedAt)) latestCreatedAt = createdAt;
654
+ if (createdAt && (latestCreatedAt === void 0 || createdAt > latestCreatedAt)) latestCreatedAt = createdAt;
711
655
  if (!reply && (payload.thread_originator_guid != null || payload.reply_to_guid != null)) reply = payload;
712
656
  if (!keepContent) continue;
713
657
  const text = textLength <= MAX_COALESCED_TEXT_CHARS ? (payload.text ?? "").trim() : "";
@@ -770,7 +714,7 @@ function extractAuthoritativeRecoveryProjection(entry) {
770
714
  if (hasNonEmptyString(entry.chat_guid)) projection.chat_guid = entry.chat_guid;
771
715
  if (hasNonEmptyString(entry.chat_identifier)) projection.chat_identifier = entry.chat_identifier;
772
716
  if (typeof entry.chat_name === "string") projection.chat_name = entry.chat_name;
773
- if (Array.isArray(entry.participants) && entry.participants.every((participant) => typeof participant === "string")) projection.participants = entry.participants;
717
+ if (isStringArray(entry.participants)) projection.participants = entry.participants;
774
718
  return hasUsableConversationAnchor(projection) ? projection : null;
775
719
  }
776
720
  function projectionConflictKey(projection) {
@@ -930,14 +874,6 @@ function historyRowSortValue(message) {
930
874
  const createdAtMs = typeof message.created_at === "string" ? Date.parse(message.created_at) : NaN;
931
875
  return Number.isFinite(createdAtMs) ? createdAtMs : 0;
932
876
  }
933
- function isBeforeCurrentMessage(params) {
934
- const { message, currentMessage } = params;
935
- if (typeof message.id === "number" && typeof currentMessage.id === "number" && Number.isFinite(message.id) && Number.isFinite(currentMessage.id)) return message.id < currentMessage.id;
936
- const guid = normalizeOptionalString(message.guid);
937
- const currentGuid = normalizeOptionalString(currentMessage.guid);
938
- if (guid && currentGuid) return guid !== currentGuid;
939
- return true;
940
- }
941
877
  function historyEntryFromMessage(message, fallbackSender) {
942
878
  const body = normalizeOptionalString(message.text);
943
879
  if (!body) return null;
@@ -960,13 +896,17 @@ async function resolveIMessageDmHistoryContext(params) {
960
896
  attachments: false
961
897
  }, { timeoutMs: DM_HISTORY_RPC_TIMEOUT_MS });
962
898
  } catch (err) {
963
- params.logVerbose?.(`imessage: DM history fetch failed for chat_id=${chatId}: ${String(err)}`);
899
+ logVerbose(`imessage: DM history fetch failed for chat_id=${chatId}: ${String(err)}`);
964
900
  return {};
965
901
  }
966
- const history = (Array.isArray(result?.messages) ? result.messages : []).map((row) => parseIMessageNotification({ message: row })).filter((message) => Boolean(message)).filter((message) => message.is_group !== true).filter((message) => isBeforeCurrentMessage({
967
- message,
968
- currentMessage: params.message
969
- })).toSorted((a, b) => historyRowSortValue(a) - historyRowSortValue(b)).map((message) => historyEntryFromMessage(message, params.senderNormalized)).filter((entry) => Boolean(entry)).slice(-maxMessages);
902
+ const history = (Array.isArray(result?.messages) ? result.messages : []).map((row) => parseIMessageNotification({ message: row })).filter((message) => Boolean(message)).filter((message) => message.is_group !== true).filter((message) => {
903
+ const currentMessage = params.message;
904
+ if (typeof message.id === "number" && typeof currentMessage.id === "number" && Number.isFinite(message.id) && Number.isFinite(currentMessage.id)) return message.id < currentMessage.id;
905
+ const guid = normalizeOptionalString(message.guid);
906
+ const currentGuid = normalizeOptionalString(currentMessage.guid);
907
+ if (guid && currentGuid) return guid !== currentGuid;
908
+ return true;
909
+ }).toSorted((a, b) => historyRowSortValue(a) - historyRowSortValue(b)).map((message) => historyEntryFromMessage(message, params.senderNormalized)).filter((entry) => Boolean(entry)).slice(-maxMessages);
970
910
  if (history.length === 0) return {};
971
911
  return {
972
912
  inboundHistory: history,
@@ -981,81 +921,56 @@ async function resolveIMessageDmHistoryContext(params) {
981
921
  })).join("\n\n")
982
922
  };
983
923
  }
984
- // extensions/imessage/src/monitor/drop-diagnostic-cache.ts
985
- const THROTTLED_DROP_DIAGNOSTIC_CACHE_MAX_SIZE = 512;
986
- function createIMessageThrottledDropDiagnosticCache() {
987
- return createDedupeCache({
988
- maxSize: THROTTLED_DROP_DIAGNOSTIC_CACHE_MAX_SIZE,
989
- ttlMs: 0
990
- });
991
- }
992
924
  // extensions/imessage/src/monitor/echo-cache.ts
993
925
  const SENT_MESSAGE_TEXT_TTL_MS = 4e3;
994
926
  const SENT_MESSAGE_ID_TTL_MS = 6e4;
995
927
  function createSentMessageCache() {
996
928
  const textCache = /* @__PURE__ */ new Map();
997
- const textBackedByIdCache = /* @__PURE__ */ new Map();
998
929
  const mediaCache = /* @__PURE__ */ new Map();
999
- const mediaBackedByIdCache = /* @__PURE__ */ new Map();
1000
930
  const messageIdCache = /* @__PURE__ */ new Map();
1001
931
  function remember(scope, lookup) {
1002
- const textKey = normalizeIMessageEchoText(lookup.text);
1003
- if (textKey) textCache.set(`${scope}:${textKey}`, Date.now());
1004
- const mediaKey = resolveIMessageEchoMediaKey(lookup.media);
1005
- if (mediaKey) mediaCache.set(`${scope}:${mediaKey}`, Date.now());
932
+ const now = Date.now();
1006
933
  const messageIdKey = normalizeIMessageMessageId(lookup.messageId);
1007
- if (messageIdKey) {
1008
- messageIdCache.set(`${scope}:${messageIdKey}`, Date.now());
1009
- if (textKey) textBackedByIdCache.set(`${scope}:${textKey}`, Date.now());
1010
- if (mediaKey) mediaBackedByIdCache.set(`${scope}:${mediaKey}`, Date.now());
934
+ if (messageIdKey) messageIdCache.set(`${scope}:${messageIdKey}`, now);
935
+ for (const [cache, value] of [[textCache, normalizeIMessageEchoText(lookup.text)], [mediaCache, resolveIMessageEchoMediaKey(lookup.media)]]) if (value) {
936
+ const key = `${scope}:${value}`;
937
+ cache.set(key, {
938
+ timestamp: now,
939
+ idTimestamp: messageIdKey ? now : cache.get(key)?.idTimestamp
940
+ });
1011
941
  }
1012
942
  cleanup();
1013
943
  }
1014
- async function has(scope, lookup, options = false) {
1015
- cleanup();
1016
- const resolvedOptions = typeof options === "boolean" ? { skipIdShortCircuit: options } : options;
944
+ async function has(scope, lookup, options = {}) {
1017
945
  if (await hasPersistedIMessageEcho({
1018
946
  scope,
1019
947
  text: lookup.text,
1020
948
  media: lookup.media,
1021
949
  messageId: lookup.messageId,
1022
- skipIdShortCircuit: resolvedOptions.skipIdShortCircuit,
1023
- includePendingText: resolvedOptions.includePendingText
950
+ skipIdShortCircuit: options.skipIdShortCircuit,
951
+ includePendingText: options.includePendingText
1024
952
  })) return true;
1025
- const matchesWithin = (cache, key, ttlMs) => {
1026
- const timestamp = key ? cache.get(`${scope}:${key}`) : void 0;
1027
- return Boolean(timestamp && Date.now() - timestamp <= ttlMs);
1028
- };
1029
- const hasContentOnlyMatch = (cache, backedById, key) => {
1030
- const timestamp = key ? cache.get(`${scope}:${key}`) : void 0;
1031
- const idTimestamp = key ? backedById.get(`${scope}:${key}`) : void 0;
1032
- return typeof timestamp === "number" && (!idTimestamp || timestamp > idTimestamp);
1033
- };
953
+ cleanup();
1034
954
  const textKey = normalizeIMessageEchoText(lookup.text);
1035
955
  const mediaKey = resolveIMessageEchoMediaKey(lookup.media);
956
+ const text = textKey ? textCache.get(`${scope}:${textKey}`) : void 0;
957
+ const media = mediaKey ? mediaCache.get(`${scope}:${mediaKey}`) : void 0;
958
+ const hasContentOnlyMatch = (entry) => Boolean(entry && (!entry.idTimestamp || entry.timestamp > entry.idTimestamp));
1036
959
  const messageIdKey = normalizeIMessageMessageId(lookup.messageId);
1037
960
  let canUseMediaFallback = !messageIdKey;
1038
961
  if (messageIdKey) {
1039
- if (matchesWithin(messageIdCache, messageIdKey, SENT_MESSAGE_ID_TTL_MS)) return true;
1040
- const hasTextOnlyMatch = hasContentOnlyMatch(textCache, textBackedByIdCache, textKey);
1041
- const hasMediaOnlyMatch = hasContentOnlyMatch(mediaCache, mediaBackedByIdCache, mediaKey);
962
+ if (messageIdCache.has(`${scope}:${messageIdKey}`)) return true;
963
+ const hasTextOnlyMatch = hasContentOnlyMatch(text);
964
+ const hasMediaOnlyMatch = hasContentOnlyMatch(media);
1042
965
  canUseMediaFallback = hasMediaOnlyMatch;
1043
- if (!resolvedOptions.skipIdShortCircuit && !hasTextOnlyMatch && !hasMediaOnlyMatch) return false;
966
+ if (!options.skipIdShortCircuit && !hasTextOnlyMatch && !hasMediaOnlyMatch) return false;
1044
967
  }
1045
- return matchesWithin(textCache, textKey, SENT_MESSAGE_TEXT_TTL_MS) || canUseMediaFallback && matchesWithin(mediaCache, mediaKey, SENT_MESSAGE_TEXT_TTL_MS);
968
+ return Boolean(text || canUseMediaFallback && media);
1046
969
  }
1047
970
  function cleanup() {
1048
971
  const now = Date.now();
1049
- for (const cache of [
1050
- textCache,
1051
- textBackedByIdCache,
1052
- mediaCache,
1053
- mediaBackedByIdCache,
1054
- messageIdCache
1055
- ]) {
1056
- const ttlMs = cache === messageIdCache ? SENT_MESSAGE_ID_TTL_MS : SENT_MESSAGE_TEXT_TTL_MS;
1057
- for (const [key, timestamp] of cache) if (now - timestamp > ttlMs) cache.delete(key);
1058
- }
972
+ for (const cache of [textCache, mediaCache]) for (const [key, entry] of cache) if (now - entry.timestamp > SENT_MESSAGE_TEXT_TTL_MS) cache.delete(key);
973
+ for (const [key, timestamp] of messageIdCache) if (now - timestamp > SENT_MESSAGE_ID_TTL_MS) messageIdCache.delete(key);
1059
974
  }
1060
975
  return {
1061
976
  remember,
@@ -1166,8 +1081,6 @@ async function resolveIMessageConversationRoute(params) {
1166
1081
  }
1167
1082
  });
1168
1083
  if (runtimeRoute.bindingRecord) await service.touchAsync(runtimeRoute.bindingRecord.bindingId, void 0, runtimeRoute.bindingRecord.conversation);
1169
- if (runtimeRoute.bindingRecord && !runtimeRoute.boundSessionKey) logVerbose(`imessage: plugin-bound conversation ${conversationId}`);
1170
- else if (runtimeRoute.boundSessionKey) logVerbose(`imessage: routed via bound conversation ${conversationId} -> ${runtimeRoute.boundSessionKey}`);
1171
1084
  return {
1172
1085
  route: runtimeRoute.route,
1173
1086
  bindingResolution: runtimeRoute.bindingRecord ? null : bindingResolution
@@ -1204,8 +1117,7 @@ async function resolveIMessageInboundMentionPolicy(params) {
1204
1117
  accountId: params.accountId,
1205
1118
  chatId: params.message.chat_id ?? void 0,
1206
1119
  chatGuid: params.message.chat_guid ?? void 0,
1207
- chatIdentifier: params.message.chat_identifier ?? void 0,
1208
- isKnownFromMeMessageId: params.isKnownFromMeMessageId
1120
+ chatIdentifier: params.message.chat_identifier ?? void 0
1209
1121
  }));
1210
1122
  return {
1211
1123
  ...resolveBotThreadMentionPolicy({
@@ -1479,12 +1391,13 @@ async function resolveIMessageInboundDecision(params) {
1479
1391
  sender,
1480
1392
  chatId
1481
1393
  });
1394
+ let storeAllowFrom;
1482
1395
  const ingressResolver = getIMessageRuntime().channel.inbound.ingress.createResolver({
1483
1396
  channelId: "imessage",
1484
1397
  accountId: params.accountId,
1485
1398
  identity: imessageIngressIdentity,
1486
1399
  cfg: params.cfg,
1487
- readStoreAllowFrom: async () => params.storeAllowFrom
1400
+ readStoreAllowFrom: () => storeAllowFrom ??= params.readStoreAllowFrom()
1488
1401
  });
1489
1402
  const resolveChannelIngress = (contextBinding) => ingressResolver.message({
1490
1403
  subject: {
@@ -1516,27 +1429,27 @@ async function resolveIMessageInboundDecision(params) {
1516
1429
  if (senderAccess.decision !== "allow") {
1517
1430
  if (isGroup) {
1518
1431
  if (senderAccess.reasonCode === "group_policy_disabled") {
1519
- params.logVerbose?.("Blocked iMessage group message (groupPolicy: disabled)");
1432
+ logVerbose("Blocked iMessage group message (groupPolicy: disabled)");
1520
1433
  return {
1521
1434
  kind: "drop",
1522
1435
  reason: "groupPolicy disabled"
1523
1436
  };
1524
1437
  }
1525
1438
  if (senderAccess.reasonCode === "group_policy_empty_allowlist") {
1526
- params.logVerbose?.("Blocked iMessage group message (groupPolicy: allowlist, no groupAllowFrom)");
1439
+ logVerbose("Blocked iMessage group message (groupPolicy: allowlist, no groupAllowFrom)");
1527
1440
  return {
1528
1441
  kind: "drop",
1529
1442
  reason: "groupPolicy allowlist (empty groupAllowFrom)"
1530
1443
  };
1531
1444
  }
1532
1445
  if (senderAccess.reasonCode === "group_policy_not_allowlisted") {
1533
- params.logVerbose?.(`Blocked iMessage sender ${sender} (not in groupAllowFrom)`);
1446
+ logVerbose(`Blocked iMessage sender ${sender} (not in groupAllowFrom)`);
1534
1447
  return {
1535
1448
  kind: "drop",
1536
1449
  reason: "not in groupAllowFrom"
1537
1450
  };
1538
1451
  }
1539
- params.logVerbose?.(`Blocked iMessage group message (${senderAccess.reasonCode})`);
1452
+ logVerbose(`Blocked iMessage group message (${senderAccess.reasonCode})`);
1540
1453
  return {
1541
1454
  kind: "drop",
1542
1455
  reason: senderAccess.reasonCode
@@ -1550,14 +1463,14 @@ async function resolveIMessageInboundDecision(params) {
1550
1463
  kind: "pairing",
1551
1464
  senderId: senderNormalized
1552
1465
  };
1553
- params.logVerbose?.(`Blocked iMessage sender ${sender} (dmPolicy=${params.dmPolicy})`);
1466
+ logVerbose(`Blocked iMessage sender ${sender} (dmPolicy=${params.dmPolicy})`);
1554
1467
  return {
1555
1468
  kind: "drop",
1556
1469
  reason: "dmPolicy blocked"
1557
1470
  };
1558
1471
  }
1559
1472
  if (isGroup && groupListPolicy.allowlistEnabled && !groupListPolicy.allowed) {
1560
- params.logVerbose?.(`imessage: skipping group message (${groupId ?? "unknown"}) not in allowlist`);
1473
+ logVerbose(`imessage: skipping group message (${groupId ?? "unknown"}) not in allowlist`);
1561
1474
  return {
1562
1475
  kind: "drop",
1563
1476
  reason: "group id not in allowlist"
@@ -1580,8 +1493,7 @@ async function resolveIMessageInboundDecision(params) {
1580
1493
  accountId: params.accountId,
1581
1494
  chatId,
1582
1495
  chatGuid,
1583
- chatIdentifier,
1584
- isKnownFromMeMessageId: params.isKnownFromMeMessageId
1496
+ chatIdentifier
1585
1497
  })));
1586
1498
  if (notificationMode === "own" && !targetIsOwn) return {
1587
1499
  kind: "drop",
@@ -1619,7 +1531,7 @@ async function resolveIMessageInboundDecision(params) {
1619
1531
  ...selfChatLookup,
1620
1532
  text: bodyText
1621
1533
  })) {
1622
- params.logVerbose?.("imessage: dropping self-chat reflected duplicate");
1534
+ logVerbose("imessage: dropping self-chat reflected duplicate");
1623
1535
  return {
1624
1536
  kind: "drop",
1625
1537
  reason: "self-chat echo"
@@ -1634,7 +1546,7 @@ async function resolveIMessageInboundDecision(params) {
1634
1546
  messageIds: inboundMessageIds,
1635
1547
  includePendingText: isSelfChat
1636
1548
  })) {
1637
- params.logVerbose?.(`imessage: skipping echo message${inboundMessageId ? ` id=${inboundMessageId}` : ""}`);
1549
+ logVerbose(`imessage: skipping echo message${inboundMessageId ? ` id=${inboundMessageId}` : ""}`);
1638
1550
  return {
1639
1551
  kind: "drop",
1640
1552
  reason: "echo"
@@ -1643,7 +1555,7 @@ async function resolveIMessageInboundDecision(params) {
1643
1555
  }
1644
1556
  const reflection = detectReflectedContent(messageText);
1645
1557
  if (reflection.isReflection) {
1646
- params.logVerbose?.(`imessage: dropping reflected assistant content (markers: ${reflection.matchedLabels.join(", ")})`);
1558
+ logVerbose(`imessage: dropping reflected assistant content (markers: ${reflection.matchedLabels.join(", ")})`);
1647
1559
  return {
1648
1560
  kind: "drop",
1649
1561
  reason: "reflected assistant content"
@@ -1684,7 +1596,7 @@ async function resolveIMessageInboundDecision(params) {
1684
1596
  body: visibleReply.body ?? "",
1685
1597
  sender: visibleReply.sender
1686
1598
  } : null;
1687
- if (replyContext && !filteredReplyContext && isGroup) params.logVerbose?.(`imessage: drop reply context (mode=${contextVisibilityMode}, sender_allowed=${replySenderAllowed ? "yes" : "no"})`);
1599
+ if (replyContext && !filteredReplyContext && isGroup) logVerbose(`imessage: drop reply context (mode=${contextVisibilityMode}, sender_allowed=${replySenderAllowed ? "yes" : "no"})`);
1688
1600
  const historyKey = isGroup ? String(chatId ?? chatGuid ?? chatIdentifier ?? "unknown") : void 0;
1689
1601
  const mentioned = isGroup ? matchesMentionPatterns(messageText, mentionRegexes) : true;
1690
1602
  const { requireMention, implicitMentionKinds, enforceMentionRequirement } = await resolveIMessageInboundMentionPolicy({
@@ -1693,14 +1605,13 @@ async function resolveIMessageInboundDecision(params) {
1693
1605
  groupId,
1694
1606
  isGroup,
1695
1607
  message: params.message,
1696
- requireMentionOverride: params.opts?.requireMention,
1697
- isKnownFromMeMessageId: params.isKnownFromMeMessageId
1608
+ requireMentionOverride: params.opts?.requireMention
1698
1609
  });
1699
1610
  const canDetectMention = mentionRegexes.length > 0 || enforceMentionRequirement;
1700
1611
  const commandAuthorized = commandAccess.authorized;
1701
1612
  if (commandAccess.shouldBlockControlCommand) {
1702
- if (params.logVerbose) logInboundDrop({
1703
- log: params.logVerbose,
1613
+ logInboundDrop({
1614
+ log: logVerbose,
1704
1615
  channel: "imessage",
1705
1616
  reason: "control command (unauthorized)",
1706
1617
  target: sender
@@ -1727,7 +1638,7 @@ async function resolveIMessageInboundDecision(params) {
1727
1638
  });
1728
1639
  const effectiveWasMentioned = mentionDecision.effectiveWasMentioned;
1729
1640
  if (isGroup && requireMention && canDetectMention && mentionDecision.shouldSkip) {
1730
- params.logVerbose?.(`imessage: skipping group message (no mention)`);
1641
+ logVerbose(`imessage: skipping group message (no mention)`);
1731
1642
  createChannelHistoryWindow({ historyMap: params.groupHistories }).record({
1732
1643
  historyKey: historyKey ?? "",
1733
1644
  limit: params.historyLimit,
@@ -1770,7 +1681,7 @@ async function resolveIMessageInboundDecision(params) {
1770
1681
  };
1771
1682
  }
1772
1683
  async function buildIMessageInboundContext(params) {
1773
- const envelopeOptions = params.envelopeOptions ?? resolveEnvelopeFormatOptions(params.cfg);
1684
+ const envelopeOptions = resolveEnvelopeFormatOptions(params.cfg);
1774
1685
  const { decision } = params;
1775
1686
  const chatId = decision.chatId;
1776
1687
  const chatTarget = decision.isGroup && chatId != null ? formatIMessageChatTarget(chatId) : void 0;
@@ -1979,7 +1890,6 @@ function resolveIMessageIngressNonRetryableFailure(error) {
1979
1890
  }
1980
1891
  function createIMessageDurableIngress(options) {
1981
1892
  const queue = options.queue ?? getIMessageRuntime().state.openChannelIngressQueue({ accountId: options.accountId });
1982
- const now = options.now ?? Date.now;
1983
1893
  const dispatchAdmissionQueue = new KeyedAsyncQueue();
1984
1894
  const priorityDispatchQueue = new KeyedAsyncQueue();
1985
1895
  const monitor = createChannelIngressMonitor({
@@ -2036,7 +1946,6 @@ function createIMessageDurableIngress(options) {
2036
1946
  resolveNonRetryableFailure: resolveIMessageIngressNonRetryableFailure,
2037
1947
  onLog: (message) => options.runtime.log?.(`imessage ${message}`)
2038
1948
  },
2039
- now,
2040
1949
  onError: (error) => options.runtime.error?.(`imessage: ingress drain failed: ${formatErrorMessage(error)}`)
2041
1950
  });
2042
1951
  let stopTask;
@@ -2150,7 +2059,7 @@ async function readAttachmentBuffer(params) {
2150
2059
  originalFilename: `${path.parse(params.attachmentPath).name || "imessage-attachment"}.jpg`
2151
2060
  };
2152
2061
  } catch (err) {
2153
- params.deps.logVerbose?.(`imessage: HEIC attachment conversion failed; staging original instead: ${String(err)}`);
2062
+ logVerbose(`imessage: HEIC attachment conversion failed; staging original instead: ${String(err)}`);
2154
2063
  }
2155
2064
  return {
2156
2065
  buffer,
@@ -2159,7 +2068,6 @@ async function readAttachmentBuffer(params) {
2159
2068
  };
2160
2069
  }
2161
2070
  async function stageIMessageAttachments(attachments, params) {
2162
- const deps = params.deps ?? {};
2163
2071
  const staged = [];
2164
2072
  let unavailableCount = 0;
2165
2073
  for (const attachment of attachments) {
@@ -2174,8 +2082,7 @@ async function stageIMessageAttachments(attachments, params) {
2174
2082
  attachmentPath,
2175
2083
  mimeType: attachment.mime_type,
2176
2084
  maxBytes: params.maxBytes,
2177
- allowedRoots: params.allowedRoots,
2178
- deps
2085
+ allowedRoots: params.allowedRoots
2179
2086
  });
2180
2087
  const saved = await saveMediaBuffer(media.buffer, media.contentType, "inbound", params.maxBytes, media.originalFilename);
2181
2088
  const contentType = saved.contentType ?? media.contentType;
@@ -2187,7 +2094,7 @@ async function stageIMessageAttachments(attachments, params) {
2187
2094
  } catch (err) {
2188
2095
  unavailableCount += 1;
2189
2096
  staged.push(createTypeOnlyIMessageAttachment(attachment));
2190
- deps.logVerbose?.(`imessage: failed to stage inbound attachment: ${String(err)}`);
2097
+ logVerbose(`imessage: failed to stage inbound attachment: ${String(err)}`);
2191
2098
  }
2192
2099
  }
2193
2100
  return {
@@ -2196,12 +2103,11 @@ async function stageIMessageAttachments(attachments, params) {
2196
2103
  };
2197
2104
  }
2198
2105
  // extensions/imessage/src/monitor/poll-comment.ts
2199
- const DEFAULT_COMMENT_WINDOW_MS = 15e3;
2200
- function createPollCommentFolder(options) {
2201
- const windowMs = options?.windowMs ?? DEFAULT_COMMENT_WINDOW_MS;
2106
+ const COMMENT_WINDOW_MS = 15e3;
2107
+ function createPollCommentFolder() {
2202
2108
  const seenPolls = /* @__PURE__ */ new Map();
2203
2109
  function prune(referenceMs) {
2204
- for (const [key, seen] of seenPolls) if (referenceMs - seen.atMs > windowMs) seenPolls.delete(key);
2110
+ for (const [key, seen] of seenPolls) if (referenceMs - seen.atMs > COMMENT_WINDOW_MS) seenPolls.delete(key);
2205
2111
  }
2206
2112
  return {
2207
2113
  rememberPoll(guid, atMs, sender) {
@@ -2217,7 +2123,7 @@ function createPollCommentFolder(options) {
2217
2123
  const key = replyToGuid?.trim();
2218
2124
  if (!key || !Number.isFinite(atMs)) return false;
2219
2125
  const seen = seenPolls.get(key);
2220
- if (!seen || atMs < seen.atMs || atMs - seen.atMs > windowMs) return false;
2126
+ if (!seen || atMs < seen.atMs || atMs - seen.atMs > COMMENT_WINDOW_MS) return false;
2221
2127
  const replySender = normalizeLowercaseStringOrEmpty(sender);
2222
2128
  return seen.sender.length > 0 && replySender.length > 0 && seen.sender === replySender;
2223
2129
  }
@@ -2269,7 +2175,7 @@ function enqueueIMessageReactionSystemEvent(params) {
2269
2175
  const { decision, runtime } = params;
2270
2176
  const queued = enqueueRoutedSystemEvent(decision.text, decision.route, { contextKey: decision.contextKey });
2271
2177
  runtime.log?.(`imessage: reaction system event ${queued ? "queued" : "deduped"} session=${decision.route.sessionKey} target=${decision.reaction.targetGuid ?? "unknown"} action=${decision.reaction.action} emoji=${decision.reaction.emoji}`);
2272
- params.logVerbose?.("imessage: reaction event enqueued");
2178
+ logVerbose("imessage: reaction event enqueued");
2273
2179
  return queued;
2274
2180
  }
2275
2181
  // extensions/imessage/src/monitor/recovery-cursor.ts
@@ -2332,36 +2238,28 @@ function applyRecoveryCursorUpdate(key, update) {
2332
2238
  return write;
2333
2239
  }
2334
2240
  async function readRecoveryCursor(accountId, dbIdentity) {
2335
- try {
2336
- const store = openRecoveryCursorStore();
2337
- const key = recoveryCursorStoreKey(accountId, dbIdentity);
2338
- const value = await store.lookup(key);
2339
- if (value) return Number.isFinite(value.lastRowid) ? value.lastRowid : null;
2340
- const legacy = await store.consume(accountId);
2341
- if (legacy && Number.isFinite(legacy.lastRowid)) {
2342
- await store.registerIfAbsent(key, { lastRowid: legacy.lastRowid });
2343
- const adopted = await store.lookup(key);
2344
- return adopted && Number.isFinite(adopted.lastRowid) ? adopted.lastRowid : null;
2345
- }
2346
- return null;
2347
- } catch {
2348
- return null;
2241
+ const store = openRecoveryCursorStore();
2242
+ const key = recoveryCursorStoreKey(accountId, dbIdentity);
2243
+ const value = await store.lookup(key);
2244
+ if (value) return Number.isFinite(value.lastRowid) ? value.lastRowid : null;
2245
+ const legacy = await store.consume(accountId);
2246
+ if (legacy && Number.isFinite(legacy.lastRowid)) {
2247
+ await store.registerIfAbsent(key, { lastRowid: legacy.lastRowid });
2248
+ const adopted = await store.lookup(key);
2249
+ return adopted && Number.isFinite(adopted.lastRowid) ? adopted.lastRowid : null;
2349
2250
  }
2251
+ return null;
2350
2252
  }
2351
2253
  async function migrateLegacyCatchupCursor(accountId, dbIdentity) {
2352
- try {
2353
- const legacy = getIMessageRuntime().state.openKeyedStoreV2({
2354
- namespace: LEGACY_CATCHUP_CURSOR_NAMESPACE,
2355
- maxEntries: LEGACY_CATCHUP_CURSOR_MAX_ENTRIES
2356
- });
2357
- const key = createHash("sha256").update(accountId, "utf8").digest("hex").slice(0, 32);
2358
- const value = await legacy.consume(key);
2359
- const rowid = typeof value?.lastSeenRowid === "number" && Number.isFinite(value.lastSeenRowid) ? value.lastSeenRowid : null;
2360
- if (rowid !== null) await advanceIMessageRecoveryCursor(accountId, dbIdentity, rowid);
2361
- return rowid;
2362
- } catch {
2363
- return null;
2364
- }
2254
+ const legacy = getIMessageRuntime().state.openKeyedStoreV2({
2255
+ namespace: LEGACY_CATCHUP_CURSOR_NAMESPACE,
2256
+ maxEntries: LEGACY_CATCHUP_CURSOR_MAX_ENTRIES
2257
+ });
2258
+ const key = createHash("sha256").update(accountId, "utf8").digest("hex").slice(0, 32);
2259
+ const value = await legacy.consume(key);
2260
+ const rowid = typeof value?.lastSeenRowid === "number" && Number.isFinite(value.lastSeenRowid) ? value.lastSeenRowid : null;
2261
+ if (rowid !== null) await advanceIMessageRecoveryCursor(accountId, dbIdentity, rowid);
2262
+ return rowid;
2365
2263
  }
2366
2264
  async function reconcileRecoveryCursorToWatermark(accountId, dbIdentity, cursorRowid, watermarkRowid) {
2367
2265
  if (cursorRowid === null || watermarkRowid === null || cursorRowid <= watermarkRowid) return cursorRowid;
@@ -2384,7 +2282,6 @@ async function loadIMessageRecoveryCursor(accountId, dbIdentity, options = {}) {
2384
2282
  const watermarkRowid = typeof options.watermarkRowid === "number" && Number.isFinite(options.watermarkRowid) ? options.watermarkRowid : null;
2385
2283
  const current = await readRecoveryCursor(accountId, dbIdentity);
2386
2284
  if (current !== null) return await reconcileRecoveryCursorToWatermark(accountId, dbIdentity, current, watermarkRowid);
2387
- if (options.migrateLegacyCatchup === false) return null;
2388
2285
  return await reconcileRecoveryCursorToWatermark(accountId, dbIdentity, await migrateLegacyCatchupCursor(accountId, dbIdentity), watermarkRowid);
2389
2286
  }
2390
2287
  /** Advance the cursor forward to `rowid` (monotonic per database; never rewinds). */
@@ -2400,7 +2297,6 @@ async function advanceIMessageRecoveryCursor(accountId, dbIdentity, rowid) {
2400
2297
  // extensions/imessage/src/monitor/self-chat-cache.ts
2401
2298
  const SELF_CHAT_TTL_MS = 1e4;
2402
2299
  const SELF_CHAT_CREATED_AT_TOLERANCE_MS = 1e3;
2403
- const MAX_SELF_CHAT_CACHE_ENTRIES = 512;
2404
2300
  const CLEANUP_MIN_INTERVAL_MS = 1e3;
2405
2301
  function normalizeText(text) {
2406
2302
  if (!text) return null;
@@ -2410,81 +2306,58 @@ function normalizeText(text) {
2410
2306
  function isUsableTimestamp(createdAt) {
2411
2307
  return typeof createdAt === "number" && Number.isFinite(createdAt);
2412
2308
  }
2413
- function digestText(text) {
2414
- return createHash("sha256").update(text).digest("hex");
2415
- }
2416
2309
  function buildScope(parts) {
2417
2310
  if (!parts.isGroup) return `${parts.accountId}:imessage:${parts.sender}`;
2418
2311
  const chatTarget = formatIMessageChatTarget(parts.chatId) || "chat_id:unknown";
2419
2312
  return `${parts.accountId}:${chatTarget}:imessage:${parts.sender}`;
2420
2313
  }
2421
- var DefaultSelfChatCache = class {
2314
+ var SelfChatCache = class {
2422
2315
  constructor() {
2423
- this.cache = /* @__PURE__ */ new Map();
2424
- this.insertionOrder = /* @__PURE__ */ new Map();
2316
+ this.entries = [];
2425
2317
  this.lastCleanupAt = 0;
2426
2318
  }
2427
2319
  buildBucketKey(lookup) {
2428
2320
  const text = normalizeText(lookup.text);
2429
2321
  if (!text) return null;
2430
- return `${buildScope(lookup)}:${digestText(text)}`;
2322
+ return `${buildScope(lookup)}:${sha256Hex(text)}`;
2431
2323
  }
2432
2324
  remember(lookup) {
2433
2325
  const key = this.buildBucketKey(lookup);
2434
2326
  if (!key || !isUsableTimestamp(lookup.createdAt)) return;
2435
- const entries = this.cache.get(key) ?? /* @__PURE__ */ new Set();
2436
- const entry = {
2327
+ this.entries.push({
2328
+ key,
2437
2329
  createdAt: lookup.createdAt,
2438
2330
  createdAtSkewToleranceMs: lookup.allowCreatedAtSkew ? SELF_CHAT_CREATED_AT_TOLERANCE_MS : 0,
2439
2331
  rememberedAt: Date.now()
2440
- };
2441
- entries.add(entry);
2442
- this.cache.set(key, entries);
2443
- this.insertionOrder.set(entry, key);
2332
+ });
2444
2333
  this.maybeCleanup();
2445
2334
  }
2446
2335
  has(lookup) {
2447
2336
  this.maybeCleanup();
2448
2337
  const key = this.buildBucketKey(lookup);
2449
2338
  if (!key || !isUsableTimestamp(lookup.createdAt)) return false;
2450
- const entries = this.cache.get(key);
2451
- if (!entries) return false;
2452
2339
  const now = Date.now();
2453
2340
  const createdAt = lookup.createdAt;
2454
- return [...entries.values()].some((entry) => {
2341
+ return this.entries.some((entry) => {
2455
2342
  const createdAtDelta = Math.abs(entry.createdAt - createdAt);
2456
- return now - entry.rememberedAt <= SELF_CHAT_TTL_MS && (createdAtDelta === 0 || createdAtDelta < entry.createdAtSkewToleranceMs);
2343
+ return entry.key === key && now - entry.rememberedAt <= SELF_CHAT_TTL_MS && (createdAtDelta === 0 || createdAtDelta < entry.createdAtSkewToleranceMs);
2457
2344
  });
2458
2345
  }
2459
- removeEntry(entry, key) {
2460
- this.insertionOrder.delete(entry);
2461
- const entries = this.cache.get(key);
2462
- entries?.delete(entry);
2463
- if (entries?.size === 0) this.cache.delete(key);
2464
- }
2465
2346
  maybeCleanup() {
2466
2347
  const now = Date.now();
2467
2348
  if (now - this.lastCleanupAt < CLEANUP_MIN_INTERVAL_MS) return;
2468
2349
  this.lastCleanupAt = now;
2469
- for (const [entry, key] of this.insertionOrder) if (now - entry.rememberedAt > SELF_CHAT_TTL_MS) this.removeEntry(entry, key);
2470
- while (this.insertionOrder.size > MAX_SELF_CHAT_CACHE_ENTRIES) {
2471
- const [entry, key] = expectDefined(this.insertionOrder.entries().next().value, "oldest iMessage self-chat cache entry");
2472
- this.removeEntry(entry, key);
2473
- }
2350
+ this.entries = this.entries.filter((entry) => now - entry.rememberedAt <= SELF_CHAT_TTL_MS).slice(-512);
2474
2351
  }
2475
2352
  };
2476
- function createSelfChatCache() {
2477
- return new DefaultSelfChatCache();
2478
- }
2479
2353
  // extensions/imessage/src/monitor/watch-error-log.ts
2480
- const MAX_WATCH_ERROR_MESSAGE_CHARS = 200;
2481
2354
  function sanitizeIMessageWatchErrorPayload(payload) {
2482
2355
  if (!isRecord(payload)) return {};
2483
2356
  const safe = {};
2484
2357
  if (typeof payload.code === "number" && Number.isFinite(payload.code)) safe.code = payload.code;
2485
2358
  if (typeof payload.message === "string") {
2486
- const sanitizedMessage = sanitizeTerminalText(payload.message);
2487
- if (sanitizedMessage) safe.message = sanitizedMessage.length > MAX_WATCH_ERROR_MESSAGE_CHARS ? `${truncateUtf16Safe(sanitizedMessage, 199)}…` : sanitizedMessage;
2359
+ const guidance = normalizeIMessageFullDiskAccessError(payload.message);
2360
+ if (guidance) safe.message = guidance;
2488
2361
  }
2489
2362
  return safe;
2490
2363
  }
@@ -2547,7 +2420,7 @@ async function monitorIMessageProvider(opts) {
2547
2420
  const historyLimit = resolvePromptHistoryLimit(imessageCfg.historyLimit ?? cfg.messages?.groupChat?.historyLimit);
2548
2421
  const groupHistories = /* @__PURE__ */ new Map();
2549
2422
  const sentMessageCache = createSentMessageCache();
2550
- const selfChatCache = createSelfChatCache();
2423
+ const selfChatCache = new SelfChatCache();
2551
2424
  const loopRateLimiter = createLoopRateLimiter();
2552
2425
  const textLimit = resolveTextChunkLimit(cfg, "imessage", accountInfo.accountId);
2553
2426
  const allowFrom = normalizeStringEntries(imessageCfg.allowFrom);
@@ -2611,7 +2484,10 @@ async function monitorIMessageProvider(opts) {
2611
2484
  error
2612
2485
  });
2613
2486
  let staleBacklogSuppressed = 0;
2614
- const loggedThrottledDropDiagnostics = createIMessageThrottledDropDiagnosticCache();
2487
+ const loggedThrottledDropDiagnostics = createDedupeCache({
2488
+ maxSize: 512,
2489
+ ttlMs: 0
2490
+ });
2615
2491
  const watchSourceDbPath = resolveIMessageChatDbLookupPath({
2616
2492
  cliPath,
2617
2493
  dbPath,
@@ -2623,10 +2499,7 @@ async function monitorIMessageProvider(opts) {
2623
2499
  dbPath,
2624
2500
  remoteHost
2625
2501
  });
2626
- const recoveryCursorRowid = await loadIMessageRecoveryCursor(accountInfo.accountId, recoveryCursorDbIdentity, {
2627
- migrateLegacyCatchup: !catchupCfg.enabled,
2628
- watermarkRowid: recoveryBoundaryRowid
2629
- });
2502
+ const recoveryCursorRowid = catchupCfg.enabled ? null : await loadIMessageRecoveryCursor(accountInfo.accountId, recoveryCursorDbIdentity, { watermarkRowid: recoveryBoundaryRowid });
2630
2503
  const reconciledWatchSinceRowid = catchupCfg.enabled ? null : recoveryCursorRowid !== null ? recoveryBoundaryRowid !== null ? Math.max(recoveryCursorRowid, recoveryBoundaryRowid - 500) : recoveryCursorRowid : recoveryBoundaryRowid;
2631
2504
  const watchSinceRowid = reconciledWatchSinceRowid === 0 ? -1 : reconciledWatchSinceRowid;
2632
2505
  let latestAdvancedRecoveryCursorRowid = recoveryCursorRowid ?? -1;
@@ -2689,14 +2562,7 @@ async function monitorIMessageProvider(opts) {
2689
2562
  await abandon();
2690
2563
  return;
2691
2564
  }
2692
- const combined = entries.length === 1 ? expectDefined(entries[0], "single iMessage dispatch entry").message : combineIMessagePayloads(entries.map((entry) => entry.message));
2693
- if (entries.length > 1 && shouldLogVerbose()) {
2694
- const text = combined.text ?? "";
2695
- const preview = sliceUtf16Safe(text, 0, 50);
2696
- const ellipsis = text.length > 50 ? "..." : "";
2697
- logVerbose(`[imessage] merged ${entries.length} debounced messages: "${preview}${ellipsis}"`);
2698
- }
2699
- await handleMessageNow(combined, admissionLifecycle);
2565
+ await handleMessageNow(entries.length === 1 ? expectDefined(entries[0], "single iMessage dispatch entry").message : combineIMessagePayloads(entries.map((entry) => entry.message)), admissionLifecycle);
2700
2566
  await settle();
2701
2567
  } catch (err) {
2702
2568
  await abandon();
@@ -2802,12 +2668,6 @@ async function monitorIMessageProvider(opts) {
2802
2668
  return;
2803
2669
  }
2804
2670
  const { messageText, bodyText, mediaFacts, mediaCandidates, rawMediaAttachments, effectiveAttachmentRoots } = resolveIMessageInboundBodyText(message);
2805
- const storeAllowFrom = await readChannelAllowFromStore("imessage", process.env, accountInfo.accountId).catch(() => []);
2806
- const isQuestionReaction = hasIMessageQuestionReactionTarget({
2807
- accountId: accountInfo.accountId,
2808
- message,
2809
- bodyText
2810
- });
2811
2671
  const decision = await resolveIMessageInboundDecision({
2812
2672
  cfg,
2813
2673
  accountId: accountInfo.accountId,
@@ -2821,13 +2681,12 @@ async function monitorIMessageProvider(opts) {
2821
2681
  allowLegacyConversationAllowFromForGroup,
2822
2682
  groupPolicy,
2823
2683
  dmPolicy,
2824
- storeAllowFrom,
2684
+ readStoreAllowFrom: () => readChannelAllowFromStore("imessage", process.env, accountInfo.accountId),
2825
2685
  historyLimit,
2826
2686
  groupHistories,
2827
2687
  echoCache: sentMessageCache,
2828
2688
  selfChatCache,
2829
- reactionNotifications: isQuestionReaction ? "all" : imessageCfg.reactionNotifications,
2830
- logVerbose
2689
+ reactionNotifications: imessageCfg.reactionNotifications
2831
2690
  });
2832
2691
  const chatId = message.chat_id ?? void 0;
2833
2692
  const senderForKey = (message.sender ?? "").trim();
@@ -2894,18 +2753,9 @@ async function monitorIMessageProvider(opts) {
2894
2753
  return;
2895
2754
  }
2896
2755
  if (decision.kind === "reaction") {
2897
- if (await maybeResolveIMessageQuestionReaction({
2898
- cfg,
2899
- accountId: accountInfo.accountId,
2900
- message,
2901
- bodyText,
2902
- senderId: decision.senderNormalized,
2903
- logDebug: logVerbose
2904
- })) return;
2905
2756
  enqueueIMessageReactionSystemEvent({
2906
2757
  decision,
2907
- runtime,
2908
- logVerbose
2758
+ runtime
2909
2759
  });
2910
2760
  return;
2911
2761
  }
@@ -2964,8 +2814,7 @@ async function monitorIMessageProvider(opts) {
2964
2814
  unavailableCount: rawMediaAttachments.filter((attachment) => !attachment.path).length
2965
2815
  } : await stageIMessageAttachments(mediaCandidates, {
2966
2816
  maxBytes: mediaMaxBytes,
2967
- allowedRoots: effectiveAttachmentRoots,
2968
- deps: { logVerbose }
2817
+ allowedRoots: effectiveAttachmentRoots
2969
2818
  });
2970
2819
  const mediaAttachments = staged.attachments;
2971
2820
  const unavailableCount = staged.unavailableCount;
@@ -2990,8 +2839,7 @@ async function monitorIMessageProvider(opts) {
2990
2839
  message,
2991
2840
  senderNormalized: decision.senderNormalized,
2992
2841
  limit: dmHistoryLimit,
2993
- envelopeOptions: resolveEnvelopeFormatOptions(cfg),
2994
- logVerbose
2842
+ envelopeOptions: resolveEnvelopeFormatOptions(cfg)
2995
2843
  }) : void 0;
2996
2844
  const pluginChannelRuntime = opts.channelRuntime;
2997
2845
  const { ctxPayload, chatTarget, imessageTo } = await buildIMessageInboundContext({
@@ -3013,10 +2861,6 @@ async function monitorIMessageProvider(opts) {
3013
2861
  allowFrom,
3014
2862
  normalizeEntry: normalizeIMessageHandle
3015
2863
  });
3016
- if (shouldLogVerbose()) {
3017
- const preview = truncateUtf16Safe(ctxPayload.Body ?? "", 200).replace(/\n/g, "\\n");
3018
- logVerbose(`imessage inbound: chatId=${chatId ?? "unknown"} from=${ctxPayload.From} len=${(ctxPayload.Body ?? "").length} preview="${preview}"`);
3019
- }
3020
2864
  const sendReadReceipts = imessageCfg.sendReadReceipts !== false;
3021
2865
  const typingTarget = ctxPayload.To;
3022
2866
  const readTarget = !decision.isGroup && decision.chatGuid ? `chat_guid:${decision.chatGuid}` : typingTarget;
@@ -3182,14 +3026,13 @@ async function monitorIMessageProvider(opts) {
3182
3026
  message,
3183
3027
  gatewayRuntime: approvalGatewayRuntime
3184
3028
  })) return true;
3185
- return await maybeResolveIMessageApprovalReaction({
3029
+ return (await handleIMessageApprovalReaction({
3186
3030
  cfg,
3187
3031
  accountId: accountInfo.accountId,
3188
3032
  message,
3189
3033
  bodyText: resolveIMessageInboundBodyText(message).bodyText,
3190
- gatewayRuntime: approvalGatewayRuntime,
3191
- logVerboseMessage: logVerbose
3192
- });
3034
+ gatewayRuntime: approvalGatewayRuntime
3035
+ })).handled;
3193
3036
  };
3194
3037
  const ingress = createIMessageDurableIngress({
3195
3038
  accountId: accountInfo.accountId,
@@ -3356,8 +3199,7 @@ async function monitorIMessageProvider(opts) {
3356
3199
  cfg,
3357
3200
  accountId: accountInfo.accountId,
3358
3201
  allowRecentChatDiscovery,
3359
- gatewayRuntime: approvalGatewayRuntime,
3360
- logVerboseMessage: logVerbose
3202
+ gatewayRuntime: approvalGatewayRuntime
3361
3203
  }).catch((err) => {
3362
3204
  logVerbose(`imessage: approval reaction poll failed: ${String(err)}`);
3363
3205
  });
@@ -3425,8 +3267,8 @@ async function sendIMessageOutbound(params) {
3425
3267
  ...params.mediaReadFile ? { mediaReadFile: params.mediaReadFile } : {},
3426
3268
  ...params.audioAsVoice ? { audioAsVoice: true } : {},
3427
3269
  maxBytes,
3428
- accountId: params.accountId ?? void 0,
3429
- replyToId: params.replyToId ?? void 0,
3270
+ accountId: params.accountId,
3271
+ replyToId: params.replyToId,
3430
3272
  conversationReadOrigin: params.conversationReadOrigin,
3431
3273
  assertDirectAdapterHandoff: params.assertDirectAdapterHandoff,
3432
3274
  onPlatformSendDispatch: params.onPlatformSendDispatch,
@@ -3490,4 +3332,4 @@ async function startIMessageGatewayAccount(ctx) {
3490
3332
  statusSink
3491
3333
  });
3492
3334
  }
3493
- export { imessageSetupWizard, probeIMessageAccount, sendIMessageOutbound, startIMessageGatewayAccount };
3335
+ export { probeIMessageAccount, sendIMessageOutbound, startIMessageGatewayAccount };