cowork-os 0.3.21

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 (526) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1638 -0
  3. package/bin/cowork.js +42 -0
  4. package/build/entitlements.mac.plist +16 -0
  5. package/build/icon.icns +0 -0
  6. package/build/icon.png +0 -0
  7. package/dist/electron/electron/activity/ActivityRepository.js +190 -0
  8. package/dist/electron/electron/agent/browser/browser-service.js +639 -0
  9. package/dist/electron/electron/agent/context-manager.js +225 -0
  10. package/dist/electron/electron/agent/custom-skill-loader.js +566 -0
  11. package/dist/electron/electron/agent/daemon.js +975 -0
  12. package/dist/electron/electron/agent/executor.js +3561 -0
  13. package/dist/electron/electron/agent/llm/anthropic-provider.js +155 -0
  14. package/dist/electron/electron/agent/llm/bedrock-provider.js +202 -0
  15. package/dist/electron/electron/agent/llm/gemini-provider.js +375 -0
  16. package/dist/electron/electron/agent/llm/index.js +34 -0
  17. package/dist/electron/electron/agent/llm/ollama-provider.js +263 -0
  18. package/dist/electron/electron/agent/llm/openai-oauth.js +101 -0
  19. package/dist/electron/electron/agent/llm/openai-provider.js +657 -0
  20. package/dist/electron/electron/agent/llm/openrouter-provider.js +232 -0
  21. package/dist/electron/electron/agent/llm/pricing.js +160 -0
  22. package/dist/electron/electron/agent/llm/provider-factory.js +880 -0
  23. package/dist/electron/electron/agent/llm/types.js +178 -0
  24. package/dist/electron/electron/agent/queue-manager.js +378 -0
  25. package/dist/electron/electron/agent/sandbox/docker-sandbox.js +402 -0
  26. package/dist/electron/electron/agent/sandbox/macos-sandbox.js +407 -0
  27. package/dist/electron/electron/agent/sandbox/runner.js +410 -0
  28. package/dist/electron/electron/agent/sandbox/sandbox-factory.js +228 -0
  29. package/dist/electron/electron/agent/sandbox/security-utils.js +258 -0
  30. package/dist/electron/electron/agent/search/brave-provider.js +119 -0
  31. package/dist/electron/electron/agent/search/google-provider.js +100 -0
  32. package/dist/electron/electron/agent/search/index.js +28 -0
  33. package/dist/electron/electron/agent/search/provider-factory.js +395 -0
  34. package/dist/electron/electron/agent/search/serpapi-provider.js +112 -0
  35. package/dist/electron/electron/agent/search/tavily-provider.js +90 -0
  36. package/dist/electron/electron/agent/search/types.js +40 -0
  37. package/dist/electron/electron/agent/security/index.js +12 -0
  38. package/dist/electron/electron/agent/security/input-sanitizer.js +303 -0
  39. package/dist/electron/electron/agent/security/output-filter.js +217 -0
  40. package/dist/electron/electron/agent/skill-eligibility.js +281 -0
  41. package/dist/electron/electron/agent/skill-registry.js +396 -0
  42. package/dist/electron/electron/agent/skills/document.js +878 -0
  43. package/dist/electron/electron/agent/skills/image-generator.js +225 -0
  44. package/dist/electron/electron/agent/skills/organizer.js +141 -0
  45. package/dist/electron/electron/agent/skills/presentation.js +367 -0
  46. package/dist/electron/electron/agent/skills/spreadsheet.js +165 -0
  47. package/dist/electron/electron/agent/tools/browser-tools.js +523 -0
  48. package/dist/electron/electron/agent/tools/builtin-settings.js +384 -0
  49. package/dist/electron/electron/agent/tools/canvas-tools.js +530 -0
  50. package/dist/electron/electron/agent/tools/cron-tools.js +577 -0
  51. package/dist/electron/electron/agent/tools/edit-tools.js +194 -0
  52. package/dist/electron/electron/agent/tools/file-tools.js +719 -0
  53. package/dist/electron/electron/agent/tools/glob-tools.js +283 -0
  54. package/dist/electron/electron/agent/tools/grep-tools.js +387 -0
  55. package/dist/electron/electron/agent/tools/image-tools.js +111 -0
  56. package/dist/electron/electron/agent/tools/mention-tools.js +282 -0
  57. package/dist/electron/electron/agent/tools/node-tools.js +476 -0
  58. package/dist/electron/electron/agent/tools/registry.js +2719 -0
  59. package/dist/electron/electron/agent/tools/search-tools.js +91 -0
  60. package/dist/electron/electron/agent/tools/shell-tools.js +574 -0
  61. package/dist/electron/electron/agent/tools/skill-tools.js +274 -0
  62. package/dist/electron/electron/agent/tools/system-tools.js +578 -0
  63. package/dist/electron/electron/agent/tools/web-fetch-tools.js +444 -0
  64. package/dist/electron/electron/agent/tools/x-tools.js +264 -0
  65. package/dist/electron/electron/agents/AgentRoleRepository.js +420 -0
  66. package/dist/electron/electron/agents/HeartbeatService.js +356 -0
  67. package/dist/electron/electron/agents/MentionRepository.js +197 -0
  68. package/dist/electron/electron/agents/TaskSubscriptionRepository.js +168 -0
  69. package/dist/electron/electron/agents/WorkingStateRepository.js +229 -0
  70. package/dist/electron/electron/canvas/canvas-manager.js +714 -0
  71. package/dist/electron/electron/canvas/canvas-preload.js +53 -0
  72. package/dist/electron/electron/canvas/canvas-protocol.js +195 -0
  73. package/dist/electron/electron/canvas/canvas-store.js +174 -0
  74. package/dist/electron/electron/canvas/index.js +13 -0
  75. package/dist/electron/electron/control-plane/client.js +364 -0
  76. package/dist/electron/electron/control-plane/handlers.js +572 -0
  77. package/dist/electron/electron/control-plane/index.js +41 -0
  78. package/dist/electron/electron/control-plane/node-manager.js +264 -0
  79. package/dist/electron/electron/control-plane/protocol.js +194 -0
  80. package/dist/electron/electron/control-plane/remote-client.js +437 -0
  81. package/dist/electron/electron/control-plane/server.js +640 -0
  82. package/dist/electron/electron/control-plane/settings.js +369 -0
  83. package/dist/electron/electron/control-plane/ssh-tunnel.js +549 -0
  84. package/dist/electron/electron/cron/index.js +30 -0
  85. package/dist/electron/electron/cron/schedule.js +190 -0
  86. package/dist/electron/electron/cron/service.js +614 -0
  87. package/dist/electron/electron/cron/store.js +155 -0
  88. package/dist/electron/electron/cron/types.js +82 -0
  89. package/dist/electron/electron/cron/webhook.js +258 -0
  90. package/dist/electron/electron/database/SecureSettingsRepository.js +444 -0
  91. package/dist/electron/electron/database/TaskLabelRepository.js +120 -0
  92. package/dist/electron/electron/database/repositories.js +1781 -0
  93. package/dist/electron/electron/database/schema.js +978 -0
  94. package/dist/electron/electron/extensions/index.js +33 -0
  95. package/dist/electron/electron/extensions/loader.js +313 -0
  96. package/dist/electron/electron/extensions/registry.js +485 -0
  97. package/dist/electron/electron/extensions/types.js +11 -0
  98. package/dist/electron/electron/gateway/channel-registry.js +1102 -0
  99. package/dist/electron/electron/gateway/channels/bluebubbles-client.js +479 -0
  100. package/dist/electron/electron/gateway/channels/bluebubbles.js +432 -0
  101. package/dist/electron/electron/gateway/channels/discord.js +975 -0
  102. package/dist/electron/electron/gateway/channels/email-client.js +593 -0
  103. package/dist/electron/electron/gateway/channels/email.js +443 -0
  104. package/dist/electron/electron/gateway/channels/google-chat.js +631 -0
  105. package/dist/electron/electron/gateway/channels/imessage-client.js +363 -0
  106. package/dist/electron/electron/gateway/channels/imessage.js +465 -0
  107. package/dist/electron/electron/gateway/channels/index.js +36 -0
  108. package/dist/electron/electron/gateway/channels/line-client.js +470 -0
  109. package/dist/electron/electron/gateway/channels/line.js +479 -0
  110. package/dist/electron/electron/gateway/channels/matrix-client.js +432 -0
  111. package/dist/electron/electron/gateway/channels/matrix.js +592 -0
  112. package/dist/electron/electron/gateway/channels/mattermost-client.js +394 -0
  113. package/dist/electron/electron/gateway/channels/mattermost.js +496 -0
  114. package/dist/electron/electron/gateway/channels/signal-client.js +500 -0
  115. package/dist/electron/electron/gateway/channels/signal.js +582 -0
  116. package/dist/electron/electron/gateway/channels/slack.js +415 -0
  117. package/dist/electron/electron/gateway/channels/teams.js +596 -0
  118. package/dist/electron/electron/gateway/channels/telegram.js +1390 -0
  119. package/dist/electron/electron/gateway/channels/twitch-client.js +502 -0
  120. package/dist/electron/electron/gateway/channels/twitch.js +396 -0
  121. package/dist/electron/electron/gateway/channels/types.js +8 -0
  122. package/dist/electron/electron/gateway/channels/whatsapp.js +953 -0
  123. package/dist/electron/electron/gateway/context-policy.js +268 -0
  124. package/dist/electron/electron/gateway/index.js +1063 -0
  125. package/dist/electron/electron/gateway/infrastructure.js +496 -0
  126. package/dist/electron/electron/gateway/router.js +2700 -0
  127. package/dist/electron/electron/gateway/security.js +375 -0
  128. package/dist/electron/electron/gateway/session.js +115 -0
  129. package/dist/electron/electron/gateway/tunnel.js +503 -0
  130. package/dist/electron/electron/guardrails/guardrail-manager.js +348 -0
  131. package/dist/electron/electron/hooks/gmail-watcher.js +300 -0
  132. package/dist/electron/electron/hooks/index.js +46 -0
  133. package/dist/electron/electron/hooks/mappings.js +381 -0
  134. package/dist/electron/electron/hooks/server.js +480 -0
  135. package/dist/electron/electron/hooks/settings.js +447 -0
  136. package/dist/electron/electron/hooks/types.js +41 -0
  137. package/dist/electron/electron/ipc/canvas-handlers.js +158 -0
  138. package/dist/electron/electron/ipc/handlers.js +3138 -0
  139. package/dist/electron/electron/ipc/mission-control-handlers.js +141 -0
  140. package/dist/electron/electron/main.js +448 -0
  141. package/dist/electron/electron/mcp/client/MCPClientManager.js +330 -0
  142. package/dist/electron/electron/mcp/client/MCPServerConnection.js +437 -0
  143. package/dist/electron/electron/mcp/client/transports/SSETransport.js +304 -0
  144. package/dist/electron/electron/mcp/client/transports/StdioTransport.js +307 -0
  145. package/dist/electron/electron/mcp/client/transports/WebSocketTransport.js +329 -0
  146. package/dist/electron/electron/mcp/host/MCPHostServer.js +354 -0
  147. package/dist/electron/electron/mcp/host/ToolAdapter.js +100 -0
  148. package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +497 -0
  149. package/dist/electron/electron/mcp/settings.js +446 -0
  150. package/dist/electron/electron/mcp/types.js +59 -0
  151. package/dist/electron/electron/memory/MemoryService.js +435 -0
  152. package/dist/electron/electron/notifications/index.js +17 -0
  153. package/dist/electron/electron/notifications/service.js +118 -0
  154. package/dist/electron/electron/notifications/store.js +144 -0
  155. package/dist/electron/electron/preload.js +842 -0
  156. package/dist/electron/electron/reports/StandupReportService.js +272 -0
  157. package/dist/electron/electron/security/concurrency.js +293 -0
  158. package/dist/electron/electron/security/index.js +15 -0
  159. package/dist/electron/electron/security/policy-manager.js +435 -0
  160. package/dist/electron/electron/settings/appearance-manager.js +193 -0
  161. package/dist/electron/electron/settings/personality-manager.js +724 -0
  162. package/dist/electron/electron/settings/x-manager.js +58 -0
  163. package/dist/electron/electron/tailscale/exposure.js +188 -0
  164. package/dist/electron/electron/tailscale/index.js +28 -0
  165. package/dist/electron/electron/tailscale/settings.js +205 -0
  166. package/dist/electron/electron/tailscale/tailscale.js +355 -0
  167. package/dist/electron/electron/tray/QuickInputWindow.js +568 -0
  168. package/dist/electron/electron/tray/TrayManager.js +895 -0
  169. package/dist/electron/electron/tray/index.js +9 -0
  170. package/dist/electron/electron/updater/index.js +6 -0
  171. package/dist/electron/electron/updater/update-manager.js +418 -0
  172. package/dist/electron/electron/utils/env-migration.js +209 -0
  173. package/dist/electron/electron/utils/process.js +102 -0
  174. package/dist/electron/electron/utils/rate-limiter.js +104 -0
  175. package/dist/electron/electron/utils/validation.js +419 -0
  176. package/dist/electron/electron/utils/x-cli.js +177 -0
  177. package/dist/electron/electron/voice/VoiceService.js +507 -0
  178. package/dist/electron/electron/voice/index.js +14 -0
  179. package/dist/electron/electron/voice/voice-settings-manager.js +359 -0
  180. package/dist/electron/shared/channelMessages.js +170 -0
  181. package/dist/electron/shared/types.js +1185 -0
  182. package/package.json +159 -0
  183. package/resources/skills/1password.json +10 -0
  184. package/resources/skills/add-documentation.json +31 -0
  185. package/resources/skills/analyze-csv.json +17 -0
  186. package/resources/skills/apple-notes.json +10 -0
  187. package/resources/skills/apple-reminders.json +10 -0
  188. package/resources/skills/auto-commenter.json +10 -0
  189. package/resources/skills/bear-notes.json +10 -0
  190. package/resources/skills/bird.json +35 -0
  191. package/resources/skills/blogwatcher.json +10 -0
  192. package/resources/skills/blucli.json +10 -0
  193. package/resources/skills/bluebubbles.json +10 -0
  194. package/resources/skills/camsnap.json +10 -0
  195. package/resources/skills/clean-imports.json +18 -0
  196. package/resources/skills/code-review.json +18 -0
  197. package/resources/skills/coding-agent.json +10 -0
  198. package/resources/skills/compare-files.json +23 -0
  199. package/resources/skills/convert-code.json +34 -0
  200. package/resources/skills/create-changelog.json +24 -0
  201. package/resources/skills/debug-error.json +17 -0
  202. package/resources/skills/dependency-check.json +10 -0
  203. package/resources/skills/discord.json +10 -0
  204. package/resources/skills/eightctl.json +10 -0
  205. package/resources/skills/explain-code.json +29 -0
  206. package/resources/skills/extract-todos.json +18 -0
  207. package/resources/skills/food-order.json +10 -0
  208. package/resources/skills/gemini.json +10 -0
  209. package/resources/skills/generate-readme.json +10 -0
  210. package/resources/skills/gifgrep.json +10 -0
  211. package/resources/skills/git-commit.json +10 -0
  212. package/resources/skills/github.json +10 -0
  213. package/resources/skills/gog.json +10 -0
  214. package/resources/skills/goplaces.json +10 -0
  215. package/resources/skills/himalaya.json +10 -0
  216. package/resources/skills/imsg.json +10 -0
  217. package/resources/skills/karpathy-guidelines.json +12 -0
  218. package/resources/skills/last30days.json +26 -0
  219. package/resources/skills/local-places.json +10 -0
  220. package/resources/skills/mcporter.json +10 -0
  221. package/resources/skills/model-usage.json +10 -0
  222. package/resources/skills/nano-banana-pro.json +10 -0
  223. package/resources/skills/nano-pdf.json +10 -0
  224. package/resources/skills/notion.json +10 -0
  225. package/resources/skills/obsidian.json +10 -0
  226. package/resources/skills/openai-image-gen.json +10 -0
  227. package/resources/skills/openai-whisper-api.json +10 -0
  228. package/resources/skills/openai-whisper.json +10 -0
  229. package/resources/skills/openhue.json +10 -0
  230. package/resources/skills/oracle.json +10 -0
  231. package/resources/skills/ordercli.json +10 -0
  232. package/resources/skills/peekaboo.json +10 -0
  233. package/resources/skills/project-structure.json +10 -0
  234. package/resources/skills/proofread.json +17 -0
  235. package/resources/skills/refactor-code.json +31 -0
  236. package/resources/skills/rename-symbol.json +23 -0
  237. package/resources/skills/sag.json +10 -0
  238. package/resources/skills/security-audit.json +18 -0
  239. package/resources/skills/session-logs.json +10 -0
  240. package/resources/skills/sherpa-onnx-tts.json +10 -0
  241. package/resources/skills/skill-creator.json +15 -0
  242. package/resources/skills/skill-hub.json +29 -0
  243. package/resources/skills/slack.json +10 -0
  244. package/resources/skills/songsee.json +10 -0
  245. package/resources/skills/sonoscli.json +10 -0
  246. package/resources/skills/spotify-player.json +10 -0
  247. package/resources/skills/startup-cfo.json +55 -0
  248. package/resources/skills/summarize-folder.json +18 -0
  249. package/resources/skills/summarize.json +10 -0
  250. package/resources/skills/things-mac.json +10 -0
  251. package/resources/skills/tmux.json +10 -0
  252. package/resources/skills/translate.json +36 -0
  253. package/resources/skills/trello.json +10 -0
  254. package/resources/skills/video-frames.json +10 -0
  255. package/resources/skills/voice-call.json +10 -0
  256. package/resources/skills/wacli.json +10 -0
  257. package/resources/skills/weather.json +10 -0
  258. package/resources/skills/write-tests.json +31 -0
  259. package/src/electron/activity/ActivityRepository.ts +238 -0
  260. package/src/electron/agent/browser/browser-service.ts +721 -0
  261. package/src/electron/agent/context-manager.ts +257 -0
  262. package/src/electron/agent/custom-skill-loader.ts +634 -0
  263. package/src/electron/agent/daemon.ts +1097 -0
  264. package/src/electron/agent/executor.ts +4017 -0
  265. package/src/electron/agent/llm/anthropic-provider.ts +175 -0
  266. package/src/electron/agent/llm/bedrock-provider.ts +236 -0
  267. package/src/electron/agent/llm/gemini-provider.ts +422 -0
  268. package/src/electron/agent/llm/index.ts +9 -0
  269. package/src/electron/agent/llm/ollama-provider.ts +347 -0
  270. package/src/electron/agent/llm/openai-oauth.ts +127 -0
  271. package/src/electron/agent/llm/openai-provider.ts +686 -0
  272. package/src/electron/agent/llm/openrouter-provider.ts +273 -0
  273. package/src/electron/agent/llm/pricing.ts +180 -0
  274. package/src/electron/agent/llm/provider-factory.ts +971 -0
  275. package/src/electron/agent/llm/types.ts +291 -0
  276. package/src/electron/agent/queue-manager.ts +408 -0
  277. package/src/electron/agent/sandbox/docker-sandbox.ts +453 -0
  278. package/src/electron/agent/sandbox/macos-sandbox.ts +426 -0
  279. package/src/electron/agent/sandbox/runner.ts +453 -0
  280. package/src/electron/agent/sandbox/sandbox-factory.ts +337 -0
  281. package/src/electron/agent/sandbox/security-utils.ts +251 -0
  282. package/src/electron/agent/search/brave-provider.ts +141 -0
  283. package/src/electron/agent/search/google-provider.ts +131 -0
  284. package/src/electron/agent/search/index.ts +6 -0
  285. package/src/electron/agent/search/provider-factory.ts +450 -0
  286. package/src/electron/agent/search/serpapi-provider.ts +138 -0
  287. package/src/electron/agent/search/tavily-provider.ts +108 -0
  288. package/src/electron/agent/search/types.ts +118 -0
  289. package/src/electron/agent/security/index.ts +20 -0
  290. package/src/electron/agent/security/input-sanitizer.ts +380 -0
  291. package/src/electron/agent/security/output-filter.ts +259 -0
  292. package/src/electron/agent/skill-eligibility.ts +334 -0
  293. package/src/electron/agent/skill-registry.ts +457 -0
  294. package/src/electron/agent/skills/document.ts +1070 -0
  295. package/src/electron/agent/skills/image-generator.ts +272 -0
  296. package/src/electron/agent/skills/organizer.ts +131 -0
  297. package/src/electron/agent/skills/presentation.ts +418 -0
  298. package/src/electron/agent/skills/spreadsheet.ts +166 -0
  299. package/src/electron/agent/tools/browser-tools.ts +546 -0
  300. package/src/electron/agent/tools/builtin-settings.ts +422 -0
  301. package/src/electron/agent/tools/canvas-tools.ts +572 -0
  302. package/src/electron/agent/tools/cron-tools.ts +723 -0
  303. package/src/electron/agent/tools/edit-tools.ts +196 -0
  304. package/src/electron/agent/tools/file-tools.ts +811 -0
  305. package/src/electron/agent/tools/glob-tools.ts +303 -0
  306. package/src/electron/agent/tools/grep-tools.ts +432 -0
  307. package/src/electron/agent/tools/image-tools.ts +126 -0
  308. package/src/electron/agent/tools/mention-tools.ts +371 -0
  309. package/src/electron/agent/tools/node-tools.ts +550 -0
  310. package/src/electron/agent/tools/registry.ts +3052 -0
  311. package/src/electron/agent/tools/search-tools.ts +111 -0
  312. package/src/electron/agent/tools/shell-tools.ts +651 -0
  313. package/src/electron/agent/tools/skill-tools.ts +340 -0
  314. package/src/electron/agent/tools/system-tools.ts +665 -0
  315. package/src/electron/agent/tools/web-fetch-tools.ts +528 -0
  316. package/src/electron/agent/tools/x-tools.ts +267 -0
  317. package/src/electron/agents/AgentRoleRepository.ts +557 -0
  318. package/src/electron/agents/HeartbeatService.ts +469 -0
  319. package/src/electron/agents/MentionRepository.ts +242 -0
  320. package/src/electron/agents/TaskSubscriptionRepository.ts +231 -0
  321. package/src/electron/agents/WorkingStateRepository.ts +278 -0
  322. package/src/electron/canvas/canvas-manager.ts +818 -0
  323. package/src/electron/canvas/canvas-preload.ts +102 -0
  324. package/src/electron/canvas/canvas-protocol.ts +174 -0
  325. package/src/electron/canvas/canvas-store.ts +200 -0
  326. package/src/electron/canvas/index.ts +8 -0
  327. package/src/electron/control-plane/client.ts +527 -0
  328. package/src/electron/control-plane/handlers.ts +723 -0
  329. package/src/electron/control-plane/index.ts +51 -0
  330. package/src/electron/control-plane/node-manager.ts +322 -0
  331. package/src/electron/control-plane/protocol.ts +269 -0
  332. package/src/electron/control-plane/remote-client.ts +517 -0
  333. package/src/electron/control-plane/server.ts +853 -0
  334. package/src/electron/control-plane/settings.ts +401 -0
  335. package/src/electron/control-plane/ssh-tunnel.ts +624 -0
  336. package/src/electron/cron/index.ts +9 -0
  337. package/src/electron/cron/schedule.ts +217 -0
  338. package/src/electron/cron/service.ts +743 -0
  339. package/src/electron/cron/store.ts +165 -0
  340. package/src/electron/cron/types.ts +291 -0
  341. package/src/electron/cron/webhook.ts +303 -0
  342. package/src/electron/database/SecureSettingsRepository.ts +514 -0
  343. package/src/electron/database/TaskLabelRepository.ts +148 -0
  344. package/src/electron/database/repositories.ts +2397 -0
  345. package/src/electron/database/schema.ts +1017 -0
  346. package/src/electron/extensions/index.ts +18 -0
  347. package/src/electron/extensions/loader.ts +336 -0
  348. package/src/electron/extensions/registry.ts +546 -0
  349. package/src/electron/extensions/types.ts +372 -0
  350. package/src/electron/gateway/channel-registry.ts +1267 -0
  351. package/src/electron/gateway/channels/bluebubbles-client.ts +641 -0
  352. package/src/electron/gateway/channels/bluebubbles.ts +509 -0
  353. package/src/electron/gateway/channels/discord.ts +1150 -0
  354. package/src/electron/gateway/channels/email-client.ts +708 -0
  355. package/src/electron/gateway/channels/email.ts +516 -0
  356. package/src/electron/gateway/channels/google-chat.ts +760 -0
  357. package/src/electron/gateway/channels/imessage-client.ts +473 -0
  358. package/src/electron/gateway/channels/imessage.ts +520 -0
  359. package/src/electron/gateway/channels/index.ts +21 -0
  360. package/src/electron/gateway/channels/line-client.ts +598 -0
  361. package/src/electron/gateway/channels/line.ts +559 -0
  362. package/src/electron/gateway/channels/matrix-client.ts +632 -0
  363. package/src/electron/gateway/channels/matrix.ts +655 -0
  364. package/src/electron/gateway/channels/mattermost-client.ts +526 -0
  365. package/src/electron/gateway/channels/mattermost.ts +550 -0
  366. package/src/electron/gateway/channels/signal-client.ts +722 -0
  367. package/src/electron/gateway/channels/signal.ts +666 -0
  368. package/src/electron/gateway/channels/slack.ts +458 -0
  369. package/src/electron/gateway/channels/teams.ts +681 -0
  370. package/src/electron/gateway/channels/telegram.ts +1727 -0
  371. package/src/electron/gateway/channels/twitch-client.ts +665 -0
  372. package/src/electron/gateway/channels/twitch.ts +468 -0
  373. package/src/electron/gateway/channels/types.ts +1002 -0
  374. package/src/electron/gateway/channels/whatsapp.ts +1101 -0
  375. package/src/electron/gateway/context-policy.ts +382 -0
  376. package/src/electron/gateway/index.ts +1274 -0
  377. package/src/electron/gateway/infrastructure.ts +645 -0
  378. package/src/electron/gateway/router.ts +3206 -0
  379. package/src/electron/gateway/security.ts +422 -0
  380. package/src/electron/gateway/session.ts +144 -0
  381. package/src/electron/gateway/tunnel.ts +626 -0
  382. package/src/electron/guardrails/guardrail-manager.ts +380 -0
  383. package/src/electron/hooks/gmail-watcher.ts +355 -0
  384. package/src/electron/hooks/index.ts +30 -0
  385. package/src/electron/hooks/mappings.ts +404 -0
  386. package/src/electron/hooks/server.ts +574 -0
  387. package/src/electron/hooks/settings.ts +466 -0
  388. package/src/electron/hooks/types.ts +245 -0
  389. package/src/electron/ipc/canvas-handlers.ts +223 -0
  390. package/src/electron/ipc/handlers.ts +3661 -0
  391. package/src/electron/ipc/mission-control-handlers.ts +182 -0
  392. package/src/electron/main.ts +496 -0
  393. package/src/electron/mcp/client/MCPClientManager.ts +406 -0
  394. package/src/electron/mcp/client/MCPServerConnection.ts +514 -0
  395. package/src/electron/mcp/client/transports/SSETransport.ts +360 -0
  396. package/src/electron/mcp/client/transports/StdioTransport.ts +355 -0
  397. package/src/electron/mcp/client/transports/WebSocketTransport.ts +384 -0
  398. package/src/electron/mcp/host/MCPHostServer.ts +388 -0
  399. package/src/electron/mcp/host/ToolAdapter.ts +140 -0
  400. package/src/electron/mcp/registry/MCPRegistryManager.ts +565 -0
  401. package/src/electron/mcp/settings.ts +468 -0
  402. package/src/electron/mcp/types.ts +371 -0
  403. package/src/electron/memory/MemoryService.ts +523 -0
  404. package/src/electron/notifications/index.ts +16 -0
  405. package/src/electron/notifications/service.ts +161 -0
  406. package/src/electron/notifications/store.ts +163 -0
  407. package/src/electron/preload.ts +2845 -0
  408. package/src/electron/reports/StandupReportService.ts +356 -0
  409. package/src/electron/security/concurrency.ts +333 -0
  410. package/src/electron/security/index.ts +17 -0
  411. package/src/electron/security/policy-manager.ts +539 -0
  412. package/src/electron/settings/appearance-manager.ts +182 -0
  413. package/src/electron/settings/personality-manager.ts +800 -0
  414. package/src/electron/settings/x-manager.ts +62 -0
  415. package/src/electron/tailscale/exposure.ts +262 -0
  416. package/src/electron/tailscale/index.ts +34 -0
  417. package/src/electron/tailscale/settings.ts +218 -0
  418. package/src/electron/tailscale/tailscale.ts +379 -0
  419. package/src/electron/tray/QuickInputWindow.ts +609 -0
  420. package/src/electron/tray/TrayManager.ts +1005 -0
  421. package/src/electron/tray/index.ts +6 -0
  422. package/src/electron/updater/index.ts +1 -0
  423. package/src/electron/updater/update-manager.ts +447 -0
  424. package/src/electron/utils/env-migration.ts +203 -0
  425. package/src/electron/utils/process.ts +124 -0
  426. package/src/electron/utils/rate-limiter.ts +130 -0
  427. package/src/electron/utils/validation.ts +493 -0
  428. package/src/electron/utils/x-cli.ts +198 -0
  429. package/src/electron/voice/VoiceService.ts +583 -0
  430. package/src/electron/voice/index.ts +9 -0
  431. package/src/electron/voice/voice-settings-manager.ts +403 -0
  432. package/src/renderer/App.tsx +775 -0
  433. package/src/renderer/components/ActivityFeed.tsx +407 -0
  434. package/src/renderer/components/ActivityFeedItem.tsx +285 -0
  435. package/src/renderer/components/AgentRoleCard.tsx +343 -0
  436. package/src/renderer/components/AgentRoleEditor.tsx +805 -0
  437. package/src/renderer/components/AgentSquadSettings.tsx +295 -0
  438. package/src/renderer/components/AgentWorkingStatePanel.tsx +411 -0
  439. package/src/renderer/components/AppearanceSettings.tsx +122 -0
  440. package/src/renderer/components/ApprovalDialog.tsx +100 -0
  441. package/src/renderer/components/BlueBubblesSettings.tsx +505 -0
  442. package/src/renderer/components/BuiltinToolsSettings.tsx +307 -0
  443. package/src/renderer/components/CanvasPreview.tsx +1189 -0
  444. package/src/renderer/components/CommandOutput.tsx +202 -0
  445. package/src/renderer/components/ContextPolicySettings.tsx +523 -0
  446. package/src/renderer/components/ControlPlaneSettings.tsx +1134 -0
  447. package/src/renderer/components/DisclaimerModal.tsx +124 -0
  448. package/src/renderer/components/DiscordSettings.tsx +436 -0
  449. package/src/renderer/components/EmailSettings.tsx +606 -0
  450. package/src/renderer/components/ExtensionsSettings.tsx +542 -0
  451. package/src/renderer/components/FileViewer.tsx +224 -0
  452. package/src/renderer/components/GoogleChatSettings.tsx +535 -0
  453. package/src/renderer/components/GuardrailSettings.tsx +487 -0
  454. package/src/renderer/components/HooksSettings.tsx +581 -0
  455. package/src/renderer/components/ImessageSettings.tsx +484 -0
  456. package/src/renderer/components/LineSettings.tsx +483 -0
  457. package/src/renderer/components/MCPRegistryBrowser.tsx +386 -0
  458. package/src/renderer/components/MCPSettings.tsx +943 -0
  459. package/src/renderer/components/MainContent.tsx +2433 -0
  460. package/src/renderer/components/MatrixSettings.tsx +510 -0
  461. package/src/renderer/components/MattermostSettings.tsx +473 -0
  462. package/src/renderer/components/MemorySettings.tsx +247 -0
  463. package/src/renderer/components/MentionBadge.tsx +87 -0
  464. package/src/renderer/components/MentionInput.tsx +409 -0
  465. package/src/renderer/components/MentionList.tsx +476 -0
  466. package/src/renderer/components/MissionControlPanel.tsx +1995 -0
  467. package/src/renderer/components/NodesSettings.tsx +316 -0
  468. package/src/renderer/components/NotificationPanel.tsx +481 -0
  469. package/src/renderer/components/Onboarding/AwakeningOrb.tsx +44 -0
  470. package/src/renderer/components/Onboarding/Onboarding.tsx +443 -0
  471. package/src/renderer/components/Onboarding/TypewriterText.tsx +102 -0
  472. package/src/renderer/components/Onboarding/index.ts +3 -0
  473. package/src/renderer/components/OnboardingModal.tsx +698 -0
  474. package/src/renderer/components/PairingCodeDisplay.tsx +324 -0
  475. package/src/renderer/components/PersonalitySettings.tsx +597 -0
  476. package/src/renderer/components/QueueSettings.tsx +119 -0
  477. package/src/renderer/components/QuickTaskFAB.tsx +71 -0
  478. package/src/renderer/components/RightPanel.tsx +413 -0
  479. package/src/renderer/components/ScheduledTasksSettings.tsx +1328 -0
  480. package/src/renderer/components/SearchSettings.tsx +328 -0
  481. package/src/renderer/components/Settings.tsx +1504 -0
  482. package/src/renderer/components/Sidebar.tsx +344 -0
  483. package/src/renderer/components/SignalSettings.tsx +673 -0
  484. package/src/renderer/components/SkillHubBrowser.tsx +458 -0
  485. package/src/renderer/components/SkillParameterModal.tsx +185 -0
  486. package/src/renderer/components/SkillsSettings.tsx +451 -0
  487. package/src/renderer/components/SlackSettings.tsx +442 -0
  488. package/src/renderer/components/StandupReportViewer.tsx +614 -0
  489. package/src/renderer/components/TaskBoard.tsx +498 -0
  490. package/src/renderer/components/TaskBoardCard.tsx +357 -0
  491. package/src/renderer/components/TaskBoardColumn.tsx +211 -0
  492. package/src/renderer/components/TaskLabelManager.tsx +472 -0
  493. package/src/renderer/components/TaskQueuePanel.tsx +144 -0
  494. package/src/renderer/components/TaskQuickActions.tsx +492 -0
  495. package/src/renderer/components/TaskTimeline.tsx +216 -0
  496. package/src/renderer/components/TaskView.tsx +162 -0
  497. package/src/renderer/components/TeamsSettings.tsx +518 -0
  498. package/src/renderer/components/TelegramSettings.tsx +421 -0
  499. package/src/renderer/components/Toast.tsx +76 -0
  500. package/src/renderer/components/TraySettings.tsx +189 -0
  501. package/src/renderer/components/TwitchSettings.tsx +511 -0
  502. package/src/renderer/components/UpdateSettings.tsx +295 -0
  503. package/src/renderer/components/VoiceIndicator.tsx +270 -0
  504. package/src/renderer/components/VoiceSettings.tsx +867 -0
  505. package/src/renderer/components/WhatsAppSettings.tsx +721 -0
  506. package/src/renderer/components/WorkingStateEditor.tsx +309 -0
  507. package/src/renderer/components/WorkingStateHistory.tsx +481 -0
  508. package/src/renderer/components/WorkspaceSelector.tsx +150 -0
  509. package/src/renderer/components/XSettings.tsx +311 -0
  510. package/src/renderer/global.d.ts +9 -0
  511. package/src/renderer/hooks/useAgentContext.ts +153 -0
  512. package/src/renderer/hooks/useOnboardingFlow.ts +548 -0
  513. package/src/renderer/hooks/useVoiceInput.ts +268 -0
  514. package/src/renderer/index.html +12 -0
  515. package/src/renderer/main.tsx +10 -0
  516. package/src/renderer/public/cowork-os-logo.png +0 -0
  517. package/src/renderer/quick-input.html +164 -0
  518. package/src/renderer/styles/index.css +14504 -0
  519. package/src/renderer/utils/agentMessages.ts +749 -0
  520. package/src/renderer/utils/voice-directives.ts +169 -0
  521. package/src/shared/channelMessages.ts +213 -0
  522. package/src/shared/types.ts +3608 -0
  523. package/tsconfig.electron.json +26 -0
  524. package/tsconfig.json +26 -0
  525. package/tsconfig.node.json +10 -0
  526. package/vite.config.ts +23 -0
@@ -0,0 +1,975 @@
1
+ "use strict";
2
+ /**
3
+ * Discord Channel Adapter
4
+ *
5
+ * Implements the ChannelAdapter interface using discord.js for Discord Bot API.
6
+ * Supports slash commands, direct messages, button components, embeds, and threads.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.DiscordAdapter = void 0;
43
+ exports.createDiscordAdapter = createDiscordAdapter;
44
+ const discord_js_1 = require("discord.js");
45
+ const fs = __importStar(require("fs"));
46
+ const path = __importStar(require("path"));
47
+ /**
48
+ * Embed color constants for different message types
49
+ */
50
+ const EMBED_COLORS = {
51
+ pending: 0xffa500, // Orange
52
+ success: 0x57f287, // Green
53
+ error: 0xed4245, // Red
54
+ info: 0x5865f2, // Blue (Discord blurple)
55
+ warning: 0xfee75c, // Yellow
56
+ neutral: 0x99aab5, // Gray
57
+ };
58
+ class DiscordAdapter {
59
+ constructor(config) {
60
+ this.type = 'discord';
61
+ this.client = null;
62
+ this._status = 'disconnected';
63
+ this.messageHandlers = [];
64
+ this.errorHandlers = [];
65
+ this.statusHandlers = [];
66
+ this.callbackQueryHandlers = [];
67
+ this.selectMenuHandlers = [];
68
+ // Track pending interactions that need reply (chatId -> interaction)
69
+ this.pendingInteractions = new Map();
70
+ // Track thread starters for context (threadId -> starter info)
71
+ this.threadStarterCache = new Map();
72
+ this.config = config;
73
+ }
74
+ get status() {
75
+ return this._status;
76
+ }
77
+ get botUsername() {
78
+ return this._botUsername;
79
+ }
80
+ /**
81
+ * Connect to Discord
82
+ */
83
+ async connect() {
84
+ if (this._status === 'connected' || this._status === 'connecting') {
85
+ return;
86
+ }
87
+ this.setStatus('connecting');
88
+ try {
89
+ // Create client instance with required intents and partials
90
+ // Partials.Channel is required to receive DM messages
91
+ this.client = new discord_js_1.Client({
92
+ intents: [
93
+ discord_js_1.GatewayIntentBits.Guilds,
94
+ discord_js_1.GatewayIntentBits.GuildMessages,
95
+ discord_js_1.GatewayIntentBits.DirectMessages,
96
+ discord_js_1.GatewayIntentBits.MessageContent,
97
+ ],
98
+ partials: [
99
+ discord_js_1.Partials.Channel, // Required to receive DMs
100
+ discord_js_1.Partials.Message, // Required for uncached message events
101
+ ],
102
+ });
103
+ // Set up event handlers
104
+ this.client.once(discord_js_1.Events.ClientReady, async (client) => {
105
+ this._botUsername = client.user.username;
106
+ this._botId = client.user.id;
107
+ console.log(`Discord bot @${this._botUsername} is ready`);
108
+ // Register slash commands
109
+ await this.registerSlashCommands();
110
+ this.setStatus('connected');
111
+ });
112
+ // Handle regular messages (for conversations)
113
+ this.client.on(discord_js_1.Events.MessageCreate, async (message) => {
114
+ // Ignore bot messages
115
+ if (message.author.bot)
116
+ return;
117
+ // Handle DMs and mentions in guilds
118
+ const isDM = message.channel.type === discord_js_1.ChannelType.DM;
119
+ const isMentioned = message.mentions.has(this.client.user);
120
+ const isThread = message.channel.isThread();
121
+ console.log(`Discord message received: isDM=${isDM}, isMentioned=${isMentioned}, isThread=${isThread}, content="${message.content.slice(0, 50)}"`);
122
+ if (isDM || isMentioned) {
123
+ const incomingMessage = this.mapMessageToIncoming(message);
124
+ console.log(`Processing Discord message from ${message.author.username}: ${incomingMessage.text.slice(0, 50)}`);
125
+ await this.handleIncomingMessage(incomingMessage);
126
+ }
127
+ });
128
+ // Handle slash command, button, and select menu interactions
129
+ this.client.on(discord_js_1.Events.InteractionCreate, async (interaction) => {
130
+ // Handle button interactions
131
+ if (interaction.isButton()) {
132
+ await this.handleButtonInteraction(interaction);
133
+ return;
134
+ }
135
+ // Handle select menu interactions
136
+ if (interaction.isStringSelectMenu()) {
137
+ await this.handleSelectMenuInteraction(interaction);
138
+ return;
139
+ }
140
+ if (!interaction.isChatInputCommand())
141
+ return;
142
+ // Defer the reply FIRST to avoid interaction timeout (Discord requires response within 3 seconds)
143
+ try {
144
+ await interaction.deferReply();
145
+ }
146
+ catch (error) {
147
+ console.error('Failed to defer reply:', error);
148
+ return;
149
+ }
150
+ // Store the interaction so sendMessage can use editReply for the first response
151
+ if (interaction.channelId) {
152
+ this.pendingInteractions.set(interaction.channelId, interaction);
153
+ // Auto-clear after 14 minutes (interactions expire after 15 minutes)
154
+ setTimeout(() => {
155
+ this.pendingInteractions.delete(interaction.channelId);
156
+ }, 14 * 60 * 1000);
157
+ }
158
+ // Convert slash command to message format
159
+ const incomingMessage = this.mapInteractionToIncoming(interaction);
160
+ await this.handleIncomingMessage(incomingMessage);
161
+ });
162
+ // Handle errors
163
+ this.client.on(discord_js_1.Events.Error, (error) => {
164
+ console.error('Discord client error:', error);
165
+ this.handleError(error, 'client.error');
166
+ });
167
+ // Login
168
+ await this.client.login(this.config.botToken);
169
+ }
170
+ catch (error) {
171
+ const err = error instanceof Error ? error : new Error(String(error));
172
+ this.setStatus('error', err);
173
+ throw err;
174
+ }
175
+ }
176
+ /**
177
+ * Handle button interaction (callback query equivalent)
178
+ */
179
+ async handleButtonInteraction(interaction) {
180
+ const customId = interaction.customId;
181
+ // Create callback query object matching our interface
182
+ const callbackQuery = {
183
+ id: interaction.id,
184
+ userId: interaction.user.id,
185
+ userName: interaction.user.displayName || interaction.user.username,
186
+ chatId: interaction.channelId,
187
+ messageId: interaction.message.id,
188
+ data: customId,
189
+ threadId: interaction.channel?.isThread() ? interaction.channelId : undefined,
190
+ raw: interaction,
191
+ };
192
+ // Notify all registered handlers
193
+ for (const handler of this.callbackQueryHandlers) {
194
+ try {
195
+ await handler(callbackQuery);
196
+ }
197
+ catch (error) {
198
+ console.error('Error in callback query handler:', error);
199
+ this.handleError(error instanceof Error ? error : new Error(String(error)), 'callbackQueryHandler');
200
+ }
201
+ }
202
+ }
203
+ /**
204
+ * Register slash commands with Discord
205
+ */
206
+ async registerSlashCommands() {
207
+ if (!this.client?.user)
208
+ return;
209
+ const commands = [
210
+ new discord_js_1.SlashCommandBuilder()
211
+ .setName('start')
212
+ .setDescription('Start the bot and get help'),
213
+ new discord_js_1.SlashCommandBuilder()
214
+ .setName('help')
215
+ .setDescription('Show available commands'),
216
+ new discord_js_1.SlashCommandBuilder()
217
+ .setName('workspaces')
218
+ .setDescription('List available workspaces'),
219
+ new discord_js_1.SlashCommandBuilder()
220
+ .setName('workspace')
221
+ .setDescription('Select or show current workspace')
222
+ .addStringOption(option => option.setName('path')
223
+ .setDescription('Workspace path to select')
224
+ .setRequired(false)),
225
+ new discord_js_1.SlashCommandBuilder()
226
+ .setName('addworkspace')
227
+ .setDescription('Add a new workspace by path')
228
+ .addStringOption(option => option.setName('path')
229
+ .setDescription('Path to the workspace folder')
230
+ .setRequired(true)),
231
+ new discord_js_1.SlashCommandBuilder()
232
+ .setName('newtask')
233
+ .setDescription('Start a fresh task/conversation'),
234
+ new discord_js_1.SlashCommandBuilder()
235
+ .setName('provider')
236
+ .setDescription('Change or show current LLM provider')
237
+ .addStringOption(option => option.setName('name')
238
+ .setDescription('Provider name (anthropic, gemini, openrouter, bedrock, ollama)')
239
+ .setRequired(false)),
240
+ new discord_js_1.SlashCommandBuilder()
241
+ .setName('providers')
242
+ .setDescription('List all available providers'),
243
+ new discord_js_1.SlashCommandBuilder()
244
+ .setName('models')
245
+ .setDescription('List available AI models'),
246
+ new discord_js_1.SlashCommandBuilder()
247
+ .setName('model')
248
+ .setDescription('Change or show current model')
249
+ .addStringOption(option => option.setName('name')
250
+ .setDescription('Model name to use')
251
+ .setRequired(false)),
252
+ new discord_js_1.SlashCommandBuilder()
253
+ .setName('status')
254
+ .setDescription('Check bot status'),
255
+ new discord_js_1.SlashCommandBuilder()
256
+ .setName('cancel')
257
+ .setDescription('Cancel current task'),
258
+ new discord_js_1.SlashCommandBuilder()
259
+ .setName('task')
260
+ .setDescription('Run a task')
261
+ .addStringOption(option => option.setName('prompt')
262
+ .setDescription('Task description')
263
+ .setRequired(true)),
264
+ new discord_js_1.SlashCommandBuilder()
265
+ .setName('pair')
266
+ .setDescription('Pair with a pairing code to gain access')
267
+ .addStringOption(option => option.setName('code')
268
+ .setDescription('The pairing code from CoWork OS app')
269
+ .setRequired(true)),
270
+ new discord_js_1.SlashCommandBuilder()
271
+ .setName('approve')
272
+ .setDescription('Approve the pending action'),
273
+ new discord_js_1.SlashCommandBuilder()
274
+ .setName('deny')
275
+ .setDescription('Deny the pending action'),
276
+ ];
277
+ const rest = new discord_js_1.REST().setToken(this.config.botToken);
278
+ try {
279
+ console.log('Registering Discord slash commands...');
280
+ // Register commands globally or to specific guilds
281
+ if (this.config.guildIds && this.config.guildIds.length > 0) {
282
+ // Register to specific guilds (faster for development)
283
+ for (const guildId of this.config.guildIds) {
284
+ await rest.put(discord_js_1.Routes.applicationGuildCommands(this.config.applicationId, guildId), { body: commands.map(c => c.toJSON()) });
285
+ }
286
+ }
287
+ else {
288
+ // Register globally (takes up to 1 hour to propagate)
289
+ await rest.put(discord_js_1.Routes.applicationCommands(this.config.applicationId), { body: commands.map(c => c.toJSON()) });
290
+ }
291
+ console.log('Discord slash commands registered');
292
+ }
293
+ catch (error) {
294
+ console.error('Failed to register Discord slash commands:', error);
295
+ }
296
+ }
297
+ /**
298
+ * Disconnect from Discord
299
+ */
300
+ async disconnect() {
301
+ if (this.client) {
302
+ this.client.destroy();
303
+ this.client = null;
304
+ }
305
+ this._botUsername = undefined;
306
+ this._botId = undefined;
307
+ this.setStatus('disconnected');
308
+ }
309
+ /**
310
+ * Send a message to a Discord channel
311
+ */
312
+ async sendMessage(message) {
313
+ if (!this.client || this._status !== 'connected') {
314
+ throw new Error('Discord bot is not connected');
315
+ }
316
+ // Process text for Discord compatibility
317
+ let processedText = message.text;
318
+ if (message.parseMode === 'markdown') {
319
+ processedText = this.convertMarkdownForDiscord(message.text);
320
+ }
321
+ // Build button components if inline keyboard is provided
322
+ const components = message.inlineKeyboard && message.inlineKeyboard.length > 0
323
+ ? this.buildButtonComponents(message.inlineKeyboard)
324
+ : [];
325
+ // Use smart chunking that preserves code fences
326
+ const chunks = this.splitMessageSmart(processedText, 2000);
327
+ let lastMessageId = '';
328
+ // Check if there's a pending interaction for this chat that needs reply
329
+ const pendingInteraction = this.pendingInteractions.get(message.chatId);
330
+ // Determine target channel (could be a thread)
331
+ let targetChannelId = message.chatId;
332
+ if (message.threadId) {
333
+ targetChannelId = message.threadId;
334
+ }
335
+ try {
336
+ for (let i = 0; i < chunks.length; i++) {
337
+ const chunk = chunks[i];
338
+ const isLastChunk = i === chunks.length - 1;
339
+ // Only add buttons to the last chunk
340
+ const chunkComponents = isLastChunk ? components : [];
341
+ // First chunk: use interaction reply if available
342
+ if (i === 0 && pendingInteraction) {
343
+ try {
344
+ const reply = await pendingInteraction.editReply({
345
+ content: chunk,
346
+ components: chunkComponents,
347
+ });
348
+ lastMessageId = typeof reply === 'object' && 'id' in reply ? reply.id : pendingInteraction.id;
349
+ // Clear the pending interaction after first reply
350
+ this.pendingInteractions.delete(message.chatId);
351
+ continue;
352
+ }
353
+ catch (interactionError) {
354
+ // Interaction may have expired, fall back to channel.send
355
+ console.warn('Interaction reply failed, falling back to channel.send:', interactionError);
356
+ this.pendingInteractions.delete(message.chatId);
357
+ }
358
+ }
359
+ // Regular channel message
360
+ const channel = await this.client.channels.fetch(targetChannelId);
361
+ if (!channel || !this.isTextBasedChannel(channel)) {
362
+ throw new Error('Invalid channel or channel is not text-based');
363
+ }
364
+ const sent = await channel.send({
365
+ content: chunk,
366
+ components: chunkComponents,
367
+ reply: message.replyTo && i === 0 ? { messageReference: message.replyTo } : undefined,
368
+ });
369
+ lastMessageId = sent.id;
370
+ }
371
+ }
372
+ catch (error) {
373
+ // If markdown parsing fails, retry without formatting
374
+ const errorMessage = error instanceof Error ? error.message : String(error);
375
+ if (errorMessage.includes('parse') || errorMessage.includes('format')) {
376
+ console.log('Markdown parsing failed, retrying without formatting');
377
+ return this.sendMessagePlain(targetChannelId, message.text, message.replyTo, components);
378
+ }
379
+ throw error;
380
+ }
381
+ return lastMessageId;
382
+ }
383
+ /**
384
+ * Send a message with an embed (rich format)
385
+ */
386
+ async sendEmbed(chatId, options, buttons) {
387
+ if (!this.client || this._status !== 'connected') {
388
+ throw new Error('Discord bot is not connected');
389
+ }
390
+ const embed = new discord_js_1.EmbedBuilder();
391
+ if (options.title)
392
+ embed.setTitle(options.title);
393
+ if (options.description)
394
+ embed.setDescription(options.description);
395
+ if (options.color)
396
+ embed.setColor(EMBED_COLORS[options.color]);
397
+ if (options.fields) {
398
+ for (const field of options.fields) {
399
+ embed.addFields({ name: field.name, value: field.value, inline: field.inline });
400
+ }
401
+ }
402
+ if (options.footer)
403
+ embed.setFooter({ text: options.footer });
404
+ if (options.timestamp)
405
+ embed.setTimestamp();
406
+ const components = buttons ? this.buildButtonComponents(buttons) : [];
407
+ const channel = await this.client.channels.fetch(chatId);
408
+ if (!channel || !this.isTextBasedChannel(channel)) {
409
+ throw new Error('Invalid channel');
410
+ }
411
+ const sent = await channel.send({
412
+ embeds: [embed],
413
+ components,
414
+ });
415
+ return sent.id;
416
+ }
417
+ /**
418
+ * Build Discord button components from our button format
419
+ */
420
+ buildButtonComponents(buttons) {
421
+ const rows = [];
422
+ for (const rowButtons of buttons) {
423
+ if (rowButtons.length === 0)
424
+ continue;
425
+ const row = new discord_js_1.ActionRowBuilder();
426
+ let buttonCount = 0;
427
+ for (const button of rowButtons) {
428
+ if (buttonCount >= 5)
429
+ break; // Discord max 5 buttons per row
430
+ const discordButton = new discord_js_1.ButtonBuilder()
431
+ .setLabel(button.text.substring(0, 80)); // Discord max 80 chars
432
+ if (button.url) {
433
+ discordButton.setStyle(discord_js_1.ButtonStyle.Link);
434
+ discordButton.setURL(button.url);
435
+ }
436
+ else if (button.callbackData) {
437
+ // Determine button style based on callback data
438
+ if (button.callbackData.startsWith('approve')) {
439
+ discordButton.setStyle(discord_js_1.ButtonStyle.Success);
440
+ }
441
+ else if (button.callbackData.startsWith('deny')) {
442
+ discordButton.setStyle(discord_js_1.ButtonStyle.Danger);
443
+ }
444
+ else {
445
+ discordButton.setStyle(discord_js_1.ButtonStyle.Primary);
446
+ }
447
+ discordButton.setCustomId(button.callbackData.substring(0, 100)); // Discord max 100 chars
448
+ }
449
+ else {
450
+ continue; // Skip buttons without action
451
+ }
452
+ row.addComponents(discordButton);
453
+ buttonCount++;
454
+ }
455
+ if (buttonCount > 0) {
456
+ rows.push(row);
457
+ }
458
+ if (rows.length >= 5)
459
+ break; // Discord max 5 rows
460
+ }
461
+ return rows;
462
+ }
463
+ /**
464
+ * Send a plain text message without formatting
465
+ */
466
+ async sendMessagePlain(chatId, text, replyTo, components = []) {
467
+ const channel = await this.client.channels.fetch(chatId);
468
+ if (!channel || !this.isTextBasedChannel(channel)) {
469
+ throw new Error('Invalid channel');
470
+ }
471
+ const chunks = this.splitMessageSmart(text, 2000);
472
+ let lastMessageId = '';
473
+ for (let i = 0; i < chunks.length; i++) {
474
+ const isLastChunk = i === chunks.length - 1;
475
+ const sent = await channel.send({
476
+ content: chunks[i],
477
+ components: isLastChunk ? components : [],
478
+ reply: replyTo && i === 0 ? { messageReference: replyTo } : undefined,
479
+ });
480
+ lastMessageId = sent.id;
481
+ }
482
+ return lastMessageId;
483
+ }
484
+ /**
485
+ * Convert GitHub-flavored markdown to Discord-compatible format
486
+ */
487
+ convertMarkdownForDiscord(text) {
488
+ let result = text;
489
+ // Convert markdown headers (## Header) to bold (**Header**)
490
+ result = result.replace(/^#{1,6}\s+(.+)$/gm, '**$1**');
491
+ // Convert horizontal rules (---, ***) to a line
492
+ result = result.replace(/^[-*]{3,}$/gm, '───────────────────');
493
+ return result;
494
+ }
495
+ /**
496
+ * Smart message splitting that preserves code fences
497
+ */
498
+ splitMessageSmart(text, maxLength) {
499
+ if (text.length <= maxLength) {
500
+ return [text];
501
+ }
502
+ const chunks = [];
503
+ let remaining = text;
504
+ let inCodeBlock = false;
505
+ let codeBlockLang = '';
506
+ while (remaining.length > 0) {
507
+ if (remaining.length <= maxLength) {
508
+ // Close any open code block at the end
509
+ if (inCodeBlock) {
510
+ chunks.push(remaining);
511
+ }
512
+ else {
513
+ chunks.push(remaining);
514
+ }
515
+ break;
516
+ }
517
+ // Find the best breaking point
518
+ let breakIndex = this.findBreakPoint(remaining, maxLength, inCodeBlock);
519
+ let chunk = remaining.substring(0, breakIndex);
520
+ // Check if we're entering or leaving a code block
521
+ const codeBlockMatches = chunk.match(/```(\w*)/g) || [];
522
+ for (const match of codeBlockMatches) {
523
+ if (inCodeBlock) {
524
+ inCodeBlock = false;
525
+ codeBlockLang = '';
526
+ }
527
+ else {
528
+ inCodeBlock = true;
529
+ codeBlockLang = match.replace('```', '');
530
+ }
531
+ }
532
+ // If we're in a code block and the chunk doesn't close it, close it manually
533
+ if (inCodeBlock && !chunk.endsWith('```')) {
534
+ chunk += '\n```';
535
+ }
536
+ chunks.push(chunk);
537
+ remaining = remaining.substring(breakIndex).trimStart();
538
+ // If we closed a code block, reopen it in the next chunk
539
+ if (inCodeBlock && remaining.length > 0) {
540
+ remaining = '```' + codeBlockLang + '\n' + remaining;
541
+ }
542
+ }
543
+ return chunks;
544
+ }
545
+ /**
546
+ * Find the best break point for message splitting
547
+ */
548
+ findBreakPoint(text, maxLength, inCodeBlock) {
549
+ // Reserve space for potential code fence closure
550
+ const reservedSpace = inCodeBlock ? 4 : 0;
551
+ const effectiveMax = maxLength - reservedSpace;
552
+ // Try to break at a newline
553
+ let breakIndex = text.lastIndexOf('\n', effectiveMax);
554
+ if (breakIndex > effectiveMax / 2) {
555
+ return breakIndex + 1;
556
+ }
557
+ // Try to break at a space
558
+ breakIndex = text.lastIndexOf(' ', effectiveMax);
559
+ if (breakIndex > effectiveMax / 2) {
560
+ return breakIndex + 1;
561
+ }
562
+ // Force break at max length
563
+ return effectiveMax;
564
+ }
565
+ /**
566
+ * Legacy split method for compatibility
567
+ */
568
+ splitMessage(text, maxLength) {
569
+ return this.splitMessageSmart(text, maxLength);
570
+ }
571
+ /**
572
+ * Edit an existing message
573
+ */
574
+ async editMessage(chatId, messageId, text) {
575
+ if (!this.client || this._status !== 'connected') {
576
+ throw new Error('Discord bot is not connected');
577
+ }
578
+ const channel = await this.client.channels.fetch(chatId);
579
+ if (!channel || !this.isTextBasedChannel(channel)) {
580
+ throw new Error('Invalid channel');
581
+ }
582
+ const message = await channel.messages.fetch(messageId);
583
+ await message.edit(text);
584
+ }
585
+ /**
586
+ * Delete a message
587
+ */
588
+ async deleteMessage(chatId, messageId) {
589
+ if (!this.client || this._status !== 'connected') {
590
+ throw new Error('Discord bot is not connected');
591
+ }
592
+ const channel = await this.client.channels.fetch(chatId);
593
+ if (!channel || !this.isTextBasedChannel(channel)) {
594
+ throw new Error('Invalid channel');
595
+ }
596
+ const message = await channel.messages.fetch(messageId);
597
+ await message.delete();
598
+ }
599
+ /**
600
+ * Send a document/file to a channel
601
+ */
602
+ async sendDocument(chatId, filePath, caption) {
603
+ if (!this.client || this._status !== 'connected') {
604
+ throw new Error('Discord bot is not connected');
605
+ }
606
+ // Check if file exists
607
+ if (!fs.existsSync(filePath)) {
608
+ throw new Error(`File not found: ${filePath}`);
609
+ }
610
+ const channel = await this.client.channels.fetch(chatId);
611
+ if (!channel || !this.isTextBasedChannel(channel)) {
612
+ throw new Error('Invalid channel');
613
+ }
614
+ const fileName = path.basename(filePath);
615
+ const attachment = new discord_js_1.AttachmentBuilder(filePath, { name: fileName });
616
+ const sent = await channel.send({
617
+ content: caption,
618
+ files: [attachment],
619
+ });
620
+ return sent.id;
621
+ }
622
+ /**
623
+ * Register a message handler
624
+ */
625
+ onMessage(handler) {
626
+ this.messageHandlers.push(handler);
627
+ }
628
+ /**
629
+ * Register a callback query handler (for button interactions)
630
+ */
631
+ onCallbackQuery(handler) {
632
+ this.callbackQueryHandlers.push(handler);
633
+ }
634
+ /**
635
+ * Answer a callback query (acknowledge button press)
636
+ * For Discord, this updates the message or sends an ephemeral response
637
+ */
638
+ async answerCallbackQuery(queryId, text, showAlert) {
639
+ // In Discord, we need to use the interaction object stored in the raw field
640
+ // The queryId is the interaction ID, but we need the actual interaction object
641
+ // This is typically handled directly in handleButtonInteraction
642
+ // This method provides API compatibility with Telegram
643
+ console.log(`answerCallbackQuery called: ${queryId}, text: ${text}, showAlert: ${showAlert}`);
644
+ }
645
+ /**
646
+ * Edit a message with a new inline keyboard
647
+ */
648
+ async editMessageWithKeyboard(chatId, messageId, text, inlineKeyboard) {
649
+ if (!this.client || this._status !== 'connected') {
650
+ throw new Error('Discord bot is not connected');
651
+ }
652
+ const channel = await this.client.channels.fetch(chatId);
653
+ if (!channel || !this.isTextBasedChannel(channel)) {
654
+ throw new Error('Invalid channel');
655
+ }
656
+ const message = await channel.messages.fetch(messageId);
657
+ const components = inlineKeyboard ? this.buildButtonComponents(inlineKeyboard) : [];
658
+ await message.edit({
659
+ content: text || message.content,
660
+ components,
661
+ });
662
+ }
663
+ // ============================================================================
664
+ // Extended Features
665
+ // ============================================================================
666
+ /**
667
+ * Send typing indicator
668
+ */
669
+ async sendTyping(chatId) {
670
+ if (!this.client || this._status !== 'connected') {
671
+ throw new Error('Discord bot is not connected');
672
+ }
673
+ const channel = await this.client.channels.fetch(chatId);
674
+ if (!channel || !this.isTextBasedChannel(channel)) {
675
+ throw new Error('Invalid channel');
676
+ }
677
+ await channel.sendTyping();
678
+ }
679
+ /**
680
+ * Add reaction to a message
681
+ */
682
+ async addReaction(chatId, messageId, emoji) {
683
+ if (!this.client || this._status !== 'connected') {
684
+ throw new Error('Discord bot is not connected');
685
+ }
686
+ const channel = await this.client.channels.fetch(chatId);
687
+ if (!channel || !this.isTextBasedChannel(channel)) {
688
+ throw new Error('Invalid channel');
689
+ }
690
+ const message = await channel.messages.fetch(messageId);
691
+ await message.react(emoji);
692
+ }
693
+ /**
694
+ * Remove reaction from a message
695
+ */
696
+ async removeReaction(chatId, messageId, emoji) {
697
+ if (!this.client || this._status !== 'connected') {
698
+ throw new Error('Discord bot is not connected');
699
+ }
700
+ const channel = await this.client.channels.fetch(chatId);
701
+ if (!channel || !this.isTextBasedChannel(channel)) {
702
+ throw new Error('Invalid channel');
703
+ }
704
+ const message = await channel.messages.fetch(messageId);
705
+ const reaction = message.reactions.cache.get(emoji);
706
+ if (reaction && this._botId) {
707
+ await reaction.users.remove(this._botId);
708
+ }
709
+ }
710
+ /**
711
+ * Send a poll (Discord native polls)
712
+ */
713
+ async sendPoll(chatId, poll) {
714
+ if (!this.client || this._status !== 'connected') {
715
+ throw new Error('Discord bot is not connected');
716
+ }
717
+ const channel = await this.client.channels.fetch(chatId);
718
+ if (!channel || !this.isTextBasedChannel(channel)) {
719
+ throw new Error('Invalid channel');
720
+ }
721
+ // Discord polls require specific formatting
722
+ const pollData = {
723
+ question: { text: poll.question },
724
+ answers: poll.options.map(opt => ({ text: opt.text })),
725
+ duration: poll.openPeriod ? Math.ceil(poll.openPeriod / 3600) : 24, // Convert seconds to hours
726
+ allow_multiselect: poll.allowsMultipleAnswers ?? false,
727
+ };
728
+ const sent = await channel.send({
729
+ poll: pollData,
730
+ });
731
+ return sent.id;
732
+ }
733
+ /**
734
+ * Send a message with a select menu (dropdown)
735
+ */
736
+ async sendWithSelectMenu(chatId, text, menu) {
737
+ if (!this.client || this._status !== 'connected') {
738
+ throw new Error('Discord bot is not connected');
739
+ }
740
+ const channel = await this.client.channels.fetch(chatId);
741
+ if (!channel || !this.isTextBasedChannel(channel)) {
742
+ throw new Error('Invalid channel');
743
+ }
744
+ const selectMenu = new discord_js_1.StringSelectMenuBuilder()
745
+ .setCustomId(menu.customId)
746
+ .setPlaceholder(menu.placeholder || 'Select an option')
747
+ .setMinValues(menu.minValues ?? 1)
748
+ .setMaxValues(menu.maxValues ?? 1)
749
+ .addOptions(menu.options.map(opt => ({
750
+ label: opt.label,
751
+ value: opt.value,
752
+ description: opt.description,
753
+ emoji: opt.emoji ? { name: opt.emoji } : undefined,
754
+ default: opt.default,
755
+ })));
756
+ if (menu.disabled) {
757
+ selectMenu.setDisabled(true);
758
+ }
759
+ const row = new discord_js_1.ActionRowBuilder().addComponents(selectMenu);
760
+ const sent = await channel.send({
761
+ content: text,
762
+ components: [row],
763
+ });
764
+ return sent.id;
765
+ }
766
+ /**
767
+ * Register a select menu handler
768
+ */
769
+ onSelectMenu(handler) {
770
+ this.selectMenuHandlers.push(handler);
771
+ }
772
+ /**
773
+ * Handle select menu interaction
774
+ */
775
+ async handleSelectMenuInteraction(interaction) {
776
+ const customId = interaction.customId;
777
+ const values = interaction.values;
778
+ // Acknowledge the interaction
779
+ try {
780
+ await interaction.deferUpdate();
781
+ }
782
+ catch (error) {
783
+ console.error('Failed to defer select menu update:', error);
784
+ }
785
+ // Notify all registered handlers
786
+ for (const handler of this.selectMenuHandlers) {
787
+ try {
788
+ await handler(customId, values, interaction.user.id, interaction.channelId, interaction.message.id, interaction);
789
+ }
790
+ catch (error) {
791
+ console.error('Error in select menu handler:', error);
792
+ this.handleError(error instanceof Error ? error : new Error(String(error)), 'selectMenuHandler');
793
+ }
794
+ }
795
+ }
796
+ // ============================================================================
797
+ // Handler Registration
798
+ // ============================================================================
799
+ /**
800
+ * Register an error handler
801
+ */
802
+ onError(handler) {
803
+ this.errorHandlers.push(handler);
804
+ }
805
+ /**
806
+ * Register a status change handler
807
+ */
808
+ onStatusChange(handler) {
809
+ this.statusHandlers.push(handler);
810
+ }
811
+ /**
812
+ * Get channel info
813
+ */
814
+ async getInfo() {
815
+ return {
816
+ type: 'discord',
817
+ status: this._status,
818
+ botId: this._botId,
819
+ botUsername: this._botUsername,
820
+ botDisplayName: this._botUsername,
821
+ extra: {
822
+ applicationId: this.config.applicationId,
823
+ guildIds: this.config.guildIds,
824
+ },
825
+ };
826
+ }
827
+ // Private methods
828
+ isTextBasedChannel(channel) {
829
+ const ch = channel;
830
+ return ch.type === discord_js_1.ChannelType.GuildText ||
831
+ ch.type === discord_js_1.ChannelType.DM ||
832
+ ch.type === discord_js_1.ChannelType.PublicThread ||
833
+ ch.type === discord_js_1.ChannelType.PrivateThread;
834
+ }
835
+ mapMessageToIncoming(message) {
836
+ // Remove bot mention from the text if present
837
+ let text = message.content;
838
+ if (this._botId) {
839
+ text = text.replace(new RegExp(`<@!?${this._botId}>\\s*`, 'g'), '').trim();
840
+ }
841
+ // Map Discord message to command format if it looks like a command
842
+ const commandText = this.parseCommand(text);
843
+ // Check for thread context
844
+ const isThread = message.channel.isThread();
845
+ const threadId = isThread ? message.channelId : undefined;
846
+ return {
847
+ messageId: message.id,
848
+ channel: 'discord',
849
+ userId: message.author.id,
850
+ userName: message.author.displayName || message.author.username,
851
+ chatId: isThread ? message.channel.parentId : message.channelId,
852
+ text: commandText || text,
853
+ timestamp: message.createdAt,
854
+ replyTo: message.reference?.messageId,
855
+ threadId,
856
+ isForumTopic: isThread,
857
+ raw: message,
858
+ };
859
+ }
860
+ mapInteractionToIncoming(interaction) {
861
+ const commandName = interaction.commandName;
862
+ let text = `/${commandName}`;
863
+ // Add options to the command text
864
+ const options = interaction.options;
865
+ // Handle specific commands with their options
866
+ switch (commandName) {
867
+ case 'workspace': {
868
+ const wsPath = options.getString('path');
869
+ if (wsPath)
870
+ text += ` ${wsPath}`;
871
+ break;
872
+ }
873
+ case 'addworkspace': {
874
+ const addPath = options.getString('path');
875
+ if (addPath)
876
+ text += ` ${addPath}`;
877
+ break;
878
+ }
879
+ case 'provider': {
880
+ const provider = options.getString('name');
881
+ if (provider)
882
+ text += ` ${provider}`;
883
+ break;
884
+ }
885
+ case 'model': {
886
+ const model = options.getString('name');
887
+ if (model)
888
+ text += ` ${model}`;
889
+ break;
890
+ }
891
+ case 'task': {
892
+ const prompt = options.getString('prompt');
893
+ if (prompt)
894
+ text = prompt; // Task prompt becomes the text directly
895
+ break;
896
+ }
897
+ case 'pair': {
898
+ const code = options.getString('code');
899
+ if (code)
900
+ text += ` ${code}`;
901
+ break;
902
+ }
903
+ }
904
+ // Check for thread context
905
+ const isThread = interaction.channel?.isThread() ?? false;
906
+ return {
907
+ messageId: interaction.id,
908
+ channel: 'discord',
909
+ userId: interaction.user.id,
910
+ userName: interaction.user.displayName || interaction.user.username,
911
+ chatId: interaction.channelId,
912
+ text,
913
+ timestamp: new Date(interaction.createdTimestamp),
914
+ threadId: isThread ? interaction.channelId : undefined,
915
+ isForumTopic: isThread,
916
+ raw: interaction,
917
+ };
918
+ }
919
+ /**
920
+ * Parse text to see if it's a command (starts with /)
921
+ */
922
+ parseCommand(text) {
923
+ // Check if text starts with a command
924
+ const commandMatch = text.match(/^\/(\w+)(?:\s+(.*))?$/);
925
+ if (commandMatch) {
926
+ return text; // Already in command format
927
+ }
928
+ return null;
929
+ }
930
+ async handleIncomingMessage(message) {
931
+ for (const handler of this.messageHandlers) {
932
+ try {
933
+ await handler(message);
934
+ }
935
+ catch (error) {
936
+ console.error('Error in message handler:', error);
937
+ this.handleError(error instanceof Error ? error : new Error(String(error)), 'messageHandler');
938
+ }
939
+ }
940
+ }
941
+ handleError(error, context) {
942
+ for (const handler of this.errorHandlers) {
943
+ try {
944
+ handler(error, context);
945
+ }
946
+ catch (e) {
947
+ console.error('Error in error handler:', e);
948
+ }
949
+ }
950
+ }
951
+ setStatus(status, error) {
952
+ this._status = status;
953
+ for (const handler of this.statusHandlers) {
954
+ try {
955
+ handler(status, error);
956
+ }
957
+ catch (e) {
958
+ console.error('Error in status handler:', e);
959
+ }
960
+ }
961
+ }
962
+ }
963
+ exports.DiscordAdapter = DiscordAdapter;
964
+ /**
965
+ * Create a Discord adapter from configuration
966
+ */
967
+ function createDiscordAdapter(config) {
968
+ if (!config.botToken) {
969
+ throw new Error('Discord bot token is required');
970
+ }
971
+ if (!config.applicationId) {
972
+ throw new Error('Discord application ID is required');
973
+ }
974
+ return new DiscordAdapter(config);
975
+ }