@openclaw/signal 2026.7.1-beta.1 → 2026.7.1-beta.4

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,5 +1,5 @@
1
1
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
2
- import { createAccountListHelpers, normalizeAccountId, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
2
+ import { createAccountListHelpers, normalizeAccountId, resolveAccountEntry, resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
3
3
  //#region extensions/signal/src/accounts.ts
4
4
  const { listAccountIds, resolveDefaultAccountId } = createAccountListHelpers("signal", { implicitDefaultAccount: { channelKeys: ["account"] } });
5
5
  const listSignalAccountIds = listAccountIds;
@@ -8,7 +8,8 @@ function mergeSignalAccountConfig(cfg, accountId) {
8
8
  return resolveMergedAccountConfig({
9
9
  channelConfig: cfg.channels?.signal,
10
10
  accounts: cfg.channels?.signal?.accounts,
11
- accountId
11
+ accountId,
12
+ nestedObjectKeys: ["aliases"]
12
13
  });
13
14
  }
14
15
  function resolveSignalAccount(params) {
@@ -36,5 +37,23 @@ function listEnabledSignalAccounts(cfg) {
36
37
  accountId
37
38
  })).filter((account) => account.enabled);
38
39
  }
40
+ function normalizeSignalReplyToMode(value) {
41
+ return value === "off" || value === "first" || value === "all" || value === "batched" ? value : void 0;
42
+ }
43
+ function resolveSignalReplyToMode(params) {
44
+ const accountId = normalizeAccountId(params.accountId ?? resolveDefaultSignalAccountId(params.cfg));
45
+ const signalConfig = params.cfg.channels?.signal;
46
+ const accountConfig = resolveAccountEntry(signalConfig?.accounts, accountId);
47
+ const chatType = params.chatType === "direct" || params.chatType === "group" ? params.chatType : void 0;
48
+ if (chatType) {
49
+ const accountScoped = normalizeSignalReplyToMode(accountConfig?.replyToModeByChatType?.[chatType]);
50
+ if (accountScoped) return accountScoped;
51
+ const accountDefault = normalizeSignalReplyToMode(accountConfig?.replyToMode);
52
+ if (accountDefault) return accountDefault;
53
+ const channelScoped = normalizeSignalReplyToMode(signalConfig?.replyToModeByChatType?.[chatType]);
54
+ if (channelScoped) return channelScoped;
55
+ }
56
+ return normalizeSignalReplyToMode(accountConfig?.replyToMode) ?? normalizeSignalReplyToMode(signalConfig?.replyToMode) ?? "all";
57
+ }
39
58
  //#endregion
40
- export { resolveSignalAccount as i, listSignalAccountIds as n, resolveDefaultSignalAccountId as r, listEnabledSignalAccounts as t };
59
+ export { resolveSignalReplyToMode as a, resolveSignalAccount as i, listSignalAccountIds as n, resolveDefaultSignalAccountId as r, listEnabledSignalAccounts as t };
package/dist/api.js CHANGED
@@ -1,12 +1,12 @@
1
- import { i as resolveSignalAccount, n as listSignalAccountIds, r as resolveDefaultSignalAccountId, t as listEnabledSignalAccounts } from "./accounts-hOCHbEhX.js";
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-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";
1
+ import { i as resolveSignalAccount, n as listSignalAccountIds, r as resolveDefaultSignalAccountId, t as listEnabledSignalAccounts } from "./accounts-Bz4HtP0g.js";
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-C8-yk4J9.js";
3
+ import { i as markdownToSignalTextChunks, n as resolveSignalReactionLevel, r as markdownToSignalText, t as signalMessageActions } from "./message-actions-3OLDgjis.js";
4
+ import { n as sendReactionSignal, t as removeReactionSignal } from "./reaction-runtime-api-EQ7cc7-Y.js";
5
+ import { a as normalizeSignalAccountInput, l as signalSetupAdapter, n as createSignalPluginBase, r as signalSetupWizard, t as signalPlugin, u as resolveSignalOutboundTarget } from "./channel-Cnhy1RwG.js";
6
+ import { a as looksLikeArchive, n as extractSignalCliArchive, o as pickAsset, r as installSignalCli } from "./install-signal-cli-ik8VPaGg.js";
7
+ import { t as monitorSignalProvider } from "./monitor-Bee8O5Ay.js";
8
+ import { n as sendReadReceiptSignal, r as sendTypingSignal, t as sendMessageSignal } from "./send-CUJy6WPt.js";
9
+ import { t as probeSignal } from "./probe-Cnht8Ihg.js";
10
10
  //#region extensions/signal/src/channel.setup.ts
11
11
  const signalSetupPlugin = { ...createSignalPluginBase({
12
12
  setupWizard: signalSetupWizard,
@@ -0,0 +1,151 @@
1
+ import { i as resolveSignalAccount } from "./accounts-Bz4HtP0g.js";
2
+ import { d as normalizeSignalMessagingTarget, l as looksLikeUuid, u as looksLikeSignalTargetId } from "./identity-C8-yk4J9.js";
3
+ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
4
+ import { createResolvedApproverActionAuthAdapter, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
5
+ import { normalizeE164 } from "openclaw/plugin-sdk/text-utility-runtime";
6
+ //#region extensions/signal/src/aliases.ts
7
+ function normalizeAliasKey(raw) {
8
+ const trimmed = raw.trim();
9
+ if (!trimmed) return;
10
+ return normalizeLowercaseStringOrEmpty(/^signal:/i.test(trimmed) ? trimmed.slice(7).trim() : trimmed) || void 0;
11
+ }
12
+ function resolveAliasMap(params) {
13
+ const account = resolveSignalAccount({
14
+ cfg: params.cfg,
15
+ accountId: params.accountId
16
+ });
17
+ const aliases = /* @__PURE__ */ new Map();
18
+ for (const [rawKey, rawValue] of Object.entries(account.config.aliases ?? {})) {
19
+ const key = normalizeAliasKey(rawKey);
20
+ if (!key) continue;
21
+ aliases.set(key, rawValue);
22
+ }
23
+ return aliases;
24
+ }
25
+ function resolveTargetKind(target) {
26
+ return normalizeLowercaseStringOrEmpty(target).startsWith("group:") ? "group" : "user";
27
+ }
28
+ function resolveRawSignalTarget(input) {
29
+ const normalized = normalizeSignalMessagingTarget(input);
30
+ if (!normalized || !looksLikeSignalTargetId(input, normalized)) return null;
31
+ return {
32
+ to: normalized,
33
+ kind: resolveTargetKind(normalized)
34
+ };
35
+ }
36
+ function resolveSignalAliasTargetFromMap(params) {
37
+ const initialAlias = normalizeAliasKey(params.input);
38
+ if (!initialAlias || !params.aliases.has(initialAlias)) return null;
39
+ const visited = /* @__PURE__ */ new Set();
40
+ let alias = initialAlias;
41
+ for (;;) {
42
+ if (visited.has(alias)) throw new Error(`Signal alias "${initialAlias}" resolves recursively through "${alias}".`);
43
+ visited.add(alias);
44
+ const rawValue = params.aliases.get(alias);
45
+ if (typeof rawValue !== "string" || !rawValue.trim()) throw new Error(`Signal alias "${alias}" must point to a non-empty Signal target.`);
46
+ const rawTarget = resolveRawSignalTarget(rawValue);
47
+ if (rawTarget) return {
48
+ ...rawTarget,
49
+ alias: initialAlias
50
+ };
51
+ const nextAlias = normalizeAliasKey(rawValue);
52
+ if (nextAlias && params.aliases.has(nextAlias)) {
53
+ alias = nextAlias;
54
+ continue;
55
+ }
56
+ throw new Error(`Signal alias "${initialAlias}" must point to an E.164 number, uuid:<id>, username:<name>, or group:<id>.`);
57
+ }
58
+ }
59
+ function resolveSignalAliasTarget(params) {
60
+ return resolveSignalAliasTargetFromMap({
61
+ aliases: resolveAliasMap(params),
62
+ input: params.input
63
+ });
64
+ }
65
+ function resolveSignalTarget(params) {
66
+ const rawTarget = resolveRawSignalTarget(params.input);
67
+ if (rawTarget) return {
68
+ ...rawTarget,
69
+ source: "raw"
70
+ };
71
+ const aliasTarget = resolveSignalAliasTarget(params);
72
+ if (aliasTarget) return {
73
+ ...aliasTarget,
74
+ source: "alias"
75
+ };
76
+ return null;
77
+ }
78
+ function listSignalAliasDirectoryEntries(params) {
79
+ const aliases = resolveAliasMap(params);
80
+ const query = normalizeLowercaseStringOrEmpty(params.query);
81
+ const limit = typeof params.limit === "number" && params.limit > 0 ? params.limit : void 0;
82
+ const exactAlias = params.query ? normalizeAliasKey(params.query) : void 0;
83
+ if (exactAlias && aliases.has(exactAlias)) {
84
+ const target = resolveSignalAliasTargetFromMap({
85
+ aliases,
86
+ input: exactAlias
87
+ });
88
+ if (target?.kind === params.kind) return [{
89
+ kind: params.kind,
90
+ id: target.to,
91
+ name: target.alias
92
+ }];
93
+ if (target) return [];
94
+ }
95
+ const entries = [];
96
+ for (const alias of aliases.keys()) {
97
+ let target;
98
+ try {
99
+ target = resolveSignalAliasTargetFromMap({
100
+ aliases,
101
+ input: alias
102
+ });
103
+ } catch {
104
+ continue;
105
+ }
106
+ if (!target) continue;
107
+ if (target.kind !== params.kind) continue;
108
+ if (query && !alias.includes(query) && !normalizeLowercaseStringOrEmpty(target.to).includes(query)) continue;
109
+ entries.push({
110
+ kind: params.kind,
111
+ id: target.to,
112
+ name: alias
113
+ });
114
+ if (typeof limit === "number" && entries.length >= limit) break;
115
+ }
116
+ return entries;
117
+ }
118
+ //#endregion
119
+ //#region extensions/signal/src/approval-auth.ts
120
+ function normalizeSignalApproverId(value) {
121
+ const normalized = normalizeSignalMessagingTarget(String(value));
122
+ if (!normalized || normalized.startsWith("group:") || normalized.startsWith("username:")) return;
123
+ if (looksLikeUuid(normalized)) return `uuid:${normalized}`;
124
+ const e164 = normalizeE164(normalized);
125
+ return e164.length > 1 ? e164 : void 0;
126
+ }
127
+ function getSignalApprovalApprovers(params) {
128
+ const account = resolveSignalAccount(params).config;
129
+ let defaultTo = account.defaultTo;
130
+ if (typeof account.defaultTo === "string") try {
131
+ defaultTo = resolveSignalTarget({
132
+ cfg: params.cfg,
133
+ accountId: params.accountId,
134
+ input: account.defaultTo
135
+ })?.to ?? account.defaultTo;
136
+ } catch {
137
+ defaultTo = account.defaultTo;
138
+ }
139
+ return resolveApprovalApprovers({
140
+ allowFrom: account.allowFrom,
141
+ defaultTo,
142
+ normalizeApprover: normalizeSignalApproverId
143
+ });
144
+ }
145
+ const signalApprovalAuth = createResolvedApproverActionAuthAdapter({
146
+ channelLabel: "Signal",
147
+ resolveApprovers: getSignalApprovalApprovers,
148
+ normalizeSenderId: (value) => normalizeSignalApproverId(value)
149
+ });
150
+ //#endregion
151
+ export { resolveSignalTarget as i, signalApprovalAuth as n, listSignalAliasDirectoryEntries as r, getSignalApprovalApprovers as t };
@@ -1,6 +1,8 @@
1
- import { d as normalizeSignalMessagingTarget } from "./identity-B6O4k8xg.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";
1
+ import { r as resolveDefaultSignalAccountId } from "./accounts-Bz4HtP0g.js";
2
+ import { d as normalizeSignalMessagingTarget } from "./identity-C8-yk4J9.js";
3
+ import { i as resolveSignalTarget } from "./approval-auth-CsHNcAiy.js";
4
+ import { d as unregisterSignalApprovalReactionTarget, i as hasSignalApprovalReactionApprovers, l as resolveSignalApprovalConversationKey, o as registerSignalApprovalReactionTarget, u as resolveSignalApprovalTargetAuthorKeys } from "./approval-reactions-UFmUPD0A.js";
5
+ import { r as sendTypingSignal, t as sendMessageSignal } from "./send-CUJy6WPt.js";
4
6
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
5
7
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
6
8
  import { buildChannelApprovalNativeTargetKey } from "openclaw/plugin-sdk/approval-native-runtime";
@@ -49,8 +51,28 @@ const signalApprovalNativeRuntime = createChannelApprovalNativeRuntimeAdapter({
49
51
  })
50
52
  },
51
53
  transport: {
52
- prepareTarget: ({ plannedTarget, accountId, context }) => {
53
- const to = normalizeSignalMessagingTarget(plannedTarget.target.to);
54
+ prepareTarget: ({ cfg, plannedTarget, accountId, context }) => {
55
+ const plannedAccountId = plannedTarget.target.accountId;
56
+ const explicitAccountId = resolvePreparedApprovalAccountId({
57
+ plannedAccountId,
58
+ contextAccountId: accountId
59
+ });
60
+ const preparedAccountId = resolvePreparedApprovalAccountId({
61
+ plannedAccountId,
62
+ contextAccountId: accountId,
63
+ fallbackAccountId: cfg ? resolveDefaultSignalAccountId(cfg) : DEFAULT_ACCOUNT_ID
64
+ });
65
+ const rawTo = plannedTarget.target.to;
66
+ let to = normalizeSignalMessagingTarget(rawTo);
67
+ if (cfg) try {
68
+ to = resolveSignalTarget({
69
+ cfg,
70
+ accountId: explicitAccountId,
71
+ input: rawTo
72
+ })?.to ?? to;
73
+ } catch {
74
+ return null;
75
+ }
54
76
  if (!to) return null;
55
77
  const runtimeContext = readSignalApprovalRuntimeContext(context);
56
78
  const targetAuthorKeys = resolveSignalApprovalTargetAuthorKeys({
@@ -59,11 +81,7 @@ const signalApprovalNativeRuntime = createChannelApprovalNativeRuntimeAdapter({
59
81
  });
60
82
  const prepared = {
61
83
  to,
62
- accountId: resolvePreparedApprovalAccountId({
63
- plannedAccountId: plannedTarget.target.accountId,
64
- contextAccountId: accountId,
65
- fallbackAccountId: DEFAULT_ACCOUNT_ID
66
- }),
84
+ accountId: preparedAccountId,
67
85
  ...runtimeContext.baseUrl ? { baseUrl: runtimeContext.baseUrl } : {},
68
86
  ...runtimeContext.account ? { account: runtimeContext.account } : {},
69
87
  ...runtimeContext.accountUuid ? { accountUuid: runtimeContext.accountUuid } : {},
@@ -1,9 +1,10 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { d as normalizeSignalMessagingTarget, l as looksLikeUuid } from "./identity-B6O4k8xg.js";
3
- import { c as getSignalApprovalApprovers, l as signalApprovalAuth } from "./message-actions-BnugYXjB.js";
2
+ import { d as normalizeSignalMessagingTarget, l as looksLikeUuid } from "./identity-C8-yk4J9.js";
3
+ import { i as resolveSignalTarget, n as signalApprovalAuth, t as getSignalApprovalApprovers } from "./approval-auth-CsHNcAiy.js";
4
+ import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
4
5
  import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
5
6
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
6
- import { buildApprovalReactionHint, createApprovalReactionTargetStore, listApprovalReactionBindings, resolveApprovalReactionTarget } from "openclaw/plugin-sdk/approval-reaction-runtime";
7
+ import { addApprovalReactionHintToText, buildApprovalReactionHint, createApprovalReactionTargetStore, hasApprovalReactionHintText, listApprovalReactionBindings, resolveApprovalReactionTarget } from "openclaw/plugin-sdk/approval-reaction-runtime";
7
8
  import { normalizeE164 } from "openclaw/plugin-sdk/text-utility-runtime";
8
9
  import { matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
9
10
  import { getExecApprovalReplyMetadata } from "openclaw/plugin-sdk/approval-reply-runtime";
@@ -18,13 +19,14 @@ const { setRuntime: setSignalRuntime, getRuntime: getSignalRuntime, tryGetRuntim
18
19
  var approval_reactions_exports = /* @__PURE__ */ __exportAll({
19
20
  addSignalApprovalReactionHintToStructuredPayload: () => addSignalApprovalReactionHintToStructuredPayload,
20
21
  addSignalApprovalReactionHintToText: () => addSignalApprovalReactionHintToText,
22
+ appendSignalApprovalReactionHintForOutboundMessage: () => appendSignalApprovalReactionHintForOutboundMessage,
21
23
  buildSignalApprovalReactionHint: () => buildSignalApprovalReactionHint,
22
24
  clearSignalApprovalReactionTargetsForTest: () => clearSignalApprovalReactionTargetsForTest,
23
25
  hasSignalApprovalReactionApprovers: () => hasSignalApprovalReactionApprovers,
24
- listSignalApprovalReactionBindings: () => listSignalApprovalReactionBindings,
25
26
  maybeResolveSignalApprovalReaction: () => maybeResolveSignalApprovalReaction,
26
27
  registerSignalApprovalReactionTarget: () => registerSignalApprovalReactionTarget,
27
28
  registerSignalApprovalReactionTargetForDeliveredPayload: () => registerSignalApprovalReactionTargetForDeliveredPayload,
29
+ registerSignalApprovalReactionTargetForOutboundMessage: () => registerSignalApprovalReactionTargetForOutboundMessage,
28
30
  resolveSignalApprovalConversationKey: () => resolveSignalApprovalConversationKey,
29
31
  resolveSignalApprovalReactionTargetWithPersistence: () => resolveSignalApprovalReactionTargetWithPersistence,
30
32
  resolveSignalApprovalTargetAuthorKeys: () => resolveSignalApprovalTargetAuthorKeys,
@@ -33,7 +35,7 @@ var approval_reactions_exports = /* @__PURE__ */ __exportAll({
33
35
  const PERSISTENT_NAMESPACE = "signal.approval-reactions.v2";
34
36
  const PERSISTENT_MAX_ENTRIES = 1e3;
35
37
  const DEFAULT_REACTION_TARGET_TTL_MS = 1440 * 60 * 1e3;
36
- let resolverRuntimePromise;
38
+ const resolverRuntimeLoader = createLazyRuntimeModule(() => import("./approval-resolver-BR0MioAA.js"));
37
39
  const signalApprovalReactionTargets = createApprovalReactionTargetStore({
38
40
  namespace: PERSISTENT_NAMESPACE,
39
41
  maxEntries: PERSISTENT_MAX_ENTRIES,
@@ -42,10 +44,7 @@ const signalApprovalReactionTargets = createApprovalReactionTargetStore({
42
44
  logPersistentError: reportPersistentApprovalReactionError,
43
45
  readPersistedTarget
44
46
  });
45
- function loadApprovalResolver() {
46
- resolverRuntimePromise ??= import("./approval-resolver-BR0MioAA.js");
47
- return resolverRuntimePromise;
48
- }
47
+ const loadApprovalResolver = resolverRuntimeLoader;
49
48
  function resolveApprovalKindFromId(approvalId) {
50
49
  return approvalId.startsWith("plugin:") ? "plugin" : "exec";
51
50
  }
@@ -78,10 +77,25 @@ function targetAccountMatches(params) {
78
77
  const routeAccountId = normalizeOptionalString(params.routeAccountId);
79
78
  return Boolean(routeAccountId && normalizeAccountId(routeAccountId) === normalizeAccountId(configuredAccountId));
80
79
  }
80
+ function resolveSignalApprovalRouteTarget(params) {
81
+ try {
82
+ return resolveSignalTarget({
83
+ cfg: params.cfg,
84
+ accountId: params.accountId,
85
+ input: params.to
86
+ })?.to ?? normalizeSignalMessagingTarget(params.to) ?? null;
87
+ } catch {
88
+ return null;
89
+ }
90
+ }
81
91
  function hasMatchingSignalApprovalReactionTarget(params) {
82
92
  return (params.config.targets ?? []).some((target) => {
83
93
  if (normalizeLowercaseStringOrEmpty(target.channel) !== "signal") return false;
84
- const configuredTo = normalizeSignalMessagingTarget(target.to);
94
+ const configuredTo = resolveSignalApprovalRouteTarget({
95
+ cfg: params.cfg,
96
+ accountId: target.accountId ?? params.route.accountId,
97
+ to: target.to
98
+ });
85
99
  if (!configuredTo || configuredTo !== params.route.to) return false;
86
100
  return targetAccountMatches({
87
101
  routeAccountId: params.route.accountId,
@@ -100,6 +114,7 @@ function isSignalApprovalReactionRouteStillEnabled(params) {
100
114
  config,
101
115
  route: params.target.route
102
116
  }) && hasMatchingSignalApprovalReactionTarget({
117
+ cfg: params.cfg,
103
118
  config,
104
119
  route: params.target.route
105
120
  });
@@ -112,6 +127,17 @@ function isSignalApprovalReactionRouteStillEnabled(params) {
112
127
  function resolveSignalApprovalConversationKey(to) {
113
128
  return normalizeSignalMessagingTarget(to) ?? null;
114
129
  }
130
+ function resolveSignalApprovalConversationKeyForDeliveredTarget(params) {
131
+ try {
132
+ return resolveSignalTarget({
133
+ cfg: params.cfg,
134
+ accountId: params.accountId,
135
+ input: params.to
136
+ })?.to ?? resolveSignalApprovalConversationKey(params.to);
137
+ } catch {
138
+ return resolveSignalApprovalConversationKey(params.to);
139
+ }
140
+ }
115
141
  function normalizeSignalApprovalTargetAuthorKey(value) {
116
142
  const normalized = normalizeOptionalString(value);
117
143
  if (!normalized) return null;
@@ -173,29 +199,53 @@ function listSignalApprovalReactionBindings(allowedDecisions) {
173
199
  function buildSignalApprovalReactionHint(allowedDecisions) {
174
200
  return buildApprovalReactionHint({ allowedDecisions });
175
201
  }
176
- function insertSignalApprovalReactionHintNearHeader(params) {
177
- const lines = params.text.split(/\r?\n/);
178
- const idLineIndex = lines.findIndex((line) => /^ID:\s*\S+/.test(line.trim()));
179
- if (idLineIndex >= 0) {
180
- const before = lines.slice(0, idLineIndex + 1).join("\n");
181
- const after = lines.slice(idLineIndex + 1).join("\n").replace(/^\n+/, "");
182
- return after ? `${before}\n\n${params.hint}\n\n${after}` : `${before}\n\n${params.hint}`;
183
- }
184
- return `${params.hint}\n\n${params.text}`;
185
- }
186
202
  function addSignalApprovalReactionHintToText(params) {
187
- if (hasSignalApprovalReactionHintText(params.text)) return params.text;
188
- const hint = buildSignalApprovalReactionHint(params.allowedDecisions);
189
- return hint ? insertSignalApprovalReactionHintNearHeader({
190
- text: params.text,
191
- hint
192
- }) : params.text;
193
- }
194
- function hasSignalApprovalReactionHintText(text) {
195
- return /(^|\n)React with:\s*(\n|$)/i.test(text ?? "");
203
+ return addApprovalReactionHintToText(params);
204
+ }
205
+ function resolveStandaloneApprovalPromptKind(text) {
206
+ const firstLine = text.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
207
+ if (/^(?:🔒\s*)?Exec approval required$/.test(firstLine ?? "")) return "exec";
208
+ if (/^(?:(?:🛡️|🛡|🚨|ℹ️|ℹ)\s*)?Plugin approval required$/.test(firstLine ?? "")) return "plugin";
209
+ return null;
210
+ }
211
+ function isStandaloneApprovalPromptText(text) {
212
+ return resolveStandaloneApprovalPromptKind(text) !== null;
213
+ }
214
+ function normalizeApprovalDecision(value) {
215
+ const normalized = value.trim().toLowerCase();
216
+ if (normalized === "always") return "allow-always";
217
+ if (normalized === "allow-once" || normalized === "allow-always" || normalized === "deny") return normalized;
218
+ return null;
219
+ }
220
+ const APPROVAL_ID_LINE_RE = /^\s*ID:\s*([A-Za-z0-9][A-Za-z0-9._:-]*)\s*$/i;
221
+ const APPROVE_REPLY_COMMAND_LINE_RE = /^\s*Reply with:\s*\/approve(?:@[^\s]+)?\s+([A-Za-z0-9][A-Za-z0-9._:-]*)\s+(.+)$/i;
222
+ function extractSignalApprovalPromptBinding(text) {
223
+ const lines = text.split(/\r?\n/);
224
+ const idHeaderMatch = lines.map((line) => line.match(APPROVAL_ID_LINE_RE)).find((match) => Boolean(match));
225
+ if (!idHeaderMatch) return null;
226
+ const approvalId = idHeaderMatch[1];
227
+ const approvalKind = resolveStandaloneApprovalPromptKind(text) ?? resolveApprovalKindFromId(approvalId);
228
+ const allowedDecisions = [];
229
+ for (const line of lines) {
230
+ const match = line.match(APPROVE_REPLY_COMMAND_LINE_RE);
231
+ if (!match || match[1] !== approvalId) continue;
232
+ for (const decisionText of match[2].split(/[\s|,]+/)) {
233
+ const decision = normalizeApprovalDecision(decisionText);
234
+ if (decision && !allowedDecisions.includes(decision)) allowedDecisions.push(decision);
235
+ }
236
+ }
237
+ return allowedDecisions.length > 0 ? {
238
+ approvalId,
239
+ approvalKind,
240
+ allowedDecisions
241
+ } : null;
196
242
  }
197
243
  function buildTargetRoute(params) {
198
- const to = normalizeSignalMessagingTarget(params.to);
244
+ const to = resolveSignalApprovalRouteTarget({
245
+ cfg: params.cfg,
246
+ accountId: params.accountId,
247
+ to: params.to
248
+ });
199
249
  if (!to) return null;
200
250
  const route = {
201
251
  deliveryMode: "target",
@@ -212,6 +262,36 @@ function buildTargetRoute(params) {
212
262
  }
213
263
  }) ? route : null;
214
264
  }
265
+ function shouldAppendSignalApprovalReactionHintForOutboundMessage(params) {
266
+ const binding = extractSignalApprovalPromptBinding(params.text);
267
+ if (!binding) return false;
268
+ if (resolveSignalApprovalTargetAuthorKeys(params).length === 0) return false;
269
+ if (!hasSignalApprovalReactionApprovers({
270
+ cfg: params.cfg,
271
+ accountId: params.accountId
272
+ })) return false;
273
+ return Boolean(buildTargetRoute({
274
+ cfg: params.cfg,
275
+ accountId: params.accountId,
276
+ to: params.to,
277
+ approvalId: binding.approvalId,
278
+ approvalKind: binding.approvalKind,
279
+ agentId: params.agentId,
280
+ sessionKey: params.sessionKey
281
+ }));
282
+ }
283
+ function appendSignalApprovalReactionHintForOutboundMessage(params) {
284
+ if (!isStandaloneApprovalPromptText(params.text)) return params.text;
285
+ const binding = extractSignalApprovalPromptBinding(params.text);
286
+ if (!binding || !shouldAppendSignalApprovalReactionHintForOutboundMessage({
287
+ ...params,
288
+ text: params.text
289
+ })) return params.text;
290
+ return addSignalApprovalReactionHintToText({
291
+ text: params.text,
292
+ allowedDecisions: binding.allowedDecisions
293
+ });
294
+ }
215
295
  function hasSignalApprovalReactionApprovers(params) {
216
296
  return getSignalApprovalApprovers(params).length > 0;
217
297
  }
@@ -269,8 +349,7 @@ function addSignalApprovalReactionHintToStructuredPayload(params) {
269
349
  };
270
350
  }
271
351
  function readSignalDeliveryVisibleText(result) {
272
- const meta = result.meta;
273
- const visibleText = meta?.signalVisibleText ?? meta?.visibleText;
352
+ const visibleText = result.meta?.signalVisibleText ?? result.meta?.visibleText;
274
353
  return typeof visibleText === "string" ? visibleText : null;
275
354
  }
276
355
  function listDeliveredSignalMessageIdsWithVisibleHint(params) {
@@ -278,19 +357,23 @@ function listDeliveredSignalMessageIdsWithVisibleHint(params) {
278
357
  const resultsWithVisibleText = signalResults.filter((result) => readSignalDeliveryVisibleText(result) !== null);
279
358
  const candidates = resultsWithVisibleText.length > 0 ? resultsWithVisibleText : signalResults;
280
359
  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"));
360
+ const ids = candidates.filter((result) => resultsWithVisibleText.length > 0 ? hasApprovalReactionHintText(readSignalDeliveryVisibleText(result)) : hasApprovalReactionHintText(params.payload.text)).map((result) => normalizeOptionalString(result.messageId)).filter((messageId) => Boolean(messageId && messageId !== "unknown"));
282
361
  return Array.from(new Set(ids));
283
362
  }
284
363
  function registerSignalApprovalReactionTargetForDeliveredPayload(params) {
285
364
  if (normalizeLowercaseStringOrEmpty(params.target.channel) !== "signal") return false;
286
365
  const metadata = getExecApprovalReplyMetadata(params.payload);
287
366
  if (!metadata?.allowedDecisions || metadata.allowedDecisions.length === 0) return false;
288
- if (!hasSignalApprovalReactionHintText(params.payload.text)) return false;
367
+ if (!hasApprovalReactionHintText(params.payload.text)) return false;
289
368
  if (!hasSignalApprovalReactionApprovers({
290
369
  cfg: params.cfg,
291
370
  accountId: params.target.accountId
292
371
  })) return false;
293
- const conversationKey = resolveSignalApprovalConversationKey(params.target.to);
372
+ const conversationKey = resolveSignalApprovalConversationKeyForDeliveredTarget({
373
+ cfg: params.cfg,
374
+ accountId: params.target.accountId,
375
+ to: params.target.to
376
+ });
294
377
  if (!conversationKey) return false;
295
378
  const route = buildTargetRoute({
296
379
  cfg: params.cfg,
@@ -322,6 +405,41 @@ function registerSignalApprovalReactionTargetForDeliveredPayload(params) {
322
405
  })) || registered;
323
406
  return registered;
324
407
  }
408
+ function registerSignalApprovalReactionTargetForOutboundMessage(params) {
409
+ if (!isStandaloneApprovalPromptText(params.text)) return false;
410
+ const binding = extractSignalApprovalPromptBinding(params.text);
411
+ if (!binding) return false;
412
+ if (!hasSignalApprovalReactionApprovers({
413
+ cfg: params.cfg,
414
+ accountId: params.accountId
415
+ })) return false;
416
+ const targetAuthorKeys = resolveSignalApprovalTargetAuthorKeys(params);
417
+ if (targetAuthorKeys.length === 0) return false;
418
+ const conversationKey = resolveSignalApprovalConversationKey(params.to);
419
+ if (!conversationKey) return false;
420
+ const route = buildTargetRoute({
421
+ cfg: params.cfg,
422
+ accountId: params.accountId,
423
+ to: params.to,
424
+ approvalId: binding.approvalId,
425
+ approvalKind: binding.approvalKind,
426
+ agentId: params.agentId,
427
+ sessionKey: params.sessionKey
428
+ });
429
+ if (!route) return false;
430
+ return Boolean(registerSignalApprovalReactionTarget({
431
+ accountId: params.accountId,
432
+ conversationKey,
433
+ messageId: params.messageId,
434
+ approvalId: binding.approvalId,
435
+ approvalKind: binding.approvalKind,
436
+ allowedDecisions: binding.allowedDecisions,
437
+ targetAuthorKeys,
438
+ route,
439
+ routeAllowed: true,
440
+ ttlMs: params.ttlMs
441
+ }));
442
+ }
325
443
  function unregisterSignalApprovalReactionTarget(params) {
326
444
  const key = buildReactionTargetKey(params);
327
445
  if (!key) return;
@@ -420,7 +538,7 @@ async function maybeResolveSignalApprovalReaction(params) {
420
538
  }
421
539
  function clearSignalApprovalReactionTargetsForTest() {
422
540
  signalApprovalReactionTargets.clearForTest();
423
- resolverRuntimePromise = void 0;
541
+ resolverRuntimeLoader.clear();
424
542
  }
425
543
  //#endregion
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 };
544
+ export { maybeResolveSignalApprovalReaction as a, registerSignalApprovalReactionTargetForOutboundMessage as c, unregisterSignalApprovalReactionTarget as d, setSignalRuntime as f, hasSignalApprovalReactionApprovers as i, resolveSignalApprovalConversationKey as l, appendSignalApprovalReactionHintForOutboundMessage as n, registerSignalApprovalReactionTarget as o, approval_reactions_exports as r, registerSignalApprovalReactionTargetForDeliveredPayload as s, addSignalApprovalReactionHintToStructuredPayload as t, resolveSignalApprovalTargetAuthorKeys as u };