@openclaw/slack 2026.6.6-beta.2 → 2026.6.8-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.js CHANGED
@@ -3,16 +3,16 @@ import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, l as resol
3
3
  import { t as inspectSlackAccount } from "./account-inspect-CdGk6R7l.js";
4
4
  import { i as resolveSlackChannelId, n as normalizeSlackMessagingTarget, r as parseSlackTarget, t as looksLikeSlackTargetId } from "./target-parsing-C7eeWg7M.js";
5
5
  import "./targets-nUqxHGgg.js";
6
- import { a as resolveSlackAutoThreadId, i as resolveSlackChannelType, n as buildSlackThreadingToolContext, r as resetSlackChannelTypeCacheForTest, t as slackPlugin } from "./channel-i43aT1rv.js";
6
+ import { a as resolveSlackAutoThreadId, i as resolveSlackChannelType, n as buildSlackThreadingToolContext, r as resetSlackChannelTypeCacheForTest, t as slackPlugin } from "./channel-CdZdGdU5.js";
7
7
  import { _ as buildSlackInteractiveBlocks, a as parseSlackBlocksInput, c as resolveSlackGroupRequireMention, d as normalizeAllowList, f as normalizeAllowListLower, g as resolveSlackUserAllowed, h as resolveSlackAllowListMatch, i as SLACK_MAX_BLOCKS, l as resolveSlackGroupToolPolicy, m as normalizeSlackSlug, o as validateSlackBlocksArray, p as normalizeSlackAllowOwnerEntry, u as allowListMatches, v as buildSlackPresentationBlocks } from "./thread-ts-Cffag8e2.js";
8
8
  import { a as getSlackWriteClient, c as resolveSlackWebClientOptions, i as createSlackWriteClient, l as resolveSlackWriteClientOptions, n as createSlackTokenCacheKey, o as SLACK_DEFAULT_RETRY_OPTIONS, r as createSlackWebClient, s as SLACK_WRITE_RETRY_OPTIONS, t as clearSlackWriteClientCacheForTest } from "./client-Cn2WwpcA.js";
9
9
  import { n as extractSlackToolSend, r as listSlackMessageActions } from "./message-tool-api-B9M0zzlQ.js";
10
10
  import { n as isSlackInteractiveRepliesEnabled, r as parseSlackOptionsLine, t as compileSlackInteractiveReplies } from "./interactive-replies-DrBq4Mld.js";
11
- import { t as slackSetupPlugin } from "./channel.setup-DrCTObMp.js";
11
+ import { t as slackSetupPlugin } from "./channel.setup-W5YnieWd.js";
12
12
  import { a as recordSlackThreadParticipation, n as clearSlackThreadParticipationCache, r as hasSlackThreadParticipation } from "./send-rekB-Xjp.js";
13
13
  import { a as listSlackEmojis, c as pinSlackMessage, d as removeOwnSlackReactions, f as removeSlackReaction, i as getSlackMemberInfo, l as reactSlackMessage, m as unpinSlackMessage, n as downloadSlackFile, o as listSlackPins, p as sendSlackMessage, r as editSlackMessage, s as listSlackReactions, t as deleteSlackMessage, u as readSlackMessages } from "./actions-YkhEE5f1.js";
14
14
  import { n as listSlackDirectoryGroupsFromConfig, r as listSlackDirectoryPeersFromConfig } from "./directory-config-8UPAEyNg.js";
15
15
  import { t as probeSlack } from "./probe-BTKzLT2u.js";
16
16
  import { t as collectSlackSecurityAuditFindings } from "./security-audit-CikQhBUY.js";
17
- import { n as resolveSlackRuntimeGroupPolicy } from "./provider-LH_j-bLA.js";
17
+ import { n as resolveSlackRuntimeGroupPolicy } from "./provider-ChhYGpXx.js";
18
18
  export { SLACK_DEFAULT_RETRY_OPTIONS, SLACK_MAX_BLOCKS, SLACK_WRITE_RETRY_OPTIONS, resetSlackChannelTypeCacheForTest as __resetSlackChannelTypeCacheForTest, resetSlackChannelTypeCacheForTest, allowListMatches, buildSlackInteractiveBlocks, buildSlackPresentationBlocks, buildSlackThreadingToolContext, clearSlackThreadParticipationCache, clearSlackWriteClientCacheForTest, collectSlackSecurityAuditFindings, compileSlackInteractiveReplies, createSlackTokenCacheKey, createSlackWebClient, createSlackWriteClient, deleteSlackMessage, downloadSlackFile, editSlackMessage, extractSlackToolSend, getSlackMemberInfo, getSlackWriteClient, handleSlackHttpRequest, hasSlackThreadParticipation, inspectSlackAccount, isSlackInteractiveRepliesEnabled, listEnabledSlackAccounts, listSlackAccountIds, listSlackDirectoryGroupsFromConfig, listSlackDirectoryPeersFromConfig, listSlackEmojis, listSlackMessageActions, listSlackPins, listSlackReactions, looksLikeSlackTargetId, mergeSlackAccountConfig, normalizeAllowList, normalizeAllowListLower, normalizeSlackAllowOwnerEntry, normalizeSlackMessagingTarget, normalizeSlackSlug, normalizeSlackWebhookPath, parseSlackBlocksInput, parseSlackOptionsLine, parseSlackTarget, pinSlackMessage, probeSlack, reactSlackMessage, readSlackMessages, recordSlackThreadParticipation, registerSlackHttpHandler, removeOwnSlackReactions, removeSlackReaction, resolveDefaultSlackAccountId, resolveSlackAccount, resolveSlackAllowListMatch, resolveSlackAutoThreadId, resolveSlackChannelId, resolveSlackChannelType, resolveSlackGroupRequireMention, resolveSlackGroupToolPolicy, resolveSlackReplyToMode, resolveSlackRuntimeGroupPolicy, resolveSlackUserAllowed, resolveSlackWebClientOptions, resolveSlackWriteClientOptions, sendSlackMessage, slackPlugin, slackSetupPlugin, unpinSlackMessage, validateSlackBlocksArray };
@@ -6,7 +6,7 @@ import { d as PAIRING_APPROVED_MESSAGE, h as isSlackPluginAccountConfigured, m a
6
6
  import { n as extractSlackToolSend, t as describeSlackMessageTool } from "./message-tool-api-B9M0zzlQ.js";
7
7
  import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-DrBq4Mld.js";
8
8
  import { t as getOptionalSlackRuntime } from "./runtime-BOk7xkOl.js";
9
- import { n as slackConfigAdapter, r as slackSecurityAdapter, t as createSlackPluginBase } from "./shared-Bviszgb6.js";
9
+ import { n as slackConfigAdapter, r as slackSecurityAdapter, t as createSlackPluginBase } from "./shared-Bkkmro6q.js";
10
10
  import { i as SLACK_CHANNEL, n as createSlackSetupWizardProxy, r as slackSetupAdapter } from "./setup-core-POfI_bgP.js";
11
11
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
12
12
  import { adaptScopedAccountAccessor } from "openclaw/plugin-sdk/channel-config-helpers";
@@ -476,7 +476,7 @@ const EXTENSION_SHARED_MODULE_ID = "openclaw/plugin-sdk/extension-shared";
476
476
  const TARGET_RESOLVER_RUNTIME_MODULE_ID = "openclaw/plugin-sdk/target-resolver-runtime";
477
477
  const loadExtensionSharedSdk = createLazyRuntimeModule(() => import(EXTENSION_SHARED_MODULE_ID));
478
478
  const loadTargetResolverRuntimeSdk = createLazyRuntimeModule(() => import(TARGET_RESOLVER_RUNTIME_MODULE_ID));
479
- const loadSlackSetupSurfaceModule = createLazyRuntimeModule(() => import("./setup-surface-BB_ivsoR.js"));
479
+ const loadSlackSetupSurfaceModule = createLazyRuntimeModule(() => import("./setup-surface-NNMjGpXc.js"));
480
480
  const loadSlackScopesModule = createLazyRuntimeModule(() => import("./scopes-CErQL0td.js"));
481
481
  const loadSlackOutboundAdapterModule = createLazyRuntimeModule(() => import("./outbound-adapter-DvXnHfDP.js"));
482
482
  async function resolveSlackHandleAction() {
@@ -514,7 +514,7 @@ async function loadSlackProbeModule() {
514
514
  return await slackProbeModulePromise;
515
515
  }
516
516
  async function loadSlackMonitorModule() {
517
- slackMonitorModulePromise ??= import("./monitor-Szm2LjsM.js").then((n) => n.t);
517
+ slackMonitorModulePromise ??= import("./monitor-DwDmBYfl.js").then((n) => n.t);
518
518
  return await slackMonitorModulePromise;
519
519
  }
520
520
  async function loadSlackDirectoryLiveModule() {
@@ -1,2 +1,2 @@
1
- import { t as slackPlugin } from "./channel-i43aT1rv.js";
1
+ import { t as slackPlugin } from "./channel-CdZdGdU5.js";
2
2
  export { slackPlugin };
@@ -4,7 +4,7 @@ import { t as SlackChannelConfigSchema } from "./config-schema-BjAjs8_6.js";
4
4
  import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
5
5
  import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
6
6
  //#region extensions/slack/src/channel.setup.ts
7
- const slackSetupWizard = createSlackSetupWizardProxy(async () => ({ slackSetupWizard: (await import("./setup-surface-BB_ivsoR.js")).slackSetupWizard }));
7
+ const slackSetupWizard = createSlackSetupWizardProxy(async () => ({ slackSetupWizard: (await import("./setup-surface-NNMjGpXc.js")).slackSetupWizard }));
8
8
  const slackSetupConfigAdapter = createScopedChannelConfigAdapter({
9
9
  sectionKey: SLACK_CHANNEL,
10
10
  listAccountIds: listSlackAccountIds,
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { t as monitorSlackProvider, y as buildSlackSlashCommandMatcher } from "./provider-LH_j-bLA.js";
2
+ import { t as monitorSlackProvider, y as buildSlackSlashCommandMatcher } from "./provider-ChhYGpXx.js";
3
3
  import { t as isSlackChannelAllowedByPolicy } from "./policy-DT3mRh58.js";
4
- import { o as resolveSlackThreadTs } from "./replies-FEOdgSlE.js";
4
+ import { o as resolveSlackThreadTs } from "./replies-Bt4nWmSd.js";
5
5
  //#region extensions/slack/src/monitor.ts
6
6
  var monitor_exports = /* @__PURE__ */ __exportAll({
7
7
  buildSlackSlashCommandMatcher: () => buildSlackSlashCommandMatcher,
@@ -7,9 +7,9 @@ import { n as resolveSlackNativeStreaming, r as resolveSlackStreamingMode, t as
7
7
  import { a as recordSlackThreadParticipation, i as hasSlackThreadParticipationWithPersistence, s as normalizeSlackOutboundText, t as sendMessageSlack } from "./send-rekB-Xjp.js";
8
8
  import { _ as resolveSlackThreadStarter, b as buildSlackEditTextPayload, f as removeSlackReaction, g as resolveSlackThreadHistory, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage, y as formatSlackFileReference } from "./actions-YkhEE5f1.js";
9
9
  import { t as formatSlackError } from "./errors-CZtmv-h0.js";
10
- import { _ as resolveStorePath, a as escapeSlackMrkdwn, b as stripSlackMentionsForCommandDetection, c as authorizeSlackBotRoomMessage, d as buildSlackAssistantThreadMetadata, f as parseSlackAssistantThreadMetadata, g as resolveChannelContextVisibilityMode, h as readSessionUpdatedAt, i as authorizeSlackDirectMessage, l as resolveSlackCommandIngress, m as resolveSlackChatType, o as recordInboundSession, p as normalizeSlackChannelType, r as resolveSlackRoomContextHints, s as resolveConversationLabel$1, u as resolveSlackEffectiveAllowFrom, v as updateLastRoute } from "./provider-LH_j-bLA.js";
10
+ import { _ as resolveStorePath, a as escapeSlackMrkdwn, b as stripSlackMentionsForCommandDetection, c as authorizeSlackBotRoomMessage, d as buildSlackAssistantThreadMetadata, f as parseSlackAssistantThreadMetadata, g as resolveChannelContextVisibilityMode, h as readSessionUpdatedAt, i as authorizeSlackDirectMessage, l as resolveSlackCommandIngress, m as resolveSlackChatType, o as recordInboundSession, p as normalizeSlackChannelType, r as resolveSlackRoomContextHints, s as resolveConversationLabel$1, u as resolveSlackEffectiveAllowFrom, v as updateLastRoute } from "./provider-ChhYGpXx.js";
11
11
  import { n as resolveSlackChannelConfig } from "./policy-DT3mRh58.js";
12
- import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, t as createSlackReplyDeliveryPlan } from "./replies-FEOdgSlE.js";
12
+ import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, s as emitSlackMessageSentHooks, t as createSlackReplyDeliveryPlan } from "./replies-Bt4nWmSd.js";
13
13
  import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
14
14
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
15
15
  import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelMessageReplyPipeline, createChannelProgressDraftGate, createDraftStreamLoop, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelProgressDraftMaxLines, resolveChannelProgressDraftRender, resolveChannelStreamingBlockEnabled, resolveChannelStreamingNativeTransport, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
@@ -415,36 +415,41 @@ async function appendSlackStream(params) {
415
415
  * text so the caller can deliver it through the normal Slack reply path.
416
416
  *
417
417
  * All other errors propagate unchanged.
418
+ *
419
+ * On success, returns the finalized message's Slack `ts` (when reported) so the
420
+ * caller can emit the `message_sent` hook with a populated `messageId`.
418
421
  */
419
422
  async function stopSlackStream(params) {
420
423
  const { session, text, chunks, metadata } = params;
421
424
  if (session.stopped) {
422
425
  logVerbose("slack-stream: stream already stopped, ignoring duplicate stop");
423
- return;
426
+ return {};
424
427
  }
425
428
  session.stopped = true;
426
429
  if (text) session.pendingText += text;
427
430
  logVerbose(`slack-stream: stopping stream in ${session.channel} thread=${session.threadTs}${text ? ` (final text: ${text.length} chars)` : ""}`);
428
431
  try {
429
- await session.streamer.stop(text || chunks?.length || metadata ? {
432
+ const stopResponse = await session.streamer.stop(text || chunks?.length || metadata ? {
430
433
  ...text ? { markdown_text: text } : {},
431
434
  ...chunks?.length ? { chunks } : {},
432
435
  ...metadata ? { metadata } : {}
433
436
  } : void 0);
434
437
  session.delivered = true;
435
438
  session.pendingText = "";
439
+ logVerbose("slack-stream: stream stopped");
440
+ const messageId = stopResponse?.ts ?? stopResponse?.message?.ts;
441
+ return messageId ? { messageId } : {};
436
442
  } catch (err) {
437
443
  if (isBenignSlackFinalizeError(err)) {
438
444
  const code = extractSlackErrorCode(err) ?? "unknown";
439
445
  if (session.pendingText) throw new SlackStreamNotDeliveredError(session.pendingText, code);
440
446
  if (session.delivered) {
441
447
  logVerbose(`slack-stream: finalize rejected by Slack (${code}); prior appends delivered, treating stream as stopped`);
442
- return;
448
+ return {};
443
449
  }
444
450
  }
445
451
  throw err;
446
452
  }
447
- logVerbose("slack-stream: stream stopped");
448
453
  }
449
454
  /**
450
455
  * Slack API error codes that indicate `chat.stopStream` (or the
@@ -473,10 +478,10 @@ function extractSlackErrorCode(err) {
473
478
  return (typeof record.message === "string" ? record.message : "").match(/An API error occurred:\s*([a-z_][a-z0-9_]*)/i)?.[1];
474
479
  }
475
480
  function markSlackStreamFallbackDelivered(session) {
476
- const hadNativeDelivery = session.delivered;
481
+ const nativeStreamWasStarted = session.delivered;
477
482
  session.pendingText = "";
478
- session.delivered = true;
479
- if (!hadNativeDelivery) session.stopped = true;
483
+ session.streamer.buffer = "";
484
+ session.stopped = !nativeStreamWasStarted;
480
485
  }
481
486
  //#endregion
482
487
  //#region extensions/slack/src/threading.ts
@@ -848,6 +853,16 @@ async function dispatchPreparedSlackMessage(prepared) {
848
853
  ctx: prepared.ctxPayload
849
854
  });
850
855
  const sourceRepliesAreToolOnly = sourceReplyDeliveryMode === "message_tool_only";
856
+ const messageSentHookTarget = prepared.ctxPayload.OriginatingTo ?? prepared.ctxPayload.To ?? prepared.replyTarget;
857
+ const messageSentHookContext = {
858
+ sessionKeyForInternalHooks: prepared.ctxPayload.SessionKey ?? route.sessionKey,
859
+ isGroup: prepared.isRoomish,
860
+ groupId: prepared.isRoomish ? message.channel : void 0
861
+ };
862
+ const messageSentDeliveryHookContext = {
863
+ ...messageSentHookContext,
864
+ messageSentHookTarget
865
+ };
851
866
  const reactionMessageTs = prepared.ackReactionMessageTs;
852
867
  const messageTs = message.ts ?? message.event_ts;
853
868
  const incomingThreadTs = message.thread_ts;
@@ -998,7 +1013,79 @@ async function dispatchPreparedSlackMessage(prepared) {
998
1013
  let usedBlockReplyThreadTs;
999
1014
  let observedReplyDelivery = false;
1000
1015
  let observedFinalReplyDelivery = false;
1016
+ const streamedDeliveries = [];
1017
+ const streamedFailuresOwnedByDispatcher = {
1018
+ tool: 0,
1019
+ block: 0,
1020
+ final: 0
1021
+ };
1022
+ const refreshStreamedAcknowledgements = (session) => {
1023
+ if (session.pendingText.length === 0) for (const delivery of streamedDeliveries) delivery.acknowledged = true;
1024
+ };
1025
+ const recordStreamedDelivery = (kind, content) => {
1026
+ const delivery = {
1027
+ kind,
1028
+ content,
1029
+ acknowledged: false
1030
+ };
1031
+ streamedDeliveries.push(delivery);
1032
+ return delivery;
1033
+ };
1034
+ const rememberStreamedDelivery = (kind, content, session) => {
1035
+ recordStreamedDelivery(kind, content);
1036
+ refreshStreamedAcknowledgements(session);
1037
+ };
1038
+ const emitAcknowledgedStreamedDeliveries = (messageId) => {
1039
+ for (const delivery of streamedDeliveries) {
1040
+ if (!delivery.acknowledged || delivery.outcome) continue;
1041
+ emitSlackMessageSentHooks({
1042
+ ...messageSentHookContext,
1043
+ to: messageSentHookTarget,
1044
+ accountId: account.accountId,
1045
+ content: delivery.content,
1046
+ success: true,
1047
+ ...messageId ? { messageId } : {}
1048
+ });
1049
+ delivery.outcome = "success";
1050
+ }
1051
+ };
1052
+ const emitFailedPendingStreamedDeliveries = (error) => {
1053
+ for (const delivery of streamedDeliveries) {
1054
+ if (delivery.acknowledged || delivery.outcome) continue;
1055
+ emitSlackMessageSentHooks({
1056
+ ...messageSentHookContext,
1057
+ to: messageSentHookTarget,
1058
+ accountId: account.accountId,
1059
+ content: delivery.content,
1060
+ success: false,
1061
+ error
1062
+ });
1063
+ delivery.outcome = "failure";
1064
+ }
1065
+ };
1066
+ const emitSuccessfulPendingStreamedDeliveries = (messageId) => {
1067
+ for (const delivery of streamedDeliveries) {
1068
+ if (delivery.acknowledged || delivery.outcome) continue;
1069
+ emitSlackMessageSentHooks({
1070
+ ...messageSentHookContext,
1071
+ to: messageSentHookTarget,
1072
+ accountId: account.accountId,
1073
+ content: delivery.content,
1074
+ success: true,
1075
+ ...messageId ? { messageId } : {}
1076
+ });
1077
+ delivery.outcome = "success";
1078
+ }
1079
+ };
1001
1080
  const deliveryTracker = createSlackEventDeliveryTracker();
1081
+ const markPreviewPayloadDelivered = (params) => {
1082
+ deliveryTracker.markDelivered(params);
1083
+ const nextThreadTs = replyPlan.peekThreadTs();
1084
+ if (nextThreadTs !== params.threadTs) deliveryTracker.markDelivered({
1085
+ ...params,
1086
+ threadTs: nextThreadTs
1087
+ });
1088
+ };
1002
1089
  const resolveDeliveryThreadTs = (params) => {
1003
1090
  const plannedThreadTs = params.forcedThreadTs ? void 0 : replyPlan.nextThreadTs();
1004
1091
  return params.forcedThreadTs ?? plannedThreadTs ?? (params.kind === "block" ? usedBlockReplyThreadTs : void 0);
@@ -1009,7 +1096,23 @@ async function dispatchPreparedSlackMessage(prepared) {
1009
1096
  if (kind === "block") usedBlockReplyThreadTs = deliveredThreadTs;
1010
1097
  };
1011
1098
  const deliverPendingStreamFallback = async (session, err) => {
1012
- const fallbackText = err.pendingText.trim();
1099
+ let fallbackError = err;
1100
+ if (!session.stopped) try {
1101
+ const stopResult = await stopSlackStream({
1102
+ session,
1103
+ ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
1104
+ });
1105
+ for (const delivery of streamedDeliveries) delivery.acknowledged = true;
1106
+ emitAcknowledgedStreamedDeliveries(stopResult.messageId);
1107
+ observedReplyDelivery = true;
1108
+ usedReplyThreadTs ??= session.threadTs;
1109
+ return true;
1110
+ } catch (stopErr) {
1111
+ if (stopErr instanceof SlackStreamNotDeliveredError) fallbackError = stopErr;
1112
+ else runtime.error?.(danger(`slack-stream: failed to finalize buffered text before fallback: ${formatSlackError(stopErr)}`));
1113
+ }
1114
+ emitAcknowledgedStreamedDeliveries();
1115
+ const fallbackText = fallbackError.pendingText.trim();
1013
1116
  if (!fallbackText) return false;
1014
1117
  try {
1015
1118
  await deliverReplies({
@@ -1023,15 +1126,27 @@ async function dispatchPreparedSlackMessage(prepared) {
1023
1126
  replyThreadTs: session.threadTs,
1024
1127
  replyToMode: replyDeliveryMode,
1025
1128
  ...slackIdentity ? { identity: slackIdentity } : {},
1026
- ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
1129
+ ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
1130
+ ...messageSentDeliveryHookContext,
1131
+ deferMessageSentHooks: true
1027
1132
  });
1028
1133
  markSlackStreamFallbackDelivered(session);
1134
+ if (!session.stopped) try {
1135
+ await stopSlackStream({
1136
+ session,
1137
+ ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
1138
+ });
1139
+ } catch (finalizeErr) {
1140
+ runtime.error?.(danger(`slack-stream: failed to finalize native stream after fallback delivery: ${formatSlackError(finalizeErr)}`));
1141
+ }
1142
+ emitSuccessfulPendingStreamedDeliveries();
1029
1143
  observedReplyDelivery = true;
1030
1144
  usedReplyThreadTs ??= session.threadTs;
1031
- logVerbose(`slack-stream: streamed delivery failed (${err.slackCode}); delivered ${fallbackText.length} chars via deliverReplies fallback`);
1145
+ logVerbose(`slack-stream: streamed delivery failed (${fallbackError.slackCode}); delivered ${fallbackText.length} chars via deliverReplies fallback`);
1032
1146
  return true;
1033
1147
  } catch (postErr) {
1034
- runtime.error?.(danger(`slack-stream: fallback deliverReplies failed after ${err.slackCode}: ${formatErrorMessage(postErr)}`));
1148
+ emitFailedPendingStreamedDeliveries(formatErrorMessage(postErr));
1149
+ runtime.error?.(danger(`slack-stream: fallback deliverReplies failed after ${fallbackError.slackCode}: ${formatErrorMessage(postErr)}`));
1035
1150
  return false;
1036
1151
  }
1037
1152
  };
@@ -1045,7 +1160,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1045
1160
  threadTs: deliveryReplyThreadTs
1046
1161
  })) {
1047
1162
  logVerbose("slack: suppressed duplicate normal delivery within the same turn");
1048
- return;
1163
+ return deliveryReplyThreadTs;
1049
1164
  }
1050
1165
  await deliverReplies({
1051
1166
  cfg: ctx.cfg,
@@ -1058,7 +1173,8 @@ async function dispatchPreparedSlackMessage(prepared) {
1058
1173
  replyThreadTs: deliveryReplyThreadTs,
1059
1174
  replyToMode: replyDeliveryMode,
1060
1175
  ...slackIdentity ? { identity: slackIdentity } : {},
1061
- ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
1176
+ ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
1177
+ ...messageSentDeliveryHookContext
1062
1178
  });
1063
1179
  observedReplyDelivery = true;
1064
1180
  if (params.kind === "final") observedFinalReplyDelivery = true;
@@ -1074,9 +1190,13 @@ async function dispatchPreparedSlackMessage(prepared) {
1074
1190
  payload: params.payload,
1075
1191
  threadTs: deliveryReplyThreadTs
1076
1192
  });
1193
+ return deliveryReplyThreadTs;
1077
1194
  };
1078
1195
  const deliverBufferedStreamFallback = async (params) => {
1079
- if (!await deliverPendingStreamFallback(params.session, params.err)) return false;
1196
+ if (!await deliverPendingStreamFallback(params.session, params.err)) {
1197
+ streamedFailuresOwnedByDispatcher[params.kind] += 1;
1198
+ return false;
1199
+ }
1080
1200
  replyPlan.markSent();
1081
1201
  if (params.kind === "final") observedFinalReplyDelivery = true;
1082
1202
  deliveryTracker.markDelivered({
@@ -1152,10 +1272,12 @@ async function dispatchPreparedSlackMessage(prepared) {
1152
1272
  }),
1153
1273
  userId: message.user
1154
1274
  });
1275
+ refreshStreamedAcknowledgements(streamSession);
1155
1276
  if (streamSession.delivered) {
1156
1277
  observedReplyDelivery = true;
1157
1278
  if (params.kind === "final") observedFinalReplyDelivery = true;
1158
1279
  }
1280
+ if (text) rememberStreamedDelivery(params.kind, text, streamSession);
1159
1281
  rememberDeliveredThreadTs(params.kind, streamThreadTs);
1160
1282
  replyPlan.markSent();
1161
1283
  deliveryTracker.markDelivered({
@@ -1197,11 +1319,13 @@ async function dispatchPreparedSlackMessage(prepared) {
1197
1319
  });
1198
1320
  return;
1199
1321
  }
1322
+ if (text) recordStreamedDelivery(params.kind, text);
1200
1323
  await appendSlackStream({
1201
1324
  session: streamSession,
1202
1325
  text: "\n" + text,
1203
1326
  chunks: completionChunks
1204
1327
  });
1328
+ refreshStreamedAcknowledgements(streamSession);
1205
1329
  if (completionChunks?.length) nativeProgressCompletionSent = true;
1206
1330
  if (streamSession.delivered) {
1207
1331
  observedReplyDelivery = true;
@@ -1244,6 +1368,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1244
1368
  kind: params.kind,
1245
1369
  textOverride: text
1246
1370
  })) return;
1371
+ throw err;
1247
1372
  }
1248
1373
  await deliverNormally({
1249
1374
  payload: params.payload,
@@ -1313,7 +1438,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1313
1438
  kind: info.kind,
1314
1439
  forcedThreadTs: finalThreadTs
1315
1440
  });
1316
- deliveryTracker.markDelivered({
1441
+ markPreviewPayloadDelivered({
1317
1442
  kind: info.kind,
1318
1443
  payload,
1319
1444
  threadTs: finalThreadTs
@@ -1363,6 +1488,14 @@ async function dispatchPreparedSlackMessage(prepared) {
1363
1488
  ...edit.blocks?.length ? { blocks: edit.blocks } : {},
1364
1489
  threadTs: edit.threadTs
1365
1490
  });
1491
+ if (!ttsSupplement) emitSlackMessageSentHooks({
1492
+ ...messageSentHookContext,
1493
+ to: messageSentHookTarget,
1494
+ accountId: account.accountId,
1495
+ content: trimmedFinalText,
1496
+ success: true,
1497
+ messageId: preview.messageId
1498
+ });
1366
1499
  draftPreviewCommitted = true;
1367
1500
  observedFinalReplyDelivery = true;
1368
1501
  },
@@ -1372,7 +1505,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1372
1505
  const finalThreadTs = usedReplyThreadTs ?? statusThreadTs;
1373
1506
  observedReplyDelivery = true;
1374
1507
  replyPlan.markSent();
1375
- deliveryTracker.markDelivered({
1508
+ if (!ttsSupplement) markPreviewPayloadDelivered({
1376
1509
  kind: info.kind,
1377
1510
  payload,
1378
1511
  threadTs: finalThreadTs
@@ -1380,9 +1513,16 @@ async function dispatchPreparedSlackMessage(prepared) {
1380
1513
  },
1381
1514
  buildSupplementalPayload: () => ttsSupplement ? buildTtsSupplementMediaPayload(payload) : void 0,
1382
1515
  deliverSupplemental: async (supplementalPayload) => {
1383
- await deliverNormally({
1516
+ const previewThreadTs = usedReplyThreadTs ?? statusThreadTs;
1517
+ const supplementalThreadTs = await deliverNormally({
1384
1518
  payload: supplementalPayload,
1385
- kind: info.kind
1519
+ kind: info.kind,
1520
+ forcedThreadTs: previewThreadTs
1521
+ });
1522
+ markPreviewPayloadDelivered({
1523
+ kind: info.kind,
1524
+ payload,
1525
+ threadTs: supplementalThreadTs
1386
1526
  });
1387
1527
  },
1388
1528
  logPreviewEditFailure: (err) => {
@@ -1792,15 +1932,35 @@ async function dispatchPreparedSlackMessage(prepared) {
1792
1932
  finalInProgressStatus: dispatchError ? "error" : "complete"
1793
1933
  }) : void 0;
1794
1934
  if (completionChunks?.length) nativeProgressCompletionSent = true;
1795
- await stopSlackStream({
1935
+ const stopResult = await stopSlackStream({
1796
1936
  session: finalStream,
1797
1937
  ...completionChunks?.length ? { chunks: completionChunks } : {},
1798
1938
  ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
1799
1939
  });
1940
+ for (const delivery of streamedDeliveries) delivery.acknowledged = true;
1941
+ emitAcknowledgedStreamedDeliveries(stopResult?.messageId);
1800
1942
  } catch (err) {
1801
1943
  if (err instanceof SlackStreamNotDeliveredError) streamFallbackDelivered = await deliverPendingStreamFallback(finalStream, err);
1802
- else runtime.error?.(danger(`slack-stream: failed to stop stream: ${formatSlackError(err)}`));
1944
+ else {
1945
+ const error = formatSlackError(err);
1946
+ emitAcknowledgedStreamedDeliveries();
1947
+ emitFailedPendingStreamedDeliveries(error);
1948
+ runtime.error?.(danger(`slack-stream: failed to stop stream: ${error}`));
1949
+ }
1950
+ }
1951
+ for (const kind of [
1952
+ "tool",
1953
+ "block",
1954
+ "final"
1955
+ ]) {
1956
+ const failedStreamedCount = streamedDeliveries.filter((delivery) => delivery.kind === kind && delivery.outcome === "failure").length;
1957
+ const additionalFailedStreamed = Math.max(0, failedStreamedCount - streamedFailuresOwnedByDispatcher[kind]);
1958
+ if (additionalFailedStreamed > 0) counts = {
1959
+ ...counts,
1960
+ [kind]: Math.max(0, (counts[kind] ?? 0) - additionalFailedStreamed)
1961
+ };
1803
1962
  }
1963
+ queuedFinal = queuedFinal && (counts.final ?? 0) > 0;
1804
1964
  const anyReplyDelivered = hasVisibleInboundReplyDispatch({
1805
1965
  queuedFinal,
1806
1966
  counts
@@ -2699,7 +2699,7 @@ function createSlackThreadTsResolver(params) {
2699
2699
  //#region extensions/slack/src/monitor/message-handler.ts
2700
2700
  let slackMessagePipelinePromise;
2701
2701
  function loadSlackMessagePipeline() {
2702
- slackMessagePipelinePromise ??= import("./pipeline.runtime-Dvqs0AT-.js");
2702
+ slackMessagePipelinePromise ??= import("./pipeline.runtime-1FYOQOt_.js");
2703
2703
  return slackMessagePipelinePromise;
2704
2704
  }
2705
2705
  const APP_MENTION_RETRY_TTL_MS = 6e4;
@@ -3341,7 +3341,7 @@ function loadSlashCommandsRuntime() {
3341
3341
  return slashCommandsRuntimePromise;
3342
3342
  }
3343
3343
  function loadSlashDispatchRuntime() {
3344
- slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-CoFAysiw.js");
3344
+ slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-Dvvs747S.js");
3345
3345
  return slashDispatchRuntimePromise;
3346
3346
  }
3347
3347
  function loadSlackPluginCommandsRuntime() {
@@ -3777,6 +3777,7 @@ async function registerSlackMonitorSlashCommands(params) {
3777
3777
  targetSessionKey: route.sessionKey,
3778
3778
  lowercaseSessionKey: true
3779
3779
  });
3780
+ const slashReplyTarget = !slashCommand.ephemeral && isRoomish ? `channel:${command.channel_id}` : `user:${command.user_id}`;
3780
3781
  const ctxPayload = finalizeInboundContext({
3781
3782
  Body: prompt,
3782
3783
  BodyForAgent: prompt,
@@ -3809,7 +3810,7 @@ async function registerSlackMonitorSlashCommands(params) {
3809
3810
  CommandSource: "native",
3810
3811
  CommandAuthorized: commandAuthorized,
3811
3812
  OriginatingChannel: "slack",
3812
- OriginatingTo: `user:${command.user_id}`
3813
+ OriginatingTo: slashReplyTarget
3813
3814
  });
3814
3815
  await recordInboundSessionMetaSafe({
3815
3816
  cfg,
@@ -3831,12 +3832,18 @@ async function registerSlackMonitorSlashCommands(params) {
3831
3832
  }) ? compileSlackInteractiveReplies(payload) : payload;
3832
3833
  }
3833
3834
  });
3835
+ const messageSentHookTarget = ctxPayload.OriginatingTo ?? ctxPayload.To ?? slashReplyTarget;
3834
3836
  const deliverSlashPayloads = async (replies) => {
3835
3837
  await deliverSlackSlashReplies({
3836
3838
  replies,
3837
3839
  respond,
3838
3840
  ephemeral: slashCommand.ephemeral,
3839
3841
  textLimit: ctx.textLimit,
3842
+ messageSentHookTarget,
3843
+ accountId: route.accountId,
3844
+ sessionKeyForInternalHooks: ctxPayload.SessionKey ?? route.sessionKey,
3845
+ isGroup: isRoomish,
3846
+ groupId: isRoomish ? command.channel_id : void 0,
3840
3847
  chunkMode: resolveChunkMode(cfg, "slack", route.accountId),
3841
3848
  tableMode: resolveMarkdownTableMode({
3842
3849
  cfg,
@@ -0,0 +1,285 @@
1
+ import { r as resolveSlackReplyBlocks, s as SLACK_TEXT_LIMIT } from "./thread-ts-Cffag8e2.js";
2
+ import { o as markdownToSlackMrkdwnChunks, t as sendMessageSlack } from "./send-rekB-Xjp.js";
3
+ import { createReplyReferencePlanner } from "openclaw/plugin-sdk/reply-reference";
4
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
+ import { SILENT_REPLY_TOKEN, chunkMarkdownTextWithMode, isSilentReplyText } from "openclaw/plugin-sdk/reply-chunking";
6
+ import { deliverTextOrMediaReply, getReplyPayloadTtsSupplement, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
7
+ import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
8
+ import { buildCanonicalSentMessageHookContext, createInternalHookEvent, fireAndForgetHook, toInternalMessageSentContext, toPluginMessageContext, toPluginMessageSentEvent, triggerInternalHook } from "openclaw/plugin-sdk/hook-runtime";
9
+ //#region extensions/slack/src/message-sent-hook.ts
10
+ /**
11
+ * Slack-side emission of the `message_sent` plugin hook.
12
+ *
13
+ * Mirrors the Telegram pattern in `extensions/telegram/src/bot/delivery.replies.ts`
14
+ * (`buildTelegramSentHookContext`, `emitMessageSentHooks`, `emitTelegramMessageSentHooks`).
15
+ *
16
+ * Without this, plugins observing `message_sent` see Telegram outbound but not
17
+ * Slack outbound — even though `docs/plugins/hooks.md` documents the hook as
18
+ * firing for all successful outbound deliveries.
19
+ */
20
+ function buildSlackSentHookContext(params) {
21
+ return buildCanonicalSentMessageHookContext({
22
+ to: params.to,
23
+ content: params.content,
24
+ success: params.success,
25
+ error: params.error,
26
+ channelId: "slack",
27
+ accountId: params.accountId ?? void 0,
28
+ conversationId: params.to,
29
+ sessionKey: params.sessionKeyForInternalHooks,
30
+ messageId: params.messageId,
31
+ isGroup: params.isGroup,
32
+ groupId: params.groupId
33
+ });
34
+ }
35
+ function emitInternalSlackMessageSentHook(params) {
36
+ if (!params.sessionKeyForInternalHooks) return;
37
+ const canonical = buildSlackSentHookContext(params);
38
+ fireAndForgetHook(triggerInternalHook(createInternalHookEvent("message", "sent", params.sessionKeyForInternalHooks, toInternalMessageSentContext(canonical))), "slack: message:sent internal hook failed");
39
+ }
40
+ function emitMessageSentHooks(params) {
41
+ if (!params.enabled && !params.sessionKeyForInternalHooks) return;
42
+ const canonical = buildSlackSentHookContext(params);
43
+ if (params.enabled) fireAndForgetHook(Promise.resolve(params.hookRunner.runMessageSent(toPluginMessageSentEvent(canonical), toPluginMessageContext(canonical))), "slack: message_sent plugin hook failed");
44
+ emitInternalSlackMessageSentHook(params);
45
+ }
46
+ /**
47
+ * Fire both the plugin `message_sent` hook and (if a session key is supplied)
48
+ * the internal `message:sent` hook for a successful or failed Slack outbound
49
+ * delivery.
50
+ *
51
+ * Safe to call after every `chat.postMessage` — the function self-gates on
52
+ * `hookRunner.hasHooks("message_sent")` so plugins not observing the hook
53
+ * incur no cost.
54
+ */
55
+ function emitSlackMessageSentHooks(params) {
56
+ const hookRunner = getGlobalHookRunner();
57
+ emitMessageSentHooks({
58
+ ...params,
59
+ hookRunner,
60
+ enabled: hookRunner?.hasHooks("message_sent") ?? false
61
+ });
62
+ }
63
+ //#endregion
64
+ //#region extensions/slack/src/monitor/replies.ts
65
+ function readSlackReplyBlocks(payload) {
66
+ return resolveSlackReplyBlocks(payload);
67
+ }
68
+ function resolveSlackMediaHookSpokenText(payload) {
69
+ return (getReplyPayloadTtsSupplement(payload)?.spokenText ?? payload.spokenText)?.trim() || void 0;
70
+ }
71
+ function resolveDeliveredSlackReplyThreadTs(params) {
72
+ return (params.replyToMode === "off" ? void 0 : params.payloadReplyToId) ?? params.replyThreadTs;
73
+ }
74
+ async function deliverReplies(params) {
75
+ let latestResult;
76
+ for (const payload of params.replies) {
77
+ if (payload.isReasoning === true) continue;
78
+ const threadTs = resolveDeliveredSlackReplyThreadTs({
79
+ replyToMode: params.replyToMode,
80
+ payloadReplyToId: payload.replyToId,
81
+ replyThreadTs: params.replyThreadTs
82
+ });
83
+ const reply = resolveSendableOutboundReplyParts(payload);
84
+ const slackBlocks = readSlackReplyBlocks(payload);
85
+ if (!reply.hasContent && !slackBlocks?.length) continue;
86
+ const emitSent = (content, result) => {
87
+ if (params.deferMessageSentHooks) return;
88
+ emitSlackMessageSentHooks({
89
+ sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
90
+ to: params.messageSentHookTarget ?? params.target,
91
+ accountId: params.accountId,
92
+ content,
93
+ success: true,
94
+ messageId: result?.messageId,
95
+ isGroup: params.isGroup,
96
+ groupId: params.groupId
97
+ });
98
+ };
99
+ const emitFailed = (content, error) => {
100
+ if (params.deferMessageSentHooks) return;
101
+ emitSlackMessageSentHooks({
102
+ sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
103
+ to: params.messageSentHookTarget ?? params.target,
104
+ accountId: params.accountId,
105
+ content,
106
+ success: false,
107
+ error: formatErrorMessage(error),
108
+ isGroup: params.isGroup,
109
+ groupId: params.groupId
110
+ });
111
+ };
112
+ if (!reply.hasMedia && slackBlocks?.length) {
113
+ const trimmed = reply.trimmedText;
114
+ if (!trimmed && !slackBlocks?.length) continue;
115
+ if (trimmed && isSilentReplyText(trimmed, SILENT_REPLY_TOKEN)) continue;
116
+ let result;
117
+ try {
118
+ result = await sendMessageSlack(params.target, trimmed, {
119
+ cfg: params.cfg,
120
+ token: params.token,
121
+ threadTs,
122
+ accountId: params.accountId,
123
+ ...slackBlocks?.length ? { blocks: slackBlocks } : {},
124
+ ...params.identity ? { identity: params.identity } : {},
125
+ ...params.metadata ? { metadata: params.metadata } : {}
126
+ });
127
+ } catch (error) {
128
+ emitFailed(trimmed, error);
129
+ throw error;
130
+ }
131
+ emitSent(trimmed, result);
132
+ latestResult = result;
133
+ params.runtime.log?.(`delivered reply to ${params.target}`);
134
+ continue;
135
+ }
136
+ const spokenText = resolveSlackMediaHookSpokenText(payload);
137
+ const mediaHookContent = reply.hasText ? reply.text : spokenText || reply.text;
138
+ const hookContent = reply.hasMedia ? mediaHookContent : reply.trimmedText;
139
+ let lastResult;
140
+ let delivered;
141
+ try {
142
+ delivered = await deliverTextOrMediaReply({
143
+ payload,
144
+ text: reply.text,
145
+ chunkText: !reply.hasMedia ? (value) => {
146
+ const trimmed = value.trim();
147
+ if (!trimmed || isSilentReplyText(trimmed, SILENT_REPLY_TOKEN)) return [];
148
+ return [trimmed];
149
+ } : void 0,
150
+ sendText: async (trimmed) => {
151
+ lastResult = await sendMessageSlack(params.target, trimmed, {
152
+ cfg: params.cfg,
153
+ token: params.token,
154
+ threadTs,
155
+ accountId: params.accountId,
156
+ ...params.identity ? { identity: params.identity } : {},
157
+ ...params.metadata ? { metadata: params.metadata } : {}
158
+ });
159
+ },
160
+ sendMedia: async ({ mediaUrl, caption }) => {
161
+ lastResult = await sendMessageSlack(params.target, caption ?? "", {
162
+ cfg: params.cfg,
163
+ token: params.token,
164
+ mediaUrl,
165
+ threadTs,
166
+ accountId: params.accountId,
167
+ ...params.identity ? { identity: params.identity } : {},
168
+ ...params.metadata ? { metadata: params.metadata } : {}
169
+ });
170
+ }
171
+ });
172
+ } catch (error) {
173
+ emitFailed(hookContent, error);
174
+ throw error;
175
+ }
176
+ if (delivered !== "empty") {
177
+ emitSent(hookContent, reply.hasMedia ? void 0 : lastResult);
178
+ latestResult = lastResult;
179
+ params.runtime.log?.(`delivered reply to ${params.target}`);
180
+ }
181
+ }
182
+ return latestResult;
183
+ }
184
+ /**
185
+ * Compute effective threadTs for a Slack reply based on replyToMode.
186
+ * - "off": stay in thread if already in one, otherwise main channel
187
+ * - "first": first reply goes to thread, subsequent replies to main channel
188
+ * - "all": all replies go to thread
189
+ */
190
+ function resolveSlackThreadTs(params) {
191
+ return createSlackReplyReferencePlanner({
192
+ replyToMode: params.replyToMode,
193
+ incomingThreadTs: params.incomingThreadTs,
194
+ messageTs: params.messageTs,
195
+ hasReplied: params.hasReplied,
196
+ isThreadReply: params.isThreadReply
197
+ }).use();
198
+ }
199
+ function createSlackReplyReferencePlanner(params) {
200
+ return createReplyReferencePlanner({
201
+ replyToMode: params.isThreadReply ?? Boolean(params.incomingThreadTs && params.incomingThreadTs !== params.messageTs) ? "all" : params.replyToMode,
202
+ existingId: params.incomingThreadTs,
203
+ startId: params.messageTs,
204
+ hasReplied: params.hasReplied
205
+ });
206
+ }
207
+ function createSlackReplyDeliveryPlan(params) {
208
+ const replyReference = createSlackReplyReferencePlanner({
209
+ replyToMode: params.replyToMode,
210
+ incomingThreadTs: params.incomingThreadTs,
211
+ messageTs: params.messageTs,
212
+ hasReplied: params.hasRepliedRef.value,
213
+ isThreadReply: params.isThreadReply
214
+ });
215
+ return {
216
+ peekThreadTs: () => replyReference.peek(),
217
+ nextThreadTs: () => replyReference.use(),
218
+ markSent: () => {
219
+ replyReference.markSent();
220
+ params.hasRepliedRef.value = replyReference.hasReplied();
221
+ }
222
+ };
223
+ }
224
+ async function deliverSlackSlashReplies(params) {
225
+ const deliveries = [];
226
+ const chunkLimit = Math.min(params.textLimit, SLACK_TEXT_LIMIT);
227
+ for (const payload of params.replies) {
228
+ if (payload.isReasoning === true) continue;
229
+ const reply = resolveSendableOutboundReplyParts(payload);
230
+ const slackBlocks = readSlackReplyBlocks(payload);
231
+ const text = reply.hasText && !isSilentReplyText(reply.trimmedText, SILENT_REPLY_TOKEN) ? reply.trimmedText : void 0;
232
+ if (slackBlocks?.length && !reply.hasMedia) {
233
+ deliveries.push({
234
+ hookContent: text ?? "",
235
+ messages: [{
236
+ text: text ?? "",
237
+ blocks: slackBlocks
238
+ }]
239
+ });
240
+ continue;
241
+ }
242
+ const combined = [text ?? "", ...reply.mediaUrls].filter(Boolean).join("\n");
243
+ if (!combined) continue;
244
+ const chunkMode = params.chunkMode ?? "length";
245
+ const chunks = (chunkMode === "newline" ? chunkMarkdownTextWithMode(combined, chunkLimit, chunkMode) : [combined]).flatMap((markdown) => markdownToSlackMrkdwnChunks(markdown, chunkLimit, { tableMode: params.tableMode }));
246
+ if (!chunks.length && combined) chunks.push(combined);
247
+ deliveries.push({
248
+ hookContent: text ?? resolveSlackMediaHookSpokenText(payload) ?? combined,
249
+ messages: chunks.map((chunk) => ({ text: chunk }))
250
+ });
251
+ }
252
+ if (deliveries.length === 0) return;
253
+ const responseType = params.ephemeral ? "ephemeral" : "in_channel";
254
+ for (const delivery of deliveries) {
255
+ try {
256
+ for (const message of delivery.messages) await params.respond({
257
+ ...message,
258
+ response_type: responseType
259
+ });
260
+ } catch (error) {
261
+ if (params.messageSentHookTarget) emitSlackMessageSentHooks({
262
+ sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
263
+ to: params.messageSentHookTarget,
264
+ accountId: params.accountId,
265
+ content: delivery.hookContent,
266
+ success: false,
267
+ error: formatErrorMessage(error),
268
+ isGroup: params.isGroup,
269
+ groupId: params.groupId
270
+ });
271
+ throw error;
272
+ }
273
+ if (params.messageSentHookTarget) emitSlackMessageSentHooks({
274
+ sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
275
+ to: params.messageSentHookTarget,
276
+ accountId: params.accountId,
277
+ content: delivery.hookContent,
278
+ success: true,
279
+ isGroup: params.isGroup,
280
+ groupId: params.groupId
281
+ });
282
+ }
283
+ }
284
+ //#endregion
285
+ export { resolveDeliveredSlackReplyThreadTs as a, readSlackReplyBlocks as i, deliverReplies as n, resolveSlackThreadTs as o, deliverSlackSlashReplies as r, emitSlackMessageSentHooks as s, createSlackReplyDeliveryPlan as t };
@@ -7,8 +7,8 @@ import { a as listSlackEmojis, c as pinSlackMessage, d as removeOwnSlackReaction
7
7
  import { t as probeSlack } from "./probe-BTKzLT2u.js";
8
8
  import { t as resolveSlackChannelAllowlist } from "./resolve-channels-DX2GSx9c.js";
9
9
  import { t as resolveSlackUserAllowlist } from "./resolve-users-DOULgUwy.js";
10
- import { t as monitorSlackProvider } from "./provider-LH_j-bLA.js";
10
+ import { t as monitorSlackProvider } from "./provider-ChhYGpXx.js";
11
11
  import { n as slackActionRuntime, t as handleSlackAction } from "./action-runtime-CgrHdqkC.js";
12
12
  import { n as listSlackDirectoryGroupsLive, r as listSlackDirectoryPeersLive } from "./directory-live-C1acgXKJ.js";
13
- import "./monitor-Szm2LjsM.js";
13
+ import "./monitor-DwDmBYfl.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 { t as slackSetupPlugin } from "./channel.setup-DrCTObMp.js";
1
+ import { t as slackSetupPlugin } from "./channel.setup-W5YnieWd.js";
2
2
  export { slackSetupPlugin };
@@ -1,5 +1,5 @@
1
1
  import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, o as resolveSlackAccountAllowFrom } from "./accounts-f6Xcv9Vi.js";
2
- import "./shared-Bviszgb6.js";
2
+ import "./shared-Bkkmro6q.js";
3
3
  import { i as SLACK_CHANNEL, t as createSlackSetupWizardBase } from "./setup-core-POfI_bgP.js";
4
4
  import { t as resolveSlackChannelAllowlist } from "./resolve-channels-DX2GSx9c.js";
5
5
  import { t as resolveSlackUserAllowlist } from "./resolve-users-DOULgUwy.js";
@@ -1,4 +1,4 @@
1
- import { a as resolveSlackAccount, c as resolveSlackConfigAccessorAccount, i as resolveDefaultSlackAccountId, n as listSlackAccountIds, o as resolveSlackAccountAllowFrom, s as resolveSlackAccountDmPolicy } from "./accounts-f6Xcv9Vi.js";
1
+ import { a as resolveSlackAccount, c as resolveSlackConfigAccessorAccount, i as resolveDefaultSlackAccountId, n as listSlackAccountIds, o as resolveSlackAccountAllowFrom, r as mergeSlackAccountConfig, s as resolveSlackAccountDmPolicy } from "./accounts-f6Xcv9Vi.js";
2
2
  import { t as inspectSlackAccount } from "./account-inspect-CdGk6R7l.js";
3
3
  import { f as getChatChannelMeta, h as isSlackPluginAccountConfigured } from "./client-Cn2WwpcA.js";
4
4
  import { n as isSlackInteractiveRepliesEnabled } from "./interactive-replies-DrBq4Mld.js";
@@ -71,6 +71,92 @@ function isSlackMutableAllowEntry(raw) {
71
71
  function asObjectRecord(value) {
72
72
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
73
73
  }
74
+ const collectSlackMutableAllowlistWarnings = createDangerousNameMatchingMutableAllowlistWarningCollector({
75
+ channel: "slack",
76
+ detector: isSlackMutableAllowEntry,
77
+ collectLists: (scope) => {
78
+ const lists = [{
79
+ pathLabel: `${scope.prefix}.allowFrom`,
80
+ list: scope.account.allowFrom
81
+ }];
82
+ const dm = asObjectRecord(scope.account.dm);
83
+ if (dm) lists.push({
84
+ pathLabel: `${scope.prefix}.dm.allowFrom`,
85
+ list: dm.allowFrom
86
+ });
87
+ const channels = asObjectRecord(scope.account.channels);
88
+ if (channels) for (const [channelKey, channelRaw] of Object.entries(channels)) {
89
+ const channel = asObjectRecord(channelRaw);
90
+ if (!channel) continue;
91
+ lists.push({
92
+ pathLabel: `${scope.prefix}.channels.${channelKey}.users`,
93
+ list: channel.users
94
+ });
95
+ }
96
+ return lists;
97
+ }
98
+ });
99
+ const SLACK_CANONICAL_CHANNEL_ID_RE = /^[CG][A-Z0-9]{8,}$/;
100
+ const SLACK_LOWERCASE_CHANNEL_ID_RE = /^[cg][0-9][a-z0-9]{7,}$/;
101
+ const SLACK_PREFIXED_CANONICAL_CHANNEL_ID_RE = /^channel:[CG][A-Z0-9]{8,}$/;
102
+ const SLACK_PREFIXED_LOWERCASE_CHANNEL_ID_RE = /^channel:[cg][0-9][a-z0-9]{7,}$/;
103
+ const SLACK_CANONICAL_DM_ID_RE = /^(?:channel:)?D[A-Z0-9]{8,}$/;
104
+ const SLACK_PREFIXED_LOWERCASE_DM_ID_RE = /^channel:d[a-z0-9]{8,}$/;
105
+ const SLACK_AMBIGUOUS_LOWERCASE_DM_ID_RE = /^d[a-z0-9]{8,}$/;
106
+ const SLACK_AMBIGUOUS_LOWERCASE_CHANNEL_ID_RE = /^(?:channel:)?[cgd][a-z][a-z0-9]{7,}$/;
107
+ const SLACK_CHANNEL_NAME_RE = /^[\p{L}\p{M}\p{N}_-]{1,80}$/u;
108
+ const SLACK_CHANNEL_NAME_ALPHANUMERIC_RE = /[\p{L}\p{N}]/u;
109
+ function looksLikeSlackChannelId(channelKey) {
110
+ return SLACK_CANONICAL_CHANNEL_ID_RE.test(channelKey) || SLACK_LOWERCASE_CHANNEL_ID_RE.test(channelKey) || SLACK_PREFIXED_CANONICAL_CHANNEL_ID_RE.test(channelKey) || SLACK_PREFIXED_LOWERCASE_CHANNEL_ID_RE.test(channelKey);
111
+ }
112
+ function looksLikeSlackDmId(channelKey) {
113
+ return SLACK_CANONICAL_DM_ID_RE.test(channelKey) || SLACK_PREFIXED_LOWERCASE_DM_ID_RE.test(channelKey);
114
+ }
115
+ function looksLikeSlackChannelNameKey(channelKey) {
116
+ const name = channelKey.startsWith("#") ? channelKey.slice(1) : channelKey;
117
+ return name === name.toLowerCase() && SLACK_CHANNEL_NAME_RE.test(name) && SLACK_CHANNEL_NAME_ALPHANUMERIC_RE.test(name);
118
+ }
119
+ function collectSlackNameKeyedChannelWarnings({ cfg }) {
120
+ const warnings = /* @__PURE__ */ new Set();
121
+ const slackCfg = asObjectRecord(asObjectRecord(cfg.channels)?.slack);
122
+ const providerChannels = asObjectRecord(slackCfg?.channels);
123
+ const accounts = asObjectRecord(slackCfg?.accounts);
124
+ for (const accountId of listSlackAccountIds(cfg)) {
125
+ const account = asObjectRecord(mergeSlackAccountConfig(cfg, accountId));
126
+ if (!account || slackCfg?.enabled === false || account.enabled === false) continue;
127
+ const effectiveGroupPolicy = (typeof account.groupPolicy === "string" ? account.groupPolicy : void 0) ?? "allowlist";
128
+ const rawAccount = asObjectRecord(accounts?.[accountId]);
129
+ const accountPrefix = rawAccount ? `channels.slack.accounts.${accountId}` : "channels.slack";
130
+ const accountChannels = asObjectRecord(rawAccount?.channels);
131
+ const channels = accountChannels ?? providerChannels;
132
+ if (!channels) continue;
133
+ const channelsPrefix = accountChannels ? `channels.slack.accounts.${accountId}` : "channels.slack";
134
+ const fallbackDescription = Object.hasOwn(channels, "*") ? `${channelsPrefix}.channels."*" applies instead and this entry's overrides are ignored` : effectiveGroupPolicy === "open" ? "this entry's overrides are ignored and the channel remains allowed by groupPolicy: \"open\"" : "messages from the channel are dropped";
135
+ for (const channelKey of Object.keys(channels)) {
136
+ if (channelKey === "*") continue;
137
+ if (looksLikeSlackDmId(channelKey)) {
138
+ warnings.add(`${channelsPrefix}.channels."${channelKey}" is a Slack DM conversation ID, but ${channelsPrefix}.channels only configures channel and group rooms. Configure DM access with ${accountPrefix}.dmPolicy and ${accountPrefix}.allowFrom instead.`);
139
+ continue;
140
+ }
141
+ if (SLACK_AMBIGUOUS_LOWERCASE_DM_ID_RE.test(channelKey)) {
142
+ if (account.dangerouslyAllowNameMatching === true && looksLikeSlackChannelNameKey(channelKey)) continue;
143
+ warnings.add(`${channelsPrefix}.channels."${channelKey}" is ambiguous: it may be a lowercase Slack DM conversation ID or a channel name. Configure DMs with ${accountPrefix}.dmPolicy and ${accountPrefix}.allowFrom; otherwise re-key the room with its stable C/G ID.`);
144
+ continue;
145
+ }
146
+ if (effectiveGroupPolicy === "disabled") continue;
147
+ const channelConfig = asObjectRecord(channels[channelKey]);
148
+ if (effectiveGroupPolicy === "open" && Object.keys(channelConfig ?? {}).length === 0) continue;
149
+ if (looksLikeSlackChannelId(channelKey)) continue;
150
+ if (account.dangerouslyAllowNameMatching === true && looksLikeSlackChannelNameKey(channelKey)) continue;
151
+ if (SLACK_AMBIGUOUS_LOWERCASE_CHANNEL_ID_RE.test(channelKey)) {
152
+ warnings.add(`${channelsPrefix}.channels."${channelKey}" is ambiguous: it may be a lowercase Slack channel ID or a channel name. If it is a channel name, inbound routing will not match it and ${fallbackDescription}. Re-key it with the channel's stable ID (e.g. C0123ABCD, from the channel's About details or conversations.info).`);
153
+ continue;
154
+ }
155
+ warnings.add(`${channelsPrefix}.channels."${channelKey}" is keyed by a channel name or non-canonical ID form, not a routable Slack channel ID; under groupPolicy: "${effectiveGroupPolicy}" inbound routing does not match this entry, so ${fallbackDescription}. Re-key it with the channel's ID (e.g. C0123ABCD, from the channel's About details or conversations.info).`);
156
+ }
157
+ }
158
+ return [...warnings];
159
+ }
74
160
  const slackDoctor = {
75
161
  dmAllowFromMode: "topOnly",
76
162
  groupModel: "route",
@@ -78,31 +164,7 @@ const slackDoctor = {
78
164
  warnOnEmptyGroupSenderAllowlist: false,
79
165
  legacyConfigRules,
80
166
  normalizeCompatibilityConfig,
81
- collectMutableAllowlistWarnings: createDangerousNameMatchingMutableAllowlistWarningCollector({
82
- channel: "slack",
83
- detector: isSlackMutableAllowEntry,
84
- collectLists: (scope) => {
85
- const lists = [{
86
- pathLabel: `${scope.prefix}.allowFrom`,
87
- list: scope.account.allowFrom
88
- }];
89
- const dm = asObjectRecord(scope.account.dm);
90
- if (dm) lists.push({
91
- pathLabel: `${scope.prefix}.dm.allowFrom`,
92
- list: dm.allowFrom
93
- });
94
- const channels = asObjectRecord(scope.account.channels);
95
- if (channels) for (const [channelKey, channelRaw] of Object.entries(channels)) {
96
- const channel = asObjectRecord(channelRaw);
97
- if (!channel) continue;
98
- lists.push({
99
- pathLabel: `${scope.prefix}.channels.${channelKey}.users`,
100
- list: channel.users
101
- });
102
- }
103
- return lists;
104
- }
105
- })
167
+ collectMutableAllowlistWarnings: ({ cfg }) => [...collectSlackMutableAllowlistWarnings({ cfg }), ...collectSlackNameKeyedChannelWarnings({ cfg })]
106
168
  };
107
169
  //#endregion
108
170
  //#region extensions/slack/src/shared.ts
@@ -1,4 +1,4 @@
1
- import { r as deliverSlackSlashReplies$1 } from "./replies-FEOdgSlE.js";
1
+ import { r as deliverSlackSlashReplies$1 } from "./replies-Bt4nWmSd.js";
2
2
  import { resolveAgentRoute as resolveAgentRoute$1 } from "openclaw/plugin-sdk/routing";
3
3
  import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
4
4
  import { recordInboundSessionMetaSafe as recordInboundSessionMetaSafe$1, resolveConversationLabel as resolveConversationLabel$1 } from "openclaw/plugin-sdk/conversation-runtime";
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/slack",
3
- "version": "2026.6.6-beta.2",
3
+ "version": "2026.6.8-beta.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/slack",
9
- "version": "2026.6.6-beta.2",
9
+ "version": "2026.6.8-beta.1",
10
10
  "dependencies": {
11
11
  "@slack/bolt": "4.7.3",
12
12
  "@slack/types": "2.21.1",
@@ -15,7 +15,7 @@
15
15
  "zod": "4.4.3"
16
16
  },
17
17
  "peerDependencies": {
18
- "openclaw": ">=2026.6.6-beta.2"
18
+ "openclaw": ">=2026.6.8-beta.1"
19
19
  },
20
20
  "peerDependenciesMeta": {
21
21
  "openclaw": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/slack",
3
- "version": "2026.6.6-beta.2",
3
+ "version": "2026.6.8-beta.1",
4
4
  "description": "OpenClaw Slack channel plugin for channels, DMs, commands, and app events.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -15,7 +15,7 @@
15
15
  "zod": "4.4.3"
16
16
  },
17
17
  "peerDependencies": {
18
- "openclaw": ">=2026.6.6-beta.2"
18
+ "openclaw": ">=2026.6.8-beta.1"
19
19
  },
20
20
  "peerDependenciesMeta": {
21
21
  "openclaw": {
@@ -60,13 +60,13 @@
60
60
  "allowInvalidConfigRecovery": true
61
61
  },
62
62
  "compat": {
63
- "pluginApi": ">=2026.6.6-beta.2"
63
+ "pluginApi": ">=2026.6.8-beta.1"
64
64
  },
65
65
  "startup": {
66
66
  "deferConfiguredChannelFullLoadUntilAfterListen": true
67
67
  },
68
68
  "build": {
69
- "openclawVersion": "2026.6.6-beta.2",
69
+ "openclawVersion": "2026.6.8-beta.1",
70
70
  "bundledDist": false
71
71
  },
72
72
  "release": {
@@ -1,142 +0,0 @@
1
- import { r as resolveSlackReplyBlocks, s as SLACK_TEXT_LIMIT } from "./thread-ts-Cffag8e2.js";
2
- import { o as markdownToSlackMrkdwnChunks, t as sendMessageSlack } from "./send-rekB-Xjp.js";
3
- import { createReplyReferencePlanner } from "openclaw/plugin-sdk/reply-reference";
4
- import { SILENT_REPLY_TOKEN, chunkMarkdownTextWithMode, isSilentReplyText } from "openclaw/plugin-sdk/reply-chunking";
5
- import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
6
- //#region extensions/slack/src/monitor/replies.ts
7
- function readSlackReplyBlocks(payload) {
8
- return resolveSlackReplyBlocks(payload);
9
- }
10
- function resolveDeliveredSlackReplyThreadTs(params) {
11
- return (params.replyToMode === "off" ? void 0 : params.payloadReplyToId) ?? params.replyThreadTs;
12
- }
13
- async function deliverReplies(params) {
14
- for (const payload of params.replies) {
15
- if (payload.isReasoning === true) continue;
16
- const threadTs = resolveDeliveredSlackReplyThreadTs({
17
- replyToMode: params.replyToMode,
18
- payloadReplyToId: payload.replyToId,
19
- replyThreadTs: params.replyThreadTs
20
- });
21
- const reply = resolveSendableOutboundReplyParts(payload);
22
- const slackBlocks = readSlackReplyBlocks(payload);
23
- if (!reply.hasContent && !slackBlocks?.length) continue;
24
- if (!reply.hasMedia && slackBlocks?.length) {
25
- const trimmed = reply.trimmedText;
26
- if (!trimmed && !slackBlocks?.length) continue;
27
- if (trimmed && isSilentReplyText(trimmed, SILENT_REPLY_TOKEN)) continue;
28
- await sendMessageSlack(params.target, trimmed, {
29
- cfg: params.cfg,
30
- token: params.token,
31
- threadTs,
32
- accountId: params.accountId,
33
- ...slackBlocks?.length ? { blocks: slackBlocks } : {},
34
- ...params.identity ? { identity: params.identity } : {},
35
- ...params.metadata ? { metadata: params.metadata } : {}
36
- });
37
- params.runtime.log?.(`delivered reply to ${params.target}`);
38
- continue;
39
- }
40
- if (await deliverTextOrMediaReply({
41
- payload,
42
- text: reply.text,
43
- chunkText: !reply.hasMedia ? (value) => {
44
- const trimmed = value.trim();
45
- if (!trimmed || isSilentReplyText(trimmed, SILENT_REPLY_TOKEN)) return [];
46
- return [trimmed];
47
- } : void 0,
48
- sendText: async (trimmed) => {
49
- await sendMessageSlack(params.target, trimmed, {
50
- cfg: params.cfg,
51
- token: params.token,
52
- threadTs,
53
- accountId: params.accountId,
54
- ...params.identity ? { identity: params.identity } : {},
55
- ...params.metadata ? { metadata: params.metadata } : {}
56
- });
57
- },
58
- sendMedia: async ({ mediaUrl, caption }) => {
59
- await sendMessageSlack(params.target, caption ?? "", {
60
- cfg: params.cfg,
61
- token: params.token,
62
- mediaUrl,
63
- threadTs,
64
- accountId: params.accountId,
65
- ...params.identity ? { identity: params.identity } : {},
66
- ...params.metadata ? { metadata: params.metadata } : {}
67
- });
68
- }
69
- }) !== "empty") params.runtime.log?.(`delivered reply to ${params.target}`);
70
- }
71
- }
72
- /**
73
- * Compute effective threadTs for a Slack reply based on replyToMode.
74
- * - "off": stay in thread if already in one, otherwise main channel
75
- * - "first": first reply goes to thread, subsequent replies to main channel
76
- * - "all": all replies go to thread
77
- */
78
- function resolveSlackThreadTs(params) {
79
- return createSlackReplyReferencePlanner({
80
- replyToMode: params.replyToMode,
81
- incomingThreadTs: params.incomingThreadTs,
82
- messageTs: params.messageTs,
83
- hasReplied: params.hasReplied,
84
- isThreadReply: params.isThreadReply
85
- }).use();
86
- }
87
- function createSlackReplyReferencePlanner(params) {
88
- return createReplyReferencePlanner({
89
- replyToMode: params.isThreadReply ?? Boolean(params.incomingThreadTs && params.incomingThreadTs !== params.messageTs) ? "all" : params.replyToMode,
90
- existingId: params.incomingThreadTs,
91
- startId: params.messageTs,
92
- hasReplied: params.hasReplied
93
- });
94
- }
95
- function createSlackReplyDeliveryPlan(params) {
96
- const replyReference = createSlackReplyReferencePlanner({
97
- replyToMode: params.replyToMode,
98
- incomingThreadTs: params.incomingThreadTs,
99
- messageTs: params.messageTs,
100
- hasReplied: params.hasRepliedRef.value,
101
- isThreadReply: params.isThreadReply
102
- });
103
- return {
104
- peekThreadTs: () => replyReference.peek(),
105
- nextThreadTs: () => replyReference.use(),
106
- markSent: () => {
107
- replyReference.markSent();
108
- params.hasRepliedRef.value = replyReference.hasReplied();
109
- }
110
- };
111
- }
112
- async function deliverSlackSlashReplies(params) {
113
- const messages = [];
114
- const chunkLimit = Math.min(params.textLimit, SLACK_TEXT_LIMIT);
115
- for (const payload of params.replies) {
116
- if (payload.isReasoning === true) continue;
117
- const reply = resolveSendableOutboundReplyParts(payload);
118
- const slackBlocks = readSlackReplyBlocks(payload);
119
- const text = reply.hasText && !isSilentReplyText(reply.trimmedText, SILENT_REPLY_TOKEN) ? reply.trimmedText : void 0;
120
- if (slackBlocks?.length && !reply.hasMedia) {
121
- messages.push({
122
- text: text ?? "",
123
- blocks: slackBlocks
124
- });
125
- continue;
126
- }
127
- const combined = [text ?? "", ...reply.mediaUrls].filter(Boolean).join("\n");
128
- if (!combined) continue;
129
- const chunkMode = params.chunkMode ?? "length";
130
- const chunks = (chunkMode === "newline" ? chunkMarkdownTextWithMode(combined, chunkLimit, chunkMode) : [combined]).flatMap((markdown) => markdownToSlackMrkdwnChunks(markdown, chunkLimit, { tableMode: params.tableMode }));
131
- if (!chunks.length && combined) chunks.push(combined);
132
- for (const chunk of chunks) messages.push({ text: chunk });
133
- }
134
- if (messages.length === 0) return;
135
- const responseType = params.ephemeral ? "ephemeral" : "in_channel";
136
- for (const message of messages) await params.respond({
137
- ...message,
138
- response_type: responseType
139
- });
140
- }
141
- //#endregion
142
- export { resolveDeliveredSlackReplyThreadTs as a, readSlackReplyBlocks as i, deliverReplies as n, resolveSlackThreadTs as o, deliverSlackSlashReplies as r, createSlackReplyDeliveryPlan as t };