@spacebar_ai/moldclaw-core 2026.3.14 → 2026.3.16

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 (1074) hide show
  1. package/README.md +108 -3
  2. package/dist/account-id-plS5L20e.d.ts +1 -0
  3. package/dist/accounts-BAYVGC2k.js +109 -0
  4. package/dist/accounts-DrjRgReV.d.ts +103 -0
  5. package/dist/acp-cli-at_UYEOS.js +2088 -0
  6. package/dist/acpx-Chy1GQ_k.d.ts +5 -0
  7. package/dist/actions.runtime-C0F7dMfO.js +114 -0
  8. package/dist/actions.runtime-caI2LG9o.js +128 -0
  9. package/dist/agent-media-payload-CkpAqaOh.d.ts +16 -0
  10. package/dist/agents-B98yPGc5.js +853 -0
  11. package/dist/agents-BrLr08L3.js +217 -0
  12. package/dist/allow-from-BIwT4dl7.d.ts +42 -0
  13. package/dist/allow-list-CHt7yvAf.js +81 -0
  14. package/dist/allowlist-CxQo2wQc.js +142 -0
  15. package/dist/allowlist-resolution-B7ib7gye.d.ts +17 -0
  16. package/dist/api-Co7TNHbL.js +6953 -0
  17. package/dist/api-cEQ_ql_8.js +112 -0
  18. package/dist/audit-AnKnnlaZ.js +787 -0
  19. package/dist/audit-channel.collect.runtime-CAk1DFQ3.js +600 -0
  20. package/dist/audit-channel.runtime-5phdZp_m.js +116 -0
  21. package/dist/audit-extra.async-B8ZXFxic.js +813 -0
  22. package/dist/audit-hdKa3D-u.js +54 -0
  23. package/dist/audit-membership-runtime-CJV5XvGU.js +157 -0
  24. package/dist/audit.deep.runtime-DNMcRQrp.js +24 -0
  25. package/dist/audit.nondeep.runtime-DhNDL6yM.js +831 -0
  26. package/dist/audit.runtime-Bx7uWEh8.js +113 -0
  27. package/dist/auth-choice-C37W9MA7.js +268 -0
  28. package/dist/auth-choice-CNppOY_V.js +117 -0
  29. package/dist/auth-choice-XYFnp6fI.js +502 -0
  30. package/dist/auth-choice-options-D6oZY4Xo.js +123 -0
  31. package/dist/auth-choice-prompt-BhRqchJx.js +110 -0
  32. package/dist/auth-choice-prompt-C1xv0N08.js +36 -0
  33. package/dist/auth-choice.plugin-providers.runtime-DhLEtbmR.js +114 -0
  34. package/dist/auth-profiles-9zZdaXJK.js +127756 -0
  35. package/dist/auth-profiles.runtime-HONFDgiu.js +111 -0
  36. package/dist/bluebubbles-BY8JhO4y.js +64 -0
  37. package/dist/bluebubbles-CQjEnzK_.d.ts +6 -0
  38. package/dist/bluebubbles-RmcKgkBa.d.ts +45 -0
  39. package/dist/boolean-param-F1sMwnPu.d.ts +5 -0
  40. package/dist/bot-BGh-ATV7.d.ts +478 -0
  41. package/dist/brave-CljenznH.js +24 -0
  42. package/dist/browser-cli-CX8i0wf0.js +1492 -0
  43. package/dist/build-info.json +3 -3
  44. package/dist/bundled/boot-md/handler.d.ts +6 -0
  45. package/dist/bundled/boot-md/handler.js +26 -26
  46. package/dist/bundled/bootstrap-extra-files/handler.d.ts +6 -0
  47. package/dist/bundled/command-logger/handler.d.ts +9 -0
  48. package/dist/bundled/session-memory/handler.d.ts +9 -0
  49. package/dist/bundled/session-memory/handler.js +27 -27
  50. package/dist/call-Bc257L16.js +37 -0
  51. package/dist/call-DYFR7oGy.js +639 -0
  52. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  53. package/dist/channel-Bd-igGEW.js +803 -0
  54. package/dist/channel-BgRMb6bZ.js +575 -0
  55. package/dist/channel-BtcLrU6J.js +1598 -0
  56. package/dist/channel-Bwf6m_hD.js +538 -0
  57. package/dist/channel-C7-kgDBd.js +562 -0
  58. package/dist/channel-CEXOAxIc.js +949 -0
  59. package/dist/channel-CpZ3p9MJ.js +226 -0
  60. package/dist/channel-CqBlN6A2.js +619 -0
  61. package/dist/channel-DKhfHW4U.js +352 -0
  62. package/dist/channel-DS3t_KdJ2.js +316 -0
  63. package/dist/channel-DY24FA1v.js +4681 -0
  64. package/dist/channel-DYFGmImJ.js +542 -0
  65. package/dist/channel-DcyIqX5p.js +207 -0
  66. package/dist/channel-J-2XcAli.js +214 -0
  67. package/dist/channel-N616f4gZ.js +306 -0
  68. package/dist/channel-NY7aU2Gj.js +397 -0
  69. package/dist/channel-PNI8BOmm.js +1321 -0
  70. package/dist/channel-UcXepDJs.js +943 -0
  71. package/dist/channel-account-context-CL3hEq1j.js +103 -0
  72. package/dist/channel-config-schema-Q2nzcCCR.d.ts +1 -0
  73. package/dist/channel-jA_jodJo.js +920 -0
  74. package/dist/channel-options-CtgU5qkG.js +50 -0
  75. package/dist/channel-policy-7wXDp6d3.d.ts +1 -0
  76. package/dist/channel-rGI8uig4.js +497 -0
  77. package/dist/channel-summary-DGJZXo0r.js +106 -0
  78. package/dist/channel.runtime--WZvlNJM.js +413 -0
  79. package/dist/channel.runtime-B0ct42DL.js +122 -0
  80. package/dist/channel.runtime-BEZUZrYB.js +177 -0
  81. package/dist/channel.runtime-BMuWmsIC.js +166 -0
  82. package/dist/channel.runtime-BtvHP0po.js +4006 -0
  83. package/dist/channel.runtime-Cwf993pX.js +194 -0
  84. package/dist/channel.runtime-Cy4lEpTX.js +174 -0
  85. package/dist/channel.runtime-DAz6axda.js +865 -0
  86. package/dist/channel.runtime-DdQ2mOVh.js +236 -0
  87. package/dist/channel.runtime-Dy3HPgOU.js +399 -0
  88. package/dist/channel.runtime-iqfC25k7.js +213 -0
  89. package/dist/channel.setup-B4VYMZlQ.js +9 -0
  90. package/dist/channel.setup-BohGbCbI.js +57 -0
  91. package/dist/channel.setup-Bq2AQqqc.js +6 -0
  92. package/dist/channel.setup-BxiSfLp1.js +8 -0
  93. package/dist/channel.setup-DOUS6fjO.js +8 -0
  94. package/dist/channel.setup-DXhdYU3g.js +9 -0
  95. package/dist/channel.setup-N51CgfNy.js +11 -0
  96. package/dist/channels/plugins/actions/discord.d.ts +3 -0
  97. package/dist/channels/plugins/actions/discord.js +26 -26
  98. package/dist/channels/plugins/actions/signal.d.ts +2 -0
  99. package/dist/channels/plugins/actions/signal.js +26 -26
  100. package/dist/channels/plugins/actions/telegram.d.ts +3 -0
  101. package/dist/channels/plugins/actions/telegram.js +26 -26
  102. package/dist/channels/plugins/agent-tools/whatsapp-login.d.ts +4 -0
  103. package/dist/channels/plugins/agent-tools/whatsapp-login.js +26 -26
  104. package/dist/channels-CueeFf0q.js +404 -0
  105. package/dist/channels-PheAd73E.js +1113 -0
  106. package/dist/channels-cli-CXzVF84v.js +286 -0
  107. package/dist/channels-status-issues-BjWBQHhU.js +16 -0
  108. package/dist/chat-type-BlSN0vo4.d.ts +5 -0
  109. package/dist/clawbot-cli-BBehDXW1.js +113 -0
  110. package/dist/cli/daemon-cli.d.ts +58 -0
  111. package/dist/cli/daemon-cli.js +1 -1
  112. package/dist/cli-CIm7d5Id.js +149 -0
  113. package/dist/command-format-pq3tS8t2.d.ts +4 -0
  114. package/dist/command-registry-CDkp__KH.js +13 -0
  115. package/dist/command-registry-DSEkUBW1.js +212 -0
  116. package/dist/command-secret-gateway-CqP_o0n8.js +106 -0
  117. package/dist/compact.runtime-Qm_csEtG.js +111 -0
  118. package/dist/completion-cli-Ch1sgSLQ.js +445 -0
  119. package/dist/completion-cli-vF067Tso.js +16 -0
  120. package/dist/config-B2W1zTP1.js +44 -0
  121. package/dist/config-CMhKplgO.js +938 -0
  122. package/dist/config-DchtRsvs.js +30 -0
  123. package/dist/config-cli-C41d88_c.js +428 -0
  124. package/dist/config-guard-B_vjkXCQ.js +117 -0
  125. package/dist/config-schema-pPBCF4hz.js +31 -0
  126. package/dist/config-validation-6om9cBUx.js +262 -0
  127. package/dist/config-value-Dl3XEpA6.js +132 -0
  128. package/dist/configure-BxzvDSzu.js +1100 -0
  129. package/dist/configure-CLMLoWAn.js +238 -0
  130. package/dist/control-ui-shared-E8Nz6uKZ.js +29 -0
  131. package/dist/core-Cd3fMFKq.d.ts +87 -0
  132. package/dist/credentials-yYt6VWCq.js +268 -0
  133. package/dist/cron-cli-CA3lV3kh.js +634 -0
  134. package/dist/daemon-cli-BtQuIXEk.js +339 -0
  135. package/dist/daemon-install-BWKGzgMm.js +175 -0
  136. package/dist/deliver-CgMNmfTy.js +106 -0
  137. package/dist/deliver-runtime-Bn1KWoiQ.js +106 -0
  138. package/dist/devices-cli-D601npiL.js +340 -0
  139. package/dist/diagnostic-CkiYEGqt.js +310 -0
  140. package/dist/diffs-B5tZ8Coj.d.ts +1 -0
  141. package/dist/directory-cli-skEV8MT7.js +306 -0
  142. package/dist/directory-config-helpers-B-tiBKIv.d.ts +38 -0
  143. package/dist/directory-runtime-BEJ2fCIR.d.ts +1 -0
  144. package/dist/directory.static-CnyzoWbV.js +44 -0
  145. package/dist/discord-B_gbzPti.js +109 -0
  146. package/dist/discovery-CqI-e_Mv.js +48 -0
  147. package/dist/dm-policy-shared-nybkS1uP.d.ts +95 -0
  148. package/dist/dns-cli-Cjes3Ruw.js +216 -0
  149. package/dist/docs-cli-C3g3Gi_d.js +173 -0
  150. package/dist/doctor-completion-TvgV4SZH.js +90 -0
  151. package/dist/doctor-config-flow-0w9Ux7V8.js +107 -0
  152. package/dist/doctor-config-flow-DLzr8W7Y.js +2437 -0
  153. package/dist/enable-VYzv8b2z.js +24 -0
  154. package/dist/entry.d.ts +7 -0
  155. package/dist/entry.js +1 -1
  156. package/dist/env-overrides-DYVIkuvN.js +434 -0
  157. package/dist/env-overrides.runtime-6kijpIuu.js +17 -0
  158. package/dist/exec-approvals-cli-D_lkTG-l.js +419 -0
  159. package/dist/exec-sVmouhA9.d.ts +39 -0
  160. package/dist/extensions/acpx/index.d.ts +11 -0
  161. package/dist/extensions/acpx/index.js +1 -1
  162. package/dist/extensions/amazon-bedrock/index.d.ts +11 -0
  163. package/dist/extensions/anthropic/index.d.ts +11 -0
  164. package/dist/extensions/anthropic/index.js +26 -26
  165. package/dist/extensions/bluebubbles/index.d.ts +11 -0
  166. package/dist/extensions/bluebubbles/index.js +30 -30
  167. package/dist/extensions/bluebubbles/setup-entry.d.ts +59 -0
  168. package/dist/extensions/bluebubbles/setup-entry.js +30 -30
  169. package/dist/extensions/brave/index.d.ts +11 -0
  170. package/dist/extensions/brave/index.js +2 -2
  171. package/dist/extensions/byteplus/index.d.ts +11 -0
  172. package/dist/extensions/byteplus/index.js +26 -26
  173. package/dist/extensions/cloudflare-ai-gateway/index.d.ts +11 -0
  174. package/dist/extensions/cloudflare-ai-gateway/index.js +27 -27
  175. package/dist/extensions/copilot-proxy/index.d.ts +11 -0
  176. package/dist/extensions/device-pair/index.d.ts +12 -0
  177. package/dist/extensions/diagnostics-otel/index.d.ts +11 -0
  178. package/dist/extensions/diffs/index.d.ts +11 -0
  179. package/dist/extensions/discord/index.d.ts +11 -0
  180. package/dist/extensions/discord/index.js +31 -31
  181. package/dist/extensions/discord/setup-entry.d.ts +7 -0
  182. package/dist/extensions/discord/setup-entry.js +29 -29
  183. package/dist/extensions/elevenlabs/index.d.ts +11 -0
  184. package/dist/extensions/elevenlabs/index.js +26 -26
  185. package/dist/extensions/feishu/index.d.ts +229 -0
  186. package/dist/extensions/feishu/index.js +31 -31
  187. package/dist/extensions/feishu/setup-entry.d.ts +9 -0
  188. package/dist/extensions/feishu/setup-entry.js +28 -28
  189. package/dist/extensions/firecrawl/index.d.ts +11 -0
  190. package/dist/extensions/firecrawl/index.js +26 -26
  191. package/dist/extensions/github-copilot/index.d.ts +11 -0
  192. package/dist/extensions/github-copilot/index.js +27 -27
  193. package/dist/extensions/google/index.d.ts +11 -0
  194. package/dist/extensions/google/index.js +26 -26
  195. package/dist/extensions/googlechat/index.d.ts +11 -0
  196. package/dist/extensions/googlechat/index.js +30 -30
  197. package/dist/extensions/googlechat/setup-entry.d.ts +19 -0
  198. package/dist/extensions/googlechat/setup-entry.js +30 -30
  199. package/dist/extensions/huggingface/index.d.ts +11 -0
  200. package/dist/extensions/huggingface/index.js +26 -26
  201. package/dist/extensions/imessage/index.d.ts +11 -0
  202. package/dist/extensions/imessage/index.js +30 -30
  203. package/dist/extensions/imessage/setup-entry.d.ts +7 -0
  204. package/dist/extensions/imessage/setup-entry.js +30 -30
  205. package/dist/extensions/irc/index.d.ts +11 -0
  206. package/dist/extensions/irc/index.js +29 -29
  207. package/dist/extensions/irc/setup-entry.d.ts +8 -0
  208. package/dist/extensions/irc/setup-entry.js +29 -29
  209. package/dist/extensions/kakao-talkchannel/index.d.ts +19 -0
  210. package/dist/extensions/kakao-talkchannel/index.js +1762 -0
  211. package/dist/extensions/kakao-talkchannel/moldclaw.plugin.json +111 -0
  212. package/dist/extensions/kakao-talkchannel/package.json +12 -0
  213. package/dist/extensions/kilocode/index.d.ts +11 -0
  214. package/dist/extensions/kilocode/index.js +26 -26
  215. package/dist/extensions/kimi-coding/index.d.ts +11 -0
  216. package/dist/extensions/kimi-coding/index.js +26 -26
  217. package/dist/extensions/line/index.d.ts +11 -0
  218. package/dist/extensions/line/index.js +28 -28
  219. package/dist/extensions/line/setup-entry.d.ts +7 -0
  220. package/dist/extensions/line/setup-entry.js +28 -28
  221. package/dist/extensions/llm-task/index.d.ts +11 -0
  222. package/dist/extensions/llm-task/index.js +28 -28
  223. package/dist/extensions/lobster/index.d.ts +11 -0
  224. package/dist/extensions/matrix/index.d.ts +11 -0
  225. package/dist/extensions/matrix/index.js +31 -31
  226. package/dist/extensions/matrix/setup-entry.d.ts +20 -0
  227. package/dist/extensions/matrix/setup-entry.js +31 -31
  228. package/dist/extensions/mattermost/index.d.ts +11 -0
  229. package/dist/extensions/mattermost/index.js +28 -28
  230. package/dist/extensions/mattermost/setup-entry.d.ts +88 -0
  231. package/dist/extensions/mattermost/setup-entry.js +28 -28
  232. package/dist/extensions/memory-core/index.d.ts +11 -0
  233. package/dist/extensions/memory-lancedb/index.d.ts +25 -0
  234. package/dist/extensions/microsoft/index.d.ts +11 -0
  235. package/dist/extensions/microsoft/index.js +26 -26
  236. package/dist/extensions/minimax/index.d.ts +11 -0
  237. package/dist/extensions/minimax/index.js +26 -26
  238. package/dist/extensions/mistral/index.d.ts +11 -0
  239. package/dist/extensions/mistral/index.js +26 -26
  240. package/dist/extensions/modelstudio/index.d.ts +11 -0
  241. package/dist/extensions/modelstudio/index.js +26 -26
  242. package/dist/extensions/moonshot/index.d.ts +11 -0
  243. package/dist/extensions/moonshot/index.js +26 -26
  244. package/dist/extensions/msteams/index.d.ts +11 -0
  245. package/dist/extensions/msteams/index.js +31 -31
  246. package/dist/extensions/msteams/setup-entry.d.ts +11 -0
  247. package/dist/extensions/msteams/setup-entry.js +31 -31
  248. package/dist/extensions/nextcloud-talk/index.d.ts +11 -0
  249. package/dist/extensions/nextcloud-talk/index.js +28 -28
  250. package/dist/extensions/nextcloud-talk/setup-entry.d.ts +60 -0
  251. package/dist/extensions/nextcloud-talk/setup-entry.js +28 -28
  252. package/dist/extensions/nostr/index.d.ts +11 -0
  253. package/dist/extensions/nostr/index.js +28 -28
  254. package/dist/extensions/nostr/setup-entry.d.ts +49 -0
  255. package/dist/extensions/nostr/setup-entry.js +28 -28
  256. package/dist/extensions/nvidia/index.d.ts +11 -0
  257. package/dist/extensions/ollama/index.d.ts +11 -0
  258. package/dist/extensions/open-prose/index.d.ts +11 -0
  259. package/dist/extensions/openai/index.d.ts +11 -0
  260. package/dist/extensions/openai/index.js +26 -26
  261. package/dist/extensions/opencode/index.d.ts +11 -0
  262. package/dist/extensions/opencode/index.js +26 -26
  263. package/dist/extensions/opencode-go/index.d.ts +11 -0
  264. package/dist/extensions/opencode-go/index.js +26 -26
  265. package/dist/extensions/openrouter/index.d.ts +11 -0
  266. package/dist/extensions/openrouter/index.js +26 -26
  267. package/dist/extensions/openshell/index.d.ts +11 -0
  268. package/dist/extensions/openshell/index.js +26 -26
  269. package/dist/extensions/perplexity/index.d.ts +11 -0
  270. package/dist/extensions/perplexity/index.js +2 -2
  271. package/dist/extensions/phone-control/index.d.ts +12 -0
  272. package/dist/extensions/qianfan/index.d.ts +11 -0
  273. package/dist/extensions/qianfan/index.js +26 -26
  274. package/dist/extensions/qwen-portal-auth/index.d.ts +12 -0
  275. package/dist/extensions/qwen-portal-auth/index.js +26 -26
  276. package/dist/extensions/sglang/index.d.ts +11 -0
  277. package/dist/extensions/sglang/index.js +26 -26
  278. package/dist/extensions/signal/index.d.ts +11 -0
  279. package/dist/extensions/signal/index.js +29 -29
  280. package/dist/extensions/signal/setup-entry.d.ts +7 -0
  281. package/dist/extensions/signal/setup-entry.js +29 -29
  282. package/dist/extensions/slack/index.d.ts +11 -0
  283. package/dist/extensions/slack/index.js +30 -30
  284. package/dist/extensions/slack/setup-entry.d.ts +7 -0
  285. package/dist/extensions/slack/setup-entry.js +29 -29
  286. package/dist/extensions/synology-chat/index.d.ts +11 -0
  287. package/dist/extensions/synology-chat/index.js +28 -28
  288. package/dist/extensions/synology-chat/setup-entry.d.ts +138 -0
  289. package/dist/extensions/synology-chat/setup-entry.js +28 -28
  290. package/dist/extensions/synthetic/index.d.ts +11 -0
  291. package/dist/extensions/synthetic/index.js +26 -26
  292. package/dist/extensions/talk-voice/index.d.ts +12 -0
  293. package/dist/extensions/talk-voice/index.js +26 -26
  294. package/dist/extensions/telegram/index.d.ts +11 -0
  295. package/dist/extensions/telegram/index.js +29 -29
  296. package/dist/extensions/telegram/setup-entry.d.ts +7 -0
  297. package/dist/extensions/telegram/setup-entry.js +28 -28
  298. package/dist/extensions/thread-ownership/index.d.ts +12 -0
  299. package/dist/extensions/tlon/index.d.ts +11 -0
  300. package/dist/extensions/tlon/index.js +28 -28
  301. package/dist/extensions/tlon/setup-entry.d.ts +7 -0
  302. package/dist/extensions/tlon/setup-entry.js +28 -28
  303. package/dist/extensions/together/index.d.ts +11 -0
  304. package/dist/extensions/together/index.js +26 -26
  305. package/dist/extensions/twitch/index.d.ts +39 -0
  306. package/dist/extensions/twitch/index.js +28 -28
  307. package/dist/extensions/venice/index.d.ts +11 -0
  308. package/dist/extensions/venice/index.js +26 -26
  309. package/dist/extensions/vercel-ai-gateway/index.d.ts +11 -0
  310. package/dist/extensions/vercel-ai-gateway/index.js +26 -26
  311. package/dist/extensions/vllm/index.d.ts +11 -0
  312. package/dist/extensions/vllm/index.js +26 -26
  313. package/dist/extensions/voice-call/index.d.ts +11 -0
  314. package/dist/extensions/voice-call/index.js +26 -26
  315. package/dist/extensions/volcengine/index.d.ts +11 -0
  316. package/dist/extensions/volcengine/index.js +26 -26
  317. package/dist/extensions/whatsapp/index.d.ts +11 -0
  318. package/dist/extensions/whatsapp/index.js +29 -29
  319. package/dist/extensions/whatsapp/setup-entry.d.ts +7 -0
  320. package/dist/extensions/whatsapp/setup-entry.js +29 -29
  321. package/dist/extensions/xai/index.d.ts +11 -0
  322. package/dist/extensions/xai/index.js +26 -26
  323. package/dist/extensions/xiaomi/index.d.ts +11 -0
  324. package/dist/extensions/xiaomi/index.js +26 -26
  325. package/dist/extensions/zai/index.d.ts +11 -0
  326. package/dist/extensions/zai/index.js +26 -26
  327. package/dist/extensions/zalo/index.d.ts +11 -0
  328. package/dist/extensions/zalo/index.js +30 -30
  329. package/dist/extensions/zalo/setup-entry.d.ts +34 -0
  330. package/dist/extensions/zalo/setup-entry.js +30 -30
  331. package/dist/extensions/zalouser/index.d.ts +11 -0
  332. package/dist/extensions/zalouser/index.js +31 -31
  333. package/dist/extensions/zalouser/setup-entry.d.ts +42 -0
  334. package/dist/extensions/zalouser/setup-entry.js +31 -31
  335. package/dist/feishu-DCKEC3ao.d.ts +36 -0
  336. package/dist/gateway-cli-DN1Ii6J-.js +26432 -0
  337. package/dist/gateway-install-token-CJYFJBaC.js +163 -0
  338. package/dist/gateway-rpc-CroQg9MB.js +26 -0
  339. package/dist/gateway-runtime-D9FRZqKP.js +69 -0
  340. package/dist/googlechat-CBCkerAy.js +307 -0
  341. package/dist/googlechat-CSUNieHX.d.ts +12 -0
  342. package/dist/group-access-rSvkIglb.d.ts +61 -0
  343. package/dist/health-B6WwLJp4.js +570 -0
  344. package/dist/health-CAlJydXv.js +108 -0
  345. package/dist/history-BwNxb0sJ.d.ts +75 -0
  346. package/dist/hooks-BYlfU3Nf.d.ts +6 -0
  347. package/dist/hooks-cli-DuKmdo_H.js +995 -0
  348. package/dist/http-registry-DX_LVtuK.d.ts +20 -0
  349. package/dist/image-generation-DKkdRpve.d.ts +9 -0
  350. package/dist/imessage-7abjbe2Q.js +31 -0
  351. package/dist/imessage-DOH1yaDE.js +110 -0
  352. package/dist/inbound-envelope-CmvweL6U.d.ts +78 -0
  353. package/dist/inbound-reply-dispatch-BvnKTOec.js +71 -0
  354. package/dist/inbound-reply-dispatch-C7LjHRZN.d.ts +72 -0
  355. package/dist/index-DTQqfqj9.d.ts +1 -0
  356. package/dist/index.d.ts +27 -0
  357. package/dist/index.js +2 -2
  358. package/dist/infra/warning-filter.d.ts +10 -0
  359. package/dist/install-target-tXRD7VkM.js +574 -0
  360. package/dist/installs-C8fz8sm3.js +532 -0
  361. package/dist/io-C6XifaT4.js +9737 -0
  362. package/dist/io-C8awRnSW.js +28 -0
  363. package/dist/ipv4-d88_Jn2p.js +82 -0
  364. package/dist/irc-DpR6FXjN.js +672 -0
  365. package/dist/json-store-Sr_kk-II.d.ts +14 -0
  366. package/dist/keyed-async-queue-BA3BKukE.d.ts +19 -0
  367. package/dist/library-DOwowAGN.js +107 -0
  368. package/dist/lifecycle-core-BHHBoRTY.js +382 -0
  369. package/dist/line/accounts.d.ts +3 -0
  370. package/dist/line/send.d.ts +2 -0
  371. package/dist/line/send.js +4 -4
  372. package/dist/line/template-messages.d.ts +2 -0
  373. package/dist/line-8rsNbJCP.js +530 -0
  374. package/dist/line-D_cvIf6B.d.ts +75 -0
  375. package/dist/links-BOnvOj1z.d.ts +7 -0
  376. package/dist/llm-slug-generator-D9HjWtJT.js +67 -0
  377. package/dist/llm-slug-generator.d.ts +12 -0
  378. package/dist/llm-slug-generator.js +27 -27
  379. package/dist/logging-BhqLWxTD.js +13 -0
  380. package/dist/logging-DfaiL4OX.js +29 -0
  381. package/dist/login-qr-COBYR52w.js +233 -0
  382. package/dist/login-qr-xK4QIpPc.js +107 -0
  383. package/dist/logs-cli-RSSTw8L_.js +254 -0
  384. package/dist/manager-runtime-DL6JoSj9.js +106 -0
  385. package/dist/manager.runtime-Cbyhg1vB.js +710 -0
  386. package/dist/markdown-to-line-BTlEkOls.d.ts +91 -0
  387. package/dist/matrix-DX-jaB88.js +1490 -0
  388. package/dist/matrix-H6Yyj1QZ.d.ts +68 -0
  389. package/dist/matrix-J8s45tRw.js +1269 -0
  390. package/dist/mattermost-D75n6bRI.d.ts +6 -0
  391. package/dist/mcp-cli-CLc3_yCO.js +86 -0
  392. package/dist/media-understanding.runtime-BI0Lljbl.js +111 -0
  393. package/dist/memory-cli-CTp2cYrf.js +106 -0
  394. package/dist/method-scopes-Du8ODGFW.js +2586 -0
  395. package/dist/model-auth-markers-DEDakSUW.d.ts +20 -0
  396. package/dist/model-picker-CDBs7LJF.js +390 -0
  397. package/dist/model-picker-CRix4Wwv.js +107 -0
  398. package/dist/model-picker.runtime-CITyy3Rn.js +120 -0
  399. package/dist/model-suppression.runtime-Ce7D6QUT.js +111 -0
  400. package/dist/models-BK1eanuP.js +113 -0
  401. package/dist/models-X4Czy3uE.js +2514 -0
  402. package/dist/models-cli-C79Ulviy.js +304 -0
  403. package/dist/models-config-DALlu3S9.js +106 -0
  404. package/dist/models-config.providers.discovery-CSJ1STM1.d.ts +18 -0
  405. package/dist/monitor-B45a_RpX.js +3468 -0
  406. package/dist/monitor-C8KbJ-i0.js +767 -0
  407. package/dist/monitor-CIhrvegZ.js +3076 -0
  408. package/dist/monitor-CQut7klP.js +6823 -0
  409. package/dist/monitor-DZb5IJle.js +777 -0
  410. package/dist/monitor-DaFkdD27.js +108 -0
  411. package/dist/monitor-Do9Tp2Ii.js +110 -0
  412. package/dist/monitor-shared-CMK9cDOb.js +444 -0
  413. package/dist/msteams-A6H_wv5F.js +852 -0
  414. package/dist/net-DpMJgN-o.d.ts +19 -0
  415. package/dist/nextcloud-talk-f1pZ5Bge.d.ts +1 -0
  416. package/dist/node-cli-BXnmsjzL.js +2498 -0
  417. package/dist/node-resolve-CupmrA0Y.js +835 -0
  418. package/dist/nodes-cli-DZVrah_8.js +1375 -0
  419. package/dist/nostr-DMV534Ks.d.ts +7 -0
  420. package/dist/nostr-SAk3tjtR.js +8744 -0
  421. package/dist/npm-resolution-Dr9wssCY.js +60 -0
  422. package/dist/oauth-utils-DnyXdWU9.d.ts +10 -0
  423. package/dist/onboard-BE5pmb1g.js +589 -0
  424. package/dist/onboard-channels-3hNVY0E7.js +1241 -0
  425. package/dist/onboard-channels-vaO3nWLL.js +200 -0
  426. package/dist/onboard-custom-CI5uFyWH.js +571 -0
  427. package/dist/onboard-custom-eIvRswgv.js +109 -0
  428. package/dist/onboard-helpers-ChMWfUnl.js +335 -0
  429. package/dist/onboard-helpers-DRFi9oaD.js +108 -0
  430. package/dist/onboard-remote-BTspTgA4.js +112 -0
  431. package/dist/onboard-remote-so38yXlX.js +181 -0
  432. package/dist/onboard-search-DS0tZS24.js +297 -0
  433. package/dist/onboard-skills-B9DxCCiU.js +133 -0
  434. package/dist/onboard-skills-so0a_BJV.js +112 -0
  435. package/dist/outbound-media-BiJscGlR.js +11 -0
  436. package/dist/outbound-media-DJF-TuJu.d.ts +11 -0
  437. package/dist/pairing-access-CuiJP9xN.d.ts +21 -0
  438. package/dist/pairing-cli-DN0u1Cez.js +212 -0
  439. package/dist/parse-finite-number-B3FJTjyQ.d.ts +5 -0
  440. package/dist/perplexity-Bw1u3CAF.js +24 -0
  441. package/dist/persistent-dedupe-DR5Ka6BX.d.ts +26 -0
  442. package/dist/pi-model-discovery-runtime-iwKNCaYu.js +106 -0
  443. package/dist/pi-tools.before-tool-call.runtime-BM_N-JZe.js +380 -0
  444. package/dist/plugin-install--KVul05Z.js +184 -0
  445. package/dist/plugin-install-DVpPsLkS.js +112 -0
  446. package/dist/plugin-install-plan-Dwc6-coz.js +49 -0
  447. package/dist/plugin-registry-XRswugE9.js +108 -0
  448. package/dist/plugin-registry-jozQafRo.js +49 -0
  449. package/dist/plugin-sdk/account-resolution.js +26 -26
  450. package/dist/plugin-sdk/acp-runtime.js +26 -26
  451. package/dist/plugin-sdk/acpx.js +1 -1
  452. package/dist/plugin-sdk/agent-runtime.js +26 -26
  453. package/dist/plugin-sdk/bluebubbles.js +29 -29
  454. package/dist/plugin-sdk/channel-config-helpers.js +26 -26
  455. package/dist/plugin-sdk/channel-config-schema.js +2 -2
  456. package/dist/plugin-sdk/channel-policy.js +26 -26
  457. package/dist/plugin-sdk/channel-runtime.js +26 -26
  458. package/dist/plugin-sdk/compat.js +27 -27
  459. package/dist/plugin-sdk/config-runtime.js +28 -28
  460. package/dist/plugin-sdk/conversation-runtime.js +26 -26
  461. package/dist/plugin-sdk/discord.js +26 -26
  462. package/dist/plugin-sdk/feishu.js +27 -27
  463. package/dist/plugin-sdk/gateway-runtime.js +8 -8
  464. package/dist/plugin-sdk/googlechat.js +29 -29
  465. package/dist/plugin-sdk/image-generation-runtime.js +26 -26
  466. package/dist/plugin-sdk/image-generation.js +26 -26
  467. package/dist/plugin-sdk/imessage.js +27 -27
  468. package/dist/plugin-sdk/index.js +26 -26
  469. package/dist/plugin-sdk/infra-runtime.js +26 -26
  470. package/dist/plugin-sdk/irc.js +29 -29
  471. package/dist/plugin-sdk/line.js +27 -27
  472. package/dist/plugin-sdk/llm-task.js +26 -26
  473. package/dist/plugin-sdk/matrix.js +29 -29
  474. package/dist/plugin-sdk/mattermost.js +28 -28
  475. package/dist/plugin-sdk/media-runtime.js +26 -26
  476. package/dist/plugin-sdk/media-understanding-runtime.js +26 -26
  477. package/dist/plugin-sdk/media-understanding.js +26 -26
  478. package/dist/plugin-sdk/msteams.js +30 -30
  479. package/dist/plugin-sdk/nextcloud-talk.js +28 -28
  480. package/dist/plugin-sdk/nostr.js +27 -27
  481. package/dist/plugin-sdk/plugin-runtime.js +26 -26
  482. package/dist/plugin-sdk/provider-auth.js +28 -28
  483. package/dist/plugin-sdk/provider-setup.js +27 -27
  484. package/dist/plugin-sdk/provider-web-search.js +1 -1
  485. package/dist/plugin-sdk/qwen-portal-auth.js +26 -26
  486. package/dist/plugin-sdk/reply-history.js +26 -26
  487. package/dist/plugin-sdk/reply-runtime.js +26 -26
  488. package/dist/plugin-sdk/sandbox.js +26 -26
  489. package/dist/plugin-sdk/security-runtime.js +26 -26
  490. package/dist/plugin-sdk/self-hosted-provider-setup.js +27 -27
  491. package/dist/plugin-sdk/setup.js +27 -27
  492. package/dist/plugin-sdk/signal.js +26 -26
  493. package/dist/plugin-sdk/slack.js +26 -26
  494. package/dist/plugin-sdk/speech-runtime.js +26 -26
  495. package/dist/plugin-sdk/speech.js +26 -26
  496. package/dist/plugin-sdk/src/channels/plugins/setup-wizard-helpers.d.ts +3 -0
  497. package/dist/plugin-sdk/src/config/config-lock.d.ts +38 -0
  498. package/dist/plugin-sdk/src/config/config.d.ts +1 -1
  499. package/dist/plugin-sdk/src/config/io.d.ts +39 -0
  500. package/dist/plugin-sdk/src/config/types.gateway.d.ts +12 -0
  501. package/dist/plugin-sdk/src/config/types.secrets.d.ts +10 -0
  502. package/dist/plugin-sdk/src/config/zod-schema.d.ts +2 -0
  503. package/dist/plugin-sdk/src/gateway/credential-planner.d.ts +3 -1
  504. package/dist/plugin-sdk/src/secrets/provider-env-vars.d.ts +61 -0
  505. package/dist/plugin-sdk/src/secrets/sec1-placeholder.d.ts +181 -0
  506. package/dist/plugin-sdk/src/secrets/sec1-utils.d.ts +57 -0
  507. package/dist/plugin-sdk/synology-chat.js +27 -27
  508. package/dist/plugin-sdk/telegram.js +26 -26
  509. package/dist/plugin-sdk/text-runtime.js +4 -4
  510. package/dist/plugin-sdk/tlon.js +27 -27
  511. package/dist/plugin-sdk/twitch.js +26 -26
  512. package/dist/plugin-sdk/voice-call.js +26 -26
  513. package/dist/plugin-sdk/whatsapp.js +26 -26
  514. package/dist/plugin-sdk/zalo.js +30 -30
  515. package/dist/plugin-sdk/zalouser.js +29 -29
  516. package/dist/plugins/runtime/index.d.ts +22 -0
  517. package/dist/plugins/runtime/index.js +26 -26
  518. package/dist/plugins-C4PiDdjc.js +106 -0
  519. package/dist/plugins-cli-zhmliYNU.js +912 -0
  520. package/dist/policy-CcSolumc.js +143 -0
  521. package/dist/preflight-audio.runtime-BAbfqqzW.js +111 -0
  522. package/dist/probe-Bgt5c-cr.js +129 -0
  523. package/dist/probe-CPk5iGcg.js +47 -0
  524. package/dist/probe-DR4KRKXz.js +19 -0
  525. package/dist/probe-DnoCyJ_m.js +1793 -0
  526. package/dist/probe-VsLtK3vQ.js +6328 -0
  527. package/dist/probe-auth-BnsKrQt7.js +38 -0
  528. package/dist/probe-auth-DYdUG8l1.js +48 -0
  529. package/dist/program-8enYYBsc.js +247 -0
  530. package/dist/prompt-select-styled-DxBcUasv.js +2673 -0
  531. package/dist/provider-api-key-auth.runtime-DsLZyt6h.js +116 -0
  532. package/dist/provider-auth-choice-30EvRxqc.js +126 -0
  533. package/dist/provider-auth-choice-preference-DMr1WmRg.js +189 -0
  534. package/dist/provider-auth-choice.runtime-CI98BgQF.js +118 -0
  535. package/dist/provider-auth-guidance-WKDIi_wk.js +34 -0
  536. package/dist/provider-auth-result-Cs8wguSI.d.ts +18 -0
  537. package/dist/provider-models-EOys_Nvi.d.ts +867 -0
  538. package/dist/provider-ollama-setup-D89zlm9C.d.ts +32 -0
  539. package/dist/provider-onboard-BzOpgCLu.d.ts +40 -0
  540. package/dist/provider-runtime.runtime-Cm4as2KG.js +106 -0
  541. package/dist/provider-self-hosted-setup-Bmv_AQmw.d.ts +61 -0
  542. package/dist/provider-self-hosted-setup-CJwFVVB4.js +182 -0
  543. package/dist/provider-usage-CVNyLLDb.js +106 -0
  544. package/dist/provider-usage.types-CdTymHNu.d.ts +16 -0
  545. package/dist/provider-web-search-BJhXD5dH.js +2392 -0
  546. package/dist/provider-wizard-DMMYXjlW.js +152 -0
  547. package/dist/push-apns-BnWTdTEk.js +1038 -0
  548. package/dist/pw-ai-CtK_7Cy2.js +1866 -0
  549. package/dist/qr-cli-CA-BF0--.js +108 -0
  550. package/dist/qr-cli-D18HiUkh.js +369 -0
  551. package/dist/reactions-Df7XG8Uh.js +281 -0
  552. package/dist/read-only-account-inspect.discord.runtime-B-FP0mwb.js +111 -0
  553. package/dist/read-only-account-inspect.slack.runtime-DkWZ2ccW.js +111 -0
  554. package/dist/read-only-account-inspect.telegram.runtime-BnlTkn_e.js +111 -0
  555. package/dist/redact-snapshot-DVdstBvO.js +2661 -0
  556. package/dist/ref-contract-RPkB754Q.js +53 -0
  557. package/dist/register.agent-DVAxXQKW.js +434 -0
  558. package/dist/register.backup-CUuL5KUZ.js +624 -0
  559. package/dist/register.configure-bC0UEwfU.js +247 -0
  560. package/dist/register.maintenance-iIqvl_eT.js +569 -0
  561. package/dist/register.message-CEDd4z07.js +704 -0
  562. package/dist/register.onboard-Cejfnysy.js +187 -0
  563. package/dist/register.setup-DU7uHdYt.js +207 -0
  564. package/dist/register.status-health-sessions-BWphMXNR.js +493 -0
  565. package/dist/register.subclis-DnIweTEG.js +315 -0
  566. package/dist/register.subclis-gJX_Pbub.js +12 -0
  567. package/dist/registry-Dgwc-7eS.js +1183 -0
  568. package/dist/replies-D9PEZ8yn.js +110 -0
  569. package/dist/reply-history-lHgoC4l3.d.ts +1 -0
  570. package/dist/reply-payload-Bd2HuR4g.d.ts +46 -0
  571. package/dist/request-url-BcSJaiiu.d.ts +5 -0
  572. package/dist/resolve-BbsCHGLY.js +660 -0
  573. package/dist/resolve-channels-BtrGC95o.js +262 -0
  574. package/dist/resolve-channels-C1SthO1N.js +226 -0
  575. package/dist/resolve-users-CgSxHrU0.js +143 -0
  576. package/dist/routes-BZtqNrBf.js +7097 -0
  577. package/dist/rpc-D3KMxG4J.js +67 -0
  578. package/dist/run-command-C8b3dCZV.d.ts +16 -0
  579. package/dist/run-main-BlWJVotF.js +423 -0
  580. package/dist/runtime-RWGbO5Qy.d.ts +26 -0
  581. package/dist/runtime-discord-ops.runtime-DUXIYvQr.js +9073 -0
  582. package/dist/runtime-slack-ops.runtime-n1yFfyp1.js +4551 -0
  583. package/dist/runtime-telegram-ops.runtime-PZUWchjT.js +128 -0
  584. package/dist/runtime-whatsapp-login.runtime-xsuNyvGz.js +109 -0
  585. package/dist/runtime-whatsapp-outbound.runtime-5EfEyCsO.js +112 -0
  586. package/dist/sandbox-cli-Dw1nWNmQ.js +530 -0
  587. package/dist/search-manager-BJoRxOaf.js +15 -0
  588. package/dist/search-manager-DxkQvUrW.js +386 -0
  589. package/dist/secret-input-schema-Cp_La9qv.d.ts +19 -0
  590. package/dist/secrets-cli-BPyV2gSq.js +2065 -0
  591. package/dist/security-cli-EK4sSRfG.js +570 -0
  592. package/dist/send-B01Gvh9m.js +629 -0
  593. package/dist/send-B4L4wRJO.js +100 -0
  594. package/dist/send-BDcGrXt0.js +1025 -0
  595. package/dist/send-BRRtHxyR.js +283 -0
  596. package/dist/send-DU6dmMXW.js +631 -0
  597. package/dist/server-CWw5GFEg.js +106 -0
  598. package/dist/server-node-events-92cDVswC.js +501 -0
  599. package/dist/session-key-DbkfhOjM.d.ts +46 -0
  600. package/dist/sessions-B052uHA3.js +218 -0
  601. package/dist/sessions-Cef4dZNP.js +107 -0
  602. package/dist/setup-BlQPyDPy.js +387 -0
  603. package/dist/setup-DcSZ_pTn.d.ts +37 -0
  604. package/dist/setup-core-B9mdZYnU.js +166 -0
  605. package/dist/setup-core-Cj0sLkpP.js +47 -0
  606. package/dist/setup-core-CkZbebOv.js +143 -0
  607. package/dist/setup-core-MRNjnrJl.js +205 -0
  608. package/dist/setup-surface-3ZY0JtWE.js +490 -0
  609. package/dist/setup-wizard-helpers-Dwzb9Dcz.d.ts +203 -0
  610. package/dist/setup.finalize-B5ETm3Ui.js +517 -0
  611. package/dist/setup.gateway-config-C8hdtlbw.js +338 -0
  612. package/dist/setup.secret-input-BZSIeiqy.js +25 -0
  613. package/dist/shared--9_eQ_lc.js +75 -0
  614. package/dist/shared-CxkH3H0U.js +102 -0
  615. package/dist/shared-DTNL0hA9.js +298 -0
  616. package/dist/shared-HSP1OV-Q.js +96 -0
  617. package/dist/shared-UIjWb_3B.js +182 -0
  618. package/dist/signal-CTI6bSmB.js +109 -0
  619. package/dist/skills-4-r1mfJM.js +853 -0
  620. package/dist/skills-RNm54CBO.js +19 -0
  621. package/dist/skills-cli-te7dSs5p.js +291 -0
  622. package/dist/skills-install-Del-Ogv8.js +763 -0
  623. package/dist/skills-status-BZpoMXrR.js +169 -0
  624. package/dist/skills-status-Dq61Sz8U.js +20 -0
  625. package/dist/slack-oc-viUtl.js +109 -0
  626. package/dist/slash-commands.runtime-NdkD2LZV.js +123 -0
  627. package/dist/slash-dispatch.runtime-DQgeaF3J.js +136 -0
  628. package/dist/slash-skill-commands.runtime-DmOl2DnL.js +111 -0
  629. package/dist/src-0wtt7seR.js +1696 -0
  630. package/dist/status-5oR_gqv_.js +121 -0
  631. package/dist/status-BO8LY0hC.js +1599 -0
  632. package/dist/status-D_oHA9yO.js +126 -0
  633. package/dist/status-IrMacJRj.js +606 -0
  634. package/dist/status-Prdeg53E.js +43 -0
  635. package/dist/status-json-Da0hR-1Z.js +286 -0
  636. package/dist/status.link-channel-BgUJEZAz.js +138 -0
  637. package/dist/status.scan.deps.runtime-D9vHTxOW.js +121 -0
  638. package/dist/status.scan.runtime-D-EdD5CW.js +114 -0
  639. package/dist/status.summary--i6xduWH.js +592 -0
  640. package/dist/status.summary.runtime-BqMXjaBc.js +113 -0
  641. package/dist/subagent-orphan-recovery-DiRJcFQc.js +302 -0
  642. package/dist/subagent-registry-runtime-B66EYEYm.js +106 -0
  643. package/dist/synology-chat-BemXqdzG.js +297 -0
  644. package/dist/system-cli-CSuiia4-.js +92 -0
  645. package/dist/telegram/audit.d.ts +2 -0
  646. package/dist/telegram/audit.js +1 -1
  647. package/dist/telegram/token.d.ts +2 -0
  648. package/dist/telegram/token.js +26 -26
  649. package/dist/telegram-DLFcRv5a.js +109 -0
  650. package/dist/testing-DZrulv-n.d.ts +1755 -0
  651. package/dist/text-chunking-BaYBIUoR.d.ts +79 -0
  652. package/dist/text-chunking-C8kmbNfa.js +84 -0
  653. package/dist/thinking-D8aqmr3o.d.ts +13 -0
  654. package/dist/tlon-Bpr4f3yF.js +433 -0
  655. package/dist/tool-send-BHKm5ztm.d.ts +9 -0
  656. package/dist/tui-BY3QRgC1.js +3834 -0
  657. package/dist/tui-cli-CCfZOlV0.js +132 -0
  658. package/dist/types-CKx5nDZB.d.ts +45 -0
  659. package/dist/types-DBhDdMQd.d.ts +22670 -0
  660. package/dist/types.base-B_TkkSS8.d.ts +188 -0
  661. package/dist/types.secrets-Bojc4omL.js +92 -0
  662. package/dist/ui-1UpZZyI3.js +31 -0
  663. package/dist/update-BR4JvFpV.js +1036 -0
  664. package/dist/update-cli-BZv44lFq.js +1498 -0
  665. package/dist/update-offset-store-DGdBotIW.js +107 -0
  666. package/dist/update-runner-D34sooPe.js +1496 -0
  667. package/dist/vllm-defaults-BCGSJ7K0.d.ts +13 -0
  668. package/dist/wait-BU9vJv22.d.ts +4 -0
  669. package/dist/web-CXpU2D41.js +107 -0
  670. package/dist/web-shared-B4sL45ah.d.ts +45 -0
  671. package/dist/webhook-memory-guards-B7oLVseG.d.ts +43 -0
  672. package/dist/webhook-request-guards-CqIH7equ.d.ts +76 -0
  673. package/dist/webhook-targets-CAAGATtk.js +181 -0
  674. package/dist/webhook-targets-oQ0jd4r0.d.ts +106 -0
  675. package/dist/webhooks-cli-B46t2VT5.js +349 -0
  676. package/dist/whatsapp-Dniwd4Rv.js +109 -0
  677. package/dist/whatsapp-actions-fL46PsNs.js +162 -0
  678. package/dist/windows-spawn-DGeE98SH.d.ts +43 -0
  679. package/dist/workspace-dirs-d3Ms_ryk.js +2002 -0
  680. package/dist/zalo-Csulx0XK.d.ts +9 -0
  681. package/dist/zalo-gh0yAWmS.js +415 -0
  682. package/dist/zalouser-CuxRvztM.js +30911 -0
  683. package/dist/zod-schema.agent-runtime-B4MkB-_3.d.ts +10 -0
  684. package/dist/zod-schema.core-D5reNip6.js +541 -0
  685. package/dist/zod-schema.core-DN3RhEUG.d.ts +173 -0
  686. package/docs/SEC1.md +523 -0
  687. package/docs/SEC1_IMPLEMENTATION/CHANNELS_REPORT.md +173 -0
  688. package/docs/SEC1_IMPLEMENTATION/CORE_UTIL_REPORT.md +139 -0
  689. package/docs/SEC1_IMPLEMENTATION/DOCS_REPORT.md +134 -0
  690. package/docs/SEC1_IMPLEMENTATION/ENV_MAP_DRAFT.md +148 -0
  691. package/docs/SEC1_IMPLEMENTATION/INTEGRATION_REPORT.md +170 -0
  692. package/docs/SEC1_IMPLEMENTATION/PROVIDERS_REPORT.md +291 -0
  693. package/docs/SEC1_IMPLEMENTATION/QA_REPORT.md +249 -0
  694. package/docs/SEC1_IMPLEMENTATION/RECURSIVE_QA/wave1-channels.md +317 -0
  695. package/docs/SEC1_IMPLEMENTATION/RECURSIVE_QA/wave1-docs.md +212 -0
  696. package/docs/SEC1_IMPLEMENTATION/RECURSIVE_QA/wave1-security.md +368 -0
  697. package/docs/SEC1_IMPLEMENTATION/RECURSIVE_QA/wave2-critic-consolidated.md +195 -0
  698. package/docs/SEC1_IMPLEMENTATION/RECURSIVE_QA/wave3-fix-report.md +105 -0
  699. package/docs/SEC1_IMPLEMENTATION/STRATEGY.md +451 -0
  700. package/docs/SEC1_IMPLEMENTATION/TEST_REPORT.md +156 -0
  701. package/docs/pipeline-sdk/CLI_SPEC.md +609 -0
  702. package/docs/pipeline-sdk/PIPELINE_SDK_DESIGN.md +1372 -0
  703. package/extensions/kakao-talkchannel/MIGRATION_ARCH_ANALYSIS.md +455 -0
  704. package/extensions/kakao-talkchannel/MIGRATION_CODE_ANALYSIS.md +383 -0
  705. package/extensions/kakao-talkchannel/MIGRATION_STRATEGY.md +115 -0
  706. package/extensions/kakao-talkchannel/README.md +50 -0
  707. package/extensions/kakao-talkchannel/index.ts +20 -0
  708. package/extensions/kakao-talkchannel/moldclaw.plugin.json +98 -0
  709. package/extensions/kakao-talkchannel/package.json +12 -0
  710. package/extensions/kakao-talkchannel/src/adapters/config.ts +132 -0
  711. package/extensions/kakao-talkchannel/src/adapters/gateway.ts +974 -0
  712. package/extensions/kakao-talkchannel/src/adapters/outbound.ts +52 -0
  713. package/extensions/kakao-talkchannel/src/adapters/pairing.ts +35 -0
  714. package/extensions/kakao-talkchannel/src/adapters/security.ts +57 -0
  715. package/extensions/kakao-talkchannel/src/adapters/setup.ts +105 -0
  716. package/extensions/kakao-talkchannel/src/adapters/status.ts +117 -0
  717. package/extensions/kakao-talkchannel/src/channel.ts +58 -0
  718. package/extensions/kakao-talkchannel/src/commands/card.ts +413 -0
  719. package/extensions/kakao-talkchannel/src/config/schema.ts +129 -0
  720. package/extensions/kakao-talkchannel/src/kakao/callback.ts +133 -0
  721. package/extensions/kakao-talkchannel/src/kakao/limits.ts +129 -0
  722. package/extensions/kakao-talkchannel/src/kakao/payload.ts +138 -0
  723. package/extensions/kakao-talkchannel/src/kakao/response.ts +373 -0
  724. package/extensions/kakao-talkchannel/src/relay/client.ts +146 -0
  725. package/extensions/kakao-talkchannel/src/relay/session.ts +137 -0
  726. package/extensions/kakao-talkchannel/src/relay/sse.ts +258 -0
  727. package/extensions/kakao-talkchannel/src/relay/stream.ts +149 -0
  728. package/extensions/kakao-talkchannel/src/runtime.ts +21 -0
  729. package/extensions/kakao-talkchannel/src/types.ts +447 -0
  730. package/extensions/kakao-talkchannel/src/version.ts +3 -0
  731. package/extensions/kakao-talkchannel/tsconfig.json +19 -0
  732. package/package.json +23 -8
  733. package/skills/meshy/SKILL.md +69 -0
  734. package/skills/meshy/scripts/__pycache__/check_status.cpython-312.pyc +0 -0
  735. package/skills/meshy/scripts/__pycache__/image_to_3d.cpython-312.pyc +0 -0
  736. package/skills/meshy/scripts/__pycache__/text_to_3d.cpython-312.pyc +0 -0
  737. package/skills/meshy/scripts/check_status.py +147 -0
  738. package/skills/meshy/scripts/image_to_3d.py +229 -0
  739. package/skills/meshy/scripts/text_to_3d.py +214 -0
  740. package/skills/nano-banana-pro/scripts/generate_image.py +1 -1
  741. package/skills/openai-whisper-api/scripts/transcribe.sh +0 -0
  742. package/skills/tavily-search/SKILL.md +61 -0
  743. package/skills/tavily-search/scripts/__pycache__/search.cpython-312.pyc +0 -0
  744. package/skills/tavily-search/scripts/search.py +238 -0
  745. package/skills/video-frames/scripts/frame.sh +0 -0
  746. package/LICENSE +0 -21
  747. package/dist/accounts-UcSvD34O.js +0 -109
  748. package/dist/acp-cli-BPb8PgHP.js +0 -2088
  749. package/dist/actions.runtime-BL5QRooG.js +0 -114
  750. package/dist/actions.runtime-DSdfSo40.js +0 -128
  751. package/dist/agents-CHeX_5-H.js +0 -217
  752. package/dist/agents-DQRL9XKP.js +0 -853
  753. package/dist/allow-list-Boi79v-U.js +0 -81
  754. package/dist/allowlist-B2eBBeMF.js +0 -142
  755. package/dist/api-CFAtRSYL.js +0 -6953
  756. package/dist/api-D5JNJj8n.js +0 -112
  757. package/dist/audit-BM0GsdzV.js +0 -787
  758. package/dist/audit-BqRK9OSj.js +0 -54
  759. package/dist/audit-channel.collect.runtime-BPvDB8aq.js +0 -600
  760. package/dist/audit-channel.runtime-D3fzHiAo.js +0 -116
  761. package/dist/audit-extra.async-NveNIzX0.js +0 -813
  762. package/dist/audit-membership-runtime-mu470WFO.js +0 -157
  763. package/dist/audit.deep.runtime-RdxvW8Tj.js +0 -24
  764. package/dist/audit.nondeep.runtime-DDu8vA9Z.js +0 -831
  765. package/dist/audit.runtime-Y8C9W7s9.js +0 -113
  766. package/dist/auth-choice-C1CIxRsi.js +0 -268
  767. package/dist/auth-choice-CTvqWiDI.js +0 -117
  768. package/dist/auth-choice-Ddzko1B8.js +0 -502
  769. package/dist/auth-choice-options-BIAmAiCe.js +0 -123
  770. package/dist/auth-choice-prompt-B815kArz.js +0 -110
  771. package/dist/auth-choice-prompt-CGhTNCJx.js +0 -36
  772. package/dist/auth-choice.plugin-providers.runtime-AvAZ6S5W.js +0 -114
  773. package/dist/auth-profiles-BJcHzwPy.js +0 -127650
  774. package/dist/auth-profiles.runtime-CieFilK5.js +0 -111
  775. package/dist/bluebubbles-F8FGE9cH.js +0 -64
  776. package/dist/brave-BG5Yopn8.js +0 -24
  777. package/dist/browser-cli-Co7PJGZF.js +0 -1492
  778. package/dist/call-CoaQYq7c.js +0 -639
  779. package/dist/call-D3eu5Jjh.js +0 -37
  780. package/dist/channel-BftWD6yu.js +0 -1321
  781. package/dist/channel-Bub9U5Xg.js +0 -214
  782. package/dist/channel-C0oDs7TO.js +0 -4681
  783. package/dist/channel-C8CnEdkZ.js +0 -352
  784. package/dist/channel-CI-RC-xf.js +0 -497
  785. package/dist/channel-CY-hZCOJ.js +0 -397
  786. package/dist/channel-CbtGJB2x.js +0 -943
  787. package/dist/channel-CcfK3wP8.js +0 -803
  788. package/dist/channel-DBoDIeVj.js +0 -619
  789. package/dist/channel-DEq6Ecs-.js +0 -920
  790. package/dist/channel-DH4dhW1n.js +0 -226
  791. package/dist/channel-DQ_wdKg_.js +0 -575
  792. package/dist/channel-DT6qD1Ic.js +0 -207
  793. package/dist/channel-DZNAyxwr.js +0 -542
  794. package/dist/channel-DtakwAEe.js +0 -538
  795. package/dist/channel-DuYgH6p1.js +0 -562
  796. package/dist/channel-Hn-AN-d52.js +0 -316
  797. package/dist/channel-_R4hbD5h.js +0 -1598
  798. package/dist/channel-account-context-DXq8dlvI.js +0 -103
  799. package/dist/channel-kQmEVn3I.js +0 -306
  800. package/dist/channel-options-DHfxaklg.js +0 -50
  801. package/dist/channel-summary-DUpnoYhI.js +0 -106
  802. package/dist/channel-t-JxCWk6.js +0 -949
  803. package/dist/channel.runtime--GYriaXU.js +0 -213
  804. package/dist/channel.runtime-BJtn3GOH.js +0 -174
  805. package/dist/channel.runtime-BV7t_oNz.js +0 -166
  806. package/dist/channel.runtime-Bi8a3n9S.js +0 -865
  807. package/dist/channel.runtime-BjsYF0NN.js +0 -122
  808. package/dist/channel.runtime-BnI6YtmI.js +0 -413
  809. package/dist/channel.runtime-CQOftcCd.js +0 -194
  810. package/dist/channel.runtime-CuIAcPjZ.js +0 -4006
  811. package/dist/channel.runtime-DH1Q1G4k.js +0 -399
  812. package/dist/channel.runtime-DYYUPKxr.js +0 -236
  813. package/dist/channel.runtime-U5Gszsr5.js +0 -177
  814. package/dist/channel.setup-BQFHmgki.js +0 -9
  815. package/dist/channel.setup-BVoDwklu.js +0 -8
  816. package/dist/channel.setup-Bf73HsXr.js +0 -57
  817. package/dist/channel.setup-CblD4flM.js +0 -11
  818. package/dist/channel.setup-DgxlrPgz.js +0 -6
  819. package/dist/channel.setup-GLIAEVKL.js +0 -8
  820. package/dist/channel.setup-YTy5R1sz.js +0 -9
  821. package/dist/channels-CTL8iR9J.js +0 -404
  822. package/dist/channels-DBGvnjHY.js +0 -1113
  823. package/dist/channels-cli-BmVO5-sq.js +0 -286
  824. package/dist/channels-status-issues-kDtsWzA-.js +0 -16
  825. package/dist/clawbot-cli-DtcMJHqX.js +0 -113
  826. package/dist/cli-BNGECGVY.js +0 -149
  827. package/dist/command-registry-1SDrWgER.js +0 -13
  828. package/dist/command-registry-DNorYU4w.js +0 -212
  829. package/dist/command-secret-gateway-DqDZparO.js +0 -106
  830. package/dist/compact.runtime-C1ZN8UGb.js +0 -111
  831. package/dist/completion-cli-Q_Jt5Foc.js +0 -16
  832. package/dist/completion-cli-QkTXhuJh.js +0 -445
  833. package/dist/config-BbxrRaLf.js +0 -938
  834. package/dist/config-CkD8DJ7L.js +0 -44
  835. package/dist/config-cli-BoPrlYTp.js +0 -428
  836. package/dist/config-guard-CEhCvr_u.js +0 -117
  837. package/dist/config-schema-GQ6uWjXe.js +0 -31
  838. package/dist/config-validation-woE2_LpC.js +0 -262
  839. package/dist/config-value-Dh8m-CFf.js +0 -132
  840. package/dist/config-y4i5g7s4.js +0 -30
  841. package/dist/configure-DGRzwdFN.js +0 -1100
  842. package/dist/configure-S4AHE3k_.js +0 -238
  843. package/dist/control-ui-shared-kLBp4YlS.js +0 -29
  844. package/dist/credentials-D5uBf_C5.js +0 -265
  845. package/dist/cron-cli-lGupeVCW.js +0 -634
  846. package/dist/daemon-cli-Cs_edi0I.js +0 -339
  847. package/dist/daemon-install-DIFpP_qv.js +0 -175
  848. package/dist/deliver-DYa_DFZU.js +0 -106
  849. package/dist/deliver-runtime-DCW_o2Ot.js +0 -106
  850. package/dist/devices-cli-YsGOW2-w.js +0 -340
  851. package/dist/diagnostic-vMghIesG.js +0 -310
  852. package/dist/directory-cli-DtjMQjU5.js +0 -306
  853. package/dist/directory.static-DBZGvsdF.js +0 -44
  854. package/dist/discord-DYCu19HT.js +0 -109
  855. package/dist/discovery-DZYAoDF_.js +0 -48
  856. package/dist/dns-cli-DqW4pNgW.js +0 -216
  857. package/dist/docs-cli-Bu9TBlDU.js +0 -173
  858. package/dist/doctor-completion-B5hcQD5c.js +0 -90
  859. package/dist/doctor-config-flow-BBB2ZKfT.js +0 -107
  860. package/dist/doctor-config-flow-DDBYUS9f.js +0 -2437
  861. package/dist/enable-Tmsp8QuB.js +0 -24
  862. package/dist/env-overrides-BHxqjYZG.js +0 -434
  863. package/dist/env-overrides.runtime-Cz98bf-l.js +0 -17
  864. package/dist/exec-approvals-cli-wO5cYfMa.js +0 -419
  865. package/dist/gateway-cli-CFvDGhB9.js +0 -26429
  866. package/dist/gateway-install-token-CskJfo_N.js +0 -163
  867. package/dist/gateway-rpc-srYfBID9.js +0 -26
  868. package/dist/gateway-runtime-C76hUmUV.js +0 -69
  869. package/dist/googlechat-Cha5utST.js +0 -307
  870. package/dist/health-DDQYYsJy.js +0 -108
  871. package/dist/health-DXZykGaX.js +0 -570
  872. package/dist/hooks-cli-DfkurPYP.js +0 -995
  873. package/dist/imessage-B26k39pl.js +0 -110
  874. package/dist/imessage-Bp1_6cws.js +0 -31
  875. package/dist/inbound-reply-dispatch-DoIJLztA.js +0 -71
  876. package/dist/install-target-BjOuS4I8.js +0 -574
  877. package/dist/installs-Cz4k0W1Y.js +0 -532
  878. package/dist/io-B0OKifLZ.js +0 -28
  879. package/dist/io-DcoxdH6t.js +0 -9570
  880. package/dist/ipv4-CTQQ4_IW.js +0 -82
  881. package/dist/irc-B8vBDigm.js +0 -672
  882. package/dist/library-VCM_cQY4.js +0 -107
  883. package/dist/lifecycle-core-Ctz36PdQ.js +0 -382
  884. package/dist/line-B_uTLrdI.js +0 -530
  885. package/dist/llm-slug-generator-YWg0g2pj.js +0 -67
  886. package/dist/logging-S-5LPdfQ.js +0 -13
  887. package/dist/logging-ueBMCGMR.js +0 -29
  888. package/dist/login-qr-pcACm2Ng.js +0 -107
  889. package/dist/login-qr-pv-kxMfF.js +0 -233
  890. package/dist/logs-cli-RgADgSMO.js +0 -254
  891. package/dist/manager-runtime-BhTkoKmb.js +0 -106
  892. package/dist/manager.runtime-BjHzikoK.js +0 -710
  893. package/dist/matrix-C4EEu2Qp.js +0 -1490
  894. package/dist/matrix-Dfzcc5nV.js +0 -1269
  895. package/dist/mcp-cli-CJmOm9Oj.js +0 -86
  896. package/dist/media-understanding.runtime-DCETFCw_.js +0 -111
  897. package/dist/memory-cli-DFqd6tYx.js +0 -106
  898. package/dist/method-scopes-D-Q9dvbj.js +0 -2586
  899. package/dist/model-picker-Z-CUcuMr.js +0 -390
  900. package/dist/model-picker-v5mUsZ4J.js +0 -107
  901. package/dist/model-picker.runtime-A_z0dHfS.js +0 -120
  902. package/dist/model-suppression.runtime-QVWVJRr-.js +0 -111
  903. package/dist/models-Bbj0xV4F.js +0 -2514
  904. package/dist/models-D-OIjZqU.js +0 -113
  905. package/dist/models-cli-Bpn-5i4h.js +0 -304
  906. package/dist/models-config-Cwa5cJbC.js +0 -106
  907. package/dist/monitor-BchfCAaU.js +0 -6823
  908. package/dist/monitor-BydV44SP.js +0 -3076
  909. package/dist/monitor-CT8axwfm.js +0 -767
  910. package/dist/monitor-CZGWNOvn.js +0 -777
  911. package/dist/monitor-DN62r69g.js +0 -3468
  912. package/dist/monitor-DZ0fzJku.js +0 -110
  913. package/dist/monitor-DvNjzWFu.js +0 -108
  914. package/dist/monitor-shared-B-DBSlkQ.js +0 -444
  915. package/dist/msteams-Bf-wk2Rp.js +0 -852
  916. package/dist/node-cli-kH16TQI7.js +0 -2498
  917. package/dist/node-resolve-DfOpQmxm.js +0 -835
  918. package/dist/nodes-cli-CkAMXW5u.js +0 -1375
  919. package/dist/nostr-B8UGHclZ.js +0 -8744
  920. package/dist/npm-resolution-DmjlifII.js +0 -60
  921. package/dist/onboard-C883nfyw.js +0 -589
  922. package/dist/onboard-channels-Dc-BxN7p.js +0 -200
  923. package/dist/onboard-channels-j5EENtum.js +0 -1241
  924. package/dist/onboard-custom-0atne0C5.js +0 -571
  925. package/dist/onboard-custom-CWMqwjJx.js +0 -109
  926. package/dist/onboard-helpers-D3wWfH8F.js +0 -335
  927. package/dist/onboard-helpers-DZmRCe8l.js +0 -108
  928. package/dist/onboard-remote-Cn6kW-p0.js +0 -112
  929. package/dist/onboard-remote-Cx4w5VAk.js +0 -181
  930. package/dist/onboard-search-Ck9HRh2M.js +0 -297
  931. package/dist/onboard-skills-BtqrGioT.js +0 -133
  932. package/dist/onboard-skills-Dnw19Os8.js +0 -112
  933. package/dist/outbound-media-C5Nv4o18.js +0 -11
  934. package/dist/pairing-cli-Cwy9QZ_4.js +0 -212
  935. package/dist/perplexity-Brhpb45X.js +0 -24
  936. package/dist/pi-model-discovery-runtime-DIOdo6D8.js +0 -106
  937. package/dist/pi-tools.before-tool-call.runtime-CFM4gsDF.js +0 -380
  938. package/dist/plugin-install-BOV00hia.js +0 -112
  939. package/dist/plugin-install-Bak8fUBv.js +0 -184
  940. package/dist/plugin-install-plan-bKkEefRf.js +0 -49
  941. package/dist/plugin-registry-DxAXQUlZ.js +0 -108
  942. package/dist/plugin-registry-n0p3phem.js +0 -49
  943. package/dist/plugins-Ca3RK8Fi.js +0 -106
  944. package/dist/plugins-cli-BnC51H2R.js +0 -912
  945. package/dist/policy-BJv97w9e.js +0 -143
  946. package/dist/preflight-audio.runtime-BrFcf-6_.js +0 -111
  947. package/dist/probe-063xvvZc.js +0 -19
  948. package/dist/probe-BJEb2wGv.js +0 -1793
  949. package/dist/probe-CJQlxgsl.js +0 -47
  950. package/dist/probe-Caa2HznF.js +0 -6328
  951. package/dist/probe-CfL4tnJ6.js +0 -129
  952. package/dist/probe-auth-DN2Ec83-.js +0 -38
  953. package/dist/probe-auth-D_UKzu4m.js +0 -48
  954. package/dist/program-BOMdC7MC.js +0 -247
  955. package/dist/prompt-select-styled-DDnCfM3j.js +0 -2673
  956. package/dist/provider-api-key-auth.runtime-DUns3fwX.js +0 -116
  957. package/dist/provider-auth-choice-B_j1ctT2.js +0 -126
  958. package/dist/provider-auth-choice-preference-BaOBZ_Xn.js +0 -189
  959. package/dist/provider-auth-choice.runtime-DOako_zV.js +0 -118
  960. package/dist/provider-auth-guidance-CrjxnoNZ.js +0 -34
  961. package/dist/provider-runtime.runtime-BkOkgmTw.js +0 -106
  962. package/dist/provider-self-hosted-setup-BFDU6dRa.js +0 -182
  963. package/dist/provider-usage-CaDE0mqq.js +0 -106
  964. package/dist/provider-web-search-BR7etTjJ.js +0 -2392
  965. package/dist/provider-wizard-DCPdKUvb.js +0 -152
  966. package/dist/push-apns-B_OZjm4v.js +0 -1038
  967. package/dist/pw-ai-dG60P0hQ.js +0 -1866
  968. package/dist/qr-cli-DWfiw79I.js +0 -369
  969. package/dist/qr-cli-DwuKtyZQ.js +0 -108
  970. package/dist/reactions-CIGAPBn8.js +0 -281
  971. package/dist/read-only-account-inspect.discord.runtime-D54mnq8l.js +0 -111
  972. package/dist/read-only-account-inspect.slack.runtime-Bxs9ObMC.js +0 -111
  973. package/dist/read-only-account-inspect.telegram.runtime-UoVuf_Yo.js +0 -111
  974. package/dist/redact-snapshot-DZ3Vq-SC.js +0 -2657
  975. package/dist/ref-contract-D96lSYLs.js +0 -53
  976. package/dist/register.agent-2KmeahEL.js +0 -434
  977. package/dist/register.backup-ECBnWVR7.js +0 -624
  978. package/dist/register.configure-Doz1daCp.js +0 -247
  979. package/dist/register.maintenance-C33cV-WM.js +0 -569
  980. package/dist/register.message-CnL0NiF6.js +0 -704
  981. package/dist/register.onboard-BrYGZeQA.js +0 -187
  982. package/dist/register.setup-Bx6gEg6X.js +0 -207
  983. package/dist/register.status-health-sessions-FLb0CUOO.js +0 -493
  984. package/dist/register.subclis-BuqgaeIf.js +0 -12
  985. package/dist/register.subclis-DwdgfdnT.js +0 -315
  986. package/dist/registry-xhgvU89y.js +0 -1107
  987. package/dist/replies-hB2aipLu.js +0 -110
  988. package/dist/resolve-3ErMOltL.js +0 -660
  989. package/dist/resolve-channels-BV8GXuPe.js +0 -226
  990. package/dist/resolve-channels-CTY_XRIP.js +0 -262
  991. package/dist/resolve-users-DQ4Ne4Zc.js +0 -143
  992. package/dist/routes-BNDsNO_e.js +0 -7097
  993. package/dist/rpc-BLGTBWXq.js +0 -67
  994. package/dist/run-main-COAE4GlI.js +0 -423
  995. package/dist/runtime-discord-ops.runtime-Dxg-nlgd.js +0 -9073
  996. package/dist/runtime-slack-ops.runtime-Di474LJr.js +0 -4551
  997. package/dist/runtime-telegram-ops.runtime-Da8vgf3O.js +0 -128
  998. package/dist/runtime-whatsapp-login.runtime-DcouP4iF.js +0 -109
  999. package/dist/runtime-whatsapp-outbound.runtime-CYamaEJX.js +0 -112
  1000. package/dist/sandbox-cli-U5ZTxhxL.js +0 -530
  1001. package/dist/search-manager-CfizyEMk.js +0 -386
  1002. package/dist/search-manager-DaF2QP4s.js +0 -15
  1003. package/dist/secrets-cli-C0gytFip.js +0 -2065
  1004. package/dist/security-cli-C74EuLUO.js +0 -570
  1005. package/dist/send-BTLVBf_E.js +0 -631
  1006. package/dist/send-BlWWCEZE.js +0 -1025
  1007. package/dist/send-CfypD1B_.js +0 -100
  1008. package/dist/send-Cm9v3uhF.js +0 -283
  1009. package/dist/send-g2odQuYI.js +0 -629
  1010. package/dist/server-C8b5QJ2s.js +0 -106
  1011. package/dist/server-node-events-xqQe5xiu.js +0 -501
  1012. package/dist/sessions-CSSzvgPQ.js +0 -107
  1013. package/dist/sessions-z0GIvdKa.js +0 -218
  1014. package/dist/setup-D9XTmlF8.js +0 -387
  1015. package/dist/setup-core-BDrLOwYO.js +0 -143
  1016. package/dist/setup-core-CM7cY7_i.js +0 -166
  1017. package/dist/setup-core-CnmgANY-.js +0 -205
  1018. package/dist/setup-core-DgcjCKmG.js +0 -47
  1019. package/dist/setup-surface-DzRrVKYj.js +0 -490
  1020. package/dist/setup.finalize-UaPu_adv.js +0 -517
  1021. package/dist/setup.gateway-config-Djc1ceEh.js +0 -338
  1022. package/dist/setup.secret-input-BkczghbR.js +0 -25
  1023. package/dist/shared-BHizGoNk.js +0 -298
  1024. package/dist/shared-CUfYhQkP.js +0 -96
  1025. package/dist/shared-DYYqr9EC.js +0 -75
  1026. package/dist/shared-DthOxMRQ.js +0 -182
  1027. package/dist/shared-On_A5_hW.js +0 -102
  1028. package/dist/signal-D6px9PGZ.js +0 -109
  1029. package/dist/skills-B4h1k-SP.js +0 -853
  1030. package/dist/skills-Bto10BGB.js +0 -19
  1031. package/dist/skills-cli-CXGR3Y5j.js +0 -291
  1032. package/dist/skills-install-B1AlkK8C.js +0 -763
  1033. package/dist/skills-status-BsmJ_iSg.js +0 -20
  1034. package/dist/skills-status-DGdxY3OI.js +0 -169
  1035. package/dist/slack-B7vWFmxP.js +0 -109
  1036. package/dist/slash-commands.runtime-DXdAT84n.js +0 -123
  1037. package/dist/slash-dispatch.runtime-CNf2-9Aj.js +0 -136
  1038. package/dist/slash-skill-commands.runtime-CBjffHRX.js +0 -111
  1039. package/dist/src-Cp7P7T08.js +0 -1696
  1040. package/dist/status-158fWh4A.js +0 -43
  1041. package/dist/status-BJIVLJnb.js +0 -1599
  1042. package/dist/status-BQiBI6N9.js +0 -126
  1043. package/dist/status-CZipXGUu.js +0 -121
  1044. package/dist/status-ZZIVFLI-.js +0 -606
  1045. package/dist/status-json-BNUy5Mem.js +0 -286
  1046. package/dist/status.link-channel-B694y1Xu.js +0 -138
  1047. package/dist/status.scan.deps.runtime-BcoKEzQD.js +0 -121
  1048. package/dist/status.scan.runtime-CqScDt-p.js +0 -114
  1049. package/dist/status.summary-AMek7qvI.js +0 -592
  1050. package/dist/status.summary.runtime-XgkcQ_kr.js +0 -113
  1051. package/dist/subagent-orphan-recovery-CrCYTmFC.js +0 -302
  1052. package/dist/subagent-registry-runtime-Cg-YvLx3.js +0 -106
  1053. package/dist/synology-chat-0G85jIqQ.js +0 -297
  1054. package/dist/system-cli-kZtSxKNm.js +0 -92
  1055. package/dist/telegram-DV0Wy89w.js +0 -109
  1056. package/dist/text-chunking-C2J2Oeul.js +0 -84
  1057. package/dist/tlon-DmK1NUVP.js +0 -433
  1058. package/dist/tui-D3bNPLG7.js +0 -3834
  1059. package/dist/tui-cli-DtMp9k_s.js +0 -132
  1060. package/dist/types.secrets-DuSPmmWB.js +0 -80
  1061. package/dist/ui-CeGztSEL.js +0 -31
  1062. package/dist/update-De7VudzP.js +0 -1036
  1063. package/dist/update-cli-BH8Pb-So.js +0 -1498
  1064. package/dist/update-offset-store-syELkdEW.js +0 -107
  1065. package/dist/update-runner-Cq-Q40T9.js +0 -1496
  1066. package/dist/web-CjMtvfSq.js +0 -107
  1067. package/dist/webhook-targets-_jTR0Bb_.js +0 -181
  1068. package/dist/webhooks-cli-DQ6u2Qau.js +0 -349
  1069. package/dist/whatsapp-CyLk16SZ.js +0 -109
  1070. package/dist/whatsapp-actions-Dzr2Wzqw.js +0 -162
  1071. package/dist/workspace-dirs-L1_QQ9mB.js +0 -2002
  1072. package/dist/zalo-CrehfXvK.js +0 -415
  1073. package/dist/zalouser-D1QD-O-I.js +0 -30911
  1074. package/dist/zod-schema.core-CWxzqcUs.js +0 -541
@@ -1,4551 +0,0 @@
1
- import "./redact-fatrROh9.js";
2
- import "./errors-DOJWZqNo.js";
3
- import "./unhandled-rejections-CTvNvnT0.js";
4
- import { i as getChildLogger } from "./logger-BFfIIIKH.js";
5
- import "./paths-D6AgsMTU.js";
6
- import "./tmp-moldclaw-dir-DWF-d8qD.js";
7
- import "./theme-BSXzMzAA.js";
8
- import { a as logVerbose, d as warn, l as shouldLogVerbose, t as danger } from "./globals-DESrFYmC.js";
9
- import { p as createNonExitingRuntime } from "./subsystem-S4LNMNHd.js";
10
- import "./ansi-BPhP6LBZ.js";
11
- import "./boolean-D8Ha5nYV.js";
12
- import "./env-Dgex_t9p.js";
13
- import "./warning-filter-gJuwHM7C.js";
14
- import "./utils-rjVNXUns.js";
15
- import "./links-C5I443Xb.js";
16
- import "./setup-binary-Fw3cCSWL.js";
17
- import { $r as resolveSlackThreadStarter, $t as createChannelInboundDebouncer, $w as formatAllowlistMatchMeta, A_ as finalizeInboundContext, Cn as shouldHandleTextCommands, Es as recordInboundSession, Fa as dispatchInboundMessage, Fd as resolvePinnedMainDmOwnerFromAllowlist, Fr as recordSlackThreadParticipation, Fs as formatInboundEnvelope, Fx as resolveSlackAppToken, GS as pruneMapToMaxSize, Gw as applyChannelMatchMeta, Hg as logAckFailure, Hw as resolveCommandAuthorizedFromAuthorizers, Id as buildUntrustedChannelMetadata, Ir as deleteSlackMessage, Ix as resolveSlackBotToken, Jv as resolvePluginConversationBindingApproval, KS as computeBackoff, Kv as parsePluginBindingApprovalCustomId, Kw as buildChannelKeyCandidates, Nr as truncateSlackText, Nx as resolveSlackAccount, Oh as issuePairingChallenge, Os as createReplyDispatcherWithTyping, Pr as hasSlackThreadParticipation, Px as resolveSlackReplyToMode, Qg as buildPendingHistoryContextFromMap, Qr as resolveSlackThreadHistory, Qt as resolveNativeCommandSessionTargets, Rn as dispatchPluginInteractiveHandler, Rr as editSlackMessage, Rs as resolveEnvelopeFormatOptions, Rv as buildPluginBindingResolvedText, Ug as logInboundDrop, Uw as resolveControlCommandGate, Vw as createDraftStreamLoop, WS as createDedupeCache, Wg as logTypingFailure, Wr as reactSlackMessage, XT as resolveConversationLabel, Xr as resolveSlackAttachmentContent, Yw as resolveChannelEntryMatchWithFallback, Zr as resolveSlackMedia, _S as installRequestBodyLimitGuard, aT as buildAllowlistResolutionSummary, a_ as buildMentionRegexes, ag as createTypingCallbacks, bs as resolveMentionGatingWithBypass, cC as updateLastRoute, cT as patchAllowlistUsersInConfigEntries, e_ as clearHistoryEntriesIfEnabled, ei as sendMessageSlack, en as shouldDebounceTextInbound, iC as readSessionUpdatedAt, iS as resolveAgentOutboundIdentity, iT as addAllowlistUserEntriesFromConfigEntry, ii as resolveSlackWebClientOptions, jc as resolveChannelConfigWrites, kd as readStoreAllowFromForDmPolicy, kr as handleSlackAction, lS as resolveHumanDelayConfig, lm as upsertChannelPairingRequest, ni as normalizeSlackOutboundText, oS as resolveAckReaction, oi as normalizeSlackWebhookPath, qS as sleepWithAbort, qr as removeSlackReaction, r_ as recordPendingHistoryEntryIfEnabled, ri as createSlackWebClient, sT as mergeAllowlist, s_ as matchesMentionWithExplicit, sg as createReplyPrefixOptions, si as registerSlackHttpHandler, tC as isDangerousNameMatchingEnabled, tn as hasControlCommand, uT as summarizeMapping, x as resolveSessionKey } from "./auth-profiles-BJcHzwPy.js";
18
- import "./model-selection-DfA4esOK.js";
19
- import "./agent-scope-DA7O8MVG.js";
20
- import { d as resolveThreadSessionKeys, l as normalizeMainKey } from "./session-key-DyhRsRh-.js";
21
- import { n as normalizeAccountId } from "./account-id-O4Og6DrK.js";
22
- import { r as normalizeStringEntries } from "./string-normalization-CkadSYwL.js";
23
- import "./boundary-file-read-Y1cMjPlu.js";
24
- import "./logger-wrbK9-ju.js";
25
- import "./exec-CoBTyh8B.js";
26
- import "./workspace-CxNKYS0V.js";
27
- import { cn as resolveSlackStreamingMode, g as writeConfigFile, nn as mapStreamingModeToSlackLegacyDraftStreamMode, s as loadConfig, sn as resolveSlackNativeStreaming } from "./io-DcoxdH6t.js";
28
- import "./host-env-security-xy11yVnm.js";
29
- import "./safe-text-BcUvBreN.js";
30
- import "./version-rW_3ob2o.js";
31
- import { c as normalizeResolvedSecretInputString } from "./types.secrets-DuSPmmWB.js";
32
- import "./env-substitution-C9xZMTDL.js";
33
- import "./config-state-D1JkXt39.js";
34
- import "./network-mode-CE-ihBf6.js";
35
- import "./registry-DcDGl2X7.js";
36
- import "./manifest-registry-_0xclaVY.js";
37
- import "./ip-w605xvSx.js";
38
- import "./zod-schema.core-CWxzqcUs.js";
39
- import "./config-CkD8DJ7L.js";
40
- import "./audit-fs-SjcfoPO7.js";
41
- import "./resolve-3ErMOltL.js";
42
- import "./provider-web-search-BR7etTjJ.js";
43
- import { b as resolveTextChunkLimit, r as chunkItems, t as withTimeout } from "./text-runtime-CgEQ9Y9_.js";
44
- import { c as resolveNativeCommandsEnabled, l as resolveNativeSkillsEnabled } from "./workspace-dirs-L1_QQ9mB.js";
45
- import "./config-BbxrRaLf.js";
46
- import "./tailnet-KyAU6tj_.js";
47
- import "./net-B_Iq_SVP.js";
48
- import "./credentials-D5uBf_C5.js";
49
- import "./routes-BNDsNO_e.js";
50
- import "./frontmatter-BTDAgsA3.js";
51
- import "./env-overrides-BHxqjYZG.js";
52
- import "./path-alias-guards-B3ZKrId1.js";
53
- import "./skills-B4h1k-SP.js";
54
- import "./ports-BVwQuCIR.js";
55
- import "./ports-lsof-DiY6GaAf.js";
56
- import "./ssh-tunnel-BaHTFPzH.js";
57
- import "./image-ops-Uw4rEShL.js";
58
- import "./fs-safe-Da4H0IOU.js";
59
- import "./mime-_IkgFMS2.js";
60
- import { t as generateSecureToken } from "./secure-random-BKKzr8he.js";
61
- import "./server-middleware-BPq4bu3A.js";
62
- import "./message-channel-CKeDAoOT.js";
63
- import { i as resolveAgentRoute } from "./resolve-route-IkBfMjBz.js";
64
- import "./internal-hooks-0x1JiQ5f.js";
65
- import { n as shouldAckReaction, t as removeAckReactionAfterReply } from "./ack-reactions-DQfgs0j1.js";
66
- import { f as warnMissingProviderGroupPolicyFallbackOnce, l as resolveDefaultGroupPolicy, t as evaluateGroupRouteAccessForPolicy, u as resolveOpenProviderRuntimeGroupPolicy } from "./group-access-BWGTPTwF.js";
67
- import { b as enqueueSystemEvent } from "./lazy-runtime-DdzFA4b9.js";
68
- import "./config-schema-GQ6uWjXe.js";
69
- import "./method-scopes-D-Q9dvbj.js";
70
- import "./session-cost-usage-B-57mlS8.js";
71
- import { l as resolveStorePath } from "./paths-CDWV-9nX.js";
72
- import "./routing-3o2D0ix4.js";
73
- import "./send-Cm9v3uhF.js";
74
- import "./node-resolve-DfOpQmxm.js";
75
- import "./provider-stream-gVymYPmd.js";
76
- import "./identity-file-DM4N5_7H.js";
77
- import "./provider-models-BA3hqqPq.js";
78
- import "./secret-file-Bjh19aoH.js";
79
- import "./logging-BdFqMomc.js";
80
- import "./runtime-env-CT-voxYE.js";
81
- import "./registry-xhgvU89y.js";
82
- import "./provider-onboard-CBC3kdk_.js";
83
- import "./model-definitions-DwehIMlw.js";
84
- import "./diagnostic-vMghIesG.js";
85
- import "./message-hook-mappers-DORrSLHM.js";
86
- import "./json-store-DlpyvQXN.js";
87
- import "./call-CoaQYq7c.js";
88
- import "./multimodal-BWF8MRkz.js";
89
- import "./memory-search-BBG2BKIh.js";
90
- import "./query-expansion-8R79qExs.js";
91
- import "./search-manager-CfizyEMk.js";
92
- import "./core-EKuk2FxR.js";
93
- import "./issue-format-i6sEuV4a.js";
94
- import "./logging-S-5LPdfQ.js";
95
- import "./note-z78_JyNp.js";
96
- import "./state-paths-C7dX__ql.js";
97
- import "./config-value-Dh8m-CFf.js";
98
- import "./command-secret-targets-CO_lZ_ZZ.js";
99
- import "./brave-BG5Yopn8.js";
100
- import "./provider-usage-DzuzbNBv.js";
101
- import "./perplexity-Brhpb45X.js";
102
- import "./restart-stale-pids-CN9ElYwR.js";
103
- import "./delivery-queue-DLjFP-0C.js";
104
- import "./pairing-token-ANLRyJSu.js";
105
- import "./accounts-BCaV8MsT.js";
106
- import "./process-runtime-CicRKAFe.js";
107
- import "./audit-BqRK9OSj.js";
108
- import "./cli-runtime-DtIDS2w7.js";
109
- import "./cli-utils-FHeUZLsT.js";
110
- import "./help-format-1yV2Xzq7.js";
111
- import "./progress-BQSTKUhd.js";
112
- import { r as createConnectedChannelStatusPatch } from "./gateway-runtime-C76hUmUV.js";
113
- import "./plugin-runtime-DE94A3N9.js";
114
- import { a as normalizeSlackSlug, i as normalizeSlackAllowOwnerEntry, n as normalizeAllowList, o as resolveSlackAllowListMatch, r as normalizeAllowListLower, s as resolveSlackUserAllowed, t as allowListMatches } from "./allow-list-Boi79v-U.js";
115
- import { n as resolveSlackUserAllowlist, t as resolveSlackChannelAllowlist } from "./resolve-channels-BV8GXuPe.js";
116
- import { a as resolveSlackThreadTs, i as readSlackReplyBlocks, n as deliverReplies, t as createSlackReplyDeliveryPlan } from "./replies-hB2aipLu.js";
117
- import * as SlackBoltNamespace from "@slack/bolt";
118
- import SlackBolt from "@slack/bolt";
119
- //#region extensions/slack/src/directory-live.ts
120
- function resolveReadToken(params) {
121
- const account = resolveSlackAccount({
122
- cfg: params.cfg,
123
- accountId: params.accountId
124
- });
125
- return account.userToken ?? account.botToken?.trim();
126
- }
127
- function normalizeQuery(value) {
128
- return value?.trim().toLowerCase() ?? "";
129
- }
130
- function buildUserRank(user) {
131
- let rank = 0;
132
- if (!user.deleted) rank += 2;
133
- if (!user.is_bot && !user.is_app_user) rank += 1;
134
- return rank;
135
- }
136
- function buildChannelRank(channel) {
137
- return channel.is_archived ? 0 : 1;
138
- }
139
- async function listSlackDirectoryPeersLive(params) {
140
- const token = resolveReadToken(params);
141
- if (!token) return [];
142
- const client = createSlackWebClient(token);
143
- const query = normalizeQuery(params.query);
144
- const members = [];
145
- let cursor;
146
- do {
147
- const res = await client.users.list({
148
- limit: 200,
149
- cursor
150
- });
151
- if (Array.isArray(res.members)) members.push(...res.members);
152
- const next = res.response_metadata?.next_cursor?.trim();
153
- cursor = next ? next : void 0;
154
- } while (cursor);
155
- const rows = members.filter((member) => {
156
- const candidates = [
157
- member.profile?.display_name || member.profile?.real_name || member.real_name,
158
- member.name,
159
- member.profile?.email
160
- ].map((item) => item?.trim().toLowerCase()).filter(Boolean);
161
- if (!query) return true;
162
- return candidates.some((candidate) => candidate?.includes(query));
163
- }).map((member) => {
164
- const id = member.id?.trim();
165
- if (!id) return null;
166
- const handle = member.name?.trim();
167
- const display = member.profile?.display_name?.trim() || member.profile?.real_name?.trim() || member.real_name?.trim() || handle;
168
- return {
169
- kind: "user",
170
- id: `user:${id}`,
171
- name: display || void 0,
172
- handle: handle ? `@${handle}` : void 0,
173
- rank: buildUserRank(member),
174
- raw: member
175
- };
176
- }).filter(Boolean);
177
- if (typeof params.limit === "number" && params.limit > 0) return rows.slice(0, params.limit);
178
- return rows;
179
- }
180
- async function listSlackDirectoryGroupsLive(params) {
181
- const token = resolveReadToken(params);
182
- if (!token) return [];
183
- const client = createSlackWebClient(token);
184
- const query = normalizeQuery(params.query);
185
- const channels = [];
186
- let cursor;
187
- do {
188
- const res = await client.conversations.list({
189
- types: "public_channel,private_channel",
190
- exclude_archived: false,
191
- limit: 1e3,
192
- cursor
193
- });
194
- if (Array.isArray(res.channels)) channels.push(...res.channels);
195
- const next = res.response_metadata?.next_cursor?.trim();
196
- cursor = next ? next : void 0;
197
- } while (cursor);
198
- const rows = channels.filter((channel) => {
199
- const name = channel.name?.trim().toLowerCase();
200
- if (!query) return true;
201
- return Boolean(name && name.includes(query));
202
- }).map((channel) => {
203
- const id = channel.id?.trim();
204
- const name = channel.name?.trim();
205
- if (!id || !name) return null;
206
- return {
207
- kind: "group",
208
- id: `channel:${id}`,
209
- name,
210
- handle: `#${name}`,
211
- rank: buildChannelRank(channel),
212
- raw: channel
213
- };
214
- }).filter(Boolean);
215
- if (typeof params.limit === "number" && params.limit > 0) return rows.slice(0, params.limit);
216
- return rows;
217
- }
218
- //#endregion
219
- //#region extensions/slack/src/monitor/commands.ts
220
- /**
221
- * Strip Slack mentions (<@U123>, <@U123|name>) so command detection works on
222
- * normalized text. Use in both prepare and debounce gate for consistency.
223
- */
224
- function stripSlackMentionsForCommandDetection(text) {
225
- return (text ?? "").replace(/<@[^>]+>/g, " ").replace(/\s+/g, " ").trim();
226
- }
227
- function normalizeSlackSlashCommandName(raw) {
228
- return raw.replace(/^\/+/, "");
229
- }
230
- function resolveSlackSlashCommandConfig(raw) {
231
- const name = normalizeSlackSlashCommandName(raw?.name?.trim() || "moldclaw") || "moldclaw";
232
- return {
233
- enabled: raw?.enabled === true,
234
- name,
235
- sessionPrefix: raw?.sessionPrefix?.trim() || "slack:slash",
236
- ephemeral: raw?.ephemeral !== false
237
- };
238
- }
239
- function buildSlackSlashCommandMatcher(name) {
240
- const escaped = normalizeSlackSlashCommandName(name).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
241
- return new RegExp(`^/?${escaped}$`);
242
- }
243
- //#endregion
244
- //#region extensions/slack/src/monitor/policy.ts
245
- function isSlackChannelAllowedByPolicy(params) {
246
- return evaluateGroupRouteAccessForPolicy({
247
- groupPolicy: params.groupPolicy,
248
- routeAllowlistConfigured: params.channelAllowlistConfigured,
249
- routeMatched: params.channelAllowed
250
- }).allowed;
251
- }
252
- //#endregion
253
- //#region extensions/slack/src/monitor/channel-config.ts
254
- function firstDefined(...values) {
255
- for (const value of values) if (typeof value !== "undefined") return value;
256
- }
257
- function resolveSlackChannelLabel(params) {
258
- const channelName = params.channelName?.trim();
259
- if (channelName) return `#${normalizeSlackSlug(channelName) || channelName}`;
260
- const channelId = params.channelId?.trim();
261
- return channelId ? `#${channelId}` : "unknown channel";
262
- }
263
- function resolveSlackChannelConfig(params) {
264
- const { channelId, channelName, channels, channelKeys, defaultRequireMention, allowNameMatching } = params;
265
- const entries = channels ?? {};
266
- const keys = channelKeys ?? Object.keys(entries);
267
- const normalizedName = channelName ? normalizeSlackSlug(channelName) : "";
268
- const directName = channelName ? channelName.trim() : "";
269
- const channelIdLower = channelId.toLowerCase();
270
- const channelIdUpper = channelId.toUpperCase();
271
- const match = resolveChannelEntryMatchWithFallback({
272
- entries,
273
- keys: buildChannelKeyCandidates(channelId, channelIdLower !== channelId ? channelIdLower : void 0, channelIdUpper !== channelId ? channelIdUpper : void 0, allowNameMatching ? channelName ? `#${directName}` : void 0 : void 0, allowNameMatching ? directName : void 0, allowNameMatching ? normalizedName : void 0),
274
- wildcardKey: "*"
275
- });
276
- const { entry: matched, wildcardEntry: fallback } = match;
277
- const requireMentionDefault = defaultRequireMention ?? true;
278
- if (keys.length === 0) return {
279
- allowed: true,
280
- requireMention: requireMentionDefault
281
- };
282
- if (!matched && !fallback) return {
283
- allowed: false,
284
- requireMention: requireMentionDefault
285
- };
286
- const resolved = matched ?? fallback ?? {};
287
- return applyChannelMatchMeta({
288
- allowed: firstDefined(resolved.enabled, resolved.allow, fallback?.enabled, fallback?.allow, true) ?? true,
289
- requireMention: firstDefined(resolved.requireMention, fallback?.requireMention, requireMentionDefault) ?? requireMentionDefault,
290
- allowBots: firstDefined(resolved.allowBots, fallback?.allowBots),
291
- users: firstDefined(resolved.users, fallback?.users),
292
- skills: firstDefined(resolved.skills, fallback?.skills),
293
- systemPrompt: firstDefined(resolved.systemPrompt, fallback?.systemPrompt)
294
- }, match);
295
- }
296
- //#endregion
297
- //#region extensions/slack/src/monitor/channel-type.ts
298
- function inferSlackChannelType(channelId) {
299
- const trimmed = channelId?.trim();
300
- if (!trimmed) return;
301
- if (trimmed.startsWith("D")) return "im";
302
- if (trimmed.startsWith("C")) return "channel";
303
- if (trimmed.startsWith("G")) return "group";
304
- }
305
- function normalizeSlackChannelType(channelType, channelId) {
306
- const normalized = channelType?.trim().toLowerCase();
307
- const inferred = inferSlackChannelType(channelId);
308
- if (normalized === "im" || normalized === "mpim" || normalized === "channel" || normalized === "group") {
309
- if (inferred === "im" && normalized !== "im") return "im";
310
- return normalized;
311
- }
312
- return inferred ?? "channel";
313
- }
314
- //#endregion
315
- //#region extensions/slack/src/monitor/context.ts
316
- function createSlackMonitorContext(params) {
317
- const channelHistories = /* @__PURE__ */ new Map();
318
- const logger = getChildLogger({ module: "slack-auto-reply" });
319
- const channelCache = /* @__PURE__ */ new Map();
320
- const userCache = /* @__PURE__ */ new Map();
321
- const seenMessages = createDedupeCache({
322
- ttlMs: 6e4,
323
- maxSize: 500
324
- });
325
- const allowFrom = normalizeAllowList(params.allowFrom);
326
- const groupDmChannels = normalizeAllowList(params.groupDmChannels);
327
- const groupDmChannelsLower = normalizeAllowListLower(groupDmChannels);
328
- const defaultRequireMention = params.defaultRequireMention ?? true;
329
- const hasChannelAllowlistConfig = Object.keys(params.channelsConfig ?? {}).length > 0;
330
- const channelsConfigKeys = Object.keys(params.channelsConfig ?? {});
331
- const markMessageSeen = (channelId, ts) => {
332
- if (!channelId || !ts) return false;
333
- return seenMessages.check(`${channelId}:${ts}`);
334
- };
335
- const resolveSlackSystemEventSessionKey = (p) => {
336
- const channelId = p.channelId?.trim() ?? "";
337
- if (!channelId) return params.mainKey;
338
- const channelType = normalizeSlackChannelType(p.channelType, channelId);
339
- const isDirectMessage = channelType === "im";
340
- const isGroup = channelType === "mpim";
341
- const from = isDirectMessage ? `slack:${channelId}` : isGroup ? `slack:group:${channelId}` : `slack:channel:${channelId}`;
342
- const chatType = isDirectMessage ? "direct" : isGroup ? "group" : "channel";
343
- const senderId = p.senderId?.trim() ?? "";
344
- try {
345
- const peerKind = isDirectMessage ? "direct" : isGroup ? "group" : "channel";
346
- const peerId = isDirectMessage ? senderId : channelId;
347
- if (peerId) return resolveAgentRoute({
348
- cfg: params.cfg,
349
- channel: "slack",
350
- accountId: params.accountId,
351
- teamId: params.teamId,
352
- peer: {
353
- kind: peerKind,
354
- id: peerId
355
- }
356
- }).sessionKey;
357
- } catch {}
358
- return resolveSessionKey(params.sessionScope, {
359
- From: from,
360
- ChatType: chatType,
361
- Provider: "slack"
362
- }, params.mainKey);
363
- };
364
- const resolveChannelName = async (channelId) => {
365
- const cached = channelCache.get(channelId);
366
- if (cached) return cached;
367
- try {
368
- const info = await params.app.client.conversations.info({
369
- token: params.botToken,
370
- channel: channelId
371
- });
372
- const name = info.channel && "name" in info.channel ? info.channel.name : void 0;
373
- const channel = info.channel ?? void 0;
374
- const entry = {
375
- name,
376
- type: channel?.is_im ? "im" : channel?.is_mpim ? "mpim" : channel?.is_channel ? "channel" : channel?.is_group ? "group" : void 0,
377
- topic: channel && "topic" in channel ? channel.topic?.value ?? void 0 : void 0,
378
- purpose: channel && "purpose" in channel ? channel.purpose?.value ?? void 0 : void 0
379
- };
380
- channelCache.set(channelId, entry);
381
- return entry;
382
- } catch {
383
- return {};
384
- }
385
- };
386
- const resolveUserName = async (userId) => {
387
- const cached = userCache.get(userId);
388
- if (cached) return cached;
389
- try {
390
- const info = await params.app.client.users.info({
391
- token: params.botToken,
392
- user: userId
393
- });
394
- const profile = info.user?.profile;
395
- const entry = { name: profile?.display_name || profile?.real_name || info.user?.name || void 0 };
396
- userCache.set(userId, entry);
397
- return entry;
398
- } catch {
399
- return {};
400
- }
401
- };
402
- const setSlackThreadStatus = async (p) => {
403
- if (!p.threadTs) return;
404
- const payload = {
405
- token: params.botToken,
406
- channel_id: p.channelId,
407
- thread_ts: p.threadTs,
408
- status: p.status
409
- };
410
- const client = params.app.client;
411
- try {
412
- if (client.assistant?.threads?.setStatus) {
413
- await client.assistant.threads.setStatus(payload);
414
- return;
415
- }
416
- if (typeof client.apiCall === "function") await client.apiCall("assistant.threads.setStatus", payload);
417
- } catch (err) {
418
- logVerbose(`slack status update failed for channel ${p.channelId}: ${String(err)}`);
419
- }
420
- };
421
- const isChannelAllowed = (p) => {
422
- const channelType = normalizeSlackChannelType(p.channelType, p.channelId);
423
- const isDirectMessage = channelType === "im";
424
- const isGroupDm = channelType === "mpim";
425
- const isRoom = channelType === "channel" || channelType === "group";
426
- if (isDirectMessage && !params.dmEnabled) return false;
427
- if (isGroupDm && !params.groupDmEnabled) return false;
428
- if (isGroupDm && groupDmChannels.length > 0) {
429
- const candidates = [
430
- p.channelId,
431
- p.channelName ? `#${p.channelName}` : void 0,
432
- p.channelName,
433
- p.channelName ? normalizeSlackSlug(p.channelName) : void 0
434
- ].filter((value) => Boolean(value)).map((value) => value.toLowerCase());
435
- if (!(groupDmChannelsLower.includes("*") || candidates.some((candidate) => groupDmChannelsLower.includes(candidate)))) return false;
436
- }
437
- if (isRoom && p.channelId) {
438
- const channelConfig = resolveSlackChannelConfig({
439
- channelId: p.channelId,
440
- channelName: p.channelName,
441
- channels: params.channelsConfig,
442
- channelKeys: channelsConfigKeys,
443
- defaultRequireMention,
444
- allowNameMatching: params.allowNameMatching
445
- });
446
- const channelMatchMeta = formatAllowlistMatchMeta(channelConfig);
447
- const channelAllowed = channelConfig?.allowed !== false;
448
- const channelAllowlistConfigured = hasChannelAllowlistConfig;
449
- if (!isSlackChannelAllowedByPolicy({
450
- groupPolicy: params.groupPolicy,
451
- channelAllowlistConfigured,
452
- channelAllowed
453
- })) {
454
- logVerbose(`slack: drop channel ${p.channelId} (groupPolicy=${params.groupPolicy}, ${channelMatchMeta})`);
455
- return false;
456
- }
457
- const hasExplicitConfig = Boolean(channelConfig?.matchSource);
458
- if (!channelAllowed && (params.groupPolicy !== "open" || hasExplicitConfig)) {
459
- logVerbose(`slack: drop channel ${p.channelId} (${channelMatchMeta})`);
460
- return false;
461
- }
462
- logVerbose(`slack: allow channel ${p.channelId} (${channelMatchMeta})`);
463
- }
464
- return true;
465
- };
466
- const shouldDropMismatchedSlackEvent = (body) => {
467
- if (!body || typeof body !== "object") return false;
468
- const raw = body;
469
- const incomingApiAppId = typeof raw.api_app_id === "string" ? raw.api_app_id : "";
470
- const incomingTeamId = typeof raw.team_id === "string" ? raw.team_id : typeof raw.team?.id === "string" ? raw.team.id : "";
471
- if (params.apiAppId && incomingApiAppId && incomingApiAppId !== params.apiAppId) {
472
- logVerbose(`slack: drop event with api_app_id=${incomingApiAppId} (expected ${params.apiAppId})`);
473
- return true;
474
- }
475
- if (params.teamId && incomingTeamId && incomingTeamId !== params.teamId) {
476
- logVerbose(`slack: drop event with team_id=${incomingTeamId} (expected ${params.teamId})`);
477
- return true;
478
- }
479
- return false;
480
- };
481
- return {
482
- cfg: params.cfg,
483
- accountId: params.accountId,
484
- botToken: params.botToken,
485
- app: params.app,
486
- runtime: params.runtime,
487
- botUserId: params.botUserId,
488
- teamId: params.teamId,
489
- apiAppId: params.apiAppId,
490
- historyLimit: params.historyLimit,
491
- channelHistories,
492
- sessionScope: params.sessionScope,
493
- mainKey: params.mainKey,
494
- dmEnabled: params.dmEnabled,
495
- dmPolicy: params.dmPolicy,
496
- allowFrom,
497
- allowNameMatching: params.allowNameMatching,
498
- groupDmEnabled: params.groupDmEnabled,
499
- groupDmChannels,
500
- defaultRequireMention,
501
- channelsConfig: params.channelsConfig,
502
- channelsConfigKeys,
503
- groupPolicy: params.groupPolicy,
504
- useAccessGroups: params.useAccessGroups,
505
- reactionMode: params.reactionMode,
506
- reactionAllowlist: params.reactionAllowlist,
507
- replyToMode: params.replyToMode,
508
- threadHistoryScope: params.threadHistoryScope,
509
- threadInheritParent: params.threadInheritParent,
510
- slashCommand: params.slashCommand,
511
- textLimit: params.textLimit,
512
- ackReactionScope: params.ackReactionScope,
513
- typingReaction: params.typingReaction,
514
- mediaMaxBytes: params.mediaMaxBytes,
515
- removeAckAfterReply: params.removeAckAfterReply,
516
- logger,
517
- markMessageSeen,
518
- shouldDropMismatchedSlackEvent,
519
- resolveSlackSystemEventSessionKey,
520
- isChannelAllowed,
521
- resolveChannelName,
522
- resolveUserName,
523
- setSlackThreadStatus
524
- };
525
- }
526
- //#endregion
527
- //#region extensions/slack/src/channel-migration.ts
528
- function resolveAccountChannels(cfg, accountId) {
529
- if (!accountId) return {};
530
- const normalized = normalizeAccountId(accountId);
531
- const accounts = cfg.channels?.slack?.accounts;
532
- if (!accounts || typeof accounts !== "object") return {};
533
- const exact = accounts[normalized];
534
- if (exact?.channels) return { channels: exact.channels };
535
- const matchKey = Object.keys(accounts).find((key) => key.toLowerCase() === normalized.toLowerCase());
536
- return { channels: matchKey ? accounts[matchKey]?.channels : void 0 };
537
- }
538
- function migrateSlackChannelsInPlace(channels, oldChannelId, newChannelId) {
539
- if (!channels) return {
540
- migrated: false,
541
- skippedExisting: false
542
- };
543
- if (oldChannelId === newChannelId) return {
544
- migrated: false,
545
- skippedExisting: false
546
- };
547
- if (!Object.hasOwn(channels, oldChannelId)) return {
548
- migrated: false,
549
- skippedExisting: false
550
- };
551
- if (Object.hasOwn(channels, newChannelId)) return {
552
- migrated: false,
553
- skippedExisting: true
554
- };
555
- channels[newChannelId] = channels[oldChannelId];
556
- delete channels[oldChannelId];
557
- return {
558
- migrated: true,
559
- skippedExisting: false
560
- };
561
- }
562
- function migrateSlackChannelConfig(params) {
563
- const scopes = [];
564
- let migrated = false;
565
- let skippedExisting = false;
566
- const accountChannels = resolveAccountChannels(params.cfg, params.accountId).channels;
567
- if (accountChannels) {
568
- const result = migrateSlackChannelsInPlace(accountChannels, params.oldChannelId, params.newChannelId);
569
- if (result.migrated) {
570
- migrated = true;
571
- scopes.push("account");
572
- }
573
- if (result.skippedExisting) skippedExisting = true;
574
- }
575
- const globalChannels = params.cfg.channels?.slack?.channels;
576
- if (globalChannels) {
577
- const result = migrateSlackChannelsInPlace(globalChannels, params.oldChannelId, params.newChannelId);
578
- if (result.migrated) {
579
- migrated = true;
580
- scopes.push("global");
581
- }
582
- if (result.skippedExisting) skippedExisting = true;
583
- }
584
- return {
585
- migrated,
586
- skippedExisting,
587
- scopes
588
- };
589
- }
590
- //#endregion
591
- //#region extensions/slack/src/monitor/events/channels.ts
592
- function registerSlackChannelEvents(params) {
593
- const { ctx, trackEvent } = params;
594
- const enqueueChannelSystemEvent = (params) => {
595
- if (!ctx.isChannelAllowed({
596
- channelId: params.channelId,
597
- channelName: params.channelName,
598
- channelType: "channel"
599
- })) return;
600
- const label = resolveSlackChannelLabel({
601
- channelId: params.channelId,
602
- channelName: params.channelName
603
- });
604
- const sessionKey = ctx.resolveSlackSystemEventSessionKey({
605
- channelId: params.channelId,
606
- channelType: "channel"
607
- });
608
- enqueueSystemEvent(`Slack channel ${params.kind}: ${label}.`, {
609
- sessionKey,
610
- contextKey: `slack:channel:${params.kind}:${params.channelId ?? params.channelName ?? "unknown"}`
611
- });
612
- };
613
- ctx.app.event("channel_created", async ({ event, body }) => {
614
- try {
615
- if (ctx.shouldDropMismatchedSlackEvent(body)) return;
616
- trackEvent?.();
617
- const payload = event;
618
- const channelId = payload.channel?.id;
619
- const channelName = payload.channel?.name;
620
- enqueueChannelSystemEvent({
621
- kind: "created",
622
- channelId,
623
- channelName
624
- });
625
- } catch (err) {
626
- ctx.runtime.error?.(danger(`slack channel created handler failed: ${String(err)}`));
627
- }
628
- });
629
- ctx.app.event("channel_rename", async ({ event, body }) => {
630
- try {
631
- if (ctx.shouldDropMismatchedSlackEvent(body)) return;
632
- trackEvent?.();
633
- const payload = event;
634
- const channelId = payload.channel?.id;
635
- enqueueChannelSystemEvent({
636
- kind: "renamed",
637
- channelId,
638
- channelName: payload.channel?.name_normalized ?? payload.channel?.name
639
- });
640
- } catch (err) {
641
- ctx.runtime.error?.(danger(`slack channel rename handler failed: ${String(err)}`));
642
- }
643
- });
644
- ctx.app.event("channel_id_changed", async ({ event, body }) => {
645
- try {
646
- if (ctx.shouldDropMismatchedSlackEvent(body)) return;
647
- trackEvent?.();
648
- const payload = event;
649
- const oldChannelId = payload.old_channel_id;
650
- const newChannelId = payload.new_channel_id;
651
- if (!oldChannelId || !newChannelId) return;
652
- const label = resolveSlackChannelLabel({
653
- channelId: newChannelId,
654
- channelName: (await ctx.resolveChannelName(newChannelId))?.name
655
- });
656
- ctx.runtime.log?.(warn(`[slack] Channel ID changed: ${oldChannelId} → ${newChannelId} (${label})`));
657
- if (!resolveChannelConfigWrites({
658
- cfg: ctx.cfg,
659
- channelId: "slack",
660
- accountId: ctx.accountId
661
- })) {
662
- ctx.runtime.log?.(warn("[slack] Config writes disabled; skipping channel config migration."));
663
- return;
664
- }
665
- const currentConfig = loadConfig();
666
- const migration = migrateSlackChannelConfig({
667
- cfg: currentConfig,
668
- accountId: ctx.accountId,
669
- oldChannelId,
670
- newChannelId
671
- });
672
- if (migration.migrated) {
673
- migrateSlackChannelConfig({
674
- cfg: ctx.cfg,
675
- accountId: ctx.accountId,
676
- oldChannelId,
677
- newChannelId
678
- });
679
- await writeConfigFile(currentConfig);
680
- ctx.runtime.log?.(warn("[slack] Channel config migrated and saved successfully."));
681
- } else if (migration.skippedExisting) ctx.runtime.log?.(warn(`[slack] Channel config already exists for ${newChannelId}; leaving ${oldChannelId} unchanged`));
682
- else ctx.runtime.log?.(warn(`[slack] No config found for old channel ID ${oldChannelId}; migration logged only`));
683
- } catch (err) {
684
- ctx.runtime.error?.(danger(`slack channel_id_changed handler failed: ${String(err)}`));
685
- }
686
- });
687
- }
688
- //#endregion
689
- //#region extensions/slack/src/monitor/auth.ts
690
- let slackAllowFromCache = /* @__PURE__ */ new WeakMap();
691
- const DEFAULT_PAIRING_ALLOW_FROM_CACHE_TTL_MS = 5e3;
692
- function getPairingAllowFromCacheTtlMs() {
693
- const raw = process.env.MOLDCLAW_SLACK_PAIRING_ALLOWFROM_CACHE_TTL_MS?.trim();
694
- if (!raw) return DEFAULT_PAIRING_ALLOW_FROM_CACHE_TTL_MS;
695
- const parsed = Number(raw);
696
- if (!Number.isFinite(parsed)) return DEFAULT_PAIRING_ALLOW_FROM_CACHE_TTL_MS;
697
- return Math.max(0, Math.floor(parsed));
698
- }
699
- function getAllowFromCacheState(ctx) {
700
- const existing = slackAllowFromCache.get(ctx);
701
- if (existing) return existing;
702
- const next = {};
703
- slackAllowFromCache.set(ctx, next);
704
- return next;
705
- }
706
- function buildBaseAllowFrom(ctx) {
707
- const allowFrom = normalizeAllowList(ctx.allowFrom);
708
- return {
709
- allowFrom,
710
- allowFromLower: normalizeAllowListLower(allowFrom)
711
- };
712
- }
713
- async function resolveSlackEffectiveAllowFrom(ctx, options) {
714
- const includePairingStore = options?.includePairingStore === true;
715
- const cache = getAllowFromCacheState(ctx);
716
- const baseSignature = JSON.stringify(ctx.allowFrom);
717
- if (cache.baseSignature !== baseSignature || !cache.base) {
718
- cache.baseSignature = baseSignature;
719
- cache.base = buildBaseAllowFrom(ctx);
720
- cache.pairing = void 0;
721
- cache.pairingKey = void 0;
722
- cache.pairingExpiresAtMs = void 0;
723
- cache.pairingPending = void 0;
724
- }
725
- if (!includePairingStore) return cache.base;
726
- const ttlMs = getPairingAllowFromCacheTtlMs();
727
- const nowMs = Date.now();
728
- const pairingKey = `${ctx.accountId}:${ctx.dmPolicy}`;
729
- if (ttlMs > 0 && cache.pairing && cache.pairingKey === pairingKey && (cache.pairingExpiresAtMs ?? 0) >= nowMs) return cache.pairing;
730
- if (cache.pairingPending && cache.pairingKey === pairingKey) return await cache.pairingPending;
731
- const pairingPending = (async () => {
732
- let storeAllowFrom = [];
733
- try {
734
- const resolved = await readStoreAllowFromForDmPolicy({
735
- provider: "slack",
736
- accountId: ctx.accountId,
737
- dmPolicy: ctx.dmPolicy
738
- });
739
- storeAllowFrom = Array.isArray(resolved) ? resolved : [];
740
- } catch {
741
- storeAllowFrom = [];
742
- }
743
- const allowFrom = normalizeAllowList([...cache.base?.allowFrom ?? [], ...storeAllowFrom]);
744
- return {
745
- allowFrom,
746
- allowFromLower: normalizeAllowListLower(allowFrom)
747
- };
748
- })();
749
- cache.pairingKey = pairingKey;
750
- cache.pairingPending = pairingPending;
751
- try {
752
- const resolved = await pairingPending;
753
- if (ttlMs > 0) {
754
- cache.pairing = resolved;
755
- cache.pairingExpiresAtMs = nowMs + ttlMs;
756
- } else {
757
- cache.pairing = void 0;
758
- cache.pairingExpiresAtMs = void 0;
759
- }
760
- return resolved;
761
- } finally {
762
- if (cache.pairingPending === pairingPending) cache.pairingPending = void 0;
763
- }
764
- }
765
- function isSlackSenderAllowListed(params) {
766
- const { allowListLower, senderId, senderName, allowNameMatching } = params;
767
- return allowListLower.length === 0 || allowListMatches({
768
- allowList: allowListLower,
769
- id: senderId,
770
- name: senderName,
771
- allowNameMatching
772
- });
773
- }
774
- async function authorizeSlackSystemEventSender(params) {
775
- const senderId = params.senderId?.trim();
776
- if (!senderId) return {
777
- allowed: false,
778
- reason: "missing-sender"
779
- };
780
- const expectedSenderId = params.expectedSenderId?.trim();
781
- if (expectedSenderId && expectedSenderId !== senderId) return {
782
- allowed: false,
783
- reason: "sender-mismatch"
784
- };
785
- const channelId = params.channelId?.trim();
786
- let channelType = normalizeSlackChannelType(params.channelType, channelId);
787
- let channelName;
788
- if (channelId) {
789
- const info = await params.ctx.resolveChannelName(channelId).catch(() => ({}));
790
- channelName = info.name;
791
- channelType = normalizeSlackChannelType(params.channelType ?? info.type, channelId);
792
- if (!params.ctx.isChannelAllowed({
793
- channelId,
794
- channelName,
795
- channelType
796
- })) return {
797
- allowed: false,
798
- reason: "channel-not-allowed",
799
- channelType,
800
- channelName
801
- };
802
- }
803
- const senderName = (await params.ctx.resolveUserName(senderId).catch(() => ({}))).name;
804
- const resolveAllowFromLower = async (includePairingStore = false) => (await resolveSlackEffectiveAllowFrom(params.ctx, { includePairingStore })).allowFromLower;
805
- if (channelType === "im") {
806
- if (!params.ctx.dmEnabled || params.ctx.dmPolicy === "disabled") return {
807
- allowed: false,
808
- reason: "dm-disabled",
809
- channelType,
810
- channelName
811
- };
812
- if (params.ctx.dmPolicy !== "open") {
813
- if (!isSlackSenderAllowListed({
814
- allowListLower: await resolveAllowFromLower(true),
815
- senderId,
816
- senderName,
817
- allowNameMatching: params.ctx.allowNameMatching
818
- })) return {
819
- allowed: false,
820
- reason: "sender-not-allowlisted",
821
- channelType,
822
- channelName
823
- };
824
- }
825
- } else if (!channelId) {
826
- const allowFromLower = await resolveAllowFromLower(false);
827
- if (allowFromLower.length > 0) {
828
- if (!isSlackSenderAllowListed({
829
- allowListLower: allowFromLower,
830
- senderId,
831
- senderName,
832
- allowNameMatching: params.ctx.allowNameMatching
833
- })) return {
834
- allowed: false,
835
- reason: "sender-not-allowlisted"
836
- };
837
- }
838
- } else {
839
- const channelConfig = resolveSlackChannelConfig({
840
- channelId,
841
- channelName,
842
- channels: params.ctx.channelsConfig,
843
- channelKeys: params.ctx.channelsConfigKeys,
844
- defaultRequireMention: params.ctx.defaultRequireMention,
845
- allowNameMatching: params.ctx.allowNameMatching
846
- });
847
- if (Array.isArray(channelConfig?.users) && channelConfig.users.length > 0) {
848
- if (!resolveSlackUserAllowed({
849
- allowList: channelConfig?.users,
850
- userId: senderId,
851
- userName: senderName,
852
- allowNameMatching: params.ctx.allowNameMatching
853
- })) return {
854
- allowed: false,
855
- reason: "sender-not-channel-allowed",
856
- channelType,
857
- channelName
858
- };
859
- }
860
- }
861
- return {
862
- allowed: true,
863
- channelType,
864
- channelName
865
- };
866
- }
867
- //#endregion
868
- //#region extensions/slack/src/monitor/mrkdwn.ts
869
- function escapeSlackMrkdwn(value) {
870
- return value.replaceAll("\\", "\\\\").replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replace(/([*_`~])/g, "\\$1");
871
- }
872
- //#endregion
873
- //#region extensions/slack/src/monitor/events/interactions.block-actions.ts
874
- function readOptionValues(options) {
875
- if (!Array.isArray(options)) return;
876
- const values = options.map((option) => option && typeof option === "object" ? option.value : null).filter((value) => typeof value === "string" && value.trim().length > 0);
877
- return values.length > 0 ? values : void 0;
878
- }
879
- function readOptionLabels(options) {
880
- if (!Array.isArray(options)) return;
881
- const labels = options.map((option) => option && typeof option === "object" ? option.text?.text ?? null : null).filter((label) => typeof label === "string" && label.trim().length > 0);
882
- return labels.length > 0 ? labels : void 0;
883
- }
884
- function uniqueNonEmptyStrings(values) {
885
- const unique = [];
886
- const seen = /* @__PURE__ */ new Set();
887
- for (const entry of values) {
888
- if (typeof entry !== "string") continue;
889
- const trimmed = entry.trim();
890
- if (!trimmed || seen.has(trimmed)) continue;
891
- seen.add(trimmed);
892
- unique.push(trimmed);
893
- }
894
- return unique;
895
- }
896
- function collectRichTextFragments(value, out) {
897
- if (!value || typeof value !== "object") return;
898
- const typed = value;
899
- if (typeof typed.text === "string" && typed.text.trim().length > 0) out.push(typed.text.trim());
900
- if (Array.isArray(typed.elements)) for (const child of typed.elements) collectRichTextFragments(child, out);
901
- }
902
- function summarizeRichTextPreview(value) {
903
- const fragments = [];
904
- collectRichTextFragments(value, fragments);
905
- if (fragments.length === 0) return;
906
- const joined = fragments.join(" ").replace(/\s+/g, " ").trim();
907
- if (!joined) return;
908
- const max = 120;
909
- return joined.length <= max ? joined : `${joined.slice(0, max - 1)}…`;
910
- }
911
- function readInteractionAction(raw) {
912
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
913
- return raw;
914
- }
915
- function summarizeAction(action) {
916
- const typed = action;
917
- const actionType = typed.type;
918
- const selectedUsers = uniqueNonEmptyStrings([...typed.selected_user ? [typed.selected_user] : [], ...Array.isArray(typed.selected_users) ? typed.selected_users : []]);
919
- const selectedChannels = uniqueNonEmptyStrings([...typed.selected_channel ? [typed.selected_channel] : [], ...Array.isArray(typed.selected_channels) ? typed.selected_channels : []]);
920
- const selectedConversations = uniqueNonEmptyStrings([...typed.selected_conversation ? [typed.selected_conversation] : [], ...Array.isArray(typed.selected_conversations) ? typed.selected_conversations : []]);
921
- const selectedValues = uniqueNonEmptyStrings([
922
- ...typed.selected_option?.value ? [typed.selected_option.value] : [],
923
- ...readOptionValues(typed.selected_options) ?? [],
924
- ...selectedUsers,
925
- ...selectedChannels,
926
- ...selectedConversations
927
- ]);
928
- const selectedLabels = uniqueNonEmptyStrings([...typed.selected_option?.text?.text ? [typed.selected_option.text.text] : [], ...readOptionLabels(typed.selected_options) ?? []]);
929
- const inputValue = typeof typed.value === "string" ? typed.value : void 0;
930
- const inputNumber = actionType === "number_input" && inputValue != null ? Number.parseFloat(inputValue) : void 0;
931
- const parsedNumber = Number.isFinite(inputNumber) ? inputNumber : void 0;
932
- const inputEmail = actionType === "email_text_input" && inputValue?.includes("@") ? inputValue : void 0;
933
- let inputUrl;
934
- if (actionType === "url_text_input" && inputValue) try {
935
- inputUrl = new URL(inputValue).toString();
936
- } catch {
937
- inputUrl = void 0;
938
- }
939
- const richTextValue = actionType === "rich_text_input" ? typed.rich_text_value : void 0;
940
- const richTextPreview = summarizeRichTextPreview(richTextValue);
941
- return {
942
- actionType,
943
- inputKind: actionType === "number_input" ? "number" : actionType === "email_text_input" ? "email" : actionType === "url_text_input" ? "url" : actionType === "rich_text_input" ? "rich_text" : inputValue != null ? "text" : void 0,
944
- value: typed.value,
945
- selectedValues: selectedValues.length > 0 ? selectedValues : void 0,
946
- selectedUsers: selectedUsers.length > 0 ? selectedUsers : void 0,
947
- selectedChannels: selectedChannels.length > 0 ? selectedChannels : void 0,
948
- selectedConversations: selectedConversations.length > 0 ? selectedConversations : void 0,
949
- selectedLabels: selectedLabels.length > 0 ? selectedLabels : void 0,
950
- selectedDate: typed.selected_date,
951
- selectedTime: typed.selected_time,
952
- selectedDateTime: typeof typed.selected_date_time === "number" ? typed.selected_date_time : void 0,
953
- inputValue,
954
- inputNumber: parsedNumber,
955
- inputEmail,
956
- inputUrl,
957
- richTextValue,
958
- richTextPreview,
959
- workflowTriggerUrl: typed.workflow?.trigger_url,
960
- workflowId: typed.workflow?.workflow_id
961
- };
962
- }
963
- function isBulkActionsBlock(block) {
964
- return block.type === "actions" && Array.isArray(block.elements) && block.elements.length > 0 && block.elements.every((el) => typeof el.action_id === "string" && el.action_id.includes("_all_"));
965
- }
966
- function formatInteractionSelectionLabel(params) {
967
- if (params.summary.actionType === "button" && params.buttonText?.trim()) return params.buttonText.trim();
968
- if (params.summary.selectedLabels?.length) {
969
- if (params.summary.selectedLabels.length <= 3) return params.summary.selectedLabels.join(", ");
970
- return `${params.summary.selectedLabels.slice(0, 3).join(", ")} +${params.summary.selectedLabels.length - 3}`;
971
- }
972
- if (params.summary.selectedValues?.length) {
973
- if (params.summary.selectedValues.length <= 3) return params.summary.selectedValues.join(", ");
974
- return `${params.summary.selectedValues.slice(0, 3).join(", ")} +${params.summary.selectedValues.length - 3}`;
975
- }
976
- if (params.summary.selectedDate) return params.summary.selectedDate;
977
- if (params.summary.selectedTime) return params.summary.selectedTime;
978
- if (typeof params.summary.selectedDateTime === "number") return (/* @__PURE__ */ new Date(params.summary.selectedDateTime * 1e3)).toISOString();
979
- if (params.summary.richTextPreview) return params.summary.richTextPreview;
980
- if (params.summary.value?.trim()) return params.summary.value.trim();
981
- return params.actionId;
982
- }
983
- function formatInteractionConfirmationText(params) {
984
- const actor = params.userId?.trim() ? ` by <@${params.userId.trim()}>` : "";
985
- return `:white_check_mark: *${escapeSlackMrkdwn(params.selectedLabel)}* selected${actor}`;
986
- }
987
- function buildSlackPluginInteractionData(params) {
988
- const actionId = params.actionId.trim();
989
- if (!actionId) return null;
990
- const payload = params.summary.value?.trim() || params.summary.selectedValues?.map((value) => value.trim()).find(Boolean) || "";
991
- if (actionId === "moldclaw:reply_button" || actionId === "moldclaw:reply_select") return payload || null;
992
- return payload ? `${actionId}:${payload}` : actionId;
993
- }
994
- function buildSlackPluginInteractionId(params) {
995
- const primaryValue = params.summary.value?.trim() || params.summary.selectedValues?.map((value) => value.trim()).find(Boolean) || "";
996
- return [
997
- params.userId?.trim() || "",
998
- params.channelId?.trim() || "",
999
- params.messageTs?.trim() || "",
1000
- params.triggerId?.trim() || "",
1001
- params.actionId.trim(),
1002
- primaryValue
1003
- ].join(":");
1004
- }
1005
- function parseSlackBlockAction(params) {
1006
- const typedBody = params.body;
1007
- const typedAction = readInteractionAction(params.action);
1008
- if (!typedAction) {
1009
- params.log?.(`slack:interaction malformed action payload channel=${typedBody.channel?.id ?? typedBody.container?.channel_id ?? "unknown"} user=${typedBody.user?.id ?? "unknown"}`);
1010
- return null;
1011
- }
1012
- const typedActionWithText = typedAction;
1013
- return {
1014
- typedBody,
1015
- typedAction,
1016
- typedActionWithText,
1017
- actionId: typeof typedActionWithText.action_id === "string" ? typedActionWithText.action_id : "unknown",
1018
- blockId: typedActionWithText.block_id,
1019
- userId: typedBody.user?.id ?? "unknown",
1020
- channelId: typedBody.channel?.id ?? typedBody.container?.channel_id,
1021
- messageTs: typedBody.message?.ts ?? typedBody.container?.message_ts,
1022
- threadTs: typedBody.container?.thread_ts,
1023
- actionSummary: summarizeAction(typedAction)
1024
- };
1025
- }
1026
- async function respondEphemeral(respond, text) {
1027
- if (!respond) return;
1028
- try {
1029
- await respond({
1030
- text,
1031
- response_type: "ephemeral"
1032
- });
1033
- } catch {}
1034
- }
1035
- async function updateSlackInteractionMessage(params) {
1036
- if (!params.channelId || !params.messageTs) return;
1037
- await params.ctx.app.client.chat.update({
1038
- channel: params.channelId,
1039
- ts: params.messageTs,
1040
- text: params.text,
1041
- ...params.blocks ? { blocks: params.blocks } : {}
1042
- });
1043
- }
1044
- async function authorizeSlackBlockAction(params) {
1045
- const auth = await authorizeSlackSystemEventSender({
1046
- ctx: params.ctx,
1047
- senderId: params.parsed.userId,
1048
- channelId: params.parsed.channelId
1049
- });
1050
- if (auth.allowed) return auth;
1051
- params.ctx.runtime.log?.(`slack:interaction drop action=${params.parsed.actionId} user=${params.parsed.userId} channel=${params.parsed.channelId ?? "unknown"} reason=${auth.reason ?? "unauthorized"}`);
1052
- await respondEphemeral(params.respond, "You are not authorized to use this control.");
1053
- return { allowed: false };
1054
- }
1055
- async function handleSlackPluginBindingApproval(params) {
1056
- const pluginBindingApproval = parsePluginBindingApprovalCustomId(params.pluginInteractionData);
1057
- if (!pluginBindingApproval) return false;
1058
- const resolved = await resolvePluginConversationBindingApproval({
1059
- approvalId: pluginBindingApproval.approvalId,
1060
- decision: pluginBindingApproval.decision,
1061
- senderId: params.parsed.userId
1062
- });
1063
- try {
1064
- await updateSlackInteractionMessage({
1065
- ctx: params.ctx,
1066
- channelId: params.parsed.channelId,
1067
- messageTs: params.parsed.messageTs,
1068
- text: params.parsed.typedBody.message?.text ?? "",
1069
- blocks: []
1070
- });
1071
- } catch {}
1072
- await respondEphemeral(params.respond, buildPluginBindingResolvedText(resolved));
1073
- return true;
1074
- }
1075
- async function dispatchSlackPluginInteraction(params) {
1076
- const pluginInteractionId = buildSlackPluginInteractionId({
1077
- userId: params.parsed.userId,
1078
- channelId: params.parsed.channelId,
1079
- messageTs: params.parsed.messageTs,
1080
- triggerId: params.parsed.typedBody.trigger_id,
1081
- actionId: params.parsed.actionId,
1082
- summary: params.parsed.actionSummary
1083
- });
1084
- if (await handleSlackPluginBindingApproval({
1085
- ctx: params.ctx,
1086
- parsed: params.parsed,
1087
- pluginInteractionData: params.pluginInteractionData,
1088
- respond: params.respond
1089
- })) return true;
1090
- const pluginResult = await dispatchPluginInteractiveHandler({
1091
- channel: "slack",
1092
- data: params.pluginInteractionData,
1093
- interactionId: pluginInteractionId,
1094
- ctx: {
1095
- accountId: params.ctx.accountId,
1096
- interactionId: pluginInteractionId,
1097
- conversationId: params.parsed.channelId ?? "",
1098
- parentConversationId: void 0,
1099
- threadId: params.parsed.threadTs,
1100
- senderId: params.parsed.userId,
1101
- senderUsername: void 0,
1102
- auth: params.auth,
1103
- interaction: {
1104
- kind: params.parsed.actionSummary.actionType === "button" ? "button" : "select",
1105
- actionId: params.parsed.actionId,
1106
- blockId: params.parsed.blockId,
1107
- messageTs: params.parsed.messageTs,
1108
- threadTs: params.parsed.threadTs,
1109
- value: params.parsed.actionSummary.value,
1110
- selectedValues: params.parsed.actionSummary.selectedValues,
1111
- selectedLabels: params.parsed.actionSummary.selectedLabels,
1112
- triggerId: params.parsed.typedBody.trigger_id,
1113
- responseUrl: params.parsed.typedBody.response_url
1114
- }
1115
- },
1116
- respond: {
1117
- acknowledge: async () => {},
1118
- reply: async ({ text, responseType }) => {
1119
- if (!text) return;
1120
- await params.respond?.({
1121
- text,
1122
- response_type: responseType ?? "ephemeral"
1123
- });
1124
- },
1125
- followUp: async ({ text, responseType }) => {
1126
- if (!text) return;
1127
- await params.respond?.({
1128
- text,
1129
- response_type: responseType ?? "ephemeral"
1130
- });
1131
- },
1132
- editMessage: async ({ text, blocks }) => {
1133
- await updateSlackInteractionMessage({
1134
- ctx: params.ctx,
1135
- channelId: params.parsed.channelId,
1136
- messageTs: params.parsed.messageTs,
1137
- text: text ?? params.parsed.typedBody.message?.text ?? "",
1138
- blocks: Array.isArray(blocks) ? blocks : void 0
1139
- });
1140
- }
1141
- }
1142
- });
1143
- return pluginResult.matched && pluginResult.handled;
1144
- }
1145
- function enqueueSlackBlockActionEvent(params) {
1146
- const eventPayload = {
1147
- interactionType: "block_action",
1148
- actionId: params.parsed.actionId,
1149
- blockId: params.parsed.blockId,
1150
- ...params.parsed.actionSummary,
1151
- userId: params.parsed.userId,
1152
- teamId: params.parsed.typedBody.team?.id,
1153
- triggerId: params.parsed.typedBody.trigger_id,
1154
- responseUrl: params.parsed.typedBody.response_url,
1155
- channelId: params.parsed.channelId,
1156
- messageTs: params.parsed.messageTs,
1157
- threadTs: params.parsed.threadTs
1158
- };
1159
- params.ctx.runtime.log?.(`slack:interaction action=${params.parsed.actionId} type=${params.parsed.actionSummary.actionType ?? "unknown"} user=${params.parsed.userId} channel=${params.parsed.channelId}`);
1160
- const sessionKey = params.ctx.resolveSlackSystemEventSessionKey({
1161
- channelId: params.parsed.channelId,
1162
- channelType: params.auth.channelType,
1163
- senderId: params.parsed.userId
1164
- });
1165
- const contextParts = [
1166
- "slack:interaction",
1167
- params.parsed.channelId,
1168
- params.parsed.messageTs,
1169
- params.parsed.actionId
1170
- ].filter(Boolean);
1171
- enqueueSystemEvent(params.formatSystemEvent(eventPayload), {
1172
- sessionKey,
1173
- contextKey: contextParts.join(":")
1174
- });
1175
- }
1176
- function buildSlackConfirmationBlocks(params) {
1177
- const selectedLabel = formatInteractionSelectionLabel({
1178
- actionId: params.parsed.actionId,
1179
- summary: params.parsed.actionSummary,
1180
- buttonText: params.parsed.typedActionWithText.text?.text
1181
- });
1182
- let updatedBlocks = params.originalBlocks.map((block) => {
1183
- const typedBlock = block;
1184
- if (typedBlock.type === "actions" && typedBlock.block_id === params.parsed.blockId) return {
1185
- type: "context",
1186
- elements: [{
1187
- type: "mrkdwn",
1188
- text: formatInteractionConfirmationText({
1189
- selectedLabel,
1190
- userId: params.parsed.userId
1191
- })
1192
- }]
1193
- };
1194
- return block;
1195
- });
1196
- if (!updatedBlocks.some((block) => {
1197
- const typedBlock = block;
1198
- return typedBlock.type === "actions" && !isBulkActionsBlock(typedBlock);
1199
- })) updatedBlocks = updatedBlocks.filter((block, index) => {
1200
- const typedBlock = block;
1201
- if (isBulkActionsBlock(typedBlock)) return false;
1202
- if (typedBlock.type !== "divider") return true;
1203
- const next = updatedBlocks[index + 1];
1204
- return !next || !isBulkActionsBlock(next);
1205
- });
1206
- return updatedBlocks;
1207
- }
1208
- async function updateSlackLegacyBlockAction(params) {
1209
- const originalBlocks = params.parsed.typedBody.message?.blocks;
1210
- if (!Array.isArray(originalBlocks) || !params.parsed.channelId || !params.parsed.messageTs || !params.parsed.blockId) return;
1211
- try {
1212
- await updateSlackInteractionMessage({
1213
- ctx: params.ctx,
1214
- channelId: params.parsed.channelId,
1215
- messageTs: params.parsed.messageTs,
1216
- text: params.parsed.typedBody.message?.text ?? "",
1217
- blocks: buildSlackConfirmationBlocks({
1218
- parsed: params.parsed,
1219
- originalBlocks
1220
- })
1221
- });
1222
- } catch {
1223
- await respondEphemeral(params.respond, `Button "${params.parsed.actionId}" clicked!`);
1224
- }
1225
- }
1226
- async function handleSlackBlockAction(params) {
1227
- const { ack, body, action, respond } = params.args;
1228
- await ack();
1229
- if (params.ctx.shouldDropMismatchedSlackEvent?.(body)) {
1230
- params.ctx.runtime.log?.("slack:interaction drop block action payload (mismatched app/team)");
1231
- return;
1232
- }
1233
- const parsed = parseSlackBlockAction({
1234
- body,
1235
- action,
1236
- log: params.ctx.runtime.log
1237
- });
1238
- if (!parsed) return;
1239
- const auth = await authorizeSlackBlockAction({
1240
- ctx: params.ctx,
1241
- parsed,
1242
- respond
1243
- });
1244
- if (!auth.allowed) return;
1245
- const pluginInteractionData = buildSlackPluginInteractionData({
1246
- actionId: parsed.actionId,
1247
- summary: parsed.actionSummary
1248
- });
1249
- if (pluginInteractionData) {
1250
- if (await dispatchSlackPluginInteraction({
1251
- ctx: params.ctx,
1252
- parsed,
1253
- pluginInteractionData,
1254
- auth: { isAuthorizedSender: true },
1255
- respond
1256
- })) return;
1257
- }
1258
- enqueueSlackBlockActionEvent({
1259
- ctx: params.ctx,
1260
- parsed,
1261
- auth,
1262
- formatSystemEvent: params.formatSystemEvent
1263
- });
1264
- await updateSlackLegacyBlockAction({
1265
- ctx: params.ctx,
1266
- parsed,
1267
- respond
1268
- });
1269
- }
1270
- function registerSlackBlockActionHandler(params) {
1271
- if (typeof params.ctx.app.action !== "function") return;
1272
- params.ctx.app.action(/.+/, async (args) => {
1273
- await handleSlackBlockAction({
1274
- ctx: params.ctx,
1275
- args,
1276
- formatSystemEvent: params.formatSystemEvent
1277
- });
1278
- });
1279
- }
1280
- //#endregion
1281
- //#region extensions/slack/src/modal-metadata.ts
1282
- function normalizeString(value) {
1283
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
1284
- }
1285
- function parseSlackModalPrivateMetadata(raw) {
1286
- if (typeof raw !== "string" || raw.trim().length === 0) return {};
1287
- try {
1288
- const parsed = JSON.parse(raw);
1289
- return {
1290
- sessionKey: normalizeString(parsed.sessionKey),
1291
- channelId: normalizeString(parsed.channelId),
1292
- channelType: normalizeString(parsed.channelType),
1293
- userId: normalizeString(parsed.userId)
1294
- };
1295
- } catch {
1296
- return {};
1297
- }
1298
- }
1299
- //#endregion
1300
- //#region extensions/slack/src/monitor/events/interactions.modal.ts
1301
- function resolveModalSessionRouting(params) {
1302
- const metadata = params.metadata;
1303
- if (metadata.sessionKey) return {
1304
- sessionKey: metadata.sessionKey,
1305
- channelId: metadata.channelId,
1306
- channelType: metadata.channelType
1307
- };
1308
- if (metadata.channelId) return {
1309
- sessionKey: params.ctx.resolveSlackSystemEventSessionKey({
1310
- channelId: metadata.channelId,
1311
- channelType: metadata.channelType,
1312
- senderId: params.userId
1313
- }),
1314
- channelId: metadata.channelId,
1315
- channelType: metadata.channelType
1316
- };
1317
- return { sessionKey: params.ctx.resolveSlackSystemEventSessionKey({}) };
1318
- }
1319
- function summarizeSlackViewLifecycleContext(view) {
1320
- const rootViewId = view.root_view_id;
1321
- const previousViewId = view.previous_view_id;
1322
- return {
1323
- rootViewId,
1324
- previousViewId,
1325
- externalId: view.external_id,
1326
- viewHash: view.hash,
1327
- isStackedView: Boolean(previousViewId)
1328
- };
1329
- }
1330
- function resolveSlackModalEventBase(params) {
1331
- const metadata = parseSlackModalPrivateMetadata(params.body.view?.private_metadata);
1332
- const callbackId = params.body.view?.callback_id ?? "unknown";
1333
- const userId = params.body.user?.id ?? "unknown";
1334
- const viewId = params.body.view?.id;
1335
- const inputs = params.summarizeViewState(params.body.view?.state?.values);
1336
- const sessionRouting = resolveModalSessionRouting({
1337
- ctx: params.ctx,
1338
- metadata,
1339
- userId
1340
- });
1341
- return {
1342
- callbackId,
1343
- userId,
1344
- expectedUserId: metadata.userId,
1345
- viewId,
1346
- sessionRouting,
1347
- payload: {
1348
- actionId: `view:${callbackId}`,
1349
- callbackId,
1350
- viewId,
1351
- userId,
1352
- teamId: params.body.team?.id,
1353
- ...summarizeSlackViewLifecycleContext({
1354
- root_view_id: params.body.view?.root_view_id,
1355
- previous_view_id: params.body.view?.previous_view_id,
1356
- external_id: params.body.view?.external_id,
1357
- hash: params.body.view?.hash
1358
- }),
1359
- privateMetadata: params.body.view?.private_metadata,
1360
- routedChannelId: sessionRouting.channelId,
1361
- routedChannelType: sessionRouting.channelType,
1362
- inputs
1363
- }
1364
- };
1365
- }
1366
- async function emitSlackModalLifecycleEvent(params) {
1367
- const { callbackId, userId, expectedUserId, viewId, sessionRouting, payload } = resolveSlackModalEventBase({
1368
- ctx: params.ctx,
1369
- body: params.body,
1370
- summarizeViewState: params.summarizeViewState
1371
- });
1372
- const isViewClosed = params.interactionType === "view_closed";
1373
- const isCleared = params.body.is_cleared === true;
1374
- const eventPayload = isViewClosed ? {
1375
- interactionType: params.interactionType,
1376
- ...payload,
1377
- isCleared
1378
- } : {
1379
- interactionType: params.interactionType,
1380
- ...payload
1381
- };
1382
- if (isViewClosed) params.ctx.runtime.log?.(`slack:interaction view_closed callback=${callbackId} user=${userId} cleared=${isCleared}`);
1383
- else params.ctx.runtime.log?.(`slack:interaction view_submission callback=${callbackId} user=${userId} inputs=${payload.inputs.length}`);
1384
- if (!expectedUserId) {
1385
- params.ctx.runtime.log?.(`slack:interaction drop modal callback=${callbackId} user=${userId} reason=missing-expected-user`);
1386
- return;
1387
- }
1388
- const auth = await authorizeSlackSystemEventSender({
1389
- ctx: params.ctx,
1390
- senderId: userId,
1391
- channelId: sessionRouting.channelId,
1392
- channelType: sessionRouting.channelType,
1393
- expectedSenderId: expectedUserId
1394
- });
1395
- if (!auth.allowed) {
1396
- params.ctx.runtime.log?.(`slack:interaction drop modal callback=${callbackId} user=${userId} reason=${auth.reason ?? "unauthorized"}`);
1397
- return;
1398
- }
1399
- enqueueSystemEvent(params.formatSystemEvent(eventPayload), {
1400
- sessionKey: sessionRouting.sessionKey,
1401
- contextKey: [
1402
- params.contextPrefix,
1403
- callbackId,
1404
- viewId,
1405
- userId
1406
- ].filter(Boolean).join(":")
1407
- });
1408
- }
1409
- function registerModalLifecycleHandler(params) {
1410
- params.register(params.matcher, async ({ ack, body }) => {
1411
- await ack();
1412
- if (params.ctx.shouldDropMismatchedSlackEvent?.(body)) {
1413
- params.ctx.runtime.log?.(`slack:interaction drop ${params.interactionType} payload (mismatched app/team)`);
1414
- return;
1415
- }
1416
- await emitSlackModalLifecycleEvent({
1417
- ctx: params.ctx,
1418
- body,
1419
- interactionType: params.interactionType,
1420
- contextPrefix: params.contextPrefix,
1421
- summarizeViewState: params.summarizeViewState,
1422
- formatSystemEvent: params.formatSystemEvent
1423
- });
1424
- });
1425
- }
1426
- //#endregion
1427
- //#region extensions/slack/src/monitor/events/interactions.ts
1428
- const MOLDCLAW_ACTION_PREFIX = "moldclaw:";
1429
- const SLACK_INTERACTION_EVENT_PREFIX = "Slack interaction: ";
1430
- const REDACTED_INTERACTION_VALUE = "[redacted]";
1431
- const SLACK_INTERACTION_EVENT_MAX_CHARS = 2400;
1432
- const SLACK_INTERACTION_STRING_MAX_CHARS = 160;
1433
- const SLACK_INTERACTION_ARRAY_MAX_ITEMS = 64;
1434
- const SLACK_INTERACTION_COMPACT_INPUTS_MAX_ITEMS = 3;
1435
- const SLACK_INTERACTION_REDACTED_KEYS = new Set([
1436
- "triggerId",
1437
- "responseUrl",
1438
- "workflowTriggerUrl",
1439
- "privateMetadata",
1440
- "viewHash"
1441
- ]);
1442
- function sanitizeSlackInteractionPayloadValue(value, key) {
1443
- if (value === void 0) return;
1444
- if (key && SLACK_INTERACTION_REDACTED_KEYS.has(key)) {
1445
- if (typeof value !== "string" || value.trim().length === 0) return;
1446
- return REDACTED_INTERACTION_VALUE;
1447
- }
1448
- if (typeof value === "string") return truncateSlackText(value, SLACK_INTERACTION_STRING_MAX_CHARS);
1449
- if (Array.isArray(value)) {
1450
- const sanitized = value.slice(0, SLACK_INTERACTION_ARRAY_MAX_ITEMS).map((entry) => sanitizeSlackInteractionPayloadValue(entry)).filter((entry) => entry !== void 0);
1451
- if (value.length > SLACK_INTERACTION_ARRAY_MAX_ITEMS) sanitized.push(`…+${value.length - SLACK_INTERACTION_ARRAY_MAX_ITEMS} more`);
1452
- return sanitized;
1453
- }
1454
- if (!value || typeof value !== "object") return value;
1455
- const output = {};
1456
- for (const [entryKey, entryValue] of Object.entries(value)) {
1457
- const sanitized = sanitizeSlackInteractionPayloadValue(entryValue, entryKey);
1458
- if (sanitized === void 0) continue;
1459
- if (typeof sanitized === "string" && sanitized.length === 0) continue;
1460
- if (Array.isArray(sanitized) && sanitized.length === 0) continue;
1461
- output[entryKey] = sanitized;
1462
- }
1463
- return output;
1464
- }
1465
- function buildCompactSlackInteractionPayload(payload) {
1466
- const rawInputs = Array.isArray(payload.inputs) ? payload.inputs : [];
1467
- const compactInputs = rawInputs.slice(0, SLACK_INTERACTION_COMPACT_INPUTS_MAX_ITEMS).flatMap((entry) => {
1468
- if (!entry || typeof entry !== "object") return [];
1469
- const typed = entry;
1470
- return [{
1471
- actionId: typed.actionId,
1472
- blockId: typed.blockId,
1473
- actionType: typed.actionType,
1474
- inputKind: typed.inputKind,
1475
- selectedValues: typed.selectedValues,
1476
- selectedLabels: typed.selectedLabels,
1477
- inputValue: typed.inputValue,
1478
- inputNumber: typed.inputNumber,
1479
- selectedDate: typed.selectedDate,
1480
- selectedTime: typed.selectedTime,
1481
- selectedDateTime: typed.selectedDateTime,
1482
- richTextPreview: typed.richTextPreview
1483
- }];
1484
- });
1485
- return {
1486
- interactionType: payload.interactionType,
1487
- actionId: payload.actionId,
1488
- callbackId: payload.callbackId,
1489
- actionType: payload.actionType,
1490
- userId: payload.userId,
1491
- teamId: payload.teamId,
1492
- channelId: payload.channelId ?? payload.routedChannelId,
1493
- messageTs: payload.messageTs,
1494
- threadTs: payload.threadTs,
1495
- viewId: payload.viewId,
1496
- isCleared: payload.isCleared,
1497
- selectedValues: payload.selectedValues,
1498
- selectedLabels: payload.selectedLabels,
1499
- selectedDate: payload.selectedDate,
1500
- selectedTime: payload.selectedTime,
1501
- selectedDateTime: payload.selectedDateTime,
1502
- workflowId: payload.workflowId,
1503
- routedChannelType: payload.routedChannelType,
1504
- inputs: compactInputs.length > 0 ? compactInputs : void 0,
1505
- inputsOmitted: rawInputs.length > SLACK_INTERACTION_COMPACT_INPUTS_MAX_ITEMS ? rawInputs.length - SLACK_INTERACTION_COMPACT_INPUTS_MAX_ITEMS : void 0,
1506
- payloadTruncated: true
1507
- };
1508
- }
1509
- function formatSlackInteractionSystemEvent(payload) {
1510
- const toEventText = (value) => `${SLACK_INTERACTION_EVENT_PREFIX}${JSON.stringify(value)}`;
1511
- const sanitizedPayload = sanitizeSlackInteractionPayloadValue(payload) ?? {};
1512
- let eventText = toEventText(sanitizedPayload);
1513
- if (eventText.length <= SLACK_INTERACTION_EVENT_MAX_CHARS) return eventText;
1514
- eventText = toEventText(sanitizeSlackInteractionPayloadValue(buildCompactSlackInteractionPayload(sanitizedPayload)));
1515
- if (eventText.length <= SLACK_INTERACTION_EVENT_MAX_CHARS) return eventText;
1516
- return toEventText({
1517
- interactionType: sanitizedPayload.interactionType,
1518
- actionId: sanitizedPayload.actionId ?? "unknown",
1519
- userId: sanitizedPayload.userId,
1520
- channelId: sanitizedPayload.channelId ?? sanitizedPayload.routedChannelId,
1521
- payloadTruncated: true
1522
- });
1523
- }
1524
- function summarizeViewState(values) {
1525
- if (!values || typeof values !== "object") return [];
1526
- const entries = [];
1527
- for (const [blockId, blockValue] of Object.entries(values)) {
1528
- if (!blockValue || typeof blockValue !== "object") continue;
1529
- for (const [actionId, rawAction] of Object.entries(blockValue)) {
1530
- if (!rawAction || typeof rawAction !== "object") continue;
1531
- const actionSummary = summarizeAction(rawAction);
1532
- entries.push({
1533
- blockId,
1534
- actionId,
1535
- ...actionSummary
1536
- });
1537
- }
1538
- }
1539
- return entries;
1540
- }
1541
- function registerSlackInteractionEvents(params) {
1542
- const { ctx } = params;
1543
- registerSlackBlockActionHandler({
1544
- ctx,
1545
- formatSystemEvent: formatSlackInteractionSystemEvent
1546
- });
1547
- if (typeof ctx.app.view !== "function") return;
1548
- const modalMatcher = new RegExp(`^${MOLDCLAW_ACTION_PREFIX}`);
1549
- registerModalLifecycleHandler({
1550
- register: (matcher, handler) => ctx.app.view(matcher, handler),
1551
- matcher: modalMatcher,
1552
- ctx,
1553
- interactionType: "view_submission",
1554
- contextPrefix: "slack:interaction:view",
1555
- summarizeViewState,
1556
- formatSystemEvent: formatSlackInteractionSystemEvent
1557
- });
1558
- const viewClosed = ctx.app.viewClosed;
1559
- if (typeof viewClosed !== "function") return;
1560
- registerModalLifecycleHandler({
1561
- register: viewClosed,
1562
- matcher: modalMatcher,
1563
- ctx,
1564
- interactionType: "view_closed",
1565
- contextPrefix: "slack:interaction:view-closed",
1566
- summarizeViewState,
1567
- formatSystemEvent: formatSlackInteractionSystemEvent
1568
- });
1569
- }
1570
- //#endregion
1571
- //#region extensions/slack/src/monitor/events/system-event-context.ts
1572
- async function authorizeAndResolveSlackSystemEventContext(params) {
1573
- const { ctx, senderId, channelId, channelType, eventKind } = params;
1574
- const auth = await authorizeSlackSystemEventSender({
1575
- ctx,
1576
- senderId,
1577
- channelId,
1578
- channelType
1579
- });
1580
- if (!auth.allowed) {
1581
- logVerbose(`slack: drop ${eventKind} sender ${senderId ?? "unknown"} channel=${channelId ?? "unknown"} reason=${auth.reason ?? "unauthorized"}`);
1582
- return;
1583
- }
1584
- return {
1585
- channelLabel: resolveSlackChannelLabel({
1586
- channelId,
1587
- channelName: auth.channelName
1588
- }),
1589
- sessionKey: ctx.resolveSlackSystemEventSessionKey({
1590
- channelId,
1591
- channelType: auth.channelType,
1592
- senderId
1593
- })
1594
- };
1595
- }
1596
- //#endregion
1597
- //#region extensions/slack/src/monitor/events/members.ts
1598
- function registerSlackMemberEvents(params) {
1599
- const { ctx, trackEvent } = params;
1600
- const handleMemberChannelEvent = async (params) => {
1601
- try {
1602
- if (ctx.shouldDropMismatchedSlackEvent(params.body)) return;
1603
- trackEvent?.();
1604
- const payload = params.event;
1605
- const channelId = payload.channel;
1606
- const channelInfo = channelId ? await ctx.resolveChannelName(channelId) : {};
1607
- const channelType = payload.channel_type ?? channelInfo?.type;
1608
- const ingressContext = await authorizeAndResolveSlackSystemEventContext({
1609
- ctx,
1610
- senderId: payload.user,
1611
- channelId,
1612
- channelType,
1613
- eventKind: `member-${params.verb}`
1614
- });
1615
- if (!ingressContext) return;
1616
- enqueueSystemEvent(`Slack: ${(payload.user ? await ctx.resolveUserName(payload.user) : {})?.name ?? payload.user ?? "someone"} ${params.verb} ${ingressContext.channelLabel}.`, {
1617
- sessionKey: ingressContext.sessionKey,
1618
- contextKey: `slack:member:${params.verb}:${channelId ?? "unknown"}:${payload.user ?? "unknown"}`
1619
- });
1620
- } catch (err) {
1621
- ctx.runtime.error?.(danger(`slack ${params.verb} handler failed: ${String(err)}`));
1622
- }
1623
- };
1624
- ctx.app.event("member_joined_channel", async ({ event, body }) => {
1625
- await handleMemberChannelEvent({
1626
- verb: "joined",
1627
- event,
1628
- body
1629
- });
1630
- });
1631
- ctx.app.event("member_left_channel", async ({ event, body }) => {
1632
- await handleMemberChannelEvent({
1633
- verb: "left",
1634
- event,
1635
- body
1636
- });
1637
- });
1638
- }
1639
- //#endregion
1640
- //#region extensions/slack/src/monitor/events/message-subtype-handlers.ts
1641
- const SUBTYPE_HANDLER_REGISTRY = {
1642
- message_changed: {
1643
- subtype: "message_changed",
1644
- eventKind: "message_changed",
1645
- describe: (channelLabel) => `Slack message edited in ${channelLabel}.`,
1646
- contextKey: (event) => {
1647
- const changed = event;
1648
- return `slack:message:changed:${changed.channel ?? "unknown"}:${changed.message?.ts ?? changed.previous_message?.ts ?? changed.event_ts ?? "unknown"}`;
1649
- },
1650
- resolveSenderId: (event) => {
1651
- const changed = event;
1652
- return changed.message?.user ?? changed.previous_message?.user ?? changed.message?.bot_id ?? changed.previous_message?.bot_id;
1653
- },
1654
- resolveChannelId: (event) => event.channel,
1655
- resolveChannelType: () => void 0
1656
- },
1657
- message_deleted: {
1658
- subtype: "message_deleted",
1659
- eventKind: "message_deleted",
1660
- describe: (channelLabel) => `Slack message deleted in ${channelLabel}.`,
1661
- contextKey: (event) => {
1662
- const deleted = event;
1663
- return `slack:message:deleted:${deleted.channel ?? "unknown"}:${deleted.deleted_ts ?? deleted.event_ts ?? "unknown"}`;
1664
- },
1665
- resolveSenderId: (event) => {
1666
- const deleted = event;
1667
- return deleted.previous_message?.user ?? deleted.previous_message?.bot_id;
1668
- },
1669
- resolveChannelId: (event) => event.channel,
1670
- resolveChannelType: () => void 0
1671
- },
1672
- thread_broadcast: {
1673
- subtype: "thread_broadcast",
1674
- eventKind: "thread_broadcast",
1675
- describe: (channelLabel) => `Slack thread reply broadcast in ${channelLabel}.`,
1676
- contextKey: (event) => {
1677
- const thread = event;
1678
- return `slack:thread:broadcast:${thread.channel ?? "unknown"}:${thread.message?.ts ?? thread.event_ts ?? "unknown"}`;
1679
- },
1680
- resolveSenderId: (event) => {
1681
- const thread = event;
1682
- return thread.user ?? thread.message?.user ?? thread.message?.bot_id;
1683
- },
1684
- resolveChannelId: (event) => event.channel,
1685
- resolveChannelType: () => void 0
1686
- }
1687
- };
1688
- function resolveSlackMessageSubtypeHandler(event) {
1689
- const subtype = event.subtype;
1690
- if (subtype !== "message_changed" && subtype !== "message_deleted" && subtype !== "thread_broadcast") return;
1691
- return SUBTYPE_HANDLER_REGISTRY[subtype];
1692
- }
1693
- //#endregion
1694
- //#region extensions/slack/src/monitor/events/messages.ts
1695
- function registerSlackMessageEvents(params) {
1696
- const { ctx, handleSlackMessage } = params;
1697
- const handleIncomingMessageEvent = async ({ event, body }) => {
1698
- try {
1699
- if (ctx.shouldDropMismatchedSlackEvent(body)) return;
1700
- const message = event;
1701
- const subtypeHandler = resolveSlackMessageSubtypeHandler(message);
1702
- if (subtypeHandler) {
1703
- const channelId = subtypeHandler.resolveChannelId(message);
1704
- const ingressContext = await authorizeAndResolveSlackSystemEventContext({
1705
- ctx,
1706
- senderId: subtypeHandler.resolveSenderId(message),
1707
- channelId,
1708
- channelType: subtypeHandler.resolveChannelType(message),
1709
- eventKind: subtypeHandler.eventKind
1710
- });
1711
- if (!ingressContext) return;
1712
- enqueueSystemEvent(subtypeHandler.describe(ingressContext.channelLabel), {
1713
- sessionKey: ingressContext.sessionKey,
1714
- contextKey: subtypeHandler.contextKey(message)
1715
- });
1716
- return;
1717
- }
1718
- await handleSlackMessage(message, { source: "message" });
1719
- } catch (err) {
1720
- ctx.runtime.error?.(danger(`slack handler failed: ${String(err)}`));
1721
- }
1722
- };
1723
- ctx.app.event("message", async ({ event, body }) => {
1724
- await handleIncomingMessageEvent({
1725
- event,
1726
- body
1727
- });
1728
- });
1729
- ctx.app.event("app_mention", async ({ event, body }) => {
1730
- try {
1731
- if (ctx.shouldDropMismatchedSlackEvent(body)) return;
1732
- const mention = event;
1733
- const channelType = normalizeSlackChannelType(mention.channel_type, mention.channel);
1734
- if (channelType === "im" || channelType === "mpim") return;
1735
- await handleSlackMessage(mention, {
1736
- source: "app_mention",
1737
- wasMentioned: true
1738
- });
1739
- } catch (err) {
1740
- ctx.runtime.error?.(danger(`slack mention handler failed: ${String(err)}`));
1741
- }
1742
- });
1743
- }
1744
- //#endregion
1745
- //#region extensions/slack/src/monitor/events/pins.ts
1746
- async function handleSlackPinEvent(params) {
1747
- const { ctx, trackEvent, body, event, action, contextKeySuffix, errorLabel } = params;
1748
- try {
1749
- if (ctx.shouldDropMismatchedSlackEvent(body)) return;
1750
- trackEvent?.();
1751
- const payload = event;
1752
- const channelId = payload.channel_id;
1753
- const ingressContext = await authorizeAndResolveSlackSystemEventContext({
1754
- ctx,
1755
- senderId: payload.user,
1756
- channelId,
1757
- eventKind: "pin"
1758
- });
1759
- if (!ingressContext) return;
1760
- const userLabel = (payload.user ? await ctx.resolveUserName(payload.user) : {})?.name ?? payload.user ?? "someone";
1761
- const itemType = payload.item?.type ?? "item";
1762
- const messageId = payload.item?.message?.ts ?? payload.event_ts;
1763
- enqueueSystemEvent(`Slack: ${userLabel} ${action} a ${itemType} in ${ingressContext.channelLabel}.`, {
1764
- sessionKey: ingressContext.sessionKey,
1765
- contextKey: `slack:pin:${contextKeySuffix}:${channelId ?? "unknown"}:${messageId ?? "unknown"}`
1766
- });
1767
- } catch (err) {
1768
- ctx.runtime.error?.(danger(`slack ${errorLabel} handler failed: ${String(err)}`));
1769
- }
1770
- }
1771
- function registerSlackPinEvents(params) {
1772
- const { ctx, trackEvent } = params;
1773
- ctx.app.event("pin_added", async ({ event, body }) => {
1774
- await handleSlackPinEvent({
1775
- ctx,
1776
- trackEvent,
1777
- body,
1778
- event,
1779
- action: "pinned",
1780
- contextKeySuffix: "added",
1781
- errorLabel: "pin added"
1782
- });
1783
- });
1784
- ctx.app.event("pin_removed", async ({ event, body }) => {
1785
- await handleSlackPinEvent({
1786
- ctx,
1787
- trackEvent,
1788
- body,
1789
- event,
1790
- action: "unpinned",
1791
- contextKeySuffix: "removed",
1792
- errorLabel: "pin removed"
1793
- });
1794
- });
1795
- }
1796
- //#endregion
1797
- //#region extensions/slack/src/monitor/events/reactions.ts
1798
- function registerSlackReactionEvents(params) {
1799
- const { ctx, trackEvent } = params;
1800
- const handleReactionEvent = async (event, action) => {
1801
- try {
1802
- const item = event.item;
1803
- if (!item || item.type !== "message") return;
1804
- trackEvent?.();
1805
- const ingressContext = await authorizeAndResolveSlackSystemEventContext({
1806
- ctx,
1807
- senderId: event.user,
1808
- channelId: item.channel,
1809
- eventKind: "reaction"
1810
- });
1811
- if (!ingressContext) return;
1812
- const actorInfoPromise = event.user ? ctx.resolveUserName(event.user) : Promise.resolve(void 0);
1813
- const authorInfoPromise = event.item_user ? ctx.resolveUserName(event.item_user) : Promise.resolve(void 0);
1814
- const [actorInfo, authorInfo] = await Promise.all([actorInfoPromise, authorInfoPromise]);
1815
- const actorLabel = actorInfo?.name ?? event.user;
1816
- const emojiLabel = event.reaction ?? "emoji";
1817
- const authorLabel = authorInfo?.name ?? event.item_user;
1818
- const baseText = `Slack reaction ${action}: :${emojiLabel}: by ${actorLabel} in ${ingressContext.channelLabel} msg ${item.ts}`;
1819
- enqueueSystemEvent(authorLabel ? `${baseText} from ${authorLabel}` : baseText, {
1820
- sessionKey: ingressContext.sessionKey,
1821
- contextKey: `slack:reaction:${action}:${item.channel}:${item.ts}:${event.user}:${emojiLabel}`
1822
- });
1823
- } catch (err) {
1824
- ctx.runtime.error?.(danger(`slack reaction handler failed: ${String(err)}`));
1825
- }
1826
- };
1827
- ctx.app.event("reaction_added", async ({ event, body }) => {
1828
- if (ctx.shouldDropMismatchedSlackEvent(body)) return;
1829
- await handleReactionEvent(event, "added");
1830
- });
1831
- ctx.app.event("reaction_removed", async ({ event, body }) => {
1832
- if (ctx.shouldDropMismatchedSlackEvent(body)) return;
1833
- await handleReactionEvent(event, "removed");
1834
- });
1835
- }
1836
- //#endregion
1837
- //#region extensions/slack/src/monitor/events.ts
1838
- function registerSlackMonitorEvents(params) {
1839
- registerSlackMessageEvents({
1840
- ctx: params.ctx,
1841
- handleSlackMessage: params.handleSlackMessage
1842
- });
1843
- registerSlackReactionEvents({
1844
- ctx: params.ctx,
1845
- trackEvent: params.trackEvent
1846
- });
1847
- registerSlackMemberEvents({
1848
- ctx: params.ctx,
1849
- trackEvent: params.trackEvent
1850
- });
1851
- registerSlackChannelEvents({
1852
- ctx: params.ctx,
1853
- trackEvent: params.trackEvent
1854
- });
1855
- registerSlackPinEvents({
1856
- ctx: params.ctx,
1857
- trackEvent: params.trackEvent
1858
- });
1859
- registerSlackInteractionEvents({ ctx: params.ctx });
1860
- }
1861
- //#endregion
1862
- //#region extensions/slack/src/draft-stream.ts
1863
- const SLACK_STREAM_MAX_CHARS = 4e3;
1864
- const DEFAULT_THROTTLE_MS = 1e3;
1865
- function createSlackDraftStream(params) {
1866
- const maxChars = Math.min(params.maxChars ?? SLACK_STREAM_MAX_CHARS, SLACK_STREAM_MAX_CHARS);
1867
- const throttleMs = Math.max(250, params.throttleMs ?? DEFAULT_THROTTLE_MS);
1868
- const send = params.send ?? sendMessageSlack;
1869
- const edit = params.edit ?? editSlackMessage;
1870
- const remove = params.remove ?? deleteSlackMessage;
1871
- let streamMessageId;
1872
- let streamChannelId;
1873
- let lastSentText = "";
1874
- let stopped = false;
1875
- const sendOrEditStreamMessage = async (text) => {
1876
- if (stopped) return;
1877
- const trimmed = text.trimEnd();
1878
- if (!trimmed) return;
1879
- if (trimmed.length > maxChars) {
1880
- stopped = true;
1881
- params.warn?.(`slack stream preview stopped (text length ${trimmed.length} > ${maxChars})`);
1882
- return;
1883
- }
1884
- if (trimmed === lastSentText) return;
1885
- lastSentText = trimmed;
1886
- try {
1887
- if (streamChannelId && streamMessageId) {
1888
- await edit(streamChannelId, streamMessageId, trimmed, {
1889
- token: params.token,
1890
- accountId: params.accountId
1891
- });
1892
- return;
1893
- }
1894
- const sent = await send(params.target, trimmed, {
1895
- token: params.token,
1896
- accountId: params.accountId,
1897
- threadTs: params.resolveThreadTs?.()
1898
- });
1899
- streamChannelId = sent.channelId || streamChannelId;
1900
- streamMessageId = sent.messageId || streamMessageId;
1901
- if (!streamChannelId || !streamMessageId) {
1902
- stopped = true;
1903
- params.warn?.("slack stream preview stopped (missing identifiers from sendMessage)");
1904
- return;
1905
- }
1906
- params.onMessageSent?.();
1907
- } catch (err) {
1908
- stopped = true;
1909
- params.warn?.(`slack stream preview failed: ${err instanceof Error ? err.message : String(err)}`);
1910
- }
1911
- };
1912
- const loop = createDraftStreamLoop({
1913
- throttleMs,
1914
- isStopped: () => stopped,
1915
- sendOrEditStreamMessage
1916
- });
1917
- const stop = () => {
1918
- stopped = true;
1919
- loop.stop();
1920
- };
1921
- const clear = async () => {
1922
- stop();
1923
- await loop.waitForInFlight();
1924
- const channelId = streamChannelId;
1925
- const messageId = streamMessageId;
1926
- streamChannelId = void 0;
1927
- streamMessageId = void 0;
1928
- lastSentText = "";
1929
- if (!channelId || !messageId) return;
1930
- try {
1931
- await remove(channelId, messageId, {
1932
- token: params.token,
1933
- accountId: params.accountId
1934
- });
1935
- } catch (err) {
1936
- params.warn?.(`slack stream preview cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
1937
- }
1938
- };
1939
- const forceNewMessage = () => {
1940
- streamMessageId = void 0;
1941
- streamChannelId = void 0;
1942
- lastSentText = "";
1943
- loop.resetPending();
1944
- };
1945
- params.log?.(`slack stream preview ready (maxChars=${maxChars}, throttleMs=${throttleMs})`);
1946
- return {
1947
- update: loop.update,
1948
- flush: loop.flush,
1949
- clear,
1950
- stop,
1951
- forceNewMessage,
1952
- messageId: () => streamMessageId,
1953
- channelId: () => streamChannelId
1954
- };
1955
- }
1956
- //#endregion
1957
- //#region extensions/slack/src/stream-mode.ts
1958
- function resolveSlackStreamingConfig(params) {
1959
- const mode = resolveSlackStreamingMode(params);
1960
- return {
1961
- mode,
1962
- nativeStreaming: resolveSlackNativeStreaming(params),
1963
- draftMode: mapStreamingModeToSlackLegacyDraftStreamMode(mode)
1964
- };
1965
- }
1966
- function applyAppendOnlyStreamUpdate(params) {
1967
- const incoming = params.incoming.trimEnd();
1968
- if (!incoming) return {
1969
- rendered: params.rendered,
1970
- source: params.source,
1971
- changed: false
1972
- };
1973
- if (!params.rendered) return {
1974
- rendered: incoming,
1975
- source: incoming,
1976
- changed: true
1977
- };
1978
- if (incoming === params.source) return {
1979
- rendered: params.rendered,
1980
- source: params.source,
1981
- changed: false
1982
- };
1983
- if (incoming.startsWith(params.source) || incoming.startsWith(params.rendered)) return {
1984
- rendered: incoming,
1985
- source: incoming,
1986
- changed: incoming !== params.rendered
1987
- };
1988
- if (params.source.startsWith(incoming)) return {
1989
- rendered: params.rendered,
1990
- source: params.source,
1991
- changed: false
1992
- };
1993
- const separator = params.rendered.endsWith("\n") ? "" : "\n";
1994
- return {
1995
- rendered: `${params.rendered}${separator}${incoming}`,
1996
- source: incoming,
1997
- changed: true
1998
- };
1999
- }
2000
- function buildStatusFinalPreviewText(updateCount) {
2001
- return `Status: thinking${".".repeat(Math.max(1, updateCount) % 3 + 1)}`;
2002
- }
2003
- //#endregion
2004
- //#region extensions/slack/src/streaming.ts
2005
- /**
2006
- * Start a new Slack text stream.
2007
- *
2008
- * Returns a {@link SlackStreamSession} that should be passed to
2009
- * {@link appendSlackStream} and {@link stopSlackStream}.
2010
- *
2011
- * The first chunk of text can optionally be included via `text`.
2012
- */
2013
- async function startSlackStream(params) {
2014
- const { client, channel, threadTs, text, teamId, userId } = params;
2015
- logVerbose(`slack-stream: starting stream in ${channel} thread=${threadTs}${teamId ? ` team=${teamId}` : ""}${userId ? ` user=${userId}` : ""}`);
2016
- const streamer = client.chatStream({
2017
- channel,
2018
- thread_ts: threadTs,
2019
- ...teamId ? { recipient_team_id: teamId } : {},
2020
- ...userId ? { recipient_user_id: userId } : {}
2021
- });
2022
- const session = {
2023
- streamer,
2024
- channel,
2025
- threadTs,
2026
- stopped: false
2027
- };
2028
- if (text) {
2029
- await streamer.append({ markdown_text: text });
2030
- logVerbose(`slack-stream: appended initial text (${text.length} chars)`);
2031
- }
2032
- return session;
2033
- }
2034
- /**
2035
- * Append markdown text to an active Slack stream.
2036
- */
2037
- async function appendSlackStream(params) {
2038
- const { session, text } = params;
2039
- if (session.stopped) {
2040
- logVerbose("slack-stream: attempted to append to a stopped stream, ignoring");
2041
- return;
2042
- }
2043
- if (!text) return;
2044
- await session.streamer.append({ markdown_text: text });
2045
- logVerbose(`slack-stream: appended ${text.length} chars`);
2046
- }
2047
- /**
2048
- * Stop (finalize) a Slack stream.
2049
- *
2050
- * After calling this the stream message becomes a normal Slack message.
2051
- * Optionally include final text to append before stopping.
2052
- */
2053
- async function stopSlackStream(params) {
2054
- const { session, text } = params;
2055
- if (session.stopped) {
2056
- logVerbose("slack-stream: stream already stopped, ignoring duplicate stop");
2057
- return;
2058
- }
2059
- session.stopped = true;
2060
- logVerbose(`slack-stream: stopping stream in ${session.channel} thread=${session.threadTs}${text ? ` (final text: ${text.length} chars)` : ""}`);
2061
- await session.streamer.stop(text ? { markdown_text: text } : void 0);
2062
- logVerbose("slack-stream: stream stopped");
2063
- }
2064
- //#endregion
2065
- //#region extensions/slack/src/threading.ts
2066
- function resolveSlackThreadContext(params) {
2067
- const incomingThreadTs = params.message.thread_ts;
2068
- const eventTs = params.message.event_ts;
2069
- const messageTs = params.message.ts ?? eventTs;
2070
- const isThreadReply = typeof incomingThreadTs === "string" && incomingThreadTs.length > 0 && (incomingThreadTs !== messageTs || Boolean(params.message.parent_user_id));
2071
- return {
2072
- incomingThreadTs,
2073
- messageTs,
2074
- isThreadReply,
2075
- replyToId: incomingThreadTs ?? messageTs,
2076
- messageThreadId: isThreadReply ? incomingThreadTs : params.replyToMode === "all" ? messageTs : void 0
2077
- };
2078
- }
2079
- /**
2080
- * Resolves Slack thread targeting for replies and status indicators.
2081
- *
2082
- * @returns replyThreadTs - Thread timestamp for reply messages
2083
- * @returns statusThreadTs - Thread timestamp for status indicators (typing, etc.)
2084
- * @returns isThreadReply - true if this is a genuine user reply in a thread,
2085
- * false if thread_ts comes from a bot status message (e.g. typing indicator)
2086
- */
2087
- function resolveSlackThreadTargets(params) {
2088
- const { incomingThreadTs, messageTs, isThreadReply } = resolveSlackThreadContext(params);
2089
- const replyThreadTs = isThreadReply ? incomingThreadTs : params.replyToMode === "all" ? messageTs : void 0;
2090
- return {
2091
- replyThreadTs,
2092
- statusThreadTs: replyThreadTs,
2093
- isThreadReply
2094
- };
2095
- }
2096
- //#endregion
2097
- //#region extensions/slack/src/monitor/message-handler/dispatch.ts
2098
- function hasMedia(payload) {
2099
- return Boolean(payload.mediaUrl) || (payload.mediaUrls?.length ?? 0) > 0;
2100
- }
2101
- function isSlackStreamingEnabled(params) {
2102
- if (params.mode !== "partial") return false;
2103
- return params.nativeStreaming;
2104
- }
2105
- function resolveSlackStreamingThreadHint(params) {
2106
- return resolveSlackThreadTs({
2107
- replyToMode: params.replyToMode,
2108
- incomingThreadTs: params.incomingThreadTs,
2109
- messageTs: params.messageTs,
2110
- hasReplied: false,
2111
- isThreadReply: params.isThreadReply
2112
- });
2113
- }
2114
- function shouldUseStreaming(params) {
2115
- if (!params.streamingEnabled) return false;
2116
- if (!params.threadTs) {
2117
- logVerbose("slack-stream: streaming disabled — no reply thread target available");
2118
- return false;
2119
- }
2120
- return true;
2121
- }
2122
- async function dispatchPreparedSlackMessage(prepared) {
2123
- const { ctx, account, message, route } = prepared;
2124
- const cfg = ctx.cfg;
2125
- const runtime = ctx.runtime;
2126
- const outboundIdentity = resolveAgentOutboundIdentity(cfg, route.agentId);
2127
- const slackIdentity = outboundIdentity ? {
2128
- username: outboundIdentity.name,
2129
- iconUrl: outboundIdentity.avatarUrl,
2130
- iconEmoji: outboundIdentity.emoji
2131
- } : void 0;
2132
- if (prepared.isDirectMessage) {
2133
- const sessionCfg = cfg.session;
2134
- const storePath = resolveStorePath(sessionCfg?.store, { agentId: route.agentId });
2135
- const pinnedMainDmOwner = resolvePinnedMainDmOwnerFromAllowlist({
2136
- dmScope: cfg.session?.dmScope,
2137
- allowFrom: ctx.allowFrom,
2138
- normalizeEntry: normalizeSlackAllowOwnerEntry
2139
- });
2140
- const senderRecipient = message.user?.trim().toLowerCase();
2141
- if (pinnedMainDmOwner && senderRecipient && pinnedMainDmOwner.trim().toLowerCase() !== senderRecipient) logVerbose(`slack: skip main-session last route for ${senderRecipient} (pinned owner ${pinnedMainDmOwner})`);
2142
- else await updateLastRoute({
2143
- storePath,
2144
- sessionKey: route.mainSessionKey,
2145
- deliveryContext: {
2146
- channel: "slack",
2147
- to: `user:${message.user}`,
2148
- accountId: route.accountId,
2149
- threadId: prepared.ctxPayload.MessageThreadId
2150
- },
2151
- ctx: prepared.ctxPayload
2152
- });
2153
- }
2154
- const { statusThreadTs, isThreadReply } = resolveSlackThreadTargets({
2155
- message,
2156
- replyToMode: prepared.replyToMode
2157
- });
2158
- const messageTs = message.ts ?? message.event_ts;
2159
- const incomingThreadTs = message.thread_ts;
2160
- let didSetStatus = false;
2161
- const hasRepliedRef = { value: false };
2162
- const replyPlan = createSlackReplyDeliveryPlan({
2163
- replyToMode: prepared.replyToMode,
2164
- incomingThreadTs,
2165
- messageTs,
2166
- hasRepliedRef,
2167
- isThreadReply
2168
- });
2169
- const typingTarget = statusThreadTs ? `${message.channel}/${statusThreadTs}` : message.channel;
2170
- const typingReaction = ctx.typingReaction;
2171
- const typingCallbacks = createTypingCallbacks({
2172
- start: async () => {
2173
- didSetStatus = true;
2174
- await ctx.setSlackThreadStatus({
2175
- channelId: message.channel,
2176
- threadTs: statusThreadTs,
2177
- status: "is typing..."
2178
- });
2179
- if (typingReaction && message.ts) await reactSlackMessage(message.channel, message.ts, typingReaction, {
2180
- token: ctx.botToken,
2181
- client: ctx.app.client
2182
- }).catch(() => {});
2183
- },
2184
- stop: async () => {
2185
- if (!didSetStatus) return;
2186
- didSetStatus = false;
2187
- await ctx.setSlackThreadStatus({
2188
- channelId: message.channel,
2189
- threadTs: statusThreadTs,
2190
- status: ""
2191
- });
2192
- if (typingReaction && message.ts) await removeSlackReaction(message.channel, message.ts, typingReaction, {
2193
- token: ctx.botToken,
2194
- client: ctx.app.client
2195
- }).catch(() => {});
2196
- },
2197
- onStartError: (err) => {
2198
- logTypingFailure({
2199
- log: (message) => runtime.error?.(danger(message)),
2200
- channel: "slack",
2201
- action: "start",
2202
- target: typingTarget,
2203
- error: err
2204
- });
2205
- },
2206
- onStopError: (err) => {
2207
- logTypingFailure({
2208
- log: (message) => runtime.error?.(danger(message)),
2209
- channel: "slack",
2210
- action: "stop",
2211
- target: typingTarget,
2212
- error: err
2213
- });
2214
- }
2215
- });
2216
- const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
2217
- cfg,
2218
- agentId: route.agentId,
2219
- channel: "slack",
2220
- accountId: route.accountId
2221
- });
2222
- const slackStreaming = resolveSlackStreamingConfig({
2223
- streaming: account.config.streaming,
2224
- streamMode: account.config.streamMode,
2225
- nativeStreaming: account.config.nativeStreaming
2226
- });
2227
- const previewStreamingEnabled = slackStreaming.mode !== "off";
2228
- const useStreaming = shouldUseStreaming({
2229
- streamingEnabled: isSlackStreamingEnabled({
2230
- mode: slackStreaming.mode,
2231
- nativeStreaming: slackStreaming.nativeStreaming
2232
- }),
2233
- threadTs: resolveSlackStreamingThreadHint({
2234
- replyToMode: prepared.replyToMode,
2235
- incomingThreadTs,
2236
- messageTs,
2237
- isThreadReply
2238
- })
2239
- });
2240
- let streamSession = null;
2241
- let streamFailed = false;
2242
- let usedReplyThreadTs;
2243
- const deliverNormally = async (payload, forcedThreadTs) => {
2244
- const replyThreadTs = forcedThreadTs ?? replyPlan.nextThreadTs();
2245
- await deliverReplies({
2246
- replies: [payload],
2247
- target: prepared.replyTarget,
2248
- token: ctx.botToken,
2249
- accountId: account.accountId,
2250
- runtime,
2251
- textLimit: ctx.textLimit,
2252
- replyThreadTs,
2253
- replyToMode: prepared.replyToMode,
2254
- ...slackIdentity ? { identity: slackIdentity } : {}
2255
- });
2256
- if (replyThreadTs) usedReplyThreadTs ??= replyThreadTs;
2257
- replyPlan.markSent();
2258
- };
2259
- const deliverWithStreaming = async (payload) => {
2260
- if (streamFailed || hasMedia(payload) || readSlackReplyBlocks(payload)?.length || !payload.text?.trim()) {
2261
- await deliverNormally(payload, streamSession?.threadTs);
2262
- return;
2263
- }
2264
- const text = payload.text.trim();
2265
- let plannedThreadTs;
2266
- try {
2267
- if (!streamSession) {
2268
- const streamThreadTs = replyPlan.nextThreadTs();
2269
- plannedThreadTs = streamThreadTs;
2270
- if (!streamThreadTs) {
2271
- logVerbose("slack-stream: no reply thread target for stream start, falling back to normal delivery");
2272
- streamFailed = true;
2273
- await deliverNormally(payload);
2274
- return;
2275
- }
2276
- streamSession = await startSlackStream({
2277
- client: ctx.app.client,
2278
- channel: message.channel,
2279
- threadTs: streamThreadTs,
2280
- text,
2281
- teamId: ctx.teamId,
2282
- userId: message.user
2283
- });
2284
- usedReplyThreadTs ??= streamThreadTs;
2285
- replyPlan.markSent();
2286
- return;
2287
- }
2288
- await appendSlackStream({
2289
- session: streamSession,
2290
- text: "\n" + text
2291
- });
2292
- } catch (err) {
2293
- runtime.error?.(danger(`slack-stream: streaming API call failed: ${String(err)}, falling back`));
2294
- streamFailed = true;
2295
- await deliverNormally(payload, streamSession?.threadTs ?? plannedThreadTs);
2296
- }
2297
- };
2298
- const { dispatcher, replyOptions, markDispatchIdle } = createReplyDispatcherWithTyping({
2299
- ...prefixOptions,
2300
- humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
2301
- typingCallbacks,
2302
- deliver: async (payload) => {
2303
- if (useStreaming) {
2304
- await deliverWithStreaming(payload);
2305
- return;
2306
- }
2307
- const mediaCount = payload.mediaUrls?.length ?? (payload.mediaUrl ? 1 : 0);
2308
- const slackBlocks = readSlackReplyBlocks(payload);
2309
- const draftMessageId = draftStream?.messageId();
2310
- const draftChannelId = draftStream?.channelId();
2311
- const trimmedFinalText = (payload.text ?? "").trim();
2312
- if (previewStreamingEnabled && streamMode !== "status_final" && mediaCount === 0 && !payload.isError && (trimmedFinalText.length > 0 || Boolean(slackBlocks?.length)) && typeof draftMessageId === "string" && typeof draftChannelId === "string") {
2313
- draftStream?.stop();
2314
- try {
2315
- await editSlackMessage(draftChannelId, draftMessageId, normalizeSlackOutboundText(trimmedFinalText), {
2316
- token: ctx.botToken,
2317
- accountId: account.accountId,
2318
- client: ctx.app.client,
2319
- ...slackBlocks?.length ? { blocks: slackBlocks } : {}
2320
- });
2321
- return;
2322
- } catch (err) {
2323
- logVerbose(`slack: preview final edit failed; falling back to standard send (${String(err)})`);
2324
- }
2325
- } else if (previewStreamingEnabled && streamMode === "status_final" && hasStreamedMessage) try {
2326
- const statusChannelId = draftStream?.channelId();
2327
- const statusMessageId = draftStream?.messageId();
2328
- if (statusChannelId && statusMessageId) await ctx.app.client.chat.update({
2329
- token: ctx.botToken,
2330
- channel: statusChannelId,
2331
- ts: statusMessageId,
2332
- text: "Status: complete. Final answer posted below."
2333
- });
2334
- } catch (err) {
2335
- logVerbose(`slack: status_final completion update failed (${String(err)})`);
2336
- }
2337
- else if (mediaCount > 0) {
2338
- await draftStream?.clear();
2339
- hasStreamedMessage = false;
2340
- }
2341
- await deliverNormally(payload);
2342
- },
2343
- onError: (err, info) => {
2344
- runtime.error?.(danger(`slack ${info.kind} reply failed: ${String(err)}`));
2345
- typingCallbacks.onIdle?.();
2346
- }
2347
- });
2348
- const draftStream = createSlackDraftStream({
2349
- target: prepared.replyTarget,
2350
- token: ctx.botToken,
2351
- accountId: account.accountId,
2352
- maxChars: Math.min(ctx.textLimit, 4e3),
2353
- resolveThreadTs: () => {
2354
- const ts = replyPlan.nextThreadTs();
2355
- if (ts) usedReplyThreadTs ??= ts;
2356
- return ts;
2357
- },
2358
- onMessageSent: () => replyPlan.markSent(),
2359
- log: logVerbose,
2360
- warn: logVerbose
2361
- });
2362
- let hasStreamedMessage = false;
2363
- const streamMode = slackStreaming.draftMode;
2364
- let appendRenderedText = "";
2365
- let appendSourceText = "";
2366
- let statusUpdateCount = 0;
2367
- const updateDraftFromPartial = (text) => {
2368
- const trimmed = text?.trimEnd();
2369
- if (!trimmed) return;
2370
- if (streamMode === "append") {
2371
- const next = applyAppendOnlyStreamUpdate({
2372
- incoming: trimmed,
2373
- rendered: appendRenderedText,
2374
- source: appendSourceText
2375
- });
2376
- appendRenderedText = next.rendered;
2377
- appendSourceText = next.source;
2378
- if (!next.changed) return;
2379
- draftStream.update(next.rendered);
2380
- hasStreamedMessage = true;
2381
- return;
2382
- }
2383
- if (streamMode === "status_final") {
2384
- statusUpdateCount += 1;
2385
- if (statusUpdateCount > 1 && statusUpdateCount % 4 !== 0) return;
2386
- draftStream.update(buildStatusFinalPreviewText(statusUpdateCount));
2387
- hasStreamedMessage = true;
2388
- return;
2389
- }
2390
- draftStream.update(trimmed);
2391
- hasStreamedMessage = true;
2392
- };
2393
- const onDraftBoundary = useStreaming || !previewStreamingEnabled ? void 0 : async () => {
2394
- if (hasStreamedMessage) {
2395
- draftStream.forceNewMessage();
2396
- hasStreamedMessage = false;
2397
- appendRenderedText = "";
2398
- appendSourceText = "";
2399
- statusUpdateCount = 0;
2400
- }
2401
- };
2402
- const { queuedFinal, counts } = await dispatchInboundMessage({
2403
- ctx: prepared.ctxPayload,
2404
- cfg,
2405
- dispatcher,
2406
- replyOptions: {
2407
- ...replyOptions,
2408
- skillFilter: prepared.channelConfig?.skills,
2409
- hasRepliedRef,
2410
- disableBlockStreaming: useStreaming ? true : typeof account.config.blockStreaming === "boolean" ? !account.config.blockStreaming : void 0,
2411
- onModelSelected,
2412
- onPartialReply: useStreaming ? void 0 : !previewStreamingEnabled ? void 0 : async (payload) => {
2413
- updateDraftFromPartial(payload.text);
2414
- },
2415
- onAssistantMessageStart: onDraftBoundary,
2416
- onReasoningEnd: onDraftBoundary
2417
- }
2418
- });
2419
- await draftStream.flush();
2420
- draftStream.stop();
2421
- markDispatchIdle();
2422
- const finalStream = streamSession;
2423
- if (finalStream && !finalStream.stopped) try {
2424
- await stopSlackStream({ session: finalStream });
2425
- } catch (err) {
2426
- runtime.error?.(danger(`slack-stream: failed to stop stream: ${String(err)}`));
2427
- }
2428
- const anyReplyDelivered = queuedFinal || (counts.block ?? 0) > 0 || (counts.final ?? 0) > 0;
2429
- const participationThreadTs = usedReplyThreadTs ?? statusThreadTs;
2430
- if (anyReplyDelivered && participationThreadTs) recordSlackThreadParticipation(account.accountId, message.channel, participationThreadTs);
2431
- if (!anyReplyDelivered) {
2432
- await draftStream.clear();
2433
- if (prepared.isRoomish) clearHistoryEntriesIfEnabled({
2434
- historyMap: ctx.channelHistories,
2435
- historyKey: prepared.historyKey,
2436
- limit: ctx.historyLimit
2437
- });
2438
- return;
2439
- }
2440
- if (shouldLogVerbose()) {
2441
- const finalCount = counts.final;
2442
- logVerbose(`slack: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${prepared.replyTarget}`);
2443
- }
2444
- removeAckReactionAfterReply({
2445
- removeAfterReply: ctx.removeAckAfterReply,
2446
- ackReactionPromise: prepared.ackReactionPromise,
2447
- ackReactionValue: prepared.ackReactionValue,
2448
- remove: () => removeSlackReaction(message.channel, prepared.ackReactionMessageTs ?? "", prepared.ackReactionValue, {
2449
- token: ctx.botToken,
2450
- client: ctx.app.client
2451
- }),
2452
- onError: (err) => {
2453
- logAckFailure({
2454
- log: logVerbose,
2455
- channel: "slack",
2456
- target: `${message.channel}/${message.ts}`,
2457
- error: err
2458
- });
2459
- }
2460
- });
2461
- if (prepared.isRoomish) clearHistoryEntriesIfEnabled({
2462
- historyMap: ctx.channelHistories,
2463
- historyKey: prepared.historyKey,
2464
- limit: ctx.historyLimit
2465
- });
2466
- }
2467
- //#endregion
2468
- //#region extensions/slack/src/monitor/dm-auth.ts
2469
- async function authorizeSlackDirectMessage(params) {
2470
- if (!params.ctx.dmEnabled || params.ctx.dmPolicy === "disabled") {
2471
- await params.onDisabled();
2472
- return false;
2473
- }
2474
- if (params.ctx.dmPolicy === "open") return true;
2475
- const senderName = (await params.resolveSenderName(params.senderId))?.name ?? void 0;
2476
- const allowMatch = resolveSlackAllowListMatch({
2477
- allowList: params.allowFromLower,
2478
- id: params.senderId,
2479
- name: senderName,
2480
- allowNameMatching: params.ctx.allowNameMatching
2481
- });
2482
- const allowMatchMeta = formatAllowlistMatchMeta(allowMatch);
2483
- if (allowMatch.allowed) return true;
2484
- if (params.ctx.dmPolicy === "pairing") {
2485
- await issuePairingChallenge({
2486
- channel: "slack",
2487
- senderId: params.senderId,
2488
- senderIdLine: `Your Slack user id: ${params.senderId}`,
2489
- meta: { name: senderName },
2490
- upsertPairingRequest: async ({ id, meta }) => await upsertChannelPairingRequest({
2491
- channel: "slack",
2492
- id,
2493
- accountId: params.accountId,
2494
- meta
2495
- }),
2496
- sendPairingReply: params.sendPairingReply,
2497
- onCreated: () => {
2498
- params.log(`slack pairing request sender=${params.senderId} name=${senderName ?? "unknown"} (${allowMatchMeta})`);
2499
- },
2500
- onReplyError: (err) => {
2501
- params.log(`slack pairing reply failed for ${params.senderId}: ${String(err)}`);
2502
- }
2503
- });
2504
- return false;
2505
- }
2506
- await params.onUnauthorized({
2507
- allowMatchMeta,
2508
- senderName
2509
- });
2510
- return false;
2511
- }
2512
- //#endregion
2513
- //#region extensions/slack/src/monitor/room-context.ts
2514
- function resolveSlackRoomContextHints(params) {
2515
- if (!params.isRoomish) return {};
2516
- const untrustedChannelMetadata = buildUntrustedChannelMetadata({
2517
- source: "slack",
2518
- label: "Slack channel description",
2519
- entries: [params.channelInfo?.topic, params.channelInfo?.purpose]
2520
- });
2521
- const systemPromptParts = [params.channelConfig?.systemPrompt?.trim() || null].filter((entry) => Boolean(entry));
2522
- return {
2523
- untrustedChannelMetadata,
2524
- groupSystemPrompt: systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : void 0
2525
- };
2526
- }
2527
- //#endregion
2528
- //#region extensions/slack/src/monitor/message-handler/prepare-content.ts
2529
- function filterInheritedParentFiles(params) {
2530
- const { files, isThreadReply, threadStarter } = params;
2531
- if (!isThreadReply || !files?.length) return files;
2532
- if (!threadStarter?.files?.length) return files;
2533
- const starterFileIds = new Set(threadStarter.files.map((file) => file.id));
2534
- const filtered = files.filter((file) => !file.id || !starterFileIds.has(file.id));
2535
- if (filtered.length < files.length) logVerbose(`slack: filtered ${files.length - filtered.length} inherited parent file(s) from thread reply`);
2536
- return filtered.length > 0 ? filtered : void 0;
2537
- }
2538
- async function resolveSlackMessageContent(params) {
2539
- const ownFiles = filterInheritedParentFiles({
2540
- files: params.message.files,
2541
- isThreadReply: params.isThreadReply,
2542
- threadStarter: params.threadStarter
2543
- });
2544
- const media = await resolveSlackMedia({
2545
- files: ownFiles,
2546
- token: params.botToken,
2547
- maxBytes: params.mediaMaxBytes
2548
- });
2549
- const attachmentContent = await resolveSlackAttachmentContent({
2550
- attachments: params.message.attachments,
2551
- token: params.botToken,
2552
- maxBytes: params.mediaMaxBytes
2553
- });
2554
- const mergedMedia = [...media ?? [], ...attachmentContent?.media ?? []];
2555
- const effectiveDirectMedia = mergedMedia.length > 0 ? mergedMedia : null;
2556
- const mediaPlaceholder = effectiveDirectMedia ? effectiveDirectMedia.map((item) => item.placeholder).join(" ") : void 0;
2557
- const fallbackFiles = ownFiles ?? [];
2558
- const fileOnlyFallback = !mediaPlaceholder && fallbackFiles.length > 0 ? fallbackFiles.slice(0, 8).map((file) => file.name?.trim() || "file").join(", ") : void 0;
2559
- const fileOnlyPlaceholder = fileOnlyFallback ? `[Slack file: ${fileOnlyFallback}]` : void 0;
2560
- const botAttachmentText = params.isBotMessage && !attachmentContent?.text ? (params.message.attachments ?? []).map((attachment) => attachment.text?.trim() || attachment.fallback?.trim()).filter(Boolean).join("\n") : void 0;
2561
- const rawBody = [
2562
- (params.message.text ?? "").trim(),
2563
- attachmentContent?.text,
2564
- botAttachmentText,
2565
- mediaPlaceholder,
2566
- fileOnlyPlaceholder
2567
- ].filter(Boolean).join("\n") || "";
2568
- if (!rawBody) return null;
2569
- return {
2570
- rawBody,
2571
- effectiveDirectMedia
2572
- };
2573
- }
2574
- //#endregion
2575
- //#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
2576
- async function resolveSlackThreadContextData(params) {
2577
- let threadStarterBody;
2578
- let threadHistoryBody;
2579
- let threadSessionPreviousTimestamp;
2580
- let threadLabel;
2581
- let threadStarterMedia = null;
2582
- if (!params.isThreadReply || !params.threadTs) return {
2583
- threadStarterBody,
2584
- threadHistoryBody,
2585
- threadSessionPreviousTimestamp,
2586
- threadLabel,
2587
- threadStarterMedia
2588
- };
2589
- const starter = params.threadStarter;
2590
- if (starter?.text) {
2591
- threadStarterBody = starter.text;
2592
- const snippet = starter.text.replace(/\s+/g, " ").slice(0, 80);
2593
- threadLabel = `Slack thread ${params.roomLabel}${snippet ? `: ${snippet}` : ""}`;
2594
- if (!params.effectiveDirectMedia && starter.files && starter.files.length > 0) {
2595
- threadStarterMedia = await resolveSlackMedia({
2596
- files: starter.files,
2597
- token: params.ctx.botToken,
2598
- maxBytes: params.ctx.mediaMaxBytes
2599
- });
2600
- if (threadStarterMedia) logVerbose(`slack: hydrated thread starter file ${threadStarterMedia.map((item) => item.placeholder).join(", ")} from root message`);
2601
- }
2602
- } else threadLabel = `Slack thread ${params.roomLabel}`;
2603
- const threadInitialHistoryLimit = params.account.config?.thread?.initialHistoryLimit ?? 20;
2604
- threadSessionPreviousTimestamp = readSessionUpdatedAt({
2605
- storePath: params.storePath,
2606
- sessionKey: params.sessionKey
2607
- });
2608
- if (threadInitialHistoryLimit > 0 && !threadSessionPreviousTimestamp) {
2609
- const threadHistory = await resolveSlackThreadHistory({
2610
- channelId: params.message.channel,
2611
- threadTs: params.threadTs,
2612
- client: params.ctx.app.client,
2613
- currentMessageTs: params.message.ts,
2614
- limit: threadInitialHistoryLimit
2615
- });
2616
- if (threadHistory.length > 0) {
2617
- const uniqueUserIds = [...new Set(threadHistory.map((item) => item.userId).filter((id) => Boolean(id)))];
2618
- const userMap = /* @__PURE__ */ new Map();
2619
- await Promise.all(uniqueUserIds.map(async (id) => {
2620
- const user = await params.ctx.resolveUserName(id);
2621
- if (user) userMap.set(id, user);
2622
- }));
2623
- const historyParts = [];
2624
- for (const historyMsg of threadHistory) {
2625
- const msgSenderName = (historyMsg.userId ? userMap.get(historyMsg.userId) : null)?.name ?? (historyMsg.botId ? `Bot (${historyMsg.botId})` : "Unknown");
2626
- const role = Boolean(historyMsg.botId) ? "assistant" : "user";
2627
- const msgWithId = `${historyMsg.text}\n[slack message id: ${historyMsg.ts ?? "unknown"} channel: ${params.message.channel}]`;
2628
- historyParts.push(formatInboundEnvelope({
2629
- channel: "Slack",
2630
- from: `${msgSenderName} (${role})`,
2631
- timestamp: historyMsg.ts ? Math.round(Number(historyMsg.ts) * 1e3) : void 0,
2632
- body: msgWithId,
2633
- chatType: "channel",
2634
- envelope: params.envelopeOptions
2635
- }));
2636
- }
2637
- threadHistoryBody = historyParts.join("\n\n");
2638
- logVerbose(`slack: populated thread history with ${threadHistory.length} messages for new session`);
2639
- }
2640
- }
2641
- return {
2642
- threadStarterBody,
2643
- threadHistoryBody,
2644
- threadSessionPreviousTimestamp,
2645
- threadLabel,
2646
- threadStarterMedia
2647
- };
2648
- }
2649
- //#endregion
2650
- //#region extensions/slack/src/monitor/message-handler/prepare.ts
2651
- const mentionRegexCache = /* @__PURE__ */ new WeakMap();
2652
- function resolveCachedMentionRegexes(ctx, agentId) {
2653
- const key = agentId?.trim() || "__default__";
2654
- let byAgent = mentionRegexCache.get(ctx);
2655
- if (!byAgent) {
2656
- byAgent = /* @__PURE__ */ new Map();
2657
- mentionRegexCache.set(ctx, byAgent);
2658
- }
2659
- const cached = byAgent.get(key);
2660
- if (cached) return cached;
2661
- const built = buildMentionRegexes(ctx.cfg, agentId);
2662
- byAgent.set(key, built);
2663
- return built;
2664
- }
2665
- async function resolveSlackConversationContext(params) {
2666
- const { ctx, account, message } = params;
2667
- const cfg = ctx.cfg;
2668
- let channelInfo = {};
2669
- let resolvedChannelType = normalizeSlackChannelType(message.channel_type, message.channel);
2670
- if (resolvedChannelType !== "im" && (!message.channel_type || message.channel_type !== "im")) {
2671
- channelInfo = await ctx.resolveChannelName(message.channel);
2672
- resolvedChannelType = normalizeSlackChannelType(message.channel_type ?? channelInfo.type, message.channel);
2673
- }
2674
- const channelName = channelInfo?.name;
2675
- const isDirectMessage = resolvedChannelType === "im";
2676
- const isGroupDm = resolvedChannelType === "mpim";
2677
- const isRoom = resolvedChannelType === "channel" || resolvedChannelType === "group";
2678
- const isRoomish = isRoom || isGroupDm;
2679
- const channelConfig = isRoom ? resolveSlackChannelConfig({
2680
- channelId: message.channel,
2681
- channelName,
2682
- channels: ctx.channelsConfig,
2683
- channelKeys: ctx.channelsConfigKeys,
2684
- defaultRequireMention: ctx.defaultRequireMention,
2685
- allowNameMatching: ctx.allowNameMatching
2686
- }) : null;
2687
- const allowBots = channelConfig?.allowBots ?? account.config?.allowBots ?? cfg.channels?.slack?.allowBots ?? false;
2688
- return {
2689
- channelInfo,
2690
- channelName,
2691
- resolvedChannelType,
2692
- isDirectMessage,
2693
- isGroupDm,
2694
- isRoom,
2695
- isRoomish,
2696
- channelConfig,
2697
- allowBots,
2698
- isBotMessage: Boolean(message.bot_id)
2699
- };
2700
- }
2701
- async function authorizeSlackInboundMessage(params) {
2702
- const { ctx, account, message, conversation } = params;
2703
- const { isDirectMessage, channelName, resolvedChannelType, isBotMessage, allowBots } = conversation;
2704
- if (isBotMessage) {
2705
- if (message.user && ctx.botUserId && message.user === ctx.botUserId) return null;
2706
- if (!allowBots) {
2707
- logVerbose(`slack: drop bot message ${message.bot_id ?? "unknown"} (allowBots=false)`);
2708
- return null;
2709
- }
2710
- }
2711
- if (isDirectMessage && !message.user) {
2712
- logVerbose("slack: drop dm message (missing user id)");
2713
- return null;
2714
- }
2715
- const senderId = message.user ?? (isBotMessage ? message.bot_id : void 0);
2716
- if (!senderId) {
2717
- logVerbose("slack: drop message (missing sender id)");
2718
- return null;
2719
- }
2720
- if (!ctx.isChannelAllowed({
2721
- channelId: message.channel,
2722
- channelName,
2723
- channelType: resolvedChannelType
2724
- })) {
2725
- logVerbose("slack: drop message (channel not allowed)");
2726
- return null;
2727
- }
2728
- const { allowFromLower } = await resolveSlackEffectiveAllowFrom(ctx, { includePairingStore: isDirectMessage });
2729
- if (isDirectMessage) {
2730
- const directUserId = message.user;
2731
- if (!directUserId) {
2732
- logVerbose("slack: drop dm message (missing user id)");
2733
- return null;
2734
- }
2735
- if (!await authorizeSlackDirectMessage({
2736
- ctx,
2737
- accountId: account.accountId,
2738
- senderId: directUserId,
2739
- allowFromLower,
2740
- resolveSenderName: ctx.resolveUserName,
2741
- sendPairingReply: async (text) => {
2742
- await sendMessageSlack(message.channel, text, {
2743
- token: ctx.botToken,
2744
- client: ctx.app.client,
2745
- accountId: account.accountId
2746
- });
2747
- },
2748
- onDisabled: () => {
2749
- logVerbose("slack: drop dm (dms disabled)");
2750
- },
2751
- onUnauthorized: ({ allowMatchMeta }) => {
2752
- logVerbose(`Blocked unauthorized slack sender ${message.user} (dmPolicy=${ctx.dmPolicy}, ${allowMatchMeta})`);
2753
- },
2754
- log: logVerbose
2755
- })) return null;
2756
- }
2757
- return {
2758
- senderId,
2759
- allowFromLower
2760
- };
2761
- }
2762
- function resolveSlackRoutingContext(params) {
2763
- const { ctx, account, message, isDirectMessage, isGroupDm, isRoom, isRoomish } = params;
2764
- const route = resolveAgentRoute({
2765
- cfg: ctx.cfg,
2766
- channel: "slack",
2767
- accountId: account.accountId,
2768
- teamId: ctx.teamId || void 0,
2769
- peer: {
2770
- kind: isDirectMessage ? "direct" : isRoom ? "channel" : "group",
2771
- id: isDirectMessage ? message.user ?? "unknown" : message.channel
2772
- }
2773
- });
2774
- const chatType = isDirectMessage ? "direct" : isGroupDm ? "group" : "channel";
2775
- const replyToMode = resolveSlackReplyToMode(account, chatType);
2776
- const threadContext = resolveSlackThreadContext({
2777
- message,
2778
- replyToMode
2779
- });
2780
- const threadTs = threadContext.incomingThreadTs;
2781
- const isThreadReply = threadContext.isThreadReply;
2782
- const autoThreadId = !isThreadReply && replyToMode === "all" && threadContext.messageTs ? threadContext.messageTs : void 0;
2783
- const canonicalThreadId = isRoomish ? isThreadReply && threadTs ? threadTs : void 0 : isThreadReply ? threadTs : autoThreadId;
2784
- const threadKeys = resolveThreadSessionKeys({
2785
- baseSessionKey: route.sessionKey,
2786
- threadId: canonicalThreadId,
2787
- parentSessionKey: canonicalThreadId && ctx.threadInheritParent ? route.sessionKey : void 0
2788
- });
2789
- const sessionKey = threadKeys.sessionKey;
2790
- return {
2791
- route,
2792
- chatType,
2793
- replyToMode,
2794
- threadContext,
2795
- threadTs,
2796
- isThreadReply,
2797
- threadKeys,
2798
- sessionKey,
2799
- historyKey: isThreadReply && ctx.threadHistoryScope === "thread" ? sessionKey : message.channel
2800
- };
2801
- }
2802
- async function prepareSlackMessage(params) {
2803
- const { ctx, account, message, opts } = params;
2804
- const cfg = ctx.cfg;
2805
- const conversation = await resolveSlackConversationContext({
2806
- ctx,
2807
- account,
2808
- message
2809
- });
2810
- const { channelInfo, channelName, isDirectMessage, isGroupDm, isRoom, isRoomish, channelConfig, isBotMessage } = conversation;
2811
- const authorization = await authorizeSlackInboundMessage({
2812
- ctx,
2813
- account,
2814
- message,
2815
- conversation
2816
- });
2817
- if (!authorization) return null;
2818
- const { senderId, allowFromLower } = authorization;
2819
- const { route, replyToMode, threadContext, threadTs, isThreadReply, threadKeys, sessionKey, historyKey } = resolveSlackRoutingContext({
2820
- ctx,
2821
- account,
2822
- message,
2823
- isDirectMessage,
2824
- isGroupDm,
2825
- isRoom,
2826
- isRoomish
2827
- });
2828
- const mentionRegexes = resolveCachedMentionRegexes(ctx, route.agentId);
2829
- const hasAnyMention = /<@[^>]+>/.test(message.text ?? "");
2830
- const explicitlyMentioned = Boolean(ctx.botUserId && message.text?.includes(`<@${ctx.botUserId}>`));
2831
- const wasMentioned = opts.wasMentioned ?? (!isDirectMessage && matchesMentionWithExplicit({
2832
- text: message.text ?? "",
2833
- mentionRegexes,
2834
- explicit: {
2835
- hasAnyMention,
2836
- isExplicitlyMentioned: explicitlyMentioned,
2837
- canResolveExplicit: Boolean(ctx.botUserId)
2838
- }
2839
- }));
2840
- const implicitMention = Boolean(!isDirectMessage && ctx.botUserId && message.thread_ts && (message.parent_user_id === ctx.botUserId || hasSlackThreadParticipation(account.accountId, message.channel, message.thread_ts)));
2841
- let resolvedSenderName = message.username?.trim() || void 0;
2842
- const resolveSenderName = async () => {
2843
- if (resolvedSenderName) return resolvedSenderName;
2844
- if (message.user) {
2845
- const normalized = (await ctx.resolveUserName(message.user))?.name?.trim();
2846
- if (normalized) {
2847
- resolvedSenderName = normalized;
2848
- return resolvedSenderName;
2849
- }
2850
- }
2851
- resolvedSenderName = message.user ?? message.bot_id ?? "unknown";
2852
- return resolvedSenderName;
2853
- };
2854
- const senderNameForAuth = ctx.allowNameMatching ? await resolveSenderName() : void 0;
2855
- const channelUserAuthorized = isRoom ? resolveSlackUserAllowed({
2856
- allowList: channelConfig?.users,
2857
- userId: senderId,
2858
- userName: senderNameForAuth,
2859
- allowNameMatching: ctx.allowNameMatching
2860
- }) : true;
2861
- if (isRoom && !channelUserAuthorized) {
2862
- logVerbose(`Blocked unauthorized slack sender ${senderId} (not in channel users)`);
2863
- return null;
2864
- }
2865
- const allowTextCommands = shouldHandleTextCommands({
2866
- cfg,
2867
- surface: "slack"
2868
- });
2869
- const textForCommandDetection = stripSlackMentionsForCommandDetection(message.text ?? "");
2870
- const hasControlCommandInMessage = hasControlCommand(textForCommandDetection, cfg);
2871
- const ownerAuthorized = resolveSlackAllowListMatch({
2872
- allowList: allowFromLower,
2873
- id: senderId,
2874
- name: senderNameForAuth,
2875
- allowNameMatching: ctx.allowNameMatching
2876
- }).allowed;
2877
- const channelUsersAllowlistConfigured = isRoom && Array.isArray(channelConfig?.users) && channelConfig.users.length > 0;
2878
- const channelCommandAuthorized = isRoom && channelUsersAllowlistConfigured ? resolveSlackUserAllowed({
2879
- allowList: channelConfig?.users,
2880
- userId: senderId,
2881
- userName: senderNameForAuth,
2882
- allowNameMatching: ctx.allowNameMatching
2883
- }) : false;
2884
- const commandGate = resolveControlCommandGate({
2885
- useAccessGroups: ctx.useAccessGroups,
2886
- authorizers: [{
2887
- configured: allowFromLower.length > 0,
2888
- allowed: ownerAuthorized
2889
- }, {
2890
- configured: channelUsersAllowlistConfigured,
2891
- allowed: channelCommandAuthorized
2892
- }],
2893
- allowTextCommands,
2894
- hasControlCommand: hasControlCommandInMessage
2895
- });
2896
- const commandAuthorized = commandGate.commandAuthorized;
2897
- if (isRoomish && commandGate.shouldBlock) {
2898
- logInboundDrop({
2899
- log: logVerbose,
2900
- channel: "slack",
2901
- reason: "control command (unauthorized)",
2902
- target: senderId
2903
- });
2904
- return null;
2905
- }
2906
- const shouldRequireMention = isRoom ? channelConfig?.requireMention ?? ctx.defaultRequireMention : false;
2907
- const canDetectMention = Boolean(ctx.botUserId) || mentionRegexes.length > 0;
2908
- const mentionGate = resolveMentionGatingWithBypass({
2909
- isGroup: isRoom,
2910
- requireMention: Boolean(shouldRequireMention),
2911
- canDetectMention,
2912
- wasMentioned,
2913
- implicitMention,
2914
- hasAnyMention,
2915
- allowTextCommands,
2916
- hasControlCommand: hasControlCommandInMessage,
2917
- commandAuthorized
2918
- });
2919
- const effectiveWasMentioned = mentionGate.effectiveWasMentioned;
2920
- if (isRoom && shouldRequireMention && mentionGate.shouldSkip) {
2921
- ctx.logger.info({
2922
- channel: message.channel,
2923
- reason: "no-mention"
2924
- }, "skipping channel message");
2925
- const pendingText = (message.text ?? "").trim();
2926
- const fallbackFile = message.files?.[0]?.name ? `[Slack file: ${message.files[0].name}]` : message.files?.length ? "[Slack file]" : "";
2927
- const pendingBody = pendingText || fallbackFile;
2928
- recordPendingHistoryEntryIfEnabled({
2929
- historyMap: ctx.channelHistories,
2930
- historyKey,
2931
- limit: ctx.historyLimit,
2932
- entry: pendingBody ? {
2933
- sender: await resolveSenderName(),
2934
- body: pendingBody,
2935
- timestamp: message.ts ? Math.round(Number(message.ts) * 1e3) : void 0,
2936
- messageId: message.ts
2937
- } : null
2938
- });
2939
- return null;
2940
- }
2941
- const threadStarter = isThreadReply && threadTs ? await resolveSlackThreadStarter({
2942
- channelId: message.channel,
2943
- threadTs,
2944
- client: ctx.app.client
2945
- }) : null;
2946
- const resolvedMessageContent = await resolveSlackMessageContent({
2947
- message,
2948
- isThreadReply,
2949
- threadStarter,
2950
- isBotMessage,
2951
- botToken: ctx.botToken,
2952
- mediaMaxBytes: ctx.mediaMaxBytes
2953
- });
2954
- if (!resolvedMessageContent) return null;
2955
- const { rawBody, effectiveDirectMedia } = resolvedMessageContent;
2956
- const ackReaction = resolveAckReaction(cfg, route.agentId, {
2957
- channel: "slack",
2958
- accountId: account.accountId
2959
- });
2960
- const ackReactionValue = ackReaction ?? "";
2961
- const shouldAckReaction$1 = () => Boolean(ackReaction && shouldAckReaction({
2962
- scope: ctx.ackReactionScope,
2963
- isDirect: isDirectMessage,
2964
- isGroup: isRoomish,
2965
- isMentionableGroup: isRoom,
2966
- requireMention: Boolean(shouldRequireMention),
2967
- canDetectMention,
2968
- effectiveWasMentioned,
2969
- shouldBypassMention: mentionGate.shouldBypassMention
2970
- }));
2971
- const ackReactionMessageTs = message.ts;
2972
- const ackReactionPromise = shouldAckReaction$1() && ackReactionMessageTs && ackReactionValue ? reactSlackMessage(message.channel, ackReactionMessageTs, ackReactionValue, {
2973
- token: ctx.botToken,
2974
- client: ctx.app.client
2975
- }).then(() => true, (err) => {
2976
- logVerbose(`slack react failed for channel ${message.channel}: ${String(err)}`);
2977
- return false;
2978
- }) : null;
2979
- const roomLabel = channelName ? `#${channelName}` : `#${message.channel}`;
2980
- const senderName = await resolveSenderName();
2981
- const preview = rawBody.replace(/\s+/g, " ").slice(0, 160);
2982
- const inboundLabel = isDirectMessage ? `Slack DM from ${senderName}` : `Slack message in ${roomLabel} from ${senderName}`;
2983
- const slackFrom = isDirectMessage ? `slack:${message.user}` : isRoom ? `slack:channel:${message.channel}` : `slack:group:${message.channel}`;
2984
- enqueueSystemEvent(`${inboundLabel}: ${preview}`, {
2985
- sessionKey,
2986
- contextKey: `slack:message:${message.channel}:${message.ts ?? "unknown"}`
2987
- });
2988
- const envelopeFrom = resolveConversationLabel({
2989
- ChatType: isDirectMessage ? "direct" : "channel",
2990
- SenderName: senderName,
2991
- GroupSubject: isRoomish ? roomLabel : void 0,
2992
- From: slackFrom
2993
- }) ?? (isDirectMessage ? senderName : roomLabel);
2994
- const threadInfo = isThreadReply && threadTs ? ` thread_ts: ${threadTs}${message.parent_user_id ? ` parent_user_id: ${message.parent_user_id}` : ""}` : "";
2995
- const textWithId = `${rawBody}\n[slack message id: ${message.ts} channel: ${message.channel}${threadInfo}]`;
2996
- const storePath = resolveStorePath(ctx.cfg.session?.store, { agentId: route.agentId });
2997
- const envelopeOptions = resolveEnvelopeFormatOptions(ctx.cfg);
2998
- const previousTimestamp = readSessionUpdatedAt({
2999
- storePath,
3000
- sessionKey
3001
- });
3002
- let combinedBody = formatInboundEnvelope({
3003
- channel: "Slack",
3004
- from: envelopeFrom,
3005
- timestamp: message.ts ? Math.round(Number(message.ts) * 1e3) : void 0,
3006
- body: textWithId,
3007
- chatType: isDirectMessage ? "direct" : "channel",
3008
- sender: {
3009
- name: senderName,
3010
- id: senderId
3011
- },
3012
- previousTimestamp,
3013
- envelope: envelopeOptions
3014
- });
3015
- if (isRoomish && ctx.historyLimit > 0) combinedBody = buildPendingHistoryContextFromMap({
3016
- historyMap: ctx.channelHistories,
3017
- historyKey,
3018
- limit: ctx.historyLimit,
3019
- currentMessage: combinedBody,
3020
- formatEntry: (entry) => formatInboundEnvelope({
3021
- channel: "Slack",
3022
- from: roomLabel,
3023
- timestamp: entry.timestamp,
3024
- body: `${entry.body}${entry.messageId ? ` [id:${entry.messageId} channel:${message.channel}]` : ""}`,
3025
- chatType: "channel",
3026
- senderLabel: entry.sender,
3027
- envelope: envelopeOptions
3028
- })
3029
- });
3030
- const slackTo = isDirectMessage ? `user:${message.user}` : `channel:${message.channel}`;
3031
- const { untrustedChannelMetadata, groupSystemPrompt } = resolveSlackRoomContextHints({
3032
- isRoomish,
3033
- channelInfo,
3034
- channelConfig
3035
- });
3036
- const { threadStarterBody, threadHistoryBody, threadSessionPreviousTimestamp, threadLabel, threadStarterMedia } = await resolveSlackThreadContextData({
3037
- ctx,
3038
- account,
3039
- message,
3040
- isThreadReply,
3041
- threadTs,
3042
- threadStarter,
3043
- roomLabel,
3044
- storePath,
3045
- sessionKey,
3046
- envelopeOptions,
3047
- effectiveDirectMedia
3048
- });
3049
- const effectiveMedia = effectiveDirectMedia ?? threadStarterMedia;
3050
- const firstMedia = effectiveMedia?.[0];
3051
- const inboundHistory = isRoomish && ctx.historyLimit > 0 ? (ctx.channelHistories.get(historyKey) ?? []).map((entry) => ({
3052
- sender: entry.sender,
3053
- body: entry.body,
3054
- timestamp: entry.timestamp
3055
- })) : void 0;
3056
- const commandBody = textForCommandDetection.trim();
3057
- const ctxPayload = finalizeInboundContext({
3058
- Body: combinedBody,
3059
- BodyForAgent: rawBody,
3060
- InboundHistory: inboundHistory,
3061
- RawBody: rawBody,
3062
- CommandBody: commandBody,
3063
- BodyForCommands: commandBody,
3064
- From: slackFrom,
3065
- To: slackTo,
3066
- SessionKey: sessionKey,
3067
- AccountId: route.accountId,
3068
- ChatType: isDirectMessage ? "direct" : "channel",
3069
- ConversationLabel: envelopeFrom,
3070
- GroupSubject: isRoomish ? roomLabel : void 0,
3071
- GroupSystemPrompt: isRoomish ? groupSystemPrompt : void 0,
3072
- UntrustedContext: untrustedChannelMetadata ? [untrustedChannelMetadata] : void 0,
3073
- SenderName: senderName,
3074
- SenderId: senderId,
3075
- Provider: "slack",
3076
- Surface: "slack",
3077
- MessageSid: message.ts,
3078
- ReplyToId: threadContext.replyToId,
3079
- MessageThreadId: threadContext.messageThreadId,
3080
- ParentSessionKey: threadKeys.parentSessionKey,
3081
- ThreadStarterBody: !threadSessionPreviousTimestamp ? threadStarterBody : void 0,
3082
- ThreadHistoryBody: threadHistoryBody,
3083
- IsFirstThreadTurn: isThreadReply && threadTs && !threadSessionPreviousTimestamp ? true : void 0,
3084
- ThreadLabel: threadLabel,
3085
- Timestamp: message.ts ? Math.round(Number(message.ts) * 1e3) : void 0,
3086
- WasMentioned: isRoomish ? effectiveWasMentioned : void 0,
3087
- MediaPath: firstMedia?.path,
3088
- MediaType: firstMedia?.contentType,
3089
- MediaUrl: firstMedia?.path,
3090
- MediaPaths: effectiveMedia && effectiveMedia.length > 0 ? effectiveMedia.map((m) => m.path) : void 0,
3091
- MediaUrls: effectiveMedia && effectiveMedia.length > 0 ? effectiveMedia.map((m) => m.path) : void 0,
3092
- MediaTypes: effectiveMedia && effectiveMedia.length > 0 ? effectiveMedia.map((m) => m.contentType ?? "") : void 0,
3093
- CommandAuthorized: commandAuthorized,
3094
- OriginatingChannel: "slack",
3095
- OriginatingTo: slackTo,
3096
- NativeChannelId: message.channel
3097
- });
3098
- const pinnedMainDmOwner = isDirectMessage ? resolvePinnedMainDmOwnerFromAllowlist({
3099
- dmScope: cfg.session?.dmScope,
3100
- allowFrom: ctx.allowFrom,
3101
- normalizeEntry: normalizeSlackAllowOwnerEntry
3102
- }) : null;
3103
- await recordInboundSession({
3104
- storePath,
3105
- sessionKey,
3106
- ctx: ctxPayload,
3107
- updateLastRoute: isDirectMessage ? {
3108
- sessionKey: route.mainSessionKey,
3109
- channel: "slack",
3110
- to: `user:${message.user}`,
3111
- accountId: route.accountId,
3112
- threadId: threadContext.messageThreadId,
3113
- mainDmOwnerPin: pinnedMainDmOwner && message.user ? {
3114
- ownerRecipient: pinnedMainDmOwner,
3115
- senderRecipient: message.user.toLowerCase(),
3116
- onSkip: ({ ownerRecipient, senderRecipient }) => {
3117
- logVerbose(`slack: skip main-session last route for ${senderRecipient} (pinned owner ${ownerRecipient})`);
3118
- }
3119
- } : void 0
3120
- } : void 0,
3121
- onRecordError: (err) => {
3122
- ctx.logger.warn({
3123
- error: String(err),
3124
- storePath,
3125
- sessionKey
3126
- }, "failed updating session meta");
3127
- }
3128
- });
3129
- const replyTarget = ctxPayload.To ?? void 0;
3130
- if (!replyTarget) return null;
3131
- if (shouldLogVerbose()) logVerbose(`slack inbound: channel=${message.channel} from=${slackFrom} preview="${preview}"`);
3132
- return {
3133
- ctx,
3134
- account,
3135
- message,
3136
- route,
3137
- channelConfig,
3138
- replyTarget,
3139
- ctxPayload,
3140
- replyToMode,
3141
- isDirectMessage,
3142
- isRoomish,
3143
- historyKey,
3144
- preview,
3145
- ackReactionMessageTs,
3146
- ackReactionValue,
3147
- ackReactionPromise
3148
- };
3149
- }
3150
- //#endregion
3151
- //#region extensions/slack/src/monitor/thread-resolution.ts
3152
- const DEFAULT_THREAD_TS_CACHE_TTL_MS = 6e4;
3153
- const DEFAULT_THREAD_TS_CACHE_MAX = 500;
3154
- const normalizeThreadTs = (threadTs) => {
3155
- const trimmed = threadTs?.trim();
3156
- return trimmed ? trimmed : void 0;
3157
- };
3158
- async function resolveThreadTsFromHistory(params) {
3159
- try {
3160
- const response = await params.client.conversations.history({
3161
- channel: params.channelId,
3162
- latest: params.messageTs,
3163
- oldest: params.messageTs,
3164
- inclusive: true,
3165
- limit: 1
3166
- });
3167
- return normalizeThreadTs((response.messages?.find((entry) => entry.ts === params.messageTs) ?? response.messages?.[0])?.thread_ts);
3168
- } catch (err) {
3169
- if (shouldLogVerbose()) logVerbose(`slack inbound: failed to resolve thread_ts via conversations.history for channel=${params.channelId} ts=${params.messageTs}: ${String(err)}`);
3170
- return;
3171
- }
3172
- }
3173
- function createSlackThreadTsResolver(params) {
3174
- const ttlMs = Math.max(0, params.cacheTtlMs ?? DEFAULT_THREAD_TS_CACHE_TTL_MS);
3175
- const maxSize = Math.max(0, params.maxSize ?? DEFAULT_THREAD_TS_CACHE_MAX);
3176
- const cache = /* @__PURE__ */ new Map();
3177
- const inflight = /* @__PURE__ */ new Map();
3178
- const getCached = (key, now) => {
3179
- const entry = cache.get(key);
3180
- if (!entry) return;
3181
- if (ttlMs > 0 && now - entry.updatedAt > ttlMs) {
3182
- cache.delete(key);
3183
- return;
3184
- }
3185
- cache.delete(key);
3186
- cache.set(key, {
3187
- ...entry,
3188
- updatedAt: now
3189
- });
3190
- return entry.threadTs;
3191
- };
3192
- const setCached = (key, threadTs, now) => {
3193
- cache.delete(key);
3194
- cache.set(key, {
3195
- threadTs,
3196
- updatedAt: now
3197
- });
3198
- pruneMapToMaxSize(cache, maxSize);
3199
- };
3200
- return { resolve: async (request) => {
3201
- const { message } = request;
3202
- if (!message.parent_user_id || message.thread_ts || !message.ts) return message;
3203
- const cacheKey = `${message.channel}:${message.ts}`;
3204
- const cached = getCached(cacheKey, Date.now());
3205
- if (cached !== void 0) return cached ? {
3206
- ...message,
3207
- thread_ts: cached
3208
- } : message;
3209
- if (shouldLogVerbose()) logVerbose(`slack inbound: missing thread_ts for thread reply channel=${message.channel} ts=${message.ts} source=${request.source}`);
3210
- let pending = inflight.get(cacheKey);
3211
- if (!pending) {
3212
- pending = resolveThreadTsFromHistory({
3213
- client: params.client,
3214
- channelId: message.channel,
3215
- messageTs: message.ts
3216
- });
3217
- inflight.set(cacheKey, pending);
3218
- }
3219
- let resolved;
3220
- try {
3221
- resolved = await pending;
3222
- } finally {
3223
- inflight.delete(cacheKey);
3224
- }
3225
- setCached(cacheKey, resolved ?? null, Date.now());
3226
- if (resolved) {
3227
- if (shouldLogVerbose()) logVerbose(`slack inbound: resolved missing thread_ts channel=${message.channel} ts=${message.ts} -> thread_ts=${resolved}`);
3228
- return {
3229
- ...message,
3230
- thread_ts: resolved
3231
- };
3232
- }
3233
- if (shouldLogVerbose()) logVerbose(`slack inbound: could not resolve missing thread_ts channel=${message.channel} ts=${message.ts}`);
3234
- return message;
3235
- } };
3236
- }
3237
- //#endregion
3238
- //#region extensions/slack/src/monitor/message-handler.ts
3239
- const APP_MENTION_RETRY_TTL_MS = 6e4;
3240
- function resolveSlackSenderId(message) {
3241
- return message.user ?? message.bot_id ?? null;
3242
- }
3243
- function isSlackDirectMessageChannel(channelId) {
3244
- return channelId.startsWith("D");
3245
- }
3246
- function isTopLevelSlackMessage(message) {
3247
- return !message.thread_ts && !message.parent_user_id;
3248
- }
3249
- function buildTopLevelSlackConversationKey(message, accountId) {
3250
- if (!isTopLevelSlackMessage(message)) return null;
3251
- const senderId = resolveSlackSenderId(message);
3252
- if (!senderId) return null;
3253
- return `slack:${accountId}:${message.channel}:${senderId}`;
3254
- }
3255
- function shouldDebounceSlackMessage(message, cfg) {
3256
- return shouldDebounceTextInbound({
3257
- text: stripSlackMentionsForCommandDetection(message.text ?? ""),
3258
- cfg,
3259
- hasMedia: Boolean(message.files && message.files.length > 0)
3260
- });
3261
- }
3262
- function buildSeenMessageKey(channelId, ts) {
3263
- if (!channelId || !ts) return null;
3264
- return `${channelId}:${ts}`;
3265
- }
3266
- /**
3267
- * Build a debounce key that isolates messages by thread (or by message timestamp
3268
- * for top-level non-DM channel messages). Without per-message scoping, concurrent
3269
- * top-level messages from the same sender can share a key and get merged
3270
- * into a single reply on the wrong thread.
3271
- *
3272
- * DMs intentionally stay channel-scoped to preserve short-message batching.
3273
- */
3274
- function buildSlackDebounceKey(message, accountId) {
3275
- const senderId = resolveSlackSenderId(message);
3276
- if (!senderId) return null;
3277
- const messageTs = message.ts ?? message.event_ts;
3278
- return `slack:${accountId}:${message.thread_ts ? `${message.channel}:${message.thread_ts}` : message.parent_user_id && messageTs ? `${message.channel}:maybe-thread:${messageTs}` : messageTs && !isSlackDirectMessageChannel(message.channel) ? `${message.channel}:${messageTs}` : message.channel}:${senderId}`;
3279
- }
3280
- function createSlackMessageHandler(params) {
3281
- const { ctx, account, trackEvent } = params;
3282
- const { debounceMs, debouncer } = createChannelInboundDebouncer({
3283
- cfg: ctx.cfg,
3284
- channel: "slack",
3285
- buildKey: (entry) => buildSlackDebounceKey(entry.message, ctx.accountId),
3286
- shouldDebounce: (entry) => shouldDebounceSlackMessage(entry.message, ctx.cfg),
3287
- onFlush: async (entries) => {
3288
- const last = entries.at(-1);
3289
- if (!last) return;
3290
- const flushedKey = buildSlackDebounceKey(last.message, ctx.accountId);
3291
- const topLevelConversationKey = buildTopLevelSlackConversationKey(last.message, ctx.accountId);
3292
- if (flushedKey && topLevelConversationKey) {
3293
- const pendingKeys = pendingTopLevelDebounceKeys.get(topLevelConversationKey);
3294
- if (pendingKeys) {
3295
- pendingKeys.delete(flushedKey);
3296
- if (pendingKeys.size === 0) pendingTopLevelDebounceKeys.delete(topLevelConversationKey);
3297
- }
3298
- }
3299
- const combinedText = entries.length === 1 ? last.message.text ?? "" : entries.map((entry) => entry.message.text ?? "").filter(Boolean).join("\n");
3300
- const combinedMentioned = entries.some((entry) => Boolean(entry.opts.wasMentioned));
3301
- const prepared = await prepareSlackMessage({
3302
- ctx,
3303
- account,
3304
- message: {
3305
- ...last.message,
3306
- text: combinedText
3307
- },
3308
- opts: {
3309
- ...last.opts,
3310
- wasMentioned: combinedMentioned || last.opts.wasMentioned
3311
- }
3312
- });
3313
- const seenMessageKey = buildSeenMessageKey(last.message.channel, last.message.ts);
3314
- if (!prepared) return;
3315
- if (seenMessageKey) {
3316
- pruneAppMentionRetryKeys(Date.now());
3317
- if (last.opts.source === "app_mention") appMentionDispatchedKeys.set(seenMessageKey, Date.now() + APP_MENTION_RETRY_TTL_MS);
3318
- else if (last.opts.source === "message" && appMentionDispatchedKeys.has(seenMessageKey)) {
3319
- appMentionDispatchedKeys.delete(seenMessageKey);
3320
- appMentionRetryKeys.delete(seenMessageKey);
3321
- return;
3322
- }
3323
- appMentionRetryKeys.delete(seenMessageKey);
3324
- }
3325
- if (entries.length > 1) {
3326
- const ids = entries.map((entry) => entry.message.ts).filter(Boolean);
3327
- if (ids.length > 0) {
3328
- prepared.ctxPayload.MessageSids = ids;
3329
- prepared.ctxPayload.MessageSidFirst = ids[0];
3330
- prepared.ctxPayload.MessageSidLast = ids[ids.length - 1];
3331
- }
3332
- }
3333
- await dispatchPreparedSlackMessage(prepared);
3334
- },
3335
- onError: (err) => {
3336
- ctx.runtime.error?.(`slack inbound debounce flush failed: ${String(err)}`);
3337
- }
3338
- });
3339
- const threadTsResolver = createSlackThreadTsResolver({ client: ctx.app.client });
3340
- const pendingTopLevelDebounceKeys = /* @__PURE__ */ new Map();
3341
- const appMentionRetryKeys = /* @__PURE__ */ new Map();
3342
- const appMentionDispatchedKeys = /* @__PURE__ */ new Map();
3343
- const pruneAppMentionRetryKeys = (now) => {
3344
- for (const [key, expiresAt] of appMentionRetryKeys) if (expiresAt <= now) appMentionRetryKeys.delete(key);
3345
- for (const [key, expiresAt] of appMentionDispatchedKeys) if (expiresAt <= now) appMentionDispatchedKeys.delete(key);
3346
- };
3347
- const rememberAppMentionRetryKey = (key) => {
3348
- const now = Date.now();
3349
- pruneAppMentionRetryKeys(now);
3350
- appMentionRetryKeys.set(key, now + APP_MENTION_RETRY_TTL_MS);
3351
- };
3352
- const consumeAppMentionRetryKey = (key) => {
3353
- pruneAppMentionRetryKeys(Date.now());
3354
- if (!appMentionRetryKeys.has(key)) return false;
3355
- appMentionRetryKeys.delete(key);
3356
- return true;
3357
- };
3358
- return async (message, opts) => {
3359
- if (opts.source === "message" && message.type !== "message") return;
3360
- if (opts.source === "message" && message.subtype && message.subtype !== "file_share" && message.subtype !== "bot_message") return;
3361
- const seenMessageKey = buildSeenMessageKey(message.channel, message.ts);
3362
- const wasSeen = seenMessageKey ? ctx.markMessageSeen(message.channel, message.ts) : false;
3363
- if (seenMessageKey && opts.source === "message" && !wasSeen) rememberAppMentionRetryKey(seenMessageKey);
3364
- if (seenMessageKey && wasSeen) {
3365
- if (opts.source !== "app_mention" || !consumeAppMentionRetryKey(seenMessageKey)) return;
3366
- }
3367
- trackEvent?.();
3368
- const resolvedMessage = await threadTsResolver.resolve({
3369
- message,
3370
- source: opts.source
3371
- });
3372
- const debounceKey = buildSlackDebounceKey(resolvedMessage, ctx.accountId);
3373
- const conversationKey = buildTopLevelSlackConversationKey(resolvedMessage, ctx.accountId);
3374
- const canDebounce = debounceMs > 0 && shouldDebounceSlackMessage(resolvedMessage, ctx.cfg);
3375
- if (!canDebounce && conversationKey) {
3376
- const pendingKeys = pendingTopLevelDebounceKeys.get(conversationKey);
3377
- if (pendingKeys && pendingKeys.size > 0) {
3378
- const keysToFlush = Array.from(pendingKeys);
3379
- for (const pendingKey of keysToFlush) await debouncer.flushKey(pendingKey);
3380
- }
3381
- }
3382
- if (canDebounce && debounceKey && conversationKey) {
3383
- const pendingKeys = pendingTopLevelDebounceKeys.get(conversationKey) ?? /* @__PURE__ */ new Set();
3384
- pendingKeys.add(debounceKey);
3385
- pendingTopLevelDebounceKeys.set(conversationKey, pendingKeys);
3386
- }
3387
- await debouncer.enqueue({
3388
- message: resolvedMessage,
3389
- opts
3390
- });
3391
- };
3392
- }
3393
- //#endregion
3394
- //#region extensions/slack/src/monitor/reconnect-policy.ts
3395
- const SLACK_AUTH_ERROR_RE = /account_inactive|invalid_auth|token_revoked|token_expired|not_authed|org_login_required|team_access_not_granted|missing_scope|cannot_find_service|invalid_token/i;
3396
- const SLACK_SOCKET_RECONNECT_POLICY = {
3397
- initialMs: 2e3,
3398
- maxMs: 3e4,
3399
- factor: 1.8,
3400
- jitter: .25,
3401
- maxAttempts: 12
3402
- };
3403
- function getSocketEmitter(app) {
3404
- const receiver = app.receiver;
3405
- const client = receiver && typeof receiver === "object" ? receiver.client : void 0;
3406
- if (!client || typeof client !== "object") return null;
3407
- const on = client.on;
3408
- const off = client.off;
3409
- if (typeof on !== "function" || typeof off !== "function") return null;
3410
- return {
3411
- on: (event, listener) => on.call(client, event, listener),
3412
- off: (event, listener) => off.call(client, event, listener)
3413
- };
3414
- }
3415
- function waitForSlackSocketDisconnect(app, abortSignal) {
3416
- return new Promise((resolve) => {
3417
- const emitter = getSocketEmitter(app);
3418
- if (!emitter) {
3419
- abortSignal?.addEventListener("abort", () => resolve({ event: "disconnect" }), { once: true });
3420
- return;
3421
- }
3422
- const disconnectListener = () => resolveOnce({ event: "disconnect" });
3423
- const startFailListener = (error) => resolveOnce({
3424
- event: "unable_to_socket_mode_start",
3425
- error
3426
- });
3427
- const errorListener = (error) => resolveOnce({
3428
- event: "error",
3429
- error
3430
- });
3431
- const abortListener = () => resolveOnce({ event: "disconnect" });
3432
- const cleanup = () => {
3433
- emitter.off("disconnected", disconnectListener);
3434
- emitter.off("unable_to_socket_mode_start", startFailListener);
3435
- emitter.off("error", errorListener);
3436
- abortSignal?.removeEventListener("abort", abortListener);
3437
- };
3438
- const resolveOnce = (value) => {
3439
- cleanup();
3440
- resolve(value);
3441
- };
3442
- emitter.on("disconnected", disconnectListener);
3443
- emitter.on("unable_to_socket_mode_start", startFailListener);
3444
- emitter.on("error", errorListener);
3445
- abortSignal?.addEventListener("abort", abortListener, { once: true });
3446
- });
3447
- }
3448
- /**
3449
- * Detect non-recoverable Slack API / auth errors that should NOT be retried.
3450
- * These indicate permanent credential problems (revoked bot, deactivated account, etc.)
3451
- * and retrying will never succeed — continuing to retry blocks the entire gateway.
3452
- */
3453
- function isNonRecoverableSlackAuthError(error) {
3454
- const msg = error instanceof Error ? error.message : typeof error === "string" ? error : "";
3455
- return SLACK_AUTH_ERROR_RE.test(msg);
3456
- }
3457
- function formatUnknownError(error) {
3458
- if (error instanceof Error) return error.message;
3459
- if (typeof error === "string") return error;
3460
- try {
3461
- return JSON.stringify(error);
3462
- } catch {
3463
- return "unknown error";
3464
- }
3465
- }
3466
- //#endregion
3467
- //#region extensions/slack/src/monitor/external-arg-menu-store.ts
3468
- const SLACK_EXTERNAL_ARG_MENU_TOKEN_BYTES = 18;
3469
- const SLACK_EXTERNAL_ARG_MENU_TOKEN_LENGTH = Math.ceil(SLACK_EXTERNAL_ARG_MENU_TOKEN_BYTES * 8 / 6);
3470
- const SLACK_EXTERNAL_ARG_MENU_TOKEN_PATTERN = new RegExp(`^[A-Za-z0-9_-]{${SLACK_EXTERNAL_ARG_MENU_TOKEN_LENGTH}}$`);
3471
- const SLACK_EXTERNAL_ARG_MENU_TTL_MS = 600 * 1e3;
3472
- const SLACK_EXTERNAL_ARG_MENU_PREFIX = "moldclaw_cmdarg_ext:";
3473
- function pruneSlackExternalArgMenuStore(store, now) {
3474
- for (const [token, entry] of store.entries()) if (entry.expiresAt <= now) store.delete(token);
3475
- }
3476
- function createSlackExternalArgMenuToken(store) {
3477
- let token = "";
3478
- do
3479
- token = generateSecureToken(SLACK_EXTERNAL_ARG_MENU_TOKEN_BYTES);
3480
- while (store.has(token));
3481
- return token;
3482
- }
3483
- function createSlackExternalArgMenuStore() {
3484
- const store = /* @__PURE__ */ new Map();
3485
- return {
3486
- create(params, now = Date.now()) {
3487
- pruneSlackExternalArgMenuStore(store, now);
3488
- const token = createSlackExternalArgMenuToken(store);
3489
- store.set(token, {
3490
- choices: params.choices,
3491
- userId: params.userId,
3492
- expiresAt: now + SLACK_EXTERNAL_ARG_MENU_TTL_MS
3493
- });
3494
- return token;
3495
- },
3496
- readToken(raw) {
3497
- if (typeof raw !== "string" || !raw.startsWith("moldclaw_cmdarg_ext:")) return;
3498
- const token = raw.slice(20).trim();
3499
- return SLACK_EXTERNAL_ARG_MENU_TOKEN_PATTERN.test(token) ? token : void 0;
3500
- },
3501
- get(token, now = Date.now()) {
3502
- pruneSlackExternalArgMenuStore(store, now);
3503
- return store.get(token);
3504
- }
3505
- };
3506
- }
3507
- //#endregion
3508
- //#region extensions/slack/src/monitor/slash.ts
3509
- const SLACK_COMMAND_ARG_ACTION_ID = "moldclaw_cmdarg";
3510
- const SLACK_COMMAND_ARG_VALUE_PREFIX = "cmdarg";
3511
- const SLACK_COMMAND_ARG_BUTTON_ROW_SIZE = 5;
3512
- const SLACK_COMMAND_ARG_OVERFLOW_MIN = 3;
3513
- const SLACK_COMMAND_ARG_OVERFLOW_MAX = 5;
3514
- const SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX = 100;
3515
- const SLACK_COMMAND_ARG_SELECT_OPTION_VALUE_MAX = 75;
3516
- const SLACK_HEADER_TEXT_MAX = 150;
3517
- let slashCommandsRuntimePromise = null;
3518
- let slashDispatchRuntimePromise = null;
3519
- let slashSkillCommandsRuntimePromise = null;
3520
- function loadSlashCommandsRuntime() {
3521
- slashCommandsRuntimePromise ??= import("./slash-commands.runtime-DXdAT84n.js");
3522
- return slashCommandsRuntimePromise;
3523
- }
3524
- function loadSlashDispatchRuntime() {
3525
- slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-CNf2-9Aj.js");
3526
- return slashDispatchRuntimePromise;
3527
- }
3528
- function loadSlashSkillCommandsRuntime() {
3529
- slashSkillCommandsRuntimePromise ??= import("./slash-skill-commands.runtime-CBjffHRX.js");
3530
- return slashSkillCommandsRuntimePromise;
3531
- }
3532
- const slackExternalArgMenuStore = createSlackExternalArgMenuStore();
3533
- function buildSlackArgMenuConfirm(params) {
3534
- return {
3535
- title: {
3536
- type: "plain_text",
3537
- text: "Confirm selection"
3538
- },
3539
- text: {
3540
- type: "mrkdwn",
3541
- text: `Run */${escapeSlackMrkdwn(params.command)}* with *${escapeSlackMrkdwn(params.arg)}* set to this value?`
3542
- },
3543
- confirm: {
3544
- type: "plain_text",
3545
- text: "Run command"
3546
- },
3547
- deny: {
3548
- type: "plain_text",
3549
- text: "Cancel"
3550
- }
3551
- };
3552
- }
3553
- function storeSlackExternalArgMenu(params) {
3554
- return slackExternalArgMenuStore.create({
3555
- choices: params.choices,
3556
- userId: params.userId
3557
- });
3558
- }
3559
- function readSlackExternalArgMenuToken(raw) {
3560
- return slackExternalArgMenuStore.readToken(raw);
3561
- }
3562
- function encodeSlackCommandArgValue(parts) {
3563
- return [
3564
- SLACK_COMMAND_ARG_VALUE_PREFIX,
3565
- encodeURIComponent(parts.command),
3566
- encodeURIComponent(parts.arg),
3567
- encodeURIComponent(parts.value),
3568
- encodeURIComponent(parts.userId)
3569
- ].join("|");
3570
- }
3571
- function parseSlackCommandArgValue(raw) {
3572
- if (!raw) return null;
3573
- const parts = raw.split("|");
3574
- if (parts.length !== 5 || parts[0] !== SLACK_COMMAND_ARG_VALUE_PREFIX) return null;
3575
- const [, command, arg, value, userId] = parts;
3576
- if (!command || !arg || !value || !userId) return null;
3577
- const decode = (text) => {
3578
- try {
3579
- return decodeURIComponent(text);
3580
- } catch {
3581
- return null;
3582
- }
3583
- };
3584
- const decodedCommand = decode(command);
3585
- const decodedArg = decode(arg);
3586
- const decodedValue = decode(value);
3587
- const decodedUserId = decode(userId);
3588
- if (!decodedCommand || !decodedArg || !decodedValue || !decodedUserId) return null;
3589
- return {
3590
- command: decodedCommand,
3591
- arg: decodedArg,
3592
- value: decodedValue,
3593
- userId: decodedUserId
3594
- };
3595
- }
3596
- function buildSlackArgMenuOptions(choices) {
3597
- return choices.map((choice) => ({
3598
- text: {
3599
- type: "plain_text",
3600
- text: choice.label.slice(0, 75)
3601
- },
3602
- value: choice.value
3603
- }));
3604
- }
3605
- function buildSlackCommandArgMenuBlocks(params) {
3606
- const encodedChoices = params.choices.map((choice) => ({
3607
- label: choice.label,
3608
- value: encodeSlackCommandArgValue({
3609
- command: params.command,
3610
- arg: params.arg,
3611
- value: choice.value,
3612
- userId: params.userId
3613
- })
3614
- }));
3615
- const canUseStaticSelect = encodedChoices.every((choice) => choice.value.length <= SLACK_COMMAND_ARG_SELECT_OPTION_VALUE_MAX);
3616
- const canUseOverflow = canUseStaticSelect && encodedChoices.length >= SLACK_COMMAND_ARG_OVERFLOW_MIN && encodedChoices.length <= SLACK_COMMAND_ARG_OVERFLOW_MAX;
3617
- const canUseExternalSelect = params.supportsExternalSelect && canUseStaticSelect && encodedChoices.length > SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX;
3618
- const rows = canUseOverflow ? [{
3619
- type: "actions",
3620
- elements: [{
3621
- type: "overflow",
3622
- action_id: SLACK_COMMAND_ARG_ACTION_ID,
3623
- confirm: buildSlackArgMenuConfirm({
3624
- command: params.command,
3625
- arg: params.arg
3626
- }),
3627
- options: buildSlackArgMenuOptions(encodedChoices)
3628
- }]
3629
- }] : canUseExternalSelect ? [{
3630
- type: "actions",
3631
- block_id: `${SLACK_EXTERNAL_ARG_MENU_PREFIX}${params.createExternalMenuToken(encodedChoices)}`,
3632
- elements: [{
3633
- type: "external_select",
3634
- action_id: SLACK_COMMAND_ARG_ACTION_ID,
3635
- confirm: buildSlackArgMenuConfirm({
3636
- command: params.command,
3637
- arg: params.arg
3638
- }),
3639
- min_query_length: 0,
3640
- placeholder: {
3641
- type: "plain_text",
3642
- text: `Search ${params.arg}`
3643
- }
3644
- }]
3645
- }] : encodedChoices.length <= SLACK_COMMAND_ARG_BUTTON_ROW_SIZE || !canUseStaticSelect ? chunkItems(encodedChoices, SLACK_COMMAND_ARG_BUTTON_ROW_SIZE).map((choices) => ({
3646
- type: "actions",
3647
- elements: choices.map((choice) => ({
3648
- type: "button",
3649
- action_id: SLACK_COMMAND_ARG_ACTION_ID,
3650
- text: {
3651
- type: "plain_text",
3652
- text: choice.label
3653
- },
3654
- value: choice.value,
3655
- confirm: buildSlackArgMenuConfirm({
3656
- command: params.command,
3657
- arg: params.arg
3658
- })
3659
- }))
3660
- })) : chunkItems(encodedChoices, SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX).map((choices, index) => ({
3661
- type: "actions",
3662
- elements: [{
3663
- type: "static_select",
3664
- action_id: SLACK_COMMAND_ARG_ACTION_ID,
3665
- confirm: buildSlackArgMenuConfirm({
3666
- command: params.command,
3667
- arg: params.arg
3668
- }),
3669
- placeholder: {
3670
- type: "plain_text",
3671
- text: index === 0 ? `Choose ${params.arg}` : `Choose ${params.arg} (${index + 1})`
3672
- },
3673
- options: buildSlackArgMenuOptions(choices)
3674
- }]
3675
- }));
3676
- const headerText = truncateSlackText(`/${params.command}: choose ${params.arg}`, SLACK_HEADER_TEXT_MAX);
3677
- const sectionText = truncateSlackText(params.title, 3e3);
3678
- const contextText = truncateSlackText(`Select one option to continue /${params.command} (${params.arg})`, 3e3);
3679
- return [
3680
- {
3681
- type: "header",
3682
- text: {
3683
- type: "plain_text",
3684
- text: headerText
3685
- }
3686
- },
3687
- {
3688
- type: "section",
3689
- text: {
3690
- type: "mrkdwn",
3691
- text: sectionText
3692
- }
3693
- },
3694
- {
3695
- type: "context",
3696
- elements: [{
3697
- type: "mrkdwn",
3698
- text: contextText
3699
- }]
3700
- },
3701
- ...rows
3702
- ];
3703
- }
3704
- async function registerSlackMonitorSlashCommands(params) {
3705
- const { ctx, account } = params;
3706
- const cfg = ctx.cfg;
3707
- const runtime = ctx.runtime;
3708
- const supportsInteractiveArgMenus = typeof ctx.app.action === "function";
3709
- let supportsExternalArgMenus = typeof ctx.app.options === "function";
3710
- const slashCommand = resolveSlackSlashCommandConfig(ctx.slashCommand ?? account.config.slashCommand);
3711
- const handleSlashCommand = async (p) => {
3712
- const { command, ack, respond, body, prompt, commandArgs, commandDefinition } = p;
3713
- try {
3714
- if (ctx.shouldDropMismatchedSlackEvent?.(body)) {
3715
- await ack();
3716
- runtime.log?.(`slack: drop slash command from user=${command.user_id ?? "unknown"} channel=${command.channel_id ?? "unknown"} (mismatched app/team)`);
3717
- return;
3718
- }
3719
- if (!prompt.trim()) {
3720
- await ack({
3721
- text: "Message required.",
3722
- response_type: "ephemeral"
3723
- });
3724
- return;
3725
- }
3726
- await ack();
3727
- if (ctx.botUserId && command.user_id === ctx.botUserId) return;
3728
- const channelInfo = await ctx.resolveChannelName(command.channel_id);
3729
- const channelType = normalizeSlackChannelType(channelInfo?.type ?? (command.channel_name === "directmessage" ? "im" : void 0), command.channel_id);
3730
- const isDirectMessage = channelType === "im";
3731
- const isGroupDm = channelType === "mpim";
3732
- const isRoom = channelType === "channel" || channelType === "group";
3733
- const isRoomish = isRoom || isGroupDm;
3734
- if (!ctx.isChannelAllowed({
3735
- channelId: command.channel_id,
3736
- channelName: channelInfo?.name,
3737
- channelType
3738
- })) {
3739
- await respond({
3740
- text: "This channel is not allowed.",
3741
- response_type: "ephemeral"
3742
- });
3743
- return;
3744
- }
3745
- const { allowFromLower: effectiveAllowFromLower } = await resolveSlackEffectiveAllowFrom(ctx, { includePairingStore: isDirectMessage });
3746
- let commandAuthorized = false;
3747
- let channelConfig = null;
3748
- if (isDirectMessage) {
3749
- if (!await authorizeSlackDirectMessage({
3750
- ctx,
3751
- accountId: ctx.accountId,
3752
- senderId: command.user_id,
3753
- allowFromLower: effectiveAllowFromLower,
3754
- resolveSenderName: ctx.resolveUserName,
3755
- sendPairingReply: async (text) => {
3756
- await respond({
3757
- text,
3758
- response_type: "ephemeral"
3759
- });
3760
- },
3761
- onDisabled: async () => {
3762
- await respond({
3763
- text: "Slack DMs are disabled.",
3764
- response_type: "ephemeral"
3765
- });
3766
- },
3767
- onUnauthorized: async ({ allowMatchMeta }) => {
3768
- logVerbose(`slack: blocked slash sender ${command.user_id} (dmPolicy=${ctx.dmPolicy}, ${allowMatchMeta})`);
3769
- await respond({
3770
- text: "You are not authorized to use this command.",
3771
- response_type: "ephemeral"
3772
- });
3773
- },
3774
- log: logVerbose
3775
- })) return;
3776
- }
3777
- if (isRoom) {
3778
- channelConfig = resolveSlackChannelConfig({
3779
- channelId: command.channel_id,
3780
- channelName: channelInfo?.name,
3781
- channels: ctx.channelsConfig,
3782
- channelKeys: ctx.channelsConfigKeys,
3783
- defaultRequireMention: ctx.defaultRequireMention,
3784
- allowNameMatching: ctx.allowNameMatching
3785
- });
3786
- if (ctx.useAccessGroups) {
3787
- const channelAllowlistConfigured = (ctx.channelsConfigKeys?.length ?? 0) > 0;
3788
- const channelAllowed = channelConfig?.allowed !== false;
3789
- if (!isSlackChannelAllowedByPolicy({
3790
- groupPolicy: ctx.groupPolicy,
3791
- channelAllowlistConfigured,
3792
- channelAllowed
3793
- })) {
3794
- await respond({
3795
- text: "This channel is not allowed.",
3796
- response_type: "ephemeral"
3797
- });
3798
- return;
3799
- }
3800
- const hasExplicitConfig = Boolean(channelConfig?.matchSource);
3801
- if (!channelAllowed && (ctx.groupPolicy !== "open" || hasExplicitConfig)) {
3802
- await respond({
3803
- text: "This channel is not allowed.",
3804
- response_type: "ephemeral"
3805
- });
3806
- return;
3807
- }
3808
- }
3809
- }
3810
- const senderName = (await ctx.resolveUserName(command.user_id))?.name ?? command.user_name ?? command.user_id;
3811
- const channelUsersAllowlistConfigured = isRoom && Array.isArray(channelConfig?.users) && channelConfig.users.length > 0;
3812
- const channelUserAllowed = channelUsersAllowlistConfigured ? resolveSlackUserAllowed({
3813
- allowList: channelConfig?.users,
3814
- userId: command.user_id,
3815
- userName: senderName,
3816
- allowNameMatching: ctx.allowNameMatching
3817
- }) : false;
3818
- if (channelUsersAllowlistConfigured && !channelUserAllowed) {
3819
- await respond({
3820
- text: "You are not authorized to use this command here.",
3821
- response_type: "ephemeral"
3822
- });
3823
- return;
3824
- }
3825
- const ownerAllowed = resolveSlackAllowListMatch({
3826
- allowList: effectiveAllowFromLower,
3827
- id: command.user_id,
3828
- name: senderName,
3829
- allowNameMatching: ctx.allowNameMatching
3830
- }).allowed;
3831
- commandAuthorized = resolveCommandAuthorizedFromAuthorizers({
3832
- useAccessGroups: ctx.useAccessGroups,
3833
- authorizers: [{
3834
- configured: effectiveAllowFromLower.length > 0,
3835
- allowed: ownerAllowed
3836
- }],
3837
- modeWhenAccessGroupsOff: "configured"
3838
- });
3839
- if (isRoomish) {
3840
- commandAuthorized = resolveCommandAuthorizedFromAuthorizers({
3841
- useAccessGroups: ctx.useAccessGroups,
3842
- authorizers: [{
3843
- configured: effectiveAllowFromLower.length > 0,
3844
- allowed: ownerAllowed
3845
- }, {
3846
- configured: channelUsersAllowlistConfigured,
3847
- allowed: channelUserAllowed
3848
- }],
3849
- modeWhenAccessGroupsOff: "configured"
3850
- });
3851
- if (ctx.useAccessGroups && !commandAuthorized) {
3852
- await respond({
3853
- text: "You are not authorized to use this command.",
3854
- response_type: "ephemeral"
3855
- });
3856
- return;
3857
- }
3858
- }
3859
- if (commandDefinition && supportsInteractiveArgMenus) {
3860
- const { resolveCommandArgMenu } = await loadSlashCommandsRuntime();
3861
- const menu = resolveCommandArgMenu({
3862
- command: commandDefinition,
3863
- args: commandArgs,
3864
- cfg
3865
- });
3866
- if (menu) {
3867
- const commandLabel = commandDefinition.nativeName ?? commandDefinition.key;
3868
- const title = menu.title ?? `Choose ${menu.arg.description || menu.arg.name} for /${commandLabel}.`;
3869
- await respond({
3870
- text: title,
3871
- blocks: buildSlackCommandArgMenuBlocks({
3872
- title,
3873
- command: commandLabel,
3874
- arg: menu.arg.name,
3875
- choices: menu.choices,
3876
- userId: command.user_id,
3877
- supportsExternalSelect: supportsExternalArgMenus,
3878
- createExternalMenuToken: (choices) => storeSlackExternalArgMenu({
3879
- choices,
3880
- userId: command.user_id
3881
- })
3882
- }),
3883
- response_type: "ephemeral"
3884
- });
3885
- return;
3886
- }
3887
- }
3888
- const channelName = channelInfo?.name;
3889
- const roomLabel = channelName ? `#${channelName}` : `#${command.channel_id}`;
3890
- const { createReplyPrefixOptions, deliverSlackSlashReplies, dispatchReplyWithDispatcher, finalizeInboundContext, recordInboundSessionMetaSafe, resolveAgentRoute, resolveChunkMode, resolveConversationLabel, resolveMarkdownTableMode } = await loadSlashDispatchRuntime();
3891
- const route = resolveAgentRoute({
3892
- cfg,
3893
- channel: "slack",
3894
- accountId: account.accountId,
3895
- teamId: ctx.teamId || void 0,
3896
- peer: {
3897
- kind: isDirectMessage ? "direct" : isRoom ? "channel" : "group",
3898
- id: isDirectMessage ? command.user_id : command.channel_id
3899
- }
3900
- });
3901
- const { untrustedChannelMetadata, groupSystemPrompt } = resolveSlackRoomContextHints({
3902
- isRoomish,
3903
- channelInfo,
3904
- channelConfig
3905
- });
3906
- const { sessionKey, commandTargetSessionKey } = resolveNativeCommandSessionTargets({
3907
- agentId: route.agentId,
3908
- sessionPrefix: slashCommand.sessionPrefix,
3909
- userId: command.user_id,
3910
- targetSessionKey: route.sessionKey,
3911
- lowercaseSessionKey: true
3912
- });
3913
- const ctxPayload = finalizeInboundContext({
3914
- Body: prompt,
3915
- BodyForAgent: prompt,
3916
- RawBody: prompt,
3917
- CommandBody: prompt,
3918
- CommandArgs: commandArgs,
3919
- From: isDirectMessage ? `slack:${command.user_id}` : isRoom ? `slack:channel:${command.channel_id}` : `slack:group:${command.channel_id}`,
3920
- To: `slash:${command.user_id}`,
3921
- ChatType: isDirectMessage ? "direct" : "channel",
3922
- ConversationLabel: resolveConversationLabel({
3923
- ChatType: isDirectMessage ? "direct" : "channel",
3924
- SenderName: senderName,
3925
- GroupSubject: isRoomish ? roomLabel : void 0,
3926
- From: isDirectMessage ? `slack:${command.user_id}` : isRoom ? `slack:channel:${command.channel_id}` : `slack:group:${command.channel_id}`
3927
- }) ?? (isDirectMessage ? senderName : roomLabel),
3928
- GroupSubject: isRoomish ? roomLabel : void 0,
3929
- GroupSystemPrompt: isRoomish ? groupSystemPrompt : void 0,
3930
- UntrustedContext: untrustedChannelMetadata ? [untrustedChannelMetadata] : void 0,
3931
- SenderName: senderName,
3932
- SenderId: command.user_id,
3933
- Provider: "slack",
3934
- Surface: "slack",
3935
- WasMentioned: true,
3936
- MessageSid: command.trigger_id,
3937
- Timestamp: Date.now(),
3938
- SessionKey: sessionKey,
3939
- CommandTargetSessionKey: commandTargetSessionKey,
3940
- AccountId: route.accountId,
3941
- CommandSource: "native",
3942
- CommandAuthorized: commandAuthorized,
3943
- OriginatingChannel: "slack",
3944
- OriginatingTo: `user:${command.user_id}`
3945
- });
3946
- await recordInboundSessionMetaSafe({
3947
- cfg,
3948
- agentId: route.agentId,
3949
- sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
3950
- ctx: ctxPayload,
3951
- onError: (err) => runtime.error?.(danger(`slack slash: failed updating session meta: ${String(err)}`))
3952
- });
3953
- const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
3954
- cfg,
3955
- agentId: route.agentId,
3956
- channel: "slack",
3957
- accountId: route.accountId
3958
- });
3959
- const deliverSlashPayloads = async (replies) => {
3960
- await deliverSlackSlashReplies({
3961
- replies,
3962
- respond,
3963
- ephemeral: slashCommand.ephemeral,
3964
- textLimit: ctx.textLimit,
3965
- chunkMode: resolveChunkMode(cfg, "slack", route.accountId),
3966
- tableMode: resolveMarkdownTableMode({
3967
- cfg,
3968
- channel: "slack",
3969
- accountId: route.accountId
3970
- })
3971
- });
3972
- };
3973
- const { counts } = await dispatchReplyWithDispatcher({
3974
- ctx: ctxPayload,
3975
- cfg,
3976
- dispatcherOptions: {
3977
- ...prefixOptions,
3978
- deliver: async (payload) => deliverSlashPayloads([payload]),
3979
- onError: (err, info) => {
3980
- runtime.error?.(danger(`slack slash ${info.kind} reply failed: ${String(err)}`));
3981
- }
3982
- },
3983
- replyOptions: {
3984
- skillFilter: channelConfig?.skills,
3985
- onModelSelected
3986
- }
3987
- });
3988
- if (counts.final + counts.tool + counts.block === 0) await deliverSlashPayloads([]);
3989
- } catch (err) {
3990
- runtime.error?.(danger(`slack slash handler failed: ${String(err)}`));
3991
- await respond({
3992
- text: "Sorry, something went wrong handling that command.",
3993
- response_type: "ephemeral"
3994
- });
3995
- }
3996
- };
3997
- const nativeEnabled = resolveNativeCommandsEnabled({
3998
- providerId: "slack",
3999
- providerSetting: account.config.commands?.native,
4000
- globalSetting: cfg.commands?.native
4001
- });
4002
- const nativeSkillsEnabled = resolveNativeSkillsEnabled({
4003
- providerId: "slack",
4004
- providerSetting: account.config.commands?.nativeSkills,
4005
- globalSetting: cfg.commands?.nativeSkills
4006
- });
4007
- let nativeCommands = [];
4008
- let slashCommandsRuntime = null;
4009
- if (nativeEnabled) {
4010
- slashCommandsRuntime = await loadSlashCommandsRuntime();
4011
- const skillCommands = nativeSkillsEnabled ? (await loadSlashSkillCommandsRuntime()).listSkillCommandsForAgents({ cfg }) : [];
4012
- nativeCommands = slashCommandsRuntime.listNativeCommandSpecsForConfig(cfg, {
4013
- skillCommands,
4014
- provider: "slack"
4015
- });
4016
- }
4017
- if (nativeCommands.length > 0) {
4018
- if (!slashCommandsRuntime) throw new Error("Missing commands runtime for native Slack commands.");
4019
- for (const command of nativeCommands) ctx.app.command(`/${command.name}`, async ({ command: cmd, ack, respond, body }) => {
4020
- const commandDefinition = slashCommandsRuntime.findCommandByNativeName(command.name, "slack");
4021
- const rawText = cmd.text?.trim() ?? "";
4022
- const commandArgs = commandDefinition ? slashCommandsRuntime.parseCommandArgs(commandDefinition, rawText) : rawText ? { raw: rawText } : void 0;
4023
- await handleSlashCommand({
4024
- command: cmd,
4025
- ack,
4026
- respond,
4027
- body,
4028
- prompt: commandDefinition ? slashCommandsRuntime.buildCommandTextFromArgs(commandDefinition, commandArgs) : rawText ? `/${command.name} ${rawText}` : `/${command.name}`,
4029
- commandArgs,
4030
- commandDefinition: commandDefinition ?? void 0
4031
- });
4032
- });
4033
- } else if (slashCommand.enabled) ctx.app.command(buildSlackSlashCommandMatcher(slashCommand.name), async ({ command, ack, respond, body }) => {
4034
- await handleSlashCommand({
4035
- command,
4036
- ack,
4037
- respond,
4038
- body,
4039
- prompt: command.text?.trim() ?? ""
4040
- });
4041
- });
4042
- else logVerbose("slack: slash commands disabled");
4043
- if (nativeCommands.length === 0 || !supportsInteractiveArgMenus) return;
4044
- const registerArgOptions = () => {
4045
- const appWithOptions = ctx.app;
4046
- if (typeof appWithOptions.options !== "function") return;
4047
- appWithOptions.options(SLACK_COMMAND_ARG_ACTION_ID, async ({ ack, body }) => {
4048
- if (ctx.shouldDropMismatchedSlackEvent?.(body)) {
4049
- await ack({ options: [] });
4050
- runtime.log?.("slack: drop slash arg options payload (mismatched app/team)");
4051
- return;
4052
- }
4053
- const typedBody = body;
4054
- const token = readSlackExternalArgMenuToken(typedBody.actions?.[0]?.block_id ?? typedBody.block_id);
4055
- if (!token) {
4056
- await ack({ options: [] });
4057
- return;
4058
- }
4059
- const entry = slackExternalArgMenuStore.get(token);
4060
- if (!entry) {
4061
- await ack({ options: [] });
4062
- return;
4063
- }
4064
- const requesterUserId = typedBody.user?.id?.trim();
4065
- if (!requesterUserId || requesterUserId !== entry.userId) {
4066
- await ack({ options: [] });
4067
- return;
4068
- }
4069
- const query = typedBody.value?.trim().toLowerCase() ?? "";
4070
- await ack({ options: entry.choices.filter((choice) => !query || choice.label.toLowerCase().includes(query)).slice(0, SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX).map((choice) => ({
4071
- text: {
4072
- type: "plain_text",
4073
- text: choice.label.slice(0, 75)
4074
- },
4075
- value: choice.value
4076
- })) });
4077
- });
4078
- };
4079
- try {
4080
- registerArgOptions();
4081
- } catch (err) {
4082
- supportsExternalArgMenus = false;
4083
- logVerbose(`slack: external arg-menu registration failed, falling back to static menus: ${String(err)}`);
4084
- }
4085
- const registerArgAction = (actionId) => {
4086
- ctx.app.action(actionId, async (args) => {
4087
- const { ack, body, respond } = args;
4088
- const action = args.action;
4089
- await ack();
4090
- if (ctx.shouldDropMismatchedSlackEvent?.(body)) {
4091
- runtime.log?.("slack: drop slash arg action payload (mismatched app/team)");
4092
- return;
4093
- }
4094
- const respondFn = respond ?? (async (payload) => {
4095
- if (!body.channel?.id || !body.user?.id) return;
4096
- await ctx.app.client.chat.postEphemeral({
4097
- token: ctx.botToken,
4098
- channel: body.channel.id,
4099
- user: body.user.id,
4100
- text: payload.text,
4101
- blocks: payload.blocks
4102
- });
4103
- });
4104
- const parsed = parseSlackCommandArgValue(action?.value ?? action?.selected_option?.value);
4105
- if (!parsed) {
4106
- await respondFn({
4107
- text: "Sorry, that button is no longer valid.",
4108
- response_type: "ephemeral"
4109
- });
4110
- return;
4111
- }
4112
- if (body.user?.id && parsed.userId !== body.user.id) {
4113
- await respondFn({
4114
- text: "That menu is for another user.",
4115
- response_type: "ephemeral"
4116
- });
4117
- return;
4118
- }
4119
- const { buildCommandTextFromArgs, findCommandByNativeName } = await loadSlashCommandsRuntime();
4120
- const commandDefinition = findCommandByNativeName(parsed.command, "slack");
4121
- const commandArgs = { values: { [parsed.arg]: parsed.value } };
4122
- const prompt = commandDefinition ? buildCommandTextFromArgs(commandDefinition, commandArgs) : `/${parsed.command} ${parsed.value}`;
4123
- const user = body.user;
4124
- const userName = user && "name" in user && user.name ? user.name : user && "username" in user && user.username ? user.username : user?.id ?? "";
4125
- const triggerId = "trigger_id" in body ? body.trigger_id : void 0;
4126
- await handleSlashCommand({
4127
- command: {
4128
- user_id: user?.id ?? "",
4129
- user_name: userName,
4130
- channel_id: body.channel?.id ?? "",
4131
- channel_name: body.channel?.name ?? body.channel?.id ?? "",
4132
- trigger_id: triggerId
4133
- },
4134
- ack: async () => {},
4135
- respond: respondFn,
4136
- body,
4137
- prompt,
4138
- commandArgs,
4139
- commandDefinition: commandDefinition ?? void 0
4140
- });
4141
- });
4142
- };
4143
- registerArgAction(SLACK_COMMAND_ARG_ACTION_ID);
4144
- }
4145
- //#endregion
4146
- //#region extensions/slack/src/monitor/provider.ts
4147
- function isConstructorFunction(value) {
4148
- return typeof value === "function";
4149
- }
4150
- function resolveSlackBoltModule(value) {
4151
- if (!value || typeof value !== "object") return null;
4152
- const app = Reflect.get(value, "App");
4153
- const httpReceiver = Reflect.get(value, "HTTPReceiver");
4154
- if (!isConstructorFunction(app) || !isConstructorFunction(httpReceiver)) return null;
4155
- return {
4156
- App: app,
4157
- HTTPReceiver: httpReceiver
4158
- };
4159
- }
4160
- function resolveSlackBoltInterop(params) {
4161
- const { defaultImport, namespaceImport } = params;
4162
- const nestedDefault = defaultImport && typeof defaultImport === "object" ? Reflect.get(defaultImport, "default") : void 0;
4163
- const namespaceDefault = namespaceImport && typeof namespaceImport === "object" ? Reflect.get(namespaceImport, "default") : void 0;
4164
- const namespaceReceiver = namespaceImport && typeof namespaceImport === "object" ? Reflect.get(namespaceImport, "HTTPReceiver") : void 0;
4165
- const directModule = resolveSlackBoltModule(defaultImport) ?? resolveSlackBoltModule(nestedDefault) ?? resolveSlackBoltModule(namespaceDefault) ?? resolveSlackBoltModule(namespaceImport);
4166
- if (directModule) return directModule;
4167
- if (isConstructorFunction(defaultImport) && isConstructorFunction(namespaceReceiver)) return {
4168
- App: defaultImport,
4169
- HTTPReceiver: namespaceReceiver
4170
- };
4171
- throw new TypeError("Unable to resolve @slack/bolt App/HTTPReceiver exports");
4172
- }
4173
- const { App, HTTPReceiver } = resolveSlackBoltInterop({
4174
- defaultImport: SlackBolt,
4175
- namespaceImport: SlackBoltNamespace
4176
- });
4177
- const SLACK_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
4178
- const SLACK_WEBHOOK_BODY_TIMEOUT_MS = 3e4;
4179
- function parseApiAppIdFromAppToken(raw) {
4180
- const token = raw?.trim();
4181
- if (!token) return;
4182
- return /^xapp-\d-([a-z0-9]+)-/i.exec(token)?.[1]?.toUpperCase();
4183
- }
4184
- function publishSlackConnectedStatus(setStatus) {
4185
- if (!setStatus) return;
4186
- setStatus({
4187
- ...createConnectedChannelStatusPatch(Date.now()),
4188
- lastError: null
4189
- });
4190
- }
4191
- function publishSlackDisconnectedStatus(setStatus, error) {
4192
- if (!setStatus) return;
4193
- const at = Date.now();
4194
- const message = error ? formatUnknownError(error) : void 0;
4195
- setStatus({
4196
- connected: false,
4197
- lastDisconnect: message ? {
4198
- at,
4199
- error: message
4200
- } : { at },
4201
- lastError: message ?? null
4202
- });
4203
- }
4204
- async function monitorSlackProvider(opts = {}) {
4205
- const cfg = opts.config ?? loadConfig();
4206
- const runtime = opts.runtime ?? createNonExitingRuntime();
4207
- let account = resolveSlackAccount({
4208
- cfg,
4209
- accountId: opts.accountId
4210
- });
4211
- if (!account.enabled) {
4212
- runtime.log?.(`[${account.accountId}] slack account disabled; monitor startup skipped`);
4213
- if (opts.abortSignal?.aborted) return;
4214
- await new Promise((resolve) => {
4215
- opts.abortSignal?.addEventListener("abort", () => resolve(), { once: true });
4216
- });
4217
- return;
4218
- }
4219
- const historyLimit = Math.max(0, account.config.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? 50);
4220
- const sessionCfg = cfg.session;
4221
- const sessionScope = sessionCfg?.scope ?? "per-sender";
4222
- const mainKey = normalizeMainKey(sessionCfg?.mainKey);
4223
- const slackMode = opts.mode ?? account.config.mode ?? "socket";
4224
- const slackWebhookPath = normalizeSlackWebhookPath(account.config.webhookPath);
4225
- const signingSecret = normalizeResolvedSecretInputString({
4226
- value: account.config.signingSecret,
4227
- path: `channels.slack.accounts.${account.accountId}.signingSecret`
4228
- });
4229
- const botToken = resolveSlackBotToken(opts.botToken ?? account.botToken);
4230
- const appToken = resolveSlackAppToken(opts.appToken ?? account.appToken);
4231
- if (!botToken || slackMode !== "http" && !appToken) {
4232
- const missing = slackMode === "http" ? `Slack bot token missing for account "${account.accountId}" (set channels.slack.accounts.${account.accountId}.botToken or SLACK_BOT_TOKEN for default).` : `Slack bot + app tokens missing for account "${account.accountId}" (set channels.slack.accounts.${account.accountId}.botToken/appToken or SLACK_BOT_TOKEN/SLACK_APP_TOKEN for default).`;
4233
- throw new Error(missing);
4234
- }
4235
- if (slackMode === "http" && !signingSecret) throw new Error(`Slack signing secret missing for account "${account.accountId}" (set channels.slack.signingSecret or channels.slack.accounts.${account.accountId}.signingSecret).`);
4236
- const slackCfg = account.config;
4237
- const dmConfig = slackCfg.dm;
4238
- const dmEnabled = dmConfig?.enabled ?? true;
4239
- const dmPolicy = slackCfg.dmPolicy ?? dmConfig?.policy ?? "pairing";
4240
- let allowFrom = slackCfg.allowFrom ?? dmConfig?.allowFrom;
4241
- const groupDmEnabled = dmConfig?.groupEnabled ?? false;
4242
- const groupDmChannels = dmConfig?.groupChannels;
4243
- let channelsConfig = slackCfg.channels;
4244
- const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
4245
- const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy({
4246
- providerConfigPresent: cfg.channels?.slack !== void 0,
4247
- groupPolicy: slackCfg.groupPolicy,
4248
- defaultGroupPolicy
4249
- });
4250
- warnMissingProviderGroupPolicyFallbackOnce({
4251
- providerMissingFallbackApplied,
4252
- providerKey: "slack",
4253
- accountId: account.accountId,
4254
- log: (message) => runtime.log?.(warn(message))
4255
- });
4256
- const resolveToken = account.userToken || botToken;
4257
- const useAccessGroups = cfg.commands?.useAccessGroups !== false;
4258
- const reactionMode = slackCfg.reactionNotifications ?? "own";
4259
- const reactionAllowlist = slackCfg.reactionAllowlist ?? [];
4260
- const replyToMode = slackCfg.replyToMode ?? "off";
4261
- const threadHistoryScope = slackCfg.thread?.historyScope ?? "thread";
4262
- const threadInheritParent = slackCfg.thread?.inheritParent ?? false;
4263
- const slashCommand = resolveSlackSlashCommandConfig(opts.slashCommand ?? slackCfg.slashCommand);
4264
- const textLimit = resolveTextChunkLimit(cfg, "slack", account.accountId);
4265
- const ackReactionScope = cfg.messages?.ackReactionScope ?? "group-mentions";
4266
- const typingReaction = slackCfg.typingReaction?.trim() ?? "";
4267
- const mediaMaxBytes = (opts.mediaMaxMb ?? slackCfg.mediaMaxMb ?? 20) * 1024 * 1024;
4268
- const removeAckAfterReply = cfg.messages?.removeAckAfterReply ?? false;
4269
- const receiver = slackMode === "http" ? new HTTPReceiver({
4270
- signingSecret: signingSecret ?? "",
4271
- endpoints: slackWebhookPath
4272
- }) : null;
4273
- const clientOptions = resolveSlackWebClientOptions();
4274
- const app = new App(slackMode === "socket" ? {
4275
- token: botToken,
4276
- appToken,
4277
- socketMode: true,
4278
- clientOptions
4279
- } : {
4280
- token: botToken,
4281
- receiver: receiver ?? void 0,
4282
- clientOptions
4283
- });
4284
- const slackHttpHandler = slackMode === "http" && receiver ? async (req, res) => {
4285
- const guard = installRequestBodyLimitGuard(req, res, {
4286
- maxBytes: SLACK_WEBHOOK_MAX_BODY_BYTES,
4287
- timeoutMs: SLACK_WEBHOOK_BODY_TIMEOUT_MS,
4288
- responseFormat: "text"
4289
- });
4290
- if (guard.isTripped()) return;
4291
- try {
4292
- await Promise.resolve(receiver.requestListener(req, res));
4293
- } catch (err) {
4294
- if (!guard.isTripped()) throw err;
4295
- } finally {
4296
- guard.dispose();
4297
- }
4298
- } : null;
4299
- let unregisterHttpHandler = null;
4300
- let botUserId = "";
4301
- let teamId = "";
4302
- let apiAppId = "";
4303
- const expectedApiAppIdFromAppToken = parseApiAppIdFromAppToken(appToken);
4304
- try {
4305
- const auth = await app.client.auth.test({ token: botToken });
4306
- botUserId = auth.user_id ?? "";
4307
- teamId = auth.team_id ?? "";
4308
- apiAppId = auth.api_app_id ?? "";
4309
- } catch {}
4310
- if (apiAppId && expectedApiAppIdFromAppToken && apiAppId !== expectedApiAppIdFromAppToken) runtime.error?.(`slack token mismatch: bot token api_app_id=${apiAppId} but app token looks like api_app_id=${expectedApiAppIdFromAppToken}`);
4311
- const ctx = createSlackMonitorContext({
4312
- cfg,
4313
- accountId: account.accountId,
4314
- botToken,
4315
- app,
4316
- runtime,
4317
- botUserId,
4318
- teamId,
4319
- apiAppId,
4320
- historyLimit,
4321
- sessionScope,
4322
- mainKey,
4323
- dmEnabled,
4324
- dmPolicy,
4325
- allowFrom,
4326
- allowNameMatching: isDangerousNameMatchingEnabled(slackCfg),
4327
- groupDmEnabled,
4328
- groupDmChannels,
4329
- defaultRequireMention: slackCfg.requireMention,
4330
- channelsConfig,
4331
- groupPolicy,
4332
- useAccessGroups,
4333
- reactionMode,
4334
- reactionAllowlist,
4335
- replyToMode,
4336
- threadHistoryScope,
4337
- threadInheritParent,
4338
- slashCommand,
4339
- textLimit,
4340
- ackReactionScope,
4341
- typingReaction,
4342
- mediaMaxBytes,
4343
- removeAckAfterReply
4344
- });
4345
- const trackEvent = opts.setStatus ? () => {
4346
- opts.setStatus({
4347
- lastEventAt: Date.now(),
4348
- lastInboundAt: Date.now()
4349
- });
4350
- } : void 0;
4351
- registerSlackMonitorEvents({
4352
- ctx,
4353
- account,
4354
- handleSlackMessage: createSlackMessageHandler({
4355
- ctx,
4356
- account,
4357
- trackEvent
4358
- }),
4359
- trackEvent
4360
- });
4361
- await registerSlackMonitorSlashCommands({
4362
- ctx,
4363
- account
4364
- });
4365
- if (slackMode === "http" && slackHttpHandler) unregisterHttpHandler = registerSlackHttpHandler({
4366
- path: slackWebhookPath,
4367
- handler: slackHttpHandler,
4368
- log: runtime.log,
4369
- accountId: account.accountId
4370
- });
4371
- if (resolveToken) (async () => {
4372
- if (opts.abortSignal?.aborted) return;
4373
- if (channelsConfig && Object.keys(channelsConfig).length > 0) try {
4374
- const entries = Object.keys(channelsConfig).filter((key) => key !== "*");
4375
- if (entries.length > 0) {
4376
- const resolved = await resolveSlackChannelAllowlist({
4377
- token: resolveToken,
4378
- entries
4379
- });
4380
- const nextChannels = { ...channelsConfig };
4381
- const mapping = [];
4382
- const unresolved = [];
4383
- for (const entry of resolved) {
4384
- const source = channelsConfig?.[entry.input];
4385
- if (!source) continue;
4386
- if (!entry.resolved || !entry.id) {
4387
- unresolved.push(entry.input);
4388
- continue;
4389
- }
4390
- mapping.push(`${entry.input}→${entry.id}${entry.archived ? " (archived)" : ""}`);
4391
- const existing = nextChannels[entry.id] ?? {};
4392
- nextChannels[entry.id] = {
4393
- ...source,
4394
- ...existing
4395
- };
4396
- }
4397
- channelsConfig = nextChannels;
4398
- ctx.channelsConfig = nextChannels;
4399
- summarizeMapping("slack channels", mapping, unresolved, runtime);
4400
- }
4401
- } catch (err) {
4402
- runtime.log?.(`slack channel resolve failed; using config entries. ${String(err)}`);
4403
- }
4404
- const allowEntries = normalizeStringEntries(allowFrom).filter((entry) => entry !== "*");
4405
- if (allowEntries.length > 0) try {
4406
- const { mapping, unresolved, additions } = buildAllowlistResolutionSummary(await resolveSlackUserAllowlist({
4407
- token: resolveToken,
4408
- entries: allowEntries
4409
- }), { formatResolved: (entry) => {
4410
- const note = entry.note ? ` (${entry.note})` : "";
4411
- return `${entry.input}→${entry.id}${note}`;
4412
- } });
4413
- allowFrom = mergeAllowlist({
4414
- existing: allowFrom,
4415
- additions
4416
- });
4417
- ctx.allowFrom = normalizeAllowList(allowFrom);
4418
- summarizeMapping("slack users", mapping, unresolved, runtime);
4419
- } catch (err) {
4420
- runtime.log?.(`slack user resolve failed; using config entries. ${String(err)}`);
4421
- }
4422
- if (channelsConfig && Object.keys(channelsConfig).length > 0) {
4423
- const userEntries = /* @__PURE__ */ new Set();
4424
- for (const channel of Object.values(channelsConfig)) addAllowlistUserEntriesFromConfigEntry(userEntries, channel);
4425
- if (userEntries.size > 0) try {
4426
- const { resolvedMap, mapping, unresolved } = buildAllowlistResolutionSummary(await resolveSlackUserAllowlist({
4427
- token: resolveToken,
4428
- entries: Array.from(userEntries)
4429
- }));
4430
- const nextChannels = patchAllowlistUsersInConfigEntries({
4431
- entries: channelsConfig,
4432
- resolvedMap
4433
- });
4434
- channelsConfig = nextChannels;
4435
- ctx.channelsConfig = nextChannels;
4436
- summarizeMapping("slack channel users", mapping, unresolved, runtime);
4437
- } catch (err) {
4438
- runtime.log?.(`slack channel user resolve failed; using config entries. ${String(err)}`);
4439
- }
4440
- }
4441
- })();
4442
- const stopOnAbort = () => {
4443
- if (opts.abortSignal?.aborted && slackMode === "socket") app.stop();
4444
- };
4445
- opts.abortSignal?.addEventListener("abort", stopOnAbort, { once: true });
4446
- try {
4447
- if (slackMode === "socket") {
4448
- let reconnectAttempts = 0;
4449
- while (!opts.abortSignal?.aborted) {
4450
- try {
4451
- await app.start();
4452
- reconnectAttempts = 0;
4453
- publishSlackConnectedStatus(opts.setStatus);
4454
- runtime.log?.("slack socket mode connected");
4455
- } catch (err) {
4456
- if (isNonRecoverableSlackAuthError(err)) {
4457
- runtime.error?.(`slack socket mode failed to start due to non-recoverable auth error — skipping channel (${formatUnknownError(err)})`);
4458
- throw err;
4459
- }
4460
- reconnectAttempts += 1;
4461
- if (SLACK_SOCKET_RECONNECT_POLICY.maxAttempts > 0 && reconnectAttempts >= SLACK_SOCKET_RECONNECT_POLICY.maxAttempts) throw err;
4462
- const delayMs = computeBackoff(SLACK_SOCKET_RECONNECT_POLICY, reconnectAttempts);
4463
- runtime.error?.(`slack socket mode failed to start. retry ${reconnectAttempts}/${SLACK_SOCKET_RECONNECT_POLICY.maxAttempts || "∞"} in ${Math.round(delayMs / 1e3)}s (${formatUnknownError(err)})`);
4464
- try {
4465
- await sleepWithAbort(delayMs, opts.abortSignal);
4466
- } catch {
4467
- break;
4468
- }
4469
- continue;
4470
- }
4471
- if (opts.abortSignal?.aborted) break;
4472
- const disconnect = await waitForSlackSocketDisconnect(app, opts.abortSignal);
4473
- if (opts.abortSignal?.aborted) break;
4474
- publishSlackDisconnectedStatus(opts.setStatus, disconnect.error);
4475
- if (disconnect.error && isNonRecoverableSlackAuthError(disconnect.error)) {
4476
- runtime.error?.(`slack socket mode disconnected due to non-recoverable auth error — skipping channel (${formatUnknownError(disconnect.error)})`);
4477
- throw disconnect.error instanceof Error ? disconnect.error : new Error(formatUnknownError(disconnect.error));
4478
- }
4479
- reconnectAttempts += 1;
4480
- if (SLACK_SOCKET_RECONNECT_POLICY.maxAttempts > 0 && reconnectAttempts >= SLACK_SOCKET_RECONNECT_POLICY.maxAttempts) throw new Error(`Slack socket mode reconnect max attempts reached (${reconnectAttempts}/${SLACK_SOCKET_RECONNECT_POLICY.maxAttempts}) after ${disconnect.event}`);
4481
- const delayMs = computeBackoff(SLACK_SOCKET_RECONNECT_POLICY, reconnectAttempts);
4482
- runtime.error?.(`slack socket disconnected (${disconnect.event}). retry ${reconnectAttempts}/${SLACK_SOCKET_RECONNECT_POLICY.maxAttempts || "∞"} in ${Math.round(delayMs / 1e3)}s${disconnect.error ? ` (${formatUnknownError(disconnect.error)})` : ""}`);
4483
- await app.stop().catch(() => void 0);
4484
- try {
4485
- await sleepWithAbort(delayMs, opts.abortSignal);
4486
- } catch {
4487
- break;
4488
- }
4489
- }
4490
- } else {
4491
- runtime.log?.(`slack http mode listening at ${slackWebhookPath}`);
4492
- if (!opts.abortSignal?.aborted) await new Promise((resolve) => {
4493
- opts.abortSignal?.addEventListener("abort", () => resolve(), { once: true });
4494
- });
4495
- }
4496
- } finally {
4497
- opts.abortSignal?.removeEventListener("abort", stopOnAbort);
4498
- unregisterHttpHandler?.();
4499
- await app.stop().catch(() => void 0);
4500
- }
4501
- }
4502
- //#endregion
4503
- //#region extensions/slack/src/probe.ts
4504
- async function probeSlack(token, timeoutMs = 2500) {
4505
- const client = createSlackWebClient(token);
4506
- const start = Date.now();
4507
- try {
4508
- const result = await withTimeout(client.auth.test(), timeoutMs);
4509
- if (!result.ok) return {
4510
- ok: false,
4511
- status: 200,
4512
- error: result.error ?? "unknown",
4513
- elapsedMs: Date.now() - start
4514
- };
4515
- return {
4516
- ok: true,
4517
- status: 200,
4518
- elapsedMs: Date.now() - start,
4519
- bot: {
4520
- id: result.user_id,
4521
- name: result.user
4522
- },
4523
- team: {
4524
- id: result.team_id,
4525
- name: result.team
4526
- }
4527
- };
4528
- } catch (err) {
4529
- const message = err instanceof Error ? err.message : String(err);
4530
- return {
4531
- ok: false,
4532
- status: typeof err.status === "number" ? err.status : null,
4533
- error: message,
4534
- elapsedMs: Date.now() - start
4535
- };
4536
- }
4537
- }
4538
- //#endregion
4539
- //#region src/plugins/runtime/runtime-slack-ops.runtime.ts
4540
- const runtimeSlackOps = {
4541
- listDirectoryGroupsLive: listSlackDirectoryGroupsLive,
4542
- listDirectoryPeersLive: listSlackDirectoryPeersLive,
4543
- probeSlack,
4544
- resolveChannelAllowlist: resolveSlackChannelAllowlist,
4545
- resolveUserAllowlist: resolveSlackUserAllowlist,
4546
- sendMessageSlack,
4547
- monitorSlackProvider,
4548
- handleSlackAction
4549
- };
4550
- //#endregion
4551
- export { runtimeSlackOps };