@kodelyth/discord 2026.5.39 → 2026.5.42

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 (639) hide show
  1. package/account-inspect-api.ts +6 -0
  2. package/action-runtime-api.ts +1 -0
  3. package/api.ts +130 -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/dist/account-inspect-Dqw-enky.js +81 -0
  11. package/dist/account-inspect-api.js +10 -0
  12. package/dist/accounts-B7OBFePq.js +224 -0
  13. package/dist/action-runtime-api.js +2 -0
  14. package/dist/agent-components.runtime-DVY_1VB4.js +4 -0
  15. package/dist/allow-list-B0s7evD7.js +354 -0
  16. package/dist/api-CXAcv9nZ.js +130 -0
  17. package/dist/api.js +23 -0
  18. package/dist/approval-handler.runtime-B9xUAF3n.js +426 -0
  19. package/dist/audit-DoiK49WO.js +24 -0
  20. package/dist/audit-core-BGrq3G7r.js +105 -0
  21. package/dist/channel-U_aeoFwW.js +795 -0
  22. package/dist/channel-actions-BxEBnEuv.js +173 -0
  23. package/dist/channel-actions.runtime-CPtpH-yl.js +263 -0
  24. package/dist/channel-api-BfjklLby.js +21 -0
  25. package/dist/channel-config-api.js +2 -0
  26. package/dist/channel-plugin-api.js +2 -0
  27. package/dist/channel.setup-BUSC0apv.js +337 -0
  28. package/dist/components-luonoe13.js +909 -0
  29. package/dist/config-api-DSYGqaLQ.js +2 -0
  30. package/dist/config-schema-DIqJBGwC.js +357 -0
  31. package/dist/configured-state.js +6 -0
  32. package/dist/contract-api.js +8 -0
  33. package/dist/conversation-identity-DXAm0_Mk.js +270 -0
  34. package/dist/directory-config-CYbuMmPS.js +49 -0
  35. package/dist/directory-contract-api.js +2 -0
  36. package/dist/directory-live-DX4dLRpJ.js +159 -0
  37. package/dist/doctor-bbKSvGVD.js +244 -0
  38. package/dist/doctor-contract-Btjt6NJD.js +383 -0
  39. package/dist/doctor-contract-api.js +2 -0
  40. package/dist/gateway-registry-BKSpa4GB.js +74 -0
  41. package/dist/handle-action.guild-admin-B5BArS2n.js +286 -0
  42. package/dist/inbound-context-WAOqhGlT.js +48 -0
  43. package/dist/inbound-event-delivery-C-1Ji3WP.js +65 -0
  44. package/dist/index.js +26 -0
  45. package/dist/manager.runtime-DXHynKE4.js +2356 -0
  46. package/dist/message-handler-mXzc3tA_.js +381 -0
  47. package/dist/message-handler.preflight-BPD1a347.js +1113 -0
  48. package/dist/message-handler.process-GUa3aV8z.js +1438 -0
  49. package/dist/message-utils-dUbem16p.js +549 -0
  50. package/dist/outbound-adapter-C18OAc1y.js +536 -0
  51. package/dist/pluralkit-D1Q2x0w5.js +22 -0
  52. package/dist/preflight-audio-CZtpWcIm.js +72 -0
  53. package/dist/preflight-audio.runtime-Brx_0_xW.js +7 -0
  54. package/dist/preview-streaming-D_slNIiO.js +8 -0
  55. package/dist/probe-D--Ca4JF.js +139 -0
  56. package/dist/probe.runtime-DQBchZzv.js +2 -0
  57. package/dist/provider-B2-31CIT.js +9565 -0
  58. package/dist/provider-session.runtime-BwzzSsrH.js +6 -0
  59. package/dist/provider.runtime-CP3oHLls.js +2 -0
  60. package/dist/resolve-allowlist-common-CqxPLcJO.js +34 -0
  61. package/dist/resolve-channels-0LX4pUbB.js +265 -0
  62. package/dist/resolve-users-CztOv0Qs.js +120 -0
  63. package/dist/runtime-DUaw66V_.js +1073 -0
  64. package/dist/runtime-api.actions.js +3 -0
  65. package/dist/runtime-api.js +30 -0
  66. package/dist/runtime-api.lookup.js +7 -0
  67. package/dist/runtime-api.monitor-CvVKvEXW.js +5 -0
  68. package/dist/runtime-api.monitor.js +8 -0
  69. package/dist/runtime-api.send.js +6 -0
  70. package/dist/runtime-api.threads.js +6 -0
  71. package/dist/runtime-fC6f4UF2.js +8 -0
  72. package/dist/runtime-setter-api.js +2 -0
  73. package/dist/secret-config-contract-B6WW5V88.js +115 -0
  74. package/dist/secret-contract-api.js +2 -0
  75. package/dist/security-audit-CnyIQKz6.js +120 -0
  76. package/dist/security-audit-contract-api.js +2 -0
  77. package/dist/security-audit.runtime-CQSkjNLu.js +2 -0
  78. package/dist/security-contract-DLvYOgLM.js +26 -0
  79. package/dist/security-contract-api.js +2 -0
  80. package/dist/security-doctor-DepqtNCI.js +18 -0
  81. package/dist/send-DCtPCHGk.js +881 -0
  82. package/dist/send.components-Bcgxvm52.js +474 -0
  83. package/dist/send.outbound-S9t0UuHc.js +330 -0
  84. package/dist/send.receipt-CDn3GBWC.js +3119 -0
  85. package/dist/send.shared-D4iBnAmn.js +669 -0
  86. package/dist/sender-identity-CxCe3_1a.js +43 -0
  87. package/dist/session-contract-Dwhw3RTY.js +6 -0
  88. package/dist/session-key-api.js +2 -0
  89. package/dist/session-key-normalization-CP8dPUid.js +23 -0
  90. package/dist/setup-entry.js +11 -0
  91. package/dist/setup-plugin-api.js +2 -0
  92. package/dist/shared-AIlvuZXt.js +171 -0
  93. package/dist/subagent-hooks-8bK-mgiU.js +120 -0
  94. package/dist/subagent-hooks-api.js +22 -0
  95. package/dist/system-events-Ba1TklaL.js +34 -0
  96. package/dist/target-resolver-BrtFQtoK.js +82 -0
  97. package/dist/targets-DWLLZE2l.js +3 -0
  98. package/dist/test-api.js +45 -0
  99. package/dist/thread-binding-api.js +4 -0
  100. package/dist/thread-bindings-9aKRmZv0.js +255 -0
  101. package/dist/thread-bindings.discord-api-ssGH5wc2.js +244 -0
  102. package/dist/thread-bindings.manager-0YBHGemk.js +534 -0
  103. package/dist/thread-bindings.session-updates-DJZGIwaU.js +54 -0
  104. package/dist/thread-bindings.state-eTFl-PqJ.js +318 -0
  105. package/dist/timeouts-CEwuGaWT.js +52 -0
  106. package/dist/timeouts.js +2 -0
  107. package/dist/typing-BmJKRpCS.js +14 -0
  108. package/doctor-contract-api.ts +1 -0
  109. package/index.test.ts +13 -0
  110. package/index.ts +24 -0
  111. package/klaw.plugin.json +2 -3822
  112. package/package.json +4 -4
  113. package/runtime-api.actions.ts +15 -0
  114. package/runtime-api.lookup.ts +22 -0
  115. package/runtime-api.monitor.ts +50 -0
  116. package/runtime-api.send.ts +79 -0
  117. package/runtime-api.threads.ts +31 -0
  118. package/runtime-api.ts +181 -0
  119. package/runtime-setter-api.ts +3 -0
  120. package/secret-contract-api.ts +4 -0
  121. package/security-audit-contract-api.ts +1 -0
  122. package/security-contract-api.ts +4 -0
  123. package/session-key-api.ts +1 -0
  124. package/setup-entry.ts +9 -0
  125. package/setup-plugin-api.ts +3 -0
  126. package/src/account-inspect.test.ts +126 -0
  127. package/src/account-inspect.ts +128 -0
  128. package/src/accounts.test.ts +381 -0
  129. package/src/accounts.ts +205 -0
  130. package/src/actions/handle-action.guild-admin.ts +421 -0
  131. package/src/actions/handle-action.test.ts +480 -0
  132. package/src/actions/handle-action.ts +402 -0
  133. package/src/actions/runtime.guild.ts +446 -0
  134. package/src/actions/runtime.messaging.messages.ts +226 -0
  135. package/src/actions/runtime.messaging.reactions.ts +67 -0
  136. package/src/actions/runtime.messaging.runtime.ts +73 -0
  137. package/src/actions/runtime.messaging.send.ts +336 -0
  138. package/src/actions/runtime.messaging.shared.ts +97 -0
  139. package/src/actions/runtime.messaging.ts +37 -0
  140. package/src/actions/runtime.moderation-shared.ts +48 -0
  141. package/src/actions/runtime.moderation.authz.test.ts +151 -0
  142. package/src/actions/runtime.moderation.ts +116 -0
  143. package/src/actions/runtime.presence.test.ts +165 -0
  144. package/src/actions/runtime.presence.ts +117 -0
  145. package/src/actions/runtime.shared.ts +86 -0
  146. package/src/actions/runtime.test.ts +1337 -0
  147. package/src/actions/runtime.ts +87 -0
  148. package/src/api-barrel.test.ts +78 -0
  149. package/src/api.test.ts +152 -0
  150. package/src/api.ts +215 -0
  151. package/src/approval-handler.runtime.test.ts +41 -0
  152. package/src/approval-handler.runtime.ts +633 -0
  153. package/src/approval-native.test.ts +330 -0
  154. package/src/approval-native.ts +219 -0
  155. package/src/approval-runtime.ts +14 -0
  156. package/src/approval-shared.ts +50 -0
  157. package/src/audit-core.ts +178 -0
  158. package/src/audit.test.ts +204 -0
  159. package/src/audit.ts +32 -0
  160. package/src/channel-actions.contract.test.ts +45 -0
  161. package/src/channel-actions.runtime.ts +1 -0
  162. package/src/channel-actions.test.ts +504 -0
  163. package/src/channel-actions.ts +254 -0
  164. package/src/channel-api.ts +29 -0
  165. package/src/channel.conversation.ts +159 -0
  166. package/src/channel.loaders.ts +50 -0
  167. package/src/channel.message-adapter.test.ts +230 -0
  168. package/src/channel.runtime.ts +1 -0
  169. package/src/channel.setup.ts +12 -0
  170. package/src/channel.test.ts +828 -0
  171. package/src/channel.ts +728 -0
  172. package/src/chunk.test.ts +170 -0
  173. package/src/chunk.ts +321 -0
  174. package/src/client.proxy.test.ts +177 -0
  175. package/src/client.test.ts +83 -0
  176. package/src/client.ts +143 -0
  177. package/src/component-custom-id.ts +72 -0
  178. package/src/components-registry.ts +356 -0
  179. package/src/components.builders.ts +409 -0
  180. package/src/components.modal.ts +124 -0
  181. package/src/components.parse.ts +407 -0
  182. package/src/components.test.ts +345 -0
  183. package/src/components.ts +54 -0
  184. package/src/components.types.ts +187 -0
  185. package/src/config-schema.test.ts +439 -0
  186. package/src/config-schema.ts +6 -0
  187. package/src/config-ui-hints.ts +354 -0
  188. package/src/conversation-identity.ts +58 -0
  189. package/src/delivery-retry.ts +52 -0
  190. package/src/directory-cache.ts +116 -0
  191. package/src/directory-config.ts +58 -0
  192. package/src/directory-contract.test.ts +129 -0
  193. package/src/directory-live.test.ts +141 -0
  194. package/src/directory-live.ts +135 -0
  195. package/src/doctor-contract.ts +477 -0
  196. package/src/doctor-shared.ts +5 -0
  197. package/src/doctor.test.ts +393 -0
  198. package/src/doctor.ts +340 -0
  199. package/src/draft-chunking.test.ts +64 -0
  200. package/src/draft-chunking.ts +43 -0
  201. package/src/draft-stream.test.ts +193 -0
  202. package/src/draft-stream.ts +162 -0
  203. package/src/durable-delivery.test.ts +103 -0
  204. package/src/error-body.ts +38 -0
  205. package/src/exec-approvals.test.ts +88 -0
  206. package/src/exec-approvals.ts +110 -0
  207. package/src/gateway-logging.test.ts +98 -0
  208. package/src/gateway-logging.ts +67 -0
  209. package/src/group-policy.ts +113 -0
  210. package/src/guilds.ts +29 -0
  211. package/src/inbound-context.contract.test.ts +11 -0
  212. package/src/inbound-event-delivery.ts +135 -0
  213. package/src/interactive-dispatch.ts +104 -0
  214. package/src/internal/api.commands.ts +51 -0
  215. package/src/internal/api.guild.ts +164 -0
  216. package/src/internal/api.interactions.ts +53 -0
  217. package/src/internal/api.messages.ts +113 -0
  218. package/src/internal/api.reactions.ts +38 -0
  219. package/src/internal/api.test.ts +260 -0
  220. package/src/internal/api.ts +61 -0
  221. package/src/internal/api.users.ts +19 -0
  222. package/src/internal/api.webhooks.ts +13 -0
  223. package/src/internal/client.test.ts +472 -0
  224. package/src/internal/client.ts +310 -0
  225. package/src/internal/command-deploy.test.ts +197 -0
  226. package/src/internal/command-deploy.ts +352 -0
  227. package/src/internal/commands.ts +188 -0
  228. package/src/internal/components.base.ts +65 -0
  229. package/src/internal/components.message.ts +279 -0
  230. package/src/internal/components.modal.ts +95 -0
  231. package/src/internal/components.ts +31 -0
  232. package/src/internal/discord.ts +11 -0
  233. package/src/internal/embeds.ts +35 -0
  234. package/src/internal/entity-cache.ts +98 -0
  235. package/src/internal/event-queue.ts +185 -0
  236. package/src/internal/gateway-close-codes.ts +25 -0
  237. package/src/internal/gateway-dispatch.ts +96 -0
  238. package/src/internal/gateway-identify-limiter.ts +26 -0
  239. package/src/internal/gateway-lifecycle.test.ts +114 -0
  240. package/src/internal/gateway-lifecycle.ts +75 -0
  241. package/src/internal/gateway-rate-limit.ts +104 -0
  242. package/src/internal/gateway.test.ts +676 -0
  243. package/src/internal/gateway.ts +479 -0
  244. package/src/internal/interaction-dispatch.test.ts +148 -0
  245. package/src/internal/interaction-dispatch.ts +162 -0
  246. package/src/internal/interaction-options.ts +98 -0
  247. package/src/internal/interaction-response.ts +53 -0
  248. package/src/internal/interactions.test.ts +329 -0
  249. package/src/internal/interactions.ts +378 -0
  250. package/src/internal/listeners.ts +91 -0
  251. package/src/internal/live-smoke.live.test.ts +26 -0
  252. package/src/internal/modal-fields.ts +95 -0
  253. package/src/internal/payload.ts +69 -0
  254. package/src/internal/rest-body.ts +115 -0
  255. package/src/internal/rest-errors.ts +88 -0
  256. package/src/internal/rest-routes.ts +50 -0
  257. package/src/internal/rest-scheduler.ts +557 -0
  258. package/src/internal/rest.test.ts +681 -0
  259. package/src/internal/rest.ts +322 -0
  260. package/src/internal/schemas.ts +36 -0
  261. package/src/internal/structures.test.ts +43 -0
  262. package/src/internal/structures.ts +280 -0
  263. package/src/internal/test-builders.test-support.ts +167 -0
  264. package/src/internal/voice.ts +49 -0
  265. package/src/media-detection.ts +28 -0
  266. package/src/mentions.test.ts +111 -0
  267. package/src/mentions.ts +147 -0
  268. package/src/monitor/ack-reactions.ts +70 -0
  269. package/src/monitor/acp-bind-here.integration.test.ts +219 -0
  270. package/src/monitor/agent-components-auth.ts +7 -0
  271. package/src/monitor/agent-components-context.ts +154 -0
  272. package/src/monitor/agent-components-data.ts +224 -0
  273. package/src/monitor/agent-components-dm-auth.ts +177 -0
  274. package/src/monitor/agent-components-guild-auth.ts +322 -0
  275. package/src/monitor/agent-components-helpers.runtime.ts +3 -0
  276. package/src/monitor/agent-components-helpers.ts +34 -0
  277. package/src/monitor/agent-components-reply.ts +10 -0
  278. package/src/monitor/agent-components.deps.runtime.ts +2 -0
  279. package/src/monitor/agent-components.dispatch.ts +359 -0
  280. package/src/monitor/agent-components.handlers.ts +303 -0
  281. package/src/monitor/agent-components.modal.ts +160 -0
  282. package/src/monitor/agent-components.plugin-interactive.ts +187 -0
  283. package/src/monitor/agent-components.runtime.ts +14 -0
  284. package/src/monitor/agent-components.system-controls.ts +215 -0
  285. package/src/monitor/agent-components.ts +70 -0
  286. package/src/monitor/agent-components.types.ts +58 -0
  287. package/src/monitor/agent-components.wildcard-controls.ts +171 -0
  288. package/src/monitor/agent-components.wildcard.test.ts +71 -0
  289. package/src/monitor/allow-list.test.ts +14 -0
  290. package/src/monitor/allow-list.ts +631 -0
  291. package/src/monitor/auto-presence.test.ts +184 -0
  292. package/src/monitor/auto-presence.ts +356 -0
  293. package/src/monitor/channel-access.test.ts +113 -0
  294. package/src/monitor/channel-access.ts +102 -0
  295. package/src/monitor/commands.test.ts +24 -0
  296. package/src/monitor/commands.ts +9 -0
  297. package/src/monitor/dm-command-auth.test.ts +274 -0
  298. package/src/monitor/dm-command-auth.ts +259 -0
  299. package/src/monitor/dm-command-decision.test.ts +108 -0
  300. package/src/monitor/dm-command-decision.ts +49 -0
  301. package/src/monitor/exec-approvals.test.ts +225 -0
  302. package/src/monitor/exec-approvals.ts +158 -0
  303. package/src/monitor/format.ts +45 -0
  304. package/src/monitor/gateway-handle.ts +33 -0
  305. package/src/monitor/gateway-metadata.test.ts +29 -0
  306. package/src/monitor/gateway-metadata.ts +298 -0
  307. package/src/monitor/gateway-plugin.test.ts +320 -0
  308. package/src/monitor/gateway-plugin.ts +302 -0
  309. package/src/monitor/gateway-registry.ts +37 -0
  310. package/src/monitor/gateway-supervisor.test.ts +157 -0
  311. package/src/monitor/gateway-supervisor.ts +206 -0
  312. package/src/monitor/inbound-context.test-helpers.ts +37 -0
  313. package/src/monitor/inbound-context.test.ts +112 -0
  314. package/src/monitor/inbound-context.ts +95 -0
  315. package/src/monitor/inbound-dedupe.ts +79 -0
  316. package/src/monitor/inbound-job.test.ts +216 -0
  317. package/src/monitor/inbound-job.ts +118 -0
  318. package/src/monitor/listeners.queue.ts +91 -0
  319. package/src/monitor/listeners.reactions.ts +594 -0
  320. package/src/monitor/listeners.test.ts +209 -0
  321. package/src/monitor/listeners.ts +150 -0
  322. package/src/monitor/message-channel-info.ts +96 -0
  323. package/src/monitor/message-forwarded.ts +114 -0
  324. package/src/monitor/message-handler.batch-gate.test.ts +22 -0
  325. package/src/monitor/message-handler.batch-gate.ts +19 -0
  326. package/src/monitor/message-handler.bot-self-filter.test.ts +68 -0
  327. package/src/monitor/message-handler.context.ts +492 -0
  328. package/src/monitor/message-handler.dm-preflight.ts +119 -0
  329. package/src/monitor/message-handler.draft-preview.ts +426 -0
  330. package/src/monitor/message-handler.hydration.test.ts +80 -0
  331. package/src/monitor/message-handler.hydration.ts +198 -0
  332. package/src/monitor/message-handler.inbound-context.test.ts +61 -0
  333. package/src/monitor/message-handler.module-test-helpers.ts +31 -0
  334. package/src/monitor/message-handler.preflight-channel-access.ts +86 -0
  335. package/src/monitor/message-handler.preflight-channel-context.test.ts +18 -0
  336. package/src/monitor/message-handler.preflight-channel-context.ts +58 -0
  337. package/src/monitor/message-handler.preflight-context.ts +54 -0
  338. package/src/monitor/message-handler.preflight-helpers.ts +164 -0
  339. package/src/monitor/message-handler.preflight-history.ts +23 -0
  340. package/src/monitor/message-handler.preflight-logging.ts +36 -0
  341. package/src/monitor/message-handler.preflight-pluralkit.ts +26 -0
  342. package/src/monitor/message-handler.preflight-runtime.ts +28 -0
  343. package/src/monitor/message-handler.preflight-thread.ts +49 -0
  344. package/src/monitor/message-handler.preflight.acp-bindings.test.ts +371 -0
  345. package/src/monitor/message-handler.preflight.test-helpers.ts +114 -0
  346. package/src/monitor/message-handler.preflight.test.ts +2255 -0
  347. package/src/monitor/message-handler.preflight.ts +822 -0
  348. package/src/monitor/message-handler.preflight.types.ts +115 -0
  349. package/src/monitor/message-handler.process.test.ts +2520 -0
  350. package/src/monitor/message-handler.process.ts +1027 -0
  351. package/src/monitor/message-handler.queue.test.ts +680 -0
  352. package/src/monitor/message-handler.routing-preflight.ts +112 -0
  353. package/src/monitor/message-handler.test-harness.ts +99 -0
  354. package/src/monitor/message-handler.test-helpers.ts +75 -0
  355. package/src/monitor/message-handler.ts +309 -0
  356. package/src/monitor/message-media.ts +536 -0
  357. package/src/monitor/message-run-queue.ts +101 -0
  358. package/src/monitor/message-text.ts +171 -0
  359. package/src/monitor/message-utils.test.ts +1234 -0
  360. package/src/monitor/message-utils.ts +34 -0
  361. package/src/monitor/model-picker-preferences.test.ts +67 -0
  362. package/src/monitor/model-picker-preferences.ts +184 -0
  363. package/src/monitor/model-picker.state.ts +364 -0
  364. package/src/monitor/model-picker.test-utils.ts +26 -0
  365. package/src/monitor/model-picker.test.ts +869 -0
  366. package/src/monitor/model-picker.ts +38 -0
  367. package/src/monitor/model-picker.view.ts +722 -0
  368. package/src/monitor/monitor.agent-components.test.ts +410 -0
  369. package/src/monitor/monitor.test.ts +919 -0
  370. package/src/monitor/monitor.threading-utils.test.ts +614 -0
  371. package/src/monitor/native-command-agent-reply.ts +125 -0
  372. package/src/monitor/native-command-arg-ui.ts +233 -0
  373. package/src/monitor/native-command-auth.ts +309 -0
  374. package/src/monitor/native-command-bypass.ts +13 -0
  375. package/src/monitor/native-command-context.test.ts +105 -0
  376. package/src/monitor/native-command-context.ts +109 -0
  377. package/src/monitor/native-command-dispatch.ts +35 -0
  378. package/src/monitor/native-command-model-picker-apply.ts +209 -0
  379. package/src/monitor/native-command-model-picker-interaction.ts +516 -0
  380. package/src/monitor/native-command-model-picker-ui.ts +357 -0
  381. package/src/monitor/native-command-reply.test.ts +68 -0
  382. package/src/monitor/native-command-reply.ts +185 -0
  383. package/src/monitor/native-command-route.ts +91 -0
  384. package/src/monitor/native-command-status.ts +76 -0
  385. package/src/monitor/native-command-ui.ts +26 -0
  386. package/src/monitor/native-command-ui.types.ts +20 -0
  387. package/src/monitor/native-command.args.ts +45 -0
  388. package/src/monitor/native-command.command-arg.test.ts +108 -0
  389. package/src/monitor/native-command.commands-allowfrom.test.ts +504 -0
  390. package/src/monitor/native-command.model-picker.test.ts +930 -0
  391. package/src/monitor/native-command.options.test.ts +379 -0
  392. package/src/monitor/native-command.options.ts +153 -0
  393. package/src/monitor/native-command.plugin-dispatch.test.ts +1212 -0
  394. package/src/monitor/native-command.runtime.ts +51 -0
  395. package/src/monitor/native-command.status-direct.test.ts +278 -0
  396. package/src/monitor/native-command.test-helpers.ts +64 -0
  397. package/src/monitor/native-command.think-autocomplete.test.ts +411 -0
  398. package/src/monitor/native-command.ts +747 -0
  399. package/src/monitor/native-command.types.ts +9 -0
  400. package/src/monitor/native-interaction-channel-context.ts +50 -0
  401. package/src/monitor/preflight-audio.runtime.ts +9 -0
  402. package/src/monitor/preflight-audio.test.ts +157 -0
  403. package/src/monitor/preflight-audio.ts +130 -0
  404. package/src/monitor/presence-cache.ts +61 -0
  405. package/src/monitor/presence.test.ts +61 -0
  406. package/src/monitor/presence.ts +50 -0
  407. package/src/monitor/provider-session.runtime.ts +12 -0
  408. package/src/monitor/provider.acp.ts +89 -0
  409. package/src/monitor/provider.allowlist.test.ts +217 -0
  410. package/src/monitor/provider.allowlist.ts +398 -0
  411. package/src/monitor/provider.cleanup.ts +41 -0
  412. package/src/monitor/provider.commands.ts +129 -0
  413. package/src/monitor/provider.config-log.ts +45 -0
  414. package/src/monitor/provider.deploy-errors.ts +362 -0
  415. package/src/monitor/provider.deploy.ts +221 -0
  416. package/src/monitor/provider.interactions.ts +160 -0
  417. package/src/monitor/provider.lifecycle.test.ts +734 -0
  418. package/src/monitor/provider.lifecycle.ts +562 -0
  419. package/src/monitor/provider.proxy.test.ts +804 -0
  420. package/src/monitor/provider.rest-proxy.test.ts +389 -0
  421. package/src/monitor/provider.runtime.ts +1 -0
  422. package/src/monitor/provider.skill-dedupe.test.ts +42 -0
  423. package/src/monitor/provider.startup-log.ts +32 -0
  424. package/src/monitor/provider.startup.test.ts +440 -0
  425. package/src/monitor/provider.startup.ts +323 -0
  426. package/src/monitor/provider.test.ts +1173 -0
  427. package/src/monitor/provider.ts +688 -0
  428. package/src/monitor/reply-context.ts +64 -0
  429. package/src/monitor/reply-delivery.test.ts +474 -0
  430. package/src/monitor/reply-delivery.ts +212 -0
  431. package/src/monitor/reply-safety.ts +96 -0
  432. package/src/monitor/rest-fetch.ts +94 -0
  433. package/src/monitor/route-resolution.test.ts +209 -0
  434. package/src/monitor/route-resolution.ts +140 -0
  435. package/src/monitor/sender-identity.ts +81 -0
  436. package/src/monitor/startup-status.test.ts +30 -0
  437. package/src/monitor/startup-status.ts +10 -0
  438. package/src/monitor/status.ts +22 -0
  439. package/src/monitor/system-events.ts +55 -0
  440. package/src/monitor/thread-bindings.config.ts +35 -0
  441. package/src/monitor/thread-bindings.discord-api.test.ts +250 -0
  442. package/src/monitor/thread-bindings.discord-api.ts +310 -0
  443. package/src/monitor/thread-bindings.lifecycle.test.ts +1994 -0
  444. package/src/monitor/thread-bindings.lifecycle.ts +354 -0
  445. package/src/monitor/thread-bindings.manager.ts +551 -0
  446. package/src/monitor/thread-bindings.messages.ts +6 -0
  447. package/src/monitor/thread-bindings.persona.test.ts +34 -0
  448. package/src/monitor/thread-bindings.persona.ts +25 -0
  449. package/src/monitor/thread-bindings.session-adapter.ts +229 -0
  450. package/src/monitor/thread-bindings.session-shared.ts +59 -0
  451. package/src/monitor/thread-bindings.session-updates.ts +35 -0
  452. package/src/monitor/thread-bindings.shared-state.test.ts +39 -0
  453. package/src/monitor/thread-bindings.state.ts +540 -0
  454. package/src/monitor/thread-bindings.ts +48 -0
  455. package/src/monitor/thread-bindings.types.ts +83 -0
  456. package/src/monitor/thread-channel-context.ts +112 -0
  457. package/src/monitor/thread-session-close.test.ts +180 -0
  458. package/src/monitor/thread-session-close.ts +63 -0
  459. package/src/monitor/thread-title.generate.test.ts +209 -0
  460. package/src/monitor/thread-title.test.ts +31 -0
  461. package/src/monitor/thread-title.ts +181 -0
  462. package/src/monitor/threading.auto-thread.test.ts +330 -0
  463. package/src/monitor/threading.auto-thread.ts +287 -0
  464. package/src/monitor/threading.cache.ts +45 -0
  465. package/src/monitor/threading.parent-info.test.ts +156 -0
  466. package/src/monitor/threading.starter.test.ts +279 -0
  467. package/src/monitor/threading.starter.ts +288 -0
  468. package/src/monitor/threading.ts +20 -0
  469. package/src/monitor/threading.types.ts +102 -0
  470. package/src/monitor/timeouts.ts +84 -0
  471. package/src/monitor/typing.test.ts +42 -0
  472. package/src/monitor/typing.ts +17 -0
  473. package/src/monitor.gateway.test.ts +187 -0
  474. package/src/monitor.gateway.ts +75 -0
  475. package/src/monitor.test.ts +1416 -0
  476. package/src/monitor.ts +28 -0
  477. package/src/network-config.test.ts +92 -0
  478. package/src/network-config.ts +79 -0
  479. package/src/normalize.test.ts +56 -0
  480. package/src/normalize.ts +86 -0
  481. package/src/outbound-adapter.interactive-order.test.ts +82 -0
  482. package/src/outbound-adapter.test-harness.ts +207 -0
  483. package/src/outbound-adapter.test.ts +804 -0
  484. package/src/outbound-adapter.ts +326 -0
  485. package/src/outbound-approval.ts +29 -0
  486. package/src/outbound-components.ts +86 -0
  487. package/src/outbound-payload.contract.test.ts +49 -0
  488. package/src/outbound-payload.ts +208 -0
  489. package/src/outbound-send-context.ts +89 -0
  490. package/src/outbound-session-route.test.ts +42 -0
  491. package/src/outbound-session-route.ts +72 -0
  492. package/src/pluralkit.test.ts +67 -0
  493. package/src/pluralkit.ts +58 -0
  494. package/src/preview-streaming.ts +18 -0
  495. package/src/probe.intents.test.ts +94 -0
  496. package/src/probe.parse-token.test.ts +43 -0
  497. package/src/probe.runtime.ts +1 -0
  498. package/src/probe.ts +237 -0
  499. package/src/proxy-fetch.ts +92 -0
  500. package/src/proxy-request-client.test.ts +100 -0
  501. package/src/proxy-request-client.ts +21 -0
  502. package/src/recipient-resolution.ts +39 -0
  503. package/src/resolve-allowlist-common.test.ts +40 -0
  504. package/src/resolve-allowlist-common.ts +39 -0
  505. package/src/resolve-channels.test.ts +341 -0
  506. package/src/resolve-channels.ts +369 -0
  507. package/src/resolve-users.test.ts +243 -0
  508. package/src/resolve-users.ts +184 -0
  509. package/src/retry.test.ts +83 -0
  510. package/src/retry.ts +98 -0
  511. package/src/runtime-api.ts +61 -0
  512. package/src/runtime-config.ts +16 -0
  513. package/src/runtime.ts +23 -0
  514. package/src/secret-config-contract.ts +140 -0
  515. package/src/security-audit.runtime.ts +1 -0
  516. package/src/security-audit.test.ts +245 -0
  517. package/src/security-audit.ts +208 -0
  518. package/src/security-contract.ts +47 -0
  519. package/src/security-doctor.test.ts +25 -0
  520. package/src/security-doctor.ts +20 -0
  521. package/src/security.ts +60 -0
  522. package/src/send-target-parsing.ts +14 -0
  523. package/src/send.channels.ts +139 -0
  524. package/src/send.components.test.ts +330 -0
  525. package/src/send.components.ts +391 -0
  526. package/src/send.creates-thread.test.ts +681 -0
  527. package/src/send.emojis-stickers.ts +57 -0
  528. package/src/send.guild.ts +170 -0
  529. package/src/send.message-request.ts +112 -0
  530. package/src/send.messages.test.ts +59 -0
  531. package/src/send.messages.ts +229 -0
  532. package/src/send.outbound.ts +459 -0
  533. package/src/send.permissions.authz.test.ts +190 -0
  534. package/src/send.permissions.ts +283 -0
  535. package/src/send.reactions.ts +155 -0
  536. package/src/send.receipt.ts +69 -0
  537. package/src/send.sends-basic-channel-messages.test.ts +1068 -0
  538. package/src/send.shared.ts +469 -0
  539. package/src/send.test-harness.ts +56 -0
  540. package/src/send.ts +82 -0
  541. package/src/send.types.ts +191 -0
  542. package/src/send.typing.test.ts +41 -0
  543. package/src/send.typing.ts +9 -0
  544. package/src/send.voice.ts +136 -0
  545. package/src/send.webhook-activity.test.ts +152 -0
  546. package/src/send.webhook.proxy.test.ts +210 -0
  547. package/src/send.webhook.ts +137 -0
  548. package/src/session-contract.ts +3 -0
  549. package/src/session-key-normalization.test.ts +44 -0
  550. package/src/session-key-normalization.ts +47 -0
  551. package/src/setup-account-state.test.ts +113 -0
  552. package/src/setup-account-state.ts +141 -0
  553. package/src/setup-adapter.ts +14 -0
  554. package/src/setup-core.ts +215 -0
  555. package/src/setup-runtime-helpers.ts +10 -0
  556. package/src/setup-surface.test.ts +137 -0
  557. package/src/setup-surface.ts +132 -0
  558. package/src/shared-interactive.test.ts +153 -0
  559. package/src/shared-interactive.ts +161 -0
  560. package/src/shared.test.ts +186 -0
  561. package/src/shared.ts +197 -0
  562. package/src/status-issues.test.ts +97 -0
  563. package/src/status-issues.ts +198 -0
  564. package/src/subagent-hooks.test.ts +465 -0
  565. package/src/subagent-hooks.ts +232 -0
  566. package/src/target-parsing.ts +70 -0
  567. package/src/target-resolver.ts +129 -0
  568. package/src/targets.test.ts +393 -0
  569. package/src/targets.ts +12 -0
  570. package/src/test-http-helpers.ts +10 -0
  571. package/src/test-support/component-runtime.ts +194 -0
  572. package/src/test-support/config.ts +7 -0
  573. package/src/test-support/configured-binding-runtime.ts +29 -0
  574. package/src/test-support/partial-channel.ts +26 -0
  575. package/src/test-support/provider.test-support.ts +547 -0
  576. package/src/token.test.ts +174 -0
  577. package/src/token.ts +107 -0
  578. package/src/ui-colors.ts +27 -0
  579. package/src/ui.ts +20 -0
  580. package/src/voice/access.test.ts +288 -0
  581. package/src/voice/access.ts +126 -0
  582. package/src/voice/audio.test.ts +47 -0
  583. package/src/voice/audio.ts +249 -0
  584. package/src/voice/capture-state.test.ts +48 -0
  585. package/src/voice/capture-state.ts +120 -0
  586. package/src/voice/command.test.ts +170 -0
  587. package/src/voice/command.ts +284 -0
  588. package/src/voice/config.ts +8 -0
  589. package/src/voice/ingress.ts +164 -0
  590. package/src/voice/manager.e2e.test.ts +3286 -0
  591. package/src/voice/manager.ready-listener.test.ts +54 -0
  592. package/src/voice/manager.runtime.ts +14 -0
  593. package/src/voice/manager.ts +1155 -0
  594. package/src/voice/prompt.test.ts +30 -0
  595. package/src/voice/prompt.ts +22 -0
  596. package/src/voice/realtime.ts +1370 -0
  597. package/src/voice/receive-recovery.test.ts +81 -0
  598. package/src/voice/receive-recovery.ts +159 -0
  599. package/src/voice/sanitize.test.ts +34 -0
  600. package/src/voice/sanitize.ts +29 -0
  601. package/src/voice/sdk-runtime.ts +14 -0
  602. package/src/voice/segment.ts +160 -0
  603. package/src/voice/session.ts +81 -0
  604. package/src/voice/speaker-context.ts +127 -0
  605. package/src/voice/tts.ts +151 -0
  606. package/src/voice-message.test.ts +376 -0
  607. package/src/voice-message.ts +474 -0
  608. package/subagent-hooks-api.ts +27 -0
  609. package/test-api.ts +4 -0
  610. package/thread-binding-api.ts +1 -0
  611. package/timeouts.ts +6 -0
  612. package/tsconfig.json +16 -0
  613. package/account-inspect-api.js +0 -7
  614. package/action-runtime-api.js +0 -7
  615. package/api.js +0 -7
  616. package/channel-config-api.js +0 -7
  617. package/channel-plugin-api.js +0 -7
  618. package/configured-state.js +0 -7
  619. package/contract-api.js +0 -7
  620. package/directory-contract-api.js +0 -7
  621. package/doctor-contract-api.js +0 -7
  622. package/index.js +0 -7
  623. package/runtime-api.actions.js +0 -7
  624. package/runtime-api.js +0 -7
  625. package/runtime-api.lookup.js +0 -7
  626. package/runtime-api.monitor.js +0 -7
  627. package/runtime-api.send.js +0 -7
  628. package/runtime-api.threads.js +0 -7
  629. package/runtime-setter-api.js +0 -7
  630. package/secret-contract-api.js +0 -7
  631. package/security-audit-contract-api.js +0 -7
  632. package/security-contract-api.js +0 -7
  633. package/session-key-api.js +0 -7
  634. package/setup-entry.js +0 -7
  635. package/setup-plugin-api.js +0 -7
  636. package/subagent-hooks-api.js +0 -7
  637. package/test-api.js +0 -7
  638. package/thread-binding-api.js +0 -7
  639. package/timeouts.js +0 -7
@@ -0,0 +1,474 @@
1
+ /**
2
+ * Discord Voice Message Support
3
+ *
4
+ * Implements sending voice messages via Discord's API.
5
+ * Voice messages require:
6
+ * - OGG/Opus format audio
7
+ * - Waveform data (base64 encoded, up to 256 samples, 0-255 values)
8
+ * - Duration in seconds
9
+ * - Message flag 8192 (IS_VOICE_MESSAGE)
10
+ * - No other content (text, embeds, etc.)
11
+ */
12
+
13
+ import crypto from "node:crypto";
14
+ import fs from "node:fs/promises";
15
+ import path from "node:path";
16
+ import { formatErrorMessage } from "klaw/plugin-sdk/error-runtime";
17
+ import {
18
+ parseFfprobeCodecAndSampleRate,
19
+ runFfmpeg,
20
+ runFfprobe,
21
+ } from "klaw/plugin-sdk/media-runtime";
22
+ import { MEDIA_FFMPEG_MAX_AUDIO_DURATION_SECS } from "klaw/plugin-sdk/media-runtime";
23
+ import { unlinkIfExists } from "klaw/plugin-sdk/media-runtime";
24
+ import type { RetryRunner } from "klaw/plugin-sdk/retry-runtime";
25
+ import { writeExternalFileWithinRoot } from "klaw/plugin-sdk/security-runtime";
26
+ import { fetchWithSsrFGuard, type SsrFPolicy } from "klaw/plugin-sdk/ssrf-runtime";
27
+ import { normalizeLowercaseStringOrEmpty } from "klaw/plugin-sdk/string-coerce-runtime";
28
+ import { resolvePreferredKlawTmpDir } from "klaw/plugin-sdk/temp-path";
29
+ import { DiscordError, RateLimitError, type RequestClient } from "./internal/discord.js";
30
+ import { readDiscordMessage, readRetryAfter } from "./internal/rest-errors.js";
31
+
32
+ const DISCORD_VOICE_MESSAGE_FLAG = 1 << 13;
33
+ const SUPPRESS_NOTIFICATIONS_FLAG = 1 << 12;
34
+ const WAVEFORM_SAMPLES = 256;
35
+ const DISCORD_OPUS_SAMPLE_RATE_HZ = 48_000;
36
+ const DISCORD_VOICE_UPLOAD_SSRF_POLICY: SsrFPolicy = {
37
+ allowRfc2544BenchmarkRange: true,
38
+ allowIpv6UniqueLocalRange: true,
39
+ };
40
+
41
+ async function runFfmpegToOutput(params: {
42
+ outputPath: string;
43
+ buildArgs: (tempPath: string) => string[];
44
+ }): Promise<void> {
45
+ const rootDir = path.dirname(params.outputPath);
46
+ await fs.mkdir(rootDir, { recursive: true });
47
+ await writeExternalFileWithinRoot({
48
+ rootDir,
49
+ path: path.basename(params.outputPath),
50
+ write: async (tempPath) => {
51
+ await runFfmpeg(params.buildArgs(tempPath));
52
+ },
53
+ });
54
+ }
55
+
56
+ function createRateLimitError(
57
+ response: Response,
58
+ body: { message: string; retry_after: number; global: boolean },
59
+ request?: Request,
60
+ ): RateLimitError {
61
+ const fallbackRequest =
62
+ request ??
63
+ new Request("https://discord.com/api/v10/channels/voice/messages", {
64
+ method: "POST",
65
+ });
66
+ const RateLimitErrorCtor = RateLimitError as unknown as new (
67
+ response: Response,
68
+ body: { message: string; retry_after: number; global: boolean },
69
+ request?: Request,
70
+ ) => RateLimitError;
71
+ return new RateLimitErrorCtor(response, body, fallbackRequest);
72
+ }
73
+
74
+ export type VoiceMessageMetadata = {
75
+ durationSecs: number;
76
+ waveform: string; // base64 encoded
77
+ };
78
+
79
+ /**
80
+ * Get audio duration using ffprobe
81
+ */
82
+ export async function getAudioDuration(filePath: string): Promise<number> {
83
+ try {
84
+ const stdout = await runFfprobe([
85
+ "-v",
86
+ "error",
87
+ "-show_entries",
88
+ "format=duration",
89
+ "-of",
90
+ "csv=p=0",
91
+ filePath,
92
+ ]);
93
+ const duration = Number.parseFloat(stdout.trim());
94
+ if (Number.isNaN(duration)) {
95
+ throw new Error("Could not parse duration");
96
+ }
97
+ return Math.round(duration * 100) / 100; // Round to 2 decimal places
98
+ } catch (err) {
99
+ const errMessage = formatErrorMessage(err);
100
+ throw new Error(`Failed to get audio duration: ${errMessage}`, { cause: err });
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Generate waveform data from audio file using ffmpeg
106
+ * Returns base64 encoded byte array of amplitude samples (0-255)
107
+ */
108
+ export async function generateWaveform(filePath: string): Promise<string> {
109
+ try {
110
+ // Extract raw PCM and sample amplitude values
111
+ return await generateWaveformFromPcm(filePath);
112
+ } catch {
113
+ // If PCM extraction fails, generate a placeholder waveform
114
+ return generatePlaceholderWaveform();
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Generate waveform by extracting raw PCM data and sampling amplitudes
120
+ */
121
+ async function generateWaveformFromPcm(filePath: string): Promise<string> {
122
+ const tempDir = resolvePreferredKlawTmpDir();
123
+ const tempPcm = path.join(tempDir, `waveform-${crypto.randomUUID()}.raw`);
124
+
125
+ try {
126
+ // Convert to raw 16-bit signed PCM, mono, 8kHz
127
+ await runFfmpegToOutput({
128
+ outputPath: tempPcm,
129
+ buildArgs: (outputPath) => [
130
+ "-y",
131
+ "-i",
132
+ filePath,
133
+ "-vn",
134
+ "-sn",
135
+ "-dn",
136
+ "-t",
137
+ String(MEDIA_FFMPEG_MAX_AUDIO_DURATION_SECS),
138
+ "-f",
139
+ "s16le",
140
+ "-acodec",
141
+ "pcm_s16le",
142
+ "-ac",
143
+ "1",
144
+ "-ar",
145
+ "8000",
146
+ outputPath,
147
+ ],
148
+ });
149
+
150
+ const pcmData = await fs.readFile(tempPcm);
151
+ const samples = new Int16Array(pcmData.buffer, pcmData.byteOffset, pcmData.byteLength / 2);
152
+
153
+ // Sample the PCM data to get WAVEFORM_SAMPLES points
154
+ const step = Math.max(1, Math.floor(samples.length / WAVEFORM_SAMPLES));
155
+ const waveform: number[] = [];
156
+
157
+ for (let i = 0; i < WAVEFORM_SAMPLES && i * step < samples.length; i++) {
158
+ // Get average absolute amplitude for this segment
159
+ let sum = 0;
160
+ let count = 0;
161
+ for (let j = 0; j < step && i * step + j < samples.length; j++) {
162
+ sum += Math.abs(samples[i * step + j]);
163
+ count++;
164
+ }
165
+ const avg = count > 0 ? sum / count : 0;
166
+ // Normalize to 0-255 (16-bit signed max is 32767)
167
+ const normalized = Math.min(255, Math.round((avg / 32767) * 255));
168
+ waveform.push(normalized);
169
+ }
170
+
171
+ // Pad with zeros if we don't have enough samples
172
+ while (waveform.length < WAVEFORM_SAMPLES) {
173
+ waveform.push(0);
174
+ }
175
+
176
+ return Buffer.from(waveform).toString("base64");
177
+ } finally {
178
+ await unlinkIfExists(tempPcm);
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Generate a placeholder waveform (for when audio processing fails)
184
+ */
185
+ function generatePlaceholderWaveform(): string {
186
+ // Generate a simple sine-wave-like pattern
187
+ const waveform: number[] = [];
188
+ for (let i = 0; i < WAVEFORM_SAMPLES; i++) {
189
+ const value = Math.round(128 + 64 * Math.sin((i / WAVEFORM_SAMPLES) * Math.PI * 8));
190
+ waveform.push(Math.min(255, Math.max(0, value)));
191
+ }
192
+ return Buffer.from(waveform).toString("base64");
193
+ }
194
+
195
+ /**
196
+ * Convert audio file to OGG/Opus format if needed
197
+ * Returns path to the OGG file (may be same as input if already OGG/Opus)
198
+ */
199
+ export async function ensureOggOpus(filePath: string): Promise<{ path: string; cleanup: boolean }> {
200
+ const trimmed = filePath.trim();
201
+ // Defense-in-depth: callers should never hand ffmpeg/ffprobe a URL/protocol path.
202
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
203
+ throw new Error(
204
+ `Voice message conversion requires a local file path; received a URL/protocol source: ${trimmed}`,
205
+ );
206
+ }
207
+
208
+ const ext = normalizeLowercaseStringOrEmpty(path.extname(filePath));
209
+
210
+ // Check if already OGG
211
+ if (ext === ".ogg") {
212
+ // Fast-path only when the file is Opus at Discord's expected 48kHz.
213
+ try {
214
+ const stdout = await runFfprobe([
215
+ "-v",
216
+ "error",
217
+ "-select_streams",
218
+ "a:0",
219
+ "-show_entries",
220
+ "stream=codec_name,sample_rate",
221
+ "-of",
222
+ "csv=p=0",
223
+ filePath,
224
+ ]);
225
+ const { codec, sampleRateHz } = parseFfprobeCodecAndSampleRate(stdout);
226
+ if (codec === "opus" && sampleRateHz === DISCORD_OPUS_SAMPLE_RATE_HZ) {
227
+ return { path: filePath, cleanup: false };
228
+ }
229
+ } catch {
230
+ // If probe fails, convert anyway
231
+ }
232
+ }
233
+
234
+ // Convert to OGG/Opus
235
+ // Always resample to 48kHz to ensure Discord voice messages play at correct speed
236
+ // (Discord expects 48kHz; lower sample rates like 24kHz from some TTS providers cause 0.5x playback)
237
+ const tempDir = resolvePreferredKlawTmpDir();
238
+ const outputPath = path.join(tempDir, `voice-${crypto.randomUUID()}.ogg`);
239
+
240
+ await runFfmpegToOutput({
241
+ outputPath,
242
+ buildArgs: (tempPath) => [
243
+ "-y",
244
+ "-i",
245
+ filePath,
246
+ "-vn",
247
+ "-sn",
248
+ "-dn",
249
+ "-t",
250
+ String(MEDIA_FFMPEG_MAX_AUDIO_DURATION_SECS),
251
+ "-ar",
252
+ String(DISCORD_OPUS_SAMPLE_RATE_HZ),
253
+ "-c:a",
254
+ "libopus",
255
+ "-b:a",
256
+ "64k",
257
+ "-f",
258
+ "ogg",
259
+ tempPath,
260
+ ],
261
+ });
262
+
263
+ return { path: outputPath, cleanup: true };
264
+ }
265
+
266
+ /**
267
+ * Get voice message metadata (duration and waveform)
268
+ */
269
+ export async function getVoiceMessageMetadata(filePath: string): Promise<VoiceMessageMetadata> {
270
+ const [durationSecs, waveform] = await Promise.all([
271
+ getAudioDuration(filePath),
272
+ generateWaveform(filePath),
273
+ ]);
274
+
275
+ return { durationSecs, waveform };
276
+ }
277
+
278
+ type UploadUrlResponse = {
279
+ attachments: Array<{
280
+ id: number;
281
+ upload_url: string;
282
+ upload_filename: string;
283
+ }>;
284
+ };
285
+
286
+ function coerceDiscordErrorBody(raw: string): unknown {
287
+ if (!raw) {
288
+ return undefined;
289
+ }
290
+ try {
291
+ return JSON.parse(raw);
292
+ } catch {
293
+ return { message: raw.slice(0, 200) };
294
+ }
295
+ }
296
+
297
+ async function createVoiceRequestError(
298
+ response: Response,
299
+ fallbackMessage: string,
300
+ ): Promise<Error> {
301
+ const raw = await response.text().catch(() => "");
302
+ const parsed = coerceDiscordErrorBody(raw);
303
+ if (response.status === 429) {
304
+ throw createRateLimitError(response, {
305
+ message: readDiscordMessage(parsed, "You are being rate limited."),
306
+ retry_after: readRetryAfter(parsed, response, 1),
307
+ global:
308
+ parsed && typeof parsed === "object" && "global" in parsed
309
+ ? Boolean((parsed as { global?: unknown }).global)
310
+ : false,
311
+ });
312
+ }
313
+ return new DiscordError(
314
+ response,
315
+ parsed ?? {
316
+ message: fallbackMessage,
317
+ },
318
+ );
319
+ }
320
+
321
+ async function requestVoiceUploadUrl(params: {
322
+ rest: RequestClient;
323
+ channelId: string;
324
+ botToken: string;
325
+ filename: string;
326
+ fileSize: number;
327
+ }): Promise<UploadUrlResponse> {
328
+ const url = `${params.rest.options?.baseUrl ?? "https://discord.com/api"}/channels/${params.channelId}/attachments`;
329
+ const uploadUrlInit: RequestInit = {
330
+ method: "POST",
331
+ headers: {
332
+ Authorization: `Bot ${params.botToken}`,
333
+ "Content-Type": "application/json",
334
+ },
335
+ body: JSON.stringify({
336
+ files: [{ filename: params.filename, file_size: params.fileSize, id: "0" }],
337
+ }),
338
+ };
339
+ const { response: res, release } = await fetchWithSsrFGuard({
340
+ url,
341
+ init: uploadUrlInit,
342
+ policy: DISCORD_VOICE_UPLOAD_SSRF_POLICY,
343
+ auditContext: "discord.voice.upload-url",
344
+ });
345
+ try {
346
+ if (!res.ok) {
347
+ throw await createVoiceRequestError(res, "Upload URL request failed");
348
+ }
349
+ return (await res.json()) as UploadUrlResponse;
350
+ } finally {
351
+ await release();
352
+ }
353
+ }
354
+
355
+ async function uploadVoiceAttachment(params: {
356
+ uploadUrl: string;
357
+ audioBuffer: Buffer;
358
+ }): Promise<void> {
359
+ const { response: uploadResponse, release } = await fetchWithSsrFGuard({
360
+ url: params.uploadUrl,
361
+ init: {
362
+ method: "PUT",
363
+ headers: {
364
+ "Content-Type": "audio/ogg",
365
+ },
366
+ body: new Uint8Array(params.audioBuffer),
367
+ },
368
+ policy: DISCORD_VOICE_UPLOAD_SSRF_POLICY,
369
+ auditContext: "discord.voice.attachment-upload",
370
+ });
371
+
372
+ try {
373
+ if (!uploadResponse.ok) {
374
+ throw await createVoiceRequestError(uploadResponse, "Failed to upload voice message");
375
+ }
376
+ } finally {
377
+ await release();
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Send a voice message to Discord
383
+ *
384
+ * This follows Discord's voice message protocol:
385
+ * 1. Request upload URL from Discord
386
+ * 2. Upload the OGG file to the provided URL
387
+ * 3. Send the message with flag 8192 and attachment metadata
388
+ */
389
+ export async function sendDiscordVoiceMessage(
390
+ rest: RequestClient,
391
+ channelId: string,
392
+ audioBuffer: Buffer,
393
+ metadata: VoiceMessageMetadata,
394
+ replyTo: string | undefined,
395
+ request: RetryRunner,
396
+ silent?: boolean,
397
+ token?: string,
398
+ ): Promise<{ id: string; channel_id: string }> {
399
+ const filename = "voice-message.ogg";
400
+ const fileSize = audioBuffer.byteLength;
401
+
402
+ // Step 1: Request upload URL from Discord
403
+ // RequestClient auto-converts "files" bodies to multipart/form-data, but Discord's
404
+ // /attachments endpoint expects JSON, so this path uses a guarded raw HTTP call.
405
+ const botToken = token;
406
+ if (!botToken) {
407
+ throw new Error("Discord bot token is required for voice message upload");
408
+ }
409
+ const { upload_filename } = await request(async () => {
410
+ const uploadUrlResponse = await requestVoiceUploadUrl({
411
+ rest,
412
+ channelId,
413
+ botToken,
414
+ filename,
415
+ fileSize,
416
+ });
417
+
418
+ if (!uploadUrlResponse.attachments?.[0]) {
419
+ throw new Error("Failed to get upload URL for voice message");
420
+ }
421
+
422
+ const attachment = uploadUrlResponse.attachments[0];
423
+ await uploadVoiceAttachment({
424
+ uploadUrl: attachment.upload_url,
425
+ audioBuffer,
426
+ });
427
+ return attachment;
428
+ }, "voice-upload");
429
+
430
+ // Step 3: Send the message with voice message flag and metadata
431
+ const flags = silent
432
+ ? DISCORD_VOICE_MESSAGE_FLAG | SUPPRESS_NOTIFICATIONS_FLAG
433
+ : DISCORD_VOICE_MESSAGE_FLAG;
434
+ const messagePayload: {
435
+ flags: number;
436
+ attachments: Array<{
437
+ id: string;
438
+ filename: string;
439
+ uploaded_filename: string;
440
+ duration_secs: number;
441
+ waveform: string;
442
+ }>;
443
+ message_reference?: { message_id: string; fail_if_not_exists: boolean };
444
+ } = {
445
+ flags,
446
+ attachments: [
447
+ {
448
+ id: "0",
449
+ filename,
450
+ uploaded_filename: upload_filename,
451
+ duration_secs: metadata.durationSecs,
452
+ waveform: metadata.waveform,
453
+ },
454
+ ],
455
+ };
456
+
457
+ // Note: Voice messages cannot have content, but can have message_reference for replies
458
+ if (replyTo) {
459
+ messagePayload.message_reference = {
460
+ message_id: replyTo,
461
+ fail_if_not_exists: false,
462
+ };
463
+ }
464
+
465
+ const res = (await request(
466
+ () =>
467
+ rest.post(`/channels/${channelId}/messages`, {
468
+ body: messagePayload,
469
+ }) as Promise<{ id: string; channel_id: string }>,
470
+ "voice-message",
471
+ )) as { id: string; channel_id: string };
472
+
473
+ return res;
474
+ }
@@ -0,0 +1,27 @@
1
+ import type { KlawPluginApi } from "klaw/plugin-sdk/channel-entry-contract";
2
+
3
+ type DiscordSubagentHooksModule = typeof import("./src/subagent-hooks.js");
4
+
5
+ let discordSubagentHooksPromise: Promise<DiscordSubagentHooksModule> | null = null;
6
+
7
+ function loadDiscordSubagentHooksModule() {
8
+ discordSubagentHooksPromise ??= import("./src/subagent-hooks.js");
9
+ return discordSubagentHooksPromise;
10
+ }
11
+
12
+ // Subagent hooks live behind a dedicated barrel so the bundled entry can
13
+ // register one stable hook wiring path while keeping the handler module lazy.
14
+ export function registerDiscordSubagentHooks(api: KlawPluginApi): void {
15
+ api.on("subagent_spawning", async (event) => {
16
+ const { handleDiscordSubagentSpawning } = await loadDiscordSubagentHooksModule();
17
+ return await handleDiscordSubagentSpawning(api, event);
18
+ });
19
+ api.on("subagent_ended", async (event) => {
20
+ const { handleDiscordSubagentEnded } = await loadDiscordSubagentHooksModule();
21
+ handleDiscordSubagentEnded(event);
22
+ });
23
+ api.on("subagent_delivery_target", async (event) => {
24
+ const { handleDiscordSubagentDeliveryTarget } = await loadDiscordSubagentHooksModule();
25
+ return handleDiscordSubagentDeliveryTarget(event);
26
+ });
27
+ }
package/test-api.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { discordPlugin } from "./src/channel.js";
2
+ export { buildFinalizedDiscordDirectInboundContext } from "./src/monitor/inbound-context.test-helpers.js";
3
+ export { testing as discordThreadBindingTesting } from "./src/monitor/thread-bindings.manager.js";
4
+ export { discordOutbound } from "./src/outbound-adapter.js";
@@ -0,0 +1 @@
1
+ export const defaultTopLevelPlacement = "child" as const;
package/timeouts.ts ADDED
@@ -0,0 +1,6 @@
1
+ export {
2
+ DISCORD_ATTACHMENT_IDLE_TIMEOUT_MS,
3
+ DISCORD_ATTACHMENT_TOTAL_TIMEOUT_MS,
4
+ DISCORD_DEFAULT_INBOUND_WORKER_TIMEOUT_MS,
5
+ DISCORD_DEFAULT_LISTENER_TIMEOUT_MS,
6
+ } from "./src/monitor/timeouts.js";
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "../tsconfig.package-boundary.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "."
5
+ },
6
+ "include": ["./*.ts", "./src/**/*.ts"],
7
+ "exclude": [
8
+ "./**/*.test.ts",
9
+ "./dist/**",
10
+ "./node_modules/**",
11
+ "./src/test-support/**",
12
+ "./src/**/*test-helpers.ts",
13
+ "./src/**/*test-harness.ts",
14
+ "./src/**/*test-support.ts"
15
+ ]
16
+ }
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/account-inspect-api.js";
2
- import * as module from "../../../dist/extensions/discord/account-inspect-api.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/action-runtime-api.js";
2
- import * as module from "../../../dist/extensions/discord/action-runtime-api.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
package/api.js DELETED
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/api.js";
2
- import * as module from "../../../dist/extensions/discord/api.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/channel-config-api.js";
2
- import * as module from "../../../dist/extensions/discord/channel-config-api.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/channel-plugin-api.js";
2
- import * as module from "../../../dist/extensions/discord/channel-plugin-api.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/configured-state.js";
2
- import * as module from "../../../dist/extensions/discord/configured-state.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
package/contract-api.js DELETED
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/contract-api.js";
2
- import * as module from "../../../dist/extensions/discord/contract-api.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/directory-contract-api.js";
2
- import * as module from "../../../dist/extensions/discord/directory-contract-api.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/doctor-contract-api.js";
2
- import * as module from "../../../dist/extensions/discord/doctor-contract-api.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
package/index.js DELETED
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/index.js";
2
- import defaultModule from "../../../dist/extensions/discord/index.js";
3
- let defaultExport = defaultModule;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/runtime-api.actions.js";
2
- import * as module from "../../../dist/extensions/discord/runtime-api.actions.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
package/runtime-api.js DELETED
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/runtime-api.js";
2
- import * as module from "../../../dist/extensions/discord/runtime-api.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/runtime-api.lookup.js";
2
- import * as module from "../../../dist/extensions/discord/runtime-api.lookup.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/runtime-api.monitor.js";
2
- import * as module from "../../../dist/extensions/discord/runtime-api.monitor.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/runtime-api.send.js";
2
- import * as module from "../../../dist/extensions/discord/runtime-api.send.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };
@@ -1,7 +0,0 @@
1
- export * from "../../../dist/extensions/discord/runtime-api.threads.js";
2
- import * as module from "../../../dist/extensions/discord/runtime-api.threads.js";
3
- let defaultExport = "default" in module ? module.default : module;
4
- for (let index = 0; index < 4 && defaultExport && typeof defaultExport === "object" && "default" in defaultExport; index += 1) {
5
- defaultExport = defaultExport.default;
6
- }
7
- export { defaultExport as default };