@openclaw/slack 2026.9.5 → 2026.9.7

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 (648) hide show
  1. package/assets/icon.png +0 -0
  2. package/dist/.setup/{account-inspect-B86OaUEt.mjs → account-inspect-DWBXpER6.mjs} +1 -1
  3. package/dist/.setup/{accounts-C1FspRF-.mjs → accounts-D_N9IqNA.mjs} +33 -10
  4. package/dist/.setup/{action-runtime-Aa-h24Fq.mjs → action-runtime-DfWvuiZd.mjs} +46 -65
  5. package/dist/.setup/action-runtime.runtime-Ck4bAWOX.mjs +2 -0
  6. package/dist/.setup/{actions-BvGUGk3w.mjs → actions-Df0CzcDl.mjs} +148 -204
  7. package/dist/.setup/{approval-handler.runtime-BcZNF-VP.mjs → approval-handler.runtime-Ed6Sw5VK.mjs} +37 -47
  8. package/dist/.setup/{approval-message-updates-Cy1cxfBn.mjs → approval-message-updates-BadvtA7R.mjs} +1 -2
  9. package/dist/.setup/{channel-DrBm0NFw.mjs → channel-C7bgPSTE.mjs} +192 -280
  10. package/dist/.setup/channel-meta-C443t-lI.mjs +27 -0
  11. package/dist/.setup/{channel-type-osC0XOES.mjs → channel-type-ClOttrZ5.mjs} +13 -21
  12. package/dist/.setup/{channel.setup-BSY5hzVz.mjs → channel.setup-Dh2jzAjN.mjs} +35 -42
  13. package/dist/.setup/{config-schema-DrY3S7Ee.mjs → config-schema-BZCxmHKG.mjs} +32 -21
  14. package/dist/.setup/{directory-config-CTs7YyuU.mjs → directory-config-BOUWH3hR.mjs} +7 -10
  15. package/dist/.setup/{directory-live-Btq0063Q.mjs → directory-live-B2O4B_Ho.mjs} +9 -21
  16. package/dist/.setup/doctor-contract-CRN1Z_c7.mjs +342 -0
  17. package/dist/.setup/{message-tool-api-DRnDCVdY.mjs → message-tool-api-Cu4lwlGy.mjs} +34 -48
  18. package/dist/.setup/{provider-w36VTgZ3.mjs → monitor-BPVfkumU.mjs} +2191 -2704
  19. package/dist/.setup/{outbound-adapter-K3Ek1iSR.mjs → outbound-adapter-Bv48XUjq.mjs} +10 -21
  20. package/dist/.setup/{pipeline.runtime-BZqGcbfi.mjs → pipeline.runtime-B1H63Frf.mjs} +901 -892
  21. package/dist/.setup/{plugin-routes-C3X0eVaK.mjs → plugin-routes-B8tooDLj.mjs} +1 -1
  22. package/dist/.setup/{policy-Db_QWCZZ.mjs → policy-D-jfM174.mjs} +5 -5
  23. package/dist/.setup/{probe-64C6k3_Q.mjs → probe-B2Y9qc_z.mjs} +101 -39
  24. package/dist/.setup/{registry-bbjH7IHX.mjs → registry-CIbMfuf8.mjs} +6 -2
  25. package/dist/.setup/{relay-source-CDqY76Vp.mjs → relay-source-BpKdjsll.mjs} +10 -12
  26. package/dist/.setup/{replies-CWFhjS9S.mjs → replies-BAnUtr2f.mjs} +16 -76
  27. package/dist/.setup/{reply-blocks-9uCeAJts.mjs → reply-blocks-SE1kTE3I.mjs} +27 -154
  28. package/dist/.setup/{resolve-channels-x8fX1SEt.mjs → resolve-channels-XXBn8UU_.mjs} +4 -5
  29. package/dist/.setup/{resolve-users-B8BpBnH2.mjs → resolve-users-DW0ngD03.mjs} +20 -22
  30. package/dist/.setup/{scopes-CJ-6l4v4.mjs → scopes-CasuDvaa.mjs} +6 -17
  31. package/dist/.setup/{secret-contract-DxmvbxBF.mjs → secret-contract-BaZcu6P_.mjs} +15 -20
  32. package/dist/.setup/{secret-contract-api-D9UfYujm.mjs → secret-contract-api-Dp6290Vg.mjs} +1 -1
  33. package/dist/.setup/{security-audit-BypHqcq5.mjs → security-audit-BqDc-6VL.mjs} +1 -1
  34. package/dist/.setup/{send-6GCb3lBP.mjs → send-BOVLQi3n.mjs} +102 -199
  35. package/dist/.setup/{session-status-BhG6pdiH.mjs → session-status-DscajIsd.mjs} +323 -250
  36. package/dist/.setup/{setup-plugin-api-DtCEByQB.mjs → setup-plugin-api-DTtbTKNk.mjs} +1 -1
  37. package/dist/.setup/{setup-surface-DsE0hsYY.mjs → setup-surface-Dn7QgWRE.mjs} +24 -56
  38. package/dist/.setup/{shared-BNmqCPgQ.mjs → shared-qau-uDGl.mjs} +30 -56
  39. package/dist/.setup/{slash-dispatch.runtime-Io4ASZsY.mjs → slash-dispatch.runtime--AEzLVBF.mjs} +1 -1
  40. package/dist/.setup/{streaming-compat-B-e0mqM0.mjs → streaming-compat-C5Wut626.mjs} +31 -8
  41. package/dist/.setup/{target-parsing-DEsPjngB.mjs → target-parsing-FFE26kOV.mjs} +1 -7
  42. package/dist/.setup/{targets-Cku2KCvg.mjs → targets-C6LZQL1Q.mjs} +1 -1
  43. package/dist/.setup/{thread-ts-CukseRVL.mjs → thread-ts-Cvt7WD6B.mjs} +0 -1
  44. package/dist/account-inspect-api.js +1 -1
  45. package/dist/api.js +18 -17
  46. package/dist/channel-config-api.js +1 -1
  47. package/dist/channel-plugin-api.js +1 -1
  48. package/dist/directory-contract-api.js +1 -1
  49. package/dist/doctor-contract-api.js +1 -1
  50. package/dist/http-routes-api.js +1 -1
  51. package/dist/index.js +1 -1
  52. package/dist/message-tool-api.js +1 -1
  53. package/dist/runtime-api.js +11 -12
  54. package/dist/secret-contract-api.js +1 -1
  55. package/dist/security-contract-api.js +1 -1
  56. package/dist/setup-entry.js +2 -2
  57. package/dist/setup-plugin-api.js +1 -1
  58. package/node_modules/@slack/bolt/node_modules/@types/node/README.md +1 -1
  59. package/node_modules/@slack/bolt/node_modules/@types/node/child_process.d.ts +1 -1
  60. package/node_modules/@slack/bolt/node_modules/@types/node/crypto.d.ts +20 -8
  61. package/node_modules/@slack/bolt/node_modules/@types/node/ffi.d.ts +14 -0
  62. package/node_modules/@slack/bolt/node_modules/@types/node/http2.d.ts +3 -2
  63. package/node_modules/@slack/bolt/node_modules/@types/node/net.d.ts +16 -4
  64. package/node_modules/@slack/bolt/node_modules/@types/node/package.json +2 -2
  65. package/node_modules/@slack/bolt/node_modules/@types/node/process.d.ts +1 -0
  66. package/node_modules/@slack/bolt/node_modules/@types/node/quic.d.ts +39 -1
  67. package/node_modules/@slack/bolt/node_modules/@types/node/stream/iter.d.ts +9 -12
  68. package/node_modules/@slack/bolt/node_modules/@types/node/test/reporters.d.ts +1 -0
  69. package/node_modules/@slack/bolt/node_modules/@types/node/test.d.ts +72 -0
  70. package/node_modules/@slack/bolt/node_modules/@types/node/tls.d.ts +14 -10
  71. package/node_modules/@slack/bolt/node_modules/@types/node/vm.d.ts +2 -1
  72. package/node_modules/@slack/bolt/node_modules/@types/node/web-globals/streams.d.ts +4 -0
  73. package/node_modules/@slack/bolt/node_modules/@types/node/worker_threads.d.ts +4 -1
  74. package/node_modules/@slack/web-api/node_modules/@types/node/README.md +1 -1
  75. package/node_modules/@slack/web-api/node_modules/@types/node/child_process.d.ts +1 -1
  76. package/node_modules/@slack/web-api/node_modules/@types/node/crypto.d.ts +20 -8
  77. package/node_modules/@slack/web-api/node_modules/@types/node/ffi.d.ts +14 -0
  78. package/node_modules/@slack/web-api/node_modules/@types/node/http2.d.ts +3 -2
  79. package/node_modules/@slack/web-api/node_modules/@types/node/net.d.ts +16 -4
  80. package/node_modules/@slack/web-api/node_modules/@types/node/package.json +2 -2
  81. package/node_modules/@slack/web-api/node_modules/@types/node/process.d.ts +1 -0
  82. package/node_modules/@slack/web-api/node_modules/@types/node/quic.d.ts +39 -1
  83. package/node_modules/@slack/web-api/node_modules/@types/node/stream/iter.d.ts +9 -12
  84. package/node_modules/@slack/web-api/node_modules/@types/node/test/reporters.d.ts +1 -0
  85. package/node_modules/@slack/web-api/node_modules/@types/node/test.d.ts +72 -0
  86. package/node_modules/@slack/web-api/node_modules/@types/node/tls.d.ts +14 -10
  87. package/node_modules/@slack/web-api/node_modules/@types/node/vm.d.ts +2 -1
  88. package/node_modules/@slack/web-api/node_modules/@types/node/web-globals/streams.d.ts +4 -0
  89. package/node_modules/@slack/web-api/node_modules/@types/node/worker_threads.d.ts +4 -1
  90. package/node_modules/get-east-asian-width/index.js +8 -3
  91. package/node_modules/get-east-asian-width/lookup-data.js +1 -1
  92. package/node_modules/get-east-asian-width/lookup.js +4 -4
  93. package/node_modules/get-east-asian-width/package.json +1 -1
  94. package/node_modules/typebox/build/compile/code.mjs +1 -2
  95. package/node_modules/typebox/build/compile/validator.mjs +5 -1
  96. package/node_modules/typebox/build/format/iri.mjs +16 -9
  97. package/node_modules/typebox/build/guard/emit.mjs +2 -2
  98. package/node_modules/typebox/build/guard/guard.d.mts +7 -5
  99. package/node_modules/typebox/build/guard/guard.mjs +14 -9
  100. package/node_modules/typebox/build/guard/index.mjs +6 -0
  101. package/node_modules/typebox/build/guard/unicode/unicode.d.mts +6 -0
  102. package/node_modules/typebox/build/guard/unicode/unicode.mjs +35 -0
  103. package/node_modules/typebox/build/guard/{string.mjs → unicode/unicode_segment.mjs} +16 -19
  104. package/node_modules/typebox/build/schema/build.mjs +2 -3
  105. package/node_modules/typebox/build/schema/engine/_pathing.d.mts +1 -0
  106. package/node_modules/typebox/build/schema/engine/_pathing.mjs +11 -0
  107. package/node_modules/typebox/build/schema/engine/_refine.mjs +13 -5
  108. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +2 -1
  109. package/node_modules/typebox/build/schema/engine/dependencies.mjs +2 -1
  110. package/node_modules/typebox/build/schema/engine/dependentSchemas.mjs +2 -1
  111. package/node_modules/typebox/build/schema/engine/dynamicRef.d.mts +1 -1
  112. package/node_modules/typebox/build/schema/engine/dynamicRef.mjs +2 -2
  113. package/node_modules/typebox/build/schema/engine/format.mjs +9 -2
  114. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +3 -2
  115. package/node_modules/typebox/build/schema/engine/properties.mjs +3 -2
  116. package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -1
  117. package/node_modules/typebox/build/schema/engine/recursiveRef.d.mts +1 -1
  118. package/node_modules/typebox/build/schema/engine/recursiveRef.mjs +2 -2
  119. package/node_modules/typebox/build/schema/engine/ref.d.mts +1 -1
  120. package/node_modules/typebox/build/schema/engine/ref.mjs +2 -2
  121. package/node_modules/typebox/build/schema/engine/schema.mjs +24 -24
  122. package/node_modules/typebox/build/schema/parse.mjs +3 -0
  123. package/node_modules/typebox/build/schema/pointer/pointer.d.mts +1 -6
  124. package/node_modules/typebox/build/schema/pointer/pointer.mjs +47 -64
  125. package/node_modules/typebox/build/system/settings/internal.d.mts +2 -0
  126. package/node_modules/typebox/build/system/settings/internal.mjs +20 -0
  127. package/node_modules/typebox/build/system/settings/settings.d.mts +11 -0
  128. package/node_modules/typebox/build/system/settings/settings.mjs +14 -0
  129. package/node_modules/typebox/build/type/extends/inference.d.mts +1 -1
  130. package/node_modules/typebox/build/type/extends/inference.mjs +15 -2
  131. package/node_modules/typebox/build/type/extends/tuple.d.mts +2 -1
  132. package/node_modules/typebox/build/type/extends/tuple.mjs +21 -1
  133. package/node_modules/typebox/build/value/parse/parse.mjs +10 -4
  134. package/node_modules/typebox/package.json +1 -1
  135. package/node_modules/typebox/readme.md +3 -1
  136. package/node_modules/undici/README.md +81 -25
  137. package/node_modules/undici/docs/docs/api/Agent.md +249 -42
  138. package/node_modules/undici/docs/docs/api/BalancedPool.md +246 -48
  139. package/node_modules/undici/docs/docs/api/CacheStorage.md +171 -13
  140. package/node_modules/undici/docs/docs/api/CacheStore.md +294 -98
  141. package/node_modules/undici/docs/docs/api/Client.md +407 -162
  142. package/node_modules/undici/docs/docs/api/ClientStats.md +80 -14
  143. package/node_modules/undici/docs/docs/api/Connector.md +118 -35
  144. package/node_modules/undici/docs/docs/api/ContentType.md +65 -25
  145. package/node_modules/undici/docs/docs/api/Cookies.md +122 -71
  146. package/node_modules/undici/docs/docs/api/Debug.md +34 -18
  147. package/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +364 -164
  148. package/node_modules/undici/docs/docs/api/Dispatcher.md +490 -1055
  149. package/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +105 -93
  150. package/node_modules/undici/docs/docs/api/Errors.md +503 -28
  151. package/node_modules/undici/docs/docs/api/EventSource.md +326 -13
  152. package/node_modules/undici/docs/docs/api/Fetch.md +681 -30
  153. package/node_modules/undici/docs/docs/api/GlobalInstallation.md +78 -96
  154. package/node_modules/undici/docs/docs/api/H2CClient.md +298 -176
  155. package/node_modules/undici/docs/docs/api/Interceptors.md +418 -0
  156. package/node_modules/undici/docs/docs/api/MockAgent.md +333 -314
  157. package/node_modules/undici/docs/docs/api/MockCallHistory.md +272 -98
  158. package/node_modules/undici/docs/docs/api/MockCallHistoryLog.md +189 -26
  159. package/node_modules/undici/docs/docs/api/MockClient.md +151 -33
  160. package/node_modules/undici/docs/docs/api/MockErrors.md +56 -5
  161. package/node_modules/undici/docs/docs/api/MockPool.md +321 -258
  162. package/node_modules/undici/docs/docs/api/Pool.md +236 -41
  163. package/node_modules/undici/docs/docs/api/PoolStats.md +119 -13
  164. package/node_modules/undici/docs/docs/api/ProxyAgent.md +179 -160
  165. package/node_modules/undici/docs/docs/api/RedirectHandler.md +238 -55
  166. package/node_modules/undici/docs/docs/api/RetryAgent.md +113 -27
  167. package/node_modules/undici/docs/docs/api/RetryHandler.md +161 -79
  168. package/node_modules/undici/docs/docs/api/RoundRobinPool.md +166 -72
  169. package/node_modules/undici/docs/docs/api/SnapshotAgent.md +262 -517
  170. package/node_modules/undici/docs/docs/api/Socks5ProxyAgent.md +177 -196
  171. package/node_modules/undici/docs/docs/api/Util.md +68 -11
  172. package/node_modules/undici/docs/docs/api/WebSocket.md +475 -80
  173. package/node_modules/undici/docs/docs/api/api-lifecycle.md +100 -32
  174. package/node_modules/undici/docs/docs/best-practices/client-certificate.md +2 -0
  175. package/node_modules/undici/docs/docs/best-practices/crawling.md +3 -1
  176. package/node_modules/undici/docs/docs/best-practices/migrating-from-v7-to-v8.md +233 -0
  177. package/node_modules/undici/docs/docs/best-practices/mocking-request.md +9 -7
  178. package/node_modules/undici/docs/docs/best-practices/proxy.md +3 -1
  179. package/node_modules/undici/docs/docs/best-practices/undici-vs-builtin-fetch.md +10 -8
  180. package/node_modules/undici/docs/docs/best-practices/writing-tests.md +2 -0
  181. package/node_modules/undici/docs/docs/getting-started.md +281 -0
  182. package/node_modules/undici/docs/docs/index.md +784 -0
  183. package/node_modules/undici/docs/docs/site.json +125 -0
  184. package/node_modules/undici/docs/docs/type-map.json +79 -0
  185. package/node_modules/undici/index.js +4 -2
  186. package/node_modules/undici/lib/api/api-connect.js +13 -11
  187. package/node_modules/undici/lib/api/api-pipeline.js +26 -13
  188. package/node_modules/undici/lib/api/api-request.js +51 -21
  189. package/node_modules/undici/lib/api/api-stream.js +81 -20
  190. package/node_modules/undici/lib/api/api-upgrade.js +21 -11
  191. package/node_modules/undici/lib/api/readable.js +45 -9
  192. package/node_modules/undici/lib/cache/memory-cache-store.js +3 -3
  193. package/node_modules/undici/lib/cache/sqlite-cache-store.js +3 -1
  194. package/node_modules/undici/lib/core/connect.js +46 -7
  195. package/node_modules/undici/lib/core/constants.js +1 -24
  196. package/node_modules/undici/lib/core/errors.js +22 -2
  197. package/node_modules/undici/lib/core/request.js +100 -18
  198. package/node_modules/undici/lib/core/symbols.js +4 -0
  199. package/node_modules/undici/lib/core/util.js +105 -48
  200. package/node_modules/undici/lib/dispatcher/agent.js +51 -32
  201. package/node_modules/undici/lib/dispatcher/balanced-pool.js +23 -27
  202. package/node_modules/undici/lib/dispatcher/client-h1.js +86 -24
  203. package/node_modules/undici/lib/dispatcher/client-h2.js +1091 -361
  204. package/node_modules/undici/lib/dispatcher/client.js +109 -36
  205. package/node_modules/undici/lib/dispatcher/dispatcher-base.js +19 -6
  206. package/node_modules/undici/lib/dispatcher/dispatcher.js +15 -5
  207. package/node_modules/undici/lib/dispatcher/dispatcher1-wrapper.js +110 -0
  208. package/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +37 -8
  209. package/node_modules/undici/lib/dispatcher/h2c-client.js +4 -4
  210. package/node_modules/undici/lib/dispatcher/pool-base.js +28 -10
  211. package/node_modules/undici/lib/dispatcher/pool.js +30 -5
  212. package/node_modules/undici/lib/dispatcher/proxy-agent.js +95 -19
  213. package/node_modules/undici/lib/dispatcher/retry-agent.js +3 -0
  214. package/node_modules/undici/lib/dispatcher/round-robin-pool.js +32 -10
  215. package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +153 -63
  216. package/node_modules/undici/lib/global.js +37 -14
  217. package/node_modules/undici/lib/handler/cache-handler.js +49 -7
  218. package/node_modules/undici/lib/handler/cache-revalidation-handler.js +10 -0
  219. package/node_modules/undici/lib/handler/decorator-handler.js +8 -3
  220. package/node_modules/undici/lib/handler/deduplication-handler.js +12 -6
  221. package/node_modules/undici/lib/handler/redirect-handler.js +66 -66
  222. package/node_modules/undici/lib/handler/retry-handler.js +191 -50
  223. package/node_modules/undici/lib/interceptor/cache.js +75 -25
  224. package/node_modules/undici/lib/interceptor/decompress.js +5 -2
  225. package/node_modules/undici/lib/interceptor/deduplicate.js +5 -4
  226. package/node_modules/undici/lib/interceptor/dns.js +12 -1
  227. package/node_modules/undici/lib/interceptor/redirect.js +3 -3
  228. package/node_modules/undici/lib/llhttp/llhttp-wasm.js +1 -1
  229. package/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +1 -1
  230. package/node_modules/undici/lib/mock/mock-agent.js +21 -9
  231. package/node_modules/undici/lib/mock/mock-call-history.js +1 -1
  232. package/node_modules/undici/lib/mock/mock-interceptor.js +25 -7
  233. package/node_modules/undici/lib/mock/mock-utils.js +281 -47
  234. package/node_modules/undici/lib/mock/snapshot-agent.js +25 -7
  235. package/node_modules/undici/lib/mock/snapshot-recorder.js +38 -3
  236. package/node_modules/undici/lib/util/cache.js +92 -10
  237. package/node_modules/undici/lib/util/runtime-features.js +3 -34
  238. package/node_modules/undici/lib/web/cache/cache.js +6 -8
  239. package/node_modules/undici/lib/web/cookies/parse.js +150 -146
  240. package/node_modules/undici/lib/web/cookies/util.js +1 -1
  241. package/node_modules/undici/lib/web/eventsource/eventsource-stream.js +28 -1
  242. package/node_modules/undici/lib/web/eventsource/eventsource.js +20 -21
  243. package/node_modules/undici/lib/web/eventsource/util.js +32 -1
  244. package/node_modules/undici/lib/web/fetch/body.js +48 -10
  245. package/node_modules/undici/lib/web/fetch/constants.js +1 -1
  246. package/node_modules/undici/lib/web/fetch/formdata.js +21 -2
  247. package/node_modules/undici/lib/web/fetch/index.js +242 -223
  248. package/node_modules/undici/lib/web/fetch/request.js +35 -6
  249. package/node_modules/undici/lib/web/fetch/response.js +1 -3
  250. package/node_modules/undici/lib/web/fetch/util.js +10 -2
  251. package/node_modules/undici/lib/web/webidl/index.js +5 -7
  252. package/node_modules/undici/lib/web/websocket/frame.js +1 -7
  253. package/node_modules/undici/lib/web/websocket/receiver.js +1 -1
  254. package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +10 -10
  255. package/node_modules/undici/lib/web/websocket/websocket.js +25 -3
  256. package/node_modules/undici/package.json +10 -10
  257. package/node_modules/undici/types/agent.d.ts +0 -2
  258. package/node_modules/undici/types/client.d.ts +69 -24
  259. package/node_modules/undici/types/connector.d.ts +1 -0
  260. package/node_modules/undici/types/cookies.d.ts +1 -1
  261. package/node_modules/undici/types/dispatcher.d.ts +8 -30
  262. package/node_modules/undici/types/dispatcher1-wrapper.d.ts +7 -0
  263. package/node_modules/undici/types/errors.d.ts +10 -0
  264. package/node_modules/undici/types/fetch.d.ts +5 -1
  265. package/node_modules/undici/types/formdata.d.ts +0 -6
  266. package/node_modules/undici/types/h2c-client.d.ts +6 -6
  267. package/node_modules/undici/types/handlers.d.ts +2 -0
  268. package/node_modules/undici/types/header.d.ts +5 -0
  269. package/node_modules/undici/types/index.d.ts +3 -1
  270. package/node_modules/undici/types/interceptors.d.ts +1 -1
  271. package/node_modules/undici/types/mock-agent.d.ts +11 -10
  272. package/node_modules/undici/types/mock-interceptor.d.ts +5 -1
  273. package/node_modules/undici/types/pool.d.ts +0 -2
  274. package/node_modules/undici/types/proxy-agent.d.ts +9 -2
  275. package/node_modules/undici/types/round-robin-pool.d.ts +0 -2
  276. package/node_modules/undici/types/snapshot-agent.d.ts +4 -0
  277. package/node_modules/undici/types/socks5-proxy-agent.d.ts +4 -2
  278. package/node_modules/undici/types/webidl.d.ts +0 -1
  279. package/node_modules/zod/README.md +2 -2
  280. package/node_modules/zod/compile.cjs +19 -0
  281. package/node_modules/zod/index.cjs +19 -0
  282. package/node_modules/zod/locales/index.cjs +19 -0
  283. package/node_modules/zod/mini/index.cjs +19 -0
  284. package/node_modules/zod/package.json +8 -7
  285. package/node_modules/zod/src/v3/helpers/errorUtil.ts +1 -1
  286. package/node_modules/zod/src/v4/classic/external.ts +2 -0
  287. package/node_modules/zod/src/v4/classic/from-json-schema.ts +248 -25
  288. package/node_modules/zod/src/v4/classic/schemas.ts +243 -168
  289. package/node_modules/zod/src/v4/classic/tests/assignability.test.ts +60 -9
  290. package/node_modules/zod/src/v4/classic/tests/bigint.test.ts +6 -0
  291. package/node_modules/zod/src/v4/classic/tests/continuability.test.ts +2 -2
  292. package/node_modules/zod/src/v4/classic/tests/cyclic-data.test.ts +265 -0
  293. package/node_modules/zod/src/v4/classic/tests/discriminated-unions.test.ts +112 -6
  294. package/node_modules/zod/src/v4/classic/tests/enum.test.ts +9 -0
  295. package/node_modules/zod/src/v4/classic/tests/error.test.ts +130 -1
  296. package/node_modules/zod/src/v4/classic/tests/from-json-schema.test.ts +157 -0
  297. package/node_modules/zod/src/v4/classic/tests/iban.test.ts +81 -0
  298. package/node_modules/zod/src/v4/classic/tests/instance-footprint.test.ts +40 -2
  299. package/node_modules/zod/src/v4/classic/tests/instanceof.test.ts +27 -7
  300. package/node_modules/zod/src/v4/classic/tests/issue-schema.test.ts +88 -56
  301. package/node_modules/zod/src/v4/classic/tests/number.test.ts +39 -0
  302. package/node_modules/zod/src/v4/classic/tests/object.test.ts +56 -0
  303. package/node_modules/zod/src/v4/classic/tests/optin-ladder.test.ts +3 -2
  304. package/node_modules/zod/src/v4/classic/tests/prefault.test.ts +70 -0
  305. package/node_modules/zod/src/v4/classic/tests/properties.test.ts +141 -0
  306. package/node_modules/zod/src/v4/classic/tests/recursive-types.test.ts +46 -1
  307. package/node_modules/zod/src/v4/classic/tests/standard-schema.test.ts +19 -0
  308. package/node_modules/zod/src/v4/classic/tests/string.test.ts +95 -1
  309. package/node_modules/zod/src/v4/classic/tests/template-literal.test.ts +33 -1
  310. package/node_modules/zod/src/v4/classic/tests/to-json-schema.test.ts +234 -38
  311. package/node_modules/zod/src/v4/classic/tests/validate.test.ts +569 -0
  312. package/node_modules/zod/src/v4/core/api.ts +44 -28
  313. package/node_modules/zod/src/v4/core/checks.ts +62 -102
  314. package/node_modules/zod/src/v4/core/compile.ts +218 -93
  315. package/node_modules/zod/src/v4/core/core.ts +1 -3
  316. package/node_modules/zod/src/v4/core/doc.ts +6 -2
  317. package/node_modules/zod/src/v4/core/errors.ts +3 -6
  318. package/node_modules/zod/src/v4/core/json-schema-generator.ts +2 -2
  319. package/node_modules/zod/src/v4/core/json-schema-processors.ts +185 -81
  320. package/node_modules/zod/src/v4/core/memoizer.ts +75 -28
  321. package/node_modules/zod/src/v4/core/parse.ts +37 -16
  322. package/node_modules/zod/src/v4/core/regexes.ts +14 -4
  323. package/node_modules/zod/src/v4/core/schemas.ts +296 -132
  324. package/node_modules/zod/src/v4/core/tests/cached.test.ts +54 -0
  325. package/node_modules/zod/src/v4/core/tests/compile-differential.test.ts +35 -0
  326. package/node_modules/zod/src/v4/core/tests/compile-generative.test.ts +445 -0
  327. package/node_modules/zod/src/v4/core/tests/compile.test.ts +399 -2
  328. package/node_modules/zod/src/v4/core/tests/email-regex.test.ts +140 -0
  329. package/node_modules/zod/src/v4/core/tests/locales/tg.test.ts +85 -0
  330. package/node_modules/zod/src/v4/core/tests/polyfill-collision.test.ts +46 -0
  331. package/node_modules/zod/src/v4/core/tests/url-no-canparse.test.ts +118 -16
  332. package/node_modules/zod/src/v4/core/to-json-schema.ts +9 -7
  333. package/node_modules/zod/src/v4/core/util.ts +218 -147
  334. package/node_modules/zod/src/v4/core/versions.ts +2 -2
  335. package/node_modules/zod/src/v4/locales/ar.ts +2 -0
  336. package/node_modules/zod/src/v4/locales/az.ts +2 -0
  337. package/node_modules/zod/src/v4/locales/be.ts +2 -0
  338. package/node_modules/zod/src/v4/locales/bg.ts +2 -0
  339. package/node_modules/zod/src/v4/locales/bn.ts +2 -0
  340. package/node_modules/zod/src/v4/locales/ca.ts +2 -0
  341. package/node_modules/zod/src/v4/locales/ckb.ts +2 -0
  342. package/node_modules/zod/src/v4/locales/cs.ts +2 -0
  343. package/node_modules/zod/src/v4/locales/da.ts +2 -0
  344. package/node_modules/zod/src/v4/locales/de.ts +2 -0
  345. package/node_modules/zod/src/v4/locales/el.ts +2 -0
  346. package/node_modules/zod/src/v4/locales/en.ts +2 -0
  347. package/node_modules/zod/src/v4/locales/eo.ts +2 -0
  348. package/node_modules/zod/src/v4/locales/es.ts +2 -0
  349. package/node_modules/zod/src/v4/locales/fa.ts +2 -0
  350. package/node_modules/zod/src/v4/locales/fi.ts +2 -0
  351. package/node_modules/zod/src/v4/locales/fr-CA.ts +2 -0
  352. package/node_modules/zod/src/v4/locales/fr.ts +2 -0
  353. package/node_modules/zod/src/v4/locales/gu.ts +2 -0
  354. package/node_modules/zod/src/v4/locales/he.ts +2 -0
  355. package/node_modules/zod/src/v4/locales/hi.ts +2 -0
  356. package/node_modules/zod/src/v4/locales/hr.ts +2 -0
  357. package/node_modules/zod/src/v4/locales/hu.ts +2 -0
  358. package/node_modules/zod/src/v4/locales/hy.ts +2 -0
  359. package/node_modules/zod/src/v4/locales/id.ts +2 -0
  360. package/node_modules/zod/src/v4/locales/index.ts +1 -0
  361. package/node_modules/zod/src/v4/locales/is.ts +2 -0
  362. package/node_modules/zod/src/v4/locales/it.ts +2 -0
  363. package/node_modules/zod/src/v4/locales/ja.ts +2 -0
  364. package/node_modules/zod/src/v4/locales/ka.ts +2 -0
  365. package/node_modules/zod/src/v4/locales/km.ts +2 -0
  366. package/node_modules/zod/src/v4/locales/kn.ts +2 -0
  367. package/node_modules/zod/src/v4/locales/ko.ts +2 -0
  368. package/node_modules/zod/src/v4/locales/lt.ts +2 -0
  369. package/node_modules/zod/src/v4/locales/mk.ts +2 -0
  370. package/node_modules/zod/src/v4/locales/ms.ts +2 -0
  371. package/node_modules/zod/src/v4/locales/ne.ts +2 -0
  372. package/node_modules/zod/src/v4/locales/nl.ts +2 -0
  373. package/node_modules/zod/src/v4/locales/nn.ts +2 -0
  374. package/node_modules/zod/src/v4/locales/no.ts +2 -0
  375. package/node_modules/zod/src/v4/locales/ota.ts +2 -0
  376. package/node_modules/zod/src/v4/locales/pl.ts +2 -0
  377. package/node_modules/zod/src/v4/locales/ps.ts +2 -0
  378. package/node_modules/zod/src/v4/locales/pt-BR.ts +2 -0
  379. package/node_modules/zod/src/v4/locales/pt.ts +2 -0
  380. package/node_modules/zod/src/v4/locales/ro.ts +2 -0
  381. package/node_modules/zod/src/v4/locales/ru.ts +2 -0
  382. package/node_modules/zod/src/v4/locales/sk.ts +2 -0
  383. package/node_modules/zod/src/v4/locales/sl.ts +2 -0
  384. package/node_modules/zod/src/v4/locales/sv.ts +2 -0
  385. package/node_modules/zod/src/v4/locales/ta.ts +2 -0
  386. package/node_modules/zod/src/v4/locales/tg.ts +135 -0
  387. package/node_modules/zod/src/v4/locales/th.ts +2 -0
  388. package/node_modules/zod/src/v4/locales/tk.ts +2 -0
  389. package/node_modules/zod/src/v4/locales/tr.ts +2 -0
  390. package/node_modules/zod/src/v4/locales/uk.ts +2 -0
  391. package/node_modules/zod/src/v4/locales/ur.ts +2 -0
  392. package/node_modules/zod/src/v4/locales/uz.ts +2 -0
  393. package/node_modules/zod/src/v4/locales/vi.ts +2 -0
  394. package/node_modules/zod/src/v4/locales/yo.ts +2 -0
  395. package/node_modules/zod/src/v4/locales/zh-CN.ts +2 -0
  396. package/node_modules/zod/src/v4/locales/zh-TW.ts +2 -0
  397. package/node_modules/zod/src/v4/mini/external.ts +2 -0
  398. package/node_modules/zod/src/v4/mini/schemas.ts +32 -7
  399. package/node_modules/zod/src/v4/mini/tests/checks.test.ts +1 -1
  400. package/node_modules/zod/src/v4/mini/tests/computed.test.ts +11 -17
  401. package/node_modules/zod/src/v4/mini/tests/index.test.ts +86 -0
  402. package/node_modules/zod/src/v4/mini/tests/recursive-types.test.ts +16 -0
  403. package/node_modules/zod/src/v4/mini/tests/string.test.ts +23 -0
  404. package/node_modules/zod/v3/ZodError.cjs +19 -0
  405. package/node_modules/zod/v3/errors.cjs +19 -0
  406. package/node_modules/zod/v3/external.cjs +19 -0
  407. package/node_modules/zod/v3/helpers/enumUtil.cjs +19 -0
  408. package/node_modules/zod/v3/helpers/errorUtil.cjs +20 -1
  409. package/node_modules/zod/v3/helpers/errorUtil.js +1 -1
  410. package/node_modules/zod/v3/helpers/parseUtil.cjs +19 -0
  411. package/node_modules/zod/v3/helpers/partialUtil.cjs +19 -0
  412. package/node_modules/zod/v3/helpers/typeAliases.cjs +19 -0
  413. package/node_modules/zod/v3/helpers/util.cjs +19 -0
  414. package/node_modules/zod/v3/index.cjs +19 -0
  415. package/node_modules/zod/v3/standard-schema.cjs +19 -0
  416. package/node_modules/zod/v3/types.cjs +19 -0
  417. package/node_modules/zod/v4/classic/checks.cjs +19 -0
  418. package/node_modules/zod/v4/classic/coerce.cjs +19 -0
  419. package/node_modules/zod/v4/classic/compat.cjs +19 -0
  420. package/node_modules/zod/v4/classic/deep-partial.cjs +19 -0
  421. package/node_modules/zod/v4/classic/errors.cjs +19 -0
  422. package/node_modules/zod/v4/classic/external.cjs +22 -1
  423. package/node_modules/zod/v4/classic/external.d.cts +1 -1
  424. package/node_modules/zod/v4/classic/external.d.ts +1 -1
  425. package/node_modules/zod/v4/classic/external.js +1 -1
  426. package/node_modules/zod/v4/classic/from-json-schema.cjs +264 -24
  427. package/node_modules/zod/v4/classic/from-json-schema.js +245 -24
  428. package/node_modules/zod/v4/classic/in-out.cjs +19 -0
  429. package/node_modules/zod/v4/classic/index.cjs +19 -0
  430. package/node_modules/zod/v4/classic/iso.cjs +19 -0
  431. package/node_modules/zod/v4/classic/parse.cjs +19 -0
  432. package/node_modules/zod/v4/classic/schemas.cjs +100 -35
  433. package/node_modules/zod/v4/classic/schemas.d.cts +15 -1
  434. package/node_modules/zod/v4/classic/schemas.d.ts +15 -1
  435. package/node_modules/zod/v4/classic/schemas.js +77 -33
  436. package/node_modules/zod/v4/core/api.cjs +45 -22
  437. package/node_modules/zod/v4/core/api.d.cts +12 -6
  438. package/node_modules/zod/v4/core/api.d.ts +12 -6
  439. package/node_modules/zod/v4/core/api.js +25 -22
  440. package/node_modules/zod/v4/core/checks.cjs +51 -97
  441. package/node_modules/zod/v4/core/checks.d.cts +17 -1
  442. package/node_modules/zod/v4/core/checks.d.ts +17 -1
  443. package/node_modules/zod/v4/core/checks.js +31 -96
  444. package/node_modules/zod/v4/core/compile.cjs +173 -78
  445. package/node_modules/zod/v4/core/compile.d.cts +18 -4
  446. package/node_modules/zod/v4/core/compile.d.ts +18 -4
  447. package/node_modules/zod/v4/core/compile.js +154 -79
  448. package/node_modules/zod/v4/core/core.cjs +20 -1
  449. package/node_modules/zod/v4/core/core.js +1 -1
  450. package/node_modules/zod/v4/core/doc.cjs +26 -2
  451. package/node_modules/zod/v4/core/doc.js +7 -2
  452. package/node_modules/zod/v4/core/errors.cjs +21 -5
  453. package/node_modules/zod/v4/core/errors.js +2 -5
  454. package/node_modules/zod/v4/core/index.cjs +19 -0
  455. package/node_modules/zod/v4/core/json-schema-generator.cjs +20 -1
  456. package/node_modules/zod/v4/core/json-schema-generator.js +2 -2
  457. package/node_modules/zod/v4/core/json-schema-processors.cjs +188 -72
  458. package/node_modules/zod/v4/core/json-schema-processors.d.cts +16 -0
  459. package/node_modules/zod/v4/core/json-schema-processors.d.ts +16 -0
  460. package/node_modules/zod/v4/core/json-schema-processors.js +170 -74
  461. package/node_modules/zod/v4/core/json-schema.cjs +19 -0
  462. package/node_modules/zod/v4/core/memoizer.cjs +111 -23
  463. package/node_modules/zod/v4/core/memoizer.js +69 -23
  464. package/node_modules/zod/v4/core/parse.cjs +56 -16
  465. package/node_modules/zod/v4/core/parse.js +37 -16
  466. package/node_modules/zod/v4/core/regexes.cjs +31 -6
  467. package/node_modules/zod/v4/core/regexes.d.cts +3 -0
  468. package/node_modules/zod/v4/core/regexes.d.ts +3 -0
  469. package/node_modules/zod/v4/core/regexes.js +10 -4
  470. package/node_modules/zod/v4/core/registries.cjs +19 -0
  471. package/node_modules/zod/v4/core/schemas.cjs +302 -114
  472. package/node_modules/zod/v4/core/schemas.d.cts +28 -8
  473. package/node_modules/zod/v4/core/schemas.d.ts +28 -8
  474. package/node_modules/zod/v4/core/schemas.js +279 -113
  475. package/node_modules/zod/v4/core/standard-schema.cjs +19 -0
  476. package/node_modules/zod/v4/core/to-json-schema.cjs +28 -7
  477. package/node_modules/zod/v4/core/to-json-schema.d.cts +4 -3
  478. package/node_modules/zod/v4/core/to-json-schema.d.ts +4 -3
  479. package/node_modules/zod/v4/core/to-json-schema.js +7 -6
  480. package/node_modules/zod/v4/core/util.cjs +177 -143
  481. package/node_modules/zod/v4/core/util.d.cts +25 -4
  482. package/node_modules/zod/v4/core/util.d.ts +25 -4
  483. package/node_modules/zod/v4/core/util.js +156 -143
  484. package/node_modules/zod/v4/core/versions.cjs +21 -2
  485. package/node_modules/zod/v4/core/versions.d.cts +1 -1
  486. package/node_modules/zod/v4/core/versions.d.ts +1 -1
  487. package/node_modules/zod/v4/core/versions.js +2 -2
  488. package/node_modules/zod/v4/core/visit.cjs +19 -0
  489. package/node_modules/zod/v4/index.cjs +19 -0
  490. package/node_modules/zod/v4/locales/ar.cjs +2 -0
  491. package/node_modules/zod/v4/locales/ar.js +2 -0
  492. package/node_modules/zod/v4/locales/az.cjs +2 -0
  493. package/node_modules/zod/v4/locales/az.js +2 -0
  494. package/node_modules/zod/v4/locales/be.cjs +2 -0
  495. package/node_modules/zod/v4/locales/be.js +2 -0
  496. package/node_modules/zod/v4/locales/bg.cjs +2 -0
  497. package/node_modules/zod/v4/locales/bg.js +2 -0
  498. package/node_modules/zod/v4/locales/bn.cjs +2 -0
  499. package/node_modules/zod/v4/locales/bn.js +2 -0
  500. package/node_modules/zod/v4/locales/ca.cjs +2 -0
  501. package/node_modules/zod/v4/locales/ca.js +2 -0
  502. package/node_modules/zod/v4/locales/ckb.cjs +2 -0
  503. package/node_modules/zod/v4/locales/ckb.js +2 -0
  504. package/node_modules/zod/v4/locales/cs.cjs +2 -0
  505. package/node_modules/zod/v4/locales/cs.js +2 -0
  506. package/node_modules/zod/v4/locales/da.cjs +2 -0
  507. package/node_modules/zod/v4/locales/da.js +2 -0
  508. package/node_modules/zod/v4/locales/de.cjs +2 -0
  509. package/node_modules/zod/v4/locales/de.js +2 -0
  510. package/node_modules/zod/v4/locales/el.cjs +2 -0
  511. package/node_modules/zod/v4/locales/el.js +2 -0
  512. package/node_modules/zod/v4/locales/en.cjs +2 -0
  513. package/node_modules/zod/v4/locales/en.js +2 -0
  514. package/node_modules/zod/v4/locales/eo.cjs +2 -0
  515. package/node_modules/zod/v4/locales/eo.js +2 -0
  516. package/node_modules/zod/v4/locales/es.cjs +2 -0
  517. package/node_modules/zod/v4/locales/es.js +2 -0
  518. package/node_modules/zod/v4/locales/fa.cjs +2 -0
  519. package/node_modules/zod/v4/locales/fa.js +2 -0
  520. package/node_modules/zod/v4/locales/fi.cjs +2 -0
  521. package/node_modules/zod/v4/locales/fi.js +2 -0
  522. package/node_modules/zod/v4/locales/fr-CA.cjs +2 -0
  523. package/node_modules/zod/v4/locales/fr-CA.js +2 -0
  524. package/node_modules/zod/v4/locales/fr.cjs +2 -0
  525. package/node_modules/zod/v4/locales/fr.js +2 -0
  526. package/node_modules/zod/v4/locales/gu.cjs +2 -0
  527. package/node_modules/zod/v4/locales/gu.js +2 -0
  528. package/node_modules/zod/v4/locales/he.cjs +2 -0
  529. package/node_modules/zod/v4/locales/he.js +2 -0
  530. package/node_modules/zod/v4/locales/hi.cjs +2 -0
  531. package/node_modules/zod/v4/locales/hi.js +2 -0
  532. package/node_modules/zod/v4/locales/hr.cjs +2 -0
  533. package/node_modules/zod/v4/locales/hr.js +2 -0
  534. package/node_modules/zod/v4/locales/hu.cjs +2 -0
  535. package/node_modules/zod/v4/locales/hu.js +2 -0
  536. package/node_modules/zod/v4/locales/hy.cjs +2 -0
  537. package/node_modules/zod/v4/locales/hy.js +2 -0
  538. package/node_modules/zod/v4/locales/id.cjs +2 -0
  539. package/node_modules/zod/v4/locales/id.js +2 -0
  540. package/node_modules/zod/v4/locales/index.cjs +22 -1
  541. package/node_modules/zod/v4/locales/index.d.cts +1 -0
  542. package/node_modules/zod/v4/locales/index.d.ts +1 -0
  543. package/node_modules/zod/v4/locales/index.js +1 -0
  544. package/node_modules/zod/v4/locales/is.cjs +2 -0
  545. package/node_modules/zod/v4/locales/is.js +2 -0
  546. package/node_modules/zod/v4/locales/it.cjs +2 -0
  547. package/node_modules/zod/v4/locales/it.js +2 -0
  548. package/node_modules/zod/v4/locales/ja.cjs +2 -0
  549. package/node_modules/zod/v4/locales/ja.js +2 -0
  550. package/node_modules/zod/v4/locales/ka.cjs +2 -0
  551. package/node_modules/zod/v4/locales/ka.js +2 -0
  552. package/node_modules/zod/v4/locales/km.cjs +2 -0
  553. package/node_modules/zod/v4/locales/km.js +2 -0
  554. package/node_modules/zod/v4/locales/kn.cjs +2 -0
  555. package/node_modules/zod/v4/locales/kn.js +2 -0
  556. package/node_modules/zod/v4/locales/ko.cjs +2 -0
  557. package/node_modules/zod/v4/locales/ko.js +2 -0
  558. package/node_modules/zod/v4/locales/lt.cjs +2 -0
  559. package/node_modules/zod/v4/locales/lt.js +2 -0
  560. package/node_modules/zod/v4/locales/mk.cjs +2 -0
  561. package/node_modules/zod/v4/locales/mk.js +2 -0
  562. package/node_modules/zod/v4/locales/ms.cjs +2 -0
  563. package/node_modules/zod/v4/locales/ms.js +2 -0
  564. package/node_modules/zod/v4/locales/ne.cjs +2 -0
  565. package/node_modules/zod/v4/locales/ne.js +2 -0
  566. package/node_modules/zod/v4/locales/nl.cjs +2 -0
  567. package/node_modules/zod/v4/locales/nl.js +2 -0
  568. package/node_modules/zod/v4/locales/nn.cjs +2 -0
  569. package/node_modules/zod/v4/locales/nn.js +2 -0
  570. package/node_modules/zod/v4/locales/no.cjs +2 -0
  571. package/node_modules/zod/v4/locales/no.js +2 -0
  572. package/node_modules/zod/v4/locales/ota.cjs +2 -0
  573. package/node_modules/zod/v4/locales/ota.js +2 -0
  574. package/node_modules/zod/v4/locales/pl.cjs +2 -0
  575. package/node_modules/zod/v4/locales/pl.js +2 -0
  576. package/node_modules/zod/v4/locales/ps.cjs +2 -0
  577. package/node_modules/zod/v4/locales/ps.js +2 -0
  578. package/node_modules/zod/v4/locales/pt-BR.cjs +2 -0
  579. package/node_modules/zod/v4/locales/pt-BR.js +2 -0
  580. package/node_modules/zod/v4/locales/pt.cjs +2 -0
  581. package/node_modules/zod/v4/locales/pt.js +2 -0
  582. package/node_modules/zod/v4/locales/ro.cjs +2 -0
  583. package/node_modules/zod/v4/locales/ro.js +2 -0
  584. package/node_modules/zod/v4/locales/ru.cjs +2 -0
  585. package/node_modules/zod/v4/locales/ru.js +2 -0
  586. package/node_modules/zod/v4/locales/sk.cjs +2 -0
  587. package/node_modules/zod/v4/locales/sk.js +2 -0
  588. package/node_modules/zod/v4/locales/sl.cjs +2 -0
  589. package/node_modules/zod/v4/locales/sl.js +2 -0
  590. package/node_modules/zod/v4/locales/sv.cjs +2 -0
  591. package/node_modules/zod/v4/locales/sv.js +2 -0
  592. package/node_modules/zod/v4/locales/ta.cjs +2 -0
  593. package/node_modules/zod/v4/locales/ta.js +2 -0
  594. package/node_modules/zod/v4/locales/tg.cjs +144 -0
  595. package/node_modules/zod/v4/locales/tg.d.cts +5 -0
  596. package/node_modules/zod/v4/locales/tg.d.ts +4 -0
  597. package/node_modules/zod/v4/locales/tg.js +117 -0
  598. package/node_modules/zod/v4/locales/th.cjs +2 -0
  599. package/node_modules/zod/v4/locales/th.js +2 -0
  600. package/node_modules/zod/v4/locales/tk.cjs +2 -0
  601. package/node_modules/zod/v4/locales/tk.js +2 -0
  602. package/node_modules/zod/v4/locales/tr.cjs +2 -0
  603. package/node_modules/zod/v4/locales/tr.js +2 -0
  604. package/node_modules/zod/v4/locales/uk.cjs +2 -0
  605. package/node_modules/zod/v4/locales/uk.js +2 -0
  606. package/node_modules/zod/v4/locales/ur.cjs +2 -0
  607. package/node_modules/zod/v4/locales/ur.js +2 -0
  608. package/node_modules/zod/v4/locales/uz.cjs +2 -0
  609. package/node_modules/zod/v4/locales/uz.js +2 -0
  610. package/node_modules/zod/v4/locales/vi.cjs +2 -0
  611. package/node_modules/zod/v4/locales/vi.js +2 -0
  612. package/node_modules/zod/v4/locales/yo.cjs +2 -0
  613. package/node_modules/zod/v4/locales/yo.js +2 -0
  614. package/node_modules/zod/v4/locales/zh-CN.cjs +2 -0
  615. package/node_modules/zod/v4/locales/zh-CN.js +2 -0
  616. package/node_modules/zod/v4/locales/zh-TW.cjs +2 -0
  617. package/node_modules/zod/v4/locales/zh-TW.js +2 -0
  618. package/node_modules/zod/v4/mini/checks.cjs +19 -0
  619. package/node_modules/zod/v4/mini/coerce.cjs +19 -0
  620. package/node_modules/zod/v4/mini/deep-partial.cjs +19 -0
  621. package/node_modules/zod/v4/mini/external.cjs +22 -1
  622. package/node_modules/zod/v4/mini/external.d.cts +1 -1
  623. package/node_modules/zod/v4/mini/external.d.ts +1 -1
  624. package/node_modules/zod/v4/mini/external.js +1 -1
  625. package/node_modules/zod/v4/mini/in-out.cjs +19 -0
  626. package/node_modules/zod/v4/mini/index.cjs +19 -0
  627. package/node_modules/zod/v4/mini/iso.cjs +19 -0
  628. package/node_modules/zod/v4/mini/parse.cjs +19 -0
  629. package/node_modules/zod/v4/mini/schemas.cjs +44 -8
  630. package/node_modules/zod/v4/mini/schemas.d.cts +6 -1
  631. package/node_modules/zod/v4/mini/schemas.d.ts +6 -1
  632. package/node_modules/zod/v4/mini/schemas.js +21 -6
  633. package/node_modules/zod/v4-mini/index.cjs +19 -0
  634. package/openclaw.plugin.json +49 -3
  635. package/package.json +8 -8
  636. package/dist/.setup/accounts.runtime-BpWv-6-2.mjs +0 -2
  637. package/dist/.setup/action-runtime.runtime-DwH3NdpT.mjs +0 -2
  638. package/dist/.setup/doctor-contract-CtMUqjvk.mjs +0 -186
  639. package/dist/.setup/monitor-Dw5uuKWM.mjs +0 -13
  640. package/dist/.setup/rolldown-runtime-D7D4PA-g.mjs +0 -13
  641. package/dist/.setup/send.runtime-BmqRP-WG.mjs +0 -2
  642. package/dist/.setup/slash-commands.runtime-BpSH0b3q.mjs +0 -2
  643. package/dist/.setup/slash-skill-commands.runtime-Bi1JWZHY.mjs +0 -2
  644. package/node_modules/undici/lib/handler/unwrap-handler.js +0 -106
  645. package/node_modules/undici/lib/handler/wrap-handler.js +0 -105
  646. package/node_modules/undici/lib/llhttp/.gitkeep +0 -0
  647. package/node_modules/undici/lib/util/promise.js +0 -28
  648. /package/node_modules/typebox/build/guard/{string.d.mts → unicode/unicode_segment.d.mts} +0 -0
@@ -1,27 +1,29 @@
1
- import { A as countSlackTextUtf8Bytes, C as buildSlackBlocksFallbackText, E as SLACK_EDIT_TEXT_MAX_BYTES, H as SLACK_SESSION_LINK_ACTION_ID, M as truncateSlackTextByUtf8Bytes, Pt as resolveSlackReplyToMode, Y as normalizeSlackOutboundText, c as qualifySlackRoutePeerId, j as truncateSlackText, k as SLACK_TEXT_LIMIT, ot as escapeSlackMrkdwn, v as hasSlackNativeDataBlock } from "./session-status-BhG6pdiH.mjs";
2
- import { n as formatSlackTarget } from "./target-parsing-DEsPjngB.mjs";
3
- import { E as normalizeSlackAllowOwnerEntry, O as resolveSlackAllowListMatch, T as normalizeAllowListLower, o as formatSlackError } from "./probe-64C6k3_Q.mjs";
4
- import { a as prepareSlackReply, l as resolveSlackReplyRenderPlan } from "./reply-blocks-9uCeAJts.mjs";
5
- import { n as resolveSlackReplyThreadTs } from "./thread-ts-CukseRVL.mjs";
6
- import { n as resolveSlackStreamingMode, t as resolveSlackNativeStreaming } from "./streaming-compat-B-e0mqM0.mjs";
7
- import { D as resolveSlackMessageText$1, E as resolveSlackBlocksText, O as buildSlackEditTextPayload, S as formatSlackFileReferenceList, T as isSlackUnfurlAttachment, a as editSlackRenderedMessage, d as reactSlackMessage, i as editSlackMessage, m as removeSlackReaction, n as deleteSlackMessage, v as logVerbose$1, w as hasSlackTableBlock, x as formatSlackFileReference } from "./actions-BvGUGk3w.mjs";
8
- import { c as hasSlackThreadFailureNotice, d as recordSlackThreadFailureNotice, f as recordSlackThreadParticipation, l as hasSlackThreadParticipation, m as buildSlackNativeDataDeliveryPlan, o as clearSlackThreadFailureNotice, r as sendMessageSlack, u as hasSlackThreadParticipationWithPersistence } from "./send-6GCb3lBP.mjs";
9
- import { A as resolveChannelContextVisibilityMode, D as readSlackAssistantThreadContext, E as buildSlackAssistantThreadMetadata, F as stripSlackMentionsForCommandDetection, M as resolveStorePath, N as updateLastRoute, O as getSessionEntry, T as resolveSlackChatType, _ as SlackStreamNotDeliveredError, b as startSlackStream, d as resolveSlackRoutingContext, f as registerSlackSessionRun, g as resolveSlackEffectiveAllowFrom, h as resolveSlackCommandIngress, j as resolveChannelResetConfig, k as readSessionUpdatedAt, l as authorizeSlackDirectMessage, m as authorizeSlackBotRoomMessage, o as resolveSlackRoomContextHints, p as resolveSlackThreadTargets, r as trackSlackDraftMessage, u as resolveConversationLabel, v as appendSlackStream, w as normalizeSlackChannelType, x as stopSlackStream, y as markSlackStreamFallbackDelivered } from "./provider-w36VTgZ3.mjs";
10
- import { n as resolveSlackChannelConfig } from "./policy-Db_QWCZZ.mjs";
11
- import { a as resolveSlackThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as sanitizeSlackMonitorReplyPayload, s as emitSlackMessageSentHooks, t as createSlackReplyDeliveryPlan } from "./replies-CWFhjS9S.mjs";
12
- import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
1
+ import { i as __reExport, n as resolveSlackStreamingMode, r as __exportAll, t as resolveSlackNativeStreaming } from "./streaming-compat-C5Wut626.mjs";
2
+ import { f as resolveSlackReplyToMode } from "./accounts-D_N9IqNA.mjs";
3
+ import { n as formatSlackTarget } from "./target-parsing-FFE26kOV.mjs";
4
+ import { A as countSlackTextUtf8Bytes, C as buildSlackBlocksFallbackText, E as SLACK_EDIT_TEXT_MAX_BYTES, H as SLACK_SESSION_LINK_ACTION_ID, M as truncateSlackTextByUtf8Bytes, Q as normalizeSlackOutboundText, c as qualifySlackRoutePeerId, j as truncateSlackText, k as SLACK_TEXT_LIMIT, lt as escapeSlackMrkdwn, v as hasSlackNativeDataBlock, w as buildSlackCompleteBlocksFallbackText } from "./session-status-DscajIsd.mjs";
5
+ import { D as normalizeSlackAllowOwnerEntry, j as resolveSlackUserAllowed, o as formatSlackError, v as readLruMapEntry, y as writeLruMapEntry } from "./probe-B2Y9qc_z.mjs";
6
+ import { a as prepareSlackReply, l as resolveSlackReplyRenderPlan, s as resolveSlackReplyBlocks } from "./reply-blocks-SE1kTE3I.mjs";
7
+ import { n as resolveSlackReplyThreadTs } from "./thread-ts-Cvt7WD6B.mjs";
8
+ import { C as hasSlackTableBlock, D as buildSlackEditTextPayload, E as resolveSlackMessageText$1, T as resolveSlackBlocksText, a as editSlackRenderedMessage, b as formatSlackFileReference, d as reactSlackMessage, i as editSlackMessage, m as removeSlackReaction, n as deleteSlackMessage, w as isSlackUnfurlAttachment, x as formatSlackFileReferenceList } from "./actions-Df0CzcDl.mjs";
9
+ import { c as hasSlackThreadParticipationWithPersistence, i as clearSlackThreadFailureNotice, l as recordSlackThreadFailureNotice, o as hasSlackThreadFailureNotice, p as buildSlackNativeDataDeliveryPlan, s as hasSlackThreadParticipation, t as sendMessageSlack, u as recordSlackThreadParticipation } from "./send-BOVLQi3n.mjs";
10
+ import { n as resolveSlackChannelConfig } from "./policy-D-jfM174.mjs";
11
+ import { C as resolveSlackChatType, E as stripSlackMentionsForCommandDetection, S as normalizeSlackChannelType, T as readSlackAssistantThreadContext, _ as appendSlackStream, b as stopSlackStream, c as resolveSlackRoutingContext, g as SlackStreamNotDeliveredError, h as resolveSlackEffectiveAllowFrom, i as resolveSlackRoomContextHints, l as registerSlackSessionRun, m as resolveSlackCommandIngress, p as authorizeSlackBotRoomMessage, r as trackSlackDraftMessage, s as authorizeSlackDirectMessage, u as resolveSlackThreadContext, v as markSlackStreamFallbackDelivered, w as buildSlackAssistantThreadMetadata, y as startSlackStream } from "./monitor-BPVfkumU.mjs";
12
+ import { a as emitSlackMessageSentHooks, i as sanitizeSlackMonitorReplyPayload, n as deliverReplies, t as createSlackReplyDeliveryPlan } from "./replies-BAnUtr2f.mjs";
13
+ import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, readNonBlankString } from "openclaw/plugin-sdk/string-coerce-runtime";
13
14
  import { resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
14
- import { buildChannelProgressDraftLine, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createChannelProgressWorkCounter, createDraftStreamLoop, createFinalizableDraftStreamControlsForState, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, formatChannelProgressDraftDiffStat, formatChannelProgressDraftText, formatPlanChecklistLines, isChannelProgressDraftWorkToolName, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
15
+ import { buildChannelProgressDraftLine, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createChannelProgressWorkCounter, createDraftStreamLoop, createFinalizableDraftLifecycle, createLivePreviewLifecycle, createMessageReceiptFromOutboundResults, formatChannelProgressDraftDiffStat, formatPlanChecklistLines, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
15
16
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
16
17
  import { danger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
17
18
  import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
18
19
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
20
+ import { normalizeStringEntriesLower } from "openclaw/plugin-sdk/string-normalization-runtime";
21
+ import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
19
22
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
20
23
  import { buildTtsSupplementMediaPayload, getReplyPayloadTtsSupplement, isReplyPayloadNonTerminalToolErrorWarning, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
21
- import { asDateTimestampMs, asFiniteNumberInRange, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
24
+ import { asDateTimestampMs, asFiniteNumberInRange, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs, resolvePromptHistoryLimit } from "openclaw/plugin-sdk/number-runtime";
22
25
  import { hash } from "node:crypto";
23
- import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
24
- import { ensureConfiguredBindingRouteReady } from "openclaw/plugin-sdk/conversation-runtime";
26
+ import { ensureConfiguredBindingRouteReady, resolveConversationLabel } from "openclaw/plugin-sdk/conversation-runtime";
25
27
  import { resolveChannelImplicitMentions } from "openclaw/plugin-sdk/channel-ingress-runtime";
26
28
  import fs from "node:fs/promises";
27
29
  import { runTasksWithConcurrency } from "openclaw/plugin-sdk/concurrency-runtime";
@@ -29,17 +31,19 @@ import { formatErrorMessage, toErrorObject } from "openclaw/plugin-sdk/error-run
29
31
  import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist, shouldIncludeSupplementalContext } from "openclaw/plugin-sdk/security-runtime";
30
32
  import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
31
33
  import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
32
- import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
34
+ import { getSessionEntry, readSessionUpdatedAt, resolveChannelResetConfig, resolveStorePath, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
33
35
  import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
34
36
  import { enqueueRoutedSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
35
37
  import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
36
- import { buildChannelInboundEventContext, buildMentionRegexes, classifyChannelInboundEvent, dispatchChannelInboundTurn, formatInboundEnvelope, formatInboundMediaUnavailableText, hasVisibleInboundReplyDispatch, implicitMentionKindWhen, isGroupThreadRouteExclusive, matchesMentionWithExplicit, readAgentRunTerminalOutcome, recordDroppedChannelInboundHistory, resolveEnvelopeFormatOptions, resolveGroupThreadConfig, resolveGroupThreadMentionFacts, resolveInboundMentionDecision, resolveInboundReplyDispatchCounts, resolveInboundSupplementalSenderAllowed, resolveUnmentionedGroupInboundPolicy, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
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
39
  import { resolveGatewayPublicOrigin } from "openclaw/plugin-sdk/config-contracts";
38
40
  import { buildControlUiSessionPath } from "openclaw/plugin-sdk/session-discussion";
39
41
  import { createStatusReactionController, logAckFailure, logTypingFailure, resolveAckReaction, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
40
42
  import { hasControlCommand } from "openclaw/plugin-sdk/command-detection";
41
43
  import { isAbortRequestText } from "openclaw/plugin-sdk/command-primitives-runtime";
42
44
  import { shouldHandleTextCommands } from "openclaw/plugin-sdk/command-surface";
45
+ import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
46
+ import { DEFAULT_GROUP_HISTORY_LIMIT, buildHistoryContextFromEntries, buildInboundHistoryFromEntries } from "openclaw/plugin-sdk/reply-history";
43
47
  import { createChannelPreflightAudio, formatAudioTranscriptForAgent } from "openclaw/plugin-sdk/media-understanding-runtime";
44
48
  //#region extensions/slack/src/monitor/message-handler/timestamp.ts
45
49
  const SLACK_TIMESTAMP_RE = /^\d+(?:\.\d+)?$/;
@@ -55,9 +59,6 @@ function resolveSlackTimestampMs(ts) {
55
59
  }
56
60
  //#endregion
57
61
  //#region extensions/slack/src/monitor/message-handler/dispatch-helpers.ts
58
- function resolveSlackMessageTimestampMs(message) {
59
- return resolveSlackTimestampMs(message.event_ts ?? message.ts);
60
- }
61
62
  function resolveSlackBotLoopProtection(prepared) {
62
63
  const senderBotId = prepared.message.bot_id;
63
64
  if (!senderBotId) return;
@@ -71,7 +72,7 @@ function resolveSlackBotLoopProtection(prepared) {
71
72
  config: mergePairLoopGuardConfig(prepared.account.config.botLoopProtection, prepared.channelConfig?.botLoopProtection),
72
73
  defaultsConfig: prepared.ctx.cfg.channels?.defaults?.botLoopProtection,
73
74
  defaultEnabled: true,
74
- nowMs: resolveSlackMessageTimestampMs(prepared.message)
75
+ nowMs: resolveSlackTimestampMs(prepared.message.event_ts ?? prepared.message.ts)
75
76
  };
76
77
  }
77
78
  function isSlackStreamingEnabled(params) {
@@ -89,26 +90,14 @@ function resolveExplicitSlackProgressTitle(entry) {
89
90
  const trimmed = label.trim();
90
91
  return trimmed && trimmed.toLowerCase() !== "auto" ? trimmed : void 0;
91
92
  }
92
- function resolveSlackProgressStyle(entry) {
93
+ function resolveSlackProgressStyle(entry, hasReplyThread) {
93
94
  const progress = entry?.streaming?.progress;
95
+ if (!hasReplyThread && !Object.entries(progress ?? {}).some(([key, value]) => key !== "nativeTaskCards" || value !== true)) return "none";
94
96
  return progress?.style ?? (progress?.toolProgress === false ? "compact" : "card");
95
97
  }
96
- function resolveSlackNativeProgressTaskCards(entry) {
97
- if (resolveSlackProgressStyle(entry) === "compact") return false;
98
- const streaming = entry?.streaming;
99
- if (!streaming || typeof streaming !== "object" || Array.isArray(streaming)) return true;
100
- const progressConfig = streaming.progress;
101
- if (!progressConfig || typeof progressConfig !== "object" || Array.isArray(progressConfig)) return true;
102
- return progressConfig.nativeTaskCards !== false;
103
- }
104
- function resolveSlackStreamingThreadHint(params) {
105
- return resolveSlackThreadTs({
106
- replyToMode: params.replyToMode,
107
- incomingThreadTs: params.incomingThreadTs,
108
- messageTs: params.messageTs,
109
- hasReplied: false,
110
- isThreadReply: params.isThreadReply
111
- });
98
+ function resolveSlackNativeProgressTaskCards(entry, style) {
99
+ if (style !== "card") return false;
100
+ return entry?.streaming?.progress?.nativeTaskCards !== false;
112
101
  }
113
102
  const SLACK_STREAM_RECIPIENT_TEAM_CACHE_MAX = 2e3;
114
103
  const slackStreamRecipientTeamCaches = /* @__PURE__ */ new WeakMap();
@@ -123,7 +112,7 @@ function buildSlackEventDeliveryKey(params, preparedReply = prepareSlackReply(pa
123
112
  const reply = resolveSendableOutboundReplyParts(params.payload, { text: params.textOverride });
124
113
  const renderPlan = preparedReply.resolvePreview(params.textOverride);
125
114
  const plannedBlocks = renderPlan.mode === "single" ? renderPlan.blocks : renderPlan.blockPart?.blocks;
126
- const slackBlocks = readSlackReplyBlocks(params.payload) ?? plannedBlocks;
115
+ const slackBlocks = resolveSlackReplyBlocks(params.payload) ?? plannedBlocks;
127
116
  const renderedText = renderPlan.mode === "single" ? renderPlan.text : renderPlan.fallbackText;
128
117
  if (!reply.hasContent && !slackBlocks?.length && !renderedText.trim()) return null;
129
118
  return JSON.stringify({
@@ -135,27 +124,6 @@ function buildSlackEventDeliveryKey(params, preparedReply = prepareSlackReply(pa
135
124
  blocks: slackBlocks ?? null
136
125
  });
137
126
  }
138
- function readSlackStreamRecipientTeamCache(params) {
139
- if (!params.fallbackTeamId || !params.userId) return;
140
- const cacheKey = `${params.fallbackTeamId}:${params.userId}`;
141
- const cache = getSlackStreamRecipientTeamCache(params.client);
142
- const cached = cache.get(cacheKey);
143
- if (!cached) return;
144
- cache.delete(cacheKey);
145
- cache.set(cacheKey, cached);
146
- return cached;
147
- }
148
- function rememberSlackStreamRecipientTeam(params) {
149
- if (!params.fallbackTeamId || !params.userId) return;
150
- const cacheKey = `${params.fallbackTeamId}:${params.userId}`;
151
- const cache = getSlackStreamRecipientTeamCache(params.client);
152
- if (cache.has(cacheKey)) cache.delete(cacheKey);
153
- cache.set(cacheKey, params.teamId);
154
- if (cache.size > SLACK_STREAM_RECIPIENT_TEAM_CACHE_MAX) {
155
- const oldest = cache.keys().next().value;
156
- if (oldest) cache.delete(oldest);
157
- }
158
- }
159
127
  function createSlackEventDeliveryTracker() {
160
128
  const deliveredKeys = /* @__PURE__ */ new Set();
161
129
  return {
@@ -176,7 +144,9 @@ function shouldUseStreaming(params) {
176
144
  return true;
177
145
  }
178
146
  async function resolveSlackStreamRecipientTeamId(params) {
179
- const cachedTeamId = readSlackStreamRecipientTeamCache(params);
147
+ const cacheKey = params.fallbackTeamId && params.userId ? `${params.fallbackTeamId}:${params.userId}` : void 0;
148
+ const cache = cacheKey ? getSlackStreamRecipientTeamCache(params.client) : void 0;
149
+ const cachedTeamId = cache && cacheKey ? readLruMapEntry(cache, cacheKey) : void 0;
180
150
  if (cachedTeamId) return cachedTeamId;
181
151
  if (params.userId) try {
182
152
  const info = await params.client.users.info({
@@ -185,10 +155,7 @@ async function resolveSlackStreamRecipientTeamId(params) {
185
155
  });
186
156
  const teamId = info.user?.team_id ?? info.user?.profile?.team;
187
157
  if (teamId) {
188
- rememberSlackStreamRecipientTeam({
189
- ...params,
190
- teamId
191
- });
158
+ if (cache && cacheKey) writeLruMapEntry(cache, cacheKey, teamId, SLACK_STREAM_RECIPIENT_TEAM_CACHE_MAX);
192
159
  return teamId;
193
160
  }
194
161
  } catch (err) {
@@ -206,17 +173,22 @@ function createSlackDraftStream(params) {
206
173
  const edit = params.edit ?? editSlackMessage;
207
174
  const remove = params.remove ?? deleteSlackMessage;
208
175
  let streamMessage;
176
+ let streamGeneration = 0;
177
+ let cleanupGeneration = -1;
209
178
  let untrackConversationBoundary;
210
179
  let lastVisibleUpdate;
211
180
  let lastSentKey = "";
212
- const pendingCleanupMessages = [];
213
- let cleanupTail = Promise.resolve();
214
- const finalizedMessageIds = /* @__PURE__ */ new Set();
181
+ let preserveHumanReplies = false;
215
182
  const streamState = {
216
183
  stopped: false,
217
184
  final: false
218
185
  };
219
186
  const normalizeUpdate = (update) => typeof update === "string" ? { text: update } : update;
187
+ const clearMessageId = () => {
188
+ streamMessage = void 0;
189
+ lastVisibleUpdate = void 0;
190
+ lastSentKey = "";
191
+ };
220
192
  const sendOrEditStreamMessage = async (pending) => {
221
193
  if (streamState.stopped) return;
222
194
  const update = normalizeUpdate(pending);
@@ -232,8 +204,10 @@ function createSlackDraftStream(params) {
232
204
  const sentKey = `${trimmed}\n${blocks ? JSON.stringify(blocks) : ""}`;
233
205
  if (sentKey === lastSentKey) return;
234
206
  lastSentKey = sentKey;
207
+ const generation = streamGeneration;
235
208
  try {
236
209
  if (streamMessage) {
210
+ const message = streamMessage;
237
211
  await edit(streamMessage.channelId, streamMessage.messageId, trimmed, {
238
212
  cfg: params.cfg,
239
213
  token: params.token,
@@ -241,7 +215,7 @@ function createSlackDraftStream(params) {
241
215
  ...params.eventScope ? { client: params.eventScope.client } : {},
242
216
  ...blocks ? { blocks } : {}
243
217
  });
244
- lastVisibleUpdate = {
218
+ if (streamMessage === message) lastVisibleUpdate = {
245
219
  text: trimmed,
246
220
  ...blocks ? { blocks } : {}
247
221
  };
@@ -266,16 +240,22 @@ function createSlackDraftStream(params) {
266
240
  ...params.metadata ? { metadata: params.metadata } : {},
267
241
  ...blocks ? { blocks } : {}
268
242
  });
243
+ const sentMessage = {
244
+ channelId: sent.channelId,
245
+ messageId: sent.messageId,
246
+ generation
247
+ };
248
+ if (generation !== streamGeneration) {
249
+ if (sent.channelId && sent.messageId) lifecycle.retire(sentMessage, { defer: true });
250
+ return;
251
+ }
269
252
  if (!sent.channelId || !sent.messageId) {
270
253
  stopTrackingConversationBoundary();
271
254
  streamState.stopped = true;
272
255
  params.warn?.("slack stream preview stopped (missing identifiers from sendMessage)");
273
256
  return;
274
257
  }
275
- streamMessage = {
276
- channelId: sent.channelId,
277
- messageId: sent.messageId
278
- };
258
+ streamMessage = sentMessage;
279
259
  lastVisibleUpdate = {
280
260
  text: trimmed,
281
261
  ...blocks ? { blocks } : {}
@@ -293,72 +273,84 @@ function createSlackDraftStream(params) {
293
273
  }).stop;
294
274
  }
295
275
  } catch (err) {
296
- stopTrackingConversationBoundary();
297
- streamState.stopped = true;
276
+ if (generation === streamGeneration) {
277
+ stopTrackingConversationBoundary();
278
+ streamState.stopped = true;
279
+ }
298
280
  params.warn?.(`slack stream preview failed: ${formatSlackError(err)}`);
299
281
  }
300
282
  };
301
- const { loop, update, discardPending, seal } = createFinalizableDraftStreamControlsForState({
283
+ const lifecycle = createFinalizableDraftLifecycle({
302
284
  throttleMs,
303
285
  coalesceInFlight: true,
304
286
  state: streamState,
305
287
  sendOrEditStreamMessage,
306
288
  emptyValue: "",
307
- isEmpty: (value) => !normalizeUpdate(value).text.trim()
289
+ isEmpty: (value) => !normalizeUpdate(value).text.trim(),
290
+ readMessageId: () => streamMessage,
291
+ clearMessageId,
292
+ isValidMessageId: (value) => typeof value === "object" && value !== null,
293
+ deleteMessage: async (message) => {
294
+ if (message.generation > cleanupGeneration) return false;
295
+ if (!message.retained && !(preserveHumanReplies && message.detachedByHuman)) await remove(message.channelId, message.messageId, {
296
+ token: params.token,
297
+ accountId: params.accountId,
298
+ ...params.eventScope ? { client: params.eventScope.client } : {}
299
+ });
300
+ return true;
301
+ },
302
+ warn: params.warn,
303
+ warnPrefix: "slack stream preview cleanup failed"
308
304
  });
305
+ const { loop, update, discardPending, seal } = lifecycle;
309
306
  const stopTrackingConversationBoundary = () => {
310
307
  untrackConversationBoundary?.();
311
308
  untrackConversationBoundary = void 0;
312
309
  };
313
- const dropDetachedMessages = (preserveHumanReplies = false) => {
314
- cleanupTail = cleanupTail.then(async () => {
315
- for (let index = 0; index < pendingCleanupMessages.length;) {
316
- const message = pendingCleanupMessages[index];
317
- if (!message) return;
318
- if (preserveHumanReplies && message.detachedByHuman) {
319
- pendingCleanupMessages.splice(index, 1);
320
- continue;
321
- }
322
- try {
323
- await remove(message.channelId, message.messageId, {
324
- token: params.token,
325
- accountId: params.accountId,
326
- ...params.eventScope ? { client: params.eventScope.client } : {}
327
- });
328
- pendingCleanupMessages.splice(index, 1);
329
- } catch (err) {
330
- params.warn?.(`slack stream preview cleanup failed: ${formatSlackError(err)}`);
331
- index += 1;
332
- }
333
- }
310
+ const dropDetachedMessages = () => {
311
+ const generation = streamGeneration;
312
+ return lifecycle.cleanupPending(() => {
313
+ preserveHumanReplies = false;
314
+ cleanupGeneration = generation;
334
315
  });
335
- return cleanupTail;
336
316
  };
337
317
  const discardPendingAndStopTracking = async () => {
318
+ const generation = streamGeneration;
338
319
  await discardPending();
339
- stopTrackingConversationBoundary();
320
+ if (generation === streamGeneration) stopTrackingConversationBoundary();
340
321
  };
341
322
  const clear = async (options) => {
342
- await discardPendingAndStopTracking();
343
- if (streamMessage) {
344
- pendingCleanupMessages.push(streamMessage);
345
- streamMessage = void 0;
346
- }
347
- lastVisibleUpdate = void 0;
348
- lastSentKey = "";
349
- await dropDetachedMessages(options?.preserveHumanReplies);
323
+ const generation = streamGeneration;
324
+ let clearingMessage = streamMessage;
325
+ await lifecycle.clearWithStop(async () => {
326
+ if (generation === streamGeneration) {
327
+ await discardPendingAndStopTracking();
328
+ if (generation === streamGeneration) {
329
+ clearingMessage = streamMessage;
330
+ clearMessageId();
331
+ }
332
+ }
333
+ cleanupGeneration = generation;
334
+ preserveHumanReplies = options?.preserveHumanReplies === true;
335
+ }, {
336
+ readMessageId: () => clearingMessage,
337
+ clearMessageId: () => {
338
+ clearingMessage = void 0;
339
+ }
340
+ });
350
341
  };
351
342
  const forceNewMessage = (reason = "turn") => {
352
343
  stopTrackingConversationBoundary();
353
- if (reason === "turn") streamState.stopped = false;
344
+ if (reason === "turn") {
345
+ streamState.stopped = false;
346
+ streamGeneration += 1;
347
+ }
354
348
  streamState.final = false;
355
- if (streamMessage && !finalizedMessageIds.has(streamMessage.messageId)) {
349
+ if (streamMessage && !streamMessage.retained) {
356
350
  streamMessage.detachedByHuman = reason === "human";
357
- pendingCleanupMessages.push(streamMessage);
351
+ lifecycle.retire(streamMessage, { defer: true });
358
352
  }
359
- streamMessage = void 0;
360
- lastVisibleUpdate = void 0;
361
- lastSentKey = "";
353
+ clearMessageId();
362
354
  loop.resetPending();
363
355
  };
364
356
  const finalizeMessage = async (messageId, editFinal) => {
@@ -368,7 +360,7 @@ function createSlackDraftStream(params) {
368
360
  const { channelId } = currentMessage;
369
361
  await editFinal();
370
362
  if (streamMessage?.channelId === channelId && streamMessage.messageId === messageId) {
371
- finalizedMessageIds.add(messageId);
363
+ currentMessage.retained = true;
372
364
  stopTrackingConversationBoundary();
373
365
  return true;
374
366
  }
@@ -459,12 +451,11 @@ const SLACK_PROGRESS_TASK_TITLE_MAX = 120;
459
451
  const SLACK_PROGRESS_PLAN_FALLBACK_TITLE = "Thinking";
460
452
  const SLACK_PROGRESS_LINE_DELTA_RE = /(?:^|\s)\+(\d+)\s+[−-](\d+)(?=\s|$)/u;
461
453
  const SLACK_ATTENTION_TASK_PREFIX = "openclaw-attention-";
462
- function buildSessionSources(url) {
463
- return [{
454
+ function buildSessionSources(links) {
455
+ return links.map((link) => ({
464
456
  type: "url_source",
465
- url,
466
- text: "Open in OpenClaw"
467
- }];
457
+ ...link
458
+ }));
468
459
  }
469
460
  function field(text) {
470
461
  return {
@@ -620,12 +611,13 @@ function buildSlackProgressStreamChunks(params) {
620
611
  });
621
612
  }
622
613
  const headline = params.title?.trim() || params.label?.trim();
614
+ const summaryTitle = params.finalInProgressStatus === "error" ? "Failed" : params.finalInProgressStatus === "complete" || !params.summaryRow ? "Completed" : "Working";
623
615
  const newest = tasks.at(-1);
624
- const title = compactChunkText(headline || (newest?.details ? `${newest.title} — ${newest.details}` : newest?.title) || (params.summaryRow ? "Working" : attention.at(-1)?.title) || SLACK_PROGRESS_PLAN_FALLBACK_TITLE);
616
+ const title = compactChunkText(headline || (newest?.details ? `${newest.title} — ${newest.details}` : newest?.title) || (params.summaryRow ? summaryTitle : attention.at(-1)?.title) || SLACK_PROGRESS_PLAN_FALLBACK_TITLE);
625
617
  const diffOutput = formatTaskDiffOutput(params.diffStat);
626
- if (tasks.length === 0 && (params.summaryRow || params.sessionUrl || diffOutput)) tasks.push({
618
+ if (tasks.length === 0 && (params.summaryRow || params.sessionLinks?.length || diffOutput)) tasks.push({
627
619
  id: "openclaw_summary",
628
- title: params.summaryRow ? compactTitle(title) : "Completed",
620
+ title: params.summaryRow ? compactTitle(title) : summaryTitle,
629
621
  status: params.finalInProgressStatus ?? (params.summaryRow ? "in_progress" : "complete")
630
622
  });
631
623
  tasks.push(...attention);
@@ -647,7 +639,7 @@ function buildSlackProgressStreamChunks(params) {
647
639
  if (task.details) chunk.details = task.details;
648
640
  if (task.output) chunk.output = task.output;
649
641
  if (index === finalTaskIndex && diffOutput) chunk.output = [task.output, diffOutput].filter(Boolean).join(" · ");
650
- if (index === finalTaskIndex && params.sessionUrl) chunk.sources = buildSessionSources(params.sessionUrl);
642
+ if (index === finalTaskIndex && params.sessionLinks?.length) chunk.sources = buildSessionSources(params.sessionLinks);
651
643
  return chunk;
652
644
  });
653
645
  return [{
@@ -699,7 +691,7 @@ function buildSlackProgressCardBlocks(params) {
699
691
  buildActivityText(params.lines.filter((line) => line.kind !== "approval" && lineTaskStatus(line) !== "error"), maxLineChars),
700
692
  joinRecentProgressRows(attention)
701
693
  ];
702
- const title = params.title.trim() || "Working";
694
+ const title = params.title?.trim() || (params.state === "working" ? "Working" : params.state === "success" ? "Done" : "Failed");
703
695
  const blocks = [
704
696
  params.titleFormat === "plain" ? progressTextSection({
705
697
  text: `${icon} ${title}`,
@@ -718,17 +710,17 @@ function buildSlackProgressCardBlocks(params) {
718
710
  type: "context",
719
711
  elements: [field(footer)]
720
712
  });
721
- if (params.state !== "working" && params.sessionUrl) blocks.push({
713
+ if (params.state !== "working" && params.sessionLinks?.length) blocks.push({
722
714
  type: "actions",
723
- elements: [{
715
+ elements: params.sessionLinks.map((link, index) => ({
724
716
  type: "button",
725
- action_id: SLACK_SESSION_LINK_ACTION_ID,
717
+ action_id: index === 0 ? SLACK_SESSION_LINK_ACTION_ID : `${SLACK_SESSION_LINK_ACTION_ID}:${index}`,
726
718
  text: {
727
719
  type: "plain_text",
728
- text: "Open in OpenClaw"
720
+ text: link.text
729
721
  },
730
- url: params.sessionUrl
731
- }]
722
+ url: link.url
723
+ }))
732
724
  });
733
725
  return blocks.slice(0, 50);
734
726
  }
@@ -913,25 +905,20 @@ function blocksMatch(expected, actual) {
913
905
  }
914
906
  }
915
907
  async function readSlackMessageAfterEditError(params) {
916
- if (params.threadTs) return ((await params.client.conversations.replies({
917
- token: params.token,
918
- channel: params.channelId,
919
- ts: params.threadTs,
920
- latest: params.messageId,
921
- oldest: params.messageId,
922
- inclusive: true,
923
- limit: 1
924
- })).messages ?? []).find((message) => message?.ts === params.messageId) ?? null;
925
- const message = (await params.client.conversations.history({
908
+ const query = {
926
909
  token: params.token,
927
910
  channel: params.channelId,
928
911
  latest: params.messageId,
929
912
  oldest: params.messageId,
930
913
  inclusive: true,
931
914
  limit: 1
932
- })).messages?.[0];
933
- if (!message?.ts || message.ts !== params.messageId) return null;
934
- return message;
915
+ };
916
+ const result = params.threadTs ? await params.client.conversations.replies({
917
+ ...query,
918
+ ts: params.threadTs
919
+ }) : await params.client.conversations.history(query);
920
+ const message = params.threadTs ? result.messages?.find((entry) => entry?.ts === params.messageId) : result.messages?.[0];
921
+ return message?.ts && message.ts === params.messageId ? message : null;
935
922
  }
936
923
  async function didSlackPreviewEditApplyAfterError(params) {
937
924
  const readback = await readSlackMessageAfterEditError(params);
@@ -958,15 +945,7 @@ async function finalizeSlackPreviewEdit(params) {
958
945
  });
959
946
  } catch (err) {
960
947
  try {
961
- if (await didSlackPreviewEditApplyAfterError({
962
- client: params.client,
963
- token: params.token,
964
- channelId: params.channelId,
965
- messageId: params.messageId,
966
- text: params.text,
967
- blocks: params.blocks,
968
- threadTs: params.threadTs
969
- })) {
948
+ if (await didSlackPreviewEditApplyAfterError(params)) {
970
949
  logVerbose(`slack: preview final edit response failed but readback matched message ${params.channelId}/${params.messageId}; suppressing duplicate fallback send`);
971
950
  return;
972
951
  }
@@ -978,37 +957,39 @@ async function finalizeSlackPreviewEdit(params) {
978
957
  }
979
958
  //#endregion
980
959
  //#region extensions/slack/src/monitor/message-handler/dispatch-progress-card.ts
960
+ const MAX_VISIBLE_WORK_LINKS = 5;
981
961
  function createSlackDraftProgressCardRuntime(params) {
982
962
  const { account, cfg, ctx, prepared, slackClient } = params.setup;
983
- let latestFallbackText = "";
984
963
  let finalStatus;
985
- const resolveSessionUrl = () => {
986
- if (cfg.gateway?.controlUi?.enabled === false) return;
964
+ const visibleWorkSessions = /* @__PURE__ */ new Map();
965
+ const resolveSessionLinks = () => {
966
+ if (visibleWorkSessions.size > 0) return [...visibleWorkSessions.values()].slice(0, MAX_VISIBLE_WORK_LINKS).map((session, index) => {
967
+ const label = session.label?.replace(/\s+/g, " ").trim();
968
+ return {
969
+ url: session.url,
970
+ text: visibleWorkSessions.size === 1 ? "Open work session" : truncateSlackText(label ? `Open ${label}` : `Open work session ${index + 1}`, 75)
971
+ };
972
+ });
973
+ if (cfg.gateway?.controlUi?.enabled === false) return [];
987
974
  const publicOrigin = resolveGatewayPublicOrigin(cfg);
988
- if (!publicOrigin) return;
975
+ if (!publicOrigin) return [];
989
976
  const url = new URL(publicOrigin);
990
977
  const path = buildControlUiSessionPath({
991
978
  namespace: "chat",
992
- sessionKey: prepared.route.sessionKey,
979
+ sessionKey: prepared.ctxPayload.SessionKey ?? prepared.route.sessionKey,
993
980
  fallbackAgentId: prepared.route.agentId,
981
+ mainKey: cfg.session?.mainKey,
994
982
  basePath: cfg.gateway?.controlUi?.basePath
995
983
  });
996
- if (!path) return;
984
+ if (!path) return [];
997
985
  url.pathname = path;
998
- return url.toString();
986
+ return [{
987
+ url: url.toString(),
988
+ text: "Open in OpenClaw"
989
+ }];
999
990
  };
1000
- const resolveText = (snapshot) => latestFallbackText || formatChannelProgressDraftText({
1001
- entry: account.config,
1002
- lines: [...snapshot.lines],
1003
- seed: params.progressSeed,
1004
- formatLine: formatSlackProgressDraftLine,
1005
- narration: snapshot.statusHeadline,
1006
- narrationFormat: snapshot.statusHeadlineFormat,
1007
- plan: snapshot.plan,
1008
- diffStat: snapshot.diffStat
1009
- });
1010
991
  const resolvePresentation = (snapshot, state) => {
1011
- const title = params.explicitTitle ?? snapshot.statusHeadline ?? "Working";
992
+ const title = params.explicitTitle ?? snapshot.statusHeadline;
1012
993
  const titleFormat = params.explicitTitle ? void 0 : snapshot.statusHeadlineFormat;
1013
994
  const narration = params.explicitTitle ? snapshot.statusHeadlineFormat === "plain" || snapshot.planExplanationFormat === "plain" ? [...snapshot.statusHeadline && (snapshot.statusHeadline !== snapshot.planExplanation || snapshot.statusHeadlineFormat !== snapshot.planExplanationFormat) ? [{
1014
995
  text: snapshot.statusHeadline,
@@ -1021,7 +1002,7 @@ function createSlackDraftProgressCardRuntime(params) {
1021
1002
  format: snapshot.planExplanationFormat
1022
1003
  }] : void 0;
1023
1004
  const workCounter = state === "working" ? params.progressWorkCounter : void 0;
1024
- const sessionUrl = state === "working" ? void 0 : resolveSessionUrl();
1005
+ const sessionLinks = state === "working" ? [] : resolveSessionLinks();
1025
1006
  return buildSlackProgressCardBlocks({
1026
1007
  state,
1027
1008
  title,
@@ -1033,10 +1014,10 @@ function createSlackDraftProgressCardRuntime(params) {
1033
1014
  diffStat: snapshot.diffStat,
1034
1015
  toolCalls: workCounter?.toolCalls,
1035
1016
  elapsedSeconds: workCounter?.elapsedSeconds,
1036
- sessionUrl
1017
+ sessionLinks
1037
1018
  });
1038
1019
  };
1039
- const finalize = async (status, snapshot = params.getSnapshot(), fallbackText = resolveText(snapshot)) => {
1020
+ const finalize = async (status, snapshot = params.getSnapshot()) => {
1040
1021
  if (!params.draftStream || !params.enabled) return false;
1041
1022
  await params.draftStream.dropDetachedMessages();
1042
1023
  const terminalStatus = finalStatus === "error" || status === "error" ? "error" : "success";
@@ -1048,14 +1029,15 @@ function createSlackDraftProgressCardRuntime(params) {
1048
1029
  await params.draftStream.seal();
1049
1030
  try {
1050
1031
  const finalized = await params.draftStream.finalizeMessage(messageId, async () => {
1032
+ const blocks = resolvePresentation(snapshot, terminalStatus);
1051
1033
  await finalizeSlackPreviewEdit({
1052
1034
  client: slackClient,
1053
1035
  token: ctx.botToken,
1054
1036
  accountId: account.accountId,
1055
1037
  channelId,
1056
1038
  messageId,
1057
- text: fallbackText,
1058
- blocks: resolvePresentation(snapshot, terminalStatus),
1039
+ text: buildSlackCompleteBlocksFallbackText(blocks),
1040
+ blocks,
1059
1041
  threadTs: params.getThreadTs()
1060
1042
  });
1061
1043
  });
@@ -1067,19 +1049,18 @@ function createSlackDraftProgressCardRuntime(params) {
1067
1049
  }
1068
1050
  };
1069
1051
  return {
1070
- resolveSessionUrl,
1071
- resolveText,
1052
+ resolveSessionLinks,
1053
+ onVisibleWorkSessions: (sessions) => {
1054
+ for (const session of sessions) if (!visibleWorkSessions.has(session.sessionKey)) visibleWorkSessions.set(session.sessionKey, session);
1055
+ },
1072
1056
  resolvePresentation,
1073
1057
  finalize,
1074
1058
  get hasTerminalized() {
1075
1059
  return finalStatus !== void 0;
1076
1060
  },
1077
- setFallbackText(text) {
1078
- latestFallbackText = text;
1079
- },
1080
1061
  reset() {
1081
- latestFallbackText = "";
1082
1062
  finalStatus = void 0;
1063
+ visibleWorkSessions.clear();
1083
1064
  }
1084
1065
  };
1085
1066
  }
@@ -1095,15 +1076,12 @@ function formatSlackProgressDraftLine(line) {
1095
1076
  //#endregion
1096
1077
  //#region extensions/slack/src/monitor/message-handler/dispatch-progress-native.ts
1097
1078
  function createSlackNativeProgressTransport(params) {
1098
- const { ctx, message, replyPlan, slackClient, slackIdentity, slackStreamFallbackTeamId } = params.setup;
1079
+ const { replyPlan } = params.setup;
1099
1080
  const { delivery } = params;
1100
1081
  const markDelivered = (threadTs) => {
1101
1082
  if (!delivery.streamSession?.delivered) return false;
1102
1083
  delivery.observedReplyDelivery = true;
1103
- if (threadTs) {
1104
- delivery.usedReplyThreadTs ??= threadTs;
1105
- delivery.rememberDeliveredThreadTs("block", threadTs);
1106
- }
1084
+ if (threadTs) delivery.rememberDeliveredThreadTs("block", threadTs);
1107
1085
  return true;
1108
1086
  };
1109
1087
  const waitForStart = async () => {
@@ -1124,30 +1102,15 @@ function createSlackNativeProgressTransport(params) {
1124
1102
  return false;
1125
1103
  }
1126
1104
  delivery.nativeProgressStreamThreadTs = streamThreadTs;
1127
- const startPromise = (async () => {
1128
- const session = await startSlackStream({
1129
- client: slackClient,
1130
- clientOptions: params.setup.slackClientOptions,
1131
- channel: message.channel,
1132
- threadTs: streamThreadTs,
1133
- ...update.text ? { text: update.text } : {},
1134
- ...update.chunks?.length ? { chunks: update.chunks } : {},
1135
- taskDisplayMode: "plan",
1136
- ...slackIdentity ? { identity: slackIdentity } : {},
1137
- teamId: await resolveSlackStreamRecipientTeamId({
1138
- client: slackClient,
1139
- token: ctx.botToken,
1140
- userId: message.user,
1141
- fallbackTeamId: slackStreamFallbackTeamId
1142
- }),
1143
- userId: message.user
1144
- });
1145
- delivery.streamSession = session;
1146
- return session;
1147
- })();
1105
+ const startPromise = delivery.startStream({
1106
+ threadTs: streamThreadTs,
1107
+ ...update.text ? { text: update.text } : {},
1108
+ ...update.chunks?.length ? { chunks: update.chunks } : {},
1109
+ taskDisplayMode: "plan"
1110
+ });
1148
1111
  delivery.nativeProgressStreamStartPromise = startPromise;
1149
1112
  try {
1150
- if (!await startPromise) return false;
1113
+ await startPromise;
1151
1114
  const delivered = markDelivered(streamThreadTs);
1152
1115
  return update.chunks?.length ? delivered : true;
1153
1116
  } finally {
@@ -1174,8 +1137,8 @@ function createSlackNativeProgressTransport(params) {
1174
1137
  //#endregion
1175
1138
  //#region extensions/slack/src/monitor/message-handler/dispatch-progress.ts
1176
1139
  function createSlackProgressRuntime(runtimeParams) {
1177
- const { setup, delivery, resetPreviewDeliveryState } = runtimeParams;
1178
- const { account, cfg, ctx, hasSlackCustomIdentity, message, prepared, replyPlan, runtime, slackClient, slackIdentity, slackMessageMetadata, slackStreaming, shouldUseDraftStream, useStreaming, previewStreamingEnabled } = setup;
1140
+ const { setup, delivery } = runtimeParams;
1141
+ const { account, cfg, ctx, hasSlackCustomIdentity, message, prepared, replyPlan, runtime, slackClient, slackIdentity, slackMessageMetadata, slackStreaming, slackProgressStyle, quietProgress, shouldUseDraftStream, useStreaming, previewStreamingEnabled } = setup;
1179
1142
  const draftStream = shouldUseDraftStream ? createSlackDraftStream({
1180
1143
  target: prepared.replyTarget,
1181
1144
  cfg,
@@ -1199,7 +1162,7 @@ function createSlackProgressRuntime(runtimeParams) {
1199
1162
  const progressDraftActive = Boolean(draftStream) || useNativeProgressStreaming;
1200
1163
  const previewToolProgressEnabled = progressDraftActive && resolveChannelStreamingPreviewToolProgress(account.config, slackStreaming.mode !== "progress", slackStreaming.mode);
1201
1164
  let shouldYieldDraftProgress = () => false;
1202
- const suppressDefaultToolProgressMessages = resolveChannelStreamingSuppressDefaultToolProgressMessages(account.config, {
1165
+ const suppressDefaultToolProgressMessages = quietProgress || resolveChannelStreamingSuppressDefaultToolProgressMessages(account.config, {
1203
1166
  draftStreamActive: Boolean(draftStream) || useNativeProgressStreaming,
1204
1167
  mode: slackStreaming.mode,
1205
1168
  previewToolProgressEnabled,
@@ -1220,7 +1183,6 @@ function createSlackProgressRuntime(runtimeParams) {
1220
1183
  };
1221
1184
  const progressWorkCounter = createChannelProgressWorkCounter();
1222
1185
  const progressSeed = `${account.accountId}:${message.channel}`;
1223
- const slackProgressStyle = resolveSlackProgressStyle(account.config);
1224
1186
  const preambleOnlyProgress = isProgressMode && slackProgressStyle === "compact" && !previewToolProgressEnabled;
1225
1187
  const useDraftProgressCard = Boolean(draftStream) && isProgressMode && slackProgressStyle === "card";
1226
1188
  const explicitProgressTitle = resolveExplicitSlackProgressTitle(account.config);
@@ -1236,7 +1198,6 @@ function createSlackProgressRuntime(runtimeParams) {
1236
1198
  draftStream,
1237
1199
  enabled: useDraftProgressCard,
1238
1200
  progressWorkCounter: previewToolProgressEnabled ? progressWorkCounter : void 0,
1239
- progressSeed,
1240
1201
  explicitTitle: explicitProgressTitle,
1241
1202
  maxLineChars: progressDraftMaxLineChars,
1242
1203
  getSnapshot: () => progressDraft.getSnapshot(),
@@ -1345,30 +1306,30 @@ function createSlackProgressRuntime(runtimeParams) {
1345
1306
  nativeUpdates.stop();
1346
1307
  await nativeUpdates.waitForInFlight();
1347
1308
  };
1348
- const appendNativeNarration = (payload, kind) => withNativeStreamOrder(() => appendNativeNarrationNow(payload, kind));
1349
- const appendNativeNarrationNow = async (payload, kind) => {
1350
- if (isRenderedAsProgressTitle(payload.text)) return false;
1309
+ const appendNativeNarration = (payload, kind) => withNativeStreamOrder(async () => {
1310
+ if (isRenderedAsProgressTitle(payload.text)) return { visibleReplySent: false };
1351
1311
  const narrationUpdate = resolveNarrationUpdate(payload.text?.trimEnd());
1352
- if (!narrationUpdate.delta) return false;
1353
- await delivery.deliverWithStreaming({
1312
+ if (!narrationUpdate.delta) return { visibleReplySent: false };
1313
+ const result = await delivery.deliverWithStreaming({
1354
1314
  payload,
1355
1315
  kind,
1356
1316
  streamText: narrationUpdate.delta,
1357
1317
  appendSeparator: false,
1358
1318
  taskDisplayMode: "plan"
1359
1319
  });
1360
- if (!delivery.streamFailed) {
1320
+ if (result.visibleReplySent && !delivery.streamFailed) {
1361
1321
  nativeNarrationRenderedText = narrationUpdate.next.rendered;
1362
1322
  nativeNarrationSourceText = narrationUpdate.next.source;
1363
1323
  }
1364
- return true;
1365
- };
1324
+ return result;
1325
+ });
1366
1326
  const resetProgressTurnState = () => {
1367
1327
  progressWorkCounter.reset();
1368
1328
  nativeNarrationRenderedText = "";
1369
1329
  nativeNarrationSourceText = "";
1370
1330
  };
1371
1331
  const progressDraft = createChannelProgressDraftCompositor({
1332
+ preparedItems: true,
1372
1333
  entry: account.config,
1373
1334
  mode: slackStreaming.mode,
1374
1335
  active: progressDraftActive,
@@ -1390,7 +1351,6 @@ function createSlackProgressRuntime(runtimeParams) {
1390
1351
  const snapshot = options.snapshot;
1391
1352
  const latestLine = snapshot.lines.at(-1);
1392
1353
  if (preambleOnlyProgress && typeof latestLine === "object" && latestLine.complete === false) return false;
1393
- progressCard.setFallbackText(previewText);
1394
1354
  draftStream.update(preambleOnlyProgress ? {
1395
1355
  text: previewText,
1396
1356
  allowNewMessage: typeof latestLine !== "object" || latestLine.complete !== false,
@@ -1415,51 +1375,70 @@ function createSlackProgressRuntime(runtimeParams) {
1415
1375
  }
1416
1376
  }
1417
1377
  });
1378
+ const previewLifecycle = createLivePreviewLifecycle({
1379
+ draft: draftStream && !useDraftProgressCard ? {
1380
+ flush: draftStream.flush,
1381
+ discardPending: draftStream.discardPending,
1382
+ seal: draftStream.seal,
1383
+ id: () => {
1384
+ const channelId = draftStream.channelId();
1385
+ const messageId = draftStream.messageId();
1386
+ return channelId && messageId ? {
1387
+ channelId,
1388
+ messageId
1389
+ } : void 0;
1390
+ },
1391
+ clear: async () => {
1392
+ await draftStream.clear({ preserveHumanReplies: !isProgressMode && previewLifecycle.finalDelivered });
1393
+ }
1394
+ } : void 0,
1395
+ cleanupUndelivered: true,
1396
+ onFinalStarted: () => progressDraft.markFinalReplyStarted(),
1397
+ onFinalDelivered: () => progressDraft.markFinalReplyDelivered(),
1398
+ onCleanupFailure: (error) => logVerbose(`slack: progress preview cleanup failed (${formatSlackError(error)})`)
1399
+ });
1418
1400
  const commentaryProgressEnabled = progressDraft.commentaryProgressEnabled;
1419
1401
  const deliverNativeFinal = async (payload, kind) => {
1420
- progressDraft.markFinalReplyStarted();
1421
1402
  await cancelNativeUpdates();
1422
- await withNativeStreamOrder(() => deliverNativeFinalNow(payload, kind));
1403
+ return await withNativeStreamOrder(() => deliverNativeFinalNow(payload, kind));
1423
1404
  };
1424
1405
  const deliverNativeFinalNow = async (payload, kind) => {
1425
- progressDraft.markFinalReplyStarted();
1426
1406
  const streamReady = await nativeTransport.waitForStart();
1427
1407
  const finalThreadTs = delivery.streamSession?.threadTs ?? delivery.nativeProgressStreamThreadTs;
1428
1408
  if (payload.isError !== true && streamReady && Boolean(delivery.streamSession) && delivery.isStreamingEligible(payload, { maxTextBytes: 4e3 })) {
1429
1409
  await appendNativeProgressCompletion(false);
1430
- await delivery.deliverWithStreaming({
1410
+ return await delivery.deliverWithStreaming({
1431
1411
  payload,
1432
1412
  kind
1433
1413
  });
1434
- } else {
1435
- await delivery.deliverNormally({
1436
- payload,
1437
- kind,
1438
- forcedThreadTs: finalThreadTs
1439
- });
1440
- await appendNativeProgressCompletion(payload.isError === true);
1441
1414
  }
1442
- progressDraft.markFinalReplyDelivered();
1415
+ const result = await delivery.deliverNormally({
1416
+ payload,
1417
+ kind,
1418
+ forcedThreadTs: finalThreadTs
1419
+ });
1420
+ await appendNativeProgressCompletion(payload.isError === true);
1421
+ return result;
1443
1422
  };
1444
1423
  const buildNativeProgressCompletionChunks = (finalInProgressStatus) => {
1445
1424
  const snapshot = progressDraft.getSnapshot();
1446
1425
  const lines = resolveNativeProgressLines(snapshot);
1447
- const sessionUrl = progressCard.resolveSessionUrl();
1426
+ const sessionLinks = progressCard.resolveSessionLinks();
1448
1427
  const narrationUpdate = resolveNarrationUpdate(resolveNativeProgressNarration(snapshot));
1449
1428
  const hasRetirableNativeTasks = [...nativeStreamSnapshot.tasks.values()].some((task) => task.status !== "complete" && task.status !== "error");
1450
- if (lines.length === 0 && !snapshot.plan?.length && !hasRetirableNativeTasks && !snapshot.diffStat && !narrationUpdate.delta && !sessionUrl) return;
1429
+ if (lines.length === 0 && !snapshot.plan?.length && !hasRetirableNativeTasks && !snapshot.diffStat && !narrationUpdate.delta && sessionLinks.length === 0) return;
1451
1430
  const completion = reconcileSlackNativeTaskChunks({
1452
1431
  previous: nativeStreamSnapshot,
1453
1432
  finalStatus: finalInProgressStatus,
1454
1433
  chunks: buildSlackProgressStreamChunks({
1455
- title: resolveNativeProgressTitle(snapshot) ?? (lines.length === 0 && !snapshot.plan?.length ? "Working" : void 0),
1434
+ title: resolveNativeProgressTitle(snapshot),
1456
1435
  lines,
1457
1436
  plan: snapshot.plan,
1458
1437
  maxLineChars: progressDraftMaxLineChars,
1459
1438
  summaryRow: !previewToolProgressEnabled,
1460
1439
  finalInProgressStatus,
1461
1440
  diffStat: snapshot.diffStat,
1462
- sessionUrl
1441
+ sessionLinks
1463
1442
  })
1464
1443
  }).chunks;
1465
1444
  return narrationUpdate.delta ? [{
@@ -1530,12 +1509,12 @@ function createSlackProgressRuntime(runtimeParams) {
1530
1509
  await cancelNativeUpdates();
1531
1510
  }
1532
1511
  const priorSnapshot = progressDraft.getSnapshot();
1533
- const priorFallbackText = progressCard.resolveText(priorSnapshot);
1534
1512
  const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent ? buildNativeProgressCompletionChunks(nativeProgressTerminalStatus) : void 0;
1535
1513
  if (!progressDraft.beginNewTurn(options)) return false;
1536
1514
  if (useNativeProgressStreaming) await finishNativeProgressTurn(completionChunks);
1537
1515
  else {
1538
- await progressCard.finalize("success", priorSnapshot, priorFallbackText);
1516
+ await progressCard.finalize("success", priorSnapshot);
1517
+ await previewLifecycle.cleanup();
1539
1518
  draftStream?.forceNewMessage();
1540
1519
  await dropDetachedProgressCards();
1541
1520
  }
@@ -1546,7 +1525,7 @@ function createSlackProgressRuntime(runtimeParams) {
1546
1525
  nativeUpdatesStopped = false;
1547
1526
  nativeUpdates.resetThrottleWindow();
1548
1527
  progressCard.reset();
1549
- resetPreviewDeliveryState();
1528
+ previewLifecycle.reset();
1550
1529
  delivery.resetDeliveryTracker();
1551
1530
  return true;
1552
1531
  };
@@ -1561,26 +1540,26 @@ function createSlackProgressRuntime(runtimeParams) {
1561
1540
  };
1562
1541
  const onQueuedFollowupAdmitted = !shouldUseDraftStream && !useNativeProgressStreaming ? void 0 : async () => {
1563
1542
  await draftStream?.flush();
1564
- resetPreviewDeliveryState();
1565
1543
  if (isProgressMode) await beginNewProgressTurn({ force: true });
1566
- else draftStream?.forceNewMessage();
1544
+ else {
1545
+ await previewLifecycle.cleanup();
1546
+ previewLifecycle.reset();
1547
+ draftStream?.forceNewMessage();
1548
+ }
1567
1549
  delivery.resetDeliveryTracker();
1568
1550
  resetDraftDeliveryState();
1569
1551
  progressDraft.reset();
1570
1552
  };
1571
- const onQueuedFollowupSettled = !useDraftProgressCard && !useNativeProgressStreaming && !preambleOnlyProgress ? void 0 : async () => {
1553
+ const onQueuedFollowupSettled = !draftStream && !useNativeProgressStreaming ? void 0 : async () => {
1572
1554
  if (useNativeProgressStreaming) {
1573
1555
  progressDraft.markFinalReplyStarted();
1574
1556
  await cancelNativeUpdates();
1575
1557
  await finishNativeProgressTurn(nativeProgressCompletionSent ? void 0 : buildNativeProgressCompletionChunks(nativeProgressTerminalStatus));
1576
- progressDraft.markFinalReplyDelivered();
1577
1558
  return;
1578
1559
  }
1579
- if (preambleOnlyProgress) {
1560
+ if (!useDraftProgressCard) {
1580
1561
  progressDraft.markFinalReplyStarted();
1581
- await draftStream?.clear();
1582
- await draftStream?.dropDetachedMessages();
1583
- progressDraft.markFinalReplyDelivered();
1562
+ await previewLifecycle.cleanup();
1584
1563
  return;
1585
1564
  }
1586
1565
  if (!progressCard.hasTerminalized) await draftStream?.clear();
@@ -1588,6 +1567,7 @@ function createSlackProgressRuntime(runtimeParams) {
1588
1567
  };
1589
1568
  return {
1590
1569
  draftStream,
1570
+ previewLifecycle,
1591
1571
  isProgressMode,
1592
1572
  useDraftProgressCard,
1593
1573
  useNativeProgressStreaming,
@@ -1616,6 +1596,7 @@ function createSlackProgressRuntime(runtimeParams) {
1616
1596
  deliverNativeFinal,
1617
1597
  dropDetachedProgressCards,
1618
1598
  finalizeDraftProgressCard: progressCard.finalize,
1599
+ onVisibleWorkSessions: progressCard.onVisibleWorkSessions,
1619
1600
  onDraftBoundary,
1620
1601
  onQueuedFollowupAdmitted,
1621
1602
  onQueuedFollowupSettled,
@@ -1672,14 +1653,14 @@ async function createSlackDispatchSetup(prepared) {
1672
1653
  ctx: prepared.ctxPayload
1673
1654
  });
1674
1655
  }
1675
- const threadTargets = resolveSlackThreadTargets({
1656
+ const threadContext = resolveSlackThreadContext({
1676
1657
  message,
1677
1658
  replyToMode: prepared.replyToMode
1678
1659
  });
1679
1660
  const forcedReplyThreadTs = prepared.forcedReplyThreadTs;
1680
1661
  const slackMessageMetadata = prepared.slackMessageMetadata;
1681
- const statusThreadTs = forcedReplyThreadTs ?? threadTargets.statusThreadTs;
1682
- const isThreadReply = threadTargets.isThreadReply;
1662
+ const statusThreadTs = forcedReplyThreadTs ?? threadContext.messageThreadId;
1663
+ const isThreadReply = threadContext.isThreadReply;
1683
1664
  const replyDeliveryMode = forcedReplyThreadTs ? "off" : prepared.replyToMode;
1684
1665
  const sourceReplyDeliveryMode = resolveChannelMessageSourceReplyDeliveryMode({
1685
1666
  cfg,
@@ -1701,6 +1682,7 @@ async function createSlackDispatchSetup(prepared) {
1701
1682
  const messageTs = message.ts ?? message.event_ts;
1702
1683
  const incomingThreadTs = message.thread_ts;
1703
1684
  let didSetStatus = false;
1685
+ let statusWasSet = false;
1704
1686
  let didAddTypingReaction = false;
1705
1687
  const statusReactionsEnabled = prepared.ctxPayload.InboundEventKind !== "room_event" && Boolean(prepared.ackReactionPromise) && Boolean(reactionMessageTs) && cfg.messages?.statusReactions?.enabled === true;
1706
1688
  const statusReactions = createStatusReactionController({
@@ -1739,8 +1721,12 @@ async function createSlackDispatchSetup(prepared) {
1739
1721
  hasRepliedRef,
1740
1722
  isThreadReply: Boolean(forcedReplyThreadTs) || isThreadReply
1741
1723
  });
1724
+ const slackStreaming = resolveSlackStreamingConfig({ streaming: account.config.streaming });
1725
+ const streamThreadHint = forcedReplyThreadTs ?? replyPlan.peekThreadTs();
1726
+ const slackProgressStyle = resolveSlackProgressStyle(account.config, Boolean(streamThreadHint));
1727
+ const quietProgress = slackStreaming.mode === "progress" && slackProgressStyle === "none";
1742
1728
  const typingTarget = statusThreadTs ? `${message.channel}/${statusThreadTs}` : message.channel;
1743
- const typingReaction = ctx.typingReaction;
1729
+ const typingReaction = quietProgress && account.config.typingReaction === void 0 ? "hourglass_flowing_sand" : ctx.typingReaction;
1744
1730
  const threadStatusGate = { hasVisibleOutput: () => false };
1745
1731
  const { onModelSelected, ...replyPipeline } = createChannelMessageReplyPipeline({
1746
1732
  cfg,
@@ -1752,7 +1738,7 @@ async function createSlackDispatchSetup(prepared) {
1752
1738
  start: async () => {
1753
1739
  if (!didSetStatus && !threadStatusGate.hasVisibleOutput()) {
1754
1740
  didSetStatus = true;
1755
- await ctx.setSlackSessionStatus({
1741
+ statusWasSet = await ctx.setSlackSessionStatus({
1756
1742
  channelId: message.channel,
1757
1743
  threadTs: statusThreadTs,
1758
1744
  status: "processing",
@@ -1773,12 +1759,17 @@ async function createSlackDispatchSetup(prepared) {
1773
1759
  stop: async () => {
1774
1760
  if (didSetStatus) {
1775
1761
  didSetStatus = false;
1776
- await ctx.setSlackSessionStatus({
1762
+ const reportFailure = statusWasSet;
1763
+ statusWasSet = false;
1764
+ const restored = await ctx.setSlackSessionStatus({
1777
1765
  channelId: message.channel,
1778
1766
  threadTs: statusThreadTs,
1779
1767
  status: "active",
1780
1768
  eventScope: prepared.eventScope
1781
1769
  });
1770
+ if (reportFailure && !restored) try {
1771
+ runtime.error?.("Slack session status could not return to active after processing. Enable verbose logging to inspect the Slack API failure.");
1772
+ } catch {}
1782
1773
  }
1783
1774
  if (didAddTypingReaction && typingReaction && message.ts) {
1784
1775
  didAddTypingReaction = false;
@@ -1810,23 +1801,16 @@ async function createSlackDispatchSetup(prepared) {
1810
1801
  }
1811
1802
  }
1812
1803
  });
1813
- const slackStreaming = resolveSlackStreamingConfig({ streaming: account.config.streaming });
1814
- const streamThreadHint = forcedReplyThreadTs ?? resolveSlackStreamingThreadHint({
1815
- replyToMode: replyDeliveryMode,
1816
- incomingThreadTs,
1817
- messageTs,
1818
- isThreadReply
1819
- });
1820
1804
  const hookRunner = getGlobalHookRunner();
1821
1805
  const modifyingHooksRegistered = (hookRunner?.hasHooks("reply_payload_sending") ?? false) || (hookRunner?.hasHooks("message_sending") ?? false);
1822
1806
  const allowPreHookProviderStreaming = !prepared.ctxPayload.GroupThread && !modifyingHooksRegistered;
1823
- const previewStreamingEnabled = allowPreHookProviderStreaming && !sourceRepliesAreToolOnly && slackStreaming.mode !== "off";
1807
+ const previewStreamingEnabled = allowPreHookProviderStreaming && !sourceRepliesAreToolOnly && !quietProgress && slackStreaming.mode !== "off";
1824
1808
  const hasSlackCustomIdentity = Boolean(slackIdentity?.username || slackIdentity?.iconUrl || slackIdentity?.iconEmoji);
1825
1809
  const useStreaming = shouldUseStreaming({
1826
1810
  streamingEnabled: !prepared.ctxPayload.GroupThread && !sourceRepliesAreToolOnly && (allowPreHookProviderStreaming || slackStreaming.mode !== "progress") && isSlackStreamingEnabled({
1827
1811
  mode: slackStreaming.mode,
1828
1812
  nativeStreaming: slackStreaming.nativeStreaming,
1829
- nativeProgressTaskCards: resolveSlackNativeProgressTaskCards(account.config)
1813
+ nativeProgressTaskCards: resolveSlackNativeProgressTaskCards(account.config, slackProgressStyle)
1830
1814
  }),
1831
1815
  threadTs: streamThreadHint
1832
1816
  });
@@ -1850,13 +1834,10 @@ async function createSlackDispatchSetup(prepared) {
1850
1834
  isThreadReply,
1851
1835
  replyDeliveryMode,
1852
1836
  sourceReplyDeliveryMode,
1853
- sourceRepliesAreToolOnly,
1854
1837
  suppressRoomEventTyping,
1855
1838
  messageSentHookTarget,
1856
1839
  messageSentHookContext,
1857
1840
  messageSentDeliveryHookContext,
1858
- incomingThreadTs,
1859
- messageTs,
1860
1841
  statusReactionsEnabled,
1861
1842
  statusReactions,
1862
1843
  hasRepliedRef,
@@ -1865,11 +1846,13 @@ async function createSlackDispatchSetup(prepared) {
1865
1846
  onModelSelected,
1866
1847
  replyPipeline,
1867
1848
  slackStreaming,
1849
+ slackProgressStyle,
1850
+ quietProgress,
1868
1851
  streamThreadHint,
1869
1852
  previewStreamingEnabled,
1870
1853
  hasSlackCustomIdentity,
1871
1854
  shouldUseDraftStream,
1872
- disableBlockStreaming: sourceRepliesAreToolOnly ? true : resolveSlackDisableBlockStreaming({
1855
+ disableBlockStreaming: sourceRepliesAreToolOnly || quietProgress ? true : resolveSlackDisableBlockStreaming({
1873
1856
  useStreaming,
1874
1857
  shouldUseDraftStream,
1875
1858
  blockStreamingEnabled
@@ -1888,9 +1871,43 @@ function createSlackStreamingDeliveryRuntime(setup) {
1888
1871
  streamFailed: false,
1889
1872
  usedReplyThreadTs: void 0,
1890
1873
  usedBlockReplyThreadTs: void 0,
1891
- observedReplyDelivery: false,
1892
- observedFinalReplyDelivery: false
1874
+ observedReplyDelivery: false
1893
1875
  };
1876
+ const startStream = async (params) => {
1877
+ const session = await startSlackStream({
1878
+ client: slackClient,
1879
+ clientOptions: slackClientOptions,
1880
+ channel: message.channel,
1881
+ ...params,
1882
+ ...slackIdentity ? { identity: slackIdentity } : {},
1883
+ teamId: await resolveSlackStreamRecipientTeamId({
1884
+ client: slackClient,
1885
+ token: ctx.botToken,
1886
+ userId: message.user,
1887
+ fallbackTeamId: slackStreamFallbackTeamId
1888
+ }),
1889
+ userId: message.user
1890
+ });
1891
+ state.streamSession = session;
1892
+ return session;
1893
+ };
1894
+ const deliverPreparedReply = (reply, replyThreadTs, deferMessageSentHooks = false) => deliverReplies({
1895
+ cfg: ctx.cfg,
1896
+ replies: [reply],
1897
+ target: prepared.replyTarget,
1898
+ token: ctx.botToken,
1899
+ accountId: account.accountId,
1900
+ runtime,
1901
+ textLimit: ctx.textLimit,
1902
+ mediaMaxBytes: ctx.mediaMaxBytes,
1903
+ replyThreadTs,
1904
+ replyToMode: replyDeliveryMode,
1905
+ ...slackIdentity ? { identity: slackIdentity } : {},
1906
+ ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
1907
+ ...messageSentDeliveryHookContext,
1908
+ ...deferMessageSentHooks ? { deferMessageSentHooks: true } : {},
1909
+ eventScope: prepared.eventScope
1910
+ });
1894
1911
  const emitStreamedDelivery = (content, result) => {
1895
1912
  emitSlackMessageSentHooks({
1896
1913
  ...messageSentHookContext,
@@ -1930,30 +1947,19 @@ function createSlackStreamingDeliveryRuntime(setup) {
1930
1947
  if (session.stoppedBySlack) return;
1931
1948
  state.observedReplyDelivery = true;
1932
1949
  state.usedReplyThreadTs ??= session.threadTs;
1933
- return stopResult;
1950
+ return {
1951
+ visibleReplySent: session.delivered,
1952
+ ...stopResult.messageId ? { messageIds: [stopResult.messageId] } : {},
1953
+ threadId: session.threadTs
1954
+ };
1934
1955
  } catch (stopErr) {
1935
1956
  if (stopErr instanceof SlackStreamNotDeliveredError) fallbackError = stopErr;
1936
1957
  else throw stopErr;
1937
1958
  }
1938
1959
  const fallbackText = fallbackError.pendingText.trim();
1939
1960
  if (!fallbackText) return;
1940
- await deliverReplies({
1941
- cfg: ctx.cfg,
1942
- replies: [prepareSlackReply({ text: fallbackText })],
1943
- target: prepared.replyTarget,
1944
- token: ctx.botToken,
1945
- accountId: account.accountId,
1946
- runtime,
1947
- textLimit: ctx.textLimit,
1948
- mediaMaxBytes: ctx.mediaMaxBytes,
1949
- replyThreadTs: session.threadTs,
1950
- replyToMode: replyDeliveryMode,
1951
- ...slackIdentity ? { identity: slackIdentity } : {},
1952
- ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
1953
- ...messageSentDeliveryHookContext,
1954
- deferMessageSentHooks: true,
1955
- eventScope: prepared.eventScope
1956
- });
1961
+ const sent = await deliverPreparedReply(prepareSlackReply({ text: fallbackText }), session.threadTs, true);
1962
+ if (!sent?.receipt.platformMessageIds.length) return;
1957
1963
  markSlackStreamFallbackDelivered(session);
1958
1964
  if (!session.stopped) try {
1959
1965
  await stopSlackStream({
@@ -1966,7 +1972,12 @@ function createSlackStreamingDeliveryRuntime(setup) {
1966
1972
  state.observedReplyDelivery = true;
1967
1973
  state.usedReplyThreadTs ??= session.threadTs;
1968
1974
  logVerbose(`slack-stream: streamed delivery failed (${fallbackError.slackCode}); delivered ${fallbackText.length} chars via deliverReplies fallback`);
1969
- return {};
1975
+ return {
1976
+ visibleReplySent: true,
1977
+ messageIds: sent.receipt.platformMessageIds,
1978
+ receipt: sent.receipt,
1979
+ threadId: sent.threadTs ?? session.threadTs
1980
+ };
1970
1981
  };
1971
1982
  const finishStream = async (chunks) => {
1972
1983
  const session = state.streamSession;
@@ -1988,7 +1999,7 @@ function createSlackStreamingDeliveryRuntime(setup) {
1988
1999
  }
1989
2000
  };
1990
2001
  const deliverNormally = async (params) => {
1991
- if (state.streamSession?.stoppedBySlack) return;
2002
+ if (state.streamSession?.stoppedBySlack) return { visibleReplySent: false };
1992
2003
  const replyThreadTs = resolveDeliveryThreadTs(params);
1993
2004
  const deliveryReplyThreadTs = replyDeliveryMode === "off" && !forcedReplyThreadTs && !isThreadReply ? void 0 : replyThreadTs;
1994
2005
  const preparedReply = prepareSlackReply(params.payload);
@@ -1999,26 +2010,11 @@ function createSlackStreamingDeliveryRuntime(setup) {
1999
2010
  }, preparedReply);
2000
2011
  if (deliveryTracker.hasDelivered(deliveryKey)) {
2001
2012
  logVerbose("slack: suppressed duplicate normal delivery within the same turn");
2002
- return deliveryReplyThreadTs;
2013
+ return { visibleReplySent: false };
2003
2014
  }
2004
- await deliverReplies({
2005
- cfg: ctx.cfg,
2006
- replies: [preparedReply],
2007
- target: prepared.replyTarget,
2008
- token: ctx.botToken,
2009
- accountId: account.accountId,
2010
- runtime,
2011
- textLimit: ctx.textLimit,
2012
- mediaMaxBytes: ctx.mediaMaxBytes,
2013
- replyThreadTs: deliveryReplyThreadTs,
2014
- replyToMode: replyDeliveryMode,
2015
- ...slackIdentity ? { identity: slackIdentity } : {},
2016
- ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
2017
- ...messageSentDeliveryHookContext,
2018
- eventScope: prepared.eventScope
2019
- });
2015
+ const sent = await deliverPreparedReply(preparedReply, deliveryReplyThreadTs);
2016
+ if (!sent?.receipt.platformMessageIds.length) return { visibleReplySent: false };
2020
2017
  state.observedReplyDelivery = true;
2021
- if (params.kind === "final") state.observedFinalReplyDelivery = true;
2022
2018
  const deliveredThreadTs = resolveSlackReplyThreadTs({
2023
2019
  replyToMode: replyDeliveryMode,
2024
2020
  replyToId: params.payload.replyToId,
@@ -2028,36 +2024,37 @@ function createSlackStreamingDeliveryRuntime(setup) {
2028
2024
  rememberDeliveredThreadTs(params.kind, deliveredThreadTs);
2029
2025
  replyPlan.markSent();
2030
2026
  deliveryTracker.markDelivered(deliveryKey);
2031
- return deliveryReplyThreadTs;
2027
+ return {
2028
+ visibleReplySent: true,
2029
+ messageIds: sent.receipt.platformMessageIds,
2030
+ receipt: sent.receipt,
2031
+ threadId: sent.threadTs ?? deliveredThreadTs
2032
+ };
2032
2033
  };
2033
2034
  const isStreamingEligible = (payload, options) => {
2034
2035
  const reply = resolveSendableOutboundReplyParts(payload);
2035
2036
  const renderPlan = resolveSlackReplyRenderPlan(payload);
2036
2037
  const plannedBlocks = renderPlan.mode === "single" ? renderPlan.blocks : renderPlan.blockPart?.blocks;
2037
- return !state.streamFailed && !reply.hasMedia && renderPlan.mode !== "split" && !renderPlan.textIsSlackPlainText && !plannedBlocks?.length && !readSlackReplyBlocks(payload)?.length && reply.hasText && (!options?.maxTextBytes || countSlackTextUtf8Bytes(reply.trimmedText) <= options.maxTextBytes);
2038
+ return !state.streamFailed && !reply.hasMedia && renderPlan.mode !== "split" && !renderPlan.textIsSlackPlainText && !plannedBlocks?.length && !resolveSlackReplyBlocks(payload)?.length && reply.hasText && (!options?.maxTextBytes || countSlackTextUtf8Bytes(reply.trimmedText) <= options.maxTextBytes);
2038
2039
  };
2039
2040
  const deliverWithStreaming = async (params) => {
2040
- if (state.streamSession?.stoppedBySlack) return;
2041
- if (!isStreamingEligible(params.payload)) {
2042
- await deliverNormally({
2043
- payload: params.payload,
2044
- kind: params.kind,
2045
- forcedThreadTs: state.streamSession?.threadTs ?? state.nativeProgressStreamThreadTs
2046
- });
2047
- return;
2048
- }
2041
+ if (state.streamSession?.stoppedBySlack) return { visibleReplySent: false };
2042
+ if (!isStreamingEligible(params.payload)) return await deliverNormally({
2043
+ payload: params.payload,
2044
+ kind: params.kind,
2045
+ forcedThreadTs: state.streamSession?.threadTs ?? state.nativeProgressStreamThreadTs
2046
+ });
2049
2047
  if (!state.streamSession && state.nativeProgressStreamStartPromise) await state.nativeProgressStreamStartPromise;
2050
- if (state.streamSession?.stoppedBySlack) return;
2048
+ if (state.streamSession?.stoppedBySlack) return { visibleReplySent: false };
2051
2049
  let session = state.streamSession;
2052
2050
  const threadTs = session?.threadTs ?? replyPlan.nextThreadTs();
2053
2051
  if (state.streamFailed || !threadTs) {
2054
2052
  state.streamFailed = true;
2055
- await deliverNormally({
2053
+ return await deliverNormally({
2056
2054
  payload: params.payload,
2057
2055
  kind: params.kind,
2058
2056
  forcedThreadTs: threadTs ?? state.nativeProgressStreamThreadTs
2059
2057
  });
2060
- return;
2061
2058
  }
2062
2059
  const hookContent = resolveSendableOutboundReplyParts(params.payload).trimmedText;
2063
2060
  const text = params.streamText ?? hookContent;
@@ -2068,35 +2065,22 @@ function createSlackStreamingDeliveryRuntime(setup) {
2068
2065
  });
2069
2066
  if (deliveryTracker.hasDelivered(deliveryKey)) {
2070
2067
  logVerbose("slack-stream: suppressed duplicate reply payload");
2071
- return;
2068
+ return { visibleReplySent: false };
2072
2069
  }
2073
2070
  let messageId;
2071
+ let fallbackDelivery;
2074
2072
  try {
2075
2073
  if (session) await appendSlackStream({
2076
2074
  session,
2077
2075
  text: `${params.appendSeparator === false ? "" : "\n"}${text}`,
2078
2076
  chunks: []
2079
2077
  });
2080
- else {
2081
- session = await startSlackStream({
2082
- client: slackClient,
2083
- clientOptions: slackClientOptions,
2084
- channel: message.channel,
2085
- threadTs,
2086
- text,
2087
- chunks: [],
2088
- ...params.taskDisplayMode ? { taskDisplayMode: params.taskDisplayMode } : {},
2089
- ...slackIdentity ? { identity: slackIdentity } : {},
2090
- teamId: await resolveSlackStreamRecipientTeamId({
2091
- client: slackClient,
2092
- token: ctx.botToken,
2093
- userId: message.user,
2094
- fallbackTeamId: slackStreamFallbackTeamId
2095
- }),
2096
- userId: message.user
2097
- });
2098
- state.streamSession = session;
2099
- }
2078
+ else session = await startStream({
2079
+ threadTs,
2080
+ text,
2081
+ chunks: [],
2082
+ ...params.taskDisplayMode ? { taskDisplayMode: params.taskDisplayMode } : {}
2083
+ });
2100
2084
  messageId = session.streamer.ts;
2101
2085
  } catch (error) {
2102
2086
  state.streamFailed = true;
@@ -2107,18 +2091,16 @@ function createSlackStreamingDeliveryRuntime(setup) {
2107
2091
  });
2108
2092
  throw error;
2109
2093
  }
2110
- if (!session) {
2111
- await deliverNormally({
2112
- payload: params.payload,
2113
- kind: params.kind,
2114
- forcedThreadTs: threadTs
2115
- });
2116
- return;
2117
- }
2094
+ if (!session) return await deliverNormally({
2095
+ payload: params.payload,
2096
+ kind: params.kind,
2097
+ forcedThreadTs: threadTs
2098
+ });
2118
2099
  try {
2119
2100
  const fallback = await deliverPendingStreamFallback(session, error);
2120
2101
  if (!fallback && !session.stoppedBySlack) throw error;
2121
- messageId = fallback?.messageId;
2102
+ fallbackDelivery = fallback;
2103
+ messageId = fallback?.receipt ? void 0 : fallback?.messageIds?.[0];
2122
2104
  } catch (fallbackError) {
2123
2105
  emitStreamedDelivery(hookContent, {
2124
2106
  success: false,
@@ -2132,10 +2114,10 @@ function createSlackStreamingDeliveryRuntime(setup) {
2132
2114
  success: false,
2133
2115
  error: "Stopped by Slack user"
2134
2116
  });
2135
- return;
2117
+ return { visibleReplySent: false };
2136
2118
  }
2119
+ if (!fallbackDelivery?.visibleReplySent && (!session.delivered || session.pendingText)) return { visibleReplySent: false };
2137
2120
  state.observedReplyDelivery = true;
2138
- if (params.kind === "final") state.observedFinalReplyDelivery = true;
2139
2121
  rememberDeliveredThreadTs(params.kind, threadTs);
2140
2122
  replyPlan.markSent();
2141
2123
  deliveryTracker.markDelivered(deliveryKey);
@@ -2143,15 +2125,20 @@ function createSlackStreamingDeliveryRuntime(setup) {
2143
2125
  success: true,
2144
2126
  ...messageId ? { messageId } : {}
2145
2127
  });
2128
+ return fallbackDelivery ?? {
2129
+ visibleReplySent: true,
2130
+ ...messageId ? { messageIds: [messageId] } : {},
2131
+ threadId: threadTs
2132
+ };
2146
2133
  };
2147
2134
  return Object.assign(state, {
2148
2135
  deliverNormally,
2149
2136
  deliverWithStreaming,
2150
2137
  finishStream,
2151
- hasDelivered: (params) => deliveryTracker.hasDelivered(buildSlackEventDeliveryKey(params)),
2152
2138
  isStreamingEligible,
2153
2139
  markPreviewPayloadDelivered,
2154
2140
  rememberDeliveredThreadTs,
2141
+ startStream,
2155
2142
  resetDeliveryTracker: () => {
2156
2143
  deliveryTracker = createSlackEventDeliveryTracker();
2157
2144
  }
@@ -2194,17 +2181,12 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2194
2181
  const { account, cfg, ctx, disableBlockStreaming, hasSlackCustomIdentity, hasRepliedRef, message, messageSentHookContext, messageSentHookTarget, onModelSelected, previewStreamingEnabled, replyPipeline, replyPlan, route, runtime, slackClient, slackStreaming, sourceReplyDeliveryMode, statusReactions, statusReactionsEnabled, statusThreadTs, suppressRoomEventTyping, useStreaming } = setup;
2195
2182
  let dispatchError;
2196
2183
  const delivery = createSlackStreamingDeliveryRuntime(setup);
2197
- const draftPreviewCommitted = { value: false };
2198
2184
  const progress = createSlackProgressRuntime({
2199
2185
  setup,
2200
- delivery,
2201
- resetPreviewDeliveryState: () => {
2202
- draftPreviewCommitted.value = false;
2203
- delivery.observedFinalReplyDelivery = false;
2204
- }
2186
+ delivery
2205
2187
  });
2206
- const draftStream = progress.draftStream;
2207
- setup.threadStatusGate.hasVisibleOutput = () => delivery.observedReplyDelivery || draftPreviewCommitted.value || Boolean(draftStream?.messageId());
2188
+ const { draftStream, previewLifecycle } = progress;
2189
+ setup.threadStatusGate.hasVisibleOutput = () => delivery.observedReplyDelivery || previewLifecycle.previewFinalized || Boolean(draftStream?.messageId());
2208
2190
  const failureNoticeThreadTs = message.thread_ts;
2209
2191
  const failureNoticeTeamId = prepared.eventScope?.teamId;
2210
2192
  let sawTerminalFailurePayload = false;
@@ -2212,7 +2194,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2212
2194
  const filterPassiveThreadFailure = (payload) => {
2213
2195
  if (payload.isError !== true || prepared.ctxPayload.ChatType !== "channel" || isReplyPayloadNonTerminalToolErrorWarning(payload)) return payload;
2214
2196
  sawTerminalFailurePayload = true;
2215
- if (delivery.observedReplyDelivery || draftPreviewCommitted.value) return payload;
2197
+ if (delivery.observedReplyDelivery || previewLifecycle.previewFinalized) return payload;
2216
2198
  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;
2217
2199
  const noticeThreadTs = failureNoticeThreadTs ?? (explicitlyAddressed ? statusThreadTs : void 0);
2218
2200
  const notice = {
@@ -2233,14 +2215,6 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2233
2215
  pendingFailureNotice = notice;
2234
2216
  return payload;
2235
2217
  };
2236
- let compactFinalDeliveryStarted = false;
2237
- const clearCompactProgress = async () => {
2238
- try {
2239
- await draftStream?.clear();
2240
- } catch (err) {
2241
- logVerbose(`slack: progress preview cleanup failed (${formatSlackError(err)})`);
2242
- }
2243
- };
2244
2218
  const deliverSlackPayload = async (incomingPayload, info) => {
2245
2219
  let payload = incomingPayload;
2246
2220
  if (info.participant && (payload.text || payload.mediaUrl || payload.mediaUrls?.length)) payload = {
@@ -2248,66 +2222,42 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2248
2222
  text: formatSlackGroupThreadReply(payload.text ?? "", info.participant)
2249
2223
  };
2250
2224
  if (info.kind === "final" && slackStreaming.mode === "progress" && progress.isProgressMode) {
2251
- if (progress.useNativeProgressStreaming) {
2252
- await progress.deliverNativeFinal(payload, info.kind);
2253
- return;
2254
- }
2255
- progress.progressDraft.markFinalReplyStarted();
2256
- if (!progress.useDraftProgressCard) {
2257
- compactFinalDeliveryStarted = true;
2258
- await draftStream?.discardPending();
2259
- const supplement = getReplyPayloadTtsSupplement(payload);
2260
- await delivery.deliverNormally({
2261
- payload: supplement && !supplement.visibleTextAlreadyDelivered && !payload.text?.trim() ? {
2262
- ...payload,
2263
- text: supplement.spokenText
2264
- } : payload,
2265
- kind: info.kind,
2266
- forcedThreadTs: delivery.usedReplyThreadTs
2267
- });
2268
- if (delivery.observedFinalReplyDelivery) {
2269
- progress.progressDraft.markFinalReplyDelivered();
2270
- await clearCompactProgress();
2271
- }
2272
- return;
2273
- }
2274
- if (progress.useDraftProgressCard) {
2275
- await delivery.deliverNormally({
2276
- payload,
2225
+ const supplement = getReplyPayloadTtsSupplement(payload);
2226
+ const finalPayload = !progress.useDraftProgressCard && !progress.useNativeProgressStreaming && supplement && !supplement.visibleTextAlreadyDelivered && !payload.text?.trim() ? {
2227
+ ...payload,
2228
+ text: supplement.spokenText
2229
+ } : payload;
2230
+ return (await previewLifecycle.deliver({
2231
+ kind: info.kind,
2232
+ payload: finalPayload,
2233
+ isError: payload.isError === true,
2234
+ deliverNormally: (reply) => progress.useNativeProgressStreaming ? progress.deliverNativeFinal(reply, info.kind) : delivery.deliverNormally({
2235
+ payload: reply,
2277
2236
  kind: info.kind,
2278
2237
  forcedThreadTs: delivery.usedReplyThreadTs
2279
- });
2280
- if (!await progress.finalizeDraftProgressCard(payload.isError === true ? "error" : "success")) await draftStream?.clear();
2281
- progress.progressDraft.markFinalReplyDelivered();
2282
- return;
2283
- }
2238
+ }),
2239
+ onNormalDelivered: progress.useDraftProgressCard ? async () => {
2240
+ if (!await progress.finalizeDraftProgressCard(payload.isError === true ? "error" : "success")) await draftStream?.clear();
2241
+ } : void 0
2242
+ })).deliveryResult ?? { visibleReplySent: false };
2284
2243
  }
2285
2244
  if (progress.useNativeProgressStreaming) {
2286
- if (info.kind !== "final" && payload.isError !== true) {
2287
- if (!delivery.isStreamingEligible(payload)) {
2288
- await delivery.deliverNormally({
2289
- payload,
2290
- kind: info.kind,
2291
- forcedThreadTs: delivery.streamSession?.threadTs ?? delivery.nativeProgressStreamThreadTs
2292
- });
2293
- return;
2294
- }
2295
- return await progress.appendNativeNarration(payload, info.kind) ? void 0 : { visibleReplySent: false };
2296
- }
2297
- await delivery.deliverNormally({
2245
+ if (info.kind !== "final" && payload.isError !== true && delivery.isStreamingEligible(payload)) return await progress.appendNativeNarration(payload, info.kind);
2246
+ return await delivery.deliverNormally({
2298
2247
  payload,
2299
2248
  kind: info.kind,
2300
2249
  forcedThreadTs: delivery.streamSession?.threadTs ?? delivery.nativeProgressStreamThreadTs
2301
2250
  });
2302
- return;
2303
2251
  }
2304
- if (useStreaming) {
2305
- await delivery.deliverWithStreaming({
2306
- payload,
2252
+ if (useStreaming) return (await previewLifecycle.deliver({
2253
+ kind: info.kind,
2254
+ payload,
2255
+ isError: payload.isError === true,
2256
+ deliverNormally: (reply) => delivery.deliverWithStreaming({
2257
+ payload: reply,
2307
2258
  kind: info.kind
2308
- });
2309
- return;
2310
- }
2259
+ })
2260
+ })).deliveryResult ?? { visibleReplySent: false };
2311
2261
  const reply = resolveSendableOutboundReplyParts(payload);
2312
2262
  const ttsSupplement = getReplyPayloadTtsSupplement(payload);
2313
2263
  const replySourceText = payload.text ?? ttsSupplement?.spokenText;
@@ -2321,26 +2271,13 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2321
2271
  accountId: account.accountId
2322
2272
  }) });
2323
2273
  const previewFinalTextFitsEdit = countSlackTextUtf8Bytes(previewFinalText) <= SLACK_EDIT_TEXT_MAX_BYTES;
2324
- const shouldRestoreTtsSupplementTextForPreviewFallback = Boolean(ttsSupplement) && ttsSupplement?.visibleTextAlreadyDelivered !== true && Boolean(draftStream) && !draftPreviewCommitted.value && !delivery.observedFinalReplyDelivery && previewStreamingEnabled && !payload.text?.trim();
2274
+ const shouldRestoreTtsSupplementTextForPreviewFallback = Boolean(ttsSupplement) && ttsSupplement?.visibleTextAlreadyDelivered !== true && Boolean(draftStream) && !previewLifecycle.previewFinalized && !previewLifecycle.finalDelivered && previewStreamingEnabled && !payload.text?.trim();
2325
2275
  let ttsPreviewFinalization;
2326
- await deliverWithFinalizableLivePreviewAdapter({
2276
+ return (await previewLifecycle.deliver({
2327
2277
  kind: info.kind,
2328
2278
  payload,
2329
- adapter: defineFinalizableLivePreviewAdapter({
2330
- draft: draftStream && !draftPreviewCommitted.value && !delivery.observedFinalReplyDelivery ? {
2331
- flush: draftStream.flush,
2332
- clear: () => draftStream.clear({ preserveHumanReplies: true }),
2333
- discardPending: draftStream.discardPending,
2334
- seal: draftStream.seal,
2335
- id: () => {
2336
- const channelId = draftStream.channelId();
2337
- const messageId = draftStream.messageId();
2338
- return channelId && messageId ? {
2339
- channelId,
2340
- messageId
2341
- } : void 0;
2342
- }
2343
- } : void 0,
2279
+ isError: payload.isError === true,
2280
+ adapter: {
2344
2281
  buildFinalEdit: () => {
2345
2282
  if (hasSlackCustomIdentity || !previewStreamingEnabled || reply.hasMedia && !ttsSupplement || payload.isError || requiresSeparateFallbackDelivery || !previewFinalTextFitsEdit || trimmedFinalText.length === 0 && !slackBlocks?.length) return;
2346
2283
  return {
@@ -2350,11 +2287,6 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2350
2287
  };
2351
2288
  },
2352
2289
  editFinal: async (preview, edit) => {
2353
- if (delivery.hasDelivered({
2354
- kind: info.kind,
2355
- payload,
2356
- threadTs: edit.threadTs
2357
- })) return;
2358
2290
  if (ttsSupplement) ttsPreviewFinalization = { threadTs: edit.threadTs };
2359
2291
  if (!await draftStream?.finalizeMessage(preview.messageId, async () => {
2360
2292
  await finalizeSlackPreviewEdit({
@@ -2368,59 +2300,66 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2368
2300
  threadTs: edit.threadTs
2369
2301
  });
2370
2302
  })) throw new Error("Slack preview moved below a newer conversation message");
2371
- if (!ttsSupplement) emitSlackMessageSentHooks({
2372
- ...messageSentHookContext,
2373
- to: messageSentHookTarget,
2374
- accountId: account.accountId,
2375
- content: trimmedFinalText,
2376
- success: true,
2377
- messageId: preview.messageId
2378
- });
2379
- draftPreviewCommitted.value = true;
2380
- delivery.observedFinalReplyDelivery = true;
2381
2303
  },
2382
- onPreviewFinalized: (_preview) => {
2383
- draftPreviewCommitted.value = true;
2384
- delivery.observedFinalReplyDelivery = true;
2304
+ createPreviewReceipt: (preview, edit) => createMessageReceiptFromOutboundResults({
2305
+ results: [{
2306
+ channel: "slack",
2307
+ channelId: preview.channelId,
2308
+ messageId: preview.messageId
2309
+ }],
2310
+ threadId: edit.threadTs,
2311
+ kind: "text"
2312
+ }),
2313
+ onPreviewFinalized: (preview) => {
2385
2314
  const finalThreadTs = delivery.usedReplyThreadTs ?? statusThreadTs;
2386
2315
  delivery.observedReplyDelivery = true;
2387
2316
  replyPlan.markSent();
2388
- if (!ttsSupplement) delivery.markPreviewPayloadDelivered({
2389
- kind: info.kind,
2390
- payload,
2391
- threadTs: finalThreadTs
2392
- });
2317
+ if (!ttsSupplement) {
2318
+ delivery.markPreviewPayloadDelivered({
2319
+ kind: info.kind,
2320
+ payload,
2321
+ threadTs: finalThreadTs
2322
+ });
2323
+ emitSlackMessageSentHooks({
2324
+ ...messageSentHookContext,
2325
+ to: messageSentHookTarget,
2326
+ accountId: account.accountId,
2327
+ content: trimmedFinalText,
2328
+ success: true,
2329
+ messageId: preview.messageId
2330
+ });
2331
+ }
2393
2332
  },
2394
2333
  buildSupplementalPayload: () => ttsSupplement ? buildTtsSupplementMediaPayload(payload) : void 0,
2395
2334
  deliverSupplemental: async (supplementalPayload) => {
2396
2335
  const previewThreadTs = delivery.usedReplyThreadTs ?? statusThreadTs;
2397
- const supplementalThreadTs = await delivery.deliverNormally({
2336
+ const supplementalResult = await delivery.deliverNormally({
2398
2337
  payload: supplementalPayload,
2399
2338
  kind: info.kind,
2400
2339
  forcedThreadTs: previewThreadTs
2401
2340
  });
2402
- delivery.markPreviewPayloadDelivered({
2341
+ if (supplementalResult.visibleReplySent) delivery.markPreviewPayloadDelivered({
2403
2342
  kind: info.kind,
2404
2343
  payload,
2405
- threadTs: supplementalThreadTs
2344
+ threadTs: supplementalResult.threadId
2406
2345
  });
2346
+ return supplementalResult;
2407
2347
  },
2408
2348
  logPreviewEditFailure: (err) => {
2409
2349
  logVerbose(`slack: preview final edit failed; falling back to standard send (${formatSlackError(err)})`);
2410
2350
  }
2411
- }),
2412
- deliverNormally: async () => {
2413
- await delivery.deliverNormally({
2414
- payload: shouldRestoreTtsSupplementTextForPreviewFallback || ttsPreviewFinalization && !payload.text?.trim() ? {
2415
- ...payload,
2351
+ },
2352
+ deliverNormally: async (normalPayload) => {
2353
+ return await delivery.deliverNormally({
2354
+ payload: normalPayload === payload && (shouldRestoreTtsSupplementTextForPreviewFallback || ttsPreviewFinalization && !payload.text?.trim()) ? {
2355
+ ...normalPayload,
2416
2356
  text: ttsSupplement?.spokenText
2417
- } : payload,
2357
+ } : normalPayload,
2418
2358
  kind: info.kind,
2419
2359
  ...ttsPreviewFinalization?.threadTs ? { forcedThreadTs: ttsPreviewFinalization.threadTs } : {}
2420
2360
  });
2421
2361
  }
2422
- });
2423
- if (info.kind === "final") progress.progressDraft.markFinalReplyDelivered();
2362
+ })).deliveryResult ?? { visibleReplySent: false };
2424
2363
  };
2425
2364
  let agentRunFailed = false;
2426
2365
  let settledDispatchResult;
@@ -2452,9 +2391,9 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2452
2391
  }
2453
2392
  },
2454
2393
  record: prepared.turn.record,
2455
- history: prepared.turn.history,
2456
2394
  botLoopProtection: resolveSlackBotLoopProtection(prepared),
2457
2395
  replyOptions: {
2396
+ onVisibleWorkSessions: progress.onVisibleWorkSessions,
2458
2397
  groupThreadReplyFormatter: formatSlackGroupThreadReply,
2459
2398
  queuedDeliveryCorrelations: [{ begin: beginSessionRun }],
2460
2399
  ...turnAdoptionLifecycle ? { turnAdoptionLifecycle } : {},
@@ -2474,9 +2413,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2474
2413
  } : void 0,
2475
2414
  allowProgressCallbacksWhenSourceDeliverySuppressed: sourceReplyDeliveryMode === "message_tool_only" && statusReactionsEnabled ? true : void 0,
2476
2415
  allowToolLifecycleWhenProgressHidden: statusReactionsEnabled ? true : void 0,
2477
- onPartialReply: useStreaming ? void 0 : !previewStreamingEnabled ? void 0 : async (payload) => {
2478
- return progress.updateDraftFromPartial(payload.text);
2479
- },
2416
+ onPartialReply: !useStreaming && previewStreamingEnabled ? async (payload) => progress.updateDraftFromPartial(payload.text) : void 0,
2480
2417
  onAssistantMessageStart: progress.onDraftBoundary ? async () => {
2481
2418
  await progress.onDraftBoundary?.();
2482
2419
  return false;
@@ -2494,30 +2431,19 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2494
2431
  },
2495
2432
  onToolStart: async (payload) => {
2496
2433
  if (statusReactionsEnabled) await statusReactions.setTool(payload.name);
2497
- if (payload.phase === "start") progress.progressWorkCounter.noteToolCall(payload.name);
2498
- if (progress.preambleOnlyProgress) return isChannelProgressDraftWorkToolName(payload.name) ? await progress.progressDraft.noteActivity() : false;
2499
2434
  return await progress.progressDraft.pushToolEvent(payload);
2500
2435
  },
2501
2436
  onItemEvent: async (payload) => {
2502
- if (payload.kind === "preamble" && (payload.phase === "start" || payload.phase === "update") && !draftStream?.messageId() && !delivery.streamSession?.delivered) return false;
2503
- if (progress.isProgressMode && payload.kind === "preamble") {
2504
- if (progress.shouldYieldDraftProgress()) return false;
2505
- const headlineVisible = await progress.progressDraft.pushPreambleHeadline(payload.progressText, { itemId: payload.itemId });
2506
- if (progress.commentaryProgressEnabled) return await progress.progressDraft.pushCommentaryProgress(payload.progressText, {
2507
- itemId: payload.itemId,
2508
- ...progress.preambleOnlyProgress ? { complete: payload.phase !== "start" && payload.phase !== "update" } : {}
2509
- }) || headlineVisible;
2510
- return headlineVisible;
2511
- }
2512
- return progress.preambleOnlyProgress ? await progress.progressDraft.noteActivity() : await progress.progressDraft.pushItemEvent(payload);
2437
+ if (payload.hideFromChannelProgress || payload.suppressChannelProgress) return progress.preambleOnlyProgress ? false : progress.progressDraft.pushItemEvent(payload);
2438
+ if (payload.kind === "preamble" && progress.shouldYieldDraftProgress()) return false;
2439
+ progress.progressWorkCounter.noteItem(payload);
2440
+ return progress.preambleOnlyProgress && payload.kind !== "preamble" ? await progress.progressDraft.noteActivity() : await progress.progressDraft.pushItemEvent(payload);
2513
2441
  },
2514
2442
  onPlanUpdate: async (payload) => {
2515
2443
  if (payload.phase !== "update") return false;
2516
2444
  return await progress.pushPlanProgress(payload.steps, payload.explanation, payload.explanationFormat);
2517
2445
  },
2518
- onApprovalEvent: (payload) => progress.progressDraft.pushApprovalEvent(payload),
2519
- onCommandOutput: async (payload) => progress.preambleOnlyProgress ? await progress.progressDraft.noteActivity() : await progress.progressDraft.pushCommandOutputEvent(payload),
2520
- onPatchSummary: async (payload) => progress.preambleOnlyProgress ? await progress.progressDraft.noteActivity() : await progress.progressDraft.pushPatchEvent(payload)
2446
+ onApprovalEvent: (payload) => progress.progressDraft.pushApprovalEvent(payload)
2521
2447
  }
2522
2448
  });
2523
2449
  if (turnResult.dispatched) {
@@ -2541,9 +2467,11 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2541
2467
  const completionChunks = progress.useNativeProgressStreaming && !progress.nativeProgressCompletionSent ? progress.buildNativeProgressCompletionChunks(dispatchError || agentRunFailed ? "error" : progress.nativeProgressTerminalStatus) : void 0;
2542
2468
  if (completionChunks?.length) progress.nativeProgressCompletionSent = true;
2543
2469
  await delivery.finishStream(completionChunks);
2544
- const anyReplyDelivered = hasVisibleInboundReplyDispatch(settledDispatchResult, { observedReplyDelivery: delivery.observedReplyDelivery });
2545
- if (!progress.isProgressMode && anyReplyDelivered && !delivery.observedFinalReplyDelivery && !dispatchError && !agentRunFailed) await draftStream?.clear({ preserveHumanReplies: true });
2546
- if (progress.isProgressMode && !progress.useNativeProgressStreaming && !progress.useDraftProgressCard && !compactFinalDeliveryStarted && !dispatchError && !agentRunFailed && anyReplyDelivered) await clearCompactProgress();
2470
+ const anyReplyDelivered = hasVisibleInboundReplyDispatch(settledDispatchResult, { observedReplyDelivery: delivery.observedReplyDelivery || previewLifecycle.finalDelivered });
2471
+ if (anyReplyDelivered && !previewLifecycle.finalStarted && !dispatchError && !agentRunFailed) {
2472
+ if (hasVisibleInboundReplyDispatch(settledDispatchResult)) await previewLifecycle.observeDelivery({ visibleReplySent: true });
2473
+ }
2474
+ await previewLifecycle.cleanup({ failed: Boolean(dispatchError || agentRunFailed) });
2547
2475
  if (pendingFailureNotice && anyReplyDelivered) recordSlackThreadFailureNotice(pendingFailureNotice);
2548
2476
  if (dispatchError || agentRunFailed) await progress.finalizeDraftProgressCard("error");
2549
2477
  await progress.dropDetachedProgressCards();
@@ -2562,14 +2490,13 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2562
2490
  teamId: prepared.eventScope?.teamId
2563
2491
  });
2564
2492
  if (dispatchError) throw toErrorObject(dispatchError, "Slack dispatch failed");
2565
- if (!anyReplyDelivered && !draftPreviewCommitted.value && !(agentRunFailed && progress.useDraftProgressCard)) {
2566
- await draftStream?.clear();
2567
- await draftStream?.dropDetachedMessages();
2493
+ if (!anyReplyDelivered && !previewLifecycle.previewFinalized && !(agentRunFailed && progress.useDraftProgressCard)) {
2494
+ if (progress.useDraftProgressCard) await draftStream?.clear();
2568
2495
  return;
2569
2496
  }
2570
2497
  if (shouldLogVerbose()) {
2571
2498
  const finalCount = resolveInboundReplyDispatchCounts(settledDispatchResult).final;
2572
- logVerbose(`slack: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${prepared.replyTarget}`);
2499
+ logVerbose(`slack: delivered ${finalCount} repl${finalCount === 1 ? "y" : "ies"} to ${prepared.replyTarget}`);
2573
2500
  }
2574
2501
  }
2575
2502
  //#endregion
@@ -2629,12 +2556,9 @@ function formatSlackFilePlaceholder(files) {
2629
2556
  return `[attached: ${formatSlackFileReferenceList(files)}]`;
2630
2557
  }
2631
2558
  function pushUniqueText(parts, value, options = {}) {
2632
- const text = options.preserveWhitespace ? typeof value === "string" && value.trim().length > 0 ? value : void 0 : normalizeOptionalString(value);
2559
+ const text = options.preserveWhitespace ? readNonBlankString(value) : normalizeOptionalString(value);
2633
2560
  if (text && !parts.includes(text)) parts.push(text);
2634
2561
  }
2635
- function resolveSlackBlocksFallbackText(blocks) {
2636
- return resolveSlackBlocksText(blocks)?.text;
2637
- }
2638
2562
  function resolveSlackAttachmentFallbackText(attachments) {
2639
2563
  if (!Array.isArray(attachments) || attachments.length === 0) return;
2640
2564
  const parts = [];
@@ -2649,13 +2573,13 @@ function resolveSlackAttachmentFallbackText(attachments) {
2649
2573
  pushUniqueText(parts, field.title);
2650
2574
  pushUniqueText(parts, field.value);
2651
2575
  }
2652
- pushUniqueText(parts, resolveSlackBlocksFallbackText(fallbackBlocks(attachment.blocks)), { preserveWhitespace: true });
2653
- pushUniqueText(parts, resolveSlackBlocksFallbackText(fallbackBlocks(attachment.message_blocks)), { preserveWhitespace: true });
2576
+ pushUniqueText(parts, resolveSlackBlocksText(fallbackBlocks(attachment.blocks))?.text, { preserveWhitespace: true });
2577
+ pushUniqueText(parts, resolveSlackBlocksText(fallbackBlocks(attachment.message_blocks))?.text, { preserveWhitespace: true });
2654
2578
  }
2655
2579
  return parts.length > 0 ? parts.join("\n") : void 0;
2656
2580
  }
2657
2581
  function resolveSlackMessageText(message) {
2658
- const messageText = normalizeOptionalString(message.text) ?? resolveSlackAttachmentFallbackText(message.attachments);
2582
+ const messageText = normalizeOptionalString(message.text) ?? resolveSlackAttachmentFallbackText(message.attachments) ?? (message.attachments?.some((attachment) => attachment.is_share === true && (normalizeOptionalString(attachment.image_url) || attachment.files?.length)) ? "[Slack media attachment]" : void 0);
2659
2583
  return resolveSlackMessageText$1({
2660
2584
  ...message,
2661
2585
  text: messageText
@@ -2670,11 +2594,12 @@ async function resolveSlackThreadStarter(params) {
2670
2594
  params.threadTs
2671
2595
  ]);
2672
2596
  const cached = THREAD_STARTER_CACHE.get(cacheKey);
2673
- if (cached) {
2597
+ if (cached && !params.refresh) {
2674
2598
  const now = asDateTimestampMs(Date.now());
2675
2599
  if (now !== void 0 && cached.expiresAt > now) return cached.value;
2676
2600
  THREAD_STARTER_CACHE.delete(cacheKey);
2677
2601
  }
2602
+ if (params.refresh) THREAD_STARTER_CACHE.delete(cacheKey);
2678
2603
  try {
2679
2604
  const message = (await params.client.conversations.replies({
2680
2605
  channel: params.channelId,
@@ -2703,7 +2628,7 @@ async function resolveSlackThreadStarter(params) {
2703
2628
  }
2704
2629
  return starter;
2705
2630
  } catch (err) {
2706
- logVerbose$1(`slack thread starter fetch failed channel=${params.channelId} ts=${params.threadTs}: ${formatErrorMessage(err)}`);
2631
+ logVerbose(`slack thread starter fetch failed channel=${params.channelId} ts=${params.threadTs}: ${formatErrorMessage(err)}`);
2707
2632
  return null;
2708
2633
  }
2709
2634
  }
@@ -2725,25 +2650,36 @@ async function resolveSlackThreadHistory(params) {
2725
2650
  try {
2726
2651
  do {
2727
2652
  pagesFetched += 1;
2653
+ params.assertCurrent?.();
2728
2654
  const response = await params.client.conversations.replies({
2729
2655
  channel: params.channelId,
2730
2656
  ts: params.threadTs,
2731
2657
  limit: fetchLimit,
2732
- inclusive: true,
2658
+ inclusive: false,
2659
+ ...params.currentMessageTs ? { latest: params.currentMessageTs } : {},
2660
+ ...params.oldest ? { oldest: params.oldest } : {},
2733
2661
  ...cursor ? { cursor } : {}
2734
2662
  });
2663
+ params.assertCurrent?.();
2735
2664
  for (const msg of response.messages ?? []) {
2665
+ const timestamp = resolveSlackTimestampMs(msg.ts);
2736
2666
  const text = resolveSlackMessageText(msg);
2737
2667
  if (!text && !msg.files?.length) continue;
2738
- if (params.currentMessageTs && msg.ts === params.currentMessageTs) continue;
2668
+ if (!msg.ts || msg.ts === params.currentMessageTs || params.currentMessageTs && timestamp !== void 0 && Number(msg.ts) >= Number(params.currentMessageTs) || params.oldest && (timestamp === void 0 || Number(msg.ts) <= Number(params.oldest)) || params.excludedMessageIds?.has(msg.ts)) continue;
2669
+ if (retained.some(([entry]) => entry.ts === msg.ts)) continue;
2739
2670
  retained.push([msg, text]);
2740
2671
  }
2741
2672
  if (retained.length > maxMessages) retained.splice(0, retained.length - maxMessages);
2742
2673
  const next = response.response_metadata?.next_cursor;
2743
2674
  cursor = typeof next === "string" && next.trim().length > 0 ? next.trim() : void 0;
2675
+ if (response.has_more && !cursor) {
2676
+ params.onOmission?.("Slack returned an incomplete thread page without a cursor");
2677
+ return [];
2678
+ }
2744
2679
  } while (cursor && pagesFetched < SLACK_THREAD_HISTORY_MAX_PAGES);
2745
2680
  if (cursor) {
2746
- logVerbose$1(`slack thread history capped channel=${params.channelId} ts=${params.threadTs} pages=${SLACK_THREAD_HISTORY_MAX_PAGES}`);
2681
+ logVerbose(`slack thread history capped channel=${params.channelId} ts=${params.threadTs} pages=${SLACK_THREAD_HISTORY_MAX_PAGES}`);
2682
+ params.onOmission?.("Slack thread exceeds the three-page automatic history budget");
2747
2683
  return [];
2748
2684
  }
2749
2685
  return retained.map(([message, text]) => ({
@@ -2751,13 +2687,55 @@ async function resolveSlackThreadHistory(params) {
2751
2687
  userId: message.user,
2752
2688
  botId: message.bot_id,
2753
2689
  ts: message.ts,
2754
- files: message.files
2690
+ files: message.files,
2691
+ attachments: message.attachments
2755
2692
  }));
2756
2693
  } catch (err) {
2757
- logVerbose$1(`slack thread history fetch failed channel=${params.channelId} ts=${params.threadTs}: ${formatErrorMessage(err)}`);
2694
+ params.assertCurrent?.();
2695
+ params.onOmission?.(formatErrorMessage(err));
2696
+ logVerbose(`slack thread history fetch failed channel=${params.channelId} ts=${params.threadTs}: ${formatErrorMessage(err)}`);
2758
2697
  return [];
2759
2698
  }
2760
2699
  }
2700
+ /** Slack channel history is newest first, unlike chronological thread replies. */
2701
+ async function resolveSlackChannelHistory(params) {
2702
+ if (!Number.isFinite(params.limit) || params.limit <= 0) return [];
2703
+ const messages = [];
2704
+ const seen = /* @__PURE__ */ new Set();
2705
+ let cursor;
2706
+ let scanned = 0;
2707
+ const maxPages = Math.ceil(params.limit / 200);
2708
+ for (let page = 0; page < maxPages; page += 1) {
2709
+ params.assertCurrent?.();
2710
+ const response = await params.client.conversations.history({
2711
+ channel: params.channelId,
2712
+ latest: params.currentMessageTs,
2713
+ ...params.oldest ? { oldest: params.oldest } : {},
2714
+ inclusive: false,
2715
+ limit: Math.min(200, params.limit - scanned),
2716
+ ...cursor ? { cursor } : {}
2717
+ });
2718
+ params.assertCurrent?.();
2719
+ for (const message of response.messages ?? []) {
2720
+ scanned += 1;
2721
+ if (!message.ts || Number(message.ts) >= Number(params.currentMessageTs) || params.oldest && Number(message.ts) <= Number(params.oldest) || params.excludedMessageIds.has(message.ts) || seen.has(message.ts)) continue;
2722
+ seen.add(message.ts);
2723
+ const text = resolveSlackMessageText(message);
2724
+ if (!text && !message.files?.length) continue;
2725
+ messages.push({
2726
+ text: text ?? formatSlackFilePlaceholder(message.files),
2727
+ userId: message.user,
2728
+ botId: message.bot_id,
2729
+ ts: message.ts,
2730
+ files: message.files,
2731
+ attachments: message.attachments
2732
+ });
2733
+ }
2734
+ cursor = response.response_metadata?.next_cursor?.trim() || void 0;
2735
+ if (!cursor || scanned >= params.limit) break;
2736
+ }
2737
+ return messages.slice(0, params.limit).toReversed();
2738
+ }
2761
2739
  //#endregion
2762
2740
  //#region extensions/slack/src/monitor/message-handler/preflight-audio.ts
2763
2741
  function isSlackAudioFile(file) {
@@ -2798,9 +2776,7 @@ async function resolveSlackPreflightAudioTranscript(params) {
2798
2776
  mediaIndex
2799
2777
  } : null;
2800
2778
  }
2801
- async function sendSlackPreflightAudioTranscriptEcho(params) {
2802
- await slackPreflightAudio.send(params);
2803
- }
2779
+ const sendSlackPreflightAudioTranscriptEcho = slackPreflightAudio.send.bind(slackPreflightAudio);
2804
2780
  async function discardSlackPreflightMedia(media) {
2805
2781
  await Promise.allSettled((media ?? []).map((entry) => fs.rm(entry.path, { force: true })));
2806
2782
  }
@@ -2810,7 +2786,7 @@ const SLACK_MENTION_RESOLUTION_CONCURRENCY = 4;
2810
2786
  const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
2811
2787
  const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
2812
2788
  const MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS = 2e3;
2813
- const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-BvGUGk3w.mjs").then((n) => n._));
2789
+ const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-Df0CzcDl.mjs").then((n) => n._));
2814
2790
  function formatSlackUnavailableMedia(params) {
2815
2791
  let fileReferences = params.files?.map((file) => `${formatSlackFileReference(file)} unavailable (${file.reason})`).join(", ");
2816
2792
  if (fileReferences && fileReferences.length > MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS) fileReferences = `${truncateUtf16Safe(fileReferences, MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS)}; … (file references truncated)`;
@@ -2829,18 +2805,15 @@ function formatSlackUnavailableMedia(params) {
2829
2805
  }
2830
2806
  function collectUniqueSlackMentionIds$1(texts) {
2831
2807
  const seen = /* @__PURE__ */ new Set();
2832
- const mentionIds = [];
2833
2808
  for (const text of texts) {
2834
2809
  if (!text) continue;
2835
2810
  SLACK_USER_MENTION_RE$1.lastIndex = 0;
2836
2811
  for (const match of text.matchAll(SLACK_USER_MENTION_RE$1)) {
2837
2812
  const userId = match[1];
2838
- if (!userId || seen.has(userId)) continue;
2839
- seen.add(userId);
2840
- mentionIds.push(userId);
2813
+ if (userId) seen.add(userId);
2841
2814
  }
2842
2815
  }
2843
- return mentionIds;
2816
+ return [...seen];
2844
2817
  }
2845
2818
  function renderSlackUserMentions(text, renderedMentions) {
2846
2819
  if (!text || renderedMentions.size === 0) return text;
@@ -2851,8 +2824,7 @@ function renderSlackUserMentions(text, renderedMentions) {
2851
2824
  }
2852
2825
  function filterInheritedParentFiles(params) {
2853
2826
  const { files, isThreadReply, threadStarter } = params;
2854
- if (!isThreadReply || !files?.length) return files;
2855
- if (!threadStarter?.files?.length) return files;
2827
+ if (!isThreadReply || !files?.length || !threadStarter?.files?.length) return files;
2856
2828
  const starterFileIds = new Set(threadStarter.files.map((file) => file.id));
2857
2829
  const filtered = files.filter((file) => !file.id || !starterFileIds.has(file.id));
2858
2830
  if (filtered.length < files.length) logVerbose(`slack: filtered ${files.length - filtered.length} inherited parent file(s) from thread reply`);
@@ -2873,19 +2845,13 @@ async function resolveSlackMessageContent(params) {
2873
2845
  readIdleTimeoutMs: params.mediaReadIdleTimeoutMs,
2874
2846
  totalTimeoutMs: params.mediaTotalTimeoutMs,
2875
2847
  abortSignal: params.abortSignal,
2848
+ assertCurrent: params.assertCurrent,
2876
2849
  preloadedMedia: params.preloadedMedia
2877
2850
  })) : null;
2878
2851
  const effectiveDirectMedia = attachmentContent?.media.length ? attachmentContent.media : null;
2879
2852
  const mediaPlaceholder = effectiveDirectMedia?.map((item) => item.placeholder).join(" ");
2880
2853
  let botAttachmentText;
2881
- if (params.isBotMessage && !attachmentContent?.text) {
2882
- const botAttachmentTextParts = [];
2883
- for (const attachment of params.message.attachments ?? []) {
2884
- const text = normalizeOptionalString(attachment.text) ?? normalizeOptionalString(attachment.fallback);
2885
- if (text) botAttachmentTextParts.push(text);
2886
- }
2887
- botAttachmentText = botAttachmentTextParts.length > 0 ? botAttachmentTextParts.join("\n") : void 0;
2888
- }
2854
+ if (params.isBotMessage && !attachmentContent?.text) botAttachmentText = (params.message.attachments ?? []).map((attachment) => normalizeOptionalString(attachment.text) ?? normalizeOptionalString(attachment.fallback)).filter(Boolean).join("\n") || void 0;
2889
2855
  const textParts = [
2890
2856
  resolveSlackMessageText$1(params.message),
2891
2857
  attachmentContent?.text,
@@ -2914,17 +2880,9 @@ async function resolveSlackMessageContent(params) {
2914
2880
  renderedMentions.set(result.userId, result.rendered);
2915
2881
  }
2916
2882
  }
2917
- const renderedMessageText = renderSlackUserMentions(textParts[0], renderedMentions);
2918
- const renderedAttachmentText = renderSlackUserMentions(textParts[1], renderedMentions);
2919
- const renderedBotAttachmentText = renderSlackUserMentions(textParts[2], renderedMentions);
2920
2883
  const commandSourceText = renderSlackUserMentions(normalizeOptionalString(params.message.text), renderedMentions) ?? "";
2921
2884
  const rawBody = formatSlackUnavailableMedia({
2922
- body: [
2923
- renderedMessageText,
2924
- renderedAttachmentText,
2925
- renderedBotAttachmentText,
2926
- mediaPlaceholder
2927
- ].filter(Boolean).join("\n") || "",
2885
+ body: [...textParts.map((text) => renderSlackUserMentions(text, renderedMentions)), mediaPlaceholder].filter(Boolean).join("\n"),
2928
2886
  files: attachmentContent?.files,
2929
2887
  unavailableMediaCount: attachmentContent?.unavailableMediaCount ?? 0
2930
2888
  });
@@ -2939,8 +2897,8 @@ async function resolveSlackMessageContent(params) {
2939
2897
  //#endregion
2940
2898
  //#region extensions/slack/src/monitor/message-handler/prepare-dm-history.ts
2941
2899
  function resolveSlackDmHistoryLimit(params) {
2942
- const override = params.userId && params.account.config.dms?.[params.userId]?.historyLimit !== void 0 ? params.account.config.dms[params.userId]?.historyLimit : void 0;
2943
- return Math.max(0, override ?? params.defaultLimit);
2900
+ const override = params.userId ? params.account.config.dms?.[params.userId]?.historyLimit : void 0;
2901
+ return resolvePromptHistoryLimit(override ?? params.defaultLimit, 0);
2944
2902
  }
2945
2903
  async function resolveSlackDmHistoryContext(params) {
2946
2904
  const maxMessages = Math.max(0, Math.floor(params.limit));
@@ -3016,56 +2974,177 @@ function isSlackThreadAuthorCurrentBot(params) {
3016
2974
  if (identity.botId && author.botId && author.botId === identity.botId) return true;
3017
2975
  return false;
3018
2976
  }
3019
- function resolveSlackThreadHistoryFilterPolicy(params) {
3020
- if (params.retainCurrentBotHistory) return { currentBot: "all" };
3021
- if (!params.includeBotStarterAsRootContext || !params.starterTs) return { currentBot: "omit" };
3022
- return {
3023
- currentBot: "root-only",
3024
- rootTs: params.starterTs
2977
+ //#endregion
2978
+ //#region extensions/slack/src/monitor/message-handler/prepare-room-history.ts
2979
+ const SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS = 4;
2980
+ const SLACK_HISTORY_MEDIA_MAX_BYTES = 10485760;
2981
+ const SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS = 1e3;
2982
+ const SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS = 3e3;
2983
+ async function resolveSlackRoomHistory(params) {
2984
+ if (params.ctx.historyLimit <= 0 || !params.message.ts) return [];
2985
+ const warnOmission = (reason) => {
2986
+ params.ctx.logger.warn({
2987
+ accountId: params.ctx.accountId,
2988
+ teamId: params.eventScope?.teamId ?? params.ctx.teamId,
2989
+ channelId: params.message.channel,
2990
+ threadTs: params.threadTs,
2991
+ reason
2992
+ }, "Slack automatic history omitted");
3025
2993
  };
3026
- }
3027
- function applySlackThreadHistoryFilterPolicy(params) {
3028
- const kept = [];
3029
- let omittedCurrentBot = 0;
3030
- for (const entry of params.history) {
3031
- if (!isSlackThreadAuthorCurrentBot({
3032
- identity: params.identity,
3033
- author: entry
3034
- })) {
3035
- kept.push(entry);
3036
- continue;
2994
+ try {
2995
+ params.assertCurrent();
2996
+ const request = {
2997
+ channelId: params.message.channel,
2998
+ client: params.eventScope?.client ?? params.ctx.app.client,
2999
+ currentMessageTs: params.message.ts,
3000
+ oldest: params.oldest,
3001
+ excludedMessageIds: params.excludedMessageIds,
3002
+ limit: Math.max(DEFAULT_GROUP_HISTORY_LIMIT, params.ctx.historyLimit),
3003
+ assertCurrent: params.assertCurrent
3004
+ };
3005
+ const messages = params.threadTs ? await resolveSlackThreadHistory({
3006
+ ...request,
3007
+ threadTs: params.threadTs,
3008
+ onOmission: warnOmission
3009
+ }) : await resolveSlackChannelHistory(request);
3010
+ params.assertCurrent();
3011
+ const permitted = [];
3012
+ for (const message of messages) {
3013
+ if (isSlackThreadAuthorCurrentBot({
3014
+ identity: params.ctx,
3015
+ author: message
3016
+ })) continue;
3017
+ const user = message.userId ? await params.ctx.resolveUserName(message.userId, params.eventScope) : void 0;
3018
+ params.assertCurrent();
3019
+ const senderAllowed = resolveSlackUserAllowed({
3020
+ allowList: params.allowFromLower,
3021
+ teamId: params.eventScope?.teamId ?? params.ctx.teamId,
3022
+ userId: message.userId ?? message.botId,
3023
+ userName: user?.name,
3024
+ allowNameMatching: params.ctx.allowNameMatching
3025
+ });
3026
+ if (!shouldIncludeSupplementalContext({
3027
+ mode: params.contextVisibilityMode,
3028
+ kind: "history",
3029
+ senderAllowed
3030
+ })) continue;
3031
+ permitted.push({
3032
+ message,
3033
+ sender: user?.name ?? message.userId ?? `Bot (${message.botId ?? "unknown"})`
3034
+ });
3037
3035
  }
3038
- if (params.policy.currentBot === "all" || params.policy.currentBot === "root-only" && entry.ts === params.policy.rootTs) kept.push(entry);
3039
- else omittedCurrentBot += 1;
3036
+ const entries = [];
3037
+ let mediaRemaining = SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS;
3038
+ for (const { message, sender } of permitted.slice(-params.ctx.historyLimit)) {
3039
+ const { media, attempted } = mediaRemaining > 0 && (message.files?.length || message.attachments?.length) ? await resolveSlackHistoryMedia({
3040
+ ctx: params.ctx,
3041
+ eventScope: params.eventScope,
3042
+ maxAttachments: mediaRemaining,
3043
+ assertCurrent: params.assertCurrent,
3044
+ abortSignal: params.abortSignal,
3045
+ message: {
3046
+ type: "message",
3047
+ channel: params.message.channel,
3048
+ ts: message.ts,
3049
+ user: message.userId,
3050
+ bot_id: message.botId,
3051
+ text: message.text,
3052
+ files: message.files,
3053
+ attachments: message.attachments
3054
+ }
3055
+ }) : {
3056
+ media: [],
3057
+ attempted: 0
3058
+ };
3059
+ params.assertCurrent();
3060
+ mediaRemaining -= attempted;
3061
+ entries.push({
3062
+ sender,
3063
+ body: message.text,
3064
+ timestamp: resolveSlackTimestampMs(message.ts),
3065
+ messageId: message.ts,
3066
+ ...media.length > 0 ? { media } : {}
3067
+ });
3068
+ }
3069
+ return entries;
3070
+ } catch (error) {
3071
+ warnOmission(formatErrorMessage(error));
3072
+ return [];
3040
3073
  }
3041
- return {
3042
- kept,
3043
- omittedCurrentBot
3044
- };
3045
3074
  }
3046
- function shouldIncludeBotThreadStarterContext(params) {
3047
- if (!params.hasStarterText) return false;
3048
- return params.starterIsCurrentBot && params.isNewThreadSession;
3075
+ function isSlackImageFileCandidate(file) {
3076
+ if ((file.mimetype?.split(";")[0]?.trim().toLowerCase())?.startsWith("image/")) return true;
3077
+ return Boolean(mimeTypeFromFilePath(file.name)?.startsWith("image/"));
3049
3078
  }
3050
- function ensureSlackThreadHistoryHasBotRoot(params) {
3051
- if (!params.includeBotStarterAsRootContext || !params.threadStarter?.text) return params.history;
3052
- if (params.history.some((entry) => entry.ts === params.threadStarter?.ts)) return params.history;
3053
- return [params.threadStarter, ...params.history];
3079
+ function sliceSlackImageFileCandidates(files, limit) {
3080
+ if (limit <= 0 || !files?.length) return [];
3081
+ return files.filter(isSlackImageFileCandidate).slice(0, limit);
3054
3082
  }
3055
- function formatSlackBotStarterThreadLabel(params) {
3056
- const base = `Slack thread ${params.roomLabel}`;
3057
- if (!params.starterText) return base;
3058
- const snippet = formatSlackThreadLabelSnippet(params.starterText).trim();
3059
- if (!snippet) return base;
3060
- return `${base} (assistant root): ${snippet}`;
3083
+ function sliceSlackHistoryAttachmentCandidates(attachments, limit) {
3084
+ if (limit <= 0 || !attachments?.length) return [];
3085
+ const out = [];
3086
+ let remaining = limit;
3087
+ for (const attachment of attachments) {
3088
+ if (attachment.is_share !== true) continue;
3089
+ const hasImageUrl = Boolean(normalizeOptionalString(attachment.image_url));
3090
+ const files = sliceSlackImageFileCandidates(attachment.files, remaining - (hasImageUrl ? 1 : 0));
3091
+ if (!hasImageUrl && files.length === 0) continue;
3092
+ out.push({
3093
+ ...attachment,
3094
+ files
3095
+ });
3096
+ remaining -= (hasImageUrl ? 1 : 0) + files.length;
3097
+ if (remaining <= 0) break;
3098
+ }
3099
+ return out;
3061
3100
  }
3062
- function formatSlackThreadLabelSnippet(text) {
3063
- return truncateUtf16Safe(text.replace(/\s+/g, " "), 80);
3101
+ function buildSlackHistoryMediaCandidateMessage(message, maxAttachments) {
3102
+ const files = sliceSlackImageFileCandidates(message.files, maxAttachments);
3103
+ const attachments = sliceSlackHistoryAttachmentCandidates(message.attachments, Math.max(0, maxAttachments - files.length));
3104
+ if (files.length === 0 && attachments.length === 0) return null;
3105
+ return {
3106
+ message: {
3107
+ ...message,
3108
+ files,
3109
+ attachments
3110
+ },
3111
+ attempted: files.length + attachments.reduce((count, attachment) => count + (normalizeOptionalString(attachment.image_url) ? 1 : 0) + (attachment.files?.length ?? 0), 0)
3112
+ };
3113
+ }
3114
+ async function resolveSlackHistoryMedia(params) {
3115
+ const candidate = buildSlackHistoryMediaCandidateMessage(params.message, params.maxAttachments);
3116
+ if (!candidate) return {
3117
+ media: [],
3118
+ attempted: 0
3119
+ };
3120
+ const content = await resolveSlackMessageContent({
3121
+ message: candidate.message,
3122
+ isThreadReply: false,
3123
+ threadStarter: null,
3124
+ isBotMessage: Boolean(params.message.bot_id),
3125
+ client: params.eventScope?.client ?? params.ctx.app.client,
3126
+ botToken: params.ctx.botToken,
3127
+ mediaMaxBytes: Math.min(params.ctx.mediaMaxBytes, SLACK_HISTORY_MEDIA_MAX_BYTES),
3128
+ mediaReadIdleTimeoutMs: SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS,
3129
+ mediaTotalTimeoutMs: SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS,
3130
+ assertCurrent: params.assertCurrent,
3131
+ abortSignal: params.abortSignal
3132
+ });
3133
+ return {
3134
+ media: await toInboundMediaFactsWithMetadata(content?.effectiveDirectMedia, {
3135
+ kind: "image",
3136
+ messageId: params.message.ts
3137
+ }),
3138
+ attempted: candidate.attempted
3139
+ };
3064
3140
  }
3065
3141
  //#endregion
3066
3142
  //#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
3067
- const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-BvGUGk3w.mjs").then((n) => n._));
3143
+ const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-Df0CzcDl.mjs").then((n) => n._));
3068
3144
  const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
3145
+ function formatSlackThreadLabelSnippet(text) {
3146
+ return truncateUtf16Safe(text.replace(/\s+/g, " "), 80);
3147
+ }
3069
3148
  function resolveSlackThreadSessionFreshness(params) {
3070
3149
  return params.ctx.channelRuntime?.session?.resolveEntryResetFreshness?.({
3071
3150
  agentId: params.agentId,
@@ -3079,45 +3158,16 @@ function resolveSlackThreadSessionFreshness(params) {
3079
3158
  })
3080
3159
  });
3081
3160
  }
3082
- function isSlackThreadContextSenderAllowed(params) {
3083
- return resolveInboundSupplementalSenderAllowed({
3084
- isGroup: true,
3085
- groupPolicy: params.allowFromLower.length === 0 ? "open" : "allowlist",
3086
- allowFrom: params.allowFromLower,
3087
- isSenderAllowed: (allowFrom) => {
3088
- if (params.botId) return true;
3089
- if (!params.userId) return false;
3090
- return resolveSlackAllowListMatch({
3091
- allowList: allowFrom,
3092
- id: params.userId,
3093
- name: params.userName,
3094
- allowNameMatching: params.allowNameMatching
3095
- }).allowed;
3096
- }
3097
- });
3098
- }
3099
3161
  async function resolveSlackThreadUserMap(params) {
3100
- const uniqueUserIds = [];
3101
- const seen = /* @__PURE__ */ new Set();
3102
- for (const item of params.messages) {
3103
- if (!item.userId || seen.has(item.userId)) continue;
3104
- seen.add(item.userId);
3105
- uniqueUserIds.push(item.userId);
3106
- }
3107
- const userMap = /* @__PURE__ */ new Map();
3108
- if (uniqueUserIds.length === 0) return userMap;
3162
+ const uniqueUserIds = [...new Set(params.messages.map((item) => item.userId).filter((id) => Boolean(id)))];
3109
3163
  const { results } = await runTasksWithConcurrency({
3110
3164
  tasks: uniqueUserIds.map((id) => async () => {
3111
3165
  const user = await params.ctx.resolveUserName(id, params.eventScope);
3112
- return user ? {
3113
- id,
3114
- user
3115
- } : null;
3166
+ return user ? [id, user] : null;
3116
3167
  }),
3117
3168
  limit: SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY
3118
3169
  });
3119
- for (const result of results) if (result) userMap.set(result.id, result.user);
3120
- return userMap;
3170
+ return new Map(results.flatMap((result) => result ? [result] : []));
3121
3171
  }
3122
3172
  async function resolveSlackThreadContextData(params) {
3123
3173
  const botIdentity = {
@@ -3155,16 +3205,14 @@ async function resolveSlackThreadContextData(params) {
3155
3205
  };
3156
3206
  const starter = params.threadStarter;
3157
3207
  const starterSenderName = params.allowNameMatching && params.allowFromLower.length > 0 && starter?.userId ? (await params.ctx.resolveUserName(starter.userId, params.eventScope))?.name : void 0;
3158
- const starterIsCurrentBot = Boolean(starter && isCurrentBotAuthor({
3159
- userId: starter.userId,
3160
- botId: starter.botId
3161
- }));
3162
- const starterAllowed = !starter || !starterIsCurrentBot && isSlackThreadContextSenderAllowed({
3163
- allowFromLower: params.allowFromLower,
3208
+ params.assertHistoryCurrent?.();
3209
+ const starterIsCurrentBot = Boolean(starter && isCurrentBotAuthor(starter));
3210
+ const starterAllowed = !starter || !starterIsCurrentBot && resolveSlackUserAllowed({
3211
+ allowList: params.allowFromLower,
3212
+ teamId: params.eventScope?.teamId ?? params.ctx.teamId,
3164
3213
  allowNameMatching: params.allowNameMatching,
3165
- userId: starter.userId,
3166
- userName: starterSenderName,
3167
- botId: starter.botId
3214
+ userId: starter.userId ?? starter.botId,
3215
+ userName: starterSenderName
3168
3216
  });
3169
3217
  const includeStarterContext = !starter || !starterIsCurrentBot && shouldIncludeSupplementalContext({
3170
3218
  mode: params.contextVisibilityMode,
@@ -3177,12 +3225,16 @@ async function resolveSlackThreadContextData(params) {
3177
3225
  threadLabel = `Slack thread ${params.roomLabel}${snippet ? `: ${snippet}` : ""}`;
3178
3226
  if (shouldSeedInitialThreadContext && !params.effectiveDirectMedia && starter.files && starter.files.length > 0) {
3179
3227
  const { resolveSlackAttachmentContent } = await loadSlackMediaModule();
3228
+ params.assertHistoryCurrent?.();
3180
3229
  const attachmentContent = await resolveSlackAttachmentContent({
3181
3230
  files: starter.files,
3182
3231
  client: params.eventScope?.client ?? params.ctx.app.client,
3183
3232
  token: params.ctx.botToken,
3184
- maxBytes: params.ctx.mediaMaxBytes
3233
+ maxBytes: params.ctx.mediaMaxBytes,
3234
+ assertCurrent: params.assertHistoryCurrent,
3235
+ abortSignal: params.abortSignal
3185
3236
  });
3237
+ params.assertHistoryCurrent?.();
3186
3238
  threadStarterMedia = attachmentContent?.media.length ? attachmentContent.media : null;
3187
3239
  if (attachmentContent) threadStarterBody = formatSlackUnavailableMedia({
3188
3240
  body: threadStarterBody,
@@ -3196,58 +3248,64 @@ async function resolveSlackThreadContextData(params) {
3196
3248
  }
3197
3249
  }
3198
3250
  } else threadLabel = `Slack thread ${params.roomLabel}`;
3199
- const includeBotStarterAsRootContext = shouldIncludeBotThreadStarterContext({
3200
- starterIsCurrentBot,
3201
- isNewThreadSession: shouldSeedInitialThreadContext,
3202
- hasStarterText: Boolean(starter?.text)
3203
- });
3251
+ const includeBotStarterAsRootContext = Boolean(starter?.text && starterIsCurrentBot && shouldSeedInitialThreadContext);
3204
3252
  if (starter?.text && starterIsCurrentBot && !includeBotStarterAsRootContext) logVerbose("slack: omitted current-bot thread starter from context");
3205
3253
  else if (starter?.text && !includeStarterContext && !starterIsCurrentBot) logVerbose(`slack: omitted thread starter from context (mode=${params.contextVisibilityMode}, sender_allowed=${starterAllowed ? "yes" : "no"})`);
3206
3254
  else if (includeBotStarterAsRootContext) {
3207
- threadLabel = formatSlackBotStarterThreadLabel({
3208
- roomLabel: params.roomLabel,
3209
- starterText: starter?.text
3210
- });
3255
+ const snippet = formatSlackThreadLabelSnippet(starter?.text ?? "").trim();
3256
+ threadLabel = `Slack thread ${params.roomLabel}${snippet ? ` (assistant root): ${snippet}` : ""}`;
3211
3257
  logVerbose("slack: retained current-bot thread starter as assistant root context");
3212
3258
  }
3213
- const threadInitialHistoryLimit = params.account.config?.thread?.initialHistoryLimit ?? 20;
3259
+ const threadInitialHistoryLimit = Math.min(params.account.config?.thread?.initialHistoryLimit ?? 20, params.historyLimit ?? Number.POSITIVE_INFINITY);
3214
3260
  if (threadInitialHistoryLimit > 0 && shouldLoadInitialThreadHistory) {
3215
3261
  const currentBotRootTs = starter?.ts ?? params.threadTs;
3262
+ let historyOmitted = false;
3216
3263
  const threadHistory = await resolveSlackThreadHistory({
3217
3264
  channelId: params.message.channel,
3218
3265
  threadTs: params.threadTs,
3219
3266
  client: params.eventScope?.client ?? params.ctx.app.client,
3220
3267
  currentMessageTs: params.message.ts,
3221
- limit: threadInitialHistoryLimit
3268
+ limit: threadInitialHistoryLimit,
3269
+ excludedMessageIds: params.excludedMessageIds,
3270
+ assertCurrent: params.assertHistoryCurrent,
3271
+ onOmission: (reason) => {
3272
+ historyOmitted = true;
3273
+ params.ctx.logger.warn({
3274
+ channelId: params.message.channel,
3275
+ threadTs: params.threadTs,
3276
+ teamId: params.eventScope?.teamId ?? params.ctx.teamId,
3277
+ accountId: params.account.accountId,
3278
+ reason
3279
+ }, "Slack automatic thread history omitted");
3280
+ }
3222
3281
  });
3282
+ if (historyOmitted) return {
3283
+ threadStarterBody,
3284
+ threadHistoryBody,
3285
+ shouldSeedInitialThreadContext,
3286
+ threadLabel,
3287
+ threadStarterMedia
3288
+ };
3223
3289
  const enrichedStarter = starter && threadStarterBody && threadStarterBody !== starter.text ? {
3224
3290
  ...starter,
3225
3291
  text: threadStarterBody,
3226
3292
  ts: currentBotRootTs
3227
3293
  } : null;
3228
- const threadHistoryWithBotRoot = ensureSlackThreadHistoryHasBotRoot({
3229
- history: enrichedStarter && !threadHistory.some((entry) => entry.ts === currentBotRootTs) ? [enrichedStarter, ...threadHistory.length >= threadInitialHistoryLimit ? threadHistory.slice(1) : threadHistory] : threadHistory,
3230
- includeBotStarterAsRootContext,
3231
- threadStarter: starter ? {
3232
- ...starter,
3233
- ts: currentBotRootTs
3234
- } : null
3235
- });
3294
+ const threadHistoryWithEnrichedRoot = enrichedStarter && !threadHistory.some((entry) => entry.ts === currentBotRootTs) ? [enrichedStarter, ...threadHistory.length >= threadInitialHistoryLimit ? threadHistory.slice(1) : threadHistory] : threadHistory;
3295
+ const threadHistoryWithBotRoot = includeBotStarterAsRootContext && starter && !threadHistoryWithEnrichedRoot.some((entry) => entry.ts === currentBotRootTs) ? [{
3296
+ ...starter,
3297
+ ts: currentBotRootTs
3298
+ }, ...threadHistoryWithEnrichedRoot] : threadHistoryWithEnrichedRoot;
3236
3299
  if (threadHistoryWithBotRoot.length > 0) {
3237
- const { kept: threadHistoryWithoutCurrentBot, omittedCurrentBot: omittedCurrentBotHistoryCount } = applySlackThreadHistoryFilterPolicy({
3238
- history: threadHistoryWithBotRoot,
3239
- policy: resolveSlackThreadHistoryFilterPolicy({
3240
- includeBotStarterAsRootContext,
3241
- starterTs: currentBotRootTs,
3242
- retainCurrentBotHistory: params.isGroupDm && (isMissingThreadSession || isOutboundOnlyThreadSession)
3243
- }),
3244
- identity: botIdentity
3245
- });
3300
+ const retainCurrentBotHistory = params.isGroupDm && (isMissingThreadSession || isOutboundOnlyThreadSession);
3301
+ const threadHistoryWithoutCurrentBot = threadHistoryWithBotRoot.filter((entry) => !isCurrentBotAuthor(entry) || retainCurrentBotHistory || includeBotStarterAsRootContext && entry.ts === currentBotRootTs);
3302
+ const omittedCurrentBotHistoryCount = threadHistoryWithBotRoot.length - threadHistoryWithoutCurrentBot.length;
3246
3303
  const userMapForFilter = params.contextVisibilityMode !== "all" && params.allowNameMatching && params.allowFromLower.length > 0 ? await resolveSlackThreadUserMap({
3247
3304
  ctx: params.ctx,
3248
3305
  messages: threadHistoryWithoutCurrentBot,
3249
3306
  eventScope: params.eventScope
3250
3307
  }) : /* @__PURE__ */ new Map();
3308
+ params.assertHistoryCurrent?.();
3251
3309
  const { items: filteredThreadHistory, omitted: omittedHistoryCount } = params.contextVisibilityMode === "all" ? {
3252
3310
  items: threadHistoryWithoutCurrentBot,
3253
3311
  omitted: 0
@@ -3256,34 +3314,29 @@ async function resolveSlackThreadContextData(params) {
3256
3314
  mode: params.contextVisibilityMode,
3257
3315
  kind: "thread",
3258
3316
  isSenderAllowed: (historyMsg) => {
3259
- if (isCurrentBotAuthor({
3260
- userId: historyMsg.userId,
3261
- botId: historyMsg.botId
3262
- })) return true;
3317
+ if (isCurrentBotAuthor(historyMsg)) return true;
3263
3318
  const msgUser = historyMsg.userId ? userMapForFilter.get(historyMsg.userId) : null;
3264
- return isSlackThreadContextSenderAllowed({
3265
- allowFromLower: params.allowFromLower,
3319
+ return resolveSlackUserAllowed({
3320
+ allowList: params.allowFromLower,
3321
+ teamId: params.eventScope?.teamId ?? params.ctx.teamId,
3266
3322
  allowNameMatching: params.allowNameMatching,
3267
- userId: historyMsg.userId,
3268
- userName: msgUser?.name,
3269
- botId: historyMsg.botId
3323
+ userId: historyMsg.userId ?? historyMsg.botId,
3324
+ userName: msgUser?.name
3270
3325
  });
3271
3326
  }
3272
3327
  });
3273
3328
  const userMap = await resolveSlackThreadUserMap({
3274
3329
  ctx: params.ctx,
3275
- messages: filteredThreadHistory
3330
+ messages: filteredThreadHistory,
3331
+ eventScope: params.eventScope
3276
3332
  });
3333
+ params.assertHistoryCurrent?.();
3277
3334
  if (omittedHistoryCount > 0 || omittedCurrentBotHistoryCount > 0) logVerbose(`slack: omitted ${omittedHistoryCount + omittedCurrentBotHistoryCount} thread message(s) from context (mode=${params.contextVisibilityMode})`);
3278
3335
  const historyParts = [];
3279
3336
  for (const historyMsg of filteredThreadHistory) {
3280
3337
  const msgUser = historyMsg.userId ? userMap.get(historyMsg.userId) : null;
3281
- const isOtherBot = Boolean(historyMsg.botId) && historyMsg.botId !== params.ctx.botId;
3282
- const isCurrentBot = isCurrentBotAuthor({
3283
- userId: historyMsg.userId,
3284
- botId: historyMsg.botId
3285
- });
3286
- const role = isCurrentBot || isOtherBot || Boolean(historyMsg.botId) ? "assistant" : "user";
3338
+ const isCurrentBot = isCurrentBotAuthor(historyMsg);
3339
+ const role = isCurrentBot || historyMsg.botId ? "assistant" : "user";
3287
3340
  const msgSenderName = isCurrentBot ? "Bot (this assistant)" : msgUser?.name ?? (historyMsg.botId ? `Bot (${historyMsg.botId})` : "Unknown");
3288
3341
  const msgWithId = `${historyMsg.ts === currentBotRootTs && threadStarterBody ? threadStarterBody : historyMsg.text}\n[slack message id: ${historyMsg.ts ?? "unknown"} channel: ${params.message.channel}]`;
3289
3342
  historyParts.push(formatInboundEnvelope({
@@ -3310,12 +3363,64 @@ async function resolveSlackThreadContextData(params) {
3310
3363
  };
3311
3364
  }
3312
3365
  //#endregion
3366
+ //#region src/channels/mention-gating.ts
3367
+ function resolveBotThreadMentionPolicy(params) {
3368
+ if (!params.isBotOwnedThread || params.requireMentionInBotThreads === void 0) return {
3369
+ requireMention: params.requireMention,
3370
+ implicitMentionKinds: params.implicitMentionKinds
3371
+ };
3372
+ return {
3373
+ requireMention: params.requireMentionInBotThreads,
3374
+ implicitMentionKinds: params.requireMentionInBotThreads ? params.implicitMentionKinds?.filter((kind) => kind === "native") : params.implicitMentionKinds
3375
+ };
3376
+ }
3377
+ //#endregion
3378
+ //#region \0openclaw:bundled-sdk:openclaw/plugin-sdk/channel-mention-gating
3379
+ var channel_mention_gating_exports = /* @__PURE__ */ __exportAll({ resolveBotThreadMentionPolicy: () => resolveBotThreadMentionPolicy });
3380
+ import * as import_openclaw_plugin_sdk_channel_mention_gating from "openclaw/plugin-sdk/channel-mention-gating";
3381
+ __reExport(channel_mention_gating_exports, import_openclaw_plugin_sdk_channel_mention_gating);
3382
+ //#endregion
3383
+ //#region extensions/slack/src/monitor/message-handler/prepare-thread-mentions.ts
3384
+ async function resolveSlackThreadMentionPolicy(params) {
3385
+ const { ctx, account, message, conversation, thread } = params;
3386
+ const { isRoom, isDirectMessage, channelConfig } = conversation;
3387
+ let implicitMentionKinds = [];
3388
+ if (!isDirectMessage && message.thread_ts && !params.wasMentioned) {
3389
+ const replyToBotKinds = (0, channel_mention_gating_exports.implicitMentionKindWhen)("reply_to_bot", Boolean(ctx.botUserId && message.parent_user_id === ctx.botUserId));
3390
+ implicitMentionKinds = replyToBotKinds.length > 0 ? replyToBotKinds : (0, channel_mention_gating_exports.implicitMentionKindWhen)("bot_thread_participant", await hasSlackThreadParticipationWithPersistence({
3391
+ accountId: account.accountId,
3392
+ channelId: message.channel,
3393
+ threadTs: message.thread_ts,
3394
+ teamId: params.teamId
3395
+ }));
3396
+ }
3397
+ const requireMentionInBotThreads = channelConfig?.requireMentionInBotThreads ?? account.config.requireMentionInBotThreads;
3398
+ let isBotOwnedThread = false;
3399
+ if (isRoom && thread.isThreadReply && requireMentionInBotThreads !== void 0) {
3400
+ const parentUserId = normalizeOptionalString(message.parent_user_id);
3401
+ if (parentUserId) isBotOwnedThread = Boolean(ctx.botUserId && parentUserId === ctx.botUserId);
3402
+ else {
3403
+ const starter = await params.getThreadStarter();
3404
+ isBotOwnedThread = Boolean(starter && starter.ts === thread.threadTs && isSlackThreadAuthorCurrentBot({
3405
+ identity: ctx,
3406
+ author: starter
3407
+ }));
3408
+ }
3409
+ }
3410
+ return resolveBotThreadMentionPolicy({
3411
+ isBotOwnedThread,
3412
+ requireMentionInBotThreads,
3413
+ requireMention: isRoom ? channelConfig?.requireMention ?? ctx.defaultRequireMention : false,
3414
+ implicitMentionKinds
3415
+ });
3416
+ }
3417
+ //#endregion
3313
3418
  //#region extensions/slack/src/monitor/message-handler/subteam-mentions.ts
3314
3419
  const SUBTEAM_MENTION_RE = /<!subteam\^([A-Z0-9]+)(?:\|[^>]*)?>/gi;
3315
3420
  const SUBTEAM_MEMBER_CACHE_TTL_MS = 3e5;
3316
3421
  const subteamMemberCache = /* @__PURE__ */ new WeakMap();
3317
3422
  function normalizeSlackId(value) {
3318
- return typeof value === "string" && value.trim() ? value.trim().toUpperCase() : void 0;
3423
+ return normalizeOptionalString(value)?.toUpperCase();
3319
3424
  }
3320
3425
  function extractSlackSubteamMentionIds(text) {
3321
3426
  if (!text) return [];
@@ -3348,7 +3453,7 @@ async function readSlackSubteamUsers(params) {
3348
3453
  params.log?.(`slack: failed to resolve user-group mention ${params.subteamId}: ${response.error ?? "unknown_error"}`);
3349
3454
  return /* @__PURE__ */ new Set();
3350
3455
  }
3351
- const users = new Set((response.users ?? []).map((userId) => normalizeSlackId(userId)).filter(Boolean));
3456
+ const users = new Set((response.users ?? []).map(normalizeSlackId).filter((userId) => userId !== void 0));
3352
3457
  const expiresAt = resolveExpiresAtMsFromDurationMs(SUBTEAM_MEMBER_CACHE_TTL_MS, { nowMs: params.now });
3353
3458
  if (expiresAt !== void 0) bySubteam.set(cacheKey, {
3354
3459
  expiresAt,
@@ -3382,10 +3487,6 @@ const SLACK_ANY_MENTION_RE = /<@[^>]+>|<!subteam\^[^>]+>/;
3382
3487
  const SLACK_USER_MENTION_RE = /<@([^>|]+)(?:\|[^>]+)?>/g;
3383
3488
  const SLACK_SUBTEAM_MENTION_RE = /<!subteam\^([^>|]+)(?:\|[^>]+)?>/g;
3384
3489
  const SLACK_SUBTEAM_MENTION_MARKER = "<!subteam^";
3385
- const SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS = 4;
3386
- const SLACK_HISTORY_MEDIA_MAX_BYTES = 10485760;
3387
- const SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS = 1e3;
3388
- const SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS = 3e3;
3389
3490
  const SLACK_CHANNEL_ACCESS_DOCS_URL = "https://docs.openclaw.ai/channels/slack#access-control-and-routing";
3390
3491
  function resolveSlackGroupSessionSubject(params) {
3391
3492
  const channelName = normalizeOptionalString(params.channelName);
@@ -3393,14 +3494,6 @@ function resolveSlackGroupSessionSubject(params) {
3393
3494
  if (channelName && workspaceName) return `${workspaceName} #${channelName}`;
3394
3495
  return `Slack Channel (Workspace ID: ${params.workspaceId}, Channel ID: ${params.channelId})`;
3395
3496
  }
3396
- function recordString(record, key) {
3397
- return normalizeOptionalString(record?.[key]);
3398
- }
3399
- function recordNullableString(record, key) {
3400
- if (!record || !(key in record)) return;
3401
- if (record[key] === null) return null;
3402
- return normalizeOptionalString(record[key]);
3403
- }
3404
3497
  function mergeSlackAssistantThreadContext(primary, fallback) {
3405
3498
  if (!primary) return fallback;
3406
3499
  if (!fallback) return primary;
@@ -3420,16 +3513,16 @@ function resolveSlackMessageAssistantThreadContext(message) {
3420
3513
  const thread = asOptionalRecord(message.assistant_thread);
3421
3514
  if (!thread) return;
3422
3515
  const context = asOptionalRecord(thread.context);
3423
- const assistantChannelId = recordString(thread, "channel_id") ?? message.channel;
3424
- const threadTs = recordString(thread, "thread_ts") ?? message.thread_ts ?? message.ts;
3516
+ const assistantChannelId = normalizeOptionalString(thread.channel_id) ?? message.channel;
3517
+ const threadTs = normalizeOptionalString(thread.thread_ts) ?? message.thread_ts ?? message.ts;
3425
3518
  if (!assistantChannelId || !threadTs) return;
3426
3519
  return {
3427
3520
  assistantChannelId,
3428
3521
  threadTs,
3429
- userId: recordString(thread, "user_id") ?? message.user,
3430
- channelId: recordString(context, "channel_id"),
3431
- teamId: recordString(context, "team_id"),
3432
- enterpriseId: recordNullableString(context, "enterprise_id")
3522
+ userId: normalizeOptionalString(thread.user_id) ?? message.user,
3523
+ channelId: normalizeOptionalString(context?.channel_id),
3524
+ teamId: normalizeOptionalString(context?.team_id),
3525
+ enterpriseId: context?.enterprise_id === null ? null : normalizeOptionalString(context?.enterprise_id)
3433
3526
  };
3434
3527
  }
3435
3528
  async function restoreSlackAssistantThreadContextFromMetadata(params) {
@@ -3465,61 +3558,6 @@ function resolveCachedMentionRegexes(ctx, agentId, options) {
3465
3558
  byAgent.set(key, built);
3466
3559
  return built;
3467
3560
  }
3468
- function isSlackImageFileCandidate(file) {
3469
- if ((file.mimetype?.split(";")[0]?.trim().toLowerCase())?.startsWith("image/")) return true;
3470
- return Boolean(mimeTypeFromFilePath(file.name)?.startsWith("image/"));
3471
- }
3472
- function sliceSlackImageFileCandidates(files, limit) {
3473
- if (limit <= 0 || !files?.length) return [];
3474
- return files.filter(isSlackImageFileCandidate).slice(0, limit);
3475
- }
3476
- function sliceSlackHistoryAttachmentCandidates(attachments, limit) {
3477
- if (limit <= 0 || !attachments?.length) return [];
3478
- const out = [];
3479
- let remaining = limit;
3480
- for (const attachment of attachments) {
3481
- if (attachment.is_share !== true) continue;
3482
- const hasImageUrl = Boolean(normalizeOptionalString(attachment.image_url));
3483
- const files = sliceSlackImageFileCandidates(attachment.files, remaining - (hasImageUrl ? 1 : 0));
3484
- if (!hasImageUrl && files.length === 0) continue;
3485
- out.push({
3486
- ...attachment,
3487
- files
3488
- });
3489
- remaining -= (hasImageUrl ? 1 : 0) + files.length;
3490
- if (remaining <= 0) break;
3491
- }
3492
- return out;
3493
- }
3494
- function buildSlackHistoryMediaCandidateMessage(message) {
3495
- const files = sliceSlackImageFileCandidates(message.files, SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS);
3496
- const attachments = sliceSlackHistoryAttachmentCandidates(message.attachments, Math.max(0, SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS - files.length));
3497
- if (files.length === 0 && attachments.length === 0) return null;
3498
- return {
3499
- ...message,
3500
- files,
3501
- attachments
3502
- };
3503
- }
3504
- async function resolveSlackHistoryMediaForPendingRecord(params) {
3505
- const mediaMessage = buildSlackHistoryMediaCandidateMessage(params.message);
3506
- if (!mediaMessage) return [];
3507
- const content = await resolveSlackMessageContent({
3508
- message: mediaMessage,
3509
- isThreadReply: params.isThreadReply,
3510
- threadStarter: params.threadStarter,
3511
- isBotMessage: params.isBotMessage,
3512
- client: params.eventScope?.client ?? params.ctx.app.client,
3513
- botToken: params.ctx.botToken,
3514
- mediaMaxBytes: Math.min(params.ctx.mediaMaxBytes, SLACK_HISTORY_MEDIA_MAX_BYTES),
3515
- mediaReadIdleTimeoutMs: SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS,
3516
- mediaTotalTimeoutMs: SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS
3517
- });
3518
- return await toInboundMediaFactsWithMetadata(content?.effectiveDirectMedia, {
3519
- kind: "image",
3520
- messageId: params.message.ts
3521
- });
3522
- }
3523
3561
  function collectUniqueSlackMentionIds(text, regex) {
3524
3562
  const ids = [];
3525
3563
  regex.lastIndex = 0;
@@ -3566,7 +3604,7 @@ async function resolveSlackConversationContext(params) {
3566
3604
  const cfg = ctx.cfg;
3567
3605
  let channelInfo = {};
3568
3606
  let resolvedChannelType = normalizeSlackChannelType(message.channel_type, message.channel);
3569
- if (resolvedChannelType !== "im" && (!message.channel_type || message.channel_type !== "im")) {
3607
+ if (resolvedChannelType !== "im" && message.channel_type !== "im") {
3570
3608
  channelInfo = await ctx.resolveChannelName(message.channel, params.eventScope);
3571
3609
  resolvedChannelType = normalizeSlackChannelType(message.channel_type ?? channelInfo.type ?? ctx.recallSlackChannelType(message.channel, params.eventScope), message.channel);
3572
3610
  }
@@ -3585,7 +3623,7 @@ async function resolveSlackConversationContext(params) {
3585
3623
  defaultRequireMention: ctx.defaultRequireMention,
3586
3624
  allowNameMatching: ctx.allowNameMatching
3587
3625
  }) : null;
3588
- const allowBotsSetting = channelConfig?.allowBots ?? account.config?.allowBots ?? cfg.channels?.slack?.allowBots ?? false;
3626
+ const allowBotsSetting = channelConfig?.allowBots ?? account.config?.allowBots ?? cfg.channels?.slack?.allowBots ?? true;
3589
3627
  return {
3590
3628
  channelInfo,
3591
3629
  channelName,
@@ -3603,7 +3641,7 @@ async function authorizeSlackInboundMessage(params) {
3603
3641
  const { ctx, account, message, conversation, drop } = params;
3604
3642
  const { isDirectMessage, channelName, resolvedChannelType, isBotMessage, allowBotsMode } = conversation;
3605
3643
  if (isBotMessage) {
3606
- if (message.user && ctx.botUserId && message.user === ctx.botUserId) return null;
3644
+ if (ctx.botUserId && message.user === ctx.botUserId || ctx.botId && message.bot_id === ctx.botId) return null;
3607
3645
  if (allowBotsMode === "off") return drop("bot-disabled");
3608
3646
  }
3609
3647
  if (isDirectMessage && !message.user) return drop("missing-user");
@@ -3752,20 +3790,21 @@ async function prepareSlackMessage(params) {
3752
3790
  eventScope: opts.eventScope
3753
3791
  });
3754
3792
  const channelRequireMention = channelConfig?.requireMention ?? ctx.defaultRequireMention ?? true;
3755
- const channelChatType = isDirectMessage ? "direct" : isGroupDm ? "group" : "channel";
3793
+ const chatType = resolveSlackChatType(conversation.resolvedChannelType);
3756
3794
  const restoredAssistantThreadContext = await restoredAssistantThreadContextPromise;
3757
3795
  const assistantThreadContext = mergeSlackAssistantThreadContext(messageAssistantThreadContext, cachedAssistantThreadContext ?? restoredAssistantThreadContext);
3758
3796
  const assistantThreadContextToCache = messageAssistantThreadContext || restoredAssistantThreadContext ? assistantThreadContext : void 0;
3759
3797
  if (assistantThreadContextToCache) ctx.saveSlackAssistantThreadContext(assistantThreadContextToCache, opts.eventScope);
3760
- const hasAgentViewMessageSignal = isDirectMessage && !opts.eventScope && !assistantThreadContext && isSlackAppContext(message.app_context);
3798
+ const canUseManagedView = isDirectMessage && !opts.eventScope && !assistantThreadContext;
3799
+ const hasAgentViewMessageSignal = canUseManagedView && isSlackAppContext(message.app_context);
3761
3800
  if (hasAgentViewMessageSignal) await ctx.recordSlackAgentView();
3762
3801
  const managedViewThreadTs = message.thread_ts;
3763
- const isManagedViewRoot = isDirectMessage && !opts.eventScope && !assistantThreadContext && !message.parent_user_id && Boolean(managedViewThreadTs && message.ts && managedViewThreadTs === message.ts);
3802
+ const isManagedViewRoot = canUseManagedView && !message.parent_user_id && Boolean(managedViewThreadTs && message.ts && managedViewThreadTs === message.ts);
3764
3803
  if (isManagedViewRoot && managedViewThreadTs) await ctx.recordSlackManagedViewThread(message.channel, managedViewThreadTs);
3765
- const hasManagedViewThread = isDirectMessage && !opts.eventScope && !assistantThreadContext && Boolean(managedViewThreadTs && await ctx.isSlackManagedViewThread(message.channel, managedViewThreadTs));
3766
- const isAgentViewMessage = isDirectMessage && !opts.eventScope && !assistantThreadContext && (hasAgentViewMessageSignal || isManagedViewRoot || hasManagedViewThread || await ctx.isSlackAgentView());
3804
+ const hasManagedViewThread = canUseManagedView && Boolean(managedViewThreadTs && await ctx.isSlackManagedViewThread(message.channel, managedViewThreadTs));
3805
+ const isAgentViewMessage = canUseManagedView && (hasAgentViewMessageSignal || isManagedViewRoot || hasManagedViewThread || await ctx.isSlackAgentView());
3767
3806
  const agentViewThreadTs = isAgentViewMessage ? message.thread_ts ?? message.ts : void 0;
3768
- const channelReplyToMode = channelConfig?.replyToMode ?? resolveSlackReplyToMode(account, channelChatType);
3807
+ const channelReplyToMode = channelConfig?.replyToMode ?? resolveSlackReplyToMode(account, chatType);
3769
3808
  const willImplicitlyThreadReply = isRoom && !channelRequireMention && channelReplyToMode !== "off";
3770
3809
  const seedTopLevelRoomThreadBySource = opts.source === "app_mention" || opts.wasMentioned === true || explicitlyMentioned || willImplicitlyThreadReply;
3771
3810
  const resolveMessageRouting = (seedTopLevelRoomThread) => resolveSlackRoutingContext({
@@ -3820,7 +3859,7 @@ async function prepareSlackMessage(params) {
3820
3859
  let mentionRegexes = buildPolicyMentionRegexes(routing.route.agentId);
3821
3860
  let wasMentioned = resolveWasMentioned(mentionRegexes);
3822
3861
  const hasBoundSession = Boolean(routing.runtimeBoundSessionKey || routing.configuredBindingSessionKey);
3823
- let { route, runtimeBinding, replyToMode, threadContext, threadTs, isThreadReply, threadKeys, sessionKey, historyKey } = routing;
3862
+ let { route, runtimeBinding, replyToMode, threadContext, threadTs, isThreadReply, threadKeys, sessionKey } = routing;
3824
3863
  const { configuredBinding, configuredBindingSessionKey } = routing;
3825
3864
  const isAssistantThreadMessage = Boolean(isDirectMessage && messageAssistantThreadContext);
3826
3865
  const forcedAssistantReplyThreadTs = Boolean(assistantThreadContext?.threadTs && (isThreadReply || isAssistantThreadMessage || replyToMode !== "off")) ? assistantThreadContext?.threadTs : void 0;
@@ -3848,75 +3887,14 @@ async function prepareSlackMessage(params) {
3848
3887
  ok: true,
3849
3888
  name: void 0
3850
3889
  });
3851
- let implicitMentionKinds = [];
3852
- if (!isDirectMessage && message.thread_ts && !wasMentioned) {
3853
- const replyToBotKinds = implicitMentionKindWhen("reply_to_bot", Boolean(ctx.botUserId && message.parent_user_id === ctx.botUserId));
3854
- implicitMentionKinds = replyToBotKinds.length > 0 ? replyToBotKinds : implicitMentionKindWhen("bot_thread_participant", await hasSlackThreadParticipationWithPersistence({
3855
- accountId: account.accountId,
3856
- channelId: message.channel,
3857
- threadTs: message.thread_ts,
3858
- teamId: opts.eventScope?.teamId
3859
- }));
3860
- }
3861
- const recordDroppedHistory = async (reason) => {
3862
- const pendingText = (message.text ?? "").trim();
3863
- const historyMediaCandidate = buildSlackHistoryMediaCandidateMessage(message);
3864
- const fallbackFile = message.files?.length ? `[Slack file: ${formatSlackFileReference(message.files[0])}]` : "";
3865
- const pendingBody = pendingText || fallbackFile || (!fallbackFile && historyMediaCandidate ? "[Slack media attachment]" : "");
3866
- const skippedThreadStarter = historyMediaCandidate && isThreadReply && threadTs ? await resolveSlackThreadStarter({
3867
- channelId: message.channel,
3868
- threadTs,
3869
- client: slackClient,
3870
- workspaceScope: threadStarterWorkspaceScope
3871
- }) : null;
3872
- const senderName = pendingBody ? await resolveSenderName() : void 0;
3873
- await recordDroppedChannelInboundHistory({
3874
- input: {
3875
- id: message.ts ?? `${message.channel}:${Date.now()}`,
3876
- timestamp: resolveSlackTimestampMs(message.ts),
3877
- rawText: pendingBody,
3878
- textForAgent: pendingBody,
3879
- raw: message
3880
- },
3881
- admission: {
3882
- kind: "drop",
3883
- reason,
3884
- recordHistory: true
3885
- },
3886
- preflight: {
3887
- message: pendingBody ? {
3888
- rawBody: pendingBody,
3889
- body: pendingBody,
3890
- bodyForAgent: pendingBody,
3891
- senderLabel: senderName,
3892
- envelopeFrom: senderName
3893
- } : void 0,
3894
- history: {
3895
- key: historyKey,
3896
- historyMap: ctx.channelHistories,
3897
- limit: ctx.historyLimit,
3898
- recordOnDrop: true,
3899
- mediaLimit: SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS,
3900
- shouldRecord: opts.shouldRecordDroppedHistory
3901
- },
3902
- media: () => resolveSlackHistoryMediaForPendingRecord({
3903
- ctx,
3904
- message,
3905
- isThreadReply,
3906
- threadStarter: skippedThreadStarter,
3907
- isBotMessage,
3908
- eventScope: opts.eventScope
3909
- })
3910
- }
3911
- });
3912
- };
3913
3890
  let threadStarterPromise;
3914
3891
  const getThreadStarter = () => {
3915
3892
  threadStarterPromise ??= isThreadReply && threadTs ? resolveSlackThreadStarter({
3916
3893
  channelId: message.channel,
3917
3894
  threadTs,
3918
3895
  client: slackClient,
3919
- workspaceScope: threadStarterWorkspaceScope
3896
+ workspaceScope: threadStarterWorkspaceScope,
3897
+ refresh: isRoomish && ctx.historyLimit > 0
3920
3898
  }) : Promise.resolve(null);
3921
3899
  return threadStarterPromise;
3922
3900
  };
@@ -3944,7 +3922,18 @@ async function prepareSlackMessage(params) {
3944
3922
  cfg,
3945
3923
  surface: "slack"
3946
3924
  });
3947
- const shouldRequireMention = isRoom ? channelConfig?.requireMention ?? ctx.defaultRequireMention : false;
3925
+ const { requireMention: shouldRequireMention, implicitMentionKinds } = await resolveSlackThreadMentionPolicy({
3926
+ ctx,
3927
+ account,
3928
+ message,
3929
+ conversation,
3930
+ thread: routing,
3931
+ wasMentioned,
3932
+ teamId: opts.eventScope?.teamId,
3933
+ getThreadStarter
3934
+ });
3935
+ opts.abortSignal?.throwIfAborted();
3936
+ if (opts.isRuntimePolicyCurrent?.() === false) return drop("final-route-denied");
3948
3937
  const implicitMentions = resolveChannelImplicitMentions({
3949
3938
  cfg,
3950
3939
  channel: "slack",
@@ -3960,6 +3949,7 @@ async function prepareSlackMessage(params) {
3960
3949
  ctx,
3961
3950
  teamId: opts.eventScope?.teamId ?? ctx.teamId,
3962
3951
  senderId,
3952
+ senderAuthentication: message.user ? opts.senderAuthentication : void 0,
3963
3953
  senderName: senderNameForAuth,
3964
3954
  channelType: conversation.resolvedChannelType ?? "channel",
3965
3955
  channelId: message.channel,
@@ -3993,7 +3983,7 @@ async function prepareSlackMessage(params) {
3993
3983
  allowFromLower,
3994
3984
  eventScope: opts.eventScope
3995
3985
  })) return drop("unauthorized-bot");
3996
- const threadContextAllowFromLower = isRoom ? channelUsersAllowlistConfigured ? normalizeAllowListLower(channelConfig?.users) : [] : isDirectMessage ? allowFromLower : [];
3986
+ const threadContextAllowFromLower = isRoom ? channelUsersAllowlistConfigured ? normalizeStringEntriesLower(channelConfig?.users) : [] : isDirectMessage ? allowFromLower : [];
3997
3987
  const contextVisibilityMode = resolveChannelContextVisibilityMode({
3998
3988
  cfg: ctx.cfg,
3999
3989
  channel: "slack",
@@ -4067,7 +4057,7 @@ async function prepareSlackMessage(params) {
4067
4057
  })) groupThread = void 0;
4068
4058
  mentionRegexes = buildPolicyMentionRegexes(routing.route.agentId);
4069
4059
  wasMentioned = resolveWasMentioned(mentionRegexes);
4070
- ({route, runtimeBinding, replyToMode, threadContext, threadTs, isThreadReply, threadKeys, sessionKey, historyKey} = routing);
4060
+ ({route, runtimeBinding, replyToMode, threadContext, threadTs, isThreadReply, threadKeys, sessionKey} = routing);
4071
4061
  canDetectMention = Boolean(groupThread) || Boolean(ctx.botUserId) || mentionRegexes.length > 0;
4072
4062
  }
4073
4063
  if (preflightAudioTranscript && !wasMentioned) {
@@ -4106,20 +4096,10 @@ async function prepareSlackMessage(params) {
4106
4096
  if (isBotMessage && allowBotsMode === "mentions") {
4107
4097
  if (!(isDirectMessage || effectiveWasMentioned || shouldBypassMention)) return drop("bot-missing-mention");
4108
4098
  }
4109
- if (isRoom && shouldRequireMention && !canDetectMention && !effectiveWasMentioned) {
4110
- await recordDroppedHistory("slack-mention-detection-unavailable");
4111
- return drop("mention-detection-unavailable");
4112
- }
4099
+ if (isRoom && shouldRequireMention && !canDetectMention && !effectiveWasMentioned) return drop("mention-detection-unavailable");
4113
4100
  const ignoreOtherMentions = channelConfig?.ignoreOtherMentions ?? false;
4114
- if (isRoom && ignoreOtherMentions && Boolean(ctx.botUserId) && hasAnyMention && !wasMentioned) {
4115
- await recordDroppedHistory("slack-other-mention");
4116
- return drop("other-mention");
4117
- }
4118
- if (isRoom && shouldRequireMention && mentionDecision.shouldSkip) {
4119
- await recordDroppedHistory("slack-no-mention");
4120
- return drop("missing-mention");
4121
- }
4122
- const chatType = resolveSlackChatType(conversation.resolvedChannelType);
4101
+ if (isRoom && ignoreOtherMentions && Boolean(ctx.botUserId) && hasAnyMention && !wasMentioned) return drop("other-mention");
4102
+ if (isRoom && shouldRequireMention && mentionDecision.shouldSkip) return drop("missing-mention");
4123
4103
  const inboundEventKind = classifyChannelInboundEvent({
4124
4104
  conversation: { kind: chatType },
4125
4105
  unmentionedGroupPolicy: resolveUnmentionedGroupInboundPolicy({
@@ -4132,6 +4112,8 @@ async function prepareSlackMessage(params) {
4132
4112
  });
4133
4113
  const threadStarter = await getThreadStarter();
4134
4114
  const resolvedMessageContent = await getMessageContent();
4115
+ opts.abortSignal?.throwIfAborted();
4116
+ if (opts.isRuntimePolicyCurrent?.() === false) return drop("final-route-denied");
4135
4117
  if (!resolvedMessageContent) return drop("empty-content");
4136
4118
  const { rawBody, effectiveDirectMedia, commandSourceText, mentionStripPatterns } = resolvedMessageContent;
4137
4119
  const bodyForAgent = preflightAudioTranscript ? formatSlackAudioTranscriptForAgent({
@@ -4152,7 +4134,7 @@ async function prepareSlackMessage(params) {
4152
4134
  }) === "message_tool_only";
4153
4135
  const statusReactionsExplicitlyEnabled = cfg.messages?.statusReactions?.enabled === true;
4154
4136
  const isRoomEvent = inboundEventKind === "room_event";
4155
- const shouldAckReaction$1 = () => Boolean(ackReaction && shouldAckReaction({
4137
+ const shouldAckReaction$1 = Boolean(ackReaction && shouldAckReaction({
4156
4138
  scope: cfg.messages?.ackReactionScope,
4157
4139
  inboundEventKind,
4158
4140
  isDirect: isDirectMessage,
@@ -4163,7 +4145,7 @@ async function prepareSlackMessage(params) {
4163
4145
  shouldBypassMention
4164
4146
  }));
4165
4147
  const ackReactionMessageTs = message.ts;
4166
- const shouldSendAckReaction = shouldAckReaction$1() && (!sourceRepliesAreToolOnly || effectiveWasMentioned || shouldBypassMention || isRoomEvent);
4148
+ const shouldSendAckReaction = shouldAckReaction$1 && (!sourceRepliesAreToolOnly || effectiveWasMentioned || shouldBypassMention || isRoomEvent);
4167
4149
  const statusReactionsWillHandle = Boolean(ackReactionMessageTs) && !isRoomEvent && statusReactionsExplicitlyEnabled && shouldSendAckReaction;
4168
4150
  const ackReactionPromise = !statusReactionsWillHandle && shouldSendAckReaction && ackReactionMessageTs && ackReactionValue ? reactSlackMessage(message.channel, ackReactionMessageTs, ackReactionValue, {
4169
4151
  token: ctx.botToken,
@@ -4200,16 +4182,34 @@ async function prepareSlackMessage(params) {
4200
4182
  const textWithId = `${bodyForAgent}\n[slack message id: ${message.ts} channel: ${message.channel}${threadInfo}]`;
4201
4183
  const storePath = resolveStorePath(ctx.cfg.session?.store, { agentId: route.agentId });
4202
4184
  const envelopeOptions = resolveEnvelopeFormatOptions(ctx.cfg);
4185
+ const effectiveMessageThreadId = assistantThreadContext?.threadTs ?? agentViewThreadTs ?? threadContext.messageThreadId;
4186
+ const boundMessageThreadId = directThreadRoutedToDmSession ? void 0 : effectiveMessageThreadId;
4187
+ messageIngress = await resolveMessageIngress({
4188
+ agentId: route.agentId,
4189
+ sessionKey,
4190
+ nativeChannelId: message.channel,
4191
+ messageId: threadContext.messageTs,
4192
+ inboundEventKind
4193
+ }, boundMessageThreadId);
4194
+ if (messageIngress.ingress.admission !== "dispatch") return drop("final-route-denied");
4203
4195
  const sessionEntry = getSessionEntry({
4204
4196
  storePath,
4205
4197
  sessionKey
4206
4198
  });
4207
4199
  const previousTimestamp = sessionEntry?.updatedAt;
4208
- if (opts.source === "app_mention" && !ctx.botUserId && message.ts) {
4209
- const pendingHistory = ctx.channelHistories.get(historyKey);
4210
- if (pendingHistory) ctx.channelHistories.set(historyKey, pendingHistory.filter((entry) => entry.messageId !== message.ts));
4211
- }
4212
- const channelHistory = createChannelHistoryWindow({ historyMap: ctx.channelHistories });
4200
+ const excludedMessageIds = new Set(opts.sourceMessageIds);
4201
+ if (message.ts) excludedMessageIds.add(message.ts);
4202
+ const isHistorySessionCurrent = () => {
4203
+ const current = getSessionEntry({
4204
+ storePath,
4205
+ sessionKey
4206
+ });
4207
+ return current?.sessionId === sessionEntry?.sessionId && current?.lifecycleRevision === sessionEntry?.lifecycleRevision && current?.sessionStartedAt === sessionEntry?.sessionStartedAt && current?.updatedAt === 0 === (sessionEntry?.updatedAt === 0);
4208
+ };
4209
+ const assertHistoryCurrent = () => {
4210
+ opts.abortSignal?.throwIfAborted();
4211
+ if (opts.isRuntimePolicyCurrent?.() === false || !isHistorySessionCurrent()) throw new Error("Slack history policy or session changed during recovery");
4212
+ };
4213
4213
  const dmHistoryLimit = isDirectMessage ? resolveSlackDmHistoryLimit({
4214
4214
  account,
4215
4215
  userId: message.user,
@@ -4240,26 +4240,12 @@ async function prepareSlackMessage(params) {
4240
4240
  inboundHistory: void 0
4241
4241
  };
4242
4242
  if (dmHistoryContext.body) combinedBody = `${dmHistoryContext.body}\n\n${combinedBody}`;
4243
- if (isRoomish && ctx.historyLimit > 0) combinedBody = channelHistory.buildPendingContext({
4244
- historyKey,
4245
- limit: ctx.historyLimit,
4246
- currentMessage: combinedBody,
4247
- formatEntry: (entry) => formatInboundEnvelope({
4248
- channel: "Slack",
4249
- from: roomLabel,
4250
- timestamp: entry.timestamp,
4251
- body: `${entry.body}${entry.messageId ? ` [id:${entry.messageId} channel:${message.channel}]` : ""}`,
4252
- chatType: "channel",
4253
- senderLabel: entry.sender,
4254
- envelope: envelopeOptions
4255
- })
4256
- });
4257
4243
  const { channelMetadata, groupSystemPrompt } = resolveSlackRoomContextHints({
4258
4244
  isRoomish,
4259
4245
  channelInfo,
4260
4246
  channelConfig
4261
4247
  });
4262
- const { threadStarterBody, threadHistoryBody, shouldSeedInitialThreadContext, threadLabel, threadStarterMedia } = await resolveSlackThreadContextData({
4248
+ const threadContextData = await resolveSlackThreadContextData({
4263
4249
  ctx,
4264
4250
  agentId: route.agentId,
4265
4251
  account,
@@ -4277,25 +4263,64 @@ async function prepareSlackMessage(params) {
4277
4263
  contextVisibilityMode,
4278
4264
  envelopeOptions,
4279
4265
  effectiveDirectMedia,
4280
- eventScope: opts.eventScope
4266
+ eventScope: opts.eventScope,
4267
+ historyLimit: isRoomish ? ctx.historyLimit : void 0,
4268
+ excludedMessageIds,
4269
+ assertHistoryCurrent,
4270
+ abortSignal: opts.abortSignal
4271
+ });
4272
+ const { threadLabel, threadStarterMedia } = threadContextData;
4273
+ let { threadStarterBody, threadHistoryBody, shouldSeedInitialThreadContext } = threadContextData;
4274
+ const threadScopedHistory = isThreadReply && ctx.threadHistoryScope === "thread";
4275
+ let roomHistory = [];
4276
+ if (isRoomish && ctx.historyLimit > 0 && (!threadScopedHistory || !shouldSeedInitialThreadContext) && sessionEntry?.updatedAt !== 0) roomHistory = await resolveSlackRoomHistory({
4277
+ ctx,
4278
+ message,
4279
+ threadTs: threadScopedHistory ? threadTs : void 0,
4280
+ oldest: sessionEntry?.sessionStartedAt ? (sessionEntry.sessionStartedAt / 1e3).toFixed(6) : void 0,
4281
+ excludedMessageIds,
4282
+ allowFromLower: threadContextAllowFromLower,
4283
+ contextVisibilityMode,
4284
+ eventScope: opts.eventScope,
4285
+ assertCurrent: assertHistoryCurrent,
4286
+ abortSignal: opts.abortSignal
4281
4287
  });
4282
4288
  const effectiveMedia = effectiveDirectMedia ?? threadStarterMedia;
4283
- const inboundMedia = await toInboundMediaFactsWithMetadata(effectiveMedia, { transcribed: (entry) => effectiveMedia === effectiveDirectMedia && entry === preflightAudioMedia });
4284
- const inboundHistory = isRoomish && ctx.historyLimit > 0 ? channelHistory.buildInboundHistory({
4285
- historyKey,
4289
+ let inboundMedia = await toInboundMediaFactsWithMetadata(effectiveMedia, { transcribed: (entry) => effectiveMedia === effectiveDirectMedia && entry === preflightAudioMedia });
4290
+ opts.abortSignal?.throwIfAborted();
4291
+ if (opts.isRuntimePolicyCurrent?.() === false) return drop("final-route-denied");
4292
+ if (isRoomish && ctx.historyLimit > 0 && !isHistorySessionCurrent()) {
4293
+ roomHistory = [];
4294
+ threadHistoryBody = void 0;
4295
+ threadStarterBody = void 0;
4296
+ shouldSeedInitialThreadContext = false;
4297
+ if (effectiveMedia === threadStarterMedia) inboundMedia = [];
4298
+ ctx.logger.warn({
4299
+ channelId: message.channel,
4300
+ sessionKey
4301
+ }, "Slack automatic history omitted after session changed");
4302
+ }
4303
+ combinedBody = buildHistoryContextFromEntries({
4304
+ entries: roomHistory,
4305
+ currentMessage: combinedBody,
4306
+ historyKind: "recent",
4307
+ excludeLast: false,
4308
+ formatEntry: (entry) => formatInboundEnvelope({
4309
+ channel: "Slack",
4310
+ from: roomLabel,
4311
+ timestamp: entry.timestamp,
4312
+ body: `${entry.body} [id:${entry.messageId} channel:${message.channel}]`,
4313
+ chatType: "channel",
4314
+ senderLabel: entry.sender,
4315
+ envelope: envelopeOptions
4316
+ })
4317
+ });
4318
+ const inboundHistory = isRoomish && ctx.historyLimit > 0 ? buildInboundHistoryFromEntries({
4319
+ entries: roomHistory,
4286
4320
  limit: ctx.historyLimit
4287
4321
  }) : dmHistoryContext.inboundHistory;
4288
4322
  const commandBody = textForCommandDetection.trim();
4289
4323
  const supplementalThreadHistoryBody = directThreadRoutedToDmSession && !threadHistoryBody ? threadStarterBody : threadHistoryBody;
4290
- const effectiveMessageThreadId = assistantThreadContext?.threadTs ?? agentViewThreadTs ?? threadContext.messageThreadId;
4291
- const boundMessageThreadId = directThreadRoutedToDmSession ? void 0 : effectiveMessageThreadId;
4292
- messageIngress = await resolveMessageIngress({
4293
- agentId: route.agentId,
4294
- sessionKey,
4295
- messageId: threadContext.messageTs,
4296
- inboundEventKind
4297
- }, boundMessageThreadId);
4298
- if (messageIngress.ingress.admission !== "dispatch") return drop("final-route-denied");
4299
4324
  const agentContextEntities = isAgentViewMessage ? normalizeSlackAppContextEntities(message.app_context) : [];
4300
4325
  const ctxPayload = (ctx.buildContext ?? buildChannelInboundEventContext)({
4301
4326
  channelIngress: messageIngress,
@@ -4328,9 +4353,7 @@ async function prepareSlackMessage(params) {
4328
4353
  avatar: conversationAvatar
4329
4354
  },
4330
4355
  route: {
4331
- agentId: route.agentId,
4332
- dmScope: route.dmScope,
4333
- accountId: route.accountId,
4356
+ ...route,
4334
4357
  routeSessionKey: route.sessionKey,
4335
4358
  dispatchSessionKey: sessionKey,
4336
4359
  parentSessionKey: threadKeys.parentSessionKey
@@ -4349,7 +4372,10 @@ async function prepareSlackMessage(params) {
4349
4372
  commandBody,
4350
4373
  inboundHistory
4351
4374
  },
4352
- sessionTranscript: { historyLimit: isRoomish ? ctx.historyLimit : dmHistoryLimit },
4375
+ sessionTranscript: {
4376
+ historyLimit: isRoomish ? ctx.historyLimit : dmHistoryLimit,
4377
+ historyKind: isRoomish ? "recent" : "pending"
4378
+ },
4353
4379
  access: {
4354
4380
  mentions: {
4355
4381
  canDetectMention: isRoomish,
@@ -4404,16 +4430,6 @@ async function prepareSlackMessage(params) {
4404
4430
  }
4405
4431
  });
4406
4432
  ctxPayload.ReplyToMode = replyToMode;
4407
- if (isRoomish && !shouldRequireMention) channelHistory.record({
4408
- historyKey,
4409
- limit: ctx.historyLimit,
4410
- entry: {
4411
- sender: senderName,
4412
- body: rawBody,
4413
- timestamp: resolveSlackTimestampMs(message.ts),
4414
- messageId: message.ts
4415
- }
4416
- });
4417
4433
  const pinnedMainDmOwner = isDirectMessage ? resolvePinnedMainDmOwnerFromAllowlist({
4418
4434
  dmScope: cfg.session?.dmScope,
4419
4435
  allowFrom: allowFromLower,
@@ -4467,13 +4483,7 @@ async function prepareSlackMessage(params) {
4467
4483
  sessionKey
4468
4484
  }, "failed updating session meta");
4469
4485
  }
4470
- },
4471
- history: isRoomish && shouldRequireMention ? {
4472
- isGroup: true,
4473
- historyKey,
4474
- historyMap: ctx.channelHistories,
4475
- limit: ctx.historyLimit
4476
- } : void 0
4486
+ }
4477
4487
  },
4478
4488
  replyToMode,
4479
4489
  ...forcedReplyThreadTs ? { forcedReplyThreadTs } : {},
@@ -4481,7 +4491,6 @@ async function prepareSlackMessage(params) {
4481
4491
  requireMention: shouldRequireMention,
4482
4492
  isDirectMessage,
4483
4493
  isRoomish,
4484
- historyKey,
4485
4494
  preview,
4486
4495
  ackReactionMessageTs,
4487
4496
  ackReactionValue,