@openclaw/imessage 2026.9.6 → 2026.9.7

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.
Files changed (81) hide show
  1. package/assets/icon.png +0 -0
  2. package/dist/.setup/{accounts-CUxZrTcY.mjs → accounts-nzcU0cq2.mjs} +3 -6
  3. package/dist/.setup/{actions.runtime-D0zG_96v.mjs → actions.runtime-DdBN29zj.mjs} +3 -5
  4. package/dist/.setup/{api-RZv6yXul.mjs → api-B9Qx21G-.mjs} +6 -5
  5. package/dist/.setup/{api-VLVcFh7r.mjs → api-D2S6FhGw.mjs} +7 -7
  6. package/dist/.setup/{approval-handler.runtime-Bv5_XgNr.mjs → approval-handler.runtime-DKSq0DWq.mjs} +8 -8
  7. package/dist/.setup/{approval-reactions-D6RFg6lU.mjs → approval-reactions-Bp33xc83.mjs} +5 -5
  8. package/dist/.setup/{channel-BLTaKdSK.mjs → channel-9fyvuN_p.mjs} +97 -152
  9. package/dist/.setup/channel.runtime-n-MPKL8k.mjs +92 -0
  10. package/dist/.setup/{config-schema-CCbTcJEV.mjs → config-schema-4Q3_EqYt.mjs} +9 -1
  11. package/dist/.setup/{group-policy-Cjztkquv.mjs → group-policy-90pw4tyG.mjs} +48 -334
  12. package/dist/.setup/{media-contract-DKJK7rh3.mjs → media-contract-CagwSMvo.mjs} +1 -1
  13. package/dist/.setup/{message-tool-api-pvlcOJPG.mjs → message-tool-api-D032l82H.mjs} +3 -18
  14. package/dist/.setup/{monitor-BJ3Ozi6I.mjs → monitor-fv9nRuTH.mjs} +211 -280
  15. package/dist/.setup/{question-reactions-lm8OxWbb.mjs → question-reactions-Cy-dmU_j.mjs} +1 -1
  16. package/dist/.setup/{remote-file-Bye99O-C.mjs → remote-file-DRPCaVmg.mjs} +70 -21
  17. package/dist/.setup/rolldown-runtime-BBjsoOtd.mjs +27 -0
  18. package/dist/.setup/{sanitize-outbound-sJPF2DVM.mjs → sanitize-outbound-3MLljgtY.mjs} +86 -24
  19. package/dist/.setup/{send-odAfTQ2J.mjs → send-Cu9nl757.mjs} +28 -112
  20. package/dist/.setup/setup-core-8MDdYHj5.mjs +226 -0
  21. package/dist/.setup/{channel.runtime---s2k4YN.mjs → setup-surface-tGXFDdWh.mjs} +6 -103
  22. package/dist/.setup/state-migrations-B-Q7bO0Y.mjs +47 -0
  23. package/dist/.setup/{targets-Cc9vthzI.mjs → targets-BicBiYsM.mjs} +10 -16
  24. package/dist/api.js +7 -7
  25. package/dist/channel-config-api.js +1 -1
  26. package/dist/channel-plugin-api.js +1 -1
  27. package/dist/contract-api.js +1 -1
  28. package/dist/doctor-contract-api.js +168 -7
  29. package/dist/media-contract-api.js +1 -1
  30. package/dist/message-tool-api.js +1 -1
  31. package/dist/runtime-api.js +6 -6
  32. package/dist/setup-entry.js +1 -1
  33. package/dist/src/chat-db.worker.js +6 -1
  34. package/node_modules/typebox/build/compile/code.mjs +1 -2
  35. package/node_modules/typebox/build/compile/validator.mjs +5 -1
  36. package/node_modules/typebox/build/format/iri.mjs +16 -9
  37. package/node_modules/typebox/build/guard/emit.mjs +2 -2
  38. package/node_modules/typebox/build/guard/guard.d.mts +7 -5
  39. package/node_modules/typebox/build/guard/guard.mjs +14 -9
  40. package/node_modules/typebox/build/guard/index.mjs +6 -0
  41. package/node_modules/typebox/build/guard/unicode/unicode.d.mts +6 -0
  42. package/node_modules/typebox/build/guard/unicode/unicode.mjs +35 -0
  43. package/node_modules/typebox/build/guard/{string.mjs → unicode/unicode_segment.mjs} +16 -19
  44. package/node_modules/typebox/build/schema/build.mjs +2 -3
  45. package/node_modules/typebox/build/schema/engine/_pathing.d.mts +1 -0
  46. package/node_modules/typebox/build/schema/engine/_pathing.mjs +11 -0
  47. package/node_modules/typebox/build/schema/engine/_refine.mjs +13 -5
  48. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +2 -1
  49. package/node_modules/typebox/build/schema/engine/dependencies.mjs +2 -1
  50. package/node_modules/typebox/build/schema/engine/dependentSchemas.mjs +2 -1
  51. package/node_modules/typebox/build/schema/engine/dynamicRef.d.mts +1 -1
  52. package/node_modules/typebox/build/schema/engine/dynamicRef.mjs +2 -2
  53. package/node_modules/typebox/build/schema/engine/format.mjs +9 -2
  54. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +3 -2
  55. package/node_modules/typebox/build/schema/engine/properties.mjs +3 -2
  56. package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -1
  57. package/node_modules/typebox/build/schema/engine/recursiveRef.d.mts +1 -1
  58. package/node_modules/typebox/build/schema/engine/recursiveRef.mjs +2 -2
  59. package/node_modules/typebox/build/schema/engine/ref.d.mts +1 -1
  60. package/node_modules/typebox/build/schema/engine/ref.mjs +2 -2
  61. package/node_modules/typebox/build/schema/engine/schema.mjs +24 -24
  62. package/node_modules/typebox/build/schema/parse.mjs +3 -0
  63. package/node_modules/typebox/build/schema/pointer/pointer.d.mts +1 -6
  64. package/node_modules/typebox/build/schema/pointer/pointer.mjs +47 -64
  65. package/node_modules/typebox/build/system/settings/internal.d.mts +2 -0
  66. package/node_modules/typebox/build/system/settings/internal.mjs +20 -0
  67. package/node_modules/typebox/build/system/settings/settings.d.mts +11 -0
  68. package/node_modules/typebox/build/system/settings/settings.mjs +14 -0
  69. package/node_modules/typebox/build/type/extends/inference.d.mts +1 -1
  70. package/node_modules/typebox/build/type/extends/inference.mjs +15 -2
  71. package/node_modules/typebox/build/type/extends/tuple.d.mts +2 -1
  72. package/node_modules/typebox/build/type/extends/tuple.mjs +21 -1
  73. package/node_modules/typebox/build/value/parse/parse.mjs +10 -4
  74. package/node_modules/typebox/package.json +1 -1
  75. package/node_modules/typebox/readme.md +3 -1
  76. package/openclaw.plugin.json +14 -0
  77. package/package.json +5 -5
  78. package/dist/.setup/rolldown-runtime-D7D4PA-g.mjs +0 -13
  79. package/dist/.setup/state-contract-DvahEY4X.mjs +0 -56
  80. package/dist/.setup/state-migrations-DIDzI99d.mjs +0 -277
  81. /package/node_modules/typebox/build/guard/{string.d.mts → unicode/unicode_segment.d.mts} +0 -0
@@ -1,4 +1,4 @@
1
- import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
1
+ import { t as __exportAll } from "./rolldown-runtime-BBjsoOtd.mjs";
2
2
  import { n as normalizeIMessageGuid, t as resolveIMessageReactionContext } from "./reaction-context-Br-vAJFp.mjs";
3
3
  import { createQuestionReactionTargetStore, questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
4
4
  //#region extensions/imessage/src/question-reactions.ts
@@ -1,13 +1,68 @@
1
- import { p as resolveLocalIMessageChatDbPath } from "./accounts-CUxZrTcY.mjs";
2
- import { _ as chatContextFromIMessageTarget, m as resolveIMessageMessageId, p as resolveIMessageCachedResourceBinding, v as isIMessageEmailChatIdentifier } from "./sanitize-outbound-sJPF2DVM.mjs";
1
+ import { p as resolveLocalIMessageChatDbPath } from "./accounts-nzcU0cq2.mjs";
2
+ import { T as isIMessageEmailChatIdentifier, m as resolveIMessageMessageId, p as resolveIMessageCachedResourceBinding, w as chatContextFromIMessageTarget } from "./sanitize-outbound-3MLljgtY.mjs";
3
3
  import path from "node:path";
4
4
  import { normalizeScpRemoteHost } from "openclaw/plugin-sdk/host-runtime";
5
5
  import { createActionGate } from "openclaw/plugin-sdk/channel-actions";
6
- import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";
6
+ import { createSubsystemLogger, logVerbose } from "openclaw/plugin-sdk/runtime-env";
7
7
  import { randomUUID } from "node:crypto";
8
- import { runCommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
9
- import { openNodeSqliteDatabase } from "openclaw/plugin-sdk/sqlite-runtime";
8
+ import { resolveRuntimeWorkerUrl, runCommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
9
+ import { openSqliteWorkerStore } from "openclaw/plugin-sdk/sqlite-runtime";
10
10
  import { sanitizeTempFileName } from "openclaw/plugin-sdk/temp-path";
11
+ //#region extensions/imessage/src/chat-db.ts
12
+ function openIMessageChatDbReader(databasePath) {
13
+ return openSqliteWorkerStore({
14
+ moduleUrl: resolveRuntimeWorkerUrl({
15
+ currentModuleUrl: import.meta.url,
16
+ sourceWorkerName: "chat-db.worker",
17
+ distWorkerPath: "extensions/imessage/src/chat-db.worker.js",
18
+ package: {
19
+ name: "@openclaw/imessage",
20
+ distWorkerPath: "src/chat-db.worker.js"
21
+ }
22
+ }),
23
+ databasePath,
24
+ existingOnly: true,
25
+ input: void 0
26
+ });
27
+ }
28
+ async function resolveIMessageStartupRowidWatermark(dbPath) {
29
+ let store;
30
+ try {
31
+ store = await openIMessageChatDbReader(dbPath);
32
+ if (!store) throw new Error("Messages database is unavailable");
33
+ return await store.execute({
34
+ type: "startupWatermark",
35
+ input: void 0
36
+ });
37
+ } catch (err) {
38
+ logVerbose(`imessage: startup rowid watermark unavailable for db=${dbPath}: ${String(err)}`);
39
+ return null;
40
+ } finally {
41
+ await store?.close();
42
+ }
43
+ }
44
+ async function withIMessageReceiptGuidReader(databasePath, use) {
45
+ let store;
46
+ let closed = false;
47
+ const read = async (command) => {
48
+ if (closed) throw new Error("iMessage receipt lookup is closed");
49
+ try {
50
+ store ??= await openIMessageChatDbReader(databasePath);
51
+ return store ? await store.execute(command) : null;
52
+ } catch {
53
+ return null;
54
+ }
55
+ };
56
+ try {
57
+ return await use(read);
58
+ } finally {
59
+ closed = true;
60
+ try {
61
+ await store?.close();
62
+ } catch {}
63
+ }
64
+ }
65
+ //#endregion
11
66
  //#region extensions/imessage/src/message-resource-db.ts
12
67
  function normalizeIMessageMessageGuidForLookup(messageId) {
13
68
  const trimmed = messageId.trim();
@@ -53,7 +108,7 @@ function matchesAnyChatCandidate(stored, candidates) {
53
108
  if (typeof stored !== "string") return false;
54
109
  return candidates.some((candidate) => matchesChatCandidate(stored, candidate));
55
110
  }
56
- function checkIMessageResourceBinding(params) {
111
+ async function checkIMessageResourceBinding(params) {
57
112
  const dbPath = resolveLocalIMessageChatDbPath(params);
58
113
  if (!dbPath) return "unavailable";
59
114
  const messageGuid = normalizeIMessageMessageGuidForLookup(params.messageId);
@@ -64,25 +119,19 @@ function checkIMessageResourceBinding(params) {
64
119
  const chatId = params.chatContext.chatId;
65
120
  const hasChatId = typeof chatId === "number" && Number.isSafeInteger(chatId) && chatId > 0;
66
121
  if (!hasChatId && expectedChatGuids.length === 0 && expectedChatIdentifiers.length === 0 && identifierChatGuids.length === 0) return "unavailable";
67
- let db;
122
+ let store;
68
123
  try {
69
- db = openNodeSqliteDatabase(dbPath, { readOnly: true });
70
- return db.prepare(`SELECT cmj.chat_id AS chatId,
71
- c.guid AS chatGuid,
72
- c.chat_identifier AS chatIdentifier
73
- FROM message m
74
- JOIN chat_message_join cmj ON cmj.message_id = m.ROWID
75
- JOIN chat c ON c.ROWID = cmj.chat_id
76
- WHERE m.guid = ?`).all(messageGuid).map((row) => ({
77
- chatId: row.chatId,
78
- chatGuid: row.chatGuid,
79
- chatIdentifier: row.chatIdentifier
124
+ store = await openIMessageChatDbReader(dbPath);
125
+ if (!store) return "unavailable";
126
+ return (await store.execute({
127
+ type: "messageChats",
128
+ input: { messageGuid }
80
129
  })).some((row) => (!hasChatId || row.chatId === chatId) && (expectedChatGuids.length === 0 || matchesAnyChatCandidate(row.chatGuid, expectedChatGuids)) && (expectedChatIdentifiers.length === 0 || matchesAnyChatCandidate(row.chatIdentifier, expectedChatIdentifiers)) && (identifierChatGuids.length === 0 || matchesAnyChatCandidate(row.chatGuid, identifierChatGuids))) ? "match" : "mismatch";
81
130
  } catch {
82
131
  return "unavailable";
83
132
  } finally {
84
133
  try {
85
- db?.close();
134
+ await store?.close();
86
135
  } catch {}
87
136
  }
88
137
  }
@@ -131,7 +180,7 @@ async function authorizeIMessageResourceReference(params) {
131
180
  if (cacheBinding === "unknown" && normalizedMessageId !== params.messageId.trim()) cacheBinding = await resolveIMessageCachedResourceBinding(normalizedMessageId, cacheContext);
132
181
  if (cacheBinding === "match") return;
133
182
  if (cacheBinding === "mismatch") throw new Error("iMessage message reference belongs to a different account or conversation.");
134
- const providerBinding = params.hasExclusiveLocalDatabase ? checkIMessageResourceBinding(params) : "unavailable";
183
+ const providerBinding = params.hasExclusiveLocalDatabase ? await checkIMessageResourceBinding(params) : "unavailable";
135
184
  if (providerBinding === "match") return;
136
185
  if (providerBinding === "mismatch") throw new Error("iMessage message reference does not belong to the selected conversation.");
137
186
  if (params.conversationReadOrigin === "direct-operator") return;
@@ -245,4 +294,4 @@ rm -rf -- ${remoteDir}
245
294
  }
246
295
  }
247
296
  //#endregion
248
- export { authorizeIMessageResourceReference as n, resolveAuthorizedIMessageReplyReference as r, withIMessageRemoteFile as t };
297
+ export { withIMessageReceiptGuidReader as a, resolveIMessageStartupRowidWatermark as i, authorizeIMessageResourceReference as n, resolveAuthorizedIMessageReplyReference as r, withIMessageRemoteFile as t };
@@ -0,0 +1,27 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __exportAll = (all, no_symbols) => {
7
+ let target = {};
8
+ for (var name in all) __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: true
11
+ });
12
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
13
+ return target;
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
17
+ key = keys[i];
18
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
19
+ get: ((k) => from[k]).bind(null, key),
20
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
21
+ });
22
+ }
23
+ return to;
24
+ };
25
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
26
+ //#endregion
27
+ export { __reExport as n, __exportAll as t };
@@ -1,10 +1,10 @@
1
- import { u as expandIMessageUserPath } from "./accounts-CUxZrTcY.mjs";
2
- import { i as invalidateCachedIMessagePrivateApiStatus } from "./message-tool-api-pvlcOJPG.mjs";
1
+ import { u as expandIMessageUserPath } from "./accounts-nzcU0cq2.mjs";
2
+ import { i as invalidateCachedIMessagePrivateApiStatus } from "./message-tool-api-D032l82H.mjs";
3
3
  import { t as getIMessageRuntime } from "./runtime-Cza4CY5T.mjs";
4
- import { a as IMESSAGE_REPLY_CACHE_NAMESPACE, f as resolveIMessageReplyCacheEntryKey, i as IMESSAGE_REPLY_CACHE_MAX_ENTRIES, n as IMESSAGE_REPLY_CACHE_COUNTER_KEY, o as IMESSAGE_REPLY_CACHE_TTL_MS, r as IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE } from "./state-contract-DvahEY4X.mjs";
5
4
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
6
5
  import { normalizeE164 } from "openclaw/plugin-sdk/account-resolution";
7
6
  import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
7
+ import { createHash } from "node:crypto";
8
8
  import { runCommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
9
9
  import { resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
10
10
  import { spawn } from "node:child_process";
@@ -98,6 +98,60 @@ function isPositiveIMessageChatMatch(cached, current) {
98
98
  const DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS = 1e4;
99
99
  const DEFAULT_IMESSAGE_SEND_TIMEOUT_MS = 18e4;
100
100
  //#endregion
101
+ //#region extensions/imessage/src/state-contract.ts
102
+ const IMESSAGE_REPLY_CACHE_NAMESPACE = "imessage.reply-cache";
103
+ const IMESSAGE_REPLY_CACHE_MAX_ENTRIES = 2e3;
104
+ const IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE = "imessage.reply-cache-counter";
105
+ const IMESSAGE_REPLY_CACHE_COUNTER_KEY = "short-id-counter";
106
+ const IMESSAGE_REPLY_CACHE_TTL_MS = 216e5;
107
+ function resolveIMessageReplyCacheEntryKey(messageId) {
108
+ return createHash("sha256").update(messageId, "utf8").digest("hex").slice(0, 32);
109
+ }
110
+ const MAX_FAILURE_RETRY_MAP_SIZE = 512;
111
+ const MAX_FAILURE_RETRY_MAP_JSON_BYTES = 48e3;
112
+ const textEncoder = new TextEncoder();
113
+ const IMESSAGE_CATCHUP_CURSOR_NAMESPACE = "imessage.catchup-cursors";
114
+ function resolveIMessageCatchupCursorKey(accountId) {
115
+ return createHash("sha256").update(accountId, "utf8").digest("hex").slice(0, 32);
116
+ }
117
+ /**
118
+ * Bound the retry map so a pathological storm of unique failing GUIDs
119
+ * cannot grow the cursor value without limit. Keeps the `maxSize` entries
120
+ * with the highest counts (closest to give-up) when over the bound.
121
+ */
122
+ function capFailureRetriesMap(map, maxSize = MAX_FAILURE_RETRY_MAP_SIZE, maxBytes = MAX_FAILURE_RETRY_MAP_JSON_BYTES) {
123
+ const entries = Object.entries(map);
124
+ if (entries.length <= maxSize && textEncoder.encode(JSON.stringify(map)).byteLength <= maxBytes) return map;
125
+ entries.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
126
+ const capped = {};
127
+ for (const [guid, count] of entries.slice(0, maxSize)) {
128
+ capped[guid] = count;
129
+ if (textEncoder.encode(JSON.stringify(capped)).byteLength > maxBytes) {
130
+ delete capped[guid];
131
+ break;
132
+ }
133
+ }
134
+ return capped;
135
+ }
136
+ const IMESSAGE_SENT_ECHOES_TTL_MS = 432e5;
137
+ const IMESSAGE_SENT_ECHOES_NAMESPACE = "imessage.sent-echoes";
138
+ function resolveIMessageSentEchoEntryKey(entry) {
139
+ return createHash("sha256").update(JSON.stringify([
140
+ entry.scope,
141
+ entry.text ?? "",
142
+ resolveIMessageEchoMediaKey(entry.media) ?? "",
143
+ entry.messageId ?? "",
144
+ entry.timestamp
145
+ ])).digest("hex").slice(0, 32);
146
+ }
147
+ function resolveIMessageEchoMediaKey(media) {
148
+ const contentType = media?.contentType?.trim().toLowerCase() || void 0;
149
+ const kind = media?.kind && media.kind !== "unknown" ? media.kind : void 0;
150
+ if (kind) return `kind:${kind}`;
151
+ const normalizedContentType = contentType?.split(";", 1)[0]?.trim();
152
+ return normalizedContentType ? `mime:${normalizedContentType}` : void 0;
153
+ }
154
+ //#endregion
101
155
  //#region extensions/imessage/src/monitor-reply-cache.ts
102
156
  /** Recency window for the "react to the latest message" fallback. */
103
157
  const LATEST_FALLBACK_MS = 6e5;
@@ -109,7 +163,6 @@ function reportPersistenceFailure(scope, err) {
109
163
  }
110
164
  const imessageReplyCacheByMessageId = /* @__PURE__ */ new Map();
111
165
  const imessageShortIdToUuid = /* @__PURE__ */ new Map();
112
- const imessageUuidToShortId = /* @__PURE__ */ new Map();
113
166
  let imessageShortIdCounter = 0;
114
167
  function openReplyCacheStore() {
115
168
  return getIMessageRuntime().state.openKeyedStore({
@@ -135,12 +188,11 @@ function hydrateCounter(counter) {
135
188
  }
136
189
  function hydrateRows(entries) {
137
190
  const cutoff = Date.now() - IMESSAGE_REPLY_CACHE_TTL_MS;
138
- for (const entry of entries.filter((cached) => cached.timestamp >= cutoff).toSorted((a, b) => a.timestamp - b.timestamp).slice(-IMESSAGE_REPLY_CACHE_MAX_ENTRIES)) {
191
+ for (const entry of entries.filter((cached) => cached.timestamp >= cutoff).toSorted((a, b) => a.timestamp - b.timestamp).slice(-2e3)) {
139
192
  const numeric = Number.parseInt(entry.shortId, 10);
140
193
  if (Number.isFinite(numeric) && numeric > imessageShortIdCounter) imessageShortIdCounter = numeric;
141
194
  imessageReplyCacheByMessageId.set(entry.messageId, entry);
142
195
  imessageShortIdToUuid.set(entry.shortId, entry.messageId);
143
- imessageUuidToShortId.set(entry.messageId, entry.shortId);
144
196
  }
145
197
  }
146
198
  async function hydrateFromStoreOnce() {
@@ -221,12 +273,11 @@ async function rememberIMessageReplyCache(entry) {
221
273
  ...entry,
222
274
  shortId: ""
223
275
  };
224
- let shortId = imessageUuidToShortId.get(messageId);
276
+ let shortId = imessageReplyCacheByMessageId.get(messageId)?.shortId;
225
277
  const isNewMessage = !shortId;
226
278
  if (!shortId) {
227
279
  shortId = generateShortId();
228
280
  imessageShortIdToUuid.set(shortId, messageId);
229
- imessageUuidToShortId.set(messageId, shortId);
230
281
  }
231
282
  const fullEntry = buildReplyCacheEntry(entry, messageId, shortId);
232
283
  imessageReplyCacheByMessageId.delete(messageId);
@@ -237,10 +288,7 @@ async function rememberIMessageReplyCache(entry) {
237
288
  if (value.timestamp >= cutoff) break;
238
289
  imessageReplyCacheByMessageId.delete(key);
239
290
  deletedMessageIds.push(key);
240
- if (value.shortId) {
241
- imessageShortIdToUuid.delete(value.shortId);
242
- imessageUuidToShortId.delete(key);
243
- }
291
+ if (value.shortId) imessageShortIdToUuid.delete(value.shortId);
244
292
  }
245
293
  while (imessageReplyCacheByMessageId.size > IMESSAGE_REPLY_CACHE_MAX_ENTRIES) {
246
294
  const oldest = imessageReplyCacheByMessageId.keys().next().value;
@@ -248,10 +296,7 @@ async function rememberIMessageReplyCache(entry) {
248
296
  const oldEntry = imessageReplyCacheByMessageId.get(oldest);
249
297
  imessageReplyCacheByMessageId.delete(oldest);
250
298
  deletedMessageIds.push(oldest);
251
- if (oldEntry?.shortId) {
252
- imessageShortIdToUuid.delete(oldEntry.shortId);
253
- imessageUuidToShortId.delete(oldest);
254
- }
299
+ if (oldEntry?.shortId) imessageShortIdToUuid.delete(oldEntry.shortId);
255
300
  }
256
301
  const counter = imessageShortIdCounter;
257
302
  persistence = persistence.then(async () => {
@@ -314,8 +359,23 @@ async function isKnownFromMeIMessageMessageId(messageId, ctx) {
314
359
  if (!trimmed || !ctx.accountId || !hasChatScope(ctx)) return false;
315
360
  await hydrateFromStoreOnce();
316
361
  const cached = imessageReplyCacheByMessageId.get(trimmed);
317
- if (!cached || cached.isFromMe !== true || cached.accountId !== ctx.accountId) return false;
318
- return isPositiveIMessageChatMatch(cached, ctx);
362
+ if (!cached || cached.isFromMe !== true) return false;
363
+ return resolveCachedResourceBinding(trimmed, {
364
+ ...ctx,
365
+ accountId: ctx.accountId
366
+ }) === "match";
367
+ }
368
+ async function isKnownFromMeIMessageTarget(params) {
369
+ const { accountId, chatId, chatGuid, chatIdentifier } = params;
370
+ const ctx = {
371
+ accountId,
372
+ chatId,
373
+ chatGuid,
374
+ chatIdentifier
375
+ };
376
+ const isKnownFromMe = params.isKnownFromMeMessageId ?? isKnownFromMeIMessageMessageId;
377
+ for (const messageId of params.messageIds) if (await isKnownFromMe(messageId, ctx)) return true;
378
+ return false;
319
379
  }
320
380
  function buildFromMeError(inputId, inputKind) {
321
381
  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.`);
@@ -360,9 +420,7 @@ function resolveCachedResourceBinding(messageId, ctx) {
360
420
  if (!entry) return "unknown";
361
421
  if (Date.now() - entry.timestamp > 216e5) return "unknown";
362
422
  if (entry.accountId !== ctx.accountId) return "mismatch";
363
- const chatMatch = resolveIMessageChatMatch(entry, ctx);
364
- if (chatMatch !== "match") return chatMatch;
365
- return "match";
423
+ return resolveIMessageChatMatch(entry, ctx);
366
424
  }
367
425
  /** @deprecated Used only by hosts without asynchronous conversation matching. */
368
426
  function isIMessageCurrentMessageInChat(params) {
@@ -457,6 +515,7 @@ function recoverIMessageBridge(cliPath) {
457
515
  }
458
516
  //#endregion
459
517
  //#region extensions/imessage/src/client.ts
518
+ const IMSG_APPLE_FRAMEWORK_STDERR_PATTERN = /^(?:\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ imsg\[\d+:\d+\] )?Could not fetch group for change type \d+ with identifier [^:]+:ABGroup, making it a delete change type\.$/u;
460
519
  var IMessageRpcRequestError = class extends Error {
461
520
  constructor(message, code, data) {
462
521
  super(message);
@@ -466,8 +525,7 @@ var IMessageRpcRequestError = class extends Error {
466
525
  }
467
526
  };
468
527
  function isIMessageBridgeStall(error) {
469
- if (!(error instanceof Error)) return false;
470
- return error.message.includes("Timed out waiting for response");
528
+ return error instanceof Error && error.message.includes("Timed out waiting for response");
471
529
  }
472
530
  const BRIDGE_STALL_GUIDANCE = "The imsg private API bridge stopped responding. Run `imsg launch` to re-inject the dylib, then `openclaw channels status --probe` to refresh capability detection.";
473
531
  function describeIMessageBridgeStall(error) {
@@ -671,6 +729,10 @@ var IMessageRpcClient = class {
671
729
  const trimmed = line.trim();
672
730
  if (!trimmed) return;
673
731
  this.recordProcessDiagnostic(trimmed);
732
+ if (IMSG_APPLE_FRAMEWORK_STDERR_PATTERN.test(trimmed)) {
733
+ logVerbose(`imsg rpc: ${trimmed}`);
734
+ return;
735
+ }
674
736
  this.runtime?.error?.(`imsg rpc: ${trimmed}`);
675
737
  }
676
738
  handleLine(line) {
@@ -1313,4 +1375,4 @@ function sanitizeOutboundText(text) {
1313
1375
  return cleaned;
1314
1376
  }
1315
1377
  //#endregion
1316
- export { chatContextFromIMessageTarget as _, IMessageRpcRequestError as a, resolveIMessageDirectChatService as b, findLatestIMessageEntryForChat as c, isKnownFromMeIMessageMessageId as d, rememberIMessageReplyCache as f, DEFAULT_IMESSAGE_SEND_TIMEOUT_MS as g, DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS as h, extractMarkdownFormatRuns as i, isIMessageCurrentMessageInChat as l, resolveIMessageMessageId as m, sanitizeIMessageFinalOutboundText as n, createIMessageRpcClient as o, resolveIMessageCachedResourceBinding as p, sanitizeOutboundText as r, runIMessageCliJsonCommand as s, protectIMessageFencedRoleMarkers as t, isIMessageCurrentMessageInChatAsync as u, isIMessageEmailChatIdentifier as v, normalizeDirectChatIdentifier as y };
1378
+ export { DEFAULT_IMESSAGE_SEND_TIMEOUT_MS as C, resolveIMessageDirectChatService as D, normalizeDirectChatIdentifier as E, DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS as S, isIMessageEmailChatIdentifier as T, IMESSAGE_SENT_ECHOES_TTL_MS as _, IMessageRpcRequestError as a, resolveIMessageEchoMediaKey as b, findLatestIMessageEntryForChat as c, isKnownFromMeIMessageTarget as d, rememberIMessageReplyCache as f, IMESSAGE_SENT_ECHOES_NAMESPACE as g, IMESSAGE_CATCHUP_CURSOR_NAMESPACE as h, extractMarkdownFormatRuns as i, isIMessageCurrentMessageInChat as l, resolveIMessageMessageId as m, sanitizeIMessageFinalOutboundText as n, createIMessageRpcClient as o, resolveIMessageCachedResourceBinding as p, sanitizeOutboundText as r, runIMessageCliJsonCommand as s, protectIMessageFencedRoleMarkers as t, isIMessageCurrentMessageInChatAsync as u, capFailureRetriesMap as v, chatContextFromIMessageTarget as w, resolveIMessageSentEchoEntryKey as x, resolveIMessageCatchupCursorKey as y };
@@ -1,12 +1,11 @@
1
- import { c as parseIMessageTarget, o as normalizeIMessageHandle, t as formatIMessageChatTarget } from "./targets-Cc9vthzI.mjs";
2
- import { d as resolveIMessageChatDbLookupPath, l as resolveIMessageRemoteHost, n as hasExclusiveIMessageLocalDatabase, o as resolveIMessageAccount } from "./accounts-CUxZrTcY.mjs";
3
- import { _ as chatContextFromIMessageTarget, a as IMessageRpcRequestError, b as resolveIMessageDirectChatService, f as rememberIMessageReplyCache, n as sanitizeIMessageFinalOutboundText, o as createIMessageRpcClient, s as runIMessageCliJsonCommand, t as protectIMessageFencedRoleMarkers } from "./sanitize-outbound-sJPF2DVM.mjs";
4
- import { c as getIMessageApprovalApprovers, l as imessageApprovalAuth } from "./group-policy-Cjztkquv.mjs";
1
+ import { c as parseIMessageTarget, o as normalizeIMessageHandle, t as formatIMessageChatTarget } from "./targets-BicBiYsM.mjs";
2
+ import { d as resolveIMessageChatDbLookupPath, l as resolveIMessageRemoteHost, n as hasExclusiveIMessageLocalDatabase, o as resolveIMessageAccount } from "./accounts-nzcU0cq2.mjs";
3
+ import { D as resolveIMessageDirectChatService, _ as IMESSAGE_SENT_ECHOES_TTL_MS, a as IMessageRpcRequestError, b as resolveIMessageEchoMediaKey, f as rememberIMessageReplyCache, g as IMESSAGE_SENT_ECHOES_NAMESPACE, n as sanitizeIMessageFinalOutboundText, o as createIMessageRpcClient, s as runIMessageCliJsonCommand, t as protectIMessageFencedRoleMarkers, w as chatContextFromIMessageTarget, x as resolveIMessageSentEchoEntryKey } from "./sanitize-outbound-3MLljgtY.mjs";
4
+ import { c as getIMessageApprovalApprovers, l as imessageApprovalAuth } from "./group-policy-90pw4tyG.mjs";
5
5
  import { n as getOptionalIMessageRuntime, t as getIMessageRuntime } from "./runtime-Cza4CY5T.mjs";
6
- import { c as IMESSAGE_SENT_ECHOES_TTL_MS, d as resolveIMessageEchoMediaKey, p as resolveIMessageSentEchoEntryKey, s as IMESSAGE_SENT_ECHOES_NAMESPACE } from "./state-contract-DvahEY4X.mjs";
7
- import { d as normalizeConversationKey, i as registerIMessageApprovalReactionTarget, l as enumerateApprovalTargetKeys, s as buildIMessageApprovalConversationKeyForInbound, u as enumerateConversationKeyForms } from "./approval-reactions-D6RFg6lU.mjs";
6
+ import { d as normalizeConversationKey, i as registerIMessageApprovalReactionTarget, l as enumerateApprovalTargetKeys, s as buildIMessageApprovalConversationKeyForInbound, u as enumerateConversationKeyForms } from "./approval-reactions-Bp33xc83.mjs";
8
7
  import { n as normalizeIMessageGuid } from "./reaction-context-Br-vAJFp.mjs";
9
- import { r as resolveAuthorizedIMessageReplyReference, t as withIMessageRemoteFile } from "./remote-file-Bye99O-C.mjs";
8
+ import { a as withIMessageReceiptGuidReader, r as resolveAuthorizedIMessageReplyReference, t as withIMessageRemoteFile } from "./remote-file-DRPCaVmg.mjs";
10
9
  import { asOptionalRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
11
10
  import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
12
11
  import { createLazyRuntimeSurface } from "openclaw/plugin-sdk/lazy-runtime";
@@ -15,13 +14,11 @@ import { basename } from "node:path";
15
14
  import { extractOriginalFilename, kindFromMime, resolveOutboundAttachmentFromUrl } from "openclaw/plugin-sdk/media-runtime";
16
15
  import { logVerbose, sleep } from "openclaw/plugin-sdk/runtime-env";
17
16
  import { asDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
18
- import { resolveRuntimeWorkerUrl } from "openclaw/plugin-sdk/process-runtime";
19
17
  import { PlatformMessageNotDispatchedError, isApprovalNotFoundError } from "openclaw/plugin-sdk/error-runtime";
20
18
  import { addApprovalReactionHintToText, createApprovalReactionTargetStore, listApprovalReactionBindings } from "openclaw/plugin-sdk/approval-reaction-runtime";
21
19
  import { convertMarkdownTables, stripInlineDirectiveTagsForDelivery } from "openclaw/plugin-sdk/text-chunking";
22
20
  import { createChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
23
21
  import { createPluginStateErrorReporter } from "openclaw/plugin-sdk/plugin-state-runtime";
24
- import { openSqliteWorkerStore } from "openclaw/plugin-sdk/sqlite-runtime";
25
22
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
26
23
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
27
24
  import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
@@ -113,7 +110,7 @@ const loadResolveApprovalOverGateway = createLazyRuntimeSurface(() => import("op
113
110
  const reportPersistentError = createPluginStateErrorReporter(getOptionalIMessageRuntime, "imessage", "approval-poll-state", "iMessage persistent approval poll state failed");
114
111
  function readPersistedTarget(value) {
115
112
  const target = value;
116
- if (!target || typeof target.approvalId !== "string" || target.approvalKind !== "exec" && target.approvalKind !== "plugin" || !Array.isArray(target.optionDecisions)) return null;
113
+ if (!target || typeof target.approvalId !== "string" || target.approvalKind !== "exec" && target.approvalKind !== "plugin" && target.approvalKind !== "system-agent" || !Array.isArray(target.optionDecisions)) return null;
117
114
  const optionDecisions = target.optionDecisions.flatMap((pair) => {
118
115
  if (!Array.isArray(pair) || pair.length !== 2) return [];
119
116
  const [optionId, decision] = pair;
@@ -275,27 +272,18 @@ function readPollVoteEvent(message) {
275
272
  malformedVotes
276
273
  };
277
274
  }
278
- async function lookupPollTarget(params) {
275
+ async function lookupPollRecord(store, params) {
279
276
  for (const key of enumeratePollTargetKeys({
280
277
  accountId: params.accountId,
281
278
  conversation: params.conversation,
282
279
  pollGuid: params.pollGuid,
283
280
  optionIds: params.optionIds
284
281
  })) {
285
- const target = await pollTargets.lookup(key);
282
+ const target = await store.lookup(key);
286
283
  if (target) return target;
287
284
  }
288
285
  return null;
289
286
  }
290
- async function hasTombstone(params) {
291
- for (const key of enumeratePollTargetKeys({
292
- accountId: params.accountId,
293
- conversation: params.conversation,
294
- pollGuid: params.pollGuid,
295
- optionIds: params.optionIds
296
- })) if (await pollTombstones.lookup(key)) return true;
297
- return false;
298
- }
299
287
  /**
300
288
  * Outcomes for votes we own are logged at info, not verbose: an approval
301
289
  * decision is security-relevant, and diagnosing "the tap did nothing" must not
@@ -331,8 +319,8 @@ async function maybeResolveIMessageApprovalPollVote(params) {
331
319
  pollGuid: event.pollGuid,
332
320
  optionIds: event.votes.map((vote) => vote.optionId)
333
321
  };
334
- const target = await lookupPollTarget(lookupKey);
335
- if (!target) return await hasTombstone(lookupKey);
322
+ const target = await lookupPollRecord(pollTargets, lookupKey);
323
+ if (!target) return Boolean(await lookupPollRecord(pollTombstones, lookupKey));
336
324
  if (event.malformedVotes) {
337
325
  warn$1("approval poll vote ignored: malformed complete vote set", {
338
326
  approvalId: target.approvalId,
@@ -450,76 +438,19 @@ const iMessageApprovalPollTargets = {
450
438
  clearForTest: clearIMessageApprovalPollTargetsForTest
451
439
  };
452
440
  //#endregion
453
- //#region extensions/imessage/src/chat-db.ts
454
- function openIMessageChatDbReader(databasePath) {
455
- return openSqliteWorkerStore({
456
- moduleUrl: resolveRuntimeWorkerUrl({
457
- currentModuleUrl: import.meta.url,
458
- sourceWorkerName: "chat-db.worker",
459
- distWorkerPath: "extensions/imessage/src/chat-db.worker.js",
460
- package: {
461
- name: "@openclaw/imessage",
462
- distWorkerPath: "src/chat-db.worker.js"
463
- }
464
- }),
465
- databasePath,
466
- existingOnly: true,
467
- input: void 0
468
- });
469
- }
470
- async function resolveIMessageStartupRowidWatermark(dbPath) {
471
- let store;
472
- try {
473
- store = await openIMessageChatDbReader(dbPath);
474
- if (!store) throw new Error("Messages database is unavailable");
475
- return await store.execute({
476
- type: "startupWatermark",
477
- input: void 0
478
- });
479
- } catch (err) {
480
- logVerbose(`imessage: startup rowid watermark unavailable for db=${dbPath}: ${String(err)}`);
481
- return null;
482
- } finally {
483
- await store?.close();
484
- }
485
- }
486
- async function withIMessageReceiptGuidReader(databasePath, use) {
487
- let store;
488
- let closed = false;
489
- const read = async (command) => {
490
- if (closed) throw new Error("iMessage receipt lookup is closed");
491
- try {
492
- store ??= await openIMessageChatDbReader(databasePath);
493
- return store ? await store.execute(command) : null;
494
- } catch {
495
- return null;
496
- }
497
- };
498
- try {
499
- return await use(read);
500
- } finally {
501
- closed = true;
502
- try {
503
- await store?.close();
504
- } catch {}
505
- }
506
- }
507
- //#endregion
508
441
  //#region extensions/imessage/src/monitor/echo-text-corruption.ts
509
442
  function isLeadingEchoTextCorruptionMarker(code) {
510
443
  return code === 0 || code === 65279 || code === 65533 || code === 65534 || code === 65535;
511
444
  }
512
- function stripLeadingEchoTextCorruptionMarkers(text) {
445
+ function normalizeIMessageEchoText(text) {
446
+ if (!text) return;
447
+ const normalized = text.replace(/\r\n?/g, "\n").trim();
513
448
  let offset = 0;
514
- while (offset < text.length && isLeadingEchoTextCorruptionMarker(text.charCodeAt(offset))) offset += 1;
515
- return offset === 0 ? text : text.slice(offset);
449
+ while (offset < normalized.length && isLeadingEchoTextCorruptionMarker(normalized.charCodeAt(offset))) offset += 1;
450
+ return normalized.slice(offset).trim() || void 0;
516
451
  }
517
452
  //#endregion
518
453
  //#region extensions/imessage/src/monitor/persisted-echo-cache.ts
519
- function normalizeText(text) {
520
- if (!text) return;
521
- return stripLeadingEchoTextCorruptionMarkers(text.replace(/\r\n?/g, "\n").trim()).trim() || void 0;
522
- }
523
454
  function normalizeMessageId(messageId) {
524
455
  const normalized = messageId?.trim();
525
456
  if (!normalized || normalized === "ok" || normalized === "unknown") return;
@@ -579,7 +510,7 @@ async function persistEntry(entry, ttlMs) {
579
510
  return key;
580
511
  }
581
512
  async function rememberPersistedIMessageEcho(params) {
582
- const text = normalizeText(params.text);
513
+ const text = normalizeIMessageEchoText(params.text);
583
514
  const media = normalizeMedia(params.media);
584
515
  const messageId = normalizeMessageId(params.messageId);
585
516
  const entry = {
@@ -603,7 +534,7 @@ async function forgetPersistedIMessageEchoKey(key) {
603
534
  }
604
535
  }
605
536
  async function hasPersistedIMessageEcho(params) {
606
- const text = normalizeText(params.text);
537
+ const text = normalizeIMessageEchoText(params.text);
607
538
  const mediaKey = resolveIMessageEchoMediaKey(params.media);
608
539
  const messageId = normalizeMessageId(params.messageId);
609
540
  if (!text && !mediaKey && !messageId) return false;
@@ -659,10 +590,6 @@ function resolveOutboundMessageGuid(result) {
659
590
  function isNumericMessageRowId(value) {
660
591
  return typeof value === "string" && /^\d+$/.test(value.trim());
661
592
  }
662
- function resolveTargetService(target) {
663
- if (target.kind !== "handle") return;
664
- if (target.serviceExplicit || target.service !== "auto") return target.service;
665
- }
666
593
  function normalizeResolvedMessageGuid(value) {
667
594
  if (typeof value !== "string") return null;
668
595
  const trimmed = value.trim();
@@ -732,9 +659,6 @@ function shouldRecoverApprovalPromptGuid(params) {
732
659
  function canCheckSentMessageAfterRpcTimeout(params) {
733
660
  return Boolean(params.resolveSentMessageGuidImpl) || canResolveLatestSentMessageGuidFromChatDb(params.dbPath);
734
661
  }
735
- function resolveOutboundEchoText(text) {
736
- return text.trim() || void 0;
737
- }
738
662
  function resolveOutboundEchoMedia(mediaContentType) {
739
663
  if (!mediaContentType) return;
740
664
  return {
@@ -862,7 +786,7 @@ async function trySendAttachmentForTarget(params) {
862
786
  let result;
863
787
  let pendingEchoKey;
864
788
  try {
865
- if (echoScope) pendingEchoKey = await rememberPersistedIMessageEcho({
789
+ pendingEchoKey = await rememberPersistedIMessageEcho({
866
790
  scope: echoScope,
867
791
  text: params.echoText,
868
792
  media: params.echoMedia,
@@ -926,7 +850,7 @@ async function trySendAttachmentForTarget(params) {
926
850
  resolveMessageGuidImpl: params.resolveMessageGuidImpl
927
851
  });
928
852
  const messageId = resolvedId ?? (result.ok || result.success ? "ok" : "unknown");
929
- if (echoScope) await rememberPersistedIMessageEcho({
853
+ await rememberPersistedIMessageEcho({
930
854
  scope: echoScope,
931
855
  text: params.echoText,
932
856
  media: params.echoMedia,
@@ -975,7 +899,7 @@ async function sendMessageIMessage(to, text, opts) {
975
899
  remoteHost
976
900
  });
977
901
  const target = parseIMessageTarget(opts.chatId ? formatIMessageChatTarget(opts.chatId) : to);
978
- const service = opts.service ?? resolveTargetService(target) ?? account.config.service;
902
+ const service = opts.service ?? (target.kind === "handle" && (target.serviceExplicit || target.service !== "auto") ? target.service : void 0) ?? account.config.service;
979
903
  const sendTransport = account.config.sendTransport ?? "auto";
980
904
  const resolvedReplyToId = await resolveAuthorizedIMessageReplyReference({
981
905
  account,
@@ -1038,7 +962,7 @@ async function sendMessageIMessage(to, text, opts) {
1038
962
  });
1039
963
  message = formatted.text;
1040
964
  if (!message.trim() && !filePath) throw new Error("iMessage send requires text or media");
1041
- const echoText = resolveOutboundEchoText(message);
965
+ const echoText = message.trim() || void 0;
1042
966
  const echoMedia = filePath ? resolveOutboundEchoMedia(mediaContentType) : void 0;
1043
967
  let effectiveReplyToId = resolvedReplyToId;
1044
968
  const runCli = opts.runCliJson ?? ((args) => runIMessageCliJsonCommand({
@@ -1053,11 +977,7 @@ async function sendMessageIMessage(to, text, opts) {
1053
977
  };
1054
978
  const requestOwnedRpc = async (method, rpcParams) => {
1055
979
  opts.assertDirectAdapterHandoff?.();
1056
- const rpcClient = opts.createClient ? await opts.createClient({
1057
- cliPath,
1058
- dbPath,
1059
- remoteHost
1060
- }) : await createIMessageRpcClient({
980
+ const rpcClient = await (opts.createClient ?? createIMessageRpcClient)({
1061
981
  cliPath,
1062
982
  dbPath,
1063
983
  remoteHost
@@ -1146,15 +1066,11 @@ async function sendMessageIMessage(to, text, opts) {
1146
1066
  target
1147
1067
  });
1148
1068
  opts.assertDirectAdapterHandoff?.();
1149
- const client = opts.client ?? (opts.createClient ? await opts.createClient({
1069
+ const client = opts.client ?? await (opts.createClient ?? createIMessageRpcClient)({
1150
1070
  cliPath,
1151
1071
  dbPath,
1152
1072
  remoteHost
1153
- }) : await createIMessageRpcClient({
1154
- cliPath,
1155
- dbPath,
1156
- remoteHost
1157
- }));
1073
+ });
1158
1074
  const shouldClose = !opts.client;
1159
1075
  const requestSuccessfulSend = async (sendParams) => {
1160
1076
  const request = async (nativeParams) => await requestIMessageRpcSend(client, "send", nativeParams, timeoutMs, opts);
@@ -1183,7 +1099,7 @@ async function sendMessageIMessage(to, text, opts) {
1183
1099
  let pendingEchoKey;
1184
1100
  try {
1185
1101
  try {
1186
- if (echoScope) pendingEchoKey = await rememberPersistedIMessageEcho({
1102
+ pendingEchoKey = await rememberPersistedIMessageEcho({
1187
1103
  scope: echoScope,
1188
1104
  text: echoText,
1189
1105
  media: echoMedia,
@@ -1240,7 +1156,7 @@ async function sendMessageIMessage(to, text, opts) {
1240
1156
  sentAfterMs: sendStartedAtMs,
1241
1157
  resolveSentMessageGuidImpl: opts.resolveSentMessageGuidImpl
1242
1158
  });
1243
- if (echoScope) await rememberPersistedIMessageEcho({
1159
+ await rememberPersistedIMessageEcho({
1244
1160
  scope: echoScope,
1245
1161
  text: echoText,
1246
1162
  media: echoMedia,
@@ -1299,4 +1215,4 @@ async function sendMessageIMessage(to, text, opts) {
1299
1215
  }
1300
1216
  }
1301
1217
  //#endregion
1302
- export { buildApprovalPollOptions as a, maybeResolveIMessageApprovalPollVote as c, resolveIMessageStartupRowidWatermark as i, iMessageApprovalControlBindings as l, hasPersistedIMessageEcho as n, iMessageApprovalPollTargets as o, stripLeadingEchoTextCorruptionMarkers as r, mapSentPollOptionsToDecisions as s, sendMessageIMessage as t };
1218
+ export { iMessageApprovalPollTargets as a, iMessageApprovalControlBindings as c, buildApprovalPollOptions as i, hasPersistedIMessageEcho as n, mapSentPollOptionsToDecisions as o, normalizeIMessageEchoText as r, maybeResolveIMessageApprovalPollVote as s, sendMessageIMessage as t };