@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,562 @@
1
+ import {
2
+ createConnectedChannelStatusPatch,
3
+ createTransportActivityStatusPatch,
4
+ } from "autobot/plugin-sdk/gateway-runtime";
5
+ import { danger } from "autobot/plugin-sdk/runtime-env";
6
+ import type { RuntimeEnv } from "autobot/plugin-sdk/runtime-env";
7
+ import { attachDiscordGatewayLogging } from "../gateway-logging.js";
8
+ import { getDiscordGatewayEmitter, waitForDiscordGatewayStop } from "../monitor.gateway.js";
9
+ import type { DiscordVoiceManager } from "../voice/manager.js";
10
+ import {
11
+ DISCORD_GATEWAY_TRANSPORT_ACTIVITY_EVENT,
12
+ type MutableDiscordGateway,
13
+ } from "./gateway-handle.js";
14
+ import { registerGateway, unregisterGateway } from "./gateway-registry.js";
15
+ import {
16
+ DiscordGatewayLifecycleError,
17
+ type DiscordGatewayEvent,
18
+ type DiscordGatewaySupervisor,
19
+ } from "./gateway-supervisor.js";
20
+ import type { DiscordMonitorStatusSink } from "./status.js";
21
+
22
+ const DEFAULT_DISCORD_GATEWAY_READY_TIMEOUT_MS = 15_000;
23
+ const DEFAULT_DISCORD_GATEWAY_RUNTIME_READY_TIMEOUT_MS = 30_000;
24
+ const MAX_DISCORD_GATEWAY_READY_TIMEOUT_MS = 120_000;
25
+ const DISCORD_GATEWAY_READY_TIMEOUT_ENV = "AUTOBOT_DISCORD_READY_TIMEOUT_MS";
26
+ const DISCORD_GATEWAY_RUNTIME_READY_TIMEOUT_ENV = "AUTOBOT_DISCORD_RUNTIME_READY_TIMEOUT_MS";
27
+ const DISCORD_GATEWAY_READY_POLL_MS = 250;
28
+ const DISCORD_GATEWAY_READY_RETRY_BACKOFF_MS = 2_000;
29
+ const DISCORD_GATEWAY_STARTUP_DISCONNECT_DRAIN_TIMEOUT_MS = 5_000;
30
+ const DISCORD_GATEWAY_STARTUP_TERMINATE_CLOSE_TIMEOUT_MS = 1_000;
31
+ const DISCORD_GATEWAY_TRANSPORT_ACTIVITY_STATUS_MIN_INTERVAL_MS = 30_000;
32
+
33
+ type GatewayReadyWaitResult = "ready" | "stopped" | "timeout";
34
+
35
+ function normalizeGatewayReadyTimeoutMs(value: unknown): number | undefined {
36
+ const numeric =
37
+ typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
38
+ if (!Number.isFinite(numeric) || numeric <= 0) {
39
+ return undefined;
40
+ }
41
+ return Math.min(Math.floor(numeric), MAX_DISCORD_GATEWAY_READY_TIMEOUT_MS);
42
+ }
43
+
44
+ export function resolveDiscordGatewayReadyTimeoutMs(params?: {
45
+ configuredTimeoutMs?: number;
46
+ env?: NodeJS.ProcessEnv;
47
+ }): number {
48
+ return (
49
+ normalizeGatewayReadyTimeoutMs(params?.configuredTimeoutMs) ??
50
+ normalizeGatewayReadyTimeoutMs(params?.env?.[DISCORD_GATEWAY_READY_TIMEOUT_ENV]) ??
51
+ DEFAULT_DISCORD_GATEWAY_READY_TIMEOUT_MS
52
+ );
53
+ }
54
+
55
+ export function resolveDiscordGatewayRuntimeReadyTimeoutMs(params?: {
56
+ configuredTimeoutMs?: number;
57
+ env?: NodeJS.ProcessEnv;
58
+ }): number {
59
+ return (
60
+ normalizeGatewayReadyTimeoutMs(params?.configuredTimeoutMs) ??
61
+ normalizeGatewayReadyTimeoutMs(params?.env?.[DISCORD_GATEWAY_RUNTIME_READY_TIMEOUT_ENV]) ??
62
+ DEFAULT_DISCORD_GATEWAY_RUNTIME_READY_TIMEOUT_MS
63
+ );
64
+ }
65
+
66
+ async function restartGatewayAfterReadyTimeout(params: {
67
+ gateway?: Pick<MutableDiscordGateway, "connect" | "disconnect" | "ws">;
68
+ abortSignal?: AbortSignal;
69
+ runtime: RuntimeEnv;
70
+ }): Promise<void> {
71
+ if (!params.gateway || params.abortSignal?.aborted) {
72
+ return;
73
+ }
74
+
75
+ const socket = params.gateway.ws;
76
+ if (!socket) {
77
+ params.gateway.disconnect();
78
+ if (!params.abortSignal?.aborted) {
79
+ params.gateway.connect(false);
80
+ }
81
+ return;
82
+ }
83
+
84
+ await new Promise<void>((resolve, reject) => {
85
+ let settled = false;
86
+ let drainTimeout: ReturnType<typeof setTimeout> | undefined;
87
+ let terminateCloseTimeout: ReturnType<typeof setTimeout> | undefined;
88
+ const ignoreSocketError = () => {};
89
+ const clearTimers = () => {
90
+ if (drainTimeout) {
91
+ clearTimeout(drainTimeout);
92
+ drainTimeout = undefined;
93
+ }
94
+ if (terminateCloseTimeout) {
95
+ clearTimeout(terminateCloseTimeout);
96
+ terminateCloseTimeout = undefined;
97
+ }
98
+ };
99
+ const cleanup = () => {
100
+ clearTimers();
101
+ socket.removeListener("close", onClose);
102
+ socket.removeListener("error", ignoreSocketError);
103
+ };
104
+ const finishResolve = () => {
105
+ if (settled) {
106
+ return;
107
+ }
108
+ settled = true;
109
+ cleanup();
110
+ resolve();
111
+ };
112
+ const finishReject = (error: Error) => {
113
+ if (params.abortSignal?.aborted) {
114
+ finishResolve();
115
+ return;
116
+ }
117
+ if (settled) {
118
+ return;
119
+ }
120
+ settled = true;
121
+ cleanup();
122
+ reject(error);
123
+ };
124
+ const onClose = () => {
125
+ finishResolve();
126
+ };
127
+
128
+ socket.on("error", ignoreSocketError);
129
+ socket.on("close", onClose);
130
+ params.gateway?.disconnect();
131
+
132
+ drainTimeout = setTimeout(() => {
133
+ if (settled) {
134
+ return;
135
+ }
136
+ if (typeof socket.terminate !== "function") {
137
+ finishReject(
138
+ new Error(
139
+ `discord gateway socket did not close within ${DISCORD_GATEWAY_STARTUP_DISCONNECT_DRAIN_TIMEOUT_MS}ms before restart`,
140
+ ),
141
+ );
142
+ return;
143
+ }
144
+ params.runtime.error?.(
145
+ danger(
146
+ `discord: startup restart waiting on a stale gateway socket for ${DISCORD_GATEWAY_STARTUP_DISCONNECT_DRAIN_TIMEOUT_MS}ms; forcing terminate before reconnect`,
147
+ ),
148
+ );
149
+ try {
150
+ socket.terminate();
151
+ } catch {
152
+ finishReject(
153
+ new Error(
154
+ `discord gateway socket did not close within ${DISCORD_GATEWAY_STARTUP_DISCONNECT_DRAIN_TIMEOUT_MS}ms before restart`,
155
+ ),
156
+ );
157
+ return;
158
+ }
159
+ terminateCloseTimeout = setTimeout(() => {
160
+ finishReject(
161
+ new Error(
162
+ `discord gateway socket did not close within ${DISCORD_GATEWAY_STARTUP_DISCONNECT_DRAIN_TIMEOUT_MS}ms before restart`,
163
+ ),
164
+ );
165
+ }, DISCORD_GATEWAY_STARTUP_TERMINATE_CLOSE_TIMEOUT_MS);
166
+ terminateCloseTimeout.unref?.();
167
+ }, DISCORD_GATEWAY_STARTUP_DISCONNECT_DRAIN_TIMEOUT_MS);
168
+ drainTimeout.unref?.();
169
+ });
170
+
171
+ if (!params.abortSignal?.aborted) {
172
+ params.gateway.connect(false);
173
+ }
174
+ }
175
+
176
+ function parseGatewayCloseCode(message: string): number | undefined {
177
+ const match = /Gateway websocket closed:\s*(\d{3,5})/.exec(message);
178
+ if (!match?.[1]) {
179
+ return undefined;
180
+ }
181
+ const code = Number.parseInt(match[1], 10);
182
+ return Number.isFinite(code) ? code : undefined;
183
+ }
184
+
185
+ function resolveTransportActivityAt(event: unknown): number {
186
+ const at = (event as { at?: unknown } | undefined)?.at;
187
+ return typeof at === "number" && Number.isFinite(at) && at >= 0 ? at : Date.now();
188
+ }
189
+
190
+ function createGatewayStatusObserver(params: {
191
+ gateway?: Pick<MutableDiscordGateway, "isConnected">;
192
+ abortSignal?: AbortSignal;
193
+ runtime: RuntimeEnv;
194
+ pushStatus: (patch: Parameters<DiscordMonitorStatusSink>[0]) => void;
195
+ isLifecycleStopping: () => boolean;
196
+ runtimeReadyTimeoutMs: number;
197
+ }) {
198
+ let forceStopHandler: ((err: unknown) => void) | undefined;
199
+ let queuedForceStopError: unknown;
200
+ let readyPollId: ReturnType<typeof setInterval> | undefined;
201
+ let readyTimeoutId: ReturnType<typeof setTimeout> | undefined;
202
+
203
+ const shouldStop = () => params.abortSignal?.aborted || params.isLifecycleStopping();
204
+ const clearReadyWatch = () => {
205
+ if (readyPollId) {
206
+ clearInterval(readyPollId);
207
+ readyPollId = undefined;
208
+ }
209
+ if (readyTimeoutId) {
210
+ clearTimeout(readyTimeoutId);
211
+ readyTimeoutId = undefined;
212
+ }
213
+ };
214
+ const triggerForceStop = (err: unknown) => {
215
+ if (forceStopHandler) {
216
+ forceStopHandler(err);
217
+ return;
218
+ }
219
+ queuedForceStopError = err;
220
+ };
221
+ const pushConnectedStatus = (at: number) => {
222
+ params.pushStatus({
223
+ ...createConnectedChannelStatusPatch(at),
224
+ lastDisconnect: null,
225
+ lastError: null,
226
+ });
227
+ };
228
+ const startReadyWatch = () => {
229
+ clearReadyWatch();
230
+ const pollConnected = () => {
231
+ if (shouldStop()) {
232
+ clearReadyWatch();
233
+ return;
234
+ }
235
+ if (!params.gateway?.isConnected) {
236
+ return;
237
+ }
238
+ clearReadyWatch();
239
+ pushConnectedStatus(Date.now());
240
+ };
241
+
242
+ pollConnected();
243
+ if (!readyTimeoutId) {
244
+ readyPollId = setInterval(pollConnected, DISCORD_GATEWAY_READY_POLL_MS);
245
+ readyPollId.unref?.();
246
+ readyTimeoutId = setTimeout(() => {
247
+ clearReadyWatch();
248
+ if (shouldStop() || params.gateway?.isConnected) {
249
+ return;
250
+ }
251
+ const at = Date.now();
252
+ const error = new Error(
253
+ `discord gateway opened but did not reach READY within ${params.runtimeReadyTimeoutMs}ms`,
254
+ );
255
+ params.pushStatus({
256
+ connected: false,
257
+ lastEventAt: at,
258
+ lastDisconnect: {
259
+ at,
260
+ error: "runtime-not-ready",
261
+ },
262
+ lastError: "runtime-not-ready",
263
+ });
264
+ params.runtime.error?.(danger(error.message));
265
+ triggerForceStop(error);
266
+ }, params.runtimeReadyTimeoutMs);
267
+ readyTimeoutId.unref?.();
268
+ }
269
+ };
270
+
271
+ const onGatewayDebug = (msg: unknown) => {
272
+ if (shouldStop()) {
273
+ return;
274
+ }
275
+ const at = Date.now();
276
+ const message = String(msg);
277
+ if (message.includes("Gateway websocket opened")) {
278
+ params.pushStatus({ connected: false, lastEventAt: at });
279
+ startReadyWatch();
280
+ return;
281
+ }
282
+ if (message.includes("Gateway websocket closed")) {
283
+ clearReadyWatch();
284
+ const code = parseGatewayCloseCode(message);
285
+ params.pushStatus({
286
+ connected: false,
287
+ lastEventAt: at,
288
+ lastDisconnect: {
289
+ at,
290
+ ...(code !== undefined ? { status: code } : {}),
291
+ },
292
+ });
293
+ return;
294
+ }
295
+ if (message.includes("Gateway reconnect scheduled in")) {
296
+ clearReadyWatch();
297
+ params.pushStatus({
298
+ connected: false,
299
+ lastEventAt: at,
300
+ lastError: message,
301
+ });
302
+ }
303
+ };
304
+
305
+ return {
306
+ onGatewayDebug,
307
+ clearReadyWatch,
308
+ registerForceStop: (handler: (err: unknown) => void) => {
309
+ forceStopHandler = handler;
310
+ if (queuedForceStopError !== undefined) {
311
+ const err = queuedForceStopError;
312
+ queuedForceStopError = undefined;
313
+ handler(err);
314
+ }
315
+ },
316
+ dispose: () => {
317
+ clearReadyWatch();
318
+ forceStopHandler = undefined;
319
+ queuedForceStopError = undefined;
320
+ },
321
+ };
322
+ }
323
+
324
+ async function waitForGatewayReady(params: {
325
+ gateway?: Pick<MutableDiscordGateway, "connect" | "disconnect" | "isConnected" | "ws">;
326
+ abortSignal?: AbortSignal;
327
+ beforePoll?: () => Promise<"continue" | "stop"> | "continue" | "stop";
328
+ pushStatus?: (patch: Parameters<DiscordMonitorStatusSink>[0]) => void;
329
+ runtime: RuntimeEnv;
330
+ beforeRestart?: () => Promise<void> | void;
331
+ readyTimeoutMs: number;
332
+ }): Promise<void> {
333
+ const waitUntilReady = async (): Promise<GatewayReadyWaitResult> => {
334
+ const deadlineAt = Date.now() + params.readyTimeoutMs;
335
+ while (!params.abortSignal?.aborted) {
336
+ if ((await params.beforePoll?.()) === "stop") {
337
+ return "stopped";
338
+ }
339
+ if (params.gateway?.isConnected === true) {
340
+ const at = Date.now();
341
+ params.pushStatus?.({
342
+ ...createConnectedChannelStatusPatch(at),
343
+ lastDisconnect: null,
344
+ lastError: null,
345
+ });
346
+ return "ready";
347
+ }
348
+ if (Date.now() >= deadlineAt) {
349
+ return "timeout";
350
+ }
351
+ await new Promise<void>((resolve) => {
352
+ const timeout = setTimeout(resolve, DISCORD_GATEWAY_READY_POLL_MS);
353
+ timeout.unref?.();
354
+ });
355
+ }
356
+ return "stopped";
357
+ };
358
+
359
+ if (!params.gateway) {
360
+ const attempt = await waitUntilReady();
361
+ if (attempt === "timeout") {
362
+ throw new Error(`discord gateway did not reach READY within ${params.readyTimeoutMs}ms`);
363
+ }
364
+ return;
365
+ }
366
+
367
+ let attempt = 0;
368
+ while (!params.abortSignal?.aborted) {
369
+ const result = await waitUntilReady();
370
+ if (result !== "timeout") {
371
+ return;
372
+ }
373
+
374
+ attempt += 1;
375
+ const restartAt = Date.now();
376
+ params.runtime.error?.(
377
+ danger(
378
+ `discord: gateway READY wait timed out after ${params.readyTimeoutMs}ms; reconnecting with backoff (attempt ${attempt})`,
379
+ ),
380
+ );
381
+ params.pushStatus?.({
382
+ connected: false,
383
+ lastEventAt: restartAt,
384
+ lastDisconnect: {
385
+ at: restartAt,
386
+ error: "startup-not-ready",
387
+ },
388
+ lastError: "startup-not-ready",
389
+ });
390
+ await params.beforeRestart?.();
391
+ await restartGatewayAfterReadyTimeout({
392
+ gateway: params.gateway,
393
+ abortSignal: params.abortSignal,
394
+ runtime: params.runtime,
395
+ });
396
+ if (params.abortSignal?.aborted) {
397
+ return;
398
+ }
399
+ await new Promise<void>((resolve) => {
400
+ const timeout = setTimeout(resolve, DISCORD_GATEWAY_READY_RETRY_BACKOFF_MS);
401
+ timeout.unref?.();
402
+ });
403
+ }
404
+ }
405
+
406
+ export async function runDiscordGatewayLifecycle(params: {
407
+ accountId: string;
408
+ gateway?: MutableDiscordGateway;
409
+ runtime: RuntimeEnv;
410
+ abortSignal?: AbortSignal;
411
+ isDisallowedIntentsError: (err: unknown) => boolean;
412
+ voiceManager: DiscordVoiceManager | null;
413
+ voiceManagerRef: { current: DiscordVoiceManager | null };
414
+ threadBindings: { stop: () => void };
415
+ gatewaySupervisor: DiscordGatewaySupervisor;
416
+ statusSink?: DiscordMonitorStatusSink;
417
+ gatewayReadyTimeoutMs?: number;
418
+ gatewayRuntimeReadyTimeoutMs?: number;
419
+ }) {
420
+ const gateway = params.gateway;
421
+ if (gateway) {
422
+ registerGateway(params.accountId, gateway);
423
+ }
424
+ const gatewayEmitter = params.gatewaySupervisor.emitter ?? getDiscordGatewayEmitter(gateway);
425
+ const stopGatewayLogging = attachDiscordGatewayLogging({
426
+ emitter: gatewayEmitter,
427
+ runtime: params.runtime,
428
+ });
429
+ let lifecycleStopping = false;
430
+
431
+ const pushStatus = (patch: Parameters<DiscordMonitorStatusSink>[0]) => {
432
+ params.statusSink?.(patch);
433
+ };
434
+ const gatewayReadyTimeoutMs = resolveDiscordGatewayReadyTimeoutMs({
435
+ configuredTimeoutMs: params.gatewayReadyTimeoutMs,
436
+ env: process.env,
437
+ });
438
+ const gatewayRuntimeReadyTimeoutMs = resolveDiscordGatewayRuntimeReadyTimeoutMs({
439
+ configuredTimeoutMs: params.gatewayRuntimeReadyTimeoutMs,
440
+ env: process.env,
441
+ });
442
+ const statusObserver = createGatewayStatusObserver({
443
+ gateway,
444
+ abortSignal: params.abortSignal,
445
+ runtime: params.runtime,
446
+ pushStatus,
447
+ isLifecycleStopping: () => lifecycleStopping,
448
+ runtimeReadyTimeoutMs: gatewayRuntimeReadyTimeoutMs,
449
+ });
450
+ gatewayEmitter?.on("debug", statusObserver.onGatewayDebug);
451
+ let lastTransportActivityStatusAt: number | undefined;
452
+ const onGatewayTransportActivity = (event: unknown) => {
453
+ if (lifecycleStopping || params.abortSignal?.aborted) {
454
+ return;
455
+ }
456
+ const at = resolveTransportActivityAt(event);
457
+ if (
458
+ lastTransportActivityStatusAt !== undefined &&
459
+ at - lastTransportActivityStatusAt < DISCORD_GATEWAY_TRANSPORT_ACTIVITY_STATUS_MIN_INTERVAL_MS
460
+ ) {
461
+ return;
462
+ }
463
+ lastTransportActivityStatusAt = at;
464
+ pushStatus(createTransportActivityStatusPatch(at));
465
+ };
466
+ gatewayEmitter?.on(DISCORD_GATEWAY_TRANSPORT_ACTIVITY_EVENT, onGatewayTransportActivity);
467
+
468
+ let sawDisallowedIntents = false;
469
+ const handleGatewayEvent = (event: DiscordGatewayEvent): "continue" | "stop" => {
470
+ if (params.abortSignal?.aborted && event.type === "reconnect-exhausted") {
471
+ lifecycleStopping = true;
472
+ params.runtime.log?.(
473
+ `discord: treating reconnect-exhausted during expected shutdown as clean: ${event.message}`,
474
+ );
475
+ return "continue";
476
+ }
477
+ if (event.type === "disallowed-intents") {
478
+ lifecycleStopping = true;
479
+ sawDisallowedIntents = true;
480
+ params.runtime.error?.(
481
+ danger(
482
+ "discord: gateway closed with code 4014 (missing privileged gateway intents). Enable the required intents in the Discord Developer Portal or disable them in config.",
483
+ ),
484
+ );
485
+ return "stop";
486
+ }
487
+ if (event.shouldStopLifecycle) {
488
+ lifecycleStopping = true;
489
+ }
490
+ params.runtime.error?.(
491
+ danger(
492
+ event.shouldStopLifecycle
493
+ ? `discord gateway ${event.type}: ${event.message}`
494
+ : `discord gateway error: ${event.message}`,
495
+ ),
496
+ );
497
+ return event.shouldStopLifecycle ? "stop" : "continue";
498
+ };
499
+ const drainPendingGatewayErrors = (): "continue" | "stop" =>
500
+ params.gatewaySupervisor.drainPending((event) => {
501
+ const decision = handleGatewayEvent(event);
502
+ if (decision !== "stop") {
503
+ return "continue";
504
+ }
505
+ if (event.type === "disallowed-intents") {
506
+ return "stop";
507
+ }
508
+ throw new DiscordGatewayLifecycleError(event);
509
+ });
510
+ try {
511
+ // Drain gateway errors emitted before lifecycle listeners were attached.
512
+ if (drainPendingGatewayErrors() === "stop") {
513
+ return;
514
+ }
515
+
516
+ await waitForGatewayReady({
517
+ gateway,
518
+ abortSignal: params.abortSignal,
519
+ beforePoll: drainPendingGatewayErrors,
520
+ pushStatus,
521
+ runtime: params.runtime,
522
+ beforeRestart: statusObserver.clearReadyWatch,
523
+ readyTimeoutMs: gatewayReadyTimeoutMs,
524
+ });
525
+
526
+ if (drainPendingGatewayErrors() === "stop") {
527
+ return;
528
+ }
529
+
530
+ await waitForDiscordGatewayStop({
531
+ gateway: gateway
532
+ ? {
533
+ disconnect: () => gateway.disconnect(),
534
+ }
535
+ : undefined,
536
+ abortSignal: params.abortSignal,
537
+ gatewaySupervisor: params.gatewaySupervisor,
538
+ onGatewayEvent: handleGatewayEvent,
539
+ registerForceStop: statusObserver.registerForceStop,
540
+ });
541
+ } catch (err) {
542
+ if (!sawDisallowedIntents && !params.isDisallowedIntentsError(err)) {
543
+ throw err;
544
+ }
545
+ } finally {
546
+ lifecycleStopping = true;
547
+ params.gatewaySupervisor.detachLifecycle();
548
+ unregisterGateway(params.accountId);
549
+ stopGatewayLogging();
550
+ statusObserver.dispose();
551
+ gatewayEmitter?.removeListener("debug", statusObserver.onGatewayDebug);
552
+ gatewayEmitter?.removeListener(
553
+ DISCORD_GATEWAY_TRANSPORT_ACTIVITY_EVENT,
554
+ onGatewayTransportActivity,
555
+ );
556
+ if (params.voiceManager) {
557
+ await params.voiceManager.destroy();
558
+ params.voiceManagerRef.current = null;
559
+ }
560
+ params.threadBindings.stop();
561
+ }
562
+ }
@@ -0,0 +1 @@
1
+ export { monitorDiscordProvider } from "./provider.js";
@@ -0,0 +1,32 @@
1
+ import { isVerbose, type RuntimeEnv } from "autobot/plugin-sdk/runtime-env";
2
+ import type { GatewayPlugin } from "../internal/gateway.js";
3
+
4
+ function formatDiscordStartupGatewayState(gateway?: GatewayPlugin): string {
5
+ if (!gateway) {
6
+ return "gateway=missing";
7
+ }
8
+ const reconnectAttempts = (gateway as unknown as { reconnectAttempts?: unknown })
9
+ .reconnectAttempts;
10
+ return `gatewayConnected=${gateway.isConnected ? "true" : "false"} reconnectAttempts=${typeof reconnectAttempts === "number" ? reconnectAttempts : "na"}`;
11
+ }
12
+
13
+ export function logDiscordStartupPhase(params: {
14
+ runtime: RuntimeEnv;
15
+ accountId: string;
16
+ phase: string;
17
+ startAt: number;
18
+ gateway?: GatewayPlugin;
19
+ details?: string;
20
+ isVerbose?: () => boolean;
21
+ }) {
22
+ if (!(params.isVerbose ?? isVerbose)()) {
23
+ return;
24
+ }
25
+ const elapsedMs = Math.max(0, Date.now() - params.startAt);
26
+ const suffix = [params.details, formatDiscordStartupGatewayState(params.gateway)]
27
+ .filter((value): value is string => Boolean(value))
28
+ .join(" ");
29
+ params.runtime.log?.(
30
+ `discord startup [${params.accountId}] ${params.phase} ${elapsedMs}ms${suffix ? ` ${suffix}` : ""}`,
31
+ );
32
+ }