@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
package/dist/catchup-Dh-Rv4Ur.js
DELETED
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
import { f as getIMessageRuntime } from "./monitor-reply-cache-BdeUQaHO.js";
|
|
2
|
-
import { resolveIntegerOption } from "openclaw/plugin-sdk/number-runtime";
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
4
|
-
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
5
|
-
//#region extensions/imessage/src/monitor/catchup.ts
|
|
6
|
-
const DEFAULT_MAX_AGE_MINUTES = 120;
|
|
7
|
-
const MAX_MAX_AGE_MINUTES = 720;
|
|
8
|
-
const DEFAULT_PER_RUN_LIMIT = 50;
|
|
9
|
-
const MAX_PER_RUN_LIMIT = 500;
|
|
10
|
-
const DEFAULT_FIRST_RUN_LOOKBACK_MINUTES = 30;
|
|
11
|
-
const DEFAULT_MAX_FAILURE_RETRIES = 10;
|
|
12
|
-
const MAX_MAX_FAILURE_RETRIES = 1e3;
|
|
13
|
-
const MAX_FAILURE_RETRY_MAP_SIZE = 512;
|
|
14
|
-
const MAX_FAILURE_RETRY_MAP_JSON_BYTES = 48e3;
|
|
15
|
-
const textEncoder = new TextEncoder();
|
|
16
|
-
const IMESSAGE_CATCHUP_CURSOR_NAMESPACE = "imessage.catchup-cursors";
|
|
17
|
-
const cursorWriteQueue = new KeyedAsyncQueue();
|
|
18
|
-
function resolveIMessageCatchupCursorKey(accountId) {
|
|
19
|
-
return createHash("sha256").update(accountId, "utf8").digest("hex").slice(0, 32);
|
|
20
|
-
}
|
|
21
|
-
function openCatchupCursorStore() {
|
|
22
|
-
return getIMessageRuntime().state.openSyncKeyedStore({
|
|
23
|
-
namespace: IMESSAGE_CATCHUP_CURSOR_NAMESPACE,
|
|
24
|
-
maxEntries: 256
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
function updateCatchupCursorStore(key, updateValue) {
|
|
28
|
-
const store = openCatchupCursorStore();
|
|
29
|
-
if (!store.update) throw new Error("iMessage catchup cursor persistence requires atomic plugin-state update.");
|
|
30
|
-
return store.update(key, updateValue);
|
|
31
|
-
}
|
|
32
|
-
function enqueueCursorWrite(accountId, fn) {
|
|
33
|
-
const key = resolveIMessageCatchupCursorKey(accountId);
|
|
34
|
-
return cursorWriteQueue.enqueue(key, fn);
|
|
35
|
-
}
|
|
36
|
-
function sanitizeFailureRetriesInput(raw) {
|
|
37
|
-
if (!raw || typeof raw !== "object") return {};
|
|
38
|
-
const out = {};
|
|
39
|
-
for (const [guid, count] of Object.entries(raw)) {
|
|
40
|
-
if (!guid || typeof guid !== "string") continue;
|
|
41
|
-
if (typeof count !== "number" || !Number.isFinite(count) || count <= 0) continue;
|
|
42
|
-
out[guid] = Math.floor(count);
|
|
43
|
-
}
|
|
44
|
-
return out;
|
|
45
|
-
}
|
|
46
|
-
function normalizeIMessageCatchupCursor(value) {
|
|
47
|
-
if (!value || typeof value !== "object") return null;
|
|
48
|
-
const raw = value;
|
|
49
|
-
if (typeof raw.lastSeenMs !== "number" || !Number.isFinite(raw.lastSeenMs)) return null;
|
|
50
|
-
if (typeof raw.lastSeenRowid !== "number" || !Number.isFinite(raw.lastSeenRowid)) return null;
|
|
51
|
-
const failureRetries = sanitizeFailureRetriesInput(raw.failureRetries);
|
|
52
|
-
const hasRetries = Object.keys(failureRetries).length > 0;
|
|
53
|
-
return {
|
|
54
|
-
lastSeenMs: raw.lastSeenMs,
|
|
55
|
-
lastSeenRowid: raw.lastSeenRowid,
|
|
56
|
-
updatedAt: typeof raw.updatedAt === "number" ? raw.updatedAt : 0,
|
|
57
|
-
...hasRetries ? { failureRetries } : {}
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
function readIMessageCatchupCursor(accountId) {
|
|
61
|
-
return normalizeIMessageCatchupCursor(openCatchupCursorStore().lookup(resolveIMessageCatchupCursorKey(accountId)));
|
|
62
|
-
}
|
|
63
|
-
async function loadIMessageCatchupCursor(accountId) {
|
|
64
|
-
return readIMessageCatchupCursor(accountId);
|
|
65
|
-
}
|
|
66
|
-
function buildIMessageCatchupCursor(next) {
|
|
67
|
-
const sanitized = sanitizeFailureRetriesInput(next.failureRetries);
|
|
68
|
-
const hasRetries = Object.keys(sanitized).length > 0;
|
|
69
|
-
return {
|
|
70
|
-
lastSeenMs: next.lastSeenMs,
|
|
71
|
-
lastSeenRowid: next.lastSeenRowid,
|
|
72
|
-
updatedAt: Date.now(),
|
|
73
|
-
...hasRetries ? { failureRetries: sanitized } : {}
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
async function saveIMessageCatchupCursor(accountId, next, options = {}) {
|
|
77
|
-
const cursor = buildIMessageCatchupCursor(next);
|
|
78
|
-
updateCatchupCursorStore(resolveIMessageCatchupCursorKey(accountId), (existingValue) => {
|
|
79
|
-
const existing = normalizeIMessageCatchupCursor(existingValue);
|
|
80
|
-
if (existing && cursor.lastSeenRowid < existing.lastSeenRowid) {
|
|
81
|
-
if (!options.allowCursorRewindForRetries) return;
|
|
82
|
-
return buildIMessageCatchupCursor({
|
|
83
|
-
lastSeenMs: cursor.lastSeenMs,
|
|
84
|
-
lastSeenRowid: cursor.lastSeenRowid,
|
|
85
|
-
failureRetries: {
|
|
86
|
-
...existing.failureRetries,
|
|
87
|
-
...cursor.failureRetries
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
return cursor;
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Bound the retry map so a pathological storm of unique failing GUIDs
|
|
96
|
-
* cannot grow the cursor file without limit. Keeps the `maxSize` entries
|
|
97
|
-
* with the highest counts (closest to give-up) when over the bound.
|
|
98
|
-
*/
|
|
99
|
-
function capFailureRetriesMap(map, maxSize = MAX_FAILURE_RETRY_MAP_SIZE, maxBytes = MAX_FAILURE_RETRY_MAP_JSON_BYTES) {
|
|
100
|
-
const entries = Object.entries(map);
|
|
101
|
-
if (entries.length <= maxSize && textEncoder.encode(JSON.stringify(map)).byteLength <= maxBytes) return map;
|
|
102
|
-
entries.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
|
|
103
|
-
const capped = {};
|
|
104
|
-
for (const [guid, count] of entries.slice(0, maxSize)) {
|
|
105
|
-
capped[guid] = count;
|
|
106
|
-
if (textEncoder.encode(JSON.stringify(capped)).byteLength > maxBytes) {
|
|
107
|
-
delete capped[guid];
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return capped;
|
|
112
|
-
}
|
|
113
|
-
function clampInt(value, min, max, fallback) {
|
|
114
|
-
return resolveIntegerOption(value, fallback, {
|
|
115
|
-
min,
|
|
116
|
-
max
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
function resolveCatchupConfig(raw) {
|
|
120
|
-
return {
|
|
121
|
-
enabled: Boolean(raw?.enabled),
|
|
122
|
-
maxAgeMinutes: clampInt(raw?.maxAgeMinutes, 1, MAX_MAX_AGE_MINUTES, DEFAULT_MAX_AGE_MINUTES),
|
|
123
|
-
perRunLimit: clampInt(raw?.perRunLimit, 1, MAX_PER_RUN_LIMIT, DEFAULT_PER_RUN_LIMIT),
|
|
124
|
-
firstRunLookbackMinutes: clampInt(raw?.firstRunLookbackMinutes, 1, MAX_MAX_AGE_MINUTES, DEFAULT_FIRST_RUN_LOOKBACK_MINUTES),
|
|
125
|
-
maxFailureRetries: clampInt(raw?.maxFailureRetries, 1, MAX_MAX_FAILURE_RETRIES, DEFAULT_MAX_FAILURE_RETRIES)
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
async function advanceIMessageCatchupCursor(accountId, next, config) {
|
|
129
|
-
if (!Number.isFinite(next.lastSeenMs) || !Number.isFinite(next.lastSeenRowid)) return false;
|
|
130
|
-
return await enqueueCursorWrite(accountId, async () => {
|
|
131
|
-
let advanced = false;
|
|
132
|
-
updateCatchupCursorStore(resolveIMessageCatchupCursorKey(accountId), (existingValue) => {
|
|
133
|
-
const cursor = normalizeIMessageCatchupCursor(existingValue);
|
|
134
|
-
if (cursor && next.lastSeenRowid <= cursor.lastSeenRowid) return;
|
|
135
|
-
if (Object.values(cursor?.failureRetries ?? {}).some((count) => count < config.maxFailureRetries)) return;
|
|
136
|
-
advanced = true;
|
|
137
|
-
return buildIMessageCatchupCursor({
|
|
138
|
-
lastSeenMs: Math.max(cursor?.lastSeenMs ?? next.lastSeenMs, next.lastSeenMs),
|
|
139
|
-
lastSeenRowid: next.lastSeenRowid,
|
|
140
|
-
failureRetries: cursor?.failureRetries
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
return advanced;
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* One catchup pass. Loads the cursor, fetches `messages.history`, replays
|
|
148
|
-
* each row through `dispatch`, advances the cursor on success / give-up,
|
|
149
|
-
* persists the cursor, returns a summary.
|
|
150
|
-
*
|
|
151
|
-
* The fetch and dispatch functions are injected so this loop is unit-testable
|
|
152
|
-
* without standing up an `imsg` daemon. The wiring in `monitor-provider.ts`
|
|
153
|
-
* passes the live `client.request("messages.history", ...)` adapter as
|
|
154
|
-
* `fetch` and the `evaluateIMessageInbound` + `runChannelInboundEvent`
|
|
155
|
-
* pipeline as `dispatch`.
|
|
156
|
-
*/
|
|
157
|
-
async function performIMessageCatchup(params) {
|
|
158
|
-
const now = params.now ?? Date.now();
|
|
159
|
-
const cfg = params.config;
|
|
160
|
-
const cursor = await loadIMessageCatchupCursor(params.accountId);
|
|
161
|
-
const lookbackMs = cursor === null ? cfg.firstRunLookbackMinutes * 6e4 : cfg.maxAgeMinutes * 6e4;
|
|
162
|
-
const ageBoundMs = now - cfg.maxAgeMinutes * 6e4;
|
|
163
|
-
const windowStartMs = Math.max(cursor?.lastSeenMs ?? now - lookbackMs, ageBoundMs);
|
|
164
|
-
const windowEndMs = now;
|
|
165
|
-
const sinceRowid = cursor?.lastSeenRowid ?? 0;
|
|
166
|
-
const summary = {
|
|
167
|
-
querySucceeded: false,
|
|
168
|
-
fullyCaughtUp: false,
|
|
169
|
-
fetchedCount: 0,
|
|
170
|
-
replayed: 0,
|
|
171
|
-
skippedFromMe: 0,
|
|
172
|
-
skippedPreCursor: 0,
|
|
173
|
-
skippedGivenUp: 0,
|
|
174
|
-
failed: 0,
|
|
175
|
-
givenUp: 0,
|
|
176
|
-
cursorBefore: cursor ? {
|
|
177
|
-
lastSeenMs: cursor.lastSeenMs,
|
|
178
|
-
lastSeenRowid: cursor.lastSeenRowid
|
|
179
|
-
} : null,
|
|
180
|
-
cursorAfter: {
|
|
181
|
-
lastSeenMs: cursor?.lastSeenMs ?? windowStartMs,
|
|
182
|
-
lastSeenRowid: cursor?.lastSeenRowid ?? 0
|
|
183
|
-
},
|
|
184
|
-
windowStartMs,
|
|
185
|
-
windowEndMs
|
|
186
|
-
};
|
|
187
|
-
let fetchResult;
|
|
188
|
-
try {
|
|
189
|
-
fetchResult = await params.fetch({
|
|
190
|
-
sinceMs: windowStartMs,
|
|
191
|
-
sinceRowid,
|
|
192
|
-
limit: cfg.perRunLimit
|
|
193
|
-
});
|
|
194
|
-
} catch (err) {
|
|
195
|
-
params.warn?.(`imessage catchup: fetch failed: ${String(err)}`);
|
|
196
|
-
return summary;
|
|
197
|
-
}
|
|
198
|
-
if (!fetchResult.resolved) {
|
|
199
|
-
params.warn?.(`imessage catchup: fetch returned unresolved result`);
|
|
200
|
-
return summary;
|
|
201
|
-
}
|
|
202
|
-
summary.querySucceeded = true;
|
|
203
|
-
summary.fullyCaughtUp = fetchResult.fullyCaughtUp !== false;
|
|
204
|
-
summary.fetchedCount = fetchResult.rows.length;
|
|
205
|
-
const rows = fetchResult.rows.toSorted((a, b) => a.rowid - b.rowid);
|
|
206
|
-
const failureRetries = { ...cursor?.failureRetries };
|
|
207
|
-
const cursorBeforeMs = cursor?.lastSeenMs ?? windowStartMs;
|
|
208
|
-
const cursorBeforeRowid = cursor?.lastSeenRowid ?? 0;
|
|
209
|
-
let highWatermarkMs = cursorBeforeMs;
|
|
210
|
-
let highWatermarkRowid = cursorBeforeRowid;
|
|
211
|
-
let earliestHeldFailureRow = null;
|
|
212
|
-
for (const row of rows) {
|
|
213
|
-
if (row.rowid <= sinceRowid) {
|
|
214
|
-
summary.skippedPreCursor += 1;
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
if (row.date < ageBoundMs) {
|
|
218
|
-
summary.skippedPreCursor += 1;
|
|
219
|
-
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
220
|
-
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
if (row.isFromMe) {
|
|
224
|
-
try {
|
|
225
|
-
await params.observeSkippedFromMe?.(row);
|
|
226
|
-
} catch (err) {
|
|
227
|
-
params.warn?.(`imessage catchup: from-me observer failed for guid=${row.guid}: ${String(err)}`);
|
|
228
|
-
}
|
|
229
|
-
summary.skippedFromMe += 1;
|
|
230
|
-
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
231
|
-
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
const priorCount = failureRetries[row.guid] ?? 0;
|
|
235
|
-
if (priorCount >= cfg.maxFailureRetries) {
|
|
236
|
-
summary.skippedGivenUp += 1;
|
|
237
|
-
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
238
|
-
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
let dispatched;
|
|
242
|
-
try {
|
|
243
|
-
dispatched = await params.dispatch(row);
|
|
244
|
-
} catch (err) {
|
|
245
|
-
params.warn?.(`imessage catchup: dispatch threw for guid=${row.guid}: ${String(err)}`);
|
|
246
|
-
dispatched = { ok: false };
|
|
247
|
-
}
|
|
248
|
-
if (dispatched.ok) {
|
|
249
|
-
summary.replayed += 1;
|
|
250
|
-
delete failureRetries[row.guid];
|
|
251
|
-
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
252
|
-
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
const nextCount = priorCount + 1;
|
|
256
|
-
failureRetries[row.guid] = nextCount;
|
|
257
|
-
summary.failed += 1;
|
|
258
|
-
if (nextCount >= cfg.maxFailureRetries) {
|
|
259
|
-
summary.givenUp += 1;
|
|
260
|
-
params.warn?.(`imessage catchup: giving up on guid=${row.guid} after ${nextCount} failures; advancing cursor past it`);
|
|
261
|
-
highWatermarkMs = Math.max(highWatermarkMs, row.date);
|
|
262
|
-
highWatermarkRowid = Math.max(highWatermarkRowid, row.rowid);
|
|
263
|
-
continue;
|
|
264
|
-
}
|
|
265
|
-
if (earliestHeldFailureRow === null || row.rowid < earliestHeldFailureRow.rowid) earliestHeldFailureRow = row;
|
|
266
|
-
}
|
|
267
|
-
if (earliestHeldFailureRow === null) {
|
|
268
|
-
if (typeof fetchResult.highWatermarkMs === "number") highWatermarkMs = Math.max(highWatermarkMs, fetchResult.highWatermarkMs);
|
|
269
|
-
if (typeof fetchResult.highWatermarkRowid === "number") highWatermarkRowid = Math.max(highWatermarkRowid, fetchResult.highWatermarkRowid);
|
|
270
|
-
}
|
|
271
|
-
let lastSeenMs;
|
|
272
|
-
let lastSeenRowid;
|
|
273
|
-
if (earliestHeldFailureRow !== null) {
|
|
274
|
-
lastSeenMs = Math.max(cursorBeforeMs, earliestHeldFailureRow.date - 1);
|
|
275
|
-
lastSeenRowid = Math.max(cursorBeforeRowid, earliestHeldFailureRow.rowid - 1);
|
|
276
|
-
} else {
|
|
277
|
-
lastSeenMs = highWatermarkMs;
|
|
278
|
-
lastSeenRowid = highWatermarkRowid;
|
|
279
|
-
}
|
|
280
|
-
const capped = capFailureRetriesMap(failureRetries);
|
|
281
|
-
summary.cursorAfter = {
|
|
282
|
-
lastSeenMs,
|
|
283
|
-
lastSeenRowid
|
|
284
|
-
};
|
|
285
|
-
await saveIMessageCatchupCursor(params.accountId, {
|
|
286
|
-
lastSeenMs,
|
|
287
|
-
lastSeenRowid,
|
|
288
|
-
failureRetries: capped
|
|
289
|
-
}, { allowCursorRewindForRetries: earliestHeldFailureRow !== null });
|
|
290
|
-
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}`);
|
|
291
|
-
return summary;
|
|
292
|
-
}
|
|
293
|
-
//#endregion
|
|
294
|
-
export { resolveCatchupConfig as a, performIMessageCatchup as i, advanceIMessageCatchupCursor as n, resolveIMessageCatchupCursorKey as o, capFailureRetriesMap as r, IMESSAGE_CATCHUP_CURSOR_NAMESPACE as t };
|
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
import { normalizeE164 } from "openclaw/plugin-sdk/account-resolution";
|
|
2
|
-
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
-
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
4
|
-
import { createHash } from "node:crypto";
|
|
5
|
-
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
6
|
-
//#region extensions/imessage/src/chat-context.ts
|
|
7
|
-
const EMAIL_HANDLE_PATTERN = /^[^\s@]+@[^\s@]+$/u;
|
|
8
|
-
function parseDirectChatIdentity(raw) {
|
|
9
|
-
const trimmed = raw.trim();
|
|
10
|
-
const parts = trimmed.split(";");
|
|
11
|
-
if (parts.length === 3 && parts[1] === "-" && parts[2]) {
|
|
12
|
-
const service = parts[0]?.toLowerCase();
|
|
13
|
-
if (service === "imessage" || service === "sms" || service === "any") {
|
|
14
|
-
const identifier = parts[2];
|
|
15
|
-
return {
|
|
16
|
-
service,
|
|
17
|
-
identifier: EMAIL_HANDLE_PATTERN.test(identifier) ? identifier.toLowerCase() : identifier
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
if (parts.length !== 1) return;
|
|
22
|
-
if (trimmed.startsWith("+") || EMAIL_HANDLE_PATTERN.test(trimmed)) return { identifier: trimmed.toLowerCase() };
|
|
23
|
-
}
|
|
24
|
-
function resolveIMessageDirectChatService(configuredService, chatGuid) {
|
|
25
|
-
if (configuredService === "imessage" || configuredService === "sms") return configuredService;
|
|
26
|
-
const observedService = chatGuid ? parseDirectChatIdentity(chatGuid)?.service : void 0;
|
|
27
|
-
return observedService === "imessage" || observedService === "sms" ? observedService : void 0;
|
|
28
|
-
}
|
|
29
|
-
function isIMessageEmailChatIdentifier(raw) {
|
|
30
|
-
const identity = parseDirectChatIdentity(raw);
|
|
31
|
-
return Boolean(identity && EMAIL_HANDLE_PATTERN.test(identity.identifier));
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Strip the `iMessage;-;` / `SMS;-;` / `any;-;` service prefix that Messages
|
|
35
|
-
* uses for direct chats. Different layers report direct DMs in different
|
|
36
|
-
* forms, so raw comparison would falsely treat one DM as different chats.
|
|
37
|
-
*/
|
|
38
|
-
function normalizeDirectChatIdentifier(raw) {
|
|
39
|
-
const trimmed = raw.trim();
|
|
40
|
-
return parseDirectChatIdentity(trimmed)?.identifier ?? trimmed;
|
|
41
|
-
}
|
|
42
|
-
function chatContextFromIMessageTarget(target, effectiveService) {
|
|
43
|
-
if (target.kind === "chat_id") return { chatId: target.chatId };
|
|
44
|
-
if (target.kind === "chat_guid") return { chatGuid: target.chatGuid };
|
|
45
|
-
if (target.kind === "chat_identifier") return { chatIdentifier: target.chatIdentifier };
|
|
46
|
-
const trimmedHandle = target.to.trim();
|
|
47
|
-
const canonicalHandle = trimmedHandle.startsWith("+") ? normalizeE164(trimmedHandle) : /^[^\s@]+@[^\s@]+$/u.test(trimmedHandle) ? trimmedHandle.toLowerCase() : void 0;
|
|
48
|
-
if (!canonicalHandle) return {};
|
|
49
|
-
const service = target.service === "auto" ? effectiveService : target.service;
|
|
50
|
-
if (service !== "imessage" && service !== "sms") return {};
|
|
51
|
-
return { chatIdentifier: `${service === "sms" ? "SMS" : "iMessage"};-;${canonicalHandle}` };
|
|
52
|
-
}
|
|
53
|
-
function compareOptional(left, right) {
|
|
54
|
-
return left === void 0 || right === void 0 ? void 0 : left === right;
|
|
55
|
-
}
|
|
56
|
-
function compareChatSelector(cachedRaw, currentRaw, crossKind = false) {
|
|
57
|
-
const cached = normalizeOptionalString(cachedRaw);
|
|
58
|
-
const current = normalizeOptionalString(currentRaw);
|
|
59
|
-
if (!cached || !current) return;
|
|
60
|
-
if (cached === current) return true;
|
|
61
|
-
const cachedDirect = parseDirectChatIdentity(cached);
|
|
62
|
-
const currentDirect = parseDirectChatIdentity(current);
|
|
63
|
-
if (!cachedDirect || !currentDirect) return crossKind ? void 0 : false;
|
|
64
|
-
if (cachedDirect.identifier !== currentDirect.identifier) return false;
|
|
65
|
-
if (cachedDirect.service === "any") return true;
|
|
66
|
-
if (!cachedDirect.service || !currentDirect.service) return;
|
|
67
|
-
return cachedDirect.service === currentDirect.service;
|
|
68
|
-
}
|
|
69
|
-
function resolveIMessageChatMatch(cached, current) {
|
|
70
|
-
const cachedChatGuid = normalizeOptionalString(cached.chatGuid);
|
|
71
|
-
const currentChatGuid = normalizeOptionalString(current.chatGuid);
|
|
72
|
-
const cachedChatIdentifier = normalizeOptionalString(cached.chatIdentifier);
|
|
73
|
-
const currentChatIdentifier = normalizeOptionalString(current.chatIdentifier);
|
|
74
|
-
const comparisons = [
|
|
75
|
-
compareChatSelector(cachedChatGuid, currentChatGuid),
|
|
76
|
-
compareChatSelector(cachedChatIdentifier, currentChatIdentifier),
|
|
77
|
-
compareOptional(cached.chatId, current.chatId),
|
|
78
|
-
compareChatSelector(cachedChatGuid, currentChatIdentifier, true),
|
|
79
|
-
compareChatSelector(cachedChatIdentifier, currentChatGuid, true)
|
|
80
|
-
].filter((comparison) => comparison !== void 0);
|
|
81
|
-
if (comparisons.length === 0) return "unknown";
|
|
82
|
-
return comparisons.every(Boolean) ? "match" : "mismatch";
|
|
83
|
-
}
|
|
84
|
-
function isPositiveIMessageChatMatch(cached, current) {
|
|
85
|
-
return resolveIMessageChatMatch(cached, current) === "match";
|
|
86
|
-
}
|
|
87
|
-
//#endregion
|
|
88
|
-
//#region extensions/imessage/src/runtime.ts
|
|
89
|
-
const { getRuntime: getIMessageRuntime, setRuntime: setIMessageRuntime, tryGetRuntime: getOptionalIMessageRuntime } = createPluginRuntimeStore({
|
|
90
|
-
pluginId: "imessage",
|
|
91
|
-
errorMessage: "iMessage runtime not initialized"
|
|
92
|
-
});
|
|
93
|
-
//#endregion
|
|
94
|
-
//#region extensions/imessage/src/monitor-reply-cache.ts
|
|
95
|
-
const IMESSAGE_REPLY_CACHE_NAMESPACE = "imessage.reply-cache";
|
|
96
|
-
const IMESSAGE_REPLY_CACHE_MAX_ENTRIES = 2e3;
|
|
97
|
-
const IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE = "imessage.reply-cache-counter";
|
|
98
|
-
const IMESSAGE_REPLY_CACHE_COUNTER_KEY = "short-id-counter";
|
|
99
|
-
const REPLY_CACHE_TTL_MS = 360 * 60 * 1e3;
|
|
100
|
-
/** Recency window for the "react to the latest message" fallback. */
|
|
101
|
-
const LATEST_FALLBACK_MS = 600 * 1e3;
|
|
102
|
-
let persistenceFailureLogged = false;
|
|
103
|
-
function reportPersistenceFailure(scope, err) {
|
|
104
|
-
if (persistenceFailureLogged) return;
|
|
105
|
-
persistenceFailureLogged = true;
|
|
106
|
-
logVerbose(`imessage reply-cache: ${scope} disabled after first failure: ${String(err)}`);
|
|
107
|
-
}
|
|
108
|
-
const imessageReplyCacheByMessageId = /* @__PURE__ */ new Map();
|
|
109
|
-
const imessageShortIdToUuid = /* @__PURE__ */ new Map();
|
|
110
|
-
const imessageUuidToShortId = /* @__PURE__ */ new Map();
|
|
111
|
-
let imessageShortIdCounter = 0;
|
|
112
|
-
function resolveIMessageReplyCacheEntryKey(messageId) {
|
|
113
|
-
return createHash("sha256").update(messageId, "utf8").digest("hex").slice(0, 32);
|
|
114
|
-
}
|
|
115
|
-
function openReplyCacheStore() {
|
|
116
|
-
return getIMessageRuntime().state.openSyncKeyedStore({
|
|
117
|
-
namespace: IMESSAGE_REPLY_CACHE_NAMESPACE,
|
|
118
|
-
maxEntries: IMESSAGE_REPLY_CACHE_MAX_ENTRIES
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
function openReplyCacheCounterStore() {
|
|
122
|
-
return getIMessageRuntime().state.openSyncKeyedStore({
|
|
123
|
-
namespace: IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE,
|
|
124
|
-
maxEntries: 1
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
function remainingTtlMs(timestamp) {
|
|
128
|
-
const remaining = REPLY_CACHE_TTL_MS - Math.max(0, Date.now() - timestamp);
|
|
129
|
-
return remaining > 0 ? remaining : void 0;
|
|
130
|
-
}
|
|
131
|
-
let hydrated = false;
|
|
132
|
-
function hydrateFromStoreOnce() {
|
|
133
|
-
if (hydrated) return;
|
|
134
|
-
hydrated = true;
|
|
135
|
-
const cutoff = Date.now() - REPLY_CACHE_TTL_MS;
|
|
136
|
-
let entries;
|
|
137
|
-
try {
|
|
138
|
-
const counter = openReplyCacheCounterStore().lookup(IMESSAGE_REPLY_CACHE_COUNTER_KEY);
|
|
139
|
-
if (counter && Number.isSafeInteger(counter.counter) && counter.counter > 0) imessageShortIdCounter = Math.max(imessageShortIdCounter, counter.counter);
|
|
140
|
-
entries = openReplyCacheStore().entries().map(({ value }) => value).filter((entry) => entry.timestamp >= cutoff).toSorted((a, b) => a.timestamp - b.timestamp).slice(-2e3);
|
|
141
|
-
for (const entry of entries) {
|
|
142
|
-
const numeric = Number.parseInt(entry.shortId, 10);
|
|
143
|
-
if (Number.isFinite(numeric) && numeric > imessageShortIdCounter) imessageShortIdCounter = numeric;
|
|
144
|
-
}
|
|
145
|
-
} catch (err) {
|
|
146
|
-
reportPersistenceFailure("read", err);
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
if (entries.length === 0) return;
|
|
150
|
-
for (const entry of entries) {
|
|
151
|
-
imessageReplyCacheByMessageId.set(entry.messageId, entry);
|
|
152
|
-
imessageShortIdToUuid.set(entry.shortId, entry.messageId);
|
|
153
|
-
imessageUuidToShortId.set(entry.messageId, entry.shortId);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
function persistReplyCacheEntry(entry) {
|
|
157
|
-
const ttlMs = remainingTtlMs(entry.timestamp);
|
|
158
|
-
if (!ttlMs) return;
|
|
159
|
-
try {
|
|
160
|
-
openReplyCacheStore().register(resolveIMessageReplyCacheEntryKey(entry.messageId), entry, { ttlMs });
|
|
161
|
-
} catch (err) {
|
|
162
|
-
reportPersistenceFailure("write", err);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
function deleteReplyCacheEntry(messageId) {
|
|
166
|
-
try {
|
|
167
|
-
openReplyCacheStore().delete(resolveIMessageReplyCacheEntryKey(messageId));
|
|
168
|
-
} catch (err) {
|
|
169
|
-
reportPersistenceFailure("delete", err);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
function persistReplyCacheCounter() {
|
|
173
|
-
try {
|
|
174
|
-
openReplyCacheCounterStore().register(IMESSAGE_REPLY_CACHE_COUNTER_KEY, { counter: imessageShortIdCounter });
|
|
175
|
-
} catch (err) {
|
|
176
|
-
reportPersistenceFailure("counter", err);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
function buildReplyCacheEntry(entry, messageId, shortId) {
|
|
180
|
-
return {
|
|
181
|
-
accountId: entry.accountId,
|
|
182
|
-
messageId,
|
|
183
|
-
shortId,
|
|
184
|
-
timestamp: entry.timestamp,
|
|
185
|
-
...typeof entry.chatGuid === "string" ? { chatGuid: entry.chatGuid } : {},
|
|
186
|
-
...typeof entry.chatIdentifier === "string" ? { chatIdentifier: entry.chatIdentifier } : {},
|
|
187
|
-
...typeof entry.chatId === "number" ? { chatId: entry.chatId } : {},
|
|
188
|
-
...typeof entry.isFromMe === "boolean" ? { isFromMe: entry.isFromMe } : {}
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
function generateShortId() {
|
|
192
|
-
imessageShortIdCounter += 1;
|
|
193
|
-
persistReplyCacheCounter();
|
|
194
|
-
return String(imessageShortIdCounter);
|
|
195
|
-
}
|
|
196
|
-
function rememberIMessageReplyCache(entry) {
|
|
197
|
-
hydrateFromStoreOnce();
|
|
198
|
-
const messageId = entry.messageId.trim();
|
|
199
|
-
if (!messageId) return {
|
|
200
|
-
...entry,
|
|
201
|
-
shortId: ""
|
|
202
|
-
};
|
|
203
|
-
let shortId = imessageUuidToShortId.get(messageId);
|
|
204
|
-
if (!shortId) {
|
|
205
|
-
shortId = generateShortId();
|
|
206
|
-
imessageShortIdToUuid.set(shortId, messageId);
|
|
207
|
-
imessageUuidToShortId.set(messageId, shortId);
|
|
208
|
-
}
|
|
209
|
-
const fullEntry = buildReplyCacheEntry(entry, messageId, shortId);
|
|
210
|
-
imessageReplyCacheByMessageId.delete(messageId);
|
|
211
|
-
imessageReplyCacheByMessageId.set(messageId, fullEntry);
|
|
212
|
-
const cutoff = Date.now() - REPLY_CACHE_TTL_MS;
|
|
213
|
-
let evicted = false;
|
|
214
|
-
const deletedMessageIds = [];
|
|
215
|
-
for (const [key, value] of imessageReplyCacheByMessageId) {
|
|
216
|
-
if (value.timestamp >= cutoff) break;
|
|
217
|
-
imessageReplyCacheByMessageId.delete(key);
|
|
218
|
-
deletedMessageIds.push(key);
|
|
219
|
-
if (value.shortId) {
|
|
220
|
-
imessageShortIdToUuid.delete(value.shortId);
|
|
221
|
-
imessageUuidToShortId.delete(key);
|
|
222
|
-
}
|
|
223
|
-
evicted = true;
|
|
224
|
-
}
|
|
225
|
-
while (imessageReplyCacheByMessageId.size > IMESSAGE_REPLY_CACHE_MAX_ENTRIES) {
|
|
226
|
-
const oldest = imessageReplyCacheByMessageId.keys().next().value;
|
|
227
|
-
if (!oldest) break;
|
|
228
|
-
const oldEntry = imessageReplyCacheByMessageId.get(oldest);
|
|
229
|
-
imessageReplyCacheByMessageId.delete(oldest);
|
|
230
|
-
deletedMessageIds.push(oldest);
|
|
231
|
-
if (oldEntry?.shortId) {
|
|
232
|
-
imessageShortIdToUuid.delete(oldEntry.shortId);
|
|
233
|
-
imessageUuidToShortId.delete(oldest);
|
|
234
|
-
}
|
|
235
|
-
evicted = true;
|
|
236
|
-
}
|
|
237
|
-
if (evicted) for (const messageIdToDelete of deletedMessageIds) deleteReplyCacheEntry(messageIdToDelete);
|
|
238
|
-
persistReplyCacheEntry(fullEntry);
|
|
239
|
-
return fullEntry;
|
|
240
|
-
}
|
|
241
|
-
function hasChatScope(ctx) {
|
|
242
|
-
if (!ctx) return false;
|
|
243
|
-
return Boolean(normalizeOptionalString(ctx.chatGuid) || normalizeOptionalString(ctx.chatIdentifier) || typeof ctx.chatId === "number");
|
|
244
|
-
}
|
|
245
|
-
function isCrossChatMismatch(cached, ctx) {
|
|
246
|
-
return resolveIMessageChatMatch(cached, ctx) === "mismatch";
|
|
247
|
-
}
|
|
248
|
-
function describeChatForError(values) {
|
|
249
|
-
const parts = [];
|
|
250
|
-
if (normalizeOptionalString(values.chatGuid)) parts.push("chatGuid=<redacted>");
|
|
251
|
-
if (normalizeOptionalString(values.chatIdentifier)) parts.push("chatIdentifier=<redacted>");
|
|
252
|
-
if (typeof values.chatId === "number") parts.push("chatId=<redacted>");
|
|
253
|
-
return parts.length === 0 ? "<unknown chat>" : parts.join(", ");
|
|
254
|
-
}
|
|
255
|
-
function describeMessageIdForError(inputId, inputKind) {
|
|
256
|
-
if (inputKind === "short") return `<short:${inputId.length}-digit>`;
|
|
257
|
-
return `<uuid:${inputId.slice(0, 8)}...>`;
|
|
258
|
-
}
|
|
259
|
-
function buildCrossChatError(inputId, inputKind, cached, ctx) {
|
|
260
|
-
const remediation = inputKind === "short" ? "Use a message ID from the current chat target; MessageSidFull from another chat is rejected." : "Retry with the correct chat target.";
|
|
261
|
-
return /* @__PURE__ */ new Error(`iMessage message id ${describeMessageIdForError(inputId, inputKind)} belongs to a different chat (${describeChatForError(cached)}) than the current call target (${describeChatForError(ctx)}). ${remediation}`);
|
|
262
|
-
}
|
|
263
|
-
function resolveIMessageMessageId(shortOrUuid, opts) {
|
|
264
|
-
const trimmed = shortOrUuid.trim();
|
|
265
|
-
if (!trimmed) return trimmed;
|
|
266
|
-
hydrateFromStoreOnce();
|
|
267
|
-
if (/^\d+$/.test(trimmed)) {
|
|
268
|
-
const uuid = imessageShortIdToUuid.get(trimmed);
|
|
269
|
-
if (uuid) {
|
|
270
|
-
const cached = imessageReplyCacheByMessageId.get(uuid);
|
|
271
|
-
if (opts?.chatContext && hasChatScope(opts.chatContext)) {
|
|
272
|
-
if (cached && isCrossChatMismatch(cached, opts.chatContext)) throw buildCrossChatError(trimmed, "short", cached, opts.chatContext);
|
|
273
|
-
}
|
|
274
|
-
if (opts?.requireFromMe && cached?.isFromMe !== true) throw buildFromMeError(trimmed, "short");
|
|
275
|
-
return uuid;
|
|
276
|
-
}
|
|
277
|
-
if (opts?.requireKnownShortId && !hasChatScope(opts.chatContext)) throw new Error(`iMessage short message id ${describeMessageIdForError(trimmed, "short")} requires a chat scope (chatGuid / chatIdentifier / chatId or a target).`);
|
|
278
|
-
if (opts?.requireKnownShortId) throw new Error(`iMessage short message id ${describeMessageIdForError(trimmed, "short")} is no longer available. Use MessageSidFull.`);
|
|
279
|
-
return trimmed;
|
|
280
|
-
}
|
|
281
|
-
const cached = imessageReplyCacheByMessageId.get(trimmed);
|
|
282
|
-
if (opts?.chatContext) {
|
|
283
|
-
if (cached && isCrossChatMismatch(cached, opts.chatContext)) throw buildCrossChatError(trimmed, "uuid", cached, opts.chatContext);
|
|
284
|
-
}
|
|
285
|
-
if (opts?.requireFromMe && cached?.isFromMe !== true) throw buildFromMeError(trimmed, "uuid");
|
|
286
|
-
return trimmed;
|
|
287
|
-
}
|
|
288
|
-
function isKnownFromMeIMessageMessageId(messageId, ctx) {
|
|
289
|
-
const trimmed = normalizeOptionalString(messageId);
|
|
290
|
-
if (!trimmed || !ctx.accountId || !hasChatScope(ctx)) return false;
|
|
291
|
-
hydrateFromStoreOnce();
|
|
292
|
-
const cached = imessageReplyCacheByMessageId.get(trimmed);
|
|
293
|
-
if (!cached || cached.isFromMe !== true || cached.accountId !== ctx.accountId) return false;
|
|
294
|
-
return isPositiveIMessageChatMatch(cached, ctx);
|
|
295
|
-
}
|
|
296
|
-
function buildFromMeError(inputId, inputKind) {
|
|
297
|
-
return /* @__PURE__ */ new Error(`iMessage message id ${describeMessageIdForError(inputId, inputKind)} is not one this agent sent. edit and unsend can only target messages the gateway delivered itself; messages received from other participants cannot be modified.`);
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Return the most recent cached entry whose chat scope matches the supplied
|
|
301
|
-
* context. Used as a fallback when an agent calls a per-message action (e.g.
|
|
302
|
-
* `react`) without specifying a `messageId` — the natural intent is "react
|
|
303
|
-
* to the message I just received in this chat."
|
|
304
|
-
*
|
|
305
|
-
* Strict semantics for safety:
|
|
306
|
-
* - Caller must supply a chat scope. We refuse to "guess" the active chat.
|
|
307
|
-
* - Cached entry must positively match on at least one identifier kind
|
|
308
|
-
* (chatGuid, chatIdentifier, chatId, or normalized direct-DM fingerprint).
|
|
309
|
-
* We do NOT fall through on "no overlapping identifier" — that's how a
|
|
310
|
-
* cached entry from a foreign chat could be returned when the caller's
|
|
311
|
-
* context didn't share any identifier kind with the cache.
|
|
312
|
-
* - Caller must supply an accountId; we never cross account boundaries.
|
|
313
|
-
* - We only consider entries newer than `LATEST_FALLBACK_MS`. The intent
|
|
314
|
-
* of "react to the latest" is "the message I just received," not
|
|
315
|
-
* "anything in this chat from any time."
|
|
316
|
-
*/
|
|
317
|
-
function findLatestIMessageEntryForChat(ctx) {
|
|
318
|
-
if (!hasChatScope(ctx)) return;
|
|
319
|
-
if (!ctx.accountId) return;
|
|
320
|
-
const cutoff = Date.now() - LATEST_FALLBACK_MS;
|
|
321
|
-
let best;
|
|
322
|
-
for (const entry of imessageReplyCacheByMessageId.values()) {
|
|
323
|
-
if (entry.accountId !== ctx.accountId) continue;
|
|
324
|
-
if (entry.timestamp < cutoff) continue;
|
|
325
|
-
if (!isPositiveIMessageChatMatch(entry, ctx)) continue;
|
|
326
|
-
if (!best || entry.timestamp > best.timestamp) best = entry;
|
|
327
|
-
}
|
|
328
|
-
return best;
|
|
329
|
-
}
|
|
330
|
-
function resolveIMessageCachedResourceBinding(messageId, ctx) {
|
|
331
|
-
hydrateFromStoreOnce();
|
|
332
|
-
const entry = imessageReplyCacheByMessageId.get(messageId.trim());
|
|
333
|
-
if (!entry) return "unknown";
|
|
334
|
-
if (Date.now() - entry.timestamp > REPLY_CACHE_TTL_MS) return "unknown";
|
|
335
|
-
if (entry.accountId !== ctx.accountId) return "mismatch";
|
|
336
|
-
const chatMatch = resolveIMessageChatMatch(entry, ctx);
|
|
337
|
-
if (chatMatch !== "match") return chatMatch;
|
|
338
|
-
return "match";
|
|
339
|
-
}
|
|
340
|
-
function isIMessageCurrentMessageInChat(params) {
|
|
341
|
-
if (!params.accountId || !hasChatScope(params.chatContext)) return false;
|
|
342
|
-
const currentMessageId = normalizeOptionalString(String(params.currentMessageId));
|
|
343
|
-
if (!currentMessageId) return false;
|
|
344
|
-
hydrateFromStoreOnce();
|
|
345
|
-
const fullMessageId = /^\d+$/.test(currentMessageId) ? imessageShortIdToUuid.get(currentMessageId) : currentMessageId;
|
|
346
|
-
if (!fullMessageId) return false;
|
|
347
|
-
return resolveIMessageCachedResourceBinding(fullMessageId, {
|
|
348
|
-
...params.chatContext,
|
|
349
|
-
accountId: params.accountId
|
|
350
|
-
}) === "match";
|
|
351
|
-
}
|
|
352
|
-
//#endregion
|
|
353
|
-
export { normalizeDirectChatIdentifier as _, findLatestIMessageEntryForChat as a, rememberIMessageReplyCache as c, resolveIMessageReplyCacheEntryKey as d, getIMessageRuntime as f, isIMessageEmailChatIdentifier as g, chatContextFromIMessageTarget as h, IMESSAGE_REPLY_CACHE_NAMESPACE as i, resolveIMessageCachedResourceBinding as l, setIMessageRuntime as m, IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE as n, isIMessageCurrentMessageInChat as o, getOptionalIMessageRuntime as p, IMESSAGE_REPLY_CACHE_MAX_ENTRIES as r, isKnownFromMeIMessageMessageId as s, IMESSAGE_REPLY_CACHE_COUNTER_KEY as t, resolveIMessageMessageId as u, resolveIMessageDirectChatService as v };
|