@openclaw/imessage 2026.8.1-beta.2 → 2026.8.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 (157) hide show
  1. package/dist/{accounts-D00Njxg2.js → accounts-CCirBEIm.js} +4 -5
  2. package/dist/{actions.runtime-DTFKDN5p.js → actions.runtime-CAl3WcqD.js} +2 -3
  3. package/dist/api.js +6 -5
  4. package/dist/{approval-handler.runtime-BtLs4gho.js → approval-handler.runtime-UBd1j2Sg.js} +15 -9
  5. package/dist/approval-reactions-LL5yn1Rc.js +562 -0
  6. package/dist/{channel-lppmI51h.js → channel-DNI9DKGN.js} +26 -21
  7. package/dist/channel-config-api.js +1 -1
  8. package/dist/channel-plugin-api.js +1 -1
  9. package/dist/{channel.runtime-5wpCKQb7.js → channel.runtime-HSkrmWfW.js} +58 -11
  10. package/dist/{config-schema-DV0KP5nC.js → config-schema-DuGbA8Kp.js} +8 -7
  11. package/dist/contract-api.js +1 -1
  12. package/dist/doctor-contract-api.js +8 -281
  13. package/dist/{group-policy-DF9xSdik.js → group-policy-Dvc-BcWo.js} +19 -651
  14. package/dist/{media-contract-BEgdvYhW.js → media-contract-D-FhGE98.js} +1 -1
  15. package/dist/media-contract-api.js +1 -1
  16. package/dist/{message-tool-api-CPDfJJoE.js → message-tool-api-CvIgJpOo.js} +7 -4
  17. package/dist/message-tool-api.js +1 -1
  18. package/dist/{monitor-DnxJ8pW9.js → monitor-BywJn1QW.js} +343 -111
  19. package/dist/{question-reactions-DVe50UhB.js → question-reactions-viP7ZcyG.js} +2 -1
  20. package/dist/reaction-context-BAYI7pz0.js +105 -0
  21. package/dist/{remote-file-DLwO9ved.js → remote-file-6mCj2ZBy.js} +7 -3
  22. package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
  23. package/dist/runtime-Cza4CY5T.js +8 -0
  24. package/dist/runtime-api.js +10 -9
  25. package/dist/{sanitize-outbound-CoTZLXMw.js → sanitize-outbound-Du-r1lNQ.js} +345 -58
  26. package/dist/{send-BUXW76tw.js → send-mgWPKqSV.js} +147 -26
  27. package/dist/state-contract-4TK_cDDb.js +56 -0
  28. package/dist/state-migrations-SFdKp3GQ.js +277 -0
  29. package/node_modules/typebox/build/format/date.d.mts +1 -1
  30. package/node_modules/typebox/build/format/date.mjs +1 -1
  31. package/node_modules/typebox/build/format/date_time.d.mts +2 -2
  32. package/node_modules/typebox/build/format/date_time.mjs +3 -3
  33. package/node_modules/typebox/build/format/email.d.mts +1 -1
  34. package/node_modules/typebox/build/format/email.mjs +2 -2
  35. package/node_modules/typebox/build/format/hostname.mjs +2 -10
  36. package/node_modules/typebox/build/format/idn_email.d.mts +1 -1
  37. package/node_modules/typebox/build/format/idn_email.mjs +3 -3
  38. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -0
  39. package/node_modules/typebox/build/format/idn_hostname.mjs +3 -11
  40. package/node_modules/typebox/build/format/idna/format/bidi.d.mts +3 -0
  41. package/node_modules/typebox/build/format/idna/format/bidi.mjs +98 -0
  42. package/node_modules/typebox/build/format/idna/format/puny.d.mts +3 -0
  43. package/node_modules/typebox/build/format/idna/format/puny.mjs +144 -0
  44. package/node_modules/typebox/build/format/idna/hostname.d.mts +1 -0
  45. package/node_modules/typebox/build/format/idna/hostname.mjs +16 -0
  46. package/node_modules/typebox/build/format/idna/idn-hostname.d.mts +1 -0
  47. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +23 -0
  48. package/node_modules/typebox/build/format/idna/index.d.mts +2 -0
  49. package/node_modules/typebox/build/format/idna/index.mjs +2 -0
  50. package/node_modules/typebox/build/format/idna/label/ascii.d.mts +1 -0
  51. package/node_modules/typebox/build/format/idna/label/ascii.mjs +9 -0
  52. package/node_modules/typebox/build/format/idna/label/puny.d.mts +1 -0
  53. package/node_modules/typebox/build/format/idna/label/puny.mjs +28 -0
  54. package/node_modules/typebox/build/format/idna/label/unicode.d.mts +1 -0
  55. package/node_modules/typebox/build/format/idna/label/unicode.mjs +106 -0
  56. package/node_modules/typebox/build/format/idna/pattern/pattern.d.mts +30 -0
  57. package/node_modules/typebox/build/format/idna/pattern/pattern.mjs +70 -0
  58. package/node_modules/typebox/build/format/ipv4.d.mts +0 -1
  59. package/node_modules/typebox/build/format/ipv4.mjs +2 -32
  60. package/node_modules/typebox/build/format/ipv6.mjs +2 -61
  61. package/node_modules/typebox/build/format/iri.d.mts +2 -2
  62. package/node_modules/typebox/build/format/iri.mjs +26 -8
  63. package/node_modules/typebox/build/format/iri_reference.d.mts +2 -2
  64. package/node_modules/typebox/build/format/iri_reference.mjs +9 -55
  65. package/node_modules/typebox/build/format/regex.d.mts +1 -2
  66. package/node_modules/typebox/build/format/regex.mjs +2 -6
  67. package/node_modules/typebox/build/format/time.d.mts +2 -2
  68. package/node_modules/typebox/build/format/time.mjs +19 -14
  69. package/node_modules/typebox/build/format/uri.d.mts +1 -1
  70. package/node_modules/typebox/build/format/uri.mjs +3 -130
  71. package/node_modules/typebox/build/format/uri_reference.d.mts +1 -1
  72. package/node_modules/typebox/build/format/uri_reference.mjs +2 -3
  73. package/node_modules/typebox/build/format/uri_template.d.mts +2 -3
  74. package/node_modules/typebox/build/format/uri_template.mjs +4 -5
  75. package/node_modules/typebox/build/format/url.d.mts +3 -3
  76. package/node_modules/typebox/build/format/url.mjs +4 -5
  77. package/node_modules/typebox/build/guard/emit.d.mts +3 -1
  78. package/node_modules/typebox/build/guard/emit.mjs +12 -5
  79. package/node_modules/typebox/build/guard/guard.d.mts +8 -2
  80. package/node_modules/typebox/build/guard/guard.mjs +25 -4
  81. package/node_modules/typebox/build/guard/string.d.mts +2 -2
  82. package/node_modules/typebox/build/guard/string.mjs +7 -7
  83. package/node_modules/typebox/build/schema/engine/_context.mjs +3 -3
  84. package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -2
  85. package/node_modules/typebox/build/schema/engine/_regexp.d.mts +2 -0
  86. package/node_modules/typebox/build/schema/engine/_regexp.mjs +4 -0
  87. package/node_modules/typebox/build/schema/engine/_stack.mjs +12 -13
  88. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +17 -7
  89. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +4 -3
  90. package/node_modules/typebox/build/schema/engine/contains.mjs +18 -6
  91. package/node_modules/typebox/build/schema/engine/enum.mjs +1 -1
  92. package/node_modules/typebox/build/schema/engine/items.mjs +26 -6
  93. package/node_modules/typebox/build/schema/engine/maxContains.mjs +3 -3
  94. package/node_modules/typebox/build/schema/engine/minContains.mjs +14 -4
  95. package/node_modules/typebox/build/schema/engine/not.mjs +4 -2
  96. package/node_modules/typebox/build/schema/engine/oneOf.mjs +7 -6
  97. package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -2
  98. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +4 -3
  99. package/node_modules/typebox/build/schema/engine/ref.mjs +1 -7
  100. package/node_modules/typebox/build/schema/engine/schema.mjs +13 -4
  101. package/node_modules/typebox/build/schema/engine/type.mjs +1 -1
  102. package/node_modules/typebox/build/schema/resolve/resolve.d.mts +4 -1
  103. package/node_modules/typebox/build/schema/resolve/resolve.mjs +202 -1
  104. package/node_modules/typebox/build/schema/static/propertyNames.d.mts +5 -0
  105. package/node_modules/typebox/build/schema/static/propertyNames.mjs +3 -0
  106. package/node_modules/typebox/build/schema/static/schema.d.mts +3 -0
  107. package/node_modules/typebox/build/schema/types/index.d.mts +0 -2
  108. package/node_modules/typebox/build/schema/types/index.mjs +0 -2
  109. package/node_modules/typebox/build/system/memory/clone.mjs +14 -11
  110. package/node_modules/typebox/build/system/memory/discard.mjs +6 -5
  111. package/node_modules/typebox/build/system/settings/settings.d.mts +9 -0
  112. package/node_modules/typebox/build/system/settings/settings.mjs +2 -0
  113. package/node_modules/typebox/build/type/engine/call/distribute_arguments.d.mts +6 -1
  114. package/node_modules/typebox/build/type/engine/call/distribute_arguments.mjs +16 -3
  115. package/node_modules/typebox/build/type/engine/call/instantiate.mjs +34 -4
  116. package/node_modules/typebox/build/type/engine/evaluate/broaden.d.mts +5 -5
  117. package/node_modules/typebox/build/type/engine/evaluate/broaden.mjs +20 -26
  118. package/node_modules/typebox/build/type/engine/evaluate/compare.d.mts +6 -10
  119. package/node_modules/typebox/build/type/engine/evaluate/compare.mjs +9 -13
  120. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +7 -4
  121. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +12 -19
  122. package/node_modules/typebox/build/type/engine/evaluate/distribute.d.mts +4 -20
  123. package/node_modules/typebox/build/type/engine/evaluate/distribute.mjs +8 -20
  124. package/node_modules/typebox/build/type/engine/evaluate/evaluate.d.mts +4 -4
  125. package/node_modules/typebox/build/type/engine/evaluate/evaluate.mjs +11 -8
  126. package/node_modules/typebox/build/type/engine/evaluate/flatten.d.mts +1 -1
  127. package/node_modules/typebox/build/type/engine/evaluate/flatten.mjs +3 -4
  128. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +17 -2
  129. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +26 -6
  130. package/node_modules/typebox/build/type/engine/exclude/operation.mjs +3 -4
  131. package/node_modules/typebox/build/type/engine/extract/operation.d.mts +1 -1
  132. package/node_modules/typebox/build/type/engine/extract/operation.mjs +3 -4
  133. package/node_modules/typebox/build/type/engine/priority/priority.d.mts +2 -2
  134. package/node_modules/typebox/build/type/engine/priority/priority.mjs +3 -4
  135. package/node_modules/typebox/build/type/script/mapping.d.mts +32 -29
  136. package/node_modules/typebox/build/type/script/mapping.mjs +18 -25
  137. package/node_modules/typebox/build/type/script/parser.d.mts +19 -25
  138. package/node_modules/typebox/build/type/script/parser.mjs +27 -23
  139. package/node_modules/typebox/build/value/errors/errors.d.mts +2 -2
  140. package/node_modules/typebox/build/value/errors/errors.mjs +1 -1
  141. package/node_modules/typebox/package.json +27 -27
  142. package/node_modules/typebox/readme.md +79 -82
  143. package/openclaw.plugin.json +0 -6
  144. package/package.json +5 -5
  145. package/dist/catchup-D32oYNxR.js +0 -294
  146. package/dist/monitor-reply-cache-DKlYGd5x.js +0 -348
  147. package/dist/persisted-echo-cache-BPWGUK-L.js +0 -143
  148. package/node_modules/typebox/build/format/_idna.d.mts +0 -2
  149. package/node_modules/typebox/build/format/_idna.mjs +0 -246
  150. package/node_modules/typebox/build/format/_puny.d.mts +0 -1
  151. package/node_modules/typebox/build/format/_puny.mjs +0 -77
  152. package/node_modules/typebox/build/schema/resolve/ref.d.mts +0 -3
  153. package/node_modules/typebox/build/schema/resolve/ref.mjs +0 -146
  154. package/node_modules/typebox/build/schema/types/contentEncoding.d.mts +0 -9
  155. package/node_modules/typebox/build/schema/types/contentEncoding.mjs +0 -13
  156. package/node_modules/typebox/build/schema/types/contentMediaType.d.mts +0 -9
  157. package/node_modules/typebox/build/schema/types/contentMediaType.mjs +0 -13
@@ -0,0 +1,562 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
+ import { l as parseIMessageTarget, s as normalizeIMessageHandle } from "./message-tool-api-CvIgJpOo.js";
3
+ import { l as getIMessageApprovalApprovers, u as imessageApprovalAuth } from "./group-policy-Dvc-BcWo.js";
4
+ import { n as getOptionalIMessageRuntime } from "./runtime-Cza4CY5T.js";
5
+ import { t as resolveIMessageReactionContext } from "./reaction-context-BAYI7pz0.js";
6
+ import { createLazyRuntimeSurface } from "openclaw/plugin-sdk/lazy-runtime";
7
+ import { asOptionalRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
8
+ import { asDateTimestampMs, isFutureDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
9
+ import { isApprovalNotFoundError } from "openclaw/plugin-sdk/error-runtime";
10
+ import { addApprovalReactionHintToText, approvalReactionDecisionSetsMatch, buildApprovalReactionDeliveredBindingMarker, buildApprovalReactionHint, createApprovalReactionTargetStore, listApprovalReactionBindings, normalizeApprovalReactionDecision, readApprovalReactionDecisionList, readApprovalReactionDeliveredBinding, readApprovalReactionPresentationBinding, resolveTypedApprovalReactionTarget } from "openclaw/plugin-sdk/approval-reaction-runtime";
11
+ import { createPluginStateErrorReporter } from "openclaw/plugin-sdk/plugin-state-runtime";
12
+ //#region extensions/imessage/src/approval-target-keys.ts
13
+ /** Strip the `p:<n>/` part prefix Messages puts on some GUIDs so keys match. */
14
+ function normalizeIMessageGuid(value) {
15
+ return value.trim().replace(/^p:\d+\//iu, "");
16
+ }
17
+ function chatIdToKeyValue(chatId) {
18
+ if (chatId == null || chatId === "") return null;
19
+ if (typeof chatId === "number") return Number.isFinite(chatId) && chatId > 0 ? String(chatId) : null;
20
+ return chatId.trim() || null;
21
+ }
22
+ function enumerateConversationKeyForms(conversation) {
23
+ const forms = [];
24
+ const chatGuid = conversation.chatGuid?.trim();
25
+ if (chatGuid) forms.push(`chat_guid:${chatGuid}`);
26
+ const chatIdentifier = conversation.chatIdentifier?.trim();
27
+ if (chatIdentifier) forms.push(`chat_identifier:${chatIdentifier}`);
28
+ const chatIdValue = chatIdToKeyValue(conversation.chatId);
29
+ if (chatIdValue) forms.push(`chat_id:${chatIdValue}`);
30
+ const handle = conversation.handle?.trim();
31
+ if (handle) forms.push(`handle:${handle}`);
32
+ return forms;
33
+ }
34
+ function normalizeConversationKey(conversation) {
35
+ return enumerateConversationKeyForms(conversation)[0];
36
+ }
37
+ /**
38
+ * Index a binding under every key derivable from the conversation. Outbound and
39
+ * inbound disagree about which key exists: send may only know
40
+ * `{handle: "+1..."}` for a DM, while the bridge populates chat_guid on the
41
+ * inbound event. Enumerating all forms keeps the two symmetric without making
42
+ * callers guess which one the bridge will pick.
43
+ */
44
+ function enumerateApprovalTargetKeys(params) {
45
+ const accountId = params.accountId.trim();
46
+ const messageId = params.messageId.trim();
47
+ if (!accountId || !messageId) return [];
48
+ return enumerateConversationKeyForms(params.conversation).map((form) => `${accountId}:${form}:${messageId}`);
49
+ }
50
+ function buildIMessageApprovalConversationKeyForTarget(to) {
51
+ try {
52
+ const target = parseIMessageTarget(to);
53
+ if (target.kind === "chat_id") return { chatId: target.chatId };
54
+ if (target.kind === "chat_guid") return { chatGuid: target.chatGuid };
55
+ if (target.kind === "chat_identifier") return { chatIdentifier: target.chatIdentifier };
56
+ const handle = normalizeIMessageHandle(target.to);
57
+ return handle ? { handle } : null;
58
+ } catch {
59
+ return null;
60
+ }
61
+ }
62
+ /** Conversation key for an inbound event, mirroring the outbound key forms. */
63
+ function buildIMessageApprovalConversationKeyForInbound(params) {
64
+ return {
65
+ ...params.chatGuid?.trim() ? { chatGuid: params.chatGuid.trim() } : {},
66
+ ...params.chatIdentifier?.trim() ? { chatIdentifier: params.chatIdentifier.trim() } : {},
67
+ ...chatIdToKeyValue(params.chatId ?? void 0) ? { chatId: params.chatId } : {},
68
+ ...params.isGroup ? {} : { handle: params.actorHandle }
69
+ };
70
+ }
71
+ //#endregion
72
+ //#region extensions/imessage/src/approval-reaction-poll-targets.ts
73
+ const PERSISTENT_POLL_TARGET_NAMESPACE = "imessage.approval-reaction-poll-targets";
74
+ const PERSISTENT_MAX_ENTRIES$1 = 1e3;
75
+ const DEFAULT_REACTION_TARGET_TTL_MS$1 = 1440 * 60 * 1e3;
76
+ const pendingReactionPollTargets = /* @__PURE__ */ new Map();
77
+ function prunePendingReactionPollTargets(nowMs = Date.now()) {
78
+ for (const [key, target] of pendingReactionPollTargets.entries()) if (!isFutureDateTimestampMs(target.expiresAtMs, { nowMs })) pendingReactionPollTargets.delete(key);
79
+ }
80
+ function resolvePendingReactionPollExpiry(ttlMs) {
81
+ const nowMs = asDateTimestampMs(Date.now());
82
+ if (nowMs === void 0) return;
83
+ const expiresAtMs = resolveExpiresAtMsFromDurationMs(ttlMs ?? DEFAULT_REACTION_TARGET_TTL_MS$1, { nowMs }) ?? resolveExpiresAtMsFromDurationMs(DEFAULT_REACTION_TARGET_TTL_MS$1, { nowMs });
84
+ if (expiresAtMs === void 0) return;
85
+ return {
86
+ ttlMs: expiresAtMs - nowMs,
87
+ expiresAtMs
88
+ };
89
+ }
90
+ function mergePollTargetConversation(left, right) {
91
+ return {
92
+ chatGuid: left.chatGuid ?? right.chatGuid,
93
+ chatIdentifier: left.chatIdentifier ?? right.chatIdentifier,
94
+ chatId: left.chatId ?? right.chatId,
95
+ handle: left.handle ?? right.handle
96
+ };
97
+ }
98
+ const reportPersistentApprovalReactionError$1 = createPluginStateErrorReporter(getOptionalIMessageRuntime, "imessage", "approval-reaction-state", "iMessage persistent approval reaction state failed");
99
+ let pendingReactionPollTargetStore;
100
+ let pendingReactionPollTargetStoreDisabled = false;
101
+ function disablePendingReactionPollTargetStore(error) {
102
+ pendingReactionPollTargetStoreDisabled = true;
103
+ pendingReactionPollTargetStore = void 0;
104
+ reportPersistentApprovalReactionError$1(error);
105
+ }
106
+ function getPendingReactionPollTargetStore() {
107
+ if (pendingReactionPollTargetStoreDisabled) return;
108
+ if (pendingReactionPollTargetStore) return pendingReactionPollTargetStore;
109
+ try {
110
+ pendingReactionPollTargetStore = getOptionalIMessageRuntime()?.state.openKeyedStore({
111
+ namespace: PERSISTENT_POLL_TARGET_NAMESPACE,
112
+ maxEntries: PERSISTENT_MAX_ENTRIES$1,
113
+ defaultTtlMs: DEFAULT_REACTION_TARGET_TTL_MS$1
114
+ });
115
+ return pendingReactionPollTargetStore;
116
+ } catch (error) {
117
+ disablePendingReactionPollTargetStore(error);
118
+ return;
119
+ }
120
+ }
121
+ function readPersistedPollTarget(value) {
122
+ const target = asOptionalRecord(value);
123
+ if (!target) return null;
124
+ const accountId = typeof target.accountId === "string" ? target.accountId.trim() : "";
125
+ const messageId = typeof target.messageId === "string" ? target.messageId.trim() : "";
126
+ const approvalId = typeof target.approvalId === "string" ? target.approvalId.trim() : "";
127
+ const expiresAtMs = asDateTimestampMs(target.expiresAtMs);
128
+ const allowedDecisions = readApprovalReactionDecisionList(target.allowedDecisions);
129
+ const rawConversation = asOptionalRecord(target.conversation) ?? {};
130
+ const conversation = {
131
+ ...typeof rawConversation.chatGuid === "string" ? { chatGuid: rawConversation.chatGuid.trim() } : {},
132
+ ...typeof rawConversation.chatIdentifier === "string" ? { chatIdentifier: rawConversation.chatIdentifier.trim() } : {},
133
+ ...typeof rawConversation.chatId === "string" || typeof rawConversation.chatId === "number" ? { chatId: rawConversation.chatId } : {},
134
+ ...typeof rawConversation.handle === "string" ? { handle: rawConversation.handle.trim() } : {}
135
+ };
136
+ if (!accountId || !messageId || !approvalId || expiresAtMs === void 0 || !allowedDecisions || target.approvalKind !== "exec" && target.approvalKind !== "plugin" || !normalizeConversationKey(conversation)) return null;
137
+ return {
138
+ accountId,
139
+ conversation,
140
+ messageId,
141
+ approvalId,
142
+ approvalKind: target.approvalKind,
143
+ allowedDecisions,
144
+ expiresAtMs
145
+ };
146
+ }
147
+ function recordIMessageApprovalReactionPollTarget(params) {
148
+ const expiry = resolvePendingReactionPollExpiry(params.ttlMs);
149
+ if (!expiry || params.keys.length === 0) return null;
150
+ const target = {
151
+ accountId: params.accountId,
152
+ conversation: params.conversation,
153
+ messageId: params.messageId,
154
+ approvalId: params.approvalId,
155
+ approvalKind: params.approvalKind,
156
+ allowedDecisions: params.allowedDecisions,
157
+ expiresAtMs: expiry.expiresAtMs
158
+ };
159
+ const store = getPendingReactionPollTargetStore();
160
+ for (const key of params.keys) {
161
+ pendingReactionPollTargets.set(key, target);
162
+ store?.register(key, target, { ttlMs: expiry.ttlMs }).catch(disablePendingReactionPollTargetStore);
163
+ }
164
+ prunePendingReactionPollTargets();
165
+ return expiry;
166
+ }
167
+ function deleteIMessageApprovalReactionPollTargets(keys) {
168
+ const store = getPendingReactionPollTargetStore();
169
+ for (const key of keys) {
170
+ pendingReactionPollTargets.delete(key);
171
+ store?.delete(key).catch(disablePendingReactionPollTargetStore);
172
+ }
173
+ }
174
+ async function listPendingIMessageApprovalReactionPollTargets(params) {
175
+ const accountId = params.accountId.trim();
176
+ if (!accountId) return [];
177
+ const nowMs = Date.now();
178
+ const store = getPendingReactionPollTargetStore();
179
+ if (store) try {
180
+ for (const entry of await store.entries()) {
181
+ const target = readPersistedPollTarget(entry.value);
182
+ if (!target || !isFutureDateTimestampMs(target.expiresAtMs, { nowMs })) {
183
+ await store.delete(entry.key);
184
+ continue;
185
+ }
186
+ pendingReactionPollTargets.set(entry.key, target);
187
+ }
188
+ } catch (error) {
189
+ disablePendingReactionPollTargetStore(error);
190
+ }
191
+ prunePendingReactionPollTargets(nowMs);
192
+ const targetByApprovalAndMessage = /* @__PURE__ */ new Map();
193
+ for (const target of pendingReactionPollTargets.values()) {
194
+ if (target.accountId !== accountId) continue;
195
+ const key = `${target.approvalId}:${normalizeIMessageGuid(target.messageId)}`;
196
+ const existing = targetByApprovalAndMessage.get(key);
197
+ if (!existing) {
198
+ targetByApprovalAndMessage.set(key, target);
199
+ continue;
200
+ }
201
+ targetByApprovalAndMessage.set(key, {
202
+ ...existing,
203
+ conversation: mergePollTargetConversation(existing.conversation, target.conversation),
204
+ expiresAtMs: Math.max(existing.expiresAtMs, target.expiresAtMs)
205
+ });
206
+ }
207
+ return [...targetByApprovalAndMessage.values()];
208
+ }
209
+ function clearIMessageApprovalReactionPollTargetsForTest() {
210
+ pendingReactionPollTargets.clear();
211
+ pendingReactionPollTargetStore = void 0;
212
+ pendingReactionPollTargetStoreDisabled = false;
213
+ }
214
+ //#endregion
215
+ //#region extensions/imessage/src/approval-reactions.ts
216
+ var approval_reactions_exports = /* @__PURE__ */ __exportAll({
217
+ addIMessageApprovalReactionHintToStructuredPayload: () => addIMessageApprovalReactionHintToStructuredPayload,
218
+ buildIMessageApprovalConversationKeyForTarget: () => buildIMessageApprovalConversationKeyForTarget,
219
+ clearIMessageApprovalReactionTargetsForTest: () => clearIMessageApprovalReactionTargetsForTest,
220
+ handleIMessageApprovalReaction: () => handleIMessageApprovalReaction,
221
+ maybeResolveIMessageApprovalReaction: () => maybeResolveIMessageApprovalReaction,
222
+ registerIMessageApprovalReactionTarget: () => registerIMessageApprovalReactionTarget,
223
+ registerIMessageApprovalReactionTargetForDeliveredPayload: () => registerIMessageApprovalReactionTargetForDeliveredPayload,
224
+ resolveIMessageApprovalReactionTargetWithPersistence: () => resolveIMessageApprovalReactionTargetWithPersistence,
225
+ unregisterIMessageApprovalReactionTarget: () => unregisterIMessageApprovalReactionTarget
226
+ });
227
+ const PERSISTENT_NAMESPACE = "imessage.approval-reactions";
228
+ const PERSISTENT_MAX_ENTRIES = 1e3;
229
+ const DEFAULT_REACTION_TARGET_TTL_MS = 1440 * 60 * 1e3;
230
+ const loadResolveApprovalOverGateway = createLazyRuntimeSurface(() => import("openclaw/plugin-sdk/approval-gateway-runtime"), (runtime) => runtime.resolveApprovalOverGateway);
231
+ const reportPersistentApprovalReactionError = createPluginStateErrorReporter(getOptionalIMessageRuntime, "imessage", "approval-reaction-state", "iMessage persistent approval reaction state failed");
232
+ function reportApprovalBindingCorrelationMismatch(binding) {
233
+ try {
234
+ getOptionalIMessageRuntime()?.logging.getChildLogger({
235
+ plugin: "imessage",
236
+ feature: "approval-reaction-state"
237
+ }).warn("iMessage approval prompt text failed binding correlation; tapbacks disabled", {
238
+ approvalId: binding.approvalId,
239
+ approvalKind: binding.approvalKind
240
+ });
241
+ } catch {}
242
+ }
243
+ function readPersistedTarget(value) {
244
+ const target = value;
245
+ if (!target || typeof target.approvalId !== "string" || target.approvalKind !== "exec" && target.approvalKind !== "plugin") return null;
246
+ const allowedDecisions = readApprovalReactionDecisionList(target.allowedDecisions);
247
+ if (!allowedDecisions) return null;
248
+ return {
249
+ approvalId: target.approvalId,
250
+ approvalKind: target.approvalKind,
251
+ allowedDecisions
252
+ };
253
+ }
254
+ const imessageApprovalReactionTargets = createApprovalReactionTargetStore({
255
+ namespace: PERSISTENT_NAMESPACE,
256
+ maxEntries: PERSISTENT_MAX_ENTRIES,
257
+ defaultTtlMs: DEFAULT_REACTION_TARGET_TTL_MS,
258
+ openStore: (params) => getOptionalIMessageRuntime()?.state.openKeyedStore(params),
259
+ logPersistentError: reportPersistentApprovalReactionError,
260
+ readPersistedTarget
261
+ });
262
+ const IMESSAGE_APPROVAL_DELIVERY_BINDING_KEY = "imessageApprovalReactionBindingV1";
263
+ function visibleApprovalBindingMatches(text, binding, options) {
264
+ if (!text) return false;
265
+ const lines = text.split(/\r?\n/).map((line) => line.replace(/\*\*/g, "").trim());
266
+ const normalizedHeaders = lines.map((line) => line.replace(/^[^A-Za-z0-9]*/, ""));
267
+ const hasKindHeader = binding.approvalKind === "exec" ? lines.includes("Approval required.") || normalizedHeaders.some((line) => /^Exec approval required$/i.test(line)) : normalizedHeaders.some((line) => /^Plugin approval required$/i.test(line));
268
+ const hasId = lines.includes(`ID: ${binding.approvalId}`) || lines.includes(`Full id: \`${binding.approvalId}\``) || lines.includes(`Full id: ${binding.approvalId}`);
269
+ if (!hasKindHeader || !hasId) return false;
270
+ const visibleDecisions = [];
271
+ for (const line of lines) {
272
+ const match = line.match(APPROVE_COMMAND_LINE_RE);
273
+ const approvalId = match?.[1];
274
+ const decisionsText = match?.[2];
275
+ if (!approvalId || !decisionsText || approvalId !== binding.approvalId && approvalId !== binding.approvalSlug) continue;
276
+ for (const token of decisionsText.split(/[\s|,]+/)) {
277
+ const decision = normalizeApprovalReactionDecision(token);
278
+ if (decision && !visibleDecisions.includes(decision)) visibleDecisions.push(decision);
279
+ }
280
+ }
281
+ if (!approvalReactionDecisionSetsMatch(binding.allowedDecisions, visibleDecisions)) return false;
282
+ if (!options.requireReactionHint) return true;
283
+ const hint = buildApprovalReactionHint({ allowedDecisions: binding.allowedDecisions });
284
+ return Boolean(hint && text.includes(hint));
285
+ }
286
+ /** Preserve a validated typed approval binding until the iMessage GUID is known. */
287
+ function addIMessageApprovalReactionHintToStructuredPayload(params) {
288
+ const metadata = readApprovalReactionPresentationBinding({
289
+ payload: params.payload,
290
+ requireApprovalSlug: true,
291
+ trimApprovalId: true
292
+ });
293
+ const text = params.payload.text;
294
+ if (metadata?.approvalKind !== params.approvalKind || !text) return null;
295
+ if (!visibleApprovalBindingMatches(text, metadata, { requireReactionHint: false })) {
296
+ reportApprovalBindingCorrelationMismatch(metadata);
297
+ return null;
298
+ }
299
+ return {
300
+ ...params.payload,
301
+ text: addApprovalReactionHintToText({
302
+ text,
303
+ allowedDecisions: metadata.allowedDecisions
304
+ }),
305
+ channelData: {
306
+ ...params.payload.channelData,
307
+ [IMESSAGE_APPROVAL_DELIVERY_BINDING_KEY]: buildApprovalReactionDeliveredBindingMarker({
308
+ approvalId: metadata.approvalId,
309
+ approvalSlug: metadata.approvalSlug,
310
+ approvalKind: metadata.approvalKind,
311
+ allowedDecisions: metadata.allowedDecisions
312
+ })
313
+ }
314
+ };
315
+ }
316
+ const APPROVE_COMMAND_LINE_RE = /\/approve(?:@[^\s]+)?\s+([A-Za-z0-9][A-Za-z0-9._:-]*)\s+(.+)$/i;
317
+ function registerIMessageApprovalReactionTarget(params) {
318
+ const accountId = params.accountId.trim();
319
+ const messageId = params.messageId.trim();
320
+ const approvalId = params.approvalId.trim();
321
+ const allowedDecisions = listApprovalReactionBindings({ allowedDecisions: params.allowedDecisions }).map((binding) => binding.decision);
322
+ if (!accountId || !messageId || !approvalId || params.approvalKind !== "exec" && params.approvalKind !== "plugin" || allowedDecisions.length === 0) return null;
323
+ const target = {
324
+ approvalId,
325
+ approvalKind: params.approvalKind,
326
+ allowedDecisions
327
+ };
328
+ const keys = enumerateApprovalTargetKeys({
329
+ accountId,
330
+ conversation: params.conversation,
331
+ messageId
332
+ });
333
+ if (keys.length === 0) return null;
334
+ const expiry = recordIMessageApprovalReactionPollTarget({
335
+ keys,
336
+ accountId,
337
+ conversation: params.conversation,
338
+ messageId,
339
+ approvalId,
340
+ approvalKind: params.approvalKind,
341
+ allowedDecisions,
342
+ ttlMs: params.ttlMs
343
+ });
344
+ if (!expiry) return null;
345
+ for (const key of keys) imessageApprovalReactionTargets.register(key, target, { ttlMs: expiry.ttlMs });
346
+ return target;
347
+ }
348
+ function listDeliveredIMessageApprovalGuids(params) {
349
+ const deliveries = [];
350
+ const seen = /* @__PURE__ */ new Set();
351
+ for (const result of params.results) {
352
+ if (result.channel !== "imessage") continue;
353
+ const guid = typeof result.meta?.imessageMessageGuid === "string" ? result.meta.imessageMessageGuid.trim() : "";
354
+ const visibleText = result.meta?.imessageVisibleText;
355
+ if (!guid || /^\d+$/.test(guid) || seen.has(guid) || typeof visibleText !== "string") continue;
356
+ seen.add(guid);
357
+ deliveries.push({
358
+ guid,
359
+ visibleText
360
+ });
361
+ }
362
+ if (!visibleApprovalBindingMatches(deliveries.map((delivery) => delivery.visibleText).join("\n"), params.binding, { requireReactionHint: true })) {
363
+ if (params.results.some((result) => result.channel === "imessage")) reportApprovalBindingCorrelationMismatch(params.binding);
364
+ return [];
365
+ }
366
+ return deliveries.map((delivery) => delivery.guid);
367
+ }
368
+ /** Bind a typed forwarded approval after iMessage returns the stable tapback GUID. */
369
+ function registerIMessageApprovalReactionTargetForDeliveredPayload(params) {
370
+ if (params.target.channel.trim().toLowerCase() !== "imessage") return false;
371
+ const binding = readApprovalReactionDeliveredBinding({
372
+ payload: params.payload,
373
+ channelDataKey: IMESSAGE_APPROVAL_DELIVERY_BINDING_KEY,
374
+ requireApprovalSlug: true,
375
+ trimApprovalId: true
376
+ });
377
+ if (!binding) return false;
378
+ const conversation = buildIMessageApprovalConversationKeyForTarget(params.target.to);
379
+ if (!conversation) return false;
380
+ let registered = false;
381
+ for (const messageId of listDeliveredIMessageApprovalGuids({
382
+ binding,
383
+ results: params.results
384
+ })) registered = Boolean(registerIMessageApprovalReactionTarget({
385
+ accountId: params.accountId,
386
+ conversation,
387
+ messageId,
388
+ approvalId: binding.approvalId,
389
+ approvalKind: binding.approvalKind,
390
+ allowedDecisions: binding.allowedDecisions,
391
+ ttlMs: params.ttlMs
392
+ })) || registered;
393
+ return registered;
394
+ }
395
+ function unregisterIMessageApprovalReactionTarget(params) {
396
+ const keys = enumerateApprovalTargetKeys(params);
397
+ for (const key of keys) imessageApprovalReactionTargets.delete(key);
398
+ deleteIMessageApprovalReactionPollTargets(keys);
399
+ }
400
+ function resolveTarget(params) {
401
+ const target = resolveTypedApprovalReactionTarget(params);
402
+ return target ? {
403
+ approvalId: target.approvalId,
404
+ approvalKind: target.approvalKind,
405
+ decision: target.decision
406
+ } : null;
407
+ }
408
+ function formatCanonicalApprovalTerminalState(approval) {
409
+ const decision = approval.status === "allowed" || approval.status === "denied" ? ` decision=${approval.decision}` : "";
410
+ return `status=${approval.status}${decision} reason=${approval.reason}`;
411
+ }
412
+ async function resolveIMessageApprovalReactionTargetWithPersistence(params) {
413
+ const keys = enumerateApprovalTargetKeys(params);
414
+ for (const key of keys) {
415
+ const target = resolveTarget({
416
+ target: await imessageApprovalReactionTargets.lookup(key),
417
+ reactionKey: params.reactionKey
418
+ });
419
+ if (target) return target;
420
+ }
421
+ return null;
422
+ }
423
+ function readApprovalReactionEvent(message, bodyText) {
424
+ const reaction = resolveIMessageReactionContext(message, bodyText);
425
+ if (!reaction) return null;
426
+ const reactionKey = reaction.emoji.trim();
427
+ const candidates = (reaction.targetGuids ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
428
+ const primary = reaction.targetGuid?.trim() || candidates[0] || "";
429
+ const messageIdCandidates = candidates.length > 0 ? candidates : primary ? [primary] : [];
430
+ const actorHandle = normalizeIMessageHandle((message.sender ?? "").trim());
431
+ if (!reactionKey || !primary || !actorHandle) return null;
432
+ const conversation = buildIMessageApprovalConversationKeyForInbound({
433
+ chatGuid: message.chat_guid,
434
+ chatIdentifier: message.chat_identifier,
435
+ chatId: message.chat_id,
436
+ isGroup: message.is_group,
437
+ actorHandle
438
+ });
439
+ if (!normalizeConversationKey(conversation)) return null;
440
+ return {
441
+ conversation,
442
+ messageId: primary,
443
+ messageIdCandidates,
444
+ actorHandle,
445
+ reactionKey,
446
+ action: reaction.action
447
+ };
448
+ }
449
+ async function handleIMessageApprovalReaction(params) {
450
+ const event = readApprovalReactionEvent(params.message, params.bodyText);
451
+ if (!event) return {
452
+ handled: false,
453
+ stopPolling: false
454
+ };
455
+ if (event.action === "removed") return {
456
+ handled: false,
457
+ stopPolling: false
458
+ };
459
+ let target = null;
460
+ let matchedMessageId = null;
461
+ for (const candidate of event.messageIdCandidates) {
462
+ target = await resolveIMessageApprovalReactionTargetWithPersistence({
463
+ accountId: params.accountId,
464
+ conversation: event.conversation,
465
+ messageId: candidate,
466
+ reactionKey: event.reactionKey
467
+ });
468
+ if (target) {
469
+ matchedMessageId = candidate;
470
+ break;
471
+ }
472
+ }
473
+ if (!target) return {
474
+ handled: false,
475
+ stopPolling: false
476
+ };
477
+ if (getIMessageApprovalApprovers({
478
+ cfg: params.cfg,
479
+ accountId: params.accountId
480
+ }).length === 0) {
481
+ params.logVerboseMessage?.(`imessage: approval reaction denied id=${target.approvalId}; reactions require explicit approvers`);
482
+ return {
483
+ handled: true,
484
+ stopPolling: false
485
+ };
486
+ }
487
+ if (!imessageApprovalAuth.authorizeActorAction({
488
+ cfg: params.cfg,
489
+ accountId: params.accountId,
490
+ senderId: event.actorHandle,
491
+ action: "approve",
492
+ approvalKind: target.approvalKind
493
+ }).authorized) {
494
+ params.logVerboseMessage?.(`imessage: approval reaction denied id=${target.approvalId} sender=${event.actorHandle}`);
495
+ return {
496
+ handled: true,
497
+ stopPolling: false
498
+ };
499
+ }
500
+ const resolveApprovalOverGateway = await loadResolveApprovalOverGateway();
501
+ try {
502
+ const result = await resolveApprovalOverGateway({
503
+ cfg: params.cfg,
504
+ approvalId: target.approvalId,
505
+ approvalKind: target.approvalKind,
506
+ decision: target.decision,
507
+ channel: "imessage",
508
+ accountId: params.accountId,
509
+ senderId: event.actorHandle,
510
+ gatewayUrl: params.gatewayUrl,
511
+ ...params.gatewayRuntime ? { gatewayRuntime: params.gatewayRuntime } : {}
512
+ });
513
+ for (const candidate of event.messageIdCandidates) unregisterIMessageApprovalReactionTarget({
514
+ accountId: params.accountId,
515
+ conversation: event.conversation,
516
+ messageId: candidate
517
+ });
518
+ const outcome = result.applied ? "resolved" : "already resolved";
519
+ params.logVerboseMessage?.(`imessage: approval reaction ${outcome} id=${target.approvalId} sender=${event.actorHandle} ${formatCanonicalApprovalTerminalState(result.approval)} via messageId=${matchedMessageId ?? event.messageId}`);
520
+ return {
521
+ handled: true,
522
+ stopPolling: true,
523
+ stopPollingReason: "resolved"
524
+ };
525
+ } catch (error) {
526
+ if (isApprovalNotFoundError(error)) {
527
+ for (const candidate of event.messageIdCandidates) unregisterIMessageApprovalReactionTarget({
528
+ accountId: params.accountId,
529
+ conversation: event.conversation,
530
+ messageId: candidate
531
+ });
532
+ params.logVerboseMessage?.(`imessage: approval reaction ignored for expired approval id=${target.approvalId} sender=${event.actorHandle}`);
533
+ return {
534
+ handled: true,
535
+ stopPolling: true,
536
+ stopPollingReason: "not-found"
537
+ };
538
+ }
539
+ try {
540
+ getOptionalIMessageRuntime()?.logging.getChildLogger({
541
+ plugin: "imessage",
542
+ feature: "approval-reactions"
543
+ }).warn("approval reaction failed", {
544
+ approvalId: target.approvalId,
545
+ senderId: event.actorHandle,
546
+ error: String(error)
547
+ });
548
+ } catch {}
549
+ params.logVerboseMessage?.(`imessage: approval reaction failed id=${target.approvalId} sender=${event.actorHandle}: ${String(error)}`);
550
+ throw error;
551
+ }
552
+ }
553
+ async function maybeResolveIMessageApprovalReaction(params) {
554
+ return (await handleIMessageApprovalReaction(params)).handled;
555
+ }
556
+ function clearIMessageApprovalReactionTargetsForTest() {
557
+ imessageApprovalReactionTargets.clearForTest();
558
+ clearIMessageApprovalReactionPollTargetsForTest();
559
+ loadResolveApprovalOverGateway.clear();
560
+ }
561
+ //#endregion
562
+ export { unregisterIMessageApprovalReactionTarget as a, buildIMessageApprovalConversationKeyForTarget as c, normalizeConversationKey as d, normalizeIMessageGuid as f, registerIMessageApprovalReactionTarget as i, enumerateApprovalTargetKeys as l, handleIMessageApprovalReaction as n, listPendingIMessageApprovalReactionPollTargets as o, maybeResolveIMessageApprovalReaction as r, buildIMessageApprovalConversationKeyForInbound as s, approval_reactions_exports as t, enumerateConversationKeyForms as u };