@openclaw/slack 2026.7.1 → 2026.7.2-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/accounts.runtime-KvftRRek.js +2 -0
  2. package/dist/{action-runtime-DoQGk_8D.js → action-runtime-p6M2ULpv.js} +205 -29
  3. package/dist/action-runtime.runtime-DQstDkHG.js +2 -0
  4. package/dist/{actions-DMpDmKEA.js → actions-DkTcWmpT.js} +87 -138
  5. package/dist/{actions.runtime-C103_BPW.js → actions.runtime-BSxpwgUc.js} +1 -1
  6. package/dist/api.js +14 -13
  7. package/dist/{approval-handler.runtime-CIUbOt5T.js → approval-handler.runtime-UGbnz9OC.js} +3 -3
  8. package/dist/{channel-BjlsaGHn.js → channel-B0_j6C6z.js} +116 -140
  9. package/dist/channel-config-api.js +1 -1
  10. package/dist/channel-plugin-api.js +1 -1
  11. package/dist/channel-type-BpeTezY4.js +109 -0
  12. package/dist/{channel.setup-Dq6syTNU.js → channel.setup-BQFut21T.js} +2 -2
  13. package/dist/{client-DGxwSL7f.js → client-D2F6m8CP.js} +38 -2
  14. package/dist/config-api-Df9a1B8C.js +3 -0
  15. package/dist/{config-schema-BzqzFW__.js → config-schema-DYI01PV2.js} +20 -37
  16. package/dist/cursor-pages-DSmTg0su.js +13 -0
  17. package/dist/{directory-config-BZuS8141.js → directory-config-DRkFujwG.js} +2 -2
  18. package/dist/directory-contract-api.js +1 -1
  19. package/dist/{directory-live-BNsxCgqP.js → directory-live-DX7yFsM-.js} +2 -2
  20. package/dist/{doctor-contract-XL6HeNCb.js → doctor-contract-DddfkMlF.js} +25 -38
  21. package/dist/doctor-contract-api.js +1 -1
  22. package/dist/group-policy-C-b2ZnSl.js +2332 -0
  23. package/dist/{interactive-replies-CXpT-vA_.js → interactive-replies-BilWUdcI.js} +5 -1
  24. package/dist/interactive-replies-api.js +1 -1
  25. package/dist/{message-tool-api-BgWKyu2r.js → message-tool-api-CA9jZrCY.js} +1 -1
  26. package/dist/message-tool-api.js +1 -1
  27. package/dist/{monitor-Ux-wzUWI.js → monitor-BN_uuvrQ.js} +3 -3
  28. package/dist/outbound-adapter-ECl0go7d.js +228 -0
  29. package/dist/{pipeline.runtime-rpVpay59.js → pipeline.runtime-f0eJJoJL.js} +492 -178
  30. package/dist/{policy-Dj-puVPb.js → policy-f8b0edSi.js} +1 -1
  31. package/dist/preflight-audio.runtime-D5Bgags0.js +3 -0
  32. package/dist/{probe-CuwRDE5j.js → probe-CfALk0l4.js} +2 -2
  33. package/dist/{provider-C1-DFSpw.js → provider-Ds-y-imP.js} +658 -192
  34. package/dist/{relay-source-D2jv1iGg.js → relay-source-B-6LFUdz.js} +6 -17
  35. package/dist/replies-BMvf3ihj.js +420 -0
  36. package/dist/{resolve-channels-CstkChBE.js → resolve-channels-DOMgNCWb.js} +17 -6
  37. package/dist/{resolve-users-C3NP2c-Q.js → resolve-users-aL6-Ssee.js} +11 -6
  38. package/dist/{runtime-BOk7xkOl.js → runtime-CauEvswP.js} +1 -1
  39. package/dist/{runtime-api-CEmqYxR-.js → runtime-api-CVb6CQjX.js} +3 -3
  40. package/dist/runtime-api.js +11 -11
  41. package/dist/runtime-setter-api.js +1 -1
  42. package/dist/{scopes-DCWVD5-8.js → scopes-Dw8wbU0j.js} +2 -2
  43. package/dist/{secret-contract-C3ChHo5A.js → secret-contract-CMohXFWS.js} +18 -113
  44. package/dist/secret-contract-api.js +1 -1
  45. package/dist/{send-DKDXoeoV.js → send-NEfJ0GvM.js} +662 -325
  46. package/dist/{send.runtime-GPRa63l1.js → send.runtime-BkKpitPu.js} +1 -1
  47. package/dist/setup-plugin-api.js +1 -1
  48. package/dist/{setup-surface-Dv2fIJNB.js → setup-surface-BN2WeG5c.js} +3 -3
  49. package/dist/{shared-CosfcFri.js → shared-BLnB9R1T.js} +7 -5
  50. package/dist/{slash-dispatch.runtime-CcMbbT3_.js → slash-dispatch.runtime-C0Otuf_T.js} +1 -1
  51. package/dist/{streaming-compat-DjlgH-Be.js → streaming-compat-3_X-21wW.js} +1 -0
  52. package/dist/{target-parsing-3jYH8LfB.js → target-parsing-N6lHogYZ.js} +16 -3
  53. package/dist/{targets-Dll4NJgs.js → targets-KmjP2E8s.js} +1 -1
  54. package/node_modules/p-map/index.d.ts +155 -0
  55. package/node_modules/p-map/index.js +284 -0
  56. package/node_modules/p-map/license +9 -0
  57. package/node_modules/p-map/package.json +57 -0
  58. package/node_modules/p-map/readme.md +190 -0
  59. package/npm-shrinkwrap.json +16 -3
  60. package/openclaw.plugin.json +16 -0
  61. package/package.json +6 -4
  62. package/dist/accounts.runtime-DWvaCb-X.js +0 -2
  63. package/dist/action-runtime.runtime-BABj3aM-.js +0 -2
  64. package/dist/config-api-DnT9ohtE.js +0 -2
  65. package/dist/format-YCNoNkM2.js +0 -107
  66. package/dist/outbound-adapter-DVoBfBW5.js +0 -272
  67. package/dist/replies-DpF38e1y.js +0 -287
  68. package/dist/reply-blocks-BGQ6zSqM.js +0 -877
  69. package/dist/resolve-allowlist-common-CP8SH0Zf.js +0 -43
@@ -1,4 +1,4 @@
1
- import { a as SLACK_SOCKET_RECONNECT_POLICY, o as formatUnknownError } from "./provider-C1-DFSpw.js";
1
+ import { c as formatUnknownError, s as SLACK_SOCKET_RECONNECT_POLICY } from "./provider-Ds-y-imP.js";
2
2
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { computeBackoff, sleepWithAbort, warn } from "openclaw/plugin-sdk/runtime-env";
4
4
  import { Buffer } from "node:buffer";
@@ -49,7 +49,7 @@ async function monitorSlackRelaySource(params) {
49
49
  params.runtime.log?.(warn(`slack relay mode disconnected; reconnecting in ${Math.round(delayMs / 1e3)}s (attempt ${reconnectAttempts}) reason="${formatUnknownError(err)}"`));
50
50
  await sleepWithAbort(delayMs, params.abortSignal);
51
51
  } finally {
52
- closeRelayWebSocket(connection?.ws);
52
+ closeRelayWebSocket(connection);
53
53
  params.setIdentity?.(void 0);
54
54
  }
55
55
  }
@@ -58,10 +58,6 @@ function openRelayWebSocket(config, abortSignal) {
58
58
  if (abortSignal?.aborted) return Promise.reject(/* @__PURE__ */ new Error("Slack relay websocket aborted before connect"));
59
59
  return new Promise((resolve, reject) => {
60
60
  const ws = new WebSocket(buildRelayWebSocketUrl(config), buildRelayWebSocketOptions(config.authToken));
61
- const bufferedMessages = [];
62
- const onEarlyMessage = (data) => bufferedMessages.push(data);
63
- const detachBuffer = () => ws.off("message", onEarlyMessage);
64
- ws.on("message", onEarlyMessage);
65
61
  const cleanup = () => {
66
62
  ws.off("open", onOpen);
67
63
  ws.off("error", onError);
@@ -69,26 +65,20 @@ function openRelayWebSocket(config, abortSignal) {
69
65
  abortSignal?.removeEventListener("abort", onAbort);
70
66
  };
71
67
  const onOpen = () => {
68
+ ws.pause();
72
69
  cleanup();
73
- resolve({
74
- ws,
75
- bufferedMessages,
76
- detachBuffer
77
- });
70
+ resolve(ws);
78
71
  };
79
72
  const onError = (error) => {
80
73
  cleanup();
81
- detachBuffer();
82
74
  reject(error);
83
75
  };
84
76
  const onClose = (code, reason) => {
85
77
  cleanup();
86
- detachBuffer();
87
78
  reject(new Error(formatRelayClose(code, reason)));
88
79
  };
89
80
  const onAbort = () => {
90
81
  cleanup();
91
- detachBuffer();
92
82
  closeRelayWebSocket(ws);
93
83
  reject(/* @__PURE__ */ new Error("Slack relay websocket aborted during connect"));
94
84
  };
@@ -99,7 +89,7 @@ function openRelayWebSocket(config, abortSignal) {
99
89
  });
100
90
  }
101
91
  function runRelayWebSocket(params) {
102
- const { ws } = params.connection;
92
+ const ws = params.connection;
103
93
  const relayState = {};
104
94
  let pending = Promise.resolve();
105
95
  return new Promise((resolve, reject) => {
@@ -149,12 +139,11 @@ function runRelayWebSocket(params) {
149
139
  closeRelayWebSocket(ws);
150
140
  settleResolve();
151
141
  };
152
- params.connection.detachBuffer();
153
142
  ws.on("message", onMessage);
154
143
  ws.once("error", onError);
155
144
  ws.once("close", onClose);
156
145
  params.abortSignal?.addEventListener("abort", onAbort, { once: true });
157
- for (const message of params.connection.bufferedMessages) onMessage(message);
146
+ ws.resume();
158
147
  });
159
148
  }
160
149
  async function handleRelayFrame(params) {
@@ -0,0 +1,420 @@
1
+ import { C as hasSlackNativeDataBlock, D as markdownToSlackMrkdwnChunks, F as SLACK_TEXT_LIMIT, f as hasSlackReplyStructuredContent, h as resolveSlackReplyBlocks, k as buildSlackBlocksFallbackText, m as resolveSlackReplyBlockResolution, w as isSlackInvalidBlocksError, x as buildSlackNativeDataAccessibilityText } from "./group-policy-C-b2ZnSl.js";
2
+ import { l as buildSlackNativeDataDeliveryPlan, r as sendMessageSlack, u as chunkSlackTextAtHardLimit } from "./send-NEfJ0GvM.js";
3
+ import { a as createSlackResponseUrlBudget, i as SlackResponseAlreadyReportedError } from "./provider-Ds-y-imP.js";
4
+ import { createReplyReferencePlanner } from "openclaw/plugin-sdk/reply-reference";
5
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
6
+ import { SILENT_REPLY_TOKEN, chunkMarkdownTextWithMode, isSilentReplyText } from "openclaw/plugin-sdk/reply-chunking";
7
+ import { deliverTextOrMediaReply, getReplyPayloadTtsSupplement, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
8
+ import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
9
+ import { buildCanonicalSentMessageHookContext, createInternalHookEvent, fireAndForgetHook, toInternalMessageSentContext, toPluginMessageContext, toPluginMessageSentEvent, triggerInternalHook } from "openclaw/plugin-sdk/hook-runtime";
10
+ //#region extensions/slack/src/message-sent-hook.ts
11
+ /**
12
+ * Slack-side emission of the `message_sent` plugin hook.
13
+ *
14
+ * Mirrors the Telegram pattern in `extensions/telegram/src/bot/delivery.replies.ts`
15
+ * (`buildTelegramSentHookContext`, `emitMessageSentHooks`, `emitTelegramMessageSentHooks`).
16
+ *
17
+ * Without this, plugins observing `message_sent` see Telegram outbound but not
18
+ * Slack outbound — even though `docs/plugins/hooks.md` documents the hook as
19
+ * firing for all successful outbound deliveries.
20
+ */
21
+ function buildSlackSentHookContext(params) {
22
+ return buildCanonicalSentMessageHookContext({
23
+ to: params.to,
24
+ content: params.content,
25
+ success: params.success,
26
+ error: params.error,
27
+ channelId: "slack",
28
+ accountId: params.accountId ?? void 0,
29
+ conversationId: params.to,
30
+ sessionKey: params.sessionKeyForInternalHooks,
31
+ messageId: params.messageId,
32
+ isGroup: params.isGroup,
33
+ groupId: params.groupId
34
+ });
35
+ }
36
+ function emitInternalSlackMessageSentHook(params) {
37
+ if (!params.sessionKeyForInternalHooks) return;
38
+ const canonical = buildSlackSentHookContext(params);
39
+ fireAndForgetHook(triggerInternalHook(createInternalHookEvent("message", "sent", params.sessionKeyForInternalHooks, toInternalMessageSentContext(canonical))), "slack: message:sent internal hook failed");
40
+ }
41
+ function emitMessageSentHooks(params) {
42
+ if (!params.enabled && !params.sessionKeyForInternalHooks) return;
43
+ const canonical = buildSlackSentHookContext(params);
44
+ if (params.enabled) fireAndForgetHook(Promise.resolve(params.hookRunner.runMessageSent(toPluginMessageSentEvent(canonical), toPluginMessageContext(canonical))), "slack: message_sent plugin hook failed");
45
+ emitInternalSlackMessageSentHook(params);
46
+ }
47
+ /**
48
+ * Fire both the plugin `message_sent` hook and (if a session key is supplied)
49
+ * the internal `message:sent` hook for a successful or failed Slack outbound
50
+ * delivery.
51
+ *
52
+ * Safe to call after every `chat.postMessage` — the function self-gates on
53
+ * `hookRunner.hasHooks("message_sent")` so plugins not observing the hook
54
+ * incur no cost.
55
+ */
56
+ function emitSlackMessageSentHooks(params) {
57
+ const hookRunner = getGlobalHookRunner();
58
+ emitMessageSentHooks({
59
+ ...params,
60
+ hookRunner,
61
+ enabled: hookRunner?.hasHooks("message_sent") ?? false
62
+ });
63
+ }
64
+ //#endregion
65
+ //#region extensions/slack/src/monitor/replies.ts
66
+ function readSlackReplyBlocks(payload) {
67
+ return resolveSlackReplyBlocks(payload);
68
+ }
69
+ function resolveSlackMediaHookSpokenText(payload) {
70
+ return (getReplyPayloadTtsSupplement(payload)?.spokenText ?? payload.spokenText)?.trim() || void 0;
71
+ }
72
+ function resolveDeliveredSlackReplyThreadTs(params) {
73
+ return (params.replyToMode === "off" ? void 0 : params.payloadReplyToId) ?? params.replyThreadTs;
74
+ }
75
+ async function deliverReplies(params) {
76
+ let latestResult;
77
+ const sendReply = async (input) => {
78
+ return await sendMessageSlack(params.target, input.text, {
79
+ cfg: params.cfg,
80
+ token: params.token,
81
+ threadTs: input.threadTs,
82
+ accountId: params.accountId,
83
+ ...input.mediaUrl ? { mediaUrl: input.mediaUrl } : {},
84
+ ...input.blocks ? { blocks: input.blocks } : {},
85
+ ...input.authoredTextPlacement ? { authoredTextPlacement: input.authoredTextPlacement } : {},
86
+ ...Object.hasOwn(input, "nativeDataFallbackBaseText") ? { nativeDataFallbackBaseText: input.nativeDataFallbackBaseText } : {},
87
+ ...input.textIsSlackMrkdwn ? { textIsSlackMrkdwn: true } : {},
88
+ ...input.textIsSlackPlainText ? { textIsSlackPlainText: true } : {},
89
+ ...params.eventScope ? {
90
+ client: params.eventScope.client,
91
+ enterpriseEventScope: params.eventScope,
92
+ textLimit: params.textLimit,
93
+ ...params.mediaMaxBytes !== void 0 ? { mediaMaxBytes: params.mediaMaxBytes } : {}
94
+ } : {},
95
+ ...params.identity ? { identity: params.identity } : {},
96
+ ...params.metadata ? { metadata: params.metadata } : {}
97
+ });
98
+ };
99
+ for (const payload of params.replies) {
100
+ if (payload.isReasoning === true) continue;
101
+ const threadTs = resolveDeliveredSlackReplyThreadTs({
102
+ replyToMode: params.replyToMode,
103
+ payloadReplyToId: payload.replyToId,
104
+ replyThreadTs: params.replyThreadTs
105
+ });
106
+ const reply = resolveSendableOutboundReplyParts(payload);
107
+ const textRaw = reply.hasText && !isSilentReplyText(reply.trimmedText, SILENT_REPLY_TOKEN) ? reply.trimmedText : void 0;
108
+ const { authoredTextPlacement, segments } = resolveSlackReplyBlockResolution(payload, { materializeAuthoredText: !reply.hasMedia && hasSlackReplyStructuredContent(payload) });
109
+ if (!textRaw && !reply.hasMedia && segments.length === 0) continue;
110
+ const emitSent = (content, result) => {
111
+ if (params.deferMessageSentHooks) return;
112
+ emitSlackMessageSentHooks({
113
+ sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
114
+ to: params.messageSentHookTarget ?? params.target,
115
+ accountId: params.accountId,
116
+ content,
117
+ success: true,
118
+ messageId: result?.messageId,
119
+ isGroup: params.isGroup,
120
+ groupId: params.groupId
121
+ });
122
+ };
123
+ const emitFailed = (content, error) => {
124
+ if (params.deferMessageSentHooks) return;
125
+ emitSlackMessageSentHooks({
126
+ sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
127
+ to: params.messageSentHookTarget ?? params.target,
128
+ accountId: params.accountId,
129
+ content,
130
+ success: false,
131
+ error: formatErrorMessage(error),
132
+ isGroup: params.isGroup,
133
+ groupId: params.groupId
134
+ });
135
+ };
136
+ const spokenText = resolveSlackMediaHookSpokenText(payload);
137
+ const hookParts = [];
138
+ let outsideText = authoredTextPlacement === "outside-blocks" ? textRaw ?? "" : "";
139
+ let lastResult;
140
+ let delivered = false;
141
+ try {
142
+ if (reply.hasMedia) {
143
+ const mediaCaption = outsideText;
144
+ if (mediaCaption) {
145
+ hookParts.push(mediaCaption);
146
+ outsideText = "";
147
+ } else if (!textRaw && spokenText) hookParts.push(spokenText);
148
+ const mediaDelivery = await deliverTextOrMediaReply({
149
+ payload,
150
+ text: mediaCaption,
151
+ sendText: async (text) => {
152
+ lastResult = await sendReply({
153
+ text,
154
+ threadTs
155
+ });
156
+ },
157
+ sendMedia: async ({ mediaUrl, caption }) => {
158
+ lastResult = await sendReply({
159
+ text: caption ?? "",
160
+ mediaUrl,
161
+ threadTs
162
+ });
163
+ }
164
+ });
165
+ delivered ||= mediaDelivery !== "empty";
166
+ }
167
+ for (const segment of segments) {
168
+ if (segment.kind === "text") {
169
+ const text = [outsideText, segment.text].filter(Boolean).join("\n\n");
170
+ outsideText = "";
171
+ if (!text) continue;
172
+ hookParts.push(text);
173
+ for (const chunk of chunkSlackTextAtHardLimit(text)) {
174
+ lastResult = await sendReply({
175
+ text: chunk,
176
+ threadTs,
177
+ textIsSlackPlainText: true
178
+ });
179
+ delivered = true;
180
+ }
181
+ continue;
182
+ }
183
+ const baseText = outsideText;
184
+ outsideText = "";
185
+ const accessibilityText = buildSlackNativeDataAccessibilityText(baseText, segment.blocks) || buildSlackBlocksFallbackText(segment.blocks);
186
+ hookParts.push(accessibilityText);
187
+ const segmentPlacement = baseText ? "outside-blocks" : authoredTextPlacement === "blocks" ? "blocks" : "none";
188
+ lastResult = await sendReply({
189
+ text: baseText,
190
+ threadTs,
191
+ blocks: segment.blocks,
192
+ authoredTextPlacement: segmentPlacement,
193
+ ...baseText ? { nativeDataFallbackBaseText: baseText } : {}
194
+ });
195
+ delivered = true;
196
+ }
197
+ if (outsideText && !reply.hasMedia) {
198
+ hookParts.push(outsideText);
199
+ lastResult = await sendReply({
200
+ text: outsideText,
201
+ threadTs
202
+ });
203
+ delivered = true;
204
+ }
205
+ } catch (error) {
206
+ emitFailed(hookParts.join("\n\n") || textRaw || spokenText || "", error);
207
+ throw error;
208
+ }
209
+ if (delivered) {
210
+ emitSent(hookParts.join("\n\n") || textRaw || spokenText || "", reply.hasMedia ? void 0 : lastResult);
211
+ latestResult = lastResult;
212
+ params.runtime.log?.(`delivered reply to ${params.target}`);
213
+ }
214
+ }
215
+ return latestResult;
216
+ }
217
+ /**
218
+ * Compute effective threadTs for a Slack reply based on replyToMode.
219
+ * - "off": stay in thread if already in one, otherwise main channel
220
+ * - "first": first reply goes to thread, subsequent replies to main channel
221
+ * - "all": all replies go to thread
222
+ */
223
+ function resolveSlackThreadTs(params) {
224
+ return createSlackReplyReferencePlanner({
225
+ replyToMode: params.replyToMode,
226
+ incomingThreadTs: params.incomingThreadTs,
227
+ messageTs: params.messageTs,
228
+ hasReplied: params.hasReplied,
229
+ isThreadReply: params.isThreadReply
230
+ }).use();
231
+ }
232
+ function createSlackReplyReferencePlanner(params) {
233
+ return createReplyReferencePlanner({
234
+ replyToMode: params.isThreadReply ?? Boolean(params.incomingThreadTs && params.incomingThreadTs !== params.messageTs) ? "all" : params.replyToMode,
235
+ existingId: params.incomingThreadTs,
236
+ startId: params.messageTs,
237
+ hasReplied: params.hasReplied
238
+ });
239
+ }
240
+ function createSlackReplyDeliveryPlan(params) {
241
+ const replyReference = createSlackReplyReferencePlanner({
242
+ replyToMode: params.replyToMode,
243
+ incomingThreadTs: params.incomingThreadTs,
244
+ messageTs: params.messageTs,
245
+ hasReplied: params.hasRepliedRef.value,
246
+ isThreadReply: params.isThreadReply
247
+ });
248
+ return {
249
+ peekThreadTs: () => replyReference.peek(),
250
+ nextThreadTs: () => replyReference.use(),
251
+ markSent: () => {
252
+ replyReference.markSent();
253
+ params.hasRepliedRef.value = replyReference.hasReplied();
254
+ }
255
+ };
256
+ }
257
+ async function deliverSlackSlashReplies(params) {
258
+ const deliveries = [];
259
+ const responseBudget = params.responseBudget ?? createSlackResponseUrlBudget(params.respond);
260
+ const chunkLimit = Math.max(1, Math.min(params.textLimit, SLACK_TEXT_LIMIT));
261
+ const createBlockMessagePlan = (input) => {
262
+ const plan = buildSlackNativeDataDeliveryPlan({
263
+ blocks: input.blocks,
264
+ baseText: input.baseText
265
+ });
266
+ return {
267
+ message: {
268
+ text: plan.accessibilityText,
269
+ blocks: input.blocks,
270
+ mrkdwn: false
271
+ },
272
+ nativeFallback: plan.fallbackMessages,
273
+ ...plan.skipOriginalBlocks ? { skipOriginalBlocks: true } : {}
274
+ };
275
+ };
276
+ for (const payload of params.replies) {
277
+ if (payload.isReasoning === true) continue;
278
+ const reply = resolveSendableOutboundReplyParts(payload);
279
+ const textRaw = reply.hasText && !isSilentReplyText(reply.trimmedText, SILENT_REPLY_TOKEN) ? reply.trimmedText : void 0;
280
+ const { authoredTextPlacement, segments } = resolveSlackReplyBlockResolution(payload, { materializeAuthoredText: hasSlackReplyStructuredContent(payload) });
281
+ let outsideText = authoredTextPlacement === "outside-blocks" ? textRaw ?? "" : "";
282
+ const messages = [];
283
+ const hookParts = [];
284
+ for (const segment of segments) {
285
+ if (segment.kind === "text") {
286
+ const text = [outsideText, segment.text].filter(Boolean).join("\n\n");
287
+ outsideText = "";
288
+ if (text) {
289
+ hookParts.push(text);
290
+ messages.push(...chunkSlackTextAtHardLimit(text).map((chunk) => ({ message: {
291
+ text: chunk,
292
+ mrkdwn: false
293
+ } })));
294
+ }
295
+ continue;
296
+ }
297
+ const baseText = outsideText;
298
+ outsideText = "";
299
+ const accessibilityText = buildSlackNativeDataAccessibilityText(baseText, segment.blocks) || buildSlackBlocksFallbackText(segment.blocks);
300
+ hookParts.push(accessibilityText);
301
+ const blockPlan = createBlockMessagePlan({
302
+ blocks: segment.blocks,
303
+ baseText
304
+ });
305
+ messages.push(hasSlackNativeDataBlock(segment.blocks) || blockPlan.skipOriginalBlocks ? blockPlan : { message: {
306
+ text: accessibilityText,
307
+ blocks: segment.blocks,
308
+ mrkdwn: false
309
+ } });
310
+ }
311
+ if (outsideText) hookParts.push(outsideText);
312
+ if (reply.mediaUrls.length > 0) hookParts.push(...reply.mediaUrls);
313
+ if (segments.length > 0) {
314
+ const trailingText = [outsideText, ...reply.mediaUrls].filter(Boolean).join("\n");
315
+ if (trailingText) messages.push(...chunkSlackTextAtHardLimit(trailingText).map((text) => ({ message: {
316
+ text,
317
+ mrkdwn: false
318
+ } })));
319
+ if (messages.length > 0) deliveries.push({
320
+ hookContent: hookParts.filter(Boolean).join("\n\n"),
321
+ messages
322
+ });
323
+ continue;
324
+ }
325
+ const combined = [textRaw ?? "", ...reply.mediaUrls].filter(Boolean).join("\n");
326
+ if (!combined) continue;
327
+ const chunkMode = params.chunkMode ?? "length";
328
+ const chunks = (chunkMode === "newline" ? chunkMarkdownTextWithMode(combined, chunkLimit, chunkMode) : [combined]).flatMap((markdown) => markdownToSlackMrkdwnChunks(markdown, chunkLimit, { tableMode: params.tableMode }));
329
+ deliveries.push({
330
+ hookContent: textRaw ?? resolveSlackMediaHookSpokenText(payload) ?? combined,
331
+ messages: (chunks.length > 0 ? chunks : [combined]).map((text) => ({ message: { text } }))
332
+ });
333
+ }
334
+ if (deliveries.length === 0) return;
335
+ const responseType = params.ephemeral ? "ephemeral" : "in_channel";
336
+ const respond = async (message) => await responseBudget.respond({
337
+ text: message.text,
338
+ response_type: responseType,
339
+ ...message.blocks ? { blocks: message.blocks } : {},
340
+ ...message.mrkdwn === false ? { mrkdwn: false } : {}
341
+ });
342
+ const emitDeliveryFailure = (delivery, error) => {
343
+ if (!params.messageSentHookTarget) return;
344
+ emitSlackMessageSentHooks({
345
+ sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
346
+ to: params.messageSentHookTarget,
347
+ accountId: params.accountId,
348
+ content: delivery.hookContent,
349
+ success: false,
350
+ error: formatErrorMessage(error),
351
+ isGroup: params.isGroup,
352
+ groupId: params.groupId
353
+ });
354
+ };
355
+ const minimumCalls = deliveries.flatMap((delivery) => delivery.messages).map((planned) => planned.nativeFallback?.length ?? 1);
356
+ const minimumRemainingCalls = Array.from({ length: minimumCalls.length + 1 }, () => 0);
357
+ for (let index = minimumCalls.length - 1; index >= 0; index -= 1) minimumRemainingCalls[index] = (minimumRemainingCalls[index + 1] ?? 0) + (minimumCalls[index] ?? 0);
358
+ const failOversizedDelivery = async () => {
359
+ const message = "Slack response exceeds the remaining response_url delivery budget.";
360
+ let failure = /* @__PURE__ */ new Error(message);
361
+ if (responseBudget.remaining() !== 0) try {
362
+ await responseBudget.respond({
363
+ text: "This Slack response is too large to deliver within the remaining response window.",
364
+ response_type: "ephemeral"
365
+ });
366
+ failure = new SlackResponseAlreadyReportedError(message);
367
+ } catch (error) {
368
+ failure = error;
369
+ }
370
+ for (const delivery of deliveries) emitDeliveryFailure(delivery, failure);
371
+ throw failure;
372
+ };
373
+ const initialRemaining = responseBudget.remaining();
374
+ const initialMinimumCalls = minimumCalls.reduce((total, calls) => total + calls, 0);
375
+ if (initialRemaining !== void 0 && initialMinimumCalls > initialRemaining) await failOversizedDelivery();
376
+ const deliverNativeFallback = async (messages) => {
377
+ for (const message of messages) if (isSlackInvalidBlocksError(await respond(message))) throw new Error("Slack rejected the native-data fallback blocks with invalid_blocks.");
378
+ };
379
+ let plannedIndex = 0;
380
+ for (const delivery of deliveries) {
381
+ try {
382
+ for (const planned of delivery.messages) {
383
+ const minimumAfter = minimumRemainingCalls[plannedIndex + 1] ?? 0;
384
+ plannedIndex += 1;
385
+ const fallback = planned.nativeFallback;
386
+ if (!fallback) {
387
+ await respond(planned.message);
388
+ continue;
389
+ }
390
+ const remaining = responseBudget.remaining();
391
+ if (!(!planned.skipOriginalBlocks && (remaining === void 0 || 1 + fallback.length + minimumAfter <= remaining))) {
392
+ await deliverNativeFallback(fallback);
393
+ continue;
394
+ }
395
+ let rejectedNativeBlocks = false;
396
+ try {
397
+ rejectedNativeBlocks = isSlackInvalidBlocksError(await respond(planned.message));
398
+ } catch (error) {
399
+ if (!isSlackInvalidBlocksError(error)) throw error;
400
+ rejectedNativeBlocks = true;
401
+ }
402
+ if (rejectedNativeBlocks) await deliverNativeFallback(fallback);
403
+ }
404
+ } catch (error) {
405
+ emitDeliveryFailure(delivery, error);
406
+ throw error;
407
+ }
408
+ if (params.messageSentHookTarget) emitSlackMessageSentHooks({
409
+ sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
410
+ to: params.messageSentHookTarget,
411
+ accountId: params.accountId,
412
+ content: delivery.hookContent,
413
+ success: true,
414
+ isGroup: params.isGroup,
415
+ groupId: params.groupId
416
+ });
417
+ }
418
+ }
419
+ //#endregion
420
+ export { resolveDeliveredSlackReplyThreadTs as a, readSlackReplyBlocks as i, deliverReplies as n, resolveSlackThreadTs as o, deliverSlackSlashReplies as r, emitSlackMessageSentHooks as s, createSlackReplyDeliveryPlan as t };
@@ -1,7 +1,8 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { r as createSlackWebClient } from "./client-DGxwSL7f.js";
3
- import { n as resolveSlackAllowlistEntries, t as collectSlackCursorItems } from "./resolve-allowlist-common-CP8SH0Zf.js";
2
+ import { n as createSlackLookupClient } from "./client-D2F6m8CP.js";
3
+ import { t as collectSlackCursorPages } from "./cursor-pages-DSmTg0su.js";
4
4
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
5
+ import { resolveDirectoryAllowlistEntries } from "openclaw/plugin-sdk/directory-runtime";
5
6
  //#region extensions/slack/src/resolve-channels.ts
6
7
  var resolve_channels_exports = /* @__PURE__ */ __exportAll({ resolveSlackChannelAllowlist: () => resolveSlackChannelAllowlist });
7
8
  function parseSlackChannelMention(raw) {
@@ -18,8 +19,8 @@ function parseSlackChannelMention(raw) {
18
19
  return name ? { name } : {};
19
20
  }
20
21
  async function listSlackChannels(client) {
21
- return collectSlackCursorItems({
22
- fetchPage: async (cursor) => await client.conversations.list({
22
+ return collectSlackCursorPages({
23
+ fetchPage: (cursor) => client.conversations.list({
23
24
  types: "public_channel,private_channel",
24
25
  exclude_archived: false,
25
26
  limit: 1e3,
@@ -46,8 +47,18 @@ function resolveByName(name, channels) {
46
47
  return matches.find((channel) => !channel.archived) ?? matches[0];
47
48
  }
48
49
  async function resolveSlackChannelAllowlist(params) {
49
- const channels = await listSlackChannels(params.client ?? createSlackWebClient(params.token));
50
- return resolveSlackAllowlistEntries({
50
+ const parsedEntries = params.entries.map((input) => ({
51
+ input,
52
+ parsed: parseSlackChannelMention(input)
53
+ }));
54
+ if (parsedEntries.every((entry) => Boolean(entry.parsed.id))) return parsedEntries.map(({ input, parsed }) => ({
55
+ input,
56
+ resolved: true,
57
+ id: parsed.id,
58
+ name: parsed.name
59
+ }));
60
+ const channels = await listSlackChannels(params.client ?? createSlackLookupClient(params.token));
61
+ return resolveDirectoryAllowlistEntries({
51
62
  entries: params.entries,
52
63
  lookup: channels,
53
64
  parseInput: parseSlackChannelMention,
@@ -1,7 +1,8 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { r as createSlackWebClient } from "./client-DGxwSL7f.js";
3
- import { n as resolveSlackAllowlistEntries, t as collectSlackCursorItems } from "./resolve-allowlist-common-CP8SH0Zf.js";
2
+ import { n as createSlackLookupClient } from "./client-D2F6m8CP.js";
3
+ import { t as collectSlackCursorPages } from "./cursor-pages-DSmTg0su.js";
4
4
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
+ import { resolveDirectoryAllowlistEntries } from "openclaw/plugin-sdk/directory-runtime";
5
6
  //#region extensions/slack/src/resolve-users.ts
6
7
  var resolve_users_exports = /* @__PURE__ */ __exportAll({ resolveSlackUserAllowlist: () => resolveSlackUserAllowlist });
7
8
  function parseSlackUserInput(raw) {
@@ -16,8 +17,8 @@ function parseSlackUserInput(raw) {
16
17
  return name ? { name } : {};
17
18
  }
18
19
  async function listSlackUsers(client) {
19
- return collectSlackCursorItems({
20
- fetchPage: async (cursor) => await client.users.list({
20
+ return collectSlackCursorPages({
21
+ fetchPage: (cursor) => client.users.list({
21
22
  limit: 200,
22
23
  cursor
23
24
  }),
@@ -59,6 +60,10 @@ function resolveSlackUserFromMatches(input, matches, parsed) {
59
60
  user,
60
61
  score: scoreSlackUser(user, parsed)
61
62
  })).toSorted((a, b) => b.score - a.score)[0]?.user ?? matches[0];
63
+ if (!best) return {
64
+ input,
65
+ resolved: false
66
+ };
62
67
  return {
63
68
  input,
64
69
  resolved: true,
@@ -71,8 +76,8 @@ function resolveSlackUserFromMatches(input, matches, parsed) {
71
76
  };
72
77
  }
73
78
  async function resolveSlackUserAllowlist(params) {
74
- const users = await listSlackUsers(params.client ?? createSlackWebClient(params.token));
75
- return resolveSlackAllowlistEntries({
79
+ const users = await listSlackUsers(params.client ?? createSlackLookupClient(params.token));
80
+ return resolveDirectoryAllowlistEntries({
76
81
  entries: params.entries,
77
82
  lookup: users,
78
83
  parseInput: parseSlackUserInput,
@@ -1,6 +1,6 @@
1
1
  import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
2
2
  //#region extensions/slack/src/runtime.ts
3
- const { setRuntime: setSlackRuntime, clearRuntime: clearSlackRuntime, tryGetRuntime: getOptionalSlackRuntime } = createPluginRuntimeStore({
3
+ const { setRuntime: setSlackRuntime, tryGetRuntime: getOptionalSlackRuntime } = createPluginRuntimeStore({
4
4
  pluginId: "slack",
5
5
  errorMessage: "Slack runtime not initialized"
6
6
  });
@@ -1,6 +1,6 @@
1
- import "./target-parsing-3jYH8LfB.js";
2
- import "./client-DGxwSL7f.js";
3
- import "./config-api-DnT9ohtE.js";
1
+ import "./target-parsing-N6lHogYZ.js";
2
+ import "./client-D2F6m8CP.js";
3
+ import "./config-api-Df9a1B8C.js";
4
4
  import "openclaw/plugin-sdk/account-id";
5
5
  import { createActionGate as createActionGate$1, imageResultFromFile, jsonResult, readPositiveIntegerParam, readReactionParams, readStringParam, withNormalizedTimestamp } from "openclaw/plugin-sdk/channel-actions";
6
6
  import "openclaw/plugin-sdk/channel-status";
@@ -1,14 +1,14 @@
1
1
  import { t as registerSlackPluginHttpRoutes } from "./plugin-routes-CLp4obpN.js";
2
2
  import { a as resolveSlackAccount, f as resolveSlackAppToken, i as resolveDefaultSlackAccountId, n as listSlackAccountIds, p as resolveSlackBotToken, t as listEnabledSlackAccounts } from "./accounts-BOJJiHSr.js";
3
- import { o as resolveSlackGroupRequireMention, s as resolveSlackGroupToolPolicy } from "./reply-blocks-BGQ6zSqM.js";
4
- import { n as setSlackRuntime } from "./runtime-BOk7xkOl.js";
5
- import { r as sendMessageSlack } from "./send-DKDXoeoV.js";
6
- import { a as listSlackEmojis, c as pinSlackMessage, d as removeOwnSlackReactions, f as removeSlackReaction, h as unpinSlackMessage, i as getSlackMemberInfo, l as reactSlackMessage, m as sendSlackMessage, o as listSlackPins, r as editSlackMessage, s as listSlackReactions, t as deleteSlackMessage, u as readSlackMessages } from "./actions-DMpDmKEA.js";
7
- import { t as probeSlack } from "./probe-CuwRDE5j.js";
8
- import { t as resolveSlackChannelAllowlist } from "./resolve-channels-CstkChBE.js";
9
- import { t as resolveSlackUserAllowlist } from "./resolve-users-C3NP2c-Q.js";
10
- import { t as monitorSlackProvider } from "./provider-C1-DFSpw.js";
11
- import { n as slackActionRuntime, t as handleSlackAction } from "./action-runtime-DoQGk_8D.js";
12
- import { n as listSlackDirectoryGroupsLive, r as listSlackDirectoryPeersLive } from "./directory-live-BNsxCgqP.js";
13
- import "./monitor-Ux-wzUWI.js";
3
+ import { n as resolveSlackGroupToolPolicy, t as resolveSlackGroupRequireMention } from "./group-policy-C-b2ZnSl.js";
4
+ import { n as setSlackRuntime } from "./runtime-CauEvswP.js";
5
+ import { a as listSlackEmojis, c as pinSlackMessage, d as removeOwnSlackReactions, f as removeSlackReaction, h as unpinSlackMessage, i as getSlackMemberInfo, l as reactSlackMessage, m as sendSlackMessage, o as listSlackPins, r as editSlackMessage, s as listSlackReactions, t as deleteSlackMessage, u as readSlackMessages } from "./actions-DkTcWmpT.js";
6
+ import { r as sendMessageSlack } from "./send-NEfJ0GvM.js";
7
+ import { t as probeSlack } from "./probe-CfALk0l4.js";
8
+ import { t as resolveSlackChannelAllowlist } from "./resolve-channels-DOMgNCWb.js";
9
+ import { t as resolveSlackUserAllowlist } from "./resolve-users-aL6-Ssee.js";
10
+ import { t as monitorSlackProvider } from "./provider-Ds-y-imP.js";
11
+ import { n as slackActionRuntime, t as handleSlackAction } from "./action-runtime-p6M2ULpv.js";
12
+ import { n as listSlackDirectoryGroupsLive, r as listSlackDirectoryPeersLive } from "./directory-live-DX7yFsM-.js";
13
+ import "./monitor-BN_uuvrQ.js";
14
14
  export { deleteSlackMessage, editSlackMessage, getSlackMemberInfo, handleSlackAction, listEnabledSlackAccounts, listSlackAccountIds, listSlackDirectoryGroupsLive, listSlackDirectoryPeersLive, listSlackEmojis, listSlackPins, listSlackReactions, monitorSlackProvider, pinSlackMessage, probeSlack, reactSlackMessage, readSlackMessages, registerSlackPluginHttpRoutes, removeOwnSlackReactions, removeSlackReaction, resolveDefaultSlackAccountId, resolveSlackAccount, resolveSlackAppToken, resolveSlackBotToken, resolveSlackChannelAllowlist, resolveSlackGroupRequireMention, resolveSlackGroupToolPolicy, resolveSlackUserAllowlist, sendMessageSlack, sendSlackMessage, setSlackRuntime, slackActionRuntime, unpinSlackMessage };
@@ -1,2 +1,2 @@
1
- import { n as setSlackRuntime } from "./runtime-BOk7xkOl.js";
1
+ import { n as setSlackRuntime } from "./runtime-CauEvswP.js";
2
2
  export { setSlackRuntime };
@@ -1,5 +1,5 @@
1
- import { c as formatSlackError } from "./reply-blocks-BGQ6zSqM.js";
2
- import { r as createSlackWebClient } from "./client-DGxwSL7f.js";
1
+ import { r as formatSlackError } from "./group-policy-C-b2ZnSl.js";
2
+ import { i as createSlackWebClient } from "./client-D2F6m8CP.js";
3
3
  import { isRecord, normalizeOptionalString, normalizeStringEntries, sortUniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  //#region extensions/slack/src/scopes.ts
5
5
  function collectScopes(value, into) {