@openclaw/imessage 2026.10.10 → 2026.11.5-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/.setup/{accounts-BH0coL46.mjs → accounts-C1Ypv3Wn.mjs} +6 -7
  2. package/dist/.setup/{actions-CW7XYYF4.mjs → actions-qOuKy8To.mjs} +360 -108
  3. package/dist/.setup/{actions.runtime-B2ZHQ3TJ.mjs → actions.runtime-Ccg9CARL.mjs} +10 -56
  4. package/dist/.setup/{api-6xi-NBrt.mjs → api-BXXuQQCS.mjs} +7 -7
  5. package/dist/.setup/{api-B0ih8Iof.mjs → api-DTbxSOPc.mjs} +8 -8
  6. package/dist/.setup/{approval-handler.runtime-BKmqba3L.mjs → approval-handler.runtime-B6vrFfZx.mjs} +112 -131
  7. package/dist/.setup/{approval-reactions-B9CJS0NT.mjs → approval-reactions-DBBsqbRC.mjs} +152 -72
  8. package/dist/.setup/{channel-6kcKpSRw.mjs → channel-CLaN4mQn.mjs} +142 -119
  9. package/dist/.setup/{channel.runtime-BW4A0m4h.mjs → channel.runtime-XJGx1MIH.mjs} +205 -363
  10. package/dist/.setup/chat-context-fTfJVqAi.mjs +77 -0
  11. package/dist/.setup/chat-lookup-xtPsOBkl.mjs +50 -0
  12. package/dist/.setup/client-D_BaG8sC.mjs +367 -0
  13. package/dist/.setup/{config-schema-DRkdRC21.mjs → config-schema-BjkQV5P5.mjs} +1 -2
  14. package/dist/.setup/{conversation-bindings-vZFn6sN2.mjs → conversation-bindings-8DjxoIYU.mjs} +9 -6
  15. package/dist/.setup/{media-contract-CoixpXRX.mjs → media-contract-BErFBKV9.mjs} +1 -1
  16. package/dist/.setup/{message-tool-api-CxSY_rh7.mjs → message-tool-api-DtZ1wKbT.mjs} +3 -6
  17. package/dist/.setup/{remote-file-CCgMYZ-u.mjs → remote-file-DpK3GQFD.mjs} +8 -22
  18. package/dist/.setup/{sanitize-outbound-C02fJrn6.mjs → sanitize-outbound-Dhjhz0a-.mjs} +23 -27
  19. package/dist/.setup/{send-D98hQFdv.mjs → send-DhEW0gfe.mjs} +150 -226
  20. package/dist/.setup/{setup-core-BWnW0czE.mjs → setup-core-B1_P_t4T.mjs} +2 -2
  21. package/dist/.setup/{setup-surface-aRZhAzzQ.mjs → setup-surface-CD28mXWK.mjs} +11 -25
  22. package/dist/.setup/{targets-CIx4BKqL.mjs → targets-C7CIWJ0k.mjs} +12 -3
  23. package/dist/.source-module-urls/929c518fa2986b0f85f66bceefead22868db0ab452dee914dc6a2bb86b2e60ad.json +1 -1
  24. package/dist/api.js +8 -8
  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/media-contract-api.js +1 -1
  29. package/dist/message-tool-api.js +1 -1
  30. package/dist/runtime-api.js +1 -3
  31. package/dist/session-binding-contract-api.js +1 -1
  32. package/dist/setup-entry.js +1 -1
  33. package/dist/src/chat-db.worker.js +1 -1
  34. package/node_modules/typebox/build/format/duration.d.mts +2 -2
  35. package/node_modules/typebox/build/format/duration.mjs +2 -2
  36. package/node_modules/typebox/build/format/idn_email.d.mts +1 -0
  37. package/node_modules/typebox/build/format/idn_email.mjs +1 -0
  38. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -1
  39. package/node_modules/typebox/build/format/idn_hostname.mjs +1 -1
  40. package/node_modules/typebox/build/format/ipv4.d.mts +2 -2
  41. package/node_modules/typebox/build/format/ipv4.mjs +2 -2
  42. package/node_modules/typebox/build/format/ipv6.d.mts +1 -1
  43. package/node_modules/typebox/build/format/ipv6.mjs +1 -1
  44. package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
  45. package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
  46. package/node_modules/typebox/build/schema/build.mjs +1 -0
  47. package/node_modules/typebox/build/schema/engine/_unique.d.mts +4 -2
  48. package/node_modules/typebox/build/schema/engine/_unique.mjs +7 -3
  49. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +3 -3
  50. package/node_modules/typebox/build/schema/engine/additionalProperties.d.mts +0 -3
  51. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +15 -17
  52. package/node_modules/typebox/build/schema/engine/contains.mjs +3 -3
  53. package/node_modules/typebox/build/schema/engine/index.d.mts +1 -0
  54. package/node_modules/typebox/build/schema/engine/index.mjs +1 -0
  55. package/node_modules/typebox/build/schema/engine/items.mjs +7 -4
  56. package/node_modules/typebox/build/schema/engine/maxContains.mjs +2 -2
  57. package/node_modules/typebox/build/schema/engine/minContains.mjs +3 -3
  58. package/node_modules/typebox/build/schema/engine/oneOf.mjs +2 -2
  59. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +2 -2
  60. package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -2
  61. package/node_modules/typebox/build/schema/engine/unevaluatedItems.mjs +2 -2
  62. package/node_modules/typebox/build/schema/engine/unevaluatedProperties.mjs +2 -2
  63. package/node_modules/typebox/build/system/hashing/hash.mjs +63 -34
  64. package/node_modules/typebox/build/system/locale/en_US.mjs +2 -1
  65. package/node_modules/typebox/build/system/memory/assign.d.mts +2 -8
  66. package/node_modules/typebox/build/system/memory/assign.mjs +13 -6
  67. package/node_modules/typebox/build/system/memory/clone.d.mts +4 -5
  68. package/node_modules/typebox/build/system/memory/clone.mjs +8 -96
  69. package/node_modules/typebox/build/system/memory/create.d.mts +1 -5
  70. package/node_modules/typebox/build/system/memory/create.mjs +18 -13
  71. package/node_modules/typebox/build/system/memory/deep.d.mts +2 -0
  72. package/node_modules/typebox/build/system/memory/deep.mjs +108 -0
  73. package/node_modules/typebox/build/system/memory/discard.d.mts +1 -1
  74. package/node_modules/typebox/build/system/memory/discard.mjs +9 -8
  75. package/node_modules/typebox/build/system/memory/finalize.d.mts +4 -0
  76. package/node_modules/typebox/build/system/memory/finalize.mjs +30 -0
  77. package/node_modules/typebox/build/system/memory/keys.d.mts +2 -0
  78. package/node_modules/typebox/build/system/memory/keys.mjs +8 -0
  79. package/node_modules/typebox/build/system/memory/update.d.mts +1 -4
  80. package/node_modules/typebox/build/system/memory/update.mjs +43 -16
  81. package/node_modules/typebox/build/type/engine/call/index.d.mts +1 -0
  82. package/node_modules/typebox/build/type/engine/call/index.mjs +1 -0
  83. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +2 -24
  84. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +2 -29
  85. package/node_modules/typebox/build/type/engine/evaluate/index.d.mts +1 -0
  86. package/node_modules/typebox/build/type/engine/evaluate/index.mjs +1 -0
  87. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +5 -1
  88. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +6 -1
  89. package/node_modules/typebox/build/type/engine/evaluate/property.d.mts +30 -0
  90. package/node_modules/typebox/build/type/engine/evaluate/property.mjs +29 -0
  91. package/node_modules/typebox/build/type/engine/immutable/instantiate_add.d.mts +1 -3
  92. package/node_modules/typebox/build/type/engine/immutable/instantiate_add.mjs +1 -4
  93. package/node_modules/typebox/build/type/engine/object/from_intersect.d.mts +2 -2
  94. package/node_modules/typebox/build/type/engine/object/from_intersect.mjs +2 -2
  95. package/node_modules/typebox/build/type/engine/object/from_union.d.mts +2 -2
  96. package/node_modules/typebox/build/type/engine/object/from_union.mjs +2 -2
  97. package/node_modules/typebox/build/type/engine/optional/instantiate_add.d.mts +1 -3
  98. package/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs +1 -4
  99. package/node_modules/typebox/build/type/engine/readonly/instantiate_add.d.mts +1 -3
  100. package/node_modules/typebox/build/type/engine/readonly/instantiate_add.mjs +1 -4
  101. package/node_modules/typebox/build/type/engine/this/index.d.mts +1 -0
  102. package/node_modules/typebox/build/type/engine/this/index.mjs +1 -0
  103. package/node_modules/typebox/build/value/clone/clone.d.mts +4 -3
  104. package/node_modules/typebox/build/value/clone/clone.mjs +5 -7
  105. package/node_modules/typebox/build/value/repair/from_cyclic.d.mts +2 -0
  106. package/node_modules/typebox/build/value/repair/from_cyclic.mjs +6 -0
  107. package/node_modules/typebox/build/value/shared/union_priority_sort.mjs +25 -14
  108. package/node_modules/typebox/package.json +1 -1
  109. package/node_modules/typebox/readme.md +1 -1
  110. package/node_modules/zod/package.json +1 -1
  111. package/node_modules/zod/src/v4/classic/tests/array.test.ts +27 -0
  112. package/node_modules/zod/src/v4/classic/tests/map.test.ts +7 -0
  113. package/node_modules/zod/src/v4/classic/tests/set.test.ts +6 -0
  114. package/node_modules/zod/src/v4/core/compile.ts +9 -0
  115. package/node_modules/zod/src/v4/core/schemas.ts +35 -1
  116. package/node_modules/zod/src/v4/core/util.ts +13 -0
  117. package/node_modules/zod/src/v4/core/versions.ts +1 -1
  118. package/node_modules/zod/v4/core/compile.cjs +8 -0
  119. package/node_modules/zod/v4/core/compile.js +8 -0
  120. package/node_modules/zod/v4/core/schemas.cjs +30 -0
  121. package/node_modules/zod/v4/core/schemas.d.cts +3 -1
  122. package/node_modules/zod/v4/core/schemas.d.ts +3 -1
  123. package/node_modules/zod/v4/core/schemas.js +30 -0
  124. package/node_modules/zod/v4/core/util.cjs +15 -0
  125. package/node_modules/zod/v4/core/util.d.cts +2 -0
  126. package/node_modules/zod/v4/core/util.d.ts +2 -0
  127. package/node_modules/zod/v4/core/util.js +14 -0
  128. package/node_modules/zod/v4/core/versions.cjs +1 -1
  129. package/node_modules/zod/v4/core/versions.js +1 -1
  130. package/openclaw.plugin.json +0 -30
  131. package/package.json +6 -6
  132. package/dist/.setup/client-C3e7ZLqA.mjs +0 -739
  133. package/dist/.setup/question-reactions-DTj-VH3N.mjs +0 -67
  134. package/dist/.setup/reaction-context-Bw00FkmU.mjs +0 -105
  135. package/dist/.setup/runtime-BRQCPuAD.mjs +0 -7
  136. package/node_modules/typebox/build/system/memory/freeze.d.mts +0 -4
  137. package/node_modules/typebox/build/system/memory/freeze.mjs +0 -6
@@ -0,0 +1,77 @@
1
+ import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
2
+ import { normalizeE164 } from "openclaw/plugin-sdk/account-resolution";
3
+ // extensions/imessage/src/chat-context.ts
4
+ const EMAIL_HANDLE_PATTERN = /^[^\s@]+@[^\s@]+$/u;
5
+ function parseDirectChatIdentity(raw) {
6
+ const trimmed = raw.trim();
7
+ const parts = trimmed.split(";");
8
+ if (parts.length === 3 && parts[1] === "-" && parts[2]) {
9
+ const service = parts[0]?.toLowerCase();
10
+ if (service === "imessage" || service === "sms" || service === "any") {
11
+ const identifier = parts[2];
12
+ return {
13
+ service,
14
+ identifier: EMAIL_HANDLE_PATTERN.test(identifier) ? identifier.toLowerCase() : identifier
15
+ };
16
+ }
17
+ }
18
+ if (parts.length !== 1) return;
19
+ if (trimmed.startsWith("+") || EMAIL_HANDLE_PATTERN.test(trimmed)) return { identifier: trimmed.toLowerCase() };
20
+ }
21
+ function resolveIMessageDirectChatService(configuredService, chatGuid) {
22
+ if (configuredService === "imessage" || configuredService === "sms") return configuredService;
23
+ const observedService = chatGuid ? parseDirectChatIdentity(chatGuid)?.service : void 0;
24
+ return observedService === "imessage" || observedService === "sms" ? observedService : void 0;
25
+ }
26
+ function isIMessageEmailChatIdentifier(raw) {
27
+ const identity = parseDirectChatIdentity(raw);
28
+ return Boolean(identity && EMAIL_HANDLE_PATTERN.test(identity.identifier));
29
+ }
30
+ /**
31
+ * Strip the `iMessage;-;` / `SMS;-;` / `any;-;` service prefix that Messages
32
+ * uses for direct chats. Different layers report direct DMs in different
33
+ * forms, so raw comparison would falsely treat one DM as different chats.
34
+ */
35
+ function normalizeDirectChatIdentifier(raw) {
36
+ const trimmed = raw.trim();
37
+ return parseDirectChatIdentity(trimmed)?.identifier ?? trimmed;
38
+ }
39
+ function chatContextFromIMessageTarget(target, effectiveService) {
40
+ if (target.kind === "chat_id") return { chatId: target.chatId };
41
+ if (target.kind === "chat_guid") return { chatGuid: target.chatGuid };
42
+ if (target.kind === "chat_identifier") return { chatIdentifier: target.chatIdentifier };
43
+ const trimmedHandle = target.to.trim();
44
+ const canonicalHandle = trimmedHandle.startsWith("+") ? normalizeE164(trimmedHandle) : EMAIL_HANDLE_PATTERN.test(trimmedHandle) ? trimmedHandle.toLowerCase() : void 0;
45
+ if (!canonicalHandle) return {};
46
+ const service = target.service === "auto" ? effectiveService : target.service;
47
+ if (service !== "imessage" && service !== "sms") return {};
48
+ return { chatIdentifier: `${service === "sms" ? "SMS" : "iMessage"};-;${canonicalHandle}` };
49
+ }
50
+ function compareOptional(left, right) {
51
+ return left === void 0 || right === void 0 ? void 0 : left === right;
52
+ }
53
+ function compareChatSelector(cachedRaw, currentRaw, crossKind = false) {
54
+ const cached = normalizeOptionalString(cachedRaw);
55
+ const current = normalizeOptionalString(currentRaw);
56
+ if (!cached || !current) return;
57
+ if (cached === current) return true;
58
+ const cachedDirect = parseDirectChatIdentity(cached);
59
+ const currentDirect = parseDirectChatIdentity(current);
60
+ if (!cachedDirect || !currentDirect) return crossKind ? void 0 : false;
61
+ if (cachedDirect.identifier !== currentDirect.identifier) return false;
62
+ if (cachedDirect.service === "any") return true;
63
+ if (!cachedDirect.service || !currentDirect.service) return;
64
+ return cachedDirect.service === currentDirect.service;
65
+ }
66
+ function resolveIMessageChatMatch(cached, current) {
67
+ const comparisons = [
68
+ compareChatSelector(cached.chatGuid, current.chatGuid),
69
+ compareChatSelector(cached.chatIdentifier, current.chatIdentifier),
70
+ compareOptional(cached.chatId, current.chatId),
71
+ compareChatSelector(cached.chatGuid, current.chatIdentifier, true),
72
+ compareChatSelector(cached.chatIdentifier, current.chatGuid, true)
73
+ ].filter((comparison) => comparison !== void 0);
74
+ if (comparisons.length === 0) return "unknown";
75
+ return comparisons.every(Boolean) ? "match" : "mismatch";
76
+ }
77
+ export { resolveIMessageDirectChatService as a, resolveIMessageChatMatch as i, isIMessageEmailChatIdentifier as n, normalizeDirectChatIdentifier as r, chatContextFromIMessageTarget as t };
@@ -0,0 +1,50 @@
1
+ import { r as normalizeDirectChatIdentifier } from "./chat-context-fTfJVqAi.mjs";
2
+ import { n as createIMessageRpcClient } from "./client-D_BaG8sC.mjs";
3
+ import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
4
+ import { parseStrictInteger } from "openclaw/plugin-sdk/number-runtime";
5
+ // extensions/imessage/src/chat-lookup.ts
6
+ const CHAT_LIST_CACHE_TTL_MS = 3e4;
7
+ const chatListCache = /* @__PURE__ */ new Map();
8
+ function numberFromUnknown(value) {
9
+ return typeof value === "number" && Number.isFinite(value) ? value : parseStrictInteger(value);
10
+ }
11
+ function findChat(chats, target) {
12
+ if (target.kind === "chat_id") return chats.find((chat) => chat.id === target.chatId) ?? null;
13
+ if (target.kind === "chat_guid") return chats.find((chat) => chat.guid === target.chatGuid) ?? null;
14
+ const wanted = normalizeDirectChatIdentifier(target.chatIdentifier);
15
+ for (const chat of chats) {
16
+ const { identifier, guid } = chat;
17
+ if (identifier === target.chatIdentifier || guid === target.chatIdentifier || identifier && normalizeDirectChatIdentifier(identifier) === wanted || normalizeDirectChatIdentifier(guid) === wanted) return chat;
18
+ }
19
+ return null;
20
+ }
21
+ async function resolveIMessageChat(params) {
22
+ const { cliPath, dbPath, remoteHost } = params.options;
23
+ const key = `${cliPath}\0${dbPath ?? ""}\0${remoteHost ?? ""}`;
24
+ const cached = chatListCache.get(key);
25
+ if (cached && cached.expiresAt > Date.now()) return findChat(cached.list, params.target);
26
+ chatListCache.delete(key);
27
+ const client = await createIMessageRpcClient(params.options);
28
+ try {
29
+ const result = await client.request("chats.list", { limit: 1e3 }, { timeoutMs: params.options.timeoutMs });
30
+ const list = [];
31
+ for (const chat of isRecord(result) && Array.isArray(result.chats) ? result.chats : []) {
32
+ if (!isRecord(chat)) continue;
33
+ const id = numberFromUnknown(chat.id);
34
+ const guid = normalizeOptionalString(chat.guid);
35
+ if (id !== void 0 && guid) list.push({
36
+ id,
37
+ guid,
38
+ identifier: normalizeOptionalString(chat.identifier)
39
+ });
40
+ }
41
+ chatListCache.set(key, {
42
+ list,
43
+ expiresAt: Date.now() + CHAT_LIST_CACHE_TTL_MS
44
+ });
45
+ return findChat(list, params.target);
46
+ } finally {
47
+ await client.stop();
48
+ }
49
+ }
50
+ export { resolveIMessageChat };
@@ -0,0 +1,367 @@
1
+ import { u as expandIMessageUserPath } from "./accounts-C1Ypv3Wn.mjs";
2
+ import { i as invalidateCachedIMessagePrivateApiStatus } from "./message-tool-api-DtZ1wKbT.mjs";
3
+ import { asNullableRecord, isRecord, normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
4
+ import { runCommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
5
+ import { resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
6
+ import { spawn } from "node:child_process";
7
+ import { StringDecoder } from "node:string_decoder";
8
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
9
+ import { captureChannelReadAuthority, captureEffectAuthority } from "openclaw/plugin-sdk/fetch-runtime";
10
+ import { raceWithTimeout } from "openclaw/plugin-sdk/time-runtime";
11
+ // extensions/imessage/src/constants.ts
12
+ const DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS = 1e4;
13
+ const DEFAULT_IMESSAGE_SEND_TIMEOUT_MS = 18e4;
14
+ // extensions/imessage/src/cli-output.ts
15
+ const IMESSAGE_CLI_STDOUT_MAX_BYTES = 8388608;
16
+ const IMESSAGE_CLI_STDERR_TAIL_BYTES = 65536;
17
+ function parseLastJsonObject(stdout) {
18
+ const last = stdout.split(/\r?\n/u).findLast((line) => line.trim().length > 0)?.trim();
19
+ if (!last) return null;
20
+ try {
21
+ return asNullableRecord(JSON.parse(last));
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+ async function runIMessageCliJsonCommand(params) {
27
+ const dbPath = params.dbPath?.trim();
28
+ const argv = [
29
+ expandIMessageUserPath(params.cliPath),
30
+ ...params.args,
31
+ ...dbPath ? ["--db", dbPath] : [],
32
+ "--json"
33
+ ];
34
+ const result = await runCommandWithTimeout(argv, {
35
+ killProcessTree: true,
36
+ maxOutputBytes: {
37
+ stdout: IMESSAGE_CLI_STDOUT_MAX_BYTES,
38
+ stderr: IMESSAGE_CLI_STDERR_TAIL_BYTES
39
+ },
40
+ outputCapture: {
41
+ stdout: "head",
42
+ stderr: "tail"
43
+ },
44
+ terminateOnOutputLimit: { stdout: true },
45
+ timeoutMs: params.timeoutMs
46
+ });
47
+ if (result.termination === "timeout") throw new Error(`iMessage action timed out after ${params.timeoutMs}ms`);
48
+ if (result.outputLimitExceeded || result.stdoutTruncatedBytes) throw new Error(`imsg stdout exceeded ${IMESSAGE_CLI_STDOUT_MAX_BYTES} bytes`);
49
+ const parsed = parseLastJsonObject(result.stdout);
50
+ if (result.code !== 0) {
51
+ const detail = typeof parsed?.error === "string" && parsed.error.trim() || result.stderr.trim() || result.stdout.trim() || `imsg exited with code ${result.code}`;
52
+ throw new Error(detail);
53
+ }
54
+ if (!parsed) throw new Error(`imsg returned non-JSON output: ${result.stdout.trim() || result.stderr.trim()}`);
55
+ if (parsed.success === false) {
56
+ const detail = typeof parsed.error === "string" && parsed.error.trim() ? parsed.error.trim() : "iMessage action failed";
57
+ throw new Error(detail);
58
+ }
59
+ return parsed;
60
+ }
61
+ // extensions/imessage/src/bridge-recovery.ts
62
+ const BRIDGE_RECOVERY_TIMEOUT_MS = 3e4;
63
+ const recoveries = /* @__PURE__ */ new Map();
64
+ /**
65
+ * Re-inject the private bridge after imsg has positively identified it as
66
+ * unresponsive. This deliberately does not retry the failed mutation: imsg may
67
+ * still reconcile a published send, so replaying it could duplicate a message.
68
+ */
69
+ function recoverIMessageBridge(cliPath) {
70
+ const existing = recoveries.get(cliPath);
71
+ if (existing) return existing;
72
+ const recovery = runIMessageCliJsonCommand({
73
+ cliPath,
74
+ args: ["launch"],
75
+ timeoutMs: BRIDGE_RECOVERY_TIMEOUT_MS
76
+ }).then(() => void 0).finally(() => {
77
+ recoveries.delete(cliPath);
78
+ });
79
+ recoveries.set(cliPath, recovery);
80
+ return recovery;
81
+ }
82
+ // extensions/imessage/src/process-diagnostics.ts
83
+ const PUBLIC_IMESSAGE_FULL_DISK_ACCESS_ERROR = "imsg cannot access ~/Library/Messages/chat.db. Grant Full Disk Access to the Gateway/launcher process and restart Gateway.";
84
+ function normalizeIMessageFullDiskAccessError(message) {
85
+ const normalized = normalizeLowercaseStringOrEmpty(message);
86
+ return normalized.includes("full disk access") && normalized.includes("chat.db") ? PUBLIC_IMESSAGE_FULL_DISK_ACCESS_ERROR : void 0;
87
+ }
88
+ // extensions/imessage/src/client.ts
89
+ 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;
90
+ var IMessageRpcRequestError = class extends Error {
91
+ constructor(message, code, data) {
92
+ super(message);
93
+ this.code = code;
94
+ this.data = data;
95
+ this.name = "IMessageRpcRequestError";
96
+ }
97
+ };
98
+ function isIMessageBridgeStall(error) {
99
+ if (error instanceof IMessageRpcRequestError && isRecord(error.data)) {
100
+ const { transport, detail } = error.data;
101
+ if (transport === "bridge_v2" && typeof detail === "string" && detail.startsWith("The bridge response deadline expired.")) return true;
102
+ }
103
+ return error instanceof Error && error.message.includes("Timed out waiting for response");
104
+ }
105
+ 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.";
106
+ function describeIMessageBridgeStall(error) {
107
+ if (error instanceof IMessageRpcRequestError) return new IMessageRpcRequestError(`${error.message} ${BRIDGE_STALL_GUIDANCE}`, error.code, error.data);
108
+ if (error instanceof Error) return new Error(`${error.message} ${BRIDGE_STALL_GUIDANCE}`, { cause: error });
109
+ return error;
110
+ }
111
+ const IMESSAGE_RPC_CLOSE_GRACE_MS = 500;
112
+ function isTestEnv() {
113
+ const vitest = normalizeLowercaseStringOrEmpty(process.env.VITEST);
114
+ return Boolean(vitest);
115
+ }
116
+ function createLfLineFramer(onLine) {
117
+ const decoder = new StringDecoder("utf8");
118
+ let buffer = "";
119
+ return {
120
+ write(chunk) {
121
+ buffer += typeof chunk === "string" ? chunk : decoder.write(chunk);
122
+ let newlineIndex = buffer.indexOf("\n");
123
+ while (newlineIndex !== -1) {
124
+ const line = buffer.slice(0, newlineIndex);
125
+ buffer = buffer.slice(newlineIndex + 1);
126
+ onLine(line);
127
+ newlineIndex = buffer.indexOf("\n");
128
+ }
129
+ },
130
+ flush() {
131
+ buffer += decoder.end();
132
+ if (!buffer) return;
133
+ const line = buffer;
134
+ buffer = "";
135
+ onLine(line);
136
+ }
137
+ };
138
+ }
139
+ var IMessageRpcClient = class {
140
+ constructor(opts = {}) {
141
+ this.pending = /* @__PURE__ */ new Map();
142
+ this.terminalResolve = null;
143
+ this.reapedResolve = null;
144
+ this.child = null;
145
+ this.stopPromise = null;
146
+ this.stdoutFramer = createLfLineFramer((line) => this.handleStdoutLine(line));
147
+ this.stderrFramer = createLfLineFramer((line) => this.handleStderrLine(line));
148
+ this.nextId = 1;
149
+ this.publicProcessError = null;
150
+ this.configuredCliPath = opts.cliPath?.trim() || "imsg";
151
+ this.cliPath = expandIMessageUserPath(this.configuredCliPath);
152
+ const dbPath = opts.dbPath?.trim();
153
+ this.dbPath = dbPath ? opts.remoteHost?.trim() ? dbPath : resolveUserPath(dbPath) : void 0;
154
+ this.runtime = opts.runtime;
155
+ this.onNotification = opts.onNotification;
156
+ this.terminal = new Promise((resolve) => {
157
+ this.terminalResolve = resolve;
158
+ });
159
+ this.reaped = new Promise((resolve) => {
160
+ this.reapedResolve = resolve;
161
+ });
162
+ }
163
+ async start() {
164
+ if (this.child) return;
165
+ if (isTestEnv()) throw new Error("Refusing to start imsg rpc in test environment; mock iMessage RPC client");
166
+ const args = ["rpc", "--json"];
167
+ if (this.dbPath) args.push("--db", this.dbPath);
168
+ const child = spawn(this.cliPath, args, { stdio: [
169
+ "pipe",
170
+ "pipe",
171
+ "pipe"
172
+ ] });
173
+ this.child = child;
174
+ for (const [stream, framer] of [[child.stdout, this.stdoutFramer], [child.stderr, this.stderrFramer]]) stream.on("data", (chunk) => {
175
+ if (this.child === child) framer.write(chunk);
176
+ });
177
+ const failFromProcessError = (err) => this.failTransport(err, child);
178
+ child.on("error", failFromProcessError);
179
+ for (const stream of [
180
+ child.stdin,
181
+ child.stdout,
182
+ child.stderr
183
+ ]) {
184
+ stream.on("error", failFromProcessError);
185
+ stream.once("close", () => {
186
+ const error = stream.errored;
187
+ if (error) failFromProcessError(error);
188
+ });
189
+ }
190
+ child.on("close", (code, signal) => {
191
+ if (this.child === child) {
192
+ this.stdoutFramer.flush();
193
+ this.stderrFramer.flush();
194
+ this.child = null;
195
+ }
196
+ this.finish(this.buildCloseError(code, signal));
197
+ const resolve = this.reapedResolve;
198
+ this.reapedResolve = null;
199
+ resolve?.();
200
+ });
201
+ }
202
+ async stop() {
203
+ if (this.stopPromise) return await this.stopPromise;
204
+ const child = this.child;
205
+ if (!child) return;
206
+ this.stopPromise = this.stopChild(child);
207
+ return await this.stopPromise;
208
+ }
209
+ async waitForClose() {
210
+ throw await this.terminal;
211
+ }
212
+ async request(method, params, opts) {
213
+ const child = this.child;
214
+ const stdin = child?.stdin;
215
+ const assertReadAuthority = captureChannelReadAuthority();
216
+ return captureEffectAuthority().initiate(() => {
217
+ if (child !== this.child || stdin !== this.child?.stdin) throw new Error("imsg rpc process changed before request initiation");
218
+ assertReadAuthority?.();
219
+ opts?.assertCurrent?.();
220
+ return this.initiateRequest(method, params, opts);
221
+ });
222
+ }
223
+ async initiateRequest(method, params, opts) {
224
+ if (!this.child || !this.child.stdin) throw new Error("imsg rpc not running");
225
+ const id = this.nextId++;
226
+ const line = `${JSON.stringify({
227
+ jsonrpc: "2.0",
228
+ id,
229
+ method,
230
+ params: params ?? {}
231
+ })}\n`;
232
+ const timeoutMs = opts?.timeoutMs ?? 1e4;
233
+ const key = String(id);
234
+ const pendingResponse = new Promise((resolve, reject) => {
235
+ this.pending.set(key, {
236
+ resolve: (value) => resolve(value),
237
+ reject
238
+ });
239
+ });
240
+ const response = timeoutMs > 0 ? raceWithTimeout(pendingResponse, timeoutMs, () => {
241
+ this.pending.delete(key);
242
+ throw new Error(`imsg rpc timeout (${method})`);
243
+ }) : pendingResponse;
244
+ try {
245
+ this.child.stdin.write(line, (err) => {
246
+ if (err) this.failTransport(err, this.child);
247
+ });
248
+ } catch (err) {
249
+ this.failTransport(err, this.child);
250
+ }
251
+ try {
252
+ return await response;
253
+ } catch (err) {
254
+ if (isIMessageBridgeStall(err)) {
255
+ invalidateCachedIMessagePrivateApiStatus(this.configuredCliPath);
256
+ try {
257
+ await recoverIMessageBridge(this.configuredCliPath);
258
+ } catch (recoveryError) {
259
+ this.runtime?.error?.(normalizeIMessageFullDiskAccessError(formatErrorMessage(recoveryError)) ?? `imessage: automatic bridge recovery failed. ${BRIDGE_STALL_GUIDANCE}`);
260
+ }
261
+ throw describeIMessageBridgeStall(err);
262
+ }
263
+ throw err;
264
+ }
265
+ }
266
+ async stopChild(child) {
267
+ try {
268
+ child.stdin.end();
269
+ } catch (err) {
270
+ this.failTransport(err, child);
271
+ }
272
+ if (await this.waitForReap(IMESSAGE_RPC_CLOSE_GRACE_MS)) return;
273
+ this.signalChild(child, "SIGTERM");
274
+ if (await this.waitForReap(IMESSAGE_RPC_CLOSE_GRACE_MS)) return;
275
+ this.signalChild(child, "SIGKILL");
276
+ if (!await this.waitForReap(IMESSAGE_RPC_CLOSE_GRACE_MS)) throw new Error("imsg rpc did not exit after SIGKILL");
277
+ }
278
+ async waitForReap(timeoutMs) {
279
+ if (!this.reapedResolve) return true;
280
+ return await raceWithTimeout(this.reaped.then(() => true), timeoutMs, () => false);
281
+ }
282
+ signalChild(child, signal) {
283
+ try {
284
+ child.kill(signal);
285
+ } catch {}
286
+ }
287
+ failTransport(err, child) {
288
+ if (!this.finish(err instanceof Error ? err : new Error(String(err)))) return;
289
+ if (child) this.signalChild(child, "SIGTERM");
290
+ }
291
+ handleStdoutLine(line) {
292
+ const trimmed = line.trim();
293
+ if (!trimmed) return;
294
+ this.handleLine(trimmed);
295
+ }
296
+ handleStderrLine(line) {
297
+ const trimmed = line.trim();
298
+ if (!trimmed) return;
299
+ this.recordProcessDiagnostic(trimmed);
300
+ if (IMSG_APPLE_FRAMEWORK_STDERR_PATTERN.test(trimmed)) return;
301
+ this.runtime?.error?.(normalizeIMessageFullDiskAccessError(trimmed) ?? "imsg rpc: stderr diagnostic received");
302
+ }
303
+ handleLine(line) {
304
+ let parsed;
305
+ try {
306
+ parsed = JSON.parse(line);
307
+ } catch {
308
+ this.recordProcessDiagnostic(line);
309
+ this.runtime?.error?.("imsg rpc: failed to parse JSON response");
310
+ return;
311
+ }
312
+ if (parsed.id !== void 0 && parsed.id !== null) {
313
+ const key = String(parsed.id);
314
+ const pending = this.pending.get(key);
315
+ if (!pending) return;
316
+ this.pending.delete(key);
317
+ if (parsed.error) {
318
+ const baseMessage = parsed.error.message ?? "imsg rpc error";
319
+ const details = parsed.error.data;
320
+ const code = parsed.error.code;
321
+ const suffixes = [];
322
+ if (typeof code === "number") suffixes.push(`code=${code}`);
323
+ if (details !== void 0) {
324
+ const detailText = typeof details === "string" ? details : JSON.stringify(details, null, 2);
325
+ if (detailText) suffixes.push(detailText);
326
+ }
327
+ const msg = suffixes.length > 0 ? `${baseMessage}: ${suffixes.join(" ")}` : baseMessage;
328
+ pending.reject(new IMessageRpcRequestError(msg, typeof code === "number" ? code : void 0, details));
329
+ return;
330
+ }
331
+ pending.resolve(parsed.result);
332
+ return;
333
+ }
334
+ if (parsed.method) this.onNotification?.({
335
+ method: parsed.method,
336
+ params: parsed.params
337
+ });
338
+ }
339
+ recordProcessDiagnostic(line) {
340
+ this.publicProcessError ??= normalizeIMessageFullDiskAccessError(line) ?? null;
341
+ }
342
+ buildCloseError(code, signal) {
343
+ if (this.publicProcessError) return new Error(this.publicProcessError);
344
+ if (code !== 0 && code !== null) {
345
+ const reason = signal ? `signal ${signal}` : `code ${code}`;
346
+ return /* @__PURE__ */ new Error(`imsg rpc exited (${reason})`);
347
+ }
348
+ return /* @__PURE__ */ new Error("imsg rpc closed");
349
+ }
350
+ finish(err) {
351
+ const resolve = this.terminalResolve;
352
+ if (!resolve) return false;
353
+ this.terminalResolve = null;
354
+ for (const [key, pending] of this.pending.entries()) {
355
+ pending.reject(err);
356
+ this.pending.delete(key);
357
+ }
358
+ resolve(err);
359
+ return true;
360
+ }
361
+ };
362
+ async function createIMessageRpcClient(opts = {}) {
363
+ const client = new IMessageRpcClient(opts);
364
+ await client.start();
365
+ return client;
366
+ }
367
+ export { DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS as a, runIMessageCliJsonCommand as i, createIMessageRpcClient as n, DEFAULT_IMESSAGE_SEND_TIMEOUT_MS as o, normalizeIMessageFullDiskAccessError as r, IMessageRpcRequestError as t };
@@ -1,7 +1,7 @@
1
1
  import { createChannelConfigUiHints } from "openclaw/plugin-sdk/channel-core";
2
2
  import { ChannelDeliveryStreamingConfigSchema, ChannelSendReadReceiptsSchema, ExecutableTokenSchema, buildChannelAccountSchemaParts, buildChannelConfigSchema, buildChannelReactionShape, buildGroupEntrySchema, isSafeScpRemoteHost, isValidInboundPathRootPattern, refineChannelDmPolicy } from "openclaw/plugin-sdk/channel-config-schema";
3
3
  import { z } from "zod";
4
- // extensions/imessage/src/config-ui-hints.ts
4
+ // extensions/imessage/src/config-schema.ts
5
5
  const iMessageChannelConfigUiHints = {
6
6
  "": {
7
7
  label: "iMessage",
@@ -35,7 +35,6 @@ const iMessageChannelConfigUiHints = {
35
35
  help: "Preferred imsg RPC send transport for normal outbound replies. \"auto\" uses the IMCore bridge when available, \"bridge\" requires it, and \"applescript\" forces Messages automation."
36
36
  }
37
37
  };
38
- // extensions/imessage/src/config-schema.ts
39
38
  const IMessageActionSchema = z.object({
40
39
  reactions: z.boolean().optional(),
41
40
  edit: z.boolean().optional(),
@@ -1,4 +1,5 @@
1
- import { createAccountScopedConversationBindingManager, createAccountScopedConversationBindingManagerV2, resetAccountScopedConversationBindingsForTests } from "openclaw/plugin-sdk/thread-bindings-runtime";
1
+ import { createAccountScopedConversationBindingManager, createAccountScopedConversationBindingManagerV2 } from "openclaw/plugin-sdk/thread-bindings-runtime";
2
+ import { warnPluginSdkDeprecation } from "openclaw/plugin-sdk/thread-bindings-session-runtime";
2
3
  // extensions/imessage/src/conversation-bindings.ts
3
4
  const IMESSAGE_CONVERSATION_BINDINGS_STATE_KEY = Symbol.for("openclaw.imessageConversationBindingsState");
4
5
  function toSessionBindingTargetKind(raw) {
@@ -7,8 +8,13 @@ function toSessionBindingTargetKind(raw) {
7
8
  function toIMessageTargetKind(raw) {
8
9
  return raw === "subagent" ? "subagent" : "acp";
9
10
  }
10
- /** @deprecated Use createIMessageConversationBindingManagerV2; final compatibility day: 2026-12-31. */
11
+ /** @deprecated Use createIMessageConversationBindingManagerV2; final compatibility day: 2026-12-31. Removal in the first January 2027 stable release. */
11
12
  function createIMessageConversationBindingManager(params) {
13
+ warnPluginSdkDeprecation({
14
+ family: "conversation-bindings",
15
+ method: "createIMessageConversationBindingManager",
16
+ replacement: "createIMessageConversationBindingManagerV2"
17
+ });
12
18
  return createAccountScopedConversationBindingManager({
13
19
  channel: "imessage",
14
20
  cfg: params.cfg,
@@ -29,7 +35,4 @@ function createIMessageConversationBindingManagerV2(params) {
29
35
  toSessionBindingTargetKind
30
36
  });
31
37
  }
32
- const testing = { resetIMessageConversationBindingsForTests() {
33
- resetAccountScopedConversationBindingsForTests({ stateKey: IMESSAGE_CONVERSATION_BINDINGS_STATE_KEY });
34
- } };
35
- export { createIMessageConversationBindingManagerV2 as n, testing as r, createIMessageConversationBindingManager as t };
38
+ export { createIMessageConversationBindingManagerV2 as n, createIMessageConversationBindingManager as t };
@@ -1,4 +1,4 @@
1
- import { o as resolveIMessageAccount } from "./accounts-BH0coL46.mjs";
1
+ import { o as resolveIMessageAccount } from "./accounts-C1Ypv3Wn.mjs";
2
2
  import { mergeInboundPathRoots } from "openclaw/plugin-sdk/channel-inbound";
3
3
  // extensions/imessage/src/media-contract.ts
4
4
  const DEFAULT_IMESSAGE_ATTACHMENT_ROOTS = ["/Users/*/Library/Messages/Attachments"];
@@ -1,8 +1,7 @@
1
- import { n as inferIMessageTargetChatType } from "./targets-CIx4BKqL.mjs";
2
- import { c as getCachedIMessageRemoteHost, o as resolveIMessageAccount } from "./accounts-BH0coL46.mjs";
1
+ import { r as inferIMessageTargetChatType } from "./targets-C7CIWJ0k.mjs";
2
+ import { c as getCachedIMessageRemoteHost, o as resolveIMessageAccount } from "./accounts-C1Ypv3Wn.mjs";
3
3
  import { createActionGate } from "openclaw/plugin-sdk/channel-actions";
4
4
  import { Type } from "typebox";
5
- import { asDateTimestampMs } from "openclaw/plugin-sdk/number-runtime";
6
5
  // extensions/imessage/src/actions-contract.ts
7
6
  const IMESSAGE_ACTIONS = {
8
7
  react: { gate: "reactions" },
@@ -57,8 +56,7 @@ function getCachedIMessagePrivateApiStatus(cliPath) {
57
56
  const entry = bridgeStatusCache.get(key);
58
57
  if (!entry) return;
59
58
  if (entry.expiresAt === 0) return entry.status;
60
- const now = asDateTimestampMs(Date.now());
61
- if (now === void 0 || entry.expiresAt <= now) {
59
+ if (entry.expiresAt <= Date.now()) {
62
60
  bridgeStatusCache.delete(key);
63
61
  return;
64
62
  }
@@ -68,7 +66,6 @@ function invalidateCachedIMessagePrivateApiStatus(cliPath) {
68
66
  bridgeStatusCache.delete(normalizeCliPath(cliPath));
69
67
  }
70
68
  function setCachedIMessagePrivateApiStatus(cliPath, status, expiresAt = 0) {
71
- if (expiresAt !== 0 && asDateTimestampMs(expiresAt) === void 0) return;
72
69
  bridgeStatusCache.set(normalizeCliPath(cliPath), {
73
70
  status,
74
71
  expiresAt
@@ -1,25 +1,18 @@
1
- import { p as resolveLocalIMessageChatDbPath } from "./accounts-BH0coL46.mjs";
2
- import { b as isIMessageEmailChatIdentifier, c as resolveIMessageCachedResourceBinding, l as resolveIMessageMessageId, y as chatContextFromIMessageTarget } from "./client-C3e7ZLqA.mjs";
1
+ import { p as resolveLocalIMessageChatDbPath } from "./accounts-C1Ypv3Wn.mjs";
2
+ import { n as isIMessageEmailChatIdentifier, t as chatContextFromIMessageTarget } from "./chat-context-fTfJVqAi.mjs";
3
+ import { o as resolveIMessageCachedResourceBinding, s as resolveIMessageMessageId } from "./actions-qOuKy8To.mjs";
3
4
  import path from "node:path";
4
5
  import { normalizeScpRemoteHost } from "openclaw/plugin-sdk/host-runtime";
5
6
  import { createActionGate } from "openclaw/plugin-sdk/channel-actions";
6
7
  import { createSubsystemLogger, logVerbose } from "openclaw/plugin-sdk/runtime-env";
7
8
  import { randomUUID } from "node:crypto";
8
- import { resolveRuntimeWorkerUrl, runCommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
9
+ import { createPluginRuntimeProcessEntrypoint, resolveRuntimeWorkerUrl, runCommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
9
10
  import { sanitizeTempFileName } from "openclaw/plugin-sdk/temp-path";
10
11
  import { openSqliteWorkerStore } from "openclaw/plugin-sdk/sqlite-runtime";
11
12
  // extensions/imessage/src/chat-db.ts
12
13
  function openIMessageChatDbReader(databasePath) {
13
14
  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
- }),
15
+ moduleUrl: resolveRuntimeWorkerUrl(createPluginRuntimeProcessEntrypoint("iMessageChatDb", import.meta.url)),
23
16
  databasePath,
24
17
  existingOnly: true,
25
18
  input: void 0
@@ -152,10 +145,7 @@ async function resolveAuthorizedIMessageReplyReference(params) {
152
145
  const rawReplyToId = sanitizeReplyToId(params.replyToId);
153
146
  if (!rawReplyToId) return;
154
147
  const chatContext = chatContextFromIMessageTarget(params.target, params.service);
155
- const messageId = await resolveIMessageMessageId(rawReplyToId, {
156
- requireKnownShortId: true,
157
- chatContext
158
- });
148
+ const messageId = await resolveIMessageMessageId(rawReplyToId, { chatContext });
159
149
  await authorizeIMessageResourceReference({
160
150
  accountId: params.account.accountId,
161
151
  chatContext,
@@ -245,8 +235,7 @@ rm -rf -- ${remoteDir}
245
235
  "sh -s"
246
236
  ], {
247
237
  input: createScript,
248
- timeoutMs: params.timeoutMs,
249
- signal: params.signal
238
+ timeoutMs: params.timeoutMs
250
239
  });
251
240
  params.assertDirectAdapterHandoff?.();
252
241
  await runChecked("iMessage remote file upload", [
@@ -255,10 +244,7 @@ rm -rf -- ${remoteDir}
255
244
  "--",
256
245
  params.localPath,
257
246
  `${remoteHost}:${remotePath}`
258
- ], {
259
- timeoutMs: params.timeoutMs,
260
- signal: params.signal
261
- });
247
+ ], { timeoutMs: params.timeoutMs });
262
248
  params.assertDirectAdapterHandoff?.();
263
249
  return await params.use(remotePath);
264
250
  } finally {