@openclaw/slack 2026.6.10-beta.2 → 2026.6.11-beta.2

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 (54) hide show
  1. package/dist/account-configured-DzC407uv.js +14 -0
  2. package/dist/{account-inspect-CdGk6R7l.js → account-inspect-DOtQbKkB.js} +5 -3
  3. package/dist/account-inspect-api.js +1 -1
  4. package/dist/{accounts-f6Xcv9Vi.js → accounts-BPCiCe8y.js} +3 -2
  5. package/dist/accounts.runtime-CgWgzjLX.js +2 -0
  6. package/dist/{action-runtime-CkD5nTPl.js → action-runtime-DfJK7zS0.js} +5 -5
  7. package/dist/action-runtime.runtime-CIYPRrNy.js +2 -0
  8. package/dist/{actions-B2w2hCZ2.js → actions-OhnDIN4l.js} +4 -4
  9. package/dist/{actions.runtime-B56qRt06.js → actions.runtime-DkFXr3-k.js} +1 -1
  10. package/dist/api.js +13 -13
  11. package/dist/{approval-handler.runtime-BS6xpR-X.js → approval-handler.runtime-CsFrthnF.js} +2 -2
  12. package/dist/{channel-DJBl-gwf.js → channel-COCbJwom.js} +28 -26
  13. package/dist/channel-config-api.js +1 -1
  14. package/dist/channel-plugin-api.js +1 -1
  15. package/dist/{channel.setup-WLGVcaSf.js → channel.setup-DYQGnKPK.js} +4 -4
  16. package/dist/{client-DhaHONzg.js → client-km2ba2l3.js} +2 -11
  17. package/dist/{config-schema-BjAjs8_6.js → config-schema-BzqzFW__.js} +16 -0
  18. package/dist/{directory-config-Dmhi8ECy.js → directory-config-DDVHDmrN.js} +1 -1
  19. package/dist/directory-contract-api.js +1 -1
  20. package/dist/{directory-live-D_pIXtL1.js → directory-live-DzOkmJVV.js} +2 -2
  21. package/dist/{interactive-replies-DrBq4Mld.js → interactive-replies-D-22gTp3.js} +1 -1
  22. package/dist/interactive-replies-api.js +1 -1
  23. package/dist/{message-tool-api-NLAYFat2.js → message-tool-api--zYyTqrR.js} +2 -2
  24. package/dist/message-tool-api.js +1 -1
  25. package/dist/{monitor-Bxdbet-b.js → monitor-Cy-0xYgF.js} +3 -3
  26. package/dist/{outbound-adapter-CBFfXlOI.js → outbound-adapter-CzX4D5K_.js} +3 -3
  27. package/dist/{pipeline.runtime-DQXhebqH.js → pipeline.runtime-5R4HgM_7.js} +13 -12
  28. package/dist/{policy-Dfbxkneh.js → policy-Bi92bs5t.js} +1 -1
  29. package/dist/{probe-C3PL00Ms.js → probe-BRsYAcGR.js} +1 -1
  30. package/dist/{provider-DNl27IZC.js → provider-7YKru1TZ.js} +159 -82
  31. package/dist/relay-source-42fP7Ktr.js +278 -0
  32. package/dist/{replies-CH4Dmgbr.js → replies-Dlxge48k.js} +3 -2
  33. package/dist/{reply-blocks-DSfujQlj.js → reply-blocks-BT_sNsDQ.js} +2 -2
  34. package/dist/{resolve-channels-DNiE0Z0_.js → resolve-channels-CtUOIX8D.js} +1 -1
  35. package/dist/{resolve-users-D_TmaIfG.js → resolve-users-DfFN2BcP.js} +1 -1
  36. package/dist/{runtime-api-DYg05-Op.js → runtime-api-ClM1_yX2.js} +1 -1
  37. package/dist/runtime-api.js +11 -11
  38. package/dist/{scopes-CR4oXiXN.js → scopes-CNKZU1t1.js} +1 -1
  39. package/dist/{secret-contract-g_PPWuoD.js → secret-contract-C3ChHo5A.js} +46 -6
  40. package/dist/secret-contract-api.js +1 -1
  41. package/dist/{send-EpqsWCGd.js → send-CRoD-UQw.js} +41 -24
  42. package/dist/send.runtime-C_y65BU0.js +2 -0
  43. package/dist/{setup-core-POfI_bgP.js → setup-core-BCGzyZhx.js} +4 -2
  44. package/dist/setup-plugin-api.js +1 -1
  45. package/dist/{setup-surface-BHsOhTJd.js → setup-surface-D4GE34e2.js} +5 -5
  46. package/dist/{shared-BxzdaqOc.js → shared-BtJS2bUx.js} +8 -7
  47. package/dist/{slash-dispatch.runtime-BOl9Cbn9.js → slash-dispatch.runtime-D1qKAWNN.js} +1 -1
  48. package/npm-shrinkwrap.json +4 -3
  49. package/openclaw.plugin.json +203 -2
  50. package/package.json +8 -5
  51. package/skills/slack/SKILL.md +78 -0
  52. package/dist/accounts.runtime-BJt1IxOS.js +0 -2
  53. package/dist/action-runtime.runtime-CJyuCUxj.js +0 -2
  54. package/dist/send.runtime-CbeGF-al.js +0 -2
@@ -1,13 +1,14 @@
1
1
  import { n as registerSlackHttpHandler, r as normalizeSlackWebhookPath } from "./registry-bbjH7IHX.js";
2
- import { a as resolveSlackAccount, d as resolveSlackBotToken, o as resolveSlackAccountAllowFrom, s as resolveSlackAccountDmPolicy, u as resolveSlackAppToken } from "./accounts-f6Xcv9Vi.js";
3
- import { F as isSlackExecApprovalAuthorizedSender, N as isSlackApprovalAuthorizedSender, S as isSlackAnyNativeApprovalClientEnabled, a as SLACK_TEXT_LIMIT, c as allowListMatches, d as normalizeSlackAllowOwnerEntry, f as normalizeSlackSlug, l as normalizeAllowList, p as resolveSlackAllowListMatch, u as normalizeAllowListLower, v as truncateSlackText } from "./reply-blocks-DSfujQlj.js";
4
- import { c as resolveSlackWebClientOptions } from "./client-DhaHONzg.js";
5
- import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-DrBq4Mld.js";
2
+ import { a as resolveSlackAccount, d as resolveSlackBotToken, o as resolveSlackAccountAllowFrom, s as resolveSlackAccountDmPolicy, u as resolveSlackAppToken } from "./accounts-BPCiCe8y.js";
3
+ import { F as isSlackExecApprovalAuthorizedSender, N as isSlackApprovalAuthorizedSender, S as isSlackAnyNativeApprovalClientEnabled, a as SLACK_TEXT_LIMIT, c as allowListMatches, d as normalizeSlackAllowOwnerEntry, f as normalizeSlackSlug, l as normalizeAllowList, p as resolveSlackAllowListMatch, u as normalizeAllowListLower, v as truncateSlackText } from "./reply-blocks-BT_sNsDQ.js";
4
+ import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-D-22gTp3.js";
5
+ import { c as resolveSlackWebClientOptions } from "./client-km2ba2l3.js";
6
6
  import { t as getOptionalSlackRuntime } from "./runtime-BOk7xkOl.js";
7
+ import { n as setSlackDefaultSendIdentity } from "./send-CRoD-UQw.js";
7
8
  import { t as formatSlackError } from "./errors-CZtmv-h0.js";
8
- import { t as resolveSlackChannelAllowlist } from "./resolve-channels-DNiE0Z0_.js";
9
- import { t as resolveSlackUserAllowlist } from "./resolve-users-D_TmaIfG.js";
10
- import { n as resolveSlackChannelConfig, r as resolveSlackChannelLabel, t as isSlackChannelAllowedByPolicy } from "./policy-Dfbxkneh.js";
9
+ import { t as resolveSlackChannelAllowlist } from "./resolve-channels-CtUOIX8D.js";
10
+ import { t as resolveSlackUserAllowlist } from "./resolve-users-DfFN2BcP.js";
11
+ import { n as resolveSlackChannelConfig, r as resolveSlackChannelLabel, t as isSlackChannelAllowedByPolicy } from "./policy-Bi92bs5t.js";
11
12
  import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
12
13
  import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, normalizeStringEntriesLower, normalizeUniqueTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
13
14
  import { normalizeAccountId, normalizeMainKey, resolveAgentRoute, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
@@ -2820,9 +2821,21 @@ function createSlackThreadTsResolver(params) {
2820
2821
  //#region extensions/slack/src/monitor/message-handler.ts
2821
2822
  let slackMessagePipelinePromise;
2822
2823
  function loadSlackMessagePipeline() {
2823
- slackMessagePipelinePromise ??= import("./pipeline.runtime-DQXhebqH.js");
2824
+ slackMessagePipelinePromise ??= import("./pipeline.runtime-5R4HgM_7.js");
2824
2825
  return slackMessagePipelinePromise;
2825
2826
  }
2827
+ function createSlackDispatchCompletion() {
2828
+ let resolve;
2829
+ let reject;
2830
+ return {
2831
+ promise: new Promise((nextResolve, nextReject) => {
2832
+ resolve = nextResolve;
2833
+ reject = nextReject;
2834
+ }),
2835
+ resolve,
2836
+ reject
2837
+ };
2838
+ }
2826
2839
  const APP_MENTION_RETRY_TTL_MS = 6e4;
2827
2840
  var SlackRetryableInboundError = class extends Error {
2828
2841
  constructor(message, options) {
@@ -2849,72 +2862,82 @@ function createSlackMessageHandler(params) {
2849
2862
  buildKey: (entry) => buildSlackDebounceKey(entry.message, ctx.accountId),
2850
2863
  shouldDebounce: (entry) => shouldDebounceSlackMessage(entry.message, ctx.cfg),
2851
2864
  onFlush: async (entries) => {
2852
- const last = entries.at(-1);
2853
- if (!last) return;
2854
- const flushedKey = buildSlackDebounceKey(last.message, ctx.accountId);
2855
- const topLevelConversationKey = buildTopLevelSlackConversationKey(last.message, ctx.accountId);
2856
- if (flushedKey && topLevelConversationKey) {
2857
- const pendingKeys = pendingTopLevelDebounceKeys.get(topLevelConversationKey);
2858
- if (pendingKeys) {
2859
- pendingKeys.delete(flushedKey);
2860
- if (pendingKeys.size === 0) pendingTopLevelDebounceKeys.delete(topLevelConversationKey);
2861
- }
2862
- }
2863
- const combinedText = entries.length === 1 ? last.message.text ?? "" : entries.map((entry) => entry.message.text ?? "").filter(Boolean).join("\n");
2864
- const combinedMentioned = entries.some((entry) => Boolean(entry.opts.wasMentioned));
2865
- const syntheticMessage = {
2866
- ...last.message,
2867
- text: combinedText
2868
- };
2869
- const seenMessageKey = buildSeenMessageKey(last.message.channel, last.message.ts);
2865
+ const completions = entries.map((entry) => entry.opts.dispatchCompletion).filter((completion) => completion !== void 0);
2870
2866
  try {
2871
- const { prepareSlackMessage, dispatchPreparedSlackMessage } = await loadSlackMessagePipeline();
2872
- const prepared = await prepareSlackMessage({
2873
- ctx,
2874
- account,
2875
- message: syntheticMessage,
2876
- opts: {
2877
- ...last.opts,
2878
- wasMentioned: combinedMentioned || last.opts.wasMentioned
2879
- }
2880
- });
2881
- if (!prepared) return;
2882
- if (seenMessageKey) {
2883
- pruneAppMentionRetryKeys(Date.now());
2884
- if (last.opts.source === "app_mention") rememberExpiringAppMentionKey(appMentionDispatchedKeys, seenMessageKey);
2885
- else if (last.opts.source === "message" && appMentionDispatchedKeys.has(seenMessageKey)) {
2886
- appMentionDispatchedKeys.delete(seenMessageKey);
2887
- appMentionRetryKeys.delete(seenMessageKey);
2888
- return;
2867
+ await (async () => {
2868
+ const last = entries.at(-1);
2869
+ if (!last) return;
2870
+ const flushedKey = buildSlackDebounceKey(last.message, ctx.accountId);
2871
+ const topLevelConversationKey = buildTopLevelSlackConversationKey(last.message, ctx.accountId);
2872
+ if (flushedKey && topLevelConversationKey) {
2873
+ const pendingKeys = pendingTopLevelDebounceKeys.get(topLevelConversationKey);
2874
+ if (pendingKeys) {
2875
+ pendingKeys.delete(flushedKey);
2876
+ if (pendingKeys.size === 0) pendingTopLevelDebounceKeys.delete(topLevelConversationKey);
2877
+ }
2889
2878
  }
2890
- appMentionRetryKeys.delete(seenMessageKey);
2891
- }
2892
- if (entries.length > 1) {
2893
- const ids = entries.map((entry) => entry.message.ts).filter(Boolean);
2894
- if (ids.length > 0) {
2895
- prepared.ctxPayload.MessageSids = ids;
2896
- prepared.ctxPayload.MessageSidFirst = ids[0];
2897
- prepared.ctxPayload.MessageSidLast = ids[ids.length - 1];
2879
+ const combinedText = entries.length === 1 ? last.message.text ?? "" : entries.map((entry) => entry.message.text ?? "").filter(Boolean).join("\n");
2880
+ const combinedMentioned = entries.some((entry) => Boolean(entry.opts.wasMentioned));
2881
+ const syntheticMessage = {
2882
+ ...last.message,
2883
+ text: combinedText
2884
+ };
2885
+ const seenMessageKey = buildSeenMessageKey(last.message.channel, last.message.ts);
2886
+ try {
2887
+ const { prepareSlackMessage, dispatchPreparedSlackMessage } = await loadSlackMessagePipeline();
2888
+ const { dispatchCompletion: _completion, awaitDispatch: _awaitDispatch, ...lastOpts } = last.opts;
2889
+ const prepared = await prepareSlackMessage({
2890
+ ctx,
2891
+ account,
2892
+ message: syntheticMessage,
2893
+ opts: {
2894
+ ...lastOpts,
2895
+ wasMentioned: combinedMentioned || last.opts.wasMentioned
2896
+ }
2897
+ });
2898
+ if (!prepared) return;
2899
+ if (seenMessageKey) {
2900
+ pruneAppMentionRetryKeys(Date.now());
2901
+ if (last.opts.source === "app_mention") rememberExpiringAppMentionKey(appMentionDispatchedKeys, seenMessageKey);
2902
+ else if (last.opts.source === "message" && appMentionDispatchedKeys.has(seenMessageKey)) {
2903
+ appMentionDispatchedKeys.delete(seenMessageKey);
2904
+ appMentionRetryKeys.delete(seenMessageKey);
2905
+ return;
2906
+ }
2907
+ appMentionRetryKeys.delete(seenMessageKey);
2908
+ }
2909
+ if (entries.length > 1) {
2910
+ const ids = entries.map((entry) => entry.message.ts).filter(Boolean);
2911
+ if (ids.length > 0) {
2912
+ prepared.ctxPayload.MessageSids = ids;
2913
+ prepared.ctxPayload.MessageSidFirst = ids[0];
2914
+ prepared.ctxPayload.MessageSidLast = ids[ids.length - 1];
2915
+ }
2916
+ }
2917
+ try {
2918
+ await dispatchPreparedSlackMessage(prepared);
2919
+ await recordSlackInboundMessageDeliveries({
2920
+ accountId: ctx.accountId,
2921
+ messages: entries.map((entry) => entry.message)
2922
+ });
2923
+ } catch (error) {
2924
+ if (!(error instanceof SlackRetryableInboundError)) await recordSlackInboundMessageDeliveries({
2925
+ accountId: ctx.accountId,
2926
+ messages: entries.map((entry) => entry.message)
2927
+ });
2928
+ throw error;
2929
+ }
2930
+ } catch (error) {
2931
+ if (error instanceof SlackRetryableInboundError) {
2932
+ if (seenMessageKey) appMentionDispatchedKeys.delete(seenMessageKey);
2933
+ ctx.releaseSeenMessage(last.message.channel, last.message.ts);
2934
+ }
2935
+ throw error;
2898
2936
  }
2899
- }
2900
- try {
2901
- await dispatchPreparedSlackMessage(prepared);
2902
- await recordSlackInboundMessageDeliveries({
2903
- accountId: ctx.accountId,
2904
- messages: entries.map((entry) => entry.message)
2905
- });
2906
- } catch (error) {
2907
- if (!(error instanceof SlackRetryableInboundError)) await recordSlackInboundMessageDeliveries({
2908
- accountId: ctx.accountId,
2909
- messages: entries.map((entry) => entry.message)
2910
- });
2911
- throw error;
2912
- }
2937
+ })();
2938
+ for (const completion of completions) completion.resolve();
2913
2939
  } catch (error) {
2914
- if (error instanceof SlackRetryableInboundError) {
2915
- if (seenMessageKey) appMentionDispatchedKeys.delete(seenMessageKey);
2916
- ctx.releaseSeenMessage(last.message.channel, last.message.ts);
2917
- }
2940
+ for (const completion of completions) completion.reject(error);
2918
2941
  throw error;
2919
2942
  }
2920
2943
  },
@@ -2986,10 +3009,18 @@ function createSlackMessageHandler(params) {
2986
3009
  pendingKeys.add(debounceKey);
2987
3010
  pendingTopLevelDebounceKeys.set(conversationKey, pendingKeys);
2988
3011
  }
3012
+ const dispatchCompletion = opts.awaitDispatch ? createSlackDispatchCompletion() : void 0;
2989
3013
  await debouncer.enqueue({
2990
3014
  message: resolvedMessage,
2991
- opts
3015
+ opts: {
3016
+ ...opts,
3017
+ ...dispatchCompletion ? { dispatchCompletion: {
3018
+ resolve: dispatchCompletion.resolve,
3019
+ reject: dispatchCompletion.reject
3020
+ } } : {}
3021
+ }
2992
3022
  });
3023
+ await dispatchCompletion?.promise;
2993
3024
  };
2994
3025
  }
2995
3026
  //#endregion
@@ -3106,6 +3137,13 @@ function installSlackNativeReconnectFailureObserver(receiver) {
3106
3137
  });
3107
3138
  });
3108
3139
  }
3140
+ function createSlackRelayReceiver() {
3141
+ return {
3142
+ init() {},
3143
+ start: () => Promise.resolve(void 0),
3144
+ stop: () => Promise.resolve(void 0)
3145
+ };
3146
+ }
3109
3147
  function resolveSlackBoltModule(value) {
3110
3148
  if (!value || typeof value !== "object") return null;
3111
3149
  const app = Reflect.get(value, "App");
@@ -3217,17 +3255,21 @@ function createSlackBoltApp(params) {
3217
3255
  };
3218
3256
  if (params.socketMode?.serverPingTimeout !== void 0) socketModeReceiverOptions.serverPingTimeout = params.socketMode.serverPingTimeout;
3219
3257
  if (params.socketMode?.pingPongLoggingEnabled !== void 0) socketModeReceiverOptions.pingPongLoggingEnabled = params.socketMode.pingPongLoggingEnabled;
3220
- const receiver = params.slackMode === "socket" ? new params.interop.SocketModeReceiver(socketModeReceiverOptions) : new params.interop.HTTPReceiver({
3258
+ let receiver;
3259
+ if (params.slackMode === "socket") {
3260
+ receiver = new params.interop.SocketModeReceiver(socketModeReceiverOptions);
3261
+ installSlackNativeReconnectFailureObserver(receiver);
3262
+ } else if (params.slackMode === "http") receiver = new params.interop.HTTPReceiver({
3221
3263
  signingSecret: params.signingSecret ?? "",
3222
3264
  endpoints: params.slackWebhookPath
3223
3265
  });
3224
- if (params.slackMode === "socket") installSlackNativeReconnectFailureObserver(receiver);
3266
+ else receiver = createSlackRelayReceiver();
3225
3267
  const app = new params.interop.App({
3226
3268
  token: params.botToken,
3227
- receiver,
3228
3269
  clientOptions: params.clientOptions,
3229
3270
  ignoreSelf: false,
3230
- tokenVerificationEnabled: false
3271
+ tokenVerificationEnabled: false,
3272
+ ...receiver ? { receiver } : {}
3231
3273
  });
3232
3274
  app.use(async (args) => {
3233
3275
  if (shouldSkipOpenClawSlackSelfEvent(args)) return;
@@ -3460,7 +3502,7 @@ function loadSlashCommandsRuntime() {
3460
3502
  return slashCommandsRuntimePromise;
3461
3503
  }
3462
3504
  function loadSlashDispatchRuntime() {
3463
- slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-BOl9Cbn9.js");
3505
+ slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-D1qKAWNN.js");
3464
3506
  return slashDispatchRuntimePromise;
3465
3507
  }
3466
3508
  function loadSlackPluginCommandsRuntime() {
@@ -4161,6 +4203,7 @@ async function registerSlackMonitorSlashCommands(params) {
4161
4203
  //#endregion
4162
4204
  //#region extensions/slack/src/monitor/provider.ts
4163
4205
  let slackBoltInterop;
4206
+ let slackRelaySourcePromise;
4164
4207
  async function getSlackBoltInterop() {
4165
4208
  if (!slackBoltInterop) {
4166
4209
  const slackBoltModule = await import("@slack/bolt");
@@ -4171,6 +4214,10 @@ async function getSlackBoltInterop() {
4171
4214
  }
4172
4215
  return slackBoltInterop;
4173
4216
  }
4217
+ function loadSlackRelaySource() {
4218
+ slackRelaySourcePromise ??= import("./relay-source-42fP7Ktr.js");
4219
+ return slackRelaySourcePromise;
4220
+ }
4174
4221
  const SLACK_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
4175
4222
  const SLACK_WEBHOOK_BODY_TIMEOUT_MS = 3e4;
4176
4223
  function resolveStableSlackUserIdEntry(raw) {
@@ -4208,6 +4255,21 @@ function parseApiAppIdFromAppToken(raw) {
4208
4255
  if (!token) return;
4209
4256
  return /^xapp-\d-([a-z0-9]+)-/i.exec(token)?.[1]?.toUpperCase();
4210
4257
  }
4258
+ function resolveSlackRelayConfig(params) {
4259
+ const relay = params.relay && typeof params.relay === "object" && !Array.isArray(params.relay) ? params.relay : {};
4260
+ const url = normalizeOptionalString(relay.url);
4261
+ const authToken = normalizeResolvedSecretInputString({
4262
+ value: relay.authToken,
4263
+ path: `channels.slack.accounts.${params.accountId}.relay.authToken`
4264
+ });
4265
+ const gatewayId = normalizeOptionalString(relay.gatewayId);
4266
+ if (!url || !authToken || !gatewayId) throw new Error(`Slack relay mode requires relay.url, relay.authToken, and relay.gatewayId for account "${params.accountId}".`);
4267
+ return {
4268
+ url,
4269
+ authToken,
4270
+ gatewayId
4271
+ };
4272
+ }
4211
4273
  async function monitorSlackProvider(opts = {}) {
4212
4274
  const cfg = opts.config ?? getRuntimeConfig$1();
4213
4275
  const runtime = opts.runtime ?? createNonExitingRuntime();
@@ -4236,8 +4298,12 @@ async function monitorSlackProvider(opts = {}) {
4236
4298
  });
4237
4299
  const botToken = resolveSlackBotToken(opts.botToken ?? account.botToken);
4238
4300
  const appToken = resolveSlackAppToken(opts.appToken ?? account.appToken);
4239
- if (!botToken || slackMode !== "http" && !appToken) {
4240
- const missing = slackMode === "http" ? `Slack bot token missing for account "${account.accountId}" (set channels.slack.accounts.${account.accountId}.botToken or SLACK_BOT_TOKEN for default).` : `Slack bot + app tokens missing for account "${account.accountId}" (set channels.slack.accounts.${account.accountId}.botToken/appToken or SLACK_BOT_TOKEN/SLACK_APP_TOKEN for default).`;
4301
+ const relayConfig = slackMode === "relay" ? resolveSlackRelayConfig({
4302
+ relay: account.config.relay,
4303
+ accountId: account.accountId
4304
+ }) : void 0;
4305
+ if (!botToken || slackMode === "socket" && !appToken) {
4306
+ const missing = slackMode === "http" ? `Slack bot token missing for account "${account.accountId}" (set channels.slack.accounts.${account.accountId}.botToken or SLACK_BOT_TOKEN for default).` : slackMode === "relay" ? `Slack bot token missing for account "${account.accountId}" (set channels.slack.accounts.${account.accountId}.botToken or SLACK_BOT_TOKEN for default).` : `Slack bot + app tokens missing for account "${account.accountId}" (set channels.slack.accounts.${account.accountId}.botToken/appToken or SLACK_BOT_TOKEN/SLACK_APP_TOKEN for default).`;
4241
4307
  throw new Error(missing);
4242
4308
  }
4243
4309
  if (slackMode === "http" && !signingSecret) throw new Error(`Slack signing secret missing for account "${account.accountId}" (set channels.slack.signingSecret or channels.slack.accounts.${account.accountId}.signingSecret).`);
@@ -4287,8 +4353,8 @@ async function monitorSlackProvider(opts = {}) {
4287
4353
  interop: await getSlackBoltInterop(),
4288
4354
  slackMode,
4289
4355
  botToken,
4290
- appToken: appToken ?? void 0,
4291
- signingSecret: signingSecret ?? void 0,
4356
+ appToken: slackMode === "socket" ? appToken ?? void 0 : void 0,
4357
+ signingSecret: slackMode === "http" ? signingSecret ?? void 0 : void 0,
4292
4358
  slackWebhookPath,
4293
4359
  clientOptions,
4294
4360
  ...slackCfg.socketMode ? { socketMode: slackCfg.socketMode } : {}
@@ -4317,7 +4383,7 @@ async function monitorSlackProvider(opts = {}) {
4317
4383
  let botId = "";
4318
4384
  let teamId = "";
4319
4385
  let apiAppId = "";
4320
- const expectedApiAppIdFromAppToken = parseApiAppIdFromAppToken(appToken);
4386
+ const expectedApiAppIdFromAppToken = slackMode === "socket" ? parseApiAppIdFromAppToken(appToken) : void 0;
4321
4387
  let authTestFailed = false;
4322
4388
  let authTestError;
4323
4389
  try {
@@ -4570,6 +4636,16 @@ async function monitorSlackProvider(opts = {}) {
4570
4636
  }
4571
4637
  continue;
4572
4638
  }
4639
+ } else if (slackMode === "relay" && relayConfig) {
4640
+ runtime.log?.(`slack relay mode connecting to ${relayConfig.url} gateway_id:${relayConfig.gatewayId}`);
4641
+ await (await loadSlackRelaySource()).monitorSlackRelaySource({
4642
+ config: relayConfig,
4643
+ handleSlackMessage,
4644
+ runtime,
4645
+ abortSignal: opts.abortSignal,
4646
+ setStatus: opts.setStatus,
4647
+ setIdentity: (identity) => setSlackDefaultSendIdentity(account.accountId, identity)
4648
+ });
4573
4649
  } else {
4574
4650
  runtime.log?.(`slack http mode listening at ${slackWebhookPath}`);
4575
4651
  if (!opts.abortSignal?.aborted) await new Promise((resolve) => {
@@ -4577,6 +4653,7 @@ async function monitorSlackProvider(opts = {}) {
4577
4653
  });
4578
4654
  }
4579
4655
  } finally {
4656
+ if (slackMode === "relay") setSlackDefaultSendIdentity(account.accountId, void 0);
4580
4657
  opts.abortSignal?.removeEventListener("abort", stopOnAbort);
4581
4658
  unregisterHttpHandler?.();
4582
4659
  await gracefulStop();
@@ -4584,4 +4661,4 @@ async function monitorSlackProvider(opts = {}) {
4584
4661
  }
4585
4662
  const resolveSlackRuntimeGroupPolicy = resolveOpenProviderRuntimeGroupPolicy$1;
4586
4663
  //#endregion
4587
- export { resolveStorePath$1 as _, escapeSlackMrkdwn as a, stripSlackMentionsForCommandDetection as b, authorizeSlackBotRoomMessage as c, buildSlackAssistantThreadMetadata as d, parseSlackAssistantThreadMetadata as f, resolveChannelContextVisibilityMode as g, readSessionUpdatedAt as h, authorizeSlackDirectMessage as i, resolveSlackCommandIngress as l, resolveSlackChatType as m, resolveSlackRuntimeGroupPolicy as n, recordInboundSession as o, normalizeSlackChannelType as p, resolveSlackRoomContextHints as r, resolveConversationLabel$1 as s, monitorSlackProvider as t, resolveSlackEffectiveAllowFrom as u, updateLastRoute as v, buildSlackSlashCommandMatcher as y };
4664
+ export { stripSlackMentionsForCommandDetection as S, readSessionUpdatedAt as _, SLACK_SOCKET_RECONNECT_POLICY as a, updateLastRoute as b, recordInboundSession as c, resolveSlackCommandIngress as d, resolveSlackEffectiveAllowFrom as f, resolveSlackChatType as g, normalizeSlackChannelType as h, authorizeSlackDirectMessage as i, resolveConversationLabel$1 as l, parseSlackAssistantThreadMetadata as m, resolveSlackRuntimeGroupPolicy as n, formatUnknownError as o, buildSlackAssistantThreadMetadata as p, resolveSlackRoomContextHints as r, escapeSlackMrkdwn as s, monitorSlackProvider as t, authorizeSlackBotRoomMessage as u, resolveChannelContextVisibilityMode as v, buildSlackSlashCommandMatcher as x, resolveStorePath$1 as y };
@@ -0,0 +1,278 @@
1
+ import { a as SLACK_SOCKET_RECONNECT_POLICY, o as formatUnknownError } from "./provider-7YKru1TZ.js";
2
+ import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
3
+ import { computeBackoff, sleepWithAbort, warn } from "openclaw/plugin-sdk/runtime-env";
4
+ import { Buffer } from "node:buffer";
5
+ import { isIP } from "node:net";
6
+ import WebSocket from "ws";
7
+ //#region extensions/slack/src/monitor/relay-source.ts
8
+ const SLACK_RELAY_ROUTE_KINDS = new Set([
9
+ "user_group",
10
+ "thread_affinity",
11
+ "channel_default"
12
+ ]);
13
+ const SLACK_RELAY_MAX_PAYLOAD_BYTES = 1024 * 1024;
14
+ async function monitorSlackRelaySource(params) {
15
+ let reconnectAttempts = 0;
16
+ while (!params.abortSignal?.aborted) {
17
+ let connection;
18
+ try {
19
+ connection = await openRelayWebSocket(params.config, params.abortSignal);
20
+ reconnectAttempts = 0;
21
+ params.setStatus?.({
22
+ connected: true,
23
+ lastConnectedAt: Date.now(),
24
+ healthState: "healthy",
25
+ lastError: null
26
+ });
27
+ params.runtime.log?.(`slack relay mode connected gateway_id:${params.config.gatewayId}`);
28
+ await runRelayWebSocket({
29
+ connection,
30
+ handleSlackMessage: params.handleSlackMessage,
31
+ runtime: params.runtime,
32
+ abortSignal: params.abortSignal,
33
+ setStatus: params.setStatus,
34
+ setIdentity: params.setIdentity
35
+ });
36
+ } catch (err) {
37
+ if (params.abortSignal?.aborted) break;
38
+ reconnectAttempts += 1;
39
+ const delayMs = computeBackoff(SLACK_SOCKET_RECONNECT_POLICY, reconnectAttempts);
40
+ params.setStatus?.({
41
+ connected: false,
42
+ healthState: "disconnected",
43
+ lastDisconnect: {
44
+ at: Date.now(),
45
+ error: formatUnknownError(err)
46
+ },
47
+ lastError: formatUnknownError(err)
48
+ });
49
+ params.runtime.log?.(warn(`slack relay mode disconnected; reconnecting in ${Math.round(delayMs / 1e3)}s (attempt ${reconnectAttempts}) reason="${formatUnknownError(err)}"`));
50
+ await sleepWithAbort(delayMs, params.abortSignal);
51
+ } finally {
52
+ closeRelayWebSocket(connection?.ws);
53
+ params.setIdentity?.(void 0);
54
+ }
55
+ }
56
+ }
57
+ function openRelayWebSocket(config, abortSignal) {
58
+ if (abortSignal?.aborted) return Promise.reject(/* @__PURE__ */ new Error("Slack relay websocket aborted before connect"));
59
+ return new Promise((resolve, reject) => {
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
+ const cleanup = () => {
66
+ ws.off("open", onOpen);
67
+ ws.off("error", onError);
68
+ ws.off("close", onClose);
69
+ abortSignal?.removeEventListener("abort", onAbort);
70
+ };
71
+ const onOpen = () => {
72
+ cleanup();
73
+ resolve({
74
+ ws,
75
+ bufferedMessages,
76
+ detachBuffer
77
+ });
78
+ };
79
+ const onError = (error) => {
80
+ cleanup();
81
+ detachBuffer();
82
+ reject(error);
83
+ };
84
+ const onClose = (code, reason) => {
85
+ cleanup();
86
+ detachBuffer();
87
+ reject(new Error(formatRelayClose(code, reason)));
88
+ };
89
+ const onAbort = () => {
90
+ cleanup();
91
+ detachBuffer();
92
+ closeRelayWebSocket(ws);
93
+ reject(/* @__PURE__ */ new Error("Slack relay websocket aborted during connect"));
94
+ };
95
+ ws.once("open", onOpen);
96
+ ws.once("error", onError);
97
+ ws.once("close", onClose);
98
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
99
+ });
100
+ }
101
+ function runRelayWebSocket(params) {
102
+ const { ws } = params.connection;
103
+ const relayState = {};
104
+ let pending = Promise.resolve();
105
+ return new Promise((resolve, reject) => {
106
+ const cleanup = () => {
107
+ ws.off("message", onMessage);
108
+ ws.off("error", onError);
109
+ ws.off("close", onClose);
110
+ params.abortSignal?.removeEventListener("abort", onAbort);
111
+ };
112
+ const settleResolve = () => {
113
+ cleanup();
114
+ pending.then(resolve, reject);
115
+ };
116
+ const settleReject = (error) => {
117
+ cleanup();
118
+ pending.then(() => reject(error), reject);
119
+ };
120
+ const onMessage = (data) => {
121
+ pending = pending.then(() => handleRelayFrame({
122
+ ws,
123
+ data,
124
+ handleSlackMessage: params.handleSlackMessage,
125
+ relayState,
126
+ setStatus: params.setStatus,
127
+ setIdentity: params.setIdentity
128
+ })).catch((err) => {
129
+ params.runtime.error?.(`slack relay frame failed: ${formatUnknownError(err)}`);
130
+ });
131
+ };
132
+ const onError = (error) => {
133
+ cleanup();
134
+ reject(error);
135
+ };
136
+ const onClose = (code, reason) => {
137
+ const closeReason = formatRelayClose(code, reason);
138
+ params.setStatus?.({
139
+ connected: false,
140
+ healthState: "disconnected",
141
+ lastDisconnect: {
142
+ at: Date.now(),
143
+ error: closeReason
144
+ }
145
+ });
146
+ settleReject(new Error(closeReason));
147
+ };
148
+ const onAbort = () => {
149
+ closeRelayWebSocket(ws);
150
+ settleResolve();
151
+ };
152
+ params.connection.detachBuffer();
153
+ ws.on("message", onMessage);
154
+ ws.once("error", onError);
155
+ ws.once("close", onClose);
156
+ params.abortSignal?.addEventListener("abort", onAbort, { once: true });
157
+ for (const message of params.connection.bufferedMessages) onMessage(message);
158
+ });
159
+ }
160
+ async function handleRelayFrame(params) {
161
+ const frame = parseRelayFrame(params.data);
162
+ const hello = extractRelayHello(frame);
163
+ if (hello) {
164
+ params.relayState.identity = hello.identity;
165
+ params.setIdentity?.(hello.identity);
166
+ params.setStatus?.({ relayIdentity: hello.identity ?? null });
167
+ return;
168
+ }
169
+ const event = extractRelaySlackMessageEvent(frame);
170
+ if (!event) return;
171
+ const now = Date.now();
172
+ params.setStatus?.({
173
+ lastEventAt: now,
174
+ lastInboundAt: now
175
+ });
176
+ params.setStatus?.({ relayRoute: event.route });
177
+ await params.handleSlackMessage(event.message, {
178
+ source: "message",
179
+ wasMentioned: true,
180
+ awaitDispatch: true,
181
+ ...params.relayState.identity ? { relayIdentity: params.relayState.identity } : {}
182
+ });
183
+ sendRelayAck(params.ws, event.deliveryId);
184
+ }
185
+ function buildRelayWebSocketOptions(authToken) {
186
+ return {
187
+ headers: { Authorization: `Bearer ${authToken}` },
188
+ handshakeTimeout: 3e4,
189
+ maxPayload: SLACK_RELAY_MAX_PAYLOAD_BYTES,
190
+ perMessageDeflate: false
191
+ };
192
+ }
193
+ function buildRelayWebSocketUrl(config) {
194
+ const url = new URL(config.url);
195
+ if (url.protocol === "http:") url.protocol = "ws:";
196
+ else if (url.protocol === "https:") url.protocol = "wss:";
197
+ if (url.protocol !== "ws:" && url.protocol !== "wss:") throw new Error(`Slack relay URL must use http(s) or ws(s): ${config.url}`);
198
+ if (url.protocol === "ws:" && !isLocalRelayHost(url.hostname)) throw new Error(`Slack relay URL uses plaintext ws:// for non-local host "${url.host}". Use wss:// for remote relay URLs; ws:// is only allowed for localhost, 127.0.0.1, or [::1].`);
199
+ if (!url.pathname || url.pathname === "/") throw new Error(`Slack relay URL must include its websocket path: ${config.url}`);
200
+ url.searchParams.set("gateway_id", config.gatewayId);
201
+ return url.toString();
202
+ }
203
+ function isLocalRelayHost(hostname) {
204
+ const normalized = hostname.trim().toLowerCase();
205
+ const host = normalized.startsWith("[") && normalized.endsWith("]") ? normalized.slice(1, -1) : normalized;
206
+ if (host === "localhost" || host === "::1") return true;
207
+ return isIP(host) === 4 && host.startsWith("127.");
208
+ }
209
+ function parseRelayFrame(data) {
210
+ const text = rawDataToString(data);
211
+ return JSON.parse(text);
212
+ }
213
+ function rawDataToString(data) {
214
+ if (typeof data === "string") return data;
215
+ if (Buffer.isBuffer(data)) return data.toString("utf8");
216
+ if (Array.isArray(data)) return Buffer.concat(data).toString("utf8");
217
+ return Buffer.from(data).toString("utf8");
218
+ }
219
+ function extractRelaySlackMessageEvent(frame) {
220
+ const record = asRecord(frame);
221
+ if (!record || record.type !== "slack_event") return;
222
+ const deliveryId = stringValue(record.delivery_id);
223
+ const routeRecord = asRecord(record.route);
224
+ const routeKind = stringValue(routeRecord?.kind);
225
+ const routeKey = stringValue(routeRecord?.key);
226
+ const event = asRecord(asRecord(record.payload)?.event);
227
+ if (event?.type !== "message" || typeof event.channel !== "string") return;
228
+ if (!deliveryId || !routeKind || !SLACK_RELAY_ROUTE_KINDS.has(routeKind) || !routeKey) return;
229
+ return {
230
+ deliveryId,
231
+ message: event,
232
+ route: {
233
+ kind: routeKind,
234
+ key: routeKey
235
+ }
236
+ };
237
+ }
238
+ function extractRelayHello(frame) {
239
+ const record = asRecord(frame);
240
+ if (!record || record.type !== "hello") return;
241
+ return { identity: extractRelayIdentity(record) };
242
+ }
243
+ function extractRelayIdentity(record) {
244
+ const identityRecord = asRecord(record.slack_identity) ?? asRecord(record.slackIdentity);
245
+ if (!identityRecord) return;
246
+ const username = normalizeOptionalString(identityRecord.username);
247
+ const iconUrl = normalizeOptionalString(identityRecord.icon_url) ?? normalizeOptionalString(identityRecord.iconUrl);
248
+ const iconEmoji = normalizeOptionalString(identityRecord.icon_emoji) ?? normalizeOptionalString(identityRecord.iconEmoji);
249
+ if (!username && !iconUrl && !iconEmoji) return;
250
+ return {
251
+ ...username ? { username } : {},
252
+ ...iconUrl ? { iconUrl } : {},
253
+ ...iconEmoji ? { iconEmoji } : {}
254
+ };
255
+ }
256
+ function sendRelayAck(ws, deliveryId) {
257
+ if (ws.readyState !== WebSocket.OPEN) return;
258
+ ws.send(JSON.stringify({
259
+ type: "ack",
260
+ delivery_id: deliveryId
261
+ }));
262
+ }
263
+ function closeRelayWebSocket(ws) {
264
+ if (!ws || ws.readyState === WebSocket.CLOSED || ws.readyState === WebSocket.CLOSING) return;
265
+ ws.close();
266
+ }
267
+ function formatRelayClose(code, reason) {
268
+ const text = reason.toString("utf8");
269
+ return text ? `Slack relay websocket closed (${code} ${text})` : `Slack relay websocket closed (${code})`;
270
+ }
271
+ function asRecord(value) {
272
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
273
+ }
274
+ function stringValue(value) {
275
+ return typeof value === "string" ? value : void 0;
276
+ }
277
+ //#endregion
278
+ export { SLACK_RELAY_MAX_PAYLOAD_BYTES, buildRelayWebSocketOptions, buildRelayWebSocketUrl, monitorSlackRelaySource };
@@ -1,5 +1,6 @@
1
- import { a as SLACK_TEXT_LIMIT, t as resolveSlackReplyBlocks } from "./reply-blocks-DSfujQlj.js";
2
- import { o as markdownToSlackMrkdwnChunks, t as sendMessageSlack } from "./send-EpqsWCGd.js";
1
+ import { a as SLACK_TEXT_LIMIT, t as resolveSlackReplyBlocks } from "./reply-blocks-BT_sNsDQ.js";
2
+ import { s as markdownToSlackMrkdwnChunks, t as sendMessageSlack } from "./send-CRoD-UQw.js";
3
+ import "./provider-7YKru1TZ.js";
3
4
  import { createReplyReferencePlanner } from "openclaw/plugin-sdk/reply-reference";
4
5
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
6
  import { SILENT_REPLY_TOKEN, chunkMarkdownTextWithMode, isSilentReplyText } from "openclaw/plugin-sdk/reply-chunking";
@@ -1,7 +1,7 @@
1
- import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, n as listSlackAccountIds, o as resolveSlackAccountAllowFrom, r as mergeSlackAccountConfig } from "./accounts-f6Xcv9Vi.js";
1
+ import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, n as listSlackAccountIds, o as resolveSlackAccountAllowFrom, r as mergeSlackAccountConfig } from "./accounts-BPCiCe8y.js";
2
2
  import { r as parseSlackTarget } from "./target-parsing-3jYH8LfB.js";
3
3
  import "./targets-Dll4NJgs.js";
4
- import { h as isSlackPluginAccountConfigured } from "./client-DhaHONzg.js";
4
+ import { t as isSlackPluginAccountConfigured } from "./account-configured-DzC407uv.js";
5
5
  import { normalizeAccountId } from "openclaw/plugin-sdk/account-resolution";
6
6
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringifiedOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
7
  import { normalizeMessageChannel } from "openclaw/plugin-sdk/routing";
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { r as createSlackWebClient } from "./client-DhaHONzg.js";
2
+ import { r as createSlackWebClient } from "./client-km2ba2l3.js";
3
3
  import { n as resolveSlackAllowlistEntries, t as collectSlackCursorItems } from "./resolve-allowlist-common-CP8SH0Zf.js";
4
4
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
5
5
  //#region extensions/slack/src/resolve-channels.ts