@openclaw/imessage 2026.10.5-beta.3 → 2026.10.10-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/.setup/{actions-Hxh12myO.mjs → actions-CZHoxlpi.mjs} +6 -10
  2. package/dist/.setup/{actions.runtime-DOIX6Fkf.mjs → actions.runtime-B-OHCffC.mjs} +7 -9
  3. package/dist/.setup/{api-BV8qHfLl.mjs → api-BifRABgX.mjs} +6 -6
  4. package/dist/.setup/{api-DC1yHVGj.mjs → api-dHebtZYA.mjs} +4 -4
  5. package/dist/.setup/{approval-handler.runtime-Dc3KzakG.mjs → approval-handler.runtime-jO19jL0F.mjs} +68 -76
  6. package/dist/.setup/{approval-reactions-JvzJCJ4a.mjs → approval-reactions-DFgEJMz2.mjs} +11 -15
  7. package/dist/.setup/{channel-9TWBUDkP.mjs → channel-D-E9DvQL.mjs} +17 -17
  8. package/dist/.setup/{channel.runtime-DrLDEY4R.mjs → channel.runtime-Cx_lRd-J.mjs} +63 -121
  9. package/dist/.setup/{client-HixFQQWY.mjs → client-BEZ_gLk-.mjs} +4 -27
  10. package/dist/.setup/{conversation-bindings-GDQ_Laxj.mjs → conversation-bindings-DR-dbHjs.mjs} +14 -2
  11. package/dist/.setup/{question-reactions-9jrqVZgE.mjs → question-reactions-BUSvdHlF.mjs} +2 -2
  12. package/dist/.setup/{reaction-context-CRXpeYAr.mjs → reaction-context-DbYCapmR.mjs} +1 -1
  13. package/dist/.setup/{remote-file-Dq5Asi_F.mjs → remote-file-vdoCnZze.mjs} +1 -1
  14. package/dist/.setup/{sanitize-outbound-DpdQERYJ.mjs → sanitize-outbound-BtEY324R.mjs} +14 -14
  15. package/dist/.setup/{send-BZZzMbdi.mjs → send-Jaf5eIJS.mjs} +10 -63
  16. package/dist/.setup/{setup-core-CaRGbZOL.mjs → setup-core-BHyLkAEJ.mjs} +1 -1
  17. package/dist/.setup/{setup-surface-BMWS7VuY.mjs → setup-surface-DPqiLGy0.mjs} +1 -1
  18. package/dist/api.js +6 -6
  19. package/dist/channel-config-api.js +1 -2
  20. package/dist/channel-plugin-api.js +1 -2
  21. package/dist/contract-api.js +1 -2
  22. package/dist/media-contract-api.js +1 -2
  23. package/dist/message-tool-api.js +1 -2
  24. package/dist/runtime-api.js +1 -1
  25. package/dist/session-binding-contract-api.js +1 -2
  26. package/dist/setup-entry.js +1 -1
  27. package/node_modules/typebox/build/value/clean/from_object.mjs +1 -1
  28. package/node_modules/typebox/package.json +1 -1
  29. package/package.json +5 -5
@@ -1,19 +1,19 @@
1
1
  import { c as parseIMessageTarget, o as normalizeIMessageHandle, r as isAllowedIMessageReplyContextSender, s as parseIMessageAllowTarget, t as formatIMessageChatTarget } from "./targets-B4oU7n-v.mjs";
2
2
  import { d as resolveIMessageChatDbLookupPath, f as resolveIMessageHomeDir, l as resolveIMessageRemoteHost, o as resolveIMessageAccount, r as listEnabledIMessageAccounts, s as resolveIMessageDuplicateSourceOwner } from "./accounts-DacCvDBt.mjs";
3
3
  import { r as imessageRpcSupportsMethod } from "./message-tool-api-B5NfPnFL.mjs";
4
- import { C as resolveIMessageDirectChatService, c as rememberIMessageReplyCache, d as IMESSAGE_CATCHUP_CURSOR_NAMESPACE, g as resolveIMessageEchoMediaKey, h as resolveIMessageCatchupCursorKey, m as capFailureRetriesMap, n as createIMessageRpcClient, s as isKnownFromMeIMessageTarget } from "./client-HixFQQWY.mjs";
5
- import { a as normalizeIMessageMessageId, i as normalizeIMessageGuid, n as probeIMessage, r as probeIMessagePrivateApi } from "./actions-Hxh12myO.mjs";
4
+ import { S as resolveIMessageDirectChatService, h as resolveIMessageEchoMediaKey, m as resolveIMessageCatchupCursorKey, n as createIMessageRpcClient, o as isKnownFromMeIMessageTarget, p as capFailureRetriesMap, s as rememberIMessageReplyCache, u as IMESSAGE_CATCHUP_CURSOR_NAMESPACE } from "./client-BEZ_gLk-.mjs";
5
+ import { a as normalizeIMessageMessageId, i as normalizeIMessageGuid, n as probeIMessage, r as probeIMessagePrivateApi } from "./actions-CZHoxlpi.mjs";
6
6
  import { t as getIMessageRuntime } from "./runtime-Cza4CY5T.mjs";
7
- import { a as resolveIMessageGroupSystemPrompt, s as resolveIMessageInboundConversationId } from "./channel-9TWBUDkP.mjs";
8
- import { r as sanitizeOutboundText } from "./sanitize-outbound-DpdQERYJ.mjs";
7
+ import { a as resolveIMessageGroupSystemPrompt, s as resolveIMessageInboundConversationId } from "./channel-D-E9DvQL.mjs";
8
+ import { r as sanitizeOutboundText } from "./sanitize-outbound-BtEY324R.mjs";
9
9
  import { n as resolveIMessageAttachmentRoots, r as resolveIMessageRemoteAttachmentRoots } from "./media-contract-BwWs5PM7.mjs";
10
10
  import { t as IMESSAGE_LEGACY_OUTBOUND_SEND_DEP_KEYS } from "./outbound-send-deps-B-QEsLSM.mjs";
11
- import { i as resolveIMessageStartupRowidWatermark } from "./remote-file-Dq5Asi_F.mjs";
12
- import { f as resolveIMessageApprovalControlActor, i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction, s as buildIMessageApprovalConversationKeyForInbound } from "./approval-reactions-JvzJCJ4a.mjs";
13
- import { c as iMessageApprovalControlBindings, n as hasPersistedIMessageEcho, r as normalizeIMessageEchoText, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-BZZzMbdi.mjs";
14
- import { t as resolveIMessageReactionContext } from "./reaction-context-CRXpeYAr.mjs";
15
- import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-9jrqVZgE.mjs";
16
- import { imessageSetupWizard } from "./setup-surface-BMWS7VuY.mjs";
11
+ import { i as resolveIMessageStartupRowidWatermark } from "./remote-file-vdoCnZze.mjs";
12
+ import { i as registerIMessageApprovalReactionTarget, n as handleIMessageApprovalReaction, o as listPendingIMessageApprovalReactionPollTargets, r as maybeResolveIMessageApprovalReaction } from "./approval-reactions-DFgEJMz2.mjs";
13
+ import { n as hasPersistedIMessageEcho, r as normalizeIMessageEchoText, s as maybeResolveIMessageApprovalPollVote, t as sendMessageIMessage } from "./send-Jaf5eIJS.mjs";
14
+ import { t as resolveIMessageReactionContext } from "./reaction-context-DbYCapmR.mjs";
15
+ import { n as maybeResolveIMessageQuestionReaction, t as hasIMessageQuestionReactionTarget } from "./question-reactions-BUSvdHlF.mjs";
16
+ import { imessageSetupWizard } from "./setup-surface-DPqiLGy0.mjs";
17
17
  import { asSafeIntegerInRange, hasNonEmptyString, isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
18
18
  import { normalizeChannelDmPolicy } from "openclaw/plugin-sdk/channel-config-helpers";
19
19
  import { bindIngressLifecycleToReplyOptions, createAccountStatusSink, createChannelDeliveryAccumulator, createChannelIngressError, createChannelIngressMonitor, createChannelMessageReplyPipeline, resolveChannelStreamingBlockEnabled, resolveOutboundSendDep } from "openclaw/plugin-sdk/channel-outbound";
@@ -27,7 +27,7 @@ import { isInboundPathAllowed, kindFromMime } from "openclaw/plugin-sdk/media-ru
27
27
  import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, sleepWithAbort, waitForAbortSignal, warn } from "openclaw/plugin-sdk/runtime-env";
28
28
  import { asDateTimestampMs, asPositiveFiniteNumber, parseDateStringTimestampMs, resolveIntegerOption, resolvePromptHistoryLimit, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
29
29
  import { createHash } from "node:crypto";
30
- import { createRuntimeConfigReader, getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
30
+ import { captureRuntimeConfigAsyncReader, createRuntimeConfigReader } from "openclaw/plugin-sdk/runtime-config-snapshot";
31
31
  import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
32
32
  import { collectErrorGraphCandidates, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
33
33
  import { resolveChannelGroupPolicy, resolveChannelGroups, resolveChannelGroupsConfigPath, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
@@ -47,7 +47,7 @@ import { ensureConfiguredBindingRouteReady, readChannelAllowFromStore, upsertCha
47
47
  import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
48
48
  import { redactIdentifier } from "openclaw/plugin-sdk/logging-core";
49
49
  import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
50
- import { getSessionEntry, readSessionUpdatedAtAsync, resolveSendPolicy, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
50
+ import { getSessionEntryAsync, readSessionUpdatedAtAsync, resolveSendPolicy, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
51
51
  import { waitForTransportReady } from "openclaw/plugin-sdk/transport-ready-runtime";
52
52
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
53
53
  import { chunkMarkdownTextWithMode, resolveChunkMode } from "openclaw/plugin-sdk/reply-chunking";
@@ -266,7 +266,7 @@ const DEFAULT_MAX_FAILURE_RETRIES = 10;
266
266
  const MAX_MAX_FAILURE_RETRIES = 1e3;
267
267
  const cursorWriteQueue = new KeyedAsyncQueue();
268
268
  function openCatchupCursorStore() {
269
- return getIMessageRuntime().state.openKeyedStore({
269
+ return getIMessageRuntime().state.openKeyedStoreV2({
270
270
  namespace: IMESSAGE_CATCHUP_CURSOR_NAMESPACE,
271
271
  maxEntries: 256
272
272
  });
@@ -308,40 +308,27 @@ function buildIMessageCatchupCursor(next, updatedAt) {
308
308
  function decideCatchupCursorSave(existingValue, cursor, allowCursorRewindForRetries) {
309
309
  const existing = normalizeIMessageCatchupCursor(existingValue);
310
310
  if (existing && cursor.lastSeenRowid < existing.lastSeenRowid) {
311
- if (!allowCursorRewindForRetries) return {
312
- operation: "update",
313
- action: "keep"
314
- };
315
- return {
316
- operation: "update",
317
- action: "set",
318
- value: buildIMessageCatchupCursor({
319
- lastSeenMs: cursor.lastSeenMs,
320
- lastSeenRowid: cursor.lastSeenRowid,
321
- failureRetries: {
322
- ...existing.failureRetries,
323
- ...cursor.failureRetries
324
- }
325
- }, cursor.updatedAt)
326
- };
311
+ if (!allowCursorRewindForRetries) return;
312
+ return buildIMessageCatchupCursor({
313
+ lastSeenMs: cursor.lastSeenMs,
314
+ lastSeenRowid: cursor.lastSeenRowid,
315
+ failureRetries: {
316
+ ...existing.failureRetries,
317
+ ...cursor.failureRetries
318
+ }
319
+ }, cursor.updatedAt);
327
320
  }
328
- return {
329
- operation: "update",
330
- action: "set",
331
- value: cursor
332
- };
321
+ return cursor;
333
322
  }
334
323
  async function updateIMessageCatchupCursor(accountId, decide) {
335
- const store = openCatchupCursorStore();
336
- if (!store.observe || !store.compareAndApply) throw new Error("iMessage catchup cursor persistence requires plugin-state comparison support.");
337
324
  const key = resolveIMessageCatchupCursorKey(accountId);
338
- let observation = await store.observe(key);
339
- for (;;) {
340
- const intent = decide(observation.value);
341
- const result = await store.compareAndApply(key, observation.comparison, intent);
342
- if (result.status !== "conflict") return result.status === "applied";
343
- observation = result.current;
344
- }
325
+ return await cursorWriteQueue.enqueue(key, async () => {
326
+ const store = openCatchupCursorStore();
327
+ const next = decide(await store.lookup(key));
328
+ if (!next) return false;
329
+ await store.register(key, next);
330
+ return true;
331
+ });
345
332
  }
346
333
  function clampInt(value, min, max, fallback) {
347
334
  return resolveIntegerOption(value, fallback, {
@@ -360,31 +347,18 @@ function resolveCatchupConfig(raw) {
360
347
  }
361
348
  function decideLiveCatchupCursorAdvance(existingValue, next, maxFailureRetries) {
362
349
  const cursor = normalizeIMessageCatchupCursor(existingValue);
363
- if (cursor && next.lastSeenRowid <= cursor.lastSeenRowid) return {
364
- operation: "update",
365
- action: "keep"
366
- };
367
- if (Object.values(cursor?.failureRetries ?? {}).some((count) => count < maxFailureRetries)) return {
368
- operation: "update",
369
- action: "keep"
370
- };
371
- return {
372
- operation: "update",
373
- action: "set",
374
- value: buildIMessageCatchupCursor({
375
- lastSeenMs: Math.max(cursor?.lastSeenMs ?? next.lastSeenMs, next.lastSeenMs),
376
- lastSeenRowid: next.lastSeenRowid,
377
- failureRetries: cursor?.failureRetries
378
- }, next.updatedAt)
379
- };
350
+ if (cursor && next.lastSeenRowid <= cursor.lastSeenRowid) return;
351
+ if (Object.values(cursor?.failureRetries ?? {}).some((count) => count < maxFailureRetries)) return;
352
+ return buildIMessageCatchupCursor({
353
+ lastSeenMs: Math.max(cursor?.lastSeenMs ?? next.lastSeenMs, next.lastSeenMs),
354
+ lastSeenRowid: next.lastSeenRowid,
355
+ failureRetries: cursor?.failureRetries
356
+ }, next.updatedAt);
380
357
  }
381
358
  async function advanceIMessageCatchupCursor(accountId, next, config) {
382
359
  if (!Number.isFinite(next.lastSeenMs) || !Number.isFinite(next.lastSeenRowid)) return false;
383
- return await cursorWriteQueue.enqueue(resolveIMessageCatchupCursorKey(accountId), async () => {
384
- const cursor = buildIMessageCatchupCursor(next, Date.now());
385
- const maxFailureRetries = config.maxFailureRetries;
386
- return updateIMessageCatchupCursor(accountId, (existing) => decideLiveCatchupCursorAdvance(existing, cursor, maxFailureRetries));
387
- });
360
+ const cursor = buildIMessageCatchupCursor(next, Date.now());
361
+ return await updateIMessageCatchupCursor(accountId, (existing) => decideLiveCatchupCursorAdvance(existing, cursor, config.maxFailureRetries));
388
362
  }
389
363
  async function performIMessageCatchup(params) {
390
364
  const now = params.now ?? Date.now();
@@ -1665,8 +1639,7 @@ async function resolveIMessageInboundDecision(params) {
1665
1639
  ...selfChatLookup,
1666
1640
  text: bodyText
1667
1641
  })) {
1668
- const preview = sanitizeTerminalText(truncateUtf16Safe(bodyText, 50));
1669
- params.logVerbose?.(`imessage: dropping self-chat reflected duplicate: "${preview}"`);
1642
+ params.logVerbose?.("imessage: dropping self-chat reflected duplicate");
1670
1643
  return {
1671
1644
  kind: "drop",
1672
1645
  reason: "self-chat echo"
@@ -1681,7 +1654,7 @@ async function resolveIMessageInboundDecision(params) {
1681
1654
  messageIds: inboundMessageIds,
1682
1655
  includePendingText: isSelfChat
1683
1656
  })) {
1684
- params.logVerbose?.(`imessage: skipping echo message${inboundMessageId ? ` id=${inboundMessageId}` : ""}: "${truncateUtf16Safe(bodyText, 50)}"`);
1657
+ params.logVerbose?.(`imessage: skipping echo message${inboundMessageId ? ` id=${inboundMessageId}` : ""}`);
1685
1658
  return {
1686
1659
  kind: "drop",
1687
1660
  reason: "echo"
@@ -2165,7 +2138,7 @@ function isHeicAttachment(attachmentPath, mimeType) {
2165
2138
  return ext === ".heic" || ext === ".heif";
2166
2139
  }
2167
2140
  async function readAttachmentBuffer(params) {
2168
- await using opened = await (params.deps.openLocalFileSafely ?? openLocalFileSafely)({ filePath: params.attachmentPath });
2141
+ await using opened = await openLocalFileSafely({ filePath: params.attachmentPath });
2169
2142
  if (opened.stat.size > params.maxBytes) throw new Error(`attachment exceeds ${Math.round(params.maxBytes / 1048576)}MB limit`);
2170
2143
  if (params.allowedRoots) {
2171
2144
  const canonicalRoots = [];
@@ -2185,14 +2158,13 @@ async function readAttachmentBuffer(params) {
2185
2158
  throw error;
2186
2159
  });
2187
2160
  if (isHeicAttachment(params.attachmentPath, params.mimeType)) try {
2188
- const convert = params.deps.convertHeicToJpeg;
2189
2161
  return {
2190
2162
  buffer: (await withTempWorkspace({
2191
2163
  rootDir: resolvePreferredOpenClawTmpDir(),
2192
2164
  prefix: "openclaw-imessage-heic-"
2193
2165
  }, async (workspace) => {
2194
2166
  const pinnedPath = await workspace.write("attachment.heic", buffer);
2195
- return convert ? { buffer: await convert(pinnedPath, params.maxBytes) } : await loadWebMedia(pinnedPath, {
2167
+ return await loadWebMedia(pinnedPath, {
2196
2168
  maxBytes: params.maxBytes,
2197
2169
  localRoots: [workspace.dir]
2198
2170
  });
@@ -2211,7 +2183,6 @@ async function readAttachmentBuffer(params) {
2211
2183
  }
2212
2184
  async function stageIMessageAttachments(attachments, params) {
2213
2185
  const deps = params.deps ?? {};
2214
- const save = deps.saveMediaBuffer ?? saveMediaBuffer;
2215
2186
  const staged = [];
2216
2187
  let unavailableCount = 0;
2217
2188
  for (const attachment of attachments) {
@@ -2229,7 +2200,7 @@ async function stageIMessageAttachments(attachments, params) {
2229
2200
  allowedRoots: params.allowedRoots,
2230
2201
  deps
2231
2202
  });
2232
- const saved = await save(media.buffer, media.contentType, "inbound", params.maxBytes, media.originalFilename);
2203
+ const saved = await saveMediaBuffer(media.buffer, media.contentType, "inbound", params.maxBytes, media.originalFilename);
2233
2204
  const contentType = saved.contentType ?? media.contentType;
2234
2205
  staged.push({
2235
2206
  path: saved.path,
@@ -2324,7 +2295,7 @@ function enqueueIMessageReactionSystemEvent(params) {
2324
2295
  const { decision, runtime } = params;
2325
2296
  const queued = enqueueRoutedSystemEvent(decision.text, decision.route, { contextKey: decision.contextKey });
2326
2297
  runtime.log?.(`imessage: reaction system event ${queued ? "queued" : "deduped"} session=${decision.route.sessionKey} target=${decision.reaction.targetGuid ?? "unknown"} action=${decision.reaction.action} emoji=${decision.reaction.emoji}`);
2327
- params.logVerbose?.(`imessage: reaction event enqueued: ${decision.text}`);
2298
+ params.logVerbose?.("imessage: reaction event enqueued");
2328
2299
  return queued;
2329
2300
  }
2330
2301
  //#endregion
@@ -2335,8 +2306,9 @@ const RECOVERY_CURSOR_STORE_OPTIONS = {
2335
2306
  };
2336
2307
  const LEGACY_CATCHUP_CURSOR_NAMESPACE = "imessage.catchup-cursors";
2337
2308
  const LEGACY_CATCHUP_CURSOR_MAX_ENTRIES = 256;
2309
+ let cursorWrites = Promise.resolve();
2338
2310
  function openRecoveryCursorStore() {
2339
- return getIMessageRuntime().state.openKeyedStore(RECOVERY_CURSOR_STORE_OPTIONS);
2311
+ return getIMessageRuntime().state.openKeyedStoreV2(RECOVERY_CURSOR_STORE_OPTIONS);
2340
2312
  }
2341
2313
  function normalizeLocalDbPath(dbPath) {
2342
2314
  let resolved = dbPath.trim();
@@ -2374,34 +2346,17 @@ function decideRecoveryCursorUpdate(current, update) {
2374
2346
  } else if (current && current.lastRowid >= update.rowid) return;
2375
2347
  return { lastRowid: update.rowid };
2376
2348
  }
2377
- async function applyRecoveryCursorUpdate(key, update) {
2378
- const state = getIMessageRuntime().state;
2379
- const store = state.openKeyedStore(RECOVERY_CURSOR_STORE_OPTIONS);
2380
- if (!store.observe || !store.compareAndApply) {
2381
- const legacy = state.openSyncKeyedStore(RECOVERY_CURSOR_STORE_OPTIONS);
2382
- if (!legacy.update) throw new Error("iMessage recovery cursor persistence requires atomic update support.");
2383
- let result;
2384
- legacy.update(key, (current) => {
2385
- const next = decideRecoveryCursorUpdate(current, update);
2386
- result = next ?? current;
2387
- return next;
2388
- });
2389
- return result;
2390
- }
2391
- const observe = store.observe.bind(store);
2392
- const compareAndApply = store.compareAndApply.bind(store);
2393
- let observation = await observe(key);
2394
- for (;;) {
2395
- const next = decideRecoveryCursorUpdate(observation.value, update);
2396
- if (!next) return observation.value;
2397
- const result = await compareAndApply(key, observation.comparison, {
2398
- operation: "update",
2399
- action: "set",
2400
- value: next
2401
- });
2402
- if (result.status !== "conflict") return next;
2403
- observation = result.current;
2404
- }
2349
+ function applyRecoveryCursorUpdate(key, update) {
2350
+ const write = cursorWrites.then(async () => {
2351
+ const store = openRecoveryCursorStore();
2352
+ const current = await store.lookup(key);
2353
+ const next = decideRecoveryCursorUpdate(current, update);
2354
+ if (!next) return current;
2355
+ await store.register(key, next);
2356
+ return next;
2357
+ });
2358
+ cursorWrites = write.catch(() => {});
2359
+ return write;
2405
2360
  }
2406
2361
  async function readRecoveryCursor(accountId, dbIdentity) {
2407
2362
  try {
@@ -2422,7 +2377,7 @@ async function readRecoveryCursor(accountId, dbIdentity) {
2422
2377
  }
2423
2378
  async function migrateLegacyCatchupCursor(accountId, dbIdentity) {
2424
2379
  try {
2425
- const legacy = getIMessageRuntime().state.openKeyedStore({
2380
+ const legacy = getIMessageRuntime().state.openKeyedStoreV2({
2426
2381
  namespace: LEGACY_CATCHUP_CURSOR_NAMESPACE,
2427
2382
  maxEntries: LEGACY_CATCHUP_CURSOR_MAX_ENTRIES
2428
2383
  });
@@ -2601,7 +2556,7 @@ const IMESSAGE_THROTTLED_DIAGNOSTIC_DROP_REASONS = /* @__PURE__ */ new Set(["fro
2601
2556
  async function monitorIMessageProvider(opts) {
2602
2557
  const { scheduler } = opts;
2603
2558
  const runtime = opts.runtime ?? createNonExitingRuntime();
2604
- const cfg = opts.config ?? getRuntimeConfig();
2559
+ const cfg = opts.config ?? await captureRuntimeConfigAsyncReader()();
2605
2560
  const readConfig = createRuntimeConfigReader(cfg);
2606
2561
  const accountInfo = resolveIMessageAccount({
2607
2562
  cfg,
@@ -3004,7 +2959,7 @@ async function monitorIMessageProvider(opts) {
3004
2959
  const configuredTypingMode = resolveAgentConfig(cfg, decision.route.agentId)?.typingMode ?? cfg.agents?.defaults?.typingMode;
3005
2960
  const sendPolicy = resolveSendPolicy({
3006
2961
  cfg,
3007
- entry: getSessionEntry({
2962
+ entry: await getSessionEntryAsync({
3008
2963
  storePath,
3009
2964
  sessionKey: decision.route.sessionKey
3010
2965
  }),
@@ -3266,14 +3221,10 @@ async function monitorIMessageProvider(opts) {
3266
3221
  logVerboseMessage: logVerbose
3267
3222
  });
3268
3223
  };
3269
- const resolveApprovalControlConversation = (message) => {
3270
- const actorHandle = resolveIMessageApprovalControlActor(message);
3271
- return actorHandle ? buildIMessageApprovalConversationKeyForInbound(message, actorHandle) : null;
3272
- };
3273
3224
  const ingress = createIMessageDurableIngress({
3274
3225
  accountId: accountInfo.accountId,
3275
3226
  runtime,
3276
- dispatchPriority: async (message, lifecycle, receivedAt, provenance) => {
3227
+ dispatchPriority: async (message, _lifecycle, receivedAt, provenance) => {
3277
3228
  const bodyText = (message.text ?? "").trim();
3278
3229
  const isApprovalCommand = /^\/approve(?:@[^\s]+)?(?:\s|$)/i.test(bodyText);
3279
3230
  if (!(isApprovalCommand || message.poll?.kind === "vote" || Boolean(resolveIMessageReactionContext(message, bodyText)))) return;
@@ -3284,16 +3235,7 @@ async function monitorIMessageProvider(opts) {
3284
3235
  await handleMessageNow(repairedMessage);
3285
3236
  return { kind: "completed" };
3286
3237
  }
3287
- const conversation = resolveApprovalControlConversation(repairedMessage);
3288
- while (true) {
3289
- if (await maybeHandleApprovalControl(repairedMessage)) return { kind: "completed" };
3290
- if (!conversation) return;
3291
- if (!await iMessageApprovalControlBindings.wait({
3292
- accountId: accountInfo.accountId,
3293
- conversation,
3294
- abortSignal: lifecycle.abortSignal
3295
- })) return await maybeHandleApprovalControl(repairedMessage) ? { kind: "completed" } : void 0;
3296
- }
3238
+ return await maybeHandleApprovalControl(repairedMessage) ? { kind: "completed" } : void 0;
3297
3239
  },
3298
3240
  dispatch: async (message, ingressLifecycle, receivedAt, provenance) => {
3299
3241
  if (suppressStaleIngress(message, receivedAt, provenance)) return { kind: "completed" };
@@ -158,13 +158,13 @@ const imessageReplyCacheByMessageId = /* @__PURE__ */ new Map();
158
158
  const imessageShortIdToUuid = /* @__PURE__ */ new Map();
159
159
  let imessageShortIdCounter = 0;
160
160
  function openReplyCacheStore() {
161
- return getIMessageRuntime().state.openKeyedStore({
161
+ return getIMessageRuntime().state.openKeyedStoreV2({
162
162
  namespace: IMESSAGE_REPLY_CACHE_NAMESPACE,
163
163
  maxEntries: IMESSAGE_REPLY_CACHE_MAX_ENTRIES
164
164
  });
165
165
  }
166
166
  function openReplyCacheCounterStore() {
167
- return getIMessageRuntime().state.openKeyedStore({
167
+ return getIMessageRuntime().state.openKeyedStoreV2({
168
168
  namespace: IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE,
169
169
  maxEntries: 1
170
170
  });
@@ -193,8 +193,7 @@ async function hydrateFromStoreOnce() {
193
193
  hydration ??= (async () => {
194
194
  try {
195
195
  hydrateCounter(await openReplyCacheCounterStore().lookup(IMESSAGE_REPLY_CACHE_COUNTER_KEY));
196
- const entries = await openReplyCacheStore().entries();
197
- if (!hydrated) hydrateRows(entries.map(({ value }) => value));
196
+ hydrateRows((await openReplyCacheStore().entries()).map(({ value }) => value));
198
197
  } catch (err) {
199
198
  reportPersistenceFailure("read", err);
200
199
  } finally {
@@ -203,23 +202,6 @@ async function hydrateFromStoreOnce() {
203
202
  })();
204
203
  await hydration;
205
204
  }
206
- function hydrateFromStoreOnceSync() {
207
- if (hydrated) return;
208
- hydrated = true;
209
- try {
210
- const state = getIMessageRuntime().state;
211
- hydrateCounter(state.openSyncKeyedStore({
212
- namespace: IMESSAGE_REPLY_CACHE_COUNTER_NAMESPACE,
213
- maxEntries: 1
214
- }).lookup(IMESSAGE_REPLY_CACHE_COUNTER_KEY));
215
- hydrateRows(state.openSyncKeyedStore({
216
- namespace: IMESSAGE_REPLY_CACHE_NAMESPACE,
217
- maxEntries: IMESSAGE_REPLY_CACHE_MAX_ENTRIES
218
- }).entries().map(({ value }) => value));
219
- } catch (err) {
220
- reportPersistenceFailure("read", err);
221
- }
222
- }
223
205
  async function persistReplyCacheEntry(entry) {
224
206
  const ttlMs = remainingTtlMs(entry.timestamp);
225
207
  if (!ttlMs) return;
@@ -379,11 +361,6 @@ function resolveCachedResourceBinding(messageId, ctx) {
379
361
  if (entry.accountId !== ctx.accountId) return "mismatch";
380
362
  return resolveIMessageChatMatch(entry, ctx);
381
363
  }
382
- /** @deprecated Used only by hosts without asynchronous conversation matching. */
383
- function isIMessageCurrentMessageInChat(params) {
384
- hydrateFromStoreOnceSync();
385
- return isCurrentMessageInChat(params);
386
- }
387
364
  async function isIMessageCurrentMessageInChatAsync(params) {
388
365
  await hydrateFromStoreOnce();
389
366
  return isCurrentMessageInChat(params);
@@ -762,4 +739,4 @@ async function createIMessageRpcClient(opts = {}) {
762
739
  return client;
763
740
  }
764
741
  //#endregion
765
- export { resolveIMessageDirectChatService as C, normalizeDirectChatIdentifier as S, resolveIMessageSentEchoEntryKey as _, isIMessageCurrentMessageInChat as a, chatContextFromIMessageTarget as b, rememberIMessageReplyCache as c, IMESSAGE_CATCHUP_CURSOR_NAMESPACE as d, IMESSAGE_SENT_ECHOES_NAMESPACE as f, resolveIMessageEchoMediaKey as g, resolveIMessageCatchupCursorKey as h, findLatestIMessageEntryForChat as i, resolveIMessageCachedResourceBinding as l, capFailureRetriesMap as m, createIMessageRpcClient as n, isIMessageCurrentMessageInChatAsync as o, IMESSAGE_SENT_ECHOES_TTL_MS as p, runIMessageCliJsonCommand as r, isKnownFromMeIMessageTarget as s, IMessageRpcRequestError as t, resolveIMessageMessageId as u, DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS as v, isIMessageEmailChatIdentifier as x, DEFAULT_IMESSAGE_SEND_TIMEOUT_MS as y };
742
+ export { resolveIMessageDirectChatService as S, DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS as _, isIMessageCurrentMessageInChatAsync as a, isIMessageEmailChatIdentifier as b, resolveIMessageCachedResourceBinding as c, IMESSAGE_SENT_ECHOES_NAMESPACE as d, IMESSAGE_SENT_ECHOES_TTL_MS as f, resolveIMessageSentEchoEntryKey as g, resolveIMessageEchoMediaKey as h, findLatestIMessageEntryForChat as i, resolveIMessageMessageId as l, resolveIMessageCatchupCursorKey as m, createIMessageRpcClient as n, isKnownFromMeIMessageTarget as o, capFailureRetriesMap as p, runIMessageCliJsonCommand as r, rememberIMessageReplyCache as s, IMessageRpcRequestError as t, IMESSAGE_CATCHUP_CURSOR_NAMESPACE as u, DEFAULT_IMESSAGE_SEND_TIMEOUT_MS as v, normalizeDirectChatIdentifier as x, chatContextFromIMessageTarget as y };
@@ -1,4 +1,4 @@
1
- import { createAccountScopedConversationBindingManager, resetAccountScopedConversationBindingsForTests } from "openclaw/plugin-sdk/thread-bindings-runtime";
1
+ import { createAccountScopedConversationBindingManager, createAccountScopedConversationBindingManagerV2, resetAccountScopedConversationBindingsForTests } from "openclaw/plugin-sdk/thread-bindings-runtime";
2
2
  //#region extensions/imessage/src/conversation-bindings.ts
3
3
  const IMESSAGE_CONVERSATION_BINDINGS_STATE_KEY = Symbol.for("openclaw.imessageConversationBindingsState");
4
4
  function toSessionBindingTargetKind(raw) {
@@ -7,6 +7,7 @@ function toSessionBindingTargetKind(raw) {
7
7
  function toIMessageTargetKind(raw) {
8
8
  return raw === "subagent" ? "subagent" : "acp";
9
9
  }
10
+ /** @deprecated Use createIMessageConversationBindingManagerV2; final compatibility day: 2026-12-31. */
10
11
  function createIMessageConversationBindingManager(params) {
11
12
  return createAccountScopedConversationBindingManager({
12
13
  channel: "imessage",
@@ -17,8 +18,19 @@ function createIMessageConversationBindingManager(params) {
17
18
  toSessionBindingTargetKind
18
19
  });
19
20
  }
21
+ /** Worker-owned manager used by bundled channel startup. */
22
+ function createIMessageConversationBindingManagerV2(params) {
23
+ return createAccountScopedConversationBindingManagerV2({
24
+ channel: "imessage",
25
+ cfg: params.cfg,
26
+ accountId: params.accountId,
27
+ stateKey: IMESSAGE_CONVERSATION_BINDINGS_STATE_KEY,
28
+ toStoredTargetKind: toIMessageTargetKind,
29
+ toSessionBindingTargetKind
30
+ });
31
+ }
20
32
  const testing = { resetIMessageConversationBindingsForTests() {
21
33
  resetAccountScopedConversationBindingsForTests({ stateKey: IMESSAGE_CONVERSATION_BINDINGS_STATE_KEY });
22
34
  } };
23
35
  //#endregion
24
- export { testing as n, createIMessageConversationBindingManager as t };
36
+ export { createIMessageConversationBindingManagerV2 as n, testing as r, createIMessageConversationBindingManager as t };
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-BBjsoOtd.mjs";
2
- import { i as normalizeIMessageGuid } from "./actions-Hxh12myO.mjs";
3
- import { t as resolveIMessageReactionContext } from "./reaction-context-CRXpeYAr.mjs";
2
+ import { i as normalizeIMessageGuid } from "./actions-CZHoxlpi.mjs";
3
+ import { t as resolveIMessageReactionContext } from "./reaction-context-DbYCapmR.mjs";
4
4
  import { createQuestionReactionTargetStore, questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
5
5
  //#region extensions/imessage/src/question-reactions.ts
6
6
  var question_reactions_exports = /* @__PURE__ */ __exportAll({
@@ -1,4 +1,4 @@
1
- import { i as normalizeIMessageGuid } from "./actions-Hxh12myO.mjs";
1
+ import { i as normalizeIMessageGuid } from "./actions-CZHoxlpi.mjs";
2
2
  //#region extensions/imessage/src/monitor/reaction-context.ts
3
3
  const TAPBACK_TEXT_PATTERNS = [
4
4
  {
@@ -1,5 +1,5 @@
1
1
  import { p as resolveLocalIMessageChatDbPath } from "./accounts-DacCvDBt.mjs";
2
- import { b as chatContextFromIMessageTarget, l as resolveIMessageCachedResourceBinding, u as resolveIMessageMessageId, x as isIMessageEmailChatIdentifier } from "./client-HixFQQWY.mjs";
2
+ import { b as isIMessageEmailChatIdentifier, c as resolveIMessageCachedResourceBinding, l as resolveIMessageMessageId, y as chatContextFromIMessageTarget } from "./client-BEZ_gLk-.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";
@@ -157,7 +157,7 @@ function assertSafeIMessageOutboundMarkup(text) {
157
157
  cursor += suffix.length;
158
158
  if (PRIVATE_PROVIDER_TAG_NAME_SET.has(candidate) && /[\s/>]/.test(text.charAt(cursor))) throw new Error("iMessage outbound ambiguous nested HTML is not allowed");
159
159
  if (!PRIVATE_PROVIDER_TAG_NAMES.some((name) => name.startsWith(candidate))) break;
160
- if (depth + 1 >= MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES && (OPAQUE_PRIVATE_RUNTIME_CONTEXT_BLOCK_RE.test(text.slice(cursor)) || REMOVABLE_PRIVATE_MARKUP_TAG_RE.test(text.slice(cursor)))) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
160
+ if (depth + 1 >= MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES && (OPAQUE_PRIVATE_RUNTIME_CONTEXT_BLOCK_RE.test(text.slice(cursor)) || REMOVABLE_PRIVATE_MARKUP_TAG_RE.test(text.slice(cursor)))) throw new Error("iMessage outbound runtime markup exceeded its limit");
161
161
  }
162
162
  }
163
163
  }
@@ -166,14 +166,14 @@ function findIMessageMarkupEnd(text, start, kind, contentOffset = 2) {
166
166
  let quote;
167
167
  let bracketDepth = 0;
168
168
  for (let index = start + contentOffset; index <= text.length - terminator.length; index += 1) {
169
- if (kind === "private" && index - start > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
169
+ if (kind === "private" && index - start > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime markup exceeded its limit");
170
170
  const character = text[index];
171
171
  if (quote) {
172
172
  if (character === quote) quote = void 0;
173
173
  } else if (character === "'" || character === "\"") quote = character;
174
174
  else if (kind === "declaration" && character === "[") bracketDepth += 1;
175
175
  else if (kind === "declaration" && character === "]" && bracketDepth > 0) bracketDepth -= 1;
176
- else if (kind === "private" && character === "<") throw new Error("iMessage outbound runtime scaffolding is malformed");
176
+ else if (kind === "private" && character === "<") throw new Error("iMessage outbound runtime markup is malformed");
177
177
  else if (bracketDepth === 0 && text.startsWith(terminator, index)) return index + terminator.length;
178
178
  }
179
179
  return -1;
@@ -211,7 +211,7 @@ function stripIMessageBalancedPrivateRuntimeBlocks(text, verifyProtectedRoles) {
211
211
  end: closing?.end ?? text.length,
212
212
  terminated: Boolean(closing)
213
213
  });
214
- if (rawTextHtml.length > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
214
+ if (rawTextHtml.length > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime markup exceeded its limit");
215
215
  }
216
216
  const opaqueHtml = [];
217
217
  for (let cursor = 0; cursor < text.length;) {
@@ -239,7 +239,7 @@ function stripIMessageBalancedPrivateRuntimeBlocks(text, verifyProtectedRoles) {
239
239
  cursor = start + 1;
240
240
  continue;
241
241
  }
242
- if (opaqueHtml.length >= MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
242
+ if (opaqueHtml.length >= MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime markup exceeded its limit");
243
243
  const terminated = end >= 0;
244
244
  const regionEnd = terminated ? end + delimiterLength : text.length;
245
245
  opaqueHtml.push({
@@ -273,15 +273,15 @@ function stripIMessageBalancedPrivateRuntimeBlocks(text, verifyProtectedRoles) {
273
273
  enclosingBlock.sawNested = true;
274
274
  continue;
275
275
  }
276
- if (enclosingBlock?.codeRegion && enclosingBlock.codeRegion !== codeRegion) throw new Error("iMessage outbound runtime scaffolding is malformed");
277
- if (++scannedTags > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
276
+ if (enclosingBlock?.codeRegion && enclosingBlock.codeRegion !== codeRegion) throw new Error("iMessage outbound runtime markup is malformed");
277
+ if (++scannedTags > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime markup exceeded its limit");
278
278
  const end = findIMessageMarkupEnd(text, start, "private", token[0].length);
279
- if (end < 0) throw new Error("iMessage outbound runtime scaffolding is malformed");
279
+ if (end < 0) throw new Error("iMessage outbound runtime markup is malformed");
280
280
  if (enclosingBlock?.codeRegion && enclosingBlock.codeRegion === codeRegion) {
281
281
  if (!enclosingBlock.nestedCodeRegions) {
282
- if (codeRegion.end - enclosingBlock.end > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 3) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
282
+ if (codeRegion.end - enclosingBlock.end > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 3) throw new Error("iMessage outbound runtime markup exceeded its limit");
283
283
  enclosingBlock.nestedCodeRegions = findCodeRegions(text.slice(enclosingBlock.end, codeRegion.end));
284
- if (enclosingBlock.nestedCodeRegions.length > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
284
+ if (enclosingBlock.nestedCodeRegions.length > MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES ** 2) throw new Error("iMessage outbound runtime markup exceeded its limit");
285
285
  }
286
286
  if (isInsideCode(start - enclosingBlock.end, enclosingBlock.nestedCodeRegions)) {
287
287
  enclosingBlock.sawNested = true;
@@ -302,7 +302,7 @@ function stripIMessageBalancedPrivateRuntimeBlocks(text, verifyProtectedRoles) {
302
302
  }
303
303
  const name = (token[2] ?? "").toLowerCase();
304
304
  if (!closing) {
305
- if (openBlocks.length >= MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES) throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
305
+ if (openBlocks.length >= MAX_PRIVATE_MARKDOWN_UNWRAP_PASSES) throw new Error("iMessage outbound runtime markup exceeded its limit");
306
306
  const parent = openBlocks.at(-1);
307
307
  if (parent) parent.sawNested = true;
308
308
  openBlocks.push({
@@ -322,14 +322,14 @@ function stripIMessageBalancedPrivateRuntimeBlocks(text, verifyProtectedRoles) {
322
322
  });
323
323
  continue;
324
324
  }
325
- if (opening.name !== name) throw new Error("iMessage outbound runtime scaffolding is malformed");
325
+ if (opening.name !== name) throw new Error("iMessage outbound runtime markup is malformed");
326
326
  openBlocks.pop();
327
327
  completedBlocks.push({
328
328
  start: opening.start,
329
329
  end
330
330
  });
331
331
  }
332
- if (openBlocks.length > 1 || openBlocks[0]?.sawNested || openBlocks[0] && [...opaqueHtml, ...rawTextHtml].some((region) => !region.terminated && openBlocks[0].start < region.start)) throw new Error("iMessage outbound runtime scaffolding is malformed");
332
+ if (openBlocks.length > 1 || openBlocks[0]?.sawNested || openBlocks[0] && [...opaqueHtml, ...rawTextHtml].some((region) => !region.terminated && openBlocks[0].start < region.start)) throw new Error("iMessage outbound runtime markup is malformed");
333
333
  const orphan = openBlocks[0];
334
334
  if (orphan) completedBlocks.push({
335
335
  start: orphan.start,
@@ -368,7 +368,7 @@ function stripIMessagePrivateRuntimeScaffolding(text, verifyProtectedRoles) {
368
368
  }
369
369
  if (!changed) return current;
370
370
  }
371
- throw new Error("iMessage outbound runtime scaffolding exceeded its limit");
371
+ throw new Error("iMessage outbound runtime markup exceeded its limit");
372
372
  }
373
373
  function sanitizeIMessageAssistantVisibleText(text, verifyProtectedRoles) {
374
374
  const cleaned = stripIMessagePrivateRuntimeScaffolding(text, verifyProtectedRoles);