@openclaw/discord 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 (421) hide show
  1. package/dist/.setup/{activities-api-DBwRt5ES.mjs → activities-api-COU4-0nR.mjs} +4 -4
  2. package/dist/.setup/{approval-handler.runtime-Du3n5ypX.mjs → approval-handler.runtime-CbBktBb4.mjs} +5 -5
  3. package/dist/.setup/{approval-message-updates-BN7JtScm.mjs → approval-message-updates-jf9zMaIC.mjs} +1 -1
  4. package/dist/.setup/{audit-C8uaKoa9.mjs → audit-vKbh-LFR.mjs} +3 -3
  5. package/dist/.setup/{channel-ChKdlBgH.mjs → channel-Dv4e3Jpc.mjs} +19 -16
  6. package/dist/.setup/{channel-actions.runtime-CJmiFfps.mjs → channel-actions.runtime-leZuw01r.mjs} +13 -7
  7. package/dist/.setup/{channel.setup-BlC3-Kzz.mjs → channel.setup-0NX6FFwi.mjs} +1 -1
  8. package/dist/.setup/{components-MqMWUWnE.mjs → components-yBEb75bB.mjs} +1 -2
  9. package/dist/.setup/{conversation-binding-route-_rz33QHO.mjs → conversation-binding-route-B7qImK_b.mjs} +6 -3
  10. package/dist/.setup/{doctor-DxcdkXzz.mjs → doctor-D1R-uFIu.mjs} +2 -2
  11. package/dist/.setup/{handle-action.guild-admin-DAFyh_yV.mjs → handle-action.guild-admin-DcFa8NwA.mjs} +3 -3
  12. package/dist/.setup/{message-handler-DnUrDpaU.mjs → message-handler-X6ejmbtY.mjs} +6 -5
  13. package/dist/.setup/{message-handler.preflight-CtqaF_fb.mjs → message-handler.preflight-DE_POxFc.mjs} +14 -117
  14. package/dist/.setup/message-handler.preflight-pluralkit-8Xji34AF.mjs +143 -0
  15. package/dist/.setup/{message-handler.process-Dz1xvuQU.mjs → message-handler.process-CCmIVnxy.mjs} +387 -317
  16. package/dist/.setup/{outbound-adapter-C8v79z9Z.mjs → outbound-adapter-ClIgmu4F.mjs} +5 -5
  17. package/dist/.setup/{outbound-session-route-CqWbuMOm.mjs → outbound-session-route-VulUE0yV.mjs} +7 -6
  18. package/dist/.setup/{preflight-audio-DrnqFA8U.mjs → preflight-audio-DHGIHEpv.mjs} +2 -1
  19. package/dist/.setup/{provider-72zIvN-i.mjs → provider-iOf73HW-.mjs} +425 -286
  20. package/dist/.setup/{provider-session.runtime-BW7QrF5C.mjs → provider-session.runtime-DrGAYnZv.mjs} +3 -3
  21. package/dist/.setup/provider.runtime-BmjW_pIw.mjs +2 -0
  22. package/dist/.setup/{realtime-session.runtime-DTccTgTM.mjs → realtime-session.runtime-Dz8uVutq.mjs} +338 -297
  23. package/dist/.setup/receive-recovery-DHst8WRx.mjs +517 -0
  24. package/dist/.setup/{retry-DBfcEgmO.mjs → retry-BEYkDy0P.mjs} +78 -13
  25. package/dist/.setup/{runtime-SchNfUsQ.mjs → runtime-B8lJfyrA.mjs} +30 -11
  26. package/dist/.setup/{runtime-BVoa7hhx.mjs → runtime-DisDbixd.mjs} +1 -1
  27. package/dist/.setup/{send-CKDczUNL.mjs → send-CIBzvXjS.mjs} +23 -9
  28. package/dist/.setup/{send.components-DWQjXhPV.mjs → send.components-ChY21qr-.mjs} +4 -4
  29. package/dist/.setup/{send.outbound-D0hQ7s50.mjs → send.outbound-QTyuFupn.mjs} +15 -16
  30. package/dist/.setup/{send.shared-Zwx6BxQ5.mjs → send.shared-VNvWfX2T.mjs} +2 -2
  31. package/dist/.setup/setup-plugin-api-Dvr41dyx.mjs +3 -0
  32. package/dist/.setup/{shared-CaA3Vv4n.mjs → shared-DHPDaMSx.mjs} +1 -1
  33. package/dist/.setup/{subagent-hooks-Bired9q6.mjs → subagent-hooks-CjW7r8Pe.mjs} +1 -1
  34. package/dist/.setup/{target-resolver-B6MrxbYv.mjs → target-resolver-BXpy6VT8.mjs} +1 -1
  35. package/dist/.setup/targets-CvemhN3i.mjs +3 -0
  36. package/dist/.setup/{thread-bindings-CK5272V3.mjs → thread-bindings-CEh2dhqP.mjs} +3 -3
  37. package/dist/.setup/{thread-bindings.manager-eexkKyrG.mjs → thread-bindings.manager-ve-i6oqt.mjs} +94 -45
  38. package/dist/.setup/{transcripts-source-CuWYgayu.mjs → transcripts-source-DVegW0WI.mjs} +61 -7
  39. package/dist/.setup/{voice-runtime-DHVQ2mFz.mjs → voice-runtime-elTtjEIG.mjs} +553 -901
  40. package/dist/action-runtime-api.js +1 -1
  41. package/dist/activities-api.js +1 -1
  42. package/dist/api.js +10 -10
  43. package/dist/channel-plugin-api.js +1 -1
  44. package/dist/index.js +1 -1
  45. package/dist/runtime-api.js +12 -12
  46. package/dist/setup-entry.js +1 -1
  47. package/dist/setup-plugin-api.js +1 -1
  48. package/dist/src/voice/audio-worker.runtime.js +1065 -0
  49. package/dist/subagent-hooks-api.js +1 -1
  50. package/dist/transcripts-source-api.js +1 -1
  51. package/node_modules/@napi-rs/wasm-runtime/dist/emnapi-plugins.cjs +8 -4
  52. package/node_modules/@napi-rs/wasm-runtime/dist/emnapi-plugins.js +8 -4
  53. package/node_modules/@napi-rs/wasm-runtime/dist/fs-proxy.cjs +1 -1
  54. package/node_modules/@napi-rs/wasm-runtime/dist/fs.js +770 -695
  55. package/node_modules/@napi-rs/wasm-runtime/fs-proxy.js +3 -2
  56. package/node_modules/@napi-rs/wasm-runtime/package.json +1 -1
  57. package/node_modules/@tybys/wasm-util/LICENSE +21 -0
  58. package/node_modules/@tybys/wasm-util/package.json +1 -1
  59. package/node_modules/@types/node/README.md +1 -1
  60. package/node_modules/@types/node/package.json +2 -2
  61. package/node_modules/@types/node/web-globals/streams.d.ts +4 -0
  62. package/node_modules/zod/README.md +2 -2
  63. package/node_modules/zod/compile.cjs +19 -0
  64. package/node_modules/zod/index.cjs +19 -0
  65. package/node_modules/zod/locales/index.cjs +19 -0
  66. package/node_modules/zod/mini/index.cjs +19 -0
  67. package/node_modules/zod/package.json +8 -7
  68. package/node_modules/zod/src/v3/helpers/errorUtil.ts +1 -1
  69. package/node_modules/zod/src/v4/classic/external.ts +2 -0
  70. package/node_modules/zod/src/v4/classic/from-json-schema.ts +248 -25
  71. package/node_modules/zod/src/v4/classic/schemas.ts +243 -168
  72. package/node_modules/zod/src/v4/classic/tests/assignability.test.ts +60 -9
  73. package/node_modules/zod/src/v4/classic/tests/bigint.test.ts +6 -0
  74. package/node_modules/zod/src/v4/classic/tests/continuability.test.ts +2 -2
  75. package/node_modules/zod/src/v4/classic/tests/cyclic-data.test.ts +265 -0
  76. package/node_modules/zod/src/v4/classic/tests/discriminated-unions.test.ts +112 -6
  77. package/node_modules/zod/src/v4/classic/tests/enum.test.ts +9 -0
  78. package/node_modules/zod/src/v4/classic/tests/error.test.ts +130 -1
  79. package/node_modules/zod/src/v4/classic/tests/from-json-schema.test.ts +157 -0
  80. package/node_modules/zod/src/v4/classic/tests/iban.test.ts +81 -0
  81. package/node_modules/zod/src/v4/classic/tests/instance-footprint.test.ts +40 -2
  82. package/node_modules/zod/src/v4/classic/tests/instanceof.test.ts +27 -7
  83. package/node_modules/zod/src/v4/classic/tests/issue-schema.test.ts +88 -56
  84. package/node_modules/zod/src/v4/classic/tests/number.test.ts +39 -0
  85. package/node_modules/zod/src/v4/classic/tests/object.test.ts +56 -0
  86. package/node_modules/zod/src/v4/classic/tests/optin-ladder.test.ts +3 -2
  87. package/node_modules/zod/src/v4/classic/tests/prefault.test.ts +70 -0
  88. package/node_modules/zod/src/v4/classic/tests/properties.test.ts +141 -0
  89. package/node_modules/zod/src/v4/classic/tests/recursive-types.test.ts +46 -1
  90. package/node_modules/zod/src/v4/classic/tests/standard-schema.test.ts +19 -0
  91. package/node_modules/zod/src/v4/classic/tests/string.test.ts +95 -1
  92. package/node_modules/zod/src/v4/classic/tests/template-literal.test.ts +33 -1
  93. package/node_modules/zod/src/v4/classic/tests/to-json-schema.test.ts +234 -38
  94. package/node_modules/zod/src/v4/classic/tests/validate.test.ts +569 -0
  95. package/node_modules/zod/src/v4/core/api.ts +44 -28
  96. package/node_modules/zod/src/v4/core/checks.ts +62 -102
  97. package/node_modules/zod/src/v4/core/compile.ts +218 -93
  98. package/node_modules/zod/src/v4/core/core.ts +1 -3
  99. package/node_modules/zod/src/v4/core/doc.ts +6 -2
  100. package/node_modules/zod/src/v4/core/errors.ts +3 -6
  101. package/node_modules/zod/src/v4/core/json-schema-generator.ts +2 -2
  102. package/node_modules/zod/src/v4/core/json-schema-processors.ts +185 -81
  103. package/node_modules/zod/src/v4/core/memoizer.ts +75 -28
  104. package/node_modules/zod/src/v4/core/parse.ts +37 -16
  105. package/node_modules/zod/src/v4/core/regexes.ts +14 -4
  106. package/node_modules/zod/src/v4/core/schemas.ts +296 -132
  107. package/node_modules/zod/src/v4/core/tests/cached.test.ts +54 -0
  108. package/node_modules/zod/src/v4/core/tests/compile-differential.test.ts +35 -0
  109. package/node_modules/zod/src/v4/core/tests/compile-generative.test.ts +445 -0
  110. package/node_modules/zod/src/v4/core/tests/compile.test.ts +399 -2
  111. package/node_modules/zod/src/v4/core/tests/email-regex.test.ts +140 -0
  112. package/node_modules/zod/src/v4/core/tests/locales/tg.test.ts +85 -0
  113. package/node_modules/zod/src/v4/core/tests/polyfill-collision.test.ts +46 -0
  114. package/node_modules/zod/src/v4/core/tests/url-no-canparse.test.ts +118 -16
  115. package/node_modules/zod/src/v4/core/to-json-schema.ts +9 -7
  116. package/node_modules/zod/src/v4/core/util.ts +218 -147
  117. package/node_modules/zod/src/v4/core/versions.ts +2 -2
  118. package/node_modules/zod/src/v4/locales/ar.ts +2 -0
  119. package/node_modules/zod/src/v4/locales/az.ts +2 -0
  120. package/node_modules/zod/src/v4/locales/be.ts +2 -0
  121. package/node_modules/zod/src/v4/locales/bg.ts +2 -0
  122. package/node_modules/zod/src/v4/locales/bn.ts +2 -0
  123. package/node_modules/zod/src/v4/locales/ca.ts +2 -0
  124. package/node_modules/zod/src/v4/locales/ckb.ts +2 -0
  125. package/node_modules/zod/src/v4/locales/cs.ts +2 -0
  126. package/node_modules/zod/src/v4/locales/da.ts +2 -0
  127. package/node_modules/zod/src/v4/locales/de.ts +2 -0
  128. package/node_modules/zod/src/v4/locales/el.ts +2 -0
  129. package/node_modules/zod/src/v4/locales/en.ts +2 -0
  130. package/node_modules/zod/src/v4/locales/eo.ts +2 -0
  131. package/node_modules/zod/src/v4/locales/es.ts +2 -0
  132. package/node_modules/zod/src/v4/locales/fa.ts +2 -0
  133. package/node_modules/zod/src/v4/locales/fi.ts +2 -0
  134. package/node_modules/zod/src/v4/locales/fr-CA.ts +2 -0
  135. package/node_modules/zod/src/v4/locales/fr.ts +2 -0
  136. package/node_modules/zod/src/v4/locales/gu.ts +2 -0
  137. package/node_modules/zod/src/v4/locales/he.ts +2 -0
  138. package/node_modules/zod/src/v4/locales/hi.ts +2 -0
  139. package/node_modules/zod/src/v4/locales/hr.ts +2 -0
  140. package/node_modules/zod/src/v4/locales/hu.ts +2 -0
  141. package/node_modules/zod/src/v4/locales/hy.ts +2 -0
  142. package/node_modules/zod/src/v4/locales/id.ts +2 -0
  143. package/node_modules/zod/src/v4/locales/index.ts +1 -0
  144. package/node_modules/zod/src/v4/locales/is.ts +2 -0
  145. package/node_modules/zod/src/v4/locales/it.ts +2 -0
  146. package/node_modules/zod/src/v4/locales/ja.ts +2 -0
  147. package/node_modules/zod/src/v4/locales/ka.ts +2 -0
  148. package/node_modules/zod/src/v4/locales/km.ts +2 -0
  149. package/node_modules/zod/src/v4/locales/kn.ts +2 -0
  150. package/node_modules/zod/src/v4/locales/ko.ts +2 -0
  151. package/node_modules/zod/src/v4/locales/lt.ts +2 -0
  152. package/node_modules/zod/src/v4/locales/mk.ts +2 -0
  153. package/node_modules/zod/src/v4/locales/ms.ts +2 -0
  154. package/node_modules/zod/src/v4/locales/ne.ts +2 -0
  155. package/node_modules/zod/src/v4/locales/nl.ts +2 -0
  156. package/node_modules/zod/src/v4/locales/nn.ts +2 -0
  157. package/node_modules/zod/src/v4/locales/no.ts +2 -0
  158. package/node_modules/zod/src/v4/locales/ota.ts +2 -0
  159. package/node_modules/zod/src/v4/locales/pl.ts +2 -0
  160. package/node_modules/zod/src/v4/locales/ps.ts +2 -0
  161. package/node_modules/zod/src/v4/locales/pt-BR.ts +2 -0
  162. package/node_modules/zod/src/v4/locales/pt.ts +2 -0
  163. package/node_modules/zod/src/v4/locales/ro.ts +2 -0
  164. package/node_modules/zod/src/v4/locales/ru.ts +2 -0
  165. package/node_modules/zod/src/v4/locales/sk.ts +2 -0
  166. package/node_modules/zod/src/v4/locales/sl.ts +2 -0
  167. package/node_modules/zod/src/v4/locales/sv.ts +2 -0
  168. package/node_modules/zod/src/v4/locales/ta.ts +2 -0
  169. package/node_modules/zod/src/v4/locales/tg.ts +135 -0
  170. package/node_modules/zod/src/v4/locales/th.ts +2 -0
  171. package/node_modules/zod/src/v4/locales/tk.ts +2 -0
  172. package/node_modules/zod/src/v4/locales/tr.ts +2 -0
  173. package/node_modules/zod/src/v4/locales/uk.ts +2 -0
  174. package/node_modules/zod/src/v4/locales/ur.ts +2 -0
  175. package/node_modules/zod/src/v4/locales/uz.ts +2 -0
  176. package/node_modules/zod/src/v4/locales/vi.ts +2 -0
  177. package/node_modules/zod/src/v4/locales/yo.ts +2 -0
  178. package/node_modules/zod/src/v4/locales/zh-CN.ts +2 -0
  179. package/node_modules/zod/src/v4/locales/zh-TW.ts +2 -0
  180. package/node_modules/zod/src/v4/mini/external.ts +2 -0
  181. package/node_modules/zod/src/v4/mini/schemas.ts +32 -7
  182. package/node_modules/zod/src/v4/mini/tests/checks.test.ts +1 -1
  183. package/node_modules/zod/src/v4/mini/tests/computed.test.ts +11 -17
  184. package/node_modules/zod/src/v4/mini/tests/index.test.ts +86 -0
  185. package/node_modules/zod/src/v4/mini/tests/recursive-types.test.ts +16 -0
  186. package/node_modules/zod/src/v4/mini/tests/string.test.ts +23 -0
  187. package/node_modules/zod/v3/ZodError.cjs +19 -0
  188. package/node_modules/zod/v3/errors.cjs +19 -0
  189. package/node_modules/zod/v3/external.cjs +19 -0
  190. package/node_modules/zod/v3/helpers/enumUtil.cjs +19 -0
  191. package/node_modules/zod/v3/helpers/errorUtil.cjs +20 -1
  192. package/node_modules/zod/v3/helpers/errorUtil.js +1 -1
  193. package/node_modules/zod/v3/helpers/parseUtil.cjs +19 -0
  194. package/node_modules/zod/v3/helpers/partialUtil.cjs +19 -0
  195. package/node_modules/zod/v3/helpers/typeAliases.cjs +19 -0
  196. package/node_modules/zod/v3/helpers/util.cjs +19 -0
  197. package/node_modules/zod/v3/index.cjs +19 -0
  198. package/node_modules/zod/v3/standard-schema.cjs +19 -0
  199. package/node_modules/zod/v3/types.cjs +19 -0
  200. package/node_modules/zod/v4/classic/checks.cjs +19 -0
  201. package/node_modules/zod/v4/classic/coerce.cjs +19 -0
  202. package/node_modules/zod/v4/classic/compat.cjs +19 -0
  203. package/node_modules/zod/v4/classic/deep-partial.cjs +19 -0
  204. package/node_modules/zod/v4/classic/errors.cjs +19 -0
  205. package/node_modules/zod/v4/classic/external.cjs +22 -1
  206. package/node_modules/zod/v4/classic/external.d.cts +1 -1
  207. package/node_modules/zod/v4/classic/external.d.ts +1 -1
  208. package/node_modules/zod/v4/classic/external.js +1 -1
  209. package/node_modules/zod/v4/classic/from-json-schema.cjs +264 -24
  210. package/node_modules/zod/v4/classic/from-json-schema.js +245 -24
  211. package/node_modules/zod/v4/classic/in-out.cjs +19 -0
  212. package/node_modules/zod/v4/classic/index.cjs +19 -0
  213. package/node_modules/zod/v4/classic/iso.cjs +19 -0
  214. package/node_modules/zod/v4/classic/parse.cjs +19 -0
  215. package/node_modules/zod/v4/classic/schemas.cjs +100 -35
  216. package/node_modules/zod/v4/classic/schemas.d.cts +15 -1
  217. package/node_modules/zod/v4/classic/schemas.d.ts +15 -1
  218. package/node_modules/zod/v4/classic/schemas.js +77 -33
  219. package/node_modules/zod/v4/core/api.cjs +45 -22
  220. package/node_modules/zod/v4/core/api.d.cts +12 -6
  221. package/node_modules/zod/v4/core/api.d.ts +12 -6
  222. package/node_modules/zod/v4/core/api.js +25 -22
  223. package/node_modules/zod/v4/core/checks.cjs +51 -97
  224. package/node_modules/zod/v4/core/checks.d.cts +17 -1
  225. package/node_modules/zod/v4/core/checks.d.ts +17 -1
  226. package/node_modules/zod/v4/core/checks.js +31 -96
  227. package/node_modules/zod/v4/core/compile.cjs +173 -78
  228. package/node_modules/zod/v4/core/compile.d.cts +18 -4
  229. package/node_modules/zod/v4/core/compile.d.ts +18 -4
  230. package/node_modules/zod/v4/core/compile.js +154 -79
  231. package/node_modules/zod/v4/core/core.cjs +20 -1
  232. package/node_modules/zod/v4/core/core.js +1 -1
  233. package/node_modules/zod/v4/core/doc.cjs +26 -2
  234. package/node_modules/zod/v4/core/doc.js +7 -2
  235. package/node_modules/zod/v4/core/errors.cjs +21 -5
  236. package/node_modules/zod/v4/core/errors.js +2 -5
  237. package/node_modules/zod/v4/core/index.cjs +19 -0
  238. package/node_modules/zod/v4/core/json-schema-generator.cjs +20 -1
  239. package/node_modules/zod/v4/core/json-schema-generator.js +2 -2
  240. package/node_modules/zod/v4/core/json-schema-processors.cjs +188 -72
  241. package/node_modules/zod/v4/core/json-schema-processors.d.cts +16 -0
  242. package/node_modules/zod/v4/core/json-schema-processors.d.ts +16 -0
  243. package/node_modules/zod/v4/core/json-schema-processors.js +170 -74
  244. package/node_modules/zod/v4/core/json-schema.cjs +19 -0
  245. package/node_modules/zod/v4/core/memoizer.cjs +111 -23
  246. package/node_modules/zod/v4/core/memoizer.js +69 -23
  247. package/node_modules/zod/v4/core/parse.cjs +56 -16
  248. package/node_modules/zod/v4/core/parse.js +37 -16
  249. package/node_modules/zod/v4/core/regexes.cjs +31 -6
  250. package/node_modules/zod/v4/core/regexes.d.cts +3 -0
  251. package/node_modules/zod/v4/core/regexes.d.ts +3 -0
  252. package/node_modules/zod/v4/core/regexes.js +10 -4
  253. package/node_modules/zod/v4/core/registries.cjs +19 -0
  254. package/node_modules/zod/v4/core/schemas.cjs +302 -114
  255. package/node_modules/zod/v4/core/schemas.d.cts +28 -8
  256. package/node_modules/zod/v4/core/schemas.d.ts +28 -8
  257. package/node_modules/zod/v4/core/schemas.js +279 -113
  258. package/node_modules/zod/v4/core/standard-schema.cjs +19 -0
  259. package/node_modules/zod/v4/core/to-json-schema.cjs +28 -7
  260. package/node_modules/zod/v4/core/to-json-schema.d.cts +4 -3
  261. package/node_modules/zod/v4/core/to-json-schema.d.ts +4 -3
  262. package/node_modules/zod/v4/core/to-json-schema.js +7 -6
  263. package/node_modules/zod/v4/core/util.cjs +177 -143
  264. package/node_modules/zod/v4/core/util.d.cts +25 -4
  265. package/node_modules/zod/v4/core/util.d.ts +25 -4
  266. package/node_modules/zod/v4/core/util.js +156 -143
  267. package/node_modules/zod/v4/core/versions.cjs +21 -2
  268. package/node_modules/zod/v4/core/versions.d.cts +1 -1
  269. package/node_modules/zod/v4/core/versions.d.ts +1 -1
  270. package/node_modules/zod/v4/core/versions.js +2 -2
  271. package/node_modules/zod/v4/core/visit.cjs +19 -0
  272. package/node_modules/zod/v4/index.cjs +19 -0
  273. package/node_modules/zod/v4/locales/ar.cjs +2 -0
  274. package/node_modules/zod/v4/locales/ar.js +2 -0
  275. package/node_modules/zod/v4/locales/az.cjs +2 -0
  276. package/node_modules/zod/v4/locales/az.js +2 -0
  277. package/node_modules/zod/v4/locales/be.cjs +2 -0
  278. package/node_modules/zod/v4/locales/be.js +2 -0
  279. package/node_modules/zod/v4/locales/bg.cjs +2 -0
  280. package/node_modules/zod/v4/locales/bg.js +2 -0
  281. package/node_modules/zod/v4/locales/bn.cjs +2 -0
  282. package/node_modules/zod/v4/locales/bn.js +2 -0
  283. package/node_modules/zod/v4/locales/ca.cjs +2 -0
  284. package/node_modules/zod/v4/locales/ca.js +2 -0
  285. package/node_modules/zod/v4/locales/ckb.cjs +2 -0
  286. package/node_modules/zod/v4/locales/ckb.js +2 -0
  287. package/node_modules/zod/v4/locales/cs.cjs +2 -0
  288. package/node_modules/zod/v4/locales/cs.js +2 -0
  289. package/node_modules/zod/v4/locales/da.cjs +2 -0
  290. package/node_modules/zod/v4/locales/da.js +2 -0
  291. package/node_modules/zod/v4/locales/de.cjs +2 -0
  292. package/node_modules/zod/v4/locales/de.js +2 -0
  293. package/node_modules/zod/v4/locales/el.cjs +2 -0
  294. package/node_modules/zod/v4/locales/el.js +2 -0
  295. package/node_modules/zod/v4/locales/en.cjs +2 -0
  296. package/node_modules/zod/v4/locales/en.js +2 -0
  297. package/node_modules/zod/v4/locales/eo.cjs +2 -0
  298. package/node_modules/zod/v4/locales/eo.js +2 -0
  299. package/node_modules/zod/v4/locales/es.cjs +2 -0
  300. package/node_modules/zod/v4/locales/es.js +2 -0
  301. package/node_modules/zod/v4/locales/fa.cjs +2 -0
  302. package/node_modules/zod/v4/locales/fa.js +2 -0
  303. package/node_modules/zod/v4/locales/fi.cjs +2 -0
  304. package/node_modules/zod/v4/locales/fi.js +2 -0
  305. package/node_modules/zod/v4/locales/fr-CA.cjs +2 -0
  306. package/node_modules/zod/v4/locales/fr-CA.js +2 -0
  307. package/node_modules/zod/v4/locales/fr.cjs +2 -0
  308. package/node_modules/zod/v4/locales/fr.js +2 -0
  309. package/node_modules/zod/v4/locales/gu.cjs +2 -0
  310. package/node_modules/zod/v4/locales/gu.js +2 -0
  311. package/node_modules/zod/v4/locales/he.cjs +2 -0
  312. package/node_modules/zod/v4/locales/he.js +2 -0
  313. package/node_modules/zod/v4/locales/hi.cjs +2 -0
  314. package/node_modules/zod/v4/locales/hi.js +2 -0
  315. package/node_modules/zod/v4/locales/hr.cjs +2 -0
  316. package/node_modules/zod/v4/locales/hr.js +2 -0
  317. package/node_modules/zod/v4/locales/hu.cjs +2 -0
  318. package/node_modules/zod/v4/locales/hu.js +2 -0
  319. package/node_modules/zod/v4/locales/hy.cjs +2 -0
  320. package/node_modules/zod/v4/locales/hy.js +2 -0
  321. package/node_modules/zod/v4/locales/id.cjs +2 -0
  322. package/node_modules/zod/v4/locales/id.js +2 -0
  323. package/node_modules/zod/v4/locales/index.cjs +22 -1
  324. package/node_modules/zod/v4/locales/index.d.cts +1 -0
  325. package/node_modules/zod/v4/locales/index.d.ts +1 -0
  326. package/node_modules/zod/v4/locales/index.js +1 -0
  327. package/node_modules/zod/v4/locales/is.cjs +2 -0
  328. package/node_modules/zod/v4/locales/is.js +2 -0
  329. package/node_modules/zod/v4/locales/it.cjs +2 -0
  330. package/node_modules/zod/v4/locales/it.js +2 -0
  331. package/node_modules/zod/v4/locales/ja.cjs +2 -0
  332. package/node_modules/zod/v4/locales/ja.js +2 -0
  333. package/node_modules/zod/v4/locales/ka.cjs +2 -0
  334. package/node_modules/zod/v4/locales/ka.js +2 -0
  335. package/node_modules/zod/v4/locales/km.cjs +2 -0
  336. package/node_modules/zod/v4/locales/km.js +2 -0
  337. package/node_modules/zod/v4/locales/kn.cjs +2 -0
  338. package/node_modules/zod/v4/locales/kn.js +2 -0
  339. package/node_modules/zod/v4/locales/ko.cjs +2 -0
  340. package/node_modules/zod/v4/locales/ko.js +2 -0
  341. package/node_modules/zod/v4/locales/lt.cjs +2 -0
  342. package/node_modules/zod/v4/locales/lt.js +2 -0
  343. package/node_modules/zod/v4/locales/mk.cjs +2 -0
  344. package/node_modules/zod/v4/locales/mk.js +2 -0
  345. package/node_modules/zod/v4/locales/ms.cjs +2 -0
  346. package/node_modules/zod/v4/locales/ms.js +2 -0
  347. package/node_modules/zod/v4/locales/ne.cjs +2 -0
  348. package/node_modules/zod/v4/locales/ne.js +2 -0
  349. package/node_modules/zod/v4/locales/nl.cjs +2 -0
  350. package/node_modules/zod/v4/locales/nl.js +2 -0
  351. package/node_modules/zod/v4/locales/nn.cjs +2 -0
  352. package/node_modules/zod/v4/locales/nn.js +2 -0
  353. package/node_modules/zod/v4/locales/no.cjs +2 -0
  354. package/node_modules/zod/v4/locales/no.js +2 -0
  355. package/node_modules/zod/v4/locales/ota.cjs +2 -0
  356. package/node_modules/zod/v4/locales/ota.js +2 -0
  357. package/node_modules/zod/v4/locales/pl.cjs +2 -0
  358. package/node_modules/zod/v4/locales/pl.js +2 -0
  359. package/node_modules/zod/v4/locales/ps.cjs +2 -0
  360. package/node_modules/zod/v4/locales/ps.js +2 -0
  361. package/node_modules/zod/v4/locales/pt-BR.cjs +2 -0
  362. package/node_modules/zod/v4/locales/pt-BR.js +2 -0
  363. package/node_modules/zod/v4/locales/pt.cjs +2 -0
  364. package/node_modules/zod/v4/locales/pt.js +2 -0
  365. package/node_modules/zod/v4/locales/ro.cjs +2 -0
  366. package/node_modules/zod/v4/locales/ro.js +2 -0
  367. package/node_modules/zod/v4/locales/ru.cjs +2 -0
  368. package/node_modules/zod/v4/locales/ru.js +2 -0
  369. package/node_modules/zod/v4/locales/sk.cjs +2 -0
  370. package/node_modules/zod/v4/locales/sk.js +2 -0
  371. package/node_modules/zod/v4/locales/sl.cjs +2 -0
  372. package/node_modules/zod/v4/locales/sl.js +2 -0
  373. package/node_modules/zod/v4/locales/sv.cjs +2 -0
  374. package/node_modules/zod/v4/locales/sv.js +2 -0
  375. package/node_modules/zod/v4/locales/ta.cjs +2 -0
  376. package/node_modules/zod/v4/locales/ta.js +2 -0
  377. package/node_modules/zod/v4/locales/tg.cjs +144 -0
  378. package/node_modules/zod/v4/locales/tg.d.cts +5 -0
  379. package/node_modules/zod/v4/locales/tg.d.ts +4 -0
  380. package/node_modules/zod/v4/locales/tg.js +117 -0
  381. package/node_modules/zod/v4/locales/th.cjs +2 -0
  382. package/node_modules/zod/v4/locales/th.js +2 -0
  383. package/node_modules/zod/v4/locales/tk.cjs +2 -0
  384. package/node_modules/zod/v4/locales/tk.js +2 -0
  385. package/node_modules/zod/v4/locales/tr.cjs +2 -0
  386. package/node_modules/zod/v4/locales/tr.js +2 -0
  387. package/node_modules/zod/v4/locales/uk.cjs +2 -0
  388. package/node_modules/zod/v4/locales/uk.js +2 -0
  389. package/node_modules/zod/v4/locales/ur.cjs +2 -0
  390. package/node_modules/zod/v4/locales/ur.js +2 -0
  391. package/node_modules/zod/v4/locales/uz.cjs +2 -0
  392. package/node_modules/zod/v4/locales/uz.js +2 -0
  393. package/node_modules/zod/v4/locales/vi.cjs +2 -0
  394. package/node_modules/zod/v4/locales/vi.js +2 -0
  395. package/node_modules/zod/v4/locales/yo.cjs +2 -0
  396. package/node_modules/zod/v4/locales/yo.js +2 -0
  397. package/node_modules/zod/v4/locales/zh-CN.cjs +2 -0
  398. package/node_modules/zod/v4/locales/zh-CN.js +2 -0
  399. package/node_modules/zod/v4/locales/zh-TW.cjs +2 -0
  400. package/node_modules/zod/v4/locales/zh-TW.js +2 -0
  401. package/node_modules/zod/v4/mini/checks.cjs +19 -0
  402. package/node_modules/zod/v4/mini/coerce.cjs +19 -0
  403. package/node_modules/zod/v4/mini/deep-partial.cjs +19 -0
  404. package/node_modules/zod/v4/mini/external.cjs +22 -1
  405. package/node_modules/zod/v4/mini/external.d.cts +1 -1
  406. package/node_modules/zod/v4/mini/external.d.ts +1 -1
  407. package/node_modules/zod/v4/mini/external.js +1 -1
  408. package/node_modules/zod/v4/mini/in-out.cjs +19 -0
  409. package/node_modules/zod/v4/mini/index.cjs +19 -0
  410. package/node_modules/zod/v4/mini/iso.cjs +19 -0
  411. package/node_modules/zod/v4/mini/parse.cjs +19 -0
  412. package/node_modules/zod/v4/mini/schemas.cjs +44 -8
  413. package/node_modules/zod/v4/mini/schemas.d.cts +6 -1
  414. package/node_modules/zod/v4/mini/schemas.d.ts +6 -1
  415. package/node_modules/zod/v4/mini/schemas.js +21 -6
  416. package/node_modules/zod/v4-mini/index.cjs +19 -0
  417. package/package.json +8 -5
  418. package/dist/.setup/message-handler.history-CJOsN9w5.mjs +0 -28
  419. package/dist/.setup/provider.runtime-DuhmKdDU.mjs +0 -2
  420. package/dist/.setup/setup-plugin-api-CINIEBLa.mjs +0 -3
  421. package/dist/.setup/targets-D1mAB5yO.mjs +0 -3
@@ -1,21 +1,20 @@
1
- import { ft as editChannelMessage, st as createChannelMessage, t as discord_exports, ut as deleteChannelMessage } from "./discord-BXpHW-cu.mjs";
1
+ import { C as withDiscordRequestAuthority, D as Message, _t as listChannelMessages, ft as editChannelMessage, jt as getGuildMember, st as createChannelMessage, t as discord_exports, ut as deleteChannelMessage } from "./discord-BXpHW-cu.mjs";
2
2
  import { C as resolveTimestampMs, i as normalizeDiscordAllowList, o as normalizeDiscordSlug } from "./channel-type-DKnjV1XW.mjs";
3
3
  import { p as resolveDiscordMaxLinesPerMessage } from "./accounts-CwJQoLjM.mjs";
4
- import { b as resolveDiscordChannelId, o as chunkDiscordTextWithMode } from "./retry-DBfcEgmO.mjs";
5
- import { F as createDiscordRuntimeAccountContext, P as createDiscordRestClient, d as resolveDiscordTargetChannelId, k as resolveDiscordMessageFlags } from "./send.shared-Zwx6BxQ5.mjs";
6
- import { a as discordTextHasBroadcastMention } from "./send.outbound-D0hQ7s50.mjs";
7
- import { D as resolveDiscordMessageStickers, b as resolveDiscordMessageText, h as resolveDiscordThreadStarter, p as resolveDiscordAutoThreadReplyPlan, w as resolveReferencedReplyMediaList, x as formatDiscordMediaText } from "./transcripts-source-CuWYgayu.mjs";
8
- import { a as removeReactionDiscord, f as editMessageDiscord, r as reactMessageDiscord } from "./send-CKDczUNL.mjs";
4
+ import { b as resolveDiscordChannelId, o as chunkDiscordTextWithMode } from "./retry-BEYkDy0P.mjs";
5
+ import { F as createDiscordRuntimeAccountContext, P as createDiscordRestClient, d as resolveDiscordTargetChannelId, k as resolveDiscordMessageFlags } from "./send.shared-VNvWfX2T.mjs";
6
+ import { a as discordTextHasBroadcastMention } from "./send.outbound-QTyuFupn.mjs";
7
+ import { O as resolveDiscordMessageStickers, S as formatDiscordMediaText, T as resolveReferencedReplyMediaList, b as resolveDiscordMessageMentionDocuments, g as resolveDiscordThreadStarter, m as resolveDiscordAutoThreadReplyPlan, x as resolveDiscordMessageText, y as resolveDiscordMessageHistoryText } from "./transcripts-source-DVegW0WI.mjs";
8
+ import { a as removeReactionDiscord, r as reactMessageDiscord } from "./send-CIBzvXjS.mjs";
9
9
  import { n as DISCORD_ATTACHMENT_TOTAL_TIMEOUT_MS, t as DISCORD_ATTACHMENT_IDLE_TIMEOUT_MS } from "./timeouts-D86uWLt_.mjs";
10
- import "./targets-D1mAB5yO.mjs";
11
- import { b as discordInboundEventDelivery, h as resolveDiscordConversationIdentity, u as buildDiscordRoutePeer } from "./outbound-session-route-CqWbuMOm.mjs";
12
- import { t as DISCORD_TEXT_CHUNK_LIMIT } from "./outbound-adapter-C8v79z9Z.mjs";
13
- import { a as formatDiscordReplySkip, c as buildGuildLabel, g as createDiscordSupplementalContextAccessChecker, h as buildDiscordInboundAccessContext, i as formatDiscordReplyDeliveryFailure, l as resolveReplyContext, o as sanitizeDiscordFrontChannelReplyPayloads, r as deliverDiscordReply, s as buildDirectLabel } from "./provider-72zIvN-i.mjs";
14
- import { n as resolveDiscordWebhookId } from "./sender-identity-0Ymgvmaj.mjs";
10
+ import "./targets-CvemhN3i.mjs";
11
+ import { b as discordInboundEventDelivery, h as resolveDiscordConversationIdentity, u as buildDiscordRoutePeer } from "./outbound-session-route-VulUE0yV.mjs";
12
+ import { t as DISCORD_TEXT_CHUNK_LIMIT } from "./outbound-adapter-ClIgmu4F.mjs";
13
+ import { a as formatDiscordReplySkip, c as buildGuildLabel, g as createDiscordSupplementalContextAccessChecker, h as buildDiscordInboundAccessContext, i as formatDiscordReplyDeliveryFailure, l as resolveReplyContext, o as sanitizeDiscordFrontChannelReplyPayloads, r as deliverDiscordReply, s as buildDirectLabel } from "./provider-iOf73HW-.mjs";
14
+ import { n as resolveDiscordWebhookId, t as resolveDiscordSenderIdentity } from "./sender-identity-0Ymgvmaj.mjs";
15
15
  import { t as beginDiscordActiveTurnThreadRoute } from "./active-turn-thread-route-fmHl1HNd.mjs";
16
- import { n as filterDiscordHistoryEntriesForContext, t as createDiscordHistorySenderProvenance } from "./message-handler.history-CJOsN9w5.mjs";
16
+ import { a as createDiscordHistorySenderProvenance, c as hasRawDiscordUserMention, d as matchesActiveDiscordMentionPatterns, h as shouldIgnoreBoundThreadWebhookMessage, l as isBoundThreadBotSystemMessage, o as filterDiscordHistoryEntriesForContext, s as resolveDiscordHistoryMediaIds, t as resolveDiscordPreflightPluralKitInfo } from "./message-handler.preflight-pluralkit-8Xji34AF.mjs";
17
17
  import { t as sendTyping } from "./typing-BKZL5fGj.mjs";
18
- import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
19
18
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
20
19
  import { buildAgentSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
21
20
  import { getReplyPayloadTtsSupplement, isReplyPayloadNonTerminalToolErrorWarning, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
@@ -23,15 +22,15 @@ import { danger, logVerbose, shouldLogVerbose, sleepWithAbort } from "openclaw/p
23
22
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
24
23
  import { resolveChunkMode } from "openclaw/plugin-sdk/reply-chunking";
25
24
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
26
- import { convertMarkdownTables, stripInlineDirectiveTagsForDelivery, stripReasoningTagsFromText } from "openclaw/plugin-sdk/text-chunking";
27
- import { bindIngressLifecycleToReplyOptions, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createFinalizableDraftStreamControlsForState, createTypingCallbacks, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, resolveChannelDraftStreamingChunking, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelPreviewStreamMode, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewCommandText, resolveChannelStreamingProgressNarration, resolveTranscriptBackedChannelFinalText } from "openclaw/plugin-sdk/channel-outbound";
25
+ import { stripInlineDirectiveTagsForDelivery, stripReasoningTagsFromText } from "openclaw/plugin-sdk/text-chunking";
26
+ import { bindIngressLifecycleToReplyOptions, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createFinalizableDraftLifecycle, createLivePreviewLifecycle, createTypingCallbacks, isRecentOutboundMessageIdentity, resolveChannelDraftStreamingChunking, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelPreviewStreamMode, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewCommandText, resolveChannelStreamingProgressNarration, resolveTranscriptBackedChannelFinalText } from "openclaw/plugin-sdk/channel-outbound";
28
27
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
29
- import { buildChannelInboundEventContext, createCommandTurnContext, dispatchChannelInboundTurn, formatInboundEnvelope, formatInboundMediaUnavailableText, getGroupThreadDeliverySession, hasFinalInboundReplyDispatch, readAgentRunTerminalOutcome, resolveEnvelopeFormatOptions, toHistoryMediaEntries, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
28
+ import { buildChannelInboundEventContext, buildMentionRegexes, createCommandTurnContext, dispatchChannelInboundTurn, formatInboundEnvelope, formatInboundMediaUnavailableText, getGroupThreadDeliverySession, hasFinalInboundReplyDispatch, readAgentRunTerminalOutcome, resolveEnvelopeFormatOptions, toHistoryMediaEntries, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
30
29
  import { evaluateSupplementalContextVisibility } from "openclaw/plugin-sdk/security-runtime";
31
30
  import { resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/conversation-runtime";
32
31
  import { resolveAgentConfig } from "openclaw/plugin-sdk/agent-scope-runtime";
33
- import { EmbeddedBlockChunker, resolveAckReaction, resolveAgentConfig as resolveAgentConfig$1, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
34
32
  import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
33
+ import { EmbeddedBlockChunker, resolveAckReaction, resolveAgentConfig as resolveAgentConfig$1, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
35
34
  import { getSessionEntry, readSessionUpdatedAt, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
36
35
  import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
37
36
  import { buildHistoryContextFromEntries, buildInboundHistoryFromEntries, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
@@ -39,6 +38,127 @@ import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context
39
38
  import { formatAudioTranscriptForAgent } from "openclaw/plugin-sdk/media-understanding-runtime";
40
39
  import { createStatusReactionController, logAckFailure, logTypingFailure, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
41
40
  import { readLatestAssistantTextByIdentity } from "openclaw/plugin-sdk/session-transcript-runtime";
41
+ //#region extensions/discord/src/monitor/message-handler.recent-history.ts
42
+ /** Fetch a physical recent window, never walking older pages to refill filtered rows. */
43
+ async function recoverDiscordChannelHistory(params) {
44
+ const { ctx, isCurrent } = params;
45
+ if (ctx.historyLimit <= 0 || !isCurrent()) return [];
46
+ const excludedIds = /* @__PURE__ */ new Set([ctx.message.id, ...ctx.sourceMessageIds ?? []]);
47
+ if (ctx.canonicalMessageId) excludedIds.add(ctx.canonicalMessageId);
48
+ let remaining = ctx.historyLimit + excludedIds.size - 1;
49
+ let before = ctx.message.id;
50
+ const triggerTimestamp = resolveTimestampMs(ctx.message.timestamp);
51
+ const guildId = ctx.data.guild?.id ?? ctx.data.guild_id;
52
+ const cachedMedia = new Map((ctx.guildHistories.get(ctx.messageChannelId) ?? []).map((entry) => [entry.messageId, entry]));
53
+ const members = /* @__PURE__ */ new Map();
54
+ const roleAllowList = ctx.channelConfig?.roles ?? ctx.guildInfo?.roles ?? [];
55
+ const mentionRegexes = ctx.discordConfig?.allowBots === "mentions" ? buildMentionRegexes(ctx.cfg, ctx.route.agentId, {
56
+ provider: "discord",
57
+ conversationId: ctx.messageChannelId,
58
+ providerPolicy: ctx.discordConfig.mentionPatterns
59
+ }) : [];
60
+ const entries = [];
61
+ try {
62
+ while (remaining > 0 && isCurrent()) {
63
+ const limit = Math.min(100, remaining);
64
+ const page = await listChannelMessages(ctx.client.rest, ctx.messageChannelId, {
65
+ before,
66
+ limit
67
+ });
68
+ if (!isCurrent()) return [];
69
+ const window = page.slice(0, limit);
70
+ remaining -= window.length;
71
+ for (const raw of window) {
72
+ const row = raw;
73
+ const timestamp = resolveTimestampMs(row.timestamp);
74
+ if (row.channel_id !== ctx.messageChannelId || row.guild_id && row.guild_id !== guildId || excludedIds.has(row.id) || /^\d+$/u.test(row.id) && /^\d+$/u.test(ctx.message.id) && BigInt(row.id) >= BigInt(ctx.message.id) || timestamp === void 0 || triggerTimestamp !== void 0 && timestamp > triggerTimestamp || params.sessionStartedAt !== void 0 && timestamp < params.sessionStartedAt || row.type === discord_exports.MessageType.ChatInputCommand || row.type === discord_exports.MessageType.ContextMenuCommand || row.author.id === ctx.botUserId) continue;
75
+ excludedIds.add(row.id);
76
+ const message = new Message(ctx.client, row);
77
+ let body = resolveDiscordMessageHistoryText(message, { includeForwarded: true });
78
+ if (!body || isRecentOutboundMessageIdentity({
79
+ channel: "discord",
80
+ accountId: ctx.accountId,
81
+ conversationId: ctx.messageChannelId,
82
+ messageId: row.id,
83
+ ...row.webhook_id ? { sourceId: row.webhook_id } : {}
84
+ }) || shouldIgnoreBoundThreadWebhookMessage({
85
+ threadId: ctx.messageChannelId,
86
+ webhookId: message.webhookId,
87
+ threadBinding: ctx.threadBinding
88
+ }) || isBoundThreadBotSystemMessage({
89
+ isBoundThreadSession: Boolean(ctx.threadBinding && ctx.threadChannel),
90
+ isBotAuthor: Boolean(row.author.bot),
91
+ text: body
92
+ })) continue;
93
+ const needsPluralKitLookup = Boolean(ctx.discordConfig?.pluralkit?.enabled && message.webhookId);
94
+ const pluralkitInfo = needsPluralKitLookup ? await resolveDiscordPreflightPluralKitInfo({
95
+ message,
96
+ webhookId: message.webhookId,
97
+ config: ctx.discordConfig?.pluralkit,
98
+ abortSignal: ctx.abortSignal
99
+ }) : null;
100
+ if (needsPluralKitLookup && !isCurrent()) return [];
101
+ const sender = resolveDiscordSenderIdentity({
102
+ author: message.author,
103
+ member: row.member,
104
+ pluralkitInfo
105
+ });
106
+ if (row.author.bot && !sender.isPluralKit) {
107
+ const allowBots = ctx.discordConfig?.allowBots;
108
+ if (!allowBots) continue;
109
+ if (allowBots === "mentions") {
110
+ const documents = resolveDiscordMessageMentionDocuments(message);
111
+ if (!(row.mentions.some((user) => user.id === ctx.botUserId) && (row.type !== discord_exports.MessageType.Reply || documents.some((text) => hasRawDiscordUserMention(text, ctx.botUserId)))) && !documents.some((text) => matchesActiveDiscordMentionPatterns(text, mentionRegexes))) continue;
112
+ }
113
+ }
114
+ let member = row.member;
115
+ if (params.mode !== "all" && roleAllowList.length > 0 && !member && guildId && !params.isSenderAllowed({
116
+ ...sender,
117
+ memberRoleIds: []
118
+ })) {
119
+ member = members.get(row.author.id);
120
+ if (!member) {
121
+ member = await getGuildMember(ctx.client.rest, guildId, row.author.id);
122
+ if (!isCurrent()) return [];
123
+ members.set(row.author.id, member);
124
+ }
125
+ }
126
+ const senderProvenance = createDiscordHistorySenderProvenance({
127
+ sender,
128
+ memberRoleIds: member?.roles ?? []
129
+ });
130
+ const mediaIds = resolveDiscordHistoryMediaIds(message);
131
+ const cached = cachedMedia.get(row.id);
132
+ const media = mediaIds.length > 0 && cached?.mediaIds?.length === mediaIds.length && cached.mediaIds.every((id, index) => id === mediaIds[index]) ? cached.media : void 0;
133
+ if (mediaIds.length > (media?.length ?? 0)) body = formatInboundMediaUnavailableText({
134
+ body,
135
+ notice: "[discord historical attachment unavailable]"
136
+ });
137
+ entries.push({
138
+ sender: sender.label,
139
+ body,
140
+ timestamp,
141
+ messageId: row.id,
142
+ senderProvenance,
143
+ ...media?.length ? { media } : {}
144
+ });
145
+ }
146
+ const nextBefore = window.at(-1)?.id;
147
+ if (window.length < limit || !nextBefore || nextBefore === before || /^\d+$/u.test(nextBefore) && /^\d+$/u.test(before) && BigInt(nextBefore) >= BigInt(before)) break;
148
+ before = nextBefore;
149
+ }
150
+ if (!isCurrent()) return [];
151
+ return filterDiscordHistoryEntriesForContext({
152
+ entries: entries.toReversed(),
153
+ mode: params.mode,
154
+ isSenderAllowed: params.isSenderAllowed
155
+ }).entries.slice(-ctx.historyLimit);
156
+ } catch (error) {
157
+ ctx.runtime.error(danger(`discord: recent history omitted for ${ctx.messageChannelId}: ${String(error)}`));
158
+ return [];
159
+ }
160
+ }
161
+ //#endregion
42
162
  //#region extensions/discord/src/monitor/message-handler.retry.ts
43
163
  const REPLY_SESSION_INIT_CONFLICT_MESSAGE_RE = /^reply session initialization conflicted for \S+$/u;
44
164
  const DISCORD_SESSION_CONFLICT_FAILURE_TEXT = "⚠️ Couldn't process this message because the session stayed busy. Please try again in a moment.";
@@ -74,6 +194,7 @@ function normalizeDiscordDmOwnerEntry(entry) {
74
194
  async function buildDiscordMessageProcessContext(params) {
75
195
  const { ctx, text, mediaList } = params;
76
196
  const { cfg, discordConfig, accountId, runtime, botUserId, mediaMaxBytes, discordRestFetch, abortSignal, guildHistories, historyLimit, replyToMode, message, author, sender, canonicalMessageId, data, client, channelInfo, channelName, messageChannelId, isGuildMessage, isDirectMessage, isGroupDm, baseText, preflightAudioTranscript, threadChannel, threadParentId, threadParentName, threadParentType, threadName, displayChannelSlug, guildInfo, guildSlug, memberRoleIds, channelConfig, baseSessionKey, boundSessionKey, route, commandAuthorized, hasControlCommand, resolveChannelIngress } = ctx;
197
+ if (abortSignal?.aborted || ctx.isPolicyCurrent?.() === false) return null;
77
198
  const fromLabel = isDirectMessage ? buildDirectLabel(author) : buildGuildLabel({
78
199
  guild: data.guild ?? void 0,
79
200
  channelName: channelName ?? messageChannelId,
@@ -119,10 +240,28 @@ async function buildDiscordMessageProcessContext(params) {
119
240
  });
120
241
  const storePath = resolveStorePath(cfg.session?.store, { agentId: route.agentId });
121
242
  const envelopeOptions = resolveEnvelopeFormatOptions(cfg);
122
- const previousTimestamp = readSessionUpdatedAt({
243
+ const routeSession = getSessionEntry({
244
+ agentId: route.agentId,
123
245
  storePath,
124
- sessionKey: route.sessionKey
246
+ sessionKey: route.sessionKey,
247
+ readConsistency: "latest"
125
248
  });
249
+ const previousTimestamp = routeSession?.updatedAt;
250
+ const shouldIncludeChannelHistory = !isDirectMessage && (ctx.inboundEventKind === "room_event" || !(isGuildMessage && channelConfig?.autoThread && !threadChannel));
251
+ const recoversHistory = shouldIncludeChannelHistory && isGuildMessage && historyLimit > 0;
252
+ const historySessionScope = {
253
+ agentId: route.agentId,
254
+ storePath,
255
+ sessionKey: boundSessionKey ?? route.sessionKey,
256
+ readConsistency: "latest"
257
+ };
258
+ const historySession = recoversHistory ? historySessionScope.sessionKey === route.sessionKey ? routeSession : getSessionEntry(historySessionScope) : void 0;
259
+ const isHistoryCurrent = () => {
260
+ if (abortSignal?.aborted || ctx.isPolicyCurrent?.() === false) return false;
261
+ if (!recoversHistory) return true;
262
+ const current = getSessionEntry(historySessionScope);
263
+ return current?.sessionId === historySession?.sessionId && current?.lifecycleRevision === historySession?.lifecycleRevision && current?.sessionStartedAt === historySession?.sessionStartedAt && current?.updatedAt === 0 === (historySession?.updatedAt === 0);
264
+ };
126
265
  const channelHistory = createChannelHistoryWindow({ historyMap: guildHistories });
127
266
  let visibleChannelHistory;
128
267
  const unavailableMediaCount = mediaList.filter((media) => !media.path).length;
@@ -142,20 +281,26 @@ async function buildDiscordMessageProcessContext(params) {
142
281
  previousTimestamp,
143
282
  envelope: envelopeOptions
144
283
  });
145
- const shouldIncludeChannelHistory = !isDirectMessage && (ctx.inboundEventKind === "room_event" || !(isGuildMessage && channelConfig?.autoThread && !threadChannel));
146
284
  if (shouldIncludeChannelHistory) {
147
285
  removeDiscordReplayHistoryEntry(guildHistories, messageChannelId, message.id);
148
286
  if (historyLimit > 0) {
149
- const filteredHistory = filterDiscordHistoryEntriesForContext({
150
- entries: guildHistories.get(messageChannelId) ?? [],
287
+ if (isGuildMessage) visibleChannelHistory = historySession?.updatedAt === 0 ? [] : await recoverDiscordChannelHistory({
288
+ ctx,
289
+ sessionStartedAt: historySession?.sessionStartedAt,
290
+ isCurrent: isHistoryCurrent,
151
291
  mode: contextVisibilityMode,
152
292
  isSenderAllowed: isSupplementalContextSenderAllowed
153
293
  });
154
- visibleChannelHistory = filteredHistory.entries;
155
- if (filteredHistory.omitted > 0) logVerbose(`discord: omit ${filteredHistory.omitted} pending history entries (mode=${contextVisibilityMode})`);
294
+ else visibleChannelHistory = filterDiscordHistoryEntriesForContext({
295
+ entries: guildHistories.get(messageChannelId) ?? [],
296
+ mode: contextVisibilityMode,
297
+ isSenderAllowed: isSupplementalContextSenderAllowed
298
+ }).entries.slice(-historyLimit);
299
+ if (!isHistoryCurrent()) return null;
156
300
  combinedBody = buildHistoryContextFromEntries({
157
301
  entries: visibleChannelHistory,
158
302
  currentMessage: combinedBody,
303
+ historyKind: isGuildMessage ? "recent" : "pending",
159
304
  formatEntry: (entry) => formatInboundEnvelope({
160
305
  channel: "Discord",
161
306
  from: fromLabel,
@@ -176,12 +321,6 @@ async function buildDiscordMessageProcessContext(params) {
176
321
  tag: replyContext.senderTag,
177
322
  memberRoleIds: replyContext.memberRoleIds
178
323
  }) : true;
179
- const replyVisible = evaluateSupplementalContextVisibility({
180
- mode: contextVisibilityMode,
181
- kind: "quote",
182
- senderAllowed: replySenderAllowed
183
- }).include;
184
- if (replyContext && !replyVisible && isGuildMessage) logVerbose(`discord: drop reply context (mode=${contextVisibilityMode})`);
185
324
  if (forumContextLine) combinedBody = `${combinedBody}\n${forumContextLine}`;
186
325
  let threadStarterBody;
187
326
  let threadLabel;
@@ -197,6 +336,7 @@ async function buildDiscordMessageProcessContext(params) {
197
336
  parentType: threadParentType,
198
337
  resolveTimestampMs
199
338
  });
339
+ if (!isHistoryCurrent()) return null;
200
340
  if (starter?.text) {
201
341
  if (evaluateSupplementalContextVisibility({
202
342
  mode: contextVisibilityMode,
@@ -235,6 +375,7 @@ async function buildDiscordMessageProcessContext(params) {
235
375
  parentSessionKey,
236
376
  useSuffix: false
237
377
  });
378
+ if (!isHistoryCurrent()) return null;
238
379
  const replyPlan = await resolveDiscordAutoThreadReplyPlan({
239
380
  client,
240
381
  message,
@@ -255,6 +396,7 @@ async function buildDiscordMessageProcessContext(params) {
255
396
  groupScope: route.groupScope,
256
397
  threadParentInheritanceEnabled
257
398
  });
399
+ if (!isHistoryCurrent()) return null;
258
400
  const deliverTarget = replyPlan.deliverTarget;
259
401
  const replyTarget = replyPlan.replyTarget;
260
402
  const replyReference = replyPlan.replyReference;
@@ -284,16 +426,19 @@ async function buildDiscordMessageProcessContext(params) {
284
426
  const channelIngress = await resolveChannelIngress({
285
427
  agentId: route.agentId,
286
428
  sessionKey: effectiveSessionKey,
429
+ nativeChannelId: messageChannelId,
287
430
  messageId: canonicalMessageId ?? message.id,
288
431
  inboundEventKind: ctx.inboundEventKind
289
432
  }, {
290
433
  parentId: conversationParentId,
291
434
  threadId: threadChannel?.id ?? autoThreadContext?.createdThreadId ?? void 0
292
435
  });
436
+ if (!isHistoryCurrent()) return null;
293
437
  const ctxPayload = await (ctx.buildContext ?? buildChannelInboundEventContext)({
294
438
  channelIngress,
295
439
  channel: "discord",
296
440
  resolveSupplementalMedia: true,
441
+ suppressSelfQuoteBody: false,
297
442
  contextVisibility: contextVisibilityMode,
298
443
  accountId: route.accountId,
299
444
  messageId: canonicalMessageId ?? message.id,
@@ -326,9 +471,7 @@ async function buildDiscordMessageProcessContext(params) {
326
471
  threadId: threadChannel?.id ?? autoThreadContext?.createdThreadId ?? void 0
327
472
  },
328
473
  route: {
329
- agentId: route.agentId,
330
- dmScope: route.dmScope,
331
- accountId: route.accountId,
474
+ ...route,
332
475
  routeSessionKey: route.sessionKey,
333
476
  dispatchSessionKey: effectiveSessionKey,
334
477
  parentSessionKey: autoThreadContext?.ParentSessionKey ?? threadKeys.parentSessionKey,
@@ -346,7 +489,10 @@ async function buildDiscordMessageProcessContext(params) {
346
489
  commandBody: baseText,
347
490
  inboundHistory
348
491
  },
349
- sessionTranscript: { historyLimit: shouldIncludeChannelHistory ? historyLimit : 0 },
492
+ sessionTranscript: {
493
+ historyLimit: shouldIncludeChannelHistory ? historyLimit : 0,
494
+ historyKind: isGuildMessage ? "recent" : "pending"
495
+ },
350
496
  access: {
351
497
  mentions: {
352
498
  canDetectMention: ctx.canDetectMention,
@@ -363,7 +509,7 @@ async function buildDiscordMessageProcessContext(params) {
363
509
  }),
364
510
  media: await toInboundMediaFactsWithMetadata(mediaList, { transcribed: (_media, index) => index === preflightAudioIndex }),
365
511
  supplemental: {
366
- quote: replyContext && replyVisible ? {
512
+ quote: replyContext ? {
367
513
  id: replyContext.id,
368
514
  body: replyContext.body,
369
515
  sender: replyContext.sender,
@@ -397,6 +543,7 @@ async function buildDiscordMessageProcessContext(params) {
397
543
  OwnerAllowFrom: ownerAllowFrom
398
544
  }
399
545
  });
546
+ if (!isHistoryCurrent()) return null;
400
547
  const persistedSessionKey = ctxPayload.SessionKey ?? route.sessionKey;
401
548
  if (ctx.inboundEventKind === "room_event" && shouldIncludeChannelHistory) {
402
549
  const historyText = [text, formatDiscordMediaText({
@@ -411,6 +558,7 @@ async function buildDiscordMessageProcessContext(params) {
411
558
  body: historyText,
412
559
  timestamp: resolveTimestampMs(message.timestamp),
413
560
  messageId: message.id,
561
+ mediaIds: resolveDiscordHistoryMediaIds(message),
414
562
  senderProvenance: createDiscordHistorySenderProvenance({
415
563
  sender,
416
564
  memberRoleIds
@@ -524,10 +672,7 @@ function createDiscordMessageProgressRuntime(params) {
524
672
  return await draftPreview.pushToolEvent(payload);
525
673
  },
526
674
  onItemEvent: async (payload) => {
527
- if (payload.kind === "preamble") {
528
- if (shouldYieldDraftCommentary()) return;
529
- return await draftPreview.pushPreambleItemEvent(payload);
530
- }
675
+ if (payload.kind === "preamble" && shouldYieldDraftCommentary()) return;
531
676
  return await draftPreview.pushItemEvent(payload);
532
677
  },
533
678
  onPlanUpdate: async (payload) => {
@@ -540,12 +685,6 @@ function createDiscordMessageProgressRuntime(params) {
540
685
  onApprovalEvent: async (payload) => {
541
686
  return await draftPreview.pushApprovalEvent(payload);
542
687
  },
543
- onCommandOutput: async (payload) => {
544
- return await draftPreview.pushCommandOutputEvent(payload);
545
- },
546
- onPatchSummary: async (payload) => {
547
- return await draftPreview.pushPatchEvent(payload);
548
- },
549
688
  onCompactionStart: async () => {
550
689
  if (!abortSignal?.aborted) await params.reactions.controller.setCompacting();
551
690
  return false;
@@ -776,14 +915,14 @@ function createDiscordDraftStream(params) {
776
915
  stopped: false,
777
916
  final: false
778
917
  };
779
- let streamMessageId;
918
+ let streamMessage;
780
919
  let lastSentText = "";
781
920
  let streamGeneration = 0;
782
921
  let activeCreateGeneration;
783
922
  let discardActiveCreate = false;
784
- let pendingCleanupMessages = [];
785
923
  const sendOrEditStreamMessage = async ({ text, complete }) => {
786
924
  const generation = streamGeneration;
925
+ const targetChannelId = channelId;
787
926
  if (streamState.stopped && !streamState.final) return false;
788
927
  const trimmed = text.trimEnd();
789
928
  if (!trimmed) return false;
@@ -793,17 +932,17 @@ function createDiscordDraftStream(params) {
793
932
  return false;
794
933
  }
795
934
  if (trimmed === lastSentText) return true;
796
- if (streamMessageId === void 0 && minInitialChars != null && !streamState.final && !complete) {
935
+ if (streamMessage === void 0 && minInitialChars != null && !streamState.final && !complete) {
797
936
  if (trimmed.length < minInitialChars) return false;
798
937
  }
799
- lastSentText = trimmed;
800
938
  try {
801
- if (streamMessageId !== void 0) {
802
- await editChannelMessage(rest, channelId, streamMessageId, { body: {
939
+ if (streamMessage !== void 0) {
940
+ await editChannelMessage(rest, streamMessage.channelId, streamMessage.messageId, { body: {
803
941
  content: trimmed,
804
942
  allowed_mentions: DISCORD_PREVIEW_ALLOWED_MENTIONS,
805
943
  ...flags ? { flags } : {}
806
944
  } });
945
+ if (generation === streamGeneration) lastSentText = trimmed;
807
946
  return true;
808
947
  }
809
948
  const replyToMessageId = resolveReplyToMessageId()?.trim();
@@ -812,7 +951,7 @@ function createDiscordDraftStream(params) {
812
951
  fail_if_not_exists: false
813
952
  } : void 0;
814
953
  activeCreateGeneration = generation;
815
- const sentMessageId = (await createChannelMessage(rest, channelId, { body: {
954
+ const sentMessageId = (await createChannelMessage(rest, targetChannelId, { body: {
816
955
  content: trimmed,
817
956
  allowed_mentions: DISCORD_PREVIEW_ALLOWED_MENTIONS,
818
957
  ...flags ? { flags } : {},
@@ -822,10 +961,9 @@ function createDiscordDraftStream(params) {
822
961
  activeCreateGeneration = void 0;
823
962
  discardActiveCreate = false;
824
963
  if (generation !== streamGeneration) {
825
- if (shouldDiscardStaleCreate && typeof sentMessageId === "string" && sentMessageId) await deleteCleanupMessage({
826
- channelId,
827
- messageId: sentMessageId,
828
- warnPrefix: "discord stale stream preview cleanup failed"
964
+ if (shouldDiscardStaleCreate && typeof sentMessageId === "string" && sentMessageId) await lifecycle.retire({
965
+ channelId: targetChannelId,
966
+ messageId: sentMessageId
829
967
  });
830
968
  return true;
831
969
  }
@@ -834,7 +972,11 @@ function createDiscordDraftStream(params) {
834
972
  params.warn?.("discord stream preview stopped (missing message id from send)");
835
973
  return false;
836
974
  }
837
- streamMessageId = sentMessageId;
975
+ streamMessage = {
976
+ channelId: targetChannelId,
977
+ messageId: sentMessageId
978
+ };
979
+ lastSentText = trimmed;
838
980
  return true;
839
981
  } catch (err) {
840
982
  if (activeCreateGeneration === generation) {
@@ -847,7 +989,12 @@ function createDiscordDraftStream(params) {
847
989
  return false;
848
990
  }
849
991
  };
850
- const { loop, update: updateDraft, stop, discardPending, seal } = createFinalizableDraftStreamControlsForState({
992
+ const clearMessageId = () => {
993
+ streamMessage = void 0;
994
+ lastSentText = "";
995
+ loop.resetThrottleWindow();
996
+ };
997
+ const lifecycle = createFinalizableDraftLifecycle({
851
998
  throttleMs,
852
999
  coalesceInFlight: true,
853
1000
  state: streamState,
@@ -856,8 +1003,15 @@ function createDiscordDraftStream(params) {
856
1003
  text: "",
857
1004
  complete: false
858
1005
  },
859
- isEmpty: (value) => !value.text
1006
+ isEmpty: (value) => !value.text,
1007
+ readMessageId: () => streamMessage,
1008
+ clearMessageId,
1009
+ isValidMessageId: (value) => value !== void 0,
1010
+ deleteMessage: (message) => deleteChannelMessage(rest, message.channelId, message.messageId),
1011
+ warn: params.warn,
1012
+ warnPrefix: "discord stream preview cleanup failed"
860
1013
  });
1014
+ const { loop, update: updateDraft, stop, discardPending, seal } = lifecycle;
861
1015
  const update = (text, options) => updateDraft({
862
1016
  text,
863
1017
  complete: options?.complete === true
@@ -867,35 +1021,21 @@ function createDiscordDraftStream(params) {
867
1021
  streamGeneration += 1;
868
1022
  streamState.stopped = false;
869
1023
  streamState.final = false;
870
- streamMessageId = void 0;
1024
+ streamMessage = void 0;
871
1025
  lastSentText = "";
872
1026
  loop.resetPending();
873
1027
  loop.resetThrottleWindow();
874
1028
  };
875
- const deleteCleanupMessage = async (message) => {
876
- try {
877
- await deleteChannelMessage(rest, message.channelId, message.messageId);
878
- } catch (err) {
879
- pendingCleanupMessages.push(message);
880
- params.warn?.(`${message.warnPrefix}: ${formatErrorMessage(err)}`);
881
- }
882
- };
883
- const cleanupPendingMessages = async () => {
884
- const pending = pendingCleanupMessages;
885
- pendingCleanupMessages = [];
886
- for (const message of pending) await deleteCleanupMessage(message);
887
- };
888
1029
  const retarget = async (nextChannelId) => {
889
1030
  const normalized = nextChannelId.trim();
890
1031
  if (!normalized || normalized === channelId) return;
891
1032
  await loop.waitForInFlight();
892
1033
  const pending = loop.takePending();
893
- const previousChannelId = channelId;
894
- const previousMessageId = streamMessageId;
1034
+ const previousMessage = streamMessage;
895
1035
  const previousText = pending.text || lastSentText;
896
1036
  streamGeneration += 1;
897
1037
  channelId = normalized;
898
- streamMessageId = void 0;
1038
+ streamMessage = void 0;
899
1039
  lastSentText = "";
900
1040
  streamState.stopped = false;
901
1041
  streamState.final = false;
@@ -904,49 +1044,40 @@ function createDiscordDraftStream(params) {
904
1044
  update(previousText, { complete: pending.text ? pending.complete : true });
905
1045
  await loop.flush();
906
1046
  }
907
- if (previousMessageId) {
908
- const stale = {
909
- channelId: previousChannelId,
910
- messageId: previousMessageId,
911
- warnPrefix: "discord stream preview retarget cleanup failed"
912
- };
913
- if (!streamMessageId) {
914
- pendingCleanupMessages.push(stale);
1047
+ if (previousMessage) {
1048
+ if (!streamMessage) {
1049
+ await lifecycle.retire(previousMessage, { defer: true });
915
1050
  throw new Error("discord stream preview retarget replacement failed");
916
1051
  }
917
- await deleteCleanupMessage(stale);
1052
+ await lifecycle.retire(previousMessage);
918
1053
  }
919
1054
  };
920
- const deleteCurrentMessage = async () => {
921
- loop.resetPending();
922
- await loop.waitForInFlight();
923
- const currentMessage = streamMessageId ? {
924
- channelId,
925
- messageId: streamMessageId,
926
- warnPrefix: "discord stream preview cleanup failed"
927
- } : void 0;
928
- streamMessageId = void 0;
929
- lastSentText = "";
930
- loop.resetThrottleWindow();
931
- if (!currentMessage) return;
932
- await deleteCleanupMessage(currentMessage);
933
- };
934
- const clear = async () => {
935
- await discardPending();
936
- await deleteCurrentMessage();
1055
+ const retireCurrentMessage = async (stopForClear) => {
1056
+ const generation = streamGeneration;
1057
+ await stopForClear();
1058
+ if (generation !== streamGeneration) return;
1059
+ const message = streamMessage;
1060
+ clearMessageId();
1061
+ if (message) await lifecycle.retire(message);
937
1062
  };
938
1063
  params.log?.(`discord stream preview ready (maxChars=${maxChars}, throttleMs=${throttleMs})`);
939
1064
  return {
940
1065
  update,
941
1066
  flush: loop.flush,
942
- messageId: () => streamMessageId,
943
- clear,
944
- deleteCurrentMessage,
1067
+ messageId: () => streamMessage?.messageId,
1068
+ lastDeliveredText: () => lastSentText,
1069
+ clear: () => retireCurrentMessage(discardPending),
1070
+ deleteCurrentMessage: () => retireCurrentMessage(async () => {
1071
+ loop.resetPending();
1072
+ await loop.waitForInFlight();
1073
+ }),
945
1074
  discardPending,
946
1075
  seal,
947
1076
  stop,
948
1077
  retarget,
949
- cleanupPendingMessages,
1078
+ cleanupPendingMessages: async () => {
1079
+ await lifecycle.cleanupPending();
1080
+ },
950
1081
  forceNewMessage
951
1082
  };
952
1083
  }
@@ -985,15 +1116,12 @@ function createDiscordDraftPreviewController(params) {
985
1116
  let lastPartialText = "";
986
1117
  let draftText = "";
987
1118
  let hasStreamedAssistantText = false;
988
- let finalizedViaPreviewMessage = false;
989
- let finalReplyError;
990
- let progressDraftStartedBeforeFinal = false;
991
- let progressDraftCollapsed = false;
992
1119
  let progressNarratorLifecycle;
993
1120
  const narrationProgressEnabled = Boolean(draftStream) && discordStreamMode === "progress" && resolveChannelStreamingProgressNarration(params.discordConfig);
994
1121
  const narrationHideCommandText = narrationProgressEnabled && resolveChannelStreamingPreviewCommandText(params.discordConfig) === "status";
995
1122
  const progressSeed = `${params.accountId}:${params.deliverChannelId}`;
996
1123
  const progressDraft = createChannelProgressDraftCompositor({
1124
+ preparedItems: true,
997
1125
  entry: params.discordConfig,
998
1126
  mode: discordStreamMode,
999
1127
  active: Boolean(draftStream),
@@ -1019,6 +1147,44 @@ function createDiscordDraftPreviewController(params) {
1019
1147
  isEmptyLine: isEmptyDiscordProgressLine,
1020
1148
  shouldStartNow: shouldStartDiscordProgressDraftNow
1021
1149
  });
1150
+ const freezeProgress = () => {
1151
+ progressDraft.markFinalReplyStarted();
1152
+ progressNarratorLifecycle?.stopTurn();
1153
+ };
1154
+ const flush = async () => {
1155
+ if (!draftStream) return;
1156
+ if (draftChunker?.hasBuffered()) {
1157
+ draftChunker.drain({
1158
+ force: true,
1159
+ emit: (chunk) => {
1160
+ draftText += chunk;
1161
+ }
1162
+ });
1163
+ draftChunker.reset();
1164
+ if (draftText) draftStream.update(draftText);
1165
+ }
1166
+ await draftStream.flush();
1167
+ };
1168
+ const lifecycle = createLivePreviewLifecycle({
1169
+ draft: draftStream ? {
1170
+ flush,
1171
+ id: draftStream.messageId,
1172
+ seal: draftStream.seal,
1173
+ discardPending: draftStream.discardPending,
1174
+ clear: draftStream.clear
1175
+ } : void 0,
1176
+ retainOnError: true,
1177
+ cleanupUndelivered: true,
1178
+ onFinalStarted: () => {
1179
+ freezeProgress();
1180
+ params.onFinalReplyStart?.();
1181
+ },
1182
+ onFinalDelivered: () => {
1183
+ progressDraft.markFinalReplyDelivered();
1184
+ params.onFinalReplyDelivered?.();
1185
+ },
1186
+ onCleanupFailure: (err) => params.log(`discord: draft cleanup failed: ${String(err)}`)
1187
+ });
1022
1188
  const resetProgressState = () => {
1023
1189
  lastPartialText = "";
1024
1190
  draftText = "";
@@ -1036,10 +1202,7 @@ function createDiscordDraftPreviewController(params) {
1036
1202
  const beganNewTurn = progressDraft.beginNewTurn(options);
1037
1203
  if (!beganNewTurn) progressDraft.beginAssistantMessage();
1038
1204
  if (beganNewTurn) {
1039
- progressDraftCollapsed = false;
1040
- progressDraftStartedBeforeFinal = false;
1041
- finalReplyError = void 0;
1042
- finalizedViaPreviewMessage = false;
1205
+ lifecycle.reset();
1043
1206
  progressNarratorLifecycle?.beginTurn();
1044
1207
  }
1045
1208
  if (discordStreamMode === "progress") {
@@ -1049,6 +1212,7 @@ function createDiscordDraftPreviewController(params) {
1049
1212
  };
1050
1213
  return {
1051
1214
  draftStream,
1215
+ lifecycle,
1052
1216
  narrationProgressEnabled,
1053
1217
  narrationHideCommandText,
1054
1218
  commentaryProgressEnabled: progressDraft.commentaryProgressEnabled,
@@ -1056,36 +1220,48 @@ function createDiscordDraftPreviewController(params) {
1056
1220
  get isProgressMode() {
1057
1221
  return discordStreamMode === "progress";
1058
1222
  },
1059
- get hasProgressDraftStarted() {
1060
- return progressDraft.hasStarted;
1061
- },
1062
1223
  get isProgressDraftVisible() {
1063
1224
  return progressDraft.isVisible;
1064
1225
  },
1065
- get hasProgressDraftToCollapse() {
1066
- return !progressDraftCollapsed && (progressDraft.hasStarted || progressDraftStartedBeforeFinal);
1067
- },
1068
- markProgressDraftCollapsed() {
1069
- progressDraftCollapsed = true;
1070
- progressDraftStartedBeforeFinal = false;
1071
- },
1072
- get finalizedViaPreviewMessage() {
1073
- return finalizedViaPreviewMessage;
1074
- },
1075
- setProgressNarratorLifecycle(lifecycle) {
1076
- progressNarratorLifecycle = lifecycle;
1077
- },
1078
- markFinalReplyStarted() {
1079
- progressDraftStartedBeforeFinal ||= progressDraft.hasStarted;
1080
- progressDraft.markFinalReplyStarted();
1081
- progressNarratorLifecycle?.stopTurn();
1082
- },
1083
- markFinalReplyDelivered(isError = false) {
1084
- finalReplyError = isError;
1085
- progressDraft.markFinalReplyDelivered();
1226
+ setProgressNarratorLifecycle(narratorLifecycle) {
1227
+ progressNarratorLifecycle = narratorLifecycle;
1086
1228
  },
1087
- markPreviewFinalized() {
1088
- finalizedViaPreviewMessage = true;
1229
+ freezeProgress,
1230
+ async adoptProgressContinuation(payload, info, target) {
1231
+ const adopt = info.adoptProgressContinuation;
1232
+ if (!draftStream || discordStreamMode !== "progress" || !adopt || info.kind !== "final" || payload.isError || payload.isCommentary || resolveSendableOutboundReplyParts(payload).hasMedia || payload.interactive !== void 0 || payload.presentation !== void 0 || payload.channelData !== void 0) return false;
1233
+ const snapshot = progressDraft.getSnapshot();
1234
+ if (!snapshot.statusHeadline && !snapshot.plan?.length && !snapshot.lines.length) return false;
1235
+ const assertCurrent = () => {
1236
+ params.abortSignal?.throwIfAborted();
1237
+ info.assertPlatformSendAuthorized?.();
1238
+ };
1239
+ return await withDiscordRequestAuthority(assertCurrent, async () => {
1240
+ assertCurrent();
1241
+ freezeProgress();
1242
+ const text = progressDraft.getText().trimEnd();
1243
+ draftStream.update(text, { complete: true });
1244
+ await draftStream.flush();
1245
+ assertCurrent();
1246
+ const messageId = draftStream.messageId();
1247
+ if (!messageId || !text || draftStream.lastDeliveredText() !== text) return false;
1248
+ if (!await adopt({
1249
+ channel: "discord",
1250
+ accountId: params.accountId,
1251
+ ...target,
1252
+ messageId,
1253
+ text,
1254
+ snapshot
1255
+ })) return false;
1256
+ if (draftStream.messageId() === messageId) {
1257
+ lifecycle.retainPreview();
1258
+ draftStream.forceNewMessage();
1259
+ progressDraft.markFinalReplyDelivered();
1260
+ resetProgressState();
1261
+ await draftStream.discardPending();
1262
+ }
1263
+ return true;
1264
+ });
1089
1265
  },
1090
1266
  async retarget(channelId) {
1091
1267
  await draftStream?.retarget(channelId);
@@ -1094,43 +1270,18 @@ function createDiscordDraftPreviewController(params) {
1094
1270
  if (!draftStream || discordStreamMode !== "progress") return false;
1095
1271
  const progressText = lastPartialText.trimEnd();
1096
1272
  if (!progressText) return false;
1273
+ lifecycle.retainPreview();
1097
1274
  draftStream.update(progressText);
1098
1275
  await draftStream.stop();
1099
- if (!draftStream.messageId()) return false;
1100
- finalizedViaPreviewMessage = true;
1101
- return true;
1276
+ return Boolean(draftStream.messageId());
1102
1277
  },
1103
1278
  disableBlockStreamingForDraft: draftStream ? true : void 0,
1104
1279
  pushToolEvent: progressDraft.pushToolEvent,
1105
- pushItemEvent: progressDraft.pushItemEvent,
1280
+ pushItemEvent: progressDraft.pushItemEvent.bind(progressDraft),
1106
1281
  pushApprovalEvent: progressDraft.pushApprovalEvent.bind(progressDraft),
1107
- pushCommandOutputEvent: progressDraft.pushCommandOutputEvent,
1108
- pushPatchEvent: progressDraft.pushPatchEvent,
1109
1282
  pushPlanProgress: progressDraft.pushPlanProgress.bind(progressDraft),
1110
1283
  pushReasoningProgress: progressDraft.pushReasoningProgress.bind(progressDraft),
1111
1284
  pushNarrationProgress: progressDraft.pushNarrationProgress.bind(progressDraft),
1112
- async pushPreambleItemEvent(payload) {
1113
- const headlineAccepted = await progressDraft.pushPreambleHeadline(payload.progressText, { itemId: payload.itemId });
1114
- if (!progressDraft.commentaryProgressEnabled) return headlineAccepted;
1115
- const commentaryAccepted = await progressDraft.pushCommentaryProgress(payload.progressText, { itemId: payload.itemId });
1116
- return headlineAccepted || commentaryAccepted;
1117
- },
1118
- resolvePreviewFinalText(text) {
1119
- if (typeof text !== "string") return;
1120
- const formatted = convertMarkdownTables(stripInlineDirectiveTagsForDelivery(text).text, params.tableMode);
1121
- const chunks = chunkDiscordTextWithMode(formatted, {
1122
- maxChars: draftMaxChars,
1123
- maxLines: params.maxLinesPerMessage,
1124
- chunkMode: params.chunkMode
1125
- });
1126
- if (!chunks.length && formatted) chunks.push(formatted);
1127
- if (chunks.length !== 1) return;
1128
- const trimmed = expectDefined(chunks.at(0), "single Discord preview chunk").trim();
1129
- if (!trimmed) return;
1130
- const currentPreviewText = discordStreamMode === "block" ? draftText : lastPartialText;
1131
- if (currentPreviewText && currentPreviewText.startsWith(trimmed) && trimmed.length < currentPreviewText.length) return;
1132
- return trimmed;
1133
- },
1134
1285
  updateFromPartial(text) {
1135
1286
  if (!draftStream || !text) return;
1136
1287
  const cleaned = stripInlineDirectiveTagsForDelivery(stripReasoningTagsFromText(text, {
@@ -1177,25 +1328,11 @@ function createDiscordDraftPreviewController(params) {
1177
1328
  handleQueuedFollowupAdmitted() {
1178
1329
  return beginNewProgressTurn({ force: true });
1179
1330
  },
1180
- async flush() {
1181
- if (!draftStream) return;
1182
- if (draftChunker?.hasBuffered()) {
1183
- draftChunker.drain({
1184
- force: true,
1185
- emit: (chunk) => {
1186
- draftText += chunk;
1187
- }
1188
- });
1189
- draftChunker.reset();
1190
- if (draftText) draftStream.update(draftText);
1191
- }
1192
- await draftStream.flush();
1193
- },
1194
- async cleanup({ finalDeliveryFailed = false } = {}) {
1331
+ flush,
1332
+ async cleanup({ failed = false } = {}) {
1195
1333
  try {
1196
1334
  progressDraft.cancel();
1197
- if (finalReplyError !== false) await draftStream?.discardPending();
1198
- if (!(discordStreamMode === "progress" && finalDeliveryFailed) && finalReplyError !== true && !finalizedViaPreviewMessage && draftStream?.messageId()) await draftStream.clear();
1335
+ await lifecycle.cleanup({ failed });
1199
1336
  await draftStream?.cleanupPendingMessages();
1200
1337
  } catch (err) {
1201
1338
  params.log(`discord: draft cleanup failed: ${String(err)}`);
@@ -1260,7 +1397,7 @@ function createDiscordBeforePayloadDelivery(params) {
1260
1397
  }
1261
1398
  if (payload.isReasoning || payload.isCommentary) return payload;
1262
1399
  if (params.draftPreview.draftStream && params.draftPreview.isProgressMode && info.kind === "block" && !resolveSendableOutboundReplyParts(payload).hasMedia && !payload.isError) return null;
1263
- if (info.kind === "final" && !params.isFallbackOnlyToolWarningFinal(payload)) params.draftPreview.markFinalReplyStarted();
1400
+ if (info.kind === "final" && !params.isFallbackOnlyToolWarningFinal(payload)) params.draftPreview.freezeProgress();
1264
1401
  return payload;
1265
1402
  };
1266
1403
  }
@@ -1348,20 +1485,19 @@ function createDiscordMessageReplyRuntime(params) {
1348
1485
  beginQueuedDeliveryCorrelation: beginDeliveryCorrelation,
1349
1486
  endDeliveryCorrelation,
1350
1487
  resolveCurrentTurnTranscriptFinalText,
1351
- deliverChannelId,
1352
1488
  draftPreview: createDiscordDraftPreviewController({
1353
1489
  groupThread: Boolean(ctxPayload.GroupThread),
1354
1490
  cfg,
1355
1491
  discordConfig,
1356
1492
  accountId,
1493
+ abortSignal: ctx.abortSignal,
1357
1494
  sourceRepliesAreToolOnly: params.sourceRepliesAreToolOnly,
1358
1495
  textLimit,
1359
1496
  deliveryRest: params.deliveryRest,
1360
1497
  deliverChannelId,
1361
1498
  replyReference,
1362
- tableMode,
1363
- maxLinesPerMessage,
1364
- chunkMode,
1499
+ onFinalReplyStart: params.onFinalReplyStart,
1500
+ onFinalReplyDelivered: params.onFinalReplyDelivered,
1365
1501
  log: logVerbose
1366
1502
  }),
1367
1503
  resolvedBlockStreamingEnabled: resolveChannelStreamingBlockEnabled(discordConfig)
@@ -1460,7 +1596,7 @@ async function processDiscordMessage(ctx, observer) {
1460
1596
  createdThreadId: replyPlan.createdThreadId,
1461
1597
  sessionKey: persistedSessionKey
1462
1598
  });
1463
- const { replyPipeline, onModelSelected, tableMode, maxLinesPerMessage, chunkMode, beginQueuedDeliveryCorrelation, endDeliveryCorrelation, resolveCurrentTurnTranscriptFinalText, deliverChannelId: initialDeliverChannelId, draftPreview, resolvedBlockStreamingEnabled } = createDiscordMessageReplyRuntime({
1599
+ const { replyPipeline, onModelSelected, tableMode, maxLinesPerMessage, chunkMode, beginQueuedDeliveryCorrelation, endDeliveryCorrelation, resolveCurrentTurnTranscriptFinalText, draftPreview, resolvedBlockStreamingEnabled } = createDiscordMessageReplyRuntime({
1464
1600
  ctx,
1465
1601
  processContext: {
1466
1602
  ...processContext,
@@ -1471,40 +1607,29 @@ async function processDiscordMessage(ctx, observer) {
1471
1607
  isRoomEvent,
1472
1608
  dispatchStartedAt,
1473
1609
  feedbackRest: reactions.feedbackRest,
1474
- deliveryRest: reactions.deliveryRest
1610
+ deliveryRest: reactions.deliveryRest,
1611
+ onFinalReplyStart: observer?.onFinalReplyStart,
1612
+ onFinalReplyDelivered: observer?.onFinalReplyDelivered
1475
1613
  });
1476
- let deliverChannelId = initialDeliverChannelId;
1614
+ let deliverThreadId = ctxPayload.MessageThreadId;
1477
1615
  activeThreadRoute.bindThreadAdoption(async (threadId) => {
1478
1616
  deliverTarget = `channel:${threadId}`;
1479
- deliverChannelId = threadId;
1617
+ deliverThreadId = threadId;
1480
1618
  await draftPreview.retarget(threadId);
1481
1619
  });
1482
- let finalReplyStartNotified = false;
1483
- const notifyFinalReplyStart = () => {
1484
- if (finalReplyStartNotified) return;
1485
- finalReplyStartNotified = true;
1486
- draftPreview.markFinalReplyStarted();
1487
- observer?.onFinalReplyStart?.();
1620
+ const { lifecycle } = draftPreview;
1621
+ const observeFinalDelivery = async () => {
1622
+ if (lifecycle.finalSucceeded) return;
1623
+ draftPreview.freezeProgress();
1624
+ const retainedProgress = activeThreadRoute.threadReplyDelivered && !lifecycle.previewFinalized ? draftPreview.finalizeProgressDraft().catch((error) => {
1625
+ logVerbose(`discord: failed to finalize adopted thread progress (${String(error)})`);
1626
+ }) : void 0;
1627
+ await lifecycle.observeDelivery({ visibleReplySent: true });
1628
+ await retainedProgress;
1488
1629
  };
1489
- let userFacingFinalDelivered = false;
1490
- let userFacingFinalDeliveryFailed = false;
1491
1630
  let pendingToolWarningFinal;
1492
- const markFinalReplyDelivered = (isError = false) => {
1493
- draftPreview.markFinalReplyDelivered(isError);
1494
- if (!isError) {
1495
- userFacingFinalDelivered = true;
1496
- userFacingFinalDeliveryFailed = false;
1497
- pendingToolWarningFinal = void 0;
1498
- observer?.onFinalReplyDelivered?.();
1499
- }
1500
- };
1501
- let clearProgressDraftAfterFinalDelivery = false;
1502
1631
  const resetDeliveryState = () => {
1503
- finalReplyStartNotified = false;
1504
- userFacingFinalDelivered = false;
1505
- userFacingFinalDeliveryFailed = false;
1506
1632
  pendingToolWarningFinal = void 0;
1507
- clearProgressDraftAfterFinalDelivery = false;
1508
1633
  };
1509
1634
  const progress = createDiscordMessageProgressRuntime({
1510
1635
  ctx,
@@ -1589,14 +1714,14 @@ async function processDiscordMessage(ctx, observer) {
1589
1714
  return result;
1590
1715
  }
1591
1716
  if (isFinal && !options?.allowFallbackOnlyToolWarning && isFallbackOnlyToolWarningFinal(payload)) {
1592
- if (!userFacingFinalDelivered && (!finalReplyStartNotified || userFacingFinalDeliveryFailed)) pendingToolWarningFinal = {
1717
+ if (!lifecycle.finalSucceeded && !lifecycle.previewFinalized && (!lifecycle.finalStarted || lifecycle.finalFailed)) pendingToolWarningFinal = {
1593
1718
  payload,
1594
1719
  info,
1595
1720
  deliverySession
1596
1721
  };
1597
1722
  return { visibleReplySent: false };
1598
1723
  }
1599
- if (isFinal) draftPreview.markFinalReplyStarted();
1724
+ if (isFinal) draftPreview.freezeProgress();
1600
1725
  const finalText = isFinal && !ctxPayload.GroupThread && typeof payload.text === "string" ? await resolveTranscriptBackedChannelFinalText({
1601
1726
  payload,
1602
1727
  finalText: payload.text,
@@ -1616,104 +1741,56 @@ async function processDiscordMessage(ctx, observer) {
1616
1741
  }));
1617
1742
  return { visibleReplySent: false };
1618
1743
  }
1744
+ if (await draftPreview.adoptProgressContinuation(deliverablePayload, info, {
1745
+ to: isDirectMessage ? ctxPayload.OriginatingTo ?? ctxPayload.To ?? deliverTarget : deliverTarget,
1746
+ threadId: deliverThreadId
1747
+ })) {
1748
+ replyReference.markSent();
1749
+ return { visibleReplySent: true };
1750
+ }
1619
1751
  if (isFinal && !replyLifecycleStarted && !isRoomEvent && configuredTypingMode !== "never") await onDiscordReplyStart();
1620
1752
  const draftStream = draftPreview.draftStream;
1621
1753
  if (draftStream && draftPreview.isProgressMode && info.kind === "block" && !deliverablePayload.isCommentary && !options?.allowProgressBlock) {
1622
1754
  if (!resolveSendableOutboundReplyParts(deliverablePayload).hasMedia && !deliverablePayload.isError) return { visibleReplySent: false };
1623
1755
  }
1624
- if (draftStream && isFinal && draftPreview.isProgressMode && !deliverablePayload.isError && draftPreview.hasProgressDraftToCollapse && draftStream) {
1625
- await draftPreview.flush();
1626
- clearProgressDraftAfterFinalDelivery = true;
1627
- }
1628
- if (draftStream && isFinal && !draftPreview.isProgressMode && !deliverablePayload.isError) {
1629
- const ttsSupplement = getReplyPayloadTtsSupplement(deliverablePayload);
1630
- if ((await deliverWithFinalizableLivePreviewAdapter({
1631
- kind: info.kind,
1632
- payload: deliverablePayload,
1633
- adapter: defineFinalizableLivePreviewAdapter({
1634
- draft: {
1635
- flush: () => draftPreview.flush(),
1636
- clear: () => draftStream.clear(),
1637
- discardPending: () => draftStream.discardPending(),
1638
- seal: () => draftStream.seal(),
1639
- id: draftStream.messageId
1640
- },
1641
- buildFinalEdit: () => {},
1642
- editFinal: async (previewMessageId, edit) => {
1643
- if (abortSignal?.aborted) throw new Error("process aborted");
1644
- notifyFinalReplyStart();
1645
- await editMessageDiscord(deliverChannelId, previewMessageId, edit, {
1646
- cfg,
1647
- accountId,
1648
- rest: reactions.deliveryRest
1649
- });
1650
- },
1651
- onPreviewFinalized: () => {
1652
- markFinalReplyDelivered();
1653
- draftPreview.markPreviewFinalized();
1654
- replyReference.markSent();
1655
- },
1656
- logPreviewEditFailure: (err) => {
1657
- logVerbose(`discord: preview final edit failed; falling back to standard send (${String(err)})`);
1658
- }
1659
- }),
1660
- deliverNormally: async () => {
1661
- if (abortSignal?.aborted) return false;
1662
- const fallbackPayload = ttsSupplement && ttsSupplement.visibleTextAlreadyDelivered !== true && !deliverablePayload.text?.trim() ? {
1663
- ...deliverablePayload,
1664
- text: ttsSupplement.spokenText
1665
- } : deliverablePayload;
1666
- const allowedMentions = discordTextHasBroadcastMention(fallbackPayload.text ?? "") ? TARGETED_ONLY_ALLOWED_MENTIONS : void 0;
1667
- const replyToId = replyReference.use();
1668
- notifyFinalReplyStart();
1669
- return (await deliverDiscordReply({
1670
- ...deliveryOptions,
1671
- replies: [fallbackPayload],
1756
+ let deliveryResult = { visibleReplySent: false };
1757
+ await lifecycle.deliver({
1758
+ kind: info.kind,
1759
+ payload: deliverablePayload,
1760
+ isError: deliverablePayload.isError === true,
1761
+ deliverNormally: async () => {
1762
+ if (abortSignal?.aborted) {
1763
+ logVerbose(formatDiscordReplySkip({
1764
+ kind: info.kind,
1765
+ reason: "aborted before delivery",
1672
1766
  target: deliverTarget,
1673
- replyToId,
1674
- allowedMentions,
1675
- kind: info.kind
1676
- })).visibleReplySent;
1677
- },
1678
- onNormalDelivered: () => {
1679
- markFinalReplyDelivered();
1680
- replyReference.markSent();
1767
+ sessionKey: ctxPayload.SessionKey
1768
+ }));
1769
+ return deliveryResult;
1681
1770
  }
1682
- })).kind !== "normal-skipped") return { visibleReplySent: true };
1683
- }
1684
- if (abortSignal?.aborted) {
1685
- logVerbose(formatDiscordReplySkip({
1686
- kind: info.kind,
1687
- reason: "aborted before delivery",
1688
- target: deliverTarget,
1689
- sessionKey: ctxPayload.SessionKey
1690
- }));
1691
- return { visibleReplySent: false };
1692
- }
1693
- const replyToId = replyReference.use();
1694
- if (isFinal) notifyFinalReplyStart();
1695
- const result = await deliverDiscordReply({
1696
- ...deliveryOptions,
1697
- replies: [deliverablePayload],
1698
- target: deliverTarget,
1699
- replyToId,
1700
- kind: info.kind
1771
+ const freshPreviewFinal = draftStream && isFinal && !draftPreview.isProgressMode && !deliverablePayload.isError;
1772
+ const ttsSupplement = freshPreviewFinal ? getReplyPayloadTtsSupplement(deliverablePayload) : void 0;
1773
+ const finalPayload = ttsSupplement && ttsSupplement.visibleTextAlreadyDelivered !== true && !deliverablePayload.text?.trim() ? {
1774
+ ...deliverablePayload,
1775
+ text: ttsSupplement.spokenText
1776
+ } : deliverablePayload;
1777
+ const allowedMentions = freshPreviewFinal && discordTextHasBroadcastMention(finalPayload.text ?? "") ? TARGETED_ONLY_ALLOWED_MENTIONS : void 0;
1778
+ deliveryResult = await deliverDiscordReply({
1779
+ ...deliveryOptions,
1780
+ replies: [finalPayload],
1781
+ target: deliverTarget,
1782
+ replyToId: replyReference.use(),
1783
+ allowedMentions,
1784
+ kind: info.kind
1785
+ });
1786
+ return deliveryResult;
1787
+ },
1788
+ onNormalDelivered: () => replyReference.markSent()
1701
1789
  });
1702
- if (!result.visibleReplySent) return result;
1703
- replyReference.markSent();
1704
- if (isFinal) {
1705
- markFinalReplyDelivered(deliverablePayload.isError === true);
1706
- if (deliverablePayload.isError !== true && clearProgressDraftAfterFinalDelivery) {
1707
- clearProgressDraftAfterFinalDelivery = false;
1708
- draftPreview.markProgressDraftCollapsed();
1709
- await draftStream?.discardPending();
1710
- await draftStream?.clear();
1711
- }
1712
- }
1713
- return result;
1790
+ return deliveryResult;
1714
1791
  };
1715
1792
  const onDiscordDeliveryError = (err, info) => {
1716
- if (info.kind === "final" && finalReplyStartNotified && !userFacingFinalDelivered) userFacingFinalDeliveryFailed = true;
1793
+ if (info.kind === "final") lifecycle.observeFailure();
1717
1794
  runtime.error(danger(formatDiscordReplyDeliveryFailure({
1718
1795
  kind: info.kind,
1719
1796
  err,
@@ -1725,7 +1802,7 @@ async function processDiscordMessage(ctx, observer) {
1725
1802
  let dispatchError = false;
1726
1803
  let dispatchAborted = false;
1727
1804
  const deliverPendingToolWarningFinalIfNeeded = async () => {
1728
- if (!pendingToolWarningFinal || userFacingFinalDelivered || abortSignal?.aborted) return;
1805
+ if (!pendingToolWarningFinal || lifecycle.finalSucceeded || lifecycle.previewFinalized || abortSignal?.aborted) return;
1729
1806
  const pending = pendingToolWarningFinal;
1730
1807
  pendingToolWarningFinal = void 0;
1731
1808
  try {
@@ -1795,6 +1872,7 @@ async function processDiscordMessage(ctx, observer) {
1795
1872
  disableBlockStreaming: sourceRepliesAreToolOnly ? true : draftPreview.disableBlockStreamingForDraft ?? (typeof resolvedBlockStreamingEnabled === "boolean" ? !resolvedBlockStreamingEnabled : void 0),
1796
1873
  onPartialReply: draftPreview.draftStream && !draftPreview.isProgressMode ? (payload) => draftPreview.updateFromPartial(payload.text) : void 0,
1797
1874
  ...progress.replyOptions,
1875
+ onObservedReplyDelivery: observeFinalDelivery,
1798
1876
  onModelSelected
1799
1877
  }
1800
1878
  });
@@ -1804,15 +1882,7 @@ async function processDiscordMessage(ctx, observer) {
1804
1882
  dispatchAborted = true;
1805
1883
  return;
1806
1884
  }
1807
- if (activeThreadRoute.threadReplyDelivered && !userFacingFinalDelivered) {
1808
- draftPreview.markFinalReplyStarted();
1809
- if (draftPreview.hasProgressDraftToCollapse) try {
1810
- await draftPreview.finalizeProgressDraft();
1811
- } catch (error) {
1812
- logVerbose(`discord: failed to finalize adopted thread progress (${String(error)})`);
1813
- }
1814
- markFinalReplyDelivered();
1815
- }
1885
+ if (activeThreadRoute.threadReplyDelivered) await observeFinalDelivery();
1816
1886
  } catch (err) {
1817
1887
  if (abortSignal?.aborted) {
1818
1888
  dispatchAborted = true;
@@ -1832,10 +1902,10 @@ async function processDiscordMessage(ctx, observer) {
1832
1902
  } finally {
1833
1903
  activeThreadRoute.end();
1834
1904
  endDeliveryCorrelation();
1835
- await draftPreview.cleanup({ finalDeliveryFailed: userFacingFinalDeliveryFailed });
1836
1905
  dispatchError ||= readAgentRunTerminalOutcome(dispatchResult) === "failed";
1837
1906
  const finalReceipt = dispatchResult?.settledReceipt?.counts.final;
1838
1907
  const finalDeliveryFailed = (finalReceipt?.failedBeforeSend ?? 0) + (finalReceipt?.failedAfterSend ?? 0) > 0;
1908
+ await draftPreview.cleanup({ failed: finalDeliveryFailed || dispatchError });
1839
1909
  await reactions.finish({
1840
1910
  dispatchAborted,
1841
1911
  dispatchError,