@openclaw/imessage 2026.9.1-beta.1 → 2026.9.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-DIpGOIiN.js → accounts-CCirBEIm.js} +2 -3
- package/dist/{actions.runtime-BhggncbG.js → actions.runtime-DCjNT81u.js} +5 -5
- package/dist/api.js +5 -5
- package/dist/{approval-handler.runtime-BC-YsgAf.js → approval-handler.runtime-BReeID1z.js} +14 -9
- package/dist/{approval-reactions-Cvwmdlev.js → approval-reactions-DcLRPx7O.js} +6 -10
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-CnKQ2MVB.js → channel-zDmMTHWD.js} +16 -12
- package/dist/{channel.runtime-BQLCh7bE.js → channel.runtime-qQBHCle8.js} +59 -14
- 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-BipELT2x.js → group-policy-BWqG2esy.js} +18 -12
- package/dist/media-contract-api.js +1 -1
- package/dist/{media-contract-BVXuQHG2.js → media-contract-pns3TSS5.js} +3 -2
- package/dist/{message-tool-api-BwIxJDoz.js → message-tool-api-CUoFESHj.js} +11 -6
- package/dist/message-tool-api.js +1 -1
- package/dist/{monitor-DRD_Dnp9.js → monitor-AmSj6QIH.js} +293 -27
- package/dist/{remote-file-CyTTuRAt.js → remote-file-COTN7196.js} +9 -8
- package/dist/runtime-Cza4CY5T.js +8 -0
- package/dist/runtime-api.js +7 -7
- package/dist/{sanitize-outbound-g651fIhi.js → sanitize-outbound-CN3Hxbjy.js} +393 -82
- package/dist/{send-DVQY6M5M.js → send-v0blZfaI.js} +145 -23
- package/dist/state-contract-DvahEY4X.js +56 -0
- package/dist/state-migrations-D-fyHVcb.js +277 -0
- package/node_modules/typebox/build/format/email.mjs +1 -1
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
- package/node_modules/typebox/package.json +1 -1
- package/openclaw.plugin.json +0 -6
- package/package.json +5 -5
- package/dist/catchup-Dh-Rv4Ur.js +0 -294
- package/dist/monitor-reply-cache-BdeUQaHO.js +0 -353
- package/dist/persisted-echo-cache-DGxy-J4t.js +0 -143
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount } from "./accounts-
|
|
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-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { a as maybeResolveIMessageApprovalPollVote, o as iMessageApprovalControlBindings, t as sendMessageIMessage } from "./send-DVQY6M5M.js";
|
|
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-CUoFESHj.js";
|
|
3
|
+
import { l as isKnownFromMeIMessageMessageId, o as createIMessageRpcClient, r as sanitizeOutboundText, u as rememberIMessageReplyCache, v as resolveIMessageDirectChatService } from "./sanitize-outbound-CN3Hxbjy.js";
|
|
4
|
+
import { f as probeIMessage, r as resolveIMessageInboundConversationId } from "./group-policy-BWqG2esy.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-DvahEY4X.js";
|
|
7
|
+
import { n as resolveIMessageAttachmentRoots, r as resolveIMessageRemoteAttachmentRoots } from "./media-contract-pns3TSS5.js";
|
|
8
|
+
import { i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction, s as buildIMessageApprovalConversationKeyForInbound } from "./approval-reactions-DcLRPx7O.js";
|
|
9
|
+
import { c as iMessageApprovalControlBindings, n as hasPersistedIMessageEcho, r as stripLeadingEchoTextCorruptionMarkers, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-v0blZfaI.js";
|
|
11
10
|
import { t as resolveIMessageReactionContext } from "./reaction-context-BAYI7pz0.js";
|
|
12
11
|
import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-viP7ZcyG.js";
|
|
13
12
|
import { bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, createMessageReceiptFromOutboundResults, listMessageReceiptPlatformIds, resolveChannelStreamingBlockEnabled } from "openclaw/plugin-sdk/channel-outbound";
|
|
@@ -19,7 +18,7 @@ import { asSafeIntegerInRange, hasNonEmptyString, isRecord, normalizeOptionalStr
|
|
|
19
18
|
import fs from "node:fs/promises";
|
|
20
19
|
import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, warn } from "openclaw/plugin-sdk/runtime-env";
|
|
21
20
|
import { isInboundPathAllowed, kindFromMime } from "openclaw/plugin-sdk/media-runtime";
|
|
22
|
-
import { asDateTimestampMs, asPositiveFiniteNumber, parseDateStringTimestampMs, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
21
|
+
import { asDateTimestampMs, asPositiveFiniteNumber, parseDateStringTimestampMs, resolveIntegerOption, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
23
22
|
import { createHash } from "node:crypto";
|
|
24
23
|
import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
25
24
|
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
@@ -27,8 +26,6 @@ import { collectErrorGraphCandidates, formatErrorMessage } from "openclaw/plugin
|
|
|
27
26
|
import { buildChannelGroupsScopeTree, resolveChannelGroupPolicy, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
|
28
27
|
import { convertMarkdownTables as convertMarkdownTables$1, findCodeRegions, isInsideCode, sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
|
|
29
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";
|
|
30
|
-
import { openLocalFileSafely, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
31
|
-
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
32
29
|
import { resolveAgentConfig, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
|
|
33
30
|
import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-runtime";
|
|
34
31
|
import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
|
|
@@ -40,12 +37,14 @@ import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
|
40
37
|
import { redactIdentifier } from "openclaw/plugin-sdk/logging-core";
|
|
41
38
|
import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
42
39
|
import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
40
|
+
import { openLocalFileSafely, resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
|
|
43
41
|
import { getSessionEntry, readSessionUpdatedAt, resolveSendPolicy, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
44
42
|
import { openNodeSqliteDatabase } from "openclaw/plugin-sdk/sqlite-runtime";
|
|
45
43
|
import { waitForTransportReady } from "openclaw/plugin-sdk/transport-ready-runtime";
|
|
46
44
|
import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
47
45
|
import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
48
46
|
import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
|
|
47
|
+
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
49
48
|
import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
50
49
|
import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
|
|
51
50
|
import { hasControlCommand } from "openclaw/plugin-sdk/command-auth-native";
|
|
@@ -264,6 +263,269 @@ function attachIMessageMonitorAbortHandler(params) {
|
|
|
264
263
|
return () => abort.removeEventListener("abort", onAbort);
|
|
265
264
|
}
|
|
266
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
|
|
267
529
|
//#region extensions/imessage/src/monitor/strip-imsg-length-prefixed-text.ts
|
|
268
530
|
const utf8Decoder = new TextDecoder();
|
|
269
531
|
function readVarint(buf, start) {
|
|
@@ -515,7 +777,7 @@ function combineIMessagePayloads(payloads) {
|
|
|
515
777
|
const first = expectDefined(payloads[0], "first iMessage payload to coalesce");
|
|
516
778
|
if (payloads.length === 1) return first;
|
|
517
779
|
const last = expectDefined(payloads.at(-1), "last iMessage payload to coalesce");
|
|
518
|
-
const boundedPayloads = payloads.length > MAX_COALESCED_ENTRIES ? [...payloads.slice(0,
|
|
780
|
+
const boundedPayloads = payloads.length > MAX_COALESCED_ENTRIES ? [...payloads.slice(0, 9), last] : payloads;
|
|
519
781
|
const seenTexts = /* @__PURE__ */ new Set();
|
|
520
782
|
const textParts = [];
|
|
521
783
|
for (const payload of boundedPayloads) {
|
|
@@ -698,7 +960,8 @@ async function deliverIMessageReply(params) {
|
|
|
698
960
|
accountId
|
|
699
961
|
});
|
|
700
962
|
const chunkMode = resolveChunkMode(cfg, "imessage", accountId);
|
|
701
|
-
const
|
|
963
|
+
const rawText = sanitizeOutboundText(payload.text ?? "");
|
|
964
|
+
const reply = resolveSendableOutboundReplyParts(payload, { text: convertMarkdownTables$1(rawText, tableMode) });
|
|
702
965
|
const accepted = [];
|
|
703
966
|
const sendAccepted = async (text, mediaUrl) => {
|
|
704
967
|
const sent = await sendMessageIMessage(target, text, {
|
|
@@ -760,7 +1023,8 @@ async function deliverIMessageReply(params) {
|
|
|
760
1023
|
}
|
|
761
1024
|
function createIMessageEchoCachingSend(params) {
|
|
762
1025
|
return async (target, text, opts) => {
|
|
763
|
-
const
|
|
1026
|
+
const sanitizedText = sanitizeOutboundText(text);
|
|
1027
|
+
const sent = await sendMessageIMessage(target, sanitizedText, opts);
|
|
764
1028
|
const scope = `${params.accountId ?? opts.accountId ?? ""}:${target}`;
|
|
765
1029
|
const echoText = sent.echoText ?? (sent.sentText || void 0);
|
|
766
1030
|
params.sentMessageCache?.remember(scope, {
|
|
@@ -975,8 +1239,8 @@ function warnGroupAllowlistDropPerChatOnce(params) {
|
|
|
975
1239
|
}
|
|
976
1240
|
//#endregion
|
|
977
1241
|
//#region extensions/imessage/src/monitor/inbound-dedupe.ts
|
|
978
|
-
const IMESSAGE_STALE_INBOUND_THRESHOLD_MS =
|
|
979
|
-
const IMESSAGE_RECOVERY_MAX_AGE_MS =
|
|
1242
|
+
const IMESSAGE_STALE_INBOUND_THRESHOLD_MS = 9e5;
|
|
1243
|
+
const IMESSAGE_RECOVERY_MAX_AGE_MS = 72e5;
|
|
980
1244
|
/**
|
|
981
1245
|
* Age fence: true when the message's own send date is materially older than
|
|
982
1246
|
* now, i.e. stale backlog rather than a live message. Fails open (returns
|
|
@@ -1720,6 +1984,7 @@ async function buildIMessageInboundContext(params) {
|
|
|
1720
1984
|
const directService = resolveIMessageDirectChatService(params.accountService, decision.chatGuid) ?? "auto";
|
|
1721
1985
|
const imessageTo = decision.isGroup ? chatTarget || `imessage:${decision.sender}` : `${directService}:${decision.sender}`;
|
|
1722
1986
|
const imessageFrom = decision.isGroup ? `imessage:group:${chatId ?? "unknown"}` : imessageTo;
|
|
1987
|
+
const replyTarget = decision.isGroup ? imessageTo : chatId != null ? `chat_id:${chatId}` : decision.chatGuid ? `chat_guid:${decision.chatGuid}` : imessageTo;
|
|
1723
1988
|
const inboundHistory = !decision.isGroup && params.dmHistory?.inboundHistory ? params.dmHistory.inboundHistory : decision.isGroup && decision.historyKey && params.historyLimit > 0 ? createChannelHistoryWindow({ historyMap: params.groupHistories }).buildInboundHistory({
|
|
1724
1989
|
historyKey: decision.historyKey,
|
|
1725
1990
|
limit: params.historyLimit
|
|
@@ -1747,7 +2012,8 @@ async function buildIMessageInboundContext(params) {
|
|
|
1747
2012
|
from: imessageFrom,
|
|
1748
2013
|
sender: {
|
|
1749
2014
|
id: decision.sender,
|
|
1750
|
-
name: decision.senderNormalized
|
|
2015
|
+
name: decision.senderNormalized,
|
|
2016
|
+
isSelf: params.message.is_from_me === true
|
|
1751
2017
|
},
|
|
1752
2018
|
conversation: {
|
|
1753
2019
|
kind: decision.isGroup ? "group" : "direct",
|
|
@@ -1764,7 +2030,7 @@ async function buildIMessageInboundContext(params) {
|
|
|
1764
2030
|
accountId: decision.route.accountId,
|
|
1765
2031
|
routeSessionKey: decision.route.sessionKey
|
|
1766
2032
|
},
|
|
1767
|
-
reply: { to:
|
|
2033
|
+
reply: { to: replyTarget },
|
|
1768
2034
|
message: {
|
|
1769
2035
|
body: combinedBody,
|
|
1770
2036
|
bodyForAgent: decision.agentBodyText ?? decision.bodyText,
|
|
@@ -1922,7 +2188,7 @@ function createIMessageDurableIngress(options) {
|
|
|
1922
2188
|
},
|
|
1923
2189
|
pollIntervalMs: IMESSAGE_INGRESS_DRAIN_INTERVAL_MS,
|
|
1924
2190
|
retention: {
|
|
1925
|
-
completedTtlMs:
|
|
2191
|
+
completedTtlMs: 144e5,
|
|
1926
2192
|
completedMaxEntries: 1e4,
|
|
1927
2193
|
failedMaxEntries: 1e3
|
|
1928
2194
|
},
|
|
@@ -2049,18 +2315,18 @@ async function readPinnedAttachmentBytes(handle, maxBytes) {
|
|
|
2049
2315
|
let totalBytes = 0;
|
|
2050
2316
|
while (totalBytes <= maxBytes) {
|
|
2051
2317
|
const remaining = maxBytes + 1 - totalBytes;
|
|
2052
|
-
const chunk = Buffer.allocUnsafe(Math.min(
|
|
2318
|
+
const chunk = Buffer.allocUnsafe(Math.min(65536, remaining));
|
|
2053
2319
|
const { bytesRead } = await handle.read(chunk, 0, chunk.length, null);
|
|
2054
2320
|
if (bytesRead === 0) return Buffer.concat(chunks, totalBytes);
|
|
2055
2321
|
chunks.push(chunk.subarray(0, bytesRead));
|
|
2056
2322
|
totalBytes += bytesRead;
|
|
2057
2323
|
}
|
|
2058
|
-
throw new Error(`attachment exceeds ${Math.round(maxBytes /
|
|
2324
|
+
throw new Error(`attachment exceeds ${Math.round(maxBytes / 1048576)}MB limit`);
|
|
2059
2325
|
}
|
|
2060
2326
|
async function readAttachmentBuffer(params) {
|
|
2061
2327
|
const opened = await (params.deps.openLocalFileSafely ?? openLocalFileSafely)({ filePath: params.attachmentPath });
|
|
2062
2328
|
try {
|
|
2063
|
-
if (opened.stat.size > params.maxBytes) throw new Error(`attachment exceeds ${Math.round(params.maxBytes /
|
|
2329
|
+
if (opened.stat.size > params.maxBytes) throw new Error(`attachment exceeds ${Math.round(params.maxBytes / 1048576)}MB limit`);
|
|
2064
2330
|
await assertAllowedCanonicalAttachmentPath({
|
|
2065
2331
|
canonicalPath: opened.realPath,
|
|
2066
2332
|
allowedRoots: params.allowedRoots
|
|
@@ -2440,7 +2706,7 @@ function sanitizeIMessageWatchErrorPayload(payload) {
|
|
|
2440
2706
|
if (typeof payload.code === "number" && Number.isFinite(payload.code)) safe.code = payload.code;
|
|
2441
2707
|
if (typeof payload.message === "string") {
|
|
2442
2708
|
const sanitizedMessage = sanitizeTerminalText(payload.message);
|
|
2443
|
-
if (sanitizedMessage) safe.message = sanitizedMessage.length > MAX_WATCH_ERROR_MESSAGE_CHARS ? `${truncateUtf16Safe(sanitizedMessage,
|
|
2709
|
+
if (sanitizedMessage) safe.message = sanitizedMessage.length > MAX_WATCH_ERROR_MESSAGE_CHARS ? `${truncateUtf16Safe(sanitizedMessage, 199)}…` : sanitizedMessage;
|
|
2444
2710
|
}
|
|
2445
2711
|
return safe;
|
|
2446
2712
|
}
|
|
@@ -2452,7 +2718,7 @@ const CHANNEL_APPROVAL_GATEWAY_RUNTIME_CONTEXT_CAPABILITY = "approval.gateway";
|
|
|
2452
2718
|
const APPROVAL_REACTION_POLL_INTERVAL_MS = 2e3;
|
|
2453
2719
|
const APPROVAL_REACTION_DISCOVERY_INTERVAL_MS = 6e4;
|
|
2454
2720
|
const IMESSAGE_TYPING_KEEPALIVE_INTERVAL_MS = 8e3;
|
|
2455
|
-
const IMESSAGE_TYPING_KEEPALIVE_MAX_DURATION_MS =
|
|
2721
|
+
const IMESSAGE_TYPING_KEEPALIVE_MAX_DURATION_MS = 6e5;
|
|
2456
2722
|
function resolveConfiguredIMessageTypingMode(cfg, agentId) {
|
|
2457
2723
|
return resolveAgentConfig(cfg, agentId)?.typingMode ?? cfg.agents?.defaults?.typingMode;
|
|
2458
2724
|
}
|
|
@@ -2836,7 +3102,7 @@ async function monitorIMessageProvider(opts = {}) {
|
|
|
2836
3102
|
const conversationKey = chatId != null ? `group:${chatId}` : `dm:${senderForKey}`;
|
|
2837
3103
|
const rateLimitKey = `${accountInfo.accountId}:${conversationKey}`;
|
|
2838
3104
|
if (decision.kind === "drop") {
|
|
2839
|
-
if (decision.reason === "echo" || decision.reason === "
|
|
3105
|
+
if (decision.reason === "echo" || decision.reason === "reflected assistant content") loopRateLimiter.record(rateLimitKey);
|
|
2840
3106
|
const diagnostic = describeIMessageInboundDropDiagnostic({
|
|
2841
3107
|
accountId: accountInfo.accountId,
|
|
2842
3108
|
reason: decision.reason,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { p as resolveLocalIMessageChatDbPath, u as expandIMessageUserPath } from "./accounts-
|
|
2
|
-
import {
|
|
1
|
+
import { p as resolveLocalIMessageChatDbPath, u as expandIMessageUserPath } from "./accounts-CCirBEIm.js";
|
|
2
|
+
import { d as resolveIMessageCachedResourceBinding, f as resolveIMessageMessageId, g as isIMessageEmailChatIdentifier, h as chatContextFromIMessageTarget } from "./sanitize-outbound-CN3Hxbjy.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { normalizeScpRemoteHost } from "openclaw/plugin-sdk/host-runtime";
|
|
5
5
|
import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";
|
|
@@ -9,8 +9,8 @@ import { runCommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
|
|
|
9
9
|
import { openNodeSqliteDatabase } from "openclaw/plugin-sdk/sqlite-runtime";
|
|
10
10
|
import { sanitizeTempFileName } from "openclaw/plugin-sdk/temp-path";
|
|
11
11
|
//#region extensions/imessage/src/cli-output.ts
|
|
12
|
-
const IMESSAGE_CLI_STDOUT_MAX_BYTES =
|
|
13
|
-
const IMESSAGE_CLI_STDERR_TAIL_BYTES =
|
|
12
|
+
const IMESSAGE_CLI_STDOUT_MAX_BYTES = 8388608;
|
|
13
|
+
const IMESSAGE_CLI_STDERR_TAIL_BYTES = 65536;
|
|
14
14
|
function parseLastJsonObject(stdout) {
|
|
15
15
|
const last = stdout.split(/\r?\n/u).findLast((line) => line.trim().length > 0)?.trim();
|
|
16
16
|
if (!last) return null;
|
|
@@ -23,12 +23,13 @@ function parseLastJsonObject(stdout) {
|
|
|
23
23
|
}
|
|
24
24
|
async function runIMessageCliJsonCommand(params) {
|
|
25
25
|
const dbPath = params.dbPath?.trim();
|
|
26
|
-
const
|
|
26
|
+
const argv = [
|
|
27
27
|
expandIMessageUserPath(params.cliPath),
|
|
28
28
|
...params.args,
|
|
29
29
|
...dbPath ? ["--db", dbPath] : [],
|
|
30
30
|
"--json"
|
|
31
|
-
]
|
|
31
|
+
];
|
|
32
|
+
const result = await runCommandWithTimeout(argv, {
|
|
32
33
|
killProcessTree: true,
|
|
33
34
|
maxOutputBytes: {
|
|
34
35
|
stdout: IMESSAGE_CLI_STDOUT_MAX_BYTES,
|
|
@@ -213,8 +214,8 @@ async function runChecked(run, label, argv, options) {
|
|
|
213
214
|
const result = await run(argv, {
|
|
214
215
|
killProcessTree: true,
|
|
215
216
|
maxOutputBytes: {
|
|
216
|
-
stdout:
|
|
217
|
-
stderr:
|
|
217
|
+
stdout: 4096,
|
|
218
|
+
stderr: 65536
|
|
218
219
|
},
|
|
219
220
|
outputCapture: {
|
|
220
221
|
stdout: "head",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
2
|
+
//#region extensions/imessage/src/runtime.ts
|
|
3
|
+
const { getRuntime: getIMessageRuntime, setRuntime: setIMessageRuntime, tryGetRuntime: getOptionalIMessageRuntime } = createPluginRuntimeStore({
|
|
4
|
+
pluginId: "imessage",
|
|
5
|
+
errorMessage: "iMessage runtime not initialized"
|
|
6
|
+
});
|
|
7
|
+
//#endregion
|
|
8
|
+
export { getOptionalIMessageRuntime as n, setIMessageRuntime as r, getIMessageRuntime as t };
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { o as resolveIMessageAccount } from "./accounts-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { n as IMessageConfigSchema } from "./config-schema-
|
|
5
|
-
import { t as sendMessageIMessage } from "./send-
|
|
6
|
-
import { t as monitorIMessageProvider } from "./monitor-
|
|
1
|
+
import { o as resolveIMessageAccount } from "./accounts-CCirBEIm.js";
|
|
2
|
+
import { c as normalizeIMessageMessagingTarget, d as imessageMessageActions, f as probeIMessage, n as resolveIMessageGroupToolPolicy, s as looksLikeIMessageTargetId, t as resolveIMessageGroupRequireMention } from "./group-policy-BWqG2esy.js";
|
|
3
|
+
import { r as setIMessageRuntime } from "./runtime-Cza4CY5T.js";
|
|
4
|
+
import { n as IMessageConfigSchema } from "./config-schema-DuGbA8Kp.js";
|
|
5
|
+
import { t as sendMessageIMessage } from "./send-v0blZfaI.js";
|
|
6
|
+
import { t as monitorIMessageProvider } from "./monitor-AmSj6QIH.js";
|
|
7
7
|
import { formatTrimmedAllowFromEntries } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
8
8
|
import { buildComputedAccountStatusSnapshot, collectStatusIssuesFromLastError } from "openclaw/plugin-sdk/status-helpers";
|
|
9
|
-
import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/
|
|
9
|
+
import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/account-helpers";
|
|
10
10
|
import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
|
|
11
11
|
import { DEFAULT_ACCOUNT_ID, getChatChannelMeta } from "openclaw/plugin-sdk/core";
|
|
12
12
|
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
|