@openclaw/slack 2026.5.26 → 2026.5.27-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/{action-runtime-aNlZUC5X.js → action-runtime-JVb7KyQs.js} +1 -1
  2. package/dist/action-runtime.runtime-BZa5VDjs.js +2 -0
  3. package/dist/{actions-DZqQvsrH.js → actions-1o9nMIY8.js} +1 -1
  4. package/dist/{actions.runtime-BnyePHn6.js → actions.runtime-qQtdNww-.js} +1 -1
  5. package/dist/api.js +5 -5
  6. package/dist/{approval-handler.runtime-XWqmvXQj.js → approval-handler.runtime-Ba8nwnYi.js} +67 -11
  7. package/dist/{channel-CxtT6zWb.js → channel-Bd7eept6.js} +9 -10
  8. package/dist/channel-plugin-api.js +1 -1
  9. package/dist/{channel.setup-BVEey2Ge.js → channel.setup-WNNVmCm0.js} +1 -1
  10. package/dist/contract-api.js +1 -1
  11. package/dist/{doctor-contract-BVvVoRt-.js → doctor-contract-DIMUXDHO.js} +1 -1
  12. package/dist/doctor-contract-api.js +1 -1
  13. package/dist/inbound-contract-test-api.js +1 -1
  14. package/dist/{monitor-CWa6zIBH.js → monitor-Cn7LUDFL.js} +2 -2
  15. package/dist/{outbound-adapter-C3lEsGEd.js → outbound-adapter-ChuR4_0v.js} +2 -2
  16. package/dist/outbound-payload-test-api.js +1 -1
  17. package/dist/{outbound-payload.test-harness-C57ipELl.js → outbound-payload.test-harness-8MnHFgR1.js} +1 -1
  18. package/dist/{pipeline.runtime-_isT5Hxs.js → pipeline.runtime-BVK8yXw_.js} +248 -247
  19. package/dist/{prepare-fzBRqJsc.js → prepare-Bl5WcC3f.js} +10 -24
  20. package/dist/{provider-HjdBmwDx.js → provider-Bccng2Wp.js} +3 -3
  21. package/dist/{replies-C6KAYDBW.js → replies-jiEDV6lH.js} +2 -2
  22. package/dist/runtime-api.js +5 -5
  23. package/dist/{send-CXGjjgpT.js → send-DvfE8LVm.js} +1 -1
  24. package/dist/send.runtime-Dv6ajTGK.js +2 -0
  25. package/dist/send.runtime-v3TSw9xY.js +2 -0
  26. package/dist/setup-plugin-api.js +1 -1
  27. package/dist/{setup-surface-DZAXKCKa.js → setup-surface-gjRthuZA.js} +1 -1
  28. package/dist/{shared-CqkL-uuZ.js → shared-D8A7iVVH.js} +1 -1
  29. package/dist/{slash-dispatch.runtime-CANn5muk.js → slash-dispatch.runtime-BBhdJHb8.js} +1 -1
  30. package/dist/{streaming-compat-gDv83t5m.js → streaming-compat-DjlgH-Be.js} +1 -1
  31. package/dist/test-api.js +5 -5
  32. package/npm-shrinkwrap.json +3 -3
  33. package/openclaw.plugin.json +2 -0
  34. package/package.json +5 -5
  35. package/dist/action-runtime.runtime-CLBURvAS.js +0 -2
  36. package/dist/send.runtime-B0Gg0Ncx.js +0 -2
  37. package/dist/send.runtime-BbMOcD7_.js +0 -2
@@ -1,29 +1,26 @@
1
1
  import { o as SLACK_TEXT_LIMIT, s as truncateSlackText } from "./thread-ts-ks-O8cEG.js";
2
2
  import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-DtYu4Sf5.js";
3
3
  import { i as normalizeSlackAllowOwnerEntry } from "./allow-list-B1lkGjwl.js";
4
- import { n as resolveSlackNativeStreaming, r as resolveSlackStreamingMode, t as mapStreamingModeToSlackLegacyDraftStreamMode } from "./streaming-compat-gDv83t5m.js";
5
- import { a as recordSlackThreadParticipation, s as normalizeSlackOutboundText, t as sendMessageSlack } from "./send-CXGjjgpT.js";
6
- import { b as buildSlackEditTextPayload, f as removeSlackReaction, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage } from "./actions-DZqQvsrH.js";
4
+ import { n as resolveSlackNativeStreaming, r as resolveSlackStreamingMode, t as mapStreamingModeToSlackLegacyDraftStreamMode } from "./streaming-compat-DjlgH-Be.js";
5
+ import { a as recordSlackThreadParticipation, s as normalizeSlackOutboundText, t as sendMessageSlack } from "./send-DvfE8LVm.js";
6
+ import { b as buildSlackEditTextPayload, f as removeSlackReaction, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage } from "./actions-1o9nMIY8.js";
7
7
  import { t as formatSlackError } from "./errors-CZtmv-h0.js";
8
8
  import { C as resolveStorePath, a as recordInboundSession, w as updateLastRoute } from "./room-context-BmNTBiw5.js";
9
- import { r as escapeSlackMrkdwn } from "./provider-HjdBmwDx.js";
10
- import { n as resolveSlackThreadTargets, t as prepareSlackMessage } from "./prepare-fzBRqJsc.js";
11
- import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, t as createSlackReplyDeliveryPlan } from "./replies-C6KAYDBW.js";
9
+ import { r as escapeSlackMrkdwn } from "./provider-Bccng2Wp.js";
10
+ import { n as resolveSlackThreadTargets, t as prepareSlackMessage } from "./prepare-Bl5WcC3f.js";
11
+ import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, t as createSlackReplyDeliveryPlan } from "./replies-jiEDV6lH.js";
12
12
  import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
13
13
  import { resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
14
- import { createChannelMessageReplyPipeline, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, resolveChannelMessageSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-message";
15
- import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, resolveChannelProgressDraftLabel, resolveChannelProgressDraftMaxLineChars, resolveChannelProgressDraftMaxLines, resolveChannelProgressDraftRender, resolveChannelStreamingBlockEnabled, resolveChannelStreamingNativeTransport, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-streaming";
14
+ import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelMessageReplyPipeline, createChannelProgressDraftGate, createDraftStreamLoop, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftLabel, resolveChannelProgressDraftMaxLineChars, resolveChannelProgressDraftMaxLines, resolveChannelProgressDraftRender, resolveChannelStreamingBlockEnabled, resolveChannelStreamingNativeTransport, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
16
15
  import { danger, logVerbose, shouldLogVerbose, sleep } from "openclaw/plugin-sdk/runtime-env";
17
16
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
18
17
  import { buildTtsSupplementMediaPayload, getReplyPayloadTtsSupplement, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
19
18
  import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
20
19
  import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
20
+ import { dispatchChannelInboundReply, hasVisibleInboundReplyDispatch } from "openclaw/plugin-sdk/channel-inbound";
21
21
  import { resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
22
22
  import { DEFAULT_TIMING, createStatusReactionController, logAckFailure, logTypingFailure, removeAckReactionAfterReply } from "openclaw/plugin-sdk/channel-feedback";
23
- import { hasVisibleInboundReplyDispatch, runPreparedInboundReplyTurn } from "openclaw/plugin-sdk/inbound-reply-dispatch";
24
- import { resolveAgentOutboundIdentity } from "openclaw/plugin-sdk/outbound-runtime";
25
- import { createDraftStreamLoop } from "openclaw/plugin-sdk/channel-lifecycle";
26
- import { createReplyDispatcherWithTyping, dispatchInboundMessage, settleReplyDispatcher } from "openclaw/plugin-sdk/reply-runtime";
23
+ import { dispatchReplyWithBufferedBlockDispatcher } from "openclaw/plugin-sdk/reply-runtime";
27
24
  //#region extensions/slack/src/draft-stream.ts
28
25
  const DEFAULT_THROTTLE_MS = 1e3;
29
26
  function createSlackDraftStream(params) {
@@ -799,6 +796,7 @@ async function dispatchPreparedSlackMessage(prepared) {
799
796
  let usedReplyThreadTs;
800
797
  let usedBlockReplyThreadTs;
801
798
  let observedReplyDelivery = false;
799
+ let observedFinalReplyDelivery = false;
802
800
  const deliveryTracker = createSlackEventDeliveryTracker();
803
801
  const resolveDeliveryThreadTs = (params) => {
804
802
  const plannedThreadTs = params.forcedThreadTs ? void 0 : replyPlan.nextThreadTs();
@@ -861,6 +859,7 @@ async function dispatchPreparedSlackMessage(prepared) {
861
859
  ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
862
860
  });
863
861
  observedReplyDelivery = true;
862
+ if (params.kind === "final") observedFinalReplyDelivery = true;
864
863
  const deliveredThreadTs = resolveDeliveredSlackReplyThreadTs({
865
864
  replyToMode: replyDeliveryMode,
866
865
  payloadReplyToId: params.payload.replyToId,
@@ -877,6 +876,7 @@ async function dispatchPreparedSlackMessage(prepared) {
877
876
  const deliverBufferedStreamFallback = async (params) => {
878
877
  if (!await deliverPendingStreamFallback(params.session, params.err)) return false;
879
878
  replyPlan.markSent();
879
+ if (params.kind === "final") observedFinalReplyDelivery = true;
880
880
  deliveryTracker.markDelivered({
881
881
  kind: params.kind,
882
882
  payload: params.payload,
@@ -934,7 +934,10 @@ async function dispatchPreparedSlackMessage(prepared) {
934
934
  }),
935
935
  userId: message.user
936
936
  });
937
- if (streamSession.delivered) observedReplyDelivery = true;
937
+ if (streamSession.delivered) {
938
+ observedReplyDelivery = true;
939
+ if (params.kind === "final") observedFinalReplyDelivery = true;
940
+ }
938
941
  rememberDeliveredThreadTs(params.kind, streamThreadTs);
939
942
  replyPlan.markSent();
940
943
  deliveryTracker.markDelivered({
@@ -958,7 +961,10 @@ async function dispatchPreparedSlackMessage(prepared) {
958
961
  session: streamSession,
959
962
  text: "\n" + text
960
963
  });
961
- if (streamSession.delivered) observedReplyDelivery = true;
964
+ if (streamSession.delivered) {
965
+ observedReplyDelivery = true;
966
+ if (params.kind === "final") observedFinalReplyDelivery = true;
967
+ }
962
968
  deliveryTracker.markDelivered({
963
969
  kind: params.kind,
964
970
  payload: params.payload,
@@ -1005,158 +1011,157 @@ async function dispatchPreparedSlackMessage(prepared) {
1005
1011
  }
1006
1012
  };
1007
1013
  let draftPreviewCommitted = false;
1008
- const { dispatcher, replyOptions, markDispatchIdle } = createReplyDispatcherWithTyping({
1009
- ...replyPipeline,
1010
- humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
1011
- deliver: async (payload, info) => {
1012
- if (payload.isReasoning === true) return;
1013
- if (useStreaming) {
1014
- await deliverWithStreaming({
1014
+ const deliverSlackPayload = async (payload, info) => {
1015
+ if (payload.isReasoning === true) return { visibleReplySent: false };
1016
+ if (useStreaming) {
1017
+ await deliverWithStreaming({
1018
+ payload,
1019
+ kind: info.kind
1020
+ });
1021
+ return;
1022
+ }
1023
+ const reply = resolveSendableOutboundReplyParts(payload);
1024
+ const slackBlocks = readSlackReplyBlocks(payload);
1025
+ const ttsSupplement = getReplyPayloadTtsSupplement(payload);
1026
+ const trimmedFinalText = (payload.text ?? ttsSupplement?.spokenText ?? "").trim();
1027
+ const shouldRestoreTtsSupplementTextForPreviewFallback = Boolean(ttsSupplement) && ttsSupplement?.visibleTextAlreadyDelivered !== true && Boolean(draftStream) && !draftPreviewCommitted && !observedFinalReplyDelivery && previewStreamingEnabled && !payload.text?.trim();
1028
+ if (info.kind === "final" && ttsSupplement && draftStream && !draftPreviewCommitted && !observedFinalReplyDelivery && previewStreamingEnabled && !payload.isError && trimmedFinalText.length > 0) {
1029
+ const channelId = draftStream.channelId();
1030
+ const messageId = draftStream.messageId();
1031
+ if (channelId && messageId) {
1032
+ const finalThreadTs = usedReplyThreadTs ?? statusThreadTs;
1033
+ await draftStream.flush();
1034
+ await draftStream.seal();
1035
+ try {
1036
+ await finalizeSlackPreviewEdit({
1037
+ client: ctx.app.client,
1038
+ token: ctx.botToken,
1039
+ accountId: account.accountId,
1040
+ channelId,
1041
+ messageId,
1042
+ text: normalizeSlackOutboundText(trimmedFinalText),
1043
+ ...slackBlocks?.length ? { blocks: slackBlocks } : {},
1044
+ threadTs: finalThreadTs
1045
+ });
1046
+ } catch (err) {
1047
+ logVerbose(`slack: preview final edit failed; falling back to standard send (${formatSlackError(err)})`);
1048
+ await draftStream.discardPending();
1049
+ let delivered = false;
1050
+ try {
1051
+ await deliverNormally({
1052
+ payload: payload.text?.trim() ? payload : {
1053
+ ...payload,
1054
+ text: trimmedFinalText
1055
+ },
1056
+ kind: info.kind,
1057
+ forcedThreadTs: finalThreadTs
1058
+ });
1059
+ delivered = true;
1060
+ } finally {
1061
+ if (delivered) await draftStream.clear();
1062
+ }
1063
+ return;
1064
+ }
1065
+ draftPreviewCommitted = true;
1066
+ observedFinalReplyDelivery = true;
1067
+ observedReplyDelivery = true;
1068
+ replyPlan.markSent();
1069
+ await deliverNormally({
1070
+ payload: buildTtsSupplementMediaPayload(payload),
1071
+ kind: info.kind,
1072
+ forcedThreadTs: finalThreadTs
1073
+ });
1074
+ deliveryTracker.markDelivered({
1075
+ kind: info.kind,
1015
1076
  payload,
1016
- kind: info.kind
1077
+ threadTs: finalThreadTs
1017
1078
  });
1018
1079
  return;
1019
1080
  }
1020
- const reply = resolveSendableOutboundReplyParts(payload);
1021
- const slackBlocks = readSlackReplyBlocks(payload);
1022
- const ttsSupplement = getReplyPayloadTtsSupplement(payload);
1023
- const trimmedFinalText = (payload.text ?? ttsSupplement?.spokenText ?? "").trim();
1024
- const shouldRestoreTtsSupplementTextForPreviewFallback = Boolean(ttsSupplement) && ttsSupplement?.visibleTextAlreadyDelivered !== true && Boolean(draftStream) && !draftPreviewCommitted && previewStreamingEnabled && !payload.text?.trim();
1025
- if (info.kind === "final" && ttsSupplement && draftStream && !draftPreviewCommitted && previewStreamingEnabled && !payload.isError && trimmedFinalText.length > 0) {
1026
- const channelId = draftStream.channelId();
1027
- const messageId = draftStream.messageId();
1028
- if (channelId && messageId) {
1029
- const finalThreadTs = usedReplyThreadTs ?? statusThreadTs;
1030
- await draftStream.flush();
1031
- await draftStream.seal();
1032
- try {
1033
- await finalizeSlackPreviewEdit({
1034
- client: ctx.app.client,
1035
- token: ctx.botToken,
1036
- accountId: account.accountId,
1081
+ }
1082
+ if ((await deliverWithFinalizableLivePreviewAdapter({
1083
+ kind: info.kind,
1084
+ payload,
1085
+ adapter: defineFinalizableLivePreviewAdapter({
1086
+ draft: draftStream && !draftPreviewCommitted && !observedFinalReplyDelivery ? {
1087
+ flush: draftStream.flush,
1088
+ clear: draftStream.clear,
1089
+ discardPending: draftStream.discardPending,
1090
+ seal: draftStream.seal,
1091
+ id: () => {
1092
+ const channelId = draftStream.channelId();
1093
+ const messageId = draftStream.messageId();
1094
+ return channelId && messageId ? {
1037
1095
  channelId,
1038
- messageId,
1039
- text: normalizeSlackOutboundText(trimmedFinalText),
1040
- ...slackBlocks?.length ? { blocks: slackBlocks } : {},
1041
- threadTs: finalThreadTs
1042
- });
1043
- } catch (err) {
1044
- logVerbose(`slack: preview final edit failed; falling back to standard send (${formatSlackError(err)})`);
1045
- await draftStream.discardPending();
1046
- let delivered = false;
1047
- try {
1048
- await deliverNormally({
1049
- payload: payload.text?.trim() ? payload : {
1050
- ...payload,
1051
- text: trimmedFinalText
1052
- },
1053
- kind: info.kind,
1054
- forcedThreadTs: finalThreadTs
1055
- });
1056
- delivered = true;
1057
- } finally {
1058
- if (delivered) await draftStream.clear();
1059
- }
1060
- return;
1096
+ messageId
1097
+ } : void 0;
1061
1098
  }
1099
+ } : void 0,
1100
+ buildFinalEdit: () => {
1101
+ if (!previewStreamingEnabled || reply.hasMedia && !ttsSupplement || payload.isError || trimmedFinalText.length === 0 && !slackBlocks?.length) return;
1102
+ return {
1103
+ text: normalizeSlackOutboundText(trimmedFinalText),
1104
+ blocks: slackBlocks,
1105
+ threadTs: usedReplyThreadTs ?? statusThreadTs
1106
+ };
1107
+ },
1108
+ editFinal: async (preview, edit) => {
1109
+ if (deliveryTracker.hasDelivered({
1110
+ kind: info.kind,
1111
+ payload,
1112
+ threadTs: edit.threadTs
1113
+ })) return;
1114
+ await finalizeSlackPreviewEdit({
1115
+ client: ctx.app.client,
1116
+ token: ctx.botToken,
1117
+ accountId: account.accountId,
1118
+ channelId: preview.channelId,
1119
+ messageId: preview.messageId,
1120
+ text: edit.text,
1121
+ ...edit.blocks?.length ? { blocks: edit.blocks } : {},
1122
+ threadTs: edit.threadTs
1123
+ });
1124
+ draftPreviewCommitted = true;
1125
+ observedFinalReplyDelivery = true;
1126
+ },
1127
+ onPreviewFinalized: (_preview) => {
1062
1128
  draftPreviewCommitted = true;
1129
+ observedFinalReplyDelivery = true;
1130
+ const finalThreadTs = usedReplyThreadTs ?? statusThreadTs;
1063
1131
  observedReplyDelivery = true;
1064
1132
  replyPlan.markSent();
1065
- await deliverNormally({
1066
- payload: buildTtsSupplementMediaPayload(payload),
1067
- kind: info.kind,
1068
- forcedThreadTs: finalThreadTs
1069
- });
1070
1133
  deliveryTracker.markDelivered({
1071
1134
  kind: info.kind,
1072
1135
  payload,
1073
1136
  threadTs: finalThreadTs
1074
1137
  });
1075
- return;
1076
- }
1077
- }
1078
- if ((await deliverWithFinalizableLivePreviewAdapter({
1079
- kind: info.kind,
1080
- payload,
1081
- adapter: defineFinalizableLivePreviewAdapter({
1082
- draft: draftStream && !draftPreviewCommitted ? {
1083
- flush: draftStream.flush,
1084
- clear: draftStream.clear,
1085
- discardPending: draftStream.discardPending,
1086
- seal: draftStream.seal,
1087
- id: () => {
1088
- const channelId = draftStream.channelId();
1089
- const messageId = draftStream.messageId();
1090
- return channelId && messageId ? {
1091
- channelId,
1092
- messageId
1093
- } : void 0;
1094
- }
1095
- } : void 0,
1096
- buildFinalEdit: () => {
1097
- if (!previewStreamingEnabled || reply.hasMedia && !ttsSupplement || payload.isError || trimmedFinalText.length === 0 && !slackBlocks?.length) return;
1098
- return {
1099
- text: normalizeSlackOutboundText(trimmedFinalText),
1100
- blocks: slackBlocks,
1101
- threadTs: usedReplyThreadTs ?? statusThreadTs
1102
- };
1103
- },
1104
- editFinal: async (preview, edit) => {
1105
- if (deliveryTracker.hasDelivered({
1106
- kind: info.kind,
1107
- payload,
1108
- threadTs: edit.threadTs
1109
- })) return;
1110
- await finalizeSlackPreviewEdit({
1111
- client: ctx.app.client,
1112
- token: ctx.botToken,
1113
- accountId: account.accountId,
1114
- channelId: preview.channelId,
1115
- messageId: preview.messageId,
1116
- text: edit.text,
1117
- ...edit.blocks?.length ? { blocks: edit.blocks } : {},
1118
- threadTs: edit.threadTs
1119
- });
1120
- draftPreviewCommitted = true;
1121
- },
1122
- onPreviewFinalized: (_preview) => {
1123
- draftPreviewCommitted = true;
1124
- const finalThreadTs = usedReplyThreadTs ?? statusThreadTs;
1125
- observedReplyDelivery = true;
1126
- replyPlan.markSent();
1127
- deliveryTracker.markDelivered({
1128
- kind: info.kind,
1129
- payload,
1130
- threadTs: finalThreadTs
1131
- });
1132
- },
1133
- buildSupplementalPayload: () => ttsSupplement ? buildTtsSupplementMediaPayload(payload) : void 0,
1134
- deliverSupplemental: async (supplementalPayload) => {
1135
- await deliverNormally({
1136
- payload: supplementalPayload,
1137
- kind: info.kind
1138
- });
1139
- },
1140
- logPreviewEditFailure: (err) => {
1141
- logVerbose(`slack: preview final edit failed; falling back to standard send (${formatSlackError(err)})`);
1142
- }
1143
- }),
1144
- deliverNormally: async () => {
1138
+ },
1139
+ buildSupplementalPayload: () => ttsSupplement ? buildTtsSupplementMediaPayload(payload) : void 0,
1140
+ deliverSupplemental: async (supplementalPayload) => {
1145
1141
  await deliverNormally({
1146
- payload: shouldRestoreTtsSupplementTextForPreviewFallback ? {
1147
- ...payload,
1148
- text: ttsSupplement?.spokenText
1149
- } : payload,
1142
+ payload: supplementalPayload,
1150
1143
  kind: info.kind
1151
1144
  });
1145
+ },
1146
+ logPreviewEditFailure: (err) => {
1147
+ logVerbose(`slack: preview final edit failed; falling back to standard send (${formatSlackError(err)})`);
1152
1148
  }
1153
- })).kind === "preview-finalized") return;
1154
- },
1155
- onError: (err, info) => {
1156
- runtime.error?.(danger(`slack ${info.kind} reply failed: ${formatSlackError(err)}`));
1157
- replyPipeline.typingCallbacks?.onIdle?.();
1158
- }
1159
- });
1149
+ }),
1150
+ deliverNormally: async () => {
1151
+ await deliverNormally({
1152
+ payload: shouldRestoreTtsSupplementTextForPreviewFallback ? {
1153
+ ...payload,
1154
+ text: ttsSupplement?.spokenText
1155
+ } : payload,
1156
+ kind: info.kind
1157
+ });
1158
+ }
1159
+ })).kind === "preview-finalized") return;
1160
+ };
1161
+ const onSlackDeliveryError = (err, info) => {
1162
+ runtime.error?.(danger(`slack ${info.kind} reply failed: ${formatSlackError(err)}`));
1163
+ replyPipeline.typingCallbacks?.onIdle?.();
1164
+ };
1160
1165
  const draftStream = shouldUseDraftStream ? createSlackDraftStream({
1161
1166
  target: prepared.replyTarget,
1162
1167
  cfg,
@@ -1285,115 +1290,112 @@ async function dispatchPreparedSlackMessage(prepared) {
1285
1290
  let dispatchError;
1286
1291
  let queuedFinal = false;
1287
1292
  let counts = {};
1288
- let dispatchSettledBeforeStart = false;
1289
1293
  try {
1290
- const turnResult = await runPreparedInboundReplyTurn({
1294
+ const turnResult = await dispatchChannelInboundReply({
1295
+ cfg,
1291
1296
  channel: "slack",
1292
1297
  accountId: route.accountId,
1298
+ agentId: route.agentId,
1293
1299
  routeSessionKey: route.sessionKey,
1294
1300
  storePath: prepared.turn.storePath,
1295
1301
  ctxPayload: prepared.ctxPayload,
1296
1302
  recordInboundSession,
1303
+ dispatchReplyWithBufferedBlockDispatcher,
1304
+ dispatcherOptions: {
1305
+ ...replyPipeline,
1306
+ humanDelay: resolveHumanDelayConfig(cfg, route.agentId)
1307
+ },
1308
+ delivery: {
1309
+ deliver: deliverSlackPayload,
1310
+ onError: onSlackDeliveryError
1311
+ },
1297
1312
  record: prepared.turn.record,
1298
1313
  history: prepared.turn.history,
1299
1314
  botLoopProtection: resolveSlackBotLoopProtection(prepared),
1300
- onPreDispatchFailure: async () => {
1301
- dispatchSettledBeforeStart = true;
1302
- await settleReplyDispatcher({
1303
- dispatcher,
1304
- onSettled: () => markDispatchIdle()
1305
- });
1306
- },
1307
- runDispatch: () => dispatchInboundMessage({
1308
- ctx: prepared.ctxPayload,
1309
- cfg,
1310
- dispatcher,
1311
- replyOptions: {
1312
- ...replyOptions,
1313
- skillFilter: prepared.channelConfig?.skills,
1314
- sourceReplyDeliveryMode,
1315
- hasRepliedRef,
1316
- disableBlockStreaming,
1317
- onModelSelected,
1318
- suppressDefaultToolProgressMessages: suppressDefaultToolProgressMessages ? true : void 0,
1319
- onPartialReply: useStreaming ? void 0 : !previewStreamingEnabled ? void 0 : async (payload) => {
1320
- updateDraftFromPartial(payload.text);
1321
- },
1322
- onAssistantMessageStart: onDraftBoundary,
1323
- onReasoningEnd: onDraftBoundary,
1324
- onReasoningStream: statusReactionsEnabled ? async () => {
1325
- await statusReactions.setThinking();
1326
- } : void 0,
1327
- onToolStart: async (payload) => {
1328
- if (statusReactionsEnabled) await statusReactions.setTool(payload.name);
1329
- await pushPreviewToolProgress(buildChannelProgressDraftLineForEntry(account.config, {
1330
- event: "tool",
1331
- name: payload.name,
1332
- phase: payload.phase,
1333
- args: payload.args
1334
- }, payload.detailMode ? { detailMode: payload.detailMode } : void 0), { toolName: payload.name });
1335
- },
1336
- onItemEvent: async (payload) => {
1337
- await pushPreviewToolProgress(buildChannelProgressDraftLineForEntry(account.config, {
1338
- event: "item",
1339
- itemId: payload.itemId,
1340
- itemKind: payload.kind,
1341
- title: payload.title,
1342
- name: payload.name,
1343
- phase: payload.phase,
1344
- status: payload.status,
1345
- summary: payload.summary,
1346
- progressText: payload.progressText,
1347
- meta: payload.meta
1348
- }));
1349
- },
1350
- onPlanUpdate: async (payload) => {
1351
- if (payload.phase !== "update") return;
1352
- await pushPreviewToolProgress(buildChannelProgressDraftLine({
1353
- event: "plan",
1354
- phase: payload.phase,
1355
- title: payload.title,
1356
- explanation: payload.explanation,
1357
- steps: payload.steps
1358
- }));
1359
- },
1360
- onApprovalEvent: async (payload) => {
1361
- if (payload.phase !== "requested") return;
1362
- await pushPreviewToolProgress(buildChannelProgressDraftLine({
1363
- event: "approval",
1364
- phase: payload.phase,
1365
- title: payload.title,
1366
- command: payload.command,
1367
- reason: payload.reason,
1368
- message: payload.message
1369
- }));
1370
- },
1371
- onCommandOutput: async (payload) => {
1372
- if (payload.phase !== "end") return;
1373
- await pushPreviewToolProgress(buildChannelProgressDraftLine({
1374
- event: "command-output",
1375
- phase: payload.phase,
1376
- title: payload.title,
1377
- name: payload.name,
1378
- status: payload.status,
1379
- exitCode: payload.exitCode
1380
- }));
1381
- },
1382
- onPatchSummary: async (payload) => {
1383
- if (payload.phase !== "end") return;
1384
- await pushPreviewToolProgress(buildChannelProgressDraftLine({
1385
- event: "patch",
1386
- phase: payload.phase,
1387
- title: payload.title,
1388
- name: payload.name,
1389
- added: payload.added,
1390
- modified: payload.modified,
1391
- deleted: payload.deleted,
1392
- summary: payload.summary
1393
- }));
1394
- }
1315
+ replyOptions: {
1316
+ skillFilter: prepared.channelConfig?.skills,
1317
+ sourceReplyDeliveryMode,
1318
+ hasRepliedRef,
1319
+ disableBlockStreaming,
1320
+ onModelSelected,
1321
+ suppressDefaultToolProgressMessages: suppressDefaultToolProgressMessages ? true : void 0,
1322
+ onPartialReply: useStreaming ? void 0 : !previewStreamingEnabled ? void 0 : async (payload) => {
1323
+ updateDraftFromPartial(payload.text);
1324
+ },
1325
+ onAssistantMessageStart: onDraftBoundary,
1326
+ onReasoningEnd: onDraftBoundary,
1327
+ onReasoningStream: statusReactionsEnabled ? async () => {
1328
+ await statusReactions.setThinking();
1329
+ } : void 0,
1330
+ onToolStart: async (payload) => {
1331
+ if (statusReactionsEnabled) await statusReactions.setTool(payload.name);
1332
+ await pushPreviewToolProgress(buildChannelProgressDraftLineForEntry(account.config, {
1333
+ event: "tool",
1334
+ name: payload.name,
1335
+ phase: payload.phase,
1336
+ args: payload.args
1337
+ }, payload.detailMode ? { detailMode: payload.detailMode } : void 0), { toolName: payload.name });
1338
+ },
1339
+ onItemEvent: async (payload) => {
1340
+ await pushPreviewToolProgress(buildChannelProgressDraftLineForEntry(account.config, {
1341
+ event: "item",
1342
+ itemId: payload.itemId,
1343
+ itemKind: payload.kind,
1344
+ title: payload.title,
1345
+ name: payload.name,
1346
+ phase: payload.phase,
1347
+ status: payload.status,
1348
+ summary: payload.summary,
1349
+ progressText: payload.progressText,
1350
+ meta: payload.meta
1351
+ }));
1352
+ },
1353
+ onPlanUpdate: async (payload) => {
1354
+ if (payload.phase !== "update") return;
1355
+ await pushPreviewToolProgress(buildChannelProgressDraftLine({
1356
+ event: "plan",
1357
+ phase: payload.phase,
1358
+ title: payload.title,
1359
+ explanation: payload.explanation,
1360
+ steps: payload.steps
1361
+ }));
1362
+ },
1363
+ onApprovalEvent: async (payload) => {
1364
+ if (payload.phase !== "requested") return;
1365
+ await pushPreviewToolProgress(buildChannelProgressDraftLine({
1366
+ event: "approval",
1367
+ phase: payload.phase,
1368
+ title: payload.title,
1369
+ command: payload.command,
1370
+ reason: payload.reason,
1371
+ message: payload.message
1372
+ }));
1373
+ },
1374
+ onCommandOutput: async (payload) => {
1375
+ if (payload.phase !== "end") return;
1376
+ await pushPreviewToolProgress(buildChannelProgressDraftLine({
1377
+ event: "command-output",
1378
+ phase: payload.phase,
1379
+ title: payload.title,
1380
+ name: payload.name,
1381
+ status: payload.status,
1382
+ exitCode: payload.exitCode
1383
+ }));
1384
+ },
1385
+ onPatchSummary: async (payload) => {
1386
+ if (payload.phase !== "end") return;
1387
+ await pushPreviewToolProgress(buildChannelProgressDraftLine({
1388
+ event: "patch",
1389
+ phase: payload.phase,
1390
+ title: payload.title,
1391
+ name: payload.name,
1392
+ added: payload.added,
1393
+ modified: payload.modified,
1394
+ deleted: payload.deleted,
1395
+ summary: payload.summary
1396
+ }));
1395
1397
  }
1396
- })
1398
+ }
1397
1399
  });
1398
1400
  if (turnResult.dispatched) {
1399
1401
  const result = turnResult.dispatchResult;
@@ -1405,7 +1407,6 @@ async function dispatchPreparedSlackMessage(prepared) {
1405
1407
  } finally {
1406
1408
  progressDraftGate.cancel();
1407
1409
  await draftStream?.discardPending();
1408
- if (!dispatchSettledBeforeStart) markDispatchIdle();
1409
1410
  }
1410
1411
  let streamFallbackDelivered = false;
1411
1412
  const finalStream = streamSession;