@openclaw/slack 2026.9.5 → 2026.9.6

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 (408) hide show
  1. package/dist/.setup/{account-inspect-B86OaUEt.mjs → account-inspect-DP1Bcwj4.mjs} +1 -1
  2. package/dist/.setup/{accounts-C1FspRF-.mjs → accounts-BBHvg0pY.mjs} +11 -10
  3. package/dist/.setup/accounts.runtime-DN-PPocC.mjs +2 -0
  4. package/dist/.setup/{action-runtime-Aa-h24Fq.mjs → action-runtime-B01d85zt.mjs} +25 -29
  5. package/dist/.setup/action-runtime.runtime-C8R8CUan.mjs +2 -0
  6. package/dist/.setup/{actions-BvGUGk3w.mjs → actions-DKCtBgCI.mjs} +80 -49
  7. package/dist/.setup/{approval-handler.runtime-BcZNF-VP.mjs → approval-handler.runtime-DMszKn_7.mjs} +5 -5
  8. package/dist/.setup/{approval-message-updates-Cy1cxfBn.mjs → approval-message-updates-Tb_qRokK.mjs} +2 -2
  9. package/dist/.setup/{channel-DrBm0NFw.mjs → channel-CErYZ5zw.mjs} +33 -29
  10. package/dist/.setup/{channel-type-osC0XOES.mjs → channel-type-Dxk8VJUb.mjs} +3 -3
  11. package/dist/.setup/{channel.setup-BSY5hzVz.mjs → channel.setup-BVoUtdPn.mjs} +4 -4
  12. package/dist/.setup/{config-schema-DrY3S7Ee.mjs → config-schema-CyL279e7.mjs} +8 -0
  13. package/dist/.setup/{directory-config-CTs7YyuU.mjs → directory-config-CQTSH45W.mjs} +1 -1
  14. package/dist/.setup/{directory-live-Btq0063Q.mjs → directory-live-BCcj89QV.mjs} +2 -2
  15. package/dist/.setup/{message-tool-api-DRnDCVdY.mjs → message-tool-api-C0PTFaNV.mjs} +2 -2
  16. package/dist/.setup/{monitor-Dw5uuKWM.mjs → monitor-_Sht7Dqy.mjs} +3 -3
  17. package/dist/.setup/{outbound-adapter-K3Ek1iSR.mjs → outbound-adapter-A7Wdn8ZT.mjs} +4 -4
  18. package/dist/.setup/{pipeline.runtime-BZqGcbfi.mjs → pipeline.runtime-BlPOexmJ.mjs} +642 -487
  19. package/dist/.setup/{policy-Db_QWCZZ.mjs → policy-BApW53pI.mjs} +2 -2
  20. package/dist/.setup/{probe-64C6k3_Q.mjs → probe-DiYIMPJo.mjs} +1 -1
  21. package/dist/.setup/{provider-w36VTgZ3.mjs → provider-C-jPaGt2.mjs} +26 -27
  22. package/dist/.setup/{relay-source-CDqY76Vp.mjs → relay-source-DfOnjJTE.mjs} +1 -1
  23. package/dist/.setup/{replies-CWFhjS9S.mjs → replies-BBAJxUOQ.mjs} +4 -4
  24. package/dist/.setup/{reply-blocks-9uCeAJts.mjs → reply-blocks-Bm8kf6mK.mjs} +1 -1
  25. package/dist/.setup/{resolve-channels-x8fX1SEt.mjs → resolve-channels-BXbMBbJD.mjs} +1 -1
  26. package/dist/.setup/{resolve-users-B8BpBnH2.mjs → resolve-users-65WqvIqD.mjs} +1 -1
  27. package/dist/.setup/{scopes-CJ-6l4v4.mjs → scopes-h3YB-mMs.mjs} +1 -1
  28. package/dist/.setup/{send-6GCb3lBP.mjs → send-Cp6omkOg.mjs} +21 -31
  29. package/dist/.setup/{send.runtime-BmqRP-WG.mjs → send.runtime-B4caZU6T.mjs} +1 -1
  30. package/dist/.setup/{session-status-BhG6pdiH.mjs → session-status-Cf7AvWOt.mjs} +11 -15
  31. package/dist/.setup/{setup-plugin-api-DtCEByQB.mjs → setup-plugin-api-dLPkL06L.mjs} +1 -1
  32. package/dist/.setup/{setup-surface-DsE0hsYY.mjs → setup-surface-D3DLcCxu.mjs} +6 -6
  33. package/dist/.setup/{shared-BNmqCPgQ.mjs → shared-CJ3arAFL.mjs} +4 -4
  34. package/dist/.setup/{slash-dispatch.runtime-Io4ASZsY.mjs → slash-dispatch.runtime-HFy_XvyO.mjs} +1 -1
  35. package/dist/account-inspect-api.js +1 -1
  36. package/dist/api.js +13 -13
  37. package/dist/channel-config-api.js +1 -1
  38. package/dist/channel-plugin-api.js +1 -1
  39. package/dist/directory-contract-api.js +1 -1
  40. package/dist/message-tool-api.js +1 -1
  41. package/dist/runtime-api.js +11 -11
  42. package/dist/setup-entry.js +1 -1
  43. package/dist/setup-plugin-api.js +1 -1
  44. package/node_modules/@slack/bolt/node_modules/@types/node/README.md +1 -1
  45. package/node_modules/@slack/bolt/node_modules/@types/node/package.json +2 -2
  46. package/node_modules/@slack/bolt/node_modules/@types/node/web-globals/streams.d.ts +4 -0
  47. package/node_modules/@slack/web-api/node_modules/@types/node/README.md +1 -1
  48. package/node_modules/@slack/web-api/node_modules/@types/node/package.json +2 -2
  49. package/node_modules/@slack/web-api/node_modules/@types/node/web-globals/streams.d.ts +4 -0
  50. package/node_modules/zod/README.md +2 -2
  51. package/node_modules/zod/compile.cjs +19 -0
  52. package/node_modules/zod/index.cjs +19 -0
  53. package/node_modules/zod/locales/index.cjs +19 -0
  54. package/node_modules/zod/mini/index.cjs +19 -0
  55. package/node_modules/zod/package.json +8 -7
  56. package/node_modules/zod/src/v3/helpers/errorUtil.ts +1 -1
  57. package/node_modules/zod/src/v4/classic/external.ts +2 -0
  58. package/node_modules/zod/src/v4/classic/from-json-schema.ts +248 -25
  59. package/node_modules/zod/src/v4/classic/schemas.ts +243 -168
  60. package/node_modules/zod/src/v4/classic/tests/assignability.test.ts +60 -9
  61. package/node_modules/zod/src/v4/classic/tests/bigint.test.ts +6 -0
  62. package/node_modules/zod/src/v4/classic/tests/continuability.test.ts +2 -2
  63. package/node_modules/zod/src/v4/classic/tests/cyclic-data.test.ts +265 -0
  64. package/node_modules/zod/src/v4/classic/tests/discriminated-unions.test.ts +112 -6
  65. package/node_modules/zod/src/v4/classic/tests/enum.test.ts +9 -0
  66. package/node_modules/zod/src/v4/classic/tests/error.test.ts +130 -1
  67. package/node_modules/zod/src/v4/classic/tests/from-json-schema.test.ts +157 -0
  68. package/node_modules/zod/src/v4/classic/tests/iban.test.ts +81 -0
  69. package/node_modules/zod/src/v4/classic/tests/instance-footprint.test.ts +40 -2
  70. package/node_modules/zod/src/v4/classic/tests/instanceof.test.ts +27 -7
  71. package/node_modules/zod/src/v4/classic/tests/issue-schema.test.ts +88 -56
  72. package/node_modules/zod/src/v4/classic/tests/number.test.ts +39 -0
  73. package/node_modules/zod/src/v4/classic/tests/object.test.ts +56 -0
  74. package/node_modules/zod/src/v4/classic/tests/optin-ladder.test.ts +3 -2
  75. package/node_modules/zod/src/v4/classic/tests/prefault.test.ts +70 -0
  76. package/node_modules/zod/src/v4/classic/tests/properties.test.ts +141 -0
  77. package/node_modules/zod/src/v4/classic/tests/recursive-types.test.ts +46 -1
  78. package/node_modules/zod/src/v4/classic/tests/standard-schema.test.ts +19 -0
  79. package/node_modules/zod/src/v4/classic/tests/string.test.ts +95 -1
  80. package/node_modules/zod/src/v4/classic/tests/template-literal.test.ts +33 -1
  81. package/node_modules/zod/src/v4/classic/tests/to-json-schema.test.ts +234 -38
  82. package/node_modules/zod/src/v4/classic/tests/validate.test.ts +569 -0
  83. package/node_modules/zod/src/v4/core/api.ts +44 -28
  84. package/node_modules/zod/src/v4/core/checks.ts +62 -102
  85. package/node_modules/zod/src/v4/core/compile.ts +218 -93
  86. package/node_modules/zod/src/v4/core/core.ts +1 -3
  87. package/node_modules/zod/src/v4/core/doc.ts +6 -2
  88. package/node_modules/zod/src/v4/core/errors.ts +3 -6
  89. package/node_modules/zod/src/v4/core/json-schema-generator.ts +2 -2
  90. package/node_modules/zod/src/v4/core/json-schema-processors.ts +185 -81
  91. package/node_modules/zod/src/v4/core/memoizer.ts +75 -28
  92. package/node_modules/zod/src/v4/core/parse.ts +37 -16
  93. package/node_modules/zod/src/v4/core/regexes.ts +14 -4
  94. package/node_modules/zod/src/v4/core/schemas.ts +296 -132
  95. package/node_modules/zod/src/v4/core/tests/cached.test.ts +54 -0
  96. package/node_modules/zod/src/v4/core/tests/compile-differential.test.ts +35 -0
  97. package/node_modules/zod/src/v4/core/tests/compile-generative.test.ts +445 -0
  98. package/node_modules/zod/src/v4/core/tests/compile.test.ts +399 -2
  99. package/node_modules/zod/src/v4/core/tests/email-regex.test.ts +140 -0
  100. package/node_modules/zod/src/v4/core/tests/locales/tg.test.ts +85 -0
  101. package/node_modules/zod/src/v4/core/tests/polyfill-collision.test.ts +46 -0
  102. package/node_modules/zod/src/v4/core/tests/url-no-canparse.test.ts +118 -16
  103. package/node_modules/zod/src/v4/core/to-json-schema.ts +9 -7
  104. package/node_modules/zod/src/v4/core/util.ts +218 -147
  105. package/node_modules/zod/src/v4/core/versions.ts +2 -2
  106. package/node_modules/zod/src/v4/locales/ar.ts +2 -0
  107. package/node_modules/zod/src/v4/locales/az.ts +2 -0
  108. package/node_modules/zod/src/v4/locales/be.ts +2 -0
  109. package/node_modules/zod/src/v4/locales/bg.ts +2 -0
  110. package/node_modules/zod/src/v4/locales/bn.ts +2 -0
  111. package/node_modules/zod/src/v4/locales/ca.ts +2 -0
  112. package/node_modules/zod/src/v4/locales/ckb.ts +2 -0
  113. package/node_modules/zod/src/v4/locales/cs.ts +2 -0
  114. package/node_modules/zod/src/v4/locales/da.ts +2 -0
  115. package/node_modules/zod/src/v4/locales/de.ts +2 -0
  116. package/node_modules/zod/src/v4/locales/el.ts +2 -0
  117. package/node_modules/zod/src/v4/locales/en.ts +2 -0
  118. package/node_modules/zod/src/v4/locales/eo.ts +2 -0
  119. package/node_modules/zod/src/v4/locales/es.ts +2 -0
  120. package/node_modules/zod/src/v4/locales/fa.ts +2 -0
  121. package/node_modules/zod/src/v4/locales/fi.ts +2 -0
  122. package/node_modules/zod/src/v4/locales/fr-CA.ts +2 -0
  123. package/node_modules/zod/src/v4/locales/fr.ts +2 -0
  124. package/node_modules/zod/src/v4/locales/gu.ts +2 -0
  125. package/node_modules/zod/src/v4/locales/he.ts +2 -0
  126. package/node_modules/zod/src/v4/locales/hi.ts +2 -0
  127. package/node_modules/zod/src/v4/locales/hr.ts +2 -0
  128. package/node_modules/zod/src/v4/locales/hu.ts +2 -0
  129. package/node_modules/zod/src/v4/locales/hy.ts +2 -0
  130. package/node_modules/zod/src/v4/locales/id.ts +2 -0
  131. package/node_modules/zod/src/v4/locales/index.ts +1 -0
  132. package/node_modules/zod/src/v4/locales/is.ts +2 -0
  133. package/node_modules/zod/src/v4/locales/it.ts +2 -0
  134. package/node_modules/zod/src/v4/locales/ja.ts +2 -0
  135. package/node_modules/zod/src/v4/locales/ka.ts +2 -0
  136. package/node_modules/zod/src/v4/locales/km.ts +2 -0
  137. package/node_modules/zod/src/v4/locales/kn.ts +2 -0
  138. package/node_modules/zod/src/v4/locales/ko.ts +2 -0
  139. package/node_modules/zod/src/v4/locales/lt.ts +2 -0
  140. package/node_modules/zod/src/v4/locales/mk.ts +2 -0
  141. package/node_modules/zod/src/v4/locales/ms.ts +2 -0
  142. package/node_modules/zod/src/v4/locales/ne.ts +2 -0
  143. package/node_modules/zod/src/v4/locales/nl.ts +2 -0
  144. package/node_modules/zod/src/v4/locales/nn.ts +2 -0
  145. package/node_modules/zod/src/v4/locales/no.ts +2 -0
  146. package/node_modules/zod/src/v4/locales/ota.ts +2 -0
  147. package/node_modules/zod/src/v4/locales/pl.ts +2 -0
  148. package/node_modules/zod/src/v4/locales/ps.ts +2 -0
  149. package/node_modules/zod/src/v4/locales/pt-BR.ts +2 -0
  150. package/node_modules/zod/src/v4/locales/pt.ts +2 -0
  151. package/node_modules/zod/src/v4/locales/ro.ts +2 -0
  152. package/node_modules/zod/src/v4/locales/ru.ts +2 -0
  153. package/node_modules/zod/src/v4/locales/sk.ts +2 -0
  154. package/node_modules/zod/src/v4/locales/sl.ts +2 -0
  155. package/node_modules/zod/src/v4/locales/sv.ts +2 -0
  156. package/node_modules/zod/src/v4/locales/ta.ts +2 -0
  157. package/node_modules/zod/src/v4/locales/tg.ts +135 -0
  158. package/node_modules/zod/src/v4/locales/th.ts +2 -0
  159. package/node_modules/zod/src/v4/locales/tk.ts +2 -0
  160. package/node_modules/zod/src/v4/locales/tr.ts +2 -0
  161. package/node_modules/zod/src/v4/locales/uk.ts +2 -0
  162. package/node_modules/zod/src/v4/locales/ur.ts +2 -0
  163. package/node_modules/zod/src/v4/locales/uz.ts +2 -0
  164. package/node_modules/zod/src/v4/locales/vi.ts +2 -0
  165. package/node_modules/zod/src/v4/locales/yo.ts +2 -0
  166. package/node_modules/zod/src/v4/locales/zh-CN.ts +2 -0
  167. package/node_modules/zod/src/v4/locales/zh-TW.ts +2 -0
  168. package/node_modules/zod/src/v4/mini/external.ts +2 -0
  169. package/node_modules/zod/src/v4/mini/schemas.ts +32 -7
  170. package/node_modules/zod/src/v4/mini/tests/checks.test.ts +1 -1
  171. package/node_modules/zod/src/v4/mini/tests/computed.test.ts +11 -17
  172. package/node_modules/zod/src/v4/mini/tests/index.test.ts +86 -0
  173. package/node_modules/zod/src/v4/mini/tests/recursive-types.test.ts +16 -0
  174. package/node_modules/zod/src/v4/mini/tests/string.test.ts +23 -0
  175. package/node_modules/zod/v3/ZodError.cjs +19 -0
  176. package/node_modules/zod/v3/errors.cjs +19 -0
  177. package/node_modules/zod/v3/external.cjs +19 -0
  178. package/node_modules/zod/v3/helpers/enumUtil.cjs +19 -0
  179. package/node_modules/zod/v3/helpers/errorUtil.cjs +20 -1
  180. package/node_modules/zod/v3/helpers/errorUtil.js +1 -1
  181. package/node_modules/zod/v3/helpers/parseUtil.cjs +19 -0
  182. package/node_modules/zod/v3/helpers/partialUtil.cjs +19 -0
  183. package/node_modules/zod/v3/helpers/typeAliases.cjs +19 -0
  184. package/node_modules/zod/v3/helpers/util.cjs +19 -0
  185. package/node_modules/zod/v3/index.cjs +19 -0
  186. package/node_modules/zod/v3/standard-schema.cjs +19 -0
  187. package/node_modules/zod/v3/types.cjs +19 -0
  188. package/node_modules/zod/v4/classic/checks.cjs +19 -0
  189. package/node_modules/zod/v4/classic/coerce.cjs +19 -0
  190. package/node_modules/zod/v4/classic/compat.cjs +19 -0
  191. package/node_modules/zod/v4/classic/deep-partial.cjs +19 -0
  192. package/node_modules/zod/v4/classic/errors.cjs +19 -0
  193. package/node_modules/zod/v4/classic/external.cjs +22 -1
  194. package/node_modules/zod/v4/classic/external.d.cts +1 -1
  195. package/node_modules/zod/v4/classic/external.d.ts +1 -1
  196. package/node_modules/zod/v4/classic/external.js +1 -1
  197. package/node_modules/zod/v4/classic/from-json-schema.cjs +264 -24
  198. package/node_modules/zod/v4/classic/from-json-schema.js +245 -24
  199. package/node_modules/zod/v4/classic/in-out.cjs +19 -0
  200. package/node_modules/zod/v4/classic/index.cjs +19 -0
  201. package/node_modules/zod/v4/classic/iso.cjs +19 -0
  202. package/node_modules/zod/v4/classic/parse.cjs +19 -0
  203. package/node_modules/zod/v4/classic/schemas.cjs +100 -35
  204. package/node_modules/zod/v4/classic/schemas.d.cts +15 -1
  205. package/node_modules/zod/v4/classic/schemas.d.ts +15 -1
  206. package/node_modules/zod/v4/classic/schemas.js +77 -33
  207. package/node_modules/zod/v4/core/api.cjs +45 -22
  208. package/node_modules/zod/v4/core/api.d.cts +12 -6
  209. package/node_modules/zod/v4/core/api.d.ts +12 -6
  210. package/node_modules/zod/v4/core/api.js +25 -22
  211. package/node_modules/zod/v4/core/checks.cjs +51 -97
  212. package/node_modules/zod/v4/core/checks.d.cts +17 -1
  213. package/node_modules/zod/v4/core/checks.d.ts +17 -1
  214. package/node_modules/zod/v4/core/checks.js +31 -96
  215. package/node_modules/zod/v4/core/compile.cjs +173 -78
  216. package/node_modules/zod/v4/core/compile.d.cts +18 -4
  217. package/node_modules/zod/v4/core/compile.d.ts +18 -4
  218. package/node_modules/zod/v4/core/compile.js +154 -79
  219. package/node_modules/zod/v4/core/core.cjs +20 -1
  220. package/node_modules/zod/v4/core/core.js +1 -1
  221. package/node_modules/zod/v4/core/doc.cjs +26 -2
  222. package/node_modules/zod/v4/core/doc.js +7 -2
  223. package/node_modules/zod/v4/core/errors.cjs +21 -5
  224. package/node_modules/zod/v4/core/errors.js +2 -5
  225. package/node_modules/zod/v4/core/index.cjs +19 -0
  226. package/node_modules/zod/v4/core/json-schema-generator.cjs +20 -1
  227. package/node_modules/zod/v4/core/json-schema-generator.js +2 -2
  228. package/node_modules/zod/v4/core/json-schema-processors.cjs +188 -72
  229. package/node_modules/zod/v4/core/json-schema-processors.d.cts +16 -0
  230. package/node_modules/zod/v4/core/json-schema-processors.d.ts +16 -0
  231. package/node_modules/zod/v4/core/json-schema-processors.js +170 -74
  232. package/node_modules/zod/v4/core/json-schema.cjs +19 -0
  233. package/node_modules/zod/v4/core/memoizer.cjs +111 -23
  234. package/node_modules/zod/v4/core/memoizer.js +69 -23
  235. package/node_modules/zod/v4/core/parse.cjs +56 -16
  236. package/node_modules/zod/v4/core/parse.js +37 -16
  237. package/node_modules/zod/v4/core/regexes.cjs +31 -6
  238. package/node_modules/zod/v4/core/regexes.d.cts +3 -0
  239. package/node_modules/zod/v4/core/regexes.d.ts +3 -0
  240. package/node_modules/zod/v4/core/regexes.js +10 -4
  241. package/node_modules/zod/v4/core/registries.cjs +19 -0
  242. package/node_modules/zod/v4/core/schemas.cjs +302 -114
  243. package/node_modules/zod/v4/core/schemas.d.cts +28 -8
  244. package/node_modules/zod/v4/core/schemas.d.ts +28 -8
  245. package/node_modules/zod/v4/core/schemas.js +279 -113
  246. package/node_modules/zod/v4/core/standard-schema.cjs +19 -0
  247. package/node_modules/zod/v4/core/to-json-schema.cjs +28 -7
  248. package/node_modules/zod/v4/core/to-json-schema.d.cts +4 -3
  249. package/node_modules/zod/v4/core/to-json-schema.d.ts +4 -3
  250. package/node_modules/zod/v4/core/to-json-schema.js +7 -6
  251. package/node_modules/zod/v4/core/util.cjs +177 -143
  252. package/node_modules/zod/v4/core/util.d.cts +25 -4
  253. package/node_modules/zod/v4/core/util.d.ts +25 -4
  254. package/node_modules/zod/v4/core/util.js +156 -143
  255. package/node_modules/zod/v4/core/versions.cjs +21 -2
  256. package/node_modules/zod/v4/core/versions.d.cts +1 -1
  257. package/node_modules/zod/v4/core/versions.d.ts +1 -1
  258. package/node_modules/zod/v4/core/versions.js +2 -2
  259. package/node_modules/zod/v4/core/visit.cjs +19 -0
  260. package/node_modules/zod/v4/index.cjs +19 -0
  261. package/node_modules/zod/v4/locales/ar.cjs +2 -0
  262. package/node_modules/zod/v4/locales/ar.js +2 -0
  263. package/node_modules/zod/v4/locales/az.cjs +2 -0
  264. package/node_modules/zod/v4/locales/az.js +2 -0
  265. package/node_modules/zod/v4/locales/be.cjs +2 -0
  266. package/node_modules/zod/v4/locales/be.js +2 -0
  267. package/node_modules/zod/v4/locales/bg.cjs +2 -0
  268. package/node_modules/zod/v4/locales/bg.js +2 -0
  269. package/node_modules/zod/v4/locales/bn.cjs +2 -0
  270. package/node_modules/zod/v4/locales/bn.js +2 -0
  271. package/node_modules/zod/v4/locales/ca.cjs +2 -0
  272. package/node_modules/zod/v4/locales/ca.js +2 -0
  273. package/node_modules/zod/v4/locales/ckb.cjs +2 -0
  274. package/node_modules/zod/v4/locales/ckb.js +2 -0
  275. package/node_modules/zod/v4/locales/cs.cjs +2 -0
  276. package/node_modules/zod/v4/locales/cs.js +2 -0
  277. package/node_modules/zod/v4/locales/da.cjs +2 -0
  278. package/node_modules/zod/v4/locales/da.js +2 -0
  279. package/node_modules/zod/v4/locales/de.cjs +2 -0
  280. package/node_modules/zod/v4/locales/de.js +2 -0
  281. package/node_modules/zod/v4/locales/el.cjs +2 -0
  282. package/node_modules/zod/v4/locales/el.js +2 -0
  283. package/node_modules/zod/v4/locales/en.cjs +2 -0
  284. package/node_modules/zod/v4/locales/en.js +2 -0
  285. package/node_modules/zod/v4/locales/eo.cjs +2 -0
  286. package/node_modules/zod/v4/locales/eo.js +2 -0
  287. package/node_modules/zod/v4/locales/es.cjs +2 -0
  288. package/node_modules/zod/v4/locales/es.js +2 -0
  289. package/node_modules/zod/v4/locales/fa.cjs +2 -0
  290. package/node_modules/zod/v4/locales/fa.js +2 -0
  291. package/node_modules/zod/v4/locales/fi.cjs +2 -0
  292. package/node_modules/zod/v4/locales/fi.js +2 -0
  293. package/node_modules/zod/v4/locales/fr-CA.cjs +2 -0
  294. package/node_modules/zod/v4/locales/fr-CA.js +2 -0
  295. package/node_modules/zod/v4/locales/fr.cjs +2 -0
  296. package/node_modules/zod/v4/locales/fr.js +2 -0
  297. package/node_modules/zod/v4/locales/gu.cjs +2 -0
  298. package/node_modules/zod/v4/locales/gu.js +2 -0
  299. package/node_modules/zod/v4/locales/he.cjs +2 -0
  300. package/node_modules/zod/v4/locales/he.js +2 -0
  301. package/node_modules/zod/v4/locales/hi.cjs +2 -0
  302. package/node_modules/zod/v4/locales/hi.js +2 -0
  303. package/node_modules/zod/v4/locales/hr.cjs +2 -0
  304. package/node_modules/zod/v4/locales/hr.js +2 -0
  305. package/node_modules/zod/v4/locales/hu.cjs +2 -0
  306. package/node_modules/zod/v4/locales/hu.js +2 -0
  307. package/node_modules/zod/v4/locales/hy.cjs +2 -0
  308. package/node_modules/zod/v4/locales/hy.js +2 -0
  309. package/node_modules/zod/v4/locales/id.cjs +2 -0
  310. package/node_modules/zod/v4/locales/id.js +2 -0
  311. package/node_modules/zod/v4/locales/index.cjs +22 -1
  312. package/node_modules/zod/v4/locales/index.d.cts +1 -0
  313. package/node_modules/zod/v4/locales/index.d.ts +1 -0
  314. package/node_modules/zod/v4/locales/index.js +1 -0
  315. package/node_modules/zod/v4/locales/is.cjs +2 -0
  316. package/node_modules/zod/v4/locales/is.js +2 -0
  317. package/node_modules/zod/v4/locales/it.cjs +2 -0
  318. package/node_modules/zod/v4/locales/it.js +2 -0
  319. package/node_modules/zod/v4/locales/ja.cjs +2 -0
  320. package/node_modules/zod/v4/locales/ja.js +2 -0
  321. package/node_modules/zod/v4/locales/ka.cjs +2 -0
  322. package/node_modules/zod/v4/locales/ka.js +2 -0
  323. package/node_modules/zod/v4/locales/km.cjs +2 -0
  324. package/node_modules/zod/v4/locales/km.js +2 -0
  325. package/node_modules/zod/v4/locales/kn.cjs +2 -0
  326. package/node_modules/zod/v4/locales/kn.js +2 -0
  327. package/node_modules/zod/v4/locales/ko.cjs +2 -0
  328. package/node_modules/zod/v4/locales/ko.js +2 -0
  329. package/node_modules/zod/v4/locales/lt.cjs +2 -0
  330. package/node_modules/zod/v4/locales/lt.js +2 -0
  331. package/node_modules/zod/v4/locales/mk.cjs +2 -0
  332. package/node_modules/zod/v4/locales/mk.js +2 -0
  333. package/node_modules/zod/v4/locales/ms.cjs +2 -0
  334. package/node_modules/zod/v4/locales/ms.js +2 -0
  335. package/node_modules/zod/v4/locales/ne.cjs +2 -0
  336. package/node_modules/zod/v4/locales/ne.js +2 -0
  337. package/node_modules/zod/v4/locales/nl.cjs +2 -0
  338. package/node_modules/zod/v4/locales/nl.js +2 -0
  339. package/node_modules/zod/v4/locales/nn.cjs +2 -0
  340. package/node_modules/zod/v4/locales/nn.js +2 -0
  341. package/node_modules/zod/v4/locales/no.cjs +2 -0
  342. package/node_modules/zod/v4/locales/no.js +2 -0
  343. package/node_modules/zod/v4/locales/ota.cjs +2 -0
  344. package/node_modules/zod/v4/locales/ota.js +2 -0
  345. package/node_modules/zod/v4/locales/pl.cjs +2 -0
  346. package/node_modules/zod/v4/locales/pl.js +2 -0
  347. package/node_modules/zod/v4/locales/ps.cjs +2 -0
  348. package/node_modules/zod/v4/locales/ps.js +2 -0
  349. package/node_modules/zod/v4/locales/pt-BR.cjs +2 -0
  350. package/node_modules/zod/v4/locales/pt-BR.js +2 -0
  351. package/node_modules/zod/v4/locales/pt.cjs +2 -0
  352. package/node_modules/zod/v4/locales/pt.js +2 -0
  353. package/node_modules/zod/v4/locales/ro.cjs +2 -0
  354. package/node_modules/zod/v4/locales/ro.js +2 -0
  355. package/node_modules/zod/v4/locales/ru.cjs +2 -0
  356. package/node_modules/zod/v4/locales/ru.js +2 -0
  357. package/node_modules/zod/v4/locales/sk.cjs +2 -0
  358. package/node_modules/zod/v4/locales/sk.js +2 -0
  359. package/node_modules/zod/v4/locales/sl.cjs +2 -0
  360. package/node_modules/zod/v4/locales/sl.js +2 -0
  361. package/node_modules/zod/v4/locales/sv.cjs +2 -0
  362. package/node_modules/zod/v4/locales/sv.js +2 -0
  363. package/node_modules/zod/v4/locales/ta.cjs +2 -0
  364. package/node_modules/zod/v4/locales/ta.js +2 -0
  365. package/node_modules/zod/v4/locales/tg.cjs +144 -0
  366. package/node_modules/zod/v4/locales/tg.d.cts +5 -0
  367. package/node_modules/zod/v4/locales/tg.d.ts +4 -0
  368. package/node_modules/zod/v4/locales/tg.js +117 -0
  369. package/node_modules/zod/v4/locales/th.cjs +2 -0
  370. package/node_modules/zod/v4/locales/th.js +2 -0
  371. package/node_modules/zod/v4/locales/tk.cjs +2 -0
  372. package/node_modules/zod/v4/locales/tk.js +2 -0
  373. package/node_modules/zod/v4/locales/tr.cjs +2 -0
  374. package/node_modules/zod/v4/locales/tr.js +2 -0
  375. package/node_modules/zod/v4/locales/uk.cjs +2 -0
  376. package/node_modules/zod/v4/locales/uk.js +2 -0
  377. package/node_modules/zod/v4/locales/ur.cjs +2 -0
  378. package/node_modules/zod/v4/locales/ur.js +2 -0
  379. package/node_modules/zod/v4/locales/uz.cjs +2 -0
  380. package/node_modules/zod/v4/locales/uz.js +2 -0
  381. package/node_modules/zod/v4/locales/vi.cjs +2 -0
  382. package/node_modules/zod/v4/locales/vi.js +2 -0
  383. package/node_modules/zod/v4/locales/yo.cjs +2 -0
  384. package/node_modules/zod/v4/locales/yo.js +2 -0
  385. package/node_modules/zod/v4/locales/zh-CN.cjs +2 -0
  386. package/node_modules/zod/v4/locales/zh-CN.js +2 -0
  387. package/node_modules/zod/v4/locales/zh-TW.cjs +2 -0
  388. package/node_modules/zod/v4/locales/zh-TW.js +2 -0
  389. package/node_modules/zod/v4/mini/checks.cjs +19 -0
  390. package/node_modules/zod/v4/mini/coerce.cjs +19 -0
  391. package/node_modules/zod/v4/mini/deep-partial.cjs +19 -0
  392. package/node_modules/zod/v4/mini/external.cjs +22 -1
  393. package/node_modules/zod/v4/mini/external.d.cts +1 -1
  394. package/node_modules/zod/v4/mini/external.d.ts +1 -1
  395. package/node_modules/zod/v4/mini/external.js +1 -1
  396. package/node_modules/zod/v4/mini/in-out.cjs +19 -0
  397. package/node_modules/zod/v4/mini/index.cjs +19 -0
  398. package/node_modules/zod/v4/mini/iso.cjs +19 -0
  399. package/node_modules/zod/v4/mini/parse.cjs +19 -0
  400. package/node_modules/zod/v4/mini/schemas.cjs +44 -8
  401. package/node_modules/zod/v4/mini/schemas.d.cts +6 -1
  402. package/node_modules/zod/v4/mini/schemas.d.ts +6 -1
  403. package/node_modules/zod/v4/mini/schemas.js +21 -6
  404. package/node_modules/zod/v4-mini/index.cjs +19 -0
  405. package/openclaw.plugin.json +18 -0
  406. package/package.json +5 -5
  407. package/dist/.setup/accounts.runtime-BpWv-6-2.mjs +0 -2
  408. package/dist/.setup/action-runtime.runtime-DwH3NdpT.mjs +0 -2
@@ -1,24 +1,24 @@
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";
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-Cf7AvWOt.mjs";
2
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";
3
+ import { A as resolveSlackUserAllowed, E as normalizeSlackAllowOwnerEntry, T as normalizeAllowListLower, o as formatSlackError } from "./probe-DiYIMPJo.mjs";
4
+ import { a as prepareSlackReply, l as resolveSlackReplyRenderPlan } from "./reply-blocks-Bm8kf6mK.mjs";
5
5
  import { n as resolveSlackReplyThreadTs } from "./thread-ts-CukseRVL.mjs";
6
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";
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-DKCtBgCI.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-Cp6omkOg.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-C-jPaGt2.mjs";
10
+ import { n as resolveSlackChannelConfig } from "./policy-BApW53pI.mjs";
11
+ import { a as resolveSlackThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as sanitizeSlackMonitorReplyPayload, s as emitSlackMessageSentHooks, t as createSlackReplyDeliveryPlan } from "./replies-BBAJxUOQ.mjs";
12
12
  import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
13
13
  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";
14
+ import { buildChannelProgressDraftLine, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createChannelProgressWorkCounter, createDraftStreamLoop, createFinalizableDraftLifecycle, createLivePreviewLifecycle, createMessageReceiptFromOutboundResults, formatChannelProgressDraftDiffStat, formatChannelProgressDraftText, formatPlanChecklistLines, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
15
15
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
16
16
  import { danger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
17
17
  import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
18
18
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
19
19
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
20
20
  import { buildTtsSupplementMediaPayload, getReplyPayloadTtsSupplement, isReplyPayloadNonTerminalToolErrorWarning, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
21
- import { asDateTimestampMs, asFiniteNumberInRange, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
21
+ import { asDateTimestampMs, asFiniteNumberInRange, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs, resolvePromptHistoryLimit } from "openclaw/plugin-sdk/number-runtime";
22
22
  import { hash } from "node:crypto";
23
23
  import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
24
24
  import { ensureConfiguredBindingRouteReady } from "openclaw/plugin-sdk/conversation-runtime";
@@ -29,17 +29,17 @@ import { formatErrorMessage, toErrorObject } from "openclaw/plugin-sdk/error-run
29
29
  import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist, shouldIncludeSupplementalContext } from "openclaw/plugin-sdk/security-runtime";
30
30
  import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
31
31
  import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
32
- import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
33
32
  import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
34
33
  import { enqueueRoutedSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
35
34
  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";
35
+ import { buildChannelInboundEventContext, buildMentionRegexes, classifyChannelInboundEvent, dispatchChannelInboundTurn, formatInboundEnvelope, formatInboundMediaUnavailableText, hasVisibleInboundReplyDispatch, implicitMentionKindWhen, isGroupThreadRouteExclusive, matchesMentionWithExplicit, readAgentRunTerminalOutcome, resolveEnvelopeFormatOptions, resolveGroupThreadConfig, resolveGroupThreadMentionFacts, resolveInboundMentionDecision, resolveInboundReplyDispatchCounts, resolveUnmentionedGroupInboundPolicy, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
37
36
  import { resolveGatewayPublicOrigin } from "openclaw/plugin-sdk/config-contracts";
38
37
  import { buildControlUiSessionPath } from "openclaw/plugin-sdk/session-discussion";
39
38
  import { createStatusReactionController, logAckFailure, logTypingFailure, resolveAckReaction, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
40
39
  import { hasControlCommand } from "openclaw/plugin-sdk/command-detection";
41
40
  import { isAbortRequestText } from "openclaw/plugin-sdk/command-primitives-runtime";
42
41
  import { shouldHandleTextCommands } from "openclaw/plugin-sdk/command-surface";
42
+ import { DEFAULT_GROUP_HISTORY_LIMIT, buildHistoryContextFromEntries, buildInboundHistoryFromEntries } from "openclaw/plugin-sdk/reply-history";
43
43
  import { createChannelPreflightAudio, formatAudioTranscriptForAgent } from "openclaw/plugin-sdk/media-understanding-runtime";
44
44
  //#region extensions/slack/src/monitor/message-handler/timestamp.ts
45
45
  const SLACK_TIMESTAMP_RE = /^\d+(?:\.\d+)?$/;
@@ -206,12 +206,12 @@ function createSlackDraftStream(params) {
206
206
  const edit = params.edit ?? editSlackMessage;
207
207
  const remove = params.remove ?? deleteSlackMessage;
208
208
  let streamMessage;
209
+ let streamGeneration = 0;
210
+ let cleanupGeneration = -1;
209
211
  let untrackConversationBoundary;
210
212
  let lastVisibleUpdate;
211
213
  let lastSentKey = "";
212
- const pendingCleanupMessages = [];
213
- let cleanupTail = Promise.resolve();
214
- const finalizedMessageIds = /* @__PURE__ */ new Set();
214
+ let preserveHumanReplies = false;
215
215
  const streamState = {
216
216
  stopped: false,
217
217
  final: false
@@ -232,8 +232,10 @@ function createSlackDraftStream(params) {
232
232
  const sentKey = `${trimmed}\n${blocks ? JSON.stringify(blocks) : ""}`;
233
233
  if (sentKey === lastSentKey) return;
234
234
  lastSentKey = sentKey;
235
+ const generation = streamGeneration;
235
236
  try {
236
237
  if (streamMessage) {
238
+ const message = streamMessage;
237
239
  await edit(streamMessage.channelId, streamMessage.messageId, trimmed, {
238
240
  cfg: params.cfg,
239
241
  token: params.token,
@@ -241,7 +243,7 @@ function createSlackDraftStream(params) {
241
243
  ...params.eventScope ? { client: params.eventScope.client } : {},
242
244
  ...blocks ? { blocks } : {}
243
245
  });
244
- lastVisibleUpdate = {
246
+ if (streamMessage === message) lastVisibleUpdate = {
245
247
  text: trimmed,
246
248
  ...blocks ? { blocks } : {}
247
249
  };
@@ -266,16 +268,22 @@ function createSlackDraftStream(params) {
266
268
  ...params.metadata ? { metadata: params.metadata } : {},
267
269
  ...blocks ? { blocks } : {}
268
270
  });
271
+ const sentMessage = {
272
+ channelId: sent.channelId,
273
+ messageId: sent.messageId,
274
+ generation
275
+ };
276
+ if (generation !== streamGeneration) {
277
+ if (sent.channelId && sent.messageId) lifecycle.retire(sentMessage, { defer: true });
278
+ return;
279
+ }
269
280
  if (!sent.channelId || !sent.messageId) {
270
281
  stopTrackingConversationBoundary();
271
282
  streamState.stopped = true;
272
283
  params.warn?.("slack stream preview stopped (missing identifiers from sendMessage)");
273
284
  return;
274
285
  }
275
- streamMessage = {
276
- channelId: sent.channelId,
277
- messageId: sent.messageId
278
- };
286
+ streamMessage = sentMessage;
279
287
  lastVisibleUpdate = {
280
288
  text: trimmed,
281
289
  ...blocks ? { blocks } : {}
@@ -293,68 +301,88 @@ function createSlackDraftStream(params) {
293
301
  }).stop;
294
302
  }
295
303
  } catch (err) {
296
- stopTrackingConversationBoundary();
297
- streamState.stopped = true;
304
+ if (generation === streamGeneration) {
305
+ stopTrackingConversationBoundary();
306
+ streamState.stopped = true;
307
+ }
298
308
  params.warn?.(`slack stream preview failed: ${formatSlackError(err)}`);
299
309
  }
300
310
  };
301
- const { loop, update, discardPending, seal } = createFinalizableDraftStreamControlsForState({
311
+ const lifecycle = createFinalizableDraftLifecycle({
302
312
  throttleMs,
303
313
  coalesceInFlight: true,
304
314
  state: streamState,
305
315
  sendOrEditStreamMessage,
306
316
  emptyValue: "",
307
- isEmpty: (value) => !normalizeUpdate(value).text.trim()
317
+ isEmpty: (value) => !normalizeUpdate(value).text.trim(),
318
+ readMessageId: () => streamMessage,
319
+ clearMessageId: () => {
320
+ streamMessage = void 0;
321
+ lastVisibleUpdate = void 0;
322
+ lastSentKey = "";
323
+ },
324
+ isValidMessageId: (value) => typeof value === "object" && value !== null,
325
+ deleteMessage: async (message) => {
326
+ if (message.generation > cleanupGeneration) return false;
327
+ if (!message.retained && !(preserveHumanReplies && message.detachedByHuman)) await remove(message.channelId, message.messageId, {
328
+ token: params.token,
329
+ accountId: params.accountId,
330
+ ...params.eventScope ? { client: params.eventScope.client } : {}
331
+ });
332
+ return true;
333
+ },
334
+ warn: params.warn,
335
+ warnPrefix: "slack stream preview cleanup failed"
308
336
  });
337
+ const { loop, update, discardPending, seal } = lifecycle;
309
338
  const stopTrackingConversationBoundary = () => {
310
339
  untrackConversationBoundary?.();
311
340
  untrackConversationBoundary = void 0;
312
341
  };
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
- }
342
+ const dropDetachedMessages = () => {
343
+ const generation = streamGeneration;
344
+ return lifecycle.cleanupPending(() => {
345
+ preserveHumanReplies = false;
346
+ cleanupGeneration = generation;
334
347
  });
335
- return cleanupTail;
336
348
  };
337
349
  const discardPendingAndStopTracking = async () => {
350
+ const generation = streamGeneration;
338
351
  await discardPending();
339
- stopTrackingConversationBoundary();
352
+ if (generation === streamGeneration) stopTrackingConversationBoundary();
340
353
  };
341
354
  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);
355
+ const generation = streamGeneration;
356
+ let clearingMessage = streamMessage;
357
+ await lifecycle.clearWithStop(async () => {
358
+ if (generation === streamGeneration) {
359
+ await discardPendingAndStopTracking();
360
+ if (generation === streamGeneration) {
361
+ clearingMessage = streamMessage;
362
+ streamMessage = void 0;
363
+ lastVisibleUpdate = void 0;
364
+ lastSentKey = "";
365
+ }
366
+ }
367
+ cleanupGeneration = generation;
368
+ preserveHumanReplies = options?.preserveHumanReplies === true;
369
+ }, {
370
+ readMessageId: () => clearingMessage,
371
+ clearMessageId: () => {
372
+ clearingMessage = void 0;
373
+ }
374
+ });
350
375
  };
351
376
  const forceNewMessage = (reason = "turn") => {
352
377
  stopTrackingConversationBoundary();
353
- if (reason === "turn") streamState.stopped = false;
378
+ if (reason === "turn") {
379
+ streamState.stopped = false;
380
+ streamGeneration += 1;
381
+ }
354
382
  streamState.final = false;
355
- if (streamMessage && !finalizedMessageIds.has(streamMessage.messageId)) {
383
+ if (streamMessage && !streamMessage.retained) {
356
384
  streamMessage.detachedByHuman = reason === "human";
357
- pendingCleanupMessages.push(streamMessage);
385
+ lifecycle.retire(streamMessage, { defer: true });
358
386
  }
359
387
  streamMessage = void 0;
360
388
  lastVisibleUpdate = void 0;
@@ -368,7 +396,7 @@ function createSlackDraftStream(params) {
368
396
  const { channelId } = currentMessage;
369
397
  await editFinal();
370
398
  if (streamMessage?.channelId === channelId && streamMessage.messageId === messageId) {
371
- finalizedMessageIds.add(messageId);
399
+ currentMessage.retained = true;
372
400
  stopTrackingConversationBoundary();
373
401
  return true;
374
402
  }
@@ -1174,7 +1202,7 @@ function createSlackNativeProgressTransport(params) {
1174
1202
  //#endregion
1175
1203
  //#region extensions/slack/src/monitor/message-handler/dispatch-progress.ts
1176
1204
  function createSlackProgressRuntime(runtimeParams) {
1177
- const { setup, delivery, resetPreviewDeliveryState } = runtimeParams;
1205
+ const { setup, delivery } = runtimeParams;
1178
1206
  const { account, cfg, ctx, hasSlackCustomIdentity, message, prepared, replyPlan, runtime, slackClient, slackIdentity, slackMessageMetadata, slackStreaming, shouldUseDraftStream, useStreaming, previewStreamingEnabled } = setup;
1179
1207
  const draftStream = shouldUseDraftStream ? createSlackDraftStream({
1180
1208
  target: prepared.replyTarget,
@@ -1347,21 +1375,21 @@ function createSlackProgressRuntime(runtimeParams) {
1347
1375
  };
1348
1376
  const appendNativeNarration = (payload, kind) => withNativeStreamOrder(() => appendNativeNarrationNow(payload, kind));
1349
1377
  const appendNativeNarrationNow = async (payload, kind) => {
1350
- if (isRenderedAsProgressTitle(payload.text)) return false;
1378
+ if (isRenderedAsProgressTitle(payload.text)) return { visibleReplySent: false };
1351
1379
  const narrationUpdate = resolveNarrationUpdate(payload.text?.trimEnd());
1352
- if (!narrationUpdate.delta) return false;
1353
- await delivery.deliverWithStreaming({
1380
+ if (!narrationUpdate.delta) return { visibleReplySent: false };
1381
+ const result = await delivery.deliverWithStreaming({
1354
1382
  payload,
1355
1383
  kind,
1356
1384
  streamText: narrationUpdate.delta,
1357
1385
  appendSeparator: false,
1358
1386
  taskDisplayMode: "plan"
1359
1387
  });
1360
- if (!delivery.streamFailed) {
1388
+ if (result.visibleReplySent && !delivery.streamFailed) {
1361
1389
  nativeNarrationRenderedText = narrationUpdate.next.rendered;
1362
1390
  nativeNarrationSourceText = narrationUpdate.next.source;
1363
1391
  }
1364
- return true;
1392
+ return result;
1365
1393
  };
1366
1394
  const resetProgressTurnState = () => {
1367
1395
  progressWorkCounter.reset();
@@ -1369,6 +1397,7 @@ function createSlackProgressRuntime(runtimeParams) {
1369
1397
  nativeNarrationSourceText = "";
1370
1398
  };
1371
1399
  const progressDraft = createChannelProgressDraftCompositor({
1400
+ preparedItems: true,
1372
1401
  entry: account.config,
1373
1402
  mode: slackStreaming.mode,
1374
1403
  active: progressDraftActive,
@@ -1415,31 +1444,50 @@ function createSlackProgressRuntime(runtimeParams) {
1415
1444
  }
1416
1445
  }
1417
1446
  });
1447
+ const previewLifecycle = createLivePreviewLifecycle({
1448
+ draft: draftStream && !useDraftProgressCard ? {
1449
+ flush: draftStream.flush,
1450
+ discardPending: draftStream.discardPending,
1451
+ seal: draftStream.seal,
1452
+ id: () => {
1453
+ const channelId = draftStream.channelId();
1454
+ const messageId = draftStream.messageId();
1455
+ return channelId && messageId ? {
1456
+ channelId,
1457
+ messageId
1458
+ } : void 0;
1459
+ },
1460
+ clear: async () => {
1461
+ await draftStream.clear({ preserveHumanReplies: !isProgressMode && previewLifecycle.finalDelivered });
1462
+ }
1463
+ } : void 0,
1464
+ cleanupUndelivered: true,
1465
+ onFinalStarted: () => progressDraft.markFinalReplyStarted(),
1466
+ onFinalDelivered: () => progressDraft.markFinalReplyDelivered(),
1467
+ onCleanupFailure: (error) => logVerbose(`slack: progress preview cleanup failed (${formatSlackError(error)})`)
1468
+ });
1418
1469
  const commentaryProgressEnabled = progressDraft.commentaryProgressEnabled;
1419
1470
  const deliverNativeFinal = async (payload, kind) => {
1420
- progressDraft.markFinalReplyStarted();
1421
1471
  await cancelNativeUpdates();
1422
- await withNativeStreamOrder(() => deliverNativeFinalNow(payload, kind));
1472
+ return await withNativeStreamOrder(() => deliverNativeFinalNow(payload, kind));
1423
1473
  };
1424
1474
  const deliverNativeFinalNow = async (payload, kind) => {
1425
- progressDraft.markFinalReplyStarted();
1426
1475
  const streamReady = await nativeTransport.waitForStart();
1427
1476
  const finalThreadTs = delivery.streamSession?.threadTs ?? delivery.nativeProgressStreamThreadTs;
1428
1477
  if (payload.isError !== true && streamReady && Boolean(delivery.streamSession) && delivery.isStreamingEligible(payload, { maxTextBytes: 4e3 })) {
1429
1478
  await appendNativeProgressCompletion(false);
1430
- await delivery.deliverWithStreaming({
1479
+ return await delivery.deliverWithStreaming({
1431
1480
  payload,
1432
1481
  kind
1433
1482
  });
1434
- } else {
1435
- await delivery.deliverNormally({
1436
- payload,
1437
- kind,
1438
- forcedThreadTs: finalThreadTs
1439
- });
1440
- await appendNativeProgressCompletion(payload.isError === true);
1441
1483
  }
1442
- progressDraft.markFinalReplyDelivered();
1484
+ const result = await delivery.deliverNormally({
1485
+ payload,
1486
+ kind,
1487
+ forcedThreadTs: finalThreadTs
1488
+ });
1489
+ await appendNativeProgressCompletion(payload.isError === true);
1490
+ return result;
1443
1491
  };
1444
1492
  const buildNativeProgressCompletionChunks = (finalInProgressStatus) => {
1445
1493
  const snapshot = progressDraft.getSnapshot();
@@ -1536,6 +1584,7 @@ function createSlackProgressRuntime(runtimeParams) {
1536
1584
  if (useNativeProgressStreaming) await finishNativeProgressTurn(completionChunks);
1537
1585
  else {
1538
1586
  await progressCard.finalize("success", priorSnapshot, priorFallbackText);
1587
+ await previewLifecycle.cleanup();
1539
1588
  draftStream?.forceNewMessage();
1540
1589
  await dropDetachedProgressCards();
1541
1590
  }
@@ -1546,7 +1595,7 @@ function createSlackProgressRuntime(runtimeParams) {
1546
1595
  nativeUpdatesStopped = false;
1547
1596
  nativeUpdates.resetThrottleWindow();
1548
1597
  progressCard.reset();
1549
- resetPreviewDeliveryState();
1598
+ previewLifecycle.reset();
1550
1599
  delivery.resetDeliveryTracker();
1551
1600
  return true;
1552
1601
  };
@@ -1561,26 +1610,26 @@ function createSlackProgressRuntime(runtimeParams) {
1561
1610
  };
1562
1611
  const onQueuedFollowupAdmitted = !shouldUseDraftStream && !useNativeProgressStreaming ? void 0 : async () => {
1563
1612
  await draftStream?.flush();
1564
- resetPreviewDeliveryState();
1565
1613
  if (isProgressMode) await beginNewProgressTurn({ force: true });
1566
- else draftStream?.forceNewMessage();
1614
+ else {
1615
+ await previewLifecycle.cleanup();
1616
+ previewLifecycle.reset();
1617
+ draftStream?.forceNewMessage();
1618
+ }
1567
1619
  delivery.resetDeliveryTracker();
1568
1620
  resetDraftDeliveryState();
1569
1621
  progressDraft.reset();
1570
1622
  };
1571
- const onQueuedFollowupSettled = !useDraftProgressCard && !useNativeProgressStreaming && !preambleOnlyProgress ? void 0 : async () => {
1623
+ const onQueuedFollowupSettled = !draftStream && !useNativeProgressStreaming ? void 0 : async () => {
1572
1624
  if (useNativeProgressStreaming) {
1573
1625
  progressDraft.markFinalReplyStarted();
1574
1626
  await cancelNativeUpdates();
1575
1627
  await finishNativeProgressTurn(nativeProgressCompletionSent ? void 0 : buildNativeProgressCompletionChunks(nativeProgressTerminalStatus));
1576
- progressDraft.markFinalReplyDelivered();
1577
1628
  return;
1578
1629
  }
1579
- if (preambleOnlyProgress) {
1630
+ if (!useDraftProgressCard) {
1580
1631
  progressDraft.markFinalReplyStarted();
1581
- await draftStream?.clear();
1582
- await draftStream?.dropDetachedMessages();
1583
- progressDraft.markFinalReplyDelivered();
1632
+ await previewLifecycle.cleanup();
1584
1633
  return;
1585
1634
  }
1586
1635
  if (!progressCard.hasTerminalized) await draftStream?.clear();
@@ -1588,6 +1637,7 @@ function createSlackProgressRuntime(runtimeParams) {
1588
1637
  };
1589
1638
  return {
1590
1639
  draftStream,
1640
+ previewLifecycle,
1591
1641
  isProgressMode,
1592
1642
  useDraftProgressCard,
1593
1643
  useNativeProgressStreaming,
@@ -1701,6 +1751,7 @@ async function createSlackDispatchSetup(prepared) {
1701
1751
  const messageTs = message.ts ?? message.event_ts;
1702
1752
  const incomingThreadTs = message.thread_ts;
1703
1753
  let didSetStatus = false;
1754
+ let statusWasSet = false;
1704
1755
  let didAddTypingReaction = false;
1705
1756
  const statusReactionsEnabled = prepared.ctxPayload.InboundEventKind !== "room_event" && Boolean(prepared.ackReactionPromise) && Boolean(reactionMessageTs) && cfg.messages?.statusReactions?.enabled === true;
1706
1757
  const statusReactions = createStatusReactionController({
@@ -1752,7 +1803,7 @@ async function createSlackDispatchSetup(prepared) {
1752
1803
  start: async () => {
1753
1804
  if (!didSetStatus && !threadStatusGate.hasVisibleOutput()) {
1754
1805
  didSetStatus = true;
1755
- await ctx.setSlackSessionStatus({
1806
+ statusWasSet = await ctx.setSlackSessionStatus({
1756
1807
  channelId: message.channel,
1757
1808
  threadTs: statusThreadTs,
1758
1809
  status: "processing",
@@ -1773,12 +1824,17 @@ async function createSlackDispatchSetup(prepared) {
1773
1824
  stop: async () => {
1774
1825
  if (didSetStatus) {
1775
1826
  didSetStatus = false;
1776
- await ctx.setSlackSessionStatus({
1827
+ const reportFailure = statusWasSet;
1828
+ statusWasSet = false;
1829
+ const restored = await ctx.setSlackSessionStatus({
1777
1830
  channelId: message.channel,
1778
1831
  threadTs: statusThreadTs,
1779
1832
  status: "active",
1780
1833
  eventScope: prepared.eventScope
1781
1834
  });
1835
+ if (reportFailure && !restored) try {
1836
+ runtime.error?.("Slack session status could not return to active after processing. Enable verbose logging to inspect the Slack API failure.");
1837
+ } catch {}
1782
1838
  }
1783
1839
  if (didAddTypingReaction && typingReaction && message.ts) {
1784
1840
  didAddTypingReaction = false;
@@ -1888,8 +1944,7 @@ function createSlackStreamingDeliveryRuntime(setup) {
1888
1944
  streamFailed: false,
1889
1945
  usedReplyThreadTs: void 0,
1890
1946
  usedBlockReplyThreadTs: void 0,
1891
- observedReplyDelivery: false,
1892
- observedFinalReplyDelivery: false
1947
+ observedReplyDelivery: false
1893
1948
  };
1894
1949
  const emitStreamedDelivery = (content, result) => {
1895
1950
  emitSlackMessageSentHooks({
@@ -1930,14 +1985,18 @@ function createSlackStreamingDeliveryRuntime(setup) {
1930
1985
  if (session.stoppedBySlack) return;
1931
1986
  state.observedReplyDelivery = true;
1932
1987
  state.usedReplyThreadTs ??= session.threadTs;
1933
- return stopResult;
1988
+ return {
1989
+ visibleReplySent: session.delivered,
1990
+ ...stopResult.messageId ? { messageIds: [stopResult.messageId] } : {},
1991
+ threadId: session.threadTs
1992
+ };
1934
1993
  } catch (stopErr) {
1935
1994
  if (stopErr instanceof SlackStreamNotDeliveredError) fallbackError = stopErr;
1936
1995
  else throw stopErr;
1937
1996
  }
1938
1997
  const fallbackText = fallbackError.pendingText.trim();
1939
1998
  if (!fallbackText) return;
1940
- await deliverReplies({
1999
+ const sent = await deliverReplies({
1941
2000
  cfg: ctx.cfg,
1942
2001
  replies: [prepareSlackReply({ text: fallbackText })],
1943
2002
  target: prepared.replyTarget,
@@ -1954,6 +2013,7 @@ function createSlackStreamingDeliveryRuntime(setup) {
1954
2013
  deferMessageSentHooks: true,
1955
2014
  eventScope: prepared.eventScope
1956
2015
  });
2016
+ if (!sent?.receipt.platformMessageIds.length) return;
1957
2017
  markSlackStreamFallbackDelivered(session);
1958
2018
  if (!session.stopped) try {
1959
2019
  await stopSlackStream({
@@ -1966,7 +2026,12 @@ function createSlackStreamingDeliveryRuntime(setup) {
1966
2026
  state.observedReplyDelivery = true;
1967
2027
  state.usedReplyThreadTs ??= session.threadTs;
1968
2028
  logVerbose(`slack-stream: streamed delivery failed (${fallbackError.slackCode}); delivered ${fallbackText.length} chars via deliverReplies fallback`);
1969
- return {};
2029
+ return {
2030
+ visibleReplySent: true,
2031
+ messageIds: sent.receipt.platformMessageIds,
2032
+ receipt: sent.receipt,
2033
+ threadId: sent.threadTs ?? session.threadTs
2034
+ };
1970
2035
  };
1971
2036
  const finishStream = async (chunks) => {
1972
2037
  const session = state.streamSession;
@@ -1988,7 +2053,7 @@ function createSlackStreamingDeliveryRuntime(setup) {
1988
2053
  }
1989
2054
  };
1990
2055
  const deliverNormally = async (params) => {
1991
- if (state.streamSession?.stoppedBySlack) return;
2056
+ if (state.streamSession?.stoppedBySlack) return { visibleReplySent: false };
1992
2057
  const replyThreadTs = resolveDeliveryThreadTs(params);
1993
2058
  const deliveryReplyThreadTs = replyDeliveryMode === "off" && !forcedReplyThreadTs && !isThreadReply ? void 0 : replyThreadTs;
1994
2059
  const preparedReply = prepareSlackReply(params.payload);
@@ -1999,9 +2064,9 @@ function createSlackStreamingDeliveryRuntime(setup) {
1999
2064
  }, preparedReply);
2000
2065
  if (deliveryTracker.hasDelivered(deliveryKey)) {
2001
2066
  logVerbose("slack: suppressed duplicate normal delivery within the same turn");
2002
- return deliveryReplyThreadTs;
2067
+ return { visibleReplySent: false };
2003
2068
  }
2004
- await deliverReplies({
2069
+ const sent = await deliverReplies({
2005
2070
  cfg: ctx.cfg,
2006
2071
  replies: [preparedReply],
2007
2072
  target: prepared.replyTarget,
@@ -2017,8 +2082,8 @@ function createSlackStreamingDeliveryRuntime(setup) {
2017
2082
  ...messageSentDeliveryHookContext,
2018
2083
  eventScope: prepared.eventScope
2019
2084
  });
2085
+ if (!sent?.receipt.platformMessageIds.length) return { visibleReplySent: false };
2020
2086
  state.observedReplyDelivery = true;
2021
- if (params.kind === "final") state.observedFinalReplyDelivery = true;
2022
2087
  const deliveredThreadTs = resolveSlackReplyThreadTs({
2023
2088
  replyToMode: replyDeliveryMode,
2024
2089
  replyToId: params.payload.replyToId,
@@ -2028,7 +2093,12 @@ function createSlackStreamingDeliveryRuntime(setup) {
2028
2093
  rememberDeliveredThreadTs(params.kind, deliveredThreadTs);
2029
2094
  replyPlan.markSent();
2030
2095
  deliveryTracker.markDelivered(deliveryKey);
2031
- return deliveryReplyThreadTs;
2096
+ return {
2097
+ visibleReplySent: true,
2098
+ messageIds: sent.receipt.platformMessageIds,
2099
+ receipt: sent.receipt,
2100
+ threadId: sent.threadTs ?? deliveredThreadTs
2101
+ };
2032
2102
  };
2033
2103
  const isStreamingEligible = (payload, options) => {
2034
2104
  const reply = resolveSendableOutboundReplyParts(payload);
@@ -2037,27 +2107,23 @@ function createSlackStreamingDeliveryRuntime(setup) {
2037
2107
  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
2108
  };
2039
2109
  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
- }
2110
+ if (state.streamSession?.stoppedBySlack) return { visibleReplySent: false };
2111
+ if (!isStreamingEligible(params.payload)) return await deliverNormally({
2112
+ payload: params.payload,
2113
+ kind: params.kind,
2114
+ forcedThreadTs: state.streamSession?.threadTs ?? state.nativeProgressStreamThreadTs
2115
+ });
2049
2116
  if (!state.streamSession && state.nativeProgressStreamStartPromise) await state.nativeProgressStreamStartPromise;
2050
- if (state.streamSession?.stoppedBySlack) return;
2117
+ if (state.streamSession?.stoppedBySlack) return { visibleReplySent: false };
2051
2118
  let session = state.streamSession;
2052
2119
  const threadTs = session?.threadTs ?? replyPlan.nextThreadTs();
2053
2120
  if (state.streamFailed || !threadTs) {
2054
2121
  state.streamFailed = true;
2055
- await deliverNormally({
2122
+ return await deliverNormally({
2056
2123
  payload: params.payload,
2057
2124
  kind: params.kind,
2058
2125
  forcedThreadTs: threadTs ?? state.nativeProgressStreamThreadTs
2059
2126
  });
2060
- return;
2061
2127
  }
2062
2128
  const hookContent = resolveSendableOutboundReplyParts(params.payload).trimmedText;
2063
2129
  const text = params.streamText ?? hookContent;
@@ -2068,9 +2134,10 @@ function createSlackStreamingDeliveryRuntime(setup) {
2068
2134
  });
2069
2135
  if (deliveryTracker.hasDelivered(deliveryKey)) {
2070
2136
  logVerbose("slack-stream: suppressed duplicate reply payload");
2071
- return;
2137
+ return { visibleReplySent: false };
2072
2138
  }
2073
2139
  let messageId;
2140
+ let fallbackDelivery;
2074
2141
  try {
2075
2142
  if (session) await appendSlackStream({
2076
2143
  session,
@@ -2107,18 +2174,16 @@ function createSlackStreamingDeliveryRuntime(setup) {
2107
2174
  });
2108
2175
  throw error;
2109
2176
  }
2110
- if (!session) {
2111
- await deliverNormally({
2112
- payload: params.payload,
2113
- kind: params.kind,
2114
- forcedThreadTs: threadTs
2115
- });
2116
- return;
2117
- }
2177
+ if (!session) return await deliverNormally({
2178
+ payload: params.payload,
2179
+ kind: params.kind,
2180
+ forcedThreadTs: threadTs
2181
+ });
2118
2182
  try {
2119
2183
  const fallback = await deliverPendingStreamFallback(session, error);
2120
2184
  if (!fallback && !session.stoppedBySlack) throw error;
2121
- messageId = fallback?.messageId;
2185
+ fallbackDelivery = fallback;
2186
+ messageId = fallback?.receipt ? void 0 : fallback?.messageIds?.[0];
2122
2187
  } catch (fallbackError) {
2123
2188
  emitStreamedDelivery(hookContent, {
2124
2189
  success: false,
@@ -2132,10 +2197,10 @@ function createSlackStreamingDeliveryRuntime(setup) {
2132
2197
  success: false,
2133
2198
  error: "Stopped by Slack user"
2134
2199
  });
2135
- return;
2200
+ return { visibleReplySent: false };
2136
2201
  }
2202
+ if (!fallbackDelivery?.visibleReplySent && (!session.delivered || session.pendingText)) return { visibleReplySent: false };
2137
2203
  state.observedReplyDelivery = true;
2138
- if (params.kind === "final") state.observedFinalReplyDelivery = true;
2139
2204
  rememberDeliveredThreadTs(params.kind, threadTs);
2140
2205
  replyPlan.markSent();
2141
2206
  deliveryTracker.markDelivered(deliveryKey);
@@ -2143,12 +2208,16 @@ function createSlackStreamingDeliveryRuntime(setup) {
2143
2208
  success: true,
2144
2209
  ...messageId ? { messageId } : {}
2145
2210
  });
2211
+ return fallbackDelivery ?? {
2212
+ visibleReplySent: true,
2213
+ ...messageId ? { messageIds: [messageId] } : {},
2214
+ threadId: threadTs
2215
+ };
2146
2216
  };
2147
2217
  return Object.assign(state, {
2148
2218
  deliverNormally,
2149
2219
  deliverWithStreaming,
2150
2220
  finishStream,
2151
- hasDelivered: (params) => deliveryTracker.hasDelivered(buildSlackEventDeliveryKey(params)),
2152
2221
  isStreamingEligible,
2153
2222
  markPreviewPayloadDelivered,
2154
2223
  rememberDeliveredThreadTs,
@@ -2194,17 +2263,12 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2194
2263
  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
2264
  let dispatchError;
2196
2265
  const delivery = createSlackStreamingDeliveryRuntime(setup);
2197
- const draftPreviewCommitted = { value: false };
2198
2266
  const progress = createSlackProgressRuntime({
2199
2267
  setup,
2200
- delivery,
2201
- resetPreviewDeliveryState: () => {
2202
- draftPreviewCommitted.value = false;
2203
- delivery.observedFinalReplyDelivery = false;
2204
- }
2268
+ delivery
2205
2269
  });
2206
- const draftStream = progress.draftStream;
2207
- setup.threadStatusGate.hasVisibleOutput = () => delivery.observedReplyDelivery || draftPreviewCommitted.value || Boolean(draftStream?.messageId());
2270
+ const { draftStream, previewLifecycle } = progress;
2271
+ setup.threadStatusGate.hasVisibleOutput = () => delivery.observedReplyDelivery || previewLifecycle.previewFinalized || Boolean(draftStream?.messageId());
2208
2272
  const failureNoticeThreadTs = message.thread_ts;
2209
2273
  const failureNoticeTeamId = prepared.eventScope?.teamId;
2210
2274
  let sawTerminalFailurePayload = false;
@@ -2212,7 +2276,7 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2212
2276
  const filterPassiveThreadFailure = (payload) => {
2213
2277
  if (payload.isError !== true || prepared.ctxPayload.ChatType !== "channel" || isReplyPayloadNonTerminalToolErrorWarning(payload)) return payload;
2214
2278
  sawTerminalFailurePayload = true;
2215
- if (delivery.observedReplyDelivery || draftPreviewCommitted.value) return payload;
2279
+ if (delivery.observedReplyDelivery || previewLifecycle.previewFinalized) return payload;
2216
2280
  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
2281
  const noticeThreadTs = failureNoticeThreadTs ?? (explicitlyAddressed ? statusThreadTs : void 0);
2218
2282
  const notice = {
@@ -2233,14 +2297,6 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2233
2297
  pendingFailureNotice = notice;
2234
2298
  return payload;
2235
2299
  };
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
2300
  const deliverSlackPayload = async (incomingPayload, info) => {
2245
2301
  let payload = incomingPayload;
2246
2302
  if (info.participant && (payload.text || payload.mediaUrl || payload.mediaUrls?.length)) payload = {
@@ -2248,66 +2304,49 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2248
2304
  text: formatSlackGroupThreadReply(payload.text ?? "", info.participant)
2249
2305
  };
2250
2306
  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,
2307
+ const supplement = getReplyPayloadTtsSupplement(payload);
2308
+ const finalPayload = !progress.useDraftProgressCard && !progress.useNativeProgressStreaming && supplement && !supplement.visibleTextAlreadyDelivered && !payload.text?.trim() ? {
2309
+ ...payload,
2310
+ text: supplement.spokenText
2311
+ } : payload;
2312
+ return (await previewLifecycle.deliver({
2313
+ kind: info.kind,
2314
+ payload: finalPayload,
2315
+ isError: payload.isError === true,
2316
+ deliverNormally: (reply) => progress.useNativeProgressStreaming ? progress.deliverNativeFinal(reply, info.kind) : delivery.deliverNormally({
2317
+ payload: reply,
2277
2318
  kind: info.kind,
2278
2319
  forcedThreadTs: delivery.usedReplyThreadTs
2279
- });
2280
- if (!await progress.finalizeDraftProgressCard(payload.isError === true ? "error" : "success")) await draftStream?.clear();
2281
- progress.progressDraft.markFinalReplyDelivered();
2282
- return;
2283
- }
2320
+ }),
2321
+ onNormalDelivered: progress.useDraftProgressCard ? async () => {
2322
+ if (!await progress.finalizeDraftProgressCard(payload.isError === true ? "error" : "success")) await draftStream?.clear();
2323
+ } : void 0
2324
+ })).deliveryResult ?? { visibleReplySent: false };
2284
2325
  }
2285
2326
  if (progress.useNativeProgressStreaming) {
2286
2327
  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 };
2328
+ if (!delivery.isStreamingEligible(payload)) return await delivery.deliverNormally({
2329
+ payload,
2330
+ kind: info.kind,
2331
+ forcedThreadTs: delivery.streamSession?.threadTs ?? delivery.nativeProgressStreamThreadTs
2332
+ });
2333
+ return await progress.appendNativeNarration(payload, info.kind);
2296
2334
  }
2297
- await delivery.deliverNormally({
2335
+ return await delivery.deliverNormally({
2298
2336
  payload,
2299
2337
  kind: info.kind,
2300
2338
  forcedThreadTs: delivery.streamSession?.threadTs ?? delivery.nativeProgressStreamThreadTs
2301
2339
  });
2302
- return;
2303
2340
  }
2304
- if (useStreaming) {
2305
- await delivery.deliverWithStreaming({
2306
- payload,
2341
+ if (useStreaming) return (await previewLifecycle.deliver({
2342
+ kind: info.kind,
2343
+ payload,
2344
+ isError: payload.isError === true,
2345
+ deliverNormally: (reply) => delivery.deliverWithStreaming({
2346
+ payload: reply,
2307
2347
  kind: info.kind
2308
- });
2309
- return;
2310
- }
2348
+ })
2349
+ })).deliveryResult ?? { visibleReplySent: false };
2311
2350
  const reply = resolveSendableOutboundReplyParts(payload);
2312
2351
  const ttsSupplement = getReplyPayloadTtsSupplement(payload);
2313
2352
  const replySourceText = payload.text ?? ttsSupplement?.spokenText;
@@ -2321,26 +2360,13 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2321
2360
  accountId: account.accountId
2322
2361
  }) });
2323
2362
  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();
2363
+ const shouldRestoreTtsSupplementTextForPreviewFallback = Boolean(ttsSupplement) && ttsSupplement?.visibleTextAlreadyDelivered !== true && Boolean(draftStream) && !previewLifecycle.previewFinalized && !previewLifecycle.finalDelivered && previewStreamingEnabled && !payload.text?.trim();
2325
2364
  let ttsPreviewFinalization;
2326
- await deliverWithFinalizableLivePreviewAdapter({
2365
+ return (await previewLifecycle.deliver({
2327
2366
  kind: info.kind,
2328
2367
  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,
2368
+ isError: payload.isError === true,
2369
+ adapter: {
2344
2370
  buildFinalEdit: () => {
2345
2371
  if (hasSlackCustomIdentity || !previewStreamingEnabled || reply.hasMedia && !ttsSupplement || payload.isError || requiresSeparateFallbackDelivery || !previewFinalTextFitsEdit || trimmedFinalText.length === 0 && !slackBlocks?.length) return;
2346
2372
  return {
@@ -2350,11 +2376,6 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2350
2376
  };
2351
2377
  },
2352
2378
  editFinal: async (preview, edit) => {
2353
- if (delivery.hasDelivered({
2354
- kind: info.kind,
2355
- payload,
2356
- threadTs: edit.threadTs
2357
- })) return;
2358
2379
  if (ttsSupplement) ttsPreviewFinalization = { threadTs: edit.threadTs };
2359
2380
  if (!await draftStream?.finalizeMessage(preview.messageId, async () => {
2360
2381
  await finalizeSlackPreviewEdit({
@@ -2368,59 +2389,66 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2368
2389
  threadTs: edit.threadTs
2369
2390
  });
2370
2391
  })) 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
2392
  },
2382
- onPreviewFinalized: (_preview) => {
2383
- draftPreviewCommitted.value = true;
2384
- delivery.observedFinalReplyDelivery = true;
2393
+ createPreviewReceipt: (preview, edit) => createMessageReceiptFromOutboundResults({
2394
+ results: [{
2395
+ channel: "slack",
2396
+ channelId: preview.channelId,
2397
+ messageId: preview.messageId
2398
+ }],
2399
+ threadId: edit.threadTs,
2400
+ kind: "text"
2401
+ }),
2402
+ onPreviewFinalized: (preview) => {
2385
2403
  const finalThreadTs = delivery.usedReplyThreadTs ?? statusThreadTs;
2386
2404
  delivery.observedReplyDelivery = true;
2387
2405
  replyPlan.markSent();
2388
- if (!ttsSupplement) delivery.markPreviewPayloadDelivered({
2389
- kind: info.kind,
2390
- payload,
2391
- threadTs: finalThreadTs
2392
- });
2406
+ if (!ttsSupplement) {
2407
+ delivery.markPreviewPayloadDelivered({
2408
+ kind: info.kind,
2409
+ payload,
2410
+ threadTs: finalThreadTs
2411
+ });
2412
+ emitSlackMessageSentHooks({
2413
+ ...messageSentHookContext,
2414
+ to: messageSentHookTarget,
2415
+ accountId: account.accountId,
2416
+ content: trimmedFinalText,
2417
+ success: true,
2418
+ messageId: preview.messageId
2419
+ });
2420
+ }
2393
2421
  },
2394
2422
  buildSupplementalPayload: () => ttsSupplement ? buildTtsSupplementMediaPayload(payload) : void 0,
2395
2423
  deliverSupplemental: async (supplementalPayload) => {
2396
2424
  const previewThreadTs = delivery.usedReplyThreadTs ?? statusThreadTs;
2397
- const supplementalThreadTs = await delivery.deliverNormally({
2425
+ const supplementalResult = await delivery.deliverNormally({
2398
2426
  payload: supplementalPayload,
2399
2427
  kind: info.kind,
2400
2428
  forcedThreadTs: previewThreadTs
2401
2429
  });
2402
- delivery.markPreviewPayloadDelivered({
2430
+ if (supplementalResult.visibleReplySent) delivery.markPreviewPayloadDelivered({
2403
2431
  kind: info.kind,
2404
2432
  payload,
2405
- threadTs: supplementalThreadTs
2433
+ threadTs: supplementalResult.threadId
2406
2434
  });
2435
+ return supplementalResult;
2407
2436
  },
2408
2437
  logPreviewEditFailure: (err) => {
2409
2438
  logVerbose(`slack: preview final edit failed; falling back to standard send (${formatSlackError(err)})`);
2410
2439
  }
2411
- }),
2412
- deliverNormally: async () => {
2413
- await delivery.deliverNormally({
2414
- payload: shouldRestoreTtsSupplementTextForPreviewFallback || ttsPreviewFinalization && !payload.text?.trim() ? {
2415
- ...payload,
2440
+ },
2441
+ deliverNormally: async (normalPayload) => {
2442
+ return await delivery.deliverNormally({
2443
+ payload: normalPayload === payload && (shouldRestoreTtsSupplementTextForPreviewFallback || ttsPreviewFinalization && !payload.text?.trim()) ? {
2444
+ ...normalPayload,
2416
2445
  text: ttsSupplement?.spokenText
2417
- } : payload,
2446
+ } : normalPayload,
2418
2447
  kind: info.kind,
2419
2448
  ...ttsPreviewFinalization?.threadTs ? { forcedThreadTs: ttsPreviewFinalization.threadTs } : {}
2420
2449
  });
2421
2450
  }
2422
- });
2423
- if (info.kind === "final") progress.progressDraft.markFinalReplyDelivered();
2451
+ })).deliveryResult ?? { visibleReplySent: false };
2424
2452
  };
2425
2453
  let agentRunFailed = false;
2426
2454
  let settledDispatchResult;
@@ -2452,7 +2480,6 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2452
2480
  }
2453
2481
  },
2454
2482
  record: prepared.turn.record,
2455
- history: prepared.turn.history,
2456
2483
  botLoopProtection: resolveSlackBotLoopProtection(prepared),
2457
2484
  replyOptions: {
2458
2485
  groupThreadReplyFormatter: formatSlackGroupThreadReply,
@@ -2494,30 +2521,19 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2494
2521
  },
2495
2522
  onToolStart: async (payload) => {
2496
2523
  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
2524
  return await progress.progressDraft.pushToolEvent(payload);
2500
2525
  },
2501
2526
  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);
2527
+ if (payload.hideFromChannelProgress || payload.suppressChannelProgress) return progress.preambleOnlyProgress ? false : progress.progressDraft.pushItemEvent(payload);
2528
+ if (payload.kind === "preamble" && progress.shouldYieldDraftProgress()) return false;
2529
+ progress.progressWorkCounter.noteItem(payload);
2530
+ return progress.preambleOnlyProgress && payload.kind !== "preamble" ? await progress.progressDraft.noteActivity() : await progress.progressDraft.pushItemEvent(payload);
2513
2531
  },
2514
2532
  onPlanUpdate: async (payload) => {
2515
2533
  if (payload.phase !== "update") return false;
2516
2534
  return await progress.pushPlanProgress(payload.steps, payload.explanation, payload.explanationFormat);
2517
2535
  },
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)
2536
+ onApprovalEvent: (payload) => progress.progressDraft.pushApprovalEvent(payload)
2521
2537
  }
2522
2538
  });
2523
2539
  if (turnResult.dispatched) {
@@ -2541,9 +2557,11 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2541
2557
  const completionChunks = progress.useNativeProgressStreaming && !progress.nativeProgressCompletionSent ? progress.buildNativeProgressCompletionChunks(dispatchError || agentRunFailed ? "error" : progress.nativeProgressTerminalStatus) : void 0;
2542
2558
  if (completionChunks?.length) progress.nativeProgressCompletionSent = true;
2543
2559
  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();
2560
+ const anyReplyDelivered = hasVisibleInboundReplyDispatch(settledDispatchResult, { observedReplyDelivery: delivery.observedReplyDelivery || previewLifecycle.finalDelivered });
2561
+ if (anyReplyDelivered && !previewLifecycle.finalStarted && !dispatchError && !agentRunFailed) {
2562
+ if (hasVisibleInboundReplyDispatch(settledDispatchResult)) await previewLifecycle.observeDelivery({ visibleReplySent: true });
2563
+ }
2564
+ await previewLifecycle.cleanup({ failed: Boolean(dispatchError || agentRunFailed) });
2547
2565
  if (pendingFailureNotice && anyReplyDelivered) recordSlackThreadFailureNotice(pendingFailureNotice);
2548
2566
  if (dispatchError || agentRunFailed) await progress.finalizeDraftProgressCard("error");
2549
2567
  await progress.dropDetachedProgressCards();
@@ -2562,9 +2580,8 @@ async function dispatchSlackMessageWithSetup(setup, beginSessionRun, turnAdoptio
2562
2580
  teamId: prepared.eventScope?.teamId
2563
2581
  });
2564
2582
  if (dispatchError) throw toErrorObject(dispatchError, "Slack dispatch failed");
2565
- if (!anyReplyDelivered && !draftPreviewCommitted.value && !(agentRunFailed && progress.useDraftProgressCard)) {
2566
- await draftStream?.clear();
2567
- await draftStream?.dropDetachedMessages();
2583
+ if (!anyReplyDelivered && !previewLifecycle.previewFinalized && !(agentRunFailed && progress.useDraftProgressCard)) {
2584
+ if (progress.useDraftProgressCard) await draftStream?.clear();
2568
2585
  return;
2569
2586
  }
2570
2587
  if (shouldLogVerbose()) {
@@ -2655,7 +2672,7 @@ function resolveSlackAttachmentFallbackText(attachments) {
2655
2672
  return parts.length > 0 ? parts.join("\n") : void 0;
2656
2673
  }
2657
2674
  function resolveSlackMessageText(message) {
2658
- const messageText = normalizeOptionalString(message.text) ?? resolveSlackAttachmentFallbackText(message.attachments);
2675
+ 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
2676
  return resolveSlackMessageText$1({
2660
2677
  ...message,
2661
2678
  text: messageText
@@ -2670,11 +2687,12 @@ async function resolveSlackThreadStarter(params) {
2670
2687
  params.threadTs
2671
2688
  ]);
2672
2689
  const cached = THREAD_STARTER_CACHE.get(cacheKey);
2673
- if (cached) {
2690
+ if (cached && !params.refresh) {
2674
2691
  const now = asDateTimestampMs(Date.now());
2675
2692
  if (now !== void 0 && cached.expiresAt > now) return cached.value;
2676
2693
  THREAD_STARTER_CACHE.delete(cacheKey);
2677
2694
  }
2695
+ if (params.refresh) THREAD_STARTER_CACHE.delete(cacheKey);
2678
2696
  try {
2679
2697
  const message = (await params.client.conversations.replies({
2680
2698
  channel: params.channelId,
@@ -2725,25 +2743,36 @@ async function resolveSlackThreadHistory(params) {
2725
2743
  try {
2726
2744
  do {
2727
2745
  pagesFetched += 1;
2746
+ params.assertCurrent?.();
2728
2747
  const response = await params.client.conversations.replies({
2729
2748
  channel: params.channelId,
2730
2749
  ts: params.threadTs,
2731
2750
  limit: fetchLimit,
2732
- inclusive: true,
2751
+ inclusive: false,
2752
+ ...params.currentMessageTs ? { latest: params.currentMessageTs } : {},
2753
+ ...params.oldest ? { oldest: params.oldest } : {},
2733
2754
  ...cursor ? { cursor } : {}
2734
2755
  });
2756
+ params.assertCurrent?.();
2735
2757
  for (const msg of response.messages ?? []) {
2758
+ const timestamp = resolveSlackTimestampMs(msg.ts);
2736
2759
  const text = resolveSlackMessageText(msg);
2737
2760
  if (!text && !msg.files?.length) continue;
2738
- if (params.currentMessageTs && msg.ts === params.currentMessageTs) continue;
2761
+ 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;
2762
+ if (retained.some(([entry]) => entry.ts === msg.ts)) continue;
2739
2763
  retained.push([msg, text]);
2740
2764
  }
2741
2765
  if (retained.length > maxMessages) retained.splice(0, retained.length - maxMessages);
2742
2766
  const next = response.response_metadata?.next_cursor;
2743
2767
  cursor = typeof next === "string" && next.trim().length > 0 ? next.trim() : void 0;
2768
+ if (response.has_more && !cursor) {
2769
+ params.onOmission?.("Slack returned an incomplete thread page without a cursor");
2770
+ return [];
2771
+ }
2744
2772
  } while (cursor && pagesFetched < SLACK_THREAD_HISTORY_MAX_PAGES);
2745
2773
  if (cursor) {
2746
2774
  logVerbose$1(`slack thread history capped channel=${params.channelId} ts=${params.threadTs} pages=${SLACK_THREAD_HISTORY_MAX_PAGES}`);
2775
+ params.onOmission?.("Slack thread exceeds the three-page automatic history budget");
2747
2776
  return [];
2748
2777
  }
2749
2778
  return retained.map(([message, text]) => ({
@@ -2751,13 +2780,55 @@ async function resolveSlackThreadHistory(params) {
2751
2780
  userId: message.user,
2752
2781
  botId: message.bot_id,
2753
2782
  ts: message.ts,
2754
- files: message.files
2783
+ files: message.files,
2784
+ attachments: message.attachments
2755
2785
  }));
2756
2786
  } catch (err) {
2787
+ params.assertCurrent?.();
2788
+ params.onOmission?.(formatErrorMessage(err));
2757
2789
  logVerbose$1(`slack thread history fetch failed channel=${params.channelId} ts=${params.threadTs}: ${formatErrorMessage(err)}`);
2758
2790
  return [];
2759
2791
  }
2760
2792
  }
2793
+ /** Slack channel history is newest first, unlike chronological thread replies. */
2794
+ async function resolveSlackChannelHistory(params) {
2795
+ if (!Number.isFinite(params.limit) || params.limit <= 0) return [];
2796
+ const messages = [];
2797
+ const seen = /* @__PURE__ */ new Set();
2798
+ let cursor;
2799
+ let scanned = 0;
2800
+ const maxPages = Math.ceil(params.limit / 200);
2801
+ for (let page = 0; page < maxPages; page += 1) {
2802
+ params.assertCurrent?.();
2803
+ const response = await params.client.conversations.history({
2804
+ channel: params.channelId,
2805
+ latest: params.currentMessageTs,
2806
+ ...params.oldest ? { oldest: params.oldest } : {},
2807
+ inclusive: false,
2808
+ limit: Math.min(200, params.limit - scanned),
2809
+ ...cursor ? { cursor } : {}
2810
+ });
2811
+ params.assertCurrent?.();
2812
+ for (const message of response.messages ?? []) {
2813
+ scanned += 1;
2814
+ 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;
2815
+ seen.add(message.ts);
2816
+ const text = resolveSlackMessageText(message);
2817
+ if (!text && !message.files?.length) continue;
2818
+ messages.push({
2819
+ text: text ?? formatSlackFilePlaceholder(message.files),
2820
+ userId: message.user,
2821
+ botId: message.bot_id,
2822
+ ts: message.ts,
2823
+ files: message.files,
2824
+ attachments: message.attachments
2825
+ });
2826
+ }
2827
+ cursor = response.response_metadata?.next_cursor?.trim() || void 0;
2828
+ if (!cursor || scanned >= params.limit) break;
2829
+ }
2830
+ return messages.slice(0, params.limit).toReversed();
2831
+ }
2761
2832
  //#endregion
2762
2833
  //#region extensions/slack/src/monitor/message-handler/preflight-audio.ts
2763
2834
  function isSlackAudioFile(file) {
@@ -2810,7 +2881,7 @@ const SLACK_MENTION_RESOLUTION_CONCURRENCY = 4;
2810
2881
  const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
2811
2882
  const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
2812
2883
  const MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS = 2e3;
2813
- const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-BvGUGk3w.mjs").then((n) => n._));
2884
+ const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-DKCtBgCI.mjs").then((n) => n._));
2814
2885
  function formatSlackUnavailableMedia(params) {
2815
2886
  let fileReferences = params.files?.map((file) => `${formatSlackFileReference(file)} unavailable (${file.reason})`).join(", ");
2816
2887
  if (fileReferences && fileReferences.length > MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS) fileReferences = `${truncateUtf16Safe(fileReferences, MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS)}; … (file references truncated)`;
@@ -2873,6 +2944,7 @@ async function resolveSlackMessageContent(params) {
2873
2944
  readIdleTimeoutMs: params.mediaReadIdleTimeoutMs,
2874
2945
  totalTimeoutMs: params.mediaTotalTimeoutMs,
2875
2946
  abortSignal: params.abortSignal,
2947
+ assertCurrent: params.assertCurrent,
2876
2948
  preloadedMedia: params.preloadedMedia
2877
2949
  })) : null;
2878
2950
  const effectiveDirectMedia = attachmentContent?.media.length ? attachmentContent.media : null;
@@ -2940,7 +3012,7 @@ async function resolveSlackMessageContent(params) {
2940
3012
  //#region extensions/slack/src/monitor/message-handler/prepare-dm-history.ts
2941
3013
  function resolveSlackDmHistoryLimit(params) {
2942
3014
  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);
3015
+ return resolvePromptHistoryLimit(override ?? params.defaultLimit, 0);
2944
3016
  }
2945
3017
  async function resolveSlackDmHistoryContext(params) {
2946
3018
  const maxMessages = Math.max(0, Math.floor(params.limit));
@@ -3063,8 +3135,172 @@ function formatSlackThreadLabelSnippet(text) {
3063
3135
  return truncateUtf16Safe(text.replace(/\s+/g, " "), 80);
3064
3136
  }
3065
3137
  //#endregion
3138
+ //#region extensions/slack/src/monitor/message-handler/prepare-room-history.ts
3139
+ const SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS = 4;
3140
+ const SLACK_HISTORY_MEDIA_MAX_BYTES = 10485760;
3141
+ const SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS = 1e3;
3142
+ const SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS = 3e3;
3143
+ async function resolveSlackRoomHistory(params) {
3144
+ if (params.ctx.historyLimit <= 0 || !params.message.ts) return [];
3145
+ const warnOmission = (reason) => {
3146
+ params.ctx.logger.warn({
3147
+ accountId: params.ctx.accountId,
3148
+ teamId: params.eventScope?.teamId ?? params.ctx.teamId,
3149
+ channelId: params.message.channel,
3150
+ threadTs: params.threadTs,
3151
+ reason
3152
+ }, "Slack automatic history omitted");
3153
+ };
3154
+ try {
3155
+ params.assertCurrent();
3156
+ const request = {
3157
+ channelId: params.message.channel,
3158
+ client: params.eventScope?.client ?? params.ctx.app.client,
3159
+ currentMessageTs: params.message.ts,
3160
+ oldest: params.oldest,
3161
+ excludedMessageIds: params.excludedMessageIds,
3162
+ limit: Math.max(DEFAULT_GROUP_HISTORY_LIMIT, params.ctx.historyLimit),
3163
+ assertCurrent: params.assertCurrent
3164
+ };
3165
+ const messages = params.threadTs ? await resolveSlackThreadHistory({
3166
+ ...request,
3167
+ threadTs: params.threadTs,
3168
+ onOmission: warnOmission
3169
+ }) : await resolveSlackChannelHistory(request);
3170
+ params.assertCurrent();
3171
+ const permitted = [];
3172
+ for (const message of messages) {
3173
+ if (isSlackThreadAuthorCurrentBot({
3174
+ identity: params.ctx,
3175
+ author: message
3176
+ })) continue;
3177
+ const user = message.userId ? await params.ctx.resolveUserName(message.userId, params.eventScope) : void 0;
3178
+ params.assertCurrent();
3179
+ const senderAllowed = resolveSlackUserAllowed({
3180
+ allowList: params.allowFromLower,
3181
+ teamId: params.eventScope?.teamId ?? params.ctx.teamId,
3182
+ userId: message.userId ?? message.botId,
3183
+ userName: user?.name,
3184
+ allowNameMatching: params.ctx.allowNameMatching
3185
+ });
3186
+ if (!shouldIncludeSupplementalContext({
3187
+ mode: params.contextVisibilityMode,
3188
+ kind: "history",
3189
+ senderAllowed
3190
+ })) continue;
3191
+ permitted.push({
3192
+ message,
3193
+ sender: user?.name ?? message.userId ?? `Bot (${message.botId ?? "unknown"})`
3194
+ });
3195
+ }
3196
+ const entries = [];
3197
+ let mediaRemaining = SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS;
3198
+ for (const { message, sender } of permitted.slice(-params.ctx.historyLimit)) {
3199
+ const { media, attempted } = mediaRemaining > 0 && (message.files?.length || message.attachments?.length) ? await resolveSlackHistoryMedia({
3200
+ ctx: params.ctx,
3201
+ eventScope: params.eventScope,
3202
+ maxAttachments: mediaRemaining,
3203
+ assertCurrent: params.assertCurrent,
3204
+ abortSignal: params.abortSignal,
3205
+ message: {
3206
+ type: "message",
3207
+ channel: params.message.channel,
3208
+ ts: message.ts,
3209
+ user: message.userId,
3210
+ bot_id: message.botId,
3211
+ text: message.text,
3212
+ files: message.files,
3213
+ attachments: message.attachments
3214
+ }
3215
+ }) : {
3216
+ media: [],
3217
+ attempted: 0
3218
+ };
3219
+ params.assertCurrent();
3220
+ mediaRemaining -= attempted;
3221
+ entries.push({
3222
+ sender,
3223
+ body: message.text,
3224
+ timestamp: resolveSlackTimestampMs(message.ts),
3225
+ messageId: message.ts,
3226
+ ...media.length > 0 ? { media } : {}
3227
+ });
3228
+ }
3229
+ return entries;
3230
+ } catch (error) {
3231
+ warnOmission(formatErrorMessage(error));
3232
+ return [];
3233
+ }
3234
+ }
3235
+ function isSlackImageFileCandidate(file) {
3236
+ if ((file.mimetype?.split(";")[0]?.trim().toLowerCase())?.startsWith("image/")) return true;
3237
+ return Boolean(mimeTypeFromFilePath(file.name)?.startsWith("image/"));
3238
+ }
3239
+ function sliceSlackImageFileCandidates(files, limit) {
3240
+ if (limit <= 0 || !files?.length) return [];
3241
+ return files.filter(isSlackImageFileCandidate).slice(0, limit);
3242
+ }
3243
+ function sliceSlackHistoryAttachmentCandidates(attachments, limit) {
3244
+ if (limit <= 0 || !attachments?.length) return [];
3245
+ const out = [];
3246
+ let remaining = limit;
3247
+ for (const attachment of attachments) {
3248
+ if (attachment.is_share !== true) continue;
3249
+ const hasImageUrl = Boolean(normalizeOptionalString(attachment.image_url));
3250
+ const files = sliceSlackImageFileCandidates(attachment.files, remaining - (hasImageUrl ? 1 : 0));
3251
+ if (!hasImageUrl && files.length === 0) continue;
3252
+ out.push({
3253
+ ...attachment,
3254
+ files
3255
+ });
3256
+ remaining -= (hasImageUrl ? 1 : 0) + files.length;
3257
+ if (remaining <= 0) break;
3258
+ }
3259
+ return out;
3260
+ }
3261
+ function buildSlackHistoryMediaCandidateMessage(message, maxAttachments) {
3262
+ const files = sliceSlackImageFileCandidates(message.files, maxAttachments);
3263
+ const attachments = sliceSlackHistoryAttachmentCandidates(message.attachments, Math.max(0, maxAttachments - files.length));
3264
+ if (files.length === 0 && attachments.length === 0) return null;
3265
+ return {
3266
+ message: {
3267
+ ...message,
3268
+ files,
3269
+ attachments
3270
+ },
3271
+ attempted: files.length + attachments.reduce((count, attachment) => count + (normalizeOptionalString(attachment.image_url) ? 1 : 0) + (attachment.files?.length ?? 0), 0)
3272
+ };
3273
+ }
3274
+ async function resolveSlackHistoryMedia(params) {
3275
+ const candidate = buildSlackHistoryMediaCandidateMessage(params.message, params.maxAttachments);
3276
+ if (!candidate) return {
3277
+ media: [],
3278
+ attempted: 0
3279
+ };
3280
+ const content = await resolveSlackMessageContent({
3281
+ message: candidate.message,
3282
+ isThreadReply: false,
3283
+ threadStarter: null,
3284
+ isBotMessage: Boolean(params.message.bot_id),
3285
+ client: params.eventScope?.client ?? params.ctx.app.client,
3286
+ botToken: params.ctx.botToken,
3287
+ mediaMaxBytes: Math.min(params.ctx.mediaMaxBytes, SLACK_HISTORY_MEDIA_MAX_BYTES),
3288
+ mediaReadIdleTimeoutMs: SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS,
3289
+ mediaTotalTimeoutMs: SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS,
3290
+ assertCurrent: params.assertCurrent,
3291
+ abortSignal: params.abortSignal
3292
+ });
3293
+ return {
3294
+ media: await toInboundMediaFactsWithMetadata(content?.effectiveDirectMedia, {
3295
+ kind: "image",
3296
+ messageId: params.message.ts
3297
+ }),
3298
+ attempted: candidate.attempted
3299
+ };
3300
+ }
3301
+ //#endregion
3066
3302
  //#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
3067
- const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-BvGUGk3w.mjs").then((n) => n._));
3303
+ const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-DKCtBgCI.mjs").then((n) => n._));
3068
3304
  const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
3069
3305
  function resolveSlackThreadSessionFreshness(params) {
3070
3306
  return params.ctx.channelRuntime?.session?.resolveEntryResetFreshness?.({
@@ -3079,23 +3315,6 @@ function resolveSlackThreadSessionFreshness(params) {
3079
3315
  })
3080
3316
  });
3081
3317
  }
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
3318
  async function resolveSlackThreadUserMap(params) {
3100
3319
  const uniqueUserIds = [];
3101
3320
  const seen = /* @__PURE__ */ new Set();
@@ -3155,16 +3374,17 @@ async function resolveSlackThreadContextData(params) {
3155
3374
  };
3156
3375
  const starter = params.threadStarter;
3157
3376
  const starterSenderName = params.allowNameMatching && params.allowFromLower.length > 0 && starter?.userId ? (await params.ctx.resolveUserName(starter.userId, params.eventScope))?.name : void 0;
3377
+ params.assertHistoryCurrent?.();
3158
3378
  const starterIsCurrentBot = Boolean(starter && isCurrentBotAuthor({
3159
3379
  userId: starter.userId,
3160
3380
  botId: starter.botId
3161
3381
  }));
3162
- const starterAllowed = !starter || !starterIsCurrentBot && isSlackThreadContextSenderAllowed({
3163
- allowFromLower: params.allowFromLower,
3382
+ const starterAllowed = !starter || !starterIsCurrentBot && resolveSlackUserAllowed({
3383
+ allowList: params.allowFromLower,
3384
+ teamId: params.eventScope?.teamId ?? params.ctx.teamId,
3164
3385
  allowNameMatching: params.allowNameMatching,
3165
- userId: starter.userId,
3166
- userName: starterSenderName,
3167
- botId: starter.botId
3386
+ userId: starter.userId ?? starter.botId,
3387
+ userName: starterSenderName
3168
3388
  });
3169
3389
  const includeStarterContext = !starter || !starterIsCurrentBot && shouldIncludeSupplementalContext({
3170
3390
  mode: params.contextVisibilityMode,
@@ -3177,12 +3397,16 @@ async function resolveSlackThreadContextData(params) {
3177
3397
  threadLabel = `Slack thread ${params.roomLabel}${snippet ? `: ${snippet}` : ""}`;
3178
3398
  if (shouldSeedInitialThreadContext && !params.effectiveDirectMedia && starter.files && starter.files.length > 0) {
3179
3399
  const { resolveSlackAttachmentContent } = await loadSlackMediaModule();
3400
+ params.assertHistoryCurrent?.();
3180
3401
  const attachmentContent = await resolveSlackAttachmentContent({
3181
3402
  files: starter.files,
3182
3403
  client: params.eventScope?.client ?? params.ctx.app.client,
3183
3404
  token: params.ctx.botToken,
3184
- maxBytes: params.ctx.mediaMaxBytes
3405
+ maxBytes: params.ctx.mediaMaxBytes,
3406
+ assertCurrent: params.assertHistoryCurrent,
3407
+ abortSignal: params.abortSignal
3185
3408
  });
3409
+ params.assertHistoryCurrent?.();
3186
3410
  threadStarterMedia = attachmentContent?.media.length ? attachmentContent.media : null;
3187
3411
  if (attachmentContent) threadStarterBody = formatSlackUnavailableMedia({
3188
3412
  body: threadStarterBody,
@@ -3210,16 +3434,36 @@ async function resolveSlackThreadContextData(params) {
3210
3434
  });
3211
3435
  logVerbose("slack: retained current-bot thread starter as assistant root context");
3212
3436
  }
3213
- const threadInitialHistoryLimit = params.account.config?.thread?.initialHistoryLimit ?? 20;
3437
+ const threadInitialHistoryLimit = Math.min(params.account.config?.thread?.initialHistoryLimit ?? 20, params.historyLimit ?? Number.POSITIVE_INFINITY);
3214
3438
  if (threadInitialHistoryLimit > 0 && shouldLoadInitialThreadHistory) {
3215
3439
  const currentBotRootTs = starter?.ts ?? params.threadTs;
3440
+ let historyOmitted = false;
3216
3441
  const threadHistory = await resolveSlackThreadHistory({
3217
3442
  channelId: params.message.channel,
3218
3443
  threadTs: params.threadTs,
3219
3444
  client: params.eventScope?.client ?? params.ctx.app.client,
3220
3445
  currentMessageTs: params.message.ts,
3221
- limit: threadInitialHistoryLimit
3446
+ limit: threadInitialHistoryLimit,
3447
+ excludedMessageIds: params.excludedMessageIds,
3448
+ assertCurrent: params.assertHistoryCurrent,
3449
+ onOmission: (reason) => {
3450
+ historyOmitted = true;
3451
+ params.ctx.logger.warn({
3452
+ channelId: params.message.channel,
3453
+ threadTs: params.threadTs,
3454
+ teamId: params.eventScope?.teamId ?? params.ctx.teamId,
3455
+ accountId: params.account.accountId,
3456
+ reason
3457
+ }, "Slack automatic thread history omitted");
3458
+ }
3222
3459
  });
3460
+ if (historyOmitted) return {
3461
+ threadStarterBody,
3462
+ threadHistoryBody,
3463
+ shouldSeedInitialThreadContext,
3464
+ threadLabel,
3465
+ threadStarterMedia
3466
+ };
3223
3467
  const enrichedStarter = starter && threadStarterBody && threadStarterBody !== starter.text ? {
3224
3468
  ...starter,
3225
3469
  text: threadStarterBody,
@@ -3248,6 +3492,7 @@ async function resolveSlackThreadContextData(params) {
3248
3492
  messages: threadHistoryWithoutCurrentBot,
3249
3493
  eventScope: params.eventScope
3250
3494
  }) : /* @__PURE__ */ new Map();
3495
+ params.assertHistoryCurrent?.();
3251
3496
  const { items: filteredThreadHistory, omitted: omittedHistoryCount } = params.contextVisibilityMode === "all" ? {
3252
3497
  items: threadHistoryWithoutCurrentBot,
3253
3498
  omitted: 0
@@ -3261,29 +3506,30 @@ async function resolveSlackThreadContextData(params) {
3261
3506
  botId: historyMsg.botId
3262
3507
  })) return true;
3263
3508
  const msgUser = historyMsg.userId ? userMapForFilter.get(historyMsg.userId) : null;
3264
- return isSlackThreadContextSenderAllowed({
3265
- allowFromLower: params.allowFromLower,
3509
+ return resolveSlackUserAllowed({
3510
+ allowList: params.allowFromLower,
3511
+ teamId: params.eventScope?.teamId ?? params.ctx.teamId,
3266
3512
  allowNameMatching: params.allowNameMatching,
3267
- userId: historyMsg.userId,
3268
- userName: msgUser?.name,
3269
- botId: historyMsg.botId
3513
+ userId: historyMsg.userId ?? historyMsg.botId,
3514
+ userName: msgUser?.name
3270
3515
  });
3271
3516
  }
3272
3517
  });
3273
3518
  const userMap = await resolveSlackThreadUserMap({
3274
3519
  ctx: params.ctx,
3275
- messages: filteredThreadHistory
3520
+ messages: filteredThreadHistory,
3521
+ eventScope: params.eventScope
3276
3522
  });
3523
+ params.assertHistoryCurrent?.();
3277
3524
  if (omittedHistoryCount > 0 || omittedCurrentBotHistoryCount > 0) logVerbose(`slack: omitted ${omittedHistoryCount + omittedCurrentBotHistoryCount} thread message(s) from context (mode=${params.contextVisibilityMode})`);
3278
3525
  const historyParts = [];
3279
3526
  for (const historyMsg of filteredThreadHistory) {
3280
3527
  const msgUser = historyMsg.userId ? userMap.get(historyMsg.userId) : null;
3281
- const isOtherBot = Boolean(historyMsg.botId) && historyMsg.botId !== params.ctx.botId;
3282
3528
  const isCurrentBot = isCurrentBotAuthor({
3283
3529
  userId: historyMsg.userId,
3284
3530
  botId: historyMsg.botId
3285
3531
  });
3286
- const role = isCurrentBot || isOtherBot || Boolean(historyMsg.botId) ? "assistant" : "user";
3532
+ const role = isCurrentBot || historyMsg.botId ? "assistant" : "user";
3287
3533
  const msgSenderName = isCurrentBot ? "Bot (this assistant)" : msgUser?.name ?? (historyMsg.botId ? `Bot (${historyMsg.botId})` : "Unknown");
3288
3534
  const msgWithId = `${historyMsg.ts === currentBotRootTs && threadStarterBody ? threadStarterBody : historyMsg.text}\n[slack message id: ${historyMsg.ts ?? "unknown"} channel: ${params.message.channel}]`;
3289
3535
  historyParts.push(formatInboundEnvelope({
@@ -3382,10 +3628,6 @@ const SLACK_ANY_MENTION_RE = /<@[^>]+>|<!subteam\^[^>]+>/;
3382
3628
  const SLACK_USER_MENTION_RE = /<@([^>|]+)(?:\|[^>]+)?>/g;
3383
3629
  const SLACK_SUBTEAM_MENTION_RE = /<!subteam\^([^>|]+)(?:\|[^>]+)?>/g;
3384
3630
  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
3631
  const SLACK_CHANNEL_ACCESS_DOCS_URL = "https://docs.openclaw.ai/channels/slack#access-control-and-routing";
3390
3632
  function resolveSlackGroupSessionSubject(params) {
3391
3633
  const channelName = normalizeOptionalString(params.channelName);
@@ -3397,9 +3639,8 @@ function recordString(record, key) {
3397
3639
  return normalizeOptionalString(record?.[key]);
3398
3640
  }
3399
3641
  function recordNullableString(record, key) {
3400
- if (!record || !(key in record)) return;
3401
- if (record[key] === null) return null;
3402
- return normalizeOptionalString(record[key]);
3642
+ const value = record?.[key];
3643
+ return value === null ? null : normalizeOptionalString(value);
3403
3644
  }
3404
3645
  function mergeSlackAssistantThreadContext(primary, fallback) {
3405
3646
  if (!primary) return fallback;
@@ -3465,61 +3706,6 @@ function resolveCachedMentionRegexes(ctx, agentId, options) {
3465
3706
  byAgent.set(key, built);
3466
3707
  return built;
3467
3708
  }
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
3709
  function collectUniqueSlackMentionIds(text, regex) {
3524
3710
  const ids = [];
3525
3711
  regex.lastIndex = 0;
@@ -3820,7 +4006,7 @@ async function prepareSlackMessage(params) {
3820
4006
  let mentionRegexes = buildPolicyMentionRegexes(routing.route.agentId);
3821
4007
  let wasMentioned = resolveWasMentioned(mentionRegexes);
3822
4008
  const hasBoundSession = Boolean(routing.runtimeBoundSessionKey || routing.configuredBindingSessionKey);
3823
- let { route, runtimeBinding, replyToMode, threadContext, threadTs, isThreadReply, threadKeys, sessionKey, historyKey } = routing;
4009
+ let { route, runtimeBinding, replyToMode, threadContext, threadTs, isThreadReply, threadKeys, sessionKey } = routing;
3824
4010
  const { configuredBinding, configuredBindingSessionKey } = routing;
3825
4011
  const isAssistantThreadMessage = Boolean(isDirectMessage && messageAssistantThreadContext);
3826
4012
  const forcedAssistantReplyThreadTs = Boolean(assistantThreadContext?.threadTs && (isThreadReply || isAssistantThreadMessage || replyToMode !== "off")) ? assistantThreadContext?.threadTs : void 0;
@@ -3858,65 +4044,14 @@ async function prepareSlackMessage(params) {
3858
4044
  teamId: opts.eventScope?.teamId
3859
4045
  }));
3860
4046
  }
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
4047
  let threadStarterPromise;
3914
4048
  const getThreadStarter = () => {
3915
4049
  threadStarterPromise ??= isThreadReply && threadTs ? resolveSlackThreadStarter({
3916
4050
  channelId: message.channel,
3917
4051
  threadTs,
3918
4052
  client: slackClient,
3919
- workspaceScope: threadStarterWorkspaceScope
4053
+ workspaceScope: threadStarterWorkspaceScope,
4054
+ refresh: isRoomish && ctx.historyLimit > 0
3920
4055
  }) : Promise.resolve(null);
3921
4056
  return threadStarterPromise;
3922
4057
  };
@@ -4067,7 +4202,7 @@ async function prepareSlackMessage(params) {
4067
4202
  })) groupThread = void 0;
4068
4203
  mentionRegexes = buildPolicyMentionRegexes(routing.route.agentId);
4069
4204
  wasMentioned = resolveWasMentioned(mentionRegexes);
4070
- ({route, runtimeBinding, replyToMode, threadContext, threadTs, isThreadReply, threadKeys, sessionKey, historyKey} = routing);
4205
+ ({route, runtimeBinding, replyToMode, threadContext, threadTs, isThreadReply, threadKeys, sessionKey} = routing);
4071
4206
  canDetectMention = Boolean(groupThread) || Boolean(ctx.botUserId) || mentionRegexes.length > 0;
4072
4207
  }
4073
4208
  if (preflightAudioTranscript && !wasMentioned) {
@@ -4106,19 +4241,10 @@ async function prepareSlackMessage(params) {
4106
4241
  if (isBotMessage && allowBotsMode === "mentions") {
4107
4242
  if (!(isDirectMessage || effectiveWasMentioned || shouldBypassMention)) return drop("bot-missing-mention");
4108
4243
  }
4109
- if (isRoom && shouldRequireMention && !canDetectMention && !effectiveWasMentioned) {
4110
- await recordDroppedHistory("slack-mention-detection-unavailable");
4111
- return drop("mention-detection-unavailable");
4112
- }
4244
+ if (isRoom && shouldRequireMention && !canDetectMention && !effectiveWasMentioned) return drop("mention-detection-unavailable");
4113
4245
  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
- }
4246
+ if (isRoom && ignoreOtherMentions && Boolean(ctx.botUserId) && hasAnyMention && !wasMentioned) return drop("other-mention");
4247
+ if (isRoom && shouldRequireMention && mentionDecision.shouldSkip) return drop("missing-mention");
4122
4248
  const chatType = resolveSlackChatType(conversation.resolvedChannelType);
4123
4249
  const inboundEventKind = classifyChannelInboundEvent({
4124
4250
  conversation: { kind: chatType },
@@ -4200,16 +4326,34 @@ async function prepareSlackMessage(params) {
4200
4326
  const textWithId = `${bodyForAgent}\n[slack message id: ${message.ts} channel: ${message.channel}${threadInfo}]`;
4201
4327
  const storePath = resolveStorePath(ctx.cfg.session?.store, { agentId: route.agentId });
4202
4328
  const envelopeOptions = resolveEnvelopeFormatOptions(ctx.cfg);
4329
+ const effectiveMessageThreadId = assistantThreadContext?.threadTs ?? agentViewThreadTs ?? threadContext.messageThreadId;
4330
+ const boundMessageThreadId = directThreadRoutedToDmSession ? void 0 : effectiveMessageThreadId;
4331
+ messageIngress = await resolveMessageIngress({
4332
+ agentId: route.agentId,
4333
+ sessionKey,
4334
+ nativeChannelId: message.channel,
4335
+ messageId: threadContext.messageTs,
4336
+ inboundEventKind
4337
+ }, boundMessageThreadId);
4338
+ if (messageIngress.ingress.admission !== "dispatch") return drop("final-route-denied");
4203
4339
  const sessionEntry = getSessionEntry({
4204
4340
  storePath,
4205
4341
  sessionKey
4206
4342
  });
4207
4343
  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 });
4344
+ const excludedMessageIds = new Set(opts.sourceMessageIds);
4345
+ if (message.ts) excludedMessageIds.add(message.ts);
4346
+ const isHistorySessionCurrent = () => {
4347
+ const current = getSessionEntry({
4348
+ storePath,
4349
+ sessionKey
4350
+ });
4351
+ return current?.sessionId === sessionEntry?.sessionId && current?.lifecycleRevision === sessionEntry?.lifecycleRevision && current?.sessionStartedAt === sessionEntry?.sessionStartedAt && current?.updatedAt === 0 === (sessionEntry?.updatedAt === 0);
4352
+ };
4353
+ const assertHistoryCurrent = () => {
4354
+ opts.abortSignal?.throwIfAborted();
4355
+ if (opts.isRuntimePolicyCurrent?.() === false || !isHistorySessionCurrent()) throw new Error("Slack history policy or session changed during recovery");
4356
+ };
4213
4357
  const dmHistoryLimit = isDirectMessage ? resolveSlackDmHistoryLimit({
4214
4358
  account,
4215
4359
  userId: message.user,
@@ -4240,26 +4384,12 @@ async function prepareSlackMessage(params) {
4240
4384
  inboundHistory: void 0
4241
4385
  };
4242
4386
  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
4387
  const { channelMetadata, groupSystemPrompt } = resolveSlackRoomContextHints({
4258
4388
  isRoomish,
4259
4389
  channelInfo,
4260
4390
  channelConfig
4261
4391
  });
4262
- const { threadStarterBody, threadHistoryBody, shouldSeedInitialThreadContext, threadLabel, threadStarterMedia } = await resolveSlackThreadContextData({
4392
+ const threadContextData = await resolveSlackThreadContextData({
4263
4393
  ctx,
4264
4394
  agentId: route.agentId,
4265
4395
  account,
@@ -4277,25 +4407,66 @@ async function prepareSlackMessage(params) {
4277
4407
  contextVisibilityMode,
4278
4408
  envelopeOptions,
4279
4409
  effectiveDirectMedia,
4280
- eventScope: opts.eventScope
4410
+ eventScope: opts.eventScope,
4411
+ historyLimit: isRoomish ? ctx.historyLimit : void 0,
4412
+ excludedMessageIds,
4413
+ assertHistoryCurrent,
4414
+ abortSignal: opts.abortSignal
4281
4415
  });
4416
+ const { threadLabel, threadStarterMedia } = threadContextData;
4417
+ let { threadStarterBody, threadHistoryBody, shouldSeedInitialThreadContext } = threadContextData;
4418
+ const threadScopedHistory = isThreadReply && ctx.threadHistoryScope === "thread";
4419
+ let roomHistory = [];
4420
+ if (isRoomish && ctx.historyLimit > 0 && (!threadScopedHistory || !shouldSeedInitialThreadContext)) {
4421
+ if (sessionEntry?.updatedAt !== 0) roomHistory = await resolveSlackRoomHistory({
4422
+ ctx,
4423
+ message,
4424
+ threadTs: threadScopedHistory ? threadTs : void 0,
4425
+ oldest: sessionEntry?.sessionStartedAt ? (sessionEntry.sessionStartedAt / 1e3).toFixed(6) : void 0,
4426
+ excludedMessageIds,
4427
+ allowFromLower: threadContextAllowFromLower,
4428
+ contextVisibilityMode,
4429
+ eventScope: opts.eventScope,
4430
+ assertCurrent: assertHistoryCurrent,
4431
+ abortSignal: opts.abortSignal
4432
+ });
4433
+ }
4282
4434
  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,
4435
+ let inboundMedia = await toInboundMediaFactsWithMetadata(effectiveMedia, { transcribed: (entry) => effectiveMedia === effectiveDirectMedia && entry === preflightAudioMedia });
4436
+ opts.abortSignal?.throwIfAborted();
4437
+ if (opts.isRuntimePolicyCurrent?.() === false) return drop("final-route-denied");
4438
+ if (isRoomish && ctx.historyLimit > 0 && !isHistorySessionCurrent()) {
4439
+ roomHistory = [];
4440
+ threadHistoryBody = void 0;
4441
+ threadStarterBody = void 0;
4442
+ shouldSeedInitialThreadContext = false;
4443
+ if (effectiveMedia === threadStarterMedia) inboundMedia = [];
4444
+ ctx.logger.warn({
4445
+ channelId: message.channel,
4446
+ sessionKey
4447
+ }, "Slack automatic history omitted after session changed");
4448
+ }
4449
+ combinedBody = buildHistoryContextFromEntries({
4450
+ entries: roomHistory,
4451
+ currentMessage: combinedBody,
4452
+ historyKind: "recent",
4453
+ excludeLast: false,
4454
+ formatEntry: (entry) => formatInboundEnvelope({
4455
+ channel: "Slack",
4456
+ from: roomLabel,
4457
+ timestamp: entry.timestamp,
4458
+ body: `${entry.body} [id:${entry.messageId} channel:${message.channel}]`,
4459
+ chatType: "channel",
4460
+ senderLabel: entry.sender,
4461
+ envelope: envelopeOptions
4462
+ })
4463
+ });
4464
+ const inboundHistory = isRoomish && ctx.historyLimit > 0 ? buildInboundHistoryFromEntries({
4465
+ entries: roomHistory,
4286
4466
  limit: ctx.historyLimit
4287
4467
  }) : dmHistoryContext.inboundHistory;
4288
4468
  const commandBody = textForCommandDetection.trim();
4289
4469
  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
4470
  const agentContextEntities = isAgentViewMessage ? normalizeSlackAppContextEntities(message.app_context) : [];
4300
4471
  const ctxPayload = (ctx.buildContext ?? buildChannelInboundEventContext)({
4301
4472
  channelIngress: messageIngress,
@@ -4328,9 +4499,7 @@ async function prepareSlackMessage(params) {
4328
4499
  avatar: conversationAvatar
4329
4500
  },
4330
4501
  route: {
4331
- agentId: route.agentId,
4332
- dmScope: route.dmScope,
4333
- accountId: route.accountId,
4502
+ ...route,
4334
4503
  routeSessionKey: route.sessionKey,
4335
4504
  dispatchSessionKey: sessionKey,
4336
4505
  parentSessionKey: threadKeys.parentSessionKey
@@ -4349,7 +4518,10 @@ async function prepareSlackMessage(params) {
4349
4518
  commandBody,
4350
4519
  inboundHistory
4351
4520
  },
4352
- sessionTranscript: { historyLimit: isRoomish ? ctx.historyLimit : dmHistoryLimit },
4521
+ sessionTranscript: {
4522
+ historyLimit: isRoomish ? ctx.historyLimit : dmHistoryLimit,
4523
+ historyKind: isRoomish ? "recent" : "pending"
4524
+ },
4353
4525
  access: {
4354
4526
  mentions: {
4355
4527
  canDetectMention: isRoomish,
@@ -4404,16 +4576,6 @@ async function prepareSlackMessage(params) {
4404
4576
  }
4405
4577
  });
4406
4578
  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
4579
  const pinnedMainDmOwner = isDirectMessage ? resolvePinnedMainDmOwnerFromAllowlist({
4418
4580
  dmScope: cfg.session?.dmScope,
4419
4581
  allowFrom: allowFromLower,
@@ -4467,13 +4629,7 @@ async function prepareSlackMessage(params) {
4467
4629
  sessionKey
4468
4630
  }, "failed updating session meta");
4469
4631
  }
4470
- },
4471
- history: isRoomish && shouldRequireMention ? {
4472
- isGroup: true,
4473
- historyKey,
4474
- historyMap: ctx.channelHistories,
4475
- limit: ctx.historyLimit
4476
- } : void 0
4632
+ }
4477
4633
  },
4478
4634
  replyToMode,
4479
4635
  ...forcedReplyThreadTs ? { forcedReplyThreadTs } : {},
@@ -4481,7 +4637,6 @@ async function prepareSlackMessage(params) {
4481
4637
  requireMention: shouldRequireMention,
4482
4638
  isDirectMessage,
4483
4639
  isRoomish,
4484
- historyKey,
4485
4640
  preview,
4486
4641
  ackReactionMessageTs,
4487
4642
  ackReactionValue,