@openclaw/slack 2026.5.16-beta.3 → 2026.5.16-beta.5

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 (60) hide show
  1. package/dist/account-inspect-api.js +1 -1
  2. package/dist/{account-inspect-BJyQLSkN.js → account-inspect-uMPQ6wIu.js} +1 -1
  3. package/dist/{accounts-yk5K3wQU.js → accounts-Cbw3i3Sa.js} +7 -2
  4. package/dist/accounts.runtime-z3fshUeC.js +2 -0
  5. package/dist/{action-runtime-p39JLqwf.js → action-runtime-DgzkekLM.js} +3 -3
  6. package/dist/action-runtime.runtime-CGEyxfMu.js +2 -0
  7. package/dist/{actions-BCRbHv1Q.js → actions-vFq9T_Kd.js} +3 -3
  8. package/dist/{actions.runtime-CpywQR3D.js → actions.runtime-3XZA8-ot.js} +1 -1
  9. package/dist/api.js +14 -14
  10. package/dist/{approval-handler.runtime-DXrdRbkT.js → approval-handler.runtime-C_hfvcRd.js} +4 -4
  11. package/dist/{blocks-render-BAVfd6r0.js → blocks-render-ziYgM0qF.js} +74 -3
  12. package/dist/{channel-DoFtZGpw.js → channel-D9Wzqu3W.js} +21 -21
  13. package/dist/channel-config-api.js +1 -1
  14. package/dist/channel-plugin-api.js +1 -1
  15. package/dist/{channel.setup-BDL4qn9U.js → channel.setup-C2_IZ9It.js} +4 -4
  16. package/dist/{client-C_IaJbi5.js → client-Dr7pteMR.js} +4 -2
  17. package/dist/{config-schema-CNRousxw.js → config-schema-C8tW0Db0.js} +4 -0
  18. package/dist/contract-api.js +1 -1
  19. package/dist/{directory-config-CMvFiswf.js → directory-config-qDXRTCcs.js} +1 -1
  20. package/dist/directory-contract-api.js +1 -1
  21. package/dist/{directory-live-CZPzpQZF.js → directory-live-BnN4Zawp.js} +2 -2
  22. package/dist/http-routes-api.js +1 -1
  23. package/dist/inbound-contract-test-api.js +2 -2
  24. package/dist/{interactive-replies-BSg5hXhj.js → interactive-replies-B1wwXeVn.js} +10 -1
  25. package/dist/interactive-replies-api.js +1 -1
  26. package/dist/{message-tool-api-C7gc7goF.js → message-tool-api-DRSTJhP8.js} +2 -2
  27. package/dist/message-tool-api.js +1 -1
  28. package/dist/{monitor-Djnd8pGL.js → monitor-beou_Rl6.js} +3 -3
  29. package/dist/{outbound-adapter-CHm6e-0Q.js → outbound-adapter-Dcy9jZv1.js} +26 -6
  30. package/dist/outbound-payload-test-api.js +1 -1
  31. package/dist/{outbound-payload.test-harness-C0CW7_CE.js → outbound-payload.test-harness-B8N8U_AA.js} +1 -1
  32. package/dist/{pipeline.runtime-DJcwOZky.js → pipeline.runtime-COwSmZG5.js} +131 -46
  33. package/dist/{plugin-routes-CRnfsTTX.js → plugin-routes-hrKOqBIn.js} +1 -1
  34. package/dist/{prepare-Or_6_XXl.js → prepare-BjlcuiLr.js} +132 -24
  35. package/dist/{prepare.test-helpers-CU1qB54Q.js → prepare.test-helpers-D807wdul.js} +1 -1
  36. package/dist/{probe-FL4sUJsH.js → probe-DDef8MfR.js} +1 -1
  37. package/dist/{provider-3Y7c15_J.js → provider-C7uiI7hL.js} +120 -9
  38. package/dist/{replies-Fg1T3ZzU.js → replies-ClYRn89v.js} +9 -6
  39. package/dist/{reply-blocks-BFaJ_ejG.js → reply-blocks-DgUdywKU.js} +9 -4
  40. package/dist/{resolve-channels-B_eKaOkE.js → resolve-channels-gx65A1PR.js} +1 -1
  41. package/dist/{resolve-users-BzBAJwvq.js → resolve-users-OknHaiRY.js} +1 -1
  42. package/dist/{room-context-Cd8jFpS-.js → room-context-BI26wVBb.js} +83 -2
  43. package/dist/{runtime-api-B5HGOzX3.js → runtime-api-D3pvtood.js} +1 -1
  44. package/dist/runtime-api.js +12 -12
  45. package/dist/{scopes-Bvg_ZzqZ.js → scopes-vRHcdO0m.js} +1 -1
  46. package/dist/{send-CxXFbqN1.js → send-Br_H5f6H.js} +8 -3
  47. package/dist/send.runtime-Cj0cqt6C.js +2 -0
  48. package/dist/send.runtime-CjgsYARn.js +2 -0
  49. package/dist/{setup-core-B7pou7oe.js → setup-core-BRD65LS_.js} +23 -4
  50. package/dist/setup-plugin-api.js +1 -1
  51. package/dist/{setup-surface-CJS-L7PA.js → setup-surface-DpNyddrJ.js} +5 -5
  52. package/dist/{shared-BEabCPK3.js → shared-DdgQfBV9.js} +6 -6
  53. package/dist/{slash-dispatch.runtime-Cg7uU92H.js → slash-dispatch.runtime-DYm6lWN8.js} +1 -1
  54. package/dist/test-api.js +6 -6
  55. package/openclaw.plugin.json +14 -0
  56. package/package.json +4 -4
  57. package/dist/accounts.runtime-BhbEu1ZK.js +0 -2
  58. package/dist/action-runtime.runtime-BzrPV3EA.js +0 -2
  59. package/dist/send.runtime-BHCPpSj_.js +0 -2
  60. package/dist/send.runtime-CDG5AgU3.js +0 -2
@@ -1,21 +1,21 @@
1
1
  import { o as SLACK_TEXT_LIMIT, s as truncateSlackText } from "./thread-ts-As_dcNbD.js";
2
- import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-BSg5hXhj.js";
2
+ import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-B1wwXeVn.js";
3
3
  import { i as normalizeSlackAllowOwnerEntry } from "./allow-list-nwXs_eCP.js";
4
4
  import { n as resolveSlackNativeStreaming, r as resolveSlackStreamingMode, t as mapStreamingModeToSlackLegacyDraftStreamMode } from "./streaming-compat-eu5Rj5gj.js";
5
- import { a as recordSlackThreadParticipation, s as normalizeSlackOutboundText, t as sendMessageSlack } from "./send-CxXFbqN1.js";
6
- import { b as buildSlackEditTextPayload, f as removeSlackReaction, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage } from "./actions-BCRbHv1Q.js";
5
+ import { a as recordSlackThreadParticipation, s as normalizeSlackOutboundText, t as sendMessageSlack } from "./send-Br_H5f6H.js";
6
+ import { b as buildSlackEditTextPayload, f as removeSlackReaction, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage } from "./actions-vFq9T_Kd.js";
7
7
  import { t as formatSlackError } from "./errors-C_sW0Zgl.js";
8
- import { T as updateLastRoute, a as recordInboundSession, w as resolveStorePath } from "./room-context-Cd8jFpS-.js";
9
- import { r as escapeSlackMrkdwn } from "./provider-3Y7c15_J.js";
10
- import { n as resolveSlackThreadTargets, t as prepareSlackMessage } from "./prepare-Or_6_XXl.js";
11
- import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, t as createSlackReplyDeliveryPlan } from "./replies-Fg1T3ZzU.js";
8
+ import { D as updateLastRoute, E as resolveStorePath, a as recordInboundSession } from "./room-context-BI26wVBb.js";
9
+ import { r as escapeSlackMrkdwn } from "./provider-C7uiI7hL.js";
10
+ import { n as resolveSlackThreadTargets, t as prepareSlackMessage } from "./prepare-BjlcuiLr.js";
11
+ import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, t as createSlackReplyDeliveryPlan } from "./replies-ClYRn89v.js";
12
12
  import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
13
13
  import { resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
14
14
  import { createChannelMessageReplyPipeline, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, resolveChannelMessageSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-message";
15
- import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, resolveChannelProgressDraftLabel, resolveChannelProgressDraftMaxLines, resolveChannelProgressDraftRender, resolveChannelStreamingBlockEnabled, resolveChannelStreamingNativeTransport, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-streaming";
15
+ import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, resolveChannelProgressDraftLabel, resolveChannelProgressDraftMaxLineChars, resolveChannelProgressDraftMaxLines, resolveChannelProgressDraftRender, resolveChannelStreamingBlockEnabled, resolveChannelStreamingNativeTransport, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-streaming";
16
16
  import { danger, logVerbose, shouldLogVerbose, sleep } from "openclaw/plugin-sdk/runtime-env";
17
17
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
18
- import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
18
+ import { buildTtsSupplementMediaPayload, getReplyPayloadTtsSupplement, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
19
19
  import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
20
20
  import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
21
21
  import { resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
@@ -68,6 +68,7 @@ function createSlackDraftStream(params) {
68
68
  accountId: params.accountId,
69
69
  threadTs: params.resolveThreadTs?.(),
70
70
  identity: params.identity,
71
+ ...params.metadata ? { metadata: params.metadata } : {},
71
72
  ...blocks ? { blocks } : {}
72
73
  });
73
74
  streamChannelId = sent.channelId || streamChannelId;
@@ -141,7 +142,7 @@ function createSlackDraftStream(params) {
141
142
  //#endregion
142
143
  //#region extensions/slack/src/progress-blocks.ts
143
144
  const SLACK_PROGRESS_FIELD_MAX = 1800;
144
- const SLACK_PROGRESS_DETAIL_MAX_CHARS = 48;
145
+ const DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS = 120;
145
146
  function field(text) {
146
147
  return {
147
148
  type: "mrkdwn",
@@ -151,26 +152,28 @@ function field(text) {
151
152
  function lineTitle(line) {
152
153
  return `${line.icon ?? "•"} *${escapeSlackMrkdwn(line.label)}*`;
153
154
  }
154
- function compactDetail(value) {
155
+ function compactDetail(value, maxChars) {
155
156
  const normalized = value.replace(/\s+/g, " ").trim();
156
157
  const chars = Array.from(normalized);
157
- if (chars.length <= SLACK_PROGRESS_DETAIL_MAX_CHARS) return normalized;
158
- const keepStart = Math.ceil((SLACK_PROGRESS_DETAIL_MAX_CHARS - 1) * .45);
159
- const keepEnd = SLACK_PROGRESS_DETAIL_MAX_CHARS - keepStart - 1;
158
+ if (chars.length <= maxChars) return normalized;
159
+ if (maxChars <= 1) return "…";
160
+ const keepStart = Math.max(1, Math.ceil((maxChars - 1) * .45));
161
+ const keepEnd = Math.max(1, maxChars - keepStart - 1);
160
162
  return `${chars.slice(0, keepStart).join("").trimEnd()}…${chars.slice(-keepEnd).join("").trimStart()}`;
161
163
  }
162
- function lineDetail(line) {
164
+ function lineDetail(line, maxChars) {
163
165
  const parts = [line.detail, line.status && !line.detail?.includes(line.status) ? line.status : void 0].map((part) => part?.trim()).filter((part) => Boolean(part));
164
- return parts.length ? escapeSlackMrkdwn(compactDetail(parts.join(" · "))) : " ";
166
+ return parts.length ? escapeSlackMrkdwn(compactDetail(parts.join(" · "), maxChars)) : " ";
165
167
  }
166
168
  function buildSlackProgressDraftBlocks(params) {
167
169
  const label = params.label?.trim();
170
+ const maxLineChars = params.maxLineChars && params.maxLineChars > 0 ? Math.floor(params.maxLineChars) : DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS;
168
171
  const blocks = [...label ? [{
169
172
  type: "section",
170
173
  text: field(`*${escapeSlackMrkdwn(label)}*`)
171
174
  }] : [], ...params.lines.map((line) => ({
172
175
  type: "section",
173
- fields: [field(lineTitle(line)), field(lineDetail(line))]
176
+ fields: [field(lineTitle(line)), field(lineDetail(line, maxLineChars))]
174
177
  }))].slice(-50);
175
178
  return blocks.length ? blocks : void 0;
176
179
  }
@@ -317,7 +320,7 @@ async function appendSlackStream(params) {
317
320
  * All other errors propagate unchanged.
318
321
  */
319
322
  async function stopSlackStream(params) {
320
- const { session, text } = params;
323
+ const { session, text, metadata } = params;
321
324
  if (session.stopped) {
322
325
  logVerbose("slack-stream: stream already stopped, ignoring duplicate stop");
323
326
  return;
@@ -326,7 +329,10 @@ async function stopSlackStream(params) {
326
329
  if (text) session.pendingText += text;
327
330
  logVerbose(`slack-stream: stopping stream in ${session.channel} thread=${session.threadTs}${text ? ` (final text: ${text.length} chars)` : ""}`);
328
331
  try {
329
- await session.streamer.stop(text ? { markdown_text: text } : void 0);
332
+ await session.streamer.stop({
333
+ ...text ? { markdown_text: text } : {},
334
+ ...metadata ? { metadata } : {}
335
+ });
330
336
  session.delivered = true;
331
337
  session.pendingText = "";
332
338
  } catch (err) {
@@ -523,7 +529,7 @@ function resolveSlackStreamingThreadHint(params) {
523
529
  }
524
530
  const SLACK_STREAM_RECIPIENT_TEAM_CACHE_MAX = 2e3;
525
531
  const slackStreamRecipientTeamCache = /* @__PURE__ */ new Map();
526
- function buildSlackTurnDeliveryKey(params) {
532
+ function buildSlackEventDeliveryKey(params) {
527
533
  const reply = resolveSendableOutboundReplyParts(params.payload, { text: params.textOverride });
528
534
  const slackBlocks = readSlackReplyBlocks(params.payload);
529
535
  if (!reply.hasContent && !slackBlocks?.length) return null;
@@ -555,15 +561,15 @@ function rememberSlackStreamRecipientTeam(params) {
555
561
  if (oldest) slackStreamRecipientTeamCache.delete(oldest);
556
562
  }
557
563
  }
558
- function createSlackTurnDeliveryTracker() {
564
+ function createSlackEventDeliveryTracker() {
559
565
  const deliveredKeys = /* @__PURE__ */ new Set();
560
566
  return {
561
567
  hasDelivered(params) {
562
- const key = buildSlackTurnDeliveryKey(params);
568
+ const key = buildSlackEventDeliveryKey(params);
563
569
  return key ? deliveredKeys.has(key) : false;
564
570
  },
565
571
  markDelivered(params) {
566
- const key = buildSlackTurnDeliveryKey(params);
572
+ const key = buildSlackEventDeliveryKey(params);
567
573
  if (key) deliveredKeys.add(key);
568
574
  }
569
575
  };
@@ -616,13 +622,14 @@ async function dispatchPreparedSlackMessage(prepared) {
616
622
  normalizeEntry: normalizeSlackAllowOwnerEntry
617
623
  });
618
624
  const senderRecipient = normalizeOptionalLowercaseString(message.user);
619
- if (pinnedMainDmOwner && senderRecipient && normalizeOptionalLowercaseString(pinnedMainDmOwner) !== senderRecipient) logVerbose(`slack: skip main-session last route for ${senderRecipient} (pinned owner ${pinnedMainDmOwner})`);
625
+ const inboundLastRouteSessionKey = resolveInboundLastRouteSessionKey({
626
+ route,
627
+ sessionKey: prepared.ctxPayload.SessionKey ?? route.sessionKey
628
+ });
629
+ if (inboundLastRouteSessionKey === route.mainSessionKey && pinnedMainDmOwner && senderRecipient && normalizeOptionalLowercaseString(pinnedMainDmOwner) !== senderRecipient) logVerbose(`slack: skip main-session last route for ${senderRecipient} (pinned owner ${pinnedMainDmOwner})`);
620
630
  else await updateLastRoute({
621
631
  storePath,
622
- sessionKey: resolveInboundLastRouteSessionKey({
623
- route,
624
- sessionKey: prepared.ctxPayload.SessionKey ?? route.sessionKey
625
- }),
632
+ sessionKey: inboundLastRouteSessionKey,
626
633
  deliveryContext: {
627
634
  channel: "slack",
628
635
  to: `user:${message.user}`,
@@ -632,10 +639,15 @@ async function dispatchPreparedSlackMessage(prepared) {
632
639
  ctx: prepared.ctxPayload
633
640
  });
634
641
  }
635
- const { statusThreadTs, isThreadReply } = resolveSlackThreadTargets({
642
+ const threadTargets = resolveSlackThreadTargets({
636
643
  message,
637
644
  replyToMode: prepared.replyToMode
638
645
  });
646
+ const forcedReplyThreadTs = prepared.forcedReplyThreadTs;
647
+ const slackMessageMetadata = prepared.slackMessageMetadata;
648
+ const statusThreadTs = forcedReplyThreadTs ?? threadTargets.statusThreadTs;
649
+ const isThreadReply = threadTargets.isThreadReply;
650
+ const replyDeliveryMode = forcedReplyThreadTs ? "off" : prepared.replyToMode;
639
651
  const sourceReplyDeliveryMode = resolveChannelMessageSourceReplyDeliveryMode({
640
652
  cfg,
641
653
  ctx: prepared.ctxPayload
@@ -688,11 +700,11 @@ async function dispatchPreparedSlackMessage(prepared) {
688
700
  if (statusReactionsEnabled) statusReactions.setQueued();
689
701
  const hasRepliedRef = { value: false };
690
702
  const replyPlan = createSlackReplyDeliveryPlan({
691
- replyToMode: prepared.replyToMode,
692
- incomingThreadTs,
703
+ replyToMode: replyDeliveryMode,
704
+ incomingThreadTs: forcedReplyThreadTs ?? incomingThreadTs,
693
705
  messageTs,
694
706
  hasRepliedRef,
695
- isThreadReply
707
+ isThreadReply: Boolean(forcedReplyThreadTs) || isThreadReply
696
708
  });
697
709
  const typingTarget = statusThreadTs ? `${message.channel}/${statusThreadTs}` : message.channel;
698
710
  const typingReaction = ctx.typingReaction;
@@ -755,8 +767,8 @@ async function dispatchPreparedSlackMessage(prepared) {
755
767
  streaming: account.config.streaming,
756
768
  nativeStreaming: resolveChannelStreamingNativeTransport(account.config)
757
769
  });
758
- const streamThreadHint = resolveSlackStreamingThreadHint({
759
- replyToMode: prepared.replyToMode,
770
+ const streamThreadHint = forcedReplyThreadTs ?? resolveSlackStreamingThreadHint({
771
+ replyToMode: replyDeliveryMode,
760
772
  incomingThreadTs,
761
773
  messageTs,
762
774
  isThreadReply
@@ -784,7 +796,7 @@ async function dispatchPreparedSlackMessage(prepared) {
784
796
  let usedReplyThreadTs;
785
797
  let usedBlockReplyThreadTs;
786
798
  let observedReplyDelivery = false;
787
- const deliveryTracker = createSlackTurnDeliveryTracker();
799
+ const deliveryTracker = createSlackEventDeliveryTracker();
788
800
  const resolveDeliveryThreadTs = (params) => {
789
801
  const plannedThreadTs = params.forcedThreadTs ? void 0 : replyPlan.nextThreadTs();
790
802
  return params.forcedThreadTs ?? plannedThreadTs ?? (params.kind === "block" ? usedBlockReplyThreadTs : void 0);
@@ -807,8 +819,9 @@ async function dispatchPreparedSlackMessage(prepared) {
807
819
  runtime,
808
820
  textLimit: ctx.textLimit,
809
821
  replyThreadTs: session.threadTs,
810
- replyToMode: prepared.replyToMode,
811
- ...slackIdentity ? { identity: slackIdentity } : {}
822
+ replyToMode: replyDeliveryMode,
823
+ ...slackIdentity ? { identity: slackIdentity } : {},
824
+ ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
812
825
  });
813
826
  markSlackStreamFallbackDelivered(session);
814
827
  observedReplyDelivery = true;
@@ -839,12 +852,13 @@ async function dispatchPreparedSlackMessage(prepared) {
839
852
  runtime,
840
853
  textLimit: ctx.textLimit,
841
854
  replyThreadTs,
842
- replyToMode: prepared.replyToMode,
843
- ...slackIdentity ? { identity: slackIdentity } : {}
855
+ replyToMode: replyDeliveryMode,
856
+ ...slackIdentity ? { identity: slackIdentity } : {},
857
+ ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
844
858
  });
845
859
  observedReplyDelivery = true;
846
860
  const deliveredThreadTs = resolveDeliveredSlackReplyThreadTs({
847
- replyToMode: prepared.replyToMode,
861
+ replyToMode: replyDeliveryMode,
848
862
  payloadReplyToId: params.payload.replyToId,
849
863
  replyThreadTs
850
864
  });
@@ -1000,7 +1014,62 @@ async function dispatchPreparedSlackMessage(prepared) {
1000
1014
  }
1001
1015
  const reply = resolveSendableOutboundReplyParts(payload);
1002
1016
  const slackBlocks = readSlackReplyBlocks(payload);
1003
- const trimmedFinalText = reply.trimmedText;
1017
+ const ttsSupplement = getReplyPayloadTtsSupplement(payload);
1018
+ const trimmedFinalText = (payload.text ?? ttsSupplement?.spokenText ?? "").trim();
1019
+ const shouldRestoreTtsSupplementTextForPreviewFallback = Boolean(ttsSupplement) && ttsSupplement?.visibleTextAlreadyDelivered !== true && Boolean(draftStream) && !draftPreviewCommitted && previewStreamingEnabled && !payload.text?.trim();
1020
+ if (info.kind === "final" && ttsSupplement && draftStream && !draftPreviewCommitted && previewStreamingEnabled && !payload.isError && trimmedFinalText.length > 0) {
1021
+ const channelId = draftStream.channelId();
1022
+ const messageId = draftStream.messageId();
1023
+ if (channelId && messageId) {
1024
+ const finalThreadTs = usedReplyThreadTs ?? statusThreadTs;
1025
+ await draftStream.flush();
1026
+ await draftStream.seal();
1027
+ try {
1028
+ await finalizeSlackPreviewEdit({
1029
+ client: ctx.app.client,
1030
+ token: ctx.botToken,
1031
+ accountId: account.accountId,
1032
+ channelId,
1033
+ messageId,
1034
+ text: normalizeSlackOutboundText(trimmedFinalText),
1035
+ ...slackBlocks?.length ? { blocks: slackBlocks } : {},
1036
+ threadTs: finalThreadTs
1037
+ });
1038
+ } catch (err) {
1039
+ logVerbose(`slack: preview final edit failed; falling back to standard send (${formatSlackError(err)})`);
1040
+ await draftStream.discardPending();
1041
+ let delivered = false;
1042
+ try {
1043
+ await deliverNormally({
1044
+ payload: payload.text?.trim() ? payload : {
1045
+ ...payload,
1046
+ text: trimmedFinalText
1047
+ },
1048
+ kind: info.kind,
1049
+ forcedThreadTs: finalThreadTs
1050
+ });
1051
+ delivered = true;
1052
+ } finally {
1053
+ if (delivered) await draftStream.clear();
1054
+ }
1055
+ return;
1056
+ }
1057
+ draftPreviewCommitted = true;
1058
+ observedReplyDelivery = true;
1059
+ replyPlan.markSent();
1060
+ await deliverNormally({
1061
+ payload: buildTtsSupplementMediaPayload(payload),
1062
+ kind: info.kind,
1063
+ forcedThreadTs: finalThreadTs
1064
+ });
1065
+ deliveryTracker.markDelivered({
1066
+ kind: info.kind,
1067
+ payload,
1068
+ threadTs: finalThreadTs
1069
+ });
1070
+ return;
1071
+ }
1072
+ }
1004
1073
  if ((await deliverWithFinalizableLivePreviewAdapter({
1005
1074
  kind: info.kind,
1006
1075
  payload,
@@ -1020,7 +1089,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1020
1089
  }
1021
1090
  } : void 0,
1022
1091
  buildFinalEdit: () => {
1023
- if (!previewStreamingEnabled || reply.hasMedia || payload.isError || trimmedFinalText.length === 0 && !slackBlocks?.length) return;
1092
+ if (!previewStreamingEnabled || reply.hasMedia && !ttsSupplement || payload.isError || trimmedFinalText.length === 0 && !slackBlocks?.length) return;
1024
1093
  return {
1025
1094
  text: normalizeSlackOutboundText(trimmedFinalText),
1026
1095
  blocks: slackBlocks,
@@ -1043,6 +1112,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1043
1112
  ...edit.blocks?.length ? { blocks: edit.blocks } : {},
1044
1113
  threadTs: edit.threadTs
1045
1114
  });
1115
+ draftPreviewCommitted = true;
1046
1116
  },
1047
1117
  onPreviewFinalized: (_preview) => {
1048
1118
  draftPreviewCommitted = true;
@@ -1055,13 +1125,23 @@ async function dispatchPreparedSlackMessage(prepared) {
1055
1125
  threadTs: finalThreadTs
1056
1126
  });
1057
1127
  },
1128
+ buildSupplementalPayload: () => ttsSupplement ? buildTtsSupplementMediaPayload(payload) : void 0,
1129
+ deliverSupplemental: async (supplementalPayload) => {
1130
+ await deliverNormally({
1131
+ payload: supplementalPayload,
1132
+ kind: info.kind
1133
+ });
1134
+ },
1058
1135
  logPreviewEditFailure: (err) => {
1059
1136
  logVerbose(`slack: preview final edit failed; falling back to standard send (${formatSlackError(err)})`);
1060
1137
  }
1061
1138
  }),
1062
1139
  deliverNormally: async () => {
1063
1140
  await deliverNormally({
1064
- payload,
1141
+ payload: shouldRestoreTtsSupplementTextForPreviewFallback ? {
1142
+ ...payload,
1143
+ text: ttsSupplement?.spokenText
1144
+ } : payload,
1065
1145
  kind: info.kind
1066
1146
  });
1067
1147
  }
@@ -1078,6 +1158,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1078
1158
  token: ctx.botToken,
1079
1159
  accountId: account.accountId,
1080
1160
  identity: slackIdentity,
1161
+ ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
1081
1162
  maxChars: Math.min(ctx.textLimit, SLACK_TEXT_LIMIT),
1082
1163
  resolveThreadTs: () => {
1083
1164
  const ts = replyPlan.peekThreadTs();
@@ -1118,7 +1199,8 @@ async function dispatchPreparedSlackMessage(prepared) {
1118
1199
  entry: account.config,
1119
1200
  seed: progressSeed
1120
1201
  }),
1121
- lines: previewToolProgressLines
1202
+ lines: previewToolProgressLines,
1203
+ maxLineChars: resolveChannelProgressDraftMaxLineChars(account.config)
1122
1204
  })
1123
1205
  } : previewText);
1124
1206
  hasStreamedMessage = true;
@@ -1323,7 +1405,10 @@ async function dispatchPreparedSlackMessage(prepared) {
1323
1405
  let streamFallbackDelivered = false;
1324
1406
  const finalStream = streamSession;
1325
1407
  if (finalStream && !finalStream.stopped) try {
1326
- await stopSlackStream({ session: finalStream });
1408
+ await stopSlackStream({
1409
+ session: finalStream,
1410
+ ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
1411
+ });
1327
1412
  } catch (err) {
1328
1413
  if (err instanceof SlackStreamNotDeliveredError) streamFallbackDelivered = await deliverPendingStreamFallback(finalStream, err);
1329
1414
  else runtime.error?.(danger(`slack-stream: failed to stop stream: ${formatSlackError(err)}`));
@@ -1352,7 +1437,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1352
1437
  if (dispatchError) throw dispatchError;
1353
1438
  const participationThreadTs = usedReplyThreadTs ?? statusThreadTs;
1354
1439
  if (anyReplyDelivered && participationThreadTs) recordSlackThreadParticipation(account.accountId, message.channel, participationThreadTs, { agentId: route.agentId });
1355
- if (!anyReplyDelivered) {
1440
+ if (!anyReplyDelivered && !draftPreviewCommitted) {
1356
1441
  await draftStream?.clear();
1357
1442
  return;
1358
1443
  }
@@ -1,4 +1,4 @@
1
- import { n as listSlackAccountIds, r as mergeSlackAccountConfig } from "./accounts-yk5K3wQU.js";
1
+ import { n as listSlackAccountIds, r as mergeSlackAccountConfig } from "./accounts-Cbw3i3Sa.js";
2
2
  import { r as normalizeSlackWebhookPath, t as handleSlackHttpRequest } from "./registry-D2cWOLZV.js";
3
3
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
4
4
  //#region extensions/slack/src/http/plugin-routes.ts