@openclaw/imessage 2026.10.1-beta.2 → 2026.10.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-evTpvJMv.mjs → accounts-DacCvDBt.mjs} +23 -45
- package/dist/.setup/{actions-DJqbUmbN.mjs → actions-Hxh12myO.mjs} +54 -101
- package/dist/.setup/{actions.runtime-DL5i-Y4J.mjs → actions.runtime-DOIX6Fkf.mjs} +29 -37
- package/dist/.setup/{api-oq_MryN7.mjs → api-BV8qHfLl.mjs} +7 -7
- package/dist/.setup/{api-SAVMfMoY.mjs → api-DC1yHVGj.mjs} +8 -8
- package/dist/.setup/{approval-handler.runtime-Bj2NGNXF.mjs → approval-handler.runtime-Dc3KzakG.mjs} +38 -66
- package/dist/.setup/{approval-reactions-L9Ul7PAu.mjs → approval-reactions-JvzJCJ4a.mjs} +26 -33
- package/dist/.setup/{channel-CL73qdMG.mjs → channel-9TWBUDkP.mjs} +14 -16
- package/dist/.setup/{channel.runtime-DY7JJ0_9.mjs → channel.runtime-DrLDEY4R.mjs} +198 -362
- package/dist/.setup/{client-12apu4Ip.mjs → client-HixFQQWY.mjs} +34 -38
- package/dist/.setup/{media-contract-DjQGZlfl.mjs → media-contract-BwWs5PM7.mjs} +1 -1
- package/dist/.setup/{message-tool-api-CqfRxLlT.mjs → message-tool-api-B5NfPnFL.mjs} +2 -2
- package/dist/.setup/{question-reactions-DJV-TnA8.mjs → question-reactions-9jrqVZgE.mjs} +20 -25
- package/dist/.setup/{reaction-context-By7kUMBC.mjs → reaction-context-CRXpeYAr.mjs} +1 -1
- package/dist/.setup/{remote-file-BU7pA-Zi.mjs → remote-file-Dq5Asi_F.mjs} +10 -16
- package/dist/.setup/{sanitize-outbound-C0x4BPky.mjs → sanitize-outbound-DpdQERYJ.mjs} +27 -62
- package/dist/.setup/{send-pkYwL6Rt.mjs → send-BZZzMbdi.mjs} +186 -251
- package/dist/.setup/{setup-core-DqOBe-n0.mjs → setup-core-CaRGbZOL.mjs} +15 -46
- package/dist/.setup/{setup-surface-zKhIinwY.mjs → setup-surface-BMWS7VuY.mjs} +2 -2
- package/dist/.setup/{targets-BicBiYsM.mjs → targets-B4oU7n-v.mjs} +47 -69
- package/dist/api.js +7 -7
- package/dist/channel-plugin-api.js +1 -1
- package/dist/contract-api.js +1 -1
- package/dist/doctor-contract-api.js +21 -45
- package/dist/media-contract-api.js +1 -1
- package/dist/message-tool-api.js +1 -1
- package/dist/runtime-api.js +1 -1
- package/dist/setup-entry.js +1 -1
- package/dist/src/chat-db.worker.js +1 -1
- package/package.json +4 -4
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { c as parseIMessageTarget, o as normalizeIMessageHandle, r as isAllowedIMessageReplyContextSender, s as parseIMessageAllowTarget, t as formatIMessageChatTarget } from "./targets-
|
|
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 { C as resolveIMessageDirectChatService, c as rememberIMessageReplyCache, d as IMESSAGE_CATCHUP_CURSOR_NAMESPACE, g as resolveIMessageEchoMediaKey, h as resolveIMessageCatchupCursorKey, m as capFailureRetriesMap, n as createIMessageRpcClient, s as isKnownFromMeIMessageTarget } from "./client-
|
|
5
|
-
import { a as normalizeIMessageMessageId, i as normalizeIMessageGuid, n as probeIMessage, r as probeIMessagePrivateApi } from "./actions-
|
|
1
|
+
import { c as parseIMessageTarget, o as normalizeIMessageHandle, r as isAllowedIMessageReplyContextSender, s as parseIMessageAllowTarget, t as formatIMessageChatTarget } from "./targets-B4oU7n-v.mjs";
|
|
2
|
+
import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount, r as listEnabledIMessageAccounts, s as resolveIMessageDuplicateSourceOwner } from "./accounts-DacCvDBt.mjs";
|
|
3
|
+
import { r as imessageRpcSupportsMethod } from "./message-tool-api-B5NfPnFL.mjs";
|
|
4
|
+
import { C as resolveIMessageDirectChatService, c as rememberIMessageReplyCache, d as IMESSAGE_CATCHUP_CURSOR_NAMESPACE, g as resolveIMessageEchoMediaKey, h as resolveIMessageCatchupCursorKey, m as capFailureRetriesMap, n as createIMessageRpcClient, s as isKnownFromMeIMessageTarget } from "./client-HixFQQWY.mjs";
|
|
5
|
+
import { a as normalizeIMessageMessageId, i as normalizeIMessageGuid, n as probeIMessage, r as probeIMessagePrivateApi } from "./actions-Hxh12myO.mjs";
|
|
6
6
|
import { t as getIMessageRuntime } from "./runtime-Cza4CY5T.mjs";
|
|
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-
|
|
7
|
+
import { a as resolveIMessageGroupSystemPrompt, s as resolveIMessageInboundConversationId } from "./channel-9TWBUDkP.mjs";
|
|
8
|
+
import { r as sanitizeOutboundText } from "./sanitize-outbound-DpdQERYJ.mjs";
|
|
9
|
+
import { n as resolveIMessageAttachmentRoots, r as resolveIMessageRemoteAttachmentRoots } from "./media-contract-BwWs5PM7.mjs";
|
|
10
10
|
import { t as IMESSAGE_LEGACY_OUTBOUND_SEND_DEP_KEYS } from "./outbound-send-deps-B-QEsLSM.mjs";
|
|
11
|
-
import { i as resolveIMessageStartupRowidWatermark } from "./remote-file-
|
|
12
|
-
import { i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction, s as buildIMessageApprovalConversationKeyForInbound } from "./approval-reactions-
|
|
13
|
-
import { c as iMessageApprovalControlBindings, n as hasPersistedIMessageEcho, r as normalizeIMessageEchoText, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-
|
|
14
|
-
import { t as resolveIMessageReactionContext } from "./reaction-context-
|
|
15
|
-
import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-
|
|
16
|
-
import { imessageSetupWizard } from "./setup-surface-
|
|
17
|
-
import { asSafeIntegerInRange, hasNonEmptyString, isRecord, normalizeOptionalString, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
11
|
+
import { i as resolveIMessageStartupRowidWatermark } from "./remote-file-Dq5Asi_F.mjs";
|
|
12
|
+
import { f as resolveIMessageApprovalControlActor, i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction, s as buildIMessageApprovalConversationKeyForInbound } from "./approval-reactions-JvzJCJ4a.mjs";
|
|
13
|
+
import { c as iMessageApprovalControlBindings, n as hasPersistedIMessageEcho, r as normalizeIMessageEchoText, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-BZZzMbdi.mjs";
|
|
14
|
+
import { t as resolveIMessageReactionContext } from "./reaction-context-CRXpeYAr.mjs";
|
|
15
|
+
import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-9jrqVZgE.mjs";
|
|
16
|
+
import { imessageSetupWizard } from "./setup-surface-BMWS7VuY.mjs";
|
|
17
|
+
import { asSafeIntegerInRange, hasNonEmptyString, isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
18
18
|
import { normalizeChannelDmPolicy } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
19
|
-
import { bindIngressLifecycleToReplyOptions, createAccountStatusSink, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, resolveChannelStreamingBlockEnabled, resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-outbound";
|
|
19
|
+
import { bindIngressLifecycleToReplyOptions, createAccountStatusSink, createChannelDeliveryAccumulator, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, resolveChannelStreamingBlockEnabled, resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-outbound";
|
|
20
20
|
import { resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
|
|
21
21
|
import { resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
|
|
22
22
|
import path from "node:path";
|
|
@@ -32,7 +32,7 @@ import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-util
|
|
|
32
32
|
import { collectErrorGraphCandidates, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
33
33
|
import { resolveChannelGroupPolicy, resolveChannelGroups, resolveChannelGroupsConfigPath, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
|
34
34
|
import { convertMarkdownTables, findCodeRegions, isInsideCode, sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
|
|
35
|
-
import { buildChannelInboundEventContext, buildMentionRegexes,
|
|
35
|
+
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
36
|
import { FsSafeError, openLocalFileSafely, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
37
37
|
import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
|
|
38
38
|
import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-runtime";
|
|
@@ -47,7 +47,7 @@ import { ensureConfiguredBindingRouteReady, readChannelAllowFromStore, upsertCha
|
|
|
47
47
|
import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
48
48
|
import { redactIdentifier } from "openclaw/plugin-sdk/logging-core";
|
|
49
49
|
import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
50
|
-
import { getSessionEntry,
|
|
50
|
+
import { getSessionEntry, readSessionUpdatedAtAsync, resolveSendPolicy, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
51
51
|
import { waitForTransportReady } from "openclaw/plugin-sdk/transport-ready-runtime";
|
|
52
52
|
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
53
53
|
import { chunkMarkdownTextWithMode, resolveChunkMode } from "openclaw/plugin-sdk/reply-chunking";
|
|
@@ -201,7 +201,7 @@ async function pollPendingIMessageApprovalReactions(params) {
|
|
|
201
201
|
}
|
|
202
202
|
//#endregion
|
|
203
203
|
//#region extensions/imessage/src/chat.ts
|
|
204
|
-
function
|
|
204
|
+
async function runChatAction(method, to, opts, isTyping) {
|
|
205
205
|
const cfg = requireRuntimeConfig(opts.cfg, "iMessage chat action");
|
|
206
206
|
const account = opts.account ?? resolveIMessageAccount({
|
|
207
207
|
cfg,
|
|
@@ -213,17 +213,11 @@ function buildChatTargetParams(to, opts) {
|
|
|
213
213
|
else if (target.kind === "chat_guid") params.chat_guid = target.chatGuid;
|
|
214
214
|
else if (target.kind === "chat_identifier") params.chat_identifier = target.chatIdentifier;
|
|
215
215
|
else params.to = target.to;
|
|
216
|
-
|
|
217
|
-
params
|
|
218
|
-
service
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
async function runChatAction(method, params, opts) {
|
|
222
|
-
const cfg = requireRuntimeConfig(opts.cfg, "iMessage chat action");
|
|
223
|
-
const account = opts.account ?? resolveIMessageAccount({
|
|
224
|
-
cfg,
|
|
225
|
-
accountId: opts.accountId
|
|
226
|
-
});
|
|
216
|
+
if (method === "typing") {
|
|
217
|
+
params.typing = isTyping;
|
|
218
|
+
const service = opts.service ?? (target.kind === "handle" ? target.service : void 0) ?? account.config.service;
|
|
219
|
+
if (service) params.service = service;
|
|
220
|
+
}
|
|
227
221
|
const cliPath = opts.cliPath?.trim() || account.config.cliPath?.trim() || "imsg";
|
|
228
222
|
const dbPath = opts.dbPath?.trim() || account.config.dbPath?.trim();
|
|
229
223
|
const remoteHost = await resolveIMessageRemoteHost({
|
|
@@ -237,20 +231,16 @@ async function runChatAction(method, params, opts) {
|
|
|
237
231
|
});
|
|
238
232
|
const shouldClose = !opts.client;
|
|
239
233
|
try {
|
|
240
|
-
|
|
234
|
+
await client.request(method, params, { timeoutMs: opts.timeoutMs });
|
|
241
235
|
} finally {
|
|
242
236
|
if (shouldClose) await client.stop();
|
|
243
237
|
}
|
|
244
238
|
}
|
|
245
239
|
async function sendIMessageTyping(to, isTyping, opts) {
|
|
246
|
-
|
|
247
|
-
params.typing = isTyping;
|
|
248
|
-
if (service) params.service = service;
|
|
249
|
-
await runChatAction("typing", params, opts);
|
|
240
|
+
await runChatAction("typing", to, opts, isTyping);
|
|
250
241
|
}
|
|
251
242
|
async function markIMessageChatRead(to, opts) {
|
|
252
|
-
|
|
253
|
-
await runChatAction("read", params, opts);
|
|
243
|
+
await runChatAction("read", to, opts);
|
|
254
244
|
}
|
|
255
245
|
//#endregion
|
|
256
246
|
//#region extensions/imessage/src/monitor/abort-handler.ts
|
|
@@ -296,14 +286,11 @@ function normalizeIMessageCatchupCursor(value) {
|
|
|
296
286
|
const raw = value;
|
|
297
287
|
if (typeof raw.lastSeenMs !== "number" || !Number.isFinite(raw.lastSeenMs)) return null;
|
|
298
288
|
if (typeof raw.lastSeenRowid !== "number" || !Number.isFinite(raw.lastSeenRowid)) return null;
|
|
299
|
-
|
|
300
|
-
const hasRetries = Object.keys(failureRetries).length > 0;
|
|
301
|
-
return {
|
|
289
|
+
return buildIMessageCatchupCursor({
|
|
302
290
|
lastSeenMs: raw.lastSeenMs,
|
|
303
291
|
lastSeenRowid: raw.lastSeenRowid,
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
};
|
|
292
|
+
failureRetries: raw.failureRetries
|
|
293
|
+
}, typeof raw.updatedAt === "number" ? raw.updatedAt : 0);
|
|
307
294
|
}
|
|
308
295
|
async function loadIMessageCatchupCursor(accountId) {
|
|
309
296
|
return normalizeIMessageCatchupCursor(await openCatchupCursorStore().lookup(resolveIMessageCatchupCursorKey(accountId)));
|
|
@@ -344,11 +331,6 @@ function decideCatchupCursorSave(existingValue, cursor, allowCursorRewindForRetr
|
|
|
344
331
|
value: cursor
|
|
345
332
|
};
|
|
346
333
|
}
|
|
347
|
-
async function saveIMessageCatchupCursor(accountId, next, options = {}) {
|
|
348
|
-
const cursor = buildIMessageCatchupCursor(next, Date.now());
|
|
349
|
-
const allowCursorRewindForRetries = options.allowCursorRewindForRetries === true;
|
|
350
|
-
await updateIMessageCatchupCursor(accountId, (existing) => decideCatchupCursorSave(existing, cursor, allowCursorRewindForRetries));
|
|
351
|
-
}
|
|
352
334
|
async function updateIMessageCatchupCursor(accountId, decide) {
|
|
353
335
|
const store = openCatchupCursorStore();
|
|
354
336
|
if (!store.observe || !store.compareAndApply) throw new Error("iMessage catchup cursor persistence requires plugin-state comparison support.");
|
|
@@ -464,10 +446,10 @@ async function performIMessageCatchup(params) {
|
|
|
464
446
|
summary.skippedPreCursor += 1;
|
|
465
447
|
continue;
|
|
466
448
|
}
|
|
449
|
+
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
450
|
+
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
467
451
|
if (row.date < ageBoundMs) {
|
|
468
452
|
summary.skippedPreCursor += 1;
|
|
469
|
-
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
470
|
-
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
471
453
|
continue;
|
|
472
454
|
}
|
|
473
455
|
if (row.isFromMe) {
|
|
@@ -477,15 +459,11 @@ async function performIMessageCatchup(params) {
|
|
|
477
459
|
params.warn?.(`imessage catchup: from-me observer failed for guid=${row.guid}: ${String(err)}`);
|
|
478
460
|
}
|
|
479
461
|
summary.skippedFromMe += 1;
|
|
480
|
-
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
481
|
-
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
482
462
|
continue;
|
|
483
463
|
}
|
|
484
464
|
const priorCount = failureRetries[row.guid] ?? 0;
|
|
485
465
|
if (priorCount >= cfg.maxFailureRetries) {
|
|
486
466
|
summary.skippedGivenUp += 1;
|
|
487
|
-
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
488
|
-
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
489
467
|
continue;
|
|
490
468
|
}
|
|
491
469
|
let dispatched;
|
|
@@ -498,8 +476,6 @@ async function performIMessageCatchup(params) {
|
|
|
498
476
|
if (dispatched.ok) {
|
|
499
477
|
summary.replayed += 1;
|
|
500
478
|
delete failureRetries[row.guid];
|
|
501
|
-
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
502
|
-
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
503
479
|
continue;
|
|
504
480
|
}
|
|
505
481
|
const nextCount = priorCount + 1;
|
|
@@ -508,8 +484,6 @@ async function performIMessageCatchup(params) {
|
|
|
508
484
|
if (nextCount >= cfg.maxFailureRetries) {
|
|
509
485
|
summary.givenUp += 1;
|
|
510
486
|
params.warn?.(`imessage catchup: giving up on guid=${row.guid} after ${nextCount} failures; advancing cursor past it`);
|
|
511
|
-
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
512
|
-
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
513
487
|
continue;
|
|
514
488
|
}
|
|
515
489
|
if (earliestHeldFailureRow === null || row.rowid < earliestHeldFailureRow.rowid) earliestHeldFailureRow = row;
|
|
@@ -532,11 +506,12 @@ async function performIMessageCatchup(params) {
|
|
|
532
506
|
lastSeenMs,
|
|
533
507
|
lastSeenRowid
|
|
534
508
|
};
|
|
535
|
-
|
|
509
|
+
const next = buildIMessageCatchupCursor({
|
|
536
510
|
lastSeenMs,
|
|
537
511
|
lastSeenRowid,
|
|
538
512
|
failureRetries: capped
|
|
539
|
-
},
|
|
513
|
+
}, Date.now());
|
|
514
|
+
await updateIMessageCatchupCursor(params.accountId, (existing) => decideCatchupCursorSave(existing, next, earliestHeldFailureRow !== null));
|
|
540
515
|
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}`);
|
|
541
516
|
return summary;
|
|
542
517
|
}
|
|
@@ -853,7 +828,7 @@ async function repairIMessageConversationAnchor(params) {
|
|
|
853
828
|
}
|
|
854
829
|
let chatsResult;
|
|
855
830
|
try {
|
|
856
|
-
chatsResult = await client.request("chats.list", { limit:
|
|
831
|
+
chatsResult = await client.request("chats.list", { limit: DEFAULT_CHATS_LIMIT }, { timeoutMs: DEFAULT_RPC_TIMEOUT_MS });
|
|
857
832
|
} catch (err) {
|
|
858
833
|
runtime?.error?.(`imessage: anchorless message recovery failed listing chats: ${String(err)}`);
|
|
859
834
|
return null;
|
|
@@ -868,8 +843,8 @@ async function repairIMessageConversationAnchor(params) {
|
|
|
868
843
|
historyResult = await client.request("messages.history", {
|
|
869
844
|
attachments: false,
|
|
870
845
|
chat_id: chatId,
|
|
871
|
-
limit:
|
|
872
|
-
}, { timeoutMs:
|
|
846
|
+
limit: DEFAULT_PER_CHAT_HISTORY_LIMIT
|
|
847
|
+
}, { timeoutMs: DEFAULT_RPC_TIMEOUT_MS });
|
|
873
848
|
} catch {
|
|
874
849
|
continue;
|
|
875
850
|
}
|
|
@@ -921,7 +896,7 @@ async function deliverIMessageReply(params) {
|
|
|
921
896
|
const chunkMode = resolveChunkMode(cfg, "imessage", accountId);
|
|
922
897
|
const rawText = sanitizeOutboundText(payload.text ?? "");
|
|
923
898
|
const reply = resolveSendableOutboundReplyParts(payload, { text: convertMarkdownTables(rawText, tableMode) });
|
|
924
|
-
const accepted =
|
|
899
|
+
const accepted = createChannelDeliveryAccumulator({ kind: reply.mediaUrls.length > 0 ? "media" : "text" });
|
|
925
900
|
const sendAccepted = async (text, mediaUrl) => {
|
|
926
901
|
const sent = await sendMessageIMessage(target, text, {
|
|
927
902
|
config: cfg,
|
|
@@ -933,7 +908,7 @@ async function deliverIMessageReply(params) {
|
|
|
933
908
|
accountId,
|
|
934
909
|
replyToId: payload.replyToId
|
|
935
910
|
});
|
|
936
|
-
accepted.
|
|
911
|
+
accepted.add({ receipt: sent.receipt }, sent.sentText);
|
|
937
912
|
const echoText = sent.echoText ?? (sent.sentText || void 0);
|
|
938
913
|
sentMessageCache?.remember(scope, {
|
|
939
914
|
...echoText ? { text: echoText } : {},
|
|
@@ -951,25 +926,10 @@ async function deliverIMessageReply(params) {
|
|
|
951
926
|
sendMedia: ({ mediaUrl, caption }) => sendAccepted(caption ?? "", mediaUrl)
|
|
952
927
|
});
|
|
953
928
|
} catch (error) {
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
deliveryResults: partial ? [partial] : [],
|
|
959
|
-
kind: reply.mediaUrls.length > 0 ? "media" : "text",
|
|
960
|
-
content: [...accepted.map((result) => result.sentText), partial?.content].filter(Boolean).join("\n")
|
|
961
|
-
}));
|
|
962
|
-
}
|
|
963
|
-
if (delivered === "empty") return {
|
|
964
|
-
visibleReplySent: false,
|
|
965
|
-
suppression: { reason: "no_visible_result" }
|
|
966
|
-
};
|
|
967
|
-
const deliveryResult = createAcceptedChannelDeliveryResult({
|
|
968
|
-
results: accepted.map((result) => ({ receipt: result.receipt })),
|
|
969
|
-
kind: delivered,
|
|
970
|
-
content: accepted.map((result) => result.sentText).filter(Boolean).join("\n")
|
|
971
|
-
});
|
|
972
|
-
runtime.log?.(`imessage: delivered reply to ${target}`);
|
|
929
|
+
throw accepted.partialError(error);
|
|
930
|
+
}
|
|
931
|
+
const deliveryResult = accepted.result();
|
|
932
|
+
if (delivered !== "empty") runtime.log?.(`imessage: delivered reply to ${target}`);
|
|
973
933
|
return deliveryResult;
|
|
974
934
|
}
|
|
975
935
|
function createIMessageEchoCachingSend(params) {
|
|
@@ -1070,29 +1030,27 @@ function createIMessageThrottledDropDiagnosticCache() {
|
|
|
1070
1030
|
//#region extensions/imessage/src/monitor/echo-cache.ts
|
|
1071
1031
|
const SENT_MESSAGE_TEXT_TTL_MS = 4e3;
|
|
1072
1032
|
const SENT_MESSAGE_ID_TTL_MS = 6e4;
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
}
|
|
1081
|
-
remember(scope, lookup) {
|
|
1033
|
+
function createSentMessageCache() {
|
|
1034
|
+
const textCache = /* @__PURE__ */ new Map();
|
|
1035
|
+
const textBackedByIdCache = /* @__PURE__ */ new Map();
|
|
1036
|
+
const mediaCache = /* @__PURE__ */ new Map();
|
|
1037
|
+
const mediaBackedByIdCache = /* @__PURE__ */ new Map();
|
|
1038
|
+
const messageIdCache = /* @__PURE__ */ new Map();
|
|
1039
|
+
function remember(scope, lookup) {
|
|
1082
1040
|
const textKey = normalizeIMessageEchoText(lookup.text);
|
|
1083
|
-
if (textKey)
|
|
1041
|
+
if (textKey) textCache.set(`${scope}:${textKey}`, Date.now());
|
|
1084
1042
|
const mediaKey = resolveIMessageEchoMediaKey(lookup.media);
|
|
1085
|
-
if (mediaKey)
|
|
1043
|
+
if (mediaKey) mediaCache.set(`${scope}:${mediaKey}`, Date.now());
|
|
1086
1044
|
const messageIdKey = normalizeIMessageMessageId(lookup.messageId);
|
|
1087
1045
|
if (messageIdKey) {
|
|
1088
|
-
|
|
1089
|
-
if (textKey)
|
|
1090
|
-
if (mediaKey)
|
|
1046
|
+
messageIdCache.set(`${scope}:${messageIdKey}`, Date.now());
|
|
1047
|
+
if (textKey) textBackedByIdCache.set(`${scope}:${textKey}`, Date.now());
|
|
1048
|
+
if (mediaKey) mediaBackedByIdCache.set(`${scope}:${mediaKey}`, Date.now());
|
|
1091
1049
|
}
|
|
1092
|
-
|
|
1050
|
+
cleanup();
|
|
1093
1051
|
}
|
|
1094
|
-
async has(scope, lookup, options = false) {
|
|
1095
|
-
|
|
1052
|
+
async function has(scope, lookup, options = false) {
|
|
1053
|
+
cleanup();
|
|
1096
1054
|
const resolvedOptions = typeof options === "boolean" ? { skipIdShortCircuit: options } : options;
|
|
1097
1055
|
if (await hasPersistedIMessageEcho({
|
|
1098
1056
|
scope,
|
|
@@ -1102,48 +1060,45 @@ var DefaultSentMessageCache = class {
|
|
|
1102
1060
|
skipIdShortCircuit: resolvedOptions.skipIdShortCircuit,
|
|
1103
1061
|
includePendingText: resolvedOptions.includePendingText
|
|
1104
1062
|
})) return true;
|
|
1063
|
+
const matchesWithin = (cache, key, ttlMs) => {
|
|
1064
|
+
const timestamp = key ? cache.get(`${scope}:${key}`) : void 0;
|
|
1065
|
+
return Boolean(timestamp && Date.now() - timestamp <= ttlMs);
|
|
1066
|
+
};
|
|
1067
|
+
const hasContentOnlyMatch = (cache, backedById, key) => {
|
|
1068
|
+
const timestamp = key ? cache.get(`${scope}:${key}`) : void 0;
|
|
1069
|
+
const idTimestamp = key ? backedById.get(`${scope}:${key}`) : void 0;
|
|
1070
|
+
return typeof timestamp === "number" && (!idTimestamp || timestamp > idTimestamp);
|
|
1071
|
+
};
|
|
1105
1072
|
const textKey = normalizeIMessageEchoText(lookup.text);
|
|
1106
1073
|
const mediaKey = resolveIMessageEchoMediaKey(lookup.media);
|
|
1107
1074
|
const messageIdKey = normalizeIMessageMessageId(lookup.messageId);
|
|
1108
1075
|
let canUseMediaFallback = !messageIdKey;
|
|
1109
1076
|
if (messageIdKey) {
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
const
|
|
1113
|
-
const textBackedByIdTimestamp = textKey ? this.textBackedByIdCache.get(`${scope}:${textKey}`) : void 0;
|
|
1114
|
-
const hasTextOnlyMatch = typeof textTimestamp === "number" && (!textBackedByIdTimestamp || textTimestamp > textBackedByIdTimestamp);
|
|
1115
|
-
const mediaTimestamp = mediaKey ? this.mediaCache.get(`${scope}:${mediaKey}`) : void 0;
|
|
1116
|
-
const mediaBackedByIdTimestamp = mediaKey ? this.mediaBackedByIdCache.get(`${scope}:${mediaKey}`) : void 0;
|
|
1117
|
-
const hasMediaOnlyMatch = typeof mediaTimestamp === "number" && (!mediaBackedByIdTimestamp || mediaTimestamp > mediaBackedByIdTimestamp);
|
|
1077
|
+
if (matchesWithin(messageIdCache, messageIdKey, SENT_MESSAGE_ID_TTL_MS)) return true;
|
|
1078
|
+
const hasTextOnlyMatch = hasContentOnlyMatch(textCache, textBackedByIdCache, textKey);
|
|
1079
|
+
const hasMediaOnlyMatch = hasContentOnlyMatch(mediaCache, mediaBackedByIdCache, mediaKey);
|
|
1118
1080
|
canUseMediaFallback = hasMediaOnlyMatch;
|
|
1119
1081
|
if (!resolvedOptions.skipIdShortCircuit && !hasTextOnlyMatch && !hasMediaOnlyMatch) return false;
|
|
1120
1082
|
}
|
|
1121
|
-
|
|
1122
|
-
const textTimestamp = this.textCache.get(`${scope}:${textKey}`);
|
|
1123
|
-
if (textTimestamp && Date.now() - textTimestamp <= SENT_MESSAGE_TEXT_TTL_MS) return true;
|
|
1124
|
-
}
|
|
1125
|
-
if (mediaKey && canUseMediaFallback) {
|
|
1126
|
-
const mediaTimestamp = this.mediaCache.get(`${scope}:${mediaKey}`);
|
|
1127
|
-
if (mediaTimestamp && Date.now() - mediaTimestamp <= SENT_MESSAGE_TEXT_TTL_MS) return true;
|
|
1128
|
-
}
|
|
1129
|
-
return false;
|
|
1083
|
+
return matchesWithin(textCache, textKey, SENT_MESSAGE_TEXT_TTL_MS) || canUseMediaFallback && matchesWithin(mediaCache, mediaKey, SENT_MESSAGE_TEXT_TTL_MS);
|
|
1130
1084
|
}
|
|
1131
|
-
cleanup() {
|
|
1085
|
+
function cleanup() {
|
|
1132
1086
|
const now = Date.now();
|
|
1133
1087
|
for (const cache of [
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1088
|
+
textCache,
|
|
1089
|
+
textBackedByIdCache,
|
|
1090
|
+
mediaCache,
|
|
1091
|
+
mediaBackedByIdCache,
|
|
1092
|
+
messageIdCache
|
|
1139
1093
|
]) {
|
|
1140
|
-
const ttlMs = cache ===
|
|
1094
|
+
const ttlMs = cache === messageIdCache ? SENT_MESSAGE_ID_TTL_MS : SENT_MESSAGE_TEXT_TTL_MS;
|
|
1141
1095
|
for (const [key, timestamp] of cache) if (now - timestamp > ttlMs) cache.delete(key);
|
|
1142
1096
|
}
|
|
1143
1097
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1098
|
+
return {
|
|
1099
|
+
remember,
|
|
1100
|
+
has
|
|
1101
|
+
};
|
|
1147
1102
|
}
|
|
1148
1103
|
//#endregion
|
|
1149
1104
|
//#region extensions/imessage/src/monitor/group-allowlist-warnings.ts
|
|
@@ -1338,10 +1293,7 @@ const REFLECTION_PATTERNS = [
|
|
|
1338
1293
|
];
|
|
1339
1294
|
function hasMatchOutsideCode(text, re, codeRegions) {
|
|
1340
1295
|
const globalRe = new RegExp(re.source, re.flags.includes("g") ? re.flags : `${re.flags}g`);
|
|
1341
|
-
for (const match of text.matchAll(globalRe))
|
|
1342
|
-
const start = match.index ?? -1;
|
|
1343
|
-
if (start >= 0 && !isInsideCode(start, codeRegions)) return true;
|
|
1344
|
-
}
|
|
1296
|
+
for (const match of text.matchAll(globalRe)) if (!isInsideCode(match.index, codeRegions)) return true;
|
|
1345
1297
|
return false;
|
|
1346
1298
|
}
|
|
1347
1299
|
/**
|
|
@@ -1372,13 +1324,12 @@ const imessageConversationIdentityKinds = /* @__PURE__ */ new Set([
|
|
|
1372
1324
|
const matchIMessageIngressEntry = ({ entry, context }) => {
|
|
1373
1325
|
if (imessageConversationIdentityKinds.has(entry.kind) && context !== "group") return false;
|
|
1374
1326
|
};
|
|
1375
|
-
function isIMessageConversationAllowTarget(entry) {
|
|
1376
|
-
const parsed = parseIMessageAllowTarget(entry);
|
|
1377
|
-
return parsed.kind === "chat_id" || parsed.kind === "chat_guid" || parsed.kind === "chat_identifier";
|
|
1378
|
-
}
|
|
1379
1327
|
function mergeIMessageGroupAllowFromWithLegacyChatTargets(params) {
|
|
1380
1328
|
if (params.groupAllowFrom.length > 0 || !params.allowLegacyConversationTargets) return params.groupAllowFrom;
|
|
1381
|
-
const legacyChatTargets = params.allowFrom.filter((entry) =>
|
|
1329
|
+
const legacyChatTargets = params.allowFrom.filter((entry) => {
|
|
1330
|
+
const parsed = parseIMessageAllowTarget(entry);
|
|
1331
|
+
return parsed.kind === "chat_id" || parsed.kind === "chat_guid" || parsed.kind === "chat_identifier";
|
|
1332
|
+
});
|
|
1382
1333
|
if (legacyChatTargets.length === 0) return params.groupAllowFrom;
|
|
1383
1334
|
return uniqueStrings([...params.groupAllowFrom, ...legacyChatTargets]);
|
|
1384
1335
|
}
|
|
@@ -1429,25 +1380,9 @@ function normalizeIMessageChatIdentifierEntry(entry) {
|
|
|
1429
1380
|
const parsed = parseIMessageAllowTarget(entry.trim());
|
|
1430
1381
|
return parsed.kind === "chat_identifier" ? parsed.chatIdentifier.trim() || null : null;
|
|
1431
1382
|
}
|
|
1432
|
-
function normalizeGroupPolicy(policy) {
|
|
1433
|
-
return policy === "open" || policy === "disabled" ? policy : "allowlist";
|
|
1434
|
-
}
|
|
1435
1383
|
function normalizeReplyField(value) {
|
|
1436
1384
|
return typeof value === "number" ? String(value) : normalizeOptionalString(value);
|
|
1437
1385
|
}
|
|
1438
|
-
function describeReplyContext(message) {
|
|
1439
|
-
const body = normalizeReplyField(message.reply_to_text);
|
|
1440
|
-
if (!body) return null;
|
|
1441
|
-
return {
|
|
1442
|
-
body,
|
|
1443
|
-
id: normalizeReplyField(message.thread_originator_guid) ?? normalizeReplyField(message.reply_to_guid),
|
|
1444
|
-
sender: normalizeReplyField(message.reply_to_sender)
|
|
1445
|
-
};
|
|
1446
|
-
}
|
|
1447
|
-
function resolveInboundEchoMessageIds(message) {
|
|
1448
|
-
const values = [message.id != null ? String(message.id) : void 0, normalizeReplyField(message.guid)];
|
|
1449
|
-
return uniqueStrings(values.filter((value) => Boolean(value)));
|
|
1450
|
-
}
|
|
1451
1386
|
function classifyIMessageSelfChat(message, isGroup, senderNormalized) {
|
|
1452
1387
|
const chatIdentifier = normalizeIMessageHandle(message.chat_identifier ?? "") || void 0;
|
|
1453
1388
|
const destination = normalizeIMessageHandle(message.destination_caller_id ?? "") || void 0;
|
|
@@ -1480,9 +1415,7 @@ function rememberIMessageSkippedFromMeForSelfChatDedupe(params) {
|
|
|
1480
1415
|
else if (isAmbiguousSelfThread) params.selfChatCache?.remember(lookup);
|
|
1481
1416
|
}
|
|
1482
1417
|
async function hasIMessageEchoMatch(params) {
|
|
1483
|
-
const
|
|
1484
|
-
for (const scope of scopes) {
|
|
1485
|
-
if (!scope) continue;
|
|
1418
|
+
for (const scope of params.scope) {
|
|
1486
1419
|
for (const messageId of params.messageIds) if (await params.echoCache.has(scope, { messageId })) return true;
|
|
1487
1420
|
const fallbackMessageId = params.messageIds[0];
|
|
1488
1421
|
if (!params.text && !params.media && !fallbackMessageId) continue;
|
|
@@ -1542,17 +1475,16 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1542
1475
|
};
|
|
1543
1476
|
const { isSelfChat, isAmbiguousSelfThread } = classifyIMessageSelfChat(params.message, isGroup, senderNormalized);
|
|
1544
1477
|
let skipSelfChatHasCheck = false;
|
|
1545
|
-
const inboundMessageIds =
|
|
1478
|
+
const inboundMessageIds = uniqueStrings([params.message.id != null ? String(params.message.id) : void 0, normalizeReplyField(params.message.guid)].filter((value) => Boolean(value)));
|
|
1546
1479
|
const inboundMessageId = inboundMessageIds[0];
|
|
1547
1480
|
const hasInboundGuid = Boolean(normalizeReplyField(params.message.guid));
|
|
1548
|
-
const echoScope =
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
});
|
|
1481
|
+
const echoScope = [];
|
|
1482
|
+
if (isGroup) {
|
|
1483
|
+
const chatIdScope = formatIMessageChatTarget(chatId);
|
|
1484
|
+
if (chatIdScope) echoScope.push(`${params.accountId}:${chatIdScope}`);
|
|
1485
|
+
} else echoScope.push(`${params.accountId}:imessage:${sender}`);
|
|
1486
|
+
if (chatGuid) echoScope.push(`${params.accountId}:chat_guid:${chatGuid}`);
|
|
1487
|
+
if (chatIdentifier) echoScope.push(`${params.accountId}:chat_identifier:${chatIdentifier}`);
|
|
1556
1488
|
if (params.message.is_from_me) {
|
|
1557
1489
|
if (isAmbiguousSelfThread) params.selfChatCache?.remember(selfChatLookup);
|
|
1558
1490
|
if (isSelfChat) {
|
|
@@ -1615,7 +1547,7 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1615
1547
|
},
|
|
1616
1548
|
contextBinding,
|
|
1617
1549
|
dmPolicy: normalizeChannelDmPolicy(params.dmPolicy) ?? "pairing",
|
|
1618
|
-
groupPolicy:
|
|
1550
|
+
groupPolicy: params.groupPolicy === "open" || params.groupPolicy === "disabled" ? params.groupPolicy : "allowlist",
|
|
1619
1551
|
policy: { groupAllowFromFallbackToAllowFrom: false },
|
|
1620
1552
|
allowFrom: params.allowFrom,
|
|
1621
1553
|
groupAllowFrom: groupAllowFromForAccess,
|
|
@@ -1749,10 +1681,7 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1749
1681
|
messageIds: inboundMessageIds,
|
|
1750
1682
|
includePendingText: isSelfChat
|
|
1751
1683
|
})) {
|
|
1752
|
-
params.logVerbose?.(
|
|
1753
|
-
messageText: bodyText,
|
|
1754
|
-
messageId: inboundMessageId
|
|
1755
|
-
}));
|
|
1684
|
+
params.logVerbose?.(`imessage: skipping echo message${inboundMessageId ? ` id=${inboundMessageId}` : ""}: "${truncateUtf16Safe(bodyText, 50)}"`);
|
|
1756
1685
|
return {
|
|
1757
1686
|
kind: "drop",
|
|
1758
1687
|
reason: "echo"
|
|
@@ -1767,7 +1696,12 @@ async function resolveIMessageInboundDecision(params) {
|
|
|
1767
1696
|
reason: "reflected assistant content"
|
|
1768
1697
|
};
|
|
1769
1698
|
}
|
|
1770
|
-
const
|
|
1699
|
+
const replyBody = normalizeReplyField(params.message.reply_to_text);
|
|
1700
|
+
const replyContext = replyBody ? {
|
|
1701
|
+
body: replyBody,
|
|
1702
|
+
id: normalizeReplyField(params.message.thread_originator_guid) ?? normalizeReplyField(params.message.reply_to_guid),
|
|
1703
|
+
sender: normalizeReplyField(params.message.reply_to_sender)
|
|
1704
|
+
} : null;
|
|
1771
1705
|
const contextVisibilityMode = resolveChannelContextVisibilityMode({
|
|
1772
1706
|
cfg: params.cfg,
|
|
1773
1707
|
channel: "imessage",
|
|
@@ -2020,20 +1954,6 @@ async function buildIMessageInboundContext(params) {
|
|
|
2020
1954
|
inboundHistory
|
|
2021
1955
|
};
|
|
2022
1956
|
}
|
|
2023
|
-
function buildIMessageEchoScope(params) {
|
|
2024
|
-
const scopes = [];
|
|
2025
|
-
if (params.isGroup) {
|
|
2026
|
-
const chatIdScope = formatIMessageChatTarget(params.chatId);
|
|
2027
|
-
if (chatIdScope) scopes.push(`${params.accountId}:${chatIdScope}`);
|
|
2028
|
-
} else scopes.push(`${params.accountId}:imessage:${params.sender}`);
|
|
2029
|
-
if (params.chatGuid) scopes.push(`${params.accountId}:chat_guid:${params.chatGuid}`);
|
|
2030
|
-
if (params.chatIdentifier) scopes.push(`${params.accountId}:chat_identifier:${params.chatIdentifier}`);
|
|
2031
|
-
return scopes;
|
|
2032
|
-
}
|
|
2033
|
-
function describeIMessageEchoDropLog(params) {
|
|
2034
|
-
const preview = truncateUtf16Safe(params.messageText, 50);
|
|
2035
|
-
return `imessage: skipping echo message${params.messageId ? ` id=${params.messageId}` : ""}: "${preview}"`;
|
|
2036
|
-
}
|
|
2037
1957
|
//#endregion
|
|
2038
1958
|
//#region extensions/imessage/src/monitor/ingress.ts
|
|
2039
1959
|
const IMESSAGE_INGRESS_PAYLOAD_VERSION = 1;
|
|
@@ -2195,9 +2115,7 @@ function createIMessageDurableIngress(options) {
|
|
|
2195
2115
|
const DEFAULT_WINDOW_MS = 6e4;
|
|
2196
2116
|
const DEFAULT_MAX_HITS = 5;
|
|
2197
2117
|
const CLEANUP_INTERVAL_MS = 12e4;
|
|
2198
|
-
function createLoopRateLimiter(
|
|
2199
|
-
const windowMs = opts?.windowMs ?? DEFAULT_WINDOW_MS;
|
|
2200
|
-
const maxHits = opts?.maxHits ?? DEFAULT_MAX_HITS;
|
|
2118
|
+
function createLoopRateLimiter() {
|
|
2201
2119
|
const conversations = /* @__PURE__ */ new Map();
|
|
2202
2120
|
let lastCleanup = Date.now();
|
|
2203
2121
|
function cleanup() {
|
|
@@ -2205,7 +2123,7 @@ function createLoopRateLimiter(opts) {
|
|
|
2205
2123
|
if (now - lastCleanup < CLEANUP_INTERVAL_MS) return;
|
|
2206
2124
|
lastCleanup = now;
|
|
2207
2125
|
for (const [key, timestamps] of conversations.entries()) {
|
|
2208
|
-
const recent = timestamps.filter((ts) => now - ts <=
|
|
2126
|
+
const recent = timestamps.filter((ts) => now - ts <= DEFAULT_WINDOW_MS);
|
|
2209
2127
|
if (recent.length === 0) conversations.delete(key);
|
|
2210
2128
|
else conversations.set(key, recent);
|
|
2211
2129
|
}
|
|
@@ -2225,9 +2143,9 @@ function createLoopRateLimiter(opts) {
|
|
|
2225
2143
|
const timestamps = conversations.get(conversationKey);
|
|
2226
2144
|
if (!timestamps) return false;
|
|
2227
2145
|
const now = Date.now();
|
|
2228
|
-
const recent = timestamps.filter((ts) => now - ts <=
|
|
2146
|
+
const recent = timestamps.filter((ts) => now - ts <= DEFAULT_WINDOW_MS);
|
|
2229
2147
|
conversations.set(conversationKey, recent);
|
|
2230
|
-
return recent.length >=
|
|
2148
|
+
return recent.length >= DEFAULT_MAX_HITS;
|
|
2231
2149
|
}
|
|
2232
2150
|
};
|
|
2233
2151
|
}
|
|
@@ -2246,37 +2164,22 @@ function isHeicAttachment(attachmentPath, mimeType) {
|
|
|
2246
2164
|
const ext = path.extname(attachmentPath).toLowerCase();
|
|
2247
2165
|
return ext === ".heic" || ext === ".heif";
|
|
2248
2166
|
}
|
|
2249
|
-
function jpegFilenameForAttachment(attachmentPath) {
|
|
2250
|
-
return `${path.parse(attachmentPath).name || "imessage-attachment"}.jpg`;
|
|
2251
|
-
}
|
|
2252
|
-
function hasWildcardSegment(root) {
|
|
2253
|
-
return root.replaceAll("\\", "/").split("/").includes("*");
|
|
2254
|
-
}
|
|
2255
|
-
async function canonicalizeAllowedRoots(roots) {
|
|
2256
|
-
const canonicalRoots = [];
|
|
2257
|
-
for (const root of roots) {
|
|
2258
|
-
canonicalRoots.push(root);
|
|
2259
|
-
if (hasWildcardSegment(root)) continue;
|
|
2260
|
-
const canonicalRoot = await fs.realpath(root).catch(() => void 0);
|
|
2261
|
-
if (canonicalRoot && canonicalRoot !== root) canonicalRoots.push(canonicalRoot);
|
|
2262
|
-
}
|
|
2263
|
-
return canonicalRoots;
|
|
2264
|
-
}
|
|
2265
|
-
async function assertAllowedCanonicalAttachmentPath(params) {
|
|
2266
|
-
if (!params.allowedRoots) return;
|
|
2267
|
-
const canonicalRoots = await canonicalizeAllowedRoots(params.allowedRoots);
|
|
2268
|
-
if (!isInboundPathAllowed({
|
|
2269
|
-
filePath: params.canonicalPath,
|
|
2270
|
-
roots: canonicalRoots
|
|
2271
|
-
})) throw new Error("attachment path resolves outside allowed roots");
|
|
2272
|
-
}
|
|
2273
2167
|
async function readAttachmentBuffer(params) {
|
|
2274
2168
|
await using opened = await (params.deps.openLocalFileSafely ?? openLocalFileSafely)({ filePath: params.attachmentPath });
|
|
2275
2169
|
if (opened.stat.size > params.maxBytes) throw new Error(`attachment exceeds ${Math.round(params.maxBytes / 1048576)}MB limit`);
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2170
|
+
if (params.allowedRoots) {
|
|
2171
|
+
const canonicalRoots = [];
|
|
2172
|
+
for (const root of params.allowedRoots) {
|
|
2173
|
+
canonicalRoots.push(root);
|
|
2174
|
+
if (root.replaceAll("\\", "/").split("/").includes("*")) continue;
|
|
2175
|
+
const canonicalRoot = await fs.realpath(root).catch(() => void 0);
|
|
2176
|
+
if (canonicalRoot && canonicalRoot !== root) canonicalRoots.push(canonicalRoot);
|
|
2177
|
+
}
|
|
2178
|
+
if (!isInboundPathAllowed({
|
|
2179
|
+
filePath: opened.realPath,
|
|
2180
|
+
roots: canonicalRoots
|
|
2181
|
+
})) throw new Error("attachment path resolves outside allowed roots");
|
|
2182
|
+
}
|
|
2280
2183
|
const buffer = await readFileHandleBounded(opened.handle, params.maxBytes).catch((error) => {
|
|
2281
2184
|
if (error instanceof FsSafeError && error.code === "too-large") throw new Error(`attachment exceeds ${Math.round(params.maxBytes / 1048576)}MB limit`);
|
|
2282
2185
|
throw error;
|
|
@@ -2295,7 +2198,7 @@ async function readAttachmentBuffer(params) {
|
|
|
2295
2198
|
});
|
|
2296
2199
|
})).buffer,
|
|
2297
2200
|
contentType: "image/jpeg",
|
|
2298
|
-
originalFilename:
|
|
2201
|
+
originalFilename: `${path.parse(params.attachmentPath).name || "imessage-attachment"}.jpg`
|
|
2299
2202
|
};
|
|
2300
2203
|
} catch (err) {
|
|
2301
2204
|
params.deps.logVerbose?.(`imessage: HEIC attachment conversion failed; staging original instead: ${String(err)}`);
|
|
@@ -2347,12 +2250,6 @@ async function stageIMessageAttachments(attachments, params) {
|
|
|
2347
2250
|
//#endregion
|
|
2348
2251
|
//#region extensions/imessage/src/monitor/poll-comment.ts
|
|
2349
2252
|
const DEFAULT_COMMENT_WINDOW_MS = 15e3;
|
|
2350
|
-
function normalizeGuid(guid) {
|
|
2351
|
-
return guid?.trim() ?? "";
|
|
2352
|
-
}
|
|
2353
|
-
function normalizeSender(sender) {
|
|
2354
|
-
return sender?.trim().toLowerCase() ?? "";
|
|
2355
|
-
}
|
|
2356
2253
|
function createPollCommentFolder(options) {
|
|
2357
2254
|
const windowMs = options?.windowMs ?? DEFAULT_COMMENT_WINDOW_MS;
|
|
2358
2255
|
const seenPolls = /* @__PURE__ */ new Map();
|
|
@@ -2361,20 +2258,20 @@ function createPollCommentFolder(options) {
|
|
|
2361
2258
|
}
|
|
2362
2259
|
return {
|
|
2363
2260
|
rememberPoll(guid, atMs, sender) {
|
|
2364
|
-
const key =
|
|
2261
|
+
const key = guid?.trim();
|
|
2365
2262
|
if (!key || !Number.isFinite(atMs)) return;
|
|
2366
2263
|
prune(atMs);
|
|
2367
2264
|
seenPolls.set(key, {
|
|
2368
2265
|
atMs,
|
|
2369
|
-
sender:
|
|
2266
|
+
sender: normalizeLowercaseStringOrEmpty(sender)
|
|
2370
2267
|
});
|
|
2371
2268
|
},
|
|
2372
2269
|
isPollComment(replyToGuid, atMs, sender) {
|
|
2373
|
-
const key =
|
|
2270
|
+
const key = replyToGuid?.trim();
|
|
2374
2271
|
if (!key || !Number.isFinite(atMs)) return false;
|
|
2375
2272
|
const seen = seenPolls.get(key);
|
|
2376
2273
|
if (!seen || atMs < seen.atMs || atMs - seen.atMs > windowMs) return false;
|
|
2377
|
-
const replySender =
|
|
2274
|
+
const replySender = normalizeLowercaseStringOrEmpty(sender);
|
|
2378
2275
|
return seen.sender.length > 0 && replySender.length > 0 && seen.sender === replySender;
|
|
2379
2276
|
}
|
|
2380
2277
|
};
|
|
@@ -2599,7 +2496,6 @@ var DefaultSelfChatCache = class {
|
|
|
2599
2496
|
this.cache = /* @__PURE__ */ new Map();
|
|
2600
2497
|
this.insertionOrder = /* @__PURE__ */ new Map();
|
|
2601
2498
|
this.lastCleanupAt = 0;
|
|
2602
|
-
this.nextEntryId = 1;
|
|
2603
2499
|
}
|
|
2604
2500
|
buildBucketKey(lookup) {
|
|
2605
2501
|
const text = normalizeText(lookup.text);
|
|
@@ -2609,16 +2505,15 @@ var DefaultSelfChatCache = class {
|
|
|
2609
2505
|
remember(lookup) {
|
|
2610
2506
|
const key = this.buildBucketKey(lookup);
|
|
2611
2507
|
if (!key || !isUsableTimestamp(lookup.createdAt)) return;
|
|
2612
|
-
const entries = this.cache.get(key) ?? /* @__PURE__ */ new
|
|
2508
|
+
const entries = this.cache.get(key) ?? /* @__PURE__ */ new Set();
|
|
2613
2509
|
const entry = {
|
|
2614
2510
|
createdAt: lookup.createdAt,
|
|
2615
2511
|
createdAtSkewToleranceMs: lookup.allowCreatedAtSkew ? SELF_CHAT_CREATED_AT_TOLERANCE_MS : 0,
|
|
2616
2512
|
rememberedAt: Date.now()
|
|
2617
2513
|
};
|
|
2618
|
-
|
|
2619
|
-
entries.set(id, entry);
|
|
2514
|
+
entries.add(entry);
|
|
2620
2515
|
this.cache.set(key, entries);
|
|
2621
|
-
this.insertionOrder.set(
|
|
2516
|
+
this.insertionOrder.set(entry, key);
|
|
2622
2517
|
this.maybeCleanup();
|
|
2623
2518
|
}
|
|
2624
2519
|
has(lookup) {
|
|
@@ -2634,24 +2529,20 @@ var DefaultSelfChatCache = class {
|
|
|
2634
2529
|
return now - entry.rememberedAt <= SELF_CHAT_TTL_MS && (createdAtDelta === 0 || createdAtDelta < entry.createdAtSkewToleranceMs);
|
|
2635
2530
|
});
|
|
2636
2531
|
}
|
|
2532
|
+
removeEntry(entry, key) {
|
|
2533
|
+
this.insertionOrder.delete(entry);
|
|
2534
|
+
const entries = this.cache.get(key);
|
|
2535
|
+
entries?.delete(entry);
|
|
2536
|
+
if (entries?.size === 0) this.cache.delete(key);
|
|
2537
|
+
}
|
|
2637
2538
|
maybeCleanup() {
|
|
2638
2539
|
const now = Date.now();
|
|
2639
2540
|
if (now - this.lastCleanupAt < CLEANUP_MIN_INTERVAL_MS) return;
|
|
2640
2541
|
this.lastCleanupAt = now;
|
|
2641
|
-
for (const [
|
|
2642
|
-
for (const [id, entry] of entries.entries()) if (now - entry.rememberedAt > SELF_CHAT_TTL_MS) {
|
|
2643
|
-
entries.delete(id);
|
|
2644
|
-
this.insertionOrder.delete(id);
|
|
2645
|
-
}
|
|
2646
|
-
if (entries.size === 0) this.cache.delete(key);
|
|
2647
|
-
}
|
|
2542
|
+
for (const [entry, key] of this.insertionOrder) if (now - entry.rememberedAt > SELF_CHAT_TTL_MS) this.removeEntry(entry, key);
|
|
2648
2543
|
while (this.insertionOrder.size > MAX_SELF_CHAT_CACHE_ENTRIES) {
|
|
2649
|
-
const [
|
|
2650
|
-
this.
|
|
2651
|
-
const entries = this.cache.get(key);
|
|
2652
|
-
if (!entries) continue;
|
|
2653
|
-
entries.delete(id);
|
|
2654
|
-
if (entries.size === 0) this.cache.delete(key);
|
|
2544
|
+
const [entry, key] = expectDefined(this.insertionOrder.entries().next().value, "oldest iMessage self-chat cache entry");
|
|
2545
|
+
this.removeEntry(entry, key);
|
|
2655
2546
|
}
|
|
2656
2547
|
}
|
|
2657
2548
|
};
|
|
@@ -2680,53 +2571,12 @@ const APPROVAL_REACTION_POLL_INTERVAL_MS = 2e3;
|
|
|
2680
2571
|
const APPROVAL_REACTION_DISCOVERY_INTERVAL_MS = 6e4;
|
|
2681
2572
|
const IMESSAGE_TYPING_KEEPALIVE_INTERVAL_MS = 8e3;
|
|
2682
2573
|
const IMESSAGE_TYPING_KEEPALIVE_MAX_DURATION_MS = 6e5;
|
|
2683
|
-
function resolveConfiguredIMessageTypingMode(cfg, agentId) {
|
|
2684
|
-
return resolveAgentConfig(cfg, agentId)?.typingMode ?? cfg.agents?.defaults?.typingMode;
|
|
2685
|
-
}
|
|
2686
2574
|
function isIMessagePluginPayloadAttachment(attachment) {
|
|
2687
2575
|
const attachmentPath = attachment.original_path?.trim().toLowerCase() ?? "";
|
|
2688
2576
|
const transferName = attachment.transfer_name?.trim().toLowerCase() ?? "";
|
|
2689
2577
|
const uti = attachment.uti?.trim().toLowerCase() ?? "";
|
|
2690
2578
|
return attachmentPath.endsWith(".pluginpayloadattachment") || transferName.endsWith(".pluginpayloadattachment") || uti === "com.apple.messages.pluginpayloadattachment";
|
|
2691
2579
|
}
|
|
2692
|
-
function resolveIMessageInboundMediaInput(params) {
|
|
2693
|
-
const mediaCandidates = params.attachments.filter((entry) => !isIMessagePluginPayloadAttachment(entry));
|
|
2694
|
-
const mediaFacts = mediaCandidates.map((attachment) => {
|
|
2695
|
-
const contentType = attachment.mime_type?.trim() || void 0;
|
|
2696
|
-
return {
|
|
2697
|
-
contentType,
|
|
2698
|
-
kind: kindFromMime(contentType) ?? "unknown"
|
|
2699
|
-
};
|
|
2700
|
-
});
|
|
2701
|
-
const rawMediaAttachments = mediaCandidates.map((attachment, index) => {
|
|
2702
|
-
const fact = mediaFacts[index] ?? { kind: "unknown" };
|
|
2703
|
-
const attachmentPath = attachment.original_path?.trim();
|
|
2704
|
-
if (!attachmentPath || attachment.missing) return fact;
|
|
2705
|
-
if (!isInboundPathAllowed({
|
|
2706
|
-
filePath: attachmentPath,
|
|
2707
|
-
roots: params.effectiveAttachmentRoots
|
|
2708
|
-
})) {
|
|
2709
|
-
params.logVerbose?.(`imessage: dropping inbound attachment outside allowed roots: ${attachmentPath}`);
|
|
2710
|
-
return fact;
|
|
2711
|
-
}
|
|
2712
|
-
return {
|
|
2713
|
-
...fact,
|
|
2714
|
-
path: attachmentPath
|
|
2715
|
-
};
|
|
2716
|
-
});
|
|
2717
|
-
return {
|
|
2718
|
-
bodyText: params.messageText,
|
|
2719
|
-
mediaFacts,
|
|
2720
|
-
mediaCandidates,
|
|
2721
|
-
rawMediaAttachments
|
|
2722
|
-
};
|
|
2723
|
-
}
|
|
2724
|
-
function formatIMessageInboundMediaBody(params) {
|
|
2725
|
-
return formatInboundMediaUnavailableText({
|
|
2726
|
-
body: params.messageText,
|
|
2727
|
-
notice: `[imessage ${params.unavailableCount > 1 ? `${params.unavailableCount} attachments` : "attachment"} unavailable]`
|
|
2728
|
-
});
|
|
2729
|
-
}
|
|
2730
2580
|
const warnIfImsgUpgradeNeeded = (() => {
|
|
2731
2581
|
let fired = false;
|
|
2732
2582
|
return { fireOnce: (rpcMethods, runtime) => {
|
|
@@ -2748,16 +2598,6 @@ const IMESSAGE_DIAGNOSTIC_DROP_REASONS = /* @__PURE__ */ new Set([
|
|
|
2748
2598
|
"self-chat echo"
|
|
2749
2599
|
]);
|
|
2750
2600
|
const IMESSAGE_THROTTLED_DIAGNOSTIC_DROP_REASONS = /* @__PURE__ */ new Set(["from me", "no mention"]);
|
|
2751
|
-
function describeIMessageInboundDropDiagnostic(params) {
|
|
2752
|
-
if (!IMESSAGE_DIAGNOSTIC_DROP_REASONS.has(params.reason)) return null;
|
|
2753
|
-
const messageId = typeof params.message.id === "number" || typeof params.message.id === "string" ? String(params.message.id) : "unknown";
|
|
2754
|
-
const mentionHint = params.reason === "no mention" ? ` Mention the agent (default patterns come from its identity name/emoji), or set ${params.groupsConfigPath}["${params.message.chat_id}"].requireMention=false. Preserve existing groups entries; when adding the first groups map, include "*": {} to keep other chats admitted.` : "";
|
|
2755
|
-
return `imessage: dropped inbound message account=${params.accountId} reason=${JSON.stringify(params.reason)} chat_id=${params.message.chat_id ?? "unknown"} group=${params.message.is_group === true} message_id=${messageId} guid=${params.message.guid ? "present" : "missing"} created_at=${params.message.created_at ?? "unknown"}${mentionHint}`;
|
|
2756
|
-
}
|
|
2757
|
-
function describeIMessageWatchSubscribeStartupFailure(params) {
|
|
2758
|
-
const retry = params.retryDelayMs !== void 0 ? ` retry_in_ms=${params.retryDelayMs}` : "";
|
|
2759
|
-
return `imessage: watch.subscribe startup failed attempt=${params.attempt}/${params.maxAttempts} account=${params.accountId} cliPath=${params.cliPath} dbPath=${params.dbPath ? "configured" : "default"} remoteHost=${params.remoteHost ? "configured" : "none"} timeoutMs=${params.probeTimeoutMs} since_rowid=${params.watchSinceRowid ?? "none"} attachments=${params.includeAttachments} include_reactions=true${retry}: ${String(params.error)}`;
|
|
2760
|
-
}
|
|
2761
2601
|
async function monitorIMessageProvider(opts) {
|
|
2762
2602
|
const { scheduler } = opts;
|
|
2763
2603
|
const runtime = opts.runtime ?? createNonExitingRuntime();
|
|
@@ -2785,8 +2625,8 @@ async function monitorIMessageProvider(opts) {
|
|
|
2785
2625
|
const selfChatCache = createSelfChatCache();
|
|
2786
2626
|
const loopRateLimiter = createLoopRateLimiter();
|
|
2787
2627
|
const textLimit = resolveTextChunkLimit(cfg, "imessage", accountInfo.accountId);
|
|
2788
|
-
const allowFrom = normalizeStringEntries(
|
|
2789
|
-
const configuredGroupAllowFrom =
|
|
2628
|
+
const allowFrom = normalizeStringEntries(imessageCfg.allowFrom);
|
|
2629
|
+
const configuredGroupAllowFrom = imessageCfg.groupAllowFrom;
|
|
2790
2630
|
const groupAllowFrom = normalizeStringEntries(configuredGroupAllowFrom ?? (imessageCfg.allowFrom && imessageCfg.allowFrom.length > 0 ? imessageCfg.allowFrom : []));
|
|
2791
2631
|
const allowLegacyConversationAllowFromForGroup = configuredGroupAllowFrom == null;
|
|
2792
2632
|
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
|
@@ -2813,10 +2653,10 @@ async function monitorIMessageProvider(opts) {
|
|
|
2813
2653
|
});
|
|
2814
2654
|
const dmPolicy = imessageCfg.dmPolicy ?? "pairing";
|
|
2815
2655
|
const catchupCfg = resolveCatchupConfig(imessageCfg.catchup);
|
|
2816
|
-
const includeAttachments =
|
|
2817
|
-
const mediaMaxBytes = (
|
|
2818
|
-
const cliPath =
|
|
2819
|
-
const dbPath =
|
|
2656
|
+
const includeAttachments = imessageCfg.includeAttachments ?? false;
|
|
2657
|
+
const mediaMaxBytes = (imessageCfg.mediaMaxMb ?? 16) * 1024 * 1024;
|
|
2658
|
+
const cliPath = imessageCfg.cliPath ?? "imsg";
|
|
2659
|
+
const dbPath = imessageCfg.dbPath;
|
|
2820
2660
|
const probeTimeoutMs = imessageCfg.probeTimeoutMs ?? 1e4;
|
|
2821
2661
|
const attachmentRoots = resolveIMessageAttachmentRoots({
|
|
2822
2662
|
cfg,
|
|
@@ -2995,13 +2835,34 @@ async function monitorIMessageProvider(opts) {
|
|
|
2995
2835
|
const messageText = ((message.poll ? renderIMessagePollBody(message.poll, message.sender, { preferOptionId: Boolean(remoteHost) }) : null) ?? message.text ?? "").trim();
|
|
2996
2836
|
const attachments = includeAttachments ? message.attachments ?? [] : [];
|
|
2997
2837
|
const effectiveAttachmentRoots = remoteHost ? remoteAttachmentRoots : attachmentRoots;
|
|
2838
|
+
const mediaCandidates = attachments.filter((entry) => !isIMessagePluginPayloadAttachment(entry));
|
|
2839
|
+
const mediaFacts = mediaCandidates.map((attachment) => {
|
|
2840
|
+
const contentType = attachment.mime_type?.trim() || void 0;
|
|
2841
|
+
return {
|
|
2842
|
+
contentType,
|
|
2843
|
+
kind: kindFromMime(contentType) ?? "unknown"
|
|
2844
|
+
};
|
|
2845
|
+
});
|
|
2998
2846
|
return {
|
|
2999
2847
|
messageText,
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
2848
|
+
bodyText: messageText,
|
|
2849
|
+
mediaFacts,
|
|
2850
|
+
mediaCandidates,
|
|
2851
|
+
rawMediaAttachments: mediaCandidates.map((attachment, index) => {
|
|
2852
|
+
const fact = mediaFacts[index] ?? { kind: "unknown" };
|
|
2853
|
+
const attachmentPath = attachment.original_path?.trim();
|
|
2854
|
+
if (!attachmentPath || attachment.missing) return fact;
|
|
2855
|
+
if (!isInboundPathAllowed({
|
|
2856
|
+
filePath: attachmentPath,
|
|
2857
|
+
roots: effectiveAttachmentRoots
|
|
2858
|
+
})) {
|
|
2859
|
+
logVerbose(`imessage: dropping inbound attachment outside allowed roots: ${attachmentPath}`);
|
|
2860
|
+
return fact;
|
|
2861
|
+
}
|
|
2862
|
+
return {
|
|
2863
|
+
...fact,
|
|
2864
|
+
path: attachmentPath
|
|
2865
|
+
};
|
|
3005
2866
|
}),
|
|
3006
2867
|
effectiveAttachmentRoots
|
|
3007
2868
|
};
|
|
@@ -3049,13 +2910,10 @@ async function monitorIMessageProvider(opts) {
|
|
|
3049
2910
|
const rateLimitKey = `${accountInfo.accountId}:${conversationKey}`;
|
|
3050
2911
|
if (decision.kind === "drop") {
|
|
3051
2912
|
if (decision.reason === "echo" || decision.reason === "reflected assistant content") loopRateLimiter.record(rateLimitKey);
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
groupsConfigPath,
|
|
3055
|
-
|
|
3056
|
-
message
|
|
3057
|
-
});
|
|
3058
|
-
if (diagnostic) {
|
|
2913
|
+
if (IMESSAGE_DIAGNOSTIC_DROP_REASONS.has(decision.reason)) {
|
|
2914
|
+
const messageId = typeof message.id === "number" || typeof message.id === "string" ? String(message.id) : "unknown";
|
|
2915
|
+
const mentionHint = decision.reason === "no mention" ? ` Mention the agent (default patterns come from its identity name/emoji), or set ${groupsConfigPath}["${message.chat_id}"].requireMention=false. Preserve existing groups entries; when adding the first groups map, include "*": {} to keep other chats admitted.` : "";
|
|
2916
|
+
const diagnostic = `imessage: dropped inbound message account=${accountInfo.accountId} reason=${JSON.stringify(decision.reason)} chat_id=${message.chat_id ?? "unknown"} group=${message.is_group === true} message_id=${messageId} guid=${message.guid ? "present" : "missing"} created_at=${message.created_at ?? "unknown"}${mentionHint}`;
|
|
3059
2917
|
const throttleKey = `${rateLimitKey}:${decision.reason}`;
|
|
3060
2918
|
if (!IMESSAGE_THROTTLED_DIAGNOSTIC_DROP_REASONS.has(decision.reason) || !loggedThrottledDropDiagnostics.check(throttleKey)) runtime.log?.(warn(diagnostic));
|
|
3061
2919
|
}
|
|
@@ -3143,7 +3001,7 @@ async function monitorIMessageProvider(opts) {
|
|
|
3143
3001
|
if (privateApiStatus.available) {
|
|
3144
3002
|
if (!supportsTyping || !supportsRead) warnIfImsgUpgradeNeeded.fireOnce(privateApiStatus.rpcMethods, runtime);
|
|
3145
3003
|
}
|
|
3146
|
-
const configuredTypingMode =
|
|
3004
|
+
const configuredTypingMode = resolveAgentConfig(cfg, decision.route.agentId)?.typingMode ?? cfg.agents?.defaults?.typingMode;
|
|
3147
3005
|
const sendPolicy = resolveSendPolicy({
|
|
3148
3006
|
cfg,
|
|
3149
3007
|
entry: getSessionEntry({
|
|
@@ -3188,12 +3046,12 @@ async function monitorIMessageProvider(opts) {
|
|
|
3188
3046
|
const unavailableCount = staged.unavailableCount;
|
|
3189
3047
|
const contextDecision = unavailableCount > 0 ? {
|
|
3190
3048
|
...decision,
|
|
3191
|
-
agentBodyText:
|
|
3192
|
-
messageText,
|
|
3193
|
-
unavailableCount
|
|
3049
|
+
agentBodyText: formatInboundMediaUnavailableText({
|
|
3050
|
+
body: messageText,
|
|
3051
|
+
notice: `[imessage ${unavailableCount > 1 ? `${unavailableCount} attachments` : "attachment"} unavailable]`
|
|
3194
3052
|
})
|
|
3195
3053
|
} : decision;
|
|
3196
|
-
const previousTimestamp =
|
|
3054
|
+
const previousTimestamp = await readSessionUpdatedAtAsync({
|
|
3197
3055
|
storePath,
|
|
3198
3056
|
sessionKey: decision.route.sessionKey
|
|
3199
3057
|
});
|
|
@@ -3409,16 +3267,8 @@ async function monitorIMessageProvider(opts) {
|
|
|
3409
3267
|
});
|
|
3410
3268
|
};
|
|
3411
3269
|
const resolveApprovalControlConversation = (message) => {
|
|
3412
|
-
const
|
|
3413
|
-
|
|
3414
|
-
const actorHandle = (message.is_from_me !== true && Boolean(sender) && sender === destination ? "" : sender) || (message.is_from_me === true ? destination : "");
|
|
3415
|
-
return actorHandle ? buildIMessageApprovalConversationKeyForInbound({
|
|
3416
|
-
chatGuid: message.chat_guid,
|
|
3417
|
-
chatIdentifier: message.chat_identifier,
|
|
3418
|
-
chatId: message.chat_id,
|
|
3419
|
-
isGroup: message.is_group,
|
|
3420
|
-
actorHandle
|
|
3421
|
-
}) : null;
|
|
3270
|
+
const actorHandle = resolveIMessageApprovalControlActor(message);
|
|
3271
|
+
return actorHandle ? buildIMessageApprovalConversationKeyForInbound(message, actorHandle) : null;
|
|
3422
3272
|
};
|
|
3423
3273
|
const ingress = createIMessageDurableIngress({
|
|
3424
3274
|
accountId: accountInfo.accountId,
|
|
@@ -3492,25 +3342,24 @@ async function monitorIMessageProvider(opts) {
|
|
|
3492
3342
|
});
|
|
3493
3343
|
if (opts.abortSignal?.aborted) return;
|
|
3494
3344
|
const abort = opts.abortSignal;
|
|
3495
|
-
const createWatchClient = async () => await createIMessageRpcClient({
|
|
3496
|
-
cliPath,
|
|
3497
|
-
dbPath,
|
|
3498
|
-
remoteHost,
|
|
3499
|
-
runtime,
|
|
3500
|
-
onNotification: (msg) => {
|
|
3501
|
-
if (msg.method === "message") ingress.receive(msg.params).catch((err) => {
|
|
3502
|
-
runtime.error?.(`imessage: durable admission failed: ${String(err)}`);
|
|
3503
|
-
});
|
|
3504
|
-
else if (msg.method === "error") runtime.error?.(`imessage: watch error ${JSON.stringify(sanitizeIMessageWatchErrorPayload(msg.params))}`);
|
|
3505
|
-
}
|
|
3506
|
-
});
|
|
3507
3345
|
for (let attempt = 1; attempt <= WATCH_SUBSCRIBE_MAX_ATTEMPTS; attempt++) {
|
|
3508
3346
|
if (abort?.aborted) return;
|
|
3509
3347
|
let attemptClient;
|
|
3510
3348
|
let attemptDetachAbortHandler = () => {};
|
|
3511
3349
|
let keepAttemptClient = false;
|
|
3512
3350
|
try {
|
|
3513
|
-
attemptClient = await
|
|
3351
|
+
attemptClient = await createIMessageRpcClient({
|
|
3352
|
+
cliPath,
|
|
3353
|
+
dbPath,
|
|
3354
|
+
remoteHost,
|
|
3355
|
+
runtime,
|
|
3356
|
+
onNotification: (msg) => {
|
|
3357
|
+
if (msg.method === "message") ingress.receive(msg.params).catch((err) => {
|
|
3358
|
+
runtime.error?.(`imessage: durable admission failed: ${String(err)}`);
|
|
3359
|
+
});
|
|
3360
|
+
else if (msg.method === "error") runtime.error?.(`imessage: watch error ${JSON.stringify(sanitizeIMessageWatchErrorPayload(msg.params))}`);
|
|
3361
|
+
}
|
|
3362
|
+
});
|
|
3514
3363
|
let attemptSubscriptionId = null;
|
|
3515
3364
|
attemptDetachAbortHandler = attachIMessageMonitorAbortHandler({
|
|
3516
3365
|
abortSignal: abort,
|
|
@@ -3531,18 +3380,8 @@ async function monitorIMessageProvider(opts) {
|
|
|
3531
3380
|
if (abort?.aborted) return;
|
|
3532
3381
|
const retriable = isRetriableWatchSubscribeStartupError(err);
|
|
3533
3382
|
const shouldRetry = attempt < WATCH_SUBSCRIBE_MAX_ATTEMPTS && retriable;
|
|
3534
|
-
const
|
|
3535
|
-
|
|
3536
|
-
attempt,
|
|
3537
|
-
maxAttempts: WATCH_SUBSCRIBE_MAX_ATTEMPTS,
|
|
3538
|
-
cliPath,
|
|
3539
|
-
dbPath,
|
|
3540
|
-
remoteHost,
|
|
3541
|
-
includeAttachments,
|
|
3542
|
-
probeTimeoutMs,
|
|
3543
|
-
watchSinceRowid,
|
|
3544
|
-
error: err
|
|
3545
|
-
};
|
|
3383
|
+
const retry = shouldRetry ? ` retry_in_ms=${WATCH_SUBSCRIBE_RETRY_DELAY_MS}` : "";
|
|
3384
|
+
const failure = `imessage: watch.subscribe startup failed attempt=${attempt}/${WATCH_SUBSCRIBE_MAX_ATTEMPTS} account=${accountInfo.accountId} cliPath=${cliPath} dbPath=${dbPath ? "configured" : "default"} remoteHost=${remoteHost ? "configured" : "none"} timeoutMs=${probeTimeoutMs} since_rowid=${watchSinceRowid ?? "none"} attachments=${includeAttachments} include_reactions=true${retry}: ${String(err)}`;
|
|
3546
3385
|
if (!shouldRetry) {
|
|
3547
3386
|
opts.statusSink?.({
|
|
3548
3387
|
connected: false,
|
|
@@ -3550,7 +3389,7 @@ async function monitorIMessageProvider(opts) {
|
|
|
3550
3389
|
terminalDisconnect: retriable ? void 0 : true,
|
|
3551
3390
|
lastError: String(err)
|
|
3552
3391
|
});
|
|
3553
|
-
runtime.error?.(danger(`imessage: monitor failed: ${
|
|
3392
|
+
runtime.error?.(danger(`imessage: monitor failed: ${failure}`));
|
|
3554
3393
|
throw err;
|
|
3555
3394
|
}
|
|
3556
3395
|
opts.statusSink?.({
|
|
@@ -3558,10 +3397,7 @@ async function monitorIMessageProvider(opts) {
|
|
|
3558
3397
|
lifecycle: "recovering",
|
|
3559
3398
|
lastError: String(err)
|
|
3560
3399
|
});
|
|
3561
|
-
runtime.log?.(warn(
|
|
3562
|
-
...failureParams,
|
|
3563
|
-
retryDelayMs: WATCH_SUBSCRIBE_RETRY_DELAY_MS
|
|
3564
|
-
})));
|
|
3400
|
+
runtime.log?.(warn(failure));
|
|
3565
3401
|
attemptDetachAbortHandler();
|
|
3566
3402
|
attemptDetachAbortHandler = () => {};
|
|
3567
3403
|
await attemptClient?.stop();
|