@openclaw/discord 2026.3.13 → 2026.5.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (498) hide show
  1. package/account-inspect-api.ts +6 -0
  2. package/action-runtime-api.ts +1 -0
  3. package/api.ts +132 -0
  4. package/channel-config-api.ts +1 -0
  5. package/channel-plugin-api.ts +3 -0
  6. package/config-api.ts +4 -0
  7. package/configured-state.ts +6 -0
  8. package/contract-api.ts +21 -0
  9. package/directory-contract-api.ts +4 -0
  10. package/doctor-contract-api.ts +1 -0
  11. package/index.test.ts +13 -0
  12. package/index.ts +18 -13
  13. package/openclaw.plugin.json +3282 -1
  14. package/package.json +67 -2
  15. package/runtime-api.actions.ts +15 -0
  16. package/runtime-api.lookup.ts +22 -0
  17. package/runtime-api.monitor.ts +50 -0
  18. package/runtime-api.send.ts +79 -0
  19. package/runtime-api.threads.ts +30 -0
  20. package/runtime-api.ts +180 -0
  21. package/runtime-setter-api.ts +3 -0
  22. package/secret-contract-api.ts +4 -0
  23. package/security-audit-contract-api.ts +1 -0
  24. package/security-contract-api.ts +4 -0
  25. package/session-key-api.ts +1 -0
  26. package/setup-entry.ts +9 -0
  27. package/setup-plugin-api.ts +3 -0
  28. package/src/account-inspect.test.ts +126 -0
  29. package/src/account-inspect.ts +132 -0
  30. package/src/accounts.test.ts +247 -0
  31. package/src/accounts.ts +196 -0
  32. package/src/actions/handle-action.guild-admin.ts +411 -0
  33. package/src/actions/handle-action.test.ts +185 -0
  34. package/src/actions/handle-action.ts +332 -0
  35. package/src/actions/runtime.guild.ts +446 -0
  36. package/src/actions/runtime.messaging.messages.ts +205 -0
  37. package/src/actions/runtime.messaging.reactions.ts +67 -0
  38. package/src/actions/runtime.messaging.runtime.ts +69 -0
  39. package/src/actions/runtime.messaging.send.ts +244 -0
  40. package/src/actions/runtime.messaging.shared.ts +92 -0
  41. package/src/actions/runtime.messaging.ts +37 -0
  42. package/src/actions/runtime.moderation-shared.ts +48 -0
  43. package/src/actions/runtime.moderation.authz.test.ts +151 -0
  44. package/src/actions/runtime.moderation.ts +116 -0
  45. package/src/actions/runtime.presence.test.ts +160 -0
  46. package/src/actions/runtime.presence.ts +117 -0
  47. package/src/actions/runtime.shared.ts +83 -0
  48. package/src/actions/runtime.test.ts +1056 -0
  49. package/src/actions/runtime.ts +81 -0
  50. package/src/api-barrel.test.ts +80 -0
  51. package/src/api.test.ts +130 -0
  52. package/src/api.ts +169 -0
  53. package/src/approval-handler.runtime.test.ts +41 -0
  54. package/src/approval-handler.runtime.ts +632 -0
  55. package/src/approval-native.test.ts +330 -0
  56. package/src/approval-native.ts +219 -0
  57. package/src/approval-runtime.ts +14 -0
  58. package/src/approval-shared.ts +53 -0
  59. package/src/audit-core.ts +141 -0
  60. package/src/audit.test.ts +145 -0
  61. package/src/audit.ts +32 -0
  62. package/src/channel-actions.contract.test.ts +45 -0
  63. package/src/channel-actions.runtime.ts +1 -0
  64. package/src/channel-actions.test.ts +236 -0
  65. package/src/channel-actions.ts +198 -0
  66. package/src/channel-api.ts +28 -0
  67. package/src/channel.conversation.ts +159 -0
  68. package/src/channel.loaders.ts +47 -0
  69. package/src/channel.runtime.ts +1 -0
  70. package/src/channel.setup.ts +12 -0
  71. package/src/channel.test.ts +539 -12
  72. package/src/channel.ts +596 -430
  73. package/src/chunk.test.ts +157 -0
  74. package/src/chunk.ts +321 -0
  75. package/src/client.proxy.test.ts +176 -0
  76. package/src/client.test.ts +76 -0
  77. package/src/client.ts +139 -0
  78. package/src/component-custom-id.ts +72 -0
  79. package/src/components-registry.ts +356 -0
  80. package/src/components.builders.ts +409 -0
  81. package/src/components.modal.ts +124 -0
  82. package/src/components.parse.ts +407 -0
  83. package/src/components.test.ts +312 -0
  84. package/src/components.ts +54 -0
  85. package/src/components.types.ts +187 -0
  86. package/src/config-schema.test.ts +325 -0
  87. package/src/config-schema.ts +6 -0
  88. package/src/config-ui-hints.ts +249 -0
  89. package/src/conversation-identity.ts +58 -0
  90. package/src/delivery-retry.ts +56 -0
  91. package/src/directory-cache.ts +116 -0
  92. package/src/directory-config.ts +58 -0
  93. package/src/directory-contract.test.ts +129 -0
  94. package/src/directory-live.test.ts +126 -0
  95. package/src/directory-live.ts +135 -0
  96. package/src/doctor-contract.ts +477 -0
  97. package/src/doctor-shared.ts +5 -0
  98. package/src/doctor.test.ts +405 -0
  99. package/src/doctor.ts +340 -0
  100. package/src/draft-chunking.test.ts +64 -0
  101. package/src/draft-chunking.ts +43 -0
  102. package/src/draft-stream.test.ts +159 -0
  103. package/src/draft-stream.ts +154 -0
  104. package/src/error-body.ts +38 -0
  105. package/src/exec-approvals.test.ts +88 -0
  106. package/src/exec-approvals.ts +110 -0
  107. package/src/gateway-logging.test.ts +98 -0
  108. package/src/gateway-logging.ts +67 -0
  109. package/src/group-policy.ts +113 -0
  110. package/src/guilds.ts +29 -0
  111. package/src/inbound-context.contract.test.ts +11 -0
  112. package/src/interactive-dispatch.ts +104 -0
  113. package/src/internal/api.commands.ts +51 -0
  114. package/src/internal/api.guild.ts +164 -0
  115. package/src/internal/api.interactions.ts +53 -0
  116. package/src/internal/api.messages.ts +113 -0
  117. package/src/internal/api.reactions.ts +38 -0
  118. package/src/internal/api.test.ts +262 -0
  119. package/src/internal/api.ts +61 -0
  120. package/src/internal/api.users.ts +19 -0
  121. package/src/internal/api.webhooks.ts +13 -0
  122. package/src/internal/client.test.ts +297 -0
  123. package/src/internal/client.ts +246 -0
  124. package/src/internal/command-deploy.ts +202 -0
  125. package/src/internal/commands.ts +188 -0
  126. package/src/internal/components.base.ts +65 -0
  127. package/src/internal/components.message.ts +279 -0
  128. package/src/internal/components.modal.ts +95 -0
  129. package/src/internal/components.ts +31 -0
  130. package/src/internal/discord.ts +11 -0
  131. package/src/internal/embeds.ts +35 -0
  132. package/src/internal/entity-cache.ts +98 -0
  133. package/src/internal/event-queue.ts +162 -0
  134. package/src/internal/gateway-close-codes.ts +25 -0
  135. package/src/internal/gateway-dispatch.ts +96 -0
  136. package/src/internal/gateway-identify-limiter.ts +26 -0
  137. package/src/internal/gateway-lifecycle.ts +61 -0
  138. package/src/internal/gateway-rate-limit.ts +104 -0
  139. package/src/internal/gateway.test.ts +475 -0
  140. package/src/internal/gateway.ts +437 -0
  141. package/src/internal/interaction-dispatch.test.ts +148 -0
  142. package/src/internal/interaction-dispatch.ts +130 -0
  143. package/src/internal/interaction-options.ts +98 -0
  144. package/src/internal/interaction-response.ts +53 -0
  145. package/src/internal/interactions.test.ts +253 -0
  146. package/src/internal/interactions.ts +337 -0
  147. package/src/internal/listeners.ts +85 -0
  148. package/src/internal/live-smoke.live.test.ts +26 -0
  149. package/src/internal/modal-fields.ts +95 -0
  150. package/src/internal/payload.ts +69 -0
  151. package/src/internal/rest-body.ts +115 -0
  152. package/src/internal/rest-errors.ts +88 -0
  153. package/src/internal/rest-routes.ts +50 -0
  154. package/src/internal/rest-scheduler.ts +412 -0
  155. package/src/internal/rest.test.ts +437 -0
  156. package/src/internal/rest.ts +213 -0
  157. package/src/internal/schemas.ts +36 -0
  158. package/src/internal/structures.ts +278 -0
  159. package/src/internal/test-builders.test-support.ts +163 -0
  160. package/src/internal/voice.ts +49 -0
  161. package/src/media-detection.ts +28 -0
  162. package/src/mentions.test.ts +111 -0
  163. package/src/mentions.ts +147 -0
  164. package/src/monitor/access-groups.ts +55 -0
  165. package/src/monitor/ack-reactions.ts +70 -0
  166. package/src/monitor/acp-bind-here.integration.test.ts +211 -0
  167. package/src/monitor/agent-components-auth.ts +7 -0
  168. package/src/monitor/agent-components-context.ts +144 -0
  169. package/src/monitor/agent-components-data.ts +224 -0
  170. package/src/monitor/agent-components-dm-auth.ts +221 -0
  171. package/src/monitor/agent-components-guild-auth.ts +322 -0
  172. package/src/monitor/agent-components-helpers.runtime.ts +5 -0
  173. package/src/monitor/agent-components-helpers.ts +34 -0
  174. package/src/monitor/agent-components-reply.ts +10 -0
  175. package/src/monitor/agent-components.deps.runtime.ts +2 -0
  176. package/src/monitor/agent-components.dispatch.ts +366 -0
  177. package/src/monitor/agent-components.handlers.ts +303 -0
  178. package/src/monitor/agent-components.modal.ts +160 -0
  179. package/src/monitor/agent-components.plugin-interactive.ts +187 -0
  180. package/src/monitor/agent-components.runtime.ts +14 -0
  181. package/src/monitor/agent-components.system-controls.ts +211 -0
  182. package/src/monitor/agent-components.ts +70 -0
  183. package/src/monitor/agent-components.types.ts +57 -0
  184. package/src/monitor/agent-components.wildcard-controls.ts +168 -0
  185. package/src/monitor/agent-components.wildcard.test.ts +71 -0
  186. package/src/monitor/allow-list.ts +623 -0
  187. package/src/monitor/auto-presence.test.ts +156 -0
  188. package/src/monitor/auto-presence.ts +356 -0
  189. package/src/monitor/channel-access.ts +70 -0
  190. package/src/monitor/commands.test.ts +24 -0
  191. package/src/monitor/commands.ts +9 -0
  192. package/src/monitor/dm-command-auth.test.ts +197 -0
  193. package/src/monitor/dm-command-auth.ts +158 -0
  194. package/src/monitor/dm-command-decision.test.ts +113 -0
  195. package/src/monitor/dm-command-decision.ts +49 -0
  196. package/src/monitor/exec-approvals.test.ts +226 -0
  197. package/src/monitor/exec-approvals.ts +158 -0
  198. package/src/monitor/format.ts +45 -0
  199. package/src/monitor/gateway-handle.ts +34 -0
  200. package/src/monitor/gateway-metadata.test.ts +29 -0
  201. package/src/monitor/gateway-metadata.ts +298 -0
  202. package/src/monitor/gateway-plugin.test.ts +297 -0
  203. package/src/monitor/gateway-plugin.ts +294 -0
  204. package/src/monitor/gateway-registry.ts +37 -0
  205. package/src/monitor/gateway-supervisor.test.ts +150 -0
  206. package/src/monitor/gateway-supervisor.ts +206 -0
  207. package/src/monitor/inbound-context.test-helpers.ts +37 -0
  208. package/src/monitor/inbound-context.test.ts +106 -0
  209. package/src/monitor/inbound-context.ts +103 -0
  210. package/src/monitor/inbound-dedupe.ts +79 -0
  211. package/src/monitor/inbound-job.test.ts +203 -0
  212. package/src/monitor/inbound-job.ts +118 -0
  213. package/src/monitor/listeners.queue.ts +91 -0
  214. package/src/monitor/listeners.reactions.ts +610 -0
  215. package/src/monitor/listeners.test.ts +200 -0
  216. package/src/monitor/listeners.ts +150 -0
  217. package/src/monitor/message-channel-info.ts +96 -0
  218. package/src/monitor/message-forwarded.ts +107 -0
  219. package/src/monitor/message-handler.batch-gate.test.ts +22 -0
  220. package/src/monitor/message-handler.batch-gate.ts +19 -0
  221. package/src/monitor/message-handler.bot-self-filter.test.ts +68 -0
  222. package/src/monitor/message-handler.context.ts +393 -0
  223. package/src/monitor/message-handler.dm-preflight.ts +123 -0
  224. package/src/monitor/message-handler.draft-preview.ts +246 -0
  225. package/src/monitor/message-handler.hydration.test.ts +80 -0
  226. package/src/monitor/message-handler.hydration.ts +198 -0
  227. package/src/monitor/message-handler.inbound-context.test.ts +59 -0
  228. package/src/monitor/message-handler.module-test-helpers.ts +31 -0
  229. package/src/monitor/message-handler.preflight-channel-access.ts +86 -0
  230. package/src/monitor/message-handler.preflight-channel-context.ts +55 -0
  231. package/src/monitor/message-handler.preflight-context.ts +54 -0
  232. package/src/monitor/message-handler.preflight-helpers.ts +164 -0
  233. package/src/monitor/message-handler.preflight-history.ts +23 -0
  234. package/src/monitor/message-handler.preflight-logging.ts +36 -0
  235. package/src/monitor/message-handler.preflight-pluralkit.ts +27 -0
  236. package/src/monitor/message-handler.preflight-runtime.ts +28 -0
  237. package/src/monitor/message-handler.preflight-thread.ts +49 -0
  238. package/src/monitor/message-handler.preflight.acp-bindings.test.ts +369 -0
  239. package/src/monitor/message-handler.preflight.test-helpers.ts +111 -0
  240. package/src/monitor/message-handler.preflight.test.ts +1544 -0
  241. package/src/monitor/message-handler.preflight.ts +680 -0
  242. package/src/monitor/message-handler.preflight.types.ts +109 -0
  243. package/src/monitor/message-handler.process.test.ts +1301 -0
  244. package/src/monitor/message-handler.process.ts +684 -0
  245. package/src/monitor/message-handler.queue.test.ts +496 -0
  246. package/src/monitor/message-handler.routing-preflight.ts +112 -0
  247. package/src/monitor/message-handler.test-harness.ts +99 -0
  248. package/src/monitor/message-handler.test-helpers.ts +75 -0
  249. package/src/monitor/message-handler.ts +274 -0
  250. package/src/monitor/message-media.ts +507 -0
  251. package/src/monitor/message-run-queue.ts +101 -0
  252. package/src/monitor/message-text.ts +171 -0
  253. package/src/monitor/message-utils.test.ts +1151 -0
  254. package/src/monitor/message-utils.ts +32 -0
  255. package/src/monitor/model-picker-preferences.test.ts +67 -0
  256. package/src/monitor/model-picker-preferences.ts +184 -0
  257. package/src/monitor/model-picker.state.ts +364 -0
  258. package/src/monitor/model-picker.test-utils.ts +26 -0
  259. package/src/monitor/model-picker.test.ts +794 -0
  260. package/src/monitor/model-picker.ts +38 -0
  261. package/src/monitor/model-picker.view.ts +695 -0
  262. package/src/monitor/monitor.agent-components.test.ts +375 -0
  263. package/src/monitor/monitor.test.ts +849 -0
  264. package/src/monitor/monitor.threading-utils.test.ts +598 -0
  265. package/src/monitor/native-command-agent-reply.ts +123 -0
  266. package/src/monitor/native-command-arg-ui.ts +233 -0
  267. package/src/monitor/native-command-auth.ts +308 -0
  268. package/src/monitor/native-command-bypass.ts +13 -0
  269. package/src/monitor/native-command-context.test.ts +98 -0
  270. package/src/monitor/native-command-context.ts +103 -0
  271. package/src/monitor/native-command-dispatch.ts +35 -0
  272. package/src/monitor/native-command-model-picker-apply.ts +177 -0
  273. package/src/monitor/native-command-model-picker-interaction.ts +461 -0
  274. package/src/monitor/native-command-model-picker-ui.ts +368 -0
  275. package/src/monitor/native-command-reply.test.ts +68 -0
  276. package/src/monitor/native-command-reply.ts +183 -0
  277. package/src/monitor/native-command-route.ts +91 -0
  278. package/src/monitor/native-command-status.ts +76 -0
  279. package/src/monitor/native-command-ui.ts +26 -0
  280. package/src/monitor/native-command-ui.types.ts +20 -0
  281. package/src/monitor/native-command.args.ts +45 -0
  282. package/src/monitor/native-command.command-arg.test.ts +99 -0
  283. package/src/monitor/native-command.commands-allowfrom.test.ts +490 -0
  284. package/src/monitor/native-command.model-picker.test.ts +767 -0
  285. package/src/monitor/native-command.options.test.ts +369 -0
  286. package/src/monitor/native-command.options.ts +153 -0
  287. package/src/monitor/native-command.plugin-dispatch.test.ts +879 -0
  288. package/src/monitor/native-command.runtime.ts +50 -0
  289. package/src/monitor/native-command.status-direct.test.ts +272 -0
  290. package/src/monitor/native-command.test-helpers.ts +64 -0
  291. package/src/monitor/native-command.think-autocomplete.test.ts +416 -0
  292. package/src/monitor/native-command.ts +699 -0
  293. package/src/monitor/native-command.types.ts +9 -0
  294. package/src/monitor/native-interaction-channel-context.ts +50 -0
  295. package/src/monitor/preflight-audio.runtime.ts +9 -0
  296. package/src/monitor/preflight-audio.test.ts +157 -0
  297. package/src/monitor/preflight-audio.ts +130 -0
  298. package/src/monitor/presence-cache.ts +61 -0
  299. package/src/monitor/presence.test.ts +44 -0
  300. package/src/monitor/presence.ts +50 -0
  301. package/src/monitor/provider-session.runtime.ts +12 -0
  302. package/src/monitor/provider.acp.ts +89 -0
  303. package/src/monitor/provider.allowlist.test.ts +149 -0
  304. package/src/monitor/provider.allowlist.ts +394 -0
  305. package/src/monitor/provider.cleanup.ts +41 -0
  306. package/src/monitor/provider.commands.ts +129 -0
  307. package/src/monitor/provider.config-log.ts +45 -0
  308. package/src/monitor/provider.deploy-errors.ts +362 -0
  309. package/src/monitor/provider.deploy.ts +221 -0
  310. package/src/monitor/provider.interactions.ts +160 -0
  311. package/src/monitor/provider.lifecycle.test.ts +658 -0
  312. package/src/monitor/provider.lifecycle.ts +545 -0
  313. package/src/monitor/provider.proxy.test.ts +745 -0
  314. package/src/monitor/provider.rest-proxy.test.ts +121 -0
  315. package/src/monitor/provider.runtime.ts +1 -0
  316. package/src/monitor/provider.skill-dedupe.test.ts +42 -0
  317. package/src/monitor/provider.startup-log.ts +32 -0
  318. package/src/monitor/provider.startup.test.ts +426 -0
  319. package/src/monitor/provider.startup.ts +323 -0
  320. package/src/monitor/provider.test.ts +1111 -0
  321. package/src/monitor/provider.ts +713 -0
  322. package/src/monitor/reply-context.ts +64 -0
  323. package/src/monitor/reply-delivery.test.ts +244 -0
  324. package/src/monitor/reply-delivery.ts +203 -0
  325. package/src/monitor/rest-fetch.ts +43 -0
  326. package/src/monitor/route-resolution.test.ts +204 -0
  327. package/src/monitor/route-resolution.ts +140 -0
  328. package/src/monitor/sender-identity.ts +81 -0
  329. package/src/monitor/startup-status.test.ts +30 -0
  330. package/src/monitor/startup-status.ts +10 -0
  331. package/src/monitor/status.ts +22 -0
  332. package/src/monitor/system-events.ts +55 -0
  333. package/src/monitor/thread-bindings.config.ts +35 -0
  334. package/src/monitor/thread-bindings.discord-api.test.ts +229 -0
  335. package/src/monitor/thread-bindings.discord-api.ts +318 -0
  336. package/src/monitor/thread-bindings.lifecycle.test.ts +1871 -0
  337. package/src/monitor/thread-bindings.lifecycle.ts +354 -0
  338. package/src/monitor/thread-bindings.manager.ts +553 -0
  339. package/src/monitor/thread-bindings.messages.ts +6 -0
  340. package/src/monitor/thread-bindings.persona.test.ts +34 -0
  341. package/src/monitor/thread-bindings.persona.ts +25 -0
  342. package/src/monitor/thread-bindings.session-adapter.ts +229 -0
  343. package/src/monitor/thread-bindings.session-shared.ts +59 -0
  344. package/src/monitor/thread-bindings.session-updates.ts +35 -0
  345. package/src/monitor/thread-bindings.shared-state.test.ts +36 -0
  346. package/src/monitor/thread-bindings.state.ts +540 -0
  347. package/src/monitor/thread-bindings.ts +48 -0
  348. package/src/monitor/thread-bindings.types.ts +83 -0
  349. package/src/monitor/thread-channel-context.ts +112 -0
  350. package/src/monitor/thread-session-close.test.ts +180 -0
  351. package/src/monitor/thread-session-close.ts +63 -0
  352. package/src/monitor/thread-title.generate.test.ts +197 -0
  353. package/src/monitor/thread-title.test.ts +31 -0
  354. package/src/monitor/thread-title.ts +181 -0
  355. package/src/monitor/threading.auto-thread.test.ts +327 -0
  356. package/src/monitor/threading.auto-thread.ts +287 -0
  357. package/src/monitor/threading.cache.ts +45 -0
  358. package/src/monitor/threading.parent-info.test.ts +156 -0
  359. package/src/monitor/threading.starter.test.ts +260 -0
  360. package/src/monitor/threading.starter.ts +287 -0
  361. package/src/monitor/threading.ts +20 -0
  362. package/src/monitor/threading.types.ts +102 -0
  363. package/src/monitor/timeouts.ts +84 -0
  364. package/src/monitor/typing.test.ts +42 -0
  365. package/src/monitor/typing.ts +17 -0
  366. package/src/monitor.gateway.test.ts +187 -0
  367. package/src/monitor.gateway.ts +75 -0
  368. package/src/monitor.test.ts +1397 -0
  369. package/src/monitor.ts +28 -0
  370. package/src/normalize.test.ts +56 -0
  371. package/src/normalize.ts +86 -0
  372. package/src/outbound-adapter.interactive-order.test.ts +64 -0
  373. package/src/outbound-adapter.test-harness.ts +207 -0
  374. package/src/outbound-adapter.test.ts +696 -0
  375. package/src/outbound-adapter.ts +291 -0
  376. package/src/outbound-approval.ts +29 -0
  377. package/src/outbound-components.ts +81 -0
  378. package/src/outbound-payload.contract.test.ts +38 -0
  379. package/src/outbound-payload.ts +134 -0
  380. package/src/outbound-send-context.ts +92 -0
  381. package/src/outbound-session-route.test.ts +34 -0
  382. package/src/outbound-session-route.ts +72 -0
  383. package/src/pluralkit.test.ts +67 -0
  384. package/src/pluralkit.ts +58 -0
  385. package/src/preview-streaming.ts +32 -0
  386. package/src/probe.intents.test.ts +94 -0
  387. package/src/probe.parse-token.test.ts +43 -0
  388. package/src/probe.runtime.ts +1 -0
  389. package/src/probe.ts +237 -0
  390. package/src/proxy-fetch.ts +92 -0
  391. package/src/proxy-request-client.test.ts +78 -0
  392. package/src/proxy-request-client.ts +54 -0
  393. package/src/recipient-resolution.ts +39 -0
  394. package/src/resolve-allowlist-common.test.ts +36 -0
  395. package/src/resolve-allowlist-common.ts +39 -0
  396. package/src/resolve-channels.test.ts +340 -0
  397. package/src/resolve-channels.ts +369 -0
  398. package/src/resolve-users.test.ts +222 -0
  399. package/src/resolve-users.ts +184 -0
  400. package/src/retry.test.ts +83 -0
  401. package/src/retry.ts +98 -0
  402. package/src/runtime-api.ts +64 -0
  403. package/src/runtime.ts +22 -5
  404. package/src/secret-config-contract.ts +140 -0
  405. package/src/security-audit.runtime.ts +1 -0
  406. package/src/security-audit.test.ts +246 -0
  407. package/src/security-audit.ts +208 -0
  408. package/src/security-contract.ts +47 -0
  409. package/src/security-doctor.test.ts +25 -0
  410. package/src/security-doctor.ts +20 -0
  411. package/src/security.ts +60 -0
  412. package/src/send-target-parsing.ts +14 -0
  413. package/src/send.channels.ts +139 -0
  414. package/src/send.components.test.ts +275 -0
  415. package/src/send.components.ts +383 -0
  416. package/src/send.creates-thread.test.ts +643 -0
  417. package/src/send.emojis-stickers.ts +57 -0
  418. package/src/send.guild.ts +170 -0
  419. package/src/send.message-request.ts +97 -0
  420. package/src/send.messages.test.ts +53 -0
  421. package/src/send.messages.ts +225 -0
  422. package/src/send.outbound.ts +414 -0
  423. package/src/send.permissions.authz.test.ts +188 -0
  424. package/src/send.permissions.ts +283 -0
  425. package/src/send.reactions.ts +155 -0
  426. package/src/send.sends-basic-channel-messages.test.ts +919 -0
  427. package/src/send.shared.ts +445 -0
  428. package/src/send.test-harness.ts +56 -0
  429. package/src/send.ts +82 -0
  430. package/src/send.types.ts +188 -0
  431. package/src/send.typing.test.ts +41 -0
  432. package/src/send.typing.ts +9 -0
  433. package/src/send.voice.ts +134 -0
  434. package/src/send.webhook-activity.test.ts +105 -0
  435. package/src/send.webhook.proxy.test.ts +191 -0
  436. package/src/send.webhook.ts +133 -0
  437. package/src/session-contract.ts +3 -0
  438. package/src/session-key-normalization.test.ts +44 -0
  439. package/src/session-key-normalization.ts +47 -0
  440. package/src/setup-account-state.test.ts +91 -0
  441. package/src/setup-account-state.ts +144 -0
  442. package/src/setup-adapter.ts +12 -0
  443. package/src/setup-core.ts +180 -0
  444. package/src/setup-runtime-helpers.ts +10 -0
  445. package/src/setup-surface.test.ts +96 -0
  446. package/src/setup-surface.ts +129 -0
  447. package/src/shared-interactive.test.ts +153 -0
  448. package/src/shared-interactive.ts +124 -0
  449. package/src/shared.test.ts +159 -0
  450. package/src/shared.ts +190 -0
  451. package/src/status-issues.test.ts +70 -0
  452. package/src/status-issues.ts +169 -0
  453. package/src/subagent-hooks.test.ts +40 -44
  454. package/src/subagent-hooks.ts +185 -122
  455. package/src/target-parsing.ts +53 -0
  456. package/src/target-resolver.ts +129 -0
  457. package/src/targets.test.ts +367 -0
  458. package/src/targets.ts +12 -0
  459. package/src/test-http-helpers.ts +10 -0
  460. package/src/test-support/component-runtime.ts +190 -0
  461. package/src/test-support/config.ts +7 -0
  462. package/src/test-support/configured-binding-runtime.ts +29 -0
  463. package/src/test-support/partial-channel.ts +26 -0
  464. package/src/test-support/provider.test-support.ts +545 -0
  465. package/src/token.test.ts +107 -0
  466. package/src/token.ts +60 -0
  467. package/src/ui-colors.ts +27 -0
  468. package/src/ui.ts +20 -0
  469. package/src/voice/access.test.ts +217 -0
  470. package/src/voice/access.ts +124 -0
  471. package/src/voice/audio.ts +173 -0
  472. package/src/voice/capture-state.test.ts +48 -0
  473. package/src/voice/capture-state.ts +120 -0
  474. package/src/voice/command.test.ts +164 -0
  475. package/src/voice/command.ts +283 -0
  476. package/src/voice/config.ts +8 -0
  477. package/src/voice/manager.e2e.test.ts +928 -0
  478. package/src/voice/manager.ready-listener.test.ts +37 -0
  479. package/src/voice/manager.runtime.ts +11 -0
  480. package/src/voice/manager.ts +691 -0
  481. package/src/voice/prompt.test.ts +16 -0
  482. package/src/voice/prompt.ts +17 -0
  483. package/src/voice/receive-recovery.test.ts +79 -0
  484. package/src/voice/receive-recovery.ts +159 -0
  485. package/src/voice/sanitize.test.ts +34 -0
  486. package/src/voice/sanitize.ts +32 -0
  487. package/src/voice/sdk-runtime.ts +14 -0
  488. package/src/voice/segment.ts +156 -0
  489. package/src/voice/session.ts +50 -0
  490. package/src/voice/speaker-context.ts +127 -0
  491. package/src/voice/tts.ts +125 -0
  492. package/src/voice-message.test.ts +234 -0
  493. package/src/voice-message.ts +444 -0
  494. package/subagent-hooks-api.ts +27 -0
  495. package/test-api.ts +4 -0
  496. package/thread-binding-api.ts +1 -0
  497. package/timeouts.ts +6 -0
  498. package/tsconfig.json +16 -0
@@ -0,0 +1,170 @@
1
+ import type {
2
+ APIGuildMember,
3
+ APIGuildScheduledEvent,
4
+ APIRole,
5
+ APIVoiceState,
6
+ RESTPostAPIGuildScheduledEventJSONBody,
7
+ } from "discord-api-types/v10";
8
+ import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
9
+ import { loadWebMediaRaw } from "openclaw/plugin-sdk/web-media";
10
+ import {
11
+ addGuildMemberRole,
12
+ createGuildBan,
13
+ createGuildScheduledEvent,
14
+ getChannel,
15
+ getGuildMember,
16
+ getGuildVoiceState,
17
+ listGuildChannels,
18
+ listGuildRoles,
19
+ listGuildScheduledEvents,
20
+ removeGuildMember,
21
+ removeGuildMemberRole,
22
+ timeoutGuildMember,
23
+ type APIChannel,
24
+ } from "./internal/discord.js";
25
+ import { resolveDiscordRest } from "./send.shared.js";
26
+ import type {
27
+ DiscordModerationTarget,
28
+ DiscordReactOpts,
29
+ DiscordRoleChange,
30
+ DiscordTimeoutTarget,
31
+ } from "./send.types.js";
32
+ import { DISCORD_MAX_EVENT_COVER_BYTES } from "./send.types.js";
33
+
34
+ export async function fetchMemberInfoDiscord(
35
+ guildId: string,
36
+ userId: string,
37
+ opts: DiscordReactOpts,
38
+ ): Promise<APIGuildMember> {
39
+ const rest = resolveDiscordRest(opts);
40
+ return await getGuildMember(rest, guildId, userId);
41
+ }
42
+
43
+ export async function fetchRoleInfoDiscord(
44
+ guildId: string,
45
+ opts: DiscordReactOpts,
46
+ ): Promise<APIRole[]> {
47
+ const rest = resolveDiscordRest(opts);
48
+ return await listGuildRoles(rest, guildId);
49
+ }
50
+
51
+ export async function addRoleDiscord(payload: DiscordRoleChange, opts: DiscordReactOpts) {
52
+ const rest = resolveDiscordRest(opts);
53
+ await addGuildMemberRole(rest, payload.guildId, payload.userId, payload.roleId);
54
+ return { ok: true };
55
+ }
56
+
57
+ export async function removeRoleDiscord(payload: DiscordRoleChange, opts: DiscordReactOpts) {
58
+ const rest = resolveDiscordRest(opts);
59
+ await removeGuildMemberRole(rest, payload.guildId, payload.userId, payload.roleId);
60
+ return { ok: true };
61
+ }
62
+
63
+ export async function fetchChannelInfoDiscord(
64
+ channelId: string,
65
+ opts: DiscordReactOpts,
66
+ ): Promise<APIChannel> {
67
+ const rest = resolveDiscordRest(opts);
68
+ return await getChannel(rest, channelId);
69
+ }
70
+
71
+ export async function listGuildChannelsDiscord(
72
+ guildId: string,
73
+ opts: DiscordReactOpts,
74
+ ): Promise<APIChannel[]> {
75
+ const rest = resolveDiscordRest(opts);
76
+ return await listGuildChannels(rest, guildId);
77
+ }
78
+
79
+ export async function fetchVoiceStatusDiscord(
80
+ guildId: string,
81
+ userId: string,
82
+ opts: DiscordReactOpts,
83
+ ): Promise<APIVoiceState> {
84
+ const rest = resolveDiscordRest(opts);
85
+ return await getGuildVoiceState(rest, guildId, userId);
86
+ }
87
+
88
+ export async function listScheduledEventsDiscord(
89
+ guildId: string,
90
+ opts: DiscordReactOpts,
91
+ ): Promise<APIGuildScheduledEvent[]> {
92
+ const rest = resolveDiscordRest(opts);
93
+ return await listGuildScheduledEvents(rest, guildId);
94
+ }
95
+
96
+ const ALLOWED_EVENT_COVER_TYPES = new Set(["image/png", "image/jpeg", "image/jpg", "image/gif"]);
97
+
98
+ // Loads an image from a URL or path and returns a data URI suitable for the Discord API.
99
+ export async function resolveEventCoverImage(
100
+ imageUrl: string,
101
+ opts?: { localRoots?: readonly string[] },
102
+ ): Promise<string> {
103
+ const media = await loadWebMediaRaw(imageUrl, DISCORD_MAX_EVENT_COVER_BYTES, {
104
+ localRoots: opts?.localRoots,
105
+ });
106
+ const contentType = normalizeOptionalLowercaseString(media.contentType);
107
+ if (!contentType || !ALLOWED_EVENT_COVER_TYPES.has(contentType)) {
108
+ throw new Error(
109
+ `Discord event cover images must be PNG, JPG, or GIF (got ${contentType ?? "unknown"})`,
110
+ );
111
+ }
112
+ return `data:${contentType};base64,${media.buffer.toString("base64")}`;
113
+ }
114
+
115
+ export async function createScheduledEventDiscord(
116
+ guildId: string,
117
+ payload: RESTPostAPIGuildScheduledEventJSONBody,
118
+ opts: DiscordReactOpts,
119
+ ): Promise<APIGuildScheduledEvent> {
120
+ const rest = resolveDiscordRest(opts);
121
+ return await createGuildScheduledEvent(rest, guildId, payload);
122
+ }
123
+
124
+ export async function timeoutMemberDiscord(
125
+ payload: DiscordTimeoutTarget,
126
+ opts: DiscordReactOpts,
127
+ ): Promise<APIGuildMember> {
128
+ const rest = resolveDiscordRest(opts);
129
+ let until = payload.until;
130
+ if (!until && payload.durationMinutes) {
131
+ const ms = payload.durationMinutes * 60 * 1000;
132
+ until = new Date(Date.now() + ms).toISOString();
133
+ }
134
+ return await timeoutGuildMember(rest, payload.guildId, payload.userId, {
135
+ body: { communication_disabled_until: until ?? null },
136
+ headers: payload.reason
137
+ ? { "X-Audit-Log-Reason": encodeURIComponent(payload.reason) }
138
+ : undefined,
139
+ });
140
+ }
141
+
142
+ export async function kickMemberDiscord(payload: DiscordModerationTarget, opts: DiscordReactOpts) {
143
+ const rest = resolveDiscordRest(opts);
144
+ await removeGuildMember(rest, payload.guildId, payload.userId, {
145
+ headers: payload.reason
146
+ ? { "X-Audit-Log-Reason": encodeURIComponent(payload.reason) }
147
+ : undefined,
148
+ });
149
+ return { ok: true };
150
+ }
151
+
152
+ export async function banMemberDiscord(
153
+ payload: DiscordModerationTarget & { deleteMessageDays?: number },
154
+ opts: DiscordReactOpts,
155
+ ) {
156
+ const rest = resolveDiscordRest(opts);
157
+ const deleteMessageDays =
158
+ typeof payload.deleteMessageDays === "number" && Number.isFinite(payload.deleteMessageDays)
159
+ ? Math.min(Math.max(Math.floor(payload.deleteMessageDays), 0), 7)
160
+ : undefined;
161
+ await createGuildBan(rest, payload.guildId, payload.userId, {
162
+ body: deleteMessageDays !== undefined ? { delete_message_days: deleteMessageDays } : undefined,
163
+ headers: payload.reason
164
+ ? { "X-Audit-Log-Reason": encodeURIComponent(payload.reason) }
165
+ : undefined,
166
+ });
167
+ return { ok: true };
168
+ }
169
+
170
+ // Channel management functions
@@ -0,0 +1,97 @@
1
+ import type { APIEmbed } from "discord-api-types/v10";
2
+ import {
3
+ Embed,
4
+ serializePayload,
5
+ type MessagePayloadFile,
6
+ type MessagePayloadObject,
7
+ type TopLevelComponents,
8
+ } from "./internal/discord.js";
9
+
10
+ export const SUPPRESS_NOTIFICATIONS_FLAG = 1 << 12;
11
+
12
+ export type DiscordSendComponentFactory = (text: string) => TopLevelComponents[];
13
+ export type DiscordSendComponents = TopLevelComponents[] | DiscordSendComponentFactory;
14
+ export type DiscordSendEmbeds = Array<APIEmbed | Embed>;
15
+
16
+ export function resolveDiscordSendComponents(params: {
17
+ components?: DiscordSendComponents;
18
+ text: string;
19
+ isFirst: boolean;
20
+ }): TopLevelComponents[] | undefined {
21
+ if (!params.components || !params.isFirst) {
22
+ return undefined;
23
+ }
24
+ return typeof params.components === "function"
25
+ ? params.components(params.text)
26
+ : params.components;
27
+ }
28
+
29
+ function normalizeDiscordEmbeds(embeds?: DiscordSendEmbeds): Embed[] | undefined {
30
+ if (!embeds?.length) {
31
+ return undefined;
32
+ }
33
+ return embeds.map((embed) => (embed instanceof Embed ? embed : new Embed(embed)));
34
+ }
35
+
36
+ export function resolveDiscordSendEmbeds(params: {
37
+ embeds?: DiscordSendEmbeds;
38
+ isFirst: boolean;
39
+ }): Embed[] | undefined {
40
+ if (!params.embeds || !params.isFirst) {
41
+ return undefined;
42
+ }
43
+ return normalizeDiscordEmbeds(params.embeds);
44
+ }
45
+
46
+ export function buildDiscordMessagePayload(params: {
47
+ text: string;
48
+ components?: TopLevelComponents[];
49
+ embeds?: Embed[];
50
+ flags?: number;
51
+ files?: MessagePayloadFile[];
52
+ }): MessagePayloadObject {
53
+ const payload: MessagePayloadObject = {};
54
+ const hasV2 = hasV2Components(params.components);
55
+ const trimmed = params.text.trim();
56
+ if (!hasV2 && trimmed) {
57
+ payload.content = params.text;
58
+ }
59
+ if (params.components?.length) {
60
+ payload.components = params.components;
61
+ }
62
+ if (!hasV2 && params.embeds?.length) {
63
+ payload.embeds = params.embeds;
64
+ }
65
+ if (params.flags !== undefined) {
66
+ payload.flags = params.flags;
67
+ }
68
+ if (params.files?.length) {
69
+ payload.files = params.files;
70
+ }
71
+ return payload;
72
+ }
73
+
74
+ export function buildDiscordMessageRequest(params: {
75
+ text: string;
76
+ components?: TopLevelComponents[];
77
+ embeds?: Embed[];
78
+ files?: MessagePayloadFile[];
79
+ flags?: number;
80
+ replyTo?: string;
81
+ }) {
82
+ const payload = buildDiscordMessagePayload(params);
83
+ return stripUndefinedFields({
84
+ ...serializePayload(payload),
85
+ ...(params.replyTo
86
+ ? { message_reference: { message_id: params.replyTo, fail_if_not_exists: false } }
87
+ : {}),
88
+ });
89
+ }
90
+
91
+ export function stripUndefinedFields<T extends object>(value: T): T {
92
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)) as T;
93
+ }
94
+
95
+ function hasV2Components(components?: TopLevelComponents[]): boolean {
96
+ return Boolean(components?.some((component) => "isV2" in component && component.isV2));
97
+ }
@@ -0,0 +1,53 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+
3
+ const restMock = {
4
+ get: vi.fn(),
5
+ };
6
+
7
+ vi.mock("./send.shared.js", () => ({
8
+ resolveDiscordRest: () => restMock,
9
+ }));
10
+
11
+ const { readMessagesDiscord, searchMessagesDiscord } = await import("./send.messages.js");
12
+
13
+ describe("readMessagesDiscord", () => {
14
+ it("returns messages from the REST client", async () => {
15
+ const messages = [{ id: "1", content: "hello" }];
16
+ restMock.get.mockResolvedValueOnce(messages);
17
+
18
+ const result = await readMessagesDiscord("C1", { limit: 5 }, { cfg: {} as never });
19
+
20
+ expect(result).toEqual(messages);
21
+ expect(restMock.get).toHaveBeenCalledWith(expect.stringContaining("C1"), { limit: 5 });
22
+ });
23
+
24
+ it("propagates REST errors", async () => {
25
+ restMock.get.mockRejectedValueOnce(new Error("Discord API error"));
26
+
27
+ await expect(readMessagesDiscord("C1", {}, { cfg: {} as never })).rejects.toThrow(
28
+ "Discord API error",
29
+ );
30
+ });
31
+ });
32
+
33
+ describe("searchMessagesDiscord", () => {
34
+ it("returns search results from the REST client", async () => {
35
+ const results = { messages: [[{ id: "1" }]], total_results: 1 };
36
+ restMock.get.mockResolvedValueOnce(results);
37
+
38
+ const result = await searchMessagesDiscord(
39
+ { guildId: "G1", content: "test", limit: 1 },
40
+ { cfg: {} as never },
41
+ );
42
+
43
+ expect(result).toEqual(results);
44
+ });
45
+
46
+ it("propagates REST errors", async () => {
47
+ restMock.get.mockRejectedValueOnce(new Error("Discord API error"));
48
+
49
+ await expect(
50
+ searchMessagesDiscord({ guildId: "G1", content: "test" }, { cfg: {} as never }),
51
+ ).rejects.toThrow("Discord API error");
52
+ });
53
+ });
@@ -0,0 +1,225 @@
1
+ import type { APIChannel, APIMessage } from "discord-api-types/v10";
2
+ import { ChannelType } from "discord-api-types/v10";
3
+ import {
4
+ createChannelMessage,
5
+ createThread,
6
+ deleteChannelMessage,
7
+ editChannelMessage,
8
+ getChannel,
9
+ getChannelMessage,
10
+ listChannelArchivedThreads,
11
+ listGuildActiveThreads,
12
+ listChannelMessages,
13
+ listChannelPins,
14
+ pinChannelMessage,
15
+ searchGuildMessages,
16
+ unpinChannelMessage,
17
+ } from "./internal/discord.js";
18
+ import { resolveDiscordRest } from "./send.shared.js";
19
+ import type {
20
+ DiscordMessageEdit,
21
+ DiscordMessageQuery,
22
+ DiscordReactOpts,
23
+ DiscordSearchQuery,
24
+ DiscordThreadCreate,
25
+ DiscordThreadList,
26
+ } from "./send.types.js";
27
+
28
+ function formatDiscordThreadInitialMessageError(error: unknown): string {
29
+ return error instanceof Error ? error.message : String(error);
30
+ }
31
+
32
+ export class DiscordThreadInitialMessageError extends Error {
33
+ readonly initialMessageError: string;
34
+ readonly thread: APIChannel;
35
+
36
+ constructor(thread: APIChannel, error: unknown) {
37
+ const initialMessageError = formatDiscordThreadInitialMessageError(error);
38
+ super(
39
+ `Discord thread was created, but sending the initial message failed: ${initialMessageError}`,
40
+ );
41
+ this.name = "DiscordThreadInitialMessageError";
42
+ this.initialMessageError = initialMessageError;
43
+ this.thread = thread;
44
+ }
45
+ }
46
+
47
+ export async function readMessagesDiscord(
48
+ channelId: string,
49
+ query: DiscordMessageQuery = {},
50
+ opts: DiscordReactOpts,
51
+ ): Promise<APIMessage[]> {
52
+ const rest = resolveDiscordRest(opts);
53
+ const limit =
54
+ typeof query.limit === "number" && Number.isFinite(query.limit)
55
+ ? Math.min(Math.max(Math.floor(query.limit), 1), 100)
56
+ : undefined;
57
+ const params: Record<string, string | number> = {};
58
+ if (limit) {
59
+ params.limit = limit;
60
+ }
61
+ if (query.before) {
62
+ params.before = query.before;
63
+ }
64
+ if (query.after) {
65
+ params.after = query.after;
66
+ }
67
+ if (query.around) {
68
+ params.around = query.around;
69
+ }
70
+ return await listChannelMessages(rest, channelId, params);
71
+ }
72
+
73
+ export async function fetchMessageDiscord(
74
+ channelId: string,
75
+ messageId: string,
76
+ opts: DiscordReactOpts,
77
+ ): Promise<APIMessage> {
78
+ const rest = resolveDiscordRest(opts);
79
+ return await getChannelMessage(rest, channelId, messageId);
80
+ }
81
+
82
+ export async function editMessageDiscord(
83
+ channelId: string,
84
+ messageId: string,
85
+ payload: DiscordMessageEdit,
86
+ opts: DiscordReactOpts,
87
+ ): Promise<APIMessage> {
88
+ const rest = resolveDiscordRest(opts);
89
+ return await editChannelMessage(rest, channelId, messageId, {
90
+ body: { content: payload.content },
91
+ });
92
+ }
93
+
94
+ export async function deleteMessageDiscord(
95
+ channelId: string,
96
+ messageId: string,
97
+ opts: DiscordReactOpts,
98
+ ) {
99
+ const rest = resolveDiscordRest(opts);
100
+ await deleteChannelMessage(rest, channelId, messageId);
101
+ return { ok: true };
102
+ }
103
+
104
+ export async function pinMessageDiscord(
105
+ channelId: string,
106
+ messageId: string,
107
+ opts: DiscordReactOpts,
108
+ ) {
109
+ const rest = resolveDiscordRest(opts);
110
+ await pinChannelMessage(rest, channelId, messageId);
111
+ return { ok: true };
112
+ }
113
+
114
+ export async function unpinMessageDiscord(
115
+ channelId: string,
116
+ messageId: string,
117
+ opts: DiscordReactOpts,
118
+ ) {
119
+ const rest = resolveDiscordRest(opts);
120
+ await unpinChannelMessage(rest, channelId, messageId);
121
+ return { ok: true };
122
+ }
123
+
124
+ export async function listPinsDiscord(
125
+ channelId: string,
126
+ opts: DiscordReactOpts,
127
+ ): Promise<APIMessage[]> {
128
+ const rest = resolveDiscordRest(opts);
129
+ return await listChannelPins(rest, channelId);
130
+ }
131
+
132
+ export async function createThreadDiscord(
133
+ channelId: string,
134
+ payload: DiscordThreadCreate,
135
+ opts: DiscordReactOpts,
136
+ ) {
137
+ const rest = resolveDiscordRest(opts);
138
+ const body: Record<string, unknown> = { name: payload.name };
139
+ if (payload.autoArchiveMinutes) {
140
+ body.auto_archive_duration = payload.autoArchiveMinutes;
141
+ }
142
+ if (!payload.messageId && payload.type !== undefined) {
143
+ body.type = payload.type;
144
+ }
145
+ let channelType: ChannelType | undefined;
146
+ if (!payload.messageId) {
147
+ // Only detect channel kind for route-less thread creation.
148
+ // If this lookup fails, keep prior behavior and let Discord validate.
149
+ try {
150
+ const channel = await getChannel(rest, channelId);
151
+ channelType = channel?.type;
152
+ } catch {
153
+ channelType = undefined;
154
+ }
155
+ }
156
+ const isForumLike =
157
+ channelType === ChannelType.GuildForum || channelType === ChannelType.GuildMedia;
158
+ if (isForumLike) {
159
+ const starterContent = payload.content?.trim() ? payload.content : payload.name;
160
+ body.message = { content: starterContent };
161
+ if (payload.appliedTags?.length) {
162
+ body.applied_tags = payload.appliedTags;
163
+ }
164
+ }
165
+ // When creating a standalone thread (no messageId) in a non-forum channel,
166
+ // default to public thread (type 11). Discord defaults to private (type 12)
167
+ // which is unexpected for most users. (#14147)
168
+ if (!payload.messageId && !isForumLike && body.type === undefined) {
169
+ body.type = ChannelType.PublicThread;
170
+ }
171
+ const thread = await createThread(rest, channelId, { body }, payload.messageId);
172
+
173
+ // For non-forum channels, send the initial message separately after thread creation.
174
+ // Forum channels handle this via the `message` field in the request body.
175
+ if (!isForumLike && payload.content?.trim() && "id" in thread) {
176
+ try {
177
+ await createChannelMessage(rest, thread.id, {
178
+ body: { content: payload.content },
179
+ });
180
+ } catch (error) {
181
+ throw new DiscordThreadInitialMessageError(thread, error);
182
+ }
183
+ }
184
+
185
+ return thread;
186
+ }
187
+
188
+ export async function listThreadsDiscord(payload: DiscordThreadList, opts: DiscordReactOpts) {
189
+ const rest = resolveDiscordRest(opts);
190
+ if (payload.includeArchived) {
191
+ if (!payload.channelId) {
192
+ throw new Error("channelId required to list archived threads");
193
+ }
194
+ const params: Record<string, string | number> = {};
195
+ if (payload.before) {
196
+ params.before = payload.before;
197
+ }
198
+ if (payload.limit) {
199
+ params.limit = payload.limit;
200
+ }
201
+ return await listChannelArchivedThreads(rest, payload.channelId, params);
202
+ }
203
+ return await listGuildActiveThreads(rest, payload.guildId);
204
+ }
205
+
206
+ export async function searchMessagesDiscord(query: DiscordSearchQuery, opts: DiscordReactOpts) {
207
+ const rest = resolveDiscordRest(opts);
208
+ const params = new URLSearchParams();
209
+ params.set("content", query.content);
210
+ if (query.channelIds?.length) {
211
+ for (const channelId of query.channelIds) {
212
+ params.append("channel_id", channelId);
213
+ }
214
+ }
215
+ if (query.authorIds?.length) {
216
+ for (const authorId of query.authorIds) {
217
+ params.append("author_id", authorId);
218
+ }
219
+ }
220
+ if (query.limit) {
221
+ const limit = Math.min(Math.max(Math.floor(query.limit), 1), 25);
222
+ params.set("limit", String(limit));
223
+ }
224
+ return await searchGuildMessages(rest, query.guildId, params);
225
+ }