@openclaw/imessage 2026.8.1-beta.2 → 2026.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{accounts-D00Njxg2.js → accounts-CCirBEIm.js} +4 -5
- package/dist/{actions.runtime-DTFKDN5p.js → actions.runtime-CAl3WcqD.js} +2 -3
- package/dist/api.js +6 -5
- package/dist/{approval-handler.runtime-BtLs4gho.js → approval-handler.runtime-UBd1j2Sg.js} +15 -9
- package/dist/approval-reactions-LL5yn1Rc.js +562 -0
- package/dist/{channel-lppmI51h.js → channel-DNI9DKGN.js} +26 -21
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-5wpCKQb7.js → channel.runtime-HSkrmWfW.js} +58 -11
- package/dist/{config-schema-DV0KP5nC.js → config-schema-DuGbA8Kp.js} +8 -7
- package/dist/contract-api.js +1 -1
- package/dist/doctor-contract-api.js +8 -281
- package/dist/{group-policy-DF9xSdik.js → group-policy-Dvc-BcWo.js} +19 -651
- package/dist/{media-contract-BEgdvYhW.js → media-contract-D-FhGE98.js} +1 -1
- package/dist/media-contract-api.js +1 -1
- package/dist/{message-tool-api-CPDfJJoE.js → message-tool-api-CvIgJpOo.js} +7 -4
- package/dist/message-tool-api.js +1 -1
- package/dist/{monitor-DnxJ8pW9.js → monitor-BywJn1QW.js} +343 -111
- package/dist/{question-reactions-DVe50UhB.js → question-reactions-viP7ZcyG.js} +2 -1
- package/dist/reaction-context-BAYI7pz0.js +105 -0
- package/dist/{remote-file-DLwO9ved.js → remote-file-6mCj2ZBy.js} +7 -3
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/runtime-Cza4CY5T.js +8 -0
- package/dist/runtime-api.js +10 -9
- package/dist/{sanitize-outbound-CoTZLXMw.js → sanitize-outbound-Du-r1lNQ.js} +345 -58
- package/dist/{send-BUXW76tw.js → send-mgWPKqSV.js} +147 -26
- package/dist/state-contract-4TK_cDDb.js +56 -0
- package/dist/state-migrations-SFdKp3GQ.js +277 -0
- package/node_modules/typebox/build/format/date.d.mts +1 -1
- package/node_modules/typebox/build/format/date.mjs +1 -1
- package/node_modules/typebox/build/format/date_time.d.mts +2 -2
- package/node_modules/typebox/build/format/date_time.mjs +3 -3
- package/node_modules/typebox/build/format/email.d.mts +1 -1
- package/node_modules/typebox/build/format/email.mjs +2 -2
- package/node_modules/typebox/build/format/hostname.mjs +2 -10
- package/node_modules/typebox/build/format/idn_email.d.mts +1 -1
- package/node_modules/typebox/build/format/idn_email.mjs +3 -3
- package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -0
- package/node_modules/typebox/build/format/idn_hostname.mjs +3 -11
- package/node_modules/typebox/build/format/idna/format/bidi.d.mts +3 -0
- package/node_modules/typebox/build/format/idna/format/bidi.mjs +98 -0
- package/node_modules/typebox/build/format/idna/format/puny.d.mts +3 -0
- package/node_modules/typebox/build/format/idna/format/puny.mjs +144 -0
- package/node_modules/typebox/build/format/idna/hostname.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/hostname.mjs +16 -0
- package/node_modules/typebox/build/format/idna/idn-hostname.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +23 -0
- package/node_modules/typebox/build/format/idna/index.d.mts +2 -0
- package/node_modules/typebox/build/format/idna/index.mjs +2 -0
- package/node_modules/typebox/build/format/idna/label/ascii.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/label/ascii.mjs +9 -0
- package/node_modules/typebox/build/format/idna/label/puny.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/label/puny.mjs +28 -0
- package/node_modules/typebox/build/format/idna/label/unicode.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/label/unicode.mjs +106 -0
- package/node_modules/typebox/build/format/idna/pattern/pattern.d.mts +30 -0
- package/node_modules/typebox/build/format/idna/pattern/pattern.mjs +70 -0
- package/node_modules/typebox/build/format/ipv4.d.mts +0 -1
- package/node_modules/typebox/build/format/ipv4.mjs +2 -32
- package/node_modules/typebox/build/format/ipv6.mjs +2 -61
- package/node_modules/typebox/build/format/iri.d.mts +2 -2
- package/node_modules/typebox/build/format/iri.mjs +26 -8
- package/node_modules/typebox/build/format/iri_reference.d.mts +2 -2
- package/node_modules/typebox/build/format/iri_reference.mjs +9 -55
- package/node_modules/typebox/build/format/regex.d.mts +1 -2
- package/node_modules/typebox/build/format/regex.mjs +2 -6
- package/node_modules/typebox/build/format/time.d.mts +2 -2
- package/node_modules/typebox/build/format/time.mjs +19 -14
- package/node_modules/typebox/build/format/uri.d.mts +1 -1
- package/node_modules/typebox/build/format/uri.mjs +3 -130
- package/node_modules/typebox/build/format/uri_reference.d.mts +1 -1
- package/node_modules/typebox/build/format/uri_reference.mjs +2 -3
- package/node_modules/typebox/build/format/uri_template.d.mts +2 -3
- package/node_modules/typebox/build/format/uri_template.mjs +4 -5
- package/node_modules/typebox/build/format/url.d.mts +3 -3
- package/node_modules/typebox/build/format/url.mjs +4 -5
- package/node_modules/typebox/build/guard/emit.d.mts +3 -1
- package/node_modules/typebox/build/guard/emit.mjs +12 -5
- package/node_modules/typebox/build/guard/guard.d.mts +8 -2
- package/node_modules/typebox/build/guard/guard.mjs +25 -4
- package/node_modules/typebox/build/guard/string.d.mts +2 -2
- package/node_modules/typebox/build/guard/string.mjs +7 -7
- package/node_modules/typebox/build/schema/engine/_context.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -2
- package/node_modules/typebox/build/schema/engine/_regexp.d.mts +2 -0
- package/node_modules/typebox/build/schema/engine/_regexp.mjs +4 -0
- package/node_modules/typebox/build/schema/engine/_stack.mjs +12 -13
- package/node_modules/typebox/build/schema/engine/additionalItems.mjs +17 -7
- package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +4 -3
- package/node_modules/typebox/build/schema/engine/contains.mjs +18 -6
- package/node_modules/typebox/build/schema/engine/enum.mjs +1 -1
- package/node_modules/typebox/build/schema/engine/items.mjs +26 -6
- package/node_modules/typebox/build/schema/engine/maxContains.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/minContains.mjs +14 -4
- package/node_modules/typebox/build/schema/engine/not.mjs +4 -2
- package/node_modules/typebox/build/schema/engine/oneOf.mjs +7 -6
- package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -2
- package/node_modules/typebox/build/schema/engine/patternProperties.mjs +4 -3
- package/node_modules/typebox/build/schema/engine/ref.mjs +1 -7
- package/node_modules/typebox/build/schema/engine/schema.mjs +13 -4
- package/node_modules/typebox/build/schema/engine/type.mjs +1 -1
- package/node_modules/typebox/build/schema/resolve/resolve.d.mts +4 -1
- package/node_modules/typebox/build/schema/resolve/resolve.mjs +202 -1
- package/node_modules/typebox/build/schema/static/propertyNames.d.mts +5 -0
- package/node_modules/typebox/build/schema/static/propertyNames.mjs +3 -0
- package/node_modules/typebox/build/schema/static/schema.d.mts +3 -0
- package/node_modules/typebox/build/schema/types/index.d.mts +0 -2
- package/node_modules/typebox/build/schema/types/index.mjs +0 -2
- package/node_modules/typebox/build/system/memory/clone.mjs +14 -11
- package/node_modules/typebox/build/system/memory/discard.mjs +6 -5
- package/node_modules/typebox/build/system/settings/settings.d.mts +9 -0
- package/node_modules/typebox/build/system/settings/settings.mjs +2 -0
- package/node_modules/typebox/build/type/engine/call/distribute_arguments.d.mts +6 -1
- package/node_modules/typebox/build/type/engine/call/distribute_arguments.mjs +16 -3
- package/node_modules/typebox/build/type/engine/call/instantiate.mjs +34 -4
- package/node_modules/typebox/build/type/engine/evaluate/broaden.d.mts +5 -5
- package/node_modules/typebox/build/type/engine/evaluate/broaden.mjs +20 -26
- package/node_modules/typebox/build/type/engine/evaluate/compare.d.mts +6 -10
- package/node_modules/typebox/build/type/engine/evaluate/compare.mjs +9 -13
- package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +7 -4
- package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +12 -19
- package/node_modules/typebox/build/type/engine/evaluate/distribute.d.mts +4 -20
- package/node_modules/typebox/build/type/engine/evaluate/distribute.mjs +8 -20
- package/node_modules/typebox/build/type/engine/evaluate/evaluate.d.mts +4 -4
- package/node_modules/typebox/build/type/engine/evaluate/evaluate.mjs +11 -8
- package/node_modules/typebox/build/type/engine/evaluate/flatten.d.mts +1 -1
- package/node_modules/typebox/build/type/engine/evaluate/flatten.mjs +3 -4
- package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +17 -2
- package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +26 -6
- package/node_modules/typebox/build/type/engine/exclude/operation.mjs +3 -4
- package/node_modules/typebox/build/type/engine/extract/operation.d.mts +1 -1
- package/node_modules/typebox/build/type/engine/extract/operation.mjs +3 -4
- package/node_modules/typebox/build/type/engine/priority/priority.d.mts +2 -2
- package/node_modules/typebox/build/type/engine/priority/priority.mjs +3 -4
- package/node_modules/typebox/build/type/script/mapping.d.mts +32 -29
- package/node_modules/typebox/build/type/script/mapping.mjs +18 -25
- package/node_modules/typebox/build/type/script/parser.d.mts +19 -25
- package/node_modules/typebox/build/type/script/parser.mjs +27 -23
- package/node_modules/typebox/build/value/errors/errors.d.mts +2 -2
- package/node_modules/typebox/build/value/errors/errors.mjs +1 -1
- package/node_modules/typebox/package.json +27 -27
- package/node_modules/typebox/readme.md +79 -82
- package/openclaw.plugin.json +0 -6
- package/package.json +5 -5
- package/dist/catchup-D32oYNxR.js +0 -294
- package/dist/monitor-reply-cache-DKlYGd5x.js +0 -348
- package/dist/persisted-echo-cache-BPWGUK-L.js +0 -143
- package/node_modules/typebox/build/format/_idna.d.mts +0 -2
- package/node_modules/typebox/build/format/_idna.mjs +0 -246
- package/node_modules/typebox/build/format/_puny.d.mts +0 -1
- package/node_modules/typebox/build/format/_puny.mjs +0 -77
- package/node_modules/typebox/build/schema/resolve/ref.d.mts +0 -3
- package/node_modules/typebox/build/schema/resolve/ref.mjs +0 -146
- package/node_modules/typebox/build/schema/types/contentEncoding.d.mts +0 -9
- package/node_modules/typebox/build/schema/types/contentEncoding.mjs +0 -13
- package/node_modules/typebox/build/schema/types/contentMediaType.d.mts +0 -9
- package/node_modules/typebox/build/schema/types/contentMediaType.mjs +0 -13
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
1
|
+
import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount } from "./accounts-CCirBEIm.js";
|
|
2
|
+
import { c as parseIMessageAllowTarget, f as getCachedIMessagePrivateApiStatus, i as isAllowedIMessageReplyContextSender, l as parseIMessageTarget, n as formatIMessageChatTarget, p as imessageRpcSupportsMethod, s as normalizeIMessageHandle } from "./message-tool-api-CvIgJpOo.js";
|
|
3
|
+
import { l as isKnownFromMeIMessageMessageId, o as createIMessageRpcClient, r as sanitizeOutboundText, u as rememberIMessageReplyCache, v as resolveIMessageDirectChatService } from "./sanitize-outbound-Du-r1lNQ.js";
|
|
4
|
+
import { f as probeIMessage, r as resolveIMessageInboundConversationId } from "./group-policy-Dvc-BcWo.js";
|
|
5
|
+
import { t as getIMessageRuntime } from "./runtime-Cza4CY5T.js";
|
|
6
|
+
import { d as resolveIMessageEchoMediaKey, l as capFailureRetriesMap, t as IMESSAGE_CATCHUP_CURSOR_NAMESPACE, u as resolveIMessageCatchupCursorKey } from "./state-contract-4TK_cDDb.js";
|
|
7
|
+
import { n as resolveIMessageAttachmentRoots, r as resolveIMessageRemoteAttachmentRoots } from "./media-contract-D-FhGE98.js";
|
|
8
|
+
import { i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction, s as buildIMessageApprovalConversationKeyForInbound } from "./approval-reactions-LL5yn1Rc.js";
|
|
9
|
+
import { c as iMessageApprovalControlBindings, n as hasPersistedIMessageEcho, r as stripLeadingEchoTextCorruptionMarkers, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-mgWPKqSV.js";
|
|
10
|
+
import { t as resolveIMessageReactionContext } from "./reaction-context-BAYI7pz0.js";
|
|
11
|
+
import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-viP7ZcyG.js";
|
|
11
12
|
import { bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
12
13
|
import { chunkMarkdownTextWithMode as chunkTextWithMode, resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
|
|
13
14
|
import { resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
|
|
@@ -16,34 +17,34 @@ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
|
16
17
|
import { asSafeIntegerInRange, hasNonEmptyString, isRecord, normalizeOptionalString, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
17
18
|
import fs from "node:fs/promises";
|
|
18
19
|
import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, warn } from "openclaw/plugin-sdk/runtime-env";
|
|
19
|
-
import {
|
|
20
|
+
import { isInboundPathAllowed, kindFromMime } from "openclaw/plugin-sdk/media-runtime";
|
|
21
|
+
import { asDateTimestampMs, asPositiveFiniteNumber, parseDateStringTimestampMs, resolveIntegerOption, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
20
22
|
import { createHash } from "node:crypto";
|
|
21
23
|
import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
22
24
|
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
23
25
|
import { collectErrorGraphCandidates, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
24
|
-
import { isInboundPathAllowed, kindFromMime } from "openclaw/plugin-sdk/media-runtime";
|
|
25
|
-
import { convertMarkdownTables as convertMarkdownTables$1, findCodeRegions, isInsideCode, sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
|
|
26
26
|
import { buildChannelGroupsScopeTree, resolveChannelGroupPolicy, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
|
27
|
+
import { convertMarkdownTables as convertMarkdownTables$1, findCodeRegions, isInsideCode, sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
|
|
27
28
|
import { buildChannelInboundEventContext, buildMentionRegexes, createChannelInboundDebouncer, createChannelPartialDeliveryError, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundFromLabel, formatInboundMediaUnavailableText, formatMediaPlaceholderText, isChannelPartialDeliveryError, logInboundDrop, matchesMentionPatterns, resolveEnvelopeFormatOptions, resolveInboundMentionDecision, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent, shouldDebounceTextInbound, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
|
|
28
|
-
import { openLocalFileSafely, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
29
|
-
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
30
29
|
import { resolveAgentConfig, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
|
|
31
30
|
import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-runtime";
|
|
32
31
|
import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
|
|
33
32
|
import { createChannelIngressResolver, defineStableChannelIngressIdentity, fanInChannelIngressLifecycles } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
34
33
|
import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
|
|
35
34
|
import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
|
|
36
|
-
import { readChannelAllowFromStore, resolveConfiguredBindingRoute, resolveRuntimeConversationBindingRoute, upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
|
|
35
|
+
import { ensureConfiguredBindingRouteReady, readChannelAllowFromStore, resolveConfiguredBindingRoute, resolveRuntimeConversationBindingRoute, upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
|
|
37
36
|
import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
38
37
|
import { redactIdentifier } from "openclaw/plugin-sdk/logging-core";
|
|
39
38
|
import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
40
39
|
import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
40
|
+
import { openLocalFileSafely, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
41
41
|
import { getSessionEntry, readSessionUpdatedAt, resolveSendPolicy, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
42
42
|
import { openNodeSqliteDatabase } from "openclaw/plugin-sdk/sqlite-runtime";
|
|
43
43
|
import { waitForTransportReady } from "openclaw/plugin-sdk/transport-ready-runtime";
|
|
44
44
|
import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
45
45
|
import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
46
46
|
import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
|
|
47
|
+
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
47
48
|
import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
48
49
|
import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
|
|
49
50
|
import { hasControlCommand } from "openclaw/plugin-sdk/command-auth-native";
|
|
@@ -55,8 +56,6 @@ import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
|
|
|
55
56
|
//#region extensions/imessage/src/approval-reaction-poller.ts
|
|
56
57
|
const RECENT_CHAT_LIMIT = 50;
|
|
57
58
|
const PER_CHAT_HISTORY_LIMIT = 30;
|
|
58
|
-
const OBSERVED_APPROVAL_PROMPT_TARGET_TTL_MS = 300 * 1e3;
|
|
59
|
-
const accountIdsWithCompletedNoTargetDiscovery = /* @__PURE__ */ new Set();
|
|
60
59
|
function normalizeChatId(value) {
|
|
61
60
|
return asPositiveFiniteNumber(value) ?? null;
|
|
62
61
|
}
|
|
@@ -150,42 +149,13 @@ function bindObservedConversation(params) {
|
|
|
150
149
|
ttlMs
|
|
151
150
|
});
|
|
152
151
|
}
|
|
153
|
-
function bindObservedApprovalPrompt(params) {
|
|
154
|
-
if (params.message.is_from_me !== true) return null;
|
|
155
|
-
const messageId = params.message.guid?.trim();
|
|
156
|
-
if (!messageId) return null;
|
|
157
|
-
const binding = extractIMessageApprovalPromptBinding(params.message.text ?? "");
|
|
158
|
-
if (!binding) return null;
|
|
159
|
-
const conversation = buildConversationKeyFromMessage(params.message);
|
|
160
|
-
const expiresAtMs = resolveExpiresAtMsFromDurationMs(OBSERVED_APPROVAL_PROMPT_TARGET_TTL_MS);
|
|
161
|
-
if (expiresAtMs === void 0) return null;
|
|
162
|
-
const target = {
|
|
163
|
-
accountId: params.accountId,
|
|
164
|
-
conversation,
|
|
165
|
-
messageId,
|
|
166
|
-
approvalId: binding.approvalId,
|
|
167
|
-
approvalKind: binding.approvalKind,
|
|
168
|
-
allowedDecisions: binding.allowedDecisions,
|
|
169
|
-
expiresAtMs
|
|
170
|
-
};
|
|
171
|
-
bindObservedConversation({
|
|
172
|
-
target,
|
|
173
|
-
message: params.message
|
|
174
|
-
});
|
|
175
|
-
return target;
|
|
176
|
-
}
|
|
177
152
|
async function pollPendingIMessageApprovalReactions(params) {
|
|
178
|
-
const targets = listPendingIMessageApprovalReactionPollTargets({ accountId: params.accountId });
|
|
179
|
-
|
|
180
|
-
if (targets.length === 0 && !shouldAttemptNoTargetDiscovery) return;
|
|
153
|
+
const targets = await listPendingIMessageApprovalReactionPollTargets({ accountId: params.accountId });
|
|
154
|
+
if (targets.length === 0) return;
|
|
181
155
|
const pendingByMessageId = buildPendingTargetsByMessageId(targets);
|
|
182
156
|
const explicitChatIds = listTargetChatIds(targets);
|
|
183
|
-
const chatIds = params.allowRecentChatDiscovery === true &&
|
|
184
|
-
if (chatIds.length === 0)
|
|
185
|
-
if (shouldAttemptNoTargetDiscovery) accountIdsWithCompletedNoTargetDiscovery.add(params.accountId);
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
let hadHistoryFetchError = false;
|
|
157
|
+
const chatIds = params.allowRecentChatDiscovery === true && targets.length > 0 && hasUnscopedTarget(targets) ? uniqueChatIds([...explicitChatIds, ...await listRecentChatIds(params.client)]) : explicitChatIds;
|
|
158
|
+
if (chatIds.length === 0) return;
|
|
189
159
|
for (const chatId of chatIds) {
|
|
190
160
|
let messages;
|
|
191
161
|
try {
|
|
@@ -194,17 +164,13 @@ async function pollPendingIMessageApprovalReactions(params) {
|
|
|
194
164
|
chatId
|
|
195
165
|
});
|
|
196
166
|
} catch (err) {
|
|
197
|
-
hadHistoryFetchError = true;
|
|
198
167
|
params.logVerboseMessage?.(`imessage: approval reaction poll skipped chat_id=${chatId}: ${String(err)}`);
|
|
199
168
|
continue;
|
|
200
169
|
}
|
|
201
170
|
for (const message of messages) {
|
|
202
171
|
const targetGuid = message.guid?.trim();
|
|
203
172
|
if (!targetGuid) continue;
|
|
204
|
-
const target = pendingByMessageId.get(targetGuid) ?? pendingByMessageId.get(normalizeMessageGuid(targetGuid))
|
|
205
|
-
accountId: params.accountId,
|
|
206
|
-
message
|
|
207
|
-
});
|
|
173
|
+
const target = pendingByMessageId.get(targetGuid) ?? pendingByMessageId.get(normalizeMessageGuid(targetGuid));
|
|
208
174
|
if (!target) continue;
|
|
209
175
|
bindObservedConversation({
|
|
210
176
|
target,
|
|
@@ -216,22 +182,17 @@ async function pollPendingIMessageApprovalReactions(params) {
|
|
|
216
182
|
reaction
|
|
217
183
|
});
|
|
218
184
|
if (!reactionPayload) continue;
|
|
219
|
-
|
|
185
|
+
if ((await handleIMessageApprovalReaction({
|
|
220
186
|
cfg: params.cfg,
|
|
221
187
|
accountId: params.accountId,
|
|
222
188
|
message: reactionPayload,
|
|
223
189
|
bodyText: reactionPayload.text ?? "",
|
|
224
190
|
gatewayRuntime: params.gatewayRuntime,
|
|
225
191
|
logVerboseMessage: params.logVerboseMessage
|
|
226
|
-
});
|
|
227
|
-
if (handled.stopPolling) {
|
|
228
|
-
if (shouldAttemptNoTargetDiscovery && handled.stopPollingReason !== "resolver-error") break;
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
192
|
+
})).stopPolling) return;
|
|
231
193
|
}
|
|
232
194
|
}
|
|
233
195
|
}
|
|
234
|
-
if (shouldAttemptNoTargetDiscovery && !hadHistoryFetchError) accountIdsWithCompletedNoTargetDiscovery.add(params.accountId);
|
|
235
196
|
}
|
|
236
197
|
//#endregion
|
|
237
198
|
//#region extensions/imessage/src/chat.ts
|
|
@@ -302,6 +263,269 @@ function attachIMessageMonitorAbortHandler(params) {
|
|
|
302
263
|
return () => abort.removeEventListener("abort", onAbort);
|
|
303
264
|
}
|
|
304
265
|
//#endregion
|
|
266
|
+
//#region extensions/imessage/src/monitor/catchup.ts
|
|
267
|
+
const DEFAULT_MAX_AGE_MINUTES = 120;
|
|
268
|
+
const MAX_MAX_AGE_MINUTES = 720;
|
|
269
|
+
const DEFAULT_PER_RUN_LIMIT = 50;
|
|
270
|
+
const MAX_PER_RUN_LIMIT = 500;
|
|
271
|
+
const DEFAULT_FIRST_RUN_LOOKBACK_MINUTES = 30;
|
|
272
|
+
const DEFAULT_MAX_FAILURE_RETRIES = 10;
|
|
273
|
+
const MAX_MAX_FAILURE_RETRIES = 1e3;
|
|
274
|
+
const cursorWriteQueue = new KeyedAsyncQueue();
|
|
275
|
+
function openCatchupCursorStore() {
|
|
276
|
+
return getIMessageRuntime().state.openSyncKeyedStore({
|
|
277
|
+
namespace: IMESSAGE_CATCHUP_CURSOR_NAMESPACE,
|
|
278
|
+
maxEntries: 256
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
function updateCatchupCursorStore(key, updateValue) {
|
|
282
|
+
const store = openCatchupCursorStore();
|
|
283
|
+
if (!store.update) throw new Error("iMessage catchup cursor persistence requires atomic plugin-state update.");
|
|
284
|
+
return store.update(key, updateValue);
|
|
285
|
+
}
|
|
286
|
+
function enqueueCursorWrite(accountId, fn) {
|
|
287
|
+
const key = resolveIMessageCatchupCursorKey(accountId);
|
|
288
|
+
return cursorWriteQueue.enqueue(key, fn);
|
|
289
|
+
}
|
|
290
|
+
function sanitizeFailureRetriesInput(raw) {
|
|
291
|
+
if (!raw || typeof raw !== "object") return {};
|
|
292
|
+
const out = {};
|
|
293
|
+
for (const [guid, count] of Object.entries(raw)) {
|
|
294
|
+
if (!guid || typeof guid !== "string") continue;
|
|
295
|
+
if (typeof count !== "number" || !Number.isFinite(count) || count <= 0) continue;
|
|
296
|
+
out[guid] = Math.floor(count);
|
|
297
|
+
}
|
|
298
|
+
return out;
|
|
299
|
+
}
|
|
300
|
+
function normalizeIMessageCatchupCursor(value) {
|
|
301
|
+
if (!value || typeof value !== "object") return null;
|
|
302
|
+
const raw = value;
|
|
303
|
+
if (typeof raw.lastSeenMs !== "number" || !Number.isFinite(raw.lastSeenMs)) return null;
|
|
304
|
+
if (typeof raw.lastSeenRowid !== "number" || !Number.isFinite(raw.lastSeenRowid)) return null;
|
|
305
|
+
const failureRetries = sanitizeFailureRetriesInput(raw.failureRetries);
|
|
306
|
+
const hasRetries = Object.keys(failureRetries).length > 0;
|
|
307
|
+
return {
|
|
308
|
+
lastSeenMs: raw.lastSeenMs,
|
|
309
|
+
lastSeenRowid: raw.lastSeenRowid,
|
|
310
|
+
updatedAt: typeof raw.updatedAt === "number" ? raw.updatedAt : 0,
|
|
311
|
+
...hasRetries ? { failureRetries } : {}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function readIMessageCatchupCursor(accountId) {
|
|
315
|
+
return normalizeIMessageCatchupCursor(openCatchupCursorStore().lookup(resolveIMessageCatchupCursorKey(accountId)));
|
|
316
|
+
}
|
|
317
|
+
async function loadIMessageCatchupCursor(accountId) {
|
|
318
|
+
return readIMessageCatchupCursor(accountId);
|
|
319
|
+
}
|
|
320
|
+
function buildIMessageCatchupCursor(next) {
|
|
321
|
+
const sanitized = sanitizeFailureRetriesInput(next.failureRetries);
|
|
322
|
+
const hasRetries = Object.keys(sanitized).length > 0;
|
|
323
|
+
return {
|
|
324
|
+
lastSeenMs: next.lastSeenMs,
|
|
325
|
+
lastSeenRowid: next.lastSeenRowid,
|
|
326
|
+
updatedAt: Date.now(),
|
|
327
|
+
...hasRetries ? { failureRetries: sanitized } : {}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
async function saveIMessageCatchupCursor(accountId, next, options = {}) {
|
|
331
|
+
const cursor = buildIMessageCatchupCursor(next);
|
|
332
|
+
updateCatchupCursorStore(resolveIMessageCatchupCursorKey(accountId), (existingValue) => {
|
|
333
|
+
const existing = normalizeIMessageCatchupCursor(existingValue);
|
|
334
|
+
if (existing && cursor.lastSeenRowid < existing.lastSeenRowid) {
|
|
335
|
+
if (!options.allowCursorRewindForRetries) return;
|
|
336
|
+
return buildIMessageCatchupCursor({
|
|
337
|
+
lastSeenMs: cursor.lastSeenMs,
|
|
338
|
+
lastSeenRowid: cursor.lastSeenRowid,
|
|
339
|
+
failureRetries: {
|
|
340
|
+
...existing.failureRetries,
|
|
341
|
+
...cursor.failureRetries
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
return cursor;
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
function clampInt(value, min, max, fallback) {
|
|
349
|
+
return resolveIntegerOption(value, fallback, {
|
|
350
|
+
min,
|
|
351
|
+
max
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
function resolveCatchupConfig(raw) {
|
|
355
|
+
return {
|
|
356
|
+
enabled: Boolean(raw?.enabled),
|
|
357
|
+
maxAgeMinutes: clampInt(raw?.maxAgeMinutes, 1, MAX_MAX_AGE_MINUTES, DEFAULT_MAX_AGE_MINUTES),
|
|
358
|
+
perRunLimit: clampInt(raw?.perRunLimit, 1, MAX_PER_RUN_LIMIT, DEFAULT_PER_RUN_LIMIT),
|
|
359
|
+
firstRunLookbackMinutes: clampInt(raw?.firstRunLookbackMinutes, 1, MAX_MAX_AGE_MINUTES, DEFAULT_FIRST_RUN_LOOKBACK_MINUTES),
|
|
360
|
+
maxFailureRetries: clampInt(raw?.maxFailureRetries, 1, MAX_MAX_FAILURE_RETRIES, DEFAULT_MAX_FAILURE_RETRIES)
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
async function advanceIMessageCatchupCursor(accountId, next, config) {
|
|
364
|
+
if (!Number.isFinite(next.lastSeenMs) || !Number.isFinite(next.lastSeenRowid)) return false;
|
|
365
|
+
return await enqueueCursorWrite(accountId, async () => {
|
|
366
|
+
let advanced = false;
|
|
367
|
+
updateCatchupCursorStore(resolveIMessageCatchupCursorKey(accountId), (existingValue) => {
|
|
368
|
+
const cursor = normalizeIMessageCatchupCursor(existingValue);
|
|
369
|
+
if (cursor && next.lastSeenRowid <= cursor.lastSeenRowid) return;
|
|
370
|
+
if (Object.values(cursor?.failureRetries ?? {}).some((count) => count < config.maxFailureRetries)) return;
|
|
371
|
+
advanced = true;
|
|
372
|
+
return buildIMessageCatchupCursor({
|
|
373
|
+
lastSeenMs: Math.max(cursor?.lastSeenMs ?? next.lastSeenMs, next.lastSeenMs),
|
|
374
|
+
lastSeenRowid: next.lastSeenRowid,
|
|
375
|
+
failureRetries: cursor?.failureRetries
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
return advanced;
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* One catchup pass. Loads the cursor, fetches `messages.history`, replays
|
|
383
|
+
* each row through `dispatch`, advances the cursor on success / give-up,
|
|
384
|
+
* persists the cursor, returns a summary.
|
|
385
|
+
*
|
|
386
|
+
* The fetch and dispatch functions are injected so this loop is unit-testable
|
|
387
|
+
* without standing up an `imsg` daemon. The wiring in `monitor-provider.ts`
|
|
388
|
+
* passes the live `client.request("messages.history", ...)` adapter as
|
|
389
|
+
* `fetch` and the `evaluateIMessageInbound` + `runChannelInboundEvent`
|
|
390
|
+
* pipeline as `dispatch`.
|
|
391
|
+
*/
|
|
392
|
+
async function performIMessageCatchup(params) {
|
|
393
|
+
const now = params.now ?? Date.now();
|
|
394
|
+
const cfg = params.config;
|
|
395
|
+
const cursor = await loadIMessageCatchupCursor(params.accountId);
|
|
396
|
+
const lookbackMs = cursor === null ? cfg.firstRunLookbackMinutes * 6e4 : cfg.maxAgeMinutes * 6e4;
|
|
397
|
+
const ageBoundMs = now - cfg.maxAgeMinutes * 6e4;
|
|
398
|
+
const windowStartMs = Math.max(cursor?.lastSeenMs ?? now - lookbackMs, ageBoundMs);
|
|
399
|
+
const windowEndMs = now;
|
|
400
|
+
const sinceRowid = cursor?.lastSeenRowid ?? 0;
|
|
401
|
+
const summary = {
|
|
402
|
+
querySucceeded: false,
|
|
403
|
+
fullyCaughtUp: false,
|
|
404
|
+
fetchedCount: 0,
|
|
405
|
+
replayed: 0,
|
|
406
|
+
skippedFromMe: 0,
|
|
407
|
+
skippedPreCursor: 0,
|
|
408
|
+
skippedGivenUp: 0,
|
|
409
|
+
failed: 0,
|
|
410
|
+
givenUp: 0,
|
|
411
|
+
cursorBefore: cursor ? {
|
|
412
|
+
lastSeenMs: cursor.lastSeenMs,
|
|
413
|
+
lastSeenRowid: cursor.lastSeenRowid
|
|
414
|
+
} : null,
|
|
415
|
+
cursorAfter: {
|
|
416
|
+
lastSeenMs: cursor?.lastSeenMs ?? windowStartMs,
|
|
417
|
+
lastSeenRowid: cursor?.lastSeenRowid ?? 0
|
|
418
|
+
},
|
|
419
|
+
windowStartMs,
|
|
420
|
+
windowEndMs
|
|
421
|
+
};
|
|
422
|
+
let fetchResult;
|
|
423
|
+
try {
|
|
424
|
+
fetchResult = await params.fetch({
|
|
425
|
+
sinceMs: windowStartMs,
|
|
426
|
+
sinceRowid,
|
|
427
|
+
limit: cfg.perRunLimit
|
|
428
|
+
});
|
|
429
|
+
} catch (err) {
|
|
430
|
+
params.warn?.(`imessage catchup: fetch failed: ${String(err)}`);
|
|
431
|
+
return summary;
|
|
432
|
+
}
|
|
433
|
+
if (!fetchResult.resolved) {
|
|
434
|
+
params.warn?.(`imessage catchup: fetch returned unresolved result`);
|
|
435
|
+
return summary;
|
|
436
|
+
}
|
|
437
|
+
summary.querySucceeded = true;
|
|
438
|
+
summary.fullyCaughtUp = fetchResult.fullyCaughtUp !== false;
|
|
439
|
+
summary.fetchedCount = fetchResult.rows.length;
|
|
440
|
+
const rows = fetchResult.rows.toSorted((a, b) => a.rowid - b.rowid);
|
|
441
|
+
const failureRetries = { ...cursor?.failureRetries };
|
|
442
|
+
const cursorBeforeMs = cursor?.lastSeenMs ?? windowStartMs;
|
|
443
|
+
const cursorBeforeRowid = cursor?.lastSeenRowid ?? 0;
|
|
444
|
+
let highWatermarkMs = cursorBeforeMs;
|
|
445
|
+
let highWatermarkRowid = cursorBeforeRowid;
|
|
446
|
+
let earliestHeldFailureRow = null;
|
|
447
|
+
for (const row of rows) {
|
|
448
|
+
if (row.rowid <= sinceRowid) {
|
|
449
|
+
summary.skippedPreCursor += 1;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
if (row.date < ageBoundMs) {
|
|
453
|
+
summary.skippedPreCursor += 1;
|
|
454
|
+
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
455
|
+
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (row.isFromMe) {
|
|
459
|
+
try {
|
|
460
|
+
await params.observeSkippedFromMe?.(row);
|
|
461
|
+
} catch (err) {
|
|
462
|
+
params.warn?.(`imessage catchup: from-me observer failed for guid=${row.guid}: ${String(err)}`);
|
|
463
|
+
}
|
|
464
|
+
summary.skippedFromMe += 1;
|
|
465
|
+
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
466
|
+
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
const priorCount = failureRetries[row.guid] ?? 0;
|
|
470
|
+
if (priorCount >= cfg.maxFailureRetries) {
|
|
471
|
+
summary.skippedGivenUp += 1;
|
|
472
|
+
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
473
|
+
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
let dispatched;
|
|
477
|
+
try {
|
|
478
|
+
dispatched = await params.dispatch(row);
|
|
479
|
+
} catch (err) {
|
|
480
|
+
params.warn?.(`imessage catchup: dispatch threw for guid=${row.guid}: ${String(err)}`);
|
|
481
|
+
dispatched = { ok: false };
|
|
482
|
+
}
|
|
483
|
+
if (dispatched.ok) {
|
|
484
|
+
summary.replayed += 1;
|
|
485
|
+
delete failureRetries[row.guid];
|
|
486
|
+
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
487
|
+
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
const nextCount = priorCount + 1;
|
|
491
|
+
failureRetries[row.guid] = nextCount;
|
|
492
|
+
summary.failed += 1;
|
|
493
|
+
if (nextCount >= cfg.maxFailureRetries) {
|
|
494
|
+
summary.givenUp += 1;
|
|
495
|
+
params.warn?.(`imessage catchup: giving up on guid=${row.guid} after ${nextCount} failures; advancing cursor past it`);
|
|
496
|
+
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
497
|
+
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (earliestHeldFailureRow === null || row.rowid < earliestHeldFailureRow.rowid) earliestHeldFailureRow = row;
|
|
501
|
+
}
|
|
502
|
+
if (earliestHeldFailureRow === null) {
|
|
503
|
+
if (typeof fetchResult.highWatermarkMs === "number") highWatermarkMs = Math.max(highWatermarkMs, fetchResult.highWatermarkMs);
|
|
504
|
+
if (typeof fetchResult.highWatermarkRowid === "number") highWatermarkRowid = Math.max(highWatermarkRowid, fetchResult.highWatermarkRowid);
|
|
505
|
+
}
|
|
506
|
+
let lastSeenMs;
|
|
507
|
+
let lastSeenRowid;
|
|
508
|
+
if (earliestHeldFailureRow !== null) {
|
|
509
|
+
lastSeenMs = Math.max(cursorBeforeMs, earliestHeldFailureRow.date - 1);
|
|
510
|
+
lastSeenRowid = Math.max(cursorBeforeRowid, earliestHeldFailureRow.rowid - 1);
|
|
511
|
+
} else {
|
|
512
|
+
lastSeenMs = highWatermarkMs;
|
|
513
|
+
lastSeenRowid = highWatermarkRowid;
|
|
514
|
+
}
|
|
515
|
+
const capped = capFailureRetriesMap(failureRetries);
|
|
516
|
+
summary.cursorAfter = {
|
|
517
|
+
lastSeenMs,
|
|
518
|
+
lastSeenRowid
|
|
519
|
+
};
|
|
520
|
+
await saveIMessageCatchupCursor(params.accountId, {
|
|
521
|
+
lastSeenMs,
|
|
522
|
+
lastSeenRowid,
|
|
523
|
+
failureRetries: capped
|
|
524
|
+
}, { allowCursorRewindForRetries: earliestHeldFailureRow !== null });
|
|
525
|
+
if (summary.replayed > 0 || summary.failed > 0 || summary.givenUp > 0) params.log?.(`imessage catchup: replayed=${summary.replayed} skippedFromMe=${summary.skippedFromMe} skippedGivenUp=${summary.skippedGivenUp} failed=${summary.failed} givenUp=${summary.givenUp} fetchedCount=${summary.fetchedCount}`);
|
|
526
|
+
return summary;
|
|
527
|
+
}
|
|
528
|
+
//#endregion
|
|
305
529
|
//#region extensions/imessage/src/monitor/strip-imsg-length-prefixed-text.ts
|
|
306
530
|
const utf8Decoder = new TextDecoder();
|
|
307
531
|
function readVarint(buf, start) {
|
|
@@ -342,9 +566,6 @@ function stripImessageLengthPrefixedUtf8Text(text) {
|
|
|
342
566
|
function isOptionalString(value) {
|
|
343
567
|
return value === void 0 || value === null || typeof value === "string";
|
|
344
568
|
}
|
|
345
|
-
function isOptionalStringOrNumber(value) {
|
|
346
|
-
return value === void 0 || value === null || typeof value === "string" || typeof value === "number";
|
|
347
|
-
}
|
|
348
569
|
function isOptionalNumber(value) {
|
|
349
570
|
return value === void 0 || value === null || typeof value === "number";
|
|
350
571
|
}
|
|
@@ -367,7 +588,7 @@ function parseIMessageNotification(raw) {
|
|
|
367
588
|
const maybeMessage = raw.message;
|
|
368
589
|
if (!isRecord(maybeMessage)) return null;
|
|
369
590
|
const message = maybeMessage;
|
|
370
|
-
if (!isOptionalNumber(message.id) || !isOptionalString(message.guid) || !isOptionalNumber(message.chat_id) || !isOptionalString(message.sender) || !isOptionalString(message.destination_caller_id) || !isOptionalBoolean(message.is_from_me) || !isOptionalString(message.text) || !
|
|
591
|
+
if (!isOptionalNumber(message.id) || !isOptionalString(message.guid) || !isOptionalNumber(message.chat_id) || !isOptionalString(message.sender) || !isOptionalString(message.destination_caller_id) || !isOptionalBoolean(message.is_from_me) || !isOptionalString(message.text) || !isOptionalString(message.thread_originator_guid) || !isOptionalString(message.reply_to_guid) || !isOptionalString(message.reply_to_text) || !isOptionalString(message.reply_to_sender) || !isOptionalString(message.created_at) || !isOptionalBoolean(message.is_reaction) || !isOptionalBoolean(message.is_tapback) || !isOptionalString(message.associated_message_guid) || !isOptionalNumber(message.associated_message_type) || !isOptionalString(message.reaction_type) || !isOptionalString(message.reaction_emoji) || !isOptionalBoolean(message.is_reaction_add) || !isOptionalString(message.reacted_to_guid) || !isOptionalAttachments(message.attachments) || !isOptionalString(message.chat_identifier) || !isOptionalString(message.chat_guid) || !isOptionalString(message.chat_name) || !isOptionalStringArray(message.participants) || !isOptionalBoolean(message.is_group)) return null;
|
|
371
592
|
return {
|
|
372
593
|
...message,
|
|
373
594
|
text: typeof message.text === "string" ? stripImessageLengthPrefixedUtf8Text(message.text) : message.text,
|
|
@@ -587,15 +808,16 @@ function combineIMessagePayloads(payloads) {
|
|
|
587
808
|
seenGuids.add(guid);
|
|
588
809
|
coalescedMessageGuids.push(guid);
|
|
589
810
|
}
|
|
590
|
-
const
|
|
811
|
+
const reply = payloads.find((payload) => payload.thread_originator_guid != null || payload.reply_to_guid != null) ?? first;
|
|
591
812
|
return {
|
|
592
813
|
...first,
|
|
593
814
|
text: combinedText,
|
|
594
815
|
attachments: allAttachments.length > 0 ? allAttachments : null,
|
|
595
816
|
created_at: latestCreatedAt,
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
817
|
+
thread_originator_guid: reply.thread_originator_guid ?? null,
|
|
818
|
+
reply_to_guid: reply.reply_to_guid ?? null,
|
|
819
|
+
reply_to_text: reply.reply_to_text ?? null,
|
|
820
|
+
reply_to_sender: reply.reply_to_sender ?? null,
|
|
599
821
|
coalescedMessageGuids: coalescedMessageGuids.length > 0 ? coalescedMessageGuids : void 0,
|
|
600
822
|
coalescedCatchupCursor: Number.isFinite(maxRowid) && Number.isFinite(maxDateMs) ? {
|
|
601
823
|
lastSeenMs: maxDateMs,
|
|
@@ -1033,7 +1255,7 @@ function isStaleIMessageBacklog(message, nowMs, thresholdMs = IMESSAGE_STALE_INB
|
|
|
1033
1255
|
//#endregion
|
|
1034
1256
|
//#region extensions/imessage/src/conversation-route.ts
|
|
1035
1257
|
function resolveIMessageConversationRoute(params) {
|
|
1036
|
-
|
|
1258
|
+
const route = resolveAgentRoute({
|
|
1037
1259
|
cfg: params.cfg,
|
|
1038
1260
|
channel: "imessage",
|
|
1039
1261
|
accountId: params.accountId,
|
|
@@ -1047,28 +1269,30 @@ function resolveIMessageConversationRoute(params) {
|
|
|
1047
1269
|
sender: params.sender,
|
|
1048
1270
|
chatId: params.chatId
|
|
1049
1271
|
});
|
|
1050
|
-
if (!conversationId) return
|
|
1051
|
-
|
|
1272
|
+
if (!conversationId) return {
|
|
1273
|
+
route,
|
|
1274
|
+
bindingResolution: null
|
|
1275
|
+
};
|
|
1276
|
+
const conversation = {
|
|
1277
|
+
channel: "imessage",
|
|
1278
|
+
accountId: params.accountId,
|
|
1279
|
+
conversationId
|
|
1280
|
+
};
|
|
1281
|
+
const configuredRoute = resolveConfiguredBindingRoute({
|
|
1052
1282
|
cfg: params.cfg,
|
|
1053
1283
|
route,
|
|
1054
|
-
conversation
|
|
1055
|
-
|
|
1056
|
-
accountId: params.accountId,
|
|
1057
|
-
conversationId
|
|
1058
|
-
}
|
|
1059
|
-
}).route;
|
|
1284
|
+
conversation
|
|
1285
|
+
});
|
|
1060
1286
|
const runtimeRoute = resolveRuntimeConversationBindingRoute({
|
|
1061
|
-
route,
|
|
1062
|
-
conversation
|
|
1063
|
-
channel: "imessage",
|
|
1064
|
-
accountId: params.accountId,
|
|
1065
|
-
conversationId
|
|
1066
|
-
}
|
|
1287
|
+
route: configuredRoute.route,
|
|
1288
|
+
conversation
|
|
1067
1289
|
});
|
|
1068
|
-
route = runtimeRoute.route;
|
|
1069
1290
|
if (runtimeRoute.bindingRecord && !runtimeRoute.boundSessionKey) logVerbose(`imessage: plugin-bound conversation ${conversationId}`);
|
|
1070
1291
|
else if (runtimeRoute.boundSessionKey) logVerbose(`imessage: routed via bound conversation ${conversationId} -> ${runtimeRoute.boundSessionKey}`);
|
|
1071
|
-
return
|
|
1292
|
+
return {
|
|
1293
|
+
route: runtimeRoute.route,
|
|
1294
|
+
bindingResolution: runtimeRoute.bindingRecord ? null : configuredRoute.bindingResolution
|
|
1295
|
+
};
|
|
1072
1296
|
}
|
|
1073
1297
|
//#endregion
|
|
1074
1298
|
//#region extensions/imessage/src/monitor/reflection-guard.ts
|
|
@@ -1213,7 +1437,7 @@ function describeReplyContext(message) {
|
|
|
1213
1437
|
if (!body) return null;
|
|
1214
1438
|
return {
|
|
1215
1439
|
body,
|
|
1216
|
-
id: normalizeReplyField(message.
|
|
1440
|
+
id: normalizeReplyField(message.thread_originator_guid) ?? normalizeReplyField(message.reply_to_guid),
|
|
1217
1441
|
sender: normalizeReplyField(message.reply_to_sender)
|
|
1218
1442
|
};
|
|
1219
1443
|
}
|
|
@@ -1391,7 +1615,7 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1391
1615
|
const groupId = isGroup ? groupIdCandidate : void 0;
|
|
1392
1616
|
const hasControlCommandInMessage = hasControlCommand(messageText, params.cfg);
|
|
1393
1617
|
const groupAllowFromForAccess = isGroup ? groupAllowFromWithLegacyChatTargets : params.groupAllowFrom;
|
|
1394
|
-
const route = resolveIMessageConversationRoute({
|
|
1618
|
+
const { route, bindingResolution } = resolveIMessageConversationRoute({
|
|
1395
1619
|
cfg: params.cfg,
|
|
1396
1620
|
accountId: params.accountId,
|
|
1397
1621
|
isGroup,
|
|
@@ -1689,6 +1913,7 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1689
1913
|
sender,
|
|
1690
1914
|
senderNormalized,
|
|
1691
1915
|
route,
|
|
1916
|
+
bindingResolution,
|
|
1692
1917
|
bodyText,
|
|
1693
1918
|
createdAt,
|
|
1694
1919
|
replyContext: filteredReplyContext,
|
|
@@ -1754,11 +1979,8 @@ async function buildIMessageInboundContext(params) {
|
|
|
1754
1979
|
envelope: envelopeOptions
|
|
1755
1980
|
})
|
|
1756
1981
|
});
|
|
1757
|
-
const
|
|
1758
|
-
|
|
1759
|
-
accountId: decision.route.accountId,
|
|
1760
|
-
sender: decision.sender
|
|
1761
|
-
});
|
|
1982
|
+
const directService = resolveIMessageDirectChatService(params.accountService, decision.chatGuid) ?? "auto";
|
|
1983
|
+
const imessageTo = decision.isGroup ? chatTarget || `imessage:${decision.sender}` : `${directService}:${decision.sender}`;
|
|
1762
1984
|
const imessageFrom = decision.isGroup ? `imessage:group:${chatId ?? "unknown"}` : imessageTo;
|
|
1763
1985
|
const inboundHistory = !decision.isGroup && params.dmHistory?.inboundHistory ? params.dmHistory.inboundHistory : decision.isGroup && decision.historyKey && params.historyLimit > 0 ? createChannelHistoryWindow({ historyMap: params.groupHistories }).buildInboundHistory({
|
|
1764
1986
|
historyKey: decision.historyKey,
|
|
@@ -1787,11 +2009,16 @@ async function buildIMessageInboundContext(params) {
|
|
|
1787
2009
|
from: imessageFrom,
|
|
1788
2010
|
sender: {
|
|
1789
2011
|
id: decision.sender,
|
|
1790
|
-
name: decision.senderNormalized
|
|
2012
|
+
name: decision.senderNormalized,
|
|
2013
|
+
isSelf: params.message.is_from_me === true
|
|
1791
2014
|
},
|
|
1792
2015
|
conversation: {
|
|
1793
2016
|
kind: decision.isGroup ? "group" : "direct",
|
|
1794
2017
|
id: chatId != null ? String(chatId) : decision.sender,
|
|
2018
|
+
...decision.isGroup && chatId == null ? {} : { routePeer: {
|
|
2019
|
+
kind: decision.isGroup ? "group" : "direct",
|
|
2020
|
+
id: decision.isGroup ? String(chatId) : decision.senderNormalized
|
|
2021
|
+
} },
|
|
1795
2022
|
label: fromLabel
|
|
1796
2023
|
},
|
|
1797
2024
|
route: {
|
|
@@ -1840,13 +2067,6 @@ function buildIMessageEchoScope(params) {
|
|
|
1840
2067
|
if (params.chatIdentifier) scopes.push(`${params.accountId}:chat_identifier:${params.chatIdentifier}`);
|
|
1841
2068
|
return scopes;
|
|
1842
2069
|
}
|
|
1843
|
-
function buildDirectIMessageReplyTarget(params) {
|
|
1844
|
-
if (resolveIMessageAccount({
|
|
1845
|
-
cfg: params.cfg,
|
|
1846
|
-
accountId: params.accountId
|
|
1847
|
-
}).config.service === "sms") return `sms:${params.sender}`;
|
|
1848
|
-
return `imessage:${params.sender}`;
|
|
1849
|
-
}
|
|
1850
2070
|
function describeIMessageEchoDropLog(params) {
|
|
1851
2071
|
const preview = truncateUtf16Safe(params.messageText, 50);
|
|
1852
2072
|
return `imessage: skipping echo message${params.messageId ? ` id=${params.messageId}` : ""}: "${preview}"`;
|
|
@@ -2879,7 +3099,7 @@ async function monitorIMessageProvider(opts = {}) {
|
|
|
2879
3099
|
const conversationKey = chatId != null ? `group:${chatId}` : `dm:${senderForKey}`;
|
|
2880
3100
|
const rateLimitKey = `${accountInfo.accountId}:${conversationKey}`;
|
|
2881
3101
|
if (decision.kind === "drop") {
|
|
2882
|
-
if (decision.reason === "echo" || decision.reason === "
|
|
3102
|
+
if (decision.reason === "echo" || decision.reason === "reflected assistant content") loopRateLimiter.record(rateLimitKey);
|
|
2883
3103
|
const diagnostic = describeIMessageInboundDropDiagnostic({
|
|
2884
3104
|
accountId: accountInfo.accountId,
|
|
2885
3105
|
reason: decision.reason,
|
|
@@ -2956,6 +3176,16 @@ async function monitorIMessageProvider(opts = {}) {
|
|
|
2956
3176
|
});
|
|
2957
3177
|
return;
|
|
2958
3178
|
}
|
|
3179
|
+
if (decision.bindingResolution) {
|
|
3180
|
+
const readiness = await ensureConfiguredBindingRouteReady({
|
|
3181
|
+
cfg,
|
|
3182
|
+
bindingResolution: decision.bindingResolution
|
|
3183
|
+
});
|
|
3184
|
+
if (!readiness.ok) {
|
|
3185
|
+
runtime.error?.(`imessage: dropped inbound message; configured ACP binding unavailable for ${decision.bindingResolution.record.conversation.conversationId}: ${readiness.error}`);
|
|
3186
|
+
return;
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
2959
3189
|
const storePath = resolveStorePath(cfg.session?.store, { agentId: decision.route.agentId });
|
|
2960
3190
|
const privateApiStatus = getCachedIMessagePrivateApiStatus(cliPath);
|
|
2961
3191
|
const supportsTyping = imessageRpcSupportsMethod(privateApiStatus, "typing");
|
|
@@ -2975,11 +3205,9 @@ async function monitorIMessageProvider(opts = {}) {
|
|
|
2975
3205
|
chatType: decision.isGroup ? "group" : "direct"
|
|
2976
3206
|
});
|
|
2977
3207
|
const shouldUseDirectToolTypingOptions = !decision.isGroup && sendPolicy !== "deny" && (configuredTypingMode === void 0 || configuredTypingMode === "instant");
|
|
2978
|
-
const
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
sender: decision.sender
|
|
2982
|
-
}) : void 0;
|
|
3208
|
+
const shouldStartDirectTyping = supportsTyping && shouldUseDirectToolTypingOptions;
|
|
3209
|
+
const earlyDirectTypingService = resolveIMessageDirectChatService(imessageCfg.service, decision.chatGuid) ?? "auto";
|
|
3210
|
+
const earlyDirectTypingTarget = shouldStartDirectTyping ? `${earlyDirectTypingService}:${decision.sender}` : void 0;
|
|
2983
3211
|
let stopEarlyDirectTyping;
|
|
2984
3212
|
if (earlyDirectTypingTarget) {
|
|
2985
3213
|
const earlyDirectTypingStarted = sendIMessageTyping(earlyDirectTypingTarget, true, {
|
|
@@ -3056,8 +3284,10 @@ async function monitorIMessageProvider(opts = {}) {
|
|
|
3056
3284
|
envelopeOptions: resolveEnvelopeFormatOptions(cfg),
|
|
3057
3285
|
logVerbose
|
|
3058
3286
|
}) : void 0;
|
|
3287
|
+
const pluginChannelRuntime = opts.channelRuntime;
|
|
3059
3288
|
const { ctxPayload, chatTarget, imessageTo } = await buildIMessageInboundContext({
|
|
3060
3289
|
cfg,
|
|
3290
|
+
accountService: imessageCfg.service,
|
|
3061
3291
|
decision: contextDecision,
|
|
3062
3292
|
message,
|
|
3063
3293
|
previousTimestamp,
|
|
@@ -3065,7 +3295,7 @@ async function monitorIMessageProvider(opts = {}) {
|
|
|
3065
3295
|
historyLimit,
|
|
3066
3296
|
groupHistories,
|
|
3067
3297
|
dmHistory,
|
|
3068
|
-
buildContext:
|
|
3298
|
+
buildContext: pluginChannelRuntime?.inbound.buildContext,
|
|
3069
3299
|
media: { facts: mediaAttachments }
|
|
3070
3300
|
});
|
|
3071
3301
|
const updateTarget = chatTarget || imessageTo;
|
|
@@ -3080,7 +3310,8 @@ async function monitorIMessageProvider(opts = {}) {
|
|
|
3080
3310
|
}
|
|
3081
3311
|
const sendReadReceipts = imessageCfg.sendReadReceipts !== false;
|
|
3082
3312
|
const typingTarget = ctxPayload.To;
|
|
3083
|
-
|
|
3313
|
+
const readTarget = !decision.isGroup && decision.chatGuid ? `chat_guid:${decision.chatGuid}` : typingTarget;
|
|
3314
|
+
if (supportsRead && sendReadReceipts && readTarget) markIMessageChatRead(readTarget, {
|
|
3084
3315
|
cfg,
|
|
3085
3316
|
accountId: accountInfo.accountId,
|
|
3086
3317
|
cliPath,
|
|
@@ -3217,6 +3448,7 @@ async function monitorIMessageProvider(opts = {}) {
|
|
|
3217
3448
|
sessionKey: decision.route.sessionKey
|
|
3218
3449
|
},
|
|
3219
3450
|
ctxPayload,
|
|
3451
|
+
dispatchReplyFromConfig: pluginChannelRuntime?.reply?.dispatchReplyFromConfig,
|
|
3220
3452
|
record: {
|
|
3221
3453
|
updateLastRoute: !decision.isGroup && updateTarget ? {
|
|
3222
3454
|
sessionKey: inboundLastRouteSessionKey,
|