@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.
- package/dist/.setup/{accounts-BH0coL46.mjs → accounts-C1Ypv3Wn.mjs} +6 -7
- package/dist/.setup/{actions-CW7XYYF4.mjs → actions-qOuKy8To.mjs} +360 -108
- package/dist/.setup/{actions.runtime-B2ZHQ3TJ.mjs → actions.runtime-Ccg9CARL.mjs} +10 -56
- package/dist/.setup/{api-6xi-NBrt.mjs → api-BXXuQQCS.mjs} +7 -7
- package/dist/.setup/{api-B0ih8Iof.mjs → api-DTbxSOPc.mjs} +8 -8
- package/dist/.setup/{approval-handler.runtime-BKmqba3L.mjs → approval-handler.runtime-B6vrFfZx.mjs} +112 -131
- package/dist/.setup/{approval-reactions-B9CJS0NT.mjs → approval-reactions-DBBsqbRC.mjs} +152 -72
- package/dist/.setup/{channel-6kcKpSRw.mjs → channel-CLaN4mQn.mjs} +142 -119
- package/dist/.setup/{channel.runtime-BW4A0m4h.mjs → channel.runtime-XJGx1MIH.mjs} +205 -363
- package/dist/.setup/chat-context-fTfJVqAi.mjs +77 -0
- package/dist/.setup/chat-lookup-xtPsOBkl.mjs +50 -0
- package/dist/.setup/client-D_BaG8sC.mjs +367 -0
- package/dist/.setup/{config-schema-DRkdRC21.mjs → config-schema-BjkQV5P5.mjs} +1 -2
- package/dist/.setup/{conversation-bindings-vZFn6sN2.mjs → conversation-bindings-8DjxoIYU.mjs} +9 -6
- package/dist/.setup/{media-contract-CoixpXRX.mjs → media-contract-BErFBKV9.mjs} +1 -1
- package/dist/.setup/{message-tool-api-CxSY_rh7.mjs → message-tool-api-DtZ1wKbT.mjs} +3 -6
- package/dist/.setup/{remote-file-CCgMYZ-u.mjs → remote-file-DpK3GQFD.mjs} +8 -22
- package/dist/.setup/{sanitize-outbound-C02fJrn6.mjs → sanitize-outbound-Dhjhz0a-.mjs} +23 -27
- package/dist/.setup/{send-D98hQFdv.mjs → send-DhEW0gfe.mjs} +150 -226
- package/dist/.setup/{setup-core-BWnW0czE.mjs → setup-core-B1_P_t4T.mjs} +2 -2
- package/dist/.setup/{setup-surface-aRZhAzzQ.mjs → setup-surface-CD28mXWK.mjs} +11 -25
- package/dist/.setup/{targets-CIx4BKqL.mjs → targets-C7CIWJ0k.mjs} +12 -3
- package/dist/.source-module-urls/929c518fa2986b0f85f66bceefead22868db0ab452dee914dc6a2bb86b2e60ad.json +1 -1
- package/dist/api.js +8 -8
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/contract-api.js +1 -1
- package/dist/media-contract-api.js +1 -1
- package/dist/message-tool-api.js +1 -1
- package/dist/runtime-api.js +1 -3
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-entry.js +1 -1
- package/dist/src/chat-db.worker.js +1 -1
- package/node_modules/typebox/build/format/duration.d.mts +2 -2
- package/node_modules/typebox/build/format/duration.mjs +2 -2
- package/node_modules/typebox/build/format/idn_email.d.mts +1 -0
- package/node_modules/typebox/build/format/idn_email.mjs +1 -0
- package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -1
- package/node_modules/typebox/build/format/idn_hostname.mjs +1 -1
- package/node_modules/typebox/build/format/ipv4.d.mts +2 -2
- package/node_modules/typebox/build/format/ipv4.mjs +2 -2
- package/node_modules/typebox/build/format/ipv6.d.mts +1 -1
- package/node_modules/typebox/build/format/ipv6.mjs +1 -1
- package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
- package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
- package/node_modules/typebox/build/schema/build.mjs +1 -0
- package/node_modules/typebox/build/schema/engine/_unique.d.mts +4 -2
- package/node_modules/typebox/build/schema/engine/_unique.mjs +7 -3
- package/node_modules/typebox/build/schema/engine/additionalItems.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/additionalProperties.d.mts +0 -3
- package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +15 -17
- package/node_modules/typebox/build/schema/engine/contains.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/index.d.mts +1 -0
- package/node_modules/typebox/build/schema/engine/index.mjs +1 -0
- package/node_modules/typebox/build/schema/engine/items.mjs +7 -4
- package/node_modules/typebox/build/schema/engine/maxContains.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/minContains.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/oneOf.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/patternProperties.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/unevaluatedItems.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/unevaluatedProperties.mjs +2 -2
- package/node_modules/typebox/build/system/hashing/hash.mjs +63 -34
- package/node_modules/typebox/build/system/locale/en_US.mjs +2 -1
- package/node_modules/typebox/build/system/memory/assign.d.mts +2 -8
- package/node_modules/typebox/build/system/memory/assign.mjs +13 -6
- package/node_modules/typebox/build/system/memory/clone.d.mts +4 -5
- package/node_modules/typebox/build/system/memory/clone.mjs +8 -96
- package/node_modules/typebox/build/system/memory/create.d.mts +1 -5
- package/node_modules/typebox/build/system/memory/create.mjs +18 -13
- package/node_modules/typebox/build/system/memory/deep.d.mts +2 -0
- package/node_modules/typebox/build/system/memory/deep.mjs +108 -0
- package/node_modules/typebox/build/system/memory/discard.d.mts +1 -1
- package/node_modules/typebox/build/system/memory/discard.mjs +9 -8
- package/node_modules/typebox/build/system/memory/finalize.d.mts +4 -0
- package/node_modules/typebox/build/system/memory/finalize.mjs +30 -0
- package/node_modules/typebox/build/system/memory/keys.d.mts +2 -0
- package/node_modules/typebox/build/system/memory/keys.mjs +8 -0
- package/node_modules/typebox/build/system/memory/update.d.mts +1 -4
- package/node_modules/typebox/build/system/memory/update.mjs +43 -16
- package/node_modules/typebox/build/type/engine/call/index.d.mts +1 -0
- package/node_modules/typebox/build/type/engine/call/index.mjs +1 -0
- package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +2 -24
- package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +2 -29
- package/node_modules/typebox/build/type/engine/evaluate/index.d.mts +1 -0
- package/node_modules/typebox/build/type/engine/evaluate/index.mjs +1 -0
- package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +5 -1
- package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +6 -1
- package/node_modules/typebox/build/type/engine/evaluate/property.d.mts +30 -0
- package/node_modules/typebox/build/type/engine/evaluate/property.mjs +29 -0
- package/node_modules/typebox/build/type/engine/immutable/instantiate_add.d.mts +1 -3
- package/node_modules/typebox/build/type/engine/immutable/instantiate_add.mjs +1 -4
- package/node_modules/typebox/build/type/engine/object/from_intersect.d.mts +2 -2
- package/node_modules/typebox/build/type/engine/object/from_intersect.mjs +2 -2
- package/node_modules/typebox/build/type/engine/object/from_union.d.mts +2 -2
- package/node_modules/typebox/build/type/engine/object/from_union.mjs +2 -2
- package/node_modules/typebox/build/type/engine/optional/instantiate_add.d.mts +1 -3
- package/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs +1 -4
- package/node_modules/typebox/build/type/engine/readonly/instantiate_add.d.mts +1 -3
- package/node_modules/typebox/build/type/engine/readonly/instantiate_add.mjs +1 -4
- package/node_modules/typebox/build/type/engine/this/index.d.mts +1 -0
- package/node_modules/typebox/build/type/engine/this/index.mjs +1 -0
- package/node_modules/typebox/build/value/clone/clone.d.mts +4 -3
- package/node_modules/typebox/build/value/clone/clone.mjs +5 -7
- package/node_modules/typebox/build/value/repair/from_cyclic.d.mts +2 -0
- package/node_modules/typebox/build/value/repair/from_cyclic.mjs +6 -0
- package/node_modules/typebox/build/value/shared/union_priority_sort.mjs +25 -14
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +1 -1
- package/node_modules/zod/package.json +1 -1
- package/node_modules/zod/src/v4/classic/tests/array.test.ts +27 -0
- package/node_modules/zod/src/v4/classic/tests/map.test.ts +7 -0
- package/node_modules/zod/src/v4/classic/tests/set.test.ts +6 -0
- package/node_modules/zod/src/v4/core/compile.ts +9 -0
- package/node_modules/zod/src/v4/core/schemas.ts +35 -1
- package/node_modules/zod/src/v4/core/util.ts +13 -0
- package/node_modules/zod/src/v4/core/versions.ts +1 -1
- package/node_modules/zod/v4/core/compile.cjs +8 -0
- package/node_modules/zod/v4/core/compile.js +8 -0
- package/node_modules/zod/v4/core/schemas.cjs +30 -0
- package/node_modules/zod/v4/core/schemas.d.cts +3 -1
- package/node_modules/zod/v4/core/schemas.d.ts +3 -1
- package/node_modules/zod/v4/core/schemas.js +30 -0
- package/node_modules/zod/v4/core/util.cjs +15 -0
- package/node_modules/zod/v4/core/util.d.cts +2 -0
- package/node_modules/zod/v4/core/util.d.ts +2 -0
- package/node_modules/zod/v4/core/util.js +14 -0
- package/node_modules/zod/v4/core/versions.cjs +1 -1
- package/node_modules/zod/v4/core/versions.js +1 -1
- package/openclaw.plugin.json +0 -30
- package/package.json +6 -6
- package/dist/.setup/client-C3e7ZLqA.mjs +0 -739
- package/dist/.setup/question-reactions-DTj-VH3N.mjs +0 -67
- package/dist/.setup/reaction-context-Bw00FkmU.mjs +0 -105
- package/dist/.setup/runtime-BRQCPuAD.mjs +0 -7
- package/node_modules/typebox/build/system/memory/freeze.d.mts +0 -4
- package/node_modules/typebox/build/system/memory/freeze.mjs +0 -6
|
@@ -1,22 +1,20 @@
|
|
|
1
|
-
import { c as
|
|
2
|
-
import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount, r as listEnabledIMessageAccounts, s as resolveIMessageDuplicateSourceOwner } from "./accounts-
|
|
3
|
-
import { r as imessageRpcSupportsMethod } from "./message-tool-api-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { a as resolveIMessageGroupSystemPrompt, s as resolveIMessageInboundConversationId } from "./channel-
|
|
8
|
-
import { r as sanitizeOutboundText } from "./sanitize-outbound-
|
|
9
|
-
import { n as resolveIMessageAttachmentRoots, r as resolveIMessageRemoteAttachmentRoots } from "./media-contract-
|
|
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-
|
|
12
|
-
import {
|
|
13
|
-
import { n as hasPersistedIMessageEcho, r as normalizeIMessageEchoText, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-
|
|
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,
|
|
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 {
|
|
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 =
|
|
159
|
-
const
|
|
160
|
-
const
|
|
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
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
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
|
|
180
|
-
|
|
181
|
-
|
|
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 =
|
|
186
|
-
|
|
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 =
|
|
176
|
+
const account = resolveIMessageAccount({
|
|
206
177
|
cfg,
|
|
207
178
|
accountId: opts.accountId
|
|
208
179
|
});
|
|
209
|
-
const target = parseIMessageTarget(
|
|
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 =
|
|
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
|
|
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 =
|
|
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 ||
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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) =>
|
|
967
|
-
message
|
|
968
|
-
currentMessage
|
|
969
|
-
|
|
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
|
|
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
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
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 =
|
|
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:
|
|
1023
|
-
includePendingText:
|
|
950
|
+
skipIdShortCircuit: options.skipIdShortCircuit,
|
|
951
|
+
includePendingText: options.includePendingText
|
|
1024
952
|
})) return true;
|
|
1025
|
-
|
|
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 (
|
|
1040
|
-
const hasTextOnlyMatch = hasContentOnlyMatch(
|
|
1041
|
-
const hasMediaOnlyMatch = hasContentOnlyMatch(
|
|
962
|
+
if (messageIdCache.has(`${scope}:${messageIdKey}`)) return true;
|
|
963
|
+
const hasTextOnlyMatch = hasContentOnlyMatch(text);
|
|
964
|
+
const hasMediaOnlyMatch = hasContentOnlyMatch(media);
|
|
1042
965
|
canUseMediaFallback = hasMediaOnlyMatch;
|
|
1043
|
-
if (!
|
|
966
|
+
if (!options.skipIdShortCircuit && !hasTextOnlyMatch && !hasMediaOnlyMatch) return false;
|
|
1044
967
|
}
|
|
1045
|
-
return
|
|
968
|
+
return Boolean(text || canUseMediaFallback && media);
|
|
1046
969
|
}
|
|
1047
970
|
function cleanup() {
|
|
1048
971
|
const now = Date.now();
|
|
1049
|
-
for (const cache of [
|
|
1050
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
1703
|
-
log:
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2200
|
-
function createPollCommentFolder(
|
|
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 >
|
|
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 >
|
|
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
|
-
|
|
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
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
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
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
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
|
|
2314
|
+
var SelfChatCache = class {
|
|
2422
2315
|
constructor() {
|
|
2423
|
-
this.
|
|
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)}:${
|
|
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
|
-
|
|
2436
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
2487
|
-
if (
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
2684
|
+
readStoreAllowFrom: () => readChannelAllowFromStore("imessage", process.env, accountInfo.accountId),
|
|
2825
2685
|
historyLimit,
|
|
2826
2686
|
groupHistories,
|
|
2827
2687
|
echoCache: sentMessageCache,
|
|
2828
2688
|
selfChatCache,
|
|
2829
|
-
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
|
|
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
|
-
|
|
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
|
|
3429
|
-
replyToId: params.replyToId
|
|
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 {
|
|
3335
|
+
export { probeIMessageAccount, sendIMessageOutbound, startIMessageGatewayAccount };
|