@openclaw/imessage 2026.8.1-beta.2 → 2026.8.1-beta.3

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.
@@ -1,8 +1,8 @@
1
1
  import { a as resolveDefaultIMessageAccountId, l as resolveIMessageRemoteHost, n as hasExclusiveIMessageLocalDatabase, o as resolveIMessageAccount, u as expandIMessageUserPath } from "./accounts-D00Njxg2.js";
2
- import { S as IMESSAGE_ACTION_NAMES, _ as normalizeBareIMessageChatIdentifier, b as setCachedIMessagePrivateApiStatus, l as parseIMessageTarget, s as normalizeIMessageHandle$1, t as describeIMessageMessageTool, v as getCachedIMessagePrivateApiStatus, x as IMESSAGE_ACTIONS, y as imessageRpcSupportsMethod } from "./message-tool-api-CPDfJJoE.js";
3
- import { a as findLatestIMessageEntryForChat, c as rememberIMessageReplyCache, h as chatContextFromIMessageTarget, o as isIMessageCurrentMessageInChat, p as getOptionalIMessageRuntime } from "./monitor-reply-cache-DKlYGd5x.js";
4
- import { o as createIMessageRpcClient } from "./sanitize-outbound-CoTZLXMw.js";
5
- import { createLazyRuntimeNamedExport, createLazyRuntimeSurface } from "openclaw/plugin-sdk/lazy-runtime";
2
+ import { C as IMESSAGE_ACTION_NAMES, S as IMESSAGE_ACTIONS, _ as isIMessagePhoneLikeHandle, b as imessageRpcSupportsMethod, l as parseIMessageTarget, s as normalizeIMessageHandle$1, t as describeIMessageMessageTool, v as normalizeBareIMessageChatIdentifier, x as setCachedIMessagePrivateApiStatus, y as getCachedIMessagePrivateApiStatus } from "./message-tool-api-TZiNYA17.js";
3
+ import { a as findLatestIMessageEntryForChat, c as rememberIMessageReplyCache, h as chatContextFromIMessageTarget, o as isIMessageCurrentMessageInChat } from "./monitor-reply-cache-BdeUQaHO.js";
4
+ import { o as createIMessageRpcClient } from "./sanitize-outbound-DaLmdkbf.js";
5
+ import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
6
6
  import { collectStatusIssuesFromLastError } from "openclaw/plugin-sdk/status-helpers";
7
7
  import path from "node:path";
8
8
  import { normalizeE164 } from "openclaw/plugin-sdk/account-resolution";
@@ -12,38 +12,23 @@ import { readBooleanParam } from "openclaw/plugin-sdk/boolean-param";
12
12
  import { createActionGate, jsonResult, readNonNegativeIntegerParam, readPositiveIntegerParam, readReactionParams, readStringArrayParam, readStringParam } from "openclaw/plugin-sdk/channel-actions";
13
13
  import { normalizePollInput } from "openclaw/plugin-sdk/poll-runtime";
14
14
  import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
15
- import { asDateTimestampMs, isFutureDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
15
+ import { asDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
16
16
  import { runCommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
17
17
  import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
18
18
  import { detectBinary } from "openclaw/plugin-sdk/setup";
19
19
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
20
- import { isApprovalNotFoundError } from "openclaw/plugin-sdk/error-runtime";
21
20
  import { parseAllowFromEntries } from "openclaw/plugin-sdk/allow-from";
22
21
  import { createChannelDmPolicy } from "openclaw/plugin-sdk/channel-dm-policy";
23
22
  import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
24
23
  import { createCliPathTextInput, createDelegatedSetupWizardProxy, createDelegatedTextInputShouldPrompt, createPatchedAccountSetupAdapter, createSetupTranslator, promptParsedAllowFromForAccount, setAccountAllowFromForChannel, setSetupChannelEnabled as setSetupChannelEnabled$1 } from "openclaw/plugin-sdk/setup-runtime";
25
24
  import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
26
25
  import { createChannelApprovalAuth } from "openclaw/plugin-sdk/approval-auth-runtime";
27
- import { addApprovalReactionHintToText, approvalReactionDecisionSetsMatch, buildApprovalReactionHint, createApprovalReactionTargetStore, extractApprovalReactionPromptBinding, hasApprovalReactionHintText, listApprovalReactionBindings, normalizeApprovalReactionDecision, readApprovalReactionDeliveredBinding, readApprovalReactionPresentationBinding, resolveTypedApprovalReactionTarget } from "openclaw/plugin-sdk/approval-reaction-runtime";
28
- import { createPluginStateErrorReporter } from "openclaw/plugin-sdk/plugin-state-runtime";
29
26
  import { formatTrimmedAllowFromEntries as formatTrimmedAllowFromEntries$1 } from "openclaw/plugin-sdk/channel-config-helpers";
30
27
  import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
31
28
  import { DEFAULT_ACCOUNT_ID, getChatChannelMeta } from "openclaw/plugin-sdk/core";
32
29
  import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/media-runtime";
33
30
  import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
34
31
  import { buildChannelGroupsScopeTree, resolveScopeRequireMention, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
35
- //#region \0rolldown/runtime.js
36
- var __defProp = Object.defineProperty;
37
- var __exportAll = (all, no_symbols) => {
38
- let target = {};
39
- for (var name in all) __defProp(target, name, {
40
- get: all[name],
41
- enumerable: true
42
- });
43
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
44
- return target;
45
- };
46
- //#endregion
47
32
  //#region extensions/imessage/src/message-action-reference.ts
48
33
  async function resolveAuthorizedIMessageActionReference(params) {
49
34
  const options = {
@@ -540,7 +525,7 @@ async function probeIMessage(timeoutMs, opts = {}) {
540
525
  }
541
526
  //#endregion
542
527
  //#region extensions/imessage/src/actions.ts
543
- const loadIMessageActionsRuntime = createLazyRuntimeNamedExport(() => import("./actions.runtime-DTFKDN5p.js"), "imessageActionsRuntime");
528
+ const loadIMessageActionsRuntime = createLazyRuntimeNamedExport(() => import("./actions.runtime-B_xJiBXF.js"), "imessageActionsRuntime");
544
529
  const log = createSubsystemLogger("channels/imessage");
545
530
  const providerId = "imessage";
546
531
  const SUPPORTED_ACTIONS = /* @__PURE__ */ new Set([...IMESSAGE_ACTION_NAMES, "upload-file"]);
@@ -1213,608 +1198,6 @@ const imessageApproval = createChannelApprovalAuth({
1213
1198
  const getIMessageApprovalApprovers = imessageApproval.resolveApprovers;
1214
1199
  const imessageApprovalAuth = imessageApproval.approvalAuth;
1215
1200
  //#endregion
1216
- //#region extensions/imessage/src/approval-target-keys.ts
1217
- /** Strip the `p:<n>/` part prefix Messages puts on some GUIDs so keys match. */
1218
- function normalizeIMessageGuid(value) {
1219
- return value.trim().replace(/^p:\d+\//iu, "");
1220
- }
1221
- function chatIdToKeyValue(chatId) {
1222
- if (chatId == null || chatId === "") return null;
1223
- if (typeof chatId === "number") return Number.isFinite(chatId) && chatId > 0 ? String(chatId) : null;
1224
- return chatId.trim() || null;
1225
- }
1226
- function enumerateConversationKeyForms(conversation) {
1227
- const forms = [];
1228
- const chatGuid = conversation.chatGuid?.trim();
1229
- if (chatGuid) forms.push(`chat_guid:${chatGuid}`);
1230
- const chatIdentifier = conversation.chatIdentifier?.trim();
1231
- if (chatIdentifier) forms.push(`chat_identifier:${chatIdentifier}`);
1232
- const chatIdValue = chatIdToKeyValue(conversation.chatId);
1233
- if (chatIdValue) forms.push(`chat_id:${chatIdValue}`);
1234
- const handle = conversation.handle?.trim();
1235
- if (handle) forms.push(`handle:${handle}`);
1236
- return forms;
1237
- }
1238
- function normalizeConversationKey(conversation) {
1239
- return enumerateConversationKeyForms(conversation)[0];
1240
- }
1241
- /**
1242
- * Index a binding under every key derivable from the conversation. Outbound and
1243
- * inbound disagree about which key exists: send may only know
1244
- * `{handle: "+1..."}` for a DM, while the bridge populates chat_guid on the
1245
- * inbound event. Enumerating all forms keeps the two symmetric without making
1246
- * callers guess which one the bridge will pick.
1247
- */
1248
- function enumerateApprovalTargetKeys(params) {
1249
- const accountId = params.accountId.trim();
1250
- const messageId = params.messageId.trim();
1251
- if (!accountId || !messageId) return [];
1252
- return enumerateConversationKeyForms(params.conversation).map((form) => `${accountId}:${form}:${messageId}`);
1253
- }
1254
- function buildIMessageApprovalConversationKeyForTarget(to) {
1255
- try {
1256
- const target = parseIMessageTarget(to);
1257
- if (target.kind === "chat_id") return { chatId: target.chatId };
1258
- if (target.kind === "chat_guid") return { chatGuid: target.chatGuid };
1259
- if (target.kind === "chat_identifier") return { chatIdentifier: target.chatIdentifier };
1260
- const handle = normalizeIMessageHandle$1(target.to);
1261
- return handle ? { handle } : null;
1262
- } catch {
1263
- return null;
1264
- }
1265
- }
1266
- /** Conversation key for an inbound event, mirroring the outbound key forms. */
1267
- function buildIMessageApprovalConversationKeyForInbound(params) {
1268
- return {
1269
- ...params.chatGuid?.trim() ? { chatGuid: params.chatGuid.trim() } : {},
1270
- ...params.chatIdentifier?.trim() ? { chatIdentifier: params.chatIdentifier.trim() } : {},
1271
- ...chatIdToKeyValue(params.chatId ?? void 0) ? { chatId: params.chatId } : {},
1272
- ...params.isGroup ? {} : { handle: params.actorHandle }
1273
- };
1274
- }
1275
- //#endregion
1276
- //#region extensions/imessage/src/monitor/reaction-context.ts
1277
- const TAPBACK_TEXT_PATTERNS = [
1278
- {
1279
- prefix: "loved",
1280
- action: "added",
1281
- emoji: "❤️"
1282
- },
1283
- {
1284
- prefix: "liked",
1285
- action: "added",
1286
- emoji: "👍"
1287
- },
1288
- {
1289
- prefix: "disliked",
1290
- action: "added",
1291
- emoji: "👎"
1292
- },
1293
- {
1294
- prefix: "laughed at",
1295
- action: "added",
1296
- emoji: "😂"
1297
- },
1298
- {
1299
- prefix: "emphasized",
1300
- action: "added",
1301
- emoji: "‼️"
1302
- },
1303
- {
1304
- prefix: "questioned",
1305
- action: "added",
1306
- emoji: "❓"
1307
- },
1308
- {
1309
- prefix: "removed a heart from",
1310
- action: "removed",
1311
- emoji: "❤️"
1312
- },
1313
- {
1314
- prefix: "removed a like from",
1315
- action: "removed",
1316
- emoji: "👍"
1317
- },
1318
- {
1319
- prefix: "removed a dislike from",
1320
- action: "removed",
1321
- emoji: "👎"
1322
- },
1323
- {
1324
- prefix: "removed a laugh from",
1325
- action: "removed",
1326
- emoji: "😂"
1327
- },
1328
- {
1329
- prefix: "removed an emphasis from",
1330
- action: "removed",
1331
- emoji: "‼️"
1332
- },
1333
- {
1334
- prefix: "removed a question from",
1335
- action: "removed",
1336
- emoji: "❓"
1337
- }
1338
- ];
1339
- function normalizeReactionValue(value) {
1340
- return typeof value === "string" ? value.trim().replace(/^p:\d+\//iu, "") || void 0 : void 0;
1341
- }
1342
- function resolveReactionTargetGuidCandidates(...values) {
1343
- const candidates = [];
1344
- for (const value of values) {
1345
- if (typeof value !== "string") continue;
1346
- const raw = value.trim();
1347
- if (!raw) continue;
1348
- const normalized = raw.replace(/^p:\d+\//iu, "");
1349
- for (const candidate of [normalized, raw]) if (candidate && !candidates.includes(candidate)) candidates.push(candidate);
1350
- }
1351
- return candidates;
1352
- }
1353
- function resolveTapbackTextContext(bodyText) {
1354
- const lower = bodyText.toLowerCase();
1355
- for (const pattern of TAPBACK_TEXT_PATTERNS) {
1356
- if (!lower.startsWith(pattern.prefix)) continue;
1357
- const afterPrefix = bodyText.slice(pattern.prefix.length).trim();
1358
- if (!/^["“]/u.test(afterPrefix)) continue;
1359
- return {
1360
- action: pattern.action,
1361
- emoji: pattern.emoji,
1362
- targetText: afterPrefix.replace(/^["“]/u, "").replace(/["”]$/u, "").trim()
1363
- };
1364
- }
1365
- return null;
1366
- }
1367
- function resolveIMessageReactionContext(message, bodyText) {
1368
- if (message.is_reaction === true || message.is_tapback === true || typeof message.associated_message_type === "number" && Number.isFinite(message.associated_message_type) && message.associated_message_type >= 2e3 && message.associated_message_type < 4e3) {
1369
- const targetGuids = resolveReactionTargetGuidCandidates(message.reacted_to_guid, message.associated_message_guid);
1370
- return {
1371
- action: message.is_reaction_add === false ? "removed" : "added",
1372
- emoji: normalizeReactionValue(message.reaction_emoji) ?? normalizeReactionValue(message.reaction_type) ?? "reaction",
1373
- targetGuid: targetGuids[0],
1374
- targetGuids
1375
- };
1376
- }
1377
- return resolveTapbackTextContext(bodyText);
1378
- }
1379
- //#endregion
1380
- //#region extensions/imessage/src/approval-reactions.ts
1381
- var approval_reactions_exports = /* @__PURE__ */ __exportAll({
1382
- addIMessageApprovalReactionHintToStructuredPayload: () => addIMessageApprovalReactionHintToStructuredPayload,
1383
- addIMessageApprovalReactionHintToText: () => addIMessageApprovalReactionHintToText,
1384
- appendIMessageApprovalReactionHintForOutboundMessage: () => appendIMessageApprovalReactionHintForOutboundMessage,
1385
- buildIMessageApprovalConversationKeyForTarget: () => buildIMessageApprovalConversationKeyForTarget,
1386
- buildIMessageApprovalReactionHint: () => buildIMessageApprovalReactionHint,
1387
- clearIMessageApprovalReactionTargetsForTest: () => clearIMessageApprovalReactionTargetsForTest,
1388
- extractIMessageApprovalPromptBinding: () => extractIMessageApprovalPromptBinding,
1389
- handleIMessageApprovalReaction: () => handleIMessageApprovalReaction,
1390
- listPendingIMessageApprovalReactionPollTargets: () => listPendingIMessageApprovalReactionPollTargets,
1391
- maybeResolveIMessageApprovalReaction: () => maybeResolveIMessageApprovalReaction,
1392
- registerIMessageApprovalReactionTarget: () => registerIMessageApprovalReactionTarget,
1393
- registerIMessageApprovalReactionTargetForDeliveredPayload: () => registerIMessageApprovalReactionTargetForDeliveredPayload,
1394
- registerIMessageApprovalReactionTargetForOutboundMessage: () => registerIMessageApprovalReactionTargetForOutboundMessage,
1395
- resolveIMessageApprovalReactionTargetWithPersistence: () => resolveIMessageApprovalReactionTargetWithPersistence,
1396
- unregisterIMessageApprovalReactionTarget: () => unregisterIMessageApprovalReactionTarget
1397
- });
1398
- const PERSISTENT_NAMESPACE = "imessage.approval-reactions";
1399
- const PERSISTENT_MAX_ENTRIES = 1e3;
1400
- const DEFAULT_REACTION_TARGET_TTL_MS = 1440 * 60 * 1e3;
1401
- const loadResolveApprovalOverGateway = createLazyRuntimeSurface(() => import("openclaw/plugin-sdk/approval-gateway-runtime"), (runtime) => runtime.resolveApprovalOverGateway);
1402
- const pendingReactionPollTargets = /* @__PURE__ */ new Map();
1403
- function prunePendingReactionPollTargets(nowMs = Date.now()) {
1404
- for (const [key, target] of pendingReactionPollTargets.entries()) if (!isFutureDateTimestampMs(target.expiresAtMs, { nowMs })) pendingReactionPollTargets.delete(key);
1405
- }
1406
- function resolvePendingReactionPollExpiry(ttlMs) {
1407
- const nowMs = asDateTimestampMs(Date.now());
1408
- if (nowMs === void 0) return;
1409
- const expiresAtMs = resolveExpiresAtMsFromDurationMs(ttlMs ?? DEFAULT_REACTION_TARGET_TTL_MS, { nowMs }) ?? resolveExpiresAtMsFromDurationMs(DEFAULT_REACTION_TARGET_TTL_MS, { nowMs });
1410
- if (expiresAtMs === void 0) return;
1411
- return {
1412
- ttlMs: expiresAtMs - nowMs,
1413
- expiresAtMs
1414
- };
1415
- }
1416
- function mergePollTargetConversation(left, right) {
1417
- return {
1418
- chatGuid: left.chatGuid ?? right.chatGuid,
1419
- chatIdentifier: left.chatIdentifier ?? right.chatIdentifier,
1420
- chatId: left.chatId ?? right.chatId,
1421
- handle: left.handle ?? right.handle
1422
- };
1423
- }
1424
- function listPendingIMessageApprovalReactionPollTargets(params) {
1425
- const accountId = params.accountId.trim();
1426
- if (!accountId) return [];
1427
- prunePendingReactionPollTargets();
1428
- const targetByApprovalAndMessage = /* @__PURE__ */ new Map();
1429
- for (const target of pendingReactionPollTargets.values()) {
1430
- if (target.accountId !== accountId) continue;
1431
- const key = `${target.approvalId}:${normalizeIMessageGuid(target.messageId)}`;
1432
- const existing = targetByApprovalAndMessage.get(key);
1433
- if (!existing) {
1434
- targetByApprovalAndMessage.set(key, target);
1435
- continue;
1436
- }
1437
- targetByApprovalAndMessage.set(key, {
1438
- ...existing,
1439
- conversation: mergePollTargetConversation(existing.conversation, target.conversation),
1440
- expiresAtMs: Math.max(existing.expiresAtMs, target.expiresAtMs)
1441
- });
1442
- }
1443
- return [...targetByApprovalAndMessage.values()];
1444
- }
1445
- const reportPersistentApprovalReactionError = createPluginStateErrorReporter(getOptionalIMessageRuntime, "imessage", "approval-reaction-state", "iMessage persistent approval reaction state failed");
1446
- function reportApprovalBindingCorrelationMismatch(binding) {
1447
- try {
1448
- getOptionalIMessageRuntime()?.logging.getChildLogger({
1449
- plugin: "imessage",
1450
- feature: "approval-reaction-state"
1451
- }).warn("iMessage approval prompt text failed binding correlation; tapbacks disabled", {
1452
- approvalId: binding.approvalId,
1453
- approvalKind: binding.approvalKind
1454
- });
1455
- } catch {}
1456
- }
1457
- function readPersistedTarget(value) {
1458
- const target = value;
1459
- if (!target || typeof target.approvalId !== "string" || !Array.isArray(target.allowedDecisions) || target.approvalKind !== "exec" && target.approvalKind !== "plugin") return null;
1460
- const allowedDecisions = target.allowedDecisions.map((valueValue) => typeof valueValue === "string" ? normalizeApprovalReactionDecision(valueValue) : null).filter((valueLocal) => Boolean(valueLocal));
1461
- if (allowedDecisions.length === 0) return null;
1462
- return {
1463
- approvalId: target.approvalId,
1464
- approvalKind: target.approvalKind,
1465
- allowedDecisions
1466
- };
1467
- }
1468
- const imessageApprovalReactionTargets = createApprovalReactionTargetStore({
1469
- namespace: PERSISTENT_NAMESPACE,
1470
- maxEntries: PERSISTENT_MAX_ENTRIES,
1471
- defaultTtlMs: DEFAULT_REACTION_TARGET_TTL_MS,
1472
- openStore: (params) => getOptionalIMessageRuntime()?.state.openKeyedStore(params),
1473
- logPersistentError: reportPersistentApprovalReactionError,
1474
- readPersistedTarget
1475
- });
1476
- function listIMessageApprovalReactionBindings(allowedDecisions) {
1477
- return listApprovalReactionBindings({ allowedDecisions });
1478
- }
1479
- function buildIMessageApprovalReactionHint(allowedDecisions) {
1480
- return buildApprovalReactionHint({ allowedDecisions });
1481
- }
1482
- function addIMessageApprovalReactionHintToText(params) {
1483
- return addApprovalReactionHintToText(params);
1484
- }
1485
- function appendIMessageApprovalReactionHintForOutboundMessage(text) {
1486
- if (hasApprovalReactionHintText(text)) return text;
1487
- const binding = extractIMessageApprovalPromptBinding(text);
1488
- if (!binding) return text;
1489
- return addIMessageApprovalReactionHintToText({
1490
- text,
1491
- allowedDecisions: binding.allowedDecisions
1492
- });
1493
- }
1494
- const IMESSAGE_APPROVAL_DELIVERY_BINDING_KEY = "imessageApprovalReactionBindingV1";
1495
- function visibleApprovalBindingMatches(text, binding, options) {
1496
- if (!text) return false;
1497
- const lines = text.split(/\r?\n/).map((line) => line.replace(/\*\*/g, "").trim());
1498
- const normalizedHeaders = lines.map((line) => line.replace(/^[^A-Za-z0-9]*/, ""));
1499
- 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));
1500
- const hasId = lines.includes(`ID: ${binding.approvalId}`) || lines.includes(`Full id: \`${binding.approvalId}\``) || lines.includes(`Full id: ${binding.approvalId}`);
1501
- if (!hasKindHeader || !hasId) return false;
1502
- const visibleDecisions = [];
1503
- for (const line of lines) {
1504
- const match = line.match(APPROVE_COMMAND_LINE_RE);
1505
- const approvalId = match?.[1];
1506
- const decisionsText = match?.[2];
1507
- if (!approvalId || !decisionsText || approvalId !== binding.approvalId && approvalId !== binding.approvalSlug) continue;
1508
- for (const token of decisionsText.split(/[\s|,]+/)) {
1509
- const decision = normalizeApprovalReactionDecision(token);
1510
- if (decision && !visibleDecisions.includes(decision)) visibleDecisions.push(decision);
1511
- }
1512
- }
1513
- if (!approvalReactionDecisionSetsMatch(binding.allowedDecisions, visibleDecisions)) return false;
1514
- if (!options.requireReactionHint) return true;
1515
- const hint = buildIMessageApprovalReactionHint(binding.allowedDecisions);
1516
- return Boolean(hint && text.includes(hint));
1517
- }
1518
- /** Preserve a validated typed approval binding until the iMessage GUID is known. */
1519
- function addIMessageApprovalReactionHintToStructuredPayload(params) {
1520
- const metadata = readApprovalReactionPresentationBinding({
1521
- payload: params.payload,
1522
- requireApprovalSlug: true,
1523
- trimApprovalId: true
1524
- });
1525
- const text = params.payload.text;
1526
- if (metadata?.approvalKind !== params.approvalKind || !text) return null;
1527
- if (!visibleApprovalBindingMatches(text, metadata, { requireReactionHint: false })) {
1528
- reportApprovalBindingCorrelationMismatch(metadata);
1529
- return null;
1530
- }
1531
- return {
1532
- ...params.payload,
1533
- text: addIMessageApprovalReactionHintToText({
1534
- text,
1535
- allowedDecisions: metadata.allowedDecisions
1536
- }),
1537
- channelData: {
1538
- ...params.payload.channelData,
1539
- [IMESSAGE_APPROVAL_DELIVERY_BINDING_KEY]: {
1540
- version: 1,
1541
- approvalId: metadata.approvalId,
1542
- approvalSlug: metadata.approvalSlug,
1543
- approvalKind: metadata.approvalKind,
1544
- allowedDecisions: metadata.allowedDecisions
1545
- }
1546
- }
1547
- };
1548
- }
1549
- const APPROVE_COMMAND_LINE_RE = /\/approve(?:@[^\s]+)?\s+([A-Za-z0-9][A-Za-z0-9._:-]*)\s+(.+)$/i;
1550
- function extractIMessageApprovalPromptBinding(text) {
1551
- return extractApprovalReactionPromptBinding({ text });
1552
- }
1553
- function registerIMessageApprovalReactionTarget(params) {
1554
- const approvalId = params.approvalId.trim();
1555
- const allowedDecisions = listIMessageApprovalReactionBindings(params.allowedDecisions).map((binding) => binding.decision);
1556
- if (!approvalId || params.approvalKind !== "exec" && params.approvalKind !== "plugin" || allowedDecisions.length === 0) return null;
1557
- const target = {
1558
- approvalId,
1559
- approvalKind: params.approvalKind,
1560
- allowedDecisions
1561
- };
1562
- const expiry = resolvePendingReactionPollExpiry(params.ttlMs);
1563
- if (!expiry) return null;
1564
- const keys = enumerateApprovalTargetKeys({
1565
- accountId: params.accountId,
1566
- conversation: params.conversation,
1567
- messageId: params.messageId
1568
- });
1569
- if (keys.length === 0) return null;
1570
- for (const key of keys) {
1571
- imessageApprovalReactionTargets.register(key, target, { ttlMs: expiry.ttlMs });
1572
- pendingReactionPollTargets.set(key, {
1573
- accountId: params.accountId,
1574
- conversation: params.conversation,
1575
- messageId: params.messageId,
1576
- approvalId,
1577
- approvalKind: params.approvalKind,
1578
- allowedDecisions,
1579
- expiresAtMs: expiry.expiresAtMs
1580
- });
1581
- }
1582
- prunePendingReactionPollTargets();
1583
- return target;
1584
- }
1585
- function registerIMessageApprovalReactionTargetForOutboundMessage(params) {
1586
- const binding = extractIMessageApprovalPromptBinding(params.text);
1587
- if (!binding || binding.approvalKind !== params.approvalKind) return false;
1588
- return Boolean(registerIMessageApprovalReactionTarget({
1589
- accountId: params.accountId,
1590
- conversation: params.conversation,
1591
- messageId: params.messageId,
1592
- approvalId: binding.approvalId,
1593
- approvalKind: params.approvalKind,
1594
- allowedDecisions: binding.allowedDecisions,
1595
- ttlMs: params.ttlMs
1596
- }));
1597
- }
1598
- function listDeliveredIMessageApprovalGuids(params) {
1599
- const deliveries = [];
1600
- const seen = /* @__PURE__ */ new Set();
1601
- for (const result of params.results) {
1602
- if (result.channel !== "imessage") continue;
1603
- const guid = typeof result.meta?.imessageMessageGuid === "string" ? result.meta.imessageMessageGuid.trim() : "";
1604
- const visibleText = result.meta?.imessageVisibleText;
1605
- if (!guid || /^\d+$/.test(guid) || seen.has(guid) || typeof visibleText !== "string") continue;
1606
- seen.add(guid);
1607
- deliveries.push({
1608
- guid,
1609
- visibleText
1610
- });
1611
- }
1612
- if (!visibleApprovalBindingMatches(deliveries.map((delivery) => delivery.visibleText).join("\n"), params.binding, { requireReactionHint: true })) {
1613
- if (params.results.some((result) => result.channel === "imessage")) reportApprovalBindingCorrelationMismatch(params.binding);
1614
- return [];
1615
- }
1616
- return deliveries.map((delivery) => delivery.guid);
1617
- }
1618
- /** Bind a typed forwarded approval after iMessage returns the stable tapback GUID. */
1619
- function registerIMessageApprovalReactionTargetForDeliveredPayload(params) {
1620
- if (params.target.channel.trim().toLowerCase() !== "imessage") return false;
1621
- const binding = readApprovalReactionDeliveredBinding({
1622
- payload: params.payload,
1623
- channelDataKey: IMESSAGE_APPROVAL_DELIVERY_BINDING_KEY,
1624
- requireApprovalSlug: true,
1625
- trimApprovalId: true
1626
- });
1627
- if (!binding) return false;
1628
- const conversation = buildIMessageApprovalConversationKeyForTarget(params.target.to);
1629
- if (!conversation) return false;
1630
- let registered = false;
1631
- for (const messageId of listDeliveredIMessageApprovalGuids({
1632
- binding,
1633
- results: params.results
1634
- })) registered = Boolean(registerIMessageApprovalReactionTarget({
1635
- accountId: params.accountId,
1636
- conversation,
1637
- messageId,
1638
- approvalId: binding.approvalId,
1639
- approvalKind: binding.approvalKind,
1640
- allowedDecisions: binding.allowedDecisions,
1641
- ttlMs: params.ttlMs
1642
- })) || registered;
1643
- return registered;
1644
- }
1645
- function unregisterIMessageApprovalReactionTarget(params) {
1646
- const keys = enumerateApprovalTargetKeys(params);
1647
- for (const key of keys) {
1648
- imessageApprovalReactionTargets.delete(key);
1649
- pendingReactionPollTargets.delete(key);
1650
- }
1651
- }
1652
- function resolveTarget(params) {
1653
- const target = resolveTypedApprovalReactionTarget(params);
1654
- return target ? {
1655
- approvalId: target.approvalId,
1656
- approvalKind: target.approvalKind,
1657
- decision: target.decision
1658
- } : null;
1659
- }
1660
- function formatCanonicalApprovalTerminalState(approval) {
1661
- const decision = approval.status === "allowed" || approval.status === "denied" ? ` decision=${approval.decision}` : "";
1662
- return `status=${approval.status}${decision} reason=${approval.reason}`;
1663
- }
1664
- async function resolveIMessageApprovalReactionTargetWithPersistence(params) {
1665
- const keys = enumerateApprovalTargetKeys(params);
1666
- for (const key of keys) {
1667
- const target = resolveTarget({
1668
- target: await imessageApprovalReactionTargets.lookup(key),
1669
- reactionKey: params.reactionKey
1670
- });
1671
- if (target) return target;
1672
- }
1673
- return null;
1674
- }
1675
- function readApprovalReactionEvent(message, bodyText) {
1676
- const reaction = resolveIMessageReactionContext(message, bodyText);
1677
- if (!reaction) return null;
1678
- const reactionKey = reaction.emoji.trim();
1679
- const candidates = (reaction.targetGuids ?? []).map((value) => value.trim()).filter((value) => value.length > 0);
1680
- const primary = reaction.targetGuid?.trim() || candidates[0] || "";
1681
- const messageIdCandidates = candidates.length > 0 ? candidates : primary ? [primary] : [];
1682
- const actorHandle = normalizeIMessageHandle$1((message.sender ?? "").trim());
1683
- if (!reactionKey || !primary || !actorHandle) return null;
1684
- const conversation = buildIMessageApprovalConversationKeyForInbound({
1685
- chatGuid: message.chat_guid,
1686
- chatIdentifier: message.chat_identifier,
1687
- chatId: message.chat_id,
1688
- isGroup: message.is_group,
1689
- actorHandle
1690
- });
1691
- if (!normalizeConversationKey(conversation)) return null;
1692
- return {
1693
- conversation,
1694
- messageId: primary,
1695
- messageIdCandidates,
1696
- actorHandle,
1697
- reactionKey,
1698
- action: reaction.action
1699
- };
1700
- }
1701
- async function handleIMessageApprovalReaction(params) {
1702
- const event = readApprovalReactionEvent(params.message, params.bodyText);
1703
- if (!event) return {
1704
- handled: false,
1705
- stopPolling: false
1706
- };
1707
- if (event.action === "removed") return {
1708
- handled: false,
1709
- stopPolling: false
1710
- };
1711
- let target = null;
1712
- let matchedMessageId = null;
1713
- for (const candidate of event.messageIdCandidates) {
1714
- target = await resolveIMessageApprovalReactionTargetWithPersistence({
1715
- accountId: params.accountId,
1716
- conversation: event.conversation,
1717
- messageId: candidate,
1718
- reactionKey: event.reactionKey
1719
- });
1720
- if (target) {
1721
- matchedMessageId = candidate;
1722
- break;
1723
- }
1724
- }
1725
- if (!target) return {
1726
- handled: false,
1727
- stopPolling: false
1728
- };
1729
- if (getIMessageApprovalApprovers({
1730
- cfg: params.cfg,
1731
- accountId: params.accountId
1732
- }).length === 0) {
1733
- params.logVerboseMessage?.(`imessage: approval reaction denied id=${target.approvalId}; reactions require explicit approvers`);
1734
- return {
1735
- handled: true,
1736
- stopPolling: false
1737
- };
1738
- }
1739
- if (!imessageApprovalAuth.authorizeActorAction({
1740
- cfg: params.cfg,
1741
- accountId: params.accountId,
1742
- senderId: event.actorHandle,
1743
- action: "approve",
1744
- approvalKind: target.approvalKind
1745
- }).authorized) {
1746
- params.logVerboseMessage?.(`imessage: approval reaction denied id=${target.approvalId} sender=${event.actorHandle}`);
1747
- return {
1748
- handled: true,
1749
- stopPolling: false
1750
- };
1751
- }
1752
- const resolveApprovalOverGateway = await loadResolveApprovalOverGateway();
1753
- try {
1754
- const result = await resolveApprovalOverGateway({
1755
- cfg: params.cfg,
1756
- approvalId: target.approvalId,
1757
- approvalKind: target.approvalKind,
1758
- decision: target.decision,
1759
- channel: "imessage",
1760
- accountId: params.accountId,
1761
- senderId: event.actorHandle,
1762
- gatewayUrl: params.gatewayUrl,
1763
- ...params.gatewayRuntime ? { gatewayRuntime: params.gatewayRuntime } : {}
1764
- });
1765
- for (const candidate of event.messageIdCandidates) unregisterIMessageApprovalReactionTarget({
1766
- accountId: params.accountId,
1767
- conversation: event.conversation,
1768
- messageId: candidate
1769
- });
1770
- const outcome = result.applied ? "resolved" : "already resolved";
1771
- params.logVerboseMessage?.(`imessage: approval reaction ${outcome} id=${target.approvalId} sender=${event.actorHandle} ${formatCanonicalApprovalTerminalState(result.approval)} via messageId=${matchedMessageId ?? event.messageId}`);
1772
- return {
1773
- handled: true,
1774
- stopPolling: true,
1775
- stopPollingReason: "resolved"
1776
- };
1777
- } catch (error) {
1778
- if (isApprovalNotFoundError(error)) {
1779
- for (const candidate of event.messageIdCandidates) unregisterIMessageApprovalReactionTarget({
1780
- accountId: params.accountId,
1781
- conversation: event.conversation,
1782
- messageId: candidate
1783
- });
1784
- params.logVerboseMessage?.(`imessage: approval reaction ignored for expired approval id=${target.approvalId} sender=${event.actorHandle}`);
1785
- return {
1786
- handled: true,
1787
- stopPolling: true,
1788
- stopPollingReason: "not-found"
1789
- };
1790
- }
1791
- try {
1792
- getOptionalIMessageRuntime()?.logging.getChildLogger({
1793
- plugin: "imessage",
1794
- feature: "approval-reactions"
1795
- }).warn("approval reaction failed", {
1796
- approvalId: target.approvalId,
1797
- senderId: event.actorHandle,
1798
- error: String(error)
1799
- });
1800
- } catch {}
1801
- params.logVerboseMessage?.(`imessage: approval reaction failed id=${target.approvalId} sender=${event.actorHandle}: ${String(error)}`);
1802
- return {
1803
- handled: true,
1804
- stopPolling: true,
1805
- stopPollingReason: "resolver-error"
1806
- };
1807
- }
1808
- }
1809
- async function maybeResolveIMessageApprovalReaction(params) {
1810
- return (await handleIMessageApprovalReaction(params)).handled;
1811
- }
1812
- function clearIMessageApprovalReactionTargetsForTest() {
1813
- imessageApprovalReactionTargets.clearForTest();
1814
- pendingReactionPollTargets.clear();
1815
- loadResolveApprovalOverGateway.clear();
1816
- }
1817
- //#endregion
1818
1201
  //#region extensions/imessage/src/normalize.ts
1819
1202
  const SERVICE_PREFIXES = [
1820
1203
  "imessage:",
@@ -1822,14 +1205,7 @@ const SERVICE_PREFIXES = [
1822
1205
  "auto:"
1823
1206
  ];
1824
1207
  const CHAT_TARGET_PREFIX_RE = /^(chat_id:|chatid:|chat:|chat_guid:|chatguid:|guid:|chat_identifier:|chatidentifier:|chatident:)/i;
1825
- function looksLikeHandleOrPhoneTarget(params) {
1826
- const trimmed = params.raw.trim();
1827
- if (!trimmed) return false;
1828
- if (params.prefixPattern.test(trimmed)) return true;
1829
- if (trimmed.includes("@")) return true;
1830
- return (params.phonePattern ?? /^\+?\d{3,}$/).test(trimmed);
1831
- }
1832
- function normalizeIMessageHandle(raw) {
1208
+ function normalizeIMessageHandle(raw, allowContactName = false) {
1833
1209
  const trimmed = raw.trim();
1834
1210
  if (!trimmed) return "";
1835
1211
  const lowered = normalizeLowercaseStringOrEmpty(trimmed);
@@ -1845,16 +1221,16 @@ function normalizeIMessageHandle(raw) {
1845
1221
  if (trimmed.includes("@")) return normalizeLowercaseStringOrEmpty(trimmed);
1846
1222
  const bareChatIdentifier = normalizeBareIMessageChatIdentifier(trimmed);
1847
1223
  if (bareChatIdentifier) return `chat_identifier:${bareChatIdentifier}`;
1848
- const normalized = normalizeE164(trimmed);
1224
+ const normalized = isIMessagePhoneLikeHandle(trimmed) ? normalizeE164(trimmed) : "";
1849
1225
  if (normalized) return normalized;
1850
- return trimmed.replace(/\s+/g, "");
1226
+ return allowContactName ? trimmed.replace(/\s+/g, "") : "";
1851
1227
  }
1852
1228
  function normalizeIMessageMessagingTarget(raw) {
1853
1229
  const trimmed = normalizeOptionalString(raw);
1854
1230
  if (!trimmed) return;
1855
1231
  const lower = normalizeLowercaseStringOrEmpty(trimmed);
1856
1232
  for (const prefix of SERVICE_PREFIXES) if (lower.startsWith(prefix)) {
1857
- const normalizedHandle = normalizeIMessageHandle(trimmed.slice(prefix.length).trim());
1233
+ const normalizedHandle = normalizeIMessageHandle(trimmed.slice(prefix.length).trim(), true);
1858
1234
  if (!normalizedHandle) return;
1859
1235
  if (CHAT_TARGET_PREFIX_RE.test(normalizedHandle)) return normalizedHandle;
1860
1236
  return `${prefix}${normalizedHandle}`;
@@ -1866,10 +1242,7 @@ function looksLikeIMessageTargetId(raw) {
1866
1242
  if (!trimmed) return false;
1867
1243
  if (CHAT_TARGET_PREFIX_RE.test(trimmed)) return true;
1868
1244
  if (normalizeBareIMessageChatIdentifier(trimmed)) return true;
1869
- return looksLikeHandleOrPhoneTarget({
1870
- raw: trimmed,
1871
- prefixPattern: /^(imessage:|sms:|auto:)/i
1872
- });
1245
+ return /^(imessage:|sms:|auto:)/i.test(trimmed) || trimmed.includes("@") || isIMessagePhoneLikeHandle(trimmed) && Boolean(normalizeE164(trimmed));
1873
1246
  }
1874
1247
  //#endregion
1875
1248
  //#region extensions/imessage/src/conversation-id-core.ts
@@ -1929,4 +1302,4 @@ function resolveIMessageGroupToolPolicy(params) {
1929
1302
  });
1930
1303
  }
1931
1304
  //#endregion
1932
- export { enumerateConversationKeyForms as A, imessageCompletionNote as B, registerIMessageApprovalReactionTarget as C, buildIMessageApprovalConversationKeyForInbound as D, resolveIMessageReactionContext as E, imessageMessageActions as F, normalizeIMessageCliPathForSetup as G, imessageSetupContract as H, probeIMessage as I, __exportAll as K, IMESSAGE_INSTALL_COMMAND as L, normalizeIMessageGuid as M, getIMessageApprovalApprovers as N, buildIMessageApprovalConversationKeyForTarget as O, imessageApprovalAuth as P, createIMessageCliPathTextInput as R, maybeResolveIMessageApprovalReaction as S, unregisterIMessageApprovalReactionTarget as T, imessageSetupStatusBase as U, imessageDmPolicy as V, isAutoManagedIMessageCliPath as W, appendIMessageApprovalReactionHintForOutboundMessage as _, normalizeIMessageAcpConversationId as a, handleIMessageApprovalReaction as b, PAIRING_APPROVED_MESSAGE as c, formatTrimmedAllowFromEntries$1 as d, getChatChannelMeta as f, addIMessageApprovalReactionHintToText as g, normalizeIMessageMessagingTarget as h, matchIMessageAcpConversation as i, normalizeConversationKey as j, enumerateApprovalTargetKeys as k, chunkTextForOutbound as l, looksLikeIMessageTargetId as m, resolveIMessageGroupToolPolicy as n, resolveIMessageConversationIdFromTarget as o, resolveChannelMediaMaxBytes as p, resolveIMessageInboundConversationId as r, DEFAULT_ACCOUNT_ID as s, resolveIMessageGroupRequireMention as t, collectStatusIssuesFromLastError as u, approval_reactions_exports as v, registerIMessageApprovalReactionTargetForOutboundMessage as w, listPendingIMessageApprovalReactionPollTargets as x, extractIMessageApprovalPromptBinding as y, createIMessageSetupWizardProxy as z };
1305
+ export { imessageCompletionNote as C, isAutoManagedIMessageCliPath as D, imessageSetupStatusBase as E, normalizeIMessageCliPathForSetup as O, createIMessageSetupWizardProxy as S, imessageSetupContract as T, imessageApprovalAuth as _, normalizeIMessageAcpConversationId as a, IMESSAGE_INSTALL_COMMAND as b, PAIRING_APPROVED_MESSAGE as c, formatTrimmedAllowFromEntries$1 as d, getChatChannelMeta as f, getIMessageApprovalApprovers as g, normalizeIMessageMessagingTarget as h, matchIMessageAcpConversation as i, chunkTextForOutbound as l, looksLikeIMessageTargetId as m, resolveIMessageGroupToolPolicy as n, resolveIMessageConversationIdFromTarget as o, resolveChannelMediaMaxBytes as p, resolveIMessageInboundConversationId as r, DEFAULT_ACCOUNT_ID as s, resolveIMessageGroupRequireMention as t, collectStatusIssuesFromLastError as u, imessageMessageActions as v, imessageDmPolicy as w, createIMessageCliPathTextInput as x, probeIMessage as y };