@openclaw/discord 2026.8.1-beta.2 → 2026.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/dist/{account-inspect-CqzrH14k.js → account-inspect-BDfKyuvL.js} +1 -1
  2. package/dist/account-inspect-api.js +1 -1
  3. package/dist/{accounts-DJTf1LtA.js → accounts-CfQ-vSyf.js} +34 -1
  4. package/dist/action-runtime-api.js +1 -1
  5. package/dist/{activities-api-DS7vpdtp.js → activities-api-Dl-TOyOX.js} +134 -175
  6. package/dist/activities-api.js +1 -1
  7. package/dist/api.js +17 -17
  8. package/dist/{approval-handler.runtime-B6yEV79q.js → approval-handler.runtime-q4Zqo44V.js} +29 -106
  9. package/dist/{audit-Db-rlntl.js → audit-DgrvFPGt.js} +5 -5
  10. package/dist/{channel-CnnbJSt5.js → channel-BsQUalPw.js} +72 -50
  11. package/dist/{channel-actions.runtime-CLKrx6tr.js → channel-actions.runtime-UMGGIfSy.js} +31 -45
  12. package/dist/channel-config-api.js +1 -1
  13. package/dist/channel-plugin-api.js +1 -1
  14. package/dist/channel-type-CVLZyGYS.js +7 -0
  15. package/dist/{channel.setup-BFAO-V1P.js → channel.setup-rPs4dsk_.js} +5 -5
  16. package/dist/{outbound-session-route-DxLkrgKQ.js → components-Dk35LTf_.js} +34 -76
  17. package/dist/{config-schema-BgEskFAs.js → config-schema-CxPqtVI1.js} +46 -14
  18. package/dist/conversation-binding-route-BjvX4e35.js +43 -0
  19. package/dist/{directory-config-BVX7Cflc.js → directory-config-B7DY_pNG.js} +2 -2
  20. package/dist/directory-contract-api.js +1 -1
  21. package/dist/{directory-live-DGIGxpIo.js → directory-live-DuAXKkan.js} +3 -3
  22. package/dist/doctor-contract-api.js +2 -2
  23. package/dist/{doctor-BzC-TLnB.js → doctor-zQTtn6hm.js} +33 -12
  24. package/dist/{handle-action.guild-admin-BNVTUISF.js → handle-action.guild-admin-DZewwLr1.js} +9 -5
  25. package/dist/index.js +3 -4
  26. package/dist/ingress-identity-CzZKcPK8.js +60 -0
  27. package/dist/{message-handler-Unk_f10F.js → message-handler-C5By3yl5.js} +82 -6
  28. package/dist/{message-handler.preflight-C9zecxR9.js → message-handler.preflight-DrszWuHw.js} +44 -118
  29. package/dist/{message-handler.process-DWQ6UHHu.js → message-handler.process-DyFPDLCt.js} +205 -235
  30. package/dist/{message-utils-BSCPVgy0.js → message-text-Ca8M6TU8.js} +43 -42
  31. package/dist/{outbound-adapter-D9nRivT1.js → outbound-adapter-Dif8p481.js} +118 -91
  32. package/dist/{outbound-components-cTzwBJ60.js → outbound-components-wjv7yyAj.js} +5 -4
  33. package/dist/outbound-session-route-Bn4uXaoE.js +665 -0
  34. package/dist/owner-access-UlYi9t0M.js +107 -0
  35. package/dist/{pluralkit-DdiJ9KMK.js → pluralkit-9EuEN4UD.js} +1 -1
  36. package/dist/{probe-Bl-k5_Ku.js → probe-n6ZXxK8V.js} +1 -1
  37. package/dist/{probe.runtime-M9fREKvp.js → probe.runtime-CN4bdPfZ.js} +1 -1
  38. package/dist/{provider-DvSNb-2v.js → provider-LXiOcJrH.js} +887 -1350
  39. package/dist/{provider-session.runtime-BUEqD0rA.js → provider-session.runtime-DSNe7r2W.js} +3 -3
  40. package/dist/provider.runtime-DiInaDXO.js +2 -0
  41. package/dist/{realtime-session.runtime-C7wen-aF.js → realtime-session.runtime-DOViOIjY.js} +129 -134
  42. package/dist/{resolve-allowlist-common-D04lpEzb.js → resolve-allowlist-common-BLoYaxEG.js} +2 -2
  43. package/dist/{resolve-channels-Dm7jEeg6.js → resolve-channels-D2yl_2CN.js} +5 -4
  44. package/dist/{resolve-users-bktrCUUU.js → resolve-users-BLxfqajZ.js} +2 -2
  45. package/dist/{retry-CLpJcXXf.js → retry-DqHCW6PV.js} +187 -19
  46. package/dist/{runtime-DGsSyDTS.js → runtime-BYamRVPN.js} +89 -86
  47. package/dist/{runtime-DoHjckYm.js → runtime-DrxVBTtp.js} +9 -10
  48. package/dist/runtime-api.js +19 -25
  49. package/dist/{sdk-runtime-QpY8PvCh.js → sdk-runtime-6tfgYDjY.js} +57 -89
  50. package/dist/{secret-config-contract-B1akrlyY.js → secret-config-contract-B3i6WxV5.js} +53 -25
  51. package/dist/secret-contract-api.js +1 -1
  52. package/dist/{security-audit-jGnJF__m.js → security-audit-DKfK-iPY.js} +11 -12
  53. package/dist/security-audit-contract-api.js +1 -1
  54. package/dist/{security-audit.runtime-CPN0xFYz.js → security-audit.runtime-sKZyKgpx.js} +1 -1
  55. package/dist/{send-B7Q6Oc1I.js → send-UJHiAm4C.js} +82 -19
  56. package/dist/{send.components-iJyFXw9L.js → send.components-CrBJpGT_.js} +22 -39
  57. package/dist/{send.outbound-DS5tHwfR.js → send.outbound-D7JYmfm9.js} +77 -78
  58. package/dist/{send.shared-BmwFjW6T.js → send.shared-DZJ1akvQ.js} +136 -121
  59. package/dist/{sender-identity-QtalnCYv.js → sender-identity-BJtfyise.js} +1 -1
  60. package/dist/setup-plugin-api.js +1 -1
  61. package/dist/{shared-BIXIX6uJ.js → shared-Dii2HTn7.js} +41 -14
  62. package/dist/subagent-hooks-JyGrtQIl.js +49 -0
  63. package/dist/subagent-hooks-api.js +1 -6
  64. package/dist/{system-events-CTqDf6_s.js → system-events-BEmcj4xJ.js} +2 -2
  65. package/dist/{target-resolver-maOKSqO0.js → target-resolver-V-IYae6s.js} +3 -3
  66. package/dist/targets-CQp53cGj.js +3 -0
  67. package/dist/{thread-bindings-3L8p82GV.js → thread-bindings-DvYP79fy.js} +28 -26
  68. package/dist/{thread-bindings.manager-DCIH6WDC.js → thread-bindings.manager-Clm77DBM.js} +11 -14
  69. package/dist/{thread-bindings.session-updates-bJ2KQFY6.js → thread-bindings.session-updates-CJb-BpPL.js} +1 -1
  70. package/dist/{thread-bindings.state-aeV8HNgV.js → thread-bindings.state-D8OMdk4O.js} +1 -1
  71. package/dist/threading-CXuu_7o9.js +478 -0
  72. package/dist/transcripts-source-BLEMH_yi.js +270 -0
  73. package/dist/transcripts-source-api.js +7 -2
  74. package/dist/{typing-CS-nNVoe.js → typing-B9snnwMe.js} +1 -1
  75. package/dist/{voice-runtime-BEdqNcST.js → voice-runtime-DY6_o73o.js} +250 -150
  76. package/node_modules/@discord/embedded-app-sdk/node_modules/uuid/README.md +19 -19
  77. package/node_modules/@discord/embedded-app-sdk/node_modules/uuid/dist/v1.js +7 -3
  78. package/node_modules/@discord/embedded-app-sdk/node_modules/uuid/dist/v7.js +5 -2
  79. package/node_modules/@discord/embedded-app-sdk/node_modules/uuid/dist-node/v1.js +7 -3
  80. package/node_modules/@discord/embedded-app-sdk/node_modules/uuid/dist-node/v7.js +5 -2
  81. package/node_modules/@discord/embedded-app-sdk/node_modules/uuid/package.json +4 -3
  82. package/node_modules/@discordjs/voice/node_modules/@types/node/README.md +1 -1
  83. package/node_modules/@discordjs/voice/node_modules/@types/node/child_process.d.ts +0 -1
  84. package/node_modules/@discordjs/voice/node_modules/@types/node/crypto.d.ts +6 -6
  85. package/node_modules/@discordjs/voice/node_modules/@types/node/dns/promises.d.ts +5 -5
  86. package/node_modules/@discordjs/voice/node_modules/@types/node/dns.d.ts +5 -5
  87. package/node_modules/@discordjs/voice/node_modules/@types/node/fs.d.ts +7 -0
  88. package/node_modules/@discordjs/voice/node_modules/@types/node/http.d.ts +30 -0
  89. package/node_modules/@discordjs/voice/node_modules/@types/node/http2.d.ts +4 -4
  90. package/node_modules/@discordjs/voice/node_modules/@types/node/os.d.ts +30 -30
  91. package/node_modules/@discordjs/voice/node_modules/@types/node/package.json +2 -2
  92. package/node_modules/@discordjs/voice/node_modules/@types/node/perf_hooks.d.ts +2 -1
  93. package/node_modules/@discordjs/voice/node_modules/@types/node/process.d.ts +42 -43
  94. package/node_modules/@discordjs/voice/node_modules/@types/node/quic.d.ts +1049 -84
  95. package/node_modules/@discordjs/voice/node_modules/@types/node/sqlite.d.ts +14 -1
  96. package/node_modules/@discordjs/voice/node_modules/@types/node/stream/iter.d.ts +3 -2
  97. package/node_modules/@discordjs/voice/node_modules/@types/node/test.d.ts +61 -0
  98. package/node_modules/@discordjs/voice/node_modules/@types/node/v8.d.ts +20 -23
  99. package/node_modules/discord-api-types/CHANGELOG.md +8 -0
  100. package/node_modules/discord-api-types/package.json +4 -4
  101. package/node_modules/discord-api-types/payloads/v10/channel.d.ts +2 -2
  102. package/node_modules/discord-api-types/payloads/v10/channel.d.ts.map +1 -1
  103. package/node_modules/discord-api-types/payloads/v10/gateway.d.ts +4 -0
  104. package/node_modules/discord-api-types/payloads/v10/gateway.d.ts.map +1 -1
  105. package/node_modules/discord-api-types/payloads/v10/gateway.js.map +1 -1
  106. package/node_modules/discord-api-types/payloads/v10/message.d.ts +4 -1
  107. package/node_modules/discord-api-types/payloads/v10/message.d.ts.map +1 -1
  108. package/node_modules/discord-api-types/payloads/v10/message.js +1 -0
  109. package/node_modules/discord-api-types/payloads/v10/message.js.map +1 -1
  110. package/node_modules/discord-api-types/payloads/v9/channel.d.ts +2 -2
  111. package/node_modules/discord-api-types/payloads/v9/channel.d.ts.map +1 -1
  112. package/node_modules/discord-api-types/payloads/v9/gateway.d.ts +4 -0
  113. package/node_modules/discord-api-types/payloads/v9/gateway.d.ts.map +1 -1
  114. package/node_modules/discord-api-types/payloads/v9/gateway.js.map +1 -1
  115. package/node_modules/discord-api-types/payloads/v9/message.d.ts +4 -1
  116. package/node_modules/discord-api-types/payloads/v9/message.d.ts.map +1 -1
  117. package/node_modules/discord-api-types/payloads/v9/message.js +1 -0
  118. package/node_modules/discord-api-types/payloads/v9/message.js.map +1 -1
  119. package/node_modules/typebox/build/format/date.d.mts +1 -1
  120. package/node_modules/typebox/build/format/date.mjs +1 -1
  121. package/node_modules/typebox/build/format/date_time.d.mts +2 -2
  122. package/node_modules/typebox/build/format/date_time.mjs +3 -3
  123. package/node_modules/typebox/build/format/email.d.mts +1 -1
  124. package/node_modules/typebox/build/format/email.mjs +2 -2
  125. package/node_modules/typebox/build/format/hostname.mjs +2 -10
  126. package/node_modules/typebox/build/format/idn_email.d.mts +1 -1
  127. package/node_modules/typebox/build/format/idn_email.mjs +3 -3
  128. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -0
  129. package/node_modules/typebox/build/format/idn_hostname.mjs +3 -11
  130. package/node_modules/typebox/build/format/idna/format/bidi.d.mts +3 -0
  131. package/node_modules/typebox/build/format/idna/format/bidi.mjs +98 -0
  132. package/node_modules/typebox/build/format/idna/format/puny.d.mts +3 -0
  133. package/node_modules/typebox/build/format/idna/format/puny.mjs +144 -0
  134. package/node_modules/typebox/build/format/idna/hostname.d.mts +1 -0
  135. package/node_modules/typebox/build/format/idna/hostname.mjs +16 -0
  136. package/node_modules/typebox/build/format/idna/idn-hostname.d.mts +1 -0
  137. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +23 -0
  138. package/node_modules/typebox/build/format/idna/index.d.mts +2 -0
  139. package/node_modules/typebox/build/format/idna/index.mjs +2 -0
  140. package/node_modules/typebox/build/format/idna/label/ascii.d.mts +1 -0
  141. package/node_modules/typebox/build/format/idna/label/ascii.mjs +9 -0
  142. package/node_modules/typebox/build/format/idna/label/puny.d.mts +1 -0
  143. package/node_modules/typebox/build/format/idna/label/puny.mjs +28 -0
  144. package/node_modules/typebox/build/format/idna/label/unicode.d.mts +1 -0
  145. package/node_modules/typebox/build/format/idna/label/unicode.mjs +106 -0
  146. package/node_modules/typebox/build/format/idna/pattern/pattern.d.mts +30 -0
  147. package/node_modules/typebox/build/format/idna/pattern/pattern.mjs +70 -0
  148. package/node_modules/typebox/build/format/ipv4.d.mts +0 -1
  149. package/node_modules/typebox/build/format/ipv4.mjs +2 -32
  150. package/node_modules/typebox/build/format/ipv6.mjs +2 -61
  151. package/node_modules/typebox/build/format/iri.d.mts +2 -2
  152. package/node_modules/typebox/build/format/iri.mjs +26 -8
  153. package/node_modules/typebox/build/format/iri_reference.d.mts +2 -2
  154. package/node_modules/typebox/build/format/iri_reference.mjs +9 -55
  155. package/node_modules/typebox/build/format/regex.d.mts +1 -2
  156. package/node_modules/typebox/build/format/regex.mjs +2 -6
  157. package/node_modules/typebox/build/format/time.d.mts +2 -2
  158. package/node_modules/typebox/build/format/time.mjs +19 -14
  159. package/node_modules/typebox/build/format/uri.d.mts +1 -1
  160. package/node_modules/typebox/build/format/uri.mjs +3 -130
  161. package/node_modules/typebox/build/format/uri_reference.d.mts +1 -1
  162. package/node_modules/typebox/build/format/uri_reference.mjs +2 -3
  163. package/node_modules/typebox/build/format/uri_template.d.mts +2 -3
  164. package/node_modules/typebox/build/format/uri_template.mjs +4 -5
  165. package/node_modules/typebox/build/format/url.d.mts +3 -3
  166. package/node_modules/typebox/build/format/url.mjs +4 -5
  167. package/node_modules/typebox/build/guard/emit.d.mts +3 -1
  168. package/node_modules/typebox/build/guard/emit.mjs +12 -5
  169. package/node_modules/typebox/build/guard/guard.d.mts +8 -2
  170. package/node_modules/typebox/build/guard/guard.mjs +25 -4
  171. package/node_modules/typebox/build/guard/string.d.mts +2 -2
  172. package/node_modules/typebox/build/guard/string.mjs +7 -7
  173. package/node_modules/typebox/build/schema/engine/_context.mjs +3 -3
  174. package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -2
  175. package/node_modules/typebox/build/schema/engine/_regexp.d.mts +2 -0
  176. package/node_modules/typebox/build/schema/engine/_regexp.mjs +4 -0
  177. package/node_modules/typebox/build/schema/engine/_stack.mjs +12 -13
  178. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +17 -7
  179. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +4 -3
  180. package/node_modules/typebox/build/schema/engine/contains.mjs +18 -6
  181. package/node_modules/typebox/build/schema/engine/enum.mjs +1 -1
  182. package/node_modules/typebox/build/schema/engine/items.mjs +26 -6
  183. package/node_modules/typebox/build/schema/engine/maxContains.mjs +3 -3
  184. package/node_modules/typebox/build/schema/engine/minContains.mjs +14 -4
  185. package/node_modules/typebox/build/schema/engine/not.mjs +4 -2
  186. package/node_modules/typebox/build/schema/engine/oneOf.mjs +7 -6
  187. package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -2
  188. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +4 -3
  189. package/node_modules/typebox/build/schema/engine/ref.mjs +1 -7
  190. package/node_modules/typebox/build/schema/engine/schema.mjs +13 -4
  191. package/node_modules/typebox/build/schema/engine/type.mjs +1 -1
  192. package/node_modules/typebox/build/schema/resolve/resolve.d.mts +4 -1
  193. package/node_modules/typebox/build/schema/resolve/resolve.mjs +202 -1
  194. package/node_modules/typebox/build/schema/static/propertyNames.d.mts +5 -0
  195. package/node_modules/typebox/build/schema/static/propertyNames.mjs +3 -0
  196. package/node_modules/typebox/build/schema/static/schema.d.mts +3 -0
  197. package/node_modules/typebox/build/schema/types/index.d.mts +0 -2
  198. package/node_modules/typebox/build/schema/types/index.mjs +0 -2
  199. package/node_modules/typebox/build/system/memory/clone.mjs +14 -11
  200. package/node_modules/typebox/build/system/memory/discard.mjs +6 -5
  201. package/node_modules/typebox/build/system/settings/settings.d.mts +9 -0
  202. package/node_modules/typebox/build/system/settings/settings.mjs +2 -0
  203. package/node_modules/typebox/build/type/engine/call/distribute_arguments.d.mts +6 -1
  204. package/node_modules/typebox/build/type/engine/call/distribute_arguments.mjs +16 -3
  205. package/node_modules/typebox/build/type/engine/call/instantiate.mjs +34 -4
  206. package/node_modules/typebox/build/type/engine/evaluate/broaden.d.mts +5 -5
  207. package/node_modules/typebox/build/type/engine/evaluate/broaden.mjs +20 -26
  208. package/node_modules/typebox/build/type/engine/evaluate/compare.d.mts +6 -10
  209. package/node_modules/typebox/build/type/engine/evaluate/compare.mjs +9 -13
  210. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +7 -4
  211. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +12 -19
  212. package/node_modules/typebox/build/type/engine/evaluate/distribute.d.mts +4 -20
  213. package/node_modules/typebox/build/type/engine/evaluate/distribute.mjs +8 -20
  214. package/node_modules/typebox/build/type/engine/evaluate/evaluate.d.mts +4 -4
  215. package/node_modules/typebox/build/type/engine/evaluate/evaluate.mjs +11 -8
  216. package/node_modules/typebox/build/type/engine/evaluate/flatten.d.mts +1 -1
  217. package/node_modules/typebox/build/type/engine/evaluate/flatten.mjs +3 -4
  218. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +17 -2
  219. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +26 -6
  220. package/node_modules/typebox/build/type/engine/exclude/operation.mjs +3 -4
  221. package/node_modules/typebox/build/type/engine/extract/operation.d.mts +1 -1
  222. package/node_modules/typebox/build/type/engine/extract/operation.mjs +3 -4
  223. package/node_modules/typebox/build/type/engine/priority/priority.d.mts +2 -2
  224. package/node_modules/typebox/build/type/engine/priority/priority.mjs +3 -4
  225. package/node_modules/typebox/build/type/script/mapping.d.mts +32 -29
  226. package/node_modules/typebox/build/type/script/mapping.mjs +18 -25
  227. package/node_modules/typebox/build/type/script/parser.d.mts +19 -25
  228. package/node_modules/typebox/build/type/script/parser.mjs +27 -23
  229. package/node_modules/typebox/build/value/errors/errors.d.mts +2 -2
  230. package/node_modules/typebox/build/value/errors/errors.mjs +1 -1
  231. package/node_modules/typebox/package.json +27 -27
  232. package/node_modules/typebox/readme.md +79 -82
  233. package/node_modules/undici/docs/docs/api/Client.md +21 -8
  234. package/node_modules/undici/lib/api/readable.js +33 -43
  235. package/node_modules/undici/lib/core/connect.js +16 -2
  236. package/node_modules/undici/lib/core/symbols.js +1 -0
  237. package/node_modules/undici/lib/dispatcher/client-h1.js +3 -3
  238. package/node_modules/undici/lib/dispatcher/client-h2.js +179 -10
  239. package/node_modules/undici/lib/dispatcher/client.js +74 -29
  240. package/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +28 -6
  241. package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +15 -1
  242. package/node_modules/undici/lib/handler/retry-handler.js +7 -2
  243. package/node_modules/undici/lib/interceptor/cache.js +5 -2
  244. package/node_modules/undici/lib/interceptor/deduplicate.js +1 -1
  245. package/node_modules/undici/lib/mock/mock-utils.js +213 -24
  246. package/node_modules/undici/lib/util/cache.js +2 -4
  247. package/node_modules/undici/lib/web/websocket/websocket.js +22 -0
  248. package/node_modules/undici/package.json +1 -1
  249. package/node_modules/undici/types/client.d.ts +40 -0
  250. package/node_modules/ws/lib/permessage-deflate.js +3 -1
  251. package/node_modules/ws/package.json +1 -1
  252. package/openclaw.plugin.json +24 -10
  253. package/package.json +8 -10
  254. package/skills/discord/SKILL.md +1 -1
  255. package/dist/approval-message-safety-AcIxvrf3.js +0 -140
  256. package/dist/audit-core-xwjIczO0.js +0 -105
  257. package/dist/channel-actions-5s_hjYKd.js +0 -208
  258. package/dist/channel-api-CAJ0wMoV.js +0 -21
  259. package/dist/inbound-event-delivery-De2qxTrP.js +0 -39
  260. package/dist/provider.runtime-C9PnJZbZ.js +0 -2
  261. package/dist/runtime-api.actions.js +0 -3
  262. package/dist/runtime-api.lookup.js +0 -7
  263. package/dist/runtime-api.monitor-CHxrFPnW.js +0 -5
  264. package/dist/runtime-api.monitor.js +0 -8
  265. package/dist/runtime-api.send.js +0 -7
  266. package/dist/runtime-api.threads.js +0 -5
  267. package/dist/send.reactions-BAn0iiVQ.js +0 -78
  268. package/dist/send.receipt-IPI1p-SP.js +0 -66
  269. package/dist/subagent-hooks-CTt5KCJr.js +0 -120
  270. package/dist/subagent-progress-DhaE_h8a.js +0 -109
  271. package/dist/targets-yUFTHoAx.js +0 -3
  272. package/dist/transcripts-source-CfFMexGt.js +0 -123
  273. package/node_modules/p-map/index.d.ts +0 -155
  274. package/node_modules/p-map/index.js +0 -285
  275. package/node_modules/p-map/license +0 -9
  276. package/node_modules/p-map/package.json +0 -57
  277. package/node_modules/p-map/readme.md +0 -190
  278. package/node_modules/typebox/build/format/_idna.d.mts +0 -2
  279. package/node_modules/typebox/build/format/_idna.mjs +0 -246
  280. package/node_modules/typebox/build/format/_puny.d.mts +0 -1
  281. package/node_modules/typebox/build/format/_puny.mjs +0 -77
  282. package/node_modules/typebox/build/schema/resolve/ref.d.mts +0 -3
  283. package/node_modules/typebox/build/schema/resolve/ref.mjs +0 -146
  284. package/node_modules/typebox/build/schema/types/contentEncoding.d.mts +0 -9
  285. package/node_modules/typebox/build/schema/types/contentEncoding.mjs +0 -13
  286. package/node_modules/typebox/build/schema/types/contentMediaType.d.mts +0 -9
  287. package/node_modules/typebox/build/schema/types/contentMediaType.mjs +0 -13
  288. package/dist/{allow-list-DnxIlPP2.js → allow-list-BDMimASD.js} +1 -1
  289. package/dist/{doctor-contract-DLa-BTiU.js → doctor-contract-DsWvKIXF.js} +1 -1
@@ -1,39 +1,44 @@
1
- import { B as Message, C as ReadyListener, Ct as editChannel, E as ThreadUpdateListener, Et as getChannelMessage, K as Modal, L as Command, M as RateLimitError, O as Client, R as CommandWithSubcommands, S as PresenceUpdateListener, T as ThreadDeleteListener, V as User, Y as Button, Z as Container, _ as GuildDeleteListener, a as chunkDiscordTextWithMode, at as Separator, b as MessageReactionAddListener, bt as createThread, g as GuildCreateListener, h as discord_exports, k as Plugin, ot as StringSelectMenu, rt as Row, st as TextDisplay, tn as __exportAll, ut as BaseMessageInteractiveComponent, v as InteractionCreateListener, x as MessageReactionRemoveListener, y as MessageCreateListener, z as Guild } from "./retry-CLpJcXXf.js";
2
- import { c as resolveDiscordAccountAllowFrom, d as resolveDiscordAccountDmPolicy, f as resolveDiscordMaxLinesPerMessage, p as normalizeDiscordToken, s as resolveDiscordAccount } from "./accounts-DJTf1LtA.js";
3
- import { n as getDiscordActivitiesRuntime } from "./runtime-DoHjckYm.js";
4
- import { C as parseDiscordActivityCustomIdForInteraction, D as parseDiscordModalCustomIdForInteraction, E as parseDiscordModalCustomId, O as decodeCustomIdComponent, T as parseDiscordComponentCustomIdForInteraction, k as encodeCustomIdComponent, l as parseExecApprovalData, s as parseDiscordQuestionData, w as parseDiscordComponentCustomId, y as buildDiscordActivityCustomId } from "./outbound-session-route-DxLkrgKQ.js";
5
- import { D as canViewDiscordGuildChannel, F as createDiscordRestClient, H as registerGateway, K as validateDiscordProxyUrl, U as unregisterGateway, q as withValidatedDiscordProxy, z as DISCORD_REST_TIMEOUT_MS } from "./send.shared-BmwFjW6T.js";
6
- import { _ as resolveDiscordModalEntryWithPersistence, g as resolveDiscordComponentEntryWithPersistence, t as editDiscordComponentMessage } from "./send.components-iJyFXw9L.js";
1
+ import { A as ThreadUpdateListener, C as MessageCreateListener, D as ReadyListener, E as PresenceUpdateListener, G as User, H as CommandWithSubcommands, I as RateLimitError, K as hasDiscordV2Components, M as Client, N as Plugin, S as InteractionCreateListener, T as MessageReactionRemoveListener, U as Guild, V as Command, W as Message, Z as Modal, a as chunkDiscordTextWithMode, b as GuildCreateListener, ct as Row, dt as StringSelectMenu, et as Button, ft as TextDisplay, ht as BaseMessageInteractiveComponent, k as ThreadDeleteListener, nt as Container, ut as Separator, w as MessageReactionAddListener, x as GuildDeleteListener, y as discord_exports } from "./retry-DqHCW6PV.js";
2
+ import { C as parseDiscordActivityCustomIdForInteraction, D as parseDiscordModalCustomIdForInteraction, E as parseDiscordModalCustomId, O as decodeCustomIdComponent, T as parseDiscordComponentCustomIdForInteraction, c as parseExecApprovalData, k as encodeCustomIdComponent, o as parseDiscordQuestionData, w as parseDiscordComponentCustomId, y as buildDiscordActivityCustomId } from "./components-Dk35LTf_.js";
3
+ import { c as resolveDiscordAccount, f as resolveDiscordAccountDmPolicy, l as resolveDiscordAccountAllowFrom, m as normalizeDiscordToken, p as resolveDiscordMaxLinesPerMessage } from "./accounts-CfQ-vSyf.js";
4
+ import { a as resolveDiscordComponentEntryWithPersistence, o as resolveDiscordModalEntryWithPersistence, t as editDiscordComponentMessage } from "./send.components-CrBJpGT_.js";
7
5
  import { t as getDiscordRuntime } from "./runtime-DgnVQ7zW.js";
8
6
  import { a as isDiscordRateLimitResponseBody, o as summarizeDiscordResponseBody } from "./api-DNVbJyEy.js";
9
- import { C as resolveTimestampMs, a as normalizeDiscordSlug, b as formatDiscordReactionEmoji, c as resolveDiscordChannelConfigWithFallback, d as resolveDiscordCommandOwnerAllowFrom, f as resolveDiscordGuildEntry, g as resolveDiscordOwnerAllowFrom, h as resolveDiscordOwnerAccess, i as normalizeDiscordDisplaySlug, l as resolveDiscordChannelPolicyCommandAuthorizer, m as resolveDiscordMemberAllowed, n as isDiscordGroupAllowedByPolicy, o as resolveDiscordAllowListMatch, p as resolveDiscordMemberAccessState, r as normalizeDiscordAllowList, t as allowListMatches, v as resolveGroupDmAllow, x as formatDiscordUserTag, y as shouldEmitDiscordReactionNotification } from "./allow-list-DnxIlPP2.js";
10
- import { a as formatMention, t as sendMessageDiscord } from "./send.outbound-DS5tHwfR.js";
7
+ import { C as resolveTimestampMs, a as normalizeDiscordSlug, b as formatDiscordReactionEmoji, c as resolveDiscordChannelConfigWithFallback, d as resolveDiscordCommandOwnerAllowFrom, f as resolveDiscordGuildEntry, g as resolveDiscordOwnerAllowFrom, h as resolveDiscordOwnerAccess, i as normalizeDiscordDisplaySlug, l as resolveDiscordChannelPolicyCommandAuthorizer, m as resolveDiscordMemberAllowed, n as isDiscordGroupAllowedByPolicy, o as resolveDiscordAllowListMatch, p as resolveDiscordMemberAccessState, r as normalizeDiscordAllowList, s as resolveDiscordChannelConfig, v as resolveGroupDmAllow, x as formatDiscordUserTag, y as shouldEmitDiscordReactionNotification } from "./allow-list-BDMimASD.js";
8
+ import { B as validateDiscordProxyUrl, G as unregisterGateway, P as createDiscordRestClient, R as DISCORD_REST_TIMEOUT_MS, V as withValidatedDiscordProxy, W as registerGateway, b as canViewDiscordGuildChannel, w as hasAnyChannelPermissionDiscord } from "./send.shared-DZJ1akvQ.js";
9
+ import { a as formatMention, t as sendMessageDiscord } from "./send.outbound-D7JYmfm9.js";
10
+ import { t as isDiscordThreadChannelType } from "./channel-type-CVLZyGYS.js";
11
+ import { n as getDiscordActivitiesRuntime } from "./runtime-DrxVBTtp.js";
12
+ import { n as resolveDiscordVoiceEnabled, r as authorizeDiscordVoiceIngress, t as resolveDiscordVoiceAccess } from "./owner-access-UlYi9t0M.js";
11
13
  import { i as setPresence, t as clearPresences } from "./presence-cache-z1N8MbJ3.js";
12
- import { r as sendVoiceMessageDiscord } from "./send-B7Q6Oc1I.js";
14
+ import { _ as readMessagesDiscord, s as sendVoiceMessageDiscord } from "./send-UJHiAm4C.js";
13
15
  import { c as raceWithTimeout, u as withAbortTimeout } from "./timeouts-B9cy6RvQ.js";
14
- import { a as getDiscordExecApprovalApprovers, n as formatDiscordApprovalDisplayValue, r as resolveDiscordConversationIdentity, s as isDiscordExecApprovalClientEnabled, t as DISCORD_APPROVAL_ALLOWED_MENTIONS } from "./approval-message-safety-AcIxvrf3.js";
15
- import { t as resolveDiscordChannelAllowlist } from "./resolve-channels-Dm7jEeg6.js";
16
- import { t as resolveDiscordUserAllowlist } from "./resolve-users-bktrCUUU.js";
17
- import { a as isThreadArchived, c as resolveDiscordChannelInfoSafe, d as resolveDiscordChannelParentSafe, f as resolveDiscordChannelTopicSafe, h as formatThreadBindingDurationLabel, l as resolveDiscordChannelNameSafe, r as getThreadBindingManager, s as resolveDiscordChannelIdSafe, u as resolveDiscordChannelParentIdSafe } from "./thread-bindings.manager-DCIH6WDC.js";
18
- import { a as probeDiscordApplicationId, r as parseApplicationIdFromToken } from "./probe-Bl-k5_Ku.js";
16
+ import { S as isDiscordExecApprovalClientEnabled, b as getDiscordExecApprovalApprovers, c as resolveDiscordEffectiveRoute, i as buildDiscordConversationRouteContext, n as DISCORD_APPROVAL_ALLOWED_MENTIONS, o as resolveDiscordBoundConversationRoute, r as formatDiscordApprovalDisplayValue, u as resolveDiscordConversationIdentity } from "./outbound-session-route-Bn4uXaoE.js";
17
+ import { t as discordIngressIdentity } from "./ingress-identity-CzZKcPK8.js";
18
+ import { t as resolveDiscordChannelAllowlist } from "./resolve-channels-D2yl_2CN.js";
19
+ import { t as resolveDiscordUserAllowlist } from "./resolve-users-BLxfqajZ.js";
20
+ import { a as isThreadArchived, c as resolveDiscordChannelInfoSafe, f as resolveDiscordChannelTopicSafe, h as formatThreadBindingDurationLabel, l as resolveDiscordChannelNameSafe, r as getThreadBindingManager, s as resolveDiscordChannelIdSafe, u as resolveDiscordChannelParentIdSafe } from "./thread-bindings.manager-Clm77DBM.js";
21
+ import { a as probeDiscordApplicationId, r as parseApplicationIdFromToken } from "./probe-n6ZXxK8V.js";
19
22
  import { i as sanitizeRecentModels, n as normalizeModelRef, r as preferenceTimestampMs, t as buildPreferenceModelKey } from "./model-picker-preference-primitives-BHT1LkR_.js";
20
- import { a as formatDiscordMediaText, d as resolveDiscordChannelInfo, f as resolveDiscordMessageChannelId, n as resolveDiscordForwardedMessagesTextFromSnapshots, t as resolveDiscordEmbedText } from "./message-utils-BSCPVgy0.js";
21
- import { t as resolveDiscordSenderIdentity } from "./sender-identity-QtalnCYv.js";
22
- import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
23
- import { buildAgentSessionKey, deriveLastRoutePolicy, isAcpSessionKey, isSubagentSessionKey, parseAgentSessionKey, resolveAgentIdFromSessionKey, resolveAgentRoute } from "openclaw/plugin-sdk/routing";
24
- import { asFiniteNumber, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeOptionalStringifiedId, normalizeStringEntries, parseFiniteNumber, parseStrictNonNegativeInteger, summarizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
25
- import { getRuntimeConfig, getRuntimeConfigSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
23
+ import { f as resolveDiscordChannelInfo, u as resolveDiscordMessageStickers } from "./message-text-Ca8M6TU8.js";
24
+ import { i as resolveDiscordThreadParentInfo } from "./threading-CXuu_7o9.js";
25
+ import { t as resolveDiscordSenderIdentity } from "./sender-identity-BJtfyise.js";
26
26
  import { logDebug, logError } from "openclaw/plugin-sdk/logging-core";
27
- import { ApplicationCommandOptionType, ButtonStyle, ChannelType, ComponentType, GatewayCloseCodes, GatewayCloseCodes as GatewayCloseCodes$1, GatewayDispatchEvents, GatewayIntentBits, GatewayOpcodes } from "discord-api-types/v10";
28
- import { createHash, randomUUID } from "node:crypto";
29
- import { MAX_DATE_TIMESTAMP_MS, asDateTimestampMs, parseFiniteNumber as parseFiniteNumber$1, parseStrictInteger, parseStrictPositiveInteger, resolveDateTimestampMs, resolveTimestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
30
- import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
27
+ import { ApplicationCommandOptionType, ButtonStyle, ChannelType, ComponentType, GatewayCloseCodes, GatewayCloseCodes as GatewayCloseCodes$1, GatewayDispatchEvents, GatewayIntentBits, GatewayOpcodes, PermissionFlagsBits } from "discord-api-types/v10";
31
28
  import { Type } from "typebox";
32
29
  import { Check, Errors } from "typebox/value";
30
+ import { createHash, randomUUID } from "node:crypto";
31
+ import { MAX_DATE_TIMESTAMP_MS, MAX_TIMER_TIMEOUT_MS, asDateTimestampMs, asSafeIntegerInRange, parseFiniteNumber, parseStrictInteger, parseStrictPositiveInteger, resolveDateTimestampMs, resolveTimestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
32
+ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
33
33
  import { inspect } from "node:util";
34
34
  import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
35
35
  import { PlatformMessageNotDispatchedError, formatErrorMessage, toErrorObject } from "openclaw/plugin-sdk/error-runtime";
36
36
  import { fetchWithSsrFGuard, formatErrorMessage as formatErrorMessage$1 } from "openclaw/plugin-sdk/ssrf-runtime";
37
+ import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
38
+ import { resolveAgentRoute } from "openclaw/plugin-sdk/routing";
39
+ import { asFiniteNumber, asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, parseFiniteNumber as parseFiniteNumber$1, parseStrictNonNegativeInteger as parseStrictNonNegativeInteger$1, summarizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
40
+ import { getRuntimeConfig, getRuntimeConfigSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
41
+ import { resolveSendableOutboundReplyParts, resolveTextChunksWithFallback } from "openclaw/plugin-sdk/reply-payload";
37
42
  import { createHttp1EnvHttpProxyAgent, createHttp1ProxyAgent, createNodeProxyAgent, resolveEnvHttpProxyAgentOptions, wrapFetchWithAbortSignal } from "openclaw/plugin-sdk/fetch-runtime";
38
43
  import { createNonExitingRuntime, createSubsystemLogger, danger, formatDurationSeconds, isVerbose, logVerbose, shouldLogVerbose, sleepWithAbort, warn } from "openclaw/plugin-sdk/runtime-env";
39
44
  import { sliceUtf16Safe, truncateUtf16Safe, withTimeout } from "openclaw/plugin-sdk/text-utility-runtime";
@@ -41,33 +46,30 @@ import { addAllowlistUserEntriesFromConfigEntry, buildAllowlistResolutionSummary
41
46
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
42
47
  import { resolveChunkMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
43
48
  import { chunkItems, findCodeRegions, sanitizeAssistantVisibleText, sanitizeAssistantVisibleTextWithProfile } from "openclaw/plugin-sdk/text-chunking";
44
- import { createReplyReferencePlanner } from "openclaw/plugin-sdk/reply-reference";
45
49
  import { buildOutboundSessionContext, listMessageReceiptPlatformIds, resolveChannelStreamingBlockEnabled, sendDurableMessageBatch } from "openclaw/plugin-sdk/channel-outbound";
46
50
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
47
- import { resolveSendableOutboundReplyParts, resolveTextChunksWithFallback } from "openclaw/plugin-sdk/reply-payload";
48
51
  import { loadWebMedia } from "openclaw/plugin-sdk/web-media";
49
52
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
50
- import { resolvePinnedMainDmOwnerFromAllowlist as resolvePinnedMainDmOwnerFromAllowlist$1 } from "openclaw/plugin-sdk/security-runtime";
53
+ import { buildCommandTextFromArgs, findCommandByNativeName, formatCommandArgMenuTitle, listChatCommands, listNativeCommandSpecsForConfig, listSkillCommandsForAgents, resolveCommandAuthorizedFromAuthorizers, resolveEffectiveAgentRuntime, resolveNativeCommandSessionTargets, resolveStoredModelOverride, serializeCommandArgs } from "openclaw/plugin-sdk/command-auth-native";
51
54
  import { GROUP_POLICY_BLOCKED_LABEL, resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
55
+ import { resolvePinnedMainDmOwnerFromAllowlist as resolvePinnedMainDmOwnerFromAllowlist$1 } from "openclaw/plugin-sdk/security-runtime";
52
56
  import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
53
57
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
54
- import { questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
55
58
  import { buildPairingReply, upsertChannelPairingRequest, upsertChannelPairingRequest as upsertChannelPairingRequest$1 } from "openclaw/plugin-sdk/conversation-runtime";
59
+ import * as conversationRuntime from "openclaw/plugin-sdk/conversation-binding-runtime";
60
+ import { questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
61
+ import { createChannelIngressResolver, readChannelIngressStoreAllowFromForDmPolicy } from "openclaw/plugin-sdk/channel-ingress-runtime";
56
62
  import { clearExpiredCooldowns, ensureAuthProfileStore, formatReasoningMessage, getPreparedModelCatalogSnapshot, isProfileInCooldown, listAgentIds, loadPreparedModelCatalog, resolveAgentAvatar, resolveAgentDir, resolveDefaultModelForAgent, resolveHumanDelayConfig, resolveProfilesUnavailableReason } from "openclaw/plugin-sdk/agent-runtime";
57
63
  import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
58
64
  import { resolveNativeCommandsEnabled, resolveNativeSkillsEnabled } from "openclaw/plugin-sdk/native-command-config-runtime";
59
65
  import { requestHeartbeat } from "openclaw/plugin-sdk/heartbeat-runtime";
60
66
  import { enqueueRoutedSystemEvent, enqueueRoutedSystemEvent as enqueueRoutedSystemEvent$1 } from "openclaw/plugin-sdk/system-event-runtime";
61
- import { createChannelIngressResolver, defineStableChannelIngressIdentity, readChannelIngressStoreAllowFromForDmPolicy } from "openclaw/plugin-sdk/channel-ingress-runtime";
62
- import { createChannelPartialDeliveryError, dispatchChannelInboundTurn, formatInboundEnvelope, hasVisibleInboundReplyDispatch, isChannelPartialDeliveryError, resolveEnvelopeFormatOptions, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent } from "openclaw/plugin-sdk/channel-inbound";
63
- import { resolveChannelModelOverride } from "openclaw/plugin-sdk/model-session-runtime";
64
- import { finalizeInboundContext, generateConversationLabel } from "openclaw/plugin-sdk/reply-dispatch-runtime";
65
67
  import { deleteSessionEntry, getSessionEntry, listSessionEntries, readSessionUpdatedAt as readSessionUpdatedAt$1, resolveStorePath, resolveStorePath as resolveStorePath$1 } from "openclaw/plugin-sdk/session-store-runtime";
66
- import { buildCommandTextFromArgs, findCommandByNativeName, formatCommandArgMenuTitle, listChatCommands, listNativeCommandSpecsForConfig, listSkillCommandsForAgents, resolveCommandAuthorizedFromAuthorizers, resolveEffectiveAgentRuntime, resolveNativeCommandSessionTargets, resolveStoredModelOverride, serializeCommandArgs } from "openclaw/plugin-sdk/command-auth-native";
68
+ import { finalizeInboundContext } from "openclaw/plugin-sdk/reply-dispatch-runtime";
69
+ import { createChannelPartialDeliveryError, dispatchChannelInboundTurn, formatInboundEnvelope, hasVisibleInboundReplyDispatch, isChannelPartialDeliveryError, resolveEnvelopeFormatOptions, resolveInboundSupplementalSenderAllowed, runChannelInboundEvent } from "openclaw/plugin-sdk/channel-inbound";
67
70
  import { buildCommandTextFromArgs as buildCommandTextFromArgs$1, findCommandByNativeName as findCommandByNativeName$1, mergeNativeCommandSpecs, parseCommandArgs, resolveCommandArgChoices, resolveCommandArgMenu, serializeCommandArgs as serializeCommandArgs$1 } from "openclaw/plugin-sdk/native-command-registry";
68
71
  import { PLUGIN_COMMAND_DISPATCH } from "openclaw/plugin-sdk/plugin-command-runtime";
69
72
  import { resolveDirectStatusReplyForSession } from "openclaw/plugin-sdk/command-status-runtime";
70
- import * as conversationRuntime from "openclaw/plugin-sdk/conversation-binding-runtime";
71
73
  import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
72
74
  import { EventEmitter } from "node:events";
73
75
  import * as ws from "ws";
@@ -79,6 +81,7 @@ import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runti
79
81
  import { stripPlainTextToolCallBlocks } from "openclaw/plugin-sdk/tool-payload";
80
82
  import { createChannelInteractiveDispatcher } from "openclaw/plugin-sdk/plugin-runtime";
81
83
  import { resolveApprovalOverGateway } from "openclaw/plugin-sdk/approval-gateway-runtime";
84
+ import { reportChannelRoomJoin } from "openclaw/plugin-sdk/channel-join-intro-runtime";
82
85
  import { resolveRequestUrl } from "openclaw/plugin-sdk/request-url";
83
86
  import { fetchWithRuntimeDispatcher } from "openclaw/plugin-sdk/runtime-fetch";
84
87
  import { Agent as Agent$1 } from "undici";
@@ -140,56 +143,216 @@ async function runDiscordListenerWithSlowLog(params) {
140
143
  }
141
144
  }
142
145
  //#endregion
146
+ //#region extensions/discord/src/monitor/presence-events.ts
147
+ const DISCORD_PRESENCE_GREETING_COOLDOWN_MS = 480 * 60 * 1e3;
148
+ function isDiscordOnlineStatus(status) {
149
+ return status === "online" || status === "idle" || status === "dnd";
150
+ }
151
+ function isDiscordOfflineStatus(status) {
152
+ return status === "offline";
153
+ }
154
+ function resolveDiscordOnlinePresenceEvent(params) {
155
+ const config = params.config;
156
+ const userId = params.data.user?.id?.trim();
157
+ if (!config || config.enabled === false || !userId || userId === params.botUserId || params.data.user.bot === true || !isDiscordOnlineStatus(params.data.status) || !params.availabilityKind) return null;
158
+ if (config.users !== void 0 && !config.users.includes(userId)) return null;
159
+ if (params.lastEmittedAtMs !== void 0 && params.nowMs - params.lastEmittedAtMs < 288e5) return null;
160
+ const lines = [
161
+ "Discord online-presence event:",
162
+ params.availabilityKind === "observed-offline" ? `A human member became available online after being observed offline in guild_id=${JSON.stringify(params.data.guild_id)} user_id=${JSON.stringify(userId)} status=${JSON.stringify(params.data.status)}.` : `A human member became newly visible online since the current guild snapshot in guild_id=${JSON.stringify(params.data.guild_id)} user_id=${JSON.stringify(userId)} status=${JSON.stringify(params.data.status)}. They may have come online or joined after the snapshot; do not claim an exact prior status.`,
163
+ `The authorized greeting target is channel_id=${JSON.stringify(config.channelId)}.`,
164
+ "Before greeting, retrieve relevant memory and wiki context for this immutable user_id, including a known timezone when available. Use their local time for the greeting; if their timezone is unknown, do not guess.",
165
+ "Send at most one short, natural greeting to the target channel. Do not reveal private memory. If no greeting is appropriate, stay silent."
166
+ ];
167
+ return {
168
+ channelId: config.channelId,
169
+ userId,
170
+ text: lines.join("\n")
171
+ };
172
+ }
173
+ //#endregion
174
+ //#region extensions/discord/src/monitor/presence-cooldown-store.ts
175
+ const DISCORD_PRESENCE_COOLDOWN_MAX_ENTRIES = 25e3;
176
+ function openDiscordPresenceCooldownStore() {
177
+ return getDiscordRuntime().state.openSyncKeyedStore({
178
+ namespace: "presence-greeting-cooldowns",
179
+ maxEntries: DISCORD_PRESENCE_COOLDOWN_MAX_ENTRIES,
180
+ overflowPolicy: "reject-new",
181
+ defaultTtlMs: DISCORD_PRESENCE_GREETING_COOLDOWN_MS
182
+ });
183
+ }
184
+ //#endregion
185
+ //#region extensions/discord/src/monitor/presence-emission-gate.ts
186
+ const DISCORD_PRESENCE_RECONNECT_SUPPRESS_MS = 300 * 1e3;
187
+ const DISCORD_PRESENCE_BURST_LIMIT = 8;
188
+ const DISCORD_PRESENCE_BURST_WINDOW_MS = 60 * 1e3;
189
+ function resolveDiscordPresenceGateOptions(config) {
190
+ return {
191
+ reconnectSuppressMs: config?.reconnectSuppressSeconds !== void 0 ? config.reconnectSuppressSeconds * 1e3 : DISCORD_PRESENCE_RECONNECT_SUPPRESS_MS,
192
+ burstLimit: config?.burstLimit ?? DISCORD_PRESENCE_BURST_LIMIT,
193
+ burstWindowMs: config?.burstWindowSeconds !== void 0 ? config.burstWindowSeconds * 1e3 : DISCORD_PRESENCE_BURST_WINDOW_MS
194
+ };
195
+ }
196
+ /**
197
+ * Per-account lifecycle gate for online-presence events. Reconnect state belongs to the Gateway
198
+ * session, while burst state follows the guild-owned configuration that supplies its limits.
199
+ */
200
+ var DiscordPresenceEmissionGate = class {
201
+ constructor() {
202
+ this.reconnectLogged = false;
203
+ this.burstByGuild = /* @__PURE__ */ new Map();
204
+ this.nextReservationId = 0;
205
+ }
206
+ noteGatewaySessionReset(nowMs) {
207
+ this.lastSessionResetAtMs = nowMs;
208
+ this.reconnectLogged = false;
209
+ }
210
+ evaluateReconnectWindow(nowMs, options) {
211
+ if (this.lastSessionResetAtMs !== void 0 && options.reconnectSuppressMs > 0 && nowMs - this.lastSessionResetAtMs < options.reconnectSuppressMs) {
212
+ const shouldLog = !this.reconnectLogged;
213
+ this.reconnectLogged = true;
214
+ return {
215
+ allowed: false,
216
+ reason: "reconnect-window",
217
+ shouldLog
218
+ };
219
+ }
220
+ return { allowed: true };
221
+ }
222
+ reserveBurst(guildId, nowMs, options) {
223
+ const state = this.burstByGuild.get(guildId) ?? {
224
+ reservations: [],
225
+ logged: false
226
+ };
227
+ state.reservations = state.reservations.filter((reservation) => !reservation.committed || nowMs - reservation.atMs < options.burstWindowMs);
228
+ this.burstByGuild.set(guildId, state);
229
+ if (state.reservations.length >= options.burstLimit) {
230
+ const shouldLog = !state.logged;
231
+ state.logged = true;
232
+ return {
233
+ allowed: false,
234
+ reason: state.reservations.some((reservation) => !reservation.committed) ? "burst-pending" : "burst",
235
+ shouldLog
236
+ };
237
+ }
238
+ state.logged = false;
239
+ const reservation = {
240
+ id: this.nextReservationId++,
241
+ atMs: nowMs,
242
+ committed: false
243
+ };
244
+ state.reservations.push(reservation);
245
+ return {
246
+ allowed: true,
247
+ reservation: reservation.id
248
+ };
249
+ }
250
+ commitBurst(guildId, reservation, nowMs) {
251
+ const entry = this.burstByGuild.get(guildId)?.reservations.find((candidate) => candidate.id === reservation);
252
+ if (!entry) return;
253
+ entry.atMs = nowMs;
254
+ entry.committed = true;
255
+ }
256
+ releaseBurst(guildId, reservation) {
257
+ const state = this.burstByGuild.get(guildId);
258
+ if (!state) return;
259
+ state.reservations = state.reservations.filter((candidate) => candidate.id !== reservation);
260
+ if (state.reservations.length === 0 && !state.logged) this.burstByGuild.delete(guildId);
261
+ }
262
+ };
263
+ //#endregion
264
+ //#region extensions/discord/src/monitor/presence-transition-cache.ts
265
+ const DEFAULT_PRESENCE_BASELINE_MAX_ENTRIES = 75e3;
266
+ var DiscordPresenceBaselineCache = class {
267
+ constructor(maxEntries = DEFAULT_PRESENCE_BASELINE_MAX_ENTRIES) {
268
+ this.maxEntries = maxEntries;
269
+ this.offlineByKey = /* @__PURE__ */ new Map();
270
+ this.onlineByKey = /* @__PURE__ */ new Map();
271
+ }
272
+ clear() {
273
+ this.offlineByKey.clear();
274
+ this.onlineByKey.clear();
275
+ }
276
+ clearScope(scope) {
277
+ for (const markers of [this.offlineByKey, this.onlineByKey]) for (const [key, markerScope] of markers) if (markerScope === scope) markers.delete(key);
278
+ }
279
+ isOffline(scope, key) {
280
+ return this.offlineByKey.get(key) === scope;
281
+ }
282
+ isOnline(scope, key) {
283
+ return this.onlineByKey.get(key) === scope;
284
+ }
285
+ observeOffline(scope, key) {
286
+ this.deleteMarker(this.onlineByKey, scope, key);
287
+ return this.observe(this.offlineByKey, scope, key);
288
+ }
289
+ observeOnline(scope, key) {
290
+ this.deleteMarker(this.offlineByKey, scope, key);
291
+ return this.observe(this.onlineByKey, scope, key);
292
+ }
293
+ deleteMarker(markers, scope, key) {
294
+ if (markers.get(key) === scope) markers.delete(key);
295
+ }
296
+ observe(markers, scope, key) {
297
+ markers.delete(key);
298
+ markers.set(key, scope);
299
+ let evictedScope;
300
+ while (markers.size > this.maxEntries) {
301
+ const oldestKey = markers.keys().next().value;
302
+ if (oldestKey === void 0) break;
303
+ evictedScope = markers.get(oldestKey);
304
+ markers.delete(oldestKey);
305
+ }
306
+ return evictedScope;
307
+ }
308
+ };
309
+ //#endregion
310
+ //#region extensions/discord/src/monitor/thread-session-close.ts
311
+ /**
312
+ * Closes every session entry in the store whose key contains {@link threadId}.
313
+ * The explicit lifecycle deletion archives the old transcript and guarantees
314
+ * that a later inbound message starts a fresh session in every reset mode.
315
+ */
316
+ async function closeDiscordThreadSessions(params) {
317
+ const { cfg, threadId } = params;
318
+ const normalizedThreadId = normalizeOptionalLowercaseString(threadId) ?? "";
319
+ if (!normalizedThreadId) return 0;
320
+ const segmentRe = new RegExp(`:${normalizedThreadId}(?::|$)`, "i");
321
+ function sessionKeyContainsThreadId(key) {
322
+ return segmentRe.test(key);
323
+ }
324
+ let resetCount = 0;
325
+ for (const agentId of listAgentIds(cfg)) {
326
+ const storePath = resolveStorePath(cfg.session?.store, { agentId });
327
+ for (const { sessionKey, entry } of listSessionEntries({
328
+ agentId,
329
+ storePath,
330
+ readOnly: true
331
+ })) {
332
+ if (!sessionKeyContainsThreadId(sessionKey)) continue;
333
+ if (await deleteSessionEntry({
334
+ archiveTranscript: true,
335
+ expectedSessionId: entry.sessionId ?? null,
336
+ expectedUpdatedAt: entry.updatedAt,
337
+ sessionKey,
338
+ storePath
339
+ })) resetCount += 1;
340
+ }
341
+ }
342
+ return resetCount;
343
+ }
344
+ //#endregion
143
345
  //#region extensions/discord/src/monitor/dm-command-auth.ts
144
- const DISCORD_ALLOW_LIST_PREFIXES = [
145
- "discord:",
146
- "user:",
147
- "pk:"
148
- ];
149
346
  const DISCORD_CHANNEL_ID = "discord";
150
- const DISCORD_USER_ID_KIND = "stable-id";
151
- const DISCORD_USER_NAME_KIND = "username";
152
- function normalizeDiscordIdEntry(entry) {
153
- const text = entry.trim();
154
- if (!text) return null;
155
- const maybeId = text.replace(/^<@!?/, "").replace(/>$/, "");
156
- if (/^\d+$/.test(maybeId)) return maybeId;
157
- const prefix = DISCORD_ALLOW_LIST_PREFIXES.find((entryPrefix) => text.startsWith(entryPrefix));
158
- if (prefix) return text.slice(prefix.length).trim() || null;
159
- return null;
160
- }
161
- function normalizeDiscordNameEntry(entry) {
162
- const text = entry.trim();
163
- if (!text || text === "*" || normalizeDiscordIdEntry(text)) return null;
164
- const nameSlug = normalizeDiscordAllowList([text], DISCORD_ALLOW_LIST_PREFIXES)?.names.values().next().value;
165
- return typeof nameSlug === "string" && nameSlug ? nameSlug : null;
166
- }
167
- function normalizeDiscordNameSubject(value) {
168
- const nameSlug = normalizeDiscordAllowList([value], DISCORD_ALLOW_LIST_PREFIXES)?.names.values().next().value;
169
- return typeof nameSlug === "string" && nameSlug ? nameSlug : null;
170
- }
171
- const discordIngressIdentity = defineStableChannelIngressIdentity({
172
- key: "discordUserId",
173
- kind: DISCORD_USER_ID_KIND,
174
- normalizeEntry: normalizeDiscordIdEntry,
175
- normalizeSubject: (value) => value.trim() || null,
176
- sensitivity: "pii",
177
- aliases: [["discordUserName", normalizeDiscordNameEntry], ["discordUserTag", () => null]].map(([key, normalizeEntry]) => ({
178
- key,
179
- kind: DISCORD_USER_NAME_KIND,
180
- normalizeEntry,
181
- normalizeSubject: normalizeDiscordNameSubject,
182
- dangerous: true,
183
- sensitivity: "pii"
184
- }))
185
- });
186
347
  function createDiscordDmIngressSubject(sender) {
187
348
  return {
188
349
  stableId: sender.id,
189
350
  aliases: {
190
351
  discordUserName: sender.name,
191
- discordUserTag: sender.tag
192
- }
352
+ discordUserTag: sender.tag,
353
+ participantKind: sender.isPluralKit ? "pluralkit-member" : sender.authorKind
354
+ },
355
+ ...sender.isPluralKit ? { authentication: { discordUserId: "asserted" } } : {}
193
356
  };
194
357
  }
195
358
  function createDiscordDynamicAccessGroupResolver(params) {
@@ -264,7 +427,10 @@ async function resolveDiscordDmCommandAccess(params) {
264
427
  },
265
428
  dmPolicy: params.dmPolicy,
266
429
  groupPolicy: "disabled",
267
- policy: { mutableIdentifierMatching: params.allowNameMatching ? "enabled" : "disabled" },
430
+ policy: {
431
+ mutableIdentifierMatching: params.allowNameMatching ? "enabled" : "disabled",
432
+ ...params.minIdentifierAuthentication ? { minIdentifierAuthentication: params.minIdentifierAuthentication } : {}
433
+ },
268
434
  allowFrom: params.configuredAllowFrom,
269
435
  command: {
270
436
  hasControlCommand: false,
@@ -294,7 +460,10 @@ async function resolveDiscordTextCommandAccess(params) {
294
460
  accessGroupMembership,
295
461
  dmPolicy: "allowlist",
296
462
  groupPolicy: "allowlist",
297
- policy: { mutableIdentifierMatching: params.allowNameMatching ? "enabled" : "disabled" },
463
+ policy: {
464
+ mutableIdentifierMatching: params.allowNameMatching ? "enabled" : "disabled",
465
+ ...params.minIdentifierAuthentication ? { minIdentifierAuthentication: params.minIdentifierAuthentication } : {}
466
+ },
298
467
  allowFrom: ownerAllowFrom,
299
468
  groupAllowFrom: commandGroup,
300
469
  command: {
@@ -305,474 +474,16 @@ async function resolveDiscordTextCommandAccess(params) {
305
474
  });
306
475
  }
307
476
  //#endregion
308
- //#region extensions/discord/src/monitor/thread-title.ts
309
- const DEFAULT_THREAD_TITLE_TIMEOUT_MS = 6e4;
310
- const MAX_THREAD_TITLE_SOURCE_CHARS = 600;
311
- const MAX_THREAD_TITLE_CHANNEL_NAME_CHARS = 120;
312
- const MAX_THREAD_TITLE_CHANNEL_DESCRIPTION_CHARS = 320;
313
- const DISCORD_THREAD_TITLE_SYSTEM_PROMPT = "Generate a concise Discord thread title (3-6 words). Return only the title. Use channel context when provided and avoid redundant channel-name words unless needed for clarity.";
314
- async function generateThreadTitle(params) {
315
- const sourceText = params.messageText.trim();
316
- if (!sourceText) return null;
317
- try {
318
- const userMessage = buildThreadTitleCompletionUserMessage({
319
- sourceText,
320
- channelName: params.channelName,
321
- channelDescription: params.channelDescription
322
- });
323
- const timeoutMs = resolveThreadTitleTimeoutMs(params.timeoutMs);
324
- const generated = await generateConversationLabel({
325
- cfg: params.cfg,
326
- agentId: params.agentId,
327
- userMessage,
328
- prompt: DISCORD_THREAD_TITLE_SYSTEM_PROMPT,
329
- ...params.modelRef ? { modelRef: params.modelRef } : {},
330
- timeoutMs,
331
- maxLength: MAX_THREAD_TITLE_SOURCE_CHARS
332
- });
333
- return generated ? normalizeGeneratedThreadTitle(generated) : null;
334
- } catch (err) {
335
- logVerbose(`thread-title: title generation failed for agent ${params.agentId}: ${String(err)}`);
336
- return null;
337
- }
338
- }
339
- function buildThreadTitleCompletionUserMessage(params) {
340
- const sourceText = truncateThreadTitleSourceText(params.sourceText);
341
- const channelName = normalizeTitleContextField(params.channelName, MAX_THREAD_TITLE_CHANNEL_NAME_CHARS);
342
- const channelDescription = normalizeTitleContextField(params.channelDescription, MAX_THREAD_TITLE_CHANNEL_DESCRIPTION_CHARS);
343
- const messageLines = [];
344
- if (channelName) messageLines.push(`Channel: ${channelName}`);
345
- if (channelDescription) messageLines.push(`Channel description: ${channelDescription}`);
346
- messageLines.push(`Message:\n${sourceText}`);
347
- return messageLines.join("\n\n");
348
- }
349
- function truncateThreadTitleSourceText(sourceText) {
350
- if (sourceText.length <= MAX_THREAD_TITLE_SOURCE_CHARS) return sourceText;
351
- return `${truncateUtf16Safe(sourceText, MAX_THREAD_TITLE_SOURCE_CHARS)}...`;
352
- }
353
- function resolveThreadTitleTimeoutMs(timeoutMs) {
354
- return Math.max(100, Math.floor(timeoutMs ?? DEFAULT_THREAD_TITLE_TIMEOUT_MS));
355
- }
356
- function normalizeGeneratedThreadTitle(raw) {
357
- const lines = raw.replace(/\r/g, "").split("\n");
358
- let firstLine = "";
359
- for (const line of lines) {
360
- const trimmed = line.trim();
361
- if (!trimmed) continue;
362
- if (!firstLine && trimmed.startsWith("```")) continue;
363
- firstLine = trimmed;
364
- break;
365
- }
366
- return stripThreadTitleWrappers(firstLine);
367
- }
368
- function stripThreadTitleWrappers(raw) {
369
- let current = raw.trim();
370
- let previous = "";
371
- while (current && current !== previous) {
372
- previous = current;
373
- current = current.replace(/^["'`]+|["'`]+$/g, "").trim();
374
- current = stripBalancedWrapper(current, "**");
375
- current = stripBalancedWrapper(current, "__");
376
- current = stripBalancedWrapper(current, "*");
377
- current = stripBalancedWrapper(current, "_");
378
- current = stripBalancedWrapper(current, "~~");
379
- }
380
- return current;
381
- }
382
- function stripBalancedWrapper(text, marker) {
383
- if (text.length < marker.length * 2 + 1) return text;
384
- if (!text.startsWith(marker) || !text.endsWith(marker)) return text;
385
- const inner = text.slice(marker.length, text.length - marker.length);
386
- if (!inner || inner.includes(marker)) return text;
387
- return inner;
388
- }
389
- function normalizeTitleContextField(raw, maxChars) {
390
- const value = raw?.trim();
391
- if (!value) return;
392
- const singleLine = value.replace(/\s+/g, " ");
393
- if (singleLine.length <= maxChars) return singleLine;
394
- return `${truncateUtf16Safe(singleLine, maxChars)}...`;
395
- }
396
- //#endregion
397
- //#region extensions/discord/src/monitor/threading.cache.ts
398
- const DISCORD_THREAD_STARTER_CACHE_TTL_MS = 300 * 1e3;
399
- const DISCORD_THREAD_STARTER_CACHE_MAX = 500;
400
- const DISCORD_THREAD_STARTER_CACHE = /* @__PURE__ */ new Map();
401
- function resetDiscordThreadStarterCacheForTest() {
402
- DISCORD_THREAD_STARTER_CACHE.clear();
403
- }
404
- function getCachedThreadStarter(key, now) {
405
- const entry = DISCORD_THREAD_STARTER_CACHE.get(key);
406
- if (!entry) return;
407
- if (now - entry.updatedAt > DISCORD_THREAD_STARTER_CACHE_TTL_MS) {
408
- DISCORD_THREAD_STARTER_CACHE.delete(key);
409
- return;
410
- }
411
- DISCORD_THREAD_STARTER_CACHE.delete(key);
412
- DISCORD_THREAD_STARTER_CACHE.set(key, {
413
- ...entry,
414
- updatedAt: now
415
- });
416
- return entry.value;
417
- }
418
- function setCachedThreadStarter(key, value, now) {
419
- DISCORD_THREAD_STARTER_CACHE.delete(key);
420
- DISCORD_THREAD_STARTER_CACHE.set(key, {
421
- value,
422
- updatedAt: now
423
- });
424
- while (DISCORD_THREAD_STARTER_CACHE.size > DISCORD_THREAD_STARTER_CACHE_MAX) {
425
- const iter = DISCORD_THREAD_STARTER_CACHE.keys().next();
426
- if (iter.done) break;
427
- DISCORD_THREAD_STARTER_CACHE.delete(iter.value);
428
- }
429
- }
430
- //#endregion
431
- //#region extensions/discord/src/monitor/threading.starter.ts
432
- function isDiscordThreadType(type) {
433
- return type === discord_exports.ChannelType.PublicThread || type === discord_exports.ChannelType.PrivateThread || type === discord_exports.ChannelType.AnnouncementThread;
434
- }
435
- function isDiscordForumParentType(parentType) {
436
- return parentType === discord_exports.ChannelType.GuildForum || parentType === discord_exports.ChannelType.GuildMedia;
437
- }
438
- function resolveDiscordThreadChannel(params) {
439
- if (!params.isGuildMessage) return null;
440
- const { message, channelInfo } = params;
441
- const channel = "channel" in message ? message.channel : void 0;
442
- if (channel && typeof channel === "object" && "isThread" in channel && typeof channel.isThread === "function" && channel.isThread()) return channel;
443
- if (!isDiscordThreadType(channelInfo?.type)) return null;
444
- const messageChannelId = params.messageChannelId || resolveDiscordMessageChannelId({ message });
445
- if (!messageChannelId) return null;
446
- return {
447
- id: messageChannelId,
448
- name: channelInfo?.name ?? void 0,
449
- parentId: channelInfo?.parentId ?? void 0,
450
- parent: void 0,
451
- ownerId: channelInfo?.ownerId ?? void 0
452
- };
453
- }
454
- async function resolveDiscordThreadParentInfo(params) {
455
- const { threadChannel, channelInfo, client } = params;
456
- const parent = resolveDiscordChannelParentSafe(threadChannel);
457
- let parentId = resolveDiscordChannelParentIdSafe(threadChannel) ?? resolveDiscordChannelIdSafe(parent) ?? channelInfo?.parentId ?? void 0;
458
- if (!parentId && threadChannel.id) parentId = (await resolveDiscordChannelInfo(client, threadChannel.id))?.parentId ?? void 0;
459
- if (!parentId) return {};
460
- let parentName = resolveDiscordChannelNameSafe(parent);
461
- const parentInfo = await resolveDiscordChannelInfo(client, parentId);
462
- parentName = parentName ?? parentInfo?.name;
463
- const parentType = parentInfo?.type;
477
+ //#region extensions/discord/src/monitor/thread-channel-context.ts
478
+ function buildFetchedChannelInfo(channel) {
479
+ const channelInfo = resolveDiscordChannelInfoSafe(channel);
480
+ if (channelInfo.type === void 0) return null;
464
481
  return {
465
- id: parentId,
466
- name: parentName,
467
- type: parentType
468
- };
469
- }
470
- async function resolveDiscordThreadStarter(params) {
471
- const cacheKey = params.channel.id;
472
- const cached = getCachedThreadStarter(cacheKey, Date.now());
473
- if (cached) return cached;
474
- try {
475
- const messageChannelId = resolveDiscordThreadStarterMessageChannelId(params);
476
- if (!messageChannelId) return null;
477
- const starter = await fetchDiscordThreadStarterMessage({
478
- client: params.client,
479
- messageChannelId,
480
- threadId: params.channel.id
481
- });
482
- if (!starter) return null;
483
- const payload = buildDiscordThreadStarterPayload({
484
- starter,
485
- resolveTimestampMs: params.resolveTimestampMs
486
- });
487
- if (!payload) return null;
488
- setCachedThreadStarter(cacheKey, payload, Date.now());
489
- return payload;
490
- } catch {
491
- return null;
492
- }
493
- }
494
- function resolveDiscordThreadStarterMessageChannelId(params) {
495
- return isDiscordForumParentType(params.parentType) ? params.channel.id : params.parentId;
496
- }
497
- async function fetchDiscordThreadStarterMessage(params) {
498
- const starter = await getChannelMessage(params.client.rest, params.messageChannelId, params.threadId);
499
- return starter ? starter : null;
500
- }
501
- function buildDiscordThreadStarterPayload(params) {
502
- const text = resolveDiscordThreadStarterText(params.starter);
503
- if (!text) return null;
504
- return {
505
- text,
506
- ...resolveDiscordThreadStarterIdentity(params.starter),
507
- timestamp: params.resolveTimestampMs(params.starter.timestamp) ?? void 0
508
- };
509
- }
510
- function resolveDiscordThreadStarterText(starter) {
511
- const content = normalizeOptionalString(starter.content) ?? "";
512
- const embedText = resolveDiscordEmbedText(starter.embeds?.[0]);
513
- const forwardedText = resolveDiscordForwardedMessagesTextFromSnapshots(starter.message_snapshots);
514
- return [content || embedText || forwardedText, formatDiscordMediaText({
515
- attachments: starter.attachments ?? void 0,
516
- stickers: starter.sticker_items ?? void 0
517
- })].filter(Boolean).join("\n");
518
- }
519
- function resolveDiscordThreadStarterIdentity(starter) {
520
- return {
521
- author: resolveDiscordThreadStarterAuthor(starter),
522
- authorId: starter.author?.id ?? void 0,
523
- authorName: starter.author?.username ?? void 0,
524
- authorTag: resolveDiscordThreadStarterAuthorTag(starter.author),
525
- memberRoleIds: resolveDiscordThreadStarterRoleIds(starter.member)
526
- };
527
- }
528
- function resolveDiscordThreadStarterAuthor(starter) {
529
- return starter.member?.nick ?? starter.member?.displayName ?? resolveDiscordThreadStarterAuthorTag(starter.author) ?? starter.author?.username ?? starter.author?.id ?? "Unknown";
530
- }
531
- function resolveDiscordThreadStarterAuthorTag(author) {
532
- if (!author?.username || !author.discriminator) return;
533
- if (author.discriminator !== "0") return `${author.username}#${author.discriminator}`;
534
- return author.username;
535
- }
536
- function resolveDiscordThreadStarterRoleIds(member) {
537
- return Array.isArray(member?.roles) ? member.roles : void 0;
538
- }
539
- function resolveDiscordReplyTarget(opts) {
540
- if (opts.replyToMode === "off") return;
541
- const replyToId = normalizeOptionalString(opts.replyToId);
542
- if (!replyToId) return;
543
- if (opts.replyToMode === "all") return replyToId;
544
- return opts.hasReplied ? void 0 : replyToId;
545
- }
546
- function sanitizeDiscordThreadName(rawName, fallbackId) {
547
- return truncateUtf16Safe(truncateUtf16Safe(rawName.replace(/<@!?\d+>/g, "").replace(/<@&\d+>/g, "").replace(/<#\d+>/g, "").replace(/\s+/g, " ").trim() || `Thread ${fallbackId}`, 80), 100) || `Thread ${fallbackId}`;
548
- }
549
- function resolveDiscordReplyDeliveryPlan(params) {
550
- const originalReplyTarget = params.replyTarget;
551
- let deliverTarget = originalReplyTarget;
552
- let replyTarget = originalReplyTarget;
553
- if (params.createdThreadId) {
554
- deliverTarget = `channel:${params.createdThreadId}`;
555
- replyTarget = deliverTarget;
556
- }
557
- const allowReference = deliverTarget === originalReplyTarget;
558
- const replyReference = createReplyReferencePlanner({
559
- replyToMode: allowReference ? params.replyToMode : "off",
560
- existingId: params.threadChannel ? params.messageId : void 0,
561
- startId: params.messageId,
562
- allowReference
563
- });
564
- return {
565
- deliverTarget,
566
- replyTarget,
567
- replyReference
568
- };
569
- }
570
- //#endregion
571
- //#region extensions/discord/src/monitor/threading.auto-thread.ts
572
- function resolveTrimmedDiscordMessageChannelId(params) {
573
- return (params.messageChannelId || resolveDiscordMessageChannelId({ message: params.message })).trim();
574
- }
575
- function resolveDiscordAutoThreadContext(params) {
576
- const createdThreadId = normalizeOptionalStringifiedId(params.createdThreadId) ?? "";
577
- if (!createdThreadId) return null;
578
- const messageChannelId = normalizeOptionalString(params.messageChannelId) ?? "";
579
- if (!messageChannelId) return null;
580
- const threadSessionKey = buildAgentSessionKey({
581
- agentId: params.agentId,
582
- channel: params.channel,
583
- peer: {
584
- kind: "channel",
585
- id: createdThreadId
586
- }
587
- });
588
- const parentSessionKey = buildAgentSessionKey({
589
- agentId: params.agentId,
590
- channel: params.channel,
591
- peer: {
592
- kind: "channel",
593
- id: messageChannelId
594
- }
595
- });
596
- return {
597
- createdThreadId,
598
- From: `${params.channel}:channel:${createdThreadId}`,
599
- To: `channel:${createdThreadId}`,
600
- OriginatingTo: `channel:${createdThreadId}`,
601
- SessionKey: threadSessionKey,
602
- ModelParentSessionKey: parentSessionKey,
603
- ...params.parentInheritanceEnabled === true ? { ParentSessionKey: parentSessionKey } : {}
604
- };
605
- }
606
- async function resolveDiscordAutoThreadReplyPlan(params) {
607
- const messageChannelId = resolveTrimmedDiscordMessageChannelId(params);
608
- const originalReplyTarget = `channel:${params.threadChannel?.id ?? (messageChannelId || "unknown")}`;
609
- const createdThreadId = await maybeCreateDiscordAutoThread({
610
- client: params.client,
611
- message: params.message,
612
- messageChannelId: messageChannelId || void 0,
613
- channel: params.channel,
614
- isGuildMessage: params.isGuildMessage,
615
- channelConfig: params.channelConfig,
616
- threadChannel: params.threadChannel,
617
- channelType: params.channelType,
618
- channelName: params.channelName,
619
- channelDescription: params.channelDescription,
620
- baseText: params.baseText,
621
- combinedBody: params.combinedBody,
622
- cfg: params.cfg,
623
- agentId: params.agentId
624
- });
625
- const deliveryPlan = resolveDiscordReplyDeliveryPlan({
626
- replyTarget: originalReplyTarget,
627
- replyToMode: params.replyToMode,
628
- messageId: params.message.id,
629
- threadChannel: params.threadChannel,
630
- createdThreadId
631
- });
632
- const autoThreadContext = params.isGuildMessage ? resolveDiscordAutoThreadContext({
633
- agentId: params.agentId,
634
- channel: params.channel,
635
- messageChannelId,
636
- createdThreadId,
637
- parentInheritanceEnabled: params.threadParentInheritanceEnabled
638
- }) : null;
639
- return {
640
- ...deliveryPlan,
641
- createdThreadId,
642
- autoThreadContext
643
- };
644
- }
645
- async function maybeCreateDiscordAutoThread(params) {
646
- if (!params.isGuildMessage) return;
647
- if (!params.channelConfig?.autoThread) return;
648
- if (params.threadChannel) return;
649
- if (params.channelType === discord_exports.ChannelType.GuildForum || params.channelType === discord_exports.ChannelType.GuildMedia || params.channelType === discord_exports.ChannelType.GuildVoice || params.channelType === discord_exports.ChannelType.GuildStageVoice) return;
650
- const messageChannelId = resolveTrimmedDiscordMessageChannelId(params);
651
- if (!messageChannelId) return;
652
- try {
653
- try {
654
- const existingThreadId = (await getChannelMessage(params.client.rest, messageChannelId, params.message.id))?.thread?.id;
655
- if (existingThreadId) {
656
- logVerbose(`discord: autoThread reusing existing thread ${existingThreadId} on ${messageChannelId}/${params.message.id}`);
657
- return existingThreadId;
658
- }
659
- } catch {}
660
- if (params.message.author?.bot) {
661
- logVerbose(`discord: autoThread skipped for bot-authored message ${messageChannelId}/${params.message.id}`);
662
- return;
663
- }
664
- const rawThreadSource = params.baseText || params.combinedBody || "Thread";
665
- const threadName = sanitizeDiscordThreadName(rawThreadSource, params.message.id);
666
- const archiveDuration = params.channelConfig?.autoArchiveDuration ? Number(params.channelConfig.autoArchiveDuration) : 60;
667
- const createdId = (await createThread(params.client.rest, messageChannelId, { body: {
668
- name: threadName,
669
- auto_archive_duration: archiveDuration
670
- } }, params.message.id))?.id || "";
671
- if (createdId && params.channelConfig?.autoThreadName === "generated" && params.cfg && params.agentId) {
672
- const modelRef = resolveDiscordThreadTitleModelRef({
673
- cfg: params.cfg,
674
- channel: params.channel,
675
- agentId: params.agentId,
676
- threadId: createdId,
677
- messageChannelId,
678
- channelName: params.channelName
679
- });
680
- maybeRenameDiscordAutoThread({
681
- client: params.client,
682
- threadId: createdId,
683
- currentName: threadName,
684
- fallbackId: params.message.id,
685
- sourceText: rawThreadSource,
686
- modelRef,
687
- channelName: params.channelName,
688
- channelDescription: params.channelDescription,
689
- cfg: params.cfg,
690
- agentId: params.agentId
691
- });
692
- }
693
- return createdId || void 0;
694
- } catch (err) {
695
- logVerbose(`discord: autoThread creation failed for ${messageChannelId}/${params.message.id}: ${String(err)}`);
696
- try {
697
- const existingThreadId = (await getChannelMessage(params.client.rest, messageChannelId, params.message.id))?.thread?.id || "";
698
- if (existingThreadId) {
699
- logVerbose(`discord: autoThread reusing existing thread ${existingThreadId} on ${messageChannelId}/${params.message.id}`);
700
- return existingThreadId;
701
- }
702
- } catch {}
703
- return;
704
- }
705
- }
706
- function resolveDiscordThreadTitleModelRef(params) {
707
- const channel = params.channel?.trim();
708
- if (!channel) return;
709
- const parentSessionKey = buildAgentSessionKey({
710
- agentId: params.agentId,
711
- channel,
712
- peer: {
713
- kind: "channel",
714
- id: params.messageChannelId
715
- }
716
- });
717
- const channelLabel = params.channelName?.trim();
718
- const groupChannel = channelLabel ? `#${channelLabel}` : void 0;
719
- return resolveChannelModelOverride({
720
- cfg: params.cfg,
721
- channel,
722
- groupId: params.threadId,
723
- groupChatType: "channel",
724
- groupChannel,
725
- groupSubject: groupChannel,
726
- parentSessionKey
727
- })?.model;
728
- }
729
- async function maybeRenameDiscordAutoThread(params) {
730
- try {
731
- const fallbackName = sanitizeDiscordThreadName("", params.fallbackId);
732
- const generated = await generateThreadTitle({
733
- cfg: params.cfg,
734
- agentId: params.agentId,
735
- messageText: params.sourceText,
736
- modelRef: params.modelRef,
737
- channelName: params.channelName,
738
- channelDescription: params.channelDescription
739
- });
740
- if (!generated) return;
741
- const nextName = sanitizeDiscordThreadName(generated, params.fallbackId);
742
- if (!nextName || nextName === params.currentName || nextName === fallbackName) return;
743
- await editChannel(params.client.rest, params.threadId, { body: { name: nextName } });
744
- } catch (err) {
745
- logVerbose(`discord: autoThread rename failed for ${params.threadId}: ${String(err)}`);
746
- }
747
- }
748
- //#endregion
749
- //#region extensions/discord/src/monitor/threading.ts
750
- var threading_exports = /* @__PURE__ */ __exportAll({
751
- maybeCreateDiscordAutoThread: () => maybeCreateDiscordAutoThread,
752
- resetDiscordThreadStarterCacheForTest: () => resetDiscordThreadStarterCacheForTest,
753
- resolveDiscordAutoThreadContext: () => resolveDiscordAutoThreadContext,
754
- resolveDiscordAutoThreadReplyPlan: () => resolveDiscordAutoThreadReplyPlan,
755
- resolveDiscordReplyDeliveryPlan: () => resolveDiscordReplyDeliveryPlan,
756
- resolveDiscordReplyTarget: () => resolveDiscordReplyTarget,
757
- resolveDiscordThreadChannel: () => resolveDiscordThreadChannel,
758
- resolveDiscordThreadParentInfo: () => resolveDiscordThreadParentInfo,
759
- resolveDiscordThreadStarter: () => resolveDiscordThreadStarter,
760
- sanitizeDiscordThreadName: () => sanitizeDiscordThreadName
761
- });
762
- //#endregion
763
- //#region extensions/discord/src/monitor/thread-channel-context.ts
764
- function isDiscordThreadChannelType(type) {
765
- return type === discord_exports.ChannelType.PublicThread || type === discord_exports.ChannelType.PrivateThread || type === discord_exports.ChannelType.AnnouncementThread;
766
- }
767
- function buildFetchedChannelInfo(channel) {
768
- const channelInfo = resolveDiscordChannelInfoSafe(channel);
769
- if (channelInfo.type === void 0) return null;
770
- return {
771
- type: channelInfo.type,
772
- name: channelInfo.name,
773
- topic: channelInfo.topic,
774
- parentId: channelInfo.parentId,
775
- ownerId: channelInfo.ownerId
482
+ type: channelInfo.type,
483
+ name: channelInfo.name,
484
+ topic: channelInfo.topic,
485
+ parentId: channelInfo.parentId,
486
+ ownerId: channelInfo.ownerId
776
487
  };
777
488
  }
778
489
  async function resolveDiscordThreadLikeChannelContext(params) {
@@ -1090,305 +801,106 @@ async function handleDiscordReactionEvent(params) {
1090
801
  const guildSlug = guildInfo?.slug || (data.guild?.name ? normalizeDiscordSlug(data.guild.name) : data.guild_id ?? (isGroupDm ? "group-dm" : "dm"));
1091
802
  const channelLabel = channelSlug ? `#${channelSlug}` : channelName ? `#${normalizeDiscordSlug(channelName)}` : `#${data.channel_id}`;
1092
803
  reactionBase = {
1093
- baseText: `Discord reaction ${action}: ${emojiLabel} by ${actorLabel} on ${guildSlug} ${channelLabel} msg ${data.message_id}`,
1094
- contextKey: `discord:reaction:${action}:${data.message_id}:${user.id}:${emojiLabel}`
1095
- };
1096
- return reactionBase;
1097
- };
1098
- const emitReaction = (text, parentPeerId) => {
1099
- const { contextKey } = resolveReactionBase();
1100
- enqueueRoutedSystemEvent(text, resolveAgentRoute({
1101
- cfg: params.cfg,
1102
- channel: "discord",
1103
- accountId: params.accountId,
1104
- guildId: data.guild_id ?? void 0,
1105
- memberRoleIds,
1106
- peer: {
1107
- kind: isDirectMessage ? "direct" : isGroupDm ? "group" : "channel",
1108
- id: isDirectMessage ? user.id : data.channel_id
1109
- },
1110
- parentPeer: parentPeerId ? {
1111
- kind: "channel",
1112
- id: parentPeerId
1113
- } : void 0
1114
- }), { contextKey });
1115
- };
1116
- const shouldNotifyReaction = (options) => shouldEmitDiscordReactionNotification({
1117
- mode: options.mode,
1118
- botId: botUserId,
1119
- messageAuthorId: options.messageAuthorId,
1120
- userId: user.id,
1121
- userName: user.username,
1122
- userTag: formatDiscordUserTag(user),
1123
- channelConfig: options.channelConfig,
1124
- guildInfo,
1125
- memberRoleIds,
1126
- allowNameMatching: params.allowNameMatching
1127
- });
1128
- const emitReactionWithAuthor = (message) => {
1129
- const { baseText } = resolveReactionBase();
1130
- const authorLabel = message?.author ? formatDiscordUserTag(message.author) : void 0;
1131
- const text = authorLabel ? `${baseText} from ${authorLabel}` : baseText;
1132
- emitReaction(text, parentId);
1133
- };
1134
- const resolveThreadChannelConfig = () => resolveDiscordChannelConfigWithFallback({
1135
- guildInfo,
1136
- channelId: data.channel_id,
1137
- channelName,
1138
- channelSlug,
1139
- parentId,
1140
- parentName,
1141
- parentSlug,
1142
- scope: "thread"
1143
- });
1144
- const authorizeReactionIngressForChannel = async (channelConfig) => await authorizeDiscordReactionIngress({
1145
- ...reactionIngressBase,
1146
- channelConfig
1147
- });
1148
- const resolveThreadChannelAccess = async () => {
1149
- const channelConfig = resolveThreadChannelConfig();
1150
- return {
1151
- access: await authorizeReactionIngressForChannel(channelConfig),
1152
- channelConfig
1153
- };
1154
- };
1155
- if (isThreadChannel) {
1156
- await handleDiscordThreadReactionNotification({
1157
- reactionMode,
1158
- message: data.message,
1159
- parentId,
1160
- resolveThreadChannelAccess,
1161
- shouldNotifyReaction,
1162
- resolveReactionBase,
1163
- emitReaction,
1164
- emitReactionWithAuthor
1165
- });
1166
- return;
1167
- }
1168
- const channelConfig = resolveDiscordChannelConfigWithFallback({
1169
- guildInfo,
1170
- channelId: data.channel_id,
1171
- channelName,
1172
- channelSlug,
1173
- parentId,
1174
- parentName,
1175
- parentSlug,
1176
- scope: "channel"
1177
- });
1178
- await handleDiscordChannelReactionNotification({
1179
- isGuildMessage,
1180
- reactionMode,
1181
- message: data.message,
1182
- channelConfig,
1183
- parentId,
1184
- authorizeReactionIngressForChannel,
1185
- shouldNotifyReaction,
1186
- resolveReactionBase,
1187
- emitReaction,
1188
- emitReactionWithAuthor
1189
- });
1190
- } catch (err) {
1191
- params.logger.error(danger(`discord reaction handler failed: ${String(err)}`));
1192
- }
1193
- }
1194
- //#endregion
1195
- //#region extensions/discord/src/monitor/presence-events.ts
1196
- const DISCORD_PRESENCE_GREETING_COOLDOWN_MS = 480 * 60 * 1e3;
1197
- function isDiscordOnlineStatus(status) {
1198
- return status === "online" || status === "idle" || status === "dnd";
1199
- }
1200
- function isDiscordOfflineStatus(status) {
1201
- return status === "offline";
1202
- }
1203
- function resolveDiscordOnlinePresenceEvent(params) {
1204
- const config = params.config;
1205
- const userId = params.data.user?.id?.trim();
1206
- if (!config || config.enabled === false || !userId || userId === params.botUserId || params.data.user.bot === true || !isDiscordOnlineStatus(params.data.status) || !params.availabilityKind) return null;
1207
- if (config.users !== void 0 && !config.users.includes(userId)) return null;
1208
- if (params.lastEmittedAtMs !== void 0 && params.nowMs - params.lastEmittedAtMs < 288e5) return null;
1209
- const lines = [
1210
- "Discord online-presence event:",
1211
- params.availabilityKind === "observed-offline" ? `A human member became available online after being observed offline in guild_id=${JSON.stringify(params.data.guild_id)} user_id=${JSON.stringify(userId)} status=${JSON.stringify(params.data.status)}.` : `A human member became newly visible online since the current guild snapshot in guild_id=${JSON.stringify(params.data.guild_id)} user_id=${JSON.stringify(userId)} status=${JSON.stringify(params.data.status)}. They may have come online or joined after the snapshot; do not claim an exact prior status.`,
1212
- `The authorized greeting target is channel_id=${JSON.stringify(config.channelId)}.`,
1213
- "Before greeting, retrieve relevant memory and wiki context for this immutable user_id, including a known timezone when available. Use their local time for the greeting; if their timezone is unknown, do not guess.",
1214
- "Send at most one short, natural greeting to the target channel. Do not reveal private memory. If no greeting is appropriate, stay silent."
1215
- ];
1216
- return {
1217
- channelId: config.channelId,
1218
- userId,
1219
- text: lines.join("\n")
1220
- };
1221
- }
1222
- //#endregion
1223
- //#region extensions/discord/src/monitor/presence-cooldown-store.ts
1224
- const DISCORD_PRESENCE_COOLDOWN_MAX_ENTRIES = 25e3;
1225
- function openDiscordPresenceCooldownStore() {
1226
- return getDiscordRuntime().state.openSyncKeyedStore({
1227
- namespace: "presence-greeting-cooldowns",
1228
- maxEntries: DISCORD_PRESENCE_COOLDOWN_MAX_ENTRIES,
1229
- overflowPolicy: "reject-new",
1230
- defaultTtlMs: DISCORD_PRESENCE_GREETING_COOLDOWN_MS
1231
- });
1232
- }
1233
- //#endregion
1234
- //#region extensions/discord/src/monitor/presence-emission-gate.ts
1235
- const DISCORD_PRESENCE_RECONNECT_SUPPRESS_MS = 300 * 1e3;
1236
- const DISCORD_PRESENCE_BURST_LIMIT = 8;
1237
- const DISCORD_PRESENCE_BURST_WINDOW_MS = 60 * 1e3;
1238
- function resolveDiscordPresenceGateOptions(config) {
1239
- return {
1240
- reconnectSuppressMs: config?.reconnectSuppressSeconds !== void 0 ? config.reconnectSuppressSeconds * 1e3 : DISCORD_PRESENCE_RECONNECT_SUPPRESS_MS,
1241
- burstLimit: config?.burstLimit ?? DISCORD_PRESENCE_BURST_LIMIT,
1242
- burstWindowMs: config?.burstWindowSeconds !== void 0 ? config.burstWindowSeconds * 1e3 : DISCORD_PRESENCE_BURST_WINDOW_MS
1243
- };
1244
- }
1245
- /**
1246
- * Per-account lifecycle gate for online-presence events. Reconnect state belongs to the Gateway
1247
- * session, while burst state follows the guild-owned configuration that supplies its limits.
1248
- */
1249
- var DiscordPresenceEmissionGate = class {
1250
- constructor() {
1251
- this.reconnectLogged = false;
1252
- this.burstByGuild = /* @__PURE__ */ new Map();
1253
- this.nextReservationId = 0;
1254
- }
1255
- noteGatewaySessionReset(nowMs) {
1256
- this.lastSessionResetAtMs = nowMs;
1257
- this.reconnectLogged = false;
1258
- }
1259
- evaluateReconnectWindow(nowMs, options) {
1260
- if (this.lastSessionResetAtMs !== void 0 && options.reconnectSuppressMs > 0 && nowMs - this.lastSessionResetAtMs < options.reconnectSuppressMs) {
1261
- const shouldLog = !this.reconnectLogged;
1262
- this.reconnectLogged = true;
1263
- return {
1264
- allowed: false,
1265
- reason: "reconnect-window",
1266
- shouldLog
1267
- };
1268
- }
1269
- return { allowed: true };
1270
- }
1271
- reserveBurst(guildId, nowMs, options) {
1272
- const state = this.burstByGuild.get(guildId) ?? {
1273
- reservations: [],
1274
- logged: false
1275
- };
1276
- state.reservations = state.reservations.filter((reservation) => !reservation.committed || nowMs - reservation.atMs < options.burstWindowMs);
1277
- this.burstByGuild.set(guildId, state);
1278
- if (state.reservations.length >= options.burstLimit) {
1279
- const shouldLog = !state.logged;
1280
- state.logged = true;
1281
- return {
1282
- allowed: false,
1283
- reason: state.reservations.some((reservation) => !reservation.committed) ? "burst-pending" : "burst",
1284
- shouldLog
1285
- };
1286
- }
1287
- state.logged = false;
1288
- const reservation = {
1289
- id: this.nextReservationId++,
1290
- atMs: nowMs,
1291
- committed: false
1292
- };
1293
- state.reservations.push(reservation);
1294
- return {
1295
- allowed: true,
1296
- reservation: reservation.id
1297
- };
1298
- }
1299
- commitBurst(guildId, reservation, nowMs) {
1300
- const entry = this.burstByGuild.get(guildId)?.reservations.find((candidate) => candidate.id === reservation);
1301
- if (!entry) return;
1302
- entry.atMs = nowMs;
1303
- entry.committed = true;
1304
- }
1305
- releaseBurst(guildId, reservation) {
1306
- const state = this.burstByGuild.get(guildId);
1307
- if (!state) return;
1308
- state.reservations = state.reservations.filter((candidate) => candidate.id !== reservation);
1309
- if (state.reservations.length === 0 && !state.logged) this.burstByGuild.delete(guildId);
1310
- }
1311
- };
1312
- //#endregion
1313
- //#region extensions/discord/src/monitor/presence-transition-cache.ts
1314
- const DEFAULT_PRESENCE_BASELINE_MAX_ENTRIES = 75e3;
1315
- var DiscordPresenceBaselineCache = class {
1316
- constructor(maxEntries = DEFAULT_PRESENCE_BASELINE_MAX_ENTRIES) {
1317
- this.maxEntries = maxEntries;
1318
- this.offlineByKey = /* @__PURE__ */ new Map();
1319
- this.onlineByKey = /* @__PURE__ */ new Map();
1320
- }
1321
- clear() {
1322
- this.offlineByKey.clear();
1323
- this.onlineByKey.clear();
1324
- }
1325
- clearScope(scope) {
1326
- for (const markers of [this.offlineByKey, this.onlineByKey]) for (const [key, markerScope] of markers) if (markerScope === scope) markers.delete(key);
1327
- }
1328
- isOffline(scope, key) {
1329
- return this.offlineByKey.get(key) === scope;
1330
- }
1331
- isOnline(scope, key) {
1332
- return this.onlineByKey.get(key) === scope;
1333
- }
1334
- observeOffline(scope, key) {
1335
- this.deleteMarker(this.onlineByKey, scope, key);
1336
- return this.observe(this.offlineByKey, scope, key);
1337
- }
1338
- observeOnline(scope, key) {
1339
- this.deleteMarker(this.offlineByKey, scope, key);
1340
- return this.observe(this.onlineByKey, scope, key);
1341
- }
1342
- deleteMarker(markers, scope, key) {
1343
- if (markers.get(key) === scope) markers.delete(key);
1344
- }
1345
- observe(markers, scope, key) {
1346
- markers.delete(key);
1347
- markers.set(key, scope);
1348
- let evictedScope;
1349
- while (markers.size > this.maxEntries) {
1350
- const oldestKey = markers.keys().next().value;
1351
- if (oldestKey === void 0) break;
1352
- evictedScope = markers.get(oldestKey);
1353
- markers.delete(oldestKey);
1354
- }
1355
- return evictedScope;
1356
- }
1357
- };
1358
- //#endregion
1359
- //#region extensions/discord/src/monitor/thread-session-close.ts
1360
- /**
1361
- * Closes every session entry in the store whose key contains {@link threadId}.
1362
- * The explicit lifecycle deletion archives the old transcript and guarantees
1363
- * that a later inbound message starts a fresh session in every reset mode.
1364
- */
1365
- async function closeDiscordThreadSessions(params) {
1366
- const { cfg, threadId } = params;
1367
- const normalizedThreadId = normalizeOptionalLowercaseString(threadId) ?? "";
1368
- if (!normalizedThreadId) return 0;
1369
- const segmentRe = new RegExp(`:${normalizedThreadId}(?::|$)`, "i");
1370
- function sessionKeyContainsThreadId(key) {
1371
- return segmentRe.test(key);
1372
- }
1373
- let resetCount = 0;
1374
- for (const agentId of listAgentIds(cfg)) {
1375
- const storePath = resolveStorePath(cfg.session?.store, { agentId });
1376
- for (const { sessionKey, entry } of listSessionEntries({
1377
- agentId,
1378
- storePath,
1379
- readOnly: true
1380
- })) {
1381
- if (!sessionKeyContainsThreadId(sessionKey)) continue;
1382
- if (await deleteSessionEntry({
1383
- archiveTranscript: true,
1384
- expectedSessionId: entry.sessionId ?? null,
1385
- expectedUpdatedAt: entry.updatedAt,
1386
- sessionKey,
1387
- storePath
1388
- })) resetCount += 1;
804
+ baseText: `Discord ${data.burst ? "super " : ""}reaction ${action}: ${emojiLabel} by ${actorLabel} on ${guildSlug} ${channelLabel} msg ${data.message_id}`,
805
+ contextKey: `discord:reaction:${action}:${data.message_id}:${user.id}:${emojiLabel}${data.burst ? ":burst" : ""}`
806
+ };
807
+ return reactionBase;
808
+ };
809
+ const emitReaction = (text, parentPeerId) => {
810
+ const { contextKey } = resolveReactionBase();
811
+ enqueueRoutedSystemEvent(text, resolveAgentRoute({
812
+ cfg: params.cfg,
813
+ channel: "discord",
814
+ accountId: params.accountId,
815
+ guildId: data.guild_id ?? void 0,
816
+ memberRoleIds,
817
+ peer: {
818
+ kind: isDirectMessage ? "direct" : isGroupDm ? "group" : "channel",
819
+ id: isDirectMessage ? user.id : data.channel_id
820
+ },
821
+ parentPeer: parentPeerId ? {
822
+ kind: "channel",
823
+ id: parentPeerId
824
+ } : void 0
825
+ }), { contextKey });
826
+ };
827
+ const shouldNotifyReaction = (options) => shouldEmitDiscordReactionNotification({
828
+ mode: options.mode,
829
+ botId: botUserId,
830
+ messageAuthorId: options.messageAuthorId,
831
+ userId: user.id,
832
+ userName: user.username,
833
+ userTag: formatDiscordUserTag(user),
834
+ channelConfig: options.channelConfig,
835
+ guildInfo,
836
+ memberRoleIds,
837
+ allowNameMatching: params.allowNameMatching
838
+ });
839
+ const emitReactionWithAuthor = (message) => {
840
+ const { baseText } = resolveReactionBase();
841
+ const authorLabel = message?.author ? formatDiscordUserTag(message.author) : void 0;
842
+ const text = authorLabel ? `${baseText} from ${authorLabel}` : baseText;
843
+ emitReaction(text, parentId);
844
+ };
845
+ const resolveThreadChannelConfig = () => resolveDiscordChannelConfigWithFallback({
846
+ guildInfo,
847
+ channelId: data.channel_id,
848
+ channelName,
849
+ channelSlug,
850
+ parentId,
851
+ parentName,
852
+ parentSlug,
853
+ scope: "thread"
854
+ });
855
+ const authorizeReactionIngressForChannel = async (channelConfig) => await authorizeDiscordReactionIngress({
856
+ ...reactionIngressBase,
857
+ channelConfig
858
+ });
859
+ const resolveThreadChannelAccess = async () => {
860
+ const channelConfig = resolveThreadChannelConfig();
861
+ return {
862
+ access: await authorizeReactionIngressForChannel(channelConfig),
863
+ channelConfig
864
+ };
865
+ };
866
+ if (isThreadChannel) {
867
+ await handleDiscordThreadReactionNotification({
868
+ reactionMode,
869
+ message: data.message,
870
+ parentId,
871
+ resolveThreadChannelAccess,
872
+ shouldNotifyReaction,
873
+ resolveReactionBase,
874
+ emitReaction,
875
+ emitReactionWithAuthor
876
+ });
877
+ return;
1389
878
  }
879
+ const channelConfig = resolveDiscordChannelConfigWithFallback({
880
+ guildInfo,
881
+ channelId: data.channel_id,
882
+ channelName,
883
+ channelSlug,
884
+ parentId,
885
+ parentName,
886
+ parentSlug,
887
+ scope: "channel"
888
+ });
889
+ await handleDiscordChannelReactionNotification({
890
+ isGuildMessage,
891
+ reactionMode,
892
+ message: data.message,
893
+ channelConfig,
894
+ parentId,
895
+ authorizeReactionIngressForChannel,
896
+ shouldNotifyReaction,
897
+ resolveReactionBase,
898
+ emitReaction,
899
+ emitReactionWithAuthor
900
+ });
901
+ } catch (err) {
902
+ params.logger.error(danger(`discord reaction handler failed: ${String(err)}`));
1390
903
  }
1391
- return resetCount;
1392
904
  }
1393
905
  //#endregion
1394
906
  //#region extensions/discord/src/monitor/listeners.ts
@@ -1815,11 +1327,16 @@ function isDiscordUnknownInteraction(error) {
1815
1327
  if (/Unknown interaction/i.test(err.rawBody?.message ?? "")) return true;
1816
1328
  return false;
1817
1329
  }
1330
+ function resolveDiscordInteractionMessageParts(payload) {
1331
+ const { components, embeds } = payload.channelData?.discord ?? {};
1332
+ return {
1333
+ components: Array.isArray(components) && components.length > 0 ? components : void 0,
1334
+ embeds: Array.isArray(embeds) && embeds.length > 0 ? embeds : void 0
1335
+ };
1336
+ }
1818
1337
  function hasRenderableReplyPayload(payload) {
1819
- if (resolveSendableOutboundReplyParts(payload).hasContent) return true;
1820
- const discordData = payload.channelData?.discord;
1821
- if (Array.isArray(discordData?.components) && discordData.components.length > 0) return true;
1822
- return false;
1338
+ const { components, embeds } = resolveDiscordInteractionMessageParts(payload);
1339
+ return resolveSendableOutboundReplyParts(payload).hasContent || Boolean(components || embeds);
1823
1340
  }
1824
1341
  async function safeDiscordInteractionCall(label, fn) {
1825
1342
  try {
@@ -1839,33 +1356,25 @@ async function settleDiscordInteractionWithoutVisibleReply(interaction) {
1839
1356
  async function deliverDiscordInteractionReply(params) {
1840
1357
  const { interaction, payload, textLimit, maxLinesPerMessage, preferFollowUp, chunkMode } = params;
1841
1358
  const reply = resolveSendableOutboundReplyParts(payload);
1842
- const discordData = payload.channelData?.discord;
1843
- let firstMessageComponents = Array.isArray(discordData?.components) && discordData.components.length > 0 ? discordData.components : void 0;
1359
+ let { components: firstMessageComponents, embeds: firstMessageEmbeds } = resolveDiscordInteractionMessageParts(payload);
1844
1360
  let payloadDelivered = false;
1845
- const sendMessage = async (content, files, components) => {
1846
- const contentPayload = content ? { content } : {};
1847
- const payloadLocal = files && files.length > 0 ? {
1848
- ...contentPayload,
1361
+ const sendMessage = async (content, files, components, embeds) => {
1362
+ const hasV2 = hasDiscordV2Components(components);
1363
+ const payloadLocal = {
1364
+ ...content && !hasV2 ? { content } : {},
1849
1365
  ...components ? { components } : {},
1366
+ ...embeds && !hasV2 ? { embeds } : {},
1850
1367
  ...params.responseEphemeral !== void 0 ? { ephemeral: params.responseEphemeral } : {},
1851
- files
1852
- } : {
1853
- ...contentPayload,
1854
- ...components ? { components } : {},
1855
- ...params.responseEphemeral !== void 0 ? { ephemeral: params.responseEphemeral } : {}
1368
+ ...files?.length ? { files } : {}
1856
1369
  };
1857
1370
  let result;
1858
1371
  try {
1859
1372
  result = await safeDiscordInteractionCall("interaction send", async () => {
1860
- if (!preferFollowUp && !payloadDelivered) {
1861
- await interaction.reply(payloadLocal);
1862
- payloadDelivered = true;
1863
- firstMessageComponents = void 0;
1864
- return;
1865
- }
1866
- await interaction.followUp(payloadLocal);
1373
+ if (!preferFollowUp && !payloadDelivered) await interaction.reply(payloadLocal);
1374
+ else await interaction.followUp(payloadLocal);
1867
1375
  payloadDelivered = true;
1868
1376
  firstMessageComponents = void 0;
1377
+ firstMessageEmbeds = void 0;
1869
1378
  });
1870
1379
  } catch (error) {
1871
1380
  if (!payloadDelivered) throw error;
@@ -1890,99 +1399,27 @@ async function deliverDiscordInteractionReply(params) {
1890
1399
  maxLines: maxLinesPerMessage,
1891
1400
  chunkMode
1892
1401
  }));
1893
- await sendMessage(chunks[0] ?? "", media, firstMessageComponents);
1402
+ await sendMessage(chunks[0] ?? "", media, firstMessageComponents, firstMessageEmbeds);
1894
1403
  for (const chunk of chunks.slice(1)) {
1895
1404
  if (!chunk.trim()) continue;
1896
1405
  await sendMessage(chunk);
1897
1406
  }
1898
1407
  return payloadDelivered;
1899
1408
  }
1900
- if (!reply.hasText && !firstMessageComponents) return false;
1901
- let chunks = reply.text || firstMessageComponents ? resolveTextChunksWithFallback(reply.text, chunkDiscordTextWithMode(reply.text, {
1409
+ if (!reply.hasText && !firstMessageComponents && !firstMessageEmbeds) return false;
1410
+ const chunks = resolveTextChunksWithFallback(reply.text, chunkDiscordTextWithMode(reply.text, {
1902
1411
  maxChars: textLimit,
1903
1412
  maxLines: maxLinesPerMessage,
1904
1413
  chunkMode
1905
- })) : [];
1906
- if (chunks.length === 0 && firstMessageComponents) chunks = [""];
1414
+ }));
1415
+ if (chunks.length === 0) chunks.push("");
1907
1416
  for (const chunk of chunks) {
1908
- if (!chunk.trim() && !firstMessageComponents) continue;
1909
- await sendMessage(chunk, void 0, firstMessageComponents);
1417
+ if (!chunk.trim() && !firstMessageComponents && !firstMessageEmbeds) continue;
1418
+ await sendMessage(chunk, void 0, firstMessageComponents, firstMessageEmbeds);
1910
1419
  }
1911
1420
  return payloadDelivered;
1912
1421
  }
1913
1422
  //#endregion
1914
- //#region extensions/discord/src/monitor/route-resolution.ts
1915
- function buildDiscordRoutePeer(params) {
1916
- return {
1917
- kind: params.isDirectMessage ? "direct" : params.isGroupDm ? "group" : "channel",
1918
- id: params.isDirectMessage ? params.directUserId?.trim() || params.conversationId : params.conversationId
1919
- };
1920
- }
1921
- function resolveDiscordConversationRoute(params) {
1922
- return resolveAgentRoute({
1923
- cfg: params.cfg,
1924
- channel: "discord",
1925
- accountId: params.accountId,
1926
- guildId: params.guildId ?? void 0,
1927
- memberRoleIds: params.memberRoleIds,
1928
- peer: params.peer,
1929
- parentPeer: params.parentConversationId ? {
1930
- kind: "channel",
1931
- id: params.parentConversationId
1932
- } : void 0
1933
- });
1934
- }
1935
- function resolveDiscordBoundConversationRoute(params) {
1936
- return resolveDiscordEffectiveRoute({
1937
- route: resolveDiscordConversationRoute({
1938
- cfg: params.cfg,
1939
- accountId: params.accountId,
1940
- guildId: params.guildId,
1941
- memberRoleIds: params.memberRoleIds,
1942
- peer: buildDiscordRoutePeer({
1943
- isDirectMessage: params.isDirectMessage,
1944
- isGroupDm: params.isGroupDm,
1945
- directUserId: params.directUserId,
1946
- conversationId: params.conversationId
1947
- }),
1948
- parentConversationId: params.parentConversationId
1949
- }),
1950
- boundSessionKey: params.boundSessionKey,
1951
- configuredRoute: params.configuredRoute,
1952
- matchedBy: params.matchedBy
1953
- });
1954
- }
1955
- function resolveDiscordEffectiveRoute(params) {
1956
- const boundSessionKey = params.boundSessionKey?.trim();
1957
- if (!boundSessionKey) return params.configuredRoute?.route ?? params.route;
1958
- return {
1959
- ...params.route,
1960
- sessionKey: boundSessionKey,
1961
- agentId: resolveAgentIdFromSessionKey(boundSessionKey),
1962
- lastRoutePolicy: deriveLastRoutePolicy({
1963
- sessionKey: boundSessionKey,
1964
- mainSessionKey: params.route.mainSessionKey
1965
- }),
1966
- ...params.matchedBy ? { matchedBy: params.matchedBy } : {}
1967
- };
1968
- }
1969
- function hasExplicitRuntimeBindingIntent(record) {
1970
- if (record.targetKind === "subagent") return true;
1971
- if (isAcpSessionKey(record.targetSessionKey) || isSubagentSessionKey(record.targetSessionKey)) return true;
1972
- const metadata = record.metadata;
1973
- if (!metadata || typeof metadata !== "object") return false;
1974
- return typeof metadata.boundBy === "string" || typeof metadata.label === "string" || typeof metadata.threadName === "string" || metadata.pluginBindingOwner === "plugin";
1975
- }
1976
- function shouldIgnoreStaleDiscordRouteBinding(params) {
1977
- const bindingRecord = params.bindingRecord;
1978
- const boundSessionKey = bindingRecord?.targetSessionKey?.trim();
1979
- if (!bindingRecord || !boundSessionKey || hasExplicitRuntimeBindingIntent(bindingRecord)) return false;
1980
- const bound = parseAgentSessionKey(boundSessionKey);
1981
- const routed = parseAgentSessionKey(params.route.sessionKey);
1982
- if (!bound || !routed || bound.rest !== routed.rest) return false;
1983
- return bound.agentId !== params.route.agentId;
1984
- }
1985
- //#endregion
1986
1423
  //#region extensions/discord/src/monitor/native-command-route.ts
1987
1424
  async function resolveDiscordNativeInteractionRouteState(params) {
1988
1425
  const route = resolveDiscordBoundConversationRoute({
@@ -2050,6 +1487,7 @@ async function dispatchDiscordNativeAgentReply(params) {
2050
1487
  sessionKey: params.ctxPayload.SessionKey ?? params.effectiveRoute.sessionKey
2051
1488
  },
2052
1489
  ctxPayload: params.ctxPayload,
1490
+ dispatchReplyFromConfig: params.dispatchReplyFromConfig,
2053
1491
  delivery: {
2054
1492
  deliver: async (payload) => {
2055
1493
  if (params.suppressReplies) return {
@@ -2099,28 +1537,171 @@ async function dispatchDiscordNativeAgentReply(params) {
2099
1537
  disableBlockStreaming: typeof blockStreamingEnabled === "boolean" ? !blockStreamingEnabled : void 0
2100
1538
  }
2101
1539
  });
2102
- const deliberateSilentTerminalReply = turnResult.dispatched && turnResult.dispatchResult.deliberateSilentTerminalReply === true;
2103
- const dispatchResult = {
2104
- dispatched: turnResult.dispatched,
2105
- ...hiddenFinalReply ? { hiddenFinalReply } : {}
1540
+ const shouldSettleWithoutVisibleReply = params.suppressReplies || finalReplyOutcome === "suppressed" || turnResult.dispatched && (turnResult.dispatchResult.deliberateSilentTerminalReply === true || turnResult.dispatchResult.deferredToActiveRun !== void 0);
1541
+ const dispatchResult = {
1542
+ dispatched: turnResult.dispatched,
1543
+ ...hiddenFinalReply ? { hiddenFinalReply } : {}
1544
+ };
1545
+ if (!didReply && shouldSettleWithoutVisibleReply) {
1546
+ await settleDiscordInteractionWithoutVisibleReply(params.interaction);
1547
+ return dispatchResult;
1548
+ }
1549
+ if (didReply || turnResult.dispatched && hasVisibleInboundReplyDispatch(turnResult.dispatchResult)) return dispatchResult;
1550
+ await safeDiscordInteractionCall("interaction empty fallback", async () => {
1551
+ const payload = {
1552
+ content: DISCORD_EMPTY_VISIBLE_REPLY_WARNING,
1553
+ ephemeral: true
1554
+ };
1555
+ if (params.preferFollowUp) {
1556
+ await params.interaction.followUp(payload);
1557
+ return;
1558
+ }
1559
+ await params.interaction.reply(payload);
1560
+ });
1561
+ return dispatchResult;
1562
+ }
1563
+ //#endregion
1564
+ //#region extensions/discord/src/monitor/commands.ts
1565
+ function resolveDiscordSlashCommandConfig(raw) {
1566
+ return { ephemeral: raw?.ephemeral !== false };
1567
+ }
1568
+ //#endregion
1569
+ //#region extensions/discord/src/monitor/native-command-arg-ui.ts
1570
+ const DISCORD_COMMAND_ARG_CUSTOM_ID_KEY = "cmdarg";
1571
+ function createCommandArgsWithValue(params) {
1572
+ return { values: { [params.argName]: params.value } };
1573
+ }
1574
+ function buildDiscordCommandArgCustomId(params) {
1575
+ return [
1576
+ `${DISCORD_COMMAND_ARG_CUSTOM_ID_KEY}:command=${encodeCustomIdComponent(params.command)}`,
1577
+ `arg=${encodeCustomIdComponent(params.arg)}`,
1578
+ `value=${encodeCustomIdComponent(params.value)}`,
1579
+ `user=${encodeCustomIdComponent(params.userId)}`
1580
+ ].join(";");
1581
+ }
1582
+ function parseDiscordCommandArgData(data) {
1583
+ if (!data || typeof data !== "object") return null;
1584
+ const coerce = (value) => typeof value === "string" || typeof value === "number" ? String(value) : "";
1585
+ const rawCommand = coerce(data.command);
1586
+ const rawArg = coerce(data.arg);
1587
+ const rawValue = coerce(data.value);
1588
+ const rawUser = coerce(data.user);
1589
+ if (!rawCommand || !rawArg || !rawValue || !rawUser) return null;
1590
+ return {
1591
+ command: decodeCustomIdComponent(rawCommand),
1592
+ arg: decodeCustomIdComponent(rawArg),
1593
+ value: decodeCustomIdComponent(rawValue),
1594
+ userId: decodeCustomIdComponent(rawUser)
1595
+ };
1596
+ }
1597
+ async function handleDiscordCommandArgInteraction(params) {
1598
+ const { interaction, data, ctx } = params;
1599
+ const clearWithMessage = async (content) => await params.safeInteractionCall("command arg update", () => interaction.update({
1600
+ content,
1601
+ components: []
1602
+ }));
1603
+ const parsed = parseDiscordCommandArgData(data);
1604
+ if (!parsed) {
1605
+ await clearWithMessage("Sorry, that selection is no longer available.");
1606
+ return;
1607
+ }
1608
+ if (interaction.user?.id && interaction.user.id !== parsed.userId) {
1609
+ await params.safeInteractionCall("command arg ack", () => interaction.acknowledge());
1610
+ return;
1611
+ }
1612
+ const commandDefinition = findCommandByNativeName(parsed.command, "discord") ?? listChatCommands().find((entry) => entry.key === parsed.command);
1613
+ if (!commandDefinition) {
1614
+ await clearWithMessage("Sorry, that command is no longer available.");
1615
+ return;
1616
+ }
1617
+ if (await clearWithMessage(`⏳ Applying ${parsed.value}...`) === null) return;
1618
+ const commandArgs = createCommandArgsWithValue({
1619
+ argName: parsed.arg,
1620
+ value: parsed.value
1621
+ });
1622
+ const commandArgsWithRaw = {
1623
+ ...commandArgs,
1624
+ raw: serializeCommandArgs(commandDefinition, commandArgs)
1625
+ };
1626
+ const prompt = buildCommandTextFromArgs(commandDefinition, commandArgsWithRaw);
1627
+ await params.dispatchCommandInteraction({
1628
+ interaction,
1629
+ prompt,
1630
+ command: commandDefinition,
1631
+ commandArgs: commandArgsWithRaw,
1632
+ cfg: ctx.cfg,
1633
+ discordConfig: ctx.discordConfig,
1634
+ accountId: ctx.accountId,
1635
+ sessionPrefix: ctx.sessionPrefix,
1636
+ preferFollowUp: true,
1637
+ threadBindings: ctx.threadBindings,
1638
+ responseEphemeral: resolveDiscordSlashCommandConfig(ctx.discordConfig?.slashCommand).ephemeral,
1639
+ dispatchReplyFromConfig: ctx.dispatchReplyFromConfig,
1640
+ pluginCommandDispatch: { kind: "non-plugin" }
1641
+ });
1642
+ }
1643
+ async function runDiscordCommandArgButton(params) {
1644
+ await handleDiscordCommandArgInteraction(params);
1645
+ }
1646
+ var DiscordCommandArgButton = class extends Button {
1647
+ constructor(params) {
1648
+ super();
1649
+ this.style = ButtonStyle.Secondary;
1650
+ this.label = params.label;
1651
+ this.customId = params.customId;
1652
+ this.params = params;
1653
+ }
1654
+ async run(interaction, data) {
1655
+ await runDiscordCommandArgButton({
1656
+ ...this.params,
1657
+ interaction,
1658
+ data
1659
+ });
1660
+ }
1661
+ };
1662
+ function buildDiscordCommandArgMenu(params) {
1663
+ const { command, menu, interaction } = params;
1664
+ const commandLabel = command.nativeName ?? command.key;
1665
+ const userId = interaction.user?.id ?? "";
1666
+ const rows = chunkItems(menu.choices, 4).map((choices) => {
1667
+ return new Row(choices.map((choice) => new DiscordCommandArgButton({
1668
+ label: choice.label,
1669
+ customId: buildDiscordCommandArgCustomId({
1670
+ command: commandLabel,
1671
+ arg: menu.arg.name,
1672
+ value: choice.value,
1673
+ userId
1674
+ }),
1675
+ ctx: params.ctx,
1676
+ safeInteractionCall: params.safeInteractionCall,
1677
+ dispatchCommandInteraction: params.dispatchCommandInteraction
1678
+ })));
1679
+ });
1680
+ return {
1681
+ content: formatCommandArgMenuTitle({
1682
+ command,
1683
+ menu
1684
+ }),
1685
+ components: rows
2106
1686
  };
2107
- if (!didReply && (params.suppressReplies || finalReplyOutcome === "suppressed" || deliberateSilentTerminalReply)) {
2108
- await settleDiscordInteractionWithoutVisibleReply(params.interaction);
2109
- return dispatchResult;
1687
+ }
1688
+ var DiscordCommandArgFallbackButton = class extends Button {
1689
+ constructor(params) {
1690
+ super();
1691
+ this.params = params;
1692
+ this.label = "cmdarg";
1693
+ this.customId = "cmdarg:seed=1";
2110
1694
  }
2111
- if (didReply || turnResult.dispatched && hasVisibleInboundReplyDispatch(turnResult.dispatchResult)) return dispatchResult;
2112
- await safeDiscordInteractionCall("interaction empty fallback", async () => {
2113
- const payload = {
2114
- content: DISCORD_EMPTY_VISIBLE_REPLY_WARNING,
2115
- ephemeral: true
2116
- };
2117
- if (params.preferFollowUp) {
2118
- await params.interaction.followUp(payload);
2119
- return;
2120
- }
2121
- await params.interaction.reply(payload);
2122
- });
2123
- return dispatchResult;
1695
+ async run(interaction, data) {
1696
+ await runDiscordCommandArgButton({
1697
+ ...this.params,
1698
+ interaction,
1699
+ data
1700
+ });
1701
+ }
1702
+ };
1703
+ function createDiscordCommandArgFallbackButton$1(params) {
1704
+ return new DiscordCommandArgFallbackButton(params);
2124
1705
  }
2125
1706
  //#endregion
2126
1707
  //#region extensions/discord/src/monitor/native-interaction-channel-context.ts
@@ -2398,11 +1979,11 @@ async function resolveDiscordNativeAutocompleteAuthorized(params) {
2398
1979
  //#endregion
2399
1980
  //#region extensions/discord/src/monitor/native-command-bypass.ts
2400
1981
  function shouldBypassConfiguredAcpEnsure(commandName) {
2401
- return normalizeLowercaseStringOrEmpty(commandName) === "acp";
1982
+ return commandName.trim().toLowerCase() === "acp";
2402
1983
  }
2403
1984
  function shouldBypassConfiguredAcpGuildGuards(commandName) {
2404
- const normalized = normalizeLowercaseStringOrEmpty(commandName);
2405
- return normalized === "new" || normalized === "reset";
1985
+ const command = commandName.trim().toLowerCase();
1986
+ return command === "new" || command === "reset";
2406
1987
  }
2407
1988
  //#endregion
2408
1989
  //#region extensions/discord/src/monitor/inbound-context.ts
@@ -2465,237 +2046,61 @@ function buildDiscordNativeCommandContext(params) {
2465
2046
  channelConfig: params.channelConfig,
2466
2047
  guildInfo: params.guildInfo,
2467
2048
  sender: params.sender,
2468
- allowNameMatching: params.allowNameMatching,
2469
- isGuild: params.isGuild,
2470
- channelTopic: params.channelTopic
2471
- });
2472
- return finalizeInboundContext({
2473
- Body: params.prompt,
2474
- BodyForAgent: params.prompt,
2475
- RawBody: params.prompt,
2476
- CommandBody: params.prompt,
2477
- CommandArgs: params.commandArgs,
2478
- From: params.isDirectMessage ? `discord:${params.user.id}` : params.isGroupDm ? `discord:group:${params.channelId}` : `discord:channel:${params.channelId}`,
2479
- To: `slash:${params.user.id}`,
2480
- SessionKey: params.sessionKey,
2481
- CommandTargetSessionKey: params.commandTargetSessionKey,
2482
- AccountId: params.accountId ?? void 0,
2483
- ChatType: params.isDirectMessage ? "direct" : params.isGroupDm ? "group" : "channel",
2484
- ConversationLabel: conversationLabel,
2485
- GroupSubject: params.isGuild ? params.guildName : void 0,
2486
- GroupSpace: params.isGuild ? params.guildInfo?.id ?? params.guildInfo?.slug ?? params.guildId : void 0,
2487
- MemberRoleIds: params.memberRoleIds,
2488
- GroupSystemPrompt: groupSystemPrompt,
2489
- ChannelStructuredContext: channelStructuredContext,
2490
- OwnerAllowFrom: ownerAllowFrom,
2491
- SenderName: params.user.globalName ?? params.user.username,
2492
- SenderId: params.user.id,
2493
- SenderUsername: params.user.username,
2494
- SenderTag: params.sender.tag,
2495
- Provider: "discord",
2496
- Surface: "discord",
2497
- WasMentioned: true,
2498
- MessageSid: params.interactionId,
2499
- MessageThreadId: params.isThreadChannel ? params.channelId : void 0,
2500
- Timestamp: params.timestampMs ?? Date.now(),
2501
- CommandAuthorized: params.commandAuthorized,
2502
- CommandTurn: {
2503
- kind: "native",
2504
- source: "native",
2505
- authorized: params.commandAuthorized,
2506
- body: params.prompt
2507
- },
2508
- CommandSource: "native",
2509
- OriginatingChannel: "discord",
2510
- OriginatingTo: resolveDiscordConversationIdentity({
2511
- isDirectMessage: params.isDirectMessage,
2512
- userId: params.user.id,
2513
- channelId: params.channelId
2514
- }) ?? (params.isDirectMessage ? `user:${params.user.id}` : `channel:${params.channelId}`),
2515
- ThreadParentId: params.isThreadChannel ? params.threadParentId : void 0
2516
- });
2517
- }
2518
- //#endregion
2519
- //#region extensions/discord/src/monitor/native-command-status.ts
2520
- async function maybeDeliverDiscordDirectStatus(params) {
2521
- if (params.suppressReplies || params.commandName !== "status") return null;
2522
- const statusReply = await params.resolveDirectStatusReplyForSession({
2523
- cfg: params.cfg,
2524
- sessionKey: params.commandTargetSessionKey?.trim() || params.sessionKey,
2525
- channel: params.channel,
2526
- senderId: params.senderId,
2527
- senderIsOwner: params.senderIsOwner,
2528
- isAuthorizedSender: params.isAuthorizedSender,
2529
- isGroup: params.isGroup,
2530
- defaultGroupActivation: params.defaultGroupActivation
2531
- });
2532
- if (statusReply && hasRenderableReplyPayload(statusReply)) {
2533
- await deliverDiscordInteractionReply({
2534
- interaction: params.interaction,
2535
- payload: statusReply,
2536
- mediaLocalRoots: params.mediaLocalRoots,
2537
- textLimit: resolveTextChunkLimit(params.cfg, "discord", params.accountId, { fallbackLimit: 2e3 }),
2538
- maxLinesPerMessage: resolveDiscordMaxLinesPerMessage({
2539
- cfg: params.cfg,
2540
- discordConfig: params.discordConfig,
2541
- accountId: params.accountId
2542
- }),
2543
- preferFollowUp: params.preferFollowUp,
2544
- responseEphemeral: params.responseEphemeral,
2545
- chunkMode: resolveChunkMode(params.cfg, "discord", params.accountId)
2546
- });
2547
- return {
2548
- accepted: true,
2549
- effectiveRoute: params.effectiveRoute
2550
- };
2551
- }
2552
- await params.respond("Status unavailable.");
2553
- return {
2554
- accepted: true,
2555
- effectiveRoute: params.effectiveRoute
2556
- };
2557
- }
2558
- //#endregion
2559
- //#region extensions/discord/src/monitor/commands.ts
2560
- function resolveDiscordSlashCommandConfig(raw) {
2561
- return { ephemeral: raw?.ephemeral !== false };
2562
- }
2563
- //#endregion
2564
- //#region extensions/discord/src/monitor/native-command-arg-ui.ts
2565
- const DISCORD_COMMAND_ARG_CUSTOM_ID_KEY = "cmdarg";
2566
- function createCommandArgsWithValue(params) {
2567
- return { values: { [params.argName]: params.value } };
2568
- }
2569
- function buildDiscordCommandArgCustomId(params) {
2570
- return [
2571
- `${DISCORD_COMMAND_ARG_CUSTOM_ID_KEY}:command=${encodeCustomIdComponent(params.command)}`,
2572
- `arg=${encodeCustomIdComponent(params.arg)}`,
2573
- `value=${encodeCustomIdComponent(params.value)}`,
2574
- `user=${encodeCustomIdComponent(params.userId)}`
2575
- ].join(";");
2576
- }
2577
- function parseDiscordCommandArgData(data) {
2578
- if (!data || typeof data !== "object") return null;
2579
- const coerce = (value) => typeof value === "string" || typeof value === "number" ? String(value) : "";
2580
- const rawCommand = coerce(data.command);
2581
- const rawArg = coerce(data.arg);
2582
- const rawValue = coerce(data.value);
2583
- const rawUser = coerce(data.user);
2584
- if (!rawCommand || !rawArg || !rawValue || !rawUser) return null;
2585
- return {
2586
- command: decodeCustomIdComponent(rawCommand),
2587
- arg: decodeCustomIdComponent(rawArg),
2588
- value: decodeCustomIdComponent(rawValue),
2589
- userId: decodeCustomIdComponent(rawUser)
2590
- };
2591
- }
2592
- async function handleDiscordCommandArgInteraction(params) {
2593
- const { interaction, data, ctx } = params;
2594
- const clearWithMessage = async (content) => await params.safeInteractionCall("command arg update", () => interaction.update({
2595
- content,
2596
- components: []
2597
- }));
2598
- const parsed = parseDiscordCommandArgData(data);
2599
- if (!parsed) {
2600
- await clearWithMessage("Sorry, that selection is no longer available.");
2601
- return;
2602
- }
2603
- if (interaction.user?.id && interaction.user.id !== parsed.userId) {
2604
- await params.safeInteractionCall("command arg ack", () => interaction.acknowledge());
2605
- return;
2606
- }
2607
- const commandDefinition = findCommandByNativeName(parsed.command, "discord") ?? listChatCommands().find((entry) => entry.key === parsed.command);
2608
- if (!commandDefinition) {
2609
- await clearWithMessage("Sorry, that command is no longer available.");
2610
- return;
2611
- }
2612
- if (await clearWithMessage(`⏳ Applying ${parsed.value}...`) === null) return;
2613
- const commandArgs = createCommandArgsWithValue({
2614
- argName: parsed.arg,
2615
- value: parsed.value
2616
- });
2617
- const commandArgsWithRaw = {
2618
- ...commandArgs,
2619
- raw: serializeCommandArgs(commandDefinition, commandArgs)
2620
- };
2621
- const prompt = buildCommandTextFromArgs(commandDefinition, commandArgsWithRaw);
2622
- await params.dispatchCommandInteraction({
2623
- interaction,
2624
- prompt,
2625
- command: commandDefinition,
2626
- commandArgs: commandArgsWithRaw,
2627
- cfg: ctx.cfg,
2628
- discordConfig: ctx.discordConfig,
2629
- accountId: ctx.accountId,
2630
- sessionPrefix: ctx.sessionPrefix,
2631
- preferFollowUp: true,
2632
- threadBindings: ctx.threadBindings,
2633
- responseEphemeral: resolveDiscordSlashCommandConfig(ctx.discordConfig?.slashCommand).ephemeral,
2634
- pluginCommandDispatch: { kind: "non-plugin" }
2635
- });
2636
- }
2637
- async function runDiscordCommandArgButton(params) {
2638
- await handleDiscordCommandArgInteraction(params);
2639
- }
2640
- var DiscordCommandArgButton = class extends Button {
2641
- constructor(params) {
2642
- super();
2643
- this.style = ButtonStyle.Secondary;
2644
- this.label = params.label;
2645
- this.customId = params.customId;
2646
- this.params = params;
2647
- }
2648
- async run(interaction, data) {
2649
- await runDiscordCommandArgButton({
2650
- ...this.params,
2651
- interaction,
2652
- data
2653
- });
2654
- }
2655
- };
2656
- function buildDiscordCommandArgMenu(params) {
2657
- const { command, menu, interaction } = params;
2658
- const commandLabel = command.nativeName ?? command.key;
2659
- const userId = interaction.user?.id ?? "";
2660
- const rows = chunkItems(menu.choices, 4).map((choices) => {
2661
- return new Row(choices.map((choice) => new DiscordCommandArgButton({
2662
- label: choice.label,
2663
- customId: buildDiscordCommandArgCustomId({
2664
- command: commandLabel,
2665
- arg: menu.arg.name,
2666
- value: choice.value,
2667
- userId
2668
- }),
2669
- ctx: params.ctx,
2670
- safeInteractionCall: params.safeInteractionCall,
2671
- dispatchCommandInteraction: params.dispatchCommandInteraction
2672
- })));
2049
+ allowNameMatching: params.allowNameMatching,
2050
+ isGuild: params.isGuild,
2051
+ channelTopic: params.channelTopic
2673
2052
  });
2674
- return {
2675
- content: formatCommandArgMenuTitle({
2676
- command,
2677
- menu
2053
+ return finalizeInboundContext({
2054
+ Body: params.prompt,
2055
+ BodyForAgent: params.prompt,
2056
+ RawBody: params.prompt,
2057
+ CommandBody: params.prompt,
2058
+ CommandArgs: params.commandArgs,
2059
+ From: params.isDirectMessage ? `discord:${params.user.id}` : params.isGroupDm ? `discord:group:${params.channelId}` : `discord:channel:${params.channelId}`,
2060
+ To: `slash:${params.user.id}`,
2061
+ SessionKey: params.sessionKey,
2062
+ CommandTargetSessionKey: params.commandTargetSessionKey,
2063
+ AccountId: params.accountId ?? void 0,
2064
+ ChatType: params.isDirectMessage ? "direct" : params.isGroupDm ? "group" : "channel",
2065
+ ...buildDiscordConversationRouteContext({
2066
+ isDirectMessage: params.isDirectMessage,
2067
+ isGroupDm: params.isGroupDm,
2068
+ directUserId: params.user.id,
2069
+ conversationId: params.channelId,
2070
+ isThread: params.isThreadChannel,
2071
+ parentConversationId: params.threadParentId
2678
2072
  }),
2679
- components: rows
2680
- };
2681
- }
2682
- var DiscordCommandArgFallbackButton = class extends Button {
2683
- constructor(params) {
2684
- super();
2685
- this.params = params;
2686
- this.label = "cmdarg";
2687
- this.customId = "cmdarg:seed=1";
2688
- }
2689
- async run(interaction, data) {
2690
- await runDiscordCommandArgButton({
2691
- ...this.params,
2692
- interaction,
2693
- data
2694
- });
2695
- }
2696
- };
2697
- function createDiscordCommandArgFallbackButton$1(params) {
2698
- return new DiscordCommandArgFallbackButton(params);
2073
+ ConversationLabel: conversationLabel,
2074
+ GroupSubject: params.isGuild ? params.guildName : void 0,
2075
+ GroupSpace: params.isGuild ? params.guildInfo?.id ?? params.guildInfo?.slug ?? params.guildId : void 0,
2076
+ MemberRoleIds: params.memberRoleIds,
2077
+ GroupSystemPrompt: groupSystemPrompt,
2078
+ ChannelStructuredContext: channelStructuredContext,
2079
+ OwnerAllowFrom: ownerAllowFrom,
2080
+ SenderName: params.user.globalName ?? params.user.username,
2081
+ SenderId: params.user.id,
2082
+ SenderUsername: params.user.username,
2083
+ SenderTag: params.sender.tag,
2084
+ Provider: "discord",
2085
+ Surface: "discord",
2086
+ WasMentioned: true,
2087
+ MessageSid: params.interactionId,
2088
+ Timestamp: params.timestampMs ?? Date.now(),
2089
+ CommandAuthorized: params.commandAuthorized,
2090
+ CommandTurn: {
2091
+ kind: "native",
2092
+ source: "native",
2093
+ authorized: params.commandAuthorized,
2094
+ body: params.prompt
2095
+ },
2096
+ CommandSource: "native",
2097
+ OriginatingChannel: "discord",
2098
+ OriginatingTo: resolveDiscordConversationIdentity({
2099
+ isDirectMessage: params.isDirectMessage,
2100
+ userId: params.user.id,
2101
+ channelId: params.channelId
2102
+ }) ?? (params.isDirectMessage ? `user:${params.user.id}` : `channel:${params.channelId}`)
2103
+ });
2699
2104
  }
2700
2105
  //#endregion
2701
2106
  //#region extensions/discord/src/monitor/model-picker-preferences.ts
@@ -2876,8 +2281,8 @@ function paginateItems(params) {
2876
2281
  };
2877
2282
  }
2878
2283
  async function loadDiscordModelPickerData(cfg, agentId) {
2879
- const { buildModelsProviderData } = await loadModelsProviderRuntime();
2880
- return buildModelsProviderData(cfg, agentId);
2284
+ const { buildPreparedModelsProviderData } = await loadModelsProviderRuntime();
2285
+ return buildPreparedModelsProviderData(cfg, agentId);
2881
2286
  }
2882
2287
  function buildDiscordModelPickerCustomId(params) {
2883
2288
  const userId = params.userId.trim();
@@ -3694,6 +3099,7 @@ async function applyDiscordModelPickerSelection(params) {
3694
3099
  preferFollowUp: true,
3695
3100
  threadBindings: params.threadBindings,
3696
3101
  suppressReplies: true,
3102
+ dispatchReplyFromConfig: params.dispatchReplyFromConfig,
3697
3103
  pluginCommandDispatch: { kind: "non-plugin" }
3698
3104
  }), 12e3);
3699
3105
  if (!dispatchResult.accepted) return {
@@ -4351,6 +3757,7 @@ async function handleDiscordModelPickerInteraction(params) {
4351
3757
  accountId: ctx.accountId,
4352
3758
  sessionPrefix: ctx.sessionPrefix,
4353
3759
  threadBindings: ctx.threadBindings,
3760
+ dispatchReplyFromConfig: ctx.dispatchReplyFromConfig,
4354
3761
  route,
4355
3762
  resolvedModelRef,
4356
3763
  selectedRuntime,
@@ -4414,6 +3821,46 @@ function createDiscordModelPickerFallbackSelect$1(params) {
4414
3821
  return new DiscordModelPickerFallbackSelect(params);
4415
3822
  }
4416
3823
  //#endregion
3824
+ //#region extensions/discord/src/monitor/native-command-status.ts
3825
+ async function maybeDeliverDiscordDirectStatus(params) {
3826
+ if (params.suppressReplies || params.commandName !== "status") return null;
3827
+ const statusReply = await params.resolveDirectStatusReplyForSession({
3828
+ cfg: params.cfg,
3829
+ sessionKey: params.commandTargetSessionKey?.trim() || params.sessionKey,
3830
+ channel: params.channel,
3831
+ senderId: params.senderId,
3832
+ senderIsOwner: params.senderIsOwner,
3833
+ isAuthorizedSender: params.isAuthorizedSender,
3834
+ isGroup: params.isGroup,
3835
+ defaultGroupActivation: params.defaultGroupActivation
3836
+ });
3837
+ if (statusReply && hasRenderableReplyPayload(statusReply)) {
3838
+ await deliverDiscordInteractionReply({
3839
+ interaction: params.interaction,
3840
+ payload: statusReply,
3841
+ mediaLocalRoots: params.mediaLocalRoots,
3842
+ textLimit: resolveTextChunkLimit(params.cfg, "discord", params.accountId, { fallbackLimit: 2e3 }),
3843
+ maxLinesPerMessage: resolveDiscordMaxLinesPerMessage({
3844
+ cfg: params.cfg,
3845
+ discordConfig: params.discordConfig,
3846
+ accountId: params.accountId
3847
+ }),
3848
+ preferFollowUp: params.preferFollowUp,
3849
+ responseEphemeral: params.responseEphemeral,
3850
+ chunkMode: resolveChunkMode(params.cfg, "discord", params.accountId)
3851
+ });
3852
+ return {
3853
+ accepted: true,
3854
+ effectiveRoute: params.effectiveRoute
3855
+ };
3856
+ }
3857
+ await params.respond("Status unavailable.");
3858
+ return {
3859
+ accepted: true,
3860
+ effectiveRoute: params.effectiveRoute
3861
+ };
3862
+ }
3863
+ //#endregion
4417
3864
  //#region extensions/discord/src/monitor/native-command.args.ts
4418
3865
  function readDiscordCommandArgs(interaction, definitions) {
4419
3866
  if (!definitions || definitions.length === 0) return;
@@ -4446,7 +3893,7 @@ const DISCORD_COMMAND_DESCRIPTION_MAX = 100;
4446
3893
  function truncateDiscordCommandDescription(params) {
4447
3894
  const { value, label } = params;
4448
3895
  if (value.length <= DISCORD_COMMAND_DESCRIPTION_MAX) return value;
4449
- log$1.warn(`discord: truncating native command description (${label}) from ${value.length} to ${DISCORD_COMMAND_DESCRIPTION_MAX}: ${JSON.stringify(value)}`);
3896
+ log$1.debug(`discord: truncating native command description (${label}) from ${value.length} to ${DISCORD_COMMAND_DESCRIPTION_MAX}: ${JSON.stringify(value)}`);
4450
3897
  return truncateUtf16Safe(value, DISCORD_COMMAND_DESCRIPTION_MAX);
4451
3898
  }
4452
3899
  function truncateDiscordCommandDescriptionLocalizations(params) {
@@ -4543,7 +3990,7 @@ function buildDiscordCommandOptions(params) {
4543
3990
  const log = createSubsystemLogger("discord/native-command");
4544
3991
  const NON_PLUGIN_COMMAND_DISPATCH = Object.freeze({ kind: "non-plugin" });
4545
3992
  function createDiscordNativeCommand(params) {
4546
- const { command, cfg, discordConfig, accountId, sessionPrefix, ephemeralDefault, threadBindings } = params;
3993
+ const { command, cfg, discordConfig, accountId, sessionPrefix, ephemeralDefault, threadBindings, dispatchReplyFromConfig } = params;
4547
3994
  const fallbackCommandDefinition = createNativeCommandDefinition(command);
4548
3995
  const pluginCommandCandidate = "prepareDispatch" in command ? command : void 0;
4549
3996
  const commandDefinition = pluginCommandCandidate ? fallbackCommandDefinition : findCommandByNativeName$1(command.name, "discord", { includeBundledChannelFallback: false }) ?? fallbackCommandDefinition;
@@ -4610,13 +4057,14 @@ function createDiscordNativeCommand(params) {
4610
4057
  preferFollowUp: true,
4611
4058
  threadBindings,
4612
4059
  responseEphemeral: ephemeralDefault,
4060
+ dispatchReplyFromConfig,
4613
4061
  pluginCommandDispatch: preparedPluginCommand ?? NON_PLUGIN_COMMAND_DISPATCH
4614
4062
  });
4615
4063
  }
4616
4064
  }();
4617
4065
  }
4618
4066
  async function dispatchDiscordCommandInteraction(params) {
4619
- const { interaction, prompt, command, commandArgs, cfg: inputConfig, discordConfig, accountId, sessionPrefix, preferFollowUp, threadBindings, responseEphemeral, suppressReplies } = params;
4067
+ const { interaction, prompt, command, commandArgs, cfg: inputConfig, discordConfig, accountId, sessionPrefix, preferFollowUp, threadBindings, responseEphemeral, suppressReplies, dispatchReplyFromConfig } = params;
4620
4068
  const cfg = getRuntimeConfigSnapshot() ?? inputConfig;
4621
4069
  const commandName = command.nativeName ?? command.key;
4622
4070
  const respond = async (content, options) => {
@@ -4854,7 +4302,8 @@ async function dispatchDiscordCommandInteraction(params) {
4854
4302
  discordConfig,
4855
4303
  accountId,
4856
4304
  sessionPrefix,
4857
- threadBindings
4305
+ threadBindings,
4306
+ dispatchReplyFromConfig
4858
4307
  },
4859
4308
  safeInteractionCall: safeDiscordInteractionCall,
4860
4309
  dispatchCommandInteraction: dispatchDiscordCommandInteraction
@@ -5041,6 +4490,7 @@ async function dispatchDiscordCommandInteraction(params) {
5041
4490
  preferFollowUp,
5042
4491
  responseEphemeral,
5043
4492
  suppressReplies,
4493
+ dispatchReplyFromConfig,
5044
4494
  log,
5045
4495
  pluginCommandDispatch: params.pluginCommandDispatch
5046
4496
  });
@@ -5097,7 +4547,7 @@ function canResumeAfterGatewayClose(code) {
5097
4547
  //#region extensions/discord/src/internal/gateway-identify-limiter.ts
5098
4548
  const IDENTIFY_WINDOW_MS = 5e3;
5099
4549
  function normalizeMaxConcurrency(value) {
5100
- const parsed = parseFiniteNumber$1(value);
4550
+ const parsed = parseFiniteNumber(value);
5101
4551
  return parsed === void 0 ? 1 : Math.max(1, Math.floor(parsed));
5102
4552
  }
5103
4553
  var GatewayIdentifyLimiter = class {
@@ -5305,7 +4755,7 @@ var DiscordGatewayVoiceStateCache = class {
5305
4755
  }
5306
4756
  listVoiceChannelStates(guildId, channelId) {
5307
4757
  const states = this.statesByGuild.get(guildId);
5308
- if (!states) return [];
4758
+ if (!states) return null;
5309
4759
  const result = [];
5310
4760
  for (const state of states.values()) if (state.channel_id === channelId) result.push({ ...state });
5311
4761
  return result;
@@ -5375,7 +4825,10 @@ const GatewayIntents = GatewayIntentBits;
5375
4825
  const READY_STATE_OPEN = 1;
5376
4826
  const DEFAULT_GATEWAY_URL = "wss://gateway.discord.gg/";
5377
4827
  const DISCORD_GATEWAY_PAYLOAD_LIMIT_BYTES = 4096;
5378
- const DISCORD_GATEWAY_WS_CLIENT_OPTIONS = Object.freeze({ maxPayload: 16 * 1024 * 1024 });
4828
+ const DISCORD_GATEWAY_WS_CLIENT_OPTIONS = Object.freeze({
4829
+ maxPayload: 16 * 1024 * 1024,
4830
+ handshakeTimeout: 3e4
4831
+ });
5379
4832
  const INVALID_SESSION_MIN_DELAY_MS = 1e3;
5380
4833
  const INVALID_SESSION_JITTER_MS = 4e3;
5381
4834
  const RESUME_FAILURE_THRESHOLD = 3;
@@ -5415,6 +4868,9 @@ var GatewayPlugin = class extends Plugin {
5415
4868
  listVoiceChannelStates(guildId, channelId) {
5416
4869
  return this.voiceStateCache.listVoiceChannelStates(guildId, channelId);
5417
4870
  }
4871
+ async fetchGuildEmojis(guildId, fetcher) {
4872
+ return this.client ? await this.client.fetchGuildEmojis(guildId, fetcher) : await fetcher();
4873
+ }
5418
4874
  takeVoiceStateTransition(state) {
5419
4875
  return this.voiceStateCache.takeTransition(state);
5420
4876
  }
@@ -5518,7 +4974,10 @@ var GatewayPlugin = class extends Plugin {
5518
4974
  if (payload.s !== null && payload.s !== void 0) this.sequence = payload.s;
5519
4975
  switch (payload.op) {
5520
4976
  case GatewayOpcodes.Hello: {
5521
- this.startHeartbeat(payload.d.heartbeat_interval ?? 45e3);
4977
+ this.startHeartbeat(asSafeIntegerInRange(asOptionalRecord(payload.d)?.heartbeat_interval, {
4978
+ min: 1,
4979
+ max: MAX_TIMER_TIMEOUT_MS
4980
+ }) ?? 45e3);
5522
4981
  const resumeState = resume ? this.getResumeState() : null;
5523
4982
  if (resumeState) this.send({
5524
4983
  op: GatewayOpcodes.Resume,
@@ -5721,12 +5180,6 @@ var GatewayPlugin = class extends Plugin {
5721
5180
  }
5722
5181
  };
5723
5182
  //#endregion
5724
- //#region extensions/discord/src/voice/config.ts
5725
- function resolveDiscordVoiceEnabled(voice) {
5726
- if (voice?.enabled !== void 0) return voice.enabled;
5727
- return voice !== void 0;
5728
- }
5729
- //#endregion
5730
5183
  //#region extensions/discord/src/monitor/presence.ts
5731
5184
  const DEFAULT_CUSTOM_ACTIVITY_TYPE$1 = 4;
5732
5185
  const CUSTOM_STATUS_NAME$1 = "Custom Status";
@@ -6200,7 +5653,6 @@ async function fetchDiscordGatewayMetadataGuarded(input, init, options) {
6200
5653
  }
6201
5654
  //#endregion
6202
5655
  //#region extensions/discord/src/monitor/gateway-plugin.ts
6203
- const DISCORD_GATEWAY_HANDSHAKE_TIMEOUT_MS = 3e4;
6204
5656
  const DISCORD_GATEWAY_POLICY_VIOLATION_CLOSE_CODE = 1008;
6205
5657
  const DISCORD_GATEWAY_WS_RECEIVER_LIMIT_CODE = "WS_ERR_TOO_MANY_BUFFERED_PARTS";
6206
5658
  const DISCORD_GATEWAY_CLOSE_REASON_LOG_MAX_CHARS = 240;
@@ -6268,7 +5720,7 @@ function resolveDiscordGatewayIntents(params) {
6268
5720
  const intentsConfig = params?.intentsConfig;
6269
5721
  const voiceEnabled = params?.voiceEnabled;
6270
5722
  const voiceStatesEnabled = intentsConfig?.voiceStates ?? voiceEnabled ?? false;
6271
- let intents = GatewayIntents.Guilds | GatewayIntents.GuildMessages | GatewayIntents.DirectMessages | GatewayIntents.GuildMessageReactions | GatewayIntents.DirectMessageReactions;
5723
+ let intents = GatewayIntents.Guilds | GatewayIntents.GuildExpressions | GatewayIntents.GuildMessages | GatewayIntents.DirectMessages | GatewayIntents.GuildMessageReactions | GatewayIntents.DirectMessageReactions;
6272
5724
  if (intentsConfig?.messageContent !== false) intents |= GatewayIntents.MessageContent;
6273
5725
  if (voiceStatesEnabled) intents |= GatewayIntents.GuildVoiceStates;
6274
5726
  if (intentsConfig?.presence) intents |= GatewayIntents.GuildPresences;
@@ -6317,7 +5769,6 @@ function createGatewayPlugin(params) {
6317
5769
  const wsFlowId = randomUUID();
6318
5770
  const socket = new ((params.testing?.webSocketCtor) ?? ws.default)(url, {
6319
5771
  ...DISCORD_GATEWAY_WS_CLIENT_OPTIONS,
6320
- handshakeTimeout: DISCORD_GATEWAY_HANDSHAKE_TIMEOUT_MS,
6321
5772
  ...params.wsAgent ? { agent: params.wsAgent } : {}
6322
5773
  });
6323
5774
  let lastTransportError;
@@ -7045,7 +6496,7 @@ async function runDiscordGatewayLifecycle(params) {
7045
6496
  gatewayEmitter?.removeListener(DISCORD_GATEWAY_TRANSPORT_ACTIVITY_EVENT, onGatewayTransportActivity);
7046
6497
  if (params.voiceManager) {
7047
6498
  await params.voiceManager.destroy();
7048
- const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-CfFMexGt.js").then((n) => n.n);
6499
+ const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-BLEMH_yi.js").then((n) => n.n);
7049
6500
  setDiscordTranscriptsVoiceManager({
7050
6501
  accountId: params.accountId,
7051
6502
  manager: null
@@ -7060,7 +6511,7 @@ async function runDiscordGatewayLifecycle(params) {
7060
6511
  let discordVoiceRuntimePromise;
7061
6512
  let discordProviderSessionRuntimePromise;
7062
6513
  async function loadDiscordVoiceRuntime() {
7063
- const promise = discordVoiceRuntimePromise ?? import("./voice-runtime-BEdqNcST.js");
6514
+ const promise = discordVoiceRuntimePromise ?? import("./voice-runtime-DY6_o73o.js");
7064
6515
  discordVoiceRuntimePromise = promise;
7065
6516
  try {
7066
6517
  return await promise;
@@ -7070,7 +6521,7 @@ async function loadDiscordVoiceRuntime() {
7070
6521
  }
7071
6522
  }
7072
6523
  async function loadDiscordProviderSessionRuntime() {
7073
- const promise = discordProviderSessionRuntimePromise ?? import("./provider-session.runtime-BUEqD0rA.js");
6524
+ const promise = discordProviderSessionRuntimePromise ?? import("./provider-session.runtime-DSNe7r2W.js");
7074
6525
  discordProviderSessionRuntimePromise = promise;
7075
6526
  try {
7076
6527
  return await promise;
@@ -7431,102 +6882,6 @@ async function cleanupDiscordProviderStartup(params) {
7431
6882
  if (!params.lifecycleStarted) params.threadBindings.stop();
7432
6883
  }
7433
6884
  //#endregion
7434
- //#region extensions/discord/src/voice/access.ts
7435
- async function authorizeDiscordVoiceIngress(params) {
7436
- const groupPolicy = params.groupPolicy ?? resolveOpenProviderRuntimeGroupPolicy({
7437
- providerConfigPresent: params.cfg.channels?.discord !== void 0,
7438
- groupPolicy: params.discordConfig.groupPolicy,
7439
- defaultGroupPolicy: params.cfg.channels?.defaults?.groupPolicy
7440
- }).groupPolicy;
7441
- const guildInfo = resolveDiscordGuildEntry({
7442
- guild: params.guild ?? {
7443
- id: params.guildId,
7444
- ...params.guildName ? { name: params.guildName } : {}
7445
- },
7446
- guildId: params.guildId,
7447
- guildEntries: params.discordConfig.guilds
7448
- });
7449
- const channelConfig = params.channelId ? resolveDiscordChannelConfigWithFallback({
7450
- guildInfo,
7451
- channelId: params.channelId,
7452
- channelName: params.channelName,
7453
- channelSlug: params.channelSlug,
7454
- parentId: params.parentId,
7455
- parentName: params.parentName,
7456
- parentSlug: params.parentSlug,
7457
- scope: params.scope
7458
- }) : null;
7459
- if (channelConfig?.enabled === false) return {
7460
- ok: false,
7461
- message: "This channel is disabled."
7462
- };
7463
- const channelAllowlistConfigured = Boolean(guildInfo?.channels) && Object.keys(guildInfo?.channels ?? {}).length > 0;
7464
- if (!params.channelId && groupPolicy === "allowlist" && channelAllowlistConfigured) return {
7465
- ok: false,
7466
- message: `${params.channelLabel ?? "This channel"} is not allowlisted for voice commands.`
7467
- };
7468
- const channelAllowed = channelConfig ? channelConfig.allowed : !channelAllowlistConfigured;
7469
- if (!isDiscordGroupAllowedByPolicy({
7470
- groupPolicy,
7471
- guildAllowlisted: Boolean(guildInfo),
7472
- channelAllowlistConfigured,
7473
- channelAllowed
7474
- }) || channelConfig?.allowed === false) return {
7475
- ok: false,
7476
- message: `${params.channelLabel ?? "This channel"} is not allowlisted for voice commands.`
7477
- };
7478
- const { hasAccessRestrictions, memberAllowed } = resolveDiscordMemberAccessState({
7479
- channelConfig,
7480
- guildInfo,
7481
- memberRoleIds: params.memberRoleIds,
7482
- sender: params.sender,
7483
- allowNameMatching: false
7484
- });
7485
- const admissionAllowList = normalizeDiscordAllowList(params.admissionAllowFrom ?? params.discordConfig.allowFrom ?? params.discordConfig.allowFrom, [
7486
- "discord:",
7487
- "user:",
7488
- "pk:"
7489
- ]);
7490
- const admissionAllowed = admissionAllowList ? allowListMatches(admissionAllowList, params.sender, { allowNameMatching: false }) : false;
7491
- const useAccessGroups = params.useAccessGroups ?? true;
7492
- return resolveCommandAuthorizedFromAuthorizers({
7493
- useAccessGroups,
7494
- authorizers: useAccessGroups ? [{
7495
- configured: admissionAllowList != null,
7496
- allowed: admissionAllowed
7497
- }, {
7498
- configured: hasAccessRestrictions,
7499
- allowed: memberAllowed
7500
- }] : [{
7501
- configured: hasAccessRestrictions,
7502
- allowed: memberAllowed
7503
- }],
7504
- modeWhenAccessGroupsOff: "configured"
7505
- }) ? {
7506
- ok: true,
7507
- channelConfig
7508
- } : {
7509
- ok: false,
7510
- message: "You are not authorized to use this command."
7511
- };
7512
- }
7513
- //#endregion
7514
- //#region extensions/discord/src/voice/owner-access.ts
7515
- function resolveDiscordVoiceAccess(params) {
7516
- const commandOwnerAllowFrom = resolveDiscordCommandOwnerAllowFrom(params.cfg);
7517
- if (commandOwnerAllowFrom) return {
7518
- admissionAllowFrom: commandOwnerAllowFrom,
7519
- ownerAllowFrom: commandOwnerAllowFrom
7520
- };
7521
- return {
7522
- admissionAllowFrom: resolveDiscordAccountAllowFrom({
7523
- cfg: params.cfg,
7524
- accountId: params.accountId
7525
- }) ?? params.discordConfig.allowFrom ?? [],
7526
- ownerAllowFrom: []
7527
- };
7528
- }
7529
- //#endregion
7530
6885
  //#region extensions/discord/src/voice/command.ts
7531
6886
  const VOICE_CHANNEL_TYPES = [ChannelType.GuildVoice, ChannelType.GuildStageVoice];
7532
6887
  const DISCORD_VOICE_COMMAND_SPEC = {
@@ -7890,8 +7245,8 @@ function formatDiscordDeployRestOperation(err) {
7890
7245
  function formatDiscordDeployErrorMessage(err) {
7891
7246
  if (!isAbortLikeError(err)) return formatErrorMessage$1(err);
7892
7247
  const deployErr = err && typeof err === "object" ? err : {};
7893
- const requestMs = parseFiniteNumber(deployErr.deployRequestMs);
7894
- const timeoutMs = parseFiniteNumber(deployErr.deployTimeoutMs);
7248
+ const requestMs = parseFiniteNumber$1(deployErr.deployRequestMs);
7249
+ const timeoutMs = parseFiniteNumber$1(deployErr.deployTimeoutMs);
7895
7250
  const operation = formatDiscordDeployRestOperation(deployErr);
7896
7251
  if (!(requestMs !== void 0 || timeoutMs !== void 0 || deployErr.deployRestMethod !== void 0 || deployErr.deployRestPath !== void 0)) return "Discord REST request was aborted";
7897
7252
  const timing = [];
@@ -7904,8 +7259,8 @@ function formatDiscordDeployErrorMessage(err) {
7904
7259
  function resolveDiscordDeployRateLimitDetails(err) {
7905
7260
  if (!err || typeof err !== "object") return;
7906
7261
  const deployErr = err;
7907
- const status = parseStrictNonNegativeInteger(deployErr.status) ?? parseStrictNonNegativeInteger(deployErr.statusCode);
7908
- const retryAfterSeconds = parseFiniteNumber(deployErr.retryAfter) ?? parseFiniteNumber(readDiscordDeployObjectField(deployErr.rawBody, "retry_after"));
7262
+ const status = parseStrictNonNegativeInteger$1(deployErr.status) ?? parseStrictNonNegativeInteger$1(deployErr.statusCode);
7263
+ const retryAfterSeconds = parseFiniteNumber$1(deployErr.retryAfter) ?? parseFiniteNumber$1(readDiscordDeployObjectField(deployErr.rawBody, "retry_after"));
7909
7264
  if (!(err instanceof RateLimitError || status === 429 || retryAfterSeconds !== void 0)) return;
7910
7265
  const rawGlobal = readDiscordDeployObjectField(deployErr.rawBody, "global");
7911
7266
  const scope = typeof deployErr.scope === "string" && deployErr.scope.trim().length > 0 ? deployErr.scope : rawGlobal === true ? "global" : rawGlobal === false ? "route" : void 0;
@@ -7995,8 +7350,8 @@ function formatDiscordDeployErrorDetails(err) {
7995
7350
  function isDiscordDeployDailyCreateLimit(err) {
7996
7351
  if (!err || typeof err !== "object") return false;
7997
7352
  const deployErr = err;
7998
- const discordCode = parseStrictNonNegativeInteger(deployErr.discordCode);
7999
- const rawCode = parseStrictNonNegativeInteger(readDiscordDeployObjectField(deployErr.rawBody, "code"));
7353
+ const discordCode = parseStrictNonNegativeInteger$1(deployErr.discordCode);
7354
+ const rawCode = parseStrictNonNegativeInteger$1(readDiscordDeployObjectField(deployErr.rawBody, "code"));
8000
7355
  return (discordCode === 30034 || rawCode === 30034) && /daily application command creates/i.test(formatErrorMessage$1(err));
8001
7356
  }
8002
7357
  //#endregion
@@ -8214,9 +7569,6 @@ function formatUsername(user) {
8214
7569
  if (user.discriminator && user.discriminator !== "0") return `${user.username}#${user.discriminator}`;
8215
7570
  return user.username;
8216
7571
  }
8217
- function isThreadChannelType(channelType) {
8218
- return channelType === ChannelType.PublicThread || channelType === ChannelType.PrivateThread || channelType === ChannelType.AnnouncementThread;
8219
- }
8220
7572
  function resolveAgentComponentRoute(params) {
8221
7573
  return resolveAgentRoute({
8222
7574
  cfg: params.ctx.cfg,
@@ -8259,7 +7611,7 @@ function resolveDiscordChannelContext(interaction) {
8259
7611
  const channelSlug = channelName ? normalizeDiscordSlug(channelName) : "";
8260
7612
  const displayChannelSlug = channelName ? normalizeDiscordDisplaySlug(channelName) : "";
8261
7613
  const channelType = channelInfo.type;
8262
- const isThread = isThreadChannelType(channelType);
7614
+ const isThread = isDiscordThreadChannelType(channelType);
8263
7615
  let parentId;
8264
7616
  let parentName;
8265
7617
  let parentSlug = "";
@@ -8757,7 +8109,8 @@ function resolveReplyContext(message, resolveDiscordMessageText) {
8757
8109
  const referenced = message.referencedMessage;
8758
8110
  if (!referenced?.author) return null;
8759
8111
  const referencedText = resolveDiscordMessageText(referenced, { includeForwarded: true });
8760
- if (!referencedText) return null;
8112
+ const hasVisibleMedia = referenced.attachments.length > 0 || !referencedText && resolveDiscordMessageStickers(referenced).length > 0;
8113
+ if (!referencedText && !hasVisibleMedia) return null;
8761
8114
  const sender = resolveDiscordSenderIdentity({
8762
8115
  author: referenced.author,
8763
8116
  pluralkitInfo: null
@@ -8773,7 +8126,7 @@ function resolveReplyContext(message, resolveDiscordMessageText) {
8773
8126
  const roles = referenced.member?.roles;
8774
8127
  return Array.isArray(roles) ? roles.map((roleId) => roleId) : void 0;
8775
8128
  })(),
8776
- body: referencedText,
8129
+ ...referencedText ? { body: referencedText } : {},
8777
8130
  timestamp: resolveTimestampMs(referenced.timestamp)
8778
8131
  };
8779
8132
  }
@@ -8933,6 +8286,7 @@ async function deliverDiscordReply(params) {
8933
8286
  formatting: delivery.formatting,
8934
8287
  threadId: delivery.threadId,
8935
8288
  identity: delivery.identity,
8289
+ onPlatformSendDispatch: params.onPlatformSendDispatch,
8936
8290
  deps: createDiscordDeliveryDeps({
8937
8291
  cfg: params.cfg,
8938
8292
  token: params.token,
@@ -8971,7 +8325,7 @@ async function deliverDiscordReply(params) {
8971
8325
  //#endregion
8972
8326
  //#region extensions/discord/src/monitor/agent-components.dispatch.ts
8973
8327
  const loadConversationRuntime$1 = createLazyRuntimeModule(() => import("./agent-components.runtime-CEPrf2SY.js"));
8974
- const loadTypingRuntime = createLazyRuntimeModule(() => import("./typing-CS-nNVoe.js").then((n) => n.n));
8328
+ const loadTypingRuntime = createLazyRuntimeModule(() => import("./typing-B9snnwMe.js").then((n) => n.n));
8975
8329
  function buildDiscordComponentConversationLabel(params) {
8976
8330
  if (params.interactionCtx.isDirectMessage) return buildDirectLabel(params.interactionCtx.user);
8977
8331
  if (params.interactionCtx.isGroupDm) return `Group DM #${params.channelCtx.channelName ?? params.interactionCtx.channelId} channel id:${params.interactionCtx.channelId}`;
@@ -9093,6 +8447,14 @@ async function dispatchDiscordComponentEvent(params) {
9093
8447
  SessionKey: sessionKey,
9094
8448
  AccountId: accountId,
9095
8449
  ChatType: chatType,
8450
+ ...buildDiscordConversationRouteContext({
8451
+ isDirectMessage: interactionCtx.isDirectMessage,
8452
+ isGroupDm: interactionCtx.isGroupDm,
8453
+ directUserId: interactionCtx.userId,
8454
+ conversationId: interactionCtx.channelId,
8455
+ isThread: channelCtx.isThread,
8456
+ parentConversationId: channelCtx.parentId
8457
+ }),
9096
8458
  ConversationLabel: fromLabel,
9097
8459
  SenderName: senderName,
9098
8460
  SenderId: interactionCtx.userId,
@@ -9161,6 +8523,7 @@ async function dispatchDiscordComponentEvent(params) {
9161
8523
  sessionKey
9162
8524
  },
9163
8525
  ctxPayload,
8526
+ dispatchReplyFromConfig: ctx.channelRuntime?.reply?.dispatchReplyFromConfig,
9164
8527
  record: {
9165
8528
  updateLastRoute: interactionCtx.isDirectMessage ? {
9166
8529
  sessionKey: inboundLastRouteSessionKey,
@@ -9202,7 +8565,8 @@ async function dispatchDiscordComponentEvent(params) {
9202
8565
  chunkMode: resolveChunkMode(ctx.cfg, "discord", accountId),
9203
8566
  mediaLocalRoots,
9204
8567
  kind: info.kind,
9205
- bindPendingFinalDelivery: info.bindPendingFinalDelivery
8568
+ bindPendingFinalDelivery: info.bindPendingFinalDelivery,
8569
+ onPlatformSendDispatch: info.onPlatformSendDispatch
9206
8570
  });
9207
8571
  if (result.visibleReplySent) replyReference.markSent();
9208
8572
  return result;
@@ -9367,7 +8731,7 @@ async function dispatchPluginDiscordInteractiveEvent(params) {
9367
8731
  }
9368
8732
  //#endregion
9369
8733
  //#region extensions/discord/src/monitor/agent-components.handlers.ts
9370
- const loadComponentsRuntime = createLazyRuntimeModule(() => import("./outbound-session-route-DxLkrgKQ.js").then((n) => n.n));
8734
+ const loadComponentsRuntime = createLazyRuntimeModule(() => import("./components-Dk35LTf_.js").then((n) => n.t));
9371
8735
  async function handleDiscordComponentEvent(params) {
9372
8736
  const parsed = parseDiscordComponentData(params.data, resolveInteractionCustomId(params.interaction));
9373
8737
  if (!parsed) {
@@ -10013,29 +9377,24 @@ var QuestionButton = class extends Button {
10013
9377
  try {
10014
9378
  await interaction.acknowledge();
10015
9379
  } catch {}
10016
- let result;
9380
+ let content;
10017
9381
  try {
10018
- result = await this.ctx.resolveQuestion({
9382
+ content = (await this.ctx.resolveQuestion({
10019
9383
  cfg: this.ctx.cfg,
10020
9384
  questionId: callback.questionId,
10021
9385
  optionIndex: callback.optionIndex,
10022
9386
  senderId: interaction.userId,
10023
9387
  clientDisplayName: `Discord question (${this.ctx.accountId})`
10024
- });
9388
+ })).status === "answered" ? "Answer submitted." : "This question was already answered.";
10025
9389
  } catch {
10026
- try {
10027
- await interaction.followUp({
10028
- content: "Could not submit this answer.",
10029
- ephemeral: true
10030
- });
10031
- } catch {}
10032
- return;
9390
+ content = "Could not submit this answer.";
10033
9391
  }
10034
9392
  try {
10035
- await interaction.followUp({
10036
- content: result.status === "answered" ? "Answer submitted." : "This question was already answered.",
9393
+ const feedback = {
9394
+ content,
10037
9395
  ephemeral: true
10038
- });
9396
+ };
9397
+ await (interaction.responseState === "unacknowledged" ? interaction.reply(feedback) : interaction.followUp(feedback));
10039
9398
  } catch {}
10040
9399
  }
10041
9400
  };
@@ -10079,7 +9438,8 @@ function createDiscordProviderInteractionSurface(params) {
10079
9438
  accountId: params.accountId,
10080
9439
  sessionPrefix: params.sessionPrefix,
10081
9440
  ephemeralDefault: params.ephemeralDefault,
10082
- threadBindings: params.threadBindings
9441
+ threadBindings: params.threadBindings,
9442
+ dispatchReplyFromConfig: params.channelRuntime?.reply?.dispatchReplyFromConfig
10083
9443
  });
10084
9444
  });
10085
9445
  const execApprovalsConfig = params.discordConfig.execApprovals ?? {};
@@ -10124,21 +9484,24 @@ function createDiscordProviderInteractionSurface(params) {
10124
9484
  discordConfig: params.discordConfig,
10125
9485
  accountId: params.accountId,
10126
9486
  sessionPrefix: params.sessionPrefix,
10127
- threadBindings: params.threadBindings
9487
+ threadBindings: params.threadBindings,
9488
+ dispatchReplyFromConfig: params.channelRuntime?.reply?.dispatchReplyFromConfig
10128
9489
  }),
10129
9490
  createDiscordModelPickerFallbackButton({
10130
9491
  cfg: params.cfg,
10131
9492
  discordConfig: params.discordConfig,
10132
9493
  accountId: params.accountId,
10133
9494
  sessionPrefix: params.sessionPrefix,
10134
- threadBindings: params.threadBindings
9495
+ threadBindings: params.threadBindings,
9496
+ dispatchReplyFromConfig: params.channelRuntime?.reply?.dispatchReplyFromConfig
10135
9497
  }),
10136
9498
  createDiscordModelPickerFallbackSelect({
10137
9499
  cfg: params.cfg,
10138
9500
  discordConfig: params.discordConfig,
10139
9501
  accountId: params.accountId,
10140
9502
  sessionPrefix: params.sessionPrefix,
10141
- threadBindings: params.threadBindings
9503
+ threadBindings: params.threadBindings,
9504
+ dispatchReplyFromConfig: params.channelRuntime?.reply?.dispatchReplyFromConfig
10142
9505
  })
10143
9506
  ];
10144
9507
  const activityButton = createDiscordActivityButton({
@@ -10149,6 +9512,7 @@ function createDiscordProviderInteractionSurface(params) {
10149
9512
  allowFrom: params.allowFrom,
10150
9513
  dmPolicy: params.dmPolicy,
10151
9514
  runtime: params.runtime,
9515
+ channelRuntime: params.channelRuntime,
10152
9516
  token: params.token
10153
9517
  }, params.applicationId);
10154
9518
  if (activityButton) components.push(activityButton);
@@ -10217,6 +9581,170 @@ var VoicePlugin = class extends Plugin {
10217
9581
  }
10218
9582
  };
10219
9583
  //#endregion
9584
+ //#region extensions/discord/src/monitor/message-handler.preflight-channel-access.ts
9585
+ function resolveDiscordPreflightChannelAccess(params) {
9586
+ if (params.isGuildMessage && params.channelConfig?.enabled === false) {
9587
+ logDebug(`[discord-preflight] drop: channel disabled`);
9588
+ logVerbose(`Blocked discord channel ${params.messageChannelId} (channel disabled, ${params.channelMatchMeta})`);
9589
+ return {
9590
+ allowed: false,
9591
+ channelAllowlistConfigured: false,
9592
+ channelAllowed: false
9593
+ };
9594
+ }
9595
+ const groupDmAllowed = params.isGroupDm && resolveGroupDmAllow({
9596
+ channels: params.groupDmChannels,
9597
+ channelId: params.messageChannelId,
9598
+ channelName: params.displayChannelName,
9599
+ channelSlug: params.displayChannelSlug
9600
+ });
9601
+ if (params.isGroupDm && !groupDmAllowed) return {
9602
+ allowed: false,
9603
+ channelAllowlistConfigured: false,
9604
+ channelAllowed: false
9605
+ };
9606
+ const channelAllowlistConfigured = Boolean(params.guildInfo?.channels) && Object.keys(params.guildInfo?.channels ?? {}).length > 0;
9607
+ const channelAllowed = params.channelConfig?.allowed !== false;
9608
+ if (params.isGuildMessage && !isDiscordGroupAllowedByPolicy({
9609
+ groupPolicy: params.groupPolicy,
9610
+ guildAllowlisted: Boolean(params.guildInfo),
9611
+ channelAllowlistConfigured,
9612
+ channelAllowed
9613
+ })) {
9614
+ if (params.groupPolicy === "disabled") {
9615
+ logDebug(`[discord-preflight] drop: groupPolicy disabled`);
9616
+ logVerbose(`discord: drop guild message (groupPolicy: disabled, ${params.channelMatchMeta})`);
9617
+ } else if (!channelAllowlistConfigured) {
9618
+ logDebug(`[discord-preflight] drop: groupPolicy allowlist, no channel allowlist configured`);
9619
+ logVerbose(`discord: drop guild message (groupPolicy: allowlist, no channel allowlist, ${params.channelMatchMeta})`);
9620
+ } else {
9621
+ logDebug(`[discord] Ignored message from channel ${params.messageChannelId} (not in guild allowlist). Add to guilds.<guildId>.channels to enable.`);
9622
+ logVerbose(`Blocked discord channel ${params.messageChannelId} not in guild channel allowlist (groupPolicy: allowlist, ${params.channelMatchMeta})`);
9623
+ }
9624
+ return {
9625
+ allowed: false,
9626
+ channelAllowlistConfigured,
9627
+ channelAllowed
9628
+ };
9629
+ }
9630
+ if (params.isGuildMessage && params.channelConfig?.allowed === false) {
9631
+ logDebug(`[discord-preflight] drop: channelConfig.allowed===false`);
9632
+ logVerbose(`Blocked discord channel ${params.messageChannelId} not in guild channel allowlist (${params.channelMatchMeta})`);
9633
+ return {
9634
+ allowed: false,
9635
+ channelAllowlistConfigured,
9636
+ channelAllowed
9637
+ };
9638
+ }
9639
+ if (params.isGuildMessage) {
9640
+ logDebug(`[discord-preflight] pass: channel allowed`);
9641
+ logVerbose(`discord: allow channel ${params.messageChannelId} (${params.channelMatchMeta})`);
9642
+ }
9643
+ return {
9644
+ allowed: true,
9645
+ channelAllowlistConfigured,
9646
+ channelAllowed
9647
+ };
9648
+ }
9649
+ //#endregion
9650
+ //#region extensions/discord/src/monitor/listeners.guild-join.ts
9651
+ const DISCORD_GUILD_JOIN_INTRO_MAX_AGE_MS = 300 * 1e3;
9652
+ var DiscordGuildJoinIntroductionListener = class extends GuildCreateListener {
9653
+ constructor(params) {
9654
+ super();
9655
+ this.params = params;
9656
+ }
9657
+ async handle(data, client) {
9658
+ if (!("joined_at" in data) || data.unavailable || !this.params.botUserId) return;
9659
+ const joinAgeMs = Date.now() - Date.parse(data.joined_at);
9660
+ if (!Number.isFinite(joinAgeMs) || joinAgeMs < 0 || joinAgeMs > DISCORD_GUILD_JOIN_INTRO_MAX_AGE_MS) return;
9661
+ const textChannels = data.channels.filter((channel) => channel.type === ChannelType.GuildText);
9662
+ const systemChannel = textChannels.find((channel) => channel.id === data.system_channel_id);
9663
+ const candidateChannels = systemChannel ? [systemChannel, ...textChannels.filter((channel) => channel !== systemChannel)] : textChannels;
9664
+ const discordOptions = {
9665
+ cfg: this.params.cfg,
9666
+ accountId: this.params.accountId,
9667
+ rest: client.rest
9668
+ };
9669
+ const guildInfo = resolveDiscordGuildEntry({
9670
+ guild: new Guild(client, data),
9671
+ guildId: data.id,
9672
+ guildEntries: this.params.guildEntries
9673
+ });
9674
+ const guildConfigured = !this.params.guildEntries || Object.keys(this.params.guildEntries).length === 0 || Boolean(guildInfo);
9675
+ let targetChannel;
9676
+ let roomAllowed = false;
9677
+ for (const channel of candidateChannels) if (await canViewDiscordGuildChannel(data.id, channel.id, this.params.botUserId, discordOptions) && await hasAnyChannelPermissionDiscord(data.id, channel.id, this.params.botUserId, [PermissionFlagsBits.SendMessages], discordOptions)) {
9678
+ targetChannel ??= channel;
9679
+ const channelConfig = resolveDiscordChannelConfig({
9680
+ guildInfo,
9681
+ channelId: channel.id,
9682
+ channelName: channel.name,
9683
+ channelSlug: normalizeDiscordSlug(channel.name)
9684
+ });
9685
+ roomAllowed = guildConfigured && resolveDiscordPreflightChannelAccess({
9686
+ isGuildMessage: true,
9687
+ isGroupDm: false,
9688
+ groupPolicy: this.params.groupPolicy,
9689
+ messageChannelId: channel.id,
9690
+ displayChannelName: channel.name,
9691
+ displayChannelSlug: normalizeDiscordDisplaySlug(channel.name),
9692
+ guildInfo,
9693
+ channelConfig,
9694
+ channelMatchMeta: `guild=${data.id} channel=${channel.id}`
9695
+ }).allowed;
9696
+ if (roomAllowed) {
9697
+ targetChannel = channel;
9698
+ break;
9699
+ }
9700
+ }
9701
+ if (!targetChannel) {
9702
+ this.params.logger?.info("Discord guild join introduction skipped: no writable text channel", {
9703
+ guildId: data.id,
9704
+ accountId: this.params.accountId
9705
+ });
9706
+ return;
9707
+ }
9708
+ const selectedChannel = targetChannel;
9709
+ await reportChannelRoomJoin({
9710
+ cfg: this.params.cfg,
9711
+ channel: "discord",
9712
+ accountId: this.params.accountId,
9713
+ conversationId: data.id,
9714
+ deliverTo: `channel:${selectedChannel.id}`,
9715
+ route: resolveAgentRoute({
9716
+ cfg: this.params.cfg,
9717
+ channel: "discord",
9718
+ accountId: this.params.accountId,
9719
+ guildId: data.id,
9720
+ peer: {
9721
+ kind: "channel",
9722
+ id: selectedChannel.id
9723
+ }
9724
+ }),
9725
+ roomAllowed,
9726
+ resolveRoomContext: async ({ messageLimit }) => {
9727
+ const roomContext = {
9728
+ title: `#${selectedChannel.name}`,
9729
+ purpose: selectedChannel.topic ?? void 0
9730
+ };
9731
+ try {
9732
+ const messages = await readMessagesDiscord(selectedChannel.id, { limit: messageLimit }, discordOptions);
9733
+ return {
9734
+ ...roomContext,
9735
+ recentMessages: messages.toReversed().flatMap(({ author, content }) => content.trim() ? [{
9736
+ sender: author.global_name ?? author.username,
9737
+ text: content
9738
+ }] : [])
9739
+ };
9740
+ } catch {
9741
+ return roomContext;
9742
+ }
9743
+ }
9744
+ });
9745
+ }
9746
+ };
9747
+ //#endregion
10220
9748
  //#region extensions/discord/src/monitor/provider.startup.ts
10221
9749
  function registerLatePlugin(client, plugin) {
10222
9750
  plugin.registerClient?.(client);
@@ -10341,6 +9869,14 @@ async function fetchDiscordBotIdentity(params) {
10341
9869
  function registerDiscordMonitorListeners(params) {
10342
9870
  registerDiscordListener(params.client.listeners, new DiscordInteractionListener(params.logger, params.trackInboundEvent));
10343
9871
  registerDiscordListener(params.client.listeners, new DiscordMessageListener(params.messageHandler, params.logger, params.trackInboundEvent));
9872
+ registerDiscordListener(params.client.listeners, new DiscordGuildJoinIntroductionListener({
9873
+ cfg: params.cfg,
9874
+ accountId: params.accountId,
9875
+ botUserId: params.botUserId,
9876
+ groupPolicy: params.groupPolicy,
9877
+ guildEntries: params.guildEntries,
9878
+ logger: params.logger
9879
+ }));
10344
9880
  if (shouldRegisterDiscordReactionListeners(params)) {
10345
9881
  const reactionListenerOptions = {
10346
9882
  cfg: params.cfg,
@@ -10641,6 +10177,7 @@ async function monitorDiscordProvider(opts = {}) {
10641
10177
  let earlyGatewayEmitter = gatewaySupervisor?.emitter;
10642
10178
  let onEarlyGatewayDebug;
10643
10179
  try {
10180
+ const pluginChannelRuntime = opts.channelRuntime;
10644
10181
  const { commands, components, modals } = createDiscordProviderInteractionSurface({
10645
10182
  cfg,
10646
10183
  discordConfig: discordCfg,
@@ -10660,7 +10197,7 @@ async function monitorDiscordProvider(opts = {}) {
10660
10197
  allowFrom,
10661
10198
  dmPolicy,
10662
10199
  runtime,
10663
- channelRuntime: opts.channelRuntime,
10200
+ channelRuntime: pluginChannelRuntime,
10664
10201
  abortSignal: opts.abortSignal,
10665
10202
  createNativeCommand: discordProviderRuntime.createDiscordNativeCommand
10666
10203
  });
@@ -10734,7 +10271,7 @@ async function monitorDiscordProvider(opts = {}) {
10734
10271
  runtime,
10735
10272
  botUserId
10736
10273
  });
10737
- const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-CfFMexGt.js").then((n) => n.n);
10274
+ const { setDiscordTranscriptsVoiceManager } = await import("./transcripts-source-BLEMH_yi.js").then((n) => n.n);
10738
10275
  setDiscordTranscriptsVoiceManager({
10739
10276
  accountId: account.accountId,
10740
10277
  manager: voiceManager
@@ -10752,7 +10289,7 @@ async function monitorDiscordProvider(opts = {}) {
10752
10289
  accountId: account.accountId,
10753
10290
  token,
10754
10291
  runtime,
10755
- buildContext: opts.channelRuntime?.inbound.buildContext,
10292
+ buildContext: pluginChannelRuntime?.inbound.buildContext,
10756
10293
  setStatus: opts.setStatus,
10757
10294
  abortSignal: opts.abortSignal,
10758
10295
  botUserId,
@@ -10840,4 +10377,4 @@ async function monitorDiscordProvider(opts = {}) {
10840
10377
  }
10841
10378
  }
10842
10379
  //#endregion
10843
- export { sanitizeDiscordThreadName as A, handleDiscordDmCommandDecision as C, resolveDiscordAutoThreadReplyPlan as D, threading_exports as E, resolveDiscordTextCommandAccess as M, resolveDiscordReplyTarget as O, shouldIgnoreStaleDiscordRouteBinding as S, registerDiscordListener as T, buildDiscordInboundAccessContext as _, sanitizeDiscordFrontChannelReplyPayloads as a, resolveDiscordConversationRoute as b, resolveReplyContext as c, createDiscordGatewayPlugin as d, resolveDiscordGatewayIntents as f, buildDiscordGroupSystemPrompt as g, createDiscordNativeCommand as h, formatDiscordReplySkip as i, resolveDiscordDmCommandAccess as j, resolveDiscordThreadStarter as k, resolveDiscordVoiceAccess as l, resolveDiscordVoiceEnabled as m, deliverDiscordReply as n, buildDirectLabel as o, waitForDiscordGatewayPluginRegistration as p, formatDiscordReplyDeliveryFailure as r, buildGuildLabel as s, monitorDiscordProvider as t, authorizeDiscordVoiceIngress as u, createDiscordSupplementalContextAccessChecker as v, mapGatewayDispatchData as w, resolveDiscordEffectiveRoute as x, buildDiscordRoutePeer as y };
10380
+ export { resolveDiscordTextCommandAccess as S, handleDiscordDmCommandDecision as _, formatDiscordReplySkip as a, resolveFetchedDiscordThreadLikeChannelContext as b, buildGuildLabel as c, resolveDiscordGatewayIntents as d, waitForDiscordGatewayPluginRegistration as f, createDiscordSupplementalContextAccessChecker as g, buildDiscordInboundAccessContext as h, formatDiscordReplyDeliveryFailure as i, resolveReplyContext as l, buildDiscordGroupSystemPrompt as m, resolveDiscordPreflightChannelAccess as n, sanitizeDiscordFrontChannelReplyPayloads as o, createDiscordNativeCommand as p, deliverDiscordReply as r, buildDirectLabel as s, monitorDiscordProvider as t, createDiscordGatewayPlugin as u, mapGatewayDispatchData as v, resolveDiscordDmCommandAccess as x, registerDiscordListener as y };