@openclaw/imessage 2026.10.10-beta.5 → 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-DacCvDBt.mjs → accounts-C1Ypv3Wn.mjs} +9 -13
- package/dist/.setup/{actions-762Azi-J.mjs → actions-qOuKy8To.mjs} +364 -116
- package/dist/.setup/{actions.runtime-DHiRiPHe.mjs → actions.runtime-Ccg9CARL.mjs} +12 -61
- package/dist/.setup/{api-Deyo8NDs.mjs → api-BXXuQQCS.mjs} +8 -9
- package/dist/.setup/{api-BFGXDHiC.mjs → api-DTbxSOPc.mjs} +9 -9
- package/dist/.setup/{approval-handler.runtime-fsSBf2jf.mjs → approval-handler.runtime-B6vrFfZx.mjs} +113 -133
- package/dist/.setup/{approval-reactions-CoAkCiwf.mjs → approval-reactions-DBBsqbRC.mjs} +156 -79
- package/dist/.setup/{channel-C61YdcZR.mjs → channel-CLaN4mQn.mjs} +151 -137
- package/dist/.setup/{channel.runtime-B5jle0g3.mjs → channel.runtime-XJGx1MIH.mjs} +236 -425
- 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-DXwa_3V5.mjs → config-schema-BjkQV5P5.mjs} +1 -4
- package/dist/.setup/{conversation-bindings-DR-dbHjs.mjs → conversation-bindings-8DjxoIYU.mjs} +10 -8
- package/dist/.setup/{media-contract-BwWs5PM7.mjs → media-contract-BErFBKV9.mjs} +2 -3
- package/dist/.setup/{message-tool-api-B5NfPnFL.mjs → message-tool-api-DtZ1wKbT.mjs} +6 -12
- package/dist/.setup/{outbound-send-deps-B-QEsLSM.mjs → outbound-send-deps-_da8jqeo.mjs} +1 -2
- package/dist/.setup/{remote-file-B94a787O.mjs → remote-file-DpK3GQFD.mjs} +12 -30
- package/dist/.setup/{rolldown-runtime-BBjsoOtd.mjs → rolldown-runtime-782esTHO.mjs} +1 -2
- package/dist/.setup/{sanitize-outbound-BtEY324R.mjs → sanitize-outbound-Dhjhz0a-.mjs} +25 -31
- package/dist/.setup/{send-BvxKNWzF.mjs → send-DhEW0gfe.mjs} +155 -236
- package/dist/.setup/{setup-core-BHyLkAEJ.mjs → setup-core-B1_P_t4T.mjs} +3 -4
- package/dist/.setup/{setup-surface-DPqiLGy0.mjs → setup-surface-CD28mXWK.mjs} +13 -29
- package/dist/.setup/{targets-B4oU7n-v.mjs → targets-C7CIWJ0k.mjs} +15 -9
- package/dist/.source-module-urls/929c518fa2986b0f85f66bceefead22868db0ab452dee914dc6a2bb86b2e60ad.json +1 -1
- package/dist/api.js +9 -9
- 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/doctor-contract-api.js +7 -13
- package/dist/index.js +1 -2
- 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/secret-contract-api.js +1 -2
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-entry.js +2 -3
- package/dist/src/chat-db.worker.js +2 -3
- 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-CcROCvRq.mjs +0 -746
- package/dist/.setup/question-reactions-BD06B1Lx.mjs +0 -68
- package/dist/.setup/reaction-context-Q26DYzQH.mjs +0 -106
- package/dist/.setup/runtime-Cza4CY5T.mjs +0 -8
- 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-
|
|
10
|
-
import { t as IMESSAGE_LEGACY_OUTBOUND_SEND_DEP_KEYS } from "./outbound-send-deps-
|
|
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-Q26DYzQH.mjs";
|
|
15
|
-
import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-BD06B1Lx.mjs";
|
|
16
|
-
import { imessageSetupWizard } from "./setup-surface-DPqiLGy0.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
|
+
import { t as IMESSAGE_LEGACY_OUTBOUND_SEND_DEP_KEYS } from "./outbound-send-deps-_da8jqeo.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";
|
|
65
|
-
|
|
63
|
+
import { sha256Hex } from "openclaw/plugin-sdk/node-crypto-runtime";
|
|
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,46 +105,83 @@ 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
|
}
|
|
200
171
|
}
|
|
201
172
|
}
|
|
202
|
-
|
|
203
|
-
//#region extensions/imessage/src/chat.ts
|
|
173
|
+
// extensions/imessage/src/chat.ts
|
|
204
174
|
async function runChatAction(method, to, opts, isTyping) {
|
|
205
175
|
const cfg = requireRuntimeConfig(opts.cfg, "iMessage chat action");
|
|
206
|
-
const account =
|
|
176
|
+
const account = resolveIMessageAccount({
|
|
207
177
|
cfg,
|
|
208
178
|
accountId: opts.accountId
|
|
209
179
|
});
|
|
210
|
-
const target = parseIMessageTarget(
|
|
211
|
-
const params =
|
|
212
|
-
if (target.kind === "chat_id") params.chat_id = target.chatId;
|
|
213
|
-
else if (target.kind === "chat_guid") params.chat_guid = target.chatGuid;
|
|
214
|
-
else if (target.kind === "chat_identifier") params.chat_identifier = target.chatIdentifier;
|
|
215
|
-
else params.to = target.to;
|
|
180
|
+
const target = parseIMessageTarget(to);
|
|
181
|
+
const params = iMessageTargetRpcParams(target);
|
|
216
182
|
if (method === "typing") {
|
|
217
183
|
params.typing = isTyping;
|
|
218
|
-
const service =
|
|
184
|
+
const service = (target.kind === "handle" ? target.service : void 0) ?? account.config.service;
|
|
219
185
|
if (service) params.service = service;
|
|
220
186
|
}
|
|
221
187
|
const cliPath = opts.cliPath?.trim() || account.config.cliPath?.trim() || "imsg";
|
|
@@ -231,7 +197,7 @@ async function runChatAction(method, to, opts, isTyping) {
|
|
|
231
197
|
});
|
|
232
198
|
const shouldClose = !opts.client;
|
|
233
199
|
try {
|
|
234
|
-
await client.request(method, params
|
|
200
|
+
await client.request(method, params);
|
|
235
201
|
} finally {
|
|
236
202
|
if (shouldClose) await client.stop();
|
|
237
203
|
}
|
|
@@ -242,8 +208,7 @@ async function sendIMessageTyping(to, isTyping, opts) {
|
|
|
242
208
|
async function markIMessageChatRead(to, opts) {
|
|
243
209
|
await runChatAction("read", to, opts);
|
|
244
210
|
}
|
|
245
|
-
|
|
246
|
-
//#region extensions/imessage/src/monitor/abort-handler.ts
|
|
211
|
+
// extensions/imessage/src/monitor/abort-handler.ts
|
|
247
212
|
function attachIMessageMonitorAbortHandler(params) {
|
|
248
213
|
const abort = params.abortSignal;
|
|
249
214
|
if (!abort) return () => {};
|
|
@@ -255,8 +220,7 @@ function attachIMessageMonitorAbortHandler(params) {
|
|
|
255
220
|
abort.addEventListener("abort", onAbort, { once: true });
|
|
256
221
|
return () => abort.removeEventListener("abort", onAbort);
|
|
257
222
|
}
|
|
258
|
-
|
|
259
|
-
//#region extensions/imessage/src/monitor/catchup.ts
|
|
223
|
+
// extensions/imessage/src/monitor/catchup.ts
|
|
260
224
|
const DEFAULT_MAX_AGE_MINUTES = 120;
|
|
261
225
|
const MAX_MAX_AGE_MINUTES = 720;
|
|
262
226
|
const DEFAULT_PER_RUN_LIMIT = 50;
|
|
@@ -361,13 +325,12 @@ async function advanceIMessageCatchupCursor(accountId, next, config) {
|
|
|
361
325
|
return await updateIMessageCatchupCursor(accountId, (existing) => decideLiveCatchupCursorAdvance(existing, cursor, config.maxFailureRetries));
|
|
362
326
|
}
|
|
363
327
|
async function performIMessageCatchup(params) {
|
|
364
|
-
const now =
|
|
328
|
+
const now = Date.now();
|
|
365
329
|
const cfg = params.config;
|
|
366
330
|
const cursor = await loadIMessageCatchupCursor(params.accountId);
|
|
367
331
|
const lookbackMs = cursor === null ? cfg.firstRunLookbackMinutes * 6e4 : cfg.maxAgeMinutes * 6e4;
|
|
368
332
|
const ageBoundMs = now - cfg.maxAgeMinutes * 6e4;
|
|
369
333
|
const windowStartMs = Math.max(cursor?.lastSeenMs ?? now - lookbackMs, ageBoundMs);
|
|
370
|
-
const windowEndMs = now;
|
|
371
334
|
const sinceRowid = cursor?.lastSeenRowid ?? 0;
|
|
372
335
|
const summary = {
|
|
373
336
|
querySucceeded: false,
|
|
@@ -375,20 +338,9 @@ async function performIMessageCatchup(params) {
|
|
|
375
338
|
fetchedCount: 0,
|
|
376
339
|
replayed: 0,
|
|
377
340
|
skippedFromMe: 0,
|
|
378
|
-
skippedPreCursor: 0,
|
|
379
341
|
skippedGivenUp: 0,
|
|
380
342
|
failed: 0,
|
|
381
|
-
givenUp: 0
|
|
382
|
-
cursorBefore: cursor ? {
|
|
383
|
-
lastSeenMs: cursor.lastSeenMs,
|
|
384
|
-
lastSeenRowid: cursor.lastSeenRowid
|
|
385
|
-
} : null,
|
|
386
|
-
cursorAfter: {
|
|
387
|
-
lastSeenMs: cursor?.lastSeenMs ?? windowStartMs,
|
|
388
|
-
lastSeenRowid: cursor?.lastSeenRowid ?? 0
|
|
389
|
-
},
|
|
390
|
-
windowStartMs,
|
|
391
|
-
windowEndMs
|
|
343
|
+
givenUp: 0
|
|
392
344
|
};
|
|
393
345
|
let fetchResult;
|
|
394
346
|
try {
|
|
@@ -416,16 +368,10 @@ async function performIMessageCatchup(params) {
|
|
|
416
368
|
let highWatermarkRowid = cursorBeforeRowid;
|
|
417
369
|
let earliestHeldFailureRow = null;
|
|
418
370
|
for (const row of rows) {
|
|
419
|
-
if (row.rowid <= sinceRowid)
|
|
420
|
-
summary.skippedPreCursor += 1;
|
|
421
|
-
continue;
|
|
422
|
-
}
|
|
371
|
+
if (row.rowid <= sinceRowid) continue;
|
|
423
372
|
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
424
373
|
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
425
|
-
if (row.date < ageBoundMs)
|
|
426
|
-
summary.skippedPreCursor += 1;
|
|
427
|
-
continue;
|
|
428
|
-
}
|
|
374
|
+
if (row.date < ageBoundMs) continue;
|
|
429
375
|
if (row.isFromMe) {
|
|
430
376
|
try {
|
|
431
377
|
await params.observeSkippedFromMe?.(row);
|
|
@@ -476,10 +422,6 @@ async function performIMessageCatchup(params) {
|
|
|
476
422
|
lastSeenRowid = highWatermarkRowid;
|
|
477
423
|
}
|
|
478
424
|
const capped = capFailureRetriesMap(failureRetries);
|
|
479
|
-
summary.cursorAfter = {
|
|
480
|
-
lastSeenMs,
|
|
481
|
-
lastSeenRowid
|
|
482
|
-
};
|
|
483
425
|
const next = buildIMessageCatchupCursor({
|
|
484
426
|
lastSeenMs,
|
|
485
427
|
lastSeenRowid,
|
|
@@ -489,8 +431,7 @@ async function performIMessageCatchup(params) {
|
|
|
489
431
|
if (summary.replayed > 0 || summary.failed > 0 || summary.givenUp > 0) params.log?.(`imessage catchup: replayed=${summary.replayed} skippedFromMe=${summary.skippedFromMe} skippedGivenUp=${summary.skippedGivenUp} failed=${summary.failed} givenUp=${summary.givenUp} fetchedCount=${summary.fetchedCount}`);
|
|
490
432
|
return summary;
|
|
491
433
|
}
|
|
492
|
-
|
|
493
|
-
//#region extensions/imessage/src/monitor/strip-imsg-length-prefixed-text.ts
|
|
434
|
+
// extensions/imessage/src/monitor/strip-imsg-length-prefixed-text.ts
|
|
494
435
|
const utf8Decoder = new TextDecoder();
|
|
495
436
|
function readVarint(buf, start) {
|
|
496
437
|
let offset = start;
|
|
@@ -525,8 +466,7 @@ function stripImessageLengthPrefixedUtf8Text(text) {
|
|
|
525
466
|
const inner = utf8Decoder.decode(stripped);
|
|
526
467
|
return inner.length > 0 ? inner : text;
|
|
527
468
|
}
|
|
528
|
-
|
|
529
|
-
//#region extensions/imessage/src/monitor/parse-notification.ts
|
|
469
|
+
// extensions/imessage/src/monitor/parse-notification.ts
|
|
530
470
|
function isOptionalString(value) {
|
|
531
471
|
return value === void 0 || value === null || typeof value === "string";
|
|
532
472
|
}
|
|
@@ -537,7 +477,7 @@ function isOptionalBoolean(value) {
|
|
|
537
477
|
return value === void 0 || value === null || typeof value === "boolean";
|
|
538
478
|
}
|
|
539
479
|
function isOptionalStringArray(value) {
|
|
540
|
-
return value === void 0 || value === null ||
|
|
480
|
+
return value === void 0 || value === null || isStringArray(value);
|
|
541
481
|
}
|
|
542
482
|
function isOptionalAttachments(value) {
|
|
543
483
|
if (value === void 0 || value === null) return true;
|
|
@@ -559,8 +499,7 @@ function parseIMessageNotification(raw) {
|
|
|
559
499
|
reply_to_text: typeof message.reply_to_text === "string" ? stripImessageLengthPrefixedUtf8Text(message.reply_to_text) : message.reply_to_text
|
|
560
500
|
};
|
|
561
501
|
}
|
|
562
|
-
|
|
563
|
-
//#region extensions/imessage/src/monitor/catchup-bridge.ts
|
|
502
|
+
// extensions/imessage/src/monitor/catchup-bridge.ts
|
|
564
503
|
const PER_CHAT_HISTORY_LIMIT = 500;
|
|
565
504
|
const CATCHUP_CHATS_LIST_LIMIT = 200;
|
|
566
505
|
const CATCHUP_RPC_TIMEOUT_MS = 3e4;
|
|
@@ -681,12 +620,10 @@ async function runIMessageCatchup(params) {
|
|
|
681
620
|
await params.observeSkippedFromMePayload?.(entry.message);
|
|
682
621
|
},
|
|
683
622
|
log,
|
|
684
|
-
warn: warnLog
|
|
685
|
-
...params.now ? { now: params.now() } : {}
|
|
623
|
+
warn: warnLog
|
|
686
624
|
});
|
|
687
625
|
}
|
|
688
|
-
|
|
689
|
-
//#region extensions/imessage/src/monitor/coalesce.ts
|
|
626
|
+
// extensions/imessage/src/monitor/coalesce.ts
|
|
690
627
|
const MAX_COALESCED_TEXT_CHARS = 4e3;
|
|
691
628
|
const MAX_COALESCED_ATTACHMENTS = 20;
|
|
692
629
|
/**
|
|
@@ -714,7 +651,7 @@ function combineIMessagePayloads(payloads) {
|
|
|
714
651
|
const keepContent = payloadIndex < 9 || payloadIndex === payloads.length - 1;
|
|
715
652
|
payloadIndex += 1;
|
|
716
653
|
const createdAt = payload.created_at;
|
|
717
|
-
if (
|
|
654
|
+
if (createdAt && (latestCreatedAt === void 0 || createdAt > latestCreatedAt)) latestCreatedAt = createdAt;
|
|
718
655
|
if (!reply && (payload.thread_originator_guid != null || payload.reply_to_guid != null)) reply = payload;
|
|
719
656
|
if (!keepContent) continue;
|
|
720
657
|
const text = textLength <= MAX_COALESCED_TEXT_CHARS ? (payload.text ?? "").trim() : "";
|
|
@@ -747,8 +684,7 @@ function combineIMessagePayloads(payloads) {
|
|
|
747
684
|
reply_to_sender: reply.reply_to_sender ?? null
|
|
748
685
|
};
|
|
749
686
|
}
|
|
750
|
-
|
|
751
|
-
//#region extensions/imessage/src/monitor/conversation-repair.ts
|
|
687
|
+
// extensions/imessage/src/monitor/conversation-repair.ts
|
|
752
688
|
const DEFAULT_CHATS_LIMIT = 20;
|
|
753
689
|
const DEFAULT_PER_CHAT_HISTORY_LIMIT = 50;
|
|
754
690
|
const DEFAULT_RPC_TIMEOUT_MS = 5e3;
|
|
@@ -778,7 +714,7 @@ function extractAuthoritativeRecoveryProjection(entry) {
|
|
|
778
714
|
if (hasNonEmptyString(entry.chat_guid)) projection.chat_guid = entry.chat_guid;
|
|
779
715
|
if (hasNonEmptyString(entry.chat_identifier)) projection.chat_identifier = entry.chat_identifier;
|
|
780
716
|
if (typeof entry.chat_name === "string") projection.chat_name = entry.chat_name;
|
|
781
|
-
if (
|
|
717
|
+
if (isStringArray(entry.participants)) projection.participants = entry.participants;
|
|
782
718
|
return hasUsableConversationAnchor(projection) ? projection : null;
|
|
783
719
|
}
|
|
784
720
|
function projectionConflictKey(projection) {
|
|
@@ -856,8 +792,7 @@ async function repairIMessageConversationAnchor(params) {
|
|
|
856
792
|
runtime?.log?.(`imessage: recovered anchorless message GUID=${guid} chat_id=${repaired.chat_id ?? "unknown"} is_group=${repaired.is_group}`);
|
|
857
793
|
return repaired;
|
|
858
794
|
}
|
|
859
|
-
|
|
860
|
-
//#region extensions/imessage/src/monitor/deliver.ts
|
|
795
|
+
// extensions/imessage/src/monitor/deliver.ts
|
|
861
796
|
async function deliverIMessageReply(params) {
|
|
862
797
|
const { payload, target, runtime, maxBytes, textLimit, accountId, sentMessageCache } = params;
|
|
863
798
|
const scope = `${accountId ?? ""}:${target}`;
|
|
@@ -920,8 +855,7 @@ function createIMessageEchoCachingSend(params) {
|
|
|
920
855
|
return sent;
|
|
921
856
|
};
|
|
922
857
|
}
|
|
923
|
-
|
|
924
|
-
//#region extensions/imessage/src/monitor/dm-history.ts
|
|
858
|
+
// extensions/imessage/src/monitor/dm-history.ts
|
|
925
859
|
const DM_HISTORY_RPC_TIMEOUT_MS = 1e4;
|
|
926
860
|
function resolveIMessageDmHistoryLimit(params) {
|
|
927
861
|
const senderCandidates = [
|
|
@@ -940,14 +874,6 @@ function historyRowSortValue(message) {
|
|
|
940
874
|
const createdAtMs = typeof message.created_at === "string" ? Date.parse(message.created_at) : NaN;
|
|
941
875
|
return Number.isFinite(createdAtMs) ? createdAtMs : 0;
|
|
942
876
|
}
|
|
943
|
-
function isBeforeCurrentMessage(params) {
|
|
944
|
-
const { message, currentMessage } = params;
|
|
945
|
-
if (typeof message.id === "number" && typeof currentMessage.id === "number" && Number.isFinite(message.id) && Number.isFinite(currentMessage.id)) return message.id < currentMessage.id;
|
|
946
|
-
const guid = normalizeOptionalString(message.guid);
|
|
947
|
-
const currentGuid = normalizeOptionalString(currentMessage.guid);
|
|
948
|
-
if (guid && currentGuid) return guid !== currentGuid;
|
|
949
|
-
return true;
|
|
950
|
-
}
|
|
951
877
|
function historyEntryFromMessage(message, fallbackSender) {
|
|
952
878
|
const body = normalizeOptionalString(message.text);
|
|
953
879
|
if (!body) return null;
|
|
@@ -970,13 +896,17 @@ async function resolveIMessageDmHistoryContext(params) {
|
|
|
970
896
|
attachments: false
|
|
971
897
|
}, { timeoutMs: DM_HISTORY_RPC_TIMEOUT_MS });
|
|
972
898
|
} catch (err) {
|
|
973
|
-
|
|
899
|
+
logVerbose(`imessage: DM history fetch failed for chat_id=${chatId}: ${String(err)}`);
|
|
974
900
|
return {};
|
|
975
901
|
}
|
|
976
|
-
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) =>
|
|
977
|
-
message
|
|
978
|
-
currentMessage
|
|
979
|
-
|
|
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);
|
|
980
910
|
if (history.length === 0) return {};
|
|
981
911
|
return {
|
|
982
912
|
inboundHistory: history,
|
|
@@ -991,91 +921,63 @@ async function resolveIMessageDmHistoryContext(params) {
|
|
|
991
921
|
})).join("\n\n")
|
|
992
922
|
};
|
|
993
923
|
}
|
|
994
|
-
|
|
995
|
-
//#region extensions/imessage/src/monitor/drop-diagnostic-cache.ts
|
|
996
|
-
const THROTTLED_DROP_DIAGNOSTIC_CACHE_MAX_SIZE = 512;
|
|
997
|
-
function createIMessageThrottledDropDiagnosticCache() {
|
|
998
|
-
return createDedupeCache({
|
|
999
|
-
maxSize: THROTTLED_DROP_DIAGNOSTIC_CACHE_MAX_SIZE,
|
|
1000
|
-
ttlMs: 0
|
|
1001
|
-
});
|
|
1002
|
-
}
|
|
1003
|
-
//#endregion
|
|
1004
|
-
//#region extensions/imessage/src/monitor/echo-cache.ts
|
|
924
|
+
// extensions/imessage/src/monitor/echo-cache.ts
|
|
1005
925
|
const SENT_MESSAGE_TEXT_TTL_MS = 4e3;
|
|
1006
926
|
const SENT_MESSAGE_ID_TTL_MS = 6e4;
|
|
1007
927
|
function createSentMessageCache() {
|
|
1008
928
|
const textCache = /* @__PURE__ */ new Map();
|
|
1009
|
-
const textBackedByIdCache = /* @__PURE__ */ new Map();
|
|
1010
929
|
const mediaCache = /* @__PURE__ */ new Map();
|
|
1011
|
-
const mediaBackedByIdCache = /* @__PURE__ */ new Map();
|
|
1012
930
|
const messageIdCache = /* @__PURE__ */ new Map();
|
|
1013
931
|
function remember(scope, lookup) {
|
|
1014
|
-
const
|
|
1015
|
-
if (textKey) textCache.set(`${scope}:${textKey}`, Date.now());
|
|
1016
|
-
const mediaKey = resolveIMessageEchoMediaKey(lookup.media);
|
|
1017
|
-
if (mediaKey) mediaCache.set(`${scope}:${mediaKey}`, Date.now());
|
|
932
|
+
const now = Date.now();
|
|
1018
933
|
const messageIdKey = normalizeIMessageMessageId(lookup.messageId);
|
|
1019
|
-
if (messageIdKey) {
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
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
|
+
});
|
|
1023
941
|
}
|
|
1024
942
|
cleanup();
|
|
1025
943
|
}
|
|
1026
|
-
async function has(scope, lookup, options =
|
|
1027
|
-
cleanup();
|
|
1028
|
-
const resolvedOptions = typeof options === "boolean" ? { skipIdShortCircuit: options } : options;
|
|
944
|
+
async function has(scope, lookup, options = {}) {
|
|
1029
945
|
if (await hasPersistedIMessageEcho({
|
|
1030
946
|
scope,
|
|
1031
947
|
text: lookup.text,
|
|
1032
948
|
media: lookup.media,
|
|
1033
949
|
messageId: lookup.messageId,
|
|
1034
|
-
skipIdShortCircuit:
|
|
1035
|
-
includePendingText:
|
|
950
|
+
skipIdShortCircuit: options.skipIdShortCircuit,
|
|
951
|
+
includePendingText: options.includePendingText
|
|
1036
952
|
})) return true;
|
|
1037
|
-
|
|
1038
|
-
const timestamp = key ? cache.get(`${scope}:${key}`) : void 0;
|
|
1039
|
-
return Boolean(timestamp && Date.now() - timestamp <= ttlMs);
|
|
1040
|
-
};
|
|
1041
|
-
const hasContentOnlyMatch = (cache, backedById, key) => {
|
|
1042
|
-
const timestamp = key ? cache.get(`${scope}:${key}`) : void 0;
|
|
1043
|
-
const idTimestamp = key ? backedById.get(`${scope}:${key}`) : void 0;
|
|
1044
|
-
return typeof timestamp === "number" && (!idTimestamp || timestamp > idTimestamp);
|
|
1045
|
-
};
|
|
953
|
+
cleanup();
|
|
1046
954
|
const textKey = normalizeIMessageEchoText(lookup.text);
|
|
1047
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));
|
|
1048
959
|
const messageIdKey = normalizeIMessageMessageId(lookup.messageId);
|
|
1049
960
|
let canUseMediaFallback = !messageIdKey;
|
|
1050
961
|
if (messageIdKey) {
|
|
1051
|
-
if (
|
|
1052
|
-
const hasTextOnlyMatch = hasContentOnlyMatch(
|
|
1053
|
-
const hasMediaOnlyMatch = hasContentOnlyMatch(
|
|
962
|
+
if (messageIdCache.has(`${scope}:${messageIdKey}`)) return true;
|
|
963
|
+
const hasTextOnlyMatch = hasContentOnlyMatch(text);
|
|
964
|
+
const hasMediaOnlyMatch = hasContentOnlyMatch(media);
|
|
1054
965
|
canUseMediaFallback = hasMediaOnlyMatch;
|
|
1055
|
-
if (!
|
|
966
|
+
if (!options.skipIdShortCircuit && !hasTextOnlyMatch && !hasMediaOnlyMatch) return false;
|
|
1056
967
|
}
|
|
1057
|
-
return
|
|
968
|
+
return Boolean(text || canUseMediaFallback && media);
|
|
1058
969
|
}
|
|
1059
970
|
function cleanup() {
|
|
1060
971
|
const now = Date.now();
|
|
1061
|
-
for (const cache of [
|
|
1062
|
-
|
|
1063
|
-
textBackedByIdCache,
|
|
1064
|
-
mediaCache,
|
|
1065
|
-
mediaBackedByIdCache,
|
|
1066
|
-
messageIdCache
|
|
1067
|
-
]) {
|
|
1068
|
-
const ttlMs = cache === messageIdCache ? SENT_MESSAGE_ID_TTL_MS : SENT_MESSAGE_TEXT_TTL_MS;
|
|
1069
|
-
for (const [key, timestamp] of cache) if (now - timestamp > ttlMs) cache.delete(key);
|
|
1070
|
-
}
|
|
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);
|
|
1071
974
|
}
|
|
1072
975
|
return {
|
|
1073
976
|
remember,
|
|
1074
977
|
has
|
|
1075
978
|
};
|
|
1076
979
|
}
|
|
1077
|
-
|
|
1078
|
-
//#region extensions/imessage/src/monitor/group-allowlist-warnings.ts
|
|
980
|
+
// extensions/imessage/src/monitor/group-allowlist-warnings.ts
|
|
1079
981
|
const PER_CHAT_WARNING_CACHE_MAX_SIZE = 512;
|
|
1080
982
|
const startupWarned = /* @__PURE__ */ new Set();
|
|
1081
983
|
const perChatWarned = createDedupeCache({
|
|
@@ -1111,8 +1013,7 @@ function warnGroupAllowlistDropPerChatOnce(params) {
|
|
|
1111
1013
|
params.log(`imessage: dropping group message from chat_id=${chat} (account "${params.accountId}") — not in channels.imessage.groups allowlist. Add channels.imessage.groups["${chat}"] or channels.imessage.groups["*"] to allow it.`);
|
|
1112
1014
|
return true;
|
|
1113
1015
|
}
|
|
1114
|
-
|
|
1115
|
-
//#region extensions/imessage/src/monitor/inbound-dedupe.ts
|
|
1016
|
+
// extensions/imessage/src/monitor/inbound-dedupe.ts
|
|
1116
1017
|
const IMESSAGE_STALE_INBOUND_THRESHOLD_MS = 9e5;
|
|
1117
1018
|
const IMESSAGE_RECOVERY_MAX_AGE_MS = 72e5;
|
|
1118
1019
|
/**
|
|
@@ -1128,8 +1029,7 @@ function isStaleIMessageBacklog(message, nowMs, thresholdMs = IMESSAGE_STALE_INB
|
|
|
1128
1029
|
if (!Number.isFinite(sentMs)) return false;
|
|
1129
1030
|
return nowMs - sentMs > thresholdMs;
|
|
1130
1031
|
}
|
|
1131
|
-
|
|
1132
|
-
//#region extensions/imessage/src/conversation-route.ts
|
|
1032
|
+
// extensions/imessage/src/conversation-route.ts
|
|
1133
1033
|
async function resolveIMessageConversationRoute(params) {
|
|
1134
1034
|
const routeInput = {
|
|
1135
1035
|
channel: "imessage",
|
|
@@ -1181,15 +1081,12 @@ async function resolveIMessageConversationRoute(params) {
|
|
|
1181
1081
|
}
|
|
1182
1082
|
});
|
|
1183
1083
|
if (runtimeRoute.bindingRecord) await service.touchAsync(runtimeRoute.bindingRecord.bindingId, void 0, runtimeRoute.bindingRecord.conversation);
|
|
1184
|
-
if (runtimeRoute.bindingRecord && !runtimeRoute.boundSessionKey) logVerbose(`imessage: plugin-bound conversation ${conversationId}`);
|
|
1185
|
-
else if (runtimeRoute.boundSessionKey) logVerbose(`imessage: routed via bound conversation ${conversationId} -> ${runtimeRoute.boundSessionKey}`);
|
|
1186
1084
|
return {
|
|
1187
1085
|
route: runtimeRoute.route,
|
|
1188
1086
|
bindingResolution: runtimeRoute.bindingRecord ? null : bindingResolution
|
|
1189
1087
|
};
|
|
1190
1088
|
}
|
|
1191
|
-
|
|
1192
|
-
//#region src/channels/mention-gating.ts
|
|
1089
|
+
// src/channels/mention-gating.ts
|
|
1193
1090
|
function resolveBotThreadMentionPolicy(params) {
|
|
1194
1091
|
if (!params.isBotOwnedThread || params.requireMentionInBotThreads === void 0) return {
|
|
1195
1092
|
requireMention: params.requireMention,
|
|
@@ -1200,8 +1097,7 @@ function resolveBotThreadMentionPolicy(params) {
|
|
|
1200
1097
|
implicitMentionKinds: params.requireMentionInBotThreads ? params.implicitMentionKinds?.filter((kind) => kind === "native") : params.implicitMentionKinds
|
|
1201
1098
|
};
|
|
1202
1099
|
}
|
|
1203
|
-
|
|
1204
|
-
//#region extensions/imessage/src/monitor/mention-policy.ts
|
|
1100
|
+
// extensions/imessage/src/monitor/mention-policy.ts
|
|
1205
1101
|
async function resolveIMessageInboundMentionPolicy(params) {
|
|
1206
1102
|
const groups = resolveChannelGroups(params.cfg, "imessage", params.accountId);
|
|
1207
1103
|
const { "*": defaults, ...scopes } = groups ?? {};
|
|
@@ -1221,8 +1117,7 @@ async function resolveIMessageInboundMentionPolicy(params) {
|
|
|
1221
1117
|
accountId: params.accountId,
|
|
1222
1118
|
chatId: params.message.chat_id ?? void 0,
|
|
1223
1119
|
chatGuid: params.message.chat_guid ?? void 0,
|
|
1224
|
-
chatIdentifier: params.message.chat_identifier ?? void 0
|
|
1225
|
-
isKnownFromMeMessageId: params.isKnownFromMeMessageId
|
|
1120
|
+
chatIdentifier: params.message.chat_identifier ?? void 0
|
|
1226
1121
|
}));
|
|
1227
1122
|
return {
|
|
1228
1123
|
...resolveBotThreadMentionPolicy({
|
|
@@ -1233,8 +1128,7 @@ async function resolveIMessageInboundMentionPolicy(params) {
|
|
|
1233
1128
|
enforceMentionRequirement: isBotOwnedThread && requireMentionInBotThreads === true
|
|
1234
1129
|
};
|
|
1235
1130
|
}
|
|
1236
|
-
|
|
1237
|
-
//#region extensions/imessage/src/monitor/reflection-guard.ts
|
|
1131
|
+
// extensions/imessage/src/monitor/reflection-guard.ts
|
|
1238
1132
|
const REFLECTION_PATTERNS = [
|
|
1239
1133
|
{
|
|
1240
1134
|
re: /(?:#\+){2,}#?/,
|
|
@@ -1287,8 +1181,7 @@ function detectReflectedContent(text) {
|
|
|
1287
1181
|
matchedLabels
|
|
1288
1182
|
};
|
|
1289
1183
|
}
|
|
1290
|
-
|
|
1291
|
-
//#region extensions/imessage/src/monitor/inbound-processing.ts
|
|
1184
|
+
// extensions/imessage/src/monitor/inbound-processing.ts
|
|
1292
1185
|
const normalizeNonEmpty = (value) => value.trim() || null;
|
|
1293
1186
|
const imessageConversationIdentityKinds = /* @__PURE__ */ new Set([
|
|
1294
1187
|
"plugin:imessage-chat-id",
|
|
@@ -1498,12 +1391,13 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1498
1391
|
sender,
|
|
1499
1392
|
chatId
|
|
1500
1393
|
});
|
|
1394
|
+
let storeAllowFrom;
|
|
1501
1395
|
const ingressResolver = getIMessageRuntime().channel.inbound.ingress.createResolver({
|
|
1502
1396
|
channelId: "imessage",
|
|
1503
1397
|
accountId: params.accountId,
|
|
1504
1398
|
identity: imessageIngressIdentity,
|
|
1505
1399
|
cfg: params.cfg,
|
|
1506
|
-
readStoreAllowFrom:
|
|
1400
|
+
readStoreAllowFrom: () => storeAllowFrom ??= params.readStoreAllowFrom()
|
|
1507
1401
|
});
|
|
1508
1402
|
const resolveChannelIngress = (contextBinding) => ingressResolver.message({
|
|
1509
1403
|
subject: {
|
|
@@ -1535,27 +1429,27 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1535
1429
|
if (senderAccess.decision !== "allow") {
|
|
1536
1430
|
if (isGroup) {
|
|
1537
1431
|
if (senderAccess.reasonCode === "group_policy_disabled") {
|
|
1538
|
-
|
|
1432
|
+
logVerbose("Blocked iMessage group message (groupPolicy: disabled)");
|
|
1539
1433
|
return {
|
|
1540
1434
|
kind: "drop",
|
|
1541
1435
|
reason: "groupPolicy disabled"
|
|
1542
1436
|
};
|
|
1543
1437
|
}
|
|
1544
1438
|
if (senderAccess.reasonCode === "group_policy_empty_allowlist") {
|
|
1545
|
-
|
|
1439
|
+
logVerbose("Blocked iMessage group message (groupPolicy: allowlist, no groupAllowFrom)");
|
|
1546
1440
|
return {
|
|
1547
1441
|
kind: "drop",
|
|
1548
1442
|
reason: "groupPolicy allowlist (empty groupAllowFrom)"
|
|
1549
1443
|
};
|
|
1550
1444
|
}
|
|
1551
1445
|
if (senderAccess.reasonCode === "group_policy_not_allowlisted") {
|
|
1552
|
-
|
|
1446
|
+
logVerbose(`Blocked iMessage sender ${sender} (not in groupAllowFrom)`);
|
|
1553
1447
|
return {
|
|
1554
1448
|
kind: "drop",
|
|
1555
1449
|
reason: "not in groupAllowFrom"
|
|
1556
1450
|
};
|
|
1557
1451
|
}
|
|
1558
|
-
|
|
1452
|
+
logVerbose(`Blocked iMessage group message (${senderAccess.reasonCode})`);
|
|
1559
1453
|
return {
|
|
1560
1454
|
kind: "drop",
|
|
1561
1455
|
reason: senderAccess.reasonCode
|
|
@@ -1569,14 +1463,14 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1569
1463
|
kind: "pairing",
|
|
1570
1464
|
senderId: senderNormalized
|
|
1571
1465
|
};
|
|
1572
|
-
|
|
1466
|
+
logVerbose(`Blocked iMessage sender ${sender} (dmPolicy=${params.dmPolicy})`);
|
|
1573
1467
|
return {
|
|
1574
1468
|
kind: "drop",
|
|
1575
1469
|
reason: "dmPolicy blocked"
|
|
1576
1470
|
};
|
|
1577
1471
|
}
|
|
1578
1472
|
if (isGroup && groupListPolicy.allowlistEnabled && !groupListPolicy.allowed) {
|
|
1579
|
-
|
|
1473
|
+
logVerbose(`imessage: skipping group message (${groupId ?? "unknown"}) not in allowlist`);
|
|
1580
1474
|
return {
|
|
1581
1475
|
kind: "drop",
|
|
1582
1476
|
reason: "group id not in allowlist"
|
|
@@ -1599,8 +1493,7 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1599
1493
|
accountId: params.accountId,
|
|
1600
1494
|
chatId,
|
|
1601
1495
|
chatGuid,
|
|
1602
|
-
chatIdentifier
|
|
1603
|
-
isKnownFromMeMessageId: params.isKnownFromMeMessageId
|
|
1496
|
+
chatIdentifier
|
|
1604
1497
|
})));
|
|
1605
1498
|
if (notificationMode === "own" && !targetIsOwn) return {
|
|
1606
1499
|
kind: "drop",
|
|
@@ -1638,7 +1531,7 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1638
1531
|
...selfChatLookup,
|
|
1639
1532
|
text: bodyText
|
|
1640
1533
|
})) {
|
|
1641
|
-
|
|
1534
|
+
logVerbose("imessage: dropping self-chat reflected duplicate");
|
|
1642
1535
|
return {
|
|
1643
1536
|
kind: "drop",
|
|
1644
1537
|
reason: "self-chat echo"
|
|
@@ -1653,7 +1546,7 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1653
1546
|
messageIds: inboundMessageIds,
|
|
1654
1547
|
includePendingText: isSelfChat
|
|
1655
1548
|
})) {
|
|
1656
|
-
|
|
1549
|
+
logVerbose(`imessage: skipping echo message${inboundMessageId ? ` id=${inboundMessageId}` : ""}`);
|
|
1657
1550
|
return {
|
|
1658
1551
|
kind: "drop",
|
|
1659
1552
|
reason: "echo"
|
|
@@ -1662,7 +1555,7 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1662
1555
|
}
|
|
1663
1556
|
const reflection = detectReflectedContent(messageText);
|
|
1664
1557
|
if (reflection.isReflection) {
|
|
1665
|
-
|
|
1558
|
+
logVerbose(`imessage: dropping reflected assistant content (markers: ${reflection.matchedLabels.join(", ")})`);
|
|
1666
1559
|
return {
|
|
1667
1560
|
kind: "drop",
|
|
1668
1561
|
reason: "reflected assistant content"
|
|
@@ -1703,7 +1596,7 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1703
1596
|
body: visibleReply.body ?? "",
|
|
1704
1597
|
sender: visibleReply.sender
|
|
1705
1598
|
} : null;
|
|
1706
|
-
if (replyContext && !filteredReplyContext && isGroup)
|
|
1599
|
+
if (replyContext && !filteredReplyContext && isGroup) logVerbose(`imessage: drop reply context (mode=${contextVisibilityMode}, sender_allowed=${replySenderAllowed ? "yes" : "no"})`);
|
|
1707
1600
|
const historyKey = isGroup ? String(chatId ?? chatGuid ?? chatIdentifier ?? "unknown") : void 0;
|
|
1708
1601
|
const mentioned = isGroup ? matchesMentionPatterns(messageText, mentionRegexes) : true;
|
|
1709
1602
|
const { requireMention, implicitMentionKinds, enforceMentionRequirement } = await resolveIMessageInboundMentionPolicy({
|
|
@@ -1712,14 +1605,13 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1712
1605
|
groupId,
|
|
1713
1606
|
isGroup,
|
|
1714
1607
|
message: params.message,
|
|
1715
|
-
requireMentionOverride: params.opts?.requireMention
|
|
1716
|
-
isKnownFromMeMessageId: params.isKnownFromMeMessageId
|
|
1608
|
+
requireMentionOverride: params.opts?.requireMention
|
|
1717
1609
|
});
|
|
1718
1610
|
const canDetectMention = mentionRegexes.length > 0 || enforceMentionRequirement;
|
|
1719
1611
|
const commandAuthorized = commandAccess.authorized;
|
|
1720
1612
|
if (commandAccess.shouldBlockControlCommand) {
|
|
1721
|
-
|
|
1722
|
-
log:
|
|
1613
|
+
logInboundDrop({
|
|
1614
|
+
log: logVerbose,
|
|
1723
1615
|
channel: "imessage",
|
|
1724
1616
|
reason: "control command (unauthorized)",
|
|
1725
1617
|
target: sender
|
|
@@ -1746,7 +1638,7 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1746
1638
|
});
|
|
1747
1639
|
const effectiveWasMentioned = mentionDecision.effectiveWasMentioned;
|
|
1748
1640
|
if (isGroup && requireMention && canDetectMention && mentionDecision.shouldSkip) {
|
|
1749
|
-
|
|
1641
|
+
logVerbose(`imessage: skipping group message (no mention)`);
|
|
1750
1642
|
createChannelHistoryWindow({ historyMap: params.groupHistories }).record({
|
|
1751
1643
|
historyKey: historyKey ?? "",
|
|
1752
1644
|
limit: params.historyLimit,
|
|
@@ -1789,7 +1681,7 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1789
1681
|
};
|
|
1790
1682
|
}
|
|
1791
1683
|
async function buildIMessageInboundContext(params) {
|
|
1792
|
-
const envelopeOptions =
|
|
1684
|
+
const envelopeOptions = resolveEnvelopeFormatOptions(params.cfg);
|
|
1793
1685
|
const { decision } = params;
|
|
1794
1686
|
const chatId = decision.chatId;
|
|
1795
1687
|
const chatTarget = decision.isGroup && chatId != null ? formatIMessageChatTarget(chatId) : void 0;
|
|
@@ -1926,8 +1818,7 @@ async function buildIMessageInboundContext(params) {
|
|
|
1926
1818
|
inboundHistory
|
|
1927
1819
|
};
|
|
1928
1820
|
}
|
|
1929
|
-
|
|
1930
|
-
//#region extensions/imessage/src/monitor/ingress.ts
|
|
1821
|
+
// extensions/imessage/src/monitor/ingress.ts
|
|
1931
1822
|
const IMESSAGE_INGRESS_PAYLOAD_VERSION = 1;
|
|
1932
1823
|
const IMESSAGE_INGRESS_DRAIN_INTERVAL_MS = 1e3;
|
|
1933
1824
|
const IMessageIngressPayloadError = createChannelIngressError("IMessageIngressPayloadError");
|
|
@@ -1999,7 +1890,6 @@ function resolveIMessageIngressNonRetryableFailure(error) {
|
|
|
1999
1890
|
}
|
|
2000
1891
|
function createIMessageDurableIngress(options) {
|
|
2001
1892
|
const queue = options.queue ?? getIMessageRuntime().state.openChannelIngressQueue({ accountId: options.accountId });
|
|
2002
|
-
const now = options.now ?? Date.now;
|
|
2003
1893
|
const dispatchAdmissionQueue = new KeyedAsyncQueue();
|
|
2004
1894
|
const priorityDispatchQueue = new KeyedAsyncQueue();
|
|
2005
1895
|
const monitor = createChannelIngressMonitor({
|
|
@@ -2056,7 +1946,6 @@ function createIMessageDurableIngress(options) {
|
|
|
2056
1946
|
resolveNonRetryableFailure: resolveIMessageIngressNonRetryableFailure,
|
|
2057
1947
|
onLog: (message) => options.runtime.log?.(`imessage ${message}`)
|
|
2058
1948
|
},
|
|
2059
|
-
now,
|
|
2060
1949
|
onError: (error) => options.runtime.error?.(`imessage: ingress drain failed: ${formatErrorMessage(error)}`)
|
|
2061
1950
|
});
|
|
2062
1951
|
let stopTask;
|
|
@@ -2078,8 +1967,7 @@ function createIMessageDurableIngress(options) {
|
|
|
2078
1967
|
waitForIdle: monitor.waitForIdle
|
|
2079
1968
|
};
|
|
2080
1969
|
}
|
|
2081
|
-
|
|
2082
|
-
//#region extensions/imessage/src/monitor/loop-rate-limiter.ts
|
|
1970
|
+
// extensions/imessage/src/monitor/loop-rate-limiter.ts
|
|
2083
1971
|
/**
|
|
2084
1972
|
* Per-conversation rate limiter that detects rapid-fire identical echo
|
|
2085
1973
|
* patterns and suppresses them before they amplify into queue overflow.
|
|
@@ -2121,8 +2009,7 @@ function createLoopRateLimiter() {
|
|
|
2121
2009
|
}
|
|
2122
2010
|
};
|
|
2123
2011
|
}
|
|
2124
|
-
|
|
2125
|
-
//#region extensions/imessage/src/monitor/media-staging.ts
|
|
2012
|
+
// extensions/imessage/src/monitor/media-staging.ts
|
|
2126
2013
|
function createTypeOnlyIMessageAttachment(attachment) {
|
|
2127
2014
|
const contentType = attachment.mime_type?.trim() || void 0;
|
|
2128
2015
|
return {
|
|
@@ -2172,7 +2059,7 @@ async function readAttachmentBuffer(params) {
|
|
|
2172
2059
|
originalFilename: `${path.parse(params.attachmentPath).name || "imessage-attachment"}.jpg`
|
|
2173
2060
|
};
|
|
2174
2061
|
} catch (err) {
|
|
2175
|
-
|
|
2062
|
+
logVerbose(`imessage: HEIC attachment conversion failed; staging original instead: ${String(err)}`);
|
|
2176
2063
|
}
|
|
2177
2064
|
return {
|
|
2178
2065
|
buffer,
|
|
@@ -2181,7 +2068,6 @@ async function readAttachmentBuffer(params) {
|
|
|
2181
2068
|
};
|
|
2182
2069
|
}
|
|
2183
2070
|
async function stageIMessageAttachments(attachments, params) {
|
|
2184
|
-
const deps = params.deps ?? {};
|
|
2185
2071
|
const staged = [];
|
|
2186
2072
|
let unavailableCount = 0;
|
|
2187
2073
|
for (const attachment of attachments) {
|
|
@@ -2196,8 +2082,7 @@ async function stageIMessageAttachments(attachments, params) {
|
|
|
2196
2082
|
attachmentPath,
|
|
2197
2083
|
mimeType: attachment.mime_type,
|
|
2198
2084
|
maxBytes: params.maxBytes,
|
|
2199
|
-
allowedRoots: params.allowedRoots
|
|
2200
|
-
deps
|
|
2085
|
+
allowedRoots: params.allowedRoots
|
|
2201
2086
|
});
|
|
2202
2087
|
const saved = await saveMediaBuffer(media.buffer, media.contentType, "inbound", params.maxBytes, media.originalFilename);
|
|
2203
2088
|
const contentType = saved.contentType ?? media.contentType;
|
|
@@ -2209,7 +2094,7 @@ async function stageIMessageAttachments(attachments, params) {
|
|
|
2209
2094
|
} catch (err) {
|
|
2210
2095
|
unavailableCount += 1;
|
|
2211
2096
|
staged.push(createTypeOnlyIMessageAttachment(attachment));
|
|
2212
|
-
|
|
2097
|
+
logVerbose(`imessage: failed to stage inbound attachment: ${String(err)}`);
|
|
2213
2098
|
}
|
|
2214
2099
|
}
|
|
2215
2100
|
return {
|
|
@@ -2217,14 +2102,12 @@ async function stageIMessageAttachments(attachments, params) {
|
|
|
2217
2102
|
unavailableCount
|
|
2218
2103
|
};
|
|
2219
2104
|
}
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
function createPollCommentFolder(options) {
|
|
2224
|
-
const windowMs = options?.windowMs ?? DEFAULT_COMMENT_WINDOW_MS;
|
|
2105
|
+
// extensions/imessage/src/monitor/poll-comment.ts
|
|
2106
|
+
const COMMENT_WINDOW_MS = 15e3;
|
|
2107
|
+
function createPollCommentFolder() {
|
|
2225
2108
|
const seenPolls = /* @__PURE__ */ new Map();
|
|
2226
2109
|
function prune(referenceMs) {
|
|
2227
|
-
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);
|
|
2228
2111
|
}
|
|
2229
2112
|
return {
|
|
2230
2113
|
rememberPoll(guid, atMs, sender) {
|
|
@@ -2240,14 +2123,13 @@ function createPollCommentFolder(options) {
|
|
|
2240
2123
|
const key = replyToGuid?.trim();
|
|
2241
2124
|
if (!key || !Number.isFinite(atMs)) return false;
|
|
2242
2125
|
const seen = seenPolls.get(key);
|
|
2243
|
-
if (!seen || atMs < seen.atMs || atMs - seen.atMs >
|
|
2126
|
+
if (!seen || atMs < seen.atMs || atMs - seen.atMs > COMMENT_WINDOW_MS) return false;
|
|
2244
2127
|
const replySender = normalizeLowercaseStringOrEmpty(sender);
|
|
2245
2128
|
return seen.sender.length > 0 && replySender.length > 0 && seen.sender === replySender;
|
|
2246
2129
|
}
|
|
2247
2130
|
};
|
|
2248
2131
|
}
|
|
2249
|
-
|
|
2250
|
-
//#region extensions/imessage/src/monitor/poll-render.ts
|
|
2132
|
+
// extensions/imessage/src/monitor/poll-render.ts
|
|
2251
2133
|
function renderIMessagePollBody(poll, sender, renderOptions) {
|
|
2252
2134
|
const options = poll.options ?? [];
|
|
2253
2135
|
if (poll.kind === "vote" || poll.vote && options.length === 0) {
|
|
@@ -2288,17 +2170,15 @@ function renderIMessagePollBody(poll, sender, renderOptions) {
|
|
|
2288
2170
|
const selector = renderOptions?.preferOptionId ? "pollOptionId = the stable option id shown above" : "pollOptionIndex = the option number";
|
|
2289
2171
|
return `\u{1F4CA} Poll${question ? `: ${question}` : ""} — options: ${optionList}. Cast your vote on this poll with the poll-vote action (${selector}); do not answer in a text reply.`;
|
|
2290
2172
|
}
|
|
2291
|
-
|
|
2292
|
-
//#region extensions/imessage/src/monitor/reaction-system-event.ts
|
|
2173
|
+
// extensions/imessage/src/monitor/reaction-system-event.ts
|
|
2293
2174
|
function enqueueIMessageReactionSystemEvent(params) {
|
|
2294
2175
|
const { decision, runtime } = params;
|
|
2295
2176
|
const queued = enqueueRoutedSystemEvent(decision.text, decision.route, { contextKey: decision.contextKey });
|
|
2296
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}`);
|
|
2297
|
-
|
|
2178
|
+
logVerbose("imessage: reaction event enqueued");
|
|
2298
2179
|
return queued;
|
|
2299
2180
|
}
|
|
2300
|
-
|
|
2301
|
-
//#region extensions/imessage/src/monitor/recovery-cursor.ts
|
|
2181
|
+
// extensions/imessage/src/monitor/recovery-cursor.ts
|
|
2302
2182
|
const RECOVERY_CURSOR_STORE_OPTIONS = {
|
|
2303
2183
|
namespace: "imessage.recovery-cursor",
|
|
2304
2184
|
maxEntries: 64
|
|
@@ -2358,36 +2238,28 @@ function applyRecoveryCursorUpdate(key, update) {
|
|
|
2358
2238
|
return write;
|
|
2359
2239
|
}
|
|
2360
2240
|
async function readRecoveryCursor(accountId, dbIdentity) {
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
return adopted && Number.isFinite(adopted.lastRowid) ? adopted.lastRowid : null;
|
|
2371
|
-
}
|
|
2372
|
-
return null;
|
|
2373
|
-
} catch {
|
|
2374
|
-
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;
|
|
2375
2250
|
}
|
|
2251
|
+
return null;
|
|
2376
2252
|
}
|
|
2377
2253
|
async function migrateLegacyCatchupCursor(accountId, dbIdentity) {
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
return rowid;
|
|
2388
|
-
} catch {
|
|
2389
|
-
return null;
|
|
2390
|
-
}
|
|
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;
|
|
2391
2263
|
}
|
|
2392
2264
|
async function reconcileRecoveryCursorToWatermark(accountId, dbIdentity, cursorRowid, watermarkRowid) {
|
|
2393
2265
|
if (cursorRowid === null || watermarkRowid === null || cursorRowid <= watermarkRowid) return cursorRowid;
|
|
@@ -2410,7 +2282,6 @@ async function loadIMessageRecoveryCursor(accountId, dbIdentity, options = {}) {
|
|
|
2410
2282
|
const watermarkRowid = typeof options.watermarkRowid === "number" && Number.isFinite(options.watermarkRowid) ? options.watermarkRowid : null;
|
|
2411
2283
|
const current = await readRecoveryCursor(accountId, dbIdentity);
|
|
2412
2284
|
if (current !== null) return await reconcileRecoveryCursorToWatermark(accountId, dbIdentity, current, watermarkRowid);
|
|
2413
|
-
if (options.migrateLegacyCatchup === false) return null;
|
|
2414
2285
|
return await reconcileRecoveryCursorToWatermark(accountId, dbIdentity, await migrateLegacyCatchupCursor(accountId, dbIdentity), watermarkRowid);
|
|
2415
2286
|
}
|
|
2416
2287
|
/** Advance the cursor forward to `rowid` (monotonic per database; never rewinds). */
|
|
@@ -2423,11 +2294,9 @@ async function advanceIMessageRecoveryCursor(accountId, dbIdentity, rowid) {
|
|
|
2423
2294
|
});
|
|
2424
2295
|
} catch {}
|
|
2425
2296
|
}
|
|
2426
|
-
|
|
2427
|
-
//#region extensions/imessage/src/monitor/self-chat-cache.ts
|
|
2297
|
+
// extensions/imessage/src/monitor/self-chat-cache.ts
|
|
2428
2298
|
const SELF_CHAT_TTL_MS = 1e4;
|
|
2429
2299
|
const SELF_CHAT_CREATED_AT_TOLERANCE_MS = 1e3;
|
|
2430
|
-
const MAX_SELF_CHAT_CACHE_ENTRIES = 512;
|
|
2431
2300
|
const CLEANUP_MIN_INTERVAL_MS = 1e3;
|
|
2432
2301
|
function normalizeText(text) {
|
|
2433
2302
|
if (!text) return null;
|
|
@@ -2437,87 +2306,62 @@ function normalizeText(text) {
|
|
|
2437
2306
|
function isUsableTimestamp(createdAt) {
|
|
2438
2307
|
return typeof createdAt === "number" && Number.isFinite(createdAt);
|
|
2439
2308
|
}
|
|
2440
|
-
function digestText(text) {
|
|
2441
|
-
return createHash("sha256").update(text).digest("hex");
|
|
2442
|
-
}
|
|
2443
2309
|
function buildScope(parts) {
|
|
2444
2310
|
if (!parts.isGroup) return `${parts.accountId}:imessage:${parts.sender}`;
|
|
2445
2311
|
const chatTarget = formatIMessageChatTarget(parts.chatId) || "chat_id:unknown";
|
|
2446
2312
|
return `${parts.accountId}:${chatTarget}:imessage:${parts.sender}`;
|
|
2447
2313
|
}
|
|
2448
|
-
var
|
|
2314
|
+
var SelfChatCache = class {
|
|
2449
2315
|
constructor() {
|
|
2450
|
-
this.
|
|
2451
|
-
this.insertionOrder = /* @__PURE__ */ new Map();
|
|
2316
|
+
this.entries = [];
|
|
2452
2317
|
this.lastCleanupAt = 0;
|
|
2453
2318
|
}
|
|
2454
2319
|
buildBucketKey(lookup) {
|
|
2455
2320
|
const text = normalizeText(lookup.text);
|
|
2456
2321
|
if (!text) return null;
|
|
2457
|
-
return `${buildScope(lookup)}:${
|
|
2322
|
+
return `${buildScope(lookup)}:${sha256Hex(text)}`;
|
|
2458
2323
|
}
|
|
2459
2324
|
remember(lookup) {
|
|
2460
2325
|
const key = this.buildBucketKey(lookup);
|
|
2461
2326
|
if (!key || !isUsableTimestamp(lookup.createdAt)) return;
|
|
2462
|
-
|
|
2463
|
-
|
|
2327
|
+
this.entries.push({
|
|
2328
|
+
key,
|
|
2464
2329
|
createdAt: lookup.createdAt,
|
|
2465
2330
|
createdAtSkewToleranceMs: lookup.allowCreatedAtSkew ? SELF_CHAT_CREATED_AT_TOLERANCE_MS : 0,
|
|
2466
2331
|
rememberedAt: Date.now()
|
|
2467
|
-
};
|
|
2468
|
-
entries.add(entry);
|
|
2469
|
-
this.cache.set(key, entries);
|
|
2470
|
-
this.insertionOrder.set(entry, key);
|
|
2332
|
+
});
|
|
2471
2333
|
this.maybeCleanup();
|
|
2472
2334
|
}
|
|
2473
2335
|
has(lookup) {
|
|
2474
2336
|
this.maybeCleanup();
|
|
2475
2337
|
const key = this.buildBucketKey(lookup);
|
|
2476
2338
|
if (!key || !isUsableTimestamp(lookup.createdAt)) return false;
|
|
2477
|
-
const entries = this.cache.get(key);
|
|
2478
|
-
if (!entries) return false;
|
|
2479
2339
|
const now = Date.now();
|
|
2480
2340
|
const createdAt = lookup.createdAt;
|
|
2481
|
-
return
|
|
2341
|
+
return this.entries.some((entry) => {
|
|
2482
2342
|
const createdAtDelta = Math.abs(entry.createdAt - createdAt);
|
|
2483
|
-
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);
|
|
2484
2344
|
});
|
|
2485
2345
|
}
|
|
2486
|
-
removeEntry(entry, key) {
|
|
2487
|
-
this.insertionOrder.delete(entry);
|
|
2488
|
-
const entries = this.cache.get(key);
|
|
2489
|
-
entries?.delete(entry);
|
|
2490
|
-
if (entries?.size === 0) this.cache.delete(key);
|
|
2491
|
-
}
|
|
2492
2346
|
maybeCleanup() {
|
|
2493
2347
|
const now = Date.now();
|
|
2494
2348
|
if (now - this.lastCleanupAt < CLEANUP_MIN_INTERVAL_MS) return;
|
|
2495
2349
|
this.lastCleanupAt = now;
|
|
2496
|
-
|
|
2497
|
-
while (this.insertionOrder.size > MAX_SELF_CHAT_CACHE_ENTRIES) {
|
|
2498
|
-
const [entry, key] = expectDefined(this.insertionOrder.entries().next().value, "oldest iMessage self-chat cache entry");
|
|
2499
|
-
this.removeEntry(entry, key);
|
|
2500
|
-
}
|
|
2350
|
+
this.entries = this.entries.filter((entry) => now - entry.rememberedAt <= SELF_CHAT_TTL_MS).slice(-512);
|
|
2501
2351
|
}
|
|
2502
2352
|
};
|
|
2503
|
-
|
|
2504
|
-
return new DefaultSelfChatCache();
|
|
2505
|
-
}
|
|
2506
|
-
//#endregion
|
|
2507
|
-
//#region extensions/imessage/src/monitor/watch-error-log.ts
|
|
2508
|
-
const MAX_WATCH_ERROR_MESSAGE_CHARS = 200;
|
|
2353
|
+
// extensions/imessage/src/monitor/watch-error-log.ts
|
|
2509
2354
|
function sanitizeIMessageWatchErrorPayload(payload) {
|
|
2510
2355
|
if (!isRecord(payload)) return {};
|
|
2511
2356
|
const safe = {};
|
|
2512
2357
|
if (typeof payload.code === "number" && Number.isFinite(payload.code)) safe.code = payload.code;
|
|
2513
2358
|
if (typeof payload.message === "string") {
|
|
2514
|
-
const
|
|
2515
|
-
if (
|
|
2359
|
+
const guidance = normalizeIMessageFullDiskAccessError(payload.message);
|
|
2360
|
+
if (guidance) safe.message = guidance;
|
|
2516
2361
|
}
|
|
2517
2362
|
return safe;
|
|
2518
2363
|
}
|
|
2519
|
-
|
|
2520
|
-
//#region extensions/imessage/src/monitor/monitor-provider.ts
|
|
2364
|
+
// extensions/imessage/src/monitor/monitor-provider.ts
|
|
2521
2365
|
const WATCH_SUBSCRIBE_MAX_ATTEMPTS = 3;
|
|
2522
2366
|
const WATCH_SUBSCRIBE_RETRY_DELAY_MS = 1e3;
|
|
2523
2367
|
const CHANNEL_APPROVAL_GATEWAY_RUNTIME_CONTEXT_CAPABILITY = "approval.gateway";
|
|
@@ -2576,7 +2420,7 @@ async function monitorIMessageProvider(opts) {
|
|
|
2576
2420
|
const historyLimit = resolvePromptHistoryLimit(imessageCfg.historyLimit ?? cfg.messages?.groupChat?.historyLimit);
|
|
2577
2421
|
const groupHistories = /* @__PURE__ */ new Map();
|
|
2578
2422
|
const sentMessageCache = createSentMessageCache();
|
|
2579
|
-
const selfChatCache =
|
|
2423
|
+
const selfChatCache = new SelfChatCache();
|
|
2580
2424
|
const loopRateLimiter = createLoopRateLimiter();
|
|
2581
2425
|
const textLimit = resolveTextChunkLimit(cfg, "imessage", accountInfo.accountId);
|
|
2582
2426
|
const allowFrom = normalizeStringEntries(imessageCfg.allowFrom);
|
|
@@ -2640,7 +2484,10 @@ async function monitorIMessageProvider(opts) {
|
|
|
2640
2484
|
error
|
|
2641
2485
|
});
|
|
2642
2486
|
let staleBacklogSuppressed = 0;
|
|
2643
|
-
const loggedThrottledDropDiagnostics =
|
|
2487
|
+
const loggedThrottledDropDiagnostics = createDedupeCache({
|
|
2488
|
+
maxSize: 512,
|
|
2489
|
+
ttlMs: 0
|
|
2490
|
+
});
|
|
2644
2491
|
const watchSourceDbPath = resolveIMessageChatDbLookupPath({
|
|
2645
2492
|
cliPath,
|
|
2646
2493
|
dbPath,
|
|
@@ -2652,10 +2499,7 @@ async function monitorIMessageProvider(opts) {
|
|
|
2652
2499
|
dbPath,
|
|
2653
2500
|
remoteHost
|
|
2654
2501
|
});
|
|
2655
|
-
const recoveryCursorRowid = await loadIMessageRecoveryCursor(accountInfo.accountId, recoveryCursorDbIdentity, {
|
|
2656
|
-
migrateLegacyCatchup: !catchupCfg.enabled,
|
|
2657
|
-
watermarkRowid: recoveryBoundaryRowid
|
|
2658
|
-
});
|
|
2502
|
+
const recoveryCursorRowid = catchupCfg.enabled ? null : await loadIMessageRecoveryCursor(accountInfo.accountId, recoveryCursorDbIdentity, { watermarkRowid: recoveryBoundaryRowid });
|
|
2659
2503
|
const reconciledWatchSinceRowid = catchupCfg.enabled ? null : recoveryCursorRowid !== null ? recoveryBoundaryRowid !== null ? Math.max(recoveryCursorRowid, recoveryBoundaryRowid - 500) : recoveryCursorRowid : recoveryBoundaryRowid;
|
|
2660
2504
|
const watchSinceRowid = reconciledWatchSinceRowid === 0 ? -1 : reconciledWatchSinceRowid;
|
|
2661
2505
|
let latestAdvancedRecoveryCursorRowid = recoveryCursorRowid ?? -1;
|
|
@@ -2718,14 +2562,7 @@ async function monitorIMessageProvider(opts) {
|
|
|
2718
2562
|
await abandon();
|
|
2719
2563
|
return;
|
|
2720
2564
|
}
|
|
2721
|
-
|
|
2722
|
-
if (entries.length > 1 && shouldLogVerbose()) {
|
|
2723
|
-
const text = combined.text ?? "";
|
|
2724
|
-
const preview = sliceUtf16Safe(text, 0, 50);
|
|
2725
|
-
const ellipsis = text.length > 50 ? "..." : "";
|
|
2726
|
-
logVerbose(`[imessage] merged ${entries.length} debounced messages: "${preview}${ellipsis}"`);
|
|
2727
|
-
}
|
|
2728
|
-
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);
|
|
2729
2566
|
await settle();
|
|
2730
2567
|
} catch (err) {
|
|
2731
2568
|
await abandon();
|
|
@@ -2831,12 +2668,6 @@ async function monitorIMessageProvider(opts) {
|
|
|
2831
2668
|
return;
|
|
2832
2669
|
}
|
|
2833
2670
|
const { messageText, bodyText, mediaFacts, mediaCandidates, rawMediaAttachments, effectiveAttachmentRoots } = resolveIMessageInboundBodyText(message);
|
|
2834
|
-
const storeAllowFrom = await readChannelAllowFromStore("imessage", process.env, accountInfo.accountId).catch(() => []);
|
|
2835
|
-
const isQuestionReaction = hasIMessageQuestionReactionTarget({
|
|
2836
|
-
accountId: accountInfo.accountId,
|
|
2837
|
-
message,
|
|
2838
|
-
bodyText
|
|
2839
|
-
});
|
|
2840
2671
|
const decision = await resolveIMessageInboundDecision({
|
|
2841
2672
|
cfg,
|
|
2842
2673
|
accountId: accountInfo.accountId,
|
|
@@ -2850,13 +2681,12 @@ async function monitorIMessageProvider(opts) {
|
|
|
2850
2681
|
allowLegacyConversationAllowFromForGroup,
|
|
2851
2682
|
groupPolicy,
|
|
2852
2683
|
dmPolicy,
|
|
2853
|
-
|
|
2684
|
+
readStoreAllowFrom: () => readChannelAllowFromStore("imessage", process.env, accountInfo.accountId),
|
|
2854
2685
|
historyLimit,
|
|
2855
2686
|
groupHistories,
|
|
2856
2687
|
echoCache: sentMessageCache,
|
|
2857
2688
|
selfChatCache,
|
|
2858
|
-
reactionNotifications:
|
|
2859
|
-
logVerbose
|
|
2689
|
+
reactionNotifications: imessageCfg.reactionNotifications
|
|
2860
2690
|
});
|
|
2861
2691
|
const chatId = message.chat_id ?? void 0;
|
|
2862
2692
|
const senderForKey = (message.sender ?? "").trim();
|
|
@@ -2923,18 +2753,9 @@ async function monitorIMessageProvider(opts) {
|
|
|
2923
2753
|
return;
|
|
2924
2754
|
}
|
|
2925
2755
|
if (decision.kind === "reaction") {
|
|
2926
|
-
if (await maybeResolveIMessageQuestionReaction({
|
|
2927
|
-
cfg,
|
|
2928
|
-
accountId: accountInfo.accountId,
|
|
2929
|
-
message,
|
|
2930
|
-
bodyText,
|
|
2931
|
-
senderId: decision.senderNormalized,
|
|
2932
|
-
logDebug: logVerbose
|
|
2933
|
-
})) return;
|
|
2934
2756
|
enqueueIMessageReactionSystemEvent({
|
|
2935
2757
|
decision,
|
|
2936
|
-
runtime
|
|
2937
|
-
logVerbose
|
|
2758
|
+
runtime
|
|
2938
2759
|
});
|
|
2939
2760
|
return;
|
|
2940
2761
|
}
|
|
@@ -2993,8 +2814,7 @@ async function monitorIMessageProvider(opts) {
|
|
|
2993
2814
|
unavailableCount: rawMediaAttachments.filter((attachment) => !attachment.path).length
|
|
2994
2815
|
} : await stageIMessageAttachments(mediaCandidates, {
|
|
2995
2816
|
maxBytes: mediaMaxBytes,
|
|
2996
|
-
allowedRoots: effectiveAttachmentRoots
|
|
2997
|
-
deps: { logVerbose }
|
|
2817
|
+
allowedRoots: effectiveAttachmentRoots
|
|
2998
2818
|
});
|
|
2999
2819
|
const mediaAttachments = staged.attachments;
|
|
3000
2820
|
const unavailableCount = staged.unavailableCount;
|
|
@@ -3019,8 +2839,7 @@ async function monitorIMessageProvider(opts) {
|
|
|
3019
2839
|
message,
|
|
3020
2840
|
senderNormalized: decision.senderNormalized,
|
|
3021
2841
|
limit: dmHistoryLimit,
|
|
3022
|
-
envelopeOptions: resolveEnvelopeFormatOptions(cfg)
|
|
3023
|
-
logVerbose
|
|
2842
|
+
envelopeOptions: resolveEnvelopeFormatOptions(cfg)
|
|
3024
2843
|
}) : void 0;
|
|
3025
2844
|
const pluginChannelRuntime = opts.channelRuntime;
|
|
3026
2845
|
const { ctxPayload, chatTarget, imessageTo } = await buildIMessageInboundContext({
|
|
@@ -3042,10 +2861,6 @@ async function monitorIMessageProvider(opts) {
|
|
|
3042
2861
|
allowFrom,
|
|
3043
2862
|
normalizeEntry: normalizeIMessageHandle
|
|
3044
2863
|
});
|
|
3045
|
-
if (shouldLogVerbose()) {
|
|
3046
|
-
const preview = truncateUtf16Safe(ctxPayload.Body ?? "", 200).replace(/\n/g, "\\n");
|
|
3047
|
-
logVerbose(`imessage inbound: chatId=${chatId ?? "unknown"} from=${ctxPayload.From} len=${(ctxPayload.Body ?? "").length} preview="${preview}"`);
|
|
3048
|
-
}
|
|
3049
2864
|
const sendReadReceipts = imessageCfg.sendReadReceipts !== false;
|
|
3050
2865
|
const typingTarget = ctxPayload.To;
|
|
3051
2866
|
const readTarget = !decision.isGroup && decision.chatGuid ? `chat_guid:${decision.chatGuid}` : typingTarget;
|
|
@@ -3211,14 +3026,13 @@ async function monitorIMessageProvider(opts) {
|
|
|
3211
3026
|
message,
|
|
3212
3027
|
gatewayRuntime: approvalGatewayRuntime
|
|
3213
3028
|
})) return true;
|
|
3214
|
-
return await
|
|
3029
|
+
return (await handleIMessageApprovalReaction({
|
|
3215
3030
|
cfg,
|
|
3216
3031
|
accountId: accountInfo.accountId,
|
|
3217
3032
|
message,
|
|
3218
3033
|
bodyText: resolveIMessageInboundBodyText(message).bodyText,
|
|
3219
|
-
gatewayRuntime: approvalGatewayRuntime
|
|
3220
|
-
|
|
3221
|
-
});
|
|
3034
|
+
gatewayRuntime: approvalGatewayRuntime
|
|
3035
|
+
})).handled;
|
|
3222
3036
|
};
|
|
3223
3037
|
const ingress = createIMessageDurableIngress({
|
|
3224
3038
|
accountId: accountInfo.accountId,
|
|
@@ -3385,8 +3199,7 @@ async function monitorIMessageProvider(opts) {
|
|
|
3385
3199
|
cfg,
|
|
3386
3200
|
accountId: accountInfo.accountId,
|
|
3387
3201
|
allowRecentChatDiscovery,
|
|
3388
|
-
gatewayRuntime: approvalGatewayRuntime
|
|
3389
|
-
logVerboseMessage: logVerbose
|
|
3202
|
+
gatewayRuntime: approvalGatewayRuntime
|
|
3390
3203
|
}).catch((err) => {
|
|
3391
3204
|
logVerbose(`imessage: approval reaction poll failed: ${String(err)}`);
|
|
3392
3205
|
});
|
|
@@ -3439,8 +3252,7 @@ async function monitorIMessageProvider(opts) {
|
|
|
3439
3252
|
await ingress.stop();
|
|
3440
3253
|
}
|
|
3441
3254
|
}
|
|
3442
|
-
|
|
3443
|
-
//#region extensions/imessage/src/channel.runtime.ts
|
|
3255
|
+
// extensions/imessage/src/channel.runtime.ts
|
|
3444
3256
|
async function sendIMessageOutbound(params) {
|
|
3445
3257
|
const send = resolveOutboundSendDep(params.deps, "imessage", { legacyKeys: IMESSAGE_LEGACY_OUTBOUND_SEND_DEP_KEYS }) ?? sendMessageIMessage;
|
|
3446
3258
|
const maxBytes = resolveChannelMediaMaxBytes({
|
|
@@ -3455,8 +3267,8 @@ async function sendIMessageOutbound(params) {
|
|
|
3455
3267
|
...params.mediaReadFile ? { mediaReadFile: params.mediaReadFile } : {},
|
|
3456
3268
|
...params.audioAsVoice ? { audioAsVoice: true } : {},
|
|
3457
3269
|
maxBytes,
|
|
3458
|
-
accountId: params.accountId
|
|
3459
|
-
replyToId: params.replyToId
|
|
3270
|
+
accountId: params.accountId,
|
|
3271
|
+
replyToId: params.replyToId,
|
|
3460
3272
|
conversationReadOrigin: params.conversationReadOrigin,
|
|
3461
3273
|
assertDirectAdapterHandoff: params.assertDirectAdapterHandoff,
|
|
3462
3274
|
onPlatformSendDispatch: params.onPlatformSendDispatch,
|
|
@@ -3520,5 +3332,4 @@ async function startIMessageGatewayAccount(ctx) {
|
|
|
3520
3332
|
statusSink
|
|
3521
3333
|
});
|
|
3522
3334
|
}
|
|
3523
|
-
|
|
3524
|
-
export { imessageSetupWizard, probeIMessageAccount, sendIMessageOutbound, startIMessageGatewayAccount };
|
|
3335
|
+
export { probeIMessageAccount, sendIMessageOutbound, startIMessageGatewayAccount };
|