@openclaw/slack 2026.11.3 → 2026.11.5-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/dist/.setup/{account-inspect-CmkKiSb9.mjs → account-inspect-BLVha-ov.mjs} +11 -8
  2. package/dist/.setup/{accounts-msihSF3-.mjs → accounts-CGJScKtk.mjs} +10 -1
  3. package/dist/.setup/{action-runtime-ChRfnnBu.mjs → action-runtime-D01wBhx5.mjs} +38 -45
  4. package/dist/.setup/action-runtime.runtime-VQGaR6g8.mjs +1 -0
  5. package/dist/.setup/{actions-By7tvpvv.mjs → actions-D5S6D_BU.mjs} +89 -76
  6. package/dist/.setup/{approval-handler.runtime-D8ArtDQt.mjs → approval-handler.runtime-Buw5jh2Y.mjs} +26 -30
  7. package/dist/.setup/{approval-message-updates-DT7XBPbV.mjs → approval-message-updates-pmXcve5E.mjs} +1 -1
  8. package/dist/.setup/{channel-DLFiMJm2.mjs → channel-C_R98Nbv.mjs} +569 -339
  9. package/dist/.setup/{channel-type-BEFP2ORi.mjs → channel-type-B5P3w9eA.mjs} +4 -8
  10. package/dist/.setup/{channel.setup-CzFw73mU.mjs → channel.setup-Lq2OxKkF.mjs} +13 -13
  11. package/dist/.setup/{config-schema-DNnh6bWU.mjs → config-schema-BWAaa5au.mjs} +3 -7
  12. package/dist/.setup/cursor-pages-CATAzbTT.mjs +313 -0
  13. package/dist/.setup/{directory-config-Cjj0N8R0.mjs → directory-config-D-hf9KGQ.mjs} +3 -3
  14. package/dist/.setup/{directory-live-BIKD-5B_.mjs → directory-live-onKAFI1E.mjs} +4 -4
  15. package/dist/.setup/{doctor-contract-Cd4-VzIM.mjs → doctor-contract-9-QJRCXk.mjs} +37 -45
  16. package/dist/.setup/{message-tool-api-BS5ekEA_.mjs → message-tool-api-LGIgVXJf.mjs} +4 -3
  17. package/dist/.setup/{monitor-CV6Z6FKP.mjs → monitor-Cm1TuqxN.mjs} +3424 -3703
  18. package/dist/.setup/{outbound-adapter-9dbnYtL1.mjs → outbound-adapter-C-95EWl2.mjs} +30 -36
  19. package/dist/.setup/paths-B1u59oLK.mjs +8 -0
  20. package/dist/.setup/{pipeline.runtime-DLIQf1Bo.mjs → pipeline.runtime-DGnV6akc.mjs} +416 -534
  21. package/dist/.setup/{policy-yhn48xf5.mjs → policy-DxhGRvc2.mjs} +1 -2
  22. package/dist/.setup/{session-status-Dv8wNBEt.mjs → probe-DANukR3O.mjs} +1206 -839
  23. package/dist/.setup/question-delivery-CWC7sRhH.mjs +39 -0
  24. package/dist/.setup/{relay-source-CxDcrn16.mjs → relay-source-DoyOlTbK.mjs} +5 -3
  25. package/dist/.setup/{replies-MRkrg3rC.mjs → replies-BcmUKord.mjs} +34 -19
  26. package/dist/.setup/{reply-blocks-Ca-raz2z.mjs → reply-blocks-BzsXUUUL.mjs} +201 -206
  27. package/dist/.setup/{resolve-channels-DRRhElwz.mjs → resolve-channels-XDPgnesf.mjs} +4 -7
  28. package/dist/.setup/{resolve-users-Cz8e17ky.mjs → resolve-users-aDAv24U2.mjs} +8 -16
  29. package/dist/.setup/runtime-setter-api-Cx_PV-xu.mjs +2 -0
  30. package/dist/.setup/{scopes-DfQVi8Jw.mjs → scopes-BocHwImV.mjs} +2 -1
  31. package/dist/.setup/secret-contract-api-BlLz88mj.mjs +2 -0
  32. package/dist/.setup/{secret-contract-vVRMoE1D.mjs → secret-contract-uiQtoNlu.mjs} +2 -2
  33. package/dist/.setup/{send-results-eeOu_HYm.mjs → send-results-BGxlroJ-.mjs} +3 -4
  34. package/dist/.setup/{send-C-8wY0lK.mjs → send-yK6_-px6.mjs} +297 -334
  35. package/dist/.setup/setup-plugin-api-Cfz58xoh.mjs +2 -0
  36. package/dist/.setup/{setup-surface--n8VsDvV.mjs → setup-surface-C0KRtyqa.mjs} +5 -6
  37. package/dist/.setup/{slash-dispatch.runtime-D3Wq45ER.mjs → slash-dispatch.runtime-CyVC3bYx.mjs} +2 -2
  38. package/dist/.setup/{target-parsing-FFE26kOV.mjs → target-parsing-Bx2UZniD.mjs} +1 -2
  39. package/dist/.setup/{targets-C6LZQL1Q.mjs → targets-D-ASeWHy.mjs} +1 -1
  40. package/dist/.source-module-urls/c5ffd0957b234c7c12090e7de4eca2482ef339b8561e078da55b734211b0ba58.json +1 -0
  41. package/dist/account-inspect-api.js +1 -1
  42. package/dist/api.js +16 -17
  43. package/dist/channel-config-api.js +1 -2
  44. package/dist/channel-plugin-api.js +1 -2
  45. package/dist/directory-contract-api.js +1 -2
  46. package/dist/doctor-contract-api.js +1 -2
  47. package/dist/index.js +0 -6
  48. package/dist/message-tool-api.js +1 -2
  49. package/dist/runtime-api.js +10 -12
  50. package/dist/runtime-setter-api.js +1 -2
  51. package/dist/secret-contract-api.js +1 -2
  52. package/dist/security-contract-api.js +1 -2
  53. package/dist/setup-entry.js +3 -3
  54. package/dist/setup-plugin-api.js +1 -2
  55. package/node_modules/@slack/bolt/node_modules/@types/node/README.md +1 -1
  56. package/node_modules/@slack/bolt/node_modules/@types/node/package.json +2 -2
  57. package/node_modules/@slack/bolt/node_modules/@types/node/util.d.ts +6 -3
  58. package/node_modules/@slack/bolt/node_modules/content-disposition/README.md +105 -35
  59. package/node_modules/@slack/bolt/node_modules/content-disposition/dist/index.d.ts +72 -0
  60. package/node_modules/@slack/bolt/node_modules/content-disposition/dist/index.js +388 -0
  61. package/node_modules/@slack/bolt/node_modules/content-disposition/dist/index.js.map +1 -0
  62. package/node_modules/@slack/bolt/node_modules/content-disposition/package.json +31 -19
  63. package/node_modules/@slack/bolt/node_modules/content-type/README.md +34 -57
  64. package/node_modules/@slack/bolt/node_modules/content-type/package.json +35 -25
  65. package/node_modules/@slack/bolt/node_modules/express/Readme.md +15 -9
  66. package/node_modules/@slack/bolt/node_modules/express/lib/application.js +2 -2
  67. package/node_modules/@slack/bolt/node_modules/express/lib/request.js +32 -19
  68. package/node_modules/@slack/bolt/node_modules/express/lib/response.js +27 -30
  69. package/node_modules/@slack/bolt/node_modules/express/package.json +10 -10
  70. package/node_modules/@slack/bolt/node_modules/qs/.github/THREAT_MODEL.md +4 -2
  71. package/node_modules/@slack/bolt/node_modules/qs/CHANGELOG.md +38 -14
  72. package/node_modules/@slack/bolt/node_modules/qs/README.md +31 -3
  73. package/node_modules/@slack/bolt/node_modules/qs/dist/qs.js +20 -20
  74. package/node_modules/@slack/bolt/node_modules/qs/lib/parse.js +94 -4
  75. package/node_modules/@slack/bolt/node_modules/qs/lib/stringify.js +4 -4
  76. package/node_modules/@slack/bolt/node_modules/qs/lib/utils.js +130 -6
  77. package/node_modules/@slack/bolt/node_modules/qs/package.json +4 -4
  78. package/node_modules/@slack/bolt/node_modules/qs/test/parse.js +174 -0
  79. package/node_modules/@slack/bolt/node_modules/qs/test/stringify.js +263 -33
  80. package/node_modules/@slack/bolt/node_modules/qs/test/utils.js +184 -0
  81. package/node_modules/@slack/web-api/node_modules/@types/node/README.md +1 -1
  82. package/node_modules/@slack/web-api/node_modules/@types/node/package.json +2 -2
  83. package/node_modules/@slack/web-api/node_modules/@types/node/util.d.ts +6 -3
  84. package/node_modules/typebox/build/format/duration.d.mts +2 -2
  85. package/node_modules/typebox/build/format/duration.mjs +2 -2
  86. package/node_modules/typebox/build/format/idn_email.d.mts +1 -0
  87. package/node_modules/typebox/build/format/idn_email.mjs +1 -0
  88. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -1
  89. package/node_modules/typebox/build/format/idn_hostname.mjs +1 -1
  90. package/node_modules/typebox/build/format/ipv4.d.mts +2 -2
  91. package/node_modules/typebox/build/format/ipv4.mjs +2 -2
  92. package/node_modules/typebox/build/format/ipv6.d.mts +1 -1
  93. package/node_modules/typebox/build/format/ipv6.mjs +1 -1
  94. package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
  95. package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
  96. package/node_modules/typebox/build/schema/build.mjs +1 -0
  97. package/node_modules/typebox/build/schema/engine/_unique.d.mts +4 -2
  98. package/node_modules/typebox/build/schema/engine/_unique.mjs +7 -3
  99. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +3 -3
  100. package/node_modules/typebox/build/schema/engine/additionalProperties.d.mts +0 -3
  101. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +15 -17
  102. package/node_modules/typebox/build/schema/engine/contains.mjs +3 -3
  103. package/node_modules/typebox/build/schema/engine/index.d.mts +1 -0
  104. package/node_modules/typebox/build/schema/engine/index.mjs +1 -0
  105. package/node_modules/typebox/build/schema/engine/items.mjs +7 -4
  106. package/node_modules/typebox/build/schema/engine/maxContains.mjs +2 -2
  107. package/node_modules/typebox/build/schema/engine/minContains.mjs +3 -3
  108. package/node_modules/typebox/build/schema/engine/oneOf.mjs +2 -2
  109. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +2 -2
  110. package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -2
  111. package/node_modules/typebox/build/schema/engine/unevaluatedItems.mjs +2 -2
  112. package/node_modules/typebox/build/schema/engine/unevaluatedProperties.mjs +2 -2
  113. package/node_modules/typebox/build/system/hashing/hash.mjs +63 -34
  114. package/node_modules/typebox/build/system/locale/en_US.mjs +2 -1
  115. package/node_modules/typebox/build/system/memory/assign.d.mts +2 -8
  116. package/node_modules/typebox/build/system/memory/assign.mjs +13 -6
  117. package/node_modules/typebox/build/system/memory/clone.d.mts +4 -5
  118. package/node_modules/typebox/build/system/memory/clone.mjs +8 -96
  119. package/node_modules/typebox/build/system/memory/create.d.mts +1 -5
  120. package/node_modules/typebox/build/system/memory/create.mjs +18 -13
  121. package/node_modules/typebox/build/system/memory/deep.d.mts +2 -0
  122. package/node_modules/typebox/build/system/memory/deep.mjs +108 -0
  123. package/node_modules/typebox/build/system/memory/discard.d.mts +1 -1
  124. package/node_modules/typebox/build/system/memory/discard.mjs +9 -8
  125. package/node_modules/typebox/build/system/memory/finalize.d.mts +4 -0
  126. package/node_modules/typebox/build/system/memory/finalize.mjs +30 -0
  127. package/node_modules/typebox/build/system/memory/keys.d.mts +2 -0
  128. package/node_modules/typebox/build/system/memory/keys.mjs +8 -0
  129. package/node_modules/typebox/build/system/memory/update.d.mts +1 -4
  130. package/node_modules/typebox/build/system/memory/update.mjs +43 -16
  131. package/node_modules/typebox/build/type/engine/call/index.d.mts +1 -0
  132. package/node_modules/typebox/build/type/engine/call/index.mjs +1 -0
  133. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +2 -24
  134. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +2 -29
  135. package/node_modules/typebox/build/type/engine/evaluate/index.d.mts +1 -0
  136. package/node_modules/typebox/build/type/engine/evaluate/index.mjs +1 -0
  137. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +5 -1
  138. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +6 -1
  139. package/node_modules/typebox/build/type/engine/evaluate/property.d.mts +30 -0
  140. package/node_modules/typebox/build/type/engine/evaluate/property.mjs +29 -0
  141. package/node_modules/typebox/build/type/engine/immutable/instantiate_add.d.mts +1 -3
  142. package/node_modules/typebox/build/type/engine/immutable/instantiate_add.mjs +1 -4
  143. package/node_modules/typebox/build/type/engine/object/from_intersect.d.mts +2 -2
  144. package/node_modules/typebox/build/type/engine/object/from_intersect.mjs +2 -2
  145. package/node_modules/typebox/build/type/engine/object/from_union.d.mts +2 -2
  146. package/node_modules/typebox/build/type/engine/object/from_union.mjs +2 -2
  147. package/node_modules/typebox/build/type/engine/optional/instantiate_add.d.mts +1 -3
  148. package/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs +1 -4
  149. package/node_modules/typebox/build/type/engine/readonly/instantiate_add.d.mts +1 -3
  150. package/node_modules/typebox/build/type/engine/readonly/instantiate_add.mjs +1 -4
  151. package/node_modules/typebox/build/type/engine/this/index.d.mts +1 -0
  152. package/node_modules/typebox/build/type/engine/this/index.mjs +1 -0
  153. package/node_modules/typebox/build/value/clone/clone.d.mts +4 -3
  154. package/node_modules/typebox/build/value/clone/clone.mjs +5 -7
  155. package/node_modules/typebox/build/value/repair/from_cyclic.d.mts +2 -0
  156. package/node_modules/typebox/build/value/repair/from_cyclic.mjs +6 -0
  157. package/node_modules/typebox/build/value/shared/union_priority_sort.mjs +25 -14
  158. package/node_modules/typebox/package.json +1 -1
  159. package/node_modules/typebox/readme.md +1 -1
  160. package/node_modules/undici/docs/docs/api/Interceptors.md +15 -2
  161. package/node_modules/undici/lib/dispatcher/client-h2.js +78 -3
  162. package/node_modules/undici/lib/dispatcher/pool-base.js +20 -3
  163. package/node_modules/undici/package.json +3 -3
  164. package/node_modules/undici/types/api.d.ts +1 -1
  165. package/node_modules/undici/types/utility.d.ts +4 -4
  166. package/node_modules/zod/package.json +1 -1
  167. package/node_modules/zod/src/v4/classic/tests/array.test.ts +27 -0
  168. package/node_modules/zod/src/v4/classic/tests/map.test.ts +7 -0
  169. package/node_modules/zod/src/v4/classic/tests/set.test.ts +6 -0
  170. package/node_modules/zod/src/v4/core/compile.ts +9 -0
  171. package/node_modules/zod/src/v4/core/schemas.ts +35 -1
  172. package/node_modules/zod/src/v4/core/util.ts +13 -0
  173. package/node_modules/zod/src/v4/core/versions.ts +1 -1
  174. package/node_modules/zod/v4/core/compile.cjs +8 -0
  175. package/node_modules/zod/v4/core/compile.js +8 -0
  176. package/node_modules/zod/v4/core/schemas.cjs +30 -0
  177. package/node_modules/zod/v4/core/schemas.d.cts +3 -1
  178. package/node_modules/zod/v4/core/schemas.d.ts +3 -1
  179. package/node_modules/zod/v4/core/schemas.js +30 -0
  180. package/node_modules/zod/v4/core/util.cjs +15 -0
  181. package/node_modules/zod/v4/core/util.d.cts +2 -0
  182. package/node_modules/zod/v4/core/util.d.ts +2 -0
  183. package/node_modules/zod/v4/core/util.js +14 -0
  184. package/node_modules/zod/v4/core/versions.cjs +1 -1
  185. package/node_modules/zod/v4/core/versions.js +1 -1
  186. package/openclaw.plugin.json +6 -30
  187. package/package.json +7 -7
  188. package/dist/.setup/action-runtime.runtime-vLxx_7s2.mjs +0 -2
  189. package/dist/.setup/http-C9sM-FXe.mjs +0 -2
  190. package/dist/.setup/plugin-routes-D4qozHVn.mjs +0 -22
  191. package/dist/.setup/probe-IiHogLQN.mjs +0 -694
  192. package/dist/.setup/registry-DizWcnNp.mjs +0 -36
  193. package/dist/.setup/runtime-setter-api-CMFvhrAr.mjs +0 -3
  194. package/dist/.setup/secret-contract-api-DK9gZldc.mjs +0 -3
  195. package/dist/.setup/setup-plugin-api-BGD1TYnK.mjs +0 -3
  196. package/dist/.setup/shared-BhJHIZs1.mjs +0 -259
  197. package/dist/http-routes-api.js +0 -2
  198. package/node_modules/@slack/bolt/node_modules/body-parser/node_modules/content-type/LICENSE +0 -22
  199. package/node_modules/@slack/bolt/node_modules/body-parser/node_modules/content-type/README.md +0 -71
  200. package/node_modules/@slack/bolt/node_modules/body-parser/node_modules/content-type/package.json +0 -52
  201. package/node_modules/@slack/bolt/node_modules/content-disposition/index.js +0 -536
  202. package/node_modules/@slack/bolt/node_modules/content-type/HISTORY.md +0 -29
  203. package/node_modules/@slack/bolt/node_modules/content-type/index.js +0 -225
  204. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/LICENSE +0 -22
  205. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/README.md +0 -71
  206. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +0 -46
  207. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.js +0 -176
  208. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.js.map +0 -1
  209. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/package.json +0 -52
  210. package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/LICENSE +0 -22
  211. package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/README.md +0 -71
  212. package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/dist/index.d.ts +0 -46
  213. package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/dist/index.js +0 -176
  214. package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/dist/index.js.map +0 -1
  215. package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/package.json +0 -52
  216. package/node_modules/typebox/build/system/memory/freeze.d.mts +0 -4
  217. package/node_modules/typebox/build/system/memory/freeze.mjs +0 -6
  218. /package/node_modules/@slack/bolt/node_modules/{body-parser/node_modules/content-type → content-type}/dist/index.d.ts +0 -0
  219. /package/node_modules/@slack/bolt/node_modules/{body-parser/node_modules/content-type → content-type}/dist/index.js +0 -0
  220. /package/node_modules/@slack/bolt/node_modules/{body-parser/node_modules/content-type → content-type}/dist/index.js.map +0 -0
@@ -1,16 +1,17 @@
1
1
  import { n as __reExport, t as __exportAll } from "./rolldown-runtime-B4FMCO8f.mjs";
2
- import { f as resolveSlackReplyToMode, o as resolveDefaultSlackAccountId } from "./accounts-msihSF3-.mjs";
3
- import { n as formatSlackTarget } from "./target-parsing-FFE26kOV.mjs";
4
- import { D as buildSlackCompleteBlocksFallbackText, E as buildSlackBlocksFallbackText, F as truncateSlackTextByUtf8Bytes, H as SLACK_SESSION_LINK_ACTION_ID, M as SLACK_TEXT_LIMIT, N as countSlackTextUtf8Bytes, P as truncateSlackText, Q as normalizeSlackOutboundText, b as hasSlackNativeDataBlock, k as SLACK_EDIT_TEXT_MAX_BYTES, l as qualifySlackRoutePeerId, st as escapeSlackMrkdwn } from "./session-status-Dv8wNBEt.mjs";
5
- import { M as resolveSlackUserAllowed, O as normalizeSlackAllowOwnerEntry, b as writeLruMapEntry, o as formatSlackError, v as pruneExpiredMapEntries, y as readLruMapEntry } from "./probe-IiHogLQN.mjs";
6
- import { a as prepareSlackReply, l as resolveSlackReplyRenderPlan, s as resolveSlackReplyBlocks } from "./reply-blocks-Ca-raz2z.mjs";
2
+ import { d as resolveSlackMarkdownOptions, o as resolveDefaultSlackAccountId, p as resolveSlackReplyToMode } from "./accounts-CGJScKtk.mjs";
3
+ import { n as formatSlackTarget } from "./target-parsing-Bx2UZniD.mjs";
4
+ import { F as truncateSlackText, Gt as resolveSlackUserAllowed, I as truncateSlackTextByUtf8Bytes, O as hasSlackNativeDataBlock, P as countSlackTextUtf8Bytes, Tt as escapeSlackMrkdwn, U as SLACK_SESSION_LINK_ACTION_ID, Vt as normalizeSlackAllowOwnerEntry, _ as qualifySlackRoutePeerId, at as buildSlackCompleteBlocksFallbackText, it as buildSlackBlocksFallbackText, m as formatSlackError, vt as SLACK_EDIT_TEXT_MAX_BYTES, wt as normalizeSlackOutboundText, xt as SLACK_TEXT_LIMIT } from "./probe-DANukR3O.mjs";
7
5
  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-By7tvpvv.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-C-8wY0lK.mjs";
10
- import { n as resolveSlackChannelConfig } from "./policy-yhn48xf5.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-CV6Z6FKP.mjs";
12
- import { a as emitSlackMessageSentHooks, i as sanitizeSlackMonitorReplyPayload, n as deliverReplies, t as createSlackReplyDeliveryPlan } from "./replies-MRkrg3rC.mjs";
6
+ import { c as resolveSlackReplyRenderPlan, i as prepareSlackReply, o as resolveSlackReplyBlocks } from "./reply-blocks-BzsXUUUL.mjs";
7
+ import { d as getOrCreateWeakOwnerMap, m as writeLruMapEntry, p as readLruMapEntry } from "./cursor-pages-CATAzbTT.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-D5S6D_BU.mjs";
9
+ import { a as clearSlackThreadFailureNotice, c as hasSlackThreadParticipation, d as recordSlackThreadParticipation, h as buildSlackNativeDataDeliveryPlan, l as hasSlackThreadParticipationWithPersistence, m as hasSlackMessageIdentity, s as hasSlackThreadFailureNotice, t as sendMessageSlack, u as recordSlackThreadFailureNotice } from "./send-yK6_-px6.mjs";
10
+ import { n as resolveSlackChannelConfig } from "./policy-DxhGRvc2.mjs";
11
+ import { C as resolveSlackChatType, E as stripSlackMentionsForCommandDetection, S as normalizeSlackChannelType, T as readSlackAssistantThreadContext, _ as appendSlackStream, b as stopSlackStream, c as registerSlackSessionRun, g as SlackStreamNotDeliveredError, h as resolveSlackEffectiveAllowFrom, l as resolveSlackThreadContext, m as resolveSlackCommandIngress, o as authorizeSlackDirectMessage, p as authorizeSlackBotRoomMessage, r as resolveSlackRoomContextHints, s as resolveSlackRoutingContext, u as trackSlackDraftMessage, v as markSlackStreamFallbackDelivered, w as buildSlackAssistantThreadMetadata, y as startSlackStream } from "./monitor-Cm1TuqxN.mjs";
12
+ import { a as emitSlackMessageSentHooks, i as sanitizeSlackMonitorReplyPayload, n as deliverReplies, t as createSlackReplyDeliveryPlan } from "./replies-BcmUKord.mjs";
13
13
  import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, readNonBlankString } from "openclaw/plugin-sdk/string-coerce-runtime";
14
+ import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
14
15
  import { resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
15
16
  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
17
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
@@ -18,10 +19,9 @@ import { danger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtim
18
19
  import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
19
20
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
20
21
  import { normalizeStringEntriesLower } from "openclaw/plugin-sdk/string-normalization-runtime";
21
- import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
22
- import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
22
+ import { createStaleWhileRevalidateCache } from "openclaw/plugin-sdk/collection-runtime";
23
+ import { asFiniteNumberInRange, parseStrictFiniteNumber, resolvePromptHistoryLimit } from "openclaw/plugin-sdk/number-runtime";
23
24
  import { buildTtsSupplementMediaPayload, getReplyPayloadTtsSupplement, isReplyPayloadNonTerminalToolErrorWarning, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
24
- import { asDateTimestampMs, asFiniteNumberInRange, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs, resolvePromptHistoryLimit } from "openclaw/plugin-sdk/number-runtime";
25
25
  import { hash } from "node:crypto";
26
26
  import { resolveChannelImplicitMentions } from "openclaw/plugin-sdk/channel-ingress-runtime";
27
27
  import fs from "node:fs/promises";
@@ -32,10 +32,10 @@ import { mimeTypeFromFilePath, normalizeMimeType } from "openclaw/plugin-sdk/med
32
32
  import { ensureConfiguredBindingRouteReady, resolveConversationLabel } from "openclaw/plugin-sdk/conversation-runtime";
33
33
  import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
34
34
  import { getSessionEntryAsync, readSessionUpdatedAtAsync, resolveChannelResetConfig, resolveStorePath, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
35
- import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
36
35
  import { enqueueRoutedSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
37
36
  import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
38
- import { buildChannelInboundEventContext, buildMentionRegexes, classifyChannelInboundEvent, dispatchChannelInboundTurn, formatInboundEnvelope, formatInboundMediaUnavailableText, hasVisibleInboundReplyDispatch, isGroupThreadRouteExclusive, matchesMentionWithExplicit, readAgentRunTerminalOutcome, resolveEnvelopeFormatOptions, resolveGroupThreadConfig, resolveGroupThreadMentionFacts, resolveInboundMentionDecision, resolveInboundReplyDispatchCounts, resolveUnmentionedGroupInboundPolicy, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
37
+ import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
38
+ import { buildChannelInboundEventContext, buildMentionRegexes, classifyChannelInboundEvent, dispatchChannelInboundTurn, formatInboundEnvelope, formatInboundMediaUnavailableText, hasVisibleInboundReplyDispatch, isGroupThreadRouteExclusive, matchesMentionWithExplicit, readAgentRunTerminalOutcome, resolveEnvelopeFormatOptions, resolveGroupThreadConfig, resolveGroupThreadMentionFacts, resolveInboundMentionDecision, resolveUnmentionedGroupInboundPolicy, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
39
39
  import { resolveGatewayPublicOrigin } from "openclaw/plugin-sdk/config-contracts";
40
40
  import { buildControlUiSessionPath } from "openclaw/plugin-sdk/session-discussion";
41
41
  import { createStatusReactionController, logAckFailure, logTypingFailure, resolveAckReaction, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
@@ -97,13 +97,6 @@ function resolveSlackNativeProgressTaskCards(entry, style) {
97
97
  }
98
98
  const SLACK_STREAM_RECIPIENT_TEAM_CACHE_MAX = 2e3;
99
99
  const slackStreamRecipientTeamCaches = /* @__PURE__ */ new WeakMap();
100
- function getSlackStreamRecipientTeamCache(client) {
101
- const existing = slackStreamRecipientTeamCaches.get(client);
102
- if (existing) return existing;
103
- const cache = /* @__PURE__ */ new Map();
104
- slackStreamRecipientTeamCaches.set(client, cache);
105
- return cache;
106
- }
107
100
  function buildSlackEventDeliveryKey(params, preparedReply = prepareSlackReply(params.payload)) {
108
101
  const reply = resolveSendableOutboundReplyParts(params.payload, { text: params.textOverride });
109
102
  const renderPlan = preparedReply.resolvePreview(params.textOverride);
@@ -122,7 +115,7 @@ function buildSlackEventDeliveryKey(params, preparedReply = prepareSlackReply(pa
122
115
  }
123
116
  async function resolveSlackStreamRecipientTeamId(params) {
124
117
  const cacheKey = params.fallbackTeamId && params.userId ? `${params.fallbackTeamId}:${params.userId}` : void 0;
125
- const cache = cacheKey ? getSlackStreamRecipientTeamCache(params.client) : void 0;
118
+ const cache = cacheKey ? getOrCreateWeakOwnerMap(slackStreamRecipientTeamCaches, params.client) : void 0;
126
119
  const cachedTeamId = cache && cacheKey ? readLruMapEntry(cache, cacheKey) : void 0;
127
120
  if (cachedTeamId) return cachedTeamId;
128
121
  if (params.userId) try {
@@ -146,8 +139,6 @@ const DEFAULT_THROTTLE_MS = 1e3;
146
139
  function createSlackDraftStream(params) {
147
140
  const maxChars = Math.min(params.maxChars ?? 8e3, SLACK_TEXT_LIMIT);
148
141
  const throttleMs = Math.max(250, params.throttleMs ?? DEFAULT_THROTTLE_MS);
149
- const send = params.send ?? sendMessageSlack;
150
- const remove = params.remove ?? deleteSlackMessage;
151
142
  let streamMessage;
152
143
  let untrackConversationBoundary;
153
144
  let lastSentKey = "";
@@ -161,7 +152,7 @@ function createSlackDraftStream(params) {
161
152
  streamMessage = void 0;
162
153
  lastSentKey = "";
163
154
  };
164
- const editPreview = (message, update) => (params.edit ?? (update.textIsSlackMrkdwn ? editSlackRenderedMessage : editSlackMessage))(message.channelId, message.messageId, update.text, {
155
+ const editPreview = (message, update) => (update.textIsSlackMrkdwn ? editSlackRenderedMessage : editSlackMessage)(message.channelId, message.messageId, update.text, {
165
156
  cfg: params.cfg,
166
157
  token: params.token,
167
158
  accountId: params.accountId,
@@ -208,7 +199,7 @@ function createSlackDraftStream(params) {
208
199
  onInterveningMessage: () => forceNewMessage("human")
209
200
  });
210
201
  await lifecycle.createMessage(async () => {
211
- const sent = await send(params.target, visibleUpdate.text, {
202
+ const sent = await sendMessageSlack(params.target, visibleUpdate.text, {
212
203
  cfg: params.cfg,
213
204
  token: params.token,
214
205
  accountId: params.accountId,
@@ -253,7 +244,7 @@ function createSlackDraftStream(params) {
253
244
  clearMessageId,
254
245
  isValidMessageId: (value) => typeof value === "object" && value !== null,
255
246
  deleteMessage: async (message) => {
256
- if (!message.retained && !(preserveHumanReplies && message.detachedByHuman)) await remove(message.channelId, message.messageId, {
247
+ if (!message.retained && !(preserveHumanReplies && message.detachedByHuman)) await deleteSlackMessage(message.channelId, message.messageId, {
257
248
  token: params.token,
258
249
  accountId: params.accountId,
259
250
  ...params.eventScope ? { client: params.eventScope.client } : {}
@@ -301,7 +292,6 @@ function createSlackDraftStream(params) {
301
292
  stopTrackingConversationBoundary();
302
293
  return true;
303
294
  };
304
- params.log?.(`slack stream preview ready (maxChars=${maxChars}, throttleMs=${throttleMs})`);
305
295
  return {
306
296
  update,
307
297
  flush: loop.flush,
@@ -375,12 +365,6 @@ const SLACK_PROGRESS_TASK_TITLE_MAX = 120;
375
365
  const SLACK_PROGRESS_PLAN_FALLBACK_TITLE = "Thinking";
376
366
  const SLACK_PROGRESS_LINE_DELTA_RE = /(?:^|\s)\+(\d+)\s+[−-](\d+)(?=\s|$)/u;
377
367
  const SLACK_ATTENTION_TASK_PREFIX = "openclaw-attention-";
378
- function buildSessionSources(links) {
379
- return links.map((link) => ({
380
- type: "url_source",
381
- ...link
382
- }));
383
- }
384
368
  function field(text) {
385
369
  return {
386
370
  type: "mrkdwn",
@@ -439,10 +423,6 @@ function lineTaskTitle(line) {
439
423
  if (fallback && fallback !== label) return compactTitle(lineDetailParts(line).length > 0 || line.status ? label : fallback);
440
424
  return compactTitle(label);
441
425
  }
442
- function lineTaskDetails(line, maxLineChars) {
443
- const detail = line.detail?.replace(SLACK_PROGRESS_LINE_DELTA_RE, "").replace(/\s+·\s*$/u, "").trim();
444
- return detail && detail !== line.status?.trim() ? compactDetail(detail, maxLineChars) : void 0;
445
- }
446
426
  function lineTaskOutput(line) {
447
427
  const match = line.detail ? SLACK_PROGRESS_LINE_DELTA_RE.exec(line.detail) : null;
448
428
  if (match) return `+${match[1]} −${match[2]}`;
@@ -473,48 +453,39 @@ function resolveLineTaskIdentity(line, contentIdOccurrences) {
473
453
  contentIdOccurrences.set(id, occurrence);
474
454
  return `${id}_${occurrence}`;
475
455
  }
476
- function buildNativeTasks(params) {
456
+ function formatProgressAttentionTitle(line, finalStatus) {
457
+ if (line.kind === "approval") return line.status === "requested" && finalStatus === void 0 ? compactTitle(`Approval required: ${line.detail || line.label}`) : void 0;
458
+ if (lineTaskStatus(line) !== "error") return;
459
+ const title = [...new Set([
460
+ line.label,
461
+ line.detail,
462
+ line.status
463
+ ].filter(Boolean))].join(" — ");
464
+ return compactTitle(finalStatus === "complete" ? `Recovered: ${title}` : title);
465
+ }
466
+ function buildSlackProgressStreamChunks(params) {
467
+ const approvals = params.lines.filter((line) => line.kind === "approval");
468
+ const taskLines = params.summaryRow ? [] : params.lines.filter((line) => line.kind !== "approval");
477
469
  const tasks = (params.plan ?? []).map((entry, index) => ({
478
470
  id: `plan_step_${index + 1}`,
479
471
  title: compactTitle(entry.step),
480
472
  status: entry.status === "completed" ? "complete" : entry.status
481
473
  }));
482
474
  const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_TASK_DETAIL_MAX_CHARS);
483
- const contentIdOccurrences = /* @__PURE__ */ new Map();
484
- for (const line of params.lines) {
475
+ const taskContentIdOccurrences = /* @__PURE__ */ new Map();
476
+ for (const line of taskLines) {
485
477
  const task = {
486
- id: resolveLineTaskIdentity(line, contentIdOccurrences),
478
+ id: resolveLineTaskIdentity(line, taskContentIdOccurrences),
487
479
  title: lineTaskTitle(line),
488
480
  status: lineTaskStatus(line)
489
481
  };
490
- const details = lineTaskDetails(line, maxLineChars);
482
+ const detail = line.detail?.replace(SLACK_PROGRESS_LINE_DELTA_RE, "").replace(/\s+·\s*$/u, "").trim();
483
+ const details = detail && detail !== line.status?.trim() ? compactDetail(detail, maxLineChars) : void 0;
491
484
  const output = lineTaskOutput(line);
492
485
  if (details) task.details = details;
493
486
  if (output) task.output = output;
494
487
  tasks.push(task);
495
488
  }
496
- return tasks;
497
- }
498
- function formatProgressAttentionTitle(line, finalStatus) {
499
- if (line.kind === "approval") return line.status === "requested" && finalStatus === void 0 ? compactTitle(`Approval required: ${line.detail || line.label}`) : void 0;
500
- if (lineTaskStatus(line) !== "error") return;
501
- const title = [...new Set([
502
- line.label,
503
- line.detail,
504
- line.status
505
- ].filter(Boolean))].join(" — ");
506
- return compactTitle(finalStatus === "complete" ? `Recovered: ${title}` : title);
507
- }
508
- function formatTaskDiffOutput(diffStat) {
509
- return diffStat && (diffStat.added > 0 || diffStat.removed > 0) ? `+${diffStat.added} −${diffStat.removed}` : void 0;
510
- }
511
- function buildSlackProgressStreamChunks(params) {
512
- const approvals = params.lines.filter((line) => line.kind === "approval");
513
- const tasks = buildNativeTasks({
514
- lines: params.summaryRow ? [] : params.lines.filter((line) => line.kind !== "approval"),
515
- plan: params.plan,
516
- maxLineChars: params.maxLineChars
517
- });
518
489
  const contentIdOccurrences = /* @__PURE__ */ new Map();
519
490
  const attention = [];
520
491
  for (const line of approvals) {
@@ -525,11 +496,11 @@ function buildSlackProgressStreamChunks(params) {
525
496
  status: "pending"
526
497
  });
527
498
  }
528
- const headline = params.title?.trim() || params.label?.trim();
499
+ const headline = params.title?.trim();
529
500
  const summaryTitle = params.finalInProgressStatus === "error" ? "Failed" : params.finalInProgressStatus === "complete" || !params.summaryRow ? "Completed" : "Working";
530
501
  const newest = tasks.at(-1);
531
502
  const title = compactTitle(headline || (newest?.details ? `${newest.title} — ${newest.details}` : newest?.title) || (params.summaryRow ? summaryTitle : attention.at(-1)?.title) || SLACK_PROGRESS_PLAN_FALLBACK_TITLE, SLACK_PROGRESS_CHUNK_TEXT_MAX);
532
- const diffOutput = formatTaskDiffOutput(params.diffStat);
503
+ const diffOutput = params.diffStat && (params.diffStat.added > 0 || params.diffStat.removed > 0) ? `+${params.diffStat.added} −${params.diffStat.removed}` : void 0;
533
504
  if (tasks.length === 0 && (params.summaryRow || params.sessionLinks?.length || diffOutput)) tasks.push({
534
505
  id: "openclaw_summary",
535
506
  title: params.summaryRow ? compactTitle(title) : summaryTitle,
@@ -549,7 +520,10 @@ function buildSlackProgressStreamChunks(params) {
549
520
  chunk.title = recovered ? compactTitle(`Recovered: ${task.title}`) : task.title;
550
521
  chunk.status = recovered ? "complete" : task.status === "in_progress" ? params.finalInProgressStatus ?? task.status : task.status;
551
522
  if (index === finalTaskIndex && diffOutput) chunk.output = [task.output, diffOutput].filter(Boolean).join(" · ");
552
- if (index === finalTaskIndex && params.sessionLinks?.length) chunk.sources = buildSessionSources(params.sessionLinks);
523
+ if (index === finalTaskIndex && params.sessionLinks?.length) chunk.sources = params.sessionLinks.map((link) => ({
524
+ type: "url_source",
525
+ ...link
526
+ }));
553
527
  return chunk;
554
528
  });
555
529
  return [{
@@ -568,13 +542,6 @@ function joinRecentProgressRows(rows) {
568
542
  }
569
543
  return rendered.toReversed().join("\n");
570
544
  }
571
- function buildActivityText(lines, maxLineChars) {
572
- return joinRecentProgressRows(lines.slice(-50).map((line) => {
573
- if (line.kind === "item" && !line.toolName) return `_${renderProgressCardText(compactDetail(line.text, maxLineChars), "italic")}_`;
574
- const detail = compactDetail(lineDetailParts(line).join(" · "), maxLineChars);
575
- return escapeSlackMrkdwn(detail ? `${line.label} — ${detail}` : line.label);
576
- }));
577
- }
578
545
  function buildSlackProgressCardBlocks(params) {
579
546
  const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS);
580
547
  const selected = selectPlanChecklistSteps(params.detailed ? params.plan ?? [] : [], { maxLines: 50 });
@@ -604,7 +571,11 @@ function buildSlackProgressCardBlocks(params) {
604
571
  });
605
572
  const sections = [
606
573
  planLines.map((line) => renderProgressCardText(line)).join("\n"),
607
- buildActivityText(lines.filter((line) => line.kind !== "approval" && lineTaskStatus(line) !== "error"), maxLineChars),
574
+ joinRecentProgressRows(lines.filter((line) => line.kind !== "approval" && lineTaskStatus(line) !== "error").slice(-50).map((line) => {
575
+ if (line.kind === "item" && !line.toolName) return `_${renderProgressCardText(compactDetail(line.text, maxLineChars), "italic")}_`;
576
+ const detail = compactDetail(lineDetailParts(line).join(" · "), maxLineChars);
577
+ return escapeSlackMrkdwn(detail ? `${line.label} — ${detail}` : line.label);
578
+ })),
608
579
  joinRecentProgressRows(attention)
609
580
  ];
610
581
  const title = params.title?.trim();
@@ -684,6 +655,13 @@ function reconcileSlackNativeTaskChunks(params) {
684
655
  const nextTasks = /* @__PURE__ */ new Map();
685
656
  let planTitle = params.previous.planTitle;
686
657
  const emitted = [];
658
+ if (!params.chunks?.length && planTitle && planTitle !== SLACK_PROGRESS_PLAN_FALLBACK_TITLE) {
659
+ planTitle = SLACK_PROGRESS_PLAN_FALLBACK_TITLE;
660
+ emitted.push({
661
+ type: "plan_update",
662
+ title: planTitle
663
+ });
664
+ }
687
665
  for (const chunk of params.chunks ?? []) {
688
666
  if (chunk.type === "plan_update") {
689
667
  if (chunk.title !== planTitle) {
@@ -795,12 +773,6 @@ function isSlackProgressTitleText(text, snapshot, explicitTitle) {
795
773
  }
796
774
  //#endregion
797
775
  //#region extensions/slack/src/monitor/message-handler/preview-finalize.ts
798
- function buildExpectedSlackEditText(params) {
799
- const trimmedText = params.text.trim();
800
- if (trimmedText) return normalizeSlackOutboundText(trimmedText);
801
- if (params.blocks?.length) return normalizeSlackOutboundText(buildSlackBlocksFallbackText(params.blocks));
802
- return " ";
803
- }
804
776
  function blocksMatch(expected, actual) {
805
777
  if (!expected?.length) return !actual?.length;
806
778
  if (!actual?.length) {
@@ -820,45 +792,6 @@ function blocksMatch(expected, actual) {
820
792
  return false;
821
793
  }
822
794
  }
823
- async function readSlackMessageAfterEditError(params) {
824
- const query = {
825
- token: params.token,
826
- channel: params.channelId,
827
- latest: params.messageId,
828
- oldest: params.messageId,
829
- inclusive: true,
830
- limit: 1
831
- };
832
- const result = params.threadTs ? await params.client.conversations.replies({
833
- ...query,
834
- ts: params.threadTs
835
- }) : await params.client.conversations.history(query);
836
- const message = params.threadTs ? result.messages?.find((entry) => entry?.ts === params.messageId) : result.messages?.[0];
837
- return message?.ts && message.ts === params.messageId ? message : null;
838
- }
839
- async function didSlackPreviewEditApplyAfterError(params) {
840
- const readback = await readSlackMessageAfterEditError(params);
841
- if (!readback) return false;
842
- const expectedText = buildExpectedSlackEditText({
843
- text: params.text,
844
- blocks: params.blocks
845
- });
846
- const acceptedTexts = /* @__PURE__ */ new Set([
847
- expectedText,
848
- normalizeSlackOutboundText(truncateSlackTextByUtf8Bytes(expectedText, SLACK_EDIT_TEXT_MAX_BYTES)),
849
- normalizeSlackOutboundText(buildSlackEditTextPayload(params.text, params.blocks))
850
- ]);
851
- if (params.blocks?.length && hasSlackNativeDataBlock(params.blocks)) {
852
- const fallbackPlan = buildSlackNativeDataDeliveryPlan({
853
- baseText: params.text,
854
- blocks: params.blocks
855
- });
856
- for (const message of fallbackPlan.fallbackMessages) acceptedTexts.add(normalizeSlackOutboundText(message.text));
857
- }
858
- const actualText = normalizeSlackOutboundText((readback.text ?? "").trim());
859
- if (params.blocks?.length) return acceptedTexts.has(actualText) && blocksMatch(params.blocks, readback.blocks);
860
- return actualText === expectedText;
861
- }
862
795
  async function finalizeSlackPreviewEdit(params) {
863
796
  try {
864
797
  await editSlackRenderedMessage(params.channelId, params.messageId, params.text, {
@@ -869,9 +802,40 @@ async function finalizeSlackPreviewEdit(params) {
869
802
  });
870
803
  } catch (err) {
871
804
  try {
872
- if (await didSlackPreviewEditApplyAfterError(params)) {
873
- logVerbose(`slack: preview final edit response failed but readback matched message ${params.channelId}/${params.messageId}; suppressing duplicate fallback send`);
874
- return;
805
+ const query = {
806
+ token: params.token,
807
+ channel: params.channelId,
808
+ latest: params.messageId,
809
+ oldest: params.messageId,
810
+ inclusive: true,
811
+ limit: 1
812
+ };
813
+ const result = params.threadTs ? await params.client.conversations.replies({
814
+ ...query,
815
+ ts: params.threadTs
816
+ }) : await params.client.conversations.history(query);
817
+ const message = params.threadTs ? result.messages?.find((entry) => entry?.ts === params.messageId) : result.messages?.[0];
818
+ const readback = message?.ts && message.ts === params.messageId ? message : null;
819
+ if (readback) {
820
+ const trimmedText = params.text.trim();
821
+ const expectedText = trimmedText ? normalizeSlackOutboundText(trimmedText) : params.blocks?.length ? normalizeSlackOutboundText(buildSlackBlocksFallbackText(params.blocks)) : " ";
822
+ const acceptedTexts = /* @__PURE__ */ new Set([
823
+ expectedText,
824
+ normalizeSlackOutboundText(truncateSlackTextByUtf8Bytes(expectedText, SLACK_EDIT_TEXT_MAX_BYTES)),
825
+ normalizeSlackOutboundText(buildSlackEditTextPayload(params.text, params.blocks))
826
+ ]);
827
+ if (params.blocks?.length && hasSlackNativeDataBlock(params.blocks)) {
828
+ const fallbackPlan = buildSlackNativeDataDeliveryPlan({
829
+ baseText: params.text,
830
+ blocks: params.blocks
831
+ });
832
+ for (const fallbackMessage of fallbackPlan.fallbackMessages) acceptedTexts.add(normalizeSlackOutboundText(fallbackMessage.text));
833
+ }
834
+ const actualText = normalizeSlackOutboundText((readback.text ?? "").trim());
835
+ if (params.blocks?.length ? acceptedTexts.has(actualText) && blocksMatch(params.blocks, readback.blocks) : actualText === expectedText) {
836
+ logVerbose(`slack: preview final edit response failed but readback matched message ${params.channelId}/${params.messageId}; suppressing duplicate fallback send`);
837
+ return;
838
+ }
875
839
  }
876
840
  } catch (readbackErr) {
877
841
  logVerbose(`slack: preview final edit readback failed (${String(readbackErr)})`);
@@ -1049,7 +1013,7 @@ function createSlackNativeProgressTransport(params) {
1049
1013
  const delivered = markDelivered(streamThreadTs);
1050
1014
  return update.chunks?.length ? delivered : true;
1051
1015
  } finally {
1052
- if (delivery.nativeProgressStreamStartPromise === startPromise) delivery.nativeProgressStreamStartPromise = null;
1016
+ delivery.nativeProgressStreamStartPromise = null;
1053
1017
  }
1054
1018
  };
1055
1019
  const append = async (update) => {
@@ -1081,7 +1045,6 @@ function createSlackProgressRuntime(runtimeParams) {
1081
1045
  cfg,
1082
1046
  token: ctx.botToken,
1083
1047
  accountId: account.accountId,
1084
- conversationChannelId: message.channel,
1085
1048
  eventScope: prepared.eventScope,
1086
1049
  ...!hasSlackCustomIdentity && slackIdentity ? { identity: slackIdentity } : {},
1087
1050
  ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
@@ -1091,7 +1054,6 @@ function createSlackProgressRuntime(runtimeParams) {
1091
1054
  if (ts) delivery.usedReplyThreadTs ??= ts;
1092
1055
  return ts;
1093
1056
  },
1094
- log: logVerbose,
1095
1057
  warn: logVerbose
1096
1058
  }) : void 0;
1097
1059
  const isProgressMode = slackStreaming.mode === "progress";
@@ -1510,7 +1472,6 @@ function createSlackProgressRuntime(runtimeParams) {
1510
1472
  return {
1511
1473
  draftStream,
1512
1474
  previewLifecycle,
1513
- isProgressMode,
1514
1475
  useDraftProgressCard,
1515
1476
  useNativeProgressStreaming,
1516
1477
  progressDraftActive,
@@ -1703,9 +1664,7 @@ async function createSlackDispatchSetup(prepared) {
1703
1664
  status: "active",
1704
1665
  eventScope: prepared.eventScope
1705
1666
  });
1706
- if (reportFailure && !restored) try {
1707
- runtime.error?.("Slack session status could not return to active after processing. Enable verbose logging to inspect the Slack API failure.");
1708
- } catch {}
1667
+ if (reportFailure && !restored) runtime.error?.("Slack session status could not return to active after processing. Enable verbose logging to inspect the Slack API failure.");
1709
1668
  }
1710
1669
  if (didAddTypingReaction && typingReaction && message.ts) {
1711
1670
  didAddTypingReaction = false;
@@ -1770,6 +1729,10 @@ async function createSlackDispatchSetup(prepared) {
1770
1729
  function createSlackStreamingDeliveryRuntime(setup) {
1771
1730
  const { isThreadReply, messageSentHookContext, messageSentHookTarget, prepared, replyDeliveryMode, replyPlan, runtime, slackClient, slackClientOptions, slackIdentity, slackStreamFallbackTeamId } = setup;
1772
1731
  const { account, ctx, forcedReplyThreadTs, message, slackMessageMetadata } = prepared;
1732
+ const markdownOptions = resolveSlackMarkdownOptions({
1733
+ cfg: ctx.cfg,
1734
+ accountId: account.accountId
1735
+ });
1773
1736
  const state = {
1774
1737
  streamSession: null,
1775
1738
  streamBoundary: null,
@@ -1850,7 +1813,7 @@ function createSlackStreamingDeliveryRuntime(setup) {
1850
1813
  if (key) deliveredKeys.add(key);
1851
1814
  };
1852
1815
  const markPreviewPayloadDelivered = (params) => {
1853
- const preparedReply = prepareSlackReply(params.payload);
1816
+ const preparedReply = prepareSlackReply(params.payload, markdownOptions);
1854
1817
  markDelivered(buildSlackEventDeliveryKey(params, preparedReply));
1855
1818
  const nextThreadTs = replyPlan.peekThreadTs();
1856
1819
  if (nextThreadTs !== params.threadTs) markDelivered(buildSlackEventDeliveryKey({
@@ -1891,7 +1854,7 @@ function createSlackStreamingDeliveryRuntime(setup) {
1891
1854
  }
1892
1855
  const fallbackText = fallbackError.pendingText.trim();
1893
1856
  if (!fallbackText) return;
1894
- const sent = await deliverPreparedReply(prepareSlackReply({ text: fallbackText }), session.threadTs, true);
1857
+ const sent = await deliverPreparedReply(prepareSlackReply({ text: fallbackText }, markdownOptions), session.threadTs, true);
1895
1858
  if (!sent?.receipt.platformMessageIds.length) return;
1896
1859
  markSlackStreamFallbackDelivered(session);
1897
1860
  if (!session.stopped) try {
@@ -1949,16 +1912,13 @@ function createSlackStreamingDeliveryRuntime(setup) {
1949
1912
  if (state.streamSession?.stoppedBySlack) return { visibleReplySent: false };
1950
1913
  const replyThreadTs = params.forcedThreadTs ?? state.interruptedThreadTs ?? resolveDeliveryThreadTs(params);
1951
1914
  const deliveryReplyThreadTs = replyDeliveryMode === "off" && !forcedReplyThreadTs && !isThreadReply ? void 0 : replyThreadTs;
1952
- const preparedReply = prepareSlackReply(params.payload);
1915
+ const preparedReply = prepareSlackReply(params.payload, markdownOptions);
1953
1916
  const deliveryKey = buildSlackEventDeliveryKey({
1954
1917
  kind: params.kind,
1955
1918
  payload: params.payload,
1956
1919
  threadTs: deliveryReplyThreadTs
1957
1920
  }, preparedReply);
1958
- if (deliveryKey && deliveredKeys.has(deliveryKey)) {
1959
- logVerbose("slack: suppressed duplicate normal delivery within the same turn");
1960
- return { visibleReplySent: false };
1961
- }
1921
+ if (deliveryKey && deliveredKeys.has(deliveryKey)) return { visibleReplySent: false };
1962
1922
  const sent = await deliverPreparedReply(preparedReply, deliveryReplyThreadTs);
1963
1923
  if (!sent?.receipt.platformMessageIds.length) return { visibleReplySent: false };
1964
1924
  state.observedReplyDelivery = true;
@@ -1980,9 +1940,16 @@ function createSlackStreamingDeliveryRuntime(setup) {
1980
1940
  };
1981
1941
  const isStreamingEligible = (payload, options) => {
1982
1942
  const reply = resolveSendableOutboundReplyParts(payload);
1983
- const renderPlan = resolveSlackReplyRenderPlan(payload);
1943
+ const renderPlan = resolveSlackReplyRenderPlan(payload, payload.text, markdownOptions);
1984
1944
  const plannedBlocks = renderPlan.mode === "single" ? renderPlan.blocks : renderPlan.blockPart?.blocks;
1985
- return !state.streamFailed && !reply.hasMedia && renderPlan.mode !== "split" && !renderPlan.textIsSlackPlainText && !plannedBlocks?.length && !resolveSlackReplyBlocks(payload)?.length && reply.hasText && (!options?.maxTextBytes || countSlackTextUtf8Bytes(reply.trimmedText) <= options.maxTextBytes);
1945
+ const previewThreadTs = state.streamSession?.threadTs ?? state.interruptedThreadTs ?? state.nativeProgressStreamThreadTs;
1946
+ const finalThreadTs = resolveSlackReplyThreadTs({
1947
+ replyToMode: replyDeliveryMode,
1948
+ replyToId: payload.replyToId,
1949
+ threadId: previewThreadTs,
1950
+ replyToCurrent: payload.replyToCurrent
1951
+ });
1952
+ return !state.streamFailed && (!previewThreadTs || finalThreadTs === previewThreadTs) && !reply.hasMedia && renderPlan.mode !== "split" && !renderPlan.textIsSlackPlainText && !plannedBlocks?.length && !resolveSlackReplyBlocks(payload)?.length && reply.hasText && (!options?.maxTextBytes || countSlackTextUtf8Bytes(reply.trimmedText) <= options.maxTextBytes);
1986
1953
  };
1987
1954
  const deliverWithStreaming = async (params) => {
1988
1955
  if (state.streamSession?.stoppedBySlack) return { visibleReplySent: false };
@@ -1995,7 +1962,12 @@ function createSlackStreamingDeliveryRuntime(setup) {
1995
1962
  await rotateInterruptedStream();
1996
1963
  if (state.streamSession?.stoppedBySlack) return { visibleReplySent: false };
1997
1964
  let session = state.streamSession;
1998
- const threadTs = session?.threadTs ?? state.interruptedThreadTs ?? replyPlan.nextThreadTs();
1965
+ const threadTs = resolveSlackReplyThreadTs({
1966
+ replyToMode: replyDeliveryMode,
1967
+ replyToId: params.payload.replyToId,
1968
+ threadId: session?.threadTs ?? state.interruptedThreadTs ?? replyPlan.nextThreadTs(),
1969
+ replyToCurrent: params.payload.replyToCurrent
1970
+ });
1999
1971
  if (state.streamFailed || !threadTs) {
2000
1972
  state.streamFailed = true;
2001
1973
  return await deliverNormally({
@@ -2010,11 +1982,8 @@ function createSlackStreamingDeliveryRuntime(setup) {
2010
1982
  ...params,
2011
1983
  threadTs,
2012
1984
  textOverride: text
2013
- });
2014
- if (deliveryKey && deliveredKeys.has(deliveryKey)) {
2015
- logVerbose("slack-stream: suppressed duplicate reply payload");
2016
- return { visibleReplySent: false };
2017
- }
1985
+ }, prepareSlackReply(params.payload, markdownOptions));
1986
+ if (deliveryKey && deliveredKeys.has(deliveryKey)) return { visibleReplySent: false };
2018
1987
  let messageId;
2019
1988
  let fallbackDelivery;
2020
1989
  try {
@@ -2096,6 +2065,75 @@ function createSlackStreamingDeliveryRuntime(setup) {
2096
2065
  });
2097
2066
  }
2098
2067
  //#endregion
2068
+ //#region extensions/slack/src/monitor/message-handler/failure-notice.ts
2069
+ /** One outage policy for immediate replies and later origin-routed queue drains. */
2070
+ function createSlackFailureNoticePolicy(params, hasVisibleReply) {
2071
+ const { context, accountId, channelId, failureNoticeThreadTs, failureNoticeTeamId, statusThreadTs } = params;
2072
+ let sawTerminalFailurePayload = false;
2073
+ let pendingFailureNotice;
2074
+ const filterPassiveThreadFailure = (payload) => {
2075
+ if (payload.isError !== true || context.ChatType !== "channel" || isReplyPayloadNonTerminalToolErrorWarning(payload)) return payload;
2076
+ sawTerminalFailurePayload = true;
2077
+ if (hasVisibleReply()) return payload;
2078
+ const explicitlyAddressed = context.ExplicitlyMentionedBot === true || context.MentionSource === "explicit_bot" || context.MentionSource === "subteam" || context.MentionSource === "mention_pattern" || context.MentionSource === "command_bypass" || context.CommandTurn?.kind !== void 0 && context.CommandTurn.kind !== "normal" && context.CommandTurn.authorized;
2079
+ const noticeThreadTs = failureNoticeThreadTs ?? (explicitlyAddressed ? statusThreadTs : void 0);
2080
+ const notice = {
2081
+ accountId,
2082
+ channelId,
2083
+ ...noticeThreadTs ? { threadTs: noticeThreadTs } : {},
2084
+ failureText: payload.text ?? "",
2085
+ ...failureNoticeTeamId ? { teamId: failureNoticeTeamId } : {}
2086
+ };
2087
+ if (failureNoticeThreadTs && !explicitlyAddressed && context.MentionSource !== "implicit_thread" && !hasSlackThreadParticipation(notice.accountId, notice.channelId, failureNoticeThreadTs, failureNoticeTeamId)) {
2088
+ logVerbose("slack: suppressed passive failure before thread participation");
2089
+ return null;
2090
+ }
2091
+ if (!explicitlyAddressed && hasSlackThreadFailureNotice(notice)) {
2092
+ logVerbose("slack: suppressed repeated passive channel or thread failure");
2093
+ return null;
2094
+ }
2095
+ pendingFailureNotice = notice;
2096
+ return payload;
2097
+ };
2098
+ return {
2099
+ filter: filterPassiveThreadFailure,
2100
+ recordDelivered(payload) {
2101
+ if (pendingFailureNotice && (!payload || payload.isError === true && !isReplyPayloadNonTerminalToolErrorWarning(payload) && (payload.text ?? "") === pendingFailureNotice.failureText)) {
2102
+ recordSlackThreadFailureNotice(pendingFailureNotice);
2103
+ pendingFailureNotice = void 0;
2104
+ }
2105
+ },
2106
+ complete() {
2107
+ if (!sawTerminalFailurePayload && context.ChatType === "channel") clearSlackThreadFailureNotice({
2108
+ accountId,
2109
+ channelId,
2110
+ ...failureNoticeThreadTs ? { threadTs: failureNoticeThreadTs } : {},
2111
+ ...failureNoticeTeamId ? { teamId: failureNoticeTeamId } : {}
2112
+ });
2113
+ }
2114
+ };
2115
+ }
2116
+ function createSlackQueuedFailureNoticePolicy(params, beginSessionRun) {
2117
+ let visibleReplySent = false;
2118
+ let policy = createSlackFailureNoticePolicy(params, () => visibleReplySent);
2119
+ return {
2120
+ begin() {
2121
+ visibleReplySent = false;
2122
+ policy = createSlackFailureNoticePolicy(params, () => visibleReplySent);
2123
+ const release = beginSessionRun();
2124
+ return (settlement) => {
2125
+ if (settlement?.completed && !settlement.failed && !settlement.transportPending) policy.complete();
2126
+ release();
2127
+ };
2128
+ },
2129
+ shouldSuppressReply: (payload) => policy.filter(payload) === null,
2130
+ onReplyDelivered(payload) {
2131
+ policy.recordDelivered(payload);
2132
+ visibleReplySent = true;
2133
+ }
2134
+ };
2135
+ }
2136
+ //#endregion
2099
2137
  //#region extensions/slack/src/monitor/message-handler/dispatch.ts
2100
2138
  function formatSlackGroupThreadReply(text, participant) {
2101
2139
  return `**${participant.name.replace(/[\\`*_{}[\]()<>#!|]/g, "\\$&").replace(/\s+/g, " ")}**\n${text}`;
@@ -2121,7 +2159,7 @@ async function dispatchPreparedSlackMessage(prepared) {
2121
2159
  },
2122
2160
  onSettled: () => {
2123
2161
  releaseDeferred?.();
2124
- upstreamLifecycle.onSettled?.();
2162
+ return upstreamLifecycle.onSettled?.();
2125
2163
  }
2126
2164
  };
2127
2165
  const release = beginSessionRun();
@@ -2130,6 +2168,10 @@ async function dispatchPreparedSlackMessage(prepared) {
2130
2168
  async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptionLifecycle) {
2131
2169
  const { prepared } = setup;
2132
2170
  const { account, ctx, message, route } = prepared;
2171
+ const markdownOptions = resolveSlackMarkdownOptions({
2172
+ cfg: ctx.cfg,
2173
+ accountId: account.accountId
2174
+ });
2133
2175
  const { cfg, disableBlockStreaming, hasSlackCustomIdentity, hasRepliedRef, messageSentHookContext, messageSentHookTarget, onModelSelected, previewStreamingEnabled, replyPipeline, replyPlan, runtime, slackClient, slackStreaming, sourceReplyDeliveryMode, statusReactions, statusReactionsEnabled, statusThreadTs, suppressRoomEventTyping, useStreaming } = setup;
2134
2176
  let dispatchError;
2135
2177
  const delivery = createSlackStreamingDeliveryRuntime(setup);
@@ -2141,41 +2183,22 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2141
2183
  let turnCommentaryVisible = false;
2142
2184
  const { draftStream, previewLifecycle } = progress;
2143
2185
  setup.threadStatusGate.hasVisibleOutput = () => delivery.observedReplyDelivery || previewLifecycle.previewFinalized || Boolean(draftStream?.messageId());
2144
- const failureNoticeThreadTs = message.thread_ts;
2145
- const failureNoticeTeamId = prepared.eventScope?.teamId;
2146
- let sawTerminalFailurePayload = false;
2147
- let pendingFailureNotice;
2148
- const filterPassiveThreadFailure = (payload) => {
2149
- if (payload.isError !== true || prepared.ctxPayload.ChatType !== "channel" || isReplyPayloadNonTerminalToolErrorWarning(payload)) return payload;
2150
- sawTerminalFailurePayload = true;
2151
- if (delivery.observedReplyDelivery || previewLifecycle.previewFinalized) return payload;
2152
- const explicitlyAddressed = prepared.ctxPayload.ExplicitlyMentionedBot === true || prepared.ctxPayload.MentionSource === "explicit_bot" || prepared.ctxPayload.MentionSource === "subteam" || prepared.ctxPayload.MentionSource === "mention_pattern" || prepared.ctxPayload.MentionSource === "command_bypass" || prepared.ctxPayload.CommandTurn?.kind !== void 0 && prepared.ctxPayload.CommandTurn.kind !== "normal" && prepared.ctxPayload.CommandTurn.authorized;
2153
- const noticeThreadTs = failureNoticeThreadTs ?? (explicitlyAddressed ? statusThreadTs : void 0);
2154
- const notice = {
2155
- accountId: account.accountId,
2156
- channelId: message.channel,
2157
- ...noticeThreadTs ? { threadTs: noticeThreadTs } : {},
2158
- failureText: payload.text ?? "",
2159
- ...failureNoticeTeamId ? { teamId: failureNoticeTeamId } : {}
2160
- };
2161
- if (failureNoticeThreadTs && !explicitlyAddressed && prepared.ctxPayload.MentionSource !== "implicit_thread" && !hasSlackThreadParticipation(notice.accountId, notice.channelId, failureNoticeThreadTs, failureNoticeTeamId)) {
2162
- logVerbose("slack: suppressed passive failure before thread participation");
2163
- return null;
2164
- }
2165
- if (!explicitlyAddressed && hasSlackThreadFailureNotice(notice)) {
2166
- logVerbose("slack: suppressed repeated passive channel or thread failure");
2167
- return null;
2168
- }
2169
- pendingFailureNotice = notice;
2170
- return payload;
2186
+ const failurePolicyParams = {
2187
+ context: prepared.ctxPayload,
2188
+ accountId: account.accountId,
2189
+ channelId: message.channel,
2190
+ failureNoticeThreadTs: message.thread_ts,
2191
+ failureNoticeTeamId: prepared.eventScope?.teamId,
2192
+ statusThreadTs
2171
2193
  };
2194
+ const failurePolicy = createSlackFailureNoticePolicy(failurePolicyParams, () => delivery.observedReplyDelivery || previewLifecycle.previewFinalized);
2172
2195
  const deliverSlackPayload = async (incomingPayload, info) => {
2173
2196
  let payload = incomingPayload;
2174
2197
  if (info.participant && (payload.text || payload.mediaUrl || payload.mediaUrls?.length)) payload = {
2175
2198
  ...payload,
2176
2199
  text: formatSlackGroupThreadReply(payload.text ?? "", info.participant)
2177
2200
  };
2178
- if (info.kind === "final" && slackStreaming.mode === "progress" && progress.isProgressMode) {
2201
+ if (info.kind === "final" && slackStreaming.mode === "progress") {
2179
2202
  const supplement = getReplyPayloadTtsSupplement(payload);
2180
2203
  const finalPayload = !progress.useDraftProgressCard && !progress.useNativeProgressStreaming && supplement && !supplement.visibleTextAlreadyDelivered && !payload.text?.trim() ? {
2181
2204
  ...payload,
@@ -2215,15 +2238,11 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2215
2238
  const reply = resolveSendableOutboundReplyParts(payload);
2216
2239
  const ttsSupplement = getReplyPayloadTtsSupplement(payload);
2217
2240
  const replySourceText = payload.text ?? ttsSupplement?.spokenText;
2218
- const replyRenderPlan = resolveSlackReplyRenderPlan(payload, replySourceText);
2241
+ const replyRenderPlan = resolveSlackReplyRenderPlan(payload, replySourceText, markdownOptions);
2219
2242
  const slackBlocks = replyRenderPlan.mode === "single" ? replyRenderPlan.blocks : replyRenderPlan.blockPart?.blocks;
2220
2243
  const requiresSeparateFallbackDelivery = replyRenderPlan.mode === "split" || replyRenderPlan.textIsSlackPlainText === true;
2221
2244
  const trimmedFinalText = replyRenderPlan.mode === "single" ? replyRenderPlan.text.trim() : replyRenderPlan.fallbackText.trim();
2222
- const previewFinalText = replyRenderPlan.mode === "single" && replyRenderPlan.textIsSlackMrkdwn ? trimmedFinalText : normalizeSlackOutboundText((replySourceText ?? "").trim(), { tableMode: resolveMarkdownTableMode({
2223
- cfg,
2224
- channel: "slack",
2225
- accountId: account.accountId
2226
- }) });
2245
+ const previewFinalText = replyRenderPlan.mode === "single" && replyRenderPlan.textIsSlackMrkdwn ? trimmedFinalText : normalizeSlackOutboundText((replySourceText ?? "").trim(), markdownOptions);
2227
2246
  const previewFinalTextFitsEdit = countSlackTextUtf8Bytes(previewFinalText) <= SLACK_EDIT_TEXT_MAX_BYTES;
2228
2247
  const shouldRestoreTtsSupplementTextForPreviewFallback = Boolean(ttsSupplement) && ttsSupplement?.visibleTextAlreadyDelivered !== true && Boolean(draftStream) && !previewLifecycle.previewFinalized && !previewLifecycle.finalDelivered && previewStreamingEnabled && !payload.text?.trim();
2229
2248
  let ttsPreviewFinalization;
@@ -2233,11 +2252,18 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2233
2252
  isError: payload.isError === true,
2234
2253
  adapter: {
2235
2254
  buildFinalEdit: () => {
2236
- if (hasSlackCustomIdentity || !previewStreamingEnabled || reply.hasMedia && !ttsSupplement || payload.isError || requiresSeparateFallbackDelivery || !previewFinalTextFitsEdit || trimmedFinalText.length === 0 && !slackBlocks?.length) return;
2255
+ const threadTs = delivery.usedReplyThreadTs ?? statusThreadTs;
2256
+ const finalThreadTs = resolveSlackReplyThreadTs({
2257
+ replyToMode: setup.replyDeliveryMode,
2258
+ replyToId: payload.replyToId,
2259
+ threadId: threadTs,
2260
+ replyToCurrent: payload.replyToCurrent
2261
+ });
2262
+ if (hasSlackCustomIdentity || finalThreadTs !== threadTs || !previewStreamingEnabled || reply.hasMedia && !ttsSupplement || payload.isError || requiresSeparateFallbackDelivery || !previewFinalTextFitsEdit || trimmedFinalText.length === 0 && !slackBlocks?.length) return;
2237
2263
  return {
2238
2264
  text: previewFinalText,
2239
2265
  blocks: slackBlocks,
2240
- threadTs: delivery.usedReplyThreadTs ?? statusThreadTs
2266
+ threadTs
2241
2267
  };
2242
2268
  },
2243
2269
  editFinal: async (preview, edit) => {
@@ -2332,7 +2358,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2332
2358
  ...replyPipeline,
2333
2359
  transformReplyPayload: (payload) => {
2334
2360
  const transformed = replyPipeline.transformReplyPayload ? replyPipeline.transformReplyPayload(payload) : payload;
2335
- return transformed ? filterPassiveThreadFailure(transformed) : null;
2361
+ return transformed ? failurePolicy.filter(transformed) : null;
2336
2362
  },
2337
2363
  humanDelay: resolveHumanDelayConfig(cfg, route.agentId)
2338
2364
  },
@@ -2349,7 +2375,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2349
2375
  replyOptions: {
2350
2376
  onVisibleWorkSessions: progress.onVisibleWorkSessions,
2351
2377
  groupThreadReplyFormatter: formatSlackGroupThreadReply,
2352
- queuedDeliveryCorrelations: [{ begin: beginSessionRun }],
2378
+ queuedDeliveryCorrelations: [createSlackQueuedFailureNoticePolicy(failurePolicyParams, beginSessionRun)],
2353
2379
  ...turnAdoptionLifecycle ? { turnAdoptionLifecycle } : {},
2354
2380
  skillFilter: prepared.channelConfig?.skills,
2355
2381
  sourceReplyDeliveryMode,
@@ -2393,7 +2419,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2393
2419
  if (payload.kind === "preamble" && await shouldYieldDraftProgress()) return false;
2394
2420
  if (prepared.turnAdoptionLifecycle?.abortSignal?.aborted) return false;
2395
2421
  progress.progressWorkCounter.noteItem(payload);
2396
- return progress.preambleOnlyProgress && payload.kind !== "preamble" ? await progress.progressDraft.noteActivity() : await progress.progressDraft.pushItemEvent(payload);
2422
+ return progress.preambleOnlyProgress && payload.kind !== "preamble" && payload.kind !== "model-wait" ? await progress.progressDraft.noteActivity() : await progress.progressDraft.pushItemEvent(payload);
2397
2423
  },
2398
2424
  onPlanUpdate: async (payload) => {
2399
2425
  if (payload.phase !== "update") return false;
@@ -2407,12 +2433,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2407
2433
  settledDispatchResult = result;
2408
2434
  const agentRunOutcome = readAgentRunTerminalOutcome(result);
2409
2435
  agentRunFailed = agentRunOutcome === "failed";
2410
- if (agentRunOutcome === "completed" && !sawTerminalFailurePayload && prepared.ctxPayload.ChatType === "channel") clearSlackThreadFailureNotice({
2411
- accountId: account.accountId,
2412
- channelId: message.channel,
2413
- ...failureNoticeThreadTs ? { threadTs: failureNoticeThreadTs } : {},
2414
- ...failureNoticeTeamId ? { teamId: failureNoticeTeamId } : {}
2415
- });
2436
+ if (agentRunOutcome === "completed") failurePolicy.complete();
2416
2437
  }
2417
2438
  } catch (err) {
2418
2439
  dispatchError ??= err;
@@ -2428,7 +2449,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2428
2449
  if (hasVisibleInboundReplyDispatch(settledDispatchResult)) await previewLifecycle.observeDelivery({ visibleReplySent: true });
2429
2450
  }
2430
2451
  await previewLifecycle.cleanup({ failed: Boolean(dispatchError || agentRunFailed) });
2431
- if (pendingFailureNotice && anyReplyDelivered) recordSlackThreadFailureNotice(pendingFailureNotice);
2452
+ if (anyReplyDelivered) failurePolicy.recordDelivered();
2432
2453
  if (dispatchError || agentRunFailed) await progress.finalizeDraftProgressCard("error", { postIfMissing: !anyReplyDelivered });
2433
2454
  await progress.dropDetachedProgressCards();
2434
2455
  if (statusReactionsEnabled) {
@@ -2448,11 +2469,6 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2448
2469
  if (dispatchError) throw toErrorObject(dispatchError, "Slack dispatch failed");
2449
2470
  if (!anyReplyDelivered && !previewLifecycle.previewFinalized && !(agentRunFailed && progress.useDraftProgressCard)) {
2450
2471
  if (progress.useDraftProgressCard) await draftStream?.clear();
2451
- return;
2452
- }
2453
- if (shouldLogVerbose()) {
2454
- const finalCount = resolveInboundReplyDispatchCounts(settledDispatchResult).final;
2455
- logVerbose(`slack: delivered ${finalCount} repl${finalCount === 1 ? "y" : "ies"} to ${prepared.replyTarget}`);
2456
2472
  }
2457
2473
  }
2458
2474
  //#endregion
@@ -2496,13 +2512,12 @@ function normalizeSlackAppContextEntities(value) {
2496
2512
  }
2497
2513
  //#endregion
2498
2514
  //#region extensions/slack/src/monitor/thread.ts
2499
- const THREAD_STARTER_CACHE = /* @__PURE__ */ new Map();
2500
- const THREAD_STARTER_CACHE_TTL_MS = 216e5;
2501
- const THREAD_STARTER_CACHE_MAX = 2e3;
2502
- function evictThreadStarterCache() {
2503
- pruneExpiredMapEntries(THREAD_STARTER_CACHE, Date.now());
2504
- pruneMapToMaxSize(THREAD_STARTER_CACHE, THREAD_STARTER_CACHE_MAX);
2505
- }
2515
+ const threadStarterCache = createStaleWhileRevalidateCache({
2516
+ maxEntries: 2e3,
2517
+ maxPending: Number.POSITIVE_INFINITY,
2518
+ ttlMs: 216e5,
2519
+ cacheable: (starter) => starter !== null
2520
+ });
2506
2521
  function formatSlackFilePlaceholder(files) {
2507
2522
  return `[attached: ${formatSlackFileReferenceList(files)}]`;
2508
2523
  }
@@ -2537,40 +2552,35 @@ function resolveSlackMessageText(message) {
2537
2552
  }, { preserveMessageTextWhitespace: true });
2538
2553
  }
2539
2554
  async function resolveSlackThreadStarter(params) {
2540
- evictThreadStarterCache();
2541
2555
  const cacheKey = JSON.stringify([
2542
2556
  params.workspaceScope.accountId,
2543
2557
  params.workspaceScope.teamId,
2544
2558
  params.channelId,
2545
2559
  params.threadTs
2546
2560
  ]);
2547
- const cached = THREAD_STARTER_CACHE.get(cacheKey);
2548
- if (cached && !params.refresh) return cached.value;
2549
- if (params.refresh) THREAD_STARTER_CACHE.delete(cacheKey);
2550
2561
  try {
2551
- const message = (await params.client.conversations.replies({
2552
- channel: params.channelId,
2553
- ts: params.threadTs,
2554
- limit: 1,
2555
- inclusive: true
2556
- }))?.messages?.[0];
2557
- const text = message ? resolveSlackMessageText(message) : void 0;
2558
- const files = message?.files?.length ? message.files : void 0;
2559
- if (!message || !text && !files) return null;
2560
- const starter = {
2561
- text: text || formatSlackFilePlaceholder(files),
2562
- userId: message.user,
2563
- botId: message.bot_id,
2564
- ts: message.ts,
2565
- files
2566
- };
2567
- THREAD_STARTER_CACHE.delete(cacheKey);
2568
- THREAD_STARTER_CACHE.set(cacheKey, {
2569
- value: starter,
2570
- expiresAt: Date.now() + THREAD_STARTER_CACHE_TTL_MS
2562
+ const { value } = await threadStarterCache.read(cacheKey, async () => {
2563
+ const message = (await params.client.conversations.replies({
2564
+ channel: params.channelId,
2565
+ ts: params.threadTs,
2566
+ limit: 1,
2567
+ inclusive: true
2568
+ }))?.messages?.[0];
2569
+ const text = message ? resolveSlackMessageText(message) : void 0;
2570
+ const files = message?.files?.length ? message.files : void 0;
2571
+ if (!message || !text && !files) return null;
2572
+ return {
2573
+ text: text || formatSlackFilePlaceholder(files),
2574
+ userId: message.user,
2575
+ botId: message.bot_id,
2576
+ ts: message.ts,
2577
+ files
2578
+ };
2579
+ }, {
2580
+ allowStale: false,
2581
+ refresh: params.refresh
2571
2582
  });
2572
- evictThreadStarterCache();
2573
- return starter;
2583
+ return value;
2574
2584
  } catch (err) {
2575
2585
  logVerbose(`slack thread starter fetch failed channel=${params.channelId} ts=${params.threadTs}: ${formatErrorMessage(err)}`);
2576
2586
  return null;
@@ -2624,8 +2634,7 @@ async function resolveSlackThreadHistory(params) {
2624
2634
  retained.push([msg, text]);
2625
2635
  }
2626
2636
  if (retained.length > maxMessages) retained.splice(0, retained.length - maxMessages);
2627
- const next = response.response_metadata?.next_cursor;
2628
- cursor = typeof next === "string" && next.trim().length > 0 ? next.trim() : void 0;
2637
+ cursor = normalizeOptionalString(response.response_metadata?.next_cursor);
2629
2638
  if (response.has_more && !cursor) {
2630
2639
  params.onOmission?.("Slack returned an incomplete thread page without a cursor");
2631
2640
  return [];
@@ -2674,7 +2683,7 @@ async function resolveSlackChannelHistory(params) {
2674
2683
  cursor = response.response_metadata?.next_cursor?.trim() || void 0;
2675
2684
  if (!cursor || scanned >= params.limit) break;
2676
2685
  }
2677
- return messages.slice(0, params.limit).toReversed();
2686
+ return messages.slice(0, params.limit).reverse();
2678
2687
  }
2679
2688
  //#endregion
2680
2689
  //#region extensions/slack/src/monitor/message-handler/preflight-audio.ts
@@ -2726,7 +2735,7 @@ const SLACK_MENTION_RESOLUTION_CONCURRENCY = 4;
2726
2735
  const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
2727
2736
  const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
2728
2737
  const MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS = 2e3;
2729
- const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-By7tvpvv.mjs").then((n) => n._));
2738
+ const loadSlackMediaModule$2 = createLazyRuntimeModule(() => import("./actions-D5S6D_BU.mjs").then((n) => n._));
2730
2739
  function formatSlackUnavailableMedia(params) {
2731
2740
  let fileReferences = params.files?.map((file) => `${formatSlackFileReference(file)} unavailable (${file.reason})`).join(", ");
2732
2741
  if (fileReferences && fileReferences.length > MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS) fileReferences = `${truncateUtf16Safe(fileReferences, MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS)}; … (file references truncated)`;
@@ -2747,7 +2756,6 @@ function collectUniqueSlackMentionIds(texts) {
2747
2756
  const seen = /* @__PURE__ */ new Set();
2748
2757
  for (const text of texts) {
2749
2758
  if (!text) continue;
2750
- SLACK_USER_MENTION_RE$1.lastIndex = 0;
2751
2759
  for (const match of text.matchAll(SLACK_USER_MENTION_RE$1)) {
2752
2760
  const userId = match[1];
2753
2761
  if (userId) seen.add(userId);
@@ -2757,35 +2765,24 @@ function collectUniqueSlackMentionIds(texts) {
2757
2765
  }
2758
2766
  function renderSlackUserMentions(text, renderedMentions) {
2759
2767
  if (!text || renderedMentions.size === 0) return text;
2760
- SLACK_USER_MENTION_RE$1.lastIndex = 0;
2761
2768
  return text.replace(SLACK_USER_MENTION_RE$1, (full, userId) => {
2762
2769
  return renderedMentions.get(userId) ?? full;
2763
2770
  });
2764
2771
  }
2765
- function filterInheritedParentFiles(params) {
2766
- const { files, isThreadReply, threadStarter } = params;
2767
- if (!isThreadReply || !files?.length || !threadStarter?.files?.length) return files;
2768
- const starterFileIds = new Set(threadStarter.files.map((file) => file.id));
2769
- const filtered = files.filter((file) => !file.id || !starterFileIds.has(file.id));
2770
- if (filtered.length < files.length) logVerbose(`slack: filtered ${files.length - filtered.length} inherited parent file(s) from thread reply`);
2771
- return filtered.length > 0 ? filtered : void 0;
2772
- }
2773
2772
  async function resolveSlackMessageContent(params) {
2774
- const ownFiles = filterInheritedParentFiles({
2775
- files: params.message.files,
2776
- isThreadReply: params.isThreadReply,
2777
- threadStarter: params.threadStarter
2778
- });
2779
- const attachmentContent = ownFiles?.length || params.message.attachments?.length ? await loadSlackMediaModule$1().then(({ resolveSlackAttachmentContent }) => resolveSlackAttachmentContent({
2773
+ let ownFiles = params.message.files;
2774
+ const { isThreadReply, threadStarter } = params;
2775
+ if (isThreadReply && ownFiles?.length && threadStarter?.files?.length) {
2776
+ const starterFileIds = new Set(threadStarter.files.map((file) => file.id));
2777
+ const filtered = ownFiles.filter((file) => !file.id || !starterFileIds.has(file.id));
2778
+ ownFiles = filtered.length > 0 ? filtered : void 0;
2779
+ }
2780
+ const attachmentContent = ownFiles?.length || params.message.attachments?.length ? await loadSlackMediaModule$2().then(({ resolveSlackAttachmentContent }) => resolveSlackAttachmentContent({
2780
2781
  files: ownFiles,
2781
2782
  attachments: params.message.attachments,
2782
2783
  client: params.client,
2783
2784
  token: params.botToken,
2784
2785
  maxBytes: params.mediaMaxBytes,
2785
- readIdleTimeoutMs: params.mediaReadIdleTimeoutMs,
2786
- totalTimeoutMs: params.mediaTotalTimeoutMs,
2787
- abortSignal: params.abortSignal,
2788
- assertCurrent: params.assertCurrent,
2789
2786
  preloadedMedia: params.preloadedMedia
2790
2787
  })) : null;
2791
2788
  const effectiveDirectMedia = attachmentContent?.media.length ? attachmentContent.media : null;
@@ -2841,7 +2838,7 @@ function resolveSlackDmHistoryLimit(params) {
2841
2838
  return resolvePromptHistoryLimit(override ?? params.defaultLimit, 0);
2842
2839
  }
2843
2840
  async function resolveSlackDmHistoryContext(params) {
2844
- const maxMessages = Math.max(0, Math.floor(params.limit));
2841
+ const maxMessages = params.limit;
2845
2842
  if (maxMessages <= 0) return {
2846
2843
  body: void 0,
2847
2844
  inboundHistory: void 0
@@ -2862,7 +2859,7 @@ async function resolveSlackDmHistoryContext(params) {
2862
2859
  message,
2863
2860
  body
2864
2861
  }] : [];
2865
- }).slice(0, maxMessages).toReversed();
2862
+ }).slice(0, maxMessages).reverse();
2866
2863
  if (messages.length === 0) return {
2867
2864
  body: void 0,
2868
2865
  inboundHistory: void 0
@@ -2922,6 +2919,7 @@ const SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS = 4;
2922
2919
  const SLACK_HISTORY_MEDIA_MAX_BYTES = 10485760;
2923
2920
  const SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS = 1e3;
2924
2921
  const SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS = 3e3;
2922
+ const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-D5S6D_BU.mjs").then((n) => n._));
2925
2923
  async function resolveSlackRoomHistory(params) {
2926
2924
  if (params.ctx.historyLimit <= 0 || !params.message.ts) return [];
2927
2925
  const warnOmission = (reason) => {
@@ -2975,26 +2973,51 @@ async function resolveSlackRoomHistory(params) {
2975
2973
  sender: user?.name ?? message.userId ?? `Bot (${message.botId ?? "unknown"})`
2976
2974
  });
2977
2975
  }
2976
+ async function resolveSlackHistoryMedia(message, maxAttachments) {
2977
+ const { ctx, eventScope, assertCurrent, abortSignal } = params;
2978
+ const files = sliceSlackImageFileCandidates(message.files, maxAttachments);
2979
+ const attachments = [];
2980
+ let remaining = Math.max(0, maxAttachments - files.length);
2981
+ if (remaining > 0 && message.attachments?.length) for (const attachment of message.attachments) {
2982
+ if (attachment.is_share !== true) continue;
2983
+ const hasImageUrl = Boolean(normalizeOptionalString(attachment.image_url));
2984
+ const attachmentFiles = sliceSlackImageFileCandidates(attachment.files, remaining - (hasImageUrl ? 1 : 0));
2985
+ if (!hasImageUrl && attachmentFiles.length === 0) continue;
2986
+ attachments.push({
2987
+ ...attachment,
2988
+ files: attachmentFiles
2989
+ });
2990
+ remaining -= (hasImageUrl ? 1 : 0) + attachmentFiles.length;
2991
+ if (remaining <= 0) break;
2992
+ }
2993
+ if (files.length === 0 && attachments.length === 0) return {
2994
+ media: [],
2995
+ attempted: 0
2996
+ };
2997
+ const { resolveSlackAttachmentContent } = await loadSlackMediaModule$1();
2998
+ const content = await resolveSlackAttachmentContent({
2999
+ files,
3000
+ attachments,
3001
+ client: eventScope?.client ?? ctx.app.client,
3002
+ token: ctx.botToken,
3003
+ maxBytes: Math.min(ctx.mediaMaxBytes, SLACK_HISTORY_MEDIA_MAX_BYTES),
3004
+ readIdleTimeoutMs: SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS,
3005
+ totalTimeoutMs: SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS,
3006
+ assertCurrent,
3007
+ abortSignal
3008
+ });
3009
+ return {
3010
+ media: await toInboundMediaFactsWithMetadata(content?.media, {
3011
+ kind: "image",
3012
+ messageId: message.ts
3013
+ }),
3014
+ attempted: maxAttachments - remaining
3015
+ };
3016
+ }
2978
3017
  const entries = [];
2979
3018
  let mediaRemaining = SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS;
2980
3019
  for (const { message, sender } of permitted.slice(-params.ctx.historyLimit)) {
2981
- const { media, attempted } = mediaRemaining > 0 && (message.files?.length || message.attachments?.length) ? await resolveSlackHistoryMedia({
2982
- ctx: params.ctx,
2983
- eventScope: params.eventScope,
2984
- maxAttachments: mediaRemaining,
2985
- assertCurrent: params.assertCurrent,
2986
- abortSignal: params.abortSignal,
2987
- message: {
2988
- type: "message",
2989
- channel: params.message.channel,
2990
- ts: message.ts,
2991
- user: message.userId,
2992
- bot_id: message.botId,
2993
- text: message.text,
2994
- files: message.files,
2995
- attachments: message.attachments
2996
- }
2997
- }) : {
3020
+ const { media, attempted } = mediaRemaining > 0 && (message.files?.length || message.attachments?.length) ? await resolveSlackHistoryMedia(message, mediaRemaining) : {
2998
3021
  media: [],
2999
3022
  attempted: 0
3000
3023
  };
@@ -3021,57 +3044,6 @@ function sliceSlackImageFileCandidates(files, limit) {
3021
3044
  if (limit <= 0 || !files?.length) return [];
3022
3045
  return files.filter(isSlackImageFileCandidate).slice(0, limit);
3023
3046
  }
3024
- function sliceSlackHistoryAttachmentCandidates(attachments, limit) {
3025
- if (limit <= 0 || !attachments?.length) return [];
3026
- const out = [];
3027
- let remaining = limit;
3028
- for (const attachment of attachments) {
3029
- if (attachment.is_share !== true) continue;
3030
- const hasImageUrl = Boolean(normalizeOptionalString(attachment.image_url));
3031
- const files = sliceSlackImageFileCandidates(attachment.files, remaining - (hasImageUrl ? 1 : 0));
3032
- if (!hasImageUrl && files.length === 0) continue;
3033
- out.push({
3034
- ...attachment,
3035
- files
3036
- });
3037
- remaining -= (hasImageUrl ? 1 : 0) + files.length;
3038
- if (remaining <= 0) break;
3039
- }
3040
- return out;
3041
- }
3042
- async function resolveSlackHistoryMedia(params) {
3043
- const files = sliceSlackImageFileCandidates(params.message.files, params.maxAttachments);
3044
- const attachments = sliceSlackHistoryAttachmentCandidates(params.message.attachments, Math.max(0, params.maxAttachments - files.length));
3045
- if (files.length === 0 && attachments.length === 0) return {
3046
- media: [],
3047
- attempted: 0
3048
- };
3049
- const attempted = files.length + attachments.reduce((count, attachment) => count + (normalizeOptionalString(attachment.image_url) ? 1 : 0) + (attachment.files?.length ?? 0), 0);
3050
- const content = await resolveSlackMessageContent({
3051
- message: {
3052
- ...params.message,
3053
- files,
3054
- attachments
3055
- },
3056
- isThreadReply: false,
3057
- threadStarter: null,
3058
- isBotMessage: Boolean(params.message.bot_id),
3059
- client: params.eventScope?.client ?? params.ctx.app.client,
3060
- botToken: params.ctx.botToken,
3061
- mediaMaxBytes: Math.min(params.ctx.mediaMaxBytes, SLACK_HISTORY_MEDIA_MAX_BYTES),
3062
- mediaReadIdleTimeoutMs: SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS,
3063
- mediaTotalTimeoutMs: SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS,
3064
- assertCurrent: params.assertCurrent,
3065
- abortSignal: params.abortSignal
3066
- });
3067
- return {
3068
- media: await toInboundMediaFactsWithMetadata(content?.effectiveDirectMedia, {
3069
- kind: "image",
3070
- messageId: params.message.ts
3071
- }),
3072
- attempted
3073
- };
3074
- }
3075
3047
  //#endregion
3076
3048
  //#region extensions/slack/src/monitor/message-handler/prepare-session-presentation.ts
3077
3049
  function resolveSlackGroupSessionSubject(params) {
@@ -3082,7 +3054,7 @@ function resolveSlackGroupSessionSubject(params) {
3082
3054
  }
3083
3055
  //#endregion
3084
3056
  //#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
3085
- const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-By7tvpvv.mjs").then((n) => n._));
3057
+ const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-D5S6D_BU.mjs").then((n) => n._));
3086
3058
  const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
3087
3059
  function formatSlackThreadLabelSnippet(text) {
3088
3060
  return truncateUtf16Safe(text.replace(/\s+/g, " "), 80);
@@ -3131,13 +3103,14 @@ async function resolveSlackThreadContextData(params) {
3131
3103
  const isOutboundOnlyThreadSession = threadSessionFreshness !== void 0 && threadSessionFreshness.state !== "missing" && threadSessionFreshness.entry.lastInteractionAt === void 0 && threadSessionFreshness.entry.updatedAt !== 0;
3132
3104
  const shouldSeedInitialThreadContext = Boolean(params.isThreadReply && params.threadTs && (threadSessionFreshness ? threadSessionFreshness.state !== "fresh" || isOutboundOnlyThreadSession : threadSessionPreviousTimestamp === void 0));
3133
3105
  const shouldLoadInitialThreadHistory = shouldSeedInitialThreadContext || params.forceInitialHistory === true;
3134
- if (!params.isThreadReply || !params.threadTs) return {
3106
+ const readThreadContextData = () => ({
3135
3107
  threadStarterBody,
3136
3108
  threadHistoryBody,
3137
3109
  shouldSeedInitialThreadContext,
3138
3110
  threadLabel,
3139
3111
  threadStarterMedia
3140
- };
3112
+ });
3113
+ if (!params.isThreadReply || !params.threadTs) return readThreadContextData();
3141
3114
  const starter = params.threadStarter;
3142
3115
  const starterSenderName = params.allowNameMatching && params.allowFromLower.length > 0 && starter?.userId ? (await params.ctx.resolveUserName(starter.userId, params.eventScope))?.name : void 0;
3143
3116
  params.assertHistoryCurrent?.();
@@ -3177,10 +3150,6 @@ async function resolveSlackThreadContextData(params) {
3177
3150
  unavailableMediaCount: attachmentContent.unavailableMediaCount,
3178
3151
  prependUnavailable: true
3179
3152
  });
3180
- if (threadStarterMedia) {
3181
- const starterPlaceholders = threadStarterMedia.map((item) => item.placeholder).join(", ");
3182
- logVerbose(`slack: hydrated thread starter file ${starterPlaceholders} from root message`);
3183
- }
3184
3153
  }
3185
3154
  } else threadLabel = `Slack thread ${params.roomLabel}`;
3186
3155
  const includeBotStarterAsRootContext = Boolean(starter?.text && starterIsCurrentBot && shouldSeedInitialThreadContext);
@@ -3189,7 +3158,6 @@ async function resolveSlackThreadContextData(params) {
3189
3158
  else if (includeBotStarterAsRootContext) {
3190
3159
  const snippet = formatSlackThreadLabelSnippet(starter?.text ?? "").trim();
3191
3160
  threadLabel = `Slack thread ${params.roomLabel}${snippet ? ` (assistant root): ${snippet}` : ""}`;
3192
- logVerbose("slack: retained current-bot thread starter as assistant root context");
3193
3161
  }
3194
3162
  const threadInitialHistoryLimit = Math.min(params.account.config?.thread?.initialHistoryLimit ?? 20, params.historyLimit ?? Number.POSITIVE_INFINITY);
3195
3163
  if (threadInitialHistoryLimit > 0 && shouldLoadInitialThreadHistory) {
@@ -3214,28 +3182,22 @@ async function resolveSlackThreadContextData(params) {
3214
3182
  }, "Slack automatic thread history omitted");
3215
3183
  }
3216
3184
  });
3217
- if (historyOmitted) return {
3218
- threadStarterBody,
3219
- threadHistoryBody,
3220
- shouldSeedInitialThreadContext,
3221
- threadLabel,
3222
- threadStarterMedia
3223
- };
3185
+ if (historyOmitted) return readThreadContextData();
3224
3186
  const enrichedStarter = starter && threadStarterBody && threadStarterBody !== starter.text ? {
3225
3187
  ...starter,
3226
3188
  text: threadStarterBody,
3227
3189
  ts: currentBotRootTs
3228
3190
  } : null;
3229
- const threadHistoryWithEnrichedRoot = enrichedStarter && !threadHistory.some((entry) => entry.ts === currentBotRootTs) ? [enrichedStarter, ...threadHistory.length >= threadInitialHistoryLimit ? threadHistory.slice(1) : threadHistory] : threadHistory;
3230
- const threadHistoryWithBotRoot = includeBotStarterAsRootContext && starter && !threadHistoryWithEnrichedRoot.some((entry) => entry.ts === currentBotRootTs) ? [{
3191
+ const rootContext = enrichedStarter ?? (includeBotStarterAsRootContext && starter ? {
3231
3192
  ...starter,
3232
3193
  ts: currentBotRootTs
3233
- }, ...threadHistoryWithEnrichedRoot] : threadHistoryWithEnrichedRoot;
3234
- if (threadHistoryWithBotRoot.length > 0) {
3194
+ } : null);
3195
+ const threadHistoryWithRoot = rootContext && !threadHistory.some((entry) => entry.ts === currentBotRootTs) ? [rootContext, ...enrichedStarter && threadHistory.length >= threadInitialHistoryLimit ? threadHistory.slice(1) : threadHistory] : threadHistory;
3196
+ if (threadHistoryWithRoot.length > 0) {
3235
3197
  const retainCurrentBotHistory = params.isGroupDm && (isMissingThreadSession || isOutboundOnlyThreadSession);
3236
- const threadHistoryWithoutCurrentBot = threadHistoryWithBotRoot.filter((entry) => !isCurrentBotAuthor(entry) || retainCurrentBotHistory || includeBotStarterAsRootContext && entry.ts === currentBotRootTs);
3237
- const omittedCurrentBotHistoryCount = threadHistoryWithBotRoot.length - threadHistoryWithoutCurrentBot.length;
3238
- const userMapForFilter = params.contextVisibilityMode !== "all" && params.allowNameMatching && params.allowFromLower.length > 0 ? await resolveThreadUserMap(threadHistoryWithoutCurrentBot) : /* @__PURE__ */ new Map();
3198
+ const threadHistoryWithoutCurrentBot = threadHistoryWithRoot.filter((entry) => !isCurrentBotAuthor(entry) || retainCurrentBotHistory || includeBotStarterAsRootContext && entry.ts === currentBotRootTs);
3199
+ const omittedCurrentBotHistoryCount = threadHistoryWithRoot.length - threadHistoryWithoutCurrentBot.length;
3200
+ const userMapForFilter = params.contextVisibilityMode !== "all" && params.allowNameMatching && params.allowFromLower.length > 0 ? await resolveThreadUserMap(threadHistoryWithoutCurrentBot) : void 0;
3239
3201
  params.assertHistoryCurrent?.();
3240
3202
  const { items: filteredThreadHistory, omitted: omittedHistoryCount } = params.contextVisibilityMode === "all" ? {
3241
3203
  items: threadHistoryWithoutCurrentBot,
@@ -3246,7 +3208,7 @@ async function resolveSlackThreadContextData(params) {
3246
3208
  kind: "thread",
3247
3209
  isSenderAllowed: (historyMsg) => {
3248
3210
  if (isCurrentBotAuthor(historyMsg)) return true;
3249
- const msgUser = historyMsg.userId ? userMapForFilter.get(historyMsg.userId) : null;
3211
+ const msgUser = historyMsg.userId ? userMapForFilter?.get(historyMsg.userId) : null;
3250
3212
  return resolveSlackUserAllowed({
3251
3213
  allowList: params.allowFromLower,
3252
3214
  teamId: params.eventScope?.teamId ?? params.ctx.teamId,
@@ -3256,7 +3218,7 @@ async function resolveSlackThreadContextData(params) {
3256
3218
  });
3257
3219
  }
3258
3220
  });
3259
- const userMap = await resolveThreadUserMap(filteredThreadHistory);
3221
+ const userMap = userMapForFilter ?? await resolveThreadUserMap(filteredThreadHistory);
3260
3222
  params.assertHistoryCurrent?.();
3261
3223
  if (omittedHistoryCount > 0 || omittedCurrentBotHistoryCount > 0) logVerbose(`slack: omitted ${omittedHistoryCount + omittedCurrentBotHistoryCount} thread message(s) from context (mode=${params.contextVisibilityMode})`);
3262
3224
  const historyParts = [];
@@ -3275,19 +3237,10 @@ async function resolveSlackThreadContextData(params) {
3275
3237
  envelope: params.envelopeOptions
3276
3238
  }));
3277
3239
  }
3278
- if (historyParts.length > 0) {
3279
- threadHistoryBody = historyParts.join("\n\n");
3280
- logVerbose(`slack: populated thread history with ${filteredThreadHistory.length} messages for new session`);
3281
- }
3240
+ if (historyParts.length > 0) threadHistoryBody = historyParts.join("\n\n");
3282
3241
  }
3283
3242
  }
3284
- return {
3285
- threadStarterBody,
3286
- threadHistoryBody,
3287
- shouldSeedInitialThreadContext,
3288
- threadLabel,
3289
- threadStarterMedia
3290
- };
3243
+ return readThreadContextData();
3291
3244
  }
3292
3245
  //#endregion
3293
3246
  //#region src/channels/mention-gating.ts
@@ -3358,53 +3311,46 @@ function collectSlackMentionIds(text, regex) {
3358
3311
  }
3359
3312
  return [...ids];
3360
3313
  }
3361
- async function readSlackSubteamUsers(params) {
3362
- let bySubteam = subteamMemberCache.get(params.client);
3363
- if (!bySubteam) {
3364
- bySubteam = /* @__PURE__ */ new Map();
3365
- subteamMemberCache.set(params.client, bySubteam);
3366
- }
3367
- const cacheKey = `${normalizeSlackId(params.teamId) ?? ""}:${params.subteamId}`;
3368
- const cached = bySubteam.get(cacheKey);
3369
- const now = asDateTimestampMs(params.now);
3370
- if (cached) {
3371
- if (now !== void 0 && asDateTimestampMs(cached.expiresAt) !== void 0 && cached.expiresAt > now) return cached.users;
3372
- bySubteam.delete(cacheKey);
3373
- }
3374
- try {
3375
- const response = await params.client.usergroups.users.list({
3376
- usergroup: params.subteamId,
3377
- ...params.teamId ? { team_id: params.teamId } : {}
3378
- });
3379
- if (!response.ok) {
3380
- params.log?.(`slack: failed to resolve user-group mention ${params.subteamId}: ${response.error ?? "unknown_error"}`);
3381
- return /* @__PURE__ */ new Set();
3382
- }
3383
- const users = new Set((response.users ?? []).map(normalizeSlackId).filter((userId) => userId !== void 0));
3384
- const expiresAt = resolveExpiresAtMsFromDurationMs(SUBTEAM_MEMBER_CACHE_TTL_MS, { nowMs: params.now });
3385
- if (expiresAt !== void 0) bySubteam.set(cacheKey, {
3386
- expiresAt,
3387
- users
3388
- });
3389
- return users;
3390
- } catch (err) {
3391
- params.log?.(`slack: failed to resolve user-group mention ${params.subteamId}: ${formatErrorMessage(err)}`);
3392
- return /* @__PURE__ */ new Set();
3393
- }
3394
- }
3395
3314
  async function isSlackSubteamMentionForBot(params) {
3396
3315
  const botUserId = normalizeSlackId(params.botUserId);
3397
3316
  if (!botUserId) return false;
3398
3317
  const subteamIds = collectSlackMentionIds(params.text, SUBTEAM_MENTION_RE);
3399
3318
  if (subteamIds.length === 0) return false;
3400
- const now = params.now ?? Date.now();
3401
- for (const subteamId of subteamIds) if ((await readSlackSubteamUsers({
3402
- client: params.client,
3403
- subteamId,
3404
- teamId: normalizeOptionalString(params.teamId),
3405
- now,
3406
- log: params.log
3407
- })).has(botUserId)) return true;
3319
+ const now = Date.now();
3320
+ for (const subteamId of subteamIds) {
3321
+ const { client, log } = params;
3322
+ const teamId = normalizeOptionalString(params.teamId);
3323
+ const bySubteam = getOrCreateWeakOwnerMap(subteamMemberCache, client);
3324
+ const cacheKey = `${normalizeSlackId(teamId) ?? ""}:${subteamId}`;
3325
+ const cached = bySubteam.get(cacheKey);
3326
+ if (cached) {
3327
+ if (cached.expiresAt > now) {
3328
+ if (cached.users.has(botUserId)) return true;
3329
+ continue;
3330
+ }
3331
+ bySubteam.delete(cacheKey);
3332
+ }
3333
+ let users;
3334
+ try {
3335
+ const response = await client.usergroups.users.list({
3336
+ usergroup: subteamId,
3337
+ ...teamId ? { team_id: teamId } : {}
3338
+ });
3339
+ if (!response.ok) {
3340
+ log?.(`slack: failed to resolve user-group mention ${subteamId}: ${response.error ?? "unknown_error"}`);
3341
+ continue;
3342
+ }
3343
+ users = new Set((response.users ?? []).map(normalizeSlackId).filter((userId) => userId !== void 0));
3344
+ bySubteam.set(cacheKey, {
3345
+ expiresAt: now + SUBTEAM_MEMBER_CACHE_TTL_MS,
3346
+ users
3347
+ });
3348
+ } catch (err) {
3349
+ log?.(`slack: failed to resolve user-group mention ${subteamId}: ${formatErrorMessage(err)}`);
3350
+ continue;
3351
+ }
3352
+ if (users.has(botUserId)) return true;
3353
+ }
3408
3354
  return false;
3409
3355
  }
3410
3356
  //#endregion
@@ -3415,18 +3361,6 @@ const SLACK_USER_MENTION_RE = /<@([^>|]+)(?:\|[^>]+)?>/g;
3415
3361
  const SLACK_SUBTEAM_MENTION_RE = /<!subteam\^([^>|]+)(?:\|[^>]+)?>/g;
3416
3362
  const SLACK_SUBTEAM_MENTION_MARKER = "<!subteam^";
3417
3363
  const SLACK_CHANNEL_ACCESS_DOCS_URL = "https://docs.openclaw.ai/channels/slack#access-control-and-routing";
3418
- function mergeSlackAssistantThreadContext(primary, fallback) {
3419
- if (!primary) return fallback;
3420
- if (!fallback) return primary;
3421
- return {
3422
- assistantChannelId: primary.assistantChannelId || fallback.assistantChannelId,
3423
- threadTs: primary.threadTs || fallback.threadTs,
3424
- userId: primary.userId ?? fallback.userId,
3425
- channelId: primary.channelId ?? fallback.channelId,
3426
- teamId: primary.teamId ?? fallback.teamId,
3427
- enterpriseId: primary.enterpriseId !== void 0 ? primary.enterpriseId : fallback.enterpriseId
3428
- };
3429
- }
3430
3364
  function hasSlackAssistantThreadMetadata(context) {
3431
3365
  return Boolean(context?.channelId || context?.teamId || context?.enterpriseId !== void 0);
3432
3366
  }
@@ -3440,7 +3374,6 @@ function resolveSlackMessageAssistantThreadContext(message) {
3440
3374
  return {
3441
3375
  assistantChannelId,
3442
3376
  threadTs,
3443
- userId: normalizeOptionalString(thread.user_id) ?? message.user,
3444
3377
  channelId: normalizeOptionalString(context?.channel_id),
3445
3378
  teamId: normalizeOptionalString(context?.team_id),
3446
3379
  enterpriseId: context?.enterprise_id === null ? null : normalizeOptionalString(context?.enterprise_id)
@@ -3454,8 +3387,7 @@ async function restoreSlackAssistantThreadContextFromMetadata(params) {
3454
3387
  return await readSlackAssistantThreadContext({
3455
3388
  client: params.eventScope?.client ?? params.ctx.app.client,
3456
3389
  channelId: params.message.channel,
3457
- threadTs,
3458
- userId: params.message.user
3390
+ threadTs
3459
3391
  });
3460
3392
  } catch (err) {
3461
3393
  logVerbose(`slack assistant context restore failed channel=${params.message.channel} ts=${threadTs}: ${formatErrorMessage(err)}`);
@@ -3468,41 +3400,40 @@ function resolveCachedMentionRegexes(ctx, agentId, options) {
3468
3400
  normalizeOptionalString(options?.conversationId ?? void 0),
3469
3401
  options?.providerPolicy ? JSON.stringify(options.providerPolicy) : ""
3470
3402
  ].join("");
3471
- let byAgent = mentionRegexCache.get(ctx);
3472
- if (!byAgent) {
3473
- byAgent = /* @__PURE__ */ new Map();
3474
- mentionRegexCache.set(ctx, byAgent);
3475
- }
3403
+ const byAgent = getOrCreateWeakOwnerMap(mentionRegexCache, ctx);
3476
3404
  const cached = byAgent.get(key);
3477
3405
  if (cached) return cached;
3478
3406
  const built = buildMentionRegexes(ctx.cfg, agentId, options);
3479
3407
  byAgent.set(key, built);
3480
3408
  return built;
3481
3409
  }
3482
- function collectSlackMentionMetadata(text) {
3483
- return {
3484
- mentionedUserIds: collectSlackMentionIds(text, SLACK_USER_MENTION_RE),
3485
- mentionedSubteamIds: collectSlackMentionIds(text, SLACK_SUBTEAM_MENTION_RE),
3486
- hasAnyMention: SLACK_ANY_MENTION_RE.test(text),
3487
- hasSubteamMention: text.includes(SLACK_SUBTEAM_MENTION_MARKER)
3410
+ async function prepareSlackMessage(params) {
3411
+ const { ctx, account, message, opts } = params;
3412
+ const drop = (reason, parentUserId) => {
3413
+ ctx.logger.info({
3414
+ provider: "slack",
3415
+ accountId: account.accountId,
3416
+ teamId: opts.eventScope?.teamId ?? ctx.teamId,
3417
+ channelId: message.channel,
3418
+ messageTs: message.ts,
3419
+ source: opts.source,
3420
+ reason,
3421
+ ...parentUserId ? { parentUserId } : {}
3422
+ }, "Slack inbound event rejected during preparation");
3423
+ return null;
3424
+ };
3425
+ const slackClient = opts.eventScope?.client ?? ctx.app.client;
3426
+ const threadStarterWorkspaceScope = {
3427
+ accountId: account.accountId,
3428
+ teamId: opts.eventScope?.teamId ?? ctx.teamId
3488
3429
  };
3489
- }
3490
- function resolveSlackMentionSource(params) {
3491
- if (params.explicitBotMention) return "explicit_bot";
3492
- if (params.explicitSubteamMention) return "subteam";
3493
- if (params.shouldBypassMention) return "command_bypass";
3494
- if (params.wasMentioned) return "mention_pattern";
3495
- if (params.matchedImplicitMentionKinds.length > 0) return "implicit_thread";
3496
- return "none";
3497
- }
3498
- async function resolveSlackConversationContext(params) {
3499
- const { ctx, account, message } = params;
3500
3430
  const cfg = ctx.cfg;
3431
+ const conversationEventScope = opts.eventScope;
3501
3432
  let channelInfo = {};
3502
3433
  let resolvedChannelType = normalizeSlackChannelType(message.channel_type, message.channel);
3503
3434
  if (resolvedChannelType !== "im" && message.channel_type !== "im") {
3504
- channelInfo = await ctx.resolveChannelName(message.channel, params.eventScope);
3505
- resolvedChannelType = normalizeSlackChannelType(message.channel_type ?? channelInfo.type ?? ctx.recallSlackChannelType(message.channel, params.eventScope), message.channel);
3435
+ channelInfo = await ctx.resolveChannelName(message.channel, conversationEventScope);
3436
+ resolvedChannelType = normalizeSlackChannelType(message.channel_type ?? channelInfo.type ?? ctx.recallSlackChannelType(message.channel, conversationEventScope), message.channel);
3506
3437
  }
3507
3438
  const channelName = channelInfo.name;
3508
3439
  const isDirectMessage = resolvedChannelType === "im";
@@ -3510,7 +3441,7 @@ async function resolveSlackConversationContext(params) {
3510
3441
  const isRoom = resolvedChannelType === "channel" || resolvedChannelType === "group";
3511
3442
  const isRoomish = isRoom || isGroupDm;
3512
3443
  const channelConfig = isRoom ? resolveSlackChannelConfig({
3513
- teamId: params.eventScope?.teamId ?? ctx.teamId,
3444
+ teamId: conversationEventScope?.teamId ?? ctx.teamId,
3514
3445
  allowUnscoped: ctx.installationIdentity?.kind !== "enterprise",
3515
3446
  channelId: message.channel,
3516
3447
  channelName,
@@ -3520,22 +3451,19 @@ async function resolveSlackConversationContext(params) {
3520
3451
  allowNameMatching: ctx.allowNameMatching
3521
3452
  }) : null;
3522
3453
  const allowBotsSetting = channelConfig?.allowBots ?? account.config?.allowBots ?? cfg.channels?.slack?.allowBots ?? true;
3523
- return {
3524
- channelInfo,
3525
- channelName,
3526
- resolvedChannelType,
3527
- isDirectMessage,
3528
- isGroupDm,
3529
- isRoom,
3530
- isRoomish,
3531
- channelConfig,
3532
- allowBotsMode: allowBotsSetting === "mentions" ? "mentions" : allowBotsSetting ? "all" : "off",
3533
- isBotMessage: Boolean(message.bot_id)
3454
+ const allowBotsMode = allowBotsSetting === "mentions" ? "mentions" : allowBotsSetting ? "all" : "off";
3455
+ const isBotMessage = Boolean(message.bot_id);
3456
+ const messageText = message.text ?? "";
3457
+ const mentionMetadata = {
3458
+ mentionedUserIds: collectSlackMentionIds(messageText, SLACK_USER_MENTION_RE),
3459
+ mentionedSubteamIds: collectSlackMentionIds(messageText, SLACK_SUBTEAM_MENTION_RE),
3460
+ hasAnyMention: SLACK_ANY_MENTION_RE.test(messageText),
3461
+ hasSubteamMention: messageText.includes(SLACK_SUBTEAM_MENTION_MARKER)
3534
3462
  };
3535
- }
3536
- async function authorizeSlackInboundMessage(params) {
3537
- const { ctx, account, message, conversation, drop } = params;
3538
- const { isDirectMessage, channelName, resolvedChannelType, isBotMessage, allowBotsMode } = conversation;
3463
+ const normalizedBotUserId = normalizeSlackId(ctx.botUserId);
3464
+ const explicitBotMention = opts.source === "app_mention" || Boolean(normalizedBotUserId && mentionMetadata.mentionedUserIds.includes(normalizedBotUserId));
3465
+ const authorizationEventScope = opts.eventScope;
3466
+ const onVisibleDrop = opts.onVisibleDrop;
3539
3467
  if (isBotMessage) {
3540
3468
  if (ctx.botUserId && message.user === ctx.botUserId || ctx.botId && message.bot_id === ctx.botId) return null;
3541
3469
  if (allowBotsMode === "off") return drop("bot-disabled");
@@ -3544,28 +3472,28 @@ async function authorizeSlackInboundMessage(params) {
3544
3472
  const senderId = message.user ?? (isBotMessage ? message.bot_id : void 0);
3545
3473
  if (!senderId) return drop("missing-sender");
3546
3474
  if (!ctx.isChannelAllowed({
3547
- teamId: params.eventScope?.teamId ?? ctx.teamId,
3475
+ teamId: authorizationEventScope?.teamId ?? ctx.teamId,
3548
3476
  channelId: message.channel,
3549
3477
  channelName,
3550
3478
  channelType: resolvedChannelType
3551
3479
  })) {
3552
- if (conversation.isRoom && ctx.groupPolicy === "allowlist" && params.explicitBotMention && !isBotMessage && message.user) {
3480
+ if (isRoom && ctx.groupPolicy === "allowlist" && explicitBotMention && !isBotMessage && message.user) {
3553
3481
  let subject = "This OpenClaw bot";
3554
3482
  if (ctx.botUserId) try {
3555
- const botIdentity = await ctx.resolveUserName(ctx.botUserId, params.eventScope);
3483
+ const botIdentity = await ctx.resolveUserName(ctx.botUserId, authorizationEventScope);
3556
3484
  const botName = normalizeOptionalString(botIdentity?.name);
3557
3485
  if (botName) subject = escapeSlackMrkdwn(botName);
3558
3486
  } catch (error) {
3559
3487
  logVerbose(`slack allowlist denial bot-name lookup failed: ${formatSlackError(error)}`);
3560
3488
  }
3561
3489
  try {
3562
- await (params.eventScope?.client ?? ctx.app.client).chat.postEphemeral({
3490
+ await (authorizationEventScope?.client ?? ctx.app.client).chat.postEphemeral({
3563
3491
  token: ctx.botToken,
3564
3492
  channel: message.channel,
3565
3493
  user: message.user,
3566
3494
  text: `${subject} can’t reply here because this channel isn’t in its OpenClaw channel allowlist. Ask the OpenClaw owner to allow this channel. <${SLACK_CHANNEL_ACCESS_DOCS_URL}|Learn how to configure Slack channel access.>`
3567
3495
  });
3568
- params.onVisibleDrop?.();
3496
+ onVisibleDrop?.();
3569
3497
  } catch (error) {
3570
3498
  ctx.runtime.error?.(`slack allowlist denial notice failed for channel ${message.channel}: ${formatSlackError(error)}`);
3571
3499
  }
@@ -3574,7 +3502,7 @@ async function authorizeSlackInboundMessage(params) {
3574
3502
  }
3575
3503
  const allowFromLower = await resolveSlackEffectiveAllowFrom(ctx, {
3576
3504
  includePairingStore: isDirectMessage,
3577
- eventScope: params.eventScope
3505
+ eventScope: authorizationEventScope
3578
3506
  });
3579
3507
  if (isDirectMessage) {
3580
3508
  const directUserId = message.user;
@@ -3584,16 +3512,16 @@ async function authorizeSlackInboundMessage(params) {
3584
3512
  ctx,
3585
3513
  accountId: account.accountId,
3586
3514
  senderId: directUserId,
3587
- eventScope: params.eventScope,
3515
+ eventScope: authorizationEventScope,
3588
3516
  allowFromLower,
3589
- resolveSenderName: (userId) => ctx.resolveUserName(userId, params.eventScope),
3517
+ resolveSenderName: (userId) => ctx.resolveUserName(userId, authorizationEventScope),
3590
3518
  sendPairingReply: async (text) => {
3591
3519
  await sendMessageSlack(message.channel, text, {
3592
3520
  cfg: ctx.cfg,
3593
3521
  token: ctx.botToken,
3594
- client: params.eventScope?.client ?? ctx.app.client,
3522
+ client: authorizationEventScope?.client ?? ctx.app.client,
3595
3523
  accountId: account.accountId,
3596
- eventScope: params.eventScope
3524
+ eventScope: authorizationEventScope
3597
3525
  });
3598
3526
  },
3599
3527
  onDisabled: () => {
@@ -3605,54 +3533,6 @@ async function authorizeSlackInboundMessage(params) {
3605
3533
  log: logVerbose
3606
3534
  })) return drop(dropReason);
3607
3535
  }
3608
- return {
3609
- senderId,
3610
- allowFromLower
3611
- };
3612
- }
3613
- async function prepareSlackMessage(params) {
3614
- const { ctx, account, message, opts } = params;
3615
- const drop = (reason, parentUserId) => {
3616
- ctx.logger.info({
3617
- provider: "slack",
3618
- accountId: account.accountId,
3619
- teamId: opts.eventScope?.teamId ?? ctx.teamId,
3620
- channelId: message.channel,
3621
- messageTs: message.ts,
3622
- source: opts.source,
3623
- reason,
3624
- ...parentUserId ? { parentUserId } : {}
3625
- }, "Slack inbound event rejected during preparation");
3626
- return null;
3627
- };
3628
- const slackClient = opts.eventScope?.client ?? ctx.app.client;
3629
- const threadStarterWorkspaceScope = {
3630
- accountId: account.accountId,
3631
- teamId: opts.eventScope?.teamId ?? ctx.teamId
3632
- };
3633
- const cfg = ctx.cfg;
3634
- const conversation = await resolveSlackConversationContext({
3635
- ctx,
3636
- account,
3637
- message,
3638
- eventScope: opts.eventScope
3639
- });
3640
- const { channelInfo, channelName, isDirectMessage, isGroupDm, isRoom, isRoomish, channelConfig, allowBotsMode, isBotMessage } = conversation;
3641
- const messageText = message.text ?? "";
3642
- const mentionMetadata = collectSlackMentionMetadata(messageText);
3643
- const normalizedBotUserId = normalizeSlackId(ctx.botUserId);
3644
- const authorization = await authorizeSlackInboundMessage({
3645
- ctx,
3646
- account,
3647
- message,
3648
- conversation,
3649
- explicitBotMention: opts.source === "app_mention" || Boolean(normalizedBotUserId && mentionMetadata.mentionedUserIds.includes(normalizedBotUserId)),
3650
- eventScope: opts.eventScope,
3651
- onVisibleDrop: opts.onVisibleDrop,
3652
- drop
3653
- });
3654
- if (!authorization) return null;
3655
- const { senderId, allowFromLower } = authorization;
3656
3536
  let resolvedSenderName = normalizeOptionalString(message.username);
3657
3537
  const resolveSenderName = async () => {
3658
3538
  if (resolvedSenderName) return resolvedSenderName;
@@ -3688,9 +3568,17 @@ async function prepareSlackMessage(params) {
3688
3568
  });
3689
3569
  const explicitlyMentioned = explicitlyMentionedBotUser || explicitlyMentionedBotSubteam || opts.source === "app_mention";
3690
3570
  const channelRequireMention = channelConfig?.requireMention ?? ctx.defaultRequireMention ?? true;
3691
- const chatType = resolveSlackChatType(conversation.resolvedChannelType);
3571
+ const chatType = resolveSlackChatType(resolvedChannelType);
3692
3572
  const restoredAssistantThreadContext = await restoredAssistantThreadContextPromise;
3693
- const assistantThreadContext = mergeSlackAssistantThreadContext(messageAssistantThreadContext, cachedAssistantThreadContext ?? restoredAssistantThreadContext);
3573
+ const primary = messageAssistantThreadContext;
3574
+ const fallback = cachedAssistantThreadContext ?? restoredAssistantThreadContext;
3575
+ const assistantThreadContext = !primary ? fallback : !fallback ? primary : {
3576
+ assistantChannelId: primary.assistantChannelId || fallback.assistantChannelId,
3577
+ threadTs: primary.threadTs || fallback.threadTs,
3578
+ channelId: primary.channelId ?? fallback.channelId,
3579
+ teamId: primary.teamId ?? fallback.teamId,
3580
+ enterpriseId: primary.enterpriseId !== void 0 ? primary.enterpriseId : fallback.enterpriseId
3581
+ };
3694
3582
  const assistantThreadContextToCache = messageAssistantThreadContext || restoredAssistantThreadContext ? assistantThreadContext : void 0;
3695
3583
  if (assistantThreadContextToCache) ctx.saveSlackAssistantThreadContext(assistantThreadContextToCache, opts.eventScope);
3696
3584
  const canUseManagedView = isDirectMessage && !opts.eventScope && !assistantThreadContext;
@@ -3754,19 +3642,16 @@ async function prepareSlackMessage(params) {
3754
3642
  let mentionRegexes = buildPolicyMentionRegexes(routing.route.agentId);
3755
3643
  let wasMentioned = resolveWasMentioned(mentionRegexes);
3756
3644
  const hasBoundSession = Boolean(routing.runtimeBoundSessionKey || routing.configuredBindingSessionKey);
3757
- const { configuredBinding, configuredBindingSessionKey } = routing;
3645
+ const { configuredBinding } = routing;
3758
3646
  const isAssistantThreadMessage = Boolean(isDirectMessage && messageAssistantThreadContext);
3759
3647
  const forcedAssistantReplyThreadTs = Boolean(assistantThreadContext?.threadTs && (routing.isThreadReply || isAssistantThreadMessage || routing.replyToMode !== "off")) ? assistantThreadContext?.threadTs : void 0;
3760
3648
  const forcedReplyThreadTs = agentViewThreadTs ?? forcedAssistantReplyThreadTs;
3761
- if (routing.runtimeBinding && shouldLogVerbose()) logVerbose(`slack: routed via bound conversation ${routing.runtimeBinding.conversation.conversationId} -> ${routing.runtimeBinding.targetSessionKey}`);
3762
3649
  if (configuredBinding) {
3763
3650
  const ensured = await ensureConfiguredBindingRouteReady({
3764
3651
  cfg,
3765
3652
  bindingResolution: configuredBinding
3766
3653
  });
3767
- if (ensured.ok) {
3768
- if (shouldLogVerbose()) logVerbose(`slack: using configured ACP binding for ${configuredBinding.record.conversation.conversationId} -> ${configuredBindingSessionKey}`);
3769
- } else {
3654
+ if (!ensured.ok) {
3770
3655
  if (shouldLogVerbose()) logVerbose(`slack: configured ACP binding unavailable for ${configuredBinding.record.conversation.conversationId}: ${ensured.error}`);
3771
3656
  return drop("configured-binding-unavailable");
3772
3657
  }
@@ -3804,7 +3689,11 @@ async function prepareSlackMessage(params) {
3804
3689
  ctx,
3805
3690
  account,
3806
3691
  message,
3807
- conversation,
3692
+ conversation: {
3693
+ isRoom,
3694
+ isDirectMessage,
3695
+ channelConfig
3696
+ },
3808
3697
  thread: routing,
3809
3698
  wasMentioned,
3810
3699
  teamId: opts.eventScope?.teamId,
@@ -3822,14 +3711,14 @@ async function prepareSlackMessage(params) {
3822
3711
  const textForCommandDetection = stripSlackMentionsForCommandDetection(message.text ?? "");
3823
3712
  const hasControlCommandInMessage = hasControlCommand(textForCommandDetection, cfg);
3824
3713
  const hasAbortRequest = isAbortRequestText(textForCommandDetection);
3825
- const channelUsersAllowlistConfigured = isRoom && Array.isArray(channelConfig?.users) && channelConfig.users.length > 0;
3714
+ const channelUsersAllowlistConfigured = isRoom && (channelConfig?.users?.length ?? 0) > 0;
3826
3715
  const resolveMessageIngress = async (contextBinding, threadId) => await resolveSlackCommandIngress({
3827
3716
  ctx,
3828
3717
  teamId: opts.eventScope?.teamId ?? ctx.teamId,
3829
3718
  senderId,
3830
3719
  senderAuthentication: message.user ? opts.senderAuthentication : void 0,
3831
3720
  senderName: senderNameForAuth,
3832
- channelType: conversation.resolvedChannelType ?? "channel",
3721
+ channelType: resolvedChannelType ?? "channel",
3833
3722
  channelId: message.channel,
3834
3723
  threadId,
3835
3724
  ownerAllowFromLower: allowFromLower,
@@ -3964,13 +3853,7 @@ async function prepareSlackMessage(params) {
3964
3853
  const effectiveWasMentioned = mentionDecision.effectiveWasMentioned;
3965
3854
  const shouldBypassMention = mentionDecision.shouldBypassMention;
3966
3855
  const matchedImplicitMentionKinds = mentionDecision.matchedImplicitMentionKinds;
3967
- const mentionSource = resolveSlackMentionSource({
3968
- explicitBotMention: explicitlyMentionedBotUser || opts.source === "app_mention",
3969
- explicitSubteamMention: explicitlyMentionedBotSubteam,
3970
- matchedImplicitMentionKinds,
3971
- shouldBypassMention,
3972
- wasMentioned
3973
- });
3856
+ const mentionSource = explicitlyMentionedBotUser || opts.source === "app_mention" ? "explicit_bot" : explicitlyMentionedBotSubteam ? "subteam" : shouldBypassMention ? "command_bypass" : wasMentioned ? "mention_pattern" : matchedImplicitMentionKinds.length > 0 ? "implicit_thread" : "none";
3974
3857
  if (isBotMessage && allowBotsMode === "mentions") {
3975
3858
  if (!(isDirectMessage || effectiveWasMentioned || shouldBypassMention)) return drop("bot-missing-mention");
3976
3859
  }
@@ -4301,7 +4184,6 @@ async function prepareSlackMessage(params) {
4301
4184
  originatingTo: preflightChannelTarget,
4302
4185
  messageThreadId: threadContext.messageThreadId
4303
4186
  });
4304
- if (shouldLogVerbose()) logVerbose(`slack inbound: account=${route.accountId} agent=${route.agentId} channel=${message.channel} message_ts=${message.ts ?? "unknown"} thread_ts=${effectiveMessageThreadId ?? "none"} from=${slackFrom} chat=${chatType} chars=${rawBody.length}`);
4305
4187
  const updateLastRouteSessionKey = resolveInboundLastRouteSessionKey({
4306
4188
  route,
4307
4189
  sessionKey