@openclaw/slack 2026.10.10 → 2026.11.5-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/dist/.setup/{account-inspect-Bc7TVU8F.mjs → account-inspect-DgQgj0wk.mjs} +11 -8
  2. package/dist/.setup/{accounts-CEgdyqoW.mjs → accounts-EaPP0fFY.mjs} +10 -1
  3. package/dist/.setup/{action-runtime-DPoxYao5.mjs → action-runtime-CK9QZgQl.mjs} +38 -45
  4. package/dist/.setup/action-runtime.runtime-CpZktret.mjs +1 -0
  5. package/dist/.setup/{actions-PF2cP6to.mjs → actions-s3CcFO45.mjs} +89 -76
  6. package/dist/.setup/{approval-handler.runtime-BtRklemy.mjs → approval-handler.runtime-Ch7BecYK.mjs} +26 -30
  7. package/dist/.setup/{approval-message-updates-CpwbsEj6.mjs → approval-message-updates-CqcRoa28.mjs} +1 -1
  8. package/dist/.setup/{channel-B6bRmYmX.mjs → channel-CAvo_WNZ.mjs} +566 -338
  9. package/dist/.setup/{channel-type-Dh4vwh1Y.mjs → channel-type-9Lw6yLf2.mjs} +4 -8
  10. package/dist/.setup/{channel.setup-BBzT6uKA.mjs → channel.setup-BlgXNuCJ.mjs} +13 -13
  11. package/dist/.setup/{config-schema-Cz3uId-c.mjs → config-schema-Bx1tCZB0.mjs} +3 -7
  12. package/dist/.setup/cursor-pages-jVn3Vkfi.mjs +309 -0
  13. package/dist/.setup/{directory-config-BEPra1E2.mjs → directory-config-CGvKYg9F.mjs} +3 -3
  14. package/dist/.setup/{directory-live-BkUwPrS0.mjs → directory-live-DNdw97LA.mjs} +4 -4
  15. package/dist/.setup/{doctor-contract-HK8aedwU.mjs → doctor-contract-BB2KJ9Ni.mjs} +37 -45
  16. package/dist/.setup/{message-tool-api-DeiQJoRV.mjs → message-tool-api-B38BpEaE.mjs} +4 -3
  17. package/dist/.setup/{monitor-DsZUQtTM.mjs → monitor-BD86SotB.mjs} +3397 -3672
  18. package/dist/.setup/{outbound-adapter-C0R5jz6S.mjs → outbound-adapter-BH6iPWBj.mjs} +30 -36
  19. package/dist/.setup/paths-CQZI_5WY.mjs +7 -0
  20. package/dist/.setup/{pipeline.runtime-CIFVL0kf.mjs → pipeline.runtime-C4ELxaRU.mjs} +415 -534
  21. package/dist/.setup/{policy-Bp1FvE6m.mjs → policy-C6_DdlSf.mjs} +1 -2
  22. package/dist/.setup/{session-status-CHkEn3HM.mjs → probe-B_Ax1vS1.mjs} +985 -622
  23. package/dist/.setup/question-delivery-D3qAIwuv.mjs +38 -0
  24. package/dist/.setup/{relay-source-DFBDTA5Z.mjs → relay-source-a8jodKuO.mjs} +11 -4
  25. package/dist/.setup/{replies-7DRN46EL.mjs → replies-Bq7n0gfM.mjs} +34 -19
  26. package/dist/.setup/{reply-blocks-DOjiDr44.mjs → reply-blocks-DyYYRrP_.mjs} +200 -205
  27. package/dist/.setup/{resolve-channels-BNQ1KZXh.mjs → resolve-channels-CF2JME9q.mjs} +4 -7
  28. package/dist/.setup/{resolve-users-C4dC8Z7D.mjs → resolve-users-DDcCAjH1.mjs} +8 -16
  29. package/dist/.setup/{scopes-C1aeCBa6.mjs → scopes-CzHC_dwV.mjs} +2 -1
  30. package/dist/.setup/{secret-contract-D0UY2JWF.mjs → secret-contract-CqVqR7MP.mjs} +4 -2
  31. package/dist/.setup/{secret-contract-api-BhzGpUb0.mjs → secret-contract-api-DaCXZ47j.mjs} +1 -1
  32. package/dist/.setup/{send-C5uXkmZV.mjs → send-WJZ73VSf.mjs} +297 -333
  33. package/dist/.setup/{send-results-D-o6PgtS.mjs → send-results-Bm6V1ekL.mjs} +3 -4
  34. package/dist/.setup/setup-plugin-api-DJXIB8CF.mjs +2 -0
  35. package/dist/.setup/{setup-surface-CtM1pm-9.mjs → setup-surface-uIqP2B0Y.mjs} +5 -6
  36. package/dist/.setup/{slash-dispatch.runtime-Bnn9a5qB.mjs → slash-dispatch.runtime-Cwuwv7FL.mjs} +2 -2
  37. package/dist/.setup/{target-parsing-0pRZtahO.mjs → target-parsing-DwYAP3ts.mjs} +1 -2
  38. package/dist/.setup/{targets-BqXz7CpG.mjs → targets-eeOxA_b_.mjs} +1 -1
  39. package/dist/.source-module-urls/c5ffd0957b234c7c12090e7de4eca2482ef339b8561e078da55b734211b0ba58.json +1 -0
  40. package/dist/account-inspect-api.js +1 -1
  41. package/dist/api.js +16 -17
  42. package/dist/channel-config-api.js +1 -1
  43. package/dist/channel-plugin-api.js +1 -1
  44. package/dist/directory-contract-api.js +1 -1
  45. package/dist/doctor-contract-api.js +1 -1
  46. package/dist/index.js +0 -6
  47. package/dist/message-tool-api.js +1 -1
  48. package/dist/runtime-api.js +10 -12
  49. package/dist/secret-contract-api.js +1 -1
  50. package/dist/setup-entry.js +2 -2
  51. package/dist/setup-plugin-api.js +1 -1
  52. package/node_modules/@slack/bolt/node_modules/@types/node/README.md +1 -1
  53. package/node_modules/@slack/bolt/node_modules/@types/node/package.json +2 -2
  54. package/node_modules/@slack/bolt/node_modules/@types/node/util.d.ts +6 -3
  55. package/node_modules/@slack/bolt/node_modules/body-parser/README.md +13 -17
  56. package/node_modules/@slack/bolt/node_modules/body-parser/lib/read.js +57 -74
  57. package/node_modules/@slack/bolt/node_modules/body-parser/lib/types/json.js +11 -11
  58. package/node_modules/@slack/bolt/node_modules/body-parser/lib/types/urlencoded.js +5 -5
  59. package/node_modules/@slack/bolt/node_modules/body-parser/lib/utils.js +15 -0
  60. package/node_modules/@slack/bolt/node_modules/body-parser/package.json +12 -7
  61. package/node_modules/@slack/bolt/node_modules/content-disposition/README.md +105 -35
  62. package/node_modules/@slack/bolt/node_modules/content-disposition/dist/index.d.ts +72 -0
  63. package/node_modules/@slack/bolt/node_modules/content-disposition/dist/index.js +388 -0
  64. package/node_modules/@slack/bolt/node_modules/content-disposition/dist/index.js.map +1 -0
  65. package/node_modules/@slack/bolt/node_modules/content-disposition/package.json +31 -19
  66. package/node_modules/@slack/bolt/node_modules/content-type/README.md +34 -57
  67. package/node_modules/@slack/bolt/node_modules/content-type/package.json +35 -25
  68. package/node_modules/@slack/bolt/node_modules/express/Readme.md +15 -9
  69. package/node_modules/@slack/bolt/node_modules/express/lib/application.js +2 -2
  70. package/node_modules/@slack/bolt/node_modules/express/lib/request.js +32 -19
  71. package/node_modules/@slack/bolt/node_modules/express/lib/response.js +27 -30
  72. package/node_modules/@slack/bolt/node_modules/express/package.json +10 -10
  73. package/node_modules/@slack/bolt/node_modules/qs/.github/THREAT_MODEL.md +4 -2
  74. package/node_modules/@slack/bolt/node_modules/qs/CHANGELOG.md +38 -14
  75. package/node_modules/@slack/bolt/node_modules/qs/README.md +31 -3
  76. package/node_modules/@slack/bolt/node_modules/qs/dist/qs.js +20 -20
  77. package/node_modules/@slack/bolt/node_modules/qs/lib/parse.js +94 -4
  78. package/node_modules/@slack/bolt/node_modules/qs/lib/stringify.js +4 -4
  79. package/node_modules/@slack/bolt/node_modules/qs/lib/utils.js +130 -6
  80. package/node_modules/@slack/bolt/node_modules/qs/package.json +4 -4
  81. package/node_modules/@slack/bolt/node_modules/qs/test/parse.js +174 -0
  82. package/node_modules/@slack/bolt/node_modules/qs/test/stringify.js +263 -33
  83. package/node_modules/@slack/bolt/node_modules/qs/test/utils.js +184 -0
  84. package/node_modules/@slack/web-api/node_modules/@types/node/README.md +1 -1
  85. package/node_modules/@slack/web-api/node_modules/@types/node/package.json +2 -2
  86. package/node_modules/@slack/web-api/node_modules/@types/node/util.d.ts +6 -3
  87. package/node_modules/typebox/build/format/duration.d.mts +2 -2
  88. package/node_modules/typebox/build/format/duration.mjs +2 -2
  89. package/node_modules/typebox/build/format/idn_email.d.mts +1 -0
  90. package/node_modules/typebox/build/format/idn_email.mjs +1 -0
  91. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -1
  92. package/node_modules/typebox/build/format/idn_hostname.mjs +1 -1
  93. package/node_modules/typebox/build/format/ipv4.d.mts +2 -2
  94. package/node_modules/typebox/build/format/ipv4.mjs +2 -2
  95. package/node_modules/typebox/build/format/ipv6.d.mts +1 -1
  96. package/node_modules/typebox/build/format/ipv6.mjs +1 -1
  97. package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
  98. package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
  99. package/node_modules/typebox/build/schema/build.mjs +1 -0
  100. package/node_modules/typebox/build/schema/engine/_unique.d.mts +4 -2
  101. package/node_modules/typebox/build/schema/engine/_unique.mjs +7 -3
  102. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +3 -3
  103. package/node_modules/typebox/build/schema/engine/additionalProperties.d.mts +0 -3
  104. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +15 -17
  105. package/node_modules/typebox/build/schema/engine/contains.mjs +3 -3
  106. package/node_modules/typebox/build/schema/engine/index.d.mts +1 -0
  107. package/node_modules/typebox/build/schema/engine/index.mjs +1 -0
  108. package/node_modules/typebox/build/schema/engine/items.mjs +7 -4
  109. package/node_modules/typebox/build/schema/engine/maxContains.mjs +2 -2
  110. package/node_modules/typebox/build/schema/engine/minContains.mjs +3 -3
  111. package/node_modules/typebox/build/schema/engine/oneOf.mjs +2 -2
  112. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +2 -2
  113. package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -2
  114. package/node_modules/typebox/build/schema/engine/unevaluatedItems.mjs +2 -2
  115. package/node_modules/typebox/build/schema/engine/unevaluatedProperties.mjs +2 -2
  116. package/node_modules/typebox/build/system/hashing/hash.mjs +63 -34
  117. package/node_modules/typebox/build/system/locale/en_US.mjs +2 -1
  118. package/node_modules/typebox/build/system/memory/assign.d.mts +2 -8
  119. package/node_modules/typebox/build/system/memory/assign.mjs +13 -6
  120. package/node_modules/typebox/build/system/memory/clone.d.mts +4 -5
  121. package/node_modules/typebox/build/system/memory/clone.mjs +8 -96
  122. package/node_modules/typebox/build/system/memory/create.d.mts +1 -5
  123. package/node_modules/typebox/build/system/memory/create.mjs +18 -13
  124. package/node_modules/typebox/build/system/memory/deep.d.mts +2 -0
  125. package/node_modules/typebox/build/system/memory/deep.mjs +108 -0
  126. package/node_modules/typebox/build/system/memory/discard.d.mts +1 -1
  127. package/node_modules/typebox/build/system/memory/discard.mjs +9 -8
  128. package/node_modules/typebox/build/system/memory/finalize.d.mts +4 -0
  129. package/node_modules/typebox/build/system/memory/finalize.mjs +30 -0
  130. package/node_modules/typebox/build/system/memory/keys.d.mts +2 -0
  131. package/node_modules/typebox/build/system/memory/keys.mjs +8 -0
  132. package/node_modules/typebox/build/system/memory/update.d.mts +1 -4
  133. package/node_modules/typebox/build/system/memory/update.mjs +43 -16
  134. package/node_modules/typebox/build/type/engine/call/index.d.mts +1 -0
  135. package/node_modules/typebox/build/type/engine/call/index.mjs +1 -0
  136. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +2 -24
  137. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +2 -29
  138. package/node_modules/typebox/build/type/engine/evaluate/index.d.mts +1 -0
  139. package/node_modules/typebox/build/type/engine/evaluate/index.mjs +1 -0
  140. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +5 -1
  141. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +6 -1
  142. package/node_modules/typebox/build/type/engine/evaluate/property.d.mts +30 -0
  143. package/node_modules/typebox/build/type/engine/evaluate/property.mjs +29 -0
  144. package/node_modules/typebox/build/type/engine/immutable/instantiate_add.d.mts +1 -3
  145. package/node_modules/typebox/build/type/engine/immutable/instantiate_add.mjs +1 -4
  146. package/node_modules/typebox/build/type/engine/object/from_intersect.d.mts +2 -2
  147. package/node_modules/typebox/build/type/engine/object/from_intersect.mjs +2 -2
  148. package/node_modules/typebox/build/type/engine/object/from_union.d.mts +2 -2
  149. package/node_modules/typebox/build/type/engine/object/from_union.mjs +2 -2
  150. package/node_modules/typebox/build/type/engine/optional/instantiate_add.d.mts +1 -3
  151. package/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs +1 -4
  152. package/node_modules/typebox/build/type/engine/readonly/instantiate_add.d.mts +1 -3
  153. package/node_modules/typebox/build/type/engine/readonly/instantiate_add.mjs +1 -4
  154. package/node_modules/typebox/build/type/engine/this/index.d.mts +1 -0
  155. package/node_modules/typebox/build/type/engine/this/index.mjs +1 -0
  156. package/node_modules/typebox/build/value/clone/clone.d.mts +4 -3
  157. package/node_modules/typebox/build/value/clone/clone.mjs +5 -7
  158. package/node_modules/typebox/build/value/repair/from_cyclic.d.mts +2 -0
  159. package/node_modules/typebox/build/value/repair/from_cyclic.mjs +6 -0
  160. package/node_modules/typebox/build/value/shared/union_priority_sort.mjs +25 -14
  161. package/node_modules/typebox/package.json +1 -1
  162. package/node_modules/typebox/readme.md +1 -1
  163. package/node_modules/undici/docs/docs/api/Interceptors.md +15 -2
  164. package/node_modules/undici/lib/dispatcher/client-h2.js +78 -3
  165. package/node_modules/undici/lib/dispatcher/pool-base.js +20 -3
  166. package/node_modules/undici/package.json +3 -3
  167. package/node_modules/undici/types/api.d.ts +1 -1
  168. package/node_modules/undici/types/utility.d.ts +4 -4
  169. package/node_modules/zod/package.json +1 -1
  170. package/node_modules/zod/src/v4/classic/tests/array.test.ts +27 -0
  171. package/node_modules/zod/src/v4/classic/tests/map.test.ts +7 -0
  172. package/node_modules/zod/src/v4/classic/tests/set.test.ts +6 -0
  173. package/node_modules/zod/src/v4/core/compile.ts +9 -0
  174. package/node_modules/zod/src/v4/core/schemas.ts +35 -1
  175. package/node_modules/zod/src/v4/core/util.ts +13 -0
  176. package/node_modules/zod/src/v4/core/versions.ts +1 -1
  177. package/node_modules/zod/v4/core/compile.cjs +8 -0
  178. package/node_modules/zod/v4/core/compile.js +8 -0
  179. package/node_modules/zod/v4/core/schemas.cjs +30 -0
  180. package/node_modules/zod/v4/core/schemas.d.cts +3 -1
  181. package/node_modules/zod/v4/core/schemas.d.ts +3 -1
  182. package/node_modules/zod/v4/core/schemas.js +30 -0
  183. package/node_modules/zod/v4/core/util.cjs +15 -0
  184. package/node_modules/zod/v4/core/util.d.cts +2 -0
  185. package/node_modules/zod/v4/core/util.d.ts +2 -0
  186. package/node_modules/zod/v4/core/util.js +14 -0
  187. package/node_modules/zod/v4/core/versions.cjs +1 -1
  188. package/node_modules/zod/v4/core/versions.js +1 -1
  189. package/openclaw.plugin.json +6 -30
  190. package/package.json +7 -7
  191. package/dist/.setup/action-runtime.runtime-DWKWaKgC.mjs +0 -1
  192. package/dist/.setup/http-GpAVKwSX.mjs +0 -2
  193. package/dist/.setup/plugin-routes-lXsy_bNV.mjs +0 -21
  194. package/dist/.setup/probe-C5VWf3sz.mjs +0 -686
  195. package/dist/.setup/registry-1G0f1zuZ.mjs +0 -34
  196. package/dist/.setup/setup-plugin-api-D7DoueEh.mjs +0 -2
  197. package/dist/.setup/shared-CohTHxM1.mjs +0 -255
  198. package/dist/.source-module-urls/562d694877afe9d9dcbb3b32a8883c16974738f2aa892c48d45572cdc9a62529.json +0 -1
  199. package/dist/http-routes-api.js +0 -1
  200. package/node_modules/@slack/bolt/node_modules/body-parser/node_modules/content-type/LICENSE +0 -22
  201. package/node_modules/@slack/bolt/node_modules/body-parser/node_modules/content-type/README.md +0 -71
  202. package/node_modules/@slack/bolt/node_modules/body-parser/node_modules/content-type/package.json +0 -52
  203. package/node_modules/@slack/bolt/node_modules/content-disposition/index.js +0 -536
  204. package/node_modules/@slack/bolt/node_modules/content-type/HISTORY.md +0 -29
  205. package/node_modules/@slack/bolt/node_modules/content-type/index.js +0 -225
  206. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/LICENSE +0 -22
  207. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/README.md +0 -71
  208. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +0 -46
  209. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.js +0 -176
  210. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.js.map +0 -1
  211. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/package.json +0 -52
  212. package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/LICENSE +0 -22
  213. package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/README.md +0 -71
  214. package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/dist/index.d.ts +0 -46
  215. package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/dist/index.js +0 -176
  216. package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/dist/index.js.map +0 -1
  217. package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/package.json +0 -52
  218. package/node_modules/typebox/build/system/memory/freeze.d.mts +0 -4
  219. package/node_modules/typebox/build/system/memory/freeze.mjs +0 -6
  220. /package/node_modules/@slack/bolt/node_modules/{body-parser/node_modules/content-type → content-type}/dist/index.d.ts +0 -0
  221. /package/node_modules/@slack/bolt/node_modules/{body-parser/node_modules/content-type → content-type}/dist/index.js +0 -0
  222. /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-nwUcYXjP.mjs";
2
- import { f as resolveSlackReplyToMode, o as resolveDefaultSlackAccountId } from "./accounts-CEgdyqoW.mjs";
3
- import { n as formatSlackTarget } from "./target-parsing-0pRZtahO.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-CHkEn3HM.mjs";
5
- import { M as resolveSlackUserAllowed, O as normalizeSlackAllowOwnerEntry, b as writeLruMapEntry, o as formatSlackError, v as pruneExpiredMapEntries, y as readLruMapEntry } from "./probe-C5VWf3sz.mjs";
6
- import { a as prepareSlackReply, l as resolveSlackReplyRenderPlan, s as resolveSlackReplyBlocks } from "./reply-blocks-DOjiDr44.mjs";
2
+ import { d as resolveSlackMarkdownOptions, o as resolveDefaultSlackAccountId, p as resolveSlackReplyToMode } from "./accounts-EaPP0fFY.mjs";
3
+ import { n as formatSlackTarget } from "./target-parsing-DwYAP3ts.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-B_Ax1vS1.mjs";
7
5
  import { n as resolveSlackReplyThreadTs } from "./thread-ts-47Au2-Yn.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-PF2cP6to.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-C5uXkmZV.mjs";
10
- import { n as resolveSlackChannelConfig } from "./policy-Bp1FvE6m.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-DsZUQtTM.mjs";
12
- import { a as emitSlackMessageSentHooks, i as sanitizeSlackMonitorReplyPayload, n as deliverReplies, t as createSlackReplyDeliveryPlan } from "./replies-7DRN46EL.mjs";
6
+ import { c as resolveSlackReplyRenderPlan, i as prepareSlackReply, o as resolveSlackReplyBlocks } from "./reply-blocks-DyYYRrP_.mjs";
7
+ import { d as getOrCreateWeakOwnerMap, m as writeLruMapEntry, p as readLruMapEntry } from "./cursor-pages-jVn3Vkfi.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-s3CcFO45.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-WJZ73VSf.mjs";
10
+ import { n as resolveSlackChannelConfig } from "./policy-C6_DdlSf.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-BD86SotB.mjs";
12
+ import { a as emitSlackMessageSentHooks, i as sanitizeSlackMonitorReplyPayload, n as deliverReplies, t as createSlackReplyDeliveryPlan } from "./replies-Bq7n0gfM.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";
@@ -96,13 +96,6 @@ function resolveSlackNativeProgressTaskCards(entry, style) {
96
96
  }
97
97
  const SLACK_STREAM_RECIPIENT_TEAM_CACHE_MAX = 2e3;
98
98
  const slackStreamRecipientTeamCaches = /* @__PURE__ */ new WeakMap();
99
- function getSlackStreamRecipientTeamCache(client) {
100
- const existing = slackStreamRecipientTeamCaches.get(client);
101
- if (existing) return existing;
102
- const cache = /* @__PURE__ */ new Map();
103
- slackStreamRecipientTeamCaches.set(client, cache);
104
- return cache;
105
- }
106
99
  function buildSlackEventDeliveryKey(params, preparedReply = prepareSlackReply(params.payload)) {
107
100
  const reply = resolveSendableOutboundReplyParts(params.payload, { text: params.textOverride });
108
101
  const renderPlan = preparedReply.resolvePreview(params.textOverride);
@@ -121,7 +114,7 @@ function buildSlackEventDeliveryKey(params, preparedReply = prepareSlackReply(pa
121
114
  }
122
115
  async function resolveSlackStreamRecipientTeamId(params) {
123
116
  const cacheKey = params.fallbackTeamId && params.userId ? `${params.fallbackTeamId}:${params.userId}` : void 0;
124
- const cache = cacheKey ? getSlackStreamRecipientTeamCache(params.client) : void 0;
117
+ const cache = cacheKey ? getOrCreateWeakOwnerMap(slackStreamRecipientTeamCaches, params.client) : void 0;
125
118
  const cachedTeamId = cache && cacheKey ? readLruMapEntry(cache, cacheKey) : void 0;
126
119
  if (cachedTeamId) return cachedTeamId;
127
120
  if (params.userId) try {
@@ -144,8 +137,6 @@ const DEFAULT_THROTTLE_MS = 1e3;
144
137
  function createSlackDraftStream(params) {
145
138
  const maxChars = Math.min(params.maxChars ?? 8e3, SLACK_TEXT_LIMIT);
146
139
  const throttleMs = Math.max(250, params.throttleMs ?? DEFAULT_THROTTLE_MS);
147
- const send = params.send ?? sendMessageSlack;
148
- const remove = params.remove ?? deleteSlackMessage;
149
140
  let streamMessage;
150
141
  let untrackConversationBoundary;
151
142
  let lastSentKey = "";
@@ -159,7 +150,7 @@ function createSlackDraftStream(params) {
159
150
  streamMessage = void 0;
160
151
  lastSentKey = "";
161
152
  };
162
- const editPreview = (message, update) => (params.edit ?? (update.textIsSlackMrkdwn ? editSlackRenderedMessage : editSlackMessage))(message.channelId, message.messageId, update.text, {
153
+ const editPreview = (message, update) => (update.textIsSlackMrkdwn ? editSlackRenderedMessage : editSlackMessage)(message.channelId, message.messageId, update.text, {
163
154
  cfg: params.cfg,
164
155
  token: params.token,
165
156
  accountId: params.accountId,
@@ -206,7 +197,7 @@ function createSlackDraftStream(params) {
206
197
  onInterveningMessage: () => forceNewMessage("human")
207
198
  });
208
199
  await lifecycle.createMessage(async () => {
209
- const sent = await send(params.target, visibleUpdate.text, {
200
+ const sent = await sendMessageSlack(params.target, visibleUpdate.text, {
210
201
  cfg: params.cfg,
211
202
  token: params.token,
212
203
  accountId: params.accountId,
@@ -251,7 +242,7 @@ function createSlackDraftStream(params) {
251
242
  clearMessageId,
252
243
  isValidMessageId: (value) => typeof value === "object" && value !== null,
253
244
  deleteMessage: async (message) => {
254
- if (!message.retained && !(preserveHumanReplies && message.detachedByHuman)) await remove(message.channelId, message.messageId, {
245
+ if (!message.retained && !(preserveHumanReplies && message.detachedByHuman)) await deleteSlackMessage(message.channelId, message.messageId, {
255
246
  token: params.token,
256
247
  accountId: params.accountId,
257
248
  ...params.eventScope ? { client: params.eventScope.client } : {}
@@ -299,7 +290,6 @@ function createSlackDraftStream(params) {
299
290
  stopTrackingConversationBoundary();
300
291
  return true;
301
292
  };
302
- params.log?.(`slack stream preview ready (maxChars=${maxChars}, throttleMs=${throttleMs})`);
303
293
  return {
304
294
  update,
305
295
  flush: loop.flush,
@@ -371,12 +361,6 @@ const SLACK_PROGRESS_TASK_TITLE_MAX = 120;
371
361
  const SLACK_PROGRESS_PLAN_FALLBACK_TITLE = "Thinking";
372
362
  const SLACK_PROGRESS_LINE_DELTA_RE = /(?:^|\s)\+(\d+)\s+[−-](\d+)(?=\s|$)/u;
373
363
  const SLACK_ATTENTION_TASK_PREFIX = "openclaw-attention-";
374
- function buildSessionSources(links) {
375
- return links.map((link) => ({
376
- type: "url_source",
377
- ...link
378
- }));
379
- }
380
364
  function field(text) {
381
365
  return {
382
366
  type: "mrkdwn",
@@ -435,10 +419,6 @@ function lineTaskTitle(line) {
435
419
  if (fallback && fallback !== label) return compactTitle(lineDetailParts(line).length > 0 || line.status ? label : fallback);
436
420
  return compactTitle(label);
437
421
  }
438
- function lineTaskDetails(line, maxLineChars) {
439
- const detail = line.detail?.replace(SLACK_PROGRESS_LINE_DELTA_RE, "").replace(/\s+·\s*$/u, "").trim();
440
- return detail && detail !== line.status?.trim() ? compactDetail(detail, maxLineChars) : void 0;
441
- }
442
422
  function lineTaskOutput(line) {
443
423
  const match = line.detail ? SLACK_PROGRESS_LINE_DELTA_RE.exec(line.detail) : null;
444
424
  if (match) return `+${match[1]} −${match[2]}`;
@@ -469,48 +449,39 @@ function resolveLineTaskIdentity(line, contentIdOccurrences) {
469
449
  contentIdOccurrences.set(id, occurrence);
470
450
  return `${id}_${occurrence}`;
471
451
  }
472
- function buildNativeTasks(params) {
452
+ function formatProgressAttentionTitle(line, finalStatus) {
453
+ if (line.kind === "approval") return line.status === "requested" && finalStatus === void 0 ? compactTitle(`Approval required: ${line.detail || line.label}`) : void 0;
454
+ if (lineTaskStatus(line) !== "error") return;
455
+ const title = [...new Set([
456
+ line.label,
457
+ line.detail,
458
+ line.status
459
+ ].filter(Boolean))].join(" — ");
460
+ return compactTitle(finalStatus === "complete" ? `Recovered: ${title}` : title);
461
+ }
462
+ function buildSlackProgressStreamChunks(params) {
463
+ const approvals = params.lines.filter((line) => line.kind === "approval");
464
+ const taskLines = params.summaryRow ? [] : params.lines.filter((line) => line.kind !== "approval");
473
465
  const tasks = (params.plan ?? []).map((entry, index) => ({
474
466
  id: `plan_step_${index + 1}`,
475
467
  title: compactTitle(entry.step),
476
468
  status: entry.status === "completed" ? "complete" : entry.status
477
469
  }));
478
470
  const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_TASK_DETAIL_MAX_CHARS);
479
- const contentIdOccurrences = /* @__PURE__ */ new Map();
480
- for (const line of params.lines) {
471
+ const taskContentIdOccurrences = /* @__PURE__ */ new Map();
472
+ for (const line of taskLines) {
481
473
  const task = {
482
- id: resolveLineTaskIdentity(line, contentIdOccurrences),
474
+ id: resolveLineTaskIdentity(line, taskContentIdOccurrences),
483
475
  title: lineTaskTitle(line),
484
476
  status: lineTaskStatus(line)
485
477
  };
486
- const details = lineTaskDetails(line, maxLineChars);
478
+ const detail = line.detail?.replace(SLACK_PROGRESS_LINE_DELTA_RE, "").replace(/\s+·\s*$/u, "").trim();
479
+ const details = detail && detail !== line.status?.trim() ? compactDetail(detail, maxLineChars) : void 0;
487
480
  const output = lineTaskOutput(line);
488
481
  if (details) task.details = details;
489
482
  if (output) task.output = output;
490
483
  tasks.push(task);
491
484
  }
492
- return tasks;
493
- }
494
- function formatProgressAttentionTitle(line, finalStatus) {
495
- if (line.kind === "approval") return line.status === "requested" && finalStatus === void 0 ? compactTitle(`Approval required: ${line.detail || line.label}`) : void 0;
496
- if (lineTaskStatus(line) !== "error") return;
497
- const title = [...new Set([
498
- line.label,
499
- line.detail,
500
- line.status
501
- ].filter(Boolean))].join(" — ");
502
- return compactTitle(finalStatus === "complete" ? `Recovered: ${title}` : title);
503
- }
504
- function formatTaskDiffOutput(diffStat) {
505
- return diffStat && (diffStat.added > 0 || diffStat.removed > 0) ? `+${diffStat.added} −${diffStat.removed}` : void 0;
506
- }
507
- function buildSlackProgressStreamChunks(params) {
508
- const approvals = params.lines.filter((line) => line.kind === "approval");
509
- const tasks = buildNativeTasks({
510
- lines: params.summaryRow ? [] : params.lines.filter((line) => line.kind !== "approval"),
511
- plan: params.plan,
512
- maxLineChars: params.maxLineChars
513
- });
514
485
  const contentIdOccurrences = /* @__PURE__ */ new Map();
515
486
  const attention = [];
516
487
  for (const line of approvals) {
@@ -521,11 +492,11 @@ function buildSlackProgressStreamChunks(params) {
521
492
  status: "pending"
522
493
  });
523
494
  }
524
- const headline = params.title?.trim() || params.label?.trim();
495
+ const headline = params.title?.trim();
525
496
  const summaryTitle = params.finalInProgressStatus === "error" ? "Failed" : params.finalInProgressStatus === "complete" || !params.summaryRow ? "Completed" : "Working";
526
497
  const newest = tasks.at(-1);
527
498
  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);
528
- const diffOutput = formatTaskDiffOutput(params.diffStat);
499
+ const diffOutput = params.diffStat && (params.diffStat.added > 0 || params.diffStat.removed > 0) ? `+${params.diffStat.added} −${params.diffStat.removed}` : void 0;
529
500
  if (tasks.length === 0 && (params.summaryRow || params.sessionLinks?.length || diffOutput)) tasks.push({
530
501
  id: "openclaw_summary",
531
502
  title: params.summaryRow ? compactTitle(title) : summaryTitle,
@@ -545,7 +516,10 @@ function buildSlackProgressStreamChunks(params) {
545
516
  chunk.title = recovered ? compactTitle(`Recovered: ${task.title}`) : task.title;
546
517
  chunk.status = recovered ? "complete" : task.status === "in_progress" ? params.finalInProgressStatus ?? task.status : task.status;
547
518
  if (index === finalTaskIndex && diffOutput) chunk.output = [task.output, diffOutput].filter(Boolean).join(" · ");
548
- if (index === finalTaskIndex && params.sessionLinks?.length) chunk.sources = buildSessionSources(params.sessionLinks);
519
+ if (index === finalTaskIndex && params.sessionLinks?.length) chunk.sources = params.sessionLinks.map((link) => ({
520
+ type: "url_source",
521
+ ...link
522
+ }));
549
523
  return chunk;
550
524
  });
551
525
  return [{
@@ -564,13 +538,6 @@ function joinRecentProgressRows(rows) {
564
538
  }
565
539
  return rendered.toReversed().join("\n");
566
540
  }
567
- function buildActivityText(lines, maxLineChars) {
568
- return joinRecentProgressRows(lines.slice(-50).map((line) => {
569
- if (line.kind === "item" && !line.toolName) return `_${renderProgressCardText(compactDetail(line.text, maxLineChars), "italic")}_`;
570
- const detail = compactDetail(lineDetailParts(line).join(" · "), maxLineChars);
571
- return escapeSlackMrkdwn(detail ? `${line.label} — ${detail}` : line.label);
572
- }));
573
- }
574
541
  function buildSlackProgressCardBlocks(params) {
575
542
  const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS);
576
543
  const selected = selectPlanChecklistSteps(params.detailed ? params.plan ?? [] : [], { maxLines: 50 });
@@ -600,7 +567,11 @@ function buildSlackProgressCardBlocks(params) {
600
567
  });
601
568
  const sections = [
602
569
  planLines.map((line) => renderProgressCardText(line)).join("\n"),
603
- buildActivityText(lines.filter((line) => line.kind !== "approval" && lineTaskStatus(line) !== "error"), maxLineChars),
570
+ joinRecentProgressRows(lines.filter((line) => line.kind !== "approval" && lineTaskStatus(line) !== "error").slice(-50).map((line) => {
571
+ if (line.kind === "item" && !line.toolName) return `_${renderProgressCardText(compactDetail(line.text, maxLineChars), "italic")}_`;
572
+ const detail = compactDetail(lineDetailParts(line).join(" · "), maxLineChars);
573
+ return escapeSlackMrkdwn(detail ? `${line.label} — ${detail}` : line.label);
574
+ })),
604
575
  joinRecentProgressRows(attention)
605
576
  ];
606
577
  const title = params.title?.trim();
@@ -680,6 +651,13 @@ function reconcileSlackNativeTaskChunks(params) {
680
651
  const nextTasks = /* @__PURE__ */ new Map();
681
652
  let planTitle = params.previous.planTitle;
682
653
  const emitted = [];
654
+ if (!params.chunks?.length && planTitle && planTitle !== SLACK_PROGRESS_PLAN_FALLBACK_TITLE) {
655
+ planTitle = SLACK_PROGRESS_PLAN_FALLBACK_TITLE;
656
+ emitted.push({
657
+ type: "plan_update",
658
+ title: planTitle
659
+ });
660
+ }
683
661
  for (const chunk of params.chunks ?? []) {
684
662
  if (chunk.type === "plan_update") {
685
663
  if (chunk.title !== planTitle) {
@@ -789,12 +767,6 @@ function isSlackProgressTitleText(text, snapshot, explicitTitle) {
789
767
  return title.length > 0 && title.includes(candidate);
790
768
  }
791
769
  // extensions/slack/src/monitor/message-handler/preview-finalize.ts
792
- function buildExpectedSlackEditText(params) {
793
- const trimmedText = params.text.trim();
794
- if (trimmedText) return normalizeSlackOutboundText(trimmedText);
795
- if (params.blocks?.length) return normalizeSlackOutboundText(buildSlackBlocksFallbackText(params.blocks));
796
- return " ";
797
- }
798
770
  function blocksMatch(expected, actual) {
799
771
  if (!expected?.length) return !actual?.length;
800
772
  if (!actual?.length) {
@@ -814,45 +786,6 @@ function blocksMatch(expected, actual) {
814
786
  return false;
815
787
  }
816
788
  }
817
- async function readSlackMessageAfterEditError(params) {
818
- const query = {
819
- token: params.token,
820
- channel: params.channelId,
821
- latest: params.messageId,
822
- oldest: params.messageId,
823
- inclusive: true,
824
- limit: 1
825
- };
826
- const result = params.threadTs ? await params.client.conversations.replies({
827
- ...query,
828
- ts: params.threadTs
829
- }) : await params.client.conversations.history(query);
830
- const message = params.threadTs ? result.messages?.find((entry) => entry?.ts === params.messageId) : result.messages?.[0];
831
- return message?.ts && message.ts === params.messageId ? message : null;
832
- }
833
- async function didSlackPreviewEditApplyAfterError(params) {
834
- const readback = await readSlackMessageAfterEditError(params);
835
- if (!readback) return false;
836
- const expectedText = buildExpectedSlackEditText({
837
- text: params.text,
838
- blocks: params.blocks
839
- });
840
- const acceptedTexts = /* @__PURE__ */ new Set([
841
- expectedText,
842
- normalizeSlackOutboundText(truncateSlackTextByUtf8Bytes(expectedText, SLACK_EDIT_TEXT_MAX_BYTES)),
843
- normalizeSlackOutboundText(buildSlackEditTextPayload(params.text, params.blocks))
844
- ]);
845
- if (params.blocks?.length && hasSlackNativeDataBlock(params.blocks)) {
846
- const fallbackPlan = buildSlackNativeDataDeliveryPlan({
847
- baseText: params.text,
848
- blocks: params.blocks
849
- });
850
- for (const message of fallbackPlan.fallbackMessages) acceptedTexts.add(normalizeSlackOutboundText(message.text));
851
- }
852
- const actualText = normalizeSlackOutboundText((readback.text ?? "").trim());
853
- if (params.blocks?.length) return acceptedTexts.has(actualText) && blocksMatch(params.blocks, readback.blocks);
854
- return actualText === expectedText;
855
- }
856
789
  async function finalizeSlackPreviewEdit(params) {
857
790
  try {
858
791
  await editSlackRenderedMessage(params.channelId, params.messageId, params.text, {
@@ -863,9 +796,40 @@ async function finalizeSlackPreviewEdit(params) {
863
796
  });
864
797
  } catch (err) {
865
798
  try {
866
- if (await didSlackPreviewEditApplyAfterError(params)) {
867
- logVerbose(`slack: preview final edit response failed but readback matched message ${params.channelId}/${params.messageId}; suppressing duplicate fallback send`);
868
- return;
799
+ const query = {
800
+ token: params.token,
801
+ channel: params.channelId,
802
+ latest: params.messageId,
803
+ oldest: params.messageId,
804
+ inclusive: true,
805
+ limit: 1
806
+ };
807
+ const result = params.threadTs ? await params.client.conversations.replies({
808
+ ...query,
809
+ ts: params.threadTs
810
+ }) : await params.client.conversations.history(query);
811
+ const message = params.threadTs ? result.messages?.find((entry) => entry?.ts === params.messageId) : result.messages?.[0];
812
+ const readback = message?.ts && message.ts === params.messageId ? message : null;
813
+ if (readback) {
814
+ const trimmedText = params.text.trim();
815
+ const expectedText = trimmedText ? normalizeSlackOutboundText(trimmedText) : params.blocks?.length ? normalizeSlackOutboundText(buildSlackBlocksFallbackText(params.blocks)) : " ";
816
+ const acceptedTexts = /* @__PURE__ */ new Set([
817
+ expectedText,
818
+ normalizeSlackOutboundText(truncateSlackTextByUtf8Bytes(expectedText, SLACK_EDIT_TEXT_MAX_BYTES)),
819
+ normalizeSlackOutboundText(buildSlackEditTextPayload(params.text, params.blocks))
820
+ ]);
821
+ if (params.blocks?.length && hasSlackNativeDataBlock(params.blocks)) {
822
+ const fallbackPlan = buildSlackNativeDataDeliveryPlan({
823
+ baseText: params.text,
824
+ blocks: params.blocks
825
+ });
826
+ for (const fallbackMessage of fallbackPlan.fallbackMessages) acceptedTexts.add(normalizeSlackOutboundText(fallbackMessage.text));
827
+ }
828
+ const actualText = normalizeSlackOutboundText((readback.text ?? "").trim());
829
+ if (params.blocks?.length ? acceptedTexts.has(actualText) && blocksMatch(params.blocks, readback.blocks) : actualText === expectedText) {
830
+ logVerbose(`slack: preview final edit response failed but readback matched message ${params.channelId}/${params.messageId}; suppressing duplicate fallback send`);
831
+ return;
832
+ }
869
833
  }
870
834
  } catch (readbackErr) {
871
835
  logVerbose(`slack: preview final edit readback failed (${String(readbackErr)})`);
@@ -1041,7 +1005,7 @@ function createSlackNativeProgressTransport(params) {
1041
1005
  const delivered = markDelivered(streamThreadTs);
1042
1006
  return update.chunks?.length ? delivered : true;
1043
1007
  } finally {
1044
- if (delivery.nativeProgressStreamStartPromise === startPromise) delivery.nativeProgressStreamStartPromise = null;
1008
+ delivery.nativeProgressStreamStartPromise = null;
1045
1009
  }
1046
1010
  };
1047
1011
  const append = async (update) => {
@@ -1072,7 +1036,6 @@ function createSlackProgressRuntime(runtimeParams) {
1072
1036
  cfg,
1073
1037
  token: ctx.botToken,
1074
1038
  accountId: account.accountId,
1075
- conversationChannelId: message.channel,
1076
1039
  eventScope: prepared.eventScope,
1077
1040
  ...!hasSlackCustomIdentity && slackIdentity ? { identity: slackIdentity } : {},
1078
1041
  ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
@@ -1082,7 +1045,6 @@ function createSlackProgressRuntime(runtimeParams) {
1082
1045
  if (ts) delivery.usedReplyThreadTs ??= ts;
1083
1046
  return ts;
1084
1047
  },
1085
- log: logVerbose,
1086
1048
  warn: logVerbose
1087
1049
  }) : void 0;
1088
1050
  const isProgressMode = slackStreaming.mode === "progress";
@@ -1501,7 +1463,6 @@ function createSlackProgressRuntime(runtimeParams) {
1501
1463
  return {
1502
1464
  draftStream,
1503
1465
  previewLifecycle,
1504
- isProgressMode,
1505
1466
  useDraftProgressCard,
1506
1467
  useNativeProgressStreaming,
1507
1468
  progressDraftActive,
@@ -1693,9 +1654,7 @@ async function createSlackDispatchSetup(prepared) {
1693
1654
  status: "active",
1694
1655
  eventScope: prepared.eventScope
1695
1656
  });
1696
- if (reportFailure && !restored) try {
1697
- runtime.error?.("Slack session status could not return to active after processing. Enable verbose logging to inspect the Slack API failure.");
1698
- } catch {}
1657
+ if (reportFailure && !restored) runtime.error?.("Slack session status could not return to active after processing. Enable verbose logging to inspect the Slack API failure.");
1699
1658
  }
1700
1659
  if (didAddTypingReaction && typingReaction && message.ts) {
1701
1660
  didAddTypingReaction = false;
@@ -1759,6 +1718,10 @@ async function createSlackDispatchSetup(prepared) {
1759
1718
  function createSlackStreamingDeliveryRuntime(setup) {
1760
1719
  const { isThreadReply, messageSentHookContext, messageSentHookTarget, prepared, replyDeliveryMode, replyPlan, runtime, slackClient, slackClientOptions, slackIdentity, slackStreamFallbackTeamId } = setup;
1761
1720
  const { account, ctx, forcedReplyThreadTs, message, slackMessageMetadata } = prepared;
1721
+ const markdownOptions = resolveSlackMarkdownOptions({
1722
+ cfg: ctx.cfg,
1723
+ accountId: account.accountId
1724
+ });
1762
1725
  const state = {
1763
1726
  streamSession: null,
1764
1727
  streamBoundary: null,
@@ -1839,7 +1802,7 @@ function createSlackStreamingDeliveryRuntime(setup) {
1839
1802
  if (key) deliveredKeys.add(key);
1840
1803
  };
1841
1804
  const markPreviewPayloadDelivered = (params) => {
1842
- const preparedReply = prepareSlackReply(params.payload);
1805
+ const preparedReply = prepareSlackReply(params.payload, markdownOptions);
1843
1806
  markDelivered(buildSlackEventDeliveryKey(params, preparedReply));
1844
1807
  const nextThreadTs = replyPlan.peekThreadTs();
1845
1808
  if (nextThreadTs !== params.threadTs) markDelivered(buildSlackEventDeliveryKey({
@@ -1880,7 +1843,7 @@ function createSlackStreamingDeliveryRuntime(setup) {
1880
1843
  }
1881
1844
  const fallbackText = fallbackError.pendingText.trim();
1882
1845
  if (!fallbackText) return;
1883
- const sent = await deliverPreparedReply(prepareSlackReply({ text: fallbackText }), session.threadTs, true);
1846
+ const sent = await deliverPreparedReply(prepareSlackReply({ text: fallbackText }, markdownOptions), session.threadTs, true);
1884
1847
  if (!sent?.receipt.platformMessageIds.length) return;
1885
1848
  markSlackStreamFallbackDelivered(session);
1886
1849
  if (!session.stopped) try {
@@ -1938,16 +1901,13 @@ function createSlackStreamingDeliveryRuntime(setup) {
1938
1901
  if (state.streamSession?.stoppedBySlack) return { visibleReplySent: false };
1939
1902
  const replyThreadTs = params.forcedThreadTs ?? state.interruptedThreadTs ?? resolveDeliveryThreadTs(params);
1940
1903
  const deliveryReplyThreadTs = replyDeliveryMode === "off" && !forcedReplyThreadTs && !isThreadReply ? void 0 : replyThreadTs;
1941
- const preparedReply = prepareSlackReply(params.payload);
1904
+ const preparedReply = prepareSlackReply(params.payload, markdownOptions);
1942
1905
  const deliveryKey = buildSlackEventDeliveryKey({
1943
1906
  kind: params.kind,
1944
1907
  payload: params.payload,
1945
1908
  threadTs: deliveryReplyThreadTs
1946
1909
  }, preparedReply);
1947
- if (deliveryKey && deliveredKeys.has(deliveryKey)) {
1948
- logVerbose("slack: suppressed duplicate normal delivery within the same turn");
1949
- return { visibleReplySent: false };
1950
- }
1910
+ if (deliveryKey && deliveredKeys.has(deliveryKey)) return { visibleReplySent: false };
1951
1911
  const sent = await deliverPreparedReply(preparedReply, deliveryReplyThreadTs);
1952
1912
  if (!sent?.receipt.platformMessageIds.length) return { visibleReplySent: false };
1953
1913
  state.observedReplyDelivery = true;
@@ -1969,9 +1929,16 @@ function createSlackStreamingDeliveryRuntime(setup) {
1969
1929
  };
1970
1930
  const isStreamingEligible = (payload, options) => {
1971
1931
  const reply = resolveSendableOutboundReplyParts(payload);
1972
- const renderPlan = resolveSlackReplyRenderPlan(payload);
1932
+ const renderPlan = resolveSlackReplyRenderPlan(payload, payload.text, markdownOptions);
1973
1933
  const plannedBlocks = renderPlan.mode === "single" ? renderPlan.blocks : renderPlan.blockPart?.blocks;
1974
- return !state.streamFailed && !reply.hasMedia && renderPlan.mode !== "split" && !renderPlan.textIsSlackPlainText && !plannedBlocks?.length && !resolveSlackReplyBlocks(payload)?.length && reply.hasText && (!options?.maxTextBytes || countSlackTextUtf8Bytes(reply.trimmedText) <= options.maxTextBytes);
1934
+ const previewThreadTs = state.streamSession?.threadTs ?? state.interruptedThreadTs ?? state.nativeProgressStreamThreadTs;
1935
+ const finalThreadTs = resolveSlackReplyThreadTs({
1936
+ replyToMode: replyDeliveryMode,
1937
+ replyToId: payload.replyToId,
1938
+ threadId: previewThreadTs,
1939
+ replyToCurrent: payload.replyToCurrent
1940
+ });
1941
+ 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);
1975
1942
  };
1976
1943
  const deliverWithStreaming = async (params) => {
1977
1944
  if (state.streamSession?.stoppedBySlack) return { visibleReplySent: false };
@@ -1984,7 +1951,12 @@ function createSlackStreamingDeliveryRuntime(setup) {
1984
1951
  await rotateInterruptedStream();
1985
1952
  if (state.streamSession?.stoppedBySlack) return { visibleReplySent: false };
1986
1953
  let session = state.streamSession;
1987
- const threadTs = session?.threadTs ?? state.interruptedThreadTs ?? replyPlan.nextThreadTs();
1954
+ const threadTs = resolveSlackReplyThreadTs({
1955
+ replyToMode: replyDeliveryMode,
1956
+ replyToId: params.payload.replyToId,
1957
+ threadId: session?.threadTs ?? state.interruptedThreadTs ?? replyPlan.nextThreadTs(),
1958
+ replyToCurrent: params.payload.replyToCurrent
1959
+ });
1988
1960
  if (state.streamFailed || !threadTs) {
1989
1961
  state.streamFailed = true;
1990
1962
  return await deliverNormally({
@@ -1999,11 +1971,8 @@ function createSlackStreamingDeliveryRuntime(setup) {
1999
1971
  ...params,
2000
1972
  threadTs,
2001
1973
  textOverride: text
2002
- });
2003
- if (deliveryKey && deliveredKeys.has(deliveryKey)) {
2004
- logVerbose("slack-stream: suppressed duplicate reply payload");
2005
- return { visibleReplySent: false };
2006
- }
1974
+ }, prepareSlackReply(params.payload, markdownOptions));
1975
+ if (deliveryKey && deliveredKeys.has(deliveryKey)) return { visibleReplySent: false };
2007
1976
  let messageId;
2008
1977
  let fallbackDelivery;
2009
1978
  try {
@@ -2084,6 +2053,74 @@ function createSlackStreamingDeliveryRuntime(setup) {
2084
2053
  resetDeliveryTracker: () => deliveredKeys.clear()
2085
2054
  });
2086
2055
  }
2056
+ // extensions/slack/src/monitor/message-handler/failure-notice.ts
2057
+ /** One outage policy for immediate replies and later origin-routed queue drains. */
2058
+ function createSlackFailureNoticePolicy(params, hasVisibleReply) {
2059
+ const { context, accountId, channelId, failureNoticeThreadTs, failureNoticeTeamId, statusThreadTs } = params;
2060
+ let sawTerminalFailurePayload = false;
2061
+ let pendingFailureNotice;
2062
+ const filterPassiveThreadFailure = (payload) => {
2063
+ if (payload.isError !== true || context.ChatType !== "channel" || isReplyPayloadNonTerminalToolErrorWarning(payload)) return payload;
2064
+ sawTerminalFailurePayload = true;
2065
+ if (hasVisibleReply()) return payload;
2066
+ 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;
2067
+ const noticeThreadTs = failureNoticeThreadTs ?? (explicitlyAddressed ? statusThreadTs : void 0);
2068
+ const notice = {
2069
+ accountId,
2070
+ channelId,
2071
+ ...noticeThreadTs ? { threadTs: noticeThreadTs } : {},
2072
+ failureText: payload.text ?? "",
2073
+ ...failureNoticeTeamId ? { teamId: failureNoticeTeamId } : {}
2074
+ };
2075
+ if (failureNoticeThreadTs && !explicitlyAddressed && context.MentionSource !== "implicit_thread" && !hasSlackThreadParticipation(notice.accountId, notice.channelId, failureNoticeThreadTs, failureNoticeTeamId)) {
2076
+ logVerbose("slack: suppressed passive failure before thread participation");
2077
+ return null;
2078
+ }
2079
+ if (!explicitlyAddressed && hasSlackThreadFailureNotice(notice)) {
2080
+ logVerbose("slack: suppressed repeated passive channel or thread failure");
2081
+ return null;
2082
+ }
2083
+ pendingFailureNotice = notice;
2084
+ return payload;
2085
+ };
2086
+ return {
2087
+ filter: filterPassiveThreadFailure,
2088
+ recordDelivered(payload) {
2089
+ if (pendingFailureNotice && (!payload || payload.isError === true && !isReplyPayloadNonTerminalToolErrorWarning(payload) && (payload.text ?? "") === pendingFailureNotice.failureText)) {
2090
+ recordSlackThreadFailureNotice(pendingFailureNotice);
2091
+ pendingFailureNotice = void 0;
2092
+ }
2093
+ },
2094
+ complete() {
2095
+ if (!sawTerminalFailurePayload && context.ChatType === "channel") clearSlackThreadFailureNotice({
2096
+ accountId,
2097
+ channelId,
2098
+ ...failureNoticeThreadTs ? { threadTs: failureNoticeThreadTs } : {},
2099
+ ...failureNoticeTeamId ? { teamId: failureNoticeTeamId } : {}
2100
+ });
2101
+ }
2102
+ };
2103
+ }
2104
+ function createSlackQueuedFailureNoticePolicy(params, beginSessionRun) {
2105
+ let visibleReplySent = false;
2106
+ let policy = createSlackFailureNoticePolicy(params, () => visibleReplySent);
2107
+ return {
2108
+ begin() {
2109
+ visibleReplySent = false;
2110
+ policy = createSlackFailureNoticePolicy(params, () => visibleReplySent);
2111
+ const release = beginSessionRun();
2112
+ return (settlement) => {
2113
+ if (settlement?.completed && !settlement.failed && !settlement.transportPending) policy.complete();
2114
+ release();
2115
+ };
2116
+ },
2117
+ shouldSuppressReply: (payload) => policy.filter(payload) === null,
2118
+ onReplyDelivered(payload) {
2119
+ policy.recordDelivered(payload);
2120
+ visibleReplySent = true;
2121
+ }
2122
+ };
2123
+ }
2087
2124
  // extensions/slack/src/monitor/message-handler/dispatch.ts
2088
2125
  function formatSlackGroupThreadReply(text, participant) {
2089
2126
  return `**${participant.name.replace(/[\\`*_{}[\]()<>#!|]/g, "\\$&").replace(/\s+/g, " ")}**\n${text}`;
@@ -2109,7 +2146,7 @@ async function dispatchPreparedSlackMessage(prepared) {
2109
2146
  },
2110
2147
  onSettled: () => {
2111
2148
  releaseDeferred?.();
2112
- upstreamLifecycle.onSettled?.();
2149
+ return upstreamLifecycle.onSettled?.();
2113
2150
  }
2114
2151
  };
2115
2152
  const release = beginSessionRun();
@@ -2118,6 +2155,10 @@ async function dispatchPreparedSlackMessage(prepared) {
2118
2155
  async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptionLifecycle) {
2119
2156
  const { prepared } = setup;
2120
2157
  const { account, ctx, message, route } = prepared;
2158
+ const markdownOptions = resolveSlackMarkdownOptions({
2159
+ cfg: ctx.cfg,
2160
+ accountId: account.accountId
2161
+ });
2121
2162
  const { cfg, disableBlockStreaming, hasSlackCustomIdentity, hasRepliedRef, messageSentHookContext, messageSentHookTarget, onModelSelected, previewStreamingEnabled, replyPipeline, replyPlan, runtime, slackClient, slackStreaming, sourceReplyDeliveryMode, statusReactions, statusReactionsEnabled, statusThreadTs, suppressRoomEventTyping, useStreaming } = setup;
2122
2163
  let dispatchError;
2123
2164
  const delivery = createSlackStreamingDeliveryRuntime(setup);
@@ -2129,41 +2170,22 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2129
2170
  let turnCommentaryVisible = false;
2130
2171
  const { draftStream, previewLifecycle } = progress;
2131
2172
  setup.threadStatusGate.hasVisibleOutput = () => delivery.observedReplyDelivery || previewLifecycle.previewFinalized || Boolean(draftStream?.messageId());
2132
- const failureNoticeThreadTs = message.thread_ts;
2133
- const failureNoticeTeamId = prepared.eventScope?.teamId;
2134
- let sawTerminalFailurePayload = false;
2135
- let pendingFailureNotice;
2136
- const filterPassiveThreadFailure = (payload) => {
2137
- if (payload.isError !== true || prepared.ctxPayload.ChatType !== "channel" || isReplyPayloadNonTerminalToolErrorWarning(payload)) return payload;
2138
- sawTerminalFailurePayload = true;
2139
- if (delivery.observedReplyDelivery || previewLifecycle.previewFinalized) return payload;
2140
- 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;
2141
- const noticeThreadTs = failureNoticeThreadTs ?? (explicitlyAddressed ? statusThreadTs : void 0);
2142
- const notice = {
2143
- accountId: account.accountId,
2144
- channelId: message.channel,
2145
- ...noticeThreadTs ? { threadTs: noticeThreadTs } : {},
2146
- failureText: payload.text ?? "",
2147
- ...failureNoticeTeamId ? { teamId: failureNoticeTeamId } : {}
2148
- };
2149
- if (failureNoticeThreadTs && !explicitlyAddressed && prepared.ctxPayload.MentionSource !== "implicit_thread" && !hasSlackThreadParticipation(notice.accountId, notice.channelId, failureNoticeThreadTs, failureNoticeTeamId)) {
2150
- logVerbose("slack: suppressed passive failure before thread participation");
2151
- return null;
2152
- }
2153
- if (!explicitlyAddressed && hasSlackThreadFailureNotice(notice)) {
2154
- logVerbose("slack: suppressed repeated passive channel or thread failure");
2155
- return null;
2156
- }
2157
- pendingFailureNotice = notice;
2158
- return payload;
2173
+ const failurePolicyParams = {
2174
+ context: prepared.ctxPayload,
2175
+ accountId: account.accountId,
2176
+ channelId: message.channel,
2177
+ failureNoticeThreadTs: message.thread_ts,
2178
+ failureNoticeTeamId: prepared.eventScope?.teamId,
2179
+ statusThreadTs
2159
2180
  };
2181
+ const failurePolicy = createSlackFailureNoticePolicy(failurePolicyParams, () => delivery.observedReplyDelivery || previewLifecycle.previewFinalized);
2160
2182
  const deliverSlackPayload = async (incomingPayload, info) => {
2161
2183
  let payload = incomingPayload;
2162
2184
  if (info.participant && (payload.text || payload.mediaUrl || payload.mediaUrls?.length)) payload = {
2163
2185
  ...payload,
2164
2186
  text: formatSlackGroupThreadReply(payload.text ?? "", info.participant)
2165
2187
  };
2166
- if (info.kind === "final" && slackStreaming.mode === "progress" && progress.isProgressMode) {
2188
+ if (info.kind === "final" && slackStreaming.mode === "progress") {
2167
2189
  const supplement = getReplyPayloadTtsSupplement(payload);
2168
2190
  const finalPayload = !progress.useDraftProgressCard && !progress.useNativeProgressStreaming && supplement && !supplement.visibleTextAlreadyDelivered && !payload.text?.trim() ? {
2169
2191
  ...payload,
@@ -2203,15 +2225,11 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2203
2225
  const reply = resolveSendableOutboundReplyParts(payload);
2204
2226
  const ttsSupplement = getReplyPayloadTtsSupplement(payload);
2205
2227
  const replySourceText = payload.text ?? ttsSupplement?.spokenText;
2206
- const replyRenderPlan = resolveSlackReplyRenderPlan(payload, replySourceText);
2228
+ const replyRenderPlan = resolveSlackReplyRenderPlan(payload, replySourceText, markdownOptions);
2207
2229
  const slackBlocks = replyRenderPlan.mode === "single" ? replyRenderPlan.blocks : replyRenderPlan.blockPart?.blocks;
2208
2230
  const requiresSeparateFallbackDelivery = replyRenderPlan.mode === "split" || replyRenderPlan.textIsSlackPlainText === true;
2209
2231
  const trimmedFinalText = replyRenderPlan.mode === "single" ? replyRenderPlan.text.trim() : replyRenderPlan.fallbackText.trim();
2210
- const previewFinalText = replyRenderPlan.mode === "single" && replyRenderPlan.textIsSlackMrkdwn ? trimmedFinalText : normalizeSlackOutboundText((replySourceText ?? "").trim(), { tableMode: resolveMarkdownTableMode({
2211
- cfg,
2212
- channel: "slack",
2213
- accountId: account.accountId
2214
- }) });
2232
+ const previewFinalText = replyRenderPlan.mode === "single" && replyRenderPlan.textIsSlackMrkdwn ? trimmedFinalText : normalizeSlackOutboundText((replySourceText ?? "").trim(), markdownOptions);
2215
2233
  const previewFinalTextFitsEdit = countSlackTextUtf8Bytes(previewFinalText) <= SLACK_EDIT_TEXT_MAX_BYTES;
2216
2234
  const shouldRestoreTtsSupplementTextForPreviewFallback = Boolean(ttsSupplement) && ttsSupplement?.visibleTextAlreadyDelivered !== true && Boolean(draftStream) && !previewLifecycle.previewFinalized && !previewLifecycle.finalDelivered && previewStreamingEnabled && !payload.text?.trim();
2217
2235
  let ttsPreviewFinalization;
@@ -2221,11 +2239,18 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2221
2239
  isError: payload.isError === true,
2222
2240
  adapter: {
2223
2241
  buildFinalEdit: () => {
2224
- if (hasSlackCustomIdentity || !previewStreamingEnabled || reply.hasMedia && !ttsSupplement || payload.isError || requiresSeparateFallbackDelivery || !previewFinalTextFitsEdit || trimmedFinalText.length === 0 && !slackBlocks?.length) return;
2242
+ const threadTs = delivery.usedReplyThreadTs ?? statusThreadTs;
2243
+ const finalThreadTs = resolveSlackReplyThreadTs({
2244
+ replyToMode: setup.replyDeliveryMode,
2245
+ replyToId: payload.replyToId,
2246
+ threadId: threadTs,
2247
+ replyToCurrent: payload.replyToCurrent
2248
+ });
2249
+ if (hasSlackCustomIdentity || finalThreadTs !== threadTs || !previewStreamingEnabled || reply.hasMedia && !ttsSupplement || payload.isError || requiresSeparateFallbackDelivery || !previewFinalTextFitsEdit || trimmedFinalText.length === 0 && !slackBlocks?.length) return;
2225
2250
  return {
2226
2251
  text: previewFinalText,
2227
2252
  blocks: slackBlocks,
2228
- threadTs: delivery.usedReplyThreadTs ?? statusThreadTs
2253
+ threadTs
2229
2254
  };
2230
2255
  },
2231
2256
  editFinal: async (preview, edit) => {
@@ -2320,7 +2345,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2320
2345
  ...replyPipeline,
2321
2346
  transformReplyPayload: (payload) => {
2322
2347
  const transformed = replyPipeline.transformReplyPayload ? replyPipeline.transformReplyPayload(payload) : payload;
2323
- return transformed ? filterPassiveThreadFailure(transformed) : null;
2348
+ return transformed ? failurePolicy.filter(transformed) : null;
2324
2349
  },
2325
2350
  humanDelay: resolveHumanDelayConfig(cfg, route.agentId)
2326
2351
  },
@@ -2337,7 +2362,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2337
2362
  replyOptions: {
2338
2363
  onVisibleWorkSessions: progress.onVisibleWorkSessions,
2339
2364
  groupThreadReplyFormatter: formatSlackGroupThreadReply,
2340
- queuedDeliveryCorrelations: [{ begin: beginSessionRun }],
2365
+ queuedDeliveryCorrelations: [createSlackQueuedFailureNoticePolicy(failurePolicyParams, beginSessionRun)],
2341
2366
  ...turnAdoptionLifecycle ? { turnAdoptionLifecycle } : {},
2342
2367
  skillFilter: prepared.channelConfig?.skills,
2343
2368
  sourceReplyDeliveryMode,
@@ -2381,7 +2406,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2381
2406
  if (payload.kind === "preamble" && await shouldYieldDraftProgress()) return false;
2382
2407
  if (prepared.turnAdoptionLifecycle?.abortSignal?.aborted) return false;
2383
2408
  progress.progressWorkCounter.noteItem(payload);
2384
- return progress.preambleOnlyProgress && payload.kind !== "preamble" ? await progress.progressDraft.noteActivity() : await progress.progressDraft.pushItemEvent(payload);
2409
+ return progress.preambleOnlyProgress && payload.kind !== "preamble" && payload.kind !== "model-wait" ? await progress.progressDraft.noteActivity() : await progress.progressDraft.pushItemEvent(payload);
2385
2410
  },
2386
2411
  onPlanUpdate: async (payload) => {
2387
2412
  if (payload.phase !== "update") return false;
@@ -2395,12 +2420,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2395
2420
  settledDispatchResult = result;
2396
2421
  const agentRunOutcome = readAgentRunTerminalOutcome(result);
2397
2422
  agentRunFailed = agentRunOutcome === "failed";
2398
- if (agentRunOutcome === "completed" && !sawTerminalFailurePayload && prepared.ctxPayload.ChatType === "channel") clearSlackThreadFailureNotice({
2399
- accountId: account.accountId,
2400
- channelId: message.channel,
2401
- ...failureNoticeThreadTs ? { threadTs: failureNoticeThreadTs } : {},
2402
- ...failureNoticeTeamId ? { teamId: failureNoticeTeamId } : {}
2403
- });
2423
+ if (agentRunOutcome === "completed") failurePolicy.complete();
2404
2424
  }
2405
2425
  } catch (err) {
2406
2426
  dispatchError ??= err;
@@ -2416,7 +2436,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2416
2436
  if (hasVisibleInboundReplyDispatch(settledDispatchResult)) await previewLifecycle.observeDelivery({ visibleReplySent: true });
2417
2437
  }
2418
2438
  await previewLifecycle.cleanup({ failed: Boolean(dispatchError || agentRunFailed) });
2419
- if (pendingFailureNotice && anyReplyDelivered) recordSlackThreadFailureNotice(pendingFailureNotice);
2439
+ if (anyReplyDelivered) failurePolicy.recordDelivered();
2420
2440
  if (dispatchError || agentRunFailed) await progress.finalizeDraftProgressCard("error", { postIfMissing: !anyReplyDelivered });
2421
2441
  await progress.dropDetachedProgressCards();
2422
2442
  if (statusReactionsEnabled) {
@@ -2436,11 +2456,6 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2436
2456
  if (dispatchError) throw toErrorObject(dispatchError, "Slack dispatch failed");
2437
2457
  if (!anyReplyDelivered && !previewLifecycle.previewFinalized && !(agentRunFailed && progress.useDraftProgressCard)) {
2438
2458
  if (progress.useDraftProgressCard) await draftStream?.clear();
2439
- return;
2440
- }
2441
- if (shouldLogVerbose()) {
2442
- const finalCount = resolveInboundReplyDispatchCounts(settledDispatchResult).final;
2443
- logVerbose(`slack: delivered ${finalCount} repl${finalCount === 1 ? "y" : "ies"} to ${prepared.replyTarget}`);
2444
2459
  }
2445
2460
  }
2446
2461
  // extensions/slack/src/agent-context.ts
@@ -2482,13 +2497,12 @@ function normalizeSlackAppContextEntities(value) {
2482
2497
  });
2483
2498
  }
2484
2499
  // extensions/slack/src/monitor/thread.ts
2485
- const THREAD_STARTER_CACHE = /* @__PURE__ */ new Map();
2486
- const THREAD_STARTER_CACHE_TTL_MS = 216e5;
2487
- const THREAD_STARTER_CACHE_MAX = 2e3;
2488
- function evictThreadStarterCache() {
2489
- pruneExpiredMapEntries(THREAD_STARTER_CACHE, Date.now());
2490
- pruneMapToMaxSize(THREAD_STARTER_CACHE, THREAD_STARTER_CACHE_MAX);
2491
- }
2500
+ const threadStarterCache = createStaleWhileRevalidateCache({
2501
+ maxEntries: 2e3,
2502
+ maxPending: Number.POSITIVE_INFINITY,
2503
+ ttlMs: 216e5,
2504
+ cacheable: (starter) => starter !== null
2505
+ });
2492
2506
  function formatSlackFilePlaceholder(files) {
2493
2507
  return `[attached: ${formatSlackFileReferenceList(files)}]`;
2494
2508
  }
@@ -2523,40 +2537,35 @@ function resolveSlackMessageText(message) {
2523
2537
  }, { preserveMessageTextWhitespace: true });
2524
2538
  }
2525
2539
  async function resolveSlackThreadStarter(params) {
2526
- evictThreadStarterCache();
2527
2540
  const cacheKey = JSON.stringify([
2528
2541
  params.workspaceScope.accountId,
2529
2542
  params.workspaceScope.teamId,
2530
2543
  params.channelId,
2531
2544
  params.threadTs
2532
2545
  ]);
2533
- const cached = THREAD_STARTER_CACHE.get(cacheKey);
2534
- if (cached && !params.refresh) return cached.value;
2535
- if (params.refresh) THREAD_STARTER_CACHE.delete(cacheKey);
2536
2546
  try {
2537
- const message = (await params.client.conversations.replies({
2538
- channel: params.channelId,
2539
- ts: params.threadTs,
2540
- limit: 1,
2541
- inclusive: true
2542
- }))?.messages?.[0];
2543
- const text = message ? resolveSlackMessageText(message) : void 0;
2544
- const files = message?.files?.length ? message.files : void 0;
2545
- if (!message || !text && !files) return null;
2546
- const starter = {
2547
- text: text || formatSlackFilePlaceholder(files),
2548
- userId: message.user,
2549
- botId: message.bot_id,
2550
- ts: message.ts,
2551
- files
2552
- };
2553
- THREAD_STARTER_CACHE.delete(cacheKey);
2554
- THREAD_STARTER_CACHE.set(cacheKey, {
2555
- value: starter,
2556
- expiresAt: Date.now() + THREAD_STARTER_CACHE_TTL_MS
2547
+ const { value } = await threadStarterCache.read(cacheKey, async () => {
2548
+ const message = (await params.client.conversations.replies({
2549
+ channel: params.channelId,
2550
+ ts: params.threadTs,
2551
+ limit: 1,
2552
+ inclusive: true
2553
+ }))?.messages?.[0];
2554
+ const text = message ? resolveSlackMessageText(message) : void 0;
2555
+ const files = message?.files?.length ? message.files : void 0;
2556
+ if (!message || !text && !files) return null;
2557
+ return {
2558
+ text: text || formatSlackFilePlaceholder(files),
2559
+ userId: message.user,
2560
+ botId: message.bot_id,
2561
+ ts: message.ts,
2562
+ files
2563
+ };
2564
+ }, {
2565
+ allowStale: false,
2566
+ refresh: params.refresh
2557
2567
  });
2558
- evictThreadStarterCache();
2559
- return starter;
2568
+ return value;
2560
2569
  } catch (err) {
2561
2570
  logVerbose(`slack thread starter fetch failed channel=${params.channelId} ts=${params.threadTs}: ${formatErrorMessage(err)}`);
2562
2571
  return null;
@@ -2610,8 +2619,7 @@ async function resolveSlackThreadHistory(params) {
2610
2619
  retained.push([msg, text]);
2611
2620
  }
2612
2621
  if (retained.length > maxMessages) retained.splice(0, retained.length - maxMessages);
2613
- const next = response.response_metadata?.next_cursor;
2614
- cursor = typeof next === "string" && next.trim().length > 0 ? next.trim() : void 0;
2622
+ cursor = normalizeOptionalString(response.response_metadata?.next_cursor);
2615
2623
  if (response.has_more && !cursor) {
2616
2624
  params.onOmission?.("Slack returned an incomplete thread page without a cursor");
2617
2625
  return [];
@@ -2660,7 +2668,7 @@ async function resolveSlackChannelHistory(params) {
2660
2668
  cursor = response.response_metadata?.next_cursor?.trim() || void 0;
2661
2669
  if (!cursor || scanned >= params.limit) break;
2662
2670
  }
2663
- return messages.slice(0, params.limit).toReversed();
2671
+ return messages.slice(0, params.limit).reverse();
2664
2672
  }
2665
2673
  // extensions/slack/src/monitor/message-handler/preflight-audio.ts
2666
2674
  function isSlackAudioFile(file) {
@@ -2710,7 +2718,7 @@ const SLACK_MENTION_RESOLUTION_CONCURRENCY = 4;
2710
2718
  const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
2711
2719
  const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
2712
2720
  const MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS = 2e3;
2713
- const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-PF2cP6to.mjs").then((n) => n._));
2721
+ const loadSlackMediaModule$2 = createLazyRuntimeModule(() => import("./actions-s3CcFO45.mjs").then((n) => n._));
2714
2722
  function formatSlackUnavailableMedia(params) {
2715
2723
  let fileReferences = params.files?.map((file) => `${formatSlackFileReference(file)} unavailable (${file.reason})`).join(", ");
2716
2724
  if (fileReferences && fileReferences.length > MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS) fileReferences = `${truncateUtf16Safe(fileReferences, MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS)}; … (file references truncated)`;
@@ -2731,7 +2739,6 @@ function collectUniqueSlackMentionIds(texts) {
2731
2739
  const seen = /* @__PURE__ */ new Set();
2732
2740
  for (const text of texts) {
2733
2741
  if (!text) continue;
2734
- SLACK_USER_MENTION_RE$1.lastIndex = 0;
2735
2742
  for (const match of text.matchAll(SLACK_USER_MENTION_RE$1)) {
2736
2743
  const userId = match[1];
2737
2744
  if (userId) seen.add(userId);
@@ -2741,35 +2748,24 @@ function collectUniqueSlackMentionIds(texts) {
2741
2748
  }
2742
2749
  function renderSlackUserMentions(text, renderedMentions) {
2743
2750
  if (!text || renderedMentions.size === 0) return text;
2744
- SLACK_USER_MENTION_RE$1.lastIndex = 0;
2745
2751
  return text.replace(SLACK_USER_MENTION_RE$1, (full, userId) => {
2746
2752
  return renderedMentions.get(userId) ?? full;
2747
2753
  });
2748
2754
  }
2749
- function filterInheritedParentFiles(params) {
2750
- const { files, isThreadReply, threadStarter } = params;
2751
- if (!isThreadReply || !files?.length || !threadStarter?.files?.length) return files;
2752
- const starterFileIds = new Set(threadStarter.files.map((file) => file.id));
2753
- const filtered = files.filter((file) => !file.id || !starterFileIds.has(file.id));
2754
- if (filtered.length < files.length) logVerbose(`slack: filtered ${files.length - filtered.length} inherited parent file(s) from thread reply`);
2755
- return filtered.length > 0 ? filtered : void 0;
2756
- }
2757
2755
  async function resolveSlackMessageContent(params) {
2758
- const ownFiles = filterInheritedParentFiles({
2759
- files: params.message.files,
2760
- isThreadReply: params.isThreadReply,
2761
- threadStarter: params.threadStarter
2762
- });
2763
- const attachmentContent = ownFiles?.length || params.message.attachments?.length ? await loadSlackMediaModule$1().then(({ resolveSlackAttachmentContent }) => resolveSlackAttachmentContent({
2756
+ let ownFiles = params.message.files;
2757
+ const { isThreadReply, threadStarter } = params;
2758
+ if (isThreadReply && ownFiles?.length && threadStarter?.files?.length) {
2759
+ const starterFileIds = new Set(threadStarter.files.map((file) => file.id));
2760
+ const filtered = ownFiles.filter((file) => !file.id || !starterFileIds.has(file.id));
2761
+ ownFiles = filtered.length > 0 ? filtered : void 0;
2762
+ }
2763
+ const attachmentContent = ownFiles?.length || params.message.attachments?.length ? await loadSlackMediaModule$2().then(({ resolveSlackAttachmentContent }) => resolveSlackAttachmentContent({
2764
2764
  files: ownFiles,
2765
2765
  attachments: params.message.attachments,
2766
2766
  client: params.client,
2767
2767
  token: params.botToken,
2768
2768
  maxBytes: params.mediaMaxBytes,
2769
- readIdleTimeoutMs: params.mediaReadIdleTimeoutMs,
2770
- totalTimeoutMs: params.mediaTotalTimeoutMs,
2771
- abortSignal: params.abortSignal,
2772
- assertCurrent: params.assertCurrent,
2773
2769
  preloadedMedia: params.preloadedMedia
2774
2770
  })) : null;
2775
2771
  const effectiveDirectMedia = attachmentContent?.media.length ? attachmentContent.media : null;
@@ -2824,7 +2820,7 @@ function resolveSlackDmHistoryLimit(params) {
2824
2820
  return resolvePromptHistoryLimit(override ?? params.defaultLimit, 0);
2825
2821
  }
2826
2822
  async function resolveSlackDmHistoryContext(params) {
2827
- const maxMessages = Math.max(0, Math.floor(params.limit));
2823
+ const maxMessages = params.limit;
2828
2824
  if (maxMessages <= 0) return {
2829
2825
  body: void 0,
2830
2826
  inboundHistory: void 0
@@ -2845,7 +2841,7 @@ async function resolveSlackDmHistoryContext(params) {
2845
2841
  message,
2846
2842
  body
2847
2843
  }] : [];
2848
- }).slice(0, maxMessages).toReversed();
2844
+ }).slice(0, maxMessages).reverse();
2849
2845
  if (messages.length === 0) return {
2850
2846
  body: void 0,
2851
2847
  inboundHistory: void 0
@@ -2903,6 +2899,7 @@ const SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS = 4;
2903
2899
  const SLACK_HISTORY_MEDIA_MAX_BYTES = 10485760;
2904
2900
  const SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS = 1e3;
2905
2901
  const SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS = 3e3;
2902
+ const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-s3CcFO45.mjs").then((n) => n._));
2906
2903
  async function resolveSlackRoomHistory(params) {
2907
2904
  if (params.ctx.historyLimit <= 0 || !params.message.ts) return [];
2908
2905
  const warnOmission = (reason) => {
@@ -2956,26 +2953,51 @@ async function resolveSlackRoomHistory(params) {
2956
2953
  sender: user?.name ?? message.userId ?? `Bot (${message.botId ?? "unknown"})`
2957
2954
  });
2958
2955
  }
2956
+ async function resolveSlackHistoryMedia(message, maxAttachments) {
2957
+ const { ctx, eventScope, assertCurrent, abortSignal } = params;
2958
+ const files = sliceSlackImageFileCandidates(message.files, maxAttachments);
2959
+ const attachments = [];
2960
+ let remaining = Math.max(0, maxAttachments - files.length);
2961
+ if (remaining > 0 && message.attachments?.length) for (const attachment of message.attachments) {
2962
+ if (attachment.is_share !== true) continue;
2963
+ const hasImageUrl = Boolean(normalizeOptionalString(attachment.image_url));
2964
+ const attachmentFiles = sliceSlackImageFileCandidates(attachment.files, remaining - (hasImageUrl ? 1 : 0));
2965
+ if (!hasImageUrl && attachmentFiles.length === 0) continue;
2966
+ attachments.push({
2967
+ ...attachment,
2968
+ files: attachmentFiles
2969
+ });
2970
+ remaining -= (hasImageUrl ? 1 : 0) + attachmentFiles.length;
2971
+ if (remaining <= 0) break;
2972
+ }
2973
+ if (files.length === 0 && attachments.length === 0) return {
2974
+ media: [],
2975
+ attempted: 0
2976
+ };
2977
+ const { resolveSlackAttachmentContent } = await loadSlackMediaModule$1();
2978
+ const content = await resolveSlackAttachmentContent({
2979
+ files,
2980
+ attachments,
2981
+ client: eventScope?.client ?? ctx.app.client,
2982
+ token: ctx.botToken,
2983
+ maxBytes: Math.min(ctx.mediaMaxBytes, SLACK_HISTORY_MEDIA_MAX_BYTES),
2984
+ readIdleTimeoutMs: SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS,
2985
+ totalTimeoutMs: SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS,
2986
+ assertCurrent,
2987
+ abortSignal
2988
+ });
2989
+ return {
2990
+ media: await toInboundMediaFactsWithMetadata(content?.media, {
2991
+ kind: "image",
2992
+ messageId: message.ts
2993
+ }),
2994
+ attempted: maxAttachments - remaining
2995
+ };
2996
+ }
2959
2997
  const entries = [];
2960
2998
  let mediaRemaining = SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS;
2961
2999
  for (const { message, sender } of permitted.slice(-params.ctx.historyLimit)) {
2962
- const { media, attempted } = mediaRemaining > 0 && (message.files?.length || message.attachments?.length) ? await resolveSlackHistoryMedia({
2963
- ctx: params.ctx,
2964
- eventScope: params.eventScope,
2965
- maxAttachments: mediaRemaining,
2966
- assertCurrent: params.assertCurrent,
2967
- abortSignal: params.abortSignal,
2968
- message: {
2969
- type: "message",
2970
- channel: params.message.channel,
2971
- ts: message.ts,
2972
- user: message.userId,
2973
- bot_id: message.botId,
2974
- text: message.text,
2975
- files: message.files,
2976
- attachments: message.attachments
2977
- }
2978
- }) : {
3000
+ const { media, attempted } = mediaRemaining > 0 && (message.files?.length || message.attachments?.length) ? await resolveSlackHistoryMedia(message, mediaRemaining) : {
2979
3001
  media: [],
2980
3002
  attempted: 0
2981
3003
  };
@@ -3002,57 +3024,6 @@ function sliceSlackImageFileCandidates(files, limit) {
3002
3024
  if (limit <= 0 || !files?.length) return [];
3003
3025
  return files.filter(isSlackImageFileCandidate).slice(0, limit);
3004
3026
  }
3005
- function sliceSlackHistoryAttachmentCandidates(attachments, limit) {
3006
- if (limit <= 0 || !attachments?.length) return [];
3007
- const out = [];
3008
- let remaining = limit;
3009
- for (const attachment of attachments) {
3010
- if (attachment.is_share !== true) continue;
3011
- const hasImageUrl = Boolean(normalizeOptionalString(attachment.image_url));
3012
- const files = sliceSlackImageFileCandidates(attachment.files, remaining - (hasImageUrl ? 1 : 0));
3013
- if (!hasImageUrl && files.length === 0) continue;
3014
- out.push({
3015
- ...attachment,
3016
- files
3017
- });
3018
- remaining -= (hasImageUrl ? 1 : 0) + files.length;
3019
- if (remaining <= 0) break;
3020
- }
3021
- return out;
3022
- }
3023
- async function resolveSlackHistoryMedia(params) {
3024
- const files = sliceSlackImageFileCandidates(params.message.files, params.maxAttachments);
3025
- const attachments = sliceSlackHistoryAttachmentCandidates(params.message.attachments, Math.max(0, params.maxAttachments - files.length));
3026
- if (files.length === 0 && attachments.length === 0) return {
3027
- media: [],
3028
- attempted: 0
3029
- };
3030
- const attempted = files.length + attachments.reduce((count, attachment) => count + (normalizeOptionalString(attachment.image_url) ? 1 : 0) + (attachment.files?.length ?? 0), 0);
3031
- const content = await resolveSlackMessageContent({
3032
- message: {
3033
- ...params.message,
3034
- files,
3035
- attachments
3036
- },
3037
- isThreadReply: false,
3038
- threadStarter: null,
3039
- isBotMessage: Boolean(params.message.bot_id),
3040
- client: params.eventScope?.client ?? params.ctx.app.client,
3041
- botToken: params.ctx.botToken,
3042
- mediaMaxBytes: Math.min(params.ctx.mediaMaxBytes, SLACK_HISTORY_MEDIA_MAX_BYTES),
3043
- mediaReadIdleTimeoutMs: SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS,
3044
- mediaTotalTimeoutMs: SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS,
3045
- assertCurrent: params.assertCurrent,
3046
- abortSignal: params.abortSignal
3047
- });
3048
- return {
3049
- media: await toInboundMediaFactsWithMetadata(content?.effectiveDirectMedia, {
3050
- kind: "image",
3051
- messageId: params.message.ts
3052
- }),
3053
- attempted
3054
- };
3055
- }
3056
3027
  // extensions/slack/src/monitor/message-handler/prepare-session-presentation.ts
3057
3028
  function resolveSlackGroupSessionSubject(params) {
3058
3029
  const channelName = normalizeOptionalString(params.channelName);
@@ -3061,7 +3032,7 @@ function resolveSlackGroupSessionSubject(params) {
3061
3032
  return `Slack Channel (Workspace ID: ${params.workspaceId}, Channel ID: ${params.channelId})`;
3062
3033
  }
3063
3034
  // extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
3064
- const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-PF2cP6to.mjs").then((n) => n._));
3035
+ const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-s3CcFO45.mjs").then((n) => n._));
3065
3036
  const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
3066
3037
  function formatSlackThreadLabelSnippet(text) {
3067
3038
  return truncateUtf16Safe(text.replace(/\s+/g, " "), 80);
@@ -3110,13 +3081,14 @@ async function resolveSlackThreadContextData(params) {
3110
3081
  const isOutboundOnlyThreadSession = threadSessionFreshness !== void 0 && threadSessionFreshness.state !== "missing" && threadSessionFreshness.entry.lastInteractionAt === void 0 && threadSessionFreshness.entry.updatedAt !== 0;
3111
3082
  const shouldSeedInitialThreadContext = Boolean(params.isThreadReply && params.threadTs && (threadSessionFreshness ? threadSessionFreshness.state !== "fresh" || isOutboundOnlyThreadSession : threadSessionPreviousTimestamp === void 0));
3112
3083
  const shouldLoadInitialThreadHistory = shouldSeedInitialThreadContext || params.forceInitialHistory === true;
3113
- if (!params.isThreadReply || !params.threadTs) return {
3084
+ const readThreadContextData = () => ({
3114
3085
  threadStarterBody,
3115
3086
  threadHistoryBody,
3116
3087
  shouldSeedInitialThreadContext,
3117
3088
  threadLabel,
3118
3089
  threadStarterMedia
3119
- };
3090
+ });
3091
+ if (!params.isThreadReply || !params.threadTs) return readThreadContextData();
3120
3092
  const starter = params.threadStarter;
3121
3093
  const starterSenderName = params.allowNameMatching && params.allowFromLower.length > 0 && starter?.userId ? (await params.ctx.resolveUserName(starter.userId, params.eventScope))?.name : void 0;
3122
3094
  params.assertHistoryCurrent?.();
@@ -3156,10 +3128,6 @@ async function resolveSlackThreadContextData(params) {
3156
3128
  unavailableMediaCount: attachmentContent.unavailableMediaCount,
3157
3129
  prependUnavailable: true
3158
3130
  });
3159
- if (threadStarterMedia) {
3160
- const starterPlaceholders = threadStarterMedia.map((item) => item.placeholder).join(", ");
3161
- logVerbose(`slack: hydrated thread starter file ${starterPlaceholders} from root message`);
3162
- }
3163
3131
  }
3164
3132
  } else threadLabel = `Slack thread ${params.roomLabel}`;
3165
3133
  const includeBotStarterAsRootContext = Boolean(starter?.text && starterIsCurrentBot && shouldSeedInitialThreadContext);
@@ -3168,7 +3136,6 @@ async function resolveSlackThreadContextData(params) {
3168
3136
  else if (includeBotStarterAsRootContext) {
3169
3137
  const snippet = formatSlackThreadLabelSnippet(starter?.text ?? "").trim();
3170
3138
  threadLabel = `Slack thread ${params.roomLabel}${snippet ? ` (assistant root): ${snippet}` : ""}`;
3171
- logVerbose("slack: retained current-bot thread starter as assistant root context");
3172
3139
  }
3173
3140
  const threadInitialHistoryLimit = Math.min(params.account.config?.thread?.initialHistoryLimit ?? 20, params.historyLimit ?? Number.POSITIVE_INFINITY);
3174
3141
  if (threadInitialHistoryLimit > 0 && shouldLoadInitialThreadHistory) {
@@ -3193,28 +3160,22 @@ async function resolveSlackThreadContextData(params) {
3193
3160
  }, "Slack automatic thread history omitted");
3194
3161
  }
3195
3162
  });
3196
- if (historyOmitted) return {
3197
- threadStarterBody,
3198
- threadHistoryBody,
3199
- shouldSeedInitialThreadContext,
3200
- threadLabel,
3201
- threadStarterMedia
3202
- };
3163
+ if (historyOmitted) return readThreadContextData();
3203
3164
  const enrichedStarter = starter && threadStarterBody && threadStarterBody !== starter.text ? {
3204
3165
  ...starter,
3205
3166
  text: threadStarterBody,
3206
3167
  ts: currentBotRootTs
3207
3168
  } : null;
3208
- const threadHistoryWithEnrichedRoot = enrichedStarter && !threadHistory.some((entry) => entry.ts === currentBotRootTs) ? [enrichedStarter, ...threadHistory.length >= threadInitialHistoryLimit ? threadHistory.slice(1) : threadHistory] : threadHistory;
3209
- const threadHistoryWithBotRoot = includeBotStarterAsRootContext && starter && !threadHistoryWithEnrichedRoot.some((entry) => entry.ts === currentBotRootTs) ? [{
3169
+ const rootContext = enrichedStarter ?? (includeBotStarterAsRootContext && starter ? {
3210
3170
  ...starter,
3211
3171
  ts: currentBotRootTs
3212
- }, ...threadHistoryWithEnrichedRoot] : threadHistoryWithEnrichedRoot;
3213
- if (threadHistoryWithBotRoot.length > 0) {
3172
+ } : null);
3173
+ const threadHistoryWithRoot = rootContext && !threadHistory.some((entry) => entry.ts === currentBotRootTs) ? [rootContext, ...enrichedStarter && threadHistory.length >= threadInitialHistoryLimit ? threadHistory.slice(1) : threadHistory] : threadHistory;
3174
+ if (threadHistoryWithRoot.length > 0) {
3214
3175
  const retainCurrentBotHistory = params.isGroupDm && (isMissingThreadSession || isOutboundOnlyThreadSession);
3215
- const threadHistoryWithoutCurrentBot = threadHistoryWithBotRoot.filter((entry) => !isCurrentBotAuthor(entry) || retainCurrentBotHistory || includeBotStarterAsRootContext && entry.ts === currentBotRootTs);
3216
- const omittedCurrentBotHistoryCount = threadHistoryWithBotRoot.length - threadHistoryWithoutCurrentBot.length;
3217
- const userMapForFilter = params.contextVisibilityMode !== "all" && params.allowNameMatching && params.allowFromLower.length > 0 ? await resolveThreadUserMap(threadHistoryWithoutCurrentBot) : /* @__PURE__ */ new Map();
3176
+ const threadHistoryWithoutCurrentBot = threadHistoryWithRoot.filter((entry) => !isCurrentBotAuthor(entry) || retainCurrentBotHistory || includeBotStarterAsRootContext && entry.ts === currentBotRootTs);
3177
+ const omittedCurrentBotHistoryCount = threadHistoryWithRoot.length - threadHistoryWithoutCurrentBot.length;
3178
+ const userMapForFilter = params.contextVisibilityMode !== "all" && params.allowNameMatching && params.allowFromLower.length > 0 ? await resolveThreadUserMap(threadHistoryWithoutCurrentBot) : void 0;
3218
3179
  params.assertHistoryCurrent?.();
3219
3180
  const { items: filteredThreadHistory, omitted: omittedHistoryCount } = params.contextVisibilityMode === "all" ? {
3220
3181
  items: threadHistoryWithoutCurrentBot,
@@ -3225,7 +3186,7 @@ async function resolveSlackThreadContextData(params) {
3225
3186
  kind: "thread",
3226
3187
  isSenderAllowed: (historyMsg) => {
3227
3188
  if (isCurrentBotAuthor(historyMsg)) return true;
3228
- const msgUser = historyMsg.userId ? userMapForFilter.get(historyMsg.userId) : null;
3189
+ const msgUser = historyMsg.userId ? userMapForFilter?.get(historyMsg.userId) : null;
3229
3190
  return resolveSlackUserAllowed({
3230
3191
  allowList: params.allowFromLower,
3231
3192
  teamId: params.eventScope?.teamId ?? params.ctx.teamId,
@@ -3235,7 +3196,7 @@ async function resolveSlackThreadContextData(params) {
3235
3196
  });
3236
3197
  }
3237
3198
  });
3238
- const userMap = await resolveThreadUserMap(filteredThreadHistory);
3199
+ const userMap = userMapForFilter ?? await resolveThreadUserMap(filteredThreadHistory);
3239
3200
  params.assertHistoryCurrent?.();
3240
3201
  if (omittedHistoryCount > 0 || omittedCurrentBotHistoryCount > 0) logVerbose(`slack: omitted ${omittedHistoryCount + omittedCurrentBotHistoryCount} thread message(s) from context (mode=${params.contextVisibilityMode})`);
3241
3202
  const historyParts = [];
@@ -3254,19 +3215,10 @@ async function resolveSlackThreadContextData(params) {
3254
3215
  envelope: params.envelopeOptions
3255
3216
  }));
3256
3217
  }
3257
- if (historyParts.length > 0) {
3258
- threadHistoryBody = historyParts.join("\n\n");
3259
- logVerbose(`slack: populated thread history with ${filteredThreadHistory.length} messages for new session`);
3260
- }
3218
+ if (historyParts.length > 0) threadHistoryBody = historyParts.join("\n\n");
3261
3219
  }
3262
3220
  }
3263
- return {
3264
- threadStarterBody,
3265
- threadHistoryBody,
3266
- shouldSeedInitialThreadContext,
3267
- threadLabel,
3268
- threadStarterMedia
3269
- };
3221
+ return readThreadContextData();
3270
3222
  }
3271
3223
  // src/channels/mention-gating.ts
3272
3224
  function resolveBotThreadMentionPolicy(params) {
@@ -3333,53 +3285,46 @@ function collectSlackMentionIds(text, regex) {
3333
3285
  }
3334
3286
  return [...ids];
3335
3287
  }
3336
- async function readSlackSubteamUsers(params) {
3337
- let bySubteam = subteamMemberCache.get(params.client);
3338
- if (!bySubteam) {
3339
- bySubteam = /* @__PURE__ */ new Map();
3340
- subteamMemberCache.set(params.client, bySubteam);
3341
- }
3342
- const cacheKey = `${normalizeSlackId(params.teamId) ?? ""}:${params.subteamId}`;
3343
- const cached = bySubteam.get(cacheKey);
3344
- const now = asDateTimestampMs(params.now);
3345
- if (cached) {
3346
- if (now !== void 0 && asDateTimestampMs(cached.expiresAt) !== void 0 && cached.expiresAt > now) return cached.users;
3347
- bySubteam.delete(cacheKey);
3348
- }
3349
- try {
3350
- const response = await params.client.usergroups.users.list({
3351
- usergroup: params.subteamId,
3352
- ...params.teamId ? { team_id: params.teamId } : {}
3353
- });
3354
- if (!response.ok) {
3355
- params.log?.(`slack: failed to resolve user-group mention ${params.subteamId}: ${response.error ?? "unknown_error"}`);
3356
- return /* @__PURE__ */ new Set();
3357
- }
3358
- const users = new Set((response.users ?? []).map(normalizeSlackId).filter((userId) => userId !== void 0));
3359
- const expiresAt = resolveExpiresAtMsFromDurationMs(SUBTEAM_MEMBER_CACHE_TTL_MS, { nowMs: params.now });
3360
- if (expiresAt !== void 0) bySubteam.set(cacheKey, {
3361
- expiresAt,
3362
- users
3363
- });
3364
- return users;
3365
- } catch (err) {
3366
- params.log?.(`slack: failed to resolve user-group mention ${params.subteamId}: ${formatErrorMessage(err)}`);
3367
- return /* @__PURE__ */ new Set();
3368
- }
3369
- }
3370
3288
  async function isSlackSubteamMentionForBot(params) {
3371
3289
  const botUserId = normalizeSlackId(params.botUserId);
3372
3290
  if (!botUserId) return false;
3373
3291
  const subteamIds = collectSlackMentionIds(params.text, SUBTEAM_MENTION_RE);
3374
3292
  if (subteamIds.length === 0) return false;
3375
- const now = params.now ?? Date.now();
3376
- for (const subteamId of subteamIds) if ((await readSlackSubteamUsers({
3377
- client: params.client,
3378
- subteamId,
3379
- teamId: normalizeOptionalString(params.teamId),
3380
- now,
3381
- log: params.log
3382
- })).has(botUserId)) return true;
3293
+ const now = Date.now();
3294
+ for (const subteamId of subteamIds) {
3295
+ const { client, log } = params;
3296
+ const teamId = normalizeOptionalString(params.teamId);
3297
+ const bySubteam = getOrCreateWeakOwnerMap(subteamMemberCache, client);
3298
+ const cacheKey = `${normalizeSlackId(teamId) ?? ""}:${subteamId}`;
3299
+ const cached = bySubteam.get(cacheKey);
3300
+ if (cached) {
3301
+ if (cached.expiresAt > now) {
3302
+ if (cached.users.has(botUserId)) return true;
3303
+ continue;
3304
+ }
3305
+ bySubteam.delete(cacheKey);
3306
+ }
3307
+ let users;
3308
+ try {
3309
+ const response = await client.usergroups.users.list({
3310
+ usergroup: subteamId,
3311
+ ...teamId ? { team_id: teamId } : {}
3312
+ });
3313
+ if (!response.ok) {
3314
+ log?.(`slack: failed to resolve user-group mention ${subteamId}: ${response.error ?? "unknown_error"}`);
3315
+ continue;
3316
+ }
3317
+ users = new Set((response.users ?? []).map(normalizeSlackId).filter((userId) => userId !== void 0));
3318
+ bySubteam.set(cacheKey, {
3319
+ expiresAt: now + SUBTEAM_MEMBER_CACHE_TTL_MS,
3320
+ users
3321
+ });
3322
+ } catch (err) {
3323
+ log?.(`slack: failed to resolve user-group mention ${subteamId}: ${formatErrorMessage(err)}`);
3324
+ continue;
3325
+ }
3326
+ if (users.has(botUserId)) return true;
3327
+ }
3383
3328
  return false;
3384
3329
  }
3385
3330
  // extensions/slack/src/monitor/message-handler/prepare.ts
@@ -3389,18 +3334,6 @@ const SLACK_USER_MENTION_RE = /<@([^>|]+)(?:\|[^>]+)?>/g;
3389
3334
  const SLACK_SUBTEAM_MENTION_RE = /<!subteam\^([^>|]+)(?:\|[^>]+)?>/g;
3390
3335
  const SLACK_SUBTEAM_MENTION_MARKER = "<!subteam^";
3391
3336
  const SLACK_CHANNEL_ACCESS_DOCS_URL = "https://docs.openclaw.ai/channels/slack#access-control-and-routing";
3392
- function mergeSlackAssistantThreadContext(primary, fallback) {
3393
- if (!primary) return fallback;
3394
- if (!fallback) return primary;
3395
- return {
3396
- assistantChannelId: primary.assistantChannelId || fallback.assistantChannelId,
3397
- threadTs: primary.threadTs || fallback.threadTs,
3398
- userId: primary.userId ?? fallback.userId,
3399
- channelId: primary.channelId ?? fallback.channelId,
3400
- teamId: primary.teamId ?? fallback.teamId,
3401
- enterpriseId: primary.enterpriseId !== void 0 ? primary.enterpriseId : fallback.enterpriseId
3402
- };
3403
- }
3404
3337
  function hasSlackAssistantThreadMetadata(context) {
3405
3338
  return Boolean(context?.channelId || context?.teamId || context?.enterpriseId !== void 0);
3406
3339
  }
@@ -3414,7 +3347,6 @@ function resolveSlackMessageAssistantThreadContext(message) {
3414
3347
  return {
3415
3348
  assistantChannelId,
3416
3349
  threadTs,
3417
- userId: normalizeOptionalString(thread.user_id) ?? message.user,
3418
3350
  channelId: normalizeOptionalString(context?.channel_id),
3419
3351
  teamId: normalizeOptionalString(context?.team_id),
3420
3352
  enterpriseId: context?.enterprise_id === null ? null : normalizeOptionalString(context?.enterprise_id)
@@ -3428,8 +3360,7 @@ async function restoreSlackAssistantThreadContextFromMetadata(params) {
3428
3360
  return await readSlackAssistantThreadContext({
3429
3361
  client: params.eventScope?.client ?? params.ctx.app.client,
3430
3362
  channelId: params.message.channel,
3431
- threadTs,
3432
- userId: params.message.user
3363
+ threadTs
3433
3364
  });
3434
3365
  } catch (err) {
3435
3366
  logVerbose(`slack assistant context restore failed channel=${params.message.channel} ts=${threadTs}: ${formatErrorMessage(err)}`);
@@ -3442,41 +3373,40 @@ function resolveCachedMentionRegexes(ctx, agentId, options) {
3442
3373
  normalizeOptionalString(options?.conversationId ?? void 0),
3443
3374
  options?.providerPolicy ? JSON.stringify(options.providerPolicy) : ""
3444
3375
  ].join("");
3445
- let byAgent = mentionRegexCache.get(ctx);
3446
- if (!byAgent) {
3447
- byAgent = /* @__PURE__ */ new Map();
3448
- mentionRegexCache.set(ctx, byAgent);
3449
- }
3376
+ const byAgent = getOrCreateWeakOwnerMap(mentionRegexCache, ctx);
3450
3377
  const cached = byAgent.get(key);
3451
3378
  if (cached) return cached;
3452
3379
  const built = buildMentionRegexes(ctx.cfg, agentId, options);
3453
3380
  byAgent.set(key, built);
3454
3381
  return built;
3455
3382
  }
3456
- function collectSlackMentionMetadata(text) {
3457
- return {
3458
- mentionedUserIds: collectSlackMentionIds(text, SLACK_USER_MENTION_RE),
3459
- mentionedSubteamIds: collectSlackMentionIds(text, SLACK_SUBTEAM_MENTION_RE),
3460
- hasAnyMention: SLACK_ANY_MENTION_RE.test(text),
3461
- hasSubteamMention: text.includes(SLACK_SUBTEAM_MENTION_MARKER)
3383
+ async function prepareSlackMessage(params) {
3384
+ const { ctx, account, message, opts } = params;
3385
+ const drop = (reason, parentUserId) => {
3386
+ ctx.logger.info({
3387
+ provider: "slack",
3388
+ accountId: account.accountId,
3389
+ teamId: opts.eventScope?.teamId ?? ctx.teamId,
3390
+ channelId: message.channel,
3391
+ messageTs: message.ts,
3392
+ source: opts.source,
3393
+ reason,
3394
+ ...parentUserId ? { parentUserId } : {}
3395
+ }, "Slack inbound event rejected during preparation");
3396
+ return null;
3397
+ };
3398
+ const slackClient = opts.eventScope?.client ?? ctx.app.client;
3399
+ const threadStarterWorkspaceScope = {
3400
+ accountId: account.accountId,
3401
+ teamId: opts.eventScope?.teamId ?? ctx.teamId
3462
3402
  };
3463
- }
3464
- function resolveSlackMentionSource(params) {
3465
- if (params.explicitBotMention) return "explicit_bot";
3466
- if (params.explicitSubteamMention) return "subteam";
3467
- if (params.shouldBypassMention) return "command_bypass";
3468
- if (params.wasMentioned) return "mention_pattern";
3469
- if (params.matchedImplicitMentionKinds.length > 0) return "implicit_thread";
3470
- return "none";
3471
- }
3472
- async function resolveSlackConversationContext(params) {
3473
- const { ctx, account, message } = params;
3474
3403
  const cfg = ctx.cfg;
3404
+ const conversationEventScope = opts.eventScope;
3475
3405
  let channelInfo = {};
3476
3406
  let resolvedChannelType = normalizeSlackChannelType(message.channel_type, message.channel);
3477
3407
  if (resolvedChannelType !== "im" && message.channel_type !== "im") {
3478
- channelInfo = await ctx.resolveChannelName(message.channel, params.eventScope);
3479
- resolvedChannelType = normalizeSlackChannelType(message.channel_type ?? channelInfo.type ?? ctx.recallSlackChannelType(message.channel, params.eventScope), message.channel);
3408
+ channelInfo = await ctx.resolveChannelName(message.channel, conversationEventScope);
3409
+ resolvedChannelType = normalizeSlackChannelType(message.channel_type ?? channelInfo.type ?? ctx.recallSlackChannelType(message.channel, conversationEventScope), message.channel);
3480
3410
  }
3481
3411
  const channelName = channelInfo.name;
3482
3412
  const isDirectMessage = resolvedChannelType === "im";
@@ -3484,7 +3414,7 @@ async function resolveSlackConversationContext(params) {
3484
3414
  const isRoom = resolvedChannelType === "channel" || resolvedChannelType === "group";
3485
3415
  const isRoomish = isRoom || isGroupDm;
3486
3416
  const channelConfig = isRoom ? resolveSlackChannelConfig({
3487
- teamId: params.eventScope?.teamId ?? ctx.teamId,
3417
+ teamId: conversationEventScope?.teamId ?? ctx.teamId,
3488
3418
  allowUnscoped: ctx.installationIdentity?.kind !== "enterprise",
3489
3419
  channelId: message.channel,
3490
3420
  channelName,
@@ -3494,22 +3424,19 @@ async function resolveSlackConversationContext(params) {
3494
3424
  allowNameMatching: ctx.allowNameMatching
3495
3425
  }) : null;
3496
3426
  const allowBotsSetting = channelConfig?.allowBots ?? account.config?.allowBots ?? cfg.channels?.slack?.allowBots ?? true;
3497
- return {
3498
- channelInfo,
3499
- channelName,
3500
- resolvedChannelType,
3501
- isDirectMessage,
3502
- isGroupDm,
3503
- isRoom,
3504
- isRoomish,
3505
- channelConfig,
3506
- allowBotsMode: allowBotsSetting === "mentions" ? "mentions" : allowBotsSetting ? "all" : "off",
3507
- isBotMessage: Boolean(message.bot_id)
3427
+ const allowBotsMode = allowBotsSetting === "mentions" ? "mentions" : allowBotsSetting ? "all" : "off";
3428
+ const isBotMessage = Boolean(message.bot_id);
3429
+ const messageText = message.text ?? "";
3430
+ const mentionMetadata = {
3431
+ mentionedUserIds: collectSlackMentionIds(messageText, SLACK_USER_MENTION_RE),
3432
+ mentionedSubteamIds: collectSlackMentionIds(messageText, SLACK_SUBTEAM_MENTION_RE),
3433
+ hasAnyMention: SLACK_ANY_MENTION_RE.test(messageText),
3434
+ hasSubteamMention: messageText.includes(SLACK_SUBTEAM_MENTION_MARKER)
3508
3435
  };
3509
- }
3510
- async function authorizeSlackInboundMessage(params) {
3511
- const { ctx, account, message, conversation, drop } = params;
3512
- const { isDirectMessage, channelName, resolvedChannelType, isBotMessage, allowBotsMode } = conversation;
3436
+ const normalizedBotUserId = normalizeSlackId(ctx.botUserId);
3437
+ const explicitBotMention = opts.source === "app_mention" || Boolean(normalizedBotUserId && mentionMetadata.mentionedUserIds.includes(normalizedBotUserId));
3438
+ const authorizationEventScope = opts.eventScope;
3439
+ const onVisibleDrop = opts.onVisibleDrop;
3513
3440
  if (isBotMessage) {
3514
3441
  if (ctx.botUserId && message.user === ctx.botUserId || ctx.botId && message.bot_id === ctx.botId) return null;
3515
3442
  if (allowBotsMode === "off") return drop("bot-disabled");
@@ -3518,28 +3445,28 @@ async function authorizeSlackInboundMessage(params) {
3518
3445
  const senderId = message.user ?? (isBotMessage ? message.bot_id : void 0);
3519
3446
  if (!senderId) return drop("missing-sender");
3520
3447
  if (!ctx.isChannelAllowed({
3521
- teamId: params.eventScope?.teamId ?? ctx.teamId,
3448
+ teamId: authorizationEventScope?.teamId ?? ctx.teamId,
3522
3449
  channelId: message.channel,
3523
3450
  channelName,
3524
3451
  channelType: resolvedChannelType
3525
3452
  })) {
3526
- if (conversation.isRoom && ctx.groupPolicy === "allowlist" && params.explicitBotMention && !isBotMessage && message.user) {
3453
+ if (isRoom && ctx.groupPolicy === "allowlist" && explicitBotMention && !isBotMessage && message.user) {
3527
3454
  let subject = "This OpenClaw bot";
3528
3455
  if (ctx.botUserId) try {
3529
- const botIdentity = await ctx.resolveUserName(ctx.botUserId, params.eventScope);
3456
+ const botIdentity = await ctx.resolveUserName(ctx.botUserId, authorizationEventScope);
3530
3457
  const botName = normalizeOptionalString(botIdentity?.name);
3531
3458
  if (botName) subject = escapeSlackMrkdwn(botName);
3532
3459
  } catch (error) {
3533
3460
  logVerbose(`slack allowlist denial bot-name lookup failed: ${formatSlackError(error)}`);
3534
3461
  }
3535
3462
  try {
3536
- await (params.eventScope?.client ?? ctx.app.client).chat.postEphemeral({
3463
+ await (authorizationEventScope?.client ?? ctx.app.client).chat.postEphemeral({
3537
3464
  token: ctx.botToken,
3538
3465
  channel: message.channel,
3539
3466
  user: message.user,
3540
3467
  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.>`
3541
3468
  });
3542
- params.onVisibleDrop?.();
3469
+ onVisibleDrop?.();
3543
3470
  } catch (error) {
3544
3471
  ctx.runtime.error?.(`slack allowlist denial notice failed for channel ${message.channel}: ${formatSlackError(error)}`);
3545
3472
  }
@@ -3548,7 +3475,7 @@ async function authorizeSlackInboundMessage(params) {
3548
3475
  }
3549
3476
  const allowFromLower = await resolveSlackEffectiveAllowFrom(ctx, {
3550
3477
  includePairingStore: isDirectMessage,
3551
- eventScope: params.eventScope
3478
+ eventScope: authorizationEventScope
3552
3479
  });
3553
3480
  if (isDirectMessage) {
3554
3481
  const directUserId = message.user;
@@ -3558,16 +3485,16 @@ async function authorizeSlackInboundMessage(params) {
3558
3485
  ctx,
3559
3486
  accountId: account.accountId,
3560
3487
  senderId: directUserId,
3561
- eventScope: params.eventScope,
3488
+ eventScope: authorizationEventScope,
3562
3489
  allowFromLower,
3563
- resolveSenderName: (userId) => ctx.resolveUserName(userId, params.eventScope),
3490
+ resolveSenderName: (userId) => ctx.resolveUserName(userId, authorizationEventScope),
3564
3491
  sendPairingReply: async (text) => {
3565
3492
  await sendMessageSlack(message.channel, text, {
3566
3493
  cfg: ctx.cfg,
3567
3494
  token: ctx.botToken,
3568
- client: params.eventScope?.client ?? ctx.app.client,
3495
+ client: authorizationEventScope?.client ?? ctx.app.client,
3569
3496
  accountId: account.accountId,
3570
- eventScope: params.eventScope
3497
+ eventScope: authorizationEventScope
3571
3498
  });
3572
3499
  },
3573
3500
  onDisabled: () => {
@@ -3579,54 +3506,6 @@ async function authorizeSlackInboundMessage(params) {
3579
3506
  log: logVerbose
3580
3507
  })) return drop(dropReason);
3581
3508
  }
3582
- return {
3583
- senderId,
3584
- allowFromLower
3585
- };
3586
- }
3587
- async function prepareSlackMessage(params) {
3588
- const { ctx, account, message, opts } = params;
3589
- const drop = (reason, parentUserId) => {
3590
- ctx.logger.info({
3591
- provider: "slack",
3592
- accountId: account.accountId,
3593
- teamId: opts.eventScope?.teamId ?? ctx.teamId,
3594
- channelId: message.channel,
3595
- messageTs: message.ts,
3596
- source: opts.source,
3597
- reason,
3598
- ...parentUserId ? { parentUserId } : {}
3599
- }, "Slack inbound event rejected during preparation");
3600
- return null;
3601
- };
3602
- const slackClient = opts.eventScope?.client ?? ctx.app.client;
3603
- const threadStarterWorkspaceScope = {
3604
- accountId: account.accountId,
3605
- teamId: opts.eventScope?.teamId ?? ctx.teamId
3606
- };
3607
- const cfg = ctx.cfg;
3608
- const conversation = await resolveSlackConversationContext({
3609
- ctx,
3610
- account,
3611
- message,
3612
- eventScope: opts.eventScope
3613
- });
3614
- const { channelInfo, channelName, isDirectMessage, isGroupDm, isRoom, isRoomish, channelConfig, allowBotsMode, isBotMessage } = conversation;
3615
- const messageText = message.text ?? "";
3616
- const mentionMetadata = collectSlackMentionMetadata(messageText);
3617
- const normalizedBotUserId = normalizeSlackId(ctx.botUserId);
3618
- const authorization = await authorizeSlackInboundMessage({
3619
- ctx,
3620
- account,
3621
- message,
3622
- conversation,
3623
- explicitBotMention: opts.source === "app_mention" || Boolean(normalizedBotUserId && mentionMetadata.mentionedUserIds.includes(normalizedBotUserId)),
3624
- eventScope: opts.eventScope,
3625
- onVisibleDrop: opts.onVisibleDrop,
3626
- drop
3627
- });
3628
- if (!authorization) return null;
3629
- const { senderId, allowFromLower } = authorization;
3630
3509
  let resolvedSenderName = normalizeOptionalString(message.username);
3631
3510
  const resolveSenderName = async () => {
3632
3511
  if (resolvedSenderName) return resolvedSenderName;
@@ -3662,9 +3541,17 @@ async function prepareSlackMessage(params) {
3662
3541
  });
3663
3542
  const explicitlyMentioned = explicitlyMentionedBotUser || explicitlyMentionedBotSubteam || opts.source === "app_mention";
3664
3543
  const channelRequireMention = channelConfig?.requireMention ?? ctx.defaultRequireMention ?? true;
3665
- const chatType = resolveSlackChatType(conversation.resolvedChannelType);
3544
+ const chatType = resolveSlackChatType(resolvedChannelType);
3666
3545
  const restoredAssistantThreadContext = await restoredAssistantThreadContextPromise;
3667
- const assistantThreadContext = mergeSlackAssistantThreadContext(messageAssistantThreadContext, cachedAssistantThreadContext ?? restoredAssistantThreadContext);
3546
+ const primary = messageAssistantThreadContext;
3547
+ const fallback = cachedAssistantThreadContext ?? restoredAssistantThreadContext;
3548
+ const assistantThreadContext = !primary ? fallback : !fallback ? primary : {
3549
+ assistantChannelId: primary.assistantChannelId || fallback.assistantChannelId,
3550
+ threadTs: primary.threadTs || fallback.threadTs,
3551
+ channelId: primary.channelId ?? fallback.channelId,
3552
+ teamId: primary.teamId ?? fallback.teamId,
3553
+ enterpriseId: primary.enterpriseId !== void 0 ? primary.enterpriseId : fallback.enterpriseId
3554
+ };
3668
3555
  const assistantThreadContextToCache = messageAssistantThreadContext || restoredAssistantThreadContext ? assistantThreadContext : void 0;
3669
3556
  if (assistantThreadContextToCache) ctx.saveSlackAssistantThreadContext(assistantThreadContextToCache, opts.eventScope);
3670
3557
  const canUseManagedView = isDirectMessage && !opts.eventScope && !assistantThreadContext;
@@ -3728,19 +3615,16 @@ async function prepareSlackMessage(params) {
3728
3615
  let mentionRegexes = buildPolicyMentionRegexes(routing.route.agentId);
3729
3616
  let wasMentioned = resolveWasMentioned(mentionRegexes);
3730
3617
  const hasBoundSession = Boolean(routing.runtimeBoundSessionKey || routing.configuredBindingSessionKey);
3731
- const { configuredBinding, configuredBindingSessionKey } = routing;
3618
+ const { configuredBinding } = routing;
3732
3619
  const isAssistantThreadMessage = Boolean(isDirectMessage && messageAssistantThreadContext);
3733
3620
  const forcedAssistantReplyThreadTs = Boolean(assistantThreadContext?.threadTs && (routing.isThreadReply || isAssistantThreadMessage || routing.replyToMode !== "off")) ? assistantThreadContext?.threadTs : void 0;
3734
3621
  const forcedReplyThreadTs = agentViewThreadTs ?? forcedAssistantReplyThreadTs;
3735
- if (routing.runtimeBinding && shouldLogVerbose()) logVerbose(`slack: routed via bound conversation ${routing.runtimeBinding.conversation.conversationId} -> ${routing.runtimeBinding.targetSessionKey}`);
3736
3622
  if (configuredBinding) {
3737
3623
  const ensured = await ensureConfiguredBindingRouteReady({
3738
3624
  cfg,
3739
3625
  bindingResolution: configuredBinding
3740
3626
  });
3741
- if (ensured.ok) {
3742
- if (shouldLogVerbose()) logVerbose(`slack: using configured ACP binding for ${configuredBinding.record.conversation.conversationId} -> ${configuredBindingSessionKey}`);
3743
- } else {
3627
+ if (!ensured.ok) {
3744
3628
  if (shouldLogVerbose()) logVerbose(`slack: configured ACP binding unavailable for ${configuredBinding.record.conversation.conversationId}: ${ensured.error}`);
3745
3629
  return drop("configured-binding-unavailable");
3746
3630
  }
@@ -3778,7 +3662,11 @@ async function prepareSlackMessage(params) {
3778
3662
  ctx,
3779
3663
  account,
3780
3664
  message,
3781
- conversation,
3665
+ conversation: {
3666
+ isRoom,
3667
+ isDirectMessage,
3668
+ channelConfig
3669
+ },
3782
3670
  thread: routing,
3783
3671
  wasMentioned,
3784
3672
  teamId: opts.eventScope?.teamId,
@@ -3796,14 +3684,14 @@ async function prepareSlackMessage(params) {
3796
3684
  const textForCommandDetection = stripSlackMentionsForCommandDetection(message.text ?? "");
3797
3685
  const hasControlCommandInMessage = hasControlCommand(textForCommandDetection, cfg);
3798
3686
  const hasAbortRequest = isAbortRequestText(textForCommandDetection);
3799
- const channelUsersAllowlistConfigured = isRoom && Array.isArray(channelConfig?.users) && channelConfig.users.length > 0;
3687
+ const channelUsersAllowlistConfigured = isRoom && (channelConfig?.users?.length ?? 0) > 0;
3800
3688
  const resolveMessageIngress = async (contextBinding, threadId) => await resolveSlackCommandIngress({
3801
3689
  ctx,
3802
3690
  teamId: opts.eventScope?.teamId ?? ctx.teamId,
3803
3691
  senderId,
3804
3692
  senderAuthentication: message.user ? opts.senderAuthentication : void 0,
3805
3693
  senderName: senderNameForAuth,
3806
- channelType: conversation.resolvedChannelType ?? "channel",
3694
+ channelType: resolvedChannelType ?? "channel",
3807
3695
  channelId: message.channel,
3808
3696
  threadId,
3809
3697
  ownerAllowFromLower: allowFromLower,
@@ -3938,13 +3826,7 @@ async function prepareSlackMessage(params) {
3938
3826
  const effectiveWasMentioned = mentionDecision.effectiveWasMentioned;
3939
3827
  const shouldBypassMention = mentionDecision.shouldBypassMention;
3940
3828
  const matchedImplicitMentionKinds = mentionDecision.matchedImplicitMentionKinds;
3941
- const mentionSource = resolveSlackMentionSource({
3942
- explicitBotMention: explicitlyMentionedBotUser || opts.source === "app_mention",
3943
- explicitSubteamMention: explicitlyMentionedBotSubteam,
3944
- matchedImplicitMentionKinds,
3945
- shouldBypassMention,
3946
- wasMentioned
3947
- });
3829
+ const mentionSource = explicitlyMentionedBotUser || opts.source === "app_mention" ? "explicit_bot" : explicitlyMentionedBotSubteam ? "subteam" : shouldBypassMention ? "command_bypass" : wasMentioned ? "mention_pattern" : matchedImplicitMentionKinds.length > 0 ? "implicit_thread" : "none";
3948
3830
  if (isBotMessage && allowBotsMode === "mentions") {
3949
3831
  if (!(isDirectMessage || effectiveWasMentioned || shouldBypassMention)) return drop("bot-missing-mention");
3950
3832
  }
@@ -4275,7 +4157,6 @@ async function prepareSlackMessage(params) {
4275
4157
  originatingTo: preflightChannelTarget,
4276
4158
  messageThreadId: threadContext.messageThreadId
4277
4159
  });
4278
- 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}`);
4279
4160
  const updateLastRouteSessionKey = resolveInboundLastRouteSessionKey({
4280
4161
  route,
4281
4162
  sessionKey