@openclaw/slack 2026.5.12-beta.8 → 2026.5.14-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 (86) hide show
  1. package/dist/{account-inspect-D7AZNs8C.js → account-inspect-BJyQLSkN.js} +1 -1
  2. package/dist/account-inspect-api.js +1 -1
  3. package/dist/{accounts-ClAPP5ry.js → accounts-yk5K3wQU.js} +2 -1
  4. package/dist/accounts.runtime-CkfFIisb.js +2 -0
  5. package/dist/{action-runtime-e2UhRsNx.js → action-runtime-gtC-RM3r.js} +4 -4
  6. package/dist/action-runtime.runtime-DbVd7_Na.js +2 -0
  7. package/dist/{actions-CYLFK-Zy.js → actions-Cqyj7oRr.js} +26 -9
  8. package/dist/{actions.runtime-CO3OaTLb.js → actions.runtime-DD6hfFCA.js} +1 -1
  9. package/dist/api.js +18 -18
  10. package/dist/approval-auth-D3xf0sS6.js +28 -0
  11. package/dist/{approval-handler.runtime-CmeRr9qA.js → approval-handler.runtime-DeYBuIaU.js} +4 -4
  12. package/dist/{blocks-render-BIDw-Pom.js → blocks-render-BdLueE_v.js} +1 -1
  13. package/dist/{channel-DRjHBTDB.js → channel-CC04F-xs.js} +28 -52
  14. package/dist/channel-config-api.js +1 -1
  15. package/dist/channel-plugin-api.js +1 -1
  16. package/dist/{channel.setup-Cayn7afd.js → channel.setup-jzYjY634.js} +4 -4
  17. package/dist/{config-schema-D9B5LB_L.js → config-schema-Bueih4yH.js} +21 -1
  18. package/dist/contract-api.js +4 -4
  19. package/dist/{directory-config-B3JiHeB7.js → directory-config-BKKNBkCq.js} +1 -1
  20. package/dist/directory-contract-api.js +1 -1
  21. package/dist/{directory-live-Bf16GwDh.js → directory-live-CuaWaGnM.js} +2 -2
  22. package/dist/{doctor-contract-KUjHnkQm.js → doctor-contract-DCkS8eNN.js} +1 -1
  23. package/dist/doctor-contract-api.js +1 -1
  24. package/dist/{exec-approvals-7xUNgLi9.js → exec-approvals-BLn4Zx7V.js} +3 -3
  25. package/dist/{group-policy-CyLUK6My.js → group-policy-utF2iWnE.js} +1 -1
  26. package/dist/http-routes-api.js +1 -1
  27. package/dist/inbound-contract-test-api.js +2 -2
  28. package/dist/{interactive-replies-qAIfuBor.js → interactive-replies-CawNPL-h.js} +1 -1
  29. package/dist/interactive-replies-api.js +1 -1
  30. package/dist/{media-D1XCd1uP.js → media-B-nNpS2G.js} +32 -21
  31. package/dist/{message-tool-api-6lowf9zE.js → message-tool-api-B_bKUmP0.js} +2 -2
  32. package/dist/message-tool-api.js +1 -1
  33. package/dist/{monitor-a97o17G6.js → monitor-B3QB1ysK.js} +3 -3
  34. package/dist/{outbound-adapter-B_5sEhCg.js → outbound-adapter-DsAvCwpZ.js} +5 -5
  35. package/dist/outbound-payload-test-api.js +1 -1
  36. package/dist/{outbound-payload.test-harness-CVCamg1x.js → outbound-payload.test-harness-DM9ZdY2f.js} +2 -2
  37. package/dist/{pipeline.runtime-DT0hLnq2.js → pipeline.runtime-D-YJZZaI.js} +49 -22
  38. package/dist/{plugin-routes-DtTPmga1.js → plugin-routes-Dt_jh9W8.js} +2 -2
  39. package/dist/{prepare-D3YqV8jB.js → prepare-CXI8nHbJ.js} +10 -10
  40. package/dist/{prepare.test-helpers-DVcjRhfG.js → prepare.test-helpers-BhFHtbz3.js} +1 -1
  41. package/dist/{probe-3eZf1FjI.js → probe-D1wYA05H.js} +2 -2
  42. package/dist/{provider-D7uAN3Fq.js → provider-Ut7IF0Mn.js} +66 -29
  43. package/dist/{replies-Xe_jMR6o.js → replies-CzfjCaLG.js} +4 -4
  44. package/dist/{reply-blocks-Z5l6_R6H.js → reply-blocks-DWoZrUll.js} +2 -2
  45. package/dist/{resolve-channels-BRYqyNVJ.js → resolve-channels-BiVxSLVm.js} +2 -2
  46. package/dist/{resolve-users-Bd_SdP8j.js → resolve-users-CcpSlYw-.js} +2 -2
  47. package/dist/{room-context-0vovmZPU.js → room-context-D0hGOp8m.js} +4 -2
  48. package/dist/{runtime-api-Dd1xIV5v.js → runtime-api-D8wiG9BS.js} +2 -2
  49. package/dist/runtime-api.js +13 -13
  50. package/dist/runtime-setter-api.js +1 -1
  51. package/dist/{scopes-CDevO8jg.js → scopes-BTkB8PPE.js} +2 -2
  52. package/dist/secret-contract-api.js +1 -1
  53. package/dist/security-contract-api.js +1 -1
  54. package/dist/{send-D_A9kL-C.js → send-ioky2Xpy.js} +6 -6
  55. package/dist/send.runtime-5Kl3Wzbf.js +2 -0
  56. package/dist/send.runtime-DoifekaA.js +2 -0
  57. package/dist/{setup-core-B9NetDkM.js → setup-core-DgLJ7dQY.js} +2 -2
  58. package/dist/setup-plugin-api.js +1 -1
  59. package/dist/{setup-surface-D88QBVOW.js → setup-surface-B6w9gtds.js} +5 -5
  60. package/dist/{shared-D8U42xFL.js → shared-CSiHkaVO.js} +9 -9
  61. package/dist/{slash-dispatch.runtime-BJgT0jwV.js → slash-dispatch.runtime-DNr1EDON.js} +1 -1
  62. package/dist/test-api.js +7 -7
  63. package/openclaw.plugin.json +116 -0
  64. package/package.json +8 -6
  65. package/dist/accounts.runtime-DDVcLJUI.js +0 -2
  66. package/dist/action-runtime.runtime-BFcqMbOm.js +0 -2
  67. package/dist/send.runtime-BRE_ncCU.js +0 -2
  68. package/dist/send.runtime-_l76lUuL.js +0 -2
  69. /package/dist/{allow-list-BPnnlRPL.js → allow-list-T7ZDpUsF.js} +0 -0
  70. /package/dist/{blocks-input-CwTFVImV.js → blocks-input-BJZ8vv03.js} +0 -0
  71. /package/dist/{channel-api-B_nZwosg.js → channel-api-BfBK89IN.js} +0 -0
  72. /package/dist/{client-CPe4GmDR.js → client-8r7r7aZ3.js} +0 -0
  73. /package/dist/{config-api-B_jq4NJW.js → config-api-B48Z4H47.js} +0 -0
  74. /package/dist/{errors-BYFHR24f.js → errors-BrtayXHa.js} +0 -0
  75. /package/dist/{magic-string.es-BMaGRRZ1.js → magic-string.es-BLAi6qQC.js} +0 -0
  76. /package/dist/{mrkdwn-Cax-eSfK.js → mrkdwn-18IzcEAY.js} +0 -0
  77. /package/dist/{registry-CeaoNfoP.js → registry-BdfKYina.js} +0 -0
  78. /package/dist/{resolve-allowlist-common-Bk3clYPK.js → resolve-allowlist-common-rhfyDyWi.js} +0 -0
  79. /package/dist/{runtime-Bo-KHM-F.js → runtime--VlVtTPu.js} +0 -0
  80. /package/dist/{secret-contract-Bo6lbSkh.js → secret-contract-BurGIyhv.js} +0 -0
  81. /package/dist/{security-audit-BtHGnD3d.js → security-audit-DvOpSaZM.js} +0 -0
  82. /package/dist/{slash-commands.runtime-22kgyst2.js → slash-commands.runtime-DRkNgIQ2.js} +0 -0
  83. /package/dist/{slash-plugin-commands.runtime-CF-n3MeP.js → slash-plugin-commands.runtime-Dj5h8hmv.js} +0 -0
  84. /package/dist/{slash-skill-commands.runtime-BMs0VjTe.js → slash-skill-commands.runtime-B-_KAk0C.js} +0 -0
  85. /package/dist/{streaming-compat-RkZgTmQ2.js → streaming-compat-C6rySwiD.js} +0 -0
  86. /package/dist/{thread-ts-C2x7c5PP.js → thread-ts-o-QBwB3k.js} +0 -0
@@ -1,11 +1,12 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-CiIaOW0V.js";
2
2
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { logVerbose as logVerbose$1 } from "openclaw/plugin-sdk/runtime-env";
4
+ import fs from "node:fs/promises";
4
5
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
6
  import { normalizeHostname } from "openclaw/plugin-sdk/host-runtime";
6
7
  import { resolveRequestUrl } from "openclaw/plugin-sdk/request-url";
7
8
  import { fetchWithRuntimeDispatcher } from "openclaw/plugin-sdk/runtime-fetch";
8
- import { fetchRemoteMedia, saveMediaBuffer } from "openclaw/plugin-sdk/media-runtime";
9
+ import { saveRemoteMedia } from "openclaw/plugin-sdk/media-runtime";
9
10
  import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
10
11
  //#region extensions/slack/src/file-reference.ts
11
12
  function formatSlackFileReference(file) {
@@ -234,7 +235,7 @@ function mergeAbortSignals(signals) {
234
235
  for (const signal of activeSignals) signal.addEventListener("abort", abort, { once: true });
235
236
  return controller.signal;
236
237
  }
237
- async function fetchSlackMedia(params) {
238
+ async function saveSlackMedia(params) {
238
239
  const timeoutAbortController = params.totalTimeoutMs ? new AbortController() : void 0;
239
240
  const signal = mergeAbortSignals([
240
241
  params.abortSignal,
@@ -243,7 +244,7 @@ async function fetchSlackMedia(params) {
243
244
  ]);
244
245
  let timedOut = false;
245
246
  let timeoutHandle = null;
246
- const fetchPromise = fetchRemoteMedia({
247
+ const savePromise = saveRemoteMedia({
247
248
  ...params.options,
248
249
  readIdleTimeoutMs: params.readIdleTimeoutMs ?? 6e4,
249
250
  ...signal ? { requestInit: {
@@ -255,7 +256,7 @@ async function fetchSlackMedia(params) {
255
256
  throw error;
256
257
  });
257
258
  try {
258
- if (!params.totalTimeoutMs) return await fetchPromise;
259
+ if (!params.totalTimeoutMs) return await savePromise;
259
260
  const timeoutPromise = new Promise((_, reject) => {
260
261
  timeoutHandle = setTimeout(() => {
261
262
  timedOut = true;
@@ -264,7 +265,7 @@ async function fetchSlackMedia(params) {
264
265
  }, params.totalTimeoutMs);
265
266
  timeoutHandle.unref?.();
266
267
  });
267
- return await Promise.race([fetchPromise, timeoutPromise]);
268
+ return await Promise.race([savePromise, timeoutPromise]);
268
269
  } finally {
269
270
  if (timeoutHandle) clearTimeout(timeoutHandle);
270
271
  }
@@ -284,6 +285,17 @@ function looksLikeHtmlBuffer(buffer) {
284
285
  const head = normalizeLowercaseStringOrEmpty(buffer.subarray(0, 512).toString("utf-8").replace(/^\s+/, ""));
285
286
  return head.startsWith("<!doctype html") || head.startsWith("<html");
286
287
  }
288
+ async function looksLikeHtmlFile(filePath) {
289
+ const handle = await fs.open(filePath, "r").catch(() => null);
290
+ if (!handle) return false;
291
+ try {
292
+ const buffer = Buffer.alloc(512);
293
+ const { bytesRead } = await handle.read(buffer, 0, buffer.byteLength, 0);
294
+ return looksLikeHtmlBuffer(buffer.subarray(0, bytesRead));
295
+ } finally {
296
+ await handle.close().catch(() => void 0);
297
+ }
298
+ }
287
299
  const MAX_SLACK_MEDIA_CONCURRENCY = 3;
288
300
  const MAX_SLACK_FORWARDED_ATTACHMENTS = 8;
289
301
  async function fetchFreshSlackFileUrl(params) {
@@ -304,12 +316,13 @@ async function fetchFreshSlackFileUrl(params) {
304
316
  }
305
317
  async function downloadSlackMediaFile(params) {
306
318
  const { url: slackUrl, requestInit } = createSlackMediaRequest(params.url, params.token);
307
- const fetched = await fetchSlackMedia({
319
+ const saved = await saveSlackMedia({
308
320
  options: {
309
321
  url: slackUrl,
310
322
  fetchImpl: createSlackMediaFetch(),
311
323
  requestInit,
312
324
  filePathHint: params.file.name,
325
+ fallbackContentType: resolveSlackMediaMimetype(params.file, params.file.mimetype),
313
326
  maxBytes: params.maxBytes,
314
327
  ssrfPolicy: SLACK_MEDIA_SSRF_POLICY
315
328
  },
@@ -317,15 +330,16 @@ async function downloadSlackMediaFile(params) {
317
330
  totalTimeoutMs: params.totalTimeoutMs ?? 12e4,
318
331
  abortSignal: params.abortSignal
319
332
  });
320
- if (fetched.buffer.byteLength > params.maxBytes) return null;
321
333
  const fileMime = normalizeOptionalLowercaseString(params.file.mimetype);
322
334
  const fileName = normalizeLowercaseStringOrEmpty(params.file.name);
323
335
  if (!(fileMime === "text/html" || fileName.endsWith(".html") || fileName.endsWith(".htm"))) {
324
- if (normalizeOptionalLowercaseString(fetched.contentType?.split(";")[0]) === "text/html" || looksLikeHtmlBuffer(fetched.buffer)) return null;
336
+ if (normalizeOptionalLowercaseString(saved.contentType?.split(";")[0]) === "text/html" || await looksLikeHtmlFile(saved.path)) {
337
+ await fs.rm(saved.path, { force: true }).catch(() => void 0);
338
+ return null;
339
+ }
325
340
  }
326
- const effectiveMime = resolveSlackMediaMimetype(params.file, fetched.contentType);
327
- const saved = await saveMediaBuffer(fetched.buffer, effectiveMime, "inbound", params.maxBytes);
328
- const label = fetched.fileName ?? params.file.name;
341
+ const effectiveMime = resolveSlackMediaMimetype(params.file, saved.contentType);
342
+ const label = saved.fileName ?? params.file.name;
329
343
  const contentType = effectiveMime ?? saved.contentType;
330
344
  return {
331
345
  path: saved.path,
@@ -423,7 +437,7 @@ async function resolveSlackAttachmentContent(params) {
423
437
  const imageUrl = resolveForwardedAttachmentImageUrl(att);
424
438
  if (imageUrl) try {
425
439
  const { url: slackUrl, requestInit } = createSlackMediaRequest(imageUrl, params.token);
426
- const fetched = await fetchSlackMedia({
440
+ const saved = await saveSlackMedia({
427
441
  options: {
428
442
  url: slackUrl,
429
443
  fetchImpl: createSlackMediaFetch(),
@@ -435,15 +449,12 @@ async function resolveSlackAttachmentContent(params) {
435
449
  totalTimeoutMs: params.totalTimeoutMs ?? 12e4,
436
450
  abortSignal: params.abortSignal
437
451
  });
438
- if (fetched.buffer.byteLength <= params.maxBytes) {
439
- const saved = await saveMediaBuffer(fetched.buffer, fetched.contentType, "inbound", params.maxBytes);
440
- const label = fetched.fileName ?? "forwarded image";
441
- allMedia.push({
442
- path: saved.path,
443
- contentType: fetched.contentType ?? saved.contentType,
444
- placeholder: `[Forwarded image: ${label}]`
445
- });
446
- }
452
+ const label = saved.fileName ?? "forwarded image";
453
+ allMedia.push({
454
+ path: saved.path,
455
+ contentType: saved.contentType,
456
+ placeholder: `[Forwarded image: ${label}]`
457
+ });
447
458
  } catch {}
448
459
  if (att.files && att.files.length > 0) {
449
460
  const fileMedia = await resolveSlackMedia({
@@ -1,5 +1,5 @@
1
- import { a as resolveSlackAccount, t as listEnabledSlackAccounts } from "./accounts-ClAPP5ry.js";
2
- import { n as isSlackInteractiveRepliesEnabled } from "./interactive-replies-qAIfuBor.js";
1
+ import { a as resolveSlackAccount, t as listEnabledSlackAccounts } from "./accounts-yk5K3wQU.js";
2
+ import { n as isSlackInteractiveRepliesEnabled } from "./interactive-replies-CawNPL-h.js";
3
3
  import { createActionGate } from "openclaw/plugin-sdk/channel-actions";
4
4
  import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
5
5
  import { Type } from "typebox";
@@ -1,2 +1,2 @@
1
- import { t as describeSlackMessageTool } from "./message-tool-api-6lowf9zE.js";
1
+ import { t as describeSlackMessageTool } from "./message-tool-api-B_bKUmP0.js";
2
2
  export { describeSlackMessageTool as describeMessageTool };
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-CiIaOW0V.js";
2
- import { D as buildSlackSlashCommandMatcher, p as isSlackChannelAllowedByPolicy } from "./room-context-0vovmZPU.js";
3
- import { t as monitorSlackProvider } from "./provider-D7uAN3Fq.js";
4
- import { o as resolveSlackThreadTs } from "./replies-Xe_jMR6o.js";
2
+ import { D as buildSlackSlashCommandMatcher, p as isSlackChannelAllowedByPolicy } from "./room-context-D0hGOp8m.js";
3
+ import { t as monitorSlackProvider } from "./provider-Ut7IF0Mn.js";
4
+ import { o as resolveSlackThreadTs } from "./replies-CzfjCaLG.js";
5
5
  //#region extensions/slack/src/monitor.ts
6
6
  var monitor_exports = /* @__PURE__ */ __exportAll({
7
7
  buildSlackSlashCommandMatcher: () => buildSlackSlashCommandMatcher,
@@ -1,8 +1,8 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-CiIaOW0V.js";
2
- import { n as buildSlackPresentationBlocks, r as resolveSlackInteractiveBlockOffsets, t as buildSlackInteractiveBlocks } from "./blocks-render-BIDw-Pom.js";
3
- import { n as resolveSlackThreadTsValue, r as SLACK_TEXT_LIMIT } from "./thread-ts-C2x7c5PP.js";
4
- import { t as compileSlackInteractiveReplies } from "./interactive-replies-qAIfuBor.js";
5
- import { n as parseSlackBlocksInput } from "./blocks-input-CwTFVImV.js";
2
+ import { n as buildSlackPresentationBlocks, r as resolveSlackInteractiveBlockOffsets, t as buildSlackInteractiveBlocks } from "./blocks-render-BdLueE_v.js";
3
+ import { n as resolveSlackThreadTsValue, r as SLACK_TEXT_LIMIT } from "./thread-ts-o-QBwB3k.js";
4
+ import { t as compileSlackInteractiveReplies } from "./interactive-replies-CawNPL-h.js";
5
+ import { n as parseSlackBlocksInput } from "./blocks-input-BJZ8vv03.js";
6
6
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
7
  import { attachChannelToResult, createAttachedChannelResultAdapter } from "openclaw/plugin-sdk/channel-send-result";
8
8
  import { resolveOutboundSendDep } from "openclaw/plugin-sdk/outbound-send-deps";
@@ -13,7 +13,7 @@ var outbound_adapter_exports = /* @__PURE__ */ __exportAll({ slackOutbound: () =
13
13
  const SLACK_MAX_BLOCKS = 50;
14
14
  let slackSendRuntimePromise;
15
15
  async function loadSlackSendRuntime() {
16
- slackSendRuntimePromise ??= import("./send.runtime-_l76lUuL.js");
16
+ slackSendRuntimePromise ??= import("./send.runtime-5Kl3Wzbf.js");
17
17
  return await slackSendRuntimePromise;
18
18
  }
19
19
  function resolveRenderedInteractiveBlocks(interactive, previousBlocks) {
@@ -1,2 +1,2 @@
1
- import { t as createSlackOutboundPayloadHarness } from "./outbound-payload.test-harness-CVCamg1x.js";
1
+ import { t as createSlackOutboundPayloadHarness } from "./outbound-payload.test-harness-DM9ZdY2f.js";
2
2
  export { createSlackOutboundPayloadHarness };
@@ -1,4 +1,4 @@
1
- import { n as slackOutbound } from "./outbound-adapter-B_5sEhCg.js";
1
+ import { n as slackOutbound } from "./outbound-adapter-DsAvCwpZ.js";
2
2
  import { primeChannelOutboundSendMock } from "openclaw/plugin-sdk/channel-contract-testing";
3
3
  //#region node_modules/tinyrainbow/dist/index.js
4
4
  var b = {
@@ -10147,7 +10147,7 @@ function memo(fn) {
10147
10147
  };
10148
10148
  }
10149
10149
  async function saveInlineSnapshots(environment, snapshots) {
10150
- const MagicString = (await import("./magic-string.es-BMaGRRZ1.js")).default;
10150
+ const MagicString = (await import("./magic-string.es-BLAi6qQC.js")).default;
10151
10151
  const files = new Set(snapshots.map((i) => i.file));
10152
10152
  await Promise.all(Array.from(files).map(async (file) => {
10153
10153
  const snaps = snapshots.filter((i) => i.file === file);
@@ -1,24 +1,25 @@
1
- import { i as truncateSlackText, r as SLACK_TEXT_LIMIT } from "./thread-ts-C2x7c5PP.js";
2
- import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-qAIfuBor.js";
3
- import { i as normalizeSlackAllowOwnerEntry } from "./allow-list-BPnnlRPL.js";
4
- import "./blocks-input-CwTFVImV.js";
5
- import { n as resolveSlackNativeStreaming, r as resolveSlackStreamingMode, t as mapStreamingModeToSlackLegacyDraftStreamMode } from "./streaming-compat-RkZgTmQ2.js";
6
- import { a as recordSlackThreadParticipation, s as normalizeSlackOutboundText, t as sendMessageSlack } from "./send-D_A9kL-C.js";
7
- import { f as removeSlackReaction, h as buildSlackEditTextPayload, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage } from "./actions-CYLFK-Zy.js";
8
- import { t as formatSlackError } from "./errors-BYFHR24f.js";
9
- import { T as updateLastRoute, a as recordInboundSession, w as resolveStorePath } from "./room-context-0vovmZPU.js";
10
- import { t as escapeSlackMrkdwn } from "./mrkdwn-Cax-eSfK.js";
11
- import { n as resolveSlackThreadTargets, t as prepareSlackMessage } from "./prepare-D3YqV8jB.js";
12
- import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, t as createSlackReplyDeliveryPlan } from "./replies-Xe_jMR6o.js";
1
+ import { i as truncateSlackText, r as SLACK_TEXT_LIMIT } from "./thread-ts-o-QBwB3k.js";
2
+ import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-CawNPL-h.js";
3
+ import { i as normalizeSlackAllowOwnerEntry } from "./allow-list-T7ZDpUsF.js";
4
+ import "./blocks-input-BJZ8vv03.js";
5
+ import { n as resolveSlackNativeStreaming, r as resolveSlackStreamingMode, t as mapStreamingModeToSlackLegacyDraftStreamMode } from "./streaming-compat-C6rySwiD.js";
6
+ import { a as recordSlackThreadParticipation, s as normalizeSlackOutboundText, t as sendMessageSlack } from "./send-ioky2Xpy.js";
7
+ import { f as removeSlackReaction, h as buildSlackEditTextPayload, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage } from "./actions-Cqyj7oRr.js";
8
+ import { t as formatSlackError } from "./errors-BrtayXHa.js";
9
+ import { T as updateLastRoute, a as recordInboundSession, w as resolveStorePath } from "./room-context-D0hGOp8m.js";
10
+ import { t as escapeSlackMrkdwn } from "./mrkdwn-18IzcEAY.js";
11
+ import { n as resolveSlackThreadTargets, t as prepareSlackMessage } from "./prepare-CXI8nHbJ.js";
12
+ import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, t as createSlackReplyDeliveryPlan } from "./replies-CzfjCaLG.js";
13
13
  import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
14
14
  import { resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
15
15
  import { createChannelMessageReplyPipeline, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, resolveChannelMessageSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-message";
16
- import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, resolveChannelProgressDraftLabel, resolveChannelProgressDraftMaxLines, resolveChannelProgressDraftRender, resolveChannelStreamingBlockEnabled, resolveChannelStreamingNativeTransport, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-streaming";
16
+ import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, resolveChannelProgressDraftLabel, resolveChannelProgressDraftMaxLines, resolveChannelProgressDraftRender, resolveChannelStreamingBlockEnabled, resolveChannelStreamingNativeTransport, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-streaming";
17
17
  import { danger, logVerbose, shouldLogVerbose, sleep } from "openclaw/plugin-sdk/runtime-env";
18
18
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
19
19
  import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
20
20
  import { clearHistoryEntriesIfEnabled } from "openclaw/plugin-sdk/reply-history";
21
21
  import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
22
+ import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
22
23
  import { resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
23
24
  import { DEFAULT_TIMING, createStatusReactionController, logAckFailure, logTypingFailure, removeAckReactionAfterReply } from "openclaw/plugin-sdk/channel-feedback";
24
25
  import { hasVisibleInboundReplyDispatch, runInboundReplyTurn } from "openclaw/plugin-sdk/inbound-reply-dispatch";
@@ -465,10 +466,34 @@ const UNICODE_TO_SLACK = {
465
466
  "⏳": "hourglass_flowing_sand",
466
467
  "⚠️": "warning",
467
468
  "✍": "writing_hand",
469
+ "🗜️": "compression",
470
+ "🗜": "compression",
468
471
  "🧠": "brain",
469
472
  "🛠️": "hammer_and_wrench",
470
473
  "💻": "computer"
471
474
  };
475
+ function resolveSlackMessageTimestampMs(message) {
476
+ const ts = message.event_ts ?? message.ts;
477
+ if (!ts) return;
478
+ const parsed = Number(ts);
479
+ return Number.isFinite(parsed) ? Math.trunc(parsed * 1e3) : void 0;
480
+ }
481
+ function resolveSlackBotLoopProtection(prepared) {
482
+ const senderBotId = prepared.message.bot_id;
483
+ if (!senderBotId) return;
484
+ const receiverBotId = prepared.ctx.botId || prepared.ctx.botUserId;
485
+ if (!receiverBotId || senderBotId === prepared.ctx.botId || prepared.message.user === prepared.ctx.botUserId) return;
486
+ return {
487
+ scopeId: prepared.route.accountId,
488
+ conversationId: prepared.message.channel,
489
+ senderId: senderBotId,
490
+ receiverId: receiverBotId,
491
+ config: mergePairLoopGuardConfig(prepared.account.config.botLoopProtection, prepared.channelConfig?.botLoopProtection),
492
+ defaultsConfig: prepared.ctx.cfg.channels?.defaults?.botLoopProtection,
493
+ defaultEnabled: true,
494
+ nowMs: resolveSlackMessageTimestampMs(prepared.message)
495
+ };
496
+ }
472
497
  function toSlackEmojiName(emoji) {
473
498
  let trimmed = emoji.trim();
474
499
  while (trimmed.startsWith(":")) trimmed = trimmed.slice(1);
@@ -1112,8 +1137,9 @@ async function dispatchPreparedSlackMessage(prepared) {
1112
1137
  }
1113
1138
  if (streamMode !== "status_final") {
1114
1139
  if (!previewToolProgressEnabled || previewToolProgressSuppressed) return;
1115
- if (previewToolProgressLines.at(-1)?.text === normalized) return;
1116
- previewToolProgressLines = [...previewToolProgressLines, line].slice(-resolveChannelProgressDraftMaxLines(account.config));
1140
+ const nextLines = mergeChannelProgressDraftLine(previewToolProgressLines, line, { maxLines: resolveChannelProgressDraftMaxLines(account.config) });
1141
+ if (nextLines === previewToolProgressLines) return;
1142
+ previewToolProgressLines = nextLines;
1117
1143
  draftStream.update(formatChannelProgressDraftText({
1118
1144
  entry: account.config,
1119
1145
  lines: previewToolProgressLines,
@@ -1123,9 +1149,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1123
1149
  hasStreamedMessage = true;
1124
1150
  return;
1125
1151
  }
1126
- if (previewToolProgressEnabled && !previewToolProgressSuppressed) {
1127
- if (previewToolProgressLines.at(-1)?.text !== normalized) previewToolProgressLines = [...previewToolProgressLines, line].slice(-resolveChannelProgressDraftMaxLines(account.config));
1128
- }
1152
+ if (previewToolProgressEnabled && !previewToolProgressSuppressed) previewToolProgressLines = mergeChannelProgressDraftLine(previewToolProgressLines, line, { maxLines: resolveChannelProgressDraftMaxLines(account.config) });
1129
1153
  const alreadyStarted = progressDraftGate.hasStarted;
1130
1154
  await progressDraftGate.noteWork();
1131
1155
  if (alreadyStarted && progressDraftGate.hasStarted) renderProgressDraft();
@@ -1197,6 +1221,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1197
1221
  ctxPayload: prepared.ctxPayload,
1198
1222
  recordInboundSession,
1199
1223
  record: prepared.turn.record,
1224
+ botLoopProtection: resolveSlackBotLoopProtection(prepared),
1200
1225
  onPreDispatchFailure: async () => {
1201
1226
  dispatchSettledBeforeStart = true;
1202
1227
  await settleReplyDispatcher({
@@ -1236,6 +1261,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1236
1261
  onItemEvent: async (payload) => {
1237
1262
  await pushPreviewToolProgress(buildChannelProgressDraftLineForEntry(account.config, {
1238
1263
  event: "item",
1264
+ itemId: payload.itemId,
1239
1265
  itemKind: payload.kind,
1240
1266
  title: payload.title,
1241
1267
  name: payload.name,
@@ -1296,10 +1322,11 @@ async function dispatchPreparedSlackMessage(prepared) {
1296
1322
  })
1297
1323
  }
1298
1324
  });
1299
- if (!turnResult.dispatched) return;
1300
- const result = turnResult.dispatchResult;
1301
- queuedFinal = result.queuedFinal;
1302
- counts = result.counts;
1325
+ if (turnResult.dispatched) {
1326
+ const result = turnResult.dispatchResult;
1327
+ queuedFinal = result.queuedFinal;
1328
+ counts = result.counts;
1329
+ }
1303
1330
  } catch (err) {
1304
1331
  dispatchError = err;
1305
1332
  } finally {
@@ -1,5 +1,5 @@
1
- import { n as listSlackAccountIds, r as mergeSlackAccountConfig } from "./accounts-ClAPP5ry.js";
2
- import { r as normalizeSlackWebhookPath, t as handleSlackHttpRequest } from "./registry-CeaoNfoP.js";
1
+ import { n as listSlackAccountIds, r as mergeSlackAccountConfig } from "./accounts-yk5K3wQU.js";
2
+ import { r as normalizeSlackWebhookPath, t as handleSlackHttpRequest } from "./registry-BdfKYina.js";
3
3
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
4
4
  //#region extensions/slack/src/http/plugin-routes.ts
5
5
  function registerSlackPluginHttpRoutes(api) {
@@ -1,13 +1,13 @@
1
- import { l as resolveSlackReplyToMode } from "./accounts-ClAPP5ry.js";
1
+ import { l as resolveSlackReplyToMode } from "./accounts-yk5K3wQU.js";
2
2
  import { r as parseSlackTarget } from "./target-parsing-CQmv-iSm.js";
3
3
  import "./targets-B1tYCAr6.js";
4
- import { i as normalizeSlackAllowOwnerEntry, o as resolveSlackAllowListMatch, r as normalizeAllowListLower } from "./allow-list-BPnnlRPL.js";
5
- import { i as hasSlackThreadParticipationWithPersistence, t as sendMessageSlack } from "./send-D_A9kL-C.js";
6
- import { l as reactSlackMessage } from "./actions-CYLFK-Zy.js";
7
- import { i as resolveSlackThreadStarter, o as formatSlackFileReference, r as resolveSlackThreadHistory } from "./media-D1XCd1uP.js";
8
- import { t as formatSlackError } from "./errors-BYFHR24f.js";
9
- import { b as readSessionUpdatedAt, c as authorizeSlackBotRoomMessage, d as resolveSlackEffectiveAllowFrom, g as resolveSlackChannelConfig, h as resolveSlackChatType, k as stripSlackMentionsForCommandDetection, m as normalizeSlackChannelType, n as authorizeSlackDirectMessage, o as resolveConversationLabel$1, t as resolveSlackRoomContextHints, u as resolveSlackCommandIngress, w as resolveStorePath, x as resolveChannelContextVisibilityMode } from "./room-context-0vovmZPU.js";
10
- import "./send.runtime-BRE_ncCU.js";
4
+ import { i as normalizeSlackAllowOwnerEntry, o as resolveSlackAllowListMatch, r as normalizeAllowListLower } from "./allow-list-T7ZDpUsF.js";
5
+ import { i as hasSlackThreadParticipationWithPersistence, t as sendMessageSlack } from "./send-ioky2Xpy.js";
6
+ import { l as reactSlackMessage } from "./actions-Cqyj7oRr.js";
7
+ import { i as resolveSlackThreadStarter, o as formatSlackFileReference, r as resolveSlackThreadHistory } from "./media-B-nNpS2G.js";
8
+ import { t as formatSlackError } from "./errors-BrtayXHa.js";
9
+ import { b as readSessionUpdatedAt, c as authorizeSlackBotRoomMessage, d as resolveSlackEffectiveAllowFrom, g as resolveSlackChannelConfig, h as resolveSlackChatType, k as stripSlackMentionsForCommandDetection, m as normalizeSlackChannelType, n as authorizeSlackDirectMessage, o as resolveConversationLabel$1, t as resolveSlackRoomContextHints, u as resolveSlackCommandIngress, w as resolveStorePath, x as resolveChannelContextVisibilityMode } from "./room-context-D0hGOp8m.js";
10
+ import "./send.runtime-DoifekaA.js";
11
11
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
12
12
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
13
13
  import { resolveChannelMessageSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-message";
@@ -29,7 +29,7 @@ const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
29
29
  const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
30
30
  let slackMediaModulePromise$1;
31
31
  function loadSlackMediaModule$1() {
32
- slackMediaModulePromise$1 ??= import("./media-D1XCd1uP.js").then((n) => n.t);
32
+ slackMediaModulePromise$1 ??= import("./media-B-nNpS2G.js").then((n) => n.t);
33
33
  return slackMediaModulePromise$1;
34
34
  }
35
35
  function collectUniqueSlackMentionIds$1(texts) {
@@ -570,7 +570,7 @@ function formatSlackBotStarterThreadLabel(params) {
570
570
  //#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
571
571
  let slackMediaModulePromise;
572
572
  function loadSlackMediaModule() {
573
- slackMediaModulePromise ??= import("./media-D1XCd1uP.js").then((n) => n.t);
573
+ slackMediaModulePromise ??= import("./media-B-nNpS2G.js").then((n) => n.t);
574
574
  return slackMediaModulePromise;
575
575
  }
576
576
  const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
@@ -1,4 +1,4 @@
1
- import { f as createSlackMonitorContext } from "./room-context-0vovmZPU.js";
1
+ import { f as createSlackMonitorContext } from "./room-context-D0hGOp8m.js";
2
2
  import "openclaw/plugin-sdk/temp-path";
3
3
  //#region extensions/slack/src/monitor/message-handler/prepare.test-helpers.ts
4
4
  function createInboundSlackTestContext(params) {
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-CiIaOW0V.js";
2
- import { r as createSlackWebClient } from "./client-CPe4GmDR.js";
3
- import { t as formatSlackError } from "./errors-BYFHR24f.js";
2
+ import { r as createSlackWebClient } from "./client-8r7r7aZ3.js";
3
+ import { t as formatSlackError } from "./errors-BrtayXHa.js";
4
4
  import { withTimeout } from "openclaw/plugin-sdk/text-utility-runtime";
5
5
  //#region extensions/slack/src/probe.ts
6
6
  var probe_exports = /* @__PURE__ */ __exportAll({ probeSlack: () => probeSlack });
@@ -1,16 +1,17 @@
1
- import { a as resolveSlackAccount, d as resolveSlackBotToken, o as resolveSlackAccountAllowFrom, s as resolveSlackAccountDmPolicy, u as resolveSlackAppToken } from "./accounts-ClAPP5ry.js";
2
- import { i as isSlackExecApprovalClientEnabled, n as isSlackExecApprovalApprover, r as isSlackExecApprovalAuthorizedSender } from "./exec-approvals-7xUNgLi9.js";
3
- import "./blocks-render-BIDw-Pom.js";
4
- import { i as truncateSlackText, r as SLACK_TEXT_LIMIT } from "./thread-ts-C2x7c5PP.js";
5
- import { c as resolveSlackWebClientOptions } from "./client-CPe4GmDR.js";
6
- import { n as normalizeAllowList } from "./allow-list-BPnnlRPL.js";
7
- import "./blocks-input-CwTFVImV.js";
8
- import { n as registerSlackHttpHandler, r as normalizeSlackWebhookPath } from "./registry-CeaoNfoP.js";
9
- import { t as formatSlackError } from "./errors-BYFHR24f.js";
10
- import { t as resolveSlackChannelAllowlist } from "./resolve-channels-BRYqyNVJ.js";
11
- import { t as resolveSlackUserAllowlist } from "./resolve-users-Bd_SdP8j.js";
12
- import { C as resolveOpenProviderRuntimeGroupPolicy, D as buildSlackSlashCommandMatcher, E as warnMissingProviderGroupPolicyFallbackOnce, O as resolveSlackSlashCommandConfig, S as resolveDefaultGroupPolicy, _ as resolveSlackChannelLabel, d as resolveSlackEffectiveAllowFrom, f as createSlackMonitorContext, g as resolveSlackChannelConfig, h as resolveSlackChatType, i as parsePluginBindingApprovalCustomId, k as stripSlackMentionsForCommandDetection, l as authorizeSlackSystemEventSender, m as normalizeSlackChannelType, n as authorizeSlackDirectMessage, p as isSlackChannelAllowedByPolicy, r as buildPluginBindingResolvedText, s as resolvePluginConversationBindingApproval, t as resolveSlackRoomContextHints, u as resolveSlackCommandIngress, v as getRuntimeConfig$1, y as isDangerousNameMatchingEnabled } from "./room-context-0vovmZPU.js";
13
- import { t as escapeSlackMrkdwn } from "./mrkdwn-Cax-eSfK.js";
1
+ import { a as resolveSlackAccount, d as resolveSlackBotToken, o as resolveSlackAccountAllowFrom, s as resolveSlackAccountDmPolicy, u as resolveSlackAppToken } from "./accounts-yk5K3wQU.js";
2
+ import { n as isSlackExecApprovalAuthorizedSender, r as isSlackExecApprovalClientEnabled } from "./exec-approvals-BLn4Zx7V.js";
3
+ import { t as isSlackApprovalAuthorizedSender } from "./approval-auth-D3xf0sS6.js";
4
+ import "./blocks-render-BdLueE_v.js";
5
+ import { i as truncateSlackText, r as SLACK_TEXT_LIMIT } from "./thread-ts-o-QBwB3k.js";
6
+ import { c as resolveSlackWebClientOptions } from "./client-8r7r7aZ3.js";
7
+ import { n as normalizeAllowList, r as normalizeAllowListLower, t as allowListMatches } from "./allow-list-T7ZDpUsF.js";
8
+ import "./blocks-input-BJZ8vv03.js";
9
+ import { n as registerSlackHttpHandler, r as normalizeSlackWebhookPath } from "./registry-BdfKYina.js";
10
+ import { t as formatSlackError } from "./errors-BrtayXHa.js";
11
+ import { t as resolveSlackChannelAllowlist } from "./resolve-channels-BiVxSLVm.js";
12
+ import { t as resolveSlackUserAllowlist } from "./resolve-users-CcpSlYw-.js";
13
+ import { C as resolveOpenProviderRuntimeGroupPolicy, D as buildSlackSlashCommandMatcher, E as warnMissingProviderGroupPolicyFallbackOnce, O as resolveSlackSlashCommandConfig, S as resolveDefaultGroupPolicy, _ as resolveSlackChannelLabel, d as resolveSlackEffectiveAllowFrom, f as createSlackMonitorContext, g as resolveSlackChannelConfig, h as resolveSlackChatType, i as parsePluginBindingApprovalCustomId, k as stripSlackMentionsForCommandDetection, l as authorizeSlackSystemEventSender, m as normalizeSlackChannelType, n as authorizeSlackDirectMessage, p as isSlackChannelAllowedByPolicy, r as buildPluginBindingResolvedText, s as resolvePluginConversationBindingApproval, t as resolveSlackRoomContextHints, u as resolveSlackCommandIngress, v as getRuntimeConfig$1, y as isDangerousNameMatchingEnabled } from "./room-context-D0hGOp8m.js";
14
+ import { t as escapeSlackMrkdwn } from "./mrkdwn-18IzcEAY.js";
14
15
  import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
15
16
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
16
17
  import { normalizeAccountId, normalizeMainKey } from "openclaw/plugin-sdk/routing";
@@ -30,7 +31,7 @@ import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
30
31
  import { loadSessionStore, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
31
32
  import { resolveDefaultModelForAgent } from "openclaw/plugin-sdk/agent-runtime";
32
33
  import { resolveChannelConfigWrites } from "openclaw/plugin-sdk/channel-config-writes";
33
- import { replaceConfigFile } from "openclaw/plugin-sdk/config-mutation";
34
+ import { mutateConfigFile } from "openclaw/plugin-sdk/config-mutation";
34
35
  import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
35
36
  import { resolveApprovalOverGateway } from "openclaw/plugin-sdk/approval-gateway-runtime";
36
37
  import { parseExecApprovalCommandText } from "openclaw/plugin-sdk/approval-reply-runtime";
@@ -122,7 +123,8 @@ function registerSlackChannelEvents(params) {
122
123
  });
123
124
  enqueueSystemEvent(`Slack channel ${params.kind}: ${label}.`, {
124
125
  sessionKey,
125
- contextKey: `slack:channel:${params.kind}:${params.channelId ?? params.channelName ?? "unknown"}`
126
+ contextKey: `slack:channel:${params.kind}:${params.channelId ?? params.channelName ?? "unknown"}`,
127
+ trusted: false
126
128
  });
127
129
  };
128
130
  ctx.app.event("channel_created", async ({ event, body }) => {
@@ -177,9 +179,8 @@ function registerSlackChannelEvents(params) {
177
179
  ctx.runtime.log?.(warn("[slack] Config writes disabled; skipping channel config migration."));
178
180
  return;
179
181
  }
180
- const currentConfig = getRuntimeConfig();
181
182
  const migration = migrateSlackChannelConfig({
182
- cfg: currentConfig,
183
+ cfg: getRuntimeConfig(),
183
184
  accountId: ctx.accountId,
184
185
  oldChannelId,
185
186
  newChannelId
@@ -191,9 +192,16 @@ function registerSlackChannelEvents(params) {
191
192
  oldChannelId,
192
193
  newChannelId
193
194
  });
194
- await replaceConfigFile({
195
- nextConfig: currentConfig,
196
- afterWrite: { mode: "auto" }
195
+ await mutateConfigFile({
196
+ afterWrite: { mode: "auto" },
197
+ mutate: (draft) => {
198
+ migrateSlackChannelConfig({
199
+ cfg: draft,
200
+ accountId: ctx.accountId,
201
+ oldChannelId,
202
+ newChannelId
203
+ });
204
+ }
197
205
  });
198
206
  ctx.runtime.log?.(warn("[slack] Channel config migrated and saved successfully."));
199
207
  } else if (migration.skippedExisting) ctx.runtime.log?.(warn(`[slack] Channel config already exists for ${newChannelId}; leaving ${oldChannelId} unchanged`));
@@ -496,7 +504,7 @@ async function handleSlackPluginBindingApproval(params) {
496
504
  async function handleSlackExecApprovalInteraction(params) {
497
505
  const approval = parseExecApprovalCommandText(params.pluginInteractionData);
498
506
  if (!approval) return false;
499
- const pluginApprovalAuthorizedSender = isSlackExecApprovalApprover({
507
+ const pluginApprovalAuthorizedSender = isSlackApprovalAuthorizedSender({
500
508
  cfg: params.ctx.cfg,
501
509
  accountId: params.ctx.accountId,
502
510
  senderId: params.parsed.userId
@@ -506,7 +514,11 @@ async function handleSlackExecApprovalInteraction(params) {
506
514
  accountId: params.ctx.accountId,
507
515
  senderId: params.parsed.userId
508
516
  });
509
- if (!(approval.approvalId.startsWith("plugin:") ? pluginApprovalAuthorizedSender : execApprovalAuthorizedSender || pluginApprovalAuthorizedSender)) {
517
+ const isPluginApproval = approval.approvalId.startsWith("plugin:");
518
+ const resolveUnprefixedAsPlugin = !isPluginApproval && !execApprovalAuthorizedSender && pluginApprovalAuthorizedSender;
519
+ const authorized = isPluginApproval ? pluginApprovalAuthorizedSender : execApprovalAuthorizedSender || resolveUnprefixedAsPlugin;
520
+ const allowPluginFallback = !isPluginApproval && execApprovalAuthorizedSender && pluginApprovalAuthorizedSender;
521
+ if (!authorized) {
510
522
  params.ctx.runtime.log?.(`slack:interaction drop exec approval user=${params.parsed.userId} (not authorized)`);
511
523
  await respondEphemeral(params.respond, "You are not authorized to approve this request.");
512
524
  return true;
@@ -517,7 +529,8 @@ async function handleSlackExecApprovalInteraction(params) {
517
529
  approvalId: approval.approvalId,
518
530
  decision: approval.decision,
519
531
  senderId: params.parsed.userId,
520
- allowPluginFallback: pluginApprovalAuthorizedSender,
532
+ allowPluginFallback,
533
+ ...resolveUnprefixedAsPlugin ? { resolveMethod: "plugin" } : {},
521
534
  clientDisplayName: `Slack approval (${params.parsed.userId.trim() || "unknown"})`
522
535
  });
523
536
  } catch (error) {
@@ -1402,12 +1415,30 @@ function registerSlackPinEvents(params) {
1402
1415
  }
1403
1416
  //#endregion
1404
1417
  //#region extensions/slack/src/monitor/events/reactions.ts
1418
+ function shouldEmitSlackReactionNotification(params) {
1419
+ const { ctx, event, actorName } = params;
1420
+ if (ctx.reactionMode === "off") return false;
1421
+ if (ctx.reactionMode === "own") return Boolean(ctx.botUserId && event.item_user === ctx.botUserId);
1422
+ if (ctx.reactionMode === "allowlist") {
1423
+ const allowList = normalizeAllowListLower(ctx.reactionAllowlist);
1424
+ if (allowList.length === 0) return false;
1425
+ return allowListMatches({
1426
+ allowList,
1427
+ id: event.user,
1428
+ name: actorName,
1429
+ allowNameMatching: ctx.allowNameMatching
1430
+ });
1431
+ }
1432
+ return ctx.reactionMode === "all";
1433
+ }
1405
1434
  function registerSlackReactionEvents(params) {
1406
1435
  const { ctx, trackEvent } = params;
1407
1436
  const handleReactionEvent = async (event, action) => {
1408
1437
  try {
1409
1438
  const item = event.item;
1410
1439
  if (!item || item.type !== "message") return;
1440
+ if (ctx.reactionMode === "off") return;
1441
+ if (ctx.reactionMode === "own" && (!ctx.botUserId || event.item_user !== ctx.botUserId)) return;
1411
1442
  trackEvent?.();
1412
1443
  const ingressContext = await authorizeAndResolveSlackSystemEventContext({
1413
1444
  ctx,
@@ -1419,13 +1450,19 @@ function registerSlackReactionEvents(params) {
1419
1450
  const actorInfoPromise = event.user ? ctx.resolveUserName(event.user) : Promise.resolve(void 0);
1420
1451
  const authorInfoPromise = event.item_user ? ctx.resolveUserName(event.item_user) : Promise.resolve(void 0);
1421
1452
  const [actorInfo, authorInfo] = await Promise.all([actorInfoPromise, authorInfoPromise]);
1453
+ if (!shouldEmitSlackReactionNotification({
1454
+ ctx,
1455
+ event,
1456
+ actorName: actorInfo?.name
1457
+ })) return;
1422
1458
  const actorLabel = actorInfo?.name ?? event.user;
1423
1459
  const emojiLabel = event.reaction ?? "emoji";
1424
1460
  const authorLabel = authorInfo?.name ?? event.item_user;
1425
1461
  const baseText = `Slack reaction ${action}: :${emojiLabel}: by ${actorLabel} in ${ingressContext.channelLabel} msg ${item.ts}`;
1426
1462
  enqueueSystemEvent(authorLabel ? `${baseText} from ${authorLabel}` : baseText, {
1427
1463
  sessionKey: ingressContext.sessionKey,
1428
- contextKey: `slack:reaction:${action}:${item.channel}:${item.ts}:${event.user}:${emojiLabel}`
1464
+ contextKey: `slack:reaction:${action}:${item.channel}:${item.ts}:${event.user}:${emojiLabel}`,
1465
+ trusted: false
1429
1466
  });
1430
1467
  } catch (err) {
1431
1468
  ctx.runtime.error?.(danger(`slack reaction handler failed: ${formatErrorMessage(err)}`));
@@ -1590,7 +1627,7 @@ function createSlackThreadTsResolver(params) {
1590
1627
  //#region extensions/slack/src/monitor/message-handler.ts
1591
1628
  let slackMessagePipelinePromise;
1592
1629
  function loadSlackMessagePipeline() {
1593
- slackMessagePipelinePromise ??= import("./pipeline.runtime-DT0hLnq2.js");
1630
+ slackMessagePipelinePromise ??= import("./pipeline.runtime-D-YJZZaI.js");
1594
1631
  return slackMessagePipelinePromise;
1595
1632
  }
1596
1633
  const APP_MENTION_RETRY_TTL_MS = 6e4;
@@ -2128,19 +2165,19 @@ let slashDispatchRuntimePromise = null;
2128
2165
  let slackPluginCommandsRuntimePromise = null;
2129
2166
  let slashSkillCommandsRuntimePromise = null;
2130
2167
  function loadSlashCommandsRuntime() {
2131
- slashCommandsRuntimePromise ??= import("./slash-commands.runtime-22kgyst2.js");
2168
+ slashCommandsRuntimePromise ??= import("./slash-commands.runtime-DRkNgIQ2.js");
2132
2169
  return slashCommandsRuntimePromise;
2133
2170
  }
2134
2171
  function loadSlashDispatchRuntime() {
2135
- slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-BJgT0jwV.js");
2172
+ slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-DNr1EDON.js");
2136
2173
  return slashDispatchRuntimePromise;
2137
2174
  }
2138
2175
  function loadSlackPluginCommandsRuntime() {
2139
- slackPluginCommandsRuntimePromise ??= import("./slash-plugin-commands.runtime-CF-n3MeP.js");
2176
+ slackPluginCommandsRuntimePromise ??= import("./slash-plugin-commands.runtime-Dj5h8hmv.js");
2140
2177
  return slackPluginCommandsRuntimePromise;
2141
2178
  }
2142
2179
  function loadSlashSkillCommandsRuntime() {
2143
- slashSkillCommandsRuntimePromise ??= import("./slash-skill-commands.runtime-BMs0VjTe.js");
2180
+ slashSkillCommandsRuntimePromise ??= import("./slash-skill-commands.runtime-B-_KAk0C.js");
2144
2181
  return slashSkillCommandsRuntimePromise;
2145
2182
  }
2146
2183
  function resolveSlackCommandMenuModelContext(params) {
@@ -1,7 +1,7 @@
1
- import { r as SLACK_TEXT_LIMIT } from "./thread-ts-C2x7c5PP.js";
2
- import { t as resolveSlackReplyBlocks } from "./reply-blocks-Z5l6_R6H.js";
3
- import { o as markdownToSlackMrkdwnChunks, t as sendMessageSlack } from "./send-D_A9kL-C.js";
4
- import "./send.runtime-BRE_ncCU.js";
1
+ import { r as SLACK_TEXT_LIMIT } from "./thread-ts-o-QBwB3k.js";
2
+ import { t as resolveSlackReplyBlocks } from "./reply-blocks-DWoZrUll.js";
3
+ import { o as markdownToSlackMrkdwnChunks, t as sendMessageSlack } from "./send-ioky2Xpy.js";
4
+ import "./send.runtime-DoifekaA.js";
5
5
  import { createReplyReferencePlanner } from "openclaw/plugin-sdk/reply-reference";
6
6
  import { SILENT_REPLY_TOKEN, chunkMarkdownTextWithMode, isSilentReplyText } from "openclaw/plugin-sdk/reply-chunking";
7
7
  import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
@@ -1,5 +1,5 @@
1
- import { t as buildSlackInteractiveBlocks } from "./blocks-render-BIDw-Pom.js";
2
- import { n as parseSlackBlocksInput } from "./blocks-input-CwTFVImV.js";
1
+ import { t as buildSlackInteractiveBlocks } from "./blocks-render-BdLueE_v.js";
2
+ import { n as parseSlackBlocksInput } from "./blocks-input-BJZ8vv03.js";
3
3
  //#region extensions/slack/src/reply-blocks.ts
4
4
  function resolveSlackReplyBlocks(payload) {
5
5
  const slackData = payload.channelData?.slack;