@openclaw/signal 2026.6.11 → 2026.7.1-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.
package/dist/api.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { i as resolveSignalAccount, n as listSignalAccountIds, r as resolveDefaultSignalAccountId, t as listEnabledSignalAccounts } from "./accounts-hOCHbEhX.js";
2
2
  import { a as normalizeSignalAllowRecipient, c as resolveSignalSender, d as normalizeSignalMessagingTarget, i as isSignalSenderAllowed, l as looksLikeUuid, n as formatSignalSenderDisplay, o as resolveSignalPeerId, r as formatSignalSenderId, s as resolveSignalRecipient, t as formatSignalPairingIdLine, u as looksLikeSignalTargetId } from "./identity-B6O4k8xg.js";
3
- import { i as markdownToSignalTextChunks, n as resolveSignalReactionLevel, r as markdownToSignalText, t as signalMessageActions } from "./message-actions-Bue0g2Kc.js";
4
- import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-C_PQ45D9.js";
5
- import { a as normalizeSignalAccountInput, l as signalSetupAdapter, n as createSignalPluginBase, r as signalSetupWizard, t as signalPlugin, u as resolveSignalOutboundTarget } from "./channel-LmY2UpOt.js";
6
- import { a as looksLikeArchive, n as extractSignalCliArchive, o as pickAsset, r as installSignalCli } from "./install-signal-cli-CXgTF3de.js";
7
- import { t as monitorSignalProvider } from "./monitor-CUhIKHJo.js";
8
- import { n as sendReadReceiptSignal, r as sendTypingSignal, t as sendMessageSignal } from "./send-CBlFUkY_.js";
9
- import { t as probeSignal } from "./probe-BQ_Izoya.js";
3
+ import { i as markdownToSignalTextChunks, n as resolveSignalReactionLevel, r as markdownToSignalText, t as signalMessageActions } from "./message-actions-BnugYXjB.js";
4
+ import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-BRJCLiqK.js";
5
+ import { a as normalizeSignalAccountInput, l as signalSetupAdapter, n as createSignalPluginBase, r as signalSetupWizard, t as signalPlugin, u as resolveSignalOutboundTarget } from "./channel-aQhurb0Z.js";
6
+ import { a as looksLikeArchive, n as extractSignalCliArchive, o as pickAsset, r as installSignalCli } from "./install-signal-cli-TwhJ0DGy.js";
7
+ import { t as monitorSignalProvider } from "./monitor-C1gdgT7W.js";
8
+ import { n as sendReadReceiptSignal, r as sendTypingSignal, t as sendMessageSignal } from "./send-De6LIOLq.js";
9
+ import { t as probeSignal } from "./probe-BvFJLOBi.js";
10
10
  //#region extensions/signal/src/channel.setup.ts
11
11
  const signalSetupPlugin = { ...createSignalPluginBase({
12
12
  setupWizard: signalSetupWizard,
@@ -1,5 +1,6 @@
1
1
  import { d as normalizeSignalMessagingTarget } from "./identity-B6O4k8xg.js";
2
- import { c as resolveSignalApprovalTargetAuthorKeys, i as hasSignalApprovalReactionApprovers, l as unregisterSignalApprovalReactionTarget, o as registerSignalApprovalReactionTarget, r as sendTypingSignal, s as resolveSignalApprovalConversationKey, t as sendMessageSignal } from "./send-CBlFUkY_.js";
2
+ import { a as registerSignalApprovalReactionTarget, c as resolveSignalApprovalTargetAuthorKeys, l as unregisterSignalApprovalReactionTarget, r as hasSignalApprovalReactionApprovers, s as resolveSignalApprovalConversationKey } from "./approval-reactions-H-RPGBK7.js";
3
+ import { r as sendTypingSignal, t as sendMessageSignal } from "./send-De6LIOLq.js";
3
4
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
4
5
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
6
  import { buildChannelApprovalNativeTargetKey } from "openclaw/plugin-sdk/approval-native-runtime";
@@ -124,6 +125,7 @@ const signalApprovalNativeRuntime = createChannelApprovalNativeRuntimeAdapter({
124
125
  conversationKey: entry.conversationKey,
125
126
  messageId: entry.messageId,
126
127
  approvalId: request.id,
128
+ approvalKind: view.approvalKind,
127
129
  allowedDecisions: pendingPayload.reactionPayload.allowedDecisions,
128
130
  targetAuthorKeys: entry.targetAuthorKeys,
129
131
  route: {
@@ -1,16 +1,12 @@
1
- import { i as resolveSignalAccount } from "./accounts-hOCHbEhX.js";
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { d as normalizeSignalMessagingTarget, l as looksLikeUuid } from "./identity-B6O4k8xg.js";
3
- import { c as getSignalApprovalApprovers, l as signalApprovalAuth, r as markdownToSignalText } from "./message-actions-Bue0g2Kc.js";
4
- import { a as signalRpcRequest, r as resolveSignalRpcContext } from "./reaction-runtime-api-C_PQ45D9.js";
5
- import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
6
- import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
7
- import { kindFromMime, resolveOutboundAttachmentFromUrl } from "openclaw/plugin-sdk/media-runtime";
3
+ import { c as getSignalApprovalApprovers, l as signalApprovalAuth } from "./message-actions-BnugYXjB.js";
8
4
  import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
9
5
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
10
6
  import { buildApprovalReactionHint, createApprovalReactionTargetStore, listApprovalReactionBindings, resolveApprovalReactionTarget } from "openclaw/plugin-sdk/approval-reaction-runtime";
11
7
  import { normalizeE164 } from "openclaw/plugin-sdk/text-utility-runtime";
12
- import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
13
8
  import { matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
9
+ import { getExecApprovalReplyMetadata } from "openclaw/plugin-sdk/approval-reply-runtime";
14
10
  import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
15
11
  //#region extensions/signal/src/runtime.ts
16
12
  const { setRuntime: setSignalRuntime, getRuntime: getSignalRuntime, tryGetRuntime: getOptionalSignalRuntime, clearRuntime: clearSignalRuntime } = createPluginRuntimeStore({
@@ -19,7 +15,22 @@ const { setRuntime: setSignalRuntime, getRuntime: getSignalRuntime, tryGetRuntim
19
15
  });
20
16
  //#endregion
21
17
  //#region extensions/signal/src/approval-reactions.ts
22
- const PERSISTENT_NAMESPACE = "signal.approval-reactions";
18
+ var approval_reactions_exports = /* @__PURE__ */ __exportAll({
19
+ addSignalApprovalReactionHintToStructuredPayload: () => addSignalApprovalReactionHintToStructuredPayload,
20
+ addSignalApprovalReactionHintToText: () => addSignalApprovalReactionHintToText,
21
+ buildSignalApprovalReactionHint: () => buildSignalApprovalReactionHint,
22
+ clearSignalApprovalReactionTargetsForTest: () => clearSignalApprovalReactionTargetsForTest,
23
+ hasSignalApprovalReactionApprovers: () => hasSignalApprovalReactionApprovers,
24
+ listSignalApprovalReactionBindings: () => listSignalApprovalReactionBindings,
25
+ maybeResolveSignalApprovalReaction: () => maybeResolveSignalApprovalReaction,
26
+ registerSignalApprovalReactionTarget: () => registerSignalApprovalReactionTarget,
27
+ registerSignalApprovalReactionTargetForDeliveredPayload: () => registerSignalApprovalReactionTargetForDeliveredPayload,
28
+ resolveSignalApprovalConversationKey: () => resolveSignalApprovalConversationKey,
29
+ resolveSignalApprovalReactionTargetWithPersistence: () => resolveSignalApprovalReactionTargetWithPersistence,
30
+ resolveSignalApprovalTargetAuthorKeys: () => resolveSignalApprovalTargetAuthorKeys,
31
+ unregisterSignalApprovalReactionTarget: () => unregisterSignalApprovalReactionTarget
32
+ });
33
+ const PERSISTENT_NAMESPACE = "signal.approval-reactions.v2";
23
34
  const PERSISTENT_MAX_ENTRIES = 1e3;
24
35
  const DEFAULT_REACTION_TARGET_TTL_MS = 1440 * 60 * 1e3;
25
36
  let resolverRuntimePromise;
@@ -173,36 +184,15 @@ function insertSignalApprovalReactionHintNearHeader(params) {
173
184
  return `${params.hint}\n\n${params.text}`;
174
185
  }
175
186
  function addSignalApprovalReactionHintToText(params) {
176
- if (/(^|\n)React with:\s*(\n|$)/i.test(params.text)) return params.text;
187
+ if (hasSignalApprovalReactionHintText(params.text)) return params.text;
177
188
  const hint = buildSignalApprovalReactionHint(params.allowedDecisions);
178
189
  return hint ? insertSignalApprovalReactionHintNearHeader({
179
190
  text: params.text,
180
191
  hint
181
192
  }) : params.text;
182
193
  }
183
- function normalizeApprovalDecision(value) {
184
- const normalized = value.trim().toLowerCase();
185
- if (normalized === "always") return "allow-always";
186
- if (normalized === "allow-once" || normalized === "allow-always" || normalized === "deny") return normalized;
187
- return null;
188
- }
189
- function extractSignalApprovalPromptBinding(text) {
190
- const allowedDecisions = [];
191
- let approvalId = "";
192
- for (const line of text.split(/\r?\n/)) {
193
- const match = line.match(/\/approve(?:@[^\s]+)?\s+([A-Za-z0-9][A-Za-z0-9._:-]*)\s+(.+)$/i);
194
- if (!match) continue;
195
- if (approvalId && match[1] !== approvalId) continue;
196
- approvalId ||= match[1];
197
- for (const decisionText of match[2].split(/[\s|,]+/)) {
198
- const decision = normalizeApprovalDecision(decisionText);
199
- if (decision && !allowedDecisions.includes(decision)) allowedDecisions.push(decision);
200
- }
201
- }
202
- return approvalId && allowedDecisions.length > 0 ? {
203
- approvalId,
204
- allowedDecisions
205
- } : null;
194
+ function hasSignalApprovalReactionHintText(text) {
195
+ return /(^|\n)React with:\s*(\n|$)/i.test(text ?? "");
206
196
  }
207
197
  function buildTargetRoute(params) {
208
198
  const to = normalizeSignalMessagingTarget(params.to);
@@ -217,39 +207,11 @@ function buildTargetRoute(params) {
217
207
  return isSignalApprovalReactionRouteStillEnabled({
218
208
  cfg: params.cfg,
219
209
  target: {
220
- approvalKind: resolveApprovalKindFromId(params.approvalId),
210
+ approvalKind: params.approvalKind ?? resolveApprovalKindFromId(params.approvalId),
221
211
  route
222
212
  }
223
213
  }) ? route : null;
224
214
  }
225
- function shouldAppendSignalApprovalReactionHintForOutboundMessage(params) {
226
- const binding = extractSignalApprovalPromptBinding(params.text);
227
- if (!binding) return false;
228
- if (resolveSignalApprovalTargetAuthorKeys(params).length === 0) return false;
229
- if (!hasSignalApprovalReactionApprovers({
230
- cfg: params.cfg,
231
- accountId: params.accountId
232
- })) return false;
233
- return Boolean(buildTargetRoute({
234
- cfg: params.cfg,
235
- accountId: params.accountId,
236
- to: params.to,
237
- approvalId: binding.approvalId,
238
- agentId: params.agentId,
239
- sessionKey: params.sessionKey
240
- }));
241
- }
242
- function appendSignalApprovalReactionHintForOutboundMessage(params) {
243
- const binding = extractSignalApprovalPromptBinding(params.text);
244
- if (!binding || !shouldAppendSignalApprovalReactionHintForOutboundMessage({
245
- ...params,
246
- text: params.text
247
- })) return params.text;
248
- return addSignalApprovalReactionHintToText({
249
- text: params.text,
250
- allowedDecisions: binding.allowedDecisions
251
- });
252
- }
253
215
  function hasSignalApprovalReactionApprovers(params) {
254
216
  return getSignalApprovalApprovers(params).length > 0;
255
217
  }
@@ -273,7 +235,7 @@ function registerSignalApprovalReactionTarget(params) {
273
235
  };
274
236
  const target = {
275
237
  approvalId,
276
- approvalKind: resolveApprovalKindFromId(approvalId),
238
+ approvalKind: params.approvalKind ?? resolveApprovalKindFromId(approvalId),
277
239
  allowedDecisions,
278
240
  targetAuthorKeys,
279
241
  route
@@ -281,31 +243,84 @@ function registerSignalApprovalReactionTarget(params) {
281
243
  signalApprovalReactionTargets.register(key, target, { ttlMs: params.ttlMs });
282
244
  return target;
283
245
  }
284
- function registerSignalApprovalReactionTargetForOutboundMessage(params) {
285
- const binding = extractSignalApprovalPromptBinding(params.text);
286
- if (!binding) return false;
287
- const conversationKey = resolveSignalApprovalConversationKey(params.to);
288
- if (!conversationKey) return false;
289
- const route = buildTargetRoute({
246
+ function addSignalApprovalReactionHintToStructuredPayload(params) {
247
+ const metadata = getExecApprovalReplyMetadata(params.payload);
248
+ if (!metadata?.allowedDecisions || metadata.allowedDecisions.length === 0) return null;
249
+ if (resolveSignalApprovalTargetAuthorKeys(params).length === 0) return null;
250
+ if (!hasSignalApprovalReactionApprovers({
251
+ cfg: params.cfg,
252
+ accountId: params.accountId
253
+ })) return null;
254
+ if (!buildTargetRoute({
290
255
  cfg: params.cfg,
291
256
  accountId: params.accountId,
292
257
  to: params.to,
293
- approvalId: binding.approvalId,
294
- agentId: params.agentId,
295
- sessionKey: params.sessionKey
258
+ approvalId: metadata.approvalId,
259
+ approvalKind: metadata.approvalKind,
260
+ agentId: metadata.agentId,
261
+ sessionKey: metadata.sessionKey
262
+ }) || !params.payload.text) return null;
263
+ return {
264
+ ...params.payload,
265
+ text: addSignalApprovalReactionHintToText({
266
+ text: params.payload.text,
267
+ allowedDecisions: metadata.allowedDecisions
268
+ })
269
+ };
270
+ }
271
+ function readSignalDeliveryVisibleText(result) {
272
+ const meta = result.meta;
273
+ const visibleText = meta?.signalVisibleText ?? meta?.visibleText;
274
+ return typeof visibleText === "string" ? visibleText : null;
275
+ }
276
+ function listDeliveredSignalMessageIdsWithVisibleHint(params) {
277
+ const signalResults = params.results.filter((result) => !result.channel || normalizeLowercaseStringOrEmpty(result.channel) === "signal");
278
+ const resultsWithVisibleText = signalResults.filter((result) => readSignalDeliveryVisibleText(result) !== null);
279
+ const candidates = resultsWithVisibleText.length > 0 ? resultsWithVisibleText : signalResults;
280
+ if (resultsWithVisibleText.length === 0 && candidates.length !== 1) return [];
281
+ const ids = candidates.filter((result) => resultsWithVisibleText.length > 0 ? hasSignalApprovalReactionHintText(readSignalDeliveryVisibleText(result)) : hasSignalApprovalReactionHintText(params.payload.text)).map((result) => normalizeOptionalString(result.messageId)).filter((messageId) => Boolean(messageId && messageId !== "unknown"));
282
+ return Array.from(new Set(ids));
283
+ }
284
+ function registerSignalApprovalReactionTargetForDeliveredPayload(params) {
285
+ if (normalizeLowercaseStringOrEmpty(params.target.channel) !== "signal") return false;
286
+ const metadata = getExecApprovalReplyMetadata(params.payload);
287
+ if (!metadata?.allowedDecisions || metadata.allowedDecisions.length === 0) return false;
288
+ if (!hasSignalApprovalReactionHintText(params.payload.text)) return false;
289
+ if (!hasSignalApprovalReactionApprovers({
290
+ cfg: params.cfg,
291
+ accountId: params.target.accountId
292
+ })) return false;
293
+ const conversationKey = resolveSignalApprovalConversationKey(params.target.to);
294
+ if (!conversationKey) return false;
295
+ const route = buildTargetRoute({
296
+ cfg: params.cfg,
297
+ accountId: params.target.accountId,
298
+ to: params.target.to,
299
+ approvalId: metadata.approvalId,
300
+ approvalKind: metadata.approvalKind,
301
+ agentId: metadata.agentId,
302
+ sessionKey: metadata.sessionKey
296
303
  });
297
304
  if (!route) return false;
298
- return Boolean(registerSignalApprovalReactionTarget({
299
- accountId: params.accountId,
305
+ const targetAuthorKeys = resolveSignalApprovalTargetAuthorKeys(params);
306
+ if (targetAuthorKeys.length === 0) return false;
307
+ let registered = false;
308
+ for (const messageId of listDeliveredSignalMessageIdsWithVisibleHint({
309
+ payload: params.payload,
310
+ results: params.results
311
+ })) registered = Boolean(registerSignalApprovalReactionTarget({
312
+ accountId: normalizeAccountId(params.target.accountId ?? void 0),
300
313
  conversationKey,
301
- messageId: params.messageId,
302
- approvalId: binding.approvalId,
303
- allowedDecisions: binding.allowedDecisions,
304
- targetAuthorKeys: resolveSignalApprovalTargetAuthorKeys(params),
314
+ messageId,
315
+ approvalId: metadata.approvalId,
316
+ approvalKind: metadata.approvalKind,
317
+ allowedDecisions: metadata.allowedDecisions,
318
+ targetAuthorKeys,
305
319
  route,
306
320
  routeAllowed: true,
307
321
  ttlMs: params.ttlMs
308
- }));
322
+ })) || registered;
323
+ return registered;
309
324
  }
310
325
  function unregisterSignalApprovalReactionTarget(params) {
311
326
  const key = buildReactionTargetKey(params);
@@ -403,197 +418,9 @@ async function maybeResolveSignalApprovalReaction(params) {
403
418
  return true;
404
419
  }
405
420
  }
406
- //#endregion
407
- //#region extensions/signal/src/send.ts
408
- async function resolveSignalRpcAccountInfo(opts) {
409
- if (opts.baseUrl?.trim() && opts.account?.trim()) return;
410
- if (!opts.cfg) throw new Error("Signal RPC account resolution requires a resolved runtime config. Load and resolve config at the command or gateway boundary, then pass cfg through the runtime path.");
411
- return resolveSignalAccount({
412
- cfg: requireRuntimeConfig(opts.cfg, "Signal RPC account resolution"),
413
- accountId: opts.accountId
414
- });
415
- }
416
- function parseTarget(raw) {
417
- let value = raw.trim();
418
- if (!value) throw new Error("Signal recipient is required");
419
- if (normalizeLowercaseStringOrEmpty(value).startsWith("signal:")) value = value.slice(7).trim();
420
- const normalized = normalizeLowercaseStringOrEmpty(value);
421
- if (normalized.startsWith("group:")) return {
422
- type: "group",
423
- groupId: value.slice(6).trim()
424
- };
425
- if (normalized.startsWith("username:")) return {
426
- type: "username",
427
- username: value.slice(9).trim()
428
- };
429
- if (normalized.startsWith("u:")) return {
430
- type: "username",
431
- username: value.trim()
432
- };
433
- return {
434
- type: "recipient",
435
- recipient: value
436
- };
437
- }
438
- function buildTargetParams(target, allow) {
439
- if (target.type === "recipient") {
440
- if (!allow.recipient) return null;
441
- return { recipient: [target.recipient] };
442
- }
443
- if (target.type === "group") {
444
- if (!allow.group) return null;
445
- return { groupId: target.groupId };
446
- }
447
- if (target.type === "username") {
448
- if (!allow.username) return null;
449
- return { username: [target.username] };
450
- }
451
- return null;
452
- }
453
- function createSignalSendReceipt(params) {
454
- const messageId = params.messageId.trim();
455
- const results = messageId && messageId !== "unknown" ? [{
456
- channel: "signal",
457
- messageId,
458
- meta: { targetType: params.target.type }
459
- }] : [];
460
- if (results[0]) {
461
- if (params.timestamp != null) results[0].timestamp = params.timestamp;
462
- if (params.target.type === "group") results[0].chatId = params.target.groupId;
463
- else if (params.target.type === "recipient") results[0].toJid = params.target.recipient;
464
- else results[0].toJid = params.target.username;
465
- }
466
- return createMessageReceiptFromOutboundResults({
467
- results,
468
- kind: params.kind
469
- });
470
- }
471
- async function sendMessageSignal(to, text, opts) {
472
- const cfg = requireRuntimeConfig(opts.cfg, "Signal send");
473
- const apiMode = cfg.channels?.signal?.apiMode;
474
- const accountInfo = resolveSignalAccount({
475
- cfg,
476
- accountId: opts.accountId
477
- });
478
- const { baseUrl, account } = resolveSignalRpcContext(opts, accountInfo);
479
- const target = parseTarget(to);
480
- const outboundText = appendSignalApprovalReactionHintForOutboundMessage({
481
- cfg,
482
- accountId: accountInfo.accountId,
483
- to,
484
- text: text ?? "",
485
- targetAuthor: account
486
- });
487
- let message = outboundText;
488
- let messageFromPlaceholder = false;
489
- let textStyles = [];
490
- const textMode = opts.textMode ?? "markdown";
491
- const maxBytes = (() => {
492
- if (typeof opts.maxBytes === "number") return opts.maxBytes;
493
- if (typeof accountInfo.config.mediaMaxMb === "number") return accountInfo.config.mediaMaxMb * 1024 * 1024;
494
- if (typeof cfg.agents?.defaults?.mediaMaxMb === "number") return cfg.agents.defaults.mediaMaxMb * 1024 * 1024;
495
- return 8 * 1024 * 1024;
496
- })();
497
- let attachments;
498
- if (opts.mediaUrl?.trim()) {
499
- const resolved = await resolveOutboundAttachmentFromUrl(opts.mediaUrl.trim(), maxBytes, {
500
- mediaAccess: opts.mediaAccess,
501
- localRoots: opts.mediaLocalRoots,
502
- readFile: opts.mediaReadFile
503
- });
504
- attachments = [resolved.path];
505
- const kind = kindFromMime(resolved.contentType ?? void 0);
506
- if (!message && kind) {
507
- message = kind === "image" ? "<media:image>" : `<media:${kind}>`;
508
- messageFromPlaceholder = true;
509
- }
510
- }
511
- if (message.trim() && !messageFromPlaceholder) if (textMode === "plain") textStyles = opts.textStyles ?? [];
512
- else {
513
- const tableMode = resolveMarkdownTableMode({
514
- cfg,
515
- channel: "signal",
516
- accountId: accountInfo.accountId
517
- });
518
- const formatted = markdownToSignalText(message, { tableMode });
519
- message = formatted.text;
520
- textStyles = formatted.styles;
521
- }
522
- if (!message.trim() && (!attachments || attachments.length === 0)) throw new Error("Signal send requires text or media");
523
- const params = { message };
524
- if (textStyles.length > 0) params["text-style"] = textStyles.map((style) => `${style.start}:${style.length}:${style.style}`);
525
- if (account) params.account = account;
526
- if (attachments && attachments.length > 0) params.attachments = attachments;
527
- const targetParams = buildTargetParams(target, {
528
- recipient: true,
529
- group: true,
530
- username: true
531
- });
532
- if (!targetParams) throw new Error("Signal recipient is required");
533
- Object.assign(params, targetParams);
534
- const timestamp = (await signalRpcRequest("send", params, {
535
- baseUrl,
536
- timeoutMs: opts.timeoutMs,
537
- apiMode
538
- }))?.timestamp;
539
- const messageId = timestamp ? String(timestamp) : "unknown";
540
- registerSignalApprovalReactionTargetForOutboundMessage({
541
- cfg,
542
- accountId: accountInfo.accountId,
543
- to,
544
- messageId,
545
- text: outboundText,
546
- targetAuthor: account
547
- });
548
- return {
549
- messageId,
550
- timestamp,
551
- receipt: createSignalSendReceipt({
552
- messageId,
553
- target,
554
- kind: attachments && attachments.length > 0 ? "media" : "text",
555
- ...timestamp != null ? { timestamp } : {}
556
- })
557
- };
558
- }
559
- async function sendTypingSignal(to, opts) {
560
- const accountInfo = await resolveSignalRpcAccountInfo(opts);
561
- const cfg = requireRuntimeConfig(opts.cfg, "Signal typing");
562
- const { baseUrl, account } = resolveSignalRpcContext(opts, accountInfo);
563
- const targetParams = buildTargetParams(parseTarget(to), {
564
- recipient: true,
565
- group: true
566
- });
567
- if (!targetParams) return false;
568
- const params = { ...targetParams };
569
- if (account) params.account = account;
570
- if (opts.stop) params.stop = true;
571
- await signalRpcRequest("sendTyping", params, {
572
- baseUrl,
573
- timeoutMs: opts.timeoutMs,
574
- apiMode: cfg.channels?.signal?.apiMode
575
- });
576
- return true;
577
- }
578
- async function sendReadReceiptSignal(to, targetTimestamp, opts) {
579
- if (!Number.isFinite(targetTimestamp) || targetTimestamp <= 0) return false;
580
- const accountInfo = await resolveSignalRpcAccountInfo(opts);
581
- const cfg = requireRuntimeConfig(opts.cfg, "Signal read receipt");
582
- const { baseUrl, account } = resolveSignalRpcContext(opts, accountInfo);
583
- const targetParams = buildTargetParams(parseTarget(to), { recipient: true });
584
- if (!targetParams) return false;
585
- const params = {
586
- ...targetParams,
587
- targetTimestamp,
588
- type: opts.type ?? "read"
589
- };
590
- if (account) params.account = account;
591
- await signalRpcRequest("sendReceipt", params, {
592
- baseUrl,
593
- timeoutMs: opts.timeoutMs,
594
- apiMode: cfg.channels?.signal?.apiMode
595
- });
596
- return true;
421
+ function clearSignalApprovalReactionTargetsForTest() {
422
+ signalApprovalReactionTargets.clearForTest();
423
+ resolverRuntimePromise = void 0;
597
424
  }
598
425
  //#endregion
599
- export { maybeResolveSignalApprovalReaction as a, resolveSignalApprovalTargetAuthorKeys as c, hasSignalApprovalReactionApprovers as i, unregisterSignalApprovalReactionTarget as l, sendReadReceiptSignal as n, registerSignalApprovalReactionTarget as o, sendTypingSignal as r, resolveSignalApprovalConversationKey as s, sendMessageSignal as t, setSignalRuntime as u };
426
+ export { registerSignalApprovalReactionTarget as a, resolveSignalApprovalTargetAuthorKeys as c, maybeResolveSignalApprovalReaction as i, unregisterSignalApprovalReactionTarget as l, approval_reactions_exports as n, registerSignalApprovalReactionTargetForDeliveredPayload as o, hasSignalApprovalReactionApprovers as r, resolveSignalApprovalConversationKey as s, addSignalApprovalReactionHintToStructuredPayload as t, setSignalRuntime as u };
@@ -1,6 +1,6 @@
1
1
  import { i as resolveSignalAccount, n as listSignalAccountIds, r as resolveDefaultSignalAccountId } from "./accounts-hOCHbEhX.js";
2
2
  import { c as resolveSignalSender, d as normalizeSignalMessagingTarget, l as looksLikeUuid, o as resolveSignalPeerId, s as resolveSignalRecipient, u as looksLikeSignalTargetId } from "./identity-B6O4k8xg.js";
3
- import { i as markdownToSignalTextChunks, n as resolveSignalReactionLevel, o as shouldSuppressLocalSignalExecApprovalPrompt, s as signalApprovalCapability, t as signalMessageActions } from "./message-actions-Bue0g2Kc.js";
3
+ import { i as markdownToSignalTextChunks, n as resolveSignalReactionLevel, o as shouldSuppressLocalSignalExecApprovalPrompt, s as signalApprovalCapability, t as signalMessageActions } from "./message-actions-BnugYXjB.js";
4
4
  import { t as SignalChannelConfigSchema } from "./config-schema-BiojLEsX.js";
5
5
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
6
6
  import { buildDmGroupAccountAllowlistAdapter } from "openclaw/plugin-sdk/allowlist-config-edit";
@@ -15,6 +15,7 @@ import { chunkText, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chun
15
15
  import { buildOutboundBaseSessionKey } from "openclaw/plugin-sdk/routing";
16
16
  import { buildBaseChannelStatusSummary, collectStatusIssuesFromLastError, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
17
17
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringifiedEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
18
+ import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
18
19
  import { normalizeE164 } from "openclaw/plugin-sdk/text-utility-runtime";
19
20
  import { createChannelPluginBase, getChatChannelMeta } from "openclaw/plugin-sdk/core";
20
21
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, createCliPathTextInput, createDelegatedSetupWizardProxy, createDelegatedTextInputShouldPrompt, createPatchedAccountSetupAdapter, createSetupInputPresenceValidator, createSetupTranslator, mergeAllowFromEntries, parseSetupEntriesAllowingWildcard, patchChannelConfigForAccount, promptParsedAllowFromForAccount, setAccountAllowFromForChannel, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup-runtime";
@@ -176,9 +177,7 @@ function createSignalCliPathTextInput(shouldPrompt) {
176
177
  accountId,
177
178
  credentialValues
178
179
  }),
179
- shouldPrompt,
180
- helpTitle: "Signal",
181
- helpLines: ["signal-cli not found. Install it, then rerun this step or set channels.signal.cliPath."]
180
+ shouldPrompt
182
181
  });
183
182
  }
184
183
  const signalNumberTextInput = {
@@ -236,7 +235,7 @@ function createSignalSetupWizardProxy(loadWizard) {
236
235
  //#region extensions/signal/src/shared.ts
237
236
  const SIGNAL_CHANNEL = "signal";
238
237
  async function loadSignalChannelRuntime() {
239
- return await import("./channel.runtime-B0-YpkC4.js");
238
+ return await import("./channel.runtime-C5skg4Q0.js");
240
239
  }
241
240
  const signalSetupWizard = createSignalSetupWizardProxy(async () => (await loadSignalChannelRuntime()).signalSetupWizard);
242
241
  const signalConfigAdapter = createScopedChannelConfigAdapter({
@@ -307,18 +306,23 @@ function createSignalPluginBase(params) {
307
306
  let signalMonitorModulePromise = null;
308
307
  let signalProbeModulePromise = null;
309
308
  let signalSendRuntimePromise = null;
309
+ let signalApprovalReactionsModulePromise = null;
310
310
  async function loadSignalMonitorModule() {
311
- signalMonitorModulePromise ??= import("./monitor-CUhIKHJo.js").then((n) => n.n);
311
+ signalMonitorModulePromise ??= import("./monitor-C1gdgT7W.js").then((n) => n.n);
312
312
  return await signalMonitorModulePromise;
313
313
  }
314
314
  async function loadSignalProbeModule() {
315
- signalProbeModulePromise ??= import("./probe-BQ_Izoya.js").then((n) => n.n);
315
+ signalProbeModulePromise ??= import("./probe-BvFJLOBi.js").then((n) => n.n);
316
316
  return await signalProbeModulePromise;
317
317
  }
318
318
  async function loadSignalSendRuntime() {
319
- signalSendRuntimePromise ??= import("./send.runtime-CfaZd8X4.js");
319
+ signalSendRuntimePromise ??= import("./send.runtime-BnjSh7QP.js");
320
320
  return await signalSendRuntimePromise;
321
321
  }
322
+ async function loadSignalApprovalReactionsModule() {
323
+ signalApprovalReactionsModulePromise ??= import("./approval-reactions-H-RPGBK7.js").then((n) => n.n);
324
+ return await signalApprovalReactionsModulePromise;
325
+ }
322
326
  async function resolveSignalSendContext(params) {
323
327
  return {
324
328
  send: resolveOutboundSendDep(params.deps, "signal") ?? (await loadSignalSendRuntime()).sendMessageSignal,
@@ -340,6 +344,16 @@ async function sendSignalOutbound(params) {
340
344
  accountId: params.accountId ?? void 0
341
345
  });
342
346
  }
347
+ function attachSignalVisibleText(result, visibleText) {
348
+ const meta = "meta" in result && result.meta && typeof result.meta === "object" ? result.meta : {};
349
+ return {
350
+ ...result,
351
+ meta: {
352
+ ...meta,
353
+ signalVisibleText: visibleText
354
+ }
355
+ };
356
+ }
343
357
  const signalMessageAdapter = defineChannelMessageAdapter({
344
358
  id: "signal",
345
359
  durableFinal: { capabilities: {
@@ -424,7 +438,7 @@ async function sendFormattedSignalText(ctx) {
424
438
  textMode: "plain",
425
439
  textStyles: chunk.styles
426
440
  });
427
- results.push(result);
441
+ results.push(attachSignalVisibleText(result, chunk.text));
428
442
  }
429
443
  return attachChannelToResults("signal", results);
430
444
  }
@@ -444,7 +458,7 @@ async function sendFormattedSignalMedia(ctx) {
444
458
  text: ctx.text,
445
459
  styles: []
446
460
  };
447
- return attachChannelToResult("signal", await send(ctx.to, formatted.text, {
461
+ return attachChannelToResult("signal", attachSignalVisibleText(await send(ctx.to, formatted.text, {
448
462
  cfg: ctx.cfg,
449
463
  mediaUrl: ctx.mediaUrl,
450
464
  mediaLocalRoots: ctx.mediaLocalRoots,
@@ -453,7 +467,37 @@ async function sendFormattedSignalMedia(ctx) {
453
467
  accountId: ctx.accountId ?? void 0,
454
468
  textMode: "plain",
455
469
  textStyles: formatted.styles
456
- }));
470
+ }), formatted.text));
471
+ }
472
+ async function registerDeliveredSignalApprovalPayloadForReactions(params) {
473
+ const account = resolveSignalAccount({
474
+ cfg: params.cfg,
475
+ accountId: params.target.accountId ?? void 0
476
+ });
477
+ if (!account.config.account) return;
478
+ const { registerSignalApprovalReactionTargetForDeliveredPayload } = await loadSignalApprovalReactionsModule();
479
+ registerSignalApprovalReactionTargetForDeliveredPayload({
480
+ cfg: params.cfg,
481
+ target: params.target,
482
+ payload: params.payload,
483
+ results: params.results,
484
+ targetAuthor: account.config.account
485
+ });
486
+ }
487
+ async function renderSignalApprovalPayloadForReactions(params) {
488
+ const account = resolveSignalAccount({
489
+ cfg: params.ctx.cfg,
490
+ accountId: params.ctx.accountId ?? void 0
491
+ });
492
+ if (!account.config.account) return null;
493
+ const { addSignalApprovalReactionHintToStructuredPayload } = await loadSignalApprovalReactionsModule();
494
+ return addSignalApprovalReactionHintToStructuredPayload({
495
+ cfg: params.ctx.cfg,
496
+ accountId: params.ctx.accountId ?? void 0,
497
+ to: params.ctx.to,
498
+ payload: params.payload,
499
+ targetAuthor: account.config.account
500
+ });
457
501
  }
458
502
  const signalPlugin = createChatChannelPlugin({
459
503
  base: {
@@ -567,12 +611,15 @@ const signalPlugin = createChatChannelPlugin({
567
611
  chunker: chunkText,
568
612
  chunkerMode: "text",
569
613
  textChunkLimit: 4e3,
614
+ sanitizeText: ({ text }) => sanitizeAssistantVisibleText(text),
570
615
  shouldSuppressLocalPayloadPrompt: ({ cfg, accountId, payload, hint }) => shouldSuppressLocalSignalExecApprovalPrompt({
571
616
  cfg,
572
617
  accountId,
573
618
  payload,
574
619
  hint
575
620
  }),
621
+ afterDeliverPayload: async (params) => await registerDeliveredSignalApprovalPayloadForReactions(params),
622
+ renderPresentation: async (params) => await renderSignalApprovalPayloadForReactions(params),
576
623
  sendFormattedText: async ({ cfg, to, text, accountId, deps, abortSignal }) => await sendFormattedSignalText({
577
624
  cfg,
578
625
  to,
@@ -1,2 +1,2 @@
1
- import { t as signalPlugin } from "./channel-LmY2UpOt.js";
1
+ import { t as signalPlugin } from "./channel-aQhurb0Z.js";
2
2
  export { signalPlugin };
@@ -1,6 +1,6 @@
1
1
  import { i as resolveSignalAccount, n as listSignalAccountIds } from "./accounts-hOCHbEhX.js";
2
- import { c as signalNumberTextInput, i as createSignalCliPathTextInput, o as signalCompletionNote, s as signalDmPolicy } from "./channel-LmY2UpOt.js";
3
- import { r as installSignalCli } from "./install-signal-cli-CXgTF3de.js";
2
+ import { c as signalNumberTextInput, i as createSignalCliPathTextInput, o as signalCompletionNote, s as signalDmPolicy } from "./channel-aQhurb0Z.js";
3
+ import { r as installSignalCli } from "./install-signal-cli-TwhJ0DGy.js";
4
4
  import { detectBinary } from "openclaw/plugin-sdk/setup-tools";
5
5
  import { createDetectedBinaryStatus, createSetupTranslator, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
6
6
  //#region extensions/signal/src/setup-surface.ts
@@ -1,3 +1,3 @@
1
1
  import { d as normalizeSignalMessagingTarget, i as isSignalSenderAllowed, u as looksLikeSignalTargetId } from "./identity-B6O4k8xg.js";
2
- import { a as looksLikeArchive, i as installSignalCliFromRelease, n as extractSignalCliArchive, o as pickAsset, r as installSignalCli, t as downloadToFile } from "./install-signal-cli-CXgTF3de.js";
2
+ import { a as looksLikeArchive, i as installSignalCliFromRelease, n as extractSignalCliArchive, o as pickAsset, r as installSignalCli, t as downloadToFile } from "./install-signal-cli-TwhJ0DGy.js";
3
3
  export { downloadToFile, extractSignalCliArchive, installSignalCli, installSignalCliFromRelease, isSignalSenderAllowed, looksLikeArchive, looksLikeSignalTargetId, normalizeSignalMessagingTarget, pickAsset };
@@ -1,6 +1,8 @@
1
1
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
2
+ import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
2
3
  import fs from "node:fs/promises";
3
4
  import path from "node:path";
5
+ import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
4
6
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
7
  import { CONFIG_DIR, extractArchive, resolveBrewExecutable } from "openclaw/plugin-sdk/setup-tools";
6
8
  import { createWriteStream } from "node:fs";
@@ -50,7 +52,7 @@ function pickAsset(assets, platform, arch) {
50
52
  if (arch === "x64") return byName(/linux-native/) || byName(/linux/) || archives[0];
51
53
  return;
52
54
  }
53
- if (platform === "darwin") return byName(/macos|osx|darwin/) || archives[0];
55
+ if (platform === "darwin") return byName(/macos|osx|darwin/);
54
56
  if (platform === "win32") return byName(/windows|win/) || archives[0];
55
57
  return archives[0];
56
58
  }
@@ -130,7 +132,7 @@ async function installSignalCliViaBrew(runtime) {
130
132
  const brewExe = resolveBrewExecutable();
131
133
  if (!brewExe) return {
132
134
  ok: false,
133
- error: `No native signal-cli build is available for ${process.arch}. Install Homebrew (https://brew.sh) and try again, or install signal-cli manually.`
135
+ error: `No native signal-cli build is available for ${process.platform}/${process.arch}. Install Homebrew (https://brew.sh) and try again, or install signal-cli manually.`
134
136
  };
135
137
  runtime.log(`Installing signal-cli via Homebrew (${brewExe})…`);
136
138
  const result = await runPluginCommandWithTimeout({
@@ -143,7 +145,7 @@ async function installSignalCliViaBrew(runtime) {
143
145
  });
144
146
  if (result.code !== 0) return {
145
147
  ok: false,
146
- error: `brew install signal-cli failed (exit ${result.code}): ${result.stderr.trim().slice(0, 200)}`
148
+ error: `brew install signal-cli failed (exit ${result.code}): ${truncateUtf16Safe(result.stderr.trim(), 200)}`
147
149
  };
148
150
  const cliPath = await resolveBrewSignalCliPath(brewExe);
149
151
  if (!cliPath) return {
@@ -184,7 +186,7 @@ async function installSignalCliFromRelease(runtime) {
184
186
  error: `Failed to fetch release info (${response.status})`
185
187
  };
186
188
  try {
187
- payload = await response.json();
189
+ payload = await readProviderJsonResponse(response, "signal.release-info");
188
190
  } catch {
189
191
  return {
190
192
  ok: false,
@@ -236,7 +238,7 @@ async function installSignalCli(runtime) {
236
238
  ok: false,
237
239
  error: "Signal CLI auto-install is not supported on Windows yet."
238
240
  };
239
- if (process.platform !== "linux" || process.arch === "x64") return installSignalCliFromRelease(runtime);
241
+ if (process.platform === "linux" && process.arch === "x64") return installSignalCliFromRelease(runtime);
240
242
  return installSignalCliViaBrew(runtime);
241
243
  }
242
244
  //#endregion
@@ -1,16 +1,16 @@
1
1
  import { i as resolveSignalAccount, n as listSignalAccountIds, r as resolveDefaultSignalAccountId, t as listEnabledSignalAccounts } from "./accounts-hOCHbEhX.js";
2
2
  import { d as normalizeSignalMessagingTarget, l as looksLikeUuid } from "./identity-B6O4k8xg.js";
3
- import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-C_PQ45D9.js";
3
+ import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-BRJCLiqK.js";
4
4
  import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
5
5
  import { resolveReactionLevel } from "openclaw/plugin-sdk/status-helpers";
6
6
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
+ import { markdownToIR, renderMarkdownIRChunksWithinLimit } from "openclaw/plugin-sdk/text-chunking";
7
8
  import { createChannelApprovalCapability } from "openclaw/plugin-sdk/approval-delivery-runtime";
8
9
  import { createLazyChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
9
10
  import { createChannelApproverDmTargetResolver, createChannelNativeOriginTargetResolver, createNativeApprovalChannelRouteGates, createNativeApprovalForwardingFallbackSuppressor, shouldSuppressLocalNativeExecApprovalPrompt } from "openclaw/plugin-sdk/approval-native-runtime";
10
11
  import { buildApprovalReactionPendingContentForRequest } from "openclaw/plugin-sdk/approval-reaction-runtime";
11
12
  import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
12
13
  import { normalizeE164 } from "openclaw/plugin-sdk/text-utility-runtime";
13
- import { markdownToIR, renderMarkdownIRChunksWithinLimit } from "openclaw/plugin-sdk/text-chunking";
14
14
  import { createActionGate, jsonResult, readStringParam, resolveReactionMessageId } from "openclaw/plugin-sdk/channel-actions";
15
15
  import { parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
16
16
  //#region extensions/signal/src/approval-auth.ts
@@ -233,7 +233,7 @@ const signalApprovalCapability = createChannelApprovalCapability({
233
233
  accountId,
234
234
  request
235
235
  }),
236
- load: async () => (await import("./approval-handler.runtime-CeJI1wow.js")).signalApprovalNativeRuntime
236
+ load: async () => (await import("./approval-handler.runtime-C4S7IAvt.js")).signalApprovalNativeRuntime
237
237
  })
238
238
  });
239
239
  //#endregion
@@ -1,16 +1,17 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { i as resolveSignalAccount } from "./accounts-hOCHbEhX.js";
3
3
  import { a as normalizeSignalAllowRecipient, c as resolveSignalSender, d as normalizeSignalMessagingTarget, i as isSignalSenderAllowed, l as looksLikeUuid, n as formatSignalSenderDisplay, o as resolveSignalPeerId, r as formatSignalSenderId, s as resolveSignalRecipient, t as formatSignalPairingIdLine } from "./identity-B6O4k8xg.js";
4
- import { a as isSignalNativeApprovalHandlerConfigured } from "./message-actions-Bue0g2Kc.js";
5
- import { a as signalRpcRequest, i as signalCheck, o as streamSignalEvents } from "./reaction-runtime-api-C_PQ45D9.js";
6
- import { a as maybeResolveSignalApprovalReaction, n as sendReadReceiptSignal, r as sendTypingSignal, s as resolveSignalApprovalConversationKey, t as sendMessageSignal } from "./send-CBlFUkY_.js";
4
+ import { a as isSignalNativeApprovalHandlerConfigured } from "./message-actions-BnugYXjB.js";
5
+ import { a as signalRpcRequest, i as signalCheck, o as streamSignalEvents } from "./reaction-runtime-api-BRJCLiqK.js";
6
+ import { i as maybeResolveSignalApprovalReaction, o as registerSignalApprovalReactionTargetForDeliveredPayload, s as resolveSignalApprovalConversationKey, t as addSignalApprovalReactionHintToStructuredPayload } from "./approval-reactions-H-RPGBK7.js";
7
+ import { n as sendReadReceiptSignal, r as sendTypingSignal, t as sendMessageSignal } from "./send-De6LIOLq.js";
7
8
  import { createChannelMessageReplyPipeline } from "openclaw/plugin-sdk/channel-outbound";
8
9
  import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
9
10
  import { detectMime, estimateBase64DecodedBytes, kindFromMime, saveMediaBuffer } from "openclaw/plugin-sdk/media-runtime";
10
11
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
11
12
  import { normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
12
13
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
13
- import { normalizeE164 } from "openclaw/plugin-sdk/text-utility-runtime";
14
+ import { normalizeE164, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
14
15
  import path from "node:path";
15
16
  import { resolveChannelGroupPolicy, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
16
17
  import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
@@ -473,7 +474,7 @@ function createSignalEventHandler(deps) {
473
474
  extra: { GroupSubject: entry.isGroup ? entry.groupName ?? void 0 : void 0 }
474
475
  });
475
476
  if (shouldLogVerbose()) {
476
- const preview = body.slice(0, 200).replace(/\\n/g, "\\\\n");
477
+ const preview = truncateUtf16Safe(body, 200).replace(/\\n/g, "\\\\n");
477
478
  logVerbose(`signal inbound: from=${ctxPayload.From} len=${body.length} preview="${preview}"`);
478
479
  }
479
480
  const { onModelSelected, typingCallbacks, ...replyPipeline } = createChannelMessageReplyPipeline({
@@ -1026,7 +1027,10 @@ async function runSignalSseLoop({ baseUrl, account, abortSignal, runtime, onEven
1026
1027
  }
1027
1028
  //#endregion
1028
1029
  //#region extensions/signal/src/monitor.ts
1029
- var monitor_exports = /* @__PURE__ */ __exportAll({ monitorSignalProvider: () => monitorSignalProvider });
1030
+ var monitor_exports = /* @__PURE__ */ __exportAll({
1031
+ deliverReplies: () => deliverReplies,
1032
+ monitorSignalProvider: () => monitorSignalProvider
1033
+ });
1030
1034
  function resolveRuntime(opts) {
1031
1035
  return opts.runtime ?? createNonExitingRuntime();
1032
1036
  }
@@ -1220,30 +1224,63 @@ async function fetchAttachment(params) {
1220
1224
  }
1221
1225
  async function deliverReplies(params) {
1222
1226
  const { replies, target, baseUrl, account, accountId, runtime, maxBytes, textLimit, chunkMode } = params;
1223
- for (const payload of replies) if (await deliverTextOrMediaReply({
1224
- payload,
1225
- text: resolveSendableOutboundReplyParts(payload).text,
1226
- chunkText: (value) => chunkTextWithMode(value, textLimit, chunkMode),
1227
- sendText: async (chunk) => {
1228
- await sendMessageSignal(target, chunk, {
1229
- cfg: params.cfg,
1230
- baseUrl,
1231
- account,
1232
- maxBytes,
1233
- accountId
1227
+ for (const payload of replies) {
1228
+ const deliveryResults = [];
1229
+ const deliveredPayload = addSignalApprovalReactionHintToStructuredPayload({
1230
+ cfg: params.cfg,
1231
+ accountId,
1232
+ to: target,
1233
+ payload,
1234
+ targetAuthor: account
1235
+ }) ?? payload;
1236
+ const reply = resolveSendableOutboundReplyParts(deliveredPayload);
1237
+ const recordDeliveryResult = (result, visibleText) => {
1238
+ const messageId = typeof result?.messageId === "string" && result.messageId.trim() ? result.messageId.trim() : null;
1239
+ if (messageId) deliveryResults.push({
1240
+ channel: "signal",
1241
+ messageId,
1242
+ meta: { signalVisibleText: visibleText }
1234
1243
  });
1235
- },
1236
- sendMedia: async ({ mediaUrl, caption }) => {
1237
- await sendMessageSignal(target, caption ?? "", {
1244
+ };
1245
+ if (await deliverTextOrMediaReply({
1246
+ payload: deliveredPayload,
1247
+ text: reply.text,
1248
+ chunkText: (value) => chunkTextWithMode(value, textLimit, chunkMode),
1249
+ sendText: async (chunk) => {
1250
+ recordDeliveryResult(await sendMessageSignal(target, chunk, {
1251
+ cfg: params.cfg,
1252
+ baseUrl,
1253
+ account,
1254
+ maxBytes,
1255
+ accountId
1256
+ }), chunk);
1257
+ },
1258
+ sendMedia: async ({ mediaUrl, caption }) => {
1259
+ const visibleText = caption ?? "";
1260
+ recordDeliveryResult(await sendMessageSignal(target, visibleText, {
1261
+ cfg: params.cfg,
1262
+ baseUrl,
1263
+ account,
1264
+ mediaUrl,
1265
+ maxBytes,
1266
+ accountId
1267
+ }), visibleText);
1268
+ }
1269
+ }) !== "empty") {
1270
+ registerSignalApprovalReactionTargetForDeliveredPayload({
1238
1271
  cfg: params.cfg,
1239
- baseUrl,
1240
- account,
1241
- mediaUrl,
1242
- maxBytes,
1243
- accountId
1272
+ target: {
1273
+ channel: "signal",
1274
+ to: target,
1275
+ accountId
1276
+ },
1277
+ payload: deliveredPayload,
1278
+ results: deliveryResults,
1279
+ targetAuthor: account
1244
1280
  });
1281
+ runtime.log?.(`delivered reply to ${target}`);
1245
1282
  }
1246
- }) !== "empty") runtime.log?.(`delivered reply to ${target}`);
1283
+ }
1247
1284
  }
1248
1285
  async function monitorSignalProvider(opts = {}) {
1249
1286
  const runtime = resolveRuntime(opts);
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { a as signalRpcRequest, i as signalCheck } from "./reaction-runtime-api-C_PQ45D9.js";
2
+ import { a as signalRpcRequest, i as signalCheck } from "./reaction-runtime-api-BRJCLiqK.js";
3
3
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
4
4
  //#region extensions/signal/src/probe.ts
5
5
  var probe_exports = /* @__PURE__ */ __exportAll({ probeSignal: () => probeSignal });
@@ -6,6 +6,7 @@ import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime"
6
6
  import fs from "node:fs/promises";
7
7
  import path from "node:path";
8
8
  import { resolveFetch } from "openclaw/plugin-sdk/fetch-runtime";
9
+ import { readProviderTextResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
9
10
  import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
10
11
  import WebSocket from "ws";
11
12
  import { Buffer as Buffer$1 } from "node:buffer";
@@ -176,12 +177,16 @@ async function containerRestRequest(endpoint, opts, method = "GET", body) {
176
177
  const res = await fetchWithTimeout(url, init, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS$2);
177
178
  if (res.status === 204) return;
178
179
  if (!res.ok) {
179
- const errorText = await res.text().catch(() => "");
180
+ const errorText = await readResponseTextLimited(res).catch(() => "");
180
181
  throw new Error(`Signal REST ${res.status}: ${errorText || res.statusText}`);
181
182
  }
182
- const text = await res.text();
183
+ const text = await readProviderTextResponse(res, "Signal REST");
183
184
  if (!text) return;
184
- return JSON.parse(text);
185
+ try {
186
+ return JSON.parse(text);
187
+ } catch {
188
+ throw new Error("Signal REST returned malformed JSON");
189
+ }
185
190
  }
186
191
  /**
187
192
  * Fetch attachment binary from bbernhard container.
@@ -1,2 +1,2 @@
1
- import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-C_PQ45D9.js";
1
+ import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-BRJCLiqK.js";
2
2
  export { removeReactionSignal, sendReactionSignal };
@@ -1,12 +1,13 @@
1
1
  import { i as resolveSignalAccount, n as listSignalAccountIds, r as resolveDefaultSignalAccountId, t as listEnabledSignalAccounts } from "./accounts-hOCHbEhX.js";
2
2
  import { d as normalizeSignalMessagingTarget, u as looksLikeSignalTargetId } from "./identity-B6O4k8xg.js";
3
- import { n as resolveSignalReactionLevel, t as signalMessageActions } from "./message-actions-Bue0g2Kc.js";
4
- import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-C_PQ45D9.js";
3
+ import { n as resolveSignalReactionLevel, t as signalMessageActions } from "./message-actions-BnugYXjB.js";
4
+ import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-BRJCLiqK.js";
5
5
  import { n as buildChannelConfigSchema, t as SignalConfigSchema } from "./config-api-KS-qhQvD.js";
6
- import { r as installSignalCli } from "./install-signal-cli-CXgTF3de.js";
7
- import { t as monitorSignalProvider } from "./monitor-CUhIKHJo.js";
8
- import { t as sendMessageSignal, u as setSignalRuntime } from "./send-CBlFUkY_.js";
9
- import { t as probeSignal } from "./probe-BQ_Izoya.js";
6
+ import { r as installSignalCli } from "./install-signal-cli-TwhJ0DGy.js";
7
+ import { u as setSignalRuntime } from "./approval-reactions-H-RPGBK7.js";
8
+ import { t as monitorSignalProvider } from "./monitor-C1gdgT7W.js";
9
+ import { t as sendMessageSignal } from "./send-De6LIOLq.js";
10
+ import { t as probeSignal } from "./probe-BvFJLOBi.js";
10
11
  import { PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/channel-status";
11
12
  import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/media-runtime";
12
13
  import { buildBaseAccountStatusSnapshot, buildBaseChannelStatusSummary, collectStatusIssuesFromLastError, createDefaultChannelRuntimeState } from "openclaw/plugin-sdk/status-helpers";
@@ -0,0 +1,186 @@
1
+ import { i as resolveSignalAccount } from "./accounts-hOCHbEhX.js";
2
+ import { r as markdownToSignalText } from "./message-actions-BnugYXjB.js";
3
+ import { a as signalRpcRequest, r as resolveSignalRpcContext } from "./reaction-runtime-api-BRJCLiqK.js";
4
+ import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
5
+ import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
6
+ import { kindFromMime, resolveOutboundAttachmentFromUrl } from "openclaw/plugin-sdk/media-runtime";
7
+ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
8
+ import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
9
+ //#region extensions/signal/src/send.ts
10
+ async function resolveSignalRpcAccountInfo(opts) {
11
+ if (opts.baseUrl?.trim() && opts.account?.trim()) return;
12
+ if (!opts.cfg) throw new Error("Signal RPC account resolution requires a resolved runtime config. Load and resolve config at the command or gateway boundary, then pass cfg through the runtime path.");
13
+ return resolveSignalAccount({
14
+ cfg: requireRuntimeConfig(opts.cfg, "Signal RPC account resolution"),
15
+ accountId: opts.accountId
16
+ });
17
+ }
18
+ function parseTarget(raw) {
19
+ let value = raw.trim();
20
+ if (!value) throw new Error("Signal recipient is required");
21
+ if (normalizeLowercaseStringOrEmpty(value).startsWith("signal:")) value = value.slice(7).trim();
22
+ const normalized = normalizeLowercaseStringOrEmpty(value);
23
+ if (normalized.startsWith("group:")) return {
24
+ type: "group",
25
+ groupId: value.slice(6).trim()
26
+ };
27
+ if (normalized.startsWith("username:")) return {
28
+ type: "username",
29
+ username: value.slice(9).trim()
30
+ };
31
+ if (normalized.startsWith("u:")) return {
32
+ type: "username",
33
+ username: value.trim()
34
+ };
35
+ return {
36
+ type: "recipient",
37
+ recipient: value
38
+ };
39
+ }
40
+ function buildTargetParams(target, allow) {
41
+ if (target.type === "recipient") {
42
+ if (!allow.recipient) return null;
43
+ return { recipient: [target.recipient] };
44
+ }
45
+ if (target.type === "group") {
46
+ if (!allow.group) return null;
47
+ return { groupId: target.groupId };
48
+ }
49
+ if (target.type === "username") {
50
+ if (!allow.username) return null;
51
+ return { username: [target.username] };
52
+ }
53
+ return null;
54
+ }
55
+ function createSignalSendReceipt(params) {
56
+ const messageId = params.messageId.trim();
57
+ const results = messageId && messageId !== "unknown" ? [{
58
+ channel: "signal",
59
+ messageId,
60
+ meta: { targetType: params.target.type }
61
+ }] : [];
62
+ if (results[0]) {
63
+ if (params.timestamp != null) results[0].timestamp = params.timestamp;
64
+ if (params.target.type === "group") results[0].chatId = params.target.groupId;
65
+ else if (params.target.type === "recipient") results[0].toJid = params.target.recipient;
66
+ else results[0].toJid = params.target.username;
67
+ }
68
+ return createMessageReceiptFromOutboundResults({
69
+ results,
70
+ kind: params.kind
71
+ });
72
+ }
73
+ async function sendMessageSignal(to, text, opts) {
74
+ const cfg = requireRuntimeConfig(opts.cfg, "Signal send");
75
+ const apiMode = cfg.channels?.signal?.apiMode;
76
+ const accountInfo = resolveSignalAccount({
77
+ cfg,
78
+ accountId: opts.accountId
79
+ });
80
+ const { baseUrl, account } = resolveSignalRpcContext(opts, accountInfo);
81
+ const target = parseTarget(to);
82
+ let message = text ?? "";
83
+ let messageFromPlaceholder = false;
84
+ let textStyles = [];
85
+ const textMode = opts.textMode ?? "markdown";
86
+ const maxBytes = (() => {
87
+ if (typeof opts.maxBytes === "number") return opts.maxBytes;
88
+ if (typeof accountInfo.config.mediaMaxMb === "number") return accountInfo.config.mediaMaxMb * 1024 * 1024;
89
+ if (typeof cfg.agents?.defaults?.mediaMaxMb === "number") return cfg.agents.defaults.mediaMaxMb * 1024 * 1024;
90
+ return 8 * 1024 * 1024;
91
+ })();
92
+ let attachments;
93
+ if (opts.mediaUrl?.trim()) {
94
+ const resolved = await resolveOutboundAttachmentFromUrl(opts.mediaUrl.trim(), maxBytes, {
95
+ mediaAccess: opts.mediaAccess,
96
+ localRoots: opts.mediaLocalRoots,
97
+ readFile: opts.mediaReadFile
98
+ });
99
+ attachments = [resolved.path];
100
+ const kind = kindFromMime(resolved.contentType ?? void 0);
101
+ if (!message && kind) {
102
+ message = kind === "image" ? "<media:image>" : `<media:${kind}>`;
103
+ messageFromPlaceholder = true;
104
+ }
105
+ }
106
+ if (message.trim() && !messageFromPlaceholder) if (textMode === "plain") textStyles = opts.textStyles ?? [];
107
+ else {
108
+ const tableMode = resolveMarkdownTableMode({
109
+ cfg,
110
+ channel: "signal",
111
+ accountId: accountInfo.accountId
112
+ });
113
+ const formatted = markdownToSignalText(message, { tableMode });
114
+ message = formatted.text;
115
+ textStyles = formatted.styles;
116
+ }
117
+ if (!message.trim() && (!attachments || attachments.length === 0)) throw new Error("Signal send requires text or media");
118
+ const params = { message };
119
+ if (textStyles.length > 0) params["text-style"] = textStyles.map((style) => `${style.start}:${style.length}:${style.style}`);
120
+ if (account) params.account = account;
121
+ if (attachments && attachments.length > 0) params.attachments = attachments;
122
+ const targetParams = buildTargetParams(target, {
123
+ recipient: true,
124
+ group: true,
125
+ username: true
126
+ });
127
+ if (!targetParams) throw new Error("Signal recipient is required");
128
+ Object.assign(params, targetParams);
129
+ const timestamp = (await signalRpcRequest("send", params, {
130
+ baseUrl,
131
+ timeoutMs: opts.timeoutMs,
132
+ apiMode
133
+ }))?.timestamp;
134
+ const messageId = timestamp ? String(timestamp) : "unknown";
135
+ return {
136
+ messageId,
137
+ timestamp,
138
+ receipt: createSignalSendReceipt({
139
+ messageId,
140
+ target,
141
+ kind: attachments && attachments.length > 0 ? "media" : "text",
142
+ ...timestamp != null ? { timestamp } : {}
143
+ })
144
+ };
145
+ }
146
+ async function sendTypingSignal(to, opts) {
147
+ const accountInfo = await resolveSignalRpcAccountInfo(opts);
148
+ const cfg = requireRuntimeConfig(opts.cfg, "Signal typing");
149
+ const { baseUrl, account } = resolveSignalRpcContext(opts, accountInfo);
150
+ const targetParams = buildTargetParams(parseTarget(to), {
151
+ recipient: true,
152
+ group: true
153
+ });
154
+ if (!targetParams) return false;
155
+ const params = { ...targetParams };
156
+ if (account) params.account = account;
157
+ if (opts.stop) params.stop = true;
158
+ await signalRpcRequest("sendTyping", params, {
159
+ baseUrl,
160
+ timeoutMs: opts.timeoutMs,
161
+ apiMode: cfg.channels?.signal?.apiMode
162
+ });
163
+ return true;
164
+ }
165
+ async function sendReadReceiptSignal(to, targetTimestamp, opts) {
166
+ if (!Number.isFinite(targetTimestamp) || targetTimestamp <= 0) return false;
167
+ const accountInfo = await resolveSignalRpcAccountInfo(opts);
168
+ const cfg = requireRuntimeConfig(opts.cfg, "Signal read receipt");
169
+ const { baseUrl, account } = resolveSignalRpcContext(opts, accountInfo);
170
+ const targetParams = buildTargetParams(parseTarget(to), { recipient: true });
171
+ if (!targetParams) return false;
172
+ const params = {
173
+ ...targetParams,
174
+ targetTimestamp,
175
+ type: opts.type ?? "read"
176
+ };
177
+ if (account) params.account = account;
178
+ await signalRpcRequest("sendReceipt", params, {
179
+ baseUrl,
180
+ timeoutMs: opts.timeoutMs,
181
+ apiMode: cfg.channels?.signal?.apiMode
182
+ });
183
+ return true;
184
+ }
185
+ //#endregion
186
+ export { sendReadReceiptSignal as n, sendTypingSignal as r, sendMessageSignal as t };
@@ -1,2 +1,2 @@
1
- import { r as sendTypingSignal, t as sendMessageSignal } from "./send-CBlFUkY_.js";
1
+ import { r as sendTypingSignal, t as sendMessageSignal } from "./send-De6LIOLq.js";
2
2
  export { sendMessageSignal, sendTypingSignal };
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/signal",
3
- "version": "2026.6.11",
3
+ "version": "2026.7.1-beta.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/signal",
9
- "version": "2026.6.11",
9
+ "version": "2026.7.1-beta.1",
10
10
  "dependencies": {
11
11
  "ws": "8.21.0"
12
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/signal",
3
- "version": "2026.6.11",
3
+ "version": "2026.7.1-beta.1",
4
4
  "description": "OpenClaw Signal channel plugin",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -44,10 +44,10 @@
44
44
  "allowInvalidConfigRecovery": true
45
45
  },
46
46
  "compat": {
47
- "pluginApi": ">=2026.6.11"
47
+ "pluginApi": ">=2026.7.1-beta.1"
48
48
  },
49
49
  "build": {
50
- "openclawVersion": "2026.6.11",
50
+ "openclawVersion": "2026.7.1-beta.1",
51
51
  "bundledDist": false
52
52
  },
53
53
  "release": {
@@ -70,7 +70,7 @@
70
70
  "README.md"
71
71
  ],
72
72
  "peerDependencies": {
73
- "openclaw": ">=2026.6.11"
73
+ "openclaw": ">=2026.7.1-beta.1"
74
74
  },
75
75
  "peerDependenciesMeta": {
76
76
  "openclaw": {