@gakr-gakr/discord 0.1.0

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 (353) 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/autobot.plugin.json +15 -0
  5. package/channel-config-api.ts +1 -0
  6. package/channel-plugin-api.ts +3 -0
  7. package/config-api.ts +4 -0
  8. package/configured-state.ts +6 -0
  9. package/contract-api.ts +21 -0
  10. package/directory-contract-api.ts +4 -0
  11. package/doctor-contract-api.ts +1 -0
  12. package/index.ts +24 -0
  13. package/package.json +79 -0
  14. package/runtime-api.actions.ts +15 -0
  15. package/runtime-api.lookup.ts +22 -0
  16. package/runtime-api.monitor.ts +50 -0
  17. package/runtime-api.send.ts +79 -0
  18. package/runtime-api.threads.ts +31 -0
  19. package/runtime-api.ts +181 -0
  20. package/runtime-setter-api.ts +3 -0
  21. package/secret-contract-api.ts +4 -0
  22. package/security-audit-contract-api.ts +1 -0
  23. package/security-contract-api.ts +4 -0
  24. package/session-key-api.ts +1 -0
  25. package/setup-entry.ts +9 -0
  26. package/setup-plugin-api.ts +3 -0
  27. package/src/account-inspect.ts +131 -0
  28. package/src/accounts.ts +205 -0
  29. package/src/actions/handle-action.guild-admin.ts +421 -0
  30. package/src/actions/handle-action.ts +402 -0
  31. package/src/actions/runtime.guild.ts +446 -0
  32. package/src/actions/runtime.messaging.messages.ts +226 -0
  33. package/src/actions/runtime.messaging.reactions.ts +67 -0
  34. package/src/actions/runtime.messaging.runtime.ts +73 -0
  35. package/src/actions/runtime.messaging.send.ts +336 -0
  36. package/src/actions/runtime.messaging.shared.ts +97 -0
  37. package/src/actions/runtime.messaging.ts +37 -0
  38. package/src/actions/runtime.moderation-shared.ts +48 -0
  39. package/src/actions/runtime.moderation.ts +116 -0
  40. package/src/actions/runtime.presence.ts +117 -0
  41. package/src/actions/runtime.shared.ts +86 -0
  42. package/src/actions/runtime.ts +87 -0
  43. package/src/api.ts +219 -0
  44. package/src/approval-handler.runtime.ts +636 -0
  45. package/src/approval-native.ts +219 -0
  46. package/src/approval-runtime.ts +14 -0
  47. package/src/approval-shared.ts +56 -0
  48. package/src/audit-core.ts +178 -0
  49. package/src/audit.ts +32 -0
  50. package/src/channel-actions.runtime.ts +1 -0
  51. package/src/channel-actions.ts +254 -0
  52. package/src/channel-api.ts +29 -0
  53. package/src/channel.conversation.ts +159 -0
  54. package/src/channel.loaders.ts +50 -0
  55. package/src/channel.runtime.ts +1 -0
  56. package/src/channel.setup.ts +12 -0
  57. package/src/channel.ts +728 -0
  58. package/src/chunk.ts +321 -0
  59. package/src/client.ts +143 -0
  60. package/src/component-custom-id.ts +72 -0
  61. package/src/components-registry.ts +356 -0
  62. package/src/components.builders.ts +410 -0
  63. package/src/components.modal.ts +124 -0
  64. package/src/components.parse.ts +407 -0
  65. package/src/components.ts +54 -0
  66. package/src/components.types.ts +187 -0
  67. package/src/config-schema.ts +6 -0
  68. package/src/config-ui-hints.ts +354 -0
  69. package/src/conversation-identity.ts +58 -0
  70. package/src/delivery-retry.ts +56 -0
  71. package/src/directory-cache.ts +116 -0
  72. package/src/directory-config.ts +58 -0
  73. package/src/directory-live.ts +135 -0
  74. package/src/doctor-contract.ts +477 -0
  75. package/src/doctor-shared.ts +5 -0
  76. package/src/doctor.ts +340 -0
  77. package/src/draft-chunking.ts +43 -0
  78. package/src/draft-stream.ts +162 -0
  79. package/src/error-body.ts +38 -0
  80. package/src/exec-approvals.ts +110 -0
  81. package/src/gateway-logging.ts +67 -0
  82. package/src/group-policy.ts +113 -0
  83. package/src/guilds.ts +29 -0
  84. package/src/inbound-event-delivery.ts +135 -0
  85. package/src/interactive-dispatch.ts +104 -0
  86. package/src/internal/api.commands.ts +51 -0
  87. package/src/internal/api.guild.ts +164 -0
  88. package/src/internal/api.interactions.ts +53 -0
  89. package/src/internal/api.messages.ts +113 -0
  90. package/src/internal/api.reactions.ts +38 -0
  91. package/src/internal/api.ts +61 -0
  92. package/src/internal/api.users.ts +19 -0
  93. package/src/internal/api.webhooks.ts +13 -0
  94. package/src/internal/client.ts +310 -0
  95. package/src/internal/command-deploy.ts +352 -0
  96. package/src/internal/commands.ts +188 -0
  97. package/src/internal/components.base.ts +65 -0
  98. package/src/internal/components.message.ts +279 -0
  99. package/src/internal/components.modal.ts +95 -0
  100. package/src/internal/components.ts +31 -0
  101. package/src/internal/discord.ts +11 -0
  102. package/src/internal/embeds.ts +35 -0
  103. package/src/internal/entity-cache.ts +98 -0
  104. package/src/internal/event-queue.ts +185 -0
  105. package/src/internal/gateway-close-codes.ts +25 -0
  106. package/src/internal/gateway-dispatch.ts +96 -0
  107. package/src/internal/gateway-identify-limiter.ts +26 -0
  108. package/src/internal/gateway-lifecycle.ts +75 -0
  109. package/src/internal/gateway-rate-limit.ts +104 -0
  110. package/src/internal/gateway.ts +479 -0
  111. package/src/internal/interaction-dispatch.ts +162 -0
  112. package/src/internal/interaction-options.ts +98 -0
  113. package/src/internal/interaction-response.ts +53 -0
  114. package/src/internal/interactions.ts +378 -0
  115. package/src/internal/listeners.ts +91 -0
  116. package/src/internal/modal-fields.ts +95 -0
  117. package/src/internal/payload.ts +69 -0
  118. package/src/internal/rest-body.ts +115 -0
  119. package/src/internal/rest-errors.ts +88 -0
  120. package/src/internal/rest-routes.ts +50 -0
  121. package/src/internal/rest-scheduler.ts +557 -0
  122. package/src/internal/rest.ts +322 -0
  123. package/src/internal/schemas.ts +36 -0
  124. package/src/internal/structures.ts +280 -0
  125. package/src/internal/test-builders.test-support.ts +167 -0
  126. package/src/internal/voice.ts +49 -0
  127. package/src/media-detection.ts +28 -0
  128. package/src/mentions.ts +147 -0
  129. package/src/monitor/ack-reactions.ts +70 -0
  130. package/src/monitor/agent-components-auth.ts +7 -0
  131. package/src/monitor/agent-components-context.ts +154 -0
  132. package/src/monitor/agent-components-data.ts +224 -0
  133. package/src/monitor/agent-components-dm-auth.ts +177 -0
  134. package/src/monitor/agent-components-guild-auth.ts +322 -0
  135. package/src/monitor/agent-components-helpers.runtime.ts +3 -0
  136. package/src/monitor/agent-components-helpers.ts +34 -0
  137. package/src/monitor/agent-components-reply.ts +10 -0
  138. package/src/monitor/agent-components.deps.runtime.ts +2 -0
  139. package/src/monitor/agent-components.dispatch.ts +359 -0
  140. package/src/monitor/agent-components.handlers.ts +303 -0
  141. package/src/monitor/agent-components.modal.ts +160 -0
  142. package/src/monitor/agent-components.plugin-interactive.ts +187 -0
  143. package/src/monitor/agent-components.runtime.ts +14 -0
  144. package/src/monitor/agent-components.system-controls.ts +215 -0
  145. package/src/monitor/agent-components.ts +70 -0
  146. package/src/monitor/agent-components.types.ts +58 -0
  147. package/src/monitor/agent-components.wildcard-controls.ts +171 -0
  148. package/src/monitor/allow-list.ts +631 -0
  149. package/src/monitor/auto-presence.ts +356 -0
  150. package/src/monitor/channel-access.ts +102 -0
  151. package/src/monitor/commands.ts +9 -0
  152. package/src/monitor/dm-command-auth.ts +259 -0
  153. package/src/monitor/dm-command-decision.ts +49 -0
  154. package/src/monitor/exec-approvals.ts +161 -0
  155. package/src/monitor/format.ts +45 -0
  156. package/src/monitor/gateway-handle.ts +34 -0
  157. package/src/monitor/gateway-metadata.ts +298 -0
  158. package/src/monitor/gateway-plugin.ts +302 -0
  159. package/src/monitor/gateway-registry.ts +37 -0
  160. package/src/monitor/gateway-supervisor.ts +206 -0
  161. package/src/monitor/inbound-context.ts +95 -0
  162. package/src/monitor/inbound-dedupe.ts +79 -0
  163. package/src/monitor/inbound-job.ts +118 -0
  164. package/src/monitor/listeners.queue.ts +91 -0
  165. package/src/monitor/listeners.reactions.ts +594 -0
  166. package/src/monitor/listeners.ts +150 -0
  167. package/src/monitor/message-channel-info.ts +96 -0
  168. package/src/monitor/message-forwarded.ts +114 -0
  169. package/src/monitor/message-handler.batch-gate.ts +19 -0
  170. package/src/monitor/message-handler.context.ts +492 -0
  171. package/src/monitor/message-handler.dm-preflight.ts +119 -0
  172. package/src/monitor/message-handler.draft-preview.ts +436 -0
  173. package/src/monitor/message-handler.hydration.ts +198 -0
  174. package/src/monitor/message-handler.module-test-helpers.ts +31 -0
  175. package/src/monitor/message-handler.preflight-channel-access.ts +86 -0
  176. package/src/monitor/message-handler.preflight-channel-context.ts +58 -0
  177. package/src/monitor/message-handler.preflight-context.ts +54 -0
  178. package/src/monitor/message-handler.preflight-helpers.ts +164 -0
  179. package/src/monitor/message-handler.preflight-history.ts +23 -0
  180. package/src/monitor/message-handler.preflight-logging.ts +36 -0
  181. package/src/monitor/message-handler.preflight-pluralkit.ts +28 -0
  182. package/src/monitor/message-handler.preflight-runtime.ts +28 -0
  183. package/src/monitor/message-handler.preflight-thread.ts +49 -0
  184. package/src/monitor/message-handler.preflight.ts +822 -0
  185. package/src/monitor/message-handler.preflight.types.ts +115 -0
  186. package/src/monitor/message-handler.process.ts +1033 -0
  187. package/src/monitor/message-handler.routing-preflight.ts +112 -0
  188. package/src/monitor/message-handler.ts +309 -0
  189. package/src/monitor/message-media.ts +536 -0
  190. package/src/monitor/message-run-queue.ts +101 -0
  191. package/src/monitor/message-text.ts +171 -0
  192. package/src/monitor/message-utils.ts +34 -0
  193. package/src/monitor/model-picker-preferences.ts +184 -0
  194. package/src/monitor/model-picker.state.ts +364 -0
  195. package/src/monitor/model-picker.test-utils.ts +26 -0
  196. package/src/monitor/model-picker.ts +38 -0
  197. package/src/monitor/model-picker.view.ts +722 -0
  198. package/src/monitor/native-command-agent-reply.ts +125 -0
  199. package/src/monitor/native-command-arg-ui.ts +233 -0
  200. package/src/monitor/native-command-auth.ts +309 -0
  201. package/src/monitor/native-command-bypass.ts +13 -0
  202. package/src/monitor/native-command-context.ts +109 -0
  203. package/src/monitor/native-command-dispatch.ts +35 -0
  204. package/src/monitor/native-command-model-picker-apply.ts +209 -0
  205. package/src/monitor/native-command-model-picker-interaction.ts +516 -0
  206. package/src/monitor/native-command-model-picker-ui.ts +357 -0
  207. package/src/monitor/native-command-reply.ts +185 -0
  208. package/src/monitor/native-command-route.ts +91 -0
  209. package/src/monitor/native-command-status.ts +76 -0
  210. package/src/monitor/native-command-ui.ts +26 -0
  211. package/src/monitor/native-command-ui.types.ts +20 -0
  212. package/src/monitor/native-command.args.ts +45 -0
  213. package/src/monitor/native-command.options.ts +153 -0
  214. package/src/monitor/native-command.runtime.ts +51 -0
  215. package/src/monitor/native-command.ts +747 -0
  216. package/src/monitor/native-command.types.ts +9 -0
  217. package/src/monitor/native-interaction-channel-context.ts +50 -0
  218. package/src/monitor/preflight-audio.runtime.ts +9 -0
  219. package/src/monitor/preflight-audio.ts +130 -0
  220. package/src/monitor/presence-cache.ts +61 -0
  221. package/src/monitor/presence.ts +50 -0
  222. package/src/monitor/provider-session.runtime.ts +12 -0
  223. package/src/monitor/provider.acp.ts +89 -0
  224. package/src/monitor/provider.allowlist.ts +398 -0
  225. package/src/monitor/provider.cleanup.ts +41 -0
  226. package/src/monitor/provider.commands.ts +129 -0
  227. package/src/monitor/provider.config-log.ts +45 -0
  228. package/src/monitor/provider.deploy-errors.ts +362 -0
  229. package/src/monitor/provider.deploy.ts +221 -0
  230. package/src/monitor/provider.interactions.ts +160 -0
  231. package/src/monitor/provider.lifecycle.ts +562 -0
  232. package/src/monitor/provider.runtime.ts +1 -0
  233. package/src/monitor/provider.startup-log.ts +32 -0
  234. package/src/monitor/provider.startup.ts +323 -0
  235. package/src/monitor/provider.ts +688 -0
  236. package/src/monitor/reply-context.ts +64 -0
  237. package/src/monitor/reply-delivery.ts +216 -0
  238. package/src/monitor/reply-safety.ts +96 -0
  239. package/src/monitor/rest-fetch.ts +97 -0
  240. package/src/monitor/route-resolution.ts +140 -0
  241. package/src/monitor/sender-identity.ts +81 -0
  242. package/src/monitor/startup-status.ts +10 -0
  243. package/src/monitor/status.ts +22 -0
  244. package/src/monitor/system-events.ts +55 -0
  245. package/src/monitor/thread-bindings.config.ts +35 -0
  246. package/src/monitor/thread-bindings.discord-api.ts +310 -0
  247. package/src/monitor/thread-bindings.lifecycle.ts +354 -0
  248. package/src/monitor/thread-bindings.manager.ts +554 -0
  249. package/src/monitor/thread-bindings.messages.ts +6 -0
  250. package/src/monitor/thread-bindings.persona.ts +25 -0
  251. package/src/monitor/thread-bindings.session-adapter.ts +229 -0
  252. package/src/monitor/thread-bindings.session-shared.ts +59 -0
  253. package/src/monitor/thread-bindings.session-updates.ts +35 -0
  254. package/src/monitor/thread-bindings.state.ts +540 -0
  255. package/src/monitor/thread-bindings.ts +48 -0
  256. package/src/monitor/thread-bindings.types.ts +83 -0
  257. package/src/monitor/thread-channel-context.ts +112 -0
  258. package/src/monitor/thread-session-close.ts +63 -0
  259. package/src/monitor/thread-title.ts +181 -0
  260. package/src/monitor/threading.auto-thread.ts +287 -0
  261. package/src/monitor/threading.cache.ts +45 -0
  262. package/src/monitor/threading.starter.ts +288 -0
  263. package/src/monitor/threading.ts +20 -0
  264. package/src/monitor/threading.types.ts +102 -0
  265. package/src/monitor/timeouts.ts +84 -0
  266. package/src/monitor/typing.ts +17 -0
  267. package/src/monitor.gateway.ts +75 -0
  268. package/src/monitor.ts +28 -0
  269. package/src/network-config.ts +79 -0
  270. package/src/normalize.ts +86 -0
  271. package/src/outbound-adapter.ts +327 -0
  272. package/src/outbound-approval.ts +29 -0
  273. package/src/outbound-components.ts +86 -0
  274. package/src/outbound-payload.ts +208 -0
  275. package/src/outbound-send-context.ts +92 -0
  276. package/src/outbound-session-route.ts +72 -0
  277. package/src/pluralkit.ts +58 -0
  278. package/src/preview-streaming.ts +18 -0
  279. package/src/probe.runtime.ts +1 -0
  280. package/src/probe.ts +237 -0
  281. package/src/proxy-fetch.ts +92 -0
  282. package/src/proxy-request-client.ts +21 -0
  283. package/src/recipient-resolution.ts +39 -0
  284. package/src/resolve-allowlist-common.ts +39 -0
  285. package/src/resolve-channels.ts +369 -0
  286. package/src/resolve-users.ts +184 -0
  287. package/src/retry.ts +98 -0
  288. package/src/runtime-api.ts +64 -0
  289. package/src/runtime-config.ts +16 -0
  290. package/src/runtime.ts +23 -0
  291. package/src/secret-config-contract.ts +140 -0
  292. package/src/security-audit.runtime.ts +1 -0
  293. package/src/security-audit.ts +208 -0
  294. package/src/security-contract.ts +47 -0
  295. package/src/security-doctor.ts +20 -0
  296. package/src/security.ts +60 -0
  297. package/src/send-target-parsing.ts +14 -0
  298. package/src/send.channels.ts +139 -0
  299. package/src/send.components.ts +391 -0
  300. package/src/send.emojis-stickers.ts +57 -0
  301. package/src/send.guild.ts +170 -0
  302. package/src/send.message-request.ts +112 -0
  303. package/src/send.messages.ts +229 -0
  304. package/src/send.outbound.ts +459 -0
  305. package/src/send.permissions.ts +283 -0
  306. package/src/send.reactions.ts +155 -0
  307. package/src/send.receipt.ts +69 -0
  308. package/src/send.shared.ts +469 -0
  309. package/src/send.ts +82 -0
  310. package/src/send.types.ts +191 -0
  311. package/src/send.typing.ts +9 -0
  312. package/src/send.voice.ts +140 -0
  313. package/src/send.webhook.ts +137 -0
  314. package/src/session-contract.ts +3 -0
  315. package/src/session-key-normalization.ts +47 -0
  316. package/src/setup-account-state.ts +144 -0
  317. package/src/setup-adapter.ts +14 -0
  318. package/src/setup-core.ts +215 -0
  319. package/src/setup-runtime-helpers.ts +10 -0
  320. package/src/setup-surface.ts +132 -0
  321. package/src/shared-interactive.ts +167 -0
  322. package/src/shared.ts +197 -0
  323. package/src/status-issues.ts +201 -0
  324. package/src/subagent-hooks.ts +232 -0
  325. package/src/target-parsing.ts +70 -0
  326. package/src/target-resolver.ts +129 -0
  327. package/src/targets.ts +12 -0
  328. package/src/token.ts +107 -0
  329. package/src/ui-colors.ts +27 -0
  330. package/src/ui.ts +20 -0
  331. package/src/voice/access.ts +126 -0
  332. package/src/voice/audio.ts +249 -0
  333. package/src/voice/capture-state.ts +120 -0
  334. package/src/voice/command.ts +284 -0
  335. package/src/voice/config.ts +8 -0
  336. package/src/voice/ingress.ts +164 -0
  337. package/src/voice/manager.runtime.ts +14 -0
  338. package/src/voice/manager.ts +1155 -0
  339. package/src/voice/prompt.ts +22 -0
  340. package/src/voice/realtime.ts +1370 -0
  341. package/src/voice/receive-recovery.ts +159 -0
  342. package/src/voice/sanitize.ts +29 -0
  343. package/src/voice/sdk-runtime.ts +14 -0
  344. package/src/voice/segment.ts +160 -0
  345. package/src/voice/session.ts +81 -0
  346. package/src/voice/speaker-context.ts +127 -0
  347. package/src/voice/tts.ts +151 -0
  348. package/src/voice-message.ts +474 -0
  349. package/subagent-hooks-api.ts +27 -0
  350. package/test-api.ts +4 -0
  351. package/thread-binding-api.ts +1 -0
  352. package/timeouts.ts +6 -0
  353. package/tsconfig.json +16 -0
@@ -0,0 +1,288 @@
1
+ import type { ReplyToMode } from "autobot/plugin-sdk/config-contracts";
2
+ import { createReplyReferencePlanner } from "autobot/plugin-sdk/reply-reference";
3
+ import { normalizeOptionalString } from "autobot/plugin-sdk/string-coerce-runtime";
4
+ import { truncateUtf16Safe } from "autobot/plugin-sdk/text-utility-runtime";
5
+ import { ChannelType, getChannelMessage, type Client } from "../internal/discord.js";
6
+ import {
7
+ resolveDiscordChannelIdSafe,
8
+ resolveDiscordChannelNameSafe,
9
+ resolveDiscordChannelParentIdSafe,
10
+ resolveDiscordChannelParentSafe,
11
+ } from "./channel-access.js";
12
+ import {
13
+ resolveDiscordChannelInfo,
14
+ resolveDiscordEmbedText,
15
+ resolveDiscordForwardedMessagesTextFromSnapshots,
16
+ resolveDiscordMessageChannelId,
17
+ type DiscordChannelInfo,
18
+ type DiscordChannelInfoClient,
19
+ } from "./message-utils.js";
20
+ import { getCachedThreadStarter, setCachedThreadStarter } from "./threading.cache.js";
21
+ import type {
22
+ DiscordMessageEvent,
23
+ DiscordReplyDeliveryPlan,
24
+ DiscordThreadChannel,
25
+ DiscordThreadParentInfo,
26
+ DiscordThreadStarter,
27
+ DiscordThreadStarterRestAuthor,
28
+ DiscordThreadStarterRestMember,
29
+ DiscordThreadStarterRestMessage,
30
+ } from "./threading.types.js";
31
+
32
+ function isDiscordThreadType(type: ChannelType | undefined): boolean {
33
+ return (
34
+ type === ChannelType.PublicThread ||
35
+ type === ChannelType.PrivateThread ||
36
+ type === ChannelType.AnnouncementThread
37
+ );
38
+ }
39
+
40
+ function isDiscordForumParentType(parentType: ChannelType | undefined): boolean {
41
+ return parentType === ChannelType.GuildForum || parentType === ChannelType.GuildMedia;
42
+ }
43
+
44
+ export function resolveDiscordThreadChannel(params: {
45
+ isGuildMessage: boolean;
46
+ message: DiscordMessageEvent["message"];
47
+ channelInfo: DiscordChannelInfo | null;
48
+ messageChannelId?: string;
49
+ }): DiscordThreadChannel | null {
50
+ if (!params.isGuildMessage) {
51
+ return null;
52
+ }
53
+ const { message, channelInfo } = params;
54
+ const channel = "channel" in message ? (message as { channel?: unknown }).channel : undefined;
55
+ const isThreadChannel =
56
+ channel &&
57
+ typeof channel === "object" &&
58
+ "isThread" in channel &&
59
+ typeof (channel as { isThread?: unknown }).isThread === "function" &&
60
+ (channel as { isThread: () => boolean }).isThread();
61
+ if (isThreadChannel) {
62
+ return channel as unknown as DiscordThreadChannel;
63
+ }
64
+ if (!isDiscordThreadType(channelInfo?.type)) {
65
+ return null;
66
+ }
67
+ const messageChannelId =
68
+ params.messageChannelId ||
69
+ resolveDiscordMessageChannelId({
70
+ message,
71
+ });
72
+ if (!messageChannelId) {
73
+ return null;
74
+ }
75
+ return {
76
+ id: messageChannelId,
77
+ name: channelInfo?.name ?? undefined,
78
+ parentId: channelInfo?.parentId ?? undefined,
79
+ parent: undefined,
80
+ ownerId: channelInfo?.ownerId ?? undefined,
81
+ };
82
+ }
83
+
84
+ export async function resolveDiscordThreadParentInfo(params: {
85
+ client: DiscordChannelInfoClient;
86
+ threadChannel: DiscordThreadChannel;
87
+ channelInfo: DiscordChannelInfo | null;
88
+ }): Promise<DiscordThreadParentInfo> {
89
+ const { threadChannel, channelInfo, client } = params;
90
+ const parent = resolveDiscordChannelParentSafe(threadChannel);
91
+ let parentId =
92
+ resolveDiscordChannelParentIdSafe(threadChannel) ??
93
+ resolveDiscordChannelIdSafe(parent) ??
94
+ channelInfo?.parentId ??
95
+ undefined;
96
+ if (!parentId && threadChannel.id) {
97
+ const threadInfo = await resolveDiscordChannelInfo(client, threadChannel.id);
98
+ parentId = threadInfo?.parentId ?? undefined;
99
+ }
100
+ if (!parentId) {
101
+ return {};
102
+ }
103
+ let parentName = resolveDiscordChannelNameSafe(parent);
104
+ const parentInfo = await resolveDiscordChannelInfo(client, parentId);
105
+ parentName = parentName ?? parentInfo?.name;
106
+ const parentType = parentInfo?.type;
107
+ return { id: parentId, name: parentName, type: parentType };
108
+ }
109
+
110
+ export async function resolveDiscordThreadStarter(params: {
111
+ channel: DiscordThreadChannel;
112
+ client: Client;
113
+ parentId?: string;
114
+ parentType?: ChannelType;
115
+ resolveTimestampMs: (value?: string | null) => number | undefined;
116
+ }): Promise<DiscordThreadStarter | null> {
117
+ const cacheKey = params.channel.id;
118
+ const now = Date.now();
119
+ const cached = getCachedThreadStarter(cacheKey, now);
120
+ if (cached) {
121
+ return cached;
122
+ }
123
+ try {
124
+ const messageChannelId = resolveDiscordThreadStarterMessageChannelId(params);
125
+ if (!messageChannelId) {
126
+ return null;
127
+ }
128
+ const starter = await fetchDiscordThreadStarterMessage({
129
+ client: params.client,
130
+ messageChannelId,
131
+ threadId: params.channel.id,
132
+ });
133
+ if (!starter) {
134
+ return null;
135
+ }
136
+ const payload = buildDiscordThreadStarterPayload({
137
+ starter,
138
+ resolveTimestampMs: params.resolveTimestampMs,
139
+ });
140
+ if (!payload) {
141
+ return null;
142
+ }
143
+ setCachedThreadStarter(cacheKey, payload, Date.now());
144
+ return payload;
145
+ } catch {
146
+ return null;
147
+ }
148
+ }
149
+
150
+ function resolveDiscordThreadStarterMessageChannelId(params: {
151
+ channel: DiscordThreadChannel;
152
+ parentId?: string;
153
+ parentType?: ChannelType;
154
+ }): string | undefined {
155
+ return isDiscordForumParentType(params.parentType) ? params.channel.id : params.parentId;
156
+ }
157
+
158
+ async function fetchDiscordThreadStarterMessage(params: {
159
+ client: Client;
160
+ messageChannelId: string;
161
+ threadId: string;
162
+ }): Promise<DiscordThreadStarterRestMessage | null> {
163
+ const starter = await getChannelMessage(
164
+ params.client.rest,
165
+ params.messageChannelId,
166
+ params.threadId,
167
+ );
168
+ return starter ? (starter as DiscordThreadStarterRestMessage) : null;
169
+ }
170
+
171
+ function buildDiscordThreadStarterPayload(params: {
172
+ starter: DiscordThreadStarterRestMessage;
173
+ resolveTimestampMs: (value?: string | null) => number | undefined;
174
+ }): DiscordThreadStarter | null {
175
+ const text = resolveDiscordThreadStarterText(params.starter);
176
+ if (!text) {
177
+ return null;
178
+ }
179
+ return {
180
+ text,
181
+ ...resolveDiscordThreadStarterIdentity(params.starter),
182
+ timestamp: params.resolveTimestampMs(params.starter.timestamp) ?? undefined,
183
+ };
184
+ }
185
+
186
+ function resolveDiscordThreadStarterText(starter: DiscordThreadStarterRestMessage): string {
187
+ const content = normalizeOptionalString(starter.content) ?? "";
188
+ const embedText = resolveDiscordEmbedText(starter.embeds?.[0]);
189
+ const forwardedText = resolveDiscordForwardedMessagesTextFromSnapshots(starter.message_snapshots);
190
+ return content || embedText || forwardedText;
191
+ }
192
+
193
+ function resolveDiscordThreadStarterIdentity(
194
+ starter: DiscordThreadStarterRestMessage,
195
+ ): Omit<DiscordThreadStarter, "text" | "timestamp"> {
196
+ const author = resolveDiscordThreadStarterAuthor(starter);
197
+ return {
198
+ author,
199
+ authorId: starter.author?.id ?? undefined,
200
+ authorName: starter.author?.username ?? undefined,
201
+ authorTag: resolveDiscordThreadStarterAuthorTag(starter.author),
202
+ memberRoleIds: resolveDiscordThreadStarterRoleIds(starter.member),
203
+ };
204
+ }
205
+
206
+ function resolveDiscordThreadStarterAuthor(starter: DiscordThreadStarterRestMessage): string {
207
+ return (
208
+ starter.member?.nick ??
209
+ starter.member?.displayName ??
210
+ resolveDiscordThreadStarterAuthorTag(starter.author) ??
211
+ starter.author?.username ??
212
+ starter.author?.id ??
213
+ "Unknown"
214
+ );
215
+ }
216
+
217
+ function resolveDiscordThreadStarterAuthorTag(
218
+ author: DiscordThreadStarterRestAuthor | null | undefined,
219
+ ): string | undefined {
220
+ if (!author?.username || !author.discriminator) {
221
+ return undefined;
222
+ }
223
+ if (author.discriminator !== "0") {
224
+ return `${author.username}#${author.discriminator}`;
225
+ }
226
+ return author.username;
227
+ }
228
+
229
+ function resolveDiscordThreadStarterRoleIds(
230
+ member: DiscordThreadStarterRestMember | null | undefined,
231
+ ): string[] | undefined {
232
+ return Array.isArray(member?.roles) ? member.roles : undefined;
233
+ }
234
+
235
+ export function resolveDiscordReplyTarget(opts: {
236
+ replyToMode: ReplyToMode;
237
+ replyToId?: string;
238
+ hasReplied: boolean;
239
+ }): string | undefined {
240
+ if (opts.replyToMode === "off") {
241
+ return undefined;
242
+ }
243
+ const replyToId = normalizeOptionalString(opts.replyToId);
244
+ if (!replyToId) {
245
+ return undefined;
246
+ }
247
+ if (opts.replyToMode === "all") {
248
+ return replyToId;
249
+ }
250
+ return opts.hasReplied ? undefined : replyToId;
251
+ }
252
+
253
+ export function sanitizeDiscordThreadName(rawName: string, fallbackId: string): string {
254
+ const cleanedName = rawName
255
+ .replace(/<@!?\d+>/g, "")
256
+ .replace(/<@&\d+>/g, "")
257
+ .replace(/<#\d+>/g, "")
258
+ .replace(/\s+/g, " ")
259
+ .trim();
260
+ const baseSource = cleanedName || `Thread ${fallbackId}`;
261
+ const base = truncateUtf16Safe(baseSource, 80);
262
+ return truncateUtf16Safe(base, 100) || `Thread ${fallbackId}`;
263
+ }
264
+
265
+ export function resolveDiscordReplyDeliveryPlan(params: {
266
+ replyTarget: string;
267
+ replyToMode: ReplyToMode;
268
+ messageId: string;
269
+ threadChannel?: DiscordThreadChannel | null;
270
+ createdThreadId?: string | null;
271
+ }): DiscordReplyDeliveryPlan {
272
+ const originalReplyTarget = params.replyTarget;
273
+ let deliverTarget = originalReplyTarget;
274
+ let replyTarget = originalReplyTarget;
275
+
276
+ if (params.createdThreadId) {
277
+ deliverTarget = `channel:${params.createdThreadId}`;
278
+ replyTarget = deliverTarget;
279
+ }
280
+ const allowReference = deliverTarget === originalReplyTarget;
281
+ const replyReference = createReplyReferencePlanner({
282
+ replyToMode: allowReference ? params.replyToMode : "off",
283
+ existingId: params.threadChannel ? params.messageId : undefined,
284
+ startId: params.messageId,
285
+ allowReference,
286
+ });
287
+ return { deliverTarget, replyTarget, replyReference };
288
+ }
@@ -0,0 +1,20 @@
1
+ export {
2
+ maybeCreateDiscordAutoThread,
3
+ resolveDiscordAutoThreadContext,
4
+ resolveDiscordAutoThreadReplyPlan,
5
+ } from "./threading.auto-thread.js";
6
+ export { resetDiscordThreadStarterCacheForTest } from "./threading.cache.js";
7
+ export {
8
+ resolveDiscordReplyDeliveryPlan,
9
+ resolveDiscordReplyTarget,
10
+ resolveDiscordThreadChannel,
11
+ resolveDiscordThreadParentInfo,
12
+ resolveDiscordThreadStarter,
13
+ sanitizeDiscordThreadName,
14
+ } from "./threading.starter.js";
15
+ export type {
16
+ DiscordAutoThreadContext,
17
+ DiscordAutoThreadReplyPlan,
18
+ DiscordThreadChannel,
19
+ DiscordThreadStarter,
20
+ } from "./threading.types.js";
@@ -0,0 +1,102 @@
1
+ import type { APIAttachment, APIStickerItem } from "discord-api-types/v10";
2
+ import type { AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
3
+ import type { createReplyReferencePlanner } from "autobot/plugin-sdk/reply-reference";
4
+ import type { ChannelType, Client, MessageCreateListener } from "../internal/discord.js";
5
+ import type { DiscordChannelConfigResolved } from "./allow-list.js";
6
+
7
+ export type DiscordThreadChannel = {
8
+ id: string;
9
+ name?: string | null;
10
+ parentId?: string | null;
11
+ parent?: { id?: string; name?: string };
12
+ ownerId?: string | null;
13
+ };
14
+
15
+ export type DiscordThreadStarter = {
16
+ text: string;
17
+ author: string;
18
+ authorId?: string;
19
+ authorName?: string;
20
+ authorTag?: string;
21
+ memberRoleIds?: string[];
22
+ timestamp?: number;
23
+ };
24
+
25
+ export type DiscordThreadParentInfo = {
26
+ id?: string;
27
+ name?: string;
28
+ type?: ChannelType;
29
+ };
30
+
31
+ type DiscordThreadStarterRestEmbed = {
32
+ title?: string | null;
33
+ description?: string | null;
34
+ };
35
+
36
+ type DiscordThreadStarterRestSnapshotMessage = {
37
+ content?: string | null;
38
+ attachments?: APIAttachment[] | null;
39
+ embeds?: DiscordThreadStarterRestEmbed[] | null;
40
+ sticker_items?: APIStickerItem[] | null;
41
+ };
42
+
43
+ export type DiscordThreadStarterRestAuthor = {
44
+ id?: string | null;
45
+ username?: string | null;
46
+ discriminator?: string | null;
47
+ };
48
+
49
+ export type DiscordThreadStarterRestMember = {
50
+ nick?: string | null;
51
+ displayName?: string | null;
52
+ roles?: string[];
53
+ };
54
+
55
+ export type DiscordThreadStarterRestMessage = {
56
+ content?: string | null;
57
+ embeds?: DiscordThreadStarterRestEmbed[] | null;
58
+ message_snapshots?: Array<{ message?: DiscordThreadStarterRestSnapshotMessage | null }> | null;
59
+ member?: DiscordThreadStarterRestMember | null;
60
+ author?: DiscordThreadStarterRestAuthor | null;
61
+ timestamp?: string | null;
62
+ };
63
+
64
+ export type DiscordMessageEvent = Parameters<MessageCreateListener["handle"]>[0];
65
+
66
+ export type DiscordReplyDeliveryPlan = {
67
+ deliverTarget: string;
68
+ replyTarget: string;
69
+ replyReference: ReturnType<typeof createReplyReferencePlanner>;
70
+ };
71
+
72
+ export type DiscordAutoThreadContext = {
73
+ createdThreadId: string;
74
+ From: string;
75
+ To: string;
76
+ OriginatingTo: string;
77
+ SessionKey: string;
78
+ ModelParentSessionKey?: string;
79
+ ParentSessionKey?: string;
80
+ };
81
+
82
+ export type DiscordAutoThreadReplyPlan = DiscordReplyDeliveryPlan & {
83
+ createdThreadId?: string;
84
+ autoThreadContext: DiscordAutoThreadContext | null;
85
+ };
86
+
87
+ export type MaybeCreateDiscordAutoThreadParams = {
88
+ client: Client;
89
+ message: DiscordMessageEvent["message"];
90
+ messageChannelId?: string;
91
+ channel?: string;
92
+ isGuildMessage: boolean;
93
+ channelConfig?: DiscordChannelConfigResolved | null;
94
+ threadChannel?: DiscordThreadChannel | null;
95
+ channelType?: ChannelType;
96
+ channelName?: string;
97
+ channelDescription?: string;
98
+ baseText: string;
99
+ combinedBody: string;
100
+ cfg: AutoBotConfig;
101
+ agentId?: string;
102
+ };
@@ -0,0 +1,84 @@
1
+ // Compatibility constants for existing imports. Discord no longer enforces
2
+ // channel-owned listener or inbound run timeouts.
3
+ export const DISCORD_DEFAULT_LISTENER_TIMEOUT_MS = 120_000;
4
+ export const DISCORD_DEFAULT_INBOUND_WORKER_TIMEOUT_MS = 30 * 60_000;
5
+
6
+ export const DISCORD_ATTACHMENT_IDLE_TIMEOUT_MS = 60_000;
7
+ export const DISCORD_ATTACHMENT_TOTAL_TIMEOUT_MS = 120_000;
8
+
9
+ export function mergeAbortSignals(
10
+ signals: Array<AbortSignal | undefined>,
11
+ ): AbortSignal | undefined {
12
+ const activeSignals = signals.filter((signal): signal is AbortSignal => Boolean(signal));
13
+ if (activeSignals.length === 0) {
14
+ return undefined;
15
+ }
16
+ if (activeSignals.length === 1) {
17
+ return activeSignals[0];
18
+ }
19
+ if (typeof AbortSignal.any === "function") {
20
+ return AbortSignal.any(activeSignals);
21
+ }
22
+ const fallbackController = new AbortController();
23
+ for (const signal of activeSignals) {
24
+ if (signal.aborted) {
25
+ fallbackController.abort();
26
+ return fallbackController.signal;
27
+ }
28
+ }
29
+ const abortFallback = () => {
30
+ fallbackController.abort();
31
+ for (const signal of activeSignals) {
32
+ signal.removeEventListener("abort", abortFallback);
33
+ }
34
+ };
35
+ for (const signal of activeSignals) {
36
+ signal.addEventListener("abort", abortFallback, { once: true });
37
+ }
38
+ return fallbackController.signal;
39
+ }
40
+
41
+ export async function raceWithTimeout<T, U>(params: {
42
+ promise: Promise<T>;
43
+ timeoutMs: number;
44
+ onTimeout: () => U;
45
+ }): Promise<T | U> {
46
+ let timeoutTimer: ReturnType<typeof setTimeout> | undefined;
47
+ const timeoutPromise = new Promise<U>((resolve) => {
48
+ timeoutTimer = setTimeout(() => resolve(params.onTimeout()), Math.max(1, params.timeoutMs));
49
+ timeoutTimer.unref?.();
50
+ });
51
+ try {
52
+ return await Promise.race([params.promise, timeoutPromise]);
53
+ } finally {
54
+ if (timeoutTimer) {
55
+ clearTimeout(timeoutTimer);
56
+ }
57
+ }
58
+ }
59
+
60
+ export async function withAbortTimeout<T>(params: {
61
+ timeoutMs: number;
62
+ createTimeoutError: () => Error;
63
+ run: (signal: AbortSignal) => Promise<T>;
64
+ }): Promise<T> {
65
+ const controller = new AbortController();
66
+ let timeoutTimer: ReturnType<typeof setTimeout> | undefined;
67
+ const timeoutPromise = new Promise<never>((_, reject) => {
68
+ timeoutTimer = setTimeout(
69
+ () => {
70
+ controller.abort();
71
+ reject(params.createTimeoutError());
72
+ },
73
+ Math.max(1, params.timeoutMs),
74
+ );
75
+ timeoutTimer.unref?.();
76
+ });
77
+ try {
78
+ return await Promise.race([params.run(controller.signal), timeoutPromise]);
79
+ } finally {
80
+ if (timeoutTimer) {
81
+ clearTimeout(timeoutTimer);
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,17 @@
1
+ import { sendChannelTyping, type RequestClient } from "../internal/discord.js";
2
+ import { raceWithTimeout } from "./timeouts.js";
3
+
4
+ const DISCORD_TYPING_START_TIMEOUT_MS = 5_000;
5
+
6
+ export async function sendTyping(params: { rest: RequestClient; channelId: string }) {
7
+ const result = await raceWithTimeout({
8
+ promise: sendChannelTyping(params.rest, params.channelId).then(() => ({
9
+ kind: "sent" as const,
10
+ })),
11
+ timeoutMs: DISCORD_TYPING_START_TIMEOUT_MS,
12
+ onTimeout: () => ({ kind: "timeout" as const }),
13
+ });
14
+ if (result.kind === "timeout") {
15
+ throw new Error(`discord typing start timed out after ${DISCORD_TYPING_START_TIMEOUT_MS}ms`);
16
+ }
17
+ }
@@ -0,0 +1,75 @@
1
+ import type { DiscordGatewayHandle } from "./monitor/gateway-handle.js";
2
+ import {
3
+ DiscordGatewayEvent,
4
+ DiscordGatewayLifecycleError,
5
+ DiscordGatewaySupervisor,
6
+ } from "./monitor/gateway-supervisor.js";
7
+
8
+ export { getDiscordGatewayEmitter } from "./monitor/gateway-supervisor.js";
9
+
10
+ export type WaitForDiscordGatewayStopParams = {
11
+ gateway?: DiscordGatewayHandle;
12
+ abortSignal?: AbortSignal;
13
+ gatewaySupervisor?: Pick<DiscordGatewaySupervisor, "attachLifecycle" | "detachLifecycle">;
14
+ onGatewayEvent?: (event: DiscordGatewayEvent) => "continue" | "stop";
15
+ registerForceStop?: (forceStop: (err: unknown) => void) => void;
16
+ };
17
+
18
+ export async function waitForDiscordGatewayStop(
19
+ params: WaitForDiscordGatewayStopParams,
20
+ ): Promise<void> {
21
+ const { gateway, abortSignal } = params;
22
+ return await new Promise<void>((resolve, reject) => {
23
+ let settled = false;
24
+ const cleanup = () => {
25
+ abortSignal?.removeEventListener("abort", onAbort);
26
+ params.gatewaySupervisor?.detachLifecycle();
27
+ };
28
+ const finishResolve = () => {
29
+ if (settled) {
30
+ return;
31
+ }
32
+ settled = true;
33
+ try {
34
+ gateway?.disconnect?.();
35
+ } finally {
36
+ // remove listeners after disconnect so late "error" events emitted
37
+ // during disconnect are still handled instead of becoming uncaught
38
+ cleanup();
39
+ resolve();
40
+ }
41
+ };
42
+ const finishReject = (err: unknown) => {
43
+ if (settled) {
44
+ return;
45
+ }
46
+ settled = true;
47
+ try {
48
+ gateway?.disconnect?.();
49
+ } finally {
50
+ cleanup();
51
+ reject(err);
52
+ }
53
+ };
54
+ const onAbort = () => {
55
+ finishResolve();
56
+ };
57
+ const onGatewayEvent = (event: DiscordGatewayEvent) => {
58
+ const shouldStop = (params.onGatewayEvent?.(event) ?? "stop") === "stop";
59
+ if (shouldStop) {
60
+ finishReject(new DiscordGatewayLifecycleError(event));
61
+ }
62
+ };
63
+ const onForceStop = (err: unknown) => {
64
+ finishReject(err);
65
+ };
66
+ if (abortSignal?.aborted) {
67
+ onAbort();
68
+ return;
69
+ }
70
+
71
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
72
+ params.gatewaySupervisor?.attachLifecycle(onGatewayEvent);
73
+ params.registerForceStop?.(onForceStop);
74
+ });
75
+ }
package/src/monitor.ts ADDED
@@ -0,0 +1,28 @@
1
+ export type {
2
+ DiscordAllowList,
3
+ DiscordChannelConfigResolved,
4
+ DiscordGuildEntryResolved,
5
+ } from "./monitor/allow-list.js";
6
+ export {
7
+ allowListMatches,
8
+ isDiscordGroupAllowedByPolicy,
9
+ normalizeDiscordAllowList,
10
+ normalizeDiscordSlug,
11
+ resolveDiscordChannelConfig,
12
+ resolveDiscordChannelConfigWithFallback,
13
+ resolveDiscordCommandAuthorized,
14
+ resolveDiscordGuildEntry,
15
+ resolveDiscordShouldRequireMention,
16
+ resolveGroupDmAllow,
17
+ shouldEmitDiscordReactionNotification,
18
+ } from "./monitor/allow-list.js";
19
+ export type { DiscordMessageEvent, DiscordMessageHandler } from "./monitor/listeners.js";
20
+ export { registerDiscordListener } from "./monitor/listeners.js";
21
+
22
+ export { createDiscordMessageHandler } from "./monitor/message-handler.js";
23
+ export { buildDiscordMediaPayload } from "./monitor/message-utils.js";
24
+ export { createDiscordNativeCommand } from "./monitor/native-command.js";
25
+ export type { MonitorDiscordOpts } from "./monitor/provider.js";
26
+ export { monitorDiscordProvider } from "./monitor/provider.js";
27
+
28
+ export { resolveDiscordReplyTarget, sanitizeDiscordThreadName } from "./monitor/threading.js";
@@ -0,0 +1,79 @@
1
+ import * as dns from "node:dns";
2
+ import type { LookupFunction } from "node:net";
3
+
4
+ const DISCORD_DNS_HOSTS = ["discord.com", "discord.gg", "gateway.discord.gg"];
5
+
6
+ function normalizeHostname(hostname: string): string {
7
+ return hostname.trim().toLowerCase();
8
+ }
9
+
10
+ function isDiscordTransportHostname(hostname: string): boolean {
11
+ const normalized = normalizeHostname(hostname);
12
+ if (!normalized) {
13
+ return false;
14
+ }
15
+ return DISCORD_DNS_HOSTS.some(
16
+ (target) => normalized === target || normalized.endsWith(`.${target}`),
17
+ );
18
+ }
19
+
20
+ function reorderLookupAddresses(addresses: dns.LookupAddress[]): dns.LookupAddress[] {
21
+ if (!Array.isArray(addresses) || addresses.length < 2) {
22
+ return addresses;
23
+ }
24
+ const ipv4 = addresses.filter((entry) => entry.family === 4);
25
+ const ipv6 = addresses.filter((entry) => entry.family === 6);
26
+ if (ipv4.length === 0) {
27
+ return ipv6;
28
+ }
29
+ if (ipv6.length === 0) {
30
+ return ipv4;
31
+ }
32
+ return [...ipv4, ...ipv6];
33
+ }
34
+
35
+ export function createDiscordDnsLookup(): LookupFunction {
36
+ return (hostname, options, callback) => {
37
+ if (!isDiscordTransportHostname(hostname)) {
38
+ return dns.lookup(hostname, options, callback);
39
+ }
40
+
41
+ const lookupOptions: dns.LookupOptions =
42
+ typeof options === "number"
43
+ ? { family: options }
44
+ : options === undefined
45
+ ? {}
46
+ : ({ ...options } as dns.LookupOptions);
47
+
48
+ if (lookupOptions.family === 4 || lookupOptions.family === 6) {
49
+ return dns.lookup(hostname, lookupOptions, callback as never);
50
+ }
51
+
52
+ dns.lookup(hostname, { ...lookupOptions, all: true }, (err, addresses) => {
53
+ if (err) {
54
+ callback(err, "", 4);
55
+ return;
56
+ }
57
+ if (!Array.isArray(addresses)) {
58
+ callback(new Error("Expected all lookup addresses to be an array"), "", 4);
59
+ return;
60
+ }
61
+
62
+ const reordered = reorderLookupAddresses(addresses);
63
+ if (lookupOptions.all === true) {
64
+ (callback as (err: NodeJS.ErrnoException | null, addresses: dns.LookupAddress[]) => void)(
65
+ null,
66
+ reordered,
67
+ );
68
+ return;
69
+ }
70
+
71
+ const first = reordered[0];
72
+ if (!first) {
73
+ callback(new Error("No Discord DNS addresses resolved"), "", 4);
74
+ return;
75
+ }
76
+ callback(null, first.address, first.family);
77
+ });
78
+ };
79
+ }