@openclaw/slack 2026.9.7 → 2026.10.1-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 (103) hide show
  1. package/dist/.setup/{account-inspect-DWBXpER6.mjs → account-inspect-Cs-7DXXW.mjs} +1 -1
  2. package/dist/.setup/{accounts-D_N9IqNA.mjs → accounts-CDgkV7Cm.mjs} +4 -16
  3. package/dist/.setup/{action-runtime-DfWvuiZd.mjs → action-runtime-DJ1daRAZ.mjs} +5 -7
  4. package/dist/.setup/action-runtime.runtime-CdWL2mLC.mjs +2 -0
  5. package/dist/.setup/{actions-Df0CzcDl.mjs → actions-DDoAJnrH.mjs} +16 -35
  6. package/dist/.setup/{approval-handler.runtime-Ed6Sw5VK.mjs → approval-handler.runtime-BcE_shWc.mjs} +5 -5
  7. package/dist/.setup/{approval-message-updates-BadvtA7R.mjs → approval-message-updates-EgKGmFXx.mjs} +1 -1
  8. package/dist/.setup/{channel-C7bgPSTE.mjs → channel-DgKSu8Iv.mjs} +46 -48
  9. package/dist/.setup/{channel-type-ClOttrZ5.mjs → channel-type-B9zYYzBU.mjs} +4 -4
  10. package/dist/.setup/{channel.setup-Dh2jzAjN.mjs → channel.setup-BYIlwbQ7.mjs} +27 -27
  11. package/dist/.setup/{config-schema-BZCxmHKG.mjs → config-schema-BH0K9-Um.mjs} +4 -0
  12. package/dist/.setup/{directory-config-BOUWH3hR.mjs → directory-config-B_by7zrc.mjs} +2 -2
  13. package/dist/.setup/{directory-live-B2O4B_Ho.mjs → directory-live-C48wazJt.mjs} +3 -3
  14. package/dist/.setup/{doctor-contract-CRN1Z_c7.mjs → doctor-contract-Cd4-VzIM.mjs} +28 -28
  15. package/dist/.setup/{message-tool-api-Cu4lwlGy.mjs → message-tool-api-CcjLPN3v.mjs} +2 -2
  16. package/dist/.setup/{monitor-BPVfkumU.mjs → monitor-BuYBDmm7.mjs} +190 -315
  17. package/dist/.setup/{outbound-adapter-Bv48XUjq.mjs → outbound-adapter-BZFkK-mw.mjs} +6 -17
  18. package/dist/.setup/{pipeline.runtime-B1H63Frf.mjs → pipeline.runtime-CIepKmZt.mjs} +241 -247
  19. package/dist/.setup/{policy-D-jfM174.mjs → policy-KKTqEiDv.mjs} +12 -22
  20. package/dist/.setup/{probe-B2Y9qc_z.mjs → probe-BVlJ3dp0.mjs} +4 -7
  21. package/dist/.setup/{relay-source-BpKdjsll.mjs → relay-source-CC3h6M7y.mjs} +2 -2
  22. package/dist/.setup/{replies-BAnUtr2f.mjs → replies-DHbuthIR.mjs} +12 -26
  23. package/dist/.setup/{reply-blocks-SE1kTE3I.mjs → reply-blocks-CKnO4BSG.mjs} +4 -29
  24. package/dist/.setup/{resolve-channels-XXBn8UU_.mjs → resolve-channels-HR0TgArR.mjs} +2 -2
  25. package/dist/.setup/{resolve-users-DW0ngD03.mjs → resolve-users-BNYBhF0q.mjs} +4 -4
  26. package/dist/.setup/rolldown-runtime-B4FMCO8f.mjs +28 -0
  27. package/dist/.setup/{scopes-CasuDvaa.mjs → scopes-DrDbyUH2.mjs} +1 -1
  28. package/dist/.setup/secret-contract-B-Qm6aQQ.mjs +65 -0
  29. package/dist/.setup/{secret-contract-api-Dp6290Vg.mjs → secret-contract-api-DmUVxIRs.mjs} +1 -1
  30. package/dist/.setup/{security-audit-BqDc-6VL.mjs → security-audit-C1nOGzJa.mjs} +1 -1
  31. package/dist/.setup/{send-BOVLQi3n.mjs → send-CgPI3Zj8.mjs} +22 -53
  32. package/dist/.setup/{session-status-DscajIsd.mjs → session-status-D46GHwOV.mjs} +39 -49
  33. package/dist/.setup/{setup-plugin-api-DTtbTKNk.mjs → setup-plugin-api-ZrHs0N4U.mjs} +1 -1
  34. package/dist/.setup/{setup-surface-Dn7QgWRE.mjs → setup-surface-83lxvE2y.mjs} +10 -13
  35. package/dist/.setup/{shared-qau-uDGl.mjs → shared-VtmYNj-v.mjs} +53 -57
  36. package/dist/.setup/{slash-dispatch.runtime--AEzLVBF.mjs → slash-dispatch.runtime-gBj_uaLM.mjs} +1 -1
  37. package/dist/account-inspect-api.js +1 -1
  38. package/dist/api.js +13 -13
  39. package/dist/channel-config-api.js +1 -1
  40. package/dist/channel-plugin-api.js +1 -1
  41. package/dist/control-ui/2f4b1e827be6b68ced2ea0d8b7da8529a4eafbe72be27ee37e1064bdd530ec10/index.js +1 -0
  42. package/dist/directory-contract-api.js +1 -1
  43. package/dist/doctor-contract-api.js +1 -1
  44. package/dist/message-tool-api.js +1 -1
  45. package/dist/runtime-api.js +10 -10
  46. package/dist/secret-contract-api.js +1 -1
  47. package/dist/security-contract-api.js +1 -1
  48. package/dist/setup-entry.js +2 -2
  49. package/dist/setup-plugin-api.js +1 -1
  50. package/node_modules/undici/README.md +10 -1
  51. package/node_modules/undici/docs/docs/api/Agent.md +8 -8
  52. package/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +9 -6
  53. package/node_modules/undici/docs/docs/api/Dispatcher.md +9 -0
  54. package/node_modules/undici/docs/docs/api/Errors.md +25 -0
  55. package/node_modules/undici/docs/docs/api/Fetch.md +10 -0
  56. package/node_modules/undici/docs/docs/api/Interceptors.md +4 -3
  57. package/node_modules/undici/docs/docs/api/Pool.md +5 -6
  58. package/node_modules/undici/docs/docs/index.md +10 -1
  59. package/node_modules/undici/lib/api/readable.js +1 -2
  60. package/node_modules/undici/lib/core/request.js +23 -2
  61. package/node_modules/undici/lib/core/util.js +0 -39
  62. package/node_modules/undici/lib/dispatcher/client-h1.js +11 -4
  63. package/node_modules/undici/lib/dispatcher/client-h2.js +52 -8
  64. package/node_modules/undici/lib/dispatcher/client.js +6 -1
  65. package/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +21 -6
  66. package/node_modules/undici/lib/dispatcher/pool-base.js +63 -2
  67. package/node_modules/undici/lib/dispatcher/pool.js +90 -7
  68. package/node_modules/undici/lib/dispatcher/proxy-agent.js +16 -3
  69. package/node_modules/undici/lib/handler/deduplication-handler.js +24 -11
  70. package/node_modules/undici/lib/handler/redirect-handler.js +5 -0
  71. package/node_modules/undici/lib/handler/retry-handler.js +72 -13
  72. package/node_modules/undici/lib/interceptor/cache.js +4 -1
  73. package/node_modules/undici/lib/interceptor/decompress.js +236 -69
  74. package/node_modules/undici/lib/web/cache/cache.js +17 -8
  75. package/node_modules/undici/lib/web/cache/cachestorage.js +11 -5
  76. package/node_modules/undici/lib/web/cookies/index.js +19 -7
  77. package/node_modules/undici/lib/web/cookies/util.js +3 -1
  78. package/node_modules/undici/lib/web/eventsource/eventsource.js +25 -1
  79. package/node_modules/undici/lib/web/fetch/body.js +22 -15
  80. package/node_modules/undici/lib/web/fetch/data-url.js +6 -6
  81. package/node_modules/undici/lib/web/fetch/formdata.js +30 -36
  82. package/node_modules/undici/lib/web/fetch/headers.js +34 -31
  83. package/node_modules/undici/lib/web/fetch/index.js +46 -25
  84. package/node_modules/undici/lib/web/fetch/request.js +74 -79
  85. package/node_modules/undici/lib/web/fetch/response.js +43 -44
  86. package/node_modules/undici/lib/web/fetch/util.js +7 -7
  87. package/node_modules/undici/lib/web/infra/index.js +16 -1
  88. package/node_modules/undici/lib/web/webidl/index.js +2 -14
  89. package/node_modules/undici/lib/web/websocket/connection.js +33 -12
  90. package/node_modules/undici/lib/web/websocket/events.js +43 -26
  91. package/node_modules/undici/lib/web/websocket/receiver.js +2 -0
  92. package/node_modules/undici/lib/web/websocket/stream/websocketerror.js +13 -10
  93. package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +19 -4
  94. package/node_modules/undici/lib/web/websocket/websocket.js +44 -41
  95. package/node_modules/undici/package.json +1 -1
  96. package/node_modules/undici/types/diagnostics-channel.d.ts +2 -1
  97. package/node_modules/undici/types/interceptors.d.ts +1 -1
  98. package/node_modules/undici/types/webidl.d.ts +13 -7
  99. package/openclaw.plugin.json +4 -1
  100. package/package.json +14 -6
  101. package/dist/.setup/action-runtime.runtime-Ck4bAWOX.mjs +0 -2
  102. package/dist/.setup/secret-contract-BaZcu6P_.mjs +0 -82
  103. package/dist/.setup/streaming-compat-C5Wut626.mjs +0 -62
@@ -1,18 +1,18 @@
1
- import { i as __reExport, n as resolveSlackStreamingMode, r as __exportAll, t as resolveSlackNativeStreaming } from "./streaming-compat-C5Wut626.mjs";
2
- import { f as resolveSlackReplyToMode } from "./accounts-D_N9IqNA.mjs";
1
+ import { n as __reExport, t as __exportAll } from "./rolldown-runtime-B4FMCO8f.mjs";
2
+ import { f as resolveSlackReplyToMode } from "./accounts-CDgkV7Cm.mjs";
3
3
  import { n as formatSlackTarget } from "./target-parsing-FFE26kOV.mjs";
4
- import { A as countSlackTextUtf8Bytes, C as buildSlackBlocksFallbackText, E as SLACK_EDIT_TEXT_MAX_BYTES, H as SLACK_SESSION_LINK_ACTION_ID, M as truncateSlackTextByUtf8Bytes, Q as normalizeSlackOutboundText, c as qualifySlackRoutePeerId, j as truncateSlackText, k as SLACK_TEXT_LIMIT, lt as escapeSlackMrkdwn, v as hasSlackNativeDataBlock, w as buildSlackCompleteBlocksFallbackText } from "./session-status-DscajIsd.mjs";
5
- import { D as normalizeSlackAllowOwnerEntry, j as resolveSlackUserAllowed, o as formatSlackError, v as readLruMapEntry, y as writeLruMapEntry } from "./probe-B2Y9qc_z.mjs";
6
- import { a as prepareSlackReply, l as resolveSlackReplyRenderPlan, s as resolveSlackReplyBlocks } from "./reply-blocks-SE1kTE3I.mjs";
4
+ import { A as countSlackTextUtf8Bytes, C as buildSlackBlocksFallbackText, E as SLACK_EDIT_TEXT_MAX_BYTES, M as truncateSlackTextByUtf8Bytes, Y as normalizeSlackOutboundText, c as qualifySlackRoutePeerId, j as truncateSlackText, k as SLACK_TEXT_LIMIT, ot as escapeSlackMrkdwn, v as hasSlackNativeDataBlock, w as buildSlackCompleteBlocksFallbackText, z as SLACK_SESSION_LINK_ACTION_ID } from "./session-status-D46GHwOV.mjs";
5
+ import { D as normalizeSlackAllowOwnerEntry, j as resolveSlackUserAllowed, o as formatSlackError, v as readLruMapEntry, y as writeLruMapEntry } from "./probe-BVlJ3dp0.mjs";
6
+ import { a as prepareSlackReply, l as resolveSlackReplyRenderPlan, s as resolveSlackReplyBlocks } from "./reply-blocks-CKnO4BSG.mjs";
7
7
  import { n as resolveSlackReplyThreadTs } from "./thread-ts-Cvt7WD6B.mjs";
8
- import { C as hasSlackTableBlock, D as buildSlackEditTextPayload, E as resolveSlackMessageText$1, T as resolveSlackBlocksText, a as editSlackRenderedMessage, b as formatSlackFileReference, d as reactSlackMessage, i as editSlackMessage, m as removeSlackReaction, n as deleteSlackMessage, w as isSlackUnfurlAttachment, x as formatSlackFileReferenceList } from "./actions-Df0CzcDl.mjs";
9
- import { c as hasSlackThreadParticipationWithPersistence, i as clearSlackThreadFailureNotice, l as recordSlackThreadFailureNotice, o as hasSlackThreadFailureNotice, p as buildSlackNativeDataDeliveryPlan, s as hasSlackThreadParticipation, t as sendMessageSlack, u as recordSlackThreadParticipation } from "./send-BOVLQi3n.mjs";
10
- import { n as resolveSlackChannelConfig } from "./policy-D-jfM174.mjs";
11
- import { C as resolveSlackChatType, E as stripSlackMentionsForCommandDetection, S as normalizeSlackChannelType, T as readSlackAssistantThreadContext, _ as appendSlackStream, b as stopSlackStream, c as resolveSlackRoutingContext, g as SlackStreamNotDeliveredError, h as resolveSlackEffectiveAllowFrom, i as resolveSlackRoomContextHints, l as registerSlackSessionRun, m as resolveSlackCommandIngress, p as authorizeSlackBotRoomMessage, r as trackSlackDraftMessage, s as authorizeSlackDirectMessage, u as resolveSlackThreadContext, v as markSlackStreamFallbackDelivered, w as buildSlackAssistantThreadMetadata, y as startSlackStream } from "./monitor-BPVfkumU.mjs";
12
- import { a as emitSlackMessageSentHooks, i as sanitizeSlackMonitorReplyPayload, n as deliverReplies, t as createSlackReplyDeliveryPlan } from "./replies-BAnUtr2f.mjs";
8
+ import { C as hasSlackTableBlock, D as buildSlackEditTextPayload, E as resolveSlackMessageText$1, T as resolveSlackBlocksText, a as editSlackRenderedMessage, b as formatSlackFileReference, d as reactSlackMessage, i as editSlackMessage, m as removeSlackReaction, n as deleteSlackMessage, w as isSlackUnfurlAttachment, x as formatSlackFileReferenceList } from "./actions-DDoAJnrH.mjs";
9
+ import { c as hasSlackThreadParticipationWithPersistence, i as clearSlackThreadFailureNotice, l as recordSlackThreadFailureNotice, m as buildSlackNativeDataDeliveryPlan, o as hasSlackThreadFailureNotice, p as hasSlackMessageIdentity, s as hasSlackThreadParticipation, t as sendMessageSlack, u as recordSlackThreadParticipation } from "./send-CgPI3Zj8.mjs";
10
+ import { n as resolveSlackChannelConfig } from "./policy-KKTqEiDv.mjs";
11
+ import { C as resolveSlackChatType, E as stripSlackMentionsForCommandDetection, S as normalizeSlackChannelType, T as readSlackAssistantThreadContext, _ as appendSlackStream, b as stopSlackStream, c as resolveSlackRoutingContext, g as SlackStreamNotDeliveredError, h as resolveSlackEffectiveAllowFrom, i as resolveSlackRoomContextHints, l as registerSlackSessionRun, m as resolveSlackCommandIngress, p as authorizeSlackBotRoomMessage, r as trackSlackDraftMessage, s as authorizeSlackDirectMessage, u as resolveSlackThreadContext, v as markSlackStreamFallbackDelivered, w as buildSlackAssistantThreadMetadata, y as startSlackStream } from "./monitor-BuYBDmm7.mjs";
12
+ import { a as emitSlackMessageSentHooks, i as sanitizeSlackMonitorReplyPayload, n as deliverReplies, t as createSlackReplyDeliveryPlan } from "./replies-DHbuthIR.mjs";
13
13
  import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, readNonBlankString } from "openclaw/plugin-sdk/string-coerce-runtime";
14
14
  import { resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
15
- import { buildChannelProgressDraftLine, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createChannelProgressWorkCounter, createDraftStreamLoop, createFinalizableDraftLifecycle, createLivePreviewLifecycle, createMessageReceiptFromOutboundResults, formatChannelProgressDraftDiffStat, formatPlanChecklistLines, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
15
+ import { buildChannelProgressDraftLine, compactChannelProgressDraftLine, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createChannelProgressWorkCounter, createDraftStreamLoop, createFinalizableDraftLifecycle, createLivePreviewLifecycle, createMessageReceiptFromOutboundResults, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages, selectPlanChecklistSteps } from "openclaw/plugin-sdk/channel-outbound";
16
16
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
17
17
  import { danger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
18
18
  import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
@@ -29,7 +29,7 @@ import fs from "node:fs/promises";
29
29
  import { runTasksWithConcurrency } from "openclaw/plugin-sdk/concurrency-runtime";
30
30
  import { formatErrorMessage, toErrorObject } from "openclaw/plugin-sdk/error-runtime";
31
31
  import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist, shouldIncludeSupplementalContext } from "openclaw/plugin-sdk/security-runtime";
32
- import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
32
+ import { mimeTypeFromFilePath, normalizeMimeType } from "openclaw/plugin-sdk/media-mime";
33
33
  import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
34
34
  import { getSessionEntry, readSessionUpdatedAt, resolveChannelResetConfig, resolveStorePath, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
35
35
  import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
@@ -80,10 +80,6 @@ function isSlackStreamingEnabled(params) {
80
80
  if (params.mode === "progress") return params.nativeStreaming && params.nativeProgressTaskCards === true;
81
81
  return false;
82
82
  }
83
- function resolveSlackDisableBlockStreaming(params) {
84
- if (params.useStreaming || params.shouldUseDraftStream) return true;
85
- return typeof params.blockStreamingEnabled === "boolean" ? !params.blockStreamingEnabled : void 0;
86
- }
87
83
  function resolveExplicitSlackProgressTitle(entry) {
88
84
  const label = resolveChannelProgressDraftConfig(entry).label;
89
85
  if (typeof label !== "string") return;
@@ -135,14 +131,6 @@ function createSlackEventDeliveryTracker() {
135
131
  }
136
132
  };
137
133
  }
138
- function shouldUseStreaming(params) {
139
- if (!params.streamingEnabled) return false;
140
- if (!params.threadTs) {
141
- logVerbose("slack-stream: streaming disabled — no reply thread target available");
142
- return false;
143
- }
144
- return true;
145
- }
146
134
  async function resolveSlackStreamRecipientTeamId(params) {
147
135
  const cacheKey = params.fallbackTeamId && params.userId ? `${params.fallbackTeamId}:${params.userId}` : void 0;
148
136
  const cache = cacheKey ? getSlackStreamRecipientTeamCache(params.client) : void 0;
@@ -173,7 +161,6 @@ function createSlackDraftStream(params) {
173
161
  const edit = params.edit ?? editSlackMessage;
174
162
  const remove = params.remove ?? deleteSlackMessage;
175
163
  let streamMessage;
176
- let streamGeneration = 0;
177
164
  let cleanupGeneration = -1;
178
165
  let untrackConversationBoundary;
179
166
  let lastVisibleUpdate;
@@ -190,7 +177,6 @@ function createSlackDraftStream(params) {
190
177
  lastSentKey = "";
191
178
  };
192
179
  const sendOrEditStreamMessage = async (pending) => {
193
- if (streamState.stopped) return;
194
180
  const update = normalizeUpdate(pending);
195
181
  const trimmed = update.text.trimEnd();
196
182
  if (!trimmed) return;
@@ -204,7 +190,7 @@ function createSlackDraftStream(params) {
204
190
  const sentKey = `${trimmed}\n${blocks ? JSON.stringify(blocks) : ""}`;
205
191
  if (sentKey === lastSentKey) return;
206
192
  lastSentKey = sentKey;
207
- const generation = streamGeneration;
193
+ const generation = lifecycle.generation;
208
194
  try {
209
195
  if (streamMessage) {
210
196
  const message = streamMessage;
@@ -222,58 +208,54 @@ function createSlackDraftStream(params) {
222
208
  return;
223
209
  }
224
210
  const threadTs = params.resolveThreadTs?.();
225
- const pendingBoundary = params.conversationChannelId ? trackSlackDraftMessage({
211
+ const trackMessage = (channelId, messageTs) => trackSlackDraftMessage({
226
212
  accountId: params.accountId,
227
213
  teamId: params.eventScope?.teamId,
228
- channelId: params.conversationChannelId,
214
+ channelId,
229
215
  threadTs,
216
+ messageTs,
230
217
  onInterveningMessage: () => forceNewMessage("human")
231
- }) : void 0;
232
- untrackConversationBoundary = pendingBoundary?.stop;
233
- const sent = await send(params.target, trimmed, {
234
- cfg: params.cfg,
235
- token: params.token,
236
- accountId: params.accountId,
237
- threadTs,
238
- identity: params.identity,
239
- eventScope: params.eventScope,
240
- ...params.metadata ? { metadata: params.metadata } : {},
241
- ...blocks ? { blocks } : {}
242
218
  });
243
- const sentMessage = {
244
- channelId: sent.channelId,
245
- messageId: sent.messageId,
246
- generation
247
- };
248
- if (generation !== streamGeneration) {
249
- if (sent.channelId && sent.messageId) lifecycle.retire(sentMessage, { defer: true });
250
- return;
251
- }
252
- if (!sent.channelId || !sent.messageId) {
253
- stopTrackingConversationBoundary();
254
- streamState.stopped = true;
255
- params.warn?.("slack stream preview stopped (missing identifiers from sendMessage)");
256
- return;
257
- }
258
- streamMessage = sentMessage;
259
- lastVisibleUpdate = {
260
- text: trimmed,
261
- ...blocks ? { blocks } : {}
262
- };
263
- if (pendingBoundary && params.conversationChannelId === streamMessage.channelId) pendingBoundary.setMessageTs(streamMessage.messageId);
264
- else {
265
- stopTrackingConversationBoundary();
266
- untrackConversationBoundary = trackSlackDraftMessage({
219
+ const pendingBoundary = params.conversationChannelId ? trackMessage(params.conversationChannelId) : void 0;
220
+ untrackConversationBoundary = pendingBoundary?.stop;
221
+ await lifecycle.createMessage(async () => {
222
+ const sent = await send(params.target, trimmed, {
223
+ cfg: params.cfg,
224
+ token: params.token,
267
225
  accountId: params.accountId,
268
- teamId: params.eventScope?.teamId,
269
- channelId: streamMessage.channelId,
270
226
  threadTs,
271
- messageTs: streamMessage.messageId,
272
- onInterveningMessage: () => forceNewMessage("human")
273
- }).stop;
274
- }
227
+ identity: params.identity,
228
+ eventScope: params.eventScope,
229
+ ...params.metadata ? { metadata: params.metadata } : {},
230
+ ...blocks ? { blocks } : {}
231
+ });
232
+ return sent.channelId && sent.messageId ? {
233
+ channelId: sent.channelId,
234
+ messageId: sent.messageId,
235
+ generation
236
+ } : void 0;
237
+ }, (sentMessage) => {
238
+ if (!sentMessage) {
239
+ stopTrackingConversationBoundary();
240
+ streamState.stopped = true;
241
+ params.warn?.("slack stream preview stopped (missing identifiers from sendMessage)");
242
+ return false;
243
+ }
244
+ const { channelId, messageId } = sentMessage;
245
+ streamMessage = sentMessage;
246
+ lastVisibleUpdate = {
247
+ text: trimmed,
248
+ ...blocks ? { blocks } : {}
249
+ };
250
+ if (pendingBoundary && params.conversationChannelId === channelId) pendingBoundary.setMessageTs(messageId);
251
+ else {
252
+ stopTrackingConversationBoundary();
253
+ untrackConversationBoundary = trackMessage(channelId, messageId).stop;
254
+ }
255
+ return true;
256
+ }, { defer: true });
275
257
  } catch (err) {
276
- if (generation === streamGeneration) {
258
+ if (generation === lifecycle.generation) {
277
259
  stopTrackingConversationBoundary();
278
260
  streamState.stopped = true;
279
261
  }
@@ -308,24 +290,24 @@ function createSlackDraftStream(params) {
308
290
  untrackConversationBoundary = void 0;
309
291
  };
310
292
  const dropDetachedMessages = () => {
311
- const generation = streamGeneration;
293
+ const generation = lifecycle.generation;
312
294
  return lifecycle.cleanupPending(() => {
313
295
  preserveHumanReplies = false;
314
296
  cleanupGeneration = generation;
315
297
  });
316
298
  };
317
299
  const discardPendingAndStopTracking = async () => {
318
- const generation = streamGeneration;
300
+ const generation = lifecycle.generation;
319
301
  await discardPending();
320
- if (generation === streamGeneration) stopTrackingConversationBoundary();
302
+ if (generation === lifecycle.generation) stopTrackingConversationBoundary();
321
303
  };
322
304
  const clear = async (options) => {
323
- const generation = streamGeneration;
305
+ const generation = lifecycle.generation;
324
306
  let clearingMessage = streamMessage;
325
307
  await lifecycle.clearWithStop(async () => {
326
- if (generation === streamGeneration) {
308
+ if (generation === lifecycle.generation) {
327
309
  await discardPendingAndStopTracking();
328
- if (generation === streamGeneration) {
310
+ if (generation === lifecycle.generation) {
329
311
  clearingMessage = streamMessage;
330
312
  clearMessageId();
331
313
  }
@@ -341,17 +323,15 @@ function createSlackDraftStream(params) {
341
323
  };
342
324
  const forceNewMessage = (reason = "turn") => {
343
325
  stopTrackingConversationBoundary();
344
- if (reason === "turn") {
345
- streamState.stopped = false;
346
- streamGeneration += 1;
347
- }
348
- streamState.final = false;
349
326
  if (streamMessage && !streamMessage.retained) {
350
327
  streamMessage.detachedByHuman = reason === "human";
351
328
  lifecycle.retire(streamMessage, { defer: true });
352
329
  }
353
- clearMessageId();
354
- loop.resetPending();
330
+ if (reason === "turn") lifecycle.reset("discard", "keep");
331
+ else {
332
+ streamState.final = false;
333
+ lifecycle.resetMessage("keep");
334
+ }
355
335
  };
356
336
  const finalizeMessage = async (messageId, editFinal) => {
357
337
  const currentMessage = streamMessage;
@@ -395,8 +375,8 @@ function createSlackDraftStream(params) {
395
375
  //#region extensions/slack/src/stream-mode.ts
396
376
  function resolveSlackStreamingConfig(params) {
397
377
  return {
398
- mode: resolveSlackStreamingMode(params),
399
- nativeStreaming: resolveSlackNativeStreaming(params)
378
+ mode: params.streaming?.mode ?? "progress",
379
+ nativeStreaming: params.streaming?.nativeTransport ?? true
400
380
  };
401
381
  }
402
382
  function applyAppendOnlyStreamUpdate(params) {
@@ -512,12 +492,6 @@ function renderProgressCardText(text, enclosingStyle) {
512
492
  function lineDetailParts(line) {
513
493
  return [line.detail, line.status && line.status !== "completed" && !line.detail?.includes(line.status) ? line.status : void 0].map((part) => part?.trim()).filter((part) => Boolean(part));
514
494
  }
515
- function activityLineDetail(line, maxChars) {
516
- const detail = lineDetailParts(line).join(" · ");
517
- if (detail) return escapeSlackMrkdwn(compactDetail(detail, maxChars));
518
- const text = line.text.replace(/^(?:🧠|💬)\s+/u, "").trim();
519
- return line.kind === "item" && !line.toolName && text && text !== line.label ? renderProgressCardText(compactDetail(text, maxChars)) : "—";
520
- }
521
495
  function lineTaskTitle(line) {
522
496
  const label = line.label.replace(/\s+/g, " ").trim() || line.toolName || line.kind || "Update";
523
497
  const fallback = line.text.replace(/\s+/g, " ").trim();
@@ -660,53 +634,63 @@ function joinRecentProgressRows(rows) {
660
634
  }
661
635
  function buildActivityText(lines, maxLineChars) {
662
636
  return joinRecentProgressRows(lines.slice(-50).map((line) => {
663
- return `${`${line.icon ?? "•"} *${renderProgressCardText(line.label, "bold")}*`} — ${activityLineDetail(line, maxLineChars)}`;
637
+ if (line.kind === "item" && !line.toolName) return `_${renderProgressCardText(compactDetail(line.text, maxLineChars), "italic")}_`;
638
+ const detail = compactDetail(lineDetailParts(line).join(" · "), maxLineChars);
639
+ return escapeSlackMrkdwn(detail ? `${line.label} — ${detail}` : line.label);
664
640
  }));
665
641
  }
666
642
  function buildSlackProgressCardBlocks(params) {
667
643
  const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS);
668
- const planLines = formatPlanChecklistLines(params.plan ?? [], {
669
- maxLines: 50,
670
- maxLineChars
671
- });
672
- const narration = (typeof params.narration === "string" ? [{ text: params.narration }] : params.narration ?? []).map(({ text, format }) => ({
673
- text: text.replace(/\s+/g, " ").trim(),
674
- format
675
- })).filter((part) => part.text);
676
- const diffStat = formatChannelProgressDraftDiffStat(params.diffStat);
644
+ const selected = selectPlanChecklistSteps(params.detailed ? params.plan ?? [] : [], { maxLines: 50 });
645
+ const planLines = [...selected.summary ? [selected.summary] : [], ...selected.steps.map(({ step, status }) => `${status === "completed" ? "✓" : status === "in_progress" ? "▸" : "▢"} ${step}`)].map((line) => compactChannelProgressDraftLine(line, maxLineChars));
646
+ const narration = /* @__PURE__ */ new Map();
647
+ for (const part of params.narration ?? []) {
648
+ const text = part.text.replace(/\s+/g, " ").trim();
649
+ if (text) narration.set(text, part.format ?? narration.get(text));
650
+ }
651
+ const lines = params.lines.filter((line) => line.kind === "item" && !line.toolName ? !narration.has(line.text.replace(/^_(.*)_$/gmu, "$1").replace(/\s+/g, " ").trim()) : params.detailed || line.kind === "approval");
652
+ const { diffStat: diff, toolCalls, elapsedSeconds } = params;
653
+ const diffStat = diff && (diff.files || diff.added || diff.removed) ? [
654
+ `${diff.files} ${diff.files === 1 ? "file" : "files"}`,
655
+ ...diff.added > 0 ? [`+${diff.added}`] : [],
656
+ ...diff.removed > 0 ? [`−${diff.removed}`] : []
657
+ ].join(" ") : void 0;
677
658
  const workingFooter = [
678
- ...params.toolCalls && params.toolCalls > 0 ? [`🛠️ ${params.toolCalls} tools`] : [],
659
+ ...toolCalls && toolCalls > 0 ? [`${toolCalls} ${toolCalls === 1 ? "tool" : "tools"}`] : [],
679
660
  ...diffStat ? [diffStat] : [],
680
- ...params.elapsedSeconds && params.elapsedSeconds > 0 ? [`⏱ ${params.elapsedSeconds}s`] : []
661
+ ...elapsedSeconds && elapsedSeconds > 0 ? [`${elapsedSeconds}s`] : []
681
662
  ].join(" · ");
682
663
  const footer = params.state === "working" ? workingFooter : diffStat;
683
- const icon = params.state === "working" ? "🔄" : params.state === "success" ? "✅" : "❌";
684
664
  const finalStatus = params.state === "working" ? void 0 : params.state === "success" ? "complete" : "error";
685
- const attention = params.lines.flatMap((line) => {
665
+ const attention = lines.flatMap((line) => {
686
666
  const title = formatProgressAttentionTitle(line, finalStatus);
687
667
  return title === void 0 ? [] : [escapeSlackMrkdwn(title)];
688
668
  });
689
669
  const sections = [
690
670
  planLines.map((line) => renderProgressCardText(line)).join("\n"),
691
- buildActivityText(params.lines.filter((line) => line.kind !== "approval" && lineTaskStatus(line) !== "error"), maxLineChars),
671
+ buildActivityText(lines.filter((line) => line.kind !== "approval" && lineTaskStatus(line) !== "error"), maxLineChars),
692
672
  joinRecentProgressRows(attention)
693
673
  ];
694
- const title = params.title?.trim() || (params.state === "working" ? "Working" : params.state === "success" ? "Done" : "Failed");
674
+ const title = params.title?.trim();
695
675
  const blocks = [
696
- params.titleFormat === "plain" ? progressTextSection({
697
- text: `${icon} ${title}`,
676
+ ...params.state === "error" ? [progressTextSection({
677
+ text: "Failed",
698
678
  format: "plain"
699
- }) : {
700
- type: "section",
701
- text: field(`${icon} *${renderProgressCardText(title, "bold")}*`)
702
- },
703
- ...narration.map((part) => progressTextSection(part, "italic")),
679
+ })] : [],
680
+ ...title ? [progressTextSection({
681
+ text: title,
682
+ format: "plain"
683
+ })] : [],
684
+ ...[...narration].map(([text, format]) => progressTextSection({
685
+ text,
686
+ format
687
+ }, "italic")),
704
688
  ...sections.filter(Boolean).map((text) => ({
705
689
  type: "section",
706
690
  text: field(text)
707
691
  }))
708
692
  ];
709
- if (footer) blocks.push({
693
+ if (params.detailed && footer) blocks.push({
710
694
  type: "context",
711
695
  elements: [field(footer)]
712
696
  });
@@ -989,54 +973,63 @@ function createSlackDraftProgressCardRuntime(params) {
989
973
  }];
990
974
  };
991
975
  const resolvePresentation = (snapshot, state) => {
992
- const title = params.explicitTitle ?? snapshot.statusHeadline;
993
- const titleFormat = params.explicitTitle ? void 0 : snapshot.statusHeadlineFormat;
994
- const narration = params.explicitTitle ? snapshot.statusHeadlineFormat === "plain" || snapshot.planExplanationFormat === "plain" ? [...snapshot.statusHeadline && (snapshot.statusHeadline !== snapshot.planExplanation || snapshot.statusHeadlineFormat !== snapshot.planExplanationFormat) ? [{
995
- text: snapshot.statusHeadline,
976
+ const narration = [{
977
+ text: snapshot.statusHeadline ?? "",
996
978
  format: snapshot.statusHeadlineFormat
997
- }] : [], ...snapshot.planExplanation ? [{
998
- text: snapshot.planExplanation,
999
- format: snapshot.planExplanationFormat
1000
- }] : []] : combineProgressHeadlineAndExplanation(snapshot.statusHeadline, snapshot.planExplanation) : snapshot.planExplanation && (snapshot.planExplanation !== title || snapshot.planExplanationFormat !== titleFormat) ? [{
1001
- text: snapshot.planExplanation,
979
+ }, {
980
+ text: snapshot.planExplanation ?? "",
1002
981
  format: snapshot.planExplanationFormat
1003
- }] : void 0;
1004
- const workCounter = state === "working" ? params.progressWorkCounter : void 0;
1005
- const sessionLinks = state === "working" ? [] : resolveSessionLinks();
982
+ }];
1006
983
  return buildSlackProgressCardBlocks({
1007
984
  state,
1008
- title,
1009
- titleFormat,
985
+ detailed: params.detailed,
986
+ title: params.explicitTitle,
1010
987
  narration,
1011
988
  plan: snapshot.plan,
1012
989
  lines: resolveStructuredProgressLines(snapshot.lines),
1013
990
  maxLineChars: params.maxLineChars,
1014
991
  diffStat: snapshot.diffStat,
1015
- toolCalls: workCounter?.toolCalls,
1016
- elapsedSeconds: workCounter?.elapsedSeconds,
1017
- sessionLinks
992
+ toolCalls: params.progressWorkCounter?.toolCalls,
993
+ elapsedSeconds: params.progressWorkCounter?.elapsedSeconds,
994
+ sessionLinks: state === "working" ? [] : resolveSessionLinks()
1018
995
  });
1019
996
  };
1020
- const finalize = async (status, snapshot = params.getSnapshot()) => {
997
+ const resolveCardText = (blocks) => truncateSlackText(buildSlackCompleteBlocksFallbackText(blocks), Math.min(ctx.textLimit, SLACK_TEXT_LIMIT));
998
+ const finalize = async (status, options = {}) => {
1021
999
  if (!params.draftStream || !params.enabled) return false;
1022
1000
  await params.draftStream.dropDetachedMessages();
1023
1001
  const terminalStatus = finalStatus === "error" || status === "error" ? "error" : "success";
1024
1002
  if (finalStatus === terminalStatus) return true;
1025
1003
  await params.draftStream.flush();
1026
- const channelId = params.draftStream.channelId();
1027
- const messageId = params.draftStream.messageId();
1004
+ const snapshot = options.snapshot ?? params.getSnapshot();
1005
+ let channelId = params.draftStream.channelId();
1006
+ let messageId = params.draftStream.messageId();
1007
+ const blocks = resolvePresentation(snapshot, terminalStatus);
1008
+ if ((!channelId || !messageId) && terminalStatus === "error" && options.postIfMissing) {
1009
+ params.draftStream.update({
1010
+ text: resolveCardText(blocks),
1011
+ blocks
1012
+ });
1013
+ await params.draftStream.flush();
1014
+ channelId = params.draftStream.channelId();
1015
+ messageId = params.draftStream.messageId();
1016
+ }
1028
1017
  if (!channelId || !messageId) return false;
1018
+ if (blocks.length === 0) {
1019
+ await params.draftStream.clear();
1020
+ finalStatus = terminalStatus;
1021
+ return true;
1022
+ }
1029
1023
  await params.draftStream.seal();
1030
1024
  try {
1031
1025
  const finalized = await params.draftStream.finalizeMessage(messageId, async () => {
1032
- const blocks = resolvePresentation(snapshot, terminalStatus);
1033
1026
  await finalizeSlackPreviewEdit({
1034
1027
  client: slackClient,
1035
1028
  token: ctx.botToken,
1036
1029
  accountId: account.accountId,
1037
1030
  channelId,
1038
1031
  messageId,
1039
- text: buildSlackCompleteBlocksFallbackText(blocks),
1032
+ text: resolveCardText(blocks),
1040
1033
  blocks,
1041
1034
  threadTs: params.getThreadTs()
1042
1035
  });
@@ -1054,6 +1047,7 @@ function createSlackDraftProgressCardRuntime(params) {
1054
1047
  for (const session of sessions) if (!visibleWorkSessions.has(session.sessionKey)) visibleWorkSessions.set(session.sessionKey, session);
1055
1048
  },
1056
1049
  resolvePresentation,
1050
+ resolveCardText,
1057
1051
  finalize,
1058
1052
  get hasTerminalized() {
1059
1053
  return finalStatus !== void 0;
@@ -1172,6 +1166,7 @@ function createSlackProgressRuntime(runtimeParams) {
1172
1166
  let appendRenderedText = "";
1173
1167
  let appendSourceText = "";
1174
1168
  let nativeProgressCompletionSent = false;
1169
+ let cardPostRequested = false;
1175
1170
  let nativeProgressTerminalStatus = "complete";
1176
1171
  let nativeNarrationRenderedText = "";
1177
1172
  let nativeNarrationSourceText = "";
@@ -1197,6 +1192,7 @@ function createSlackProgressRuntime(runtimeParams) {
1197
1192
  },
1198
1193
  draftStream,
1199
1194
  enabled: useDraftProgressCard,
1195
+ detailed: previewToolProgressEnabled,
1200
1196
  progressWorkCounter: previewToolProgressEnabled ? progressWorkCounter : void 0,
1201
1197
  explicitTitle: explicitProgressTitle,
1202
1198
  maxLineChars: progressDraftMaxLineChars,
@@ -1325,6 +1321,7 @@ function createSlackProgressRuntime(runtimeParams) {
1325
1321
  });
1326
1322
  const resetProgressTurnState = () => {
1327
1323
  progressWorkCounter.reset();
1324
+ cardPostRequested = false;
1328
1325
  nativeNarrationRenderedText = "";
1329
1326
  nativeNarrationSourceText = "";
1330
1327
  };
@@ -1351,17 +1348,27 @@ function createSlackProgressRuntime(runtimeParams) {
1351
1348
  const snapshot = options.snapshot;
1352
1349
  const latestLine = snapshot.lines.at(-1);
1353
1350
  if (preambleOnlyProgress && typeof latestLine === "object" && latestLine.complete === false) return false;
1351
+ const cardBlocks = useDraftProgressCard ? progressCard.resolvePresentation(snapshot, "working") : void 0;
1352
+ if (cardBlocks?.length === 0) {
1353
+ if (cardPostRequested || draftStream.messageId()) {
1354
+ cardPostRequested = false;
1355
+ await draftStream.clear();
1356
+ draftStream.forceNewMessage();
1357
+ }
1358
+ return false;
1359
+ }
1354
1360
  draftStream.update(preambleOnlyProgress ? {
1355
1361
  text: previewText,
1356
1362
  allowNewMessage: typeof latestLine !== "object" || latestLine.complete !== false,
1357
1363
  ...snapshot.preparedBlocks ? { blocks: buildSlackProgressTextBlocks(snapshot.preparedBlocks) } : {}
1358
- } : useDraftProgressCard ? {
1359
- text: previewText,
1360
- blocks: progressCard.resolvePresentation(snapshot, "working")
1364
+ } : cardBlocks ? {
1365
+ text: progressCard.resolveCardText(cardBlocks),
1366
+ blocks: cardBlocks
1361
1367
  } : snapshot.preparedBlocks ? {
1362
1368
  text: previewText,
1363
1369
  blocks: buildSlackProgressTextBlocks(snapshot.preparedBlocks)
1364
1370
  } : previewText);
1371
+ if (cardBlocks) cardPostRequested = true;
1365
1372
  if (options?.flush) await draftStream.flush();
1366
1373
  return Boolean(draftStream.messageId() && draftStream.channelId());
1367
1374
  },
@@ -1370,6 +1377,7 @@ function createSlackProgressRuntime(runtimeParams) {
1370
1377
  nativeUpdates.update(true);
1371
1378
  await nativeUpdates.flush();
1372
1379
  } else {
1380
+ cardPostRequested = false;
1373
1381
  await draftStream?.clear();
1374
1382
  draftStream?.forceNewMessage();
1375
1383
  }
@@ -1513,7 +1521,7 @@ function createSlackProgressRuntime(runtimeParams) {
1513
1521
  if (!progressDraft.beginNewTurn(options)) return false;
1514
1522
  if (useNativeProgressStreaming) await finishNativeProgressTurn(completionChunks);
1515
1523
  else {
1516
- await progressCard.finalize("success", priorSnapshot);
1524
+ await progressCard.finalize("success", { snapshot: priorSnapshot });
1517
1525
  await previewLifecycle.cleanup();
1518
1526
  draftStream?.forceNewMessage();
1519
1527
  await dropDetachedProgressCards();
@@ -1572,7 +1580,6 @@ function createSlackProgressRuntime(runtimeParams) {
1572
1580
  useDraftProgressCard,
1573
1581
  useNativeProgressStreaming,
1574
1582
  progressDraftActive,
1575
- previewToolProgressEnabled,
1576
1583
  preambleOnlyProgress,
1577
1584
  suppressDefaultToolProgressMessages,
1578
1585
  progressDraft,
@@ -1805,15 +1812,14 @@ async function createSlackDispatchSetup(prepared) {
1805
1812
  const modifyingHooksRegistered = (hookRunner?.hasHooks("reply_payload_sending") ?? false) || (hookRunner?.hasHooks("message_sending") ?? false);
1806
1813
  const allowPreHookProviderStreaming = !prepared.ctxPayload.GroupThread && !modifyingHooksRegistered;
1807
1814
  const previewStreamingEnabled = allowPreHookProviderStreaming && !sourceRepliesAreToolOnly && !quietProgress && slackStreaming.mode !== "off";
1808
- const hasSlackCustomIdentity = Boolean(slackIdentity?.username || slackIdentity?.iconUrl || slackIdentity?.iconEmoji);
1809
- const useStreaming = shouldUseStreaming({
1810
- streamingEnabled: !prepared.ctxPayload.GroupThread && !sourceRepliesAreToolOnly && (allowPreHookProviderStreaming || slackStreaming.mode !== "progress") && isSlackStreamingEnabled({
1811
- mode: slackStreaming.mode,
1812
- nativeStreaming: slackStreaming.nativeStreaming,
1813
- nativeProgressTaskCards: resolveSlackNativeProgressTaskCards(account.config, slackProgressStyle)
1814
- }),
1815
- threadTs: streamThreadHint
1815
+ const hasSlackCustomIdentity = hasSlackMessageIdentity(slackIdentity);
1816
+ const streamingEnabled = !prepared.ctxPayload.GroupThread && !sourceRepliesAreToolOnly && (allowPreHookProviderStreaming || slackStreaming.mode !== "progress") && isSlackStreamingEnabled({
1817
+ mode: slackStreaming.mode,
1818
+ nativeStreaming: slackStreaming.nativeStreaming,
1819
+ nativeProgressTaskCards: resolveSlackNativeProgressTaskCards(account.config, slackProgressStyle)
1816
1820
  });
1821
+ const useStreaming = streamingEnabled && Boolean(streamThreadHint);
1822
+ if (streamingEnabled && !streamThreadHint) logVerbose("slack-stream: streaming disabled — no reply thread target available");
1817
1823
  const shouldUseDraftStream = previewStreamingEnabled && !useStreaming;
1818
1824
  const blockStreamingEnabled = resolveChannelStreamingBlockEnabled(account.config);
1819
1825
  return {
@@ -1852,11 +1858,7 @@ async function createSlackDispatchSetup(prepared) {
1852
1858
  previewStreamingEnabled,
1853
1859
  hasSlackCustomIdentity,
1854
1860
  shouldUseDraftStream,
1855
- disableBlockStreaming: sourceRepliesAreToolOnly || quietProgress ? true : resolveSlackDisableBlockStreaming({
1856
- useStreaming,
1857
- shouldUseDraftStream,
1858
- blockStreamingEnabled
1859
- }),
1861
+ disableBlockStreaming: sourceRepliesAreToolOnly || quietProgress || useStreaming || shouldUseDraftStream ? true : typeof blockStreamingEnabled === "boolean" ? !blockStreamingEnabled : void 0,
1860
1862
  useStreaming
1861
1863
  };
1862
1864
  }
@@ -2473,7 +2475,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2473
2475
  }
2474
2476
  await previewLifecycle.cleanup({ failed: Boolean(dispatchError || agentRunFailed) });
2475
2477
  if (pendingFailureNotice && anyReplyDelivered) recordSlackThreadFailureNotice(pendingFailureNotice);
2476
- if (dispatchError || agentRunFailed) await progress.finalizeDraftProgressCard("error");
2478
+ if (dispatchError || agentRunFailed) await progress.finalizeDraftProgressCard("error", { postIfMissing: !anyReplyDelivered });
2477
2479
  await progress.dropDetachedProgressCards();
2478
2480
  if (statusReactionsEnabled) {
2479
2481
  if (dispatchError || agentRunFailed) {
@@ -2786,7 +2788,7 @@ const SLACK_MENTION_RESOLUTION_CONCURRENCY = 4;
2786
2788
  const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
2787
2789
  const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
2788
2790
  const MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS = 2e3;
2789
- const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-Df0CzcDl.mjs").then((n) => n._));
2791
+ const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-DDoAJnrH.mjs").then((n) => n._));
2790
2792
  function formatSlackUnavailableMedia(params) {
2791
2793
  let fileReferences = params.files?.map((file) => `${formatSlackFileReference(file)} unavailable (${file.reason})`).join(", ");
2792
2794
  if (fileReferences && fileReferences.length > MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS) fileReferences = `${truncateUtf16Safe(fileReferences, MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS)}; … (file references truncated)`;
@@ -2915,9 +2917,13 @@ async function resolveSlackDmHistoryContext(params) {
2915
2917
  inclusive: true
2916
2918
  } : {},
2917
2919
  limit: maxMessages + 1
2918
- })).messages ?? []).filter((message) => {
2919
- if (params.currentMessageTs && message.ts === params.currentMessageTs) return false;
2920
- return Boolean(normalizeOptionalString(message.text));
2920
+ })).messages ?? []).flatMap((message) => {
2921
+ if (params.currentMessageTs && message.ts === params.currentMessageTs) return [];
2922
+ const body = normalizeOptionalString(message.text);
2923
+ return body ? [{
2924
+ message,
2925
+ body
2926
+ }] : [];
2921
2927
  }).slice(0, maxMessages).toReversed();
2922
2928
  if (messages.length === 0) return {
2923
2929
  body: void 0,
@@ -2933,9 +2939,7 @@ async function resolveSlackDmHistoryContext(params) {
2933
2939
  };
2934
2940
  const entries = [];
2935
2941
  const formatted = [];
2936
- for (const message of messages) {
2937
- const body = normalizeOptionalString(message.text);
2938
- if (!body) continue;
2942
+ for (const { message, body } of messages) {
2939
2943
  const isCurrentBot = params.ctx.botUserId && message.user === params.ctx.botUserId || params.ctx.botId && message.bot_id === params.ctx.botId;
2940
2944
  const role = isCurrentBot || message.bot_id ? "assistant" : "user";
2941
2945
  const sender = `${isCurrentBot ? "Assistant" : message.user ? await resolveUserLabel(message.user) : normalizeOptionalString(message.username) ?? (message.bot_id ? "Bot" : "Unknown")} (${role})`;
@@ -2955,8 +2959,8 @@ async function resolveSlackDmHistoryContext(params) {
2955
2959
  }));
2956
2960
  }
2957
2961
  return {
2958
- body: formatted.length > 0 ? formatted.join("\n\n") : void 0,
2959
- inboundHistory: entries.length > 0 ? entries : void 0
2962
+ body: formatted.join("\n\n"),
2963
+ inboundHistory: entries
2960
2964
  };
2961
2965
  } catch (err) {
2962
2966
  logVerbose(`slack: failed to fetch DM history for channel ${params.channelId}: ${formatErrorMessage(err)}`);
@@ -3073,8 +3077,7 @@ async function resolveSlackRoomHistory(params) {
3073
3077
  }
3074
3078
  }
3075
3079
  function isSlackImageFileCandidate(file) {
3076
- if ((file.mimetype?.split(";")[0]?.trim().toLowerCase())?.startsWith("image/")) return true;
3077
- return Boolean(mimeTypeFromFilePath(file.name)?.startsWith("image/"));
3080
+ return Boolean(normalizeMimeType(file.mimetype)?.startsWith("image/") || mimeTypeFromFilePath(file.name)?.startsWith("image/"));
3078
3081
  }
3079
3082
  function sliceSlackImageFileCandidates(files, limit) {
3080
3083
  if (limit <= 0 || !files?.length) return [];
@@ -3098,27 +3101,20 @@ function sliceSlackHistoryAttachmentCandidates(attachments, limit) {
3098
3101
  }
3099
3102
  return out;
3100
3103
  }
3101
- function buildSlackHistoryMediaCandidateMessage(message, maxAttachments) {
3102
- const files = sliceSlackImageFileCandidates(message.files, maxAttachments);
3103
- const attachments = sliceSlackHistoryAttachmentCandidates(message.attachments, Math.max(0, maxAttachments - files.length));
3104
- if (files.length === 0 && attachments.length === 0) return null;
3105
- return {
3106
- message: {
3107
- ...message,
3108
- files,
3109
- attachments
3110
- },
3111
- attempted: files.length + attachments.reduce((count, attachment) => count + (normalizeOptionalString(attachment.image_url) ? 1 : 0) + (attachment.files?.length ?? 0), 0)
3112
- };
3113
- }
3114
3104
  async function resolveSlackHistoryMedia(params) {
3115
- const candidate = buildSlackHistoryMediaCandidateMessage(params.message, params.maxAttachments);
3116
- if (!candidate) return {
3105
+ const files = sliceSlackImageFileCandidates(params.message.files, params.maxAttachments);
3106
+ const attachments = sliceSlackHistoryAttachmentCandidates(params.message.attachments, Math.max(0, params.maxAttachments - files.length));
3107
+ if (files.length === 0 && attachments.length === 0) return {
3117
3108
  media: [],
3118
3109
  attempted: 0
3119
3110
  };
3111
+ const attempted = files.length + attachments.reduce((count, attachment) => count + (normalizeOptionalString(attachment.image_url) ? 1 : 0) + (attachment.files?.length ?? 0), 0);
3120
3112
  const content = await resolveSlackMessageContent({
3121
- message: candidate.message,
3113
+ message: {
3114
+ ...params.message,
3115
+ files,
3116
+ attachments
3117
+ },
3122
3118
  isThreadReply: false,
3123
3119
  threadStarter: null,
3124
3120
  isBotMessage: Boolean(params.message.bot_id),
@@ -3135,12 +3131,35 @@ async function resolveSlackHistoryMedia(params) {
3135
3131
  kind: "image",
3136
3132
  messageId: params.message.ts
3137
3133
  }),
3138
- attempted: candidate.attempted
3134
+ attempted
3135
+ };
3136
+ }
3137
+ //#endregion
3138
+ //#region extensions/slack/src/monitor/message-handler/prepare-session-presentation.ts
3139
+ function resolveSlackGroupSessionSubject(params) {
3140
+ const channelName = normalizeOptionalString(params.channelName);
3141
+ const workspaceName = normalizeOptionalString(params.installationIdentity?.kind === "workspace" && params.installationIdentity.teamId === params.workspaceId ? params.installationIdentity.teamName : void 0);
3142
+ if (channelName && workspaceName) return `${workspaceName} #${channelName}`;
3143
+ return `Slack Channel (Workspace ID: ${params.workspaceId}, Channel ID: ${params.channelId})`;
3144
+ }
3145
+ function resolveSlackConversationLink(params) {
3146
+ if (params.existingLink) return params.existingLink;
3147
+ const teamId = normalizeOptionalString(params.teamId);
3148
+ let apiHost = "";
3149
+ try {
3150
+ apiHost = params.slackApiUrl ? new URL(params.slackApiUrl).hostname.toLowerCase() : "";
3151
+ } catch {}
3152
+ const url = new URL(`https://${apiHost === "slack-gov.com" ? "slack-gov.com" : "slack.com"}/app_redirect`);
3153
+ url.searchParams.set("channel", params.channelId);
3154
+ if (teamId) url.searchParams.set("team", teamId);
3155
+ return {
3156
+ url: url.href,
3157
+ label: "Slack"
3139
3158
  };
3140
3159
  }
3141
3160
  //#endregion
3142
3161
  //#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
3143
- const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-Df0CzcDl.mjs").then((n) => n._));
3162
+ const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-DDoAJnrH.mjs").then((n) => n._));
3144
3163
  const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
3145
3164
  function formatSlackThreadLabelSnippet(text) {
3146
3165
  return truncateUtf16Safe(text.replace(/\s+/g, " "), 80);
@@ -3488,12 +3507,6 @@ const SLACK_USER_MENTION_RE = /<@([^>|]+)(?:\|[^>]+)?>/g;
3488
3507
  const SLACK_SUBTEAM_MENTION_RE = /<!subteam\^([^>|]+)(?:\|[^>]+)?>/g;
3489
3508
  const SLACK_SUBTEAM_MENTION_MARKER = "<!subteam^";
3490
3509
  const SLACK_CHANNEL_ACCESS_DOCS_URL = "https://docs.openclaw.ai/channels/slack#access-control-and-routing";
3491
- function resolveSlackGroupSessionSubject(params) {
3492
- const channelName = normalizeOptionalString(params.channelName);
3493
- const workspaceName = normalizeOptionalString(params.workspaceName);
3494
- if (channelName && workspaceName) return `${workspaceName} #${channelName}`;
3495
- return `Slack Channel (Workspace ID: ${params.workspaceId}, Channel ID: ${params.channelId})`;
3496
- }
3497
3510
  function mergeSlackAssistantThreadContext(primary, fallback) {
3498
3511
  if (!primary) return fallback;
3499
3512
  if (!fallback) return primary;
@@ -3608,7 +3621,7 @@ async function resolveSlackConversationContext(params) {
3608
3621
  channelInfo = await ctx.resolveChannelName(message.channel, params.eventScope);
3609
3622
  resolvedChannelType = normalizeSlackChannelType(message.channel_type ?? channelInfo.type ?? ctx.recallSlackChannelType(message.channel, params.eventScope), message.channel);
3610
3623
  }
3611
- const channelName = channelInfo?.name;
3624
+ const channelName = channelInfo.name;
3612
3625
  const isDirectMessage = resolvedChannelType === "im";
3613
3626
  const isGroupDm = resolvedChannelType === "mpim";
3614
3627
  const isRoom = resolvedChannelType === "channel" || resolvedChannelType === "group";
@@ -3811,10 +3824,7 @@ async function prepareSlackMessage(params) {
3811
3824
  ctx,
3812
3825
  account,
3813
3826
  message,
3814
- isDirectMessage,
3815
- isGroupDm,
3816
- isRoom,
3817
- isRoomish,
3827
+ chatType,
3818
3828
  channelConfig,
3819
3829
  seedTopLevelRoomThread,
3820
3830
  assistantThreadTs: assistantThreadContext?.threadTs,
@@ -3877,16 +3887,7 @@ async function prepareSlackMessage(params) {
3877
3887
  return drop("configured-binding-unavailable");
3878
3888
  }
3879
3889
  }
3880
- const senderNameForAuthPromise = ctx.allowNameMatching ? resolveSenderName().then((name) => ({
3881
- ok: true,
3882
- name
3883
- }), (error) => ({
3884
- ok: false,
3885
- error
3886
- })) : Promise.resolve({
3887
- ok: true,
3888
- name: void 0
3889
- });
3890
+ const senderNameForAuthPromise = ctx.allowNameMatching ? resolveSenderName() : void 0;
3890
3891
  let threadStarterPromise;
3891
3892
  const getThreadStarter = () => {
3892
3893
  threadStarterPromise ??= isThreadReply && threadTs ? resolveSlackThreadStarter({
@@ -3910,14 +3911,7 @@ async function prepareSlackMessage(params) {
3910
3911
  preloadedMedia
3911
3912
  }));
3912
3913
  let preloadedDirectMedia;
3913
- let messageContentPromise;
3914
- const getMessageContent = () => {
3915
- messageContentPromise ??= resolveMessageContent(message, preloadedDirectMedia);
3916
- return messageContentPromise;
3917
- };
3918
- const senderNameForAuthResult = await senderNameForAuthPromise;
3919
- if (!senderNameForAuthResult.ok) throw senderNameForAuthResult.error;
3920
- const senderNameForAuth = senderNameForAuthResult.name;
3914
+ const senderNameForAuth = await senderNameForAuthPromise;
3921
3915
  const allowTextCommands = shouldHandleTextCommands({
3922
3916
  cfg,
3923
3917
  surface: "slack"
@@ -4111,7 +4105,7 @@ async function prepareSlackMessage(params) {
4111
4105
  hasAbortRequest
4112
4106
  });
4113
4107
  const threadStarter = await getThreadStarter();
4114
- const resolvedMessageContent = await getMessageContent();
4108
+ const resolvedMessageContent = await resolveMessageContent(message, preloadedDirectMedia);
4115
4109
  opts.abortSignal?.throwIfAborted();
4116
4110
  if (opts.isRuntimePolicyCurrent?.() === false) return drop("final-route-denied");
4117
4111
  if (!resolvedMessageContent) return drop("empty-content");
@@ -4156,16 +4150,14 @@ async function prepareSlackMessage(params) {
4156
4150
  }) : statusReactionsWillHandle ? Promise.resolve(true) : null;
4157
4151
  const roomLabel = channelName ? `#${channelName}` : `#${message.channel}`;
4158
4152
  const workspaceId = opts.eventScope?.teamId || ctx.teamId;
4159
- const workspaceName = ctx.installationIdentity?.kind === "workspace" && ctx.installationIdentity.teamId === workspaceId ? ctx.installationIdentity.teamName : void 0;
4160
4153
  const groupSessionSubject = isRoomish ? resolveSlackGroupSessionSubject({
4161
4154
  channelId: message.channel,
4162
4155
  channelName,
4163
4156
  workspaceId,
4164
- workspaceName
4157
+ installationIdentity: ctx.installationIdentity
4165
4158
  }) : void 0;
4166
4159
  const senderName = await resolveSenderName();
4167
4160
  const conversationAvatar = isDirectMessage && message.user ? ctx.resolveUserAvatar(message.user, opts.eventScope) : void 0;
4168
- const preview = truncateUtf16Safe(bodyForAgent.replace(/\s+/g, " "), 160);
4169
4161
  const inboundLabel = isDirectMessage ? `Slack DM from ${senderName}` : `Slack message in ${roomLabel} from ${senderName}`;
4170
4162
  const slackFrom = isDirectMessage ? `slack:${message.user}` : isRoom ? `slack:channel:${message.channel}` : `slack:group:${message.channel}`;
4171
4163
  enqueueRoutedSystemEvent(inboundLabel, {
@@ -4196,6 +4188,12 @@ async function prepareSlackMessage(params) {
4196
4188
  storePath,
4197
4189
  sessionKey
4198
4190
  });
4191
+ const conversationLink = resolveSlackConversationLink({
4192
+ channelId: message.channel,
4193
+ teamId: opts.eventScope?.teamId ?? ctx.teamId,
4194
+ slackApiUrl: slackClient.slackApiUrl,
4195
+ existingLink: sessionEntry?.conversationLink
4196
+ });
4199
4197
  const previousTimestamp = sessionEntry?.updatedAt;
4200
4198
  const excludedMessageIds = new Set(opts.sourceMessageIds);
4201
4199
  if (message.ts) excludedMessageIds.add(message.ts);
@@ -4350,7 +4348,8 @@ async function prepareSlackMessage(params) {
4350
4348
  spaceId: opts.eventScope?.teamId || ctx.teamId || void 0,
4351
4349
  threadId: boundMessageThreadId,
4352
4350
  nativeChannelId: message.channel,
4353
- avatar: conversationAvatar
4351
+ avatar: conversationAvatar,
4352
+ link: conversationLink
4354
4353
  },
4355
4354
  route: {
4356
4355
  ...route,
@@ -4459,39 +4458,34 @@ async function prepareSlackMessage(params) {
4459
4458
  replyTarget,
4460
4459
  ctxPayload,
4461
4460
  sessionDisplayName: sessionEntry?.displayName,
4462
- turn: {
4463
- storePath,
4464
- record: {
4465
- updateLastRoute: isDirectMessage || opts.eventScope ? {
4466
- sessionKey: updateLastRouteSessionKey,
4467
- channel: "slack",
4468
- to: replyRouteTarget,
4469
- accountId: route.accountId,
4470
- threadId: effectiveMessageThreadId,
4471
- mainDmOwnerPin: isDirectMessage && updateLastRouteSessionKey === route.mainSessionKey && pinnedMainDmOwner && message.user ? {
4472
- ownerRecipient: pinnedMainDmOwner,
4473
- senderRecipient: normalizeLowercaseStringOrEmpty(message.user),
4474
- onSkip: ({ ownerRecipient, senderRecipient }) => {
4475
- logVerbose(`slack: skip main-session last route for ${senderRecipient} (pinned owner ${ownerRecipient})`);
4476
- }
4477
- } : void 0
4478
- } : void 0,
4479
- onRecordError: (err) => {
4480
- ctx.logger.warn({
4481
- error: formatErrorMessage(err),
4482
- storePath,
4483
- sessionKey
4484
- }, "failed updating session meta");
4485
- }
4461
+ turn: { record: {
4462
+ updateLastRoute: isDirectMessage || opts.eventScope ? {
4463
+ sessionKey: updateLastRouteSessionKey,
4464
+ channel: "slack",
4465
+ to: replyRouteTarget,
4466
+ accountId: route.accountId,
4467
+ threadId: effectiveMessageThreadId,
4468
+ mainDmOwnerPin: isDirectMessage && updateLastRouteSessionKey === route.mainSessionKey && pinnedMainDmOwner && message.user ? {
4469
+ ownerRecipient: pinnedMainDmOwner,
4470
+ senderRecipient: normalizeLowercaseStringOrEmpty(message.user),
4471
+ onSkip: ({ ownerRecipient, senderRecipient }) => {
4472
+ logVerbose(`slack: skip main-session last route for ${senderRecipient} (pinned owner ${ownerRecipient})`);
4473
+ }
4474
+ } : void 0
4475
+ } : void 0,
4476
+ onRecordError: (err) => {
4477
+ ctx.logger.warn({
4478
+ error: formatErrorMessage(err),
4479
+ storePath,
4480
+ sessionKey
4481
+ }, "failed updating session meta");
4486
4482
  }
4487
- },
4483
+ } },
4488
4484
  replyToMode,
4489
4485
  ...forcedReplyThreadTs ? { forcedReplyThreadTs } : {},
4490
4486
  ...assistantThreadContext ? { slackMessageMetadata: buildSlackAssistantThreadMetadata(assistantThreadContext) } : {},
4491
- requireMention: shouldRequireMention,
4492
4487
  isDirectMessage,
4493
4488
  isRoomish,
4494
- preview,
4495
4489
  ackReactionMessageTs,
4496
4490
  ackReactionValue,
4497
4491
  ackReactionPromise