@spacebar_ai/moldclaw-core 2026.3.43 → 2026.3.44
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.
- package/dist/accounts-5qY-dKca.d.ts +103 -0
- package/dist/accounts-SqdHz2ZP.js +114 -0
- package/dist/acp-cli-E6bcNqiE.js +2093 -0
- package/dist/actions.runtime-BU_XMuLk.js +119 -0
- package/dist/actions.runtime-CY5h8lqH.js +133 -0
- package/dist/agent-scope-lZlwP1At.js +208 -0
- package/dist/agents-C4SkadR1.js +853 -0
- package/dist/agents-RfwqGCzE.js +222 -0
- package/dist/agents.config-CX9CPNfP.js +17 -0
- package/dist/agents.config-DF9Zwn9n.js +121 -0
- package/dist/allow-list-3WSjz1zl.js +81 -0
- package/dist/allowlist-DNbDjFjw.js +142 -0
- package/dist/api-BEOpJ7dR.js +117 -0
- package/dist/audit-CpJz_eu6.js +787 -0
- package/dist/audit-CpfSjvyo.js +54 -0
- package/dist/audit-channel.collect.runtime-BeGotloZ.js +605 -0
- package/dist/audit-channel.runtime-BJDZ7ETt.js +121 -0
- package/dist/audit-extra.async-C2G0mqmk.js +813 -0
- package/dist/audit-membership-runtime-B1FqJsPV.js +162 -0
- package/dist/audit.deep.runtime-DyL9O_sU.js +25 -0
- package/dist/audit.nondeep.runtime-C6jFgJfH.js +832 -0
- package/dist/audit.runtime-Dnlsn23e.js +118 -0
- package/dist/auth-Ch3Rchm4.js +101 -0
- package/dist/auth-choice-CEFSlnLT.js +122 -0
- package/dist/auth-choice-CVCef-eU.js +268 -0
- package/dist/auth-choice-Cez-pXrg.js +507 -0
- package/dist/auth-choice-options-DO78mvPe.js +123 -0
- package/dist/auth-choice-prompt-CUkC7Mmb.js +36 -0
- package/dist/auth-choice-prompt-DCuQRiVl.js +115 -0
- package/dist/auth-choice.apply-helpers-BhbNIV8X.js +66 -0
- package/dist/auth-choice.plugin-providers.runtime-4BhqvEw_.js +119 -0
- package/dist/auth-profiles-smABVXzp.js +128040 -0
- package/dist/auth-profiles.runtime-Cr-ojtTc.js +116 -0
- package/dist/banner-CojBHPWr.js +342 -0
- package/dist/bluebubbles-BnLsj2Fy.d.ts +6 -0
- package/dist/bluebubbles-CVk7M3Bl.js +64 -0
- package/dist/bot-DdyrB2z9.d.ts +478 -0
- package/dist/brave-w4Fo8WZ3.js +24 -0
- package/dist/browser-cli-DWFs3P_i.js +1494 -0
- package/dist/build-info.json +3 -3
- package/dist/bundled/boot-md/handler.d.ts +1 -1
- package/dist/bundled/boot-md/handler.js +35 -35
- package/dist/bundled/bootstrap-extra-files/handler.d.ts +1 -1
- package/dist/bundled/bootstrap-extra-files/handler.js +1 -1
- package/dist/bundled/command-logger/handler.d.ts +1 -1
- package/dist/bundled/session-memory/handler.d.ts +1 -1
- package/dist/bundled/session-memory/handler.js +36 -36
- package/dist/call-Do7wTSr7.js +39 -0
- package/dist/call-gdDAt07d.js +640 -0
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/channel-B26pkce0.js +214 -0
- package/dist/channel-BJHp0AQC.js +352 -0
- package/dist/channel-BKFOv51P.js +4681 -0
- package/dist/channel-BNgpOY8v.js +538 -0
- package/dist/channel-BcQAAo2P.js +226 -0
- package/dist/channel-BvNdnhbx.js +1598 -0
- package/dist/channel-C1Rda3Jd.js +306 -0
- package/dist/channel-C87DG-F7.js +803 -0
- package/dist/channel-CIip0kvZ.js +619 -0
- package/dist/channel-CTPxoT_E2.js +316 -0
- package/dist/channel-CklaCzUG.js +562 -0
- package/dist/channel-CoJnAdLs.js +920 -0
- package/dist/channel-D3tafL1_.js +949 -0
- package/dist/channel-DFMrP2uu.js +542 -0
- package/dist/channel-DMd5cJQe.js +397 -0
- package/dist/channel-Dm34kxAJ.js +207 -0
- package/dist/channel-DmwF9udn.js +1321 -0
- package/dist/channel-account-context-Bjur9nlh.js +103 -0
- package/dist/channel-bGnST659.js +943 -0
- package/dist/channel-hIgbkTZf.js +575 -0
- package/dist/channel-m_TGrDKo.js +497 -0
- package/dist/channel-options-DoUPBMa8.js +50 -0
- package/dist/channel-plugin-ids-TZIY4hFs.js +26 -0
- package/dist/channel-summary-qD54bOBO.js +111 -0
- package/dist/channel.runtime-B0H04Dkk.js +199 -0
- package/dist/channel.runtime-BU1f3NkV.js +418 -0
- package/dist/channel.runtime-Bj1sfLep.js +4011 -0
- package/dist/channel.runtime-BtPAAJc3.js +870 -0
- package/dist/channel.runtime-Bx-10m_j.js +171 -0
- package/dist/channel.runtime-CI_TBywQ.js +179 -0
- package/dist/channel.runtime-CSLj14-Z.js +182 -0
- package/dist/channel.runtime-D-lTSYAd.js +404 -0
- package/dist/channel.runtime-DJqIOSji.js +127 -0
- package/dist/channel.runtime-Ec8aQ9V2.js +241 -0
- package/dist/channel.runtime-ax5a1jBm.js +218 -0
- package/dist/channel.setup-B-ncdYLT.js +9 -0
- package/dist/channel.setup-BY4bh5dm.js +9 -0
- package/dist/channel.setup-BovsdMnL.js +57 -0
- package/dist/channel.setup-CXzXA25h.js +6 -0
- package/dist/channel.setup-DcZUEufN.js +8 -0
- package/dist/channel.setup-E6zceRsE.js +8 -0
- package/dist/channel.setup-Pc7nGbdX.js +11 -0
- package/dist/channels/plugins/actions/discord.d.ts +2 -2
- package/dist/channels/plugins/actions/discord.js +35 -35
- package/dist/channels/plugins/actions/signal.d.ts +1 -1
- package/dist/channels/plugins/actions/signal.js +35 -35
- package/dist/channels/plugins/actions/telegram.d.ts +2 -2
- package/dist/channels/plugins/actions/telegram.js +35 -35
- package/dist/channels/plugins/agent-tools/whatsapp-login.d.ts +3 -3
- package/dist/channels/plugins/agent-tools/whatsapp-login.js +35 -35
- package/dist/channels-CPtE5ND6.js +404 -0
- package/dist/channels-Cj8ZolHI.js +1118 -0
- package/dist/channels-cli-D2sKrntt.js +291 -0
- package/dist/channels-status-issues-CzIHODg2.js +16 -0
- package/dist/clawbot-cli-BcwEDmUn.js +118 -0
- package/dist/cleanup-utils-D0L17RsX.js +96 -0
- package/dist/cli/daemon-cli.js +1 -1
- package/dist/cli-BvGVPKnD.js +154 -0
- package/dist/command-registry-CADQzTAg.js +14 -0
- package/dist/command-registry-ktiJNAJd.js +242 -0
- package/dist/command-secret-gateway-CXp10RTM.js +111 -0
- package/dist/compact.runtime-DyKL-Iar.js +116 -0
- package/dist/completion-cli-Bz4STrpt.js +17 -0
- package/dist/completion-cli-pVda2OFb.js +445 -0
- package/dist/config-BbvDRSYp.js +31 -0
- package/dist/config-CwBv71QC.js +44 -0
- package/dist/config-cli-Y0uXHbOw.js +678 -0
- package/dist/config-guard-BpW5g7JE.js +118 -0
- package/dist/config-validation-B-vLIsbo.js +262 -0
- package/dist/config-value-DT3-5958.js +132 -0
- package/dist/configure-B9U-jCqP.js +1100 -0
- package/dist/configure-BJ3Wrs5b.js +243 -0
- package/dist/control-ui-assets-C1YDYi82.js +232 -0
- package/dist/control-ui-shared-Dm5Dh0Lo.js +29 -0
- package/dist/core-BwKq3krw.js +150 -0
- package/dist/core-hjBwfDsW.d.ts +87 -0
- package/dist/cron-cli-DTDgfoMh.js +639 -0
- package/dist/daemon-cli-C-dkAXR1.js +339 -0
- package/dist/daemon-install-Oy0Q5pMF.js +180 -0
- package/dist/deliver-DNGnDqF9.js +111 -0
- package/dist/deliver-runtime-CCNZIhET.js +111 -0
- package/dist/device-id-cli-XvwZbIyC.js +52 -0
- package/dist/device-identity-IG5DngWM.js +365 -0
- package/dist/devices-cli-DIsxj4xp.js +342 -0
- package/dist/diagnostic-DTPopFvh.js +310 -0
- package/dist/directory-cli-DTSY3Ktr.js +311 -0
- package/dist/directory-config-helpers-DpFcAbmo.d.ts +38 -0
- package/dist/directory.static-CBRAUwUW.js +44 -0
- package/dist/discord-CrgxhEWw.js +114 -0
- package/dist/discovery-DrG7wmAR.js +48 -0
- package/dist/dm-policy-shared-DKoGdUpY.d.ts +95 -0
- package/dist/dns-cli-BJiz6CLK.js +217 -0
- package/dist/docs-cli-Dq2Yi5qO.js +174 -0
- package/dist/doctor-completion-D3GeVcFP.js +90 -0
- package/dist/doctor-config-flow-B1cMjr8h.js +112 -0
- package/dist/doctor-config-flow-BUe7JpV3.js +2437 -0
- package/dist/enable-Bc8bCuVe.js +24 -0
- package/dist/entry.js +4 -4
- package/dist/exec-approvals-cli-kLAev6bP.js +421 -0
- package/dist/extensions/acpx/index.d.ts +1 -1
- package/dist/extensions/amazon-bedrock/index.d.ts +1 -1
- package/dist/extensions/amazon-bedrock/index.js +4 -4
- package/dist/extensions/anthropic/index.d.ts +1 -1
- package/dist/extensions/anthropic/index.js +35 -35
- package/dist/extensions/bluebubbles/index.d.ts +1 -1
- package/dist/extensions/bluebubbles/index.js +39 -39
- package/dist/extensions/bluebubbles/setup-entry.d.ts +2 -2
- package/dist/extensions/bluebubbles/setup-entry.js +39 -39
- package/dist/extensions/brave/index.d.ts +1 -1
- package/dist/extensions/brave/index.js +5 -5
- package/dist/extensions/byteplus/index.d.ts +1 -1
- package/dist/extensions/byteplus/index.js +35 -35
- package/dist/extensions/cloudflare-ai-gateway/index.d.ts +1 -1
- package/dist/extensions/cloudflare-ai-gateway/index.js +36 -36
- package/dist/extensions/copilot-proxy/index.d.ts +1 -1
- package/dist/extensions/copilot-proxy/index.js +4 -4
- package/dist/extensions/device-pair/index.d.ts +1 -1
- package/dist/extensions/device-pair/index.js +4 -4
- package/dist/extensions/diagnostics-otel/index.d.ts +1 -1
- package/dist/extensions/diagnostics-otel/index.js +4 -4
- package/dist/extensions/diffs/index.d.ts +1 -1
- package/dist/extensions/discord/index.d.ts +1 -1
- package/dist/extensions/discord/index.js +40 -40
- package/dist/extensions/discord/setup-entry.d.ts +1 -1
- package/dist/extensions/discord/setup-entry.js +38 -38
- package/dist/extensions/elevenlabs/index.d.ts +1 -1
- package/dist/extensions/elevenlabs/index.js +35 -35
- package/dist/extensions/feishu/index.d.ts +2 -2
- package/dist/extensions/feishu/index.js +40 -40
- package/dist/extensions/feishu/setup-entry.d.ts +2 -2
- package/dist/extensions/feishu/setup-entry.js +37 -37
- package/dist/extensions/firecrawl/index.d.ts +1 -1
- package/dist/extensions/firecrawl/index.js +35 -35
- package/dist/extensions/github-copilot/index.d.ts +1 -1
- package/dist/extensions/github-copilot/index.js +35 -35
- package/dist/extensions/google/index.d.ts +1 -1
- package/dist/extensions/google/index.js +35 -35
- package/dist/extensions/googlechat/index.d.ts +1 -1
- package/dist/extensions/googlechat/index.js +38 -38
- package/dist/extensions/googlechat/setup-entry.d.ts +1 -1
- package/dist/extensions/googlechat/setup-entry.js +38 -38
- package/dist/extensions/huggingface/index.d.ts +1 -1
- package/dist/extensions/huggingface/index.js +35 -35
- package/dist/extensions/imessage/index.d.ts +1 -1
- package/dist/extensions/imessage/index.js +39 -39
- package/dist/extensions/imessage/setup-entry.d.ts +1 -1
- package/dist/extensions/imessage/setup-entry.js +39 -39
- package/dist/extensions/irc/index.d.ts +1 -1
- package/dist/extensions/irc/index.js +38 -38
- package/dist/extensions/irc/setup-entry.d.ts +2 -2
- package/dist/extensions/irc/setup-entry.js +38 -38
- package/dist/extensions/kakao-talkchannel/index.d.ts +1 -1
- package/dist/extensions/kakao-talkchannel/index.js +4 -4
- package/dist/extensions/kilocode/index.d.ts +1 -1
- package/dist/extensions/kilocode/index.js +35 -35
- package/dist/extensions/kimi-coding/index.d.ts +1 -1
- package/dist/extensions/kimi-coding/index.js +35 -35
- package/dist/extensions/line/index.d.ts +1 -1
- package/dist/extensions/line/index.js +37 -37
- package/dist/extensions/line/setup-entry.d.ts +1 -1
- package/dist/extensions/line/setup-entry.js +37 -37
- package/dist/extensions/llm-task/index.d.ts +1 -1
- package/dist/extensions/llm-task/index.js +35 -35
- package/dist/extensions/lobster/index.d.ts +1 -1
- package/dist/extensions/lobster/index.js +4 -4
- package/dist/extensions/matrix/index.d.ts +1 -1
- package/dist/extensions/matrix/index.js +40 -40
- package/dist/extensions/matrix/setup-entry.d.ts +2 -2
- package/dist/extensions/matrix/setup-entry.js +40 -40
- package/dist/extensions/mattermost/index.d.ts +1 -1
- package/dist/extensions/mattermost/index.js +37 -37
- package/dist/extensions/mattermost/setup-entry.d.ts +2 -2
- package/dist/extensions/mattermost/setup-entry.js +37 -37
- package/dist/extensions/memory-core/index.d.ts +1 -1
- package/dist/extensions/memory-core/index.js +4 -4
- package/dist/extensions/memory-lancedb/index.d.ts +1 -1
- package/dist/extensions/memory-lancedb/index.js +4 -4
- package/dist/extensions/microsoft/index.d.ts +1 -1
- package/dist/extensions/microsoft/index.js +35 -35
- package/dist/extensions/minimax/index.d.ts +1 -1
- package/dist/extensions/minimax/index.js +35 -35
- package/dist/extensions/mistral/index.d.ts +1 -1
- package/dist/extensions/mistral/index.js +35 -35
- package/dist/extensions/modelstudio/index.d.ts +1 -1
- package/dist/extensions/modelstudio/index.js +35 -35
- package/dist/extensions/moonshot/index.d.ts +1 -1
- package/dist/extensions/moonshot/index.js +35 -35
- package/dist/extensions/msteams/index.d.ts +1 -1
- package/dist/extensions/msteams/index.js +40 -40
- package/dist/extensions/msteams/setup-entry.d.ts +1 -1
- package/dist/extensions/msteams/setup-entry.js +40 -40
- package/dist/extensions/nextcloud-talk/index.d.ts +1 -1
- package/dist/extensions/nextcloud-talk/index.js +37 -37
- package/dist/extensions/nextcloud-talk/setup-entry.d.ts +2 -2
- package/dist/extensions/nextcloud-talk/setup-entry.js +37 -37
- package/dist/extensions/nostr/index.d.ts +1 -1
- package/dist/extensions/nostr/index.js +37 -37
- package/dist/extensions/nostr/setup-entry.d.ts +1 -1
- package/dist/extensions/nostr/setup-entry.js +37 -37
- package/dist/extensions/nvidia/index.d.ts +1 -1
- package/dist/extensions/nvidia/index.js +4 -4
- package/dist/extensions/ollama/index.d.ts +1 -1
- package/dist/extensions/ollama/index.js +7 -7
- package/dist/extensions/open-prose/index.d.ts +1 -1
- package/dist/extensions/open-prose/index.js +4 -4
- package/dist/extensions/openai/index.d.ts +1 -1
- package/dist/extensions/openai/index.js +35 -35
- package/dist/extensions/opencode/index.d.ts +1 -1
- package/dist/extensions/opencode/index.js +35 -35
- package/dist/extensions/opencode-go/index.d.ts +1 -1
- package/dist/extensions/opencode-go/index.js +35 -35
- package/dist/extensions/openrouter/index.d.ts +1 -1
- package/dist/extensions/openrouter/index.js +35 -35
- package/dist/extensions/openshell/index.d.ts +1 -1
- package/dist/extensions/openshell/index.js +35 -35
- package/dist/extensions/perplexity/index.d.ts +1 -1
- package/dist/extensions/perplexity/index.js +5 -5
- package/dist/extensions/phone-control/index.d.ts +1 -1
- package/dist/extensions/phone-control/index.js +4 -4
- package/dist/extensions/qianfan/index.d.ts +1 -1
- package/dist/extensions/qianfan/index.js +35 -35
- package/dist/extensions/qwen-portal-auth/index.d.ts +1 -1
- package/dist/extensions/qwen-portal-auth/index.js +35 -35
- package/dist/extensions/sglang/index.d.ts +1 -1
- package/dist/extensions/sglang/index.js +35 -35
- package/dist/extensions/signal/index.d.ts +1 -1
- package/dist/extensions/signal/index.js +38 -38
- package/dist/extensions/signal/setup-entry.d.ts +1 -1
- package/dist/extensions/signal/setup-entry.js +38 -38
- package/dist/extensions/slack/index.d.ts +1 -1
- package/dist/extensions/slack/index.js +39 -39
- package/dist/extensions/slack/setup-entry.d.ts +1 -1
- package/dist/extensions/slack/setup-entry.js +38 -38
- package/dist/extensions/synology-chat/index.d.ts +1 -1
- package/dist/extensions/synology-chat/index.js +37 -37
- package/dist/extensions/synology-chat/setup-entry.d.ts +1 -1
- package/dist/extensions/synology-chat/setup-entry.js +37 -37
- package/dist/extensions/synthetic/index.d.ts +1 -1
- package/dist/extensions/synthetic/index.js +35 -35
- package/dist/extensions/talk-voice/index.d.ts +1 -1
- package/dist/extensions/talk-voice/index.js +35 -35
- package/dist/extensions/telegram/index.d.ts +1 -1
- package/dist/extensions/telegram/index.js +38 -38
- package/dist/extensions/telegram/setup-entry.d.ts +1 -1
- package/dist/extensions/telegram/setup-entry.js +37 -37
- package/dist/extensions/thread-ownership/index.d.ts +1 -1
- package/dist/extensions/thread-ownership/index.js +4 -4
- package/dist/extensions/tlon/index.d.ts +1 -1
- package/dist/extensions/tlon/index.js +37 -37
- package/dist/extensions/tlon/setup-entry.d.ts +1 -1
- package/dist/extensions/tlon/setup-entry.js +37 -37
- package/dist/extensions/together/index.d.ts +1 -1
- package/dist/extensions/together/index.js +35 -35
- package/dist/extensions/twitch/index.d.ts +2 -2
- package/dist/extensions/twitch/index.js +37 -37
- package/dist/extensions/venice/index.d.ts +1 -1
- package/dist/extensions/venice/index.js +35 -35
- package/dist/extensions/vercel-ai-gateway/index.d.ts +1 -1
- package/dist/extensions/vercel-ai-gateway/index.js +36 -36
- package/dist/extensions/vllm/index.d.ts +1 -1
- package/dist/extensions/vllm/index.js +35 -35
- package/dist/extensions/voice-call/index.d.ts +1 -1
- package/dist/extensions/voice-call/index.js +35 -35
- package/dist/extensions/volcengine/index.d.ts +1 -1
- package/dist/extensions/volcengine/index.js +35 -35
- package/dist/extensions/whatsapp/index.d.ts +1 -1
- package/dist/extensions/whatsapp/index.js +38 -38
- package/dist/extensions/whatsapp/setup-entry.d.ts +1 -1
- package/dist/extensions/whatsapp/setup-entry.js +38 -38
- package/dist/extensions/xai/index.d.ts +1 -1
- package/dist/extensions/xai/index.js +35 -35
- package/dist/extensions/xiaomi/index.d.ts +1 -1
- package/dist/extensions/xiaomi/index.js +35 -35
- package/dist/extensions/zai/index.d.ts +1 -1
- package/dist/extensions/zai/index.js +35 -35
- package/dist/extensions/zalo/index.d.ts +1 -1
- package/dist/extensions/zalo/index.js +39 -39
- package/dist/extensions/zalo/setup-entry.d.ts +1 -1
- package/dist/extensions/zalo/setup-entry.js +39 -39
- package/dist/extensions/zalouser/index.d.ts +1 -1
- package/dist/extensions/zalouser/index.js +40 -40
- package/dist/extensions/zalouser/setup-entry.d.ts +1 -1
- package/dist/extensions/zalouser/setup-entry.js +40 -40
- package/dist/feishu-fIcnHDTd.d.ts +36 -0
- package/dist/gateway-cli-0c-8h93_.js +26437 -0
- package/dist/gateway-install-token-1PwJvrBY.js +163 -0
- package/dist/gateway-rpc-C0Vk51W7.js +26 -0
- package/dist/gateway-runtime-CBm3CCoA.js +69 -0
- package/dist/git-commit-BTWXFY41.js +177 -0
- package/dist/git-commit-D6GTN5Yt.js +2 -0
- package/dist/googlechat-BQr4xgoZ.js +307 -0
- package/dist/googlechat-BvwsCVKl.d.ts +12 -0
- package/dist/group-access-DpiQnd-G.d.ts +61 -0
- package/dist/health-6yZQGADY.js +113 -0
- package/dist/health-C9DYGyRe.js +570 -0
- package/dist/heartbeat-summary-Dct2lqJj.js +57 -0
- package/dist/help-CtwSApfq.js +81 -0
- package/dist/hooks-9gokOxZ5.d.ts +6 -0
- package/dist/hooks-cli-BegKzHZT.js +1000 -0
- package/dist/hooks-status-Bm_pGORf.js +78 -0
- package/dist/http-registry-D-S6a1Na.d.ts +20 -0
- package/dist/identity-file-Diub2a0t.js +60 -0
- package/dist/image-generation-CbIVzmAR.d.ts +9 -0
- package/dist/imessage-Bgok9kfl.js +31 -0
- package/dist/imessage-VIHePprL.js +115 -0
- package/dist/inbound-reply-dispatch-B53GAGWq.js +71 -0
- package/dist/inbound-reply-dispatch-n7U3qg15.d.ts +72 -0
- package/dist/index.js +2 -2
- package/dist/install-target-oz1pjfHH.js +574 -0
- package/dist/installs-CUFm5V8a.js +532 -0
- package/dist/io-BaBxjB1v.js +9739 -0
- package/dist/io-CgHb1Jld.js +29 -0
- package/dist/irc-CaRKzGvW.js +672 -0
- package/dist/library-C5SNBCMb.js +112 -0
- package/dist/lifecycle-core-Dn8PK6nk.js +382 -0
- package/dist/line/accounts.d.ts +2 -2
- package/dist/line/send.d.ts +1 -1
- package/dist/line/send.js +7 -7
- package/dist/line/template-messages.d.ts +1 -1
- package/dist/line-B5QFpgN_.d.ts +75 -0
- package/dist/line-fePrrQOD.js +530 -0
- package/dist/llm-slug-generator-hKae3XDA.js +67 -0
- package/dist/llm-slug-generator.d.ts +1 -1
- package/dist/llm-slug-generator.js +36 -36
- package/dist/logging-CdisccbY.js +13 -0
- package/dist/logging-LKQSgX1d.js +30 -0
- package/dist/login-qr-C1YWh4nE.js +233 -0
- package/dist/login-qr-WFluMDMb.js +112 -0
- package/dist/logs-cli-CNzOvZ2d.js +256 -0
- package/dist/manager-runtime-DgMhLTkR.js +111 -0
- package/dist/manager.runtime-hUWgpPt2.js +715 -0
- package/dist/manifest-registry-CS_p1OBQ.js +1329 -0
- package/dist/matrix-43_RGLZN.d.ts +68 -0
- package/dist/matrix-CCFxHfxa.js +1269 -0
- package/dist/matrix-DWs_qIkJ.js +1495 -0
- package/dist/mcp-cli-Ci2jvv3s.js +87 -0
- package/dist/media-understanding.runtime-Cdr6iTW6.js +116 -0
- package/dist/memory-cli-LZbyF0Iu.js +111 -0
- package/dist/memory-search-BHhETk6u.js +17 -0
- package/dist/memory-search-tTD5o_rU.js +204 -0
- package/dist/method-scopes-B2ZKSsxQ.js +2452 -0
- package/dist/model-auth-markers-LqZ4qhrZ.d.ts +20 -0
- package/dist/model-picker-CTR5mo4v.js +112 -0
- package/dist/model-picker-DG4z_dBs.js +390 -0
- package/dist/model-picker.runtime-DMQ9Pj9_.js +125 -0
- package/dist/model-selection-bBBxfXdb.js +653 -0
- package/dist/model-suppression.runtime-BVG75tZ7.js +116 -0
- package/dist/models-BjkVLfgw.js +2514 -0
- package/dist/models-ZO01Q4cx.js +118 -0
- package/dist/models-cli-DemdF-bm.js +309 -0
- package/dist/models-config-B2Jja8ua.js +111 -0
- package/dist/models-config.providers.discovery-puxTsH39.d.ts +18 -0
- package/dist/moldclaw-root-Cb6HRlUO.js +92 -0
- package/dist/monitor-BP4idxJD.js +782 -0
- package/dist/monitor-B_eP8Eim.js +772 -0
- package/dist/monitor-CRHYNl5J.js +3468 -0
- package/dist/monitor-Ci1Xg4g3.js +113 -0
- package/dist/monitor-DEodDl3z.js +6823 -0
- package/dist/monitor-DJlNKuMz.js +115 -0
- package/dist/monitor-DvFwDS9w.js +3076 -0
- package/dist/monitor-shared--cEjSf8s.js +444 -0
- package/dist/msteams-CV2a8uE8.js +852 -0
- package/dist/node-cli-Of2g7DSd.js +2503 -0
- package/dist/node-resolve-BYC2FbO2.js +835 -0
- package/dist/nodes-cli-CPHM6Upj.js +1380 -0
- package/dist/nostr-BFKRoOlz.d.ts +7 -0
- package/dist/nostr-lHpcBzz4.js +8744 -0
- package/dist/npm-resolution-kqHN85wB.js +60 -0
- package/dist/oauth-env-CLG8KOrz.js +10 -0
- package/dist/onboard-BON0C360.js +48 -0
- package/dist/onboard-CRkIBgOI.js +589 -0
- package/dist/onboard-DsKI17iq.js +25 -0
- package/dist/onboard-channels-BY3IbBBf.js +1241 -0
- package/dist/onboard-channels-CLKdRxvW.js +205 -0
- package/dist/onboard-custom-BjPrMo_R.js +571 -0
- package/dist/onboard-custom-DqcPiZBN.js +114 -0
- package/dist/onboard-helpers-BkrOY5OE.js +113 -0
- package/dist/onboard-helpers-DiSRTpZC.js +335 -0
- package/dist/onboard-hooks-pzEPZAvl.js +72 -0
- package/dist/onboard-remote-ChyLC6Dk.js +181 -0
- package/dist/onboard-remote-DHmK9ntl.js +117 -0
- package/dist/onboard-search-BgA3jEMW.js +302 -0
- package/dist/onboard-skills-BMo_NvnW.js +133 -0
- package/dist/onboard-skills-Bba-Z2p8.js +117 -0
- package/dist/outbound-media-BHD4aJEX.d.ts +11 -0
- package/dist/outbound-media-DSno0N82.js +11 -0
- package/dist/pairing-access-CzHpaM0R.d.ts +21 -0
- package/dist/pairing-cli-CmklqK0q.js +217 -0
- package/dist/perplexity-CXwMDD3u.js +24 -0
- package/dist/persistent-dedupe-B9vrAf8t.d.ts +26 -0
- package/dist/pi-model-discovery-runtime-BrK7tcaO.js +111 -0
- package/dist/pi-tools.before-tool-call.runtime-C5yLUogH.js +381 -0
- package/dist/plugin-install-C4AWJIFP.js +117 -0
- package/dist/plugin-install-CB3J1hfV.js +184 -0
- package/dist/plugin-install-plan-7itZiegi.js +49 -0
- package/dist/plugin-registry-DX_GFoiz.js +113 -0
- package/dist/plugin-registry-e3cxTtvb.js +49 -0
- package/dist/plugin-sdk/account-resolution.js +35 -35
- package/dist/plugin-sdk/acp-runtime.js +35 -35
- package/dist/plugin-sdk/agent-runtime.js +35 -35
- package/dist/plugin-sdk/bluebubbles.js +37 -37
- package/dist/plugin-sdk/channel-config-helpers.js +35 -35
- package/dist/plugin-sdk/channel-policy.js +35 -35
- package/dist/plugin-sdk/channel-runtime.js +35 -35
- package/dist/plugin-sdk/compat.js +36 -36
- package/dist/plugin-sdk/config-runtime.js +35 -35
- package/dist/plugin-sdk/conversation-runtime.js +35 -35
- package/dist/plugin-sdk/copilot-proxy.js +4 -4
- package/dist/plugin-sdk/core.js +4 -4
- package/dist/plugin-sdk/device-pair.js +4 -4
- package/dist/plugin-sdk/discord.js +35 -35
- package/dist/plugin-sdk/feishu.js +35 -35
- package/dist/plugin-sdk/gateway-runtime.js +10 -10
- package/dist/plugin-sdk/googlechat.js +37 -37
- package/dist/plugin-sdk/image-generation-runtime.js +35 -35
- package/dist/plugin-sdk/image-generation.js +35 -35
- package/dist/plugin-sdk/imessage.js +36 -36
- package/dist/plugin-sdk/index.js +35 -35
- package/dist/plugin-sdk/infra-runtime.js +35 -35
- package/dist/plugin-sdk/irc.js +37 -37
- package/dist/plugin-sdk/line.js +36 -36
- package/dist/plugin-sdk/llm-task.js +35 -35
- package/dist/plugin-sdk/lobster.js +4 -4
- package/dist/plugin-sdk/matrix.js +37 -37
- package/dist/plugin-sdk/mattermost.js +36 -36
- package/dist/plugin-sdk/media-runtime.js +35 -35
- package/dist/plugin-sdk/media-understanding-runtime.js +35 -35
- package/dist/plugin-sdk/media-understanding.js +35 -35
- package/dist/plugin-sdk/memory-lancedb.js +4 -4
- package/dist/plugin-sdk/minimax-portal-auth.js +4 -4
- package/dist/plugin-sdk/msteams.js +38 -38
- package/dist/plugin-sdk/nextcloud-talk.js +36 -36
- package/dist/plugin-sdk/nostr.js +36 -36
- package/dist/plugin-sdk/ollama-setup.js +9 -9
- package/dist/plugin-sdk/open-prose.js +4 -4
- package/dist/plugin-sdk/phone-control.js +4 -4
- package/dist/plugin-sdk/plugin-runtime.js +35 -35
- package/dist/plugin-sdk/provider-auth.js +35 -35
- package/dist/plugin-sdk/provider-models.js +5 -5
- package/dist/plugin-sdk/provider-onboard.js +4 -4
- package/dist/plugin-sdk/provider-setup.js +39 -39
- package/dist/plugin-sdk/provider-stream.js +4 -4
- package/dist/plugin-sdk/provider-usage.js +4 -4
- package/dist/plugin-sdk/qwen-portal-auth.js +35 -35
- package/dist/plugin-sdk/reply-history.js +35 -35
- package/dist/plugin-sdk/reply-runtime.js +35 -35
- package/dist/plugin-sdk/routing.js +3 -3
- package/dist/plugin-sdk/sandbox.js +35 -35
- package/dist/plugin-sdk/security-runtime.js +35 -35
- package/dist/plugin-sdk/self-hosted-provider-setup.js +37 -37
- package/dist/plugin-sdk/setup.js +35 -35
- package/dist/plugin-sdk/signal.js +35 -35
- package/dist/plugin-sdk/slack.js +35 -35
- package/dist/plugin-sdk/speech-runtime.js +35 -35
- package/dist/plugin-sdk/speech.js +35 -35
- package/dist/plugin-sdk/src/secrets/secure-file-store.d.ts +26 -0
- package/dist/plugin-sdk/src/subscription/provider.d.ts +5 -3
- package/dist/plugin-sdk/synology-chat.js +36 -36
- package/dist/plugin-sdk/talk-voice.js +4 -4
- package/dist/plugin-sdk/telegram.js +35 -35
- package/dist/plugin-sdk/text-runtime.js +7 -7
- package/dist/plugin-sdk/thread-ownership.js +4 -4
- package/dist/plugin-sdk/tlon.js +36 -36
- package/dist/plugin-sdk/twitch.js +35 -35
- package/dist/plugin-sdk/voice-call.js +35 -35
- package/dist/plugin-sdk/whatsapp.js +35 -35
- package/dist/plugin-sdk/zalo.js +38 -38
- package/dist/plugin-sdk/zalouser.js +38 -38
- package/dist/plugins/runtime/index.d.ts +1 -1
- package/dist/plugins/runtime/index.js +35 -35
- package/dist/plugins-DF5FaTO0.js +111 -0
- package/dist/plugins-cli-CvTJemqC.js +917 -0
- package/dist/policy-CNXISK_a.js +143 -0
- package/dist/preflight-audio.runtime-RP000oxo.js +116 -0
- package/dist/probe-BkM5pykD.js +21 -0
- package/dist/probe-DKbRTJv5.js +1793 -0
- package/dist/probe-DkrfRsjU.js +47 -0
- package/dist/probe-DpcJ0WeP.js +129 -0
- package/dist/probe-auth-BcNjX8hy.js +40 -0
- package/dist/probe-auth-DhuAb8ls.js +48 -0
- package/dist/probe-wciBj-aL.js +6329 -0
- package/dist/program-C8-p0mW5.js +253 -0
- package/dist/prompt-select-styled-DH0pVoc0.js +2673 -0
- package/dist/provider-api-key-auth.runtime-CAFeIQ1u.js +121 -0
- package/dist/provider-auth-choice-CB_HzdTl.js +126 -0
- package/dist/provider-auth-choice-helpers-hzDkh3f1.js +48 -0
- package/dist/provider-auth-choice-preference-BHCXvNSE.js +189 -0
- package/dist/provider-auth-choice.runtime-Dx4ms2C5.js +123 -0
- package/dist/provider-auth-choices-0KaDNPBQ.js +57 -0
- package/dist/provider-auth-guidance-BaAUiNr_.js +34 -0
- package/dist/provider-auth-result-Bto1bYtS.d.ts +18 -0
- package/dist/provider-models-DxOmeToO.d.ts +867 -0
- package/dist/provider-models-xnyxy6mO.js +2113 -0
- package/dist/provider-ollama-setup-DBYK__ov.d.ts +32 -0
- package/dist/provider-ollama-setup-QzgCxj44.js +314 -0
- package/dist/provider-onboard-B9ionepI.js +139 -0
- package/dist/provider-onboard-CURxJ_UX.d.ts +40 -0
- package/dist/provider-runtime.runtime-4xwmsl5L.js +111 -0
- package/dist/provider-self-hosted-setup-BHd24EDG.js +182 -0
- package/dist/provider-self-hosted-setup-qeY8BYSy.d.ts +61 -0
- package/dist/provider-stream-Chz_EFw3.js +512 -0
- package/dist/provider-usage-C11Q7UwS.js +111 -0
- package/dist/provider-usage-kxemdMp2.js +633 -0
- package/dist/provider-wizard-CanJoxNC.js +152 -0
- package/dist/push-apns-Dsajnm8C.js +1038 -0
- package/dist/pw-ai-DUe4BbH2.js +1867 -0
- package/dist/qmd-manager-CAAFp7qK.js +1570 -0
- package/dist/qr-cli-Bu2jqTPY.js +113 -0
- package/dist/qr-cli-Bu9Z-X48.js +369 -0
- package/dist/reactions-Cpfum4iU.js +281 -0
- package/dist/read-only-account-inspect.discord.runtime-BK0LaMgC.js +116 -0
- package/dist/read-only-account-inspect.slack.runtime-DgKiC5wT.js +116 -0
- package/dist/read-only-account-inspect.telegram.runtime-mxfgFVOU.js +116 -0
- package/dist/redact-snapshot-DD8A4tdd.js +2663 -0
- package/dist/register.agent-DU4FtrU2.js +439 -0
- package/dist/register.backup-8nOYtJqg.js +625 -0
- package/dist/register.configure-DmtecqIH.js +252 -0
- package/dist/register.maintenance-Dir3ulKP.js +574 -0
- package/dist/register.message-Cfl-f3Ju.js +709 -0
- package/dist/register.onboard-Bv7WVzEi.js +192 -0
- package/dist/register.setup-BIyeI8RY.js +212 -0
- package/dist/register.status-health-sessions-C69WQcF4.js +498 -0
- package/dist/register.subclis-B_4KCgTd.js +315 -0
- package/dist/register.subclis-BeXsmgBL.js +13 -0
- package/dist/replies-DdcFUmki.js +110 -0
- package/dist/resolve-channels-DRZqPV5o.js +226 -0
- package/dist/resolve-channels-DxW1kqxA.js +262 -0
- package/dist/resolve-route-DdX-HBVt.js +538 -0
- package/dist/resolve-users-rgCQvkLs.js +143 -0
- package/dist/root-help-QAkoA7GD.js +32 -0
- package/dist/routes-CcJNnwTF.js +7097 -0
- package/dist/rpc-DDUAlBbH.js +67 -0
- package/dist/run-main-D9ci5pn7.js +424 -0
- package/dist/runtime-Bitmi8Er.d.ts +26 -0
- package/dist/runtime-discord-ops.runtime-T4sf7aRB.js +9078 -0
- package/dist/runtime-slack-ops.runtime-BQpP48mC.js +4556 -0
- package/dist/runtime-telegram-ops.runtime-cVO5dqOp.js +133 -0
- package/dist/runtime-whatsapp-login.runtime-DtNx0dSY.js +114 -0
- package/dist/runtime-whatsapp-outbound.runtime-Bw47QbFK.js +117 -0
- package/dist/sandbox-cli-DsFwjbjC.js +535 -0
- package/dist/search-manager-BRAK8fEe.js +16 -0
- package/dist/search-manager-BS5Db0A6.js +386 -0
- package/dist/secrets-cli-D3J46TJp.js +2070 -0
- package/dist/security-cli-B866M9cB.js +575 -0
- package/dist/send-B1pX9_Oc.js +283 -0
- package/dist/send-B2RrLg83.js +100 -0
- package/dist/send-DFnV__Aq.js +1025 -0
- package/dist/send-DZIH6CJt.js +629 -0
- package/dist/send-sl9WnKbW.js +631 -0
- package/dist/server-node-events-BT6egg20.js +506 -0
- package/dist/server-zI_K-D05.js +107 -0
- package/dist/sessions-C8kiAcoJ.js +112 -0
- package/dist/sessions-DLBpp52_.js +218 -0
- package/dist/setup-C7eOzMiC.js +387 -0
- package/dist/setup-CFIMq-Pz.d.ts +37 -0
- package/dist/setup-binary-CcAv8NXz.js +406 -0
- package/dist/setup-browser-C4eRV3h6.js +70 -0
- package/dist/setup-core-BnR486P-.js +143 -0
- package/dist/setup-core-CIswIiu5.js +166 -0
- package/dist/setup-core-CcbcrXXg.js +47 -0
- package/dist/setup-core-nZSw5BSv.js +205 -0
- package/dist/setup-surface-C5iSpT4M.js +490 -0
- package/dist/setup-wizard-helpers-r0J6l8ST.d.ts +203 -0
- package/dist/setup.finalize-adiRfo0U.js +522 -0
- package/dist/setup.gateway-config-BwFWKDfT.js +343 -0
- package/dist/shared-12TimyeF.js +182 -0
- package/dist/shared-9EWO34-k.js +298 -0
- package/dist/shared-B4vUbaRR.js +75 -0
- package/dist/shared-bNWpW3Dd.js +96 -0
- package/dist/shared-lU1y5dvS.js +102 -0
- package/dist/signal-DBlETRu9.js +114 -0
- package/dist/skills-Bio8GwTE.js +20 -0
- package/dist/skills-DE_MXFSN.js +853 -0
- package/dist/skills-cli-BGuW-tKw.js +292 -0
- package/dist/skills-install--rnorIoJ.js +763 -0
- package/dist/skills-status-B08PtBc_.js +21 -0
- package/dist/skills-status-CzM008aB.js +169 -0
- package/dist/slack-C4T53Nc-.js +114 -0
- package/dist/slash-commands.runtime-B7fsD8Be.js +128 -0
- package/dist/slash-dispatch.runtime-t0PAX4vQ.js +141 -0
- package/dist/slash-skill-commands.runtime-DIhPnEfR.js +116 -0
- package/dist/src-DrDirlvw.js +1701 -0
- package/dist/status-Bld14WSA.js +131 -0
- package/dist/status-CgeO4RuH.js +43 -0
- package/dist/status-HlvixAOq.js +606 -0
- package/dist/status-Rom_Lf3c.js +1599 -0
- package/dist/status-TwbMH6Am.js +126 -0
- package/dist/status-json-DMW7cmuK.js +288 -0
- package/dist/status.link-channel-V4LkB6Gq.js +143 -0
- package/dist/status.scan.deps.runtime-BE3X-dcP.js +126 -0
- package/dist/status.scan.runtime-BxVY4mty.js +119 -0
- package/dist/status.summary-CzLM0vVr.js +592 -0
- package/dist/status.summary.runtime-BSBnHZ1Q.js +118 -0
- package/dist/status.update-BxblMS7P.js +77 -0
- package/dist/subagent-orphan-recovery-BpRPryEj.js +307 -0
- package/dist/subagent-registry-runtime-DYYU5p3X.js +111 -0
- package/dist/subscription-CpFdxuFS.js +33 -0
- package/dist/subscription-DaA1urx-.js +102 -0
- package/dist/subscription-cli-Bvto9EmO.js +134 -0
- package/dist/synology-chat-3nwk-Nj0.js +297 -0
- package/dist/system-cli-BvNps8sl.js +94 -0
- package/dist/telegram/audit.d.ts +1 -1
- package/dist/telegram/audit.js +1 -1
- package/dist/telegram/token.d.ts +1 -1
- package/dist/telegram/token.js +35 -35
- package/dist/telegram-RtKXoEsF.js +114 -0
- package/dist/text-chunking-BD5mQe2R.js +84 -0
- package/dist/text-chunking-DDUU_vAF.d.ts +79 -0
- package/dist/tlon-z-kYmJE-.js +433 -0
- package/dist/tui-cli-CzSK08Rh.js +137 -0
- package/dist/tui-wV7R1Tlc.js +3834 -0
- package/dist/types-2H_e7eWT.d.ts +45 -0
- package/dist/types-ZKnGUchG.d.ts +22692 -0
- package/dist/types.base-BFiQZ4J9.d.ts +188 -0
- package/dist/ui-BWVHreeR.js +31 -0
- package/dist/update-D1Wgh1Tj.js +1036 -0
- package/dist/update-cli-CZh99uyY.js +1503 -0
- package/dist/update-offset-store-D5xTdUr0.js +112 -0
- package/dist/update-runner-GbKfoCHs.js +1496 -0
- package/dist/upsert-with-lock-BZU7Le8n.js +33 -0
- package/dist/usage-Czgwvg0h.js +115 -0
- package/dist/web-CMczmL90.js +112 -0
- package/dist/web-shared-B5Q0mIJq.d.ts +45 -0
- package/dist/webhook-request-guards-CsKDhZJr.d.ts +76 -0
- package/dist/webhook-targets-BSmFtesN.js +181 -0
- package/dist/webhook-targets-CjxuEE9C.d.ts +106 -0
- package/dist/webhooks-cli-Wl9y6AWW.js +350 -0
- package/dist/whatsapp-VzRW8MdR.js +114 -0
- package/dist/whatsapp-actions-Cg1Wxv8W.js +167 -0
- package/dist/workspace-DJ_S272u.js +484 -0
- package/dist/workspace-DbZSqjw0.js +289 -0
- package/dist/workspace-cli-D93DLmAh.js +154 -0
- package/dist/workspace-dirs-CGeIPpGN.js +2003 -0
- package/dist/zalo-CK2dlGmu.d.ts +9 -0
- package/dist/zalo-Db7s2boL.js +415 -0
- package/dist/zalouser-Jh5YTJX3.js +30911 -0
- package/extensions/discord/src/monitor/allow-list.ts +8 -1
- package/extensions/discord/src/monitor/message-handler.preflight.ts +4 -1
- package/package.json +1 -1
- package/dist/accounts-CS8U4v8C.js +0 -114
- package/dist/accounts-gLr-Udmt.d.ts +0 -103
- package/dist/acp-cli-BGT0jXcC.js +0 -2093
- package/dist/actions.runtime-BfckTw6c.js +0 -119
- package/dist/actions.runtime-Cl9mBfqH.js +0 -133
- package/dist/agent-scope-C-YmLnnb.js +0 -208
- package/dist/agents-CydD54p8.js +0 -222
- package/dist/agents-DpQsZO6O.js +0 -853
- package/dist/agents.config-XU7IsYE-.js +0 -121
- package/dist/agents.config-ssoQXuvF.js +0 -17
- package/dist/allow-list-Cfn6lmMK.js +0 -81
- package/dist/allowlist-CCYXVpM9.js +0 -142
- package/dist/api-BoXoFKxy.js +0 -117
- package/dist/audit-Bv05N5o9.js +0 -787
- package/dist/audit-CIWW1Aqm.js +0 -54
- package/dist/audit-channel.collect.runtime-Bi7yrdcO.js +0 -605
- package/dist/audit-channel.runtime-C_NDweiW.js +0 -121
- package/dist/audit-extra.async-Dp7OKSXg.js +0 -813
- package/dist/audit-membership-runtime-B8FQ6VtN.js +0 -162
- package/dist/audit.deep.runtime-CXhobL6b.js +0 -25
- package/dist/audit.nondeep.runtime-CrEm3T16.js +0 -832
- package/dist/audit.runtime-CJPKj1Zg.js +0 -118
- package/dist/auth-Byfp0flq.js +0 -101
- package/dist/auth-choice-BgOjdeXN.js +0 -507
- package/dist/auth-choice-CD1Heq0M.js +0 -122
- package/dist/auth-choice-ePNfg0iQ.js +0 -268
- package/dist/auth-choice-options-BlewQWI0.js +0 -123
- package/dist/auth-choice-prompt-BP2b6aXz.js +0 -36
- package/dist/auth-choice-prompt-Cmwl4n97.js +0 -115
- package/dist/auth-choice.apply-helpers-Dq-nxuuX.js +0 -66
- package/dist/auth-choice.plugin-providers.runtime-B23kOUzQ.js +0 -119
- package/dist/auth-profiles-1kPLbBwI.js +0 -127823
- package/dist/auth-profiles.runtime-DAfSjku1.js +0 -116
- package/dist/banner-DeOsobLO.js +0 -342
- package/dist/bluebubbles-BsLGedBM.js +0 -64
- package/dist/bluebubbles-U2sAfO4_.d.ts +0 -6
- package/dist/bot-DW12K3bO.d.ts +0 -478
- package/dist/brave-BoWimrLe.js +0 -24
- package/dist/browser-cli-D_S3wEYE.js +0 -1494
- package/dist/call-ByEzDJ1_.js +0 -640
- package/dist/call-CHCWVg-O.js +0 -39
- package/dist/channel-3VC0oOMu.js +0 -214
- package/dist/channel-B9fCBPiS.js +0 -207
- package/dist/channel-B9q775cM.js +0 -562
- package/dist/channel-BG3UK54j.js +0 -803
- package/dist/channel-BRQAdMML.js +0 -352
- package/dist/channel-BmlLp933.js +0 -1321
- package/dist/channel-By6KvdTG.js +0 -920
- package/dist/channel-C8rRsdf6.js +0 -226
- package/dist/channel-CLEDBbXE.js +0 -943
- package/dist/channel-CMvBAG7o.js +0 -306
- package/dist/channel-CmlxxjHY.js +0 -1598
- package/dist/channel-CqG6_xN0.js +0 -949
- package/dist/channel-DNueHKs92.js +0 -316
- package/dist/channel-DUtyN7BX.js +0 -4681
- package/dist/channel-DWD6GrfZ.js +0 -538
- package/dist/channel-DaRYMYzj.js +0 -619
- package/dist/channel-Dj6BgLp8.js +0 -575
- package/dist/channel-account-context-Ba3u5D21.js +0 -103
- package/dist/channel-crabk6Em.js +0 -542
- package/dist/channel-i8uqQaK2.js +0 -497
- package/dist/channel-options-xljvwHS2.js +0 -50
- package/dist/channel-plugin-ids-DAgknSG4.js +0 -26
- package/dist/channel-summary-dHTMCG75.js +0 -111
- package/dist/channel-xVWQ96Ni.js +0 -397
- package/dist/channel.runtime-B6PoZ4BV.js +0 -182
- package/dist/channel.runtime-BPZmo57e.js +0 -404
- package/dist/channel.runtime-B_1uGR-U.js +0 -199
- package/dist/channel.runtime-BiXnPU0d.js +0 -218
- package/dist/channel.runtime-BpvDc9sv.js +0 -870
- package/dist/channel.runtime-CUua3W80.js +0 -418
- package/dist/channel.runtime-CaCBTd0A.js +0 -179
- package/dist/channel.runtime-D0FfYvUj.js +0 -4011
- package/dist/channel.runtime-DhoJtpvJ.js +0 -241
- package/dist/channel.runtime-Kj9EXNE0.js +0 -127
- package/dist/channel.runtime-r4tPuPyh.js +0 -171
- package/dist/channel.setup-B7d_grfe.js +0 -6
- package/dist/channel.setup-C0vu1fhi.js +0 -9
- package/dist/channel.setup-CAI0FNHj.js +0 -11
- package/dist/channel.setup-CkDVwv5R.js +0 -57
- package/dist/channel.setup-Cpd00YqQ.js +0 -8
- package/dist/channel.setup-DbBz1-WT.js +0 -9
- package/dist/channel.setup-GZnAvD9g.js +0 -8
- package/dist/channels-5H484RSw.js +0 -1118
- package/dist/channels-BnPudfyx.js +0 -404
- package/dist/channels-cli-WIC-QeH_.js +0 -291
- package/dist/channels-status-issues-RDmzovJU.js +0 -16
- package/dist/clawbot-cli-BgutNwf8.js +0 -118
- package/dist/cleanup-utils-DBl1Aij1.js +0 -96
- package/dist/cli-1P7u6zqu.js +0 -154
- package/dist/command-registry-B8jovrws.js +0 -232
- package/dist/command-registry-DtDl1FVm.js +0 -14
- package/dist/command-secret-gateway-BgUo3FxJ.js +0 -111
- package/dist/compact.runtime-CXbXM0AU.js +0 -116
- package/dist/completion-cli-Cik_owAE.js +0 -17
- package/dist/completion-cli-RU3P2RSl.js +0 -445
- package/dist/config-5HUpB1L1.js +0 -31
- package/dist/config-cli-QHaUHoZI.js +0 -433
- package/dist/config-guard-C9Sn3pE-.js +0 -118
- package/dist/config-sW57gztj.js +0 -44
- package/dist/config-validation-5LkjIKNt.js +0 -262
- package/dist/config-value-CtTWALxG.js +0 -132
- package/dist/configure-BmR2TPLf.js +0 -243
- package/dist/configure-DaLN-5xM.js +0 -1100
- package/dist/control-ui-assets-CH3MYmAo.js +0 -232
- package/dist/control-ui-shared-CA77PTml.js +0 -29
- package/dist/core-CvDzLs7B.js +0 -150
- package/dist/core-dPA4nFkn.d.ts +0 -87
- package/dist/cron-cli-tguLpzyq.js +0 -639
- package/dist/daemon-cli-ptosOkL8.js +0 -339
- package/dist/daemon-install-DzU4EnVa.js +0 -180
- package/dist/deliver-DwxFoHM3.js +0 -111
- package/dist/deliver-runtime-DOdDyaPI.js +0 -111
- package/dist/device-id-cli-GopvlxxZ.js +0 -52
- package/dist/device-identity-CRfhC3_s.js +0 -365
- package/dist/devices-cli-ain7ESqU.js +0 -342
- package/dist/diagnostic-D96Xaqrj.js +0 -310
- package/dist/directory-cli-fh1UxGgY.js +0 -311
- package/dist/directory-config-helpers-CpU1oflo.d.ts +0 -38
- package/dist/directory.static-CKjJUNGl.js +0 -44
- package/dist/discord-CflhwDEM.js +0 -114
- package/dist/discovery-x0ZqY4AB.js +0 -48
- package/dist/dm-policy-shared-73A52W6E.d.ts +0 -95
- package/dist/dns-cli-DCHyKjGf.js +0 -217
- package/dist/docs-cli-D3OoqYSP.js +0 -174
- package/dist/doctor-completion-Bq2eP87s.js +0 -90
- package/dist/doctor-config-flow-D8XRG9Ku.js +0 -2437
- package/dist/doctor-config-flow-DGiF1HGc.js +0 -112
- package/dist/enable-0QSF4YGH.js +0 -24
- package/dist/exec-approvals-cli-Bncym0Gd.js +0 -421
- package/dist/feishu-B5JDcyF9.d.ts +0 -36
- package/dist/gateway-cli-DYscsmA-.js +0 -26437
- package/dist/gateway-install-token-CNv17ac9.js +0 -163
- package/dist/gateway-rpc-BGC1Rxvg.js +0 -26
- package/dist/gateway-runtime-D89mSQPB.js +0 -69
- package/dist/git-commit-CeLH5Ozm.js +0 -2
- package/dist/git-commit-DUKRiCP-.js +0 -177
- package/dist/googlechat-BgXeXjd1.js +0 -307
- package/dist/googlechat-De-T7C31.d.ts +0 -12
- package/dist/group-access-Deh1tVNr.d.ts +0 -61
- package/dist/health-BEjzWwaB.js +0 -570
- package/dist/health-FjqrWQL6.js +0 -113
- package/dist/heartbeat-summary-CfdSA9M1.js +0 -57
- package/dist/help-BZeVprq1.js +0 -81
- package/dist/hooks-B5pYs_d7.d.ts +0 -6
- package/dist/hooks-cli-B7uGJs2O.js +0 -1000
- package/dist/hooks-status-CfceaUSg.js +0 -78
- package/dist/http-registry-C-KXqwnj.d.ts +0 -20
- package/dist/identity-file-sshkKKIr.js +0 -60
- package/dist/image-generation-CafM5hZh.d.ts +0 -9
- package/dist/imessage-BcV3WGx_.js +0 -31
- package/dist/imessage-Dhje7Ty-.js +0 -115
- package/dist/inbound-reply-dispatch-C73_7SOl.js +0 -71
- package/dist/inbound-reply-dispatch-DmL0KWLe.d.ts +0 -72
- package/dist/install-target-D7NRhfzc.js +0 -574
- package/dist/installs-Bj6jblqc.js +0 -532
- package/dist/io-CMfWWPXQ.js +0 -9738
- package/dist/io-CV844hAM.js +0 -29
- package/dist/irc-DKi1fDYI.js +0 -672
- package/dist/library-rygTG3oA.js +0 -112
- package/dist/lifecycle-core-BPlvShWY.js +0 -382
- package/dist/line-CGsemKWJ.js +0 -530
- package/dist/line-CKU3ER-n.d.ts +0 -75
- package/dist/llm-slug-generator-DlhVyMqT.js +0 -67
- package/dist/logging-5wu9k6w4.js +0 -30
- package/dist/logging-CxP9suT8.js +0 -13
- package/dist/login-qr-BcDsiwHs.js +0 -233
- package/dist/login-qr-Y8pJ5yV4.js +0 -112
- package/dist/logs-cli-XI9oVXpH.js +0 -256
- package/dist/manager-runtime-DkIlXBhD.js +0 -111
- package/dist/manager.runtime-Q0q2rJCC.js +0 -715
- package/dist/manifest-registry-DAd0SRAP.js +0 -1329
- package/dist/matrix-BI0DBBrG.js +0 -1495
- package/dist/matrix-DiABGjJR.js +0 -1269
- package/dist/matrix-fC6NrFM5.d.ts +0 -68
- package/dist/mcp-cli-BOyn_DLL.js +0 -87
- package/dist/media-understanding.runtime-DjUa7Dka.js +0 -116
- package/dist/memory-cli-CJd_vl-Y.js +0 -111
- package/dist/memory-search-CEEItIFR.js +0 -17
- package/dist/memory-search-Cv1SBrn7.js +0 -204
- package/dist/method-scopes-CQE7-bZ-.js +0 -2452
- package/dist/model-auth-markers-B1bbs9Qd.d.ts +0 -20
- package/dist/model-picker-D6_89XHg.js +0 -112
- package/dist/model-picker-Svaw-APs.js +0 -390
- package/dist/model-picker.runtime-Chi9nV7A.js +0 -125
- package/dist/model-selection-hL8i1Jbs.js +0 -653
- package/dist/model-suppression.runtime-DjWJZ0X-.js +0 -116
- package/dist/models-7qj1dG_W.js +0 -118
- package/dist/models-BPOB_xJF.js +0 -2514
- package/dist/models-cli-DdlOVUjS.js +0 -309
- package/dist/models-config-CBqUS-jX.js +0 -111
- package/dist/models-config.providers.discovery-BKB5JH9M.d.ts +0 -18
- package/dist/moldclaw-root-D6PbhbZk.js +0 -88
- package/dist/monitor-BPYhkEqF.js +0 -782
- package/dist/monitor-BuTcQ24j.js +0 -3468
- package/dist/monitor-CuXvNhFh.js +0 -113
- package/dist/monitor-D-TqSIHF.js +0 -6823
- package/dist/monitor-DRSgo9u2.js +0 -3076
- package/dist/monitor-DcHch39z.js +0 -772
- package/dist/monitor-DsHBMrXp.js +0 -115
- package/dist/monitor-shared-CL8T4gt1.js +0 -444
- package/dist/msteams-7FMwTvQG.js +0 -852
- package/dist/node-cli-BCjaSCZM.js +0 -2503
- package/dist/node-resolve-D5Hvcgyx.js +0 -835
- package/dist/nodes-cli-Dd_SNbkt.js +0 -1380
- package/dist/nostr-D8scBiYq.d.ts +0 -7
- package/dist/nostr-DBTFTxKs.js +0 -8744
- package/dist/npm-resolution-CYfb3MHG.js +0 -60
- package/dist/oauth-env-zPt5RywA.js +0 -10
- package/dist/onboard-BEFQQeig.js +0 -25
- package/dist/onboard-CJHNyxJh.js +0 -48
- package/dist/onboard-D_3UeLEN.js +0 -589
- package/dist/onboard-channels-B_JL0Djc.js +0 -1241
- package/dist/onboard-channels-CqZzHt2C.js +0 -205
- package/dist/onboard-custom-CER3Ggbq.js +0 -571
- package/dist/onboard-custom-bNRdGECb.js +0 -114
- package/dist/onboard-helpers-BK0Hsb7Y.js +0 -335
- package/dist/onboard-helpers-CXZ5RPoR.js +0 -113
- package/dist/onboard-hooks-1NsxEDjH.js +0 -72
- package/dist/onboard-remote-DuKhC_7W.js +0 -117
- package/dist/onboard-remote-OwRcDuB3.js +0 -181
- package/dist/onboard-search-Cy8dOq2W.js +0 -302
- package/dist/onboard-skills-D5phRa6r.js +0 -117
- package/dist/onboard-skills-c9qWCNe9.js +0 -133
- package/dist/outbound-media-CgNYEQWb.d.ts +0 -11
- package/dist/outbound-media-DYRO2vTD.js +0 -11
- package/dist/pairing-access-Dsiu5Mvl.d.ts +0 -21
- package/dist/pairing-cli-BOnv0TYn.js +0 -217
- package/dist/perplexity-EZwC3y2b.js +0 -24
- package/dist/persistent-dedupe-DMLOqJ23.d.ts +0 -26
- package/dist/pi-model-discovery-runtime-BToY3A6K.js +0 -111
- package/dist/pi-tools.before-tool-call.runtime-D_acPtld.js +0 -381
- package/dist/plugin-install-CgJpSjYd.js +0 -184
- package/dist/plugin-install-Cl1A4EF6.js +0 -117
- package/dist/plugin-install-plan-Dc2Z4DeU.js +0 -49
- package/dist/plugin-registry-B1UaWrQD.js +0 -49
- package/dist/plugin-registry-Cy8biwnn.js +0 -113
- package/dist/plugins-CXwvg50F.js +0 -111
- package/dist/plugins-cli-Uvzp2aYV.js +0 -917
- package/dist/policy-DsMBbEe7.js +0 -143
- package/dist/preflight-audio.runtime-hWsZIYvc.js +0 -116
- package/dist/probe-CNsSf1Uf.js +0 -6329
- package/dist/probe-CqOIrPhb.js +0 -47
- package/dist/probe-DH6gDw-h.js +0 -129
- package/dist/probe-DM16PLf4.js +0 -21
- package/dist/probe-DvAEEWYr.js +0 -1793
- package/dist/probe-auth-COfgCble.js +0 -48
- package/dist/probe-auth-I_5TX1Eh.js +0 -40
- package/dist/program-Dz80sgTU.js +0 -253
- package/dist/prompt-select-styled-wQehwFxK.js +0 -2673
- package/dist/provider-api-key-auth.runtime-BR9GU4ya.js +0 -121
- package/dist/provider-auth-choice-CdhA84kr.js +0 -126
- package/dist/provider-auth-choice-helpers-kabp_0zA.js +0 -48
- package/dist/provider-auth-choice-preference-se3zAM_2.js +0 -189
- package/dist/provider-auth-choice.runtime-BMc8-xNQ.js +0 -123
- package/dist/provider-auth-choices-CYsCViGi.js +0 -57
- package/dist/provider-auth-guidance-CMjUWlNf.js +0 -34
- package/dist/provider-auth-result-Cw6qIhO-.d.ts +0 -18
- package/dist/provider-models-BCId_Lfu.js +0 -2113
- package/dist/provider-models-Ok-DrSiY.d.ts +0 -867
- package/dist/provider-ollama-setup-B6XJZ0So.js +0 -314
- package/dist/provider-ollama-setup-lGDdTl0b.d.ts +0 -32
- package/dist/provider-onboard-CSPi7jOK.d.ts +0 -40
- package/dist/provider-onboard-Ca0TaNud.js +0 -139
- package/dist/provider-runtime.runtime-DwwkHw_7.js +0 -111
- package/dist/provider-self-hosted-setup-BEKLVGpj.js +0 -182
- package/dist/provider-self-hosted-setup-Df91By-J.d.ts +0 -61
- package/dist/provider-stream-DrUD69ai.js +0 -512
- package/dist/provider-usage-BgKHCnjr.js +0 -111
- package/dist/provider-usage-D8EZpFz9.js +0 -633
- package/dist/provider-wizard-DMdb-zj_.js +0 -152
- package/dist/push-apns-BPH6d4VV.js +0 -1038
- package/dist/pw-ai-DttfldtL.js +0 -1867
- package/dist/qmd-manager-CybcDUfk.js +0 -1570
- package/dist/qr-cli-8NcmJ8Ft.js +0 -369
- package/dist/qr-cli-DWe0Our3.js +0 -113
- package/dist/reactions-D6N0LR16.js +0 -281
- package/dist/read-only-account-inspect.discord.runtime-CqUWTRfl.js +0 -116
- package/dist/read-only-account-inspect.slack.runtime-9-jpln3q.js +0 -116
- package/dist/read-only-account-inspect.telegram.runtime-EKPI1D7n.js +0 -116
- package/dist/redact-snapshot-DwJEIVk9.js +0 -2663
- package/dist/register.agent-D3YdDirP.js +0 -439
- package/dist/register.backup-dR27qCuo.js +0 -625
- package/dist/register.configure-BjFhkkka.js +0 -252
- package/dist/register.maintenance-DiMQJIOa.js +0 -574
- package/dist/register.message-CdZsKYH1.js +0 -709
- package/dist/register.onboard-B0rV1eaO.js +0 -192
- package/dist/register.setup-wKMvohzo.js +0 -212
- package/dist/register.status-health-sessions-BJ68m6pt.js +0 -498
- package/dist/register.subclis-CnnrWt2a.js +0 -315
- package/dist/register.subclis-lSvTkC6z.js +0 -13
- package/dist/replies-BABt9b48.js +0 -110
- package/dist/resolve-channels-BqZFl2Ux.js +0 -262
- package/dist/resolve-channels-DjQLXb7B.js +0 -226
- package/dist/resolve-route-CSHDsa_m.js +0 -538
- package/dist/resolve-users-BG6HaSR5.js +0 -143
- package/dist/root-help-ohmaCyC_.js +0 -32
- package/dist/routes-4k2kpvoT.js +0 -7097
- package/dist/rpc-Cnwn4Q6L.js +0 -67
- package/dist/run-main-VYlacKA0.js +0 -424
- package/dist/runtime-D61jzMiI.d.ts +0 -26
- package/dist/runtime-discord-ops.runtime-DafrU-rI.js +0 -9078
- package/dist/runtime-slack-ops.runtime-CdXBKXwd.js +0 -4556
- package/dist/runtime-telegram-ops.runtime-B12sF7gE.js +0 -133
- package/dist/runtime-whatsapp-login.runtime-CqEudH37.js +0 -114
- package/dist/runtime-whatsapp-outbound.runtime-D5m2qyn-.js +0 -117
- package/dist/sandbox-cli-CHJiEWXB.js +0 -535
- package/dist/search-manager-BtNC3-i_.js +0 -16
- package/dist/search-manager-C7J7B3_a.js +0 -386
- package/dist/secrets-cli-C6yIWBbN.js +0 -2070
- package/dist/security-cli-BVu9BkjD.js +0 -575
- package/dist/send-BSreC7rr.js +0 -631
- package/dist/send-BsLHQG_B.js +0 -1025
- package/dist/send-BuNhp8PH.js +0 -283
- package/dist/send-DOCswVar.js +0 -100
- package/dist/send-Dl0LLErk.js +0 -629
- package/dist/server-node-events-Bq2067EG.js +0 -506
- package/dist/server-y38L7N5H.js +0 -107
- package/dist/sessions-BV8gXURR.js +0 -112
- package/dist/sessions-dl1Kc-Ci.js +0 -218
- package/dist/setup-BSPXdMuK.d.ts +0 -37
- package/dist/setup-DGszQH0_.js +0 -387
- package/dist/setup-binary-C17YnmA8.js +0 -406
- package/dist/setup-browser-CPx-nEsr.js +0 -70
- package/dist/setup-core-BByHN1ME.js +0 -143
- package/dist/setup-core-C0KPlBmL.js +0 -47
- package/dist/setup-core-Cq37G6of.js +0 -166
- package/dist/setup-core-uO84_Y75.js +0 -205
- package/dist/setup-surface-BEMi7Rmb.js +0 -490
- package/dist/setup-wizard-helpers-Ck9wDR0b.d.ts +0 -203
- package/dist/setup.finalize-BzPBa8zW.js +0 -522
- package/dist/setup.gateway-config-DdwkF-8e.js +0 -343
- package/dist/shared-BCw4SKjB.js +0 -96
- package/dist/shared-CjNzsULP.js +0 -75
- package/dist/shared-Cu1BE7ZE.js +0 -298
- package/dist/shared-DSClmyUn.js +0 -182
- package/dist/shared-DyJdGH6y.js +0 -102
- package/dist/signal-Dyv4NZsB.js +0 -114
- package/dist/skills-CbB5b27M.js +0 -853
- package/dist/skills-CnfI7Szw.js +0 -20
- package/dist/skills-cli-CavB1f_3.js +0 -292
- package/dist/skills-install-B1OBdgd0.js +0 -763
- package/dist/skills-status-B3gAmIbW.js +0 -169
- package/dist/skills-status-DrHhFgU9.js +0 -21
- package/dist/slack-BRzqnoAz.js +0 -114
- package/dist/slash-commands.runtime-BK88kgds.js +0 -128
- package/dist/slash-dispatch.runtime-COGywwJE.js +0 -141
- package/dist/slash-skill-commands.runtime-Ti4brxgh.js +0 -116
- package/dist/src-DUR6OQxI.js +0 -1701
- package/dist/status-C6dgQY9a.js +0 -131
- package/dist/status-CNK0Q7QH.js +0 -606
- package/dist/status-DBcX0DSC.js +0 -43
- package/dist/status-DKgFgbwv.js +0 -1599
- package/dist/status-Wn5lhNAc.js +0 -126
- package/dist/status-json-D2EkWqAl.js +0 -288
- package/dist/status.link-channel-D3ULIdEa.js +0 -143
- package/dist/status.scan.deps.runtime-BsjWTAm4.js +0 -126
- package/dist/status.scan.runtime-D4HbzROD.js +0 -119
- package/dist/status.summary-C3YxPrDK.js +0 -592
- package/dist/status.summary.runtime-DAkXPSaK.js +0 -118
- package/dist/status.update-B4NnN9P1.js +0 -77
- package/dist/subagent-orphan-recovery-QiQEBv36.js +0 -307
- package/dist/subagent-registry-runtime-BJatPQFK.js +0 -111
- package/dist/subscription-BhZORXN9.js +0 -100
- package/dist/subscription-QEUjQRMv.js +0 -33
- package/dist/subscription-cli-HrULlAgc.js +0 -134
- package/dist/synology-chat-DB76GWMN.js +0 -297
- package/dist/system-cli-D8jDwWuL.js +0 -94
- package/dist/telegram-BHiiqKkQ.js +0 -114
- package/dist/text-chunking-Baonm9Lu.js +0 -84
- package/dist/text-chunking-DzB11ONk.d.ts +0 -79
- package/dist/tlon-DLESxNgD.js +0 -433
- package/dist/tui-C75zi2Cl.js +0 -3834
- package/dist/tui-cli-DFwx5e6i.js +0 -137
- package/dist/types-BKldC9YN.d.ts +0 -22692
- package/dist/types-MeyueBE0.d.ts +0 -45
- package/dist/types.base-Cw0-zIvE.d.ts +0 -188
- package/dist/ui-B55NOIB6.js +0 -31
- package/dist/update--ojavYQ4.js +0 -1036
- package/dist/update-cli-Cvj5aWYM.js +0 -1503
- package/dist/update-offset-store-upatuWwX.js +0 -112
- package/dist/update-runner-DHkY_-76.js +0 -1496
- package/dist/upsert-with-lock-C171GLaR.js +0 -33
- package/dist/usage-N3bxnbmt.js +0 -115
- package/dist/web-RdvT7gKa.js +0 -112
- package/dist/web-shared-C2qHVxw1.d.ts +0 -45
- package/dist/webhook-request-guards-CosLyl01.d.ts +0 -76
- package/dist/webhook-targets-Bfnag-du.js +0 -181
- package/dist/webhook-targets-DP_EkQa4.d.ts +0 -106
- package/dist/webhooks-cli-ZpnXrq7G.js +0 -350
- package/dist/whatsapp-DNTAyZHt.js +0 -114
- package/dist/whatsapp-actions-o1zKQzKZ.js +0 -167
- package/dist/workspace-CpWi5wPr.js +0 -479
- package/dist/workspace-Ii7aRS7c.js +0 -289
- package/dist/workspace-dirs-x10McA9t.js +0 -2003
- package/dist/zalo-C9OQRYRw.d.ts +0 -9
- package/dist/zalo-zm_bYCKg.js +0 -415
- package/dist/zalouser-CvVEUvc5.js +0 -30911
- /package/dist/{account-id-B3YSn4hl.d.ts → account-id-B8ce6G_4.d.ts} +0 -0
- /package/dist/{acpx-CnNv70m2.d.ts → acpx-Ci50I9T2.d.ts} +0 -0
- /package/dist/{agent-media-payload-DE2pEcsz.d.ts → agent-media-payload-en-gS5p6.d.ts} +0 -0
- /package/dist/{allow-from-DPpHnT2A.d.ts → allow-from-cMeQ47Ot.d.ts} +0 -0
- /package/dist/{allowlist-resolution-CLFiZ6nE.d.ts → allowlist-resolution-DoAWbfXV.d.ts} +0 -0
- /package/dist/{bluebubbles-Duhu-Jer.d.ts → bluebubbles-C6yYmUl0.d.ts} +0 -0
- /package/dist/{boolean-param-BhFjB3gp.d.ts → boolean-param-CdO2TFTk.d.ts} +0 -0
- /package/dist/{channel-config-schema-DnnVMdjR.d.ts → channel-config-schema-Chp38wel.d.ts} +0 -0
- /package/dist/{channel-policy-Baq-Z06b.d.ts → channel-policy-g2h6AbYQ.d.ts} +0 -0
- /package/dist/{chat-type-DpiBgwuG.d.ts → chat-type-BLt59pPT.d.ts} +0 -0
- /package/dist/{command-format-vi4xq8e8.d.ts → command-format-BDJC05Jp.d.ts} +0 -0
- /package/dist/{diffs-DK7fVSDo.d.ts → diffs-D_iNKCyn.d.ts} +0 -0
- /package/dist/{directory-runtime-BTLPaysA.d.ts → directory-runtime-DhMex6HY.d.ts} +0 -0
- /package/dist/{exec-C01wtBHu.d.ts → exec-pjfUY4KM.d.ts} +0 -0
- /package/dist/{gaxios-fetch-compat-wZ38b3w3.js → gaxios-fetch-compat-B_vtINdV.js} +0 -0
- /package/dist/{history-CwXuP2TW.d.ts → history-aqSS5VGQ.d.ts} +0 -0
- /package/dist/{inbound-envelope-SggrBs9m.d.ts → inbound-envelope-C5hWuZod.d.ts} +0 -0
- /package/dist/{index-apAZHsDo.d.ts → index-DXVQFYGX.d.ts} +0 -0
- /package/dist/{json-store-r75IZGk9.d.ts → json-store-UnqQ5aV3.d.ts} +0 -0
- /package/dist/{keyed-async-queue-DHIr7yNe.d.ts → keyed-async-queue-guucpLw3.d.ts} +0 -0
- /package/dist/{links-HeQ3r_L0.d.ts → links-Bar0meEK.d.ts} +0 -0
- /package/dist/{markdown-to-line-CDb4Jy3V.d.ts → markdown-to-line-D8uH_KOj.d.ts} +0 -0
- /package/dist/{mattermost-DtCsxpgg.d.ts → mattermost-xl7jAFJL.d.ts} +0 -0
- /package/dist/{net-BATPDwdQ.d.ts → net-rGOKGds6.d.ts} +0 -0
- /package/dist/{nextcloud-talk-Bb2wHOwp.d.ts → nextcloud-talk-De2CZ9dV.d.ts} +0 -0
- /package/dist/{oauth-utils-u567CLT0.d.ts → oauth-utils-DzN1AlEH.d.ts} +0 -0
- /package/dist/{parse-finite-number-l3tNlrZh.d.ts → parse-finite-number-odgyqhi0.d.ts} +0 -0
- /package/dist/{provider-usage.types-C6061OVN.d.ts → provider-usage.types-EDE9o-H_.d.ts} +0 -0
- /package/dist/{reply-history-BDsFnZFl.d.ts → reply-history-CVuU31xe.d.ts} +0 -0
- /package/dist/{reply-payload-CCvM4W9u.d.ts → reply-payload-CHkpBYwL.d.ts} +0 -0
- /package/dist/{request-url-C54l4-xC.d.ts → request-url-DHisbiHY.d.ts} +0 -0
- /package/dist/{run-command-D3RqWcHu.d.ts → run-command-y0Cndsb1.d.ts} +0 -0
- /package/dist/{secret-input-schema-BLBt-NAP.d.ts → secret-input-schema-b1vpYDQN.d.ts} +0 -0
- /package/dist/{session-key-BQ2-bR-9.d.ts → session-key-DTHQl57f.d.ts} +0 -0
- /package/dist/{ssh-config-C4mcH9Ly.js → ssh-config-hEHBfU2_.js} +0 -0
- /package/dist/{testing-DLkhGsoz.d.ts → testing-DszuZXgK.d.ts} +0 -0
- /package/dist/{thinking-DRkjX18p.d.ts → thinking-IwXTGSeT.d.ts} +0 -0
- /package/dist/{tool-send-CMMD1uDu.d.ts → tool-send-DWHRmKpz.d.ts} +0 -0
- /package/dist/{vllm-defaults-CcGuf4hL.d.ts → vllm-defaults-CrxZgE6-.d.ts} +0 -0
- /package/dist/{wait-Daog8bxM.d.ts → wait-wDWw_MTI.d.ts} +0 -0
- /package/dist/{webhook-memory-guards-C5MrExwT.d.ts → webhook-memory-guards-DreORuJy.d.ts} +0 -0
- /package/dist/{windows-spawn-j2l-dqu8.d.ts → windows-spawn-BIzH92x2.d.ts} +0 -0
- /package/dist/{zod-schema.agent-runtime-krMrBnIn.d.ts → zod-schema.agent-runtime-CP2rmis3.d.ts} +0 -0
- /package/dist/{zod-schema.core-BNDieZDZ.d.ts → zod-schema.core-Foi1tYwi.d.ts} +0 -0
|
@@ -1,4556 +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 { t as createNonExitingRuntime } from "./runtime-_tQz41uA.js";
|
|
10
|
-
import "./ansi-BPhP6LBZ.js";
|
|
11
|
-
import "./subsystem-CPmDTJ2P.js";
|
|
12
|
-
import "./boolean-B6zcAynR.js";
|
|
13
|
-
import "./env-D42cffog.js";
|
|
14
|
-
import "./warning-filter-B1UOeM0G.js";
|
|
15
|
-
import "./utils-C7ykRPCQ.js";
|
|
16
|
-
import "./links-BcahUP5U.js";
|
|
17
|
-
import "./setup-binary-C17YnmA8.js";
|
|
18
|
-
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-1kPLbBwI.js";
|
|
19
|
-
import "./model-selection-hL8i1Jbs.js";
|
|
20
|
-
import "./agent-scope-C-YmLnnb.js";
|
|
21
|
-
import { d as resolveThreadSessionKeys, l as normalizeMainKey } from "./session-key-UoG7Kfw5.js";
|
|
22
|
-
import { n as normalizeAccountId } from "./account-id-BuyZMNja.js";
|
|
23
|
-
import { r as normalizeStringEntries } from "./string-normalization-28MhO2sd.js";
|
|
24
|
-
import "./boundary-file-read-tPYh_8fH.js";
|
|
25
|
-
import "./logger-BGzLUitz.js";
|
|
26
|
-
import "./exec-CvEtXqTZ.js";
|
|
27
|
-
import "./workspace-CpWi5wPr.js";
|
|
28
|
-
import { c as loadConfig, cn as mapStreamingModeToSlackLegacyDraftStreamMode, mn as resolveSlackStreamingMode, pn as resolveSlackNativeStreaming, y as writeConfigFile } from "./io-CMfWWPXQ.js";
|
|
29
|
-
import "./host-env-security-DQ2i_W12.js";
|
|
30
|
-
import "./safe-text-Cnulee_z.js";
|
|
31
|
-
import "./version-T8nMYUnU.js";
|
|
32
|
-
import { c as normalizeResolvedSecretInputString } from "./types.secrets-Ca-9L8vU.js";
|
|
33
|
-
import "./env-substitution-68cyvF5h.js";
|
|
34
|
-
import "./config-state-h5jUoHya.js";
|
|
35
|
-
import "./network-mode-BtWXzwYn.js";
|
|
36
|
-
import "./registry-C1pRrsQl.js";
|
|
37
|
-
import "./manifest-registry-DAd0SRAP.js";
|
|
38
|
-
import "./ip-C4YAIpr4.js";
|
|
39
|
-
import "./zod-schema.core-DvwgNmpd.js";
|
|
40
|
-
import "./config-sW57gztj.js";
|
|
41
|
-
import "./audit-fs-CMb-YUHX.js";
|
|
42
|
-
import "./resolve-PSlwZjg3.js";
|
|
43
|
-
import "./provider-web-search-CcUC9ktE.js";
|
|
44
|
-
import { b as resolveTextChunkLimit, r as chunkItems, t as withTimeout } from "./text-runtime-Cfq-Uyx0.js";
|
|
45
|
-
import { c as resolveNativeCommandsEnabled, l as resolveNativeSkillsEnabled } from "./workspace-dirs-x10McA9t.js";
|
|
46
|
-
import "./config-BwkGZjD5.js";
|
|
47
|
-
import "./tailnet-fFTz5Twr.js";
|
|
48
|
-
import "./net-K181nxTH.js";
|
|
49
|
-
import "./credentials-D-5Pb-aZ.js";
|
|
50
|
-
import "./routes-4k2kpvoT.js";
|
|
51
|
-
import "./frontmatter-Cgg0ICvh.js";
|
|
52
|
-
import "./env-overrides-DBQl3LRc.js";
|
|
53
|
-
import "./path-alias-guards-BtSO7sk7.js";
|
|
54
|
-
import "./skills-CbB5b27M.js";
|
|
55
|
-
import "./ports-Ca74cFb2.js";
|
|
56
|
-
import "./ports-lsof-CoiADo0p.js";
|
|
57
|
-
import "./ssh-tunnel-DsY-9yao.js";
|
|
58
|
-
import "./image-ops-Ck_D_vpe.js";
|
|
59
|
-
import "./fs-safe-CRXFoBmh.js";
|
|
60
|
-
import "./mime-DGFQe4XX.js";
|
|
61
|
-
import { t as generateSecureToken } from "./secure-random-DCbze_y8.js";
|
|
62
|
-
import "./server-middleware-Djfoa1s0.js";
|
|
63
|
-
import "./message-channel-DFE4FuE_.js";
|
|
64
|
-
import { i as resolveAgentRoute } from "./resolve-route-CSHDsa_m.js";
|
|
65
|
-
import "./internal-hooks-83AcmxP3.js";
|
|
66
|
-
import { n as shouldAckReaction, t as removeAckReactionAfterReply } from "./ack-reactions-DCQB2y3w.js";
|
|
67
|
-
import { f as warnMissingProviderGroupPolicyFallbackOnce, l as resolveDefaultGroupPolicy, t as evaluateGroupRouteAccessForPolicy, u as resolveOpenProviderRuntimeGroupPolicy } from "./group-access-UAqUyJod.js";
|
|
68
|
-
import { b as enqueueSystemEvent } from "./lazy-runtime-BoGB4usD.js";
|
|
69
|
-
import "./config-schema-BNU4GQh_.js";
|
|
70
|
-
import "./method-scopes-CQE7-bZ-.js";
|
|
71
|
-
import "./session-cost-usage-DWgQk6XT.js";
|
|
72
|
-
import { l as resolveStorePath } from "./paths-ApLcu1Uu.js";
|
|
73
|
-
import "./routing-DQ-fpTaA.js";
|
|
74
|
-
import "./send-BuNhp8PH.js";
|
|
75
|
-
import "./node-resolve-D5Hvcgyx.js";
|
|
76
|
-
import "./provider-stream-DrUD69ai.js";
|
|
77
|
-
import "./identity-file-sshkKKIr.js";
|
|
78
|
-
import "./provider-models-BCId_Lfu.js";
|
|
79
|
-
import "./secret-file-p1IhQzwJ.js";
|
|
80
|
-
import "./logging-Dy7UYzIN.js";
|
|
81
|
-
import "./runtime-env-BlEtPF6b.js";
|
|
82
|
-
import "./registry-BFMbkmgR.js";
|
|
83
|
-
import "./provider-onboard-Ca0TaNud.js";
|
|
84
|
-
import "./model-definitions-Cyyzm6Kr.js";
|
|
85
|
-
import "./usage-N3bxnbmt.js";
|
|
86
|
-
import "./device-identity-CRfhC3_s.js";
|
|
87
|
-
import "./auth-Byfp0flq.js";
|
|
88
|
-
import "./subscription-BhZORXN9.js";
|
|
89
|
-
import "./diagnostic-D96Xaqrj.js";
|
|
90
|
-
import "./message-hook-mappers-CeiHXgSQ.js";
|
|
91
|
-
import "./json-store--7cBPxTG.js";
|
|
92
|
-
import "./call-ByEzDJ1_.js";
|
|
93
|
-
import "./multimodal-BJBBn_4F.js";
|
|
94
|
-
import "./memory-search-Cv1SBrn7.js";
|
|
95
|
-
import "./query-expansion-D_Mm5Hhi.js";
|
|
96
|
-
import "./search-manager-C7J7B3_a.js";
|
|
97
|
-
import "./core-CvDzLs7B.js";
|
|
98
|
-
import "./issue-format-B0SI57Es.js";
|
|
99
|
-
import "./logging-CxP9suT8.js";
|
|
100
|
-
import "./note-dOl5kPAy.js";
|
|
101
|
-
import "./state-paths-DsMoTg25.js";
|
|
102
|
-
import "./config-value-CtTWALxG.js";
|
|
103
|
-
import "./command-secret-targets-BFF4x_RB.js";
|
|
104
|
-
import "./brave-BoWimrLe.js";
|
|
105
|
-
import "./provider-usage-D8EZpFz9.js";
|
|
106
|
-
import "./perplexity-EZwC3y2b.js";
|
|
107
|
-
import "./restart-stale-pids-CPF1_61W.js";
|
|
108
|
-
import "./delivery-queue-BOf5wYIc.js";
|
|
109
|
-
import "./pairing-token-bu1e6z6X.js";
|
|
110
|
-
import "./accounts-J2OhhhQi.js";
|
|
111
|
-
import "./process-runtime-D27SftX_.js";
|
|
112
|
-
import "./audit-CIWW1Aqm.js";
|
|
113
|
-
import "./cli-runtime-DTCHPjCi.js";
|
|
114
|
-
import "./cli-utils-BCuSS4l6.js";
|
|
115
|
-
import "./help-format-BFzPm_8V.js";
|
|
116
|
-
import "./progress-Cwq59vgZ.js";
|
|
117
|
-
import { r as createConnectedChannelStatusPatch } from "./gateway-runtime-D89mSQPB.js";
|
|
118
|
-
import "./plugin-runtime-CUDsBEl9.js";
|
|
119
|
-
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-Cfn6lmMK.js";
|
|
120
|
-
import { n as resolveSlackUserAllowlist, t as resolveSlackChannelAllowlist } from "./resolve-channels-DjQLXb7B.js";
|
|
121
|
-
import { a as resolveSlackThreadTs, i as readSlackReplyBlocks, n as deliverReplies, t as createSlackReplyDeliveryPlan } from "./replies-BABt9b48.js";
|
|
122
|
-
import * as SlackBoltNamespace from "@slack/bolt";
|
|
123
|
-
import SlackBolt from "@slack/bolt";
|
|
124
|
-
//#region extensions/slack/src/directory-live.ts
|
|
125
|
-
function resolveReadToken(params) {
|
|
126
|
-
const account = resolveSlackAccount({
|
|
127
|
-
cfg: params.cfg,
|
|
128
|
-
accountId: params.accountId
|
|
129
|
-
});
|
|
130
|
-
return account.userToken ?? account.botToken?.trim();
|
|
131
|
-
}
|
|
132
|
-
function normalizeQuery(value) {
|
|
133
|
-
return value?.trim().toLowerCase() ?? "";
|
|
134
|
-
}
|
|
135
|
-
function buildUserRank(user) {
|
|
136
|
-
let rank = 0;
|
|
137
|
-
if (!user.deleted) rank += 2;
|
|
138
|
-
if (!user.is_bot && !user.is_app_user) rank += 1;
|
|
139
|
-
return rank;
|
|
140
|
-
}
|
|
141
|
-
function buildChannelRank(channel) {
|
|
142
|
-
return channel.is_archived ? 0 : 1;
|
|
143
|
-
}
|
|
144
|
-
async function listSlackDirectoryPeersLive(params) {
|
|
145
|
-
const token = resolveReadToken(params);
|
|
146
|
-
if (!token) return [];
|
|
147
|
-
const client = createSlackWebClient(token);
|
|
148
|
-
const query = normalizeQuery(params.query);
|
|
149
|
-
const members = [];
|
|
150
|
-
let cursor;
|
|
151
|
-
do {
|
|
152
|
-
const res = await client.users.list({
|
|
153
|
-
limit: 200,
|
|
154
|
-
cursor
|
|
155
|
-
});
|
|
156
|
-
if (Array.isArray(res.members)) members.push(...res.members);
|
|
157
|
-
const next = res.response_metadata?.next_cursor?.trim();
|
|
158
|
-
cursor = next ? next : void 0;
|
|
159
|
-
} while (cursor);
|
|
160
|
-
const rows = members.filter((member) => {
|
|
161
|
-
const candidates = [
|
|
162
|
-
member.profile?.display_name || member.profile?.real_name || member.real_name,
|
|
163
|
-
member.name,
|
|
164
|
-
member.profile?.email
|
|
165
|
-
].map((item) => item?.trim().toLowerCase()).filter(Boolean);
|
|
166
|
-
if (!query) return true;
|
|
167
|
-
return candidates.some((candidate) => candidate?.includes(query));
|
|
168
|
-
}).map((member) => {
|
|
169
|
-
const id = member.id?.trim();
|
|
170
|
-
if (!id) return null;
|
|
171
|
-
const handle = member.name?.trim();
|
|
172
|
-
const display = member.profile?.display_name?.trim() || member.profile?.real_name?.trim() || member.real_name?.trim() || handle;
|
|
173
|
-
return {
|
|
174
|
-
kind: "user",
|
|
175
|
-
id: `user:${id}`,
|
|
176
|
-
name: display || void 0,
|
|
177
|
-
handle: handle ? `@${handle}` : void 0,
|
|
178
|
-
rank: buildUserRank(member),
|
|
179
|
-
raw: member
|
|
180
|
-
};
|
|
181
|
-
}).filter(Boolean);
|
|
182
|
-
if (typeof params.limit === "number" && params.limit > 0) return rows.slice(0, params.limit);
|
|
183
|
-
return rows;
|
|
184
|
-
}
|
|
185
|
-
async function listSlackDirectoryGroupsLive(params) {
|
|
186
|
-
const token = resolveReadToken(params);
|
|
187
|
-
if (!token) return [];
|
|
188
|
-
const client = createSlackWebClient(token);
|
|
189
|
-
const query = normalizeQuery(params.query);
|
|
190
|
-
const channels = [];
|
|
191
|
-
let cursor;
|
|
192
|
-
do {
|
|
193
|
-
const res = await client.conversations.list({
|
|
194
|
-
types: "public_channel,private_channel",
|
|
195
|
-
exclude_archived: false,
|
|
196
|
-
limit: 1e3,
|
|
197
|
-
cursor
|
|
198
|
-
});
|
|
199
|
-
if (Array.isArray(res.channels)) channels.push(...res.channels);
|
|
200
|
-
const next = res.response_metadata?.next_cursor?.trim();
|
|
201
|
-
cursor = next ? next : void 0;
|
|
202
|
-
} while (cursor);
|
|
203
|
-
const rows = channels.filter((channel) => {
|
|
204
|
-
const name = channel.name?.trim().toLowerCase();
|
|
205
|
-
if (!query) return true;
|
|
206
|
-
return Boolean(name && name.includes(query));
|
|
207
|
-
}).map((channel) => {
|
|
208
|
-
const id = channel.id?.trim();
|
|
209
|
-
const name = channel.name?.trim();
|
|
210
|
-
if (!id || !name) return null;
|
|
211
|
-
return {
|
|
212
|
-
kind: "group",
|
|
213
|
-
id: `channel:${id}`,
|
|
214
|
-
name,
|
|
215
|
-
handle: `#${name}`,
|
|
216
|
-
rank: buildChannelRank(channel),
|
|
217
|
-
raw: channel
|
|
218
|
-
};
|
|
219
|
-
}).filter(Boolean);
|
|
220
|
-
if (typeof params.limit === "number" && params.limit > 0) return rows.slice(0, params.limit);
|
|
221
|
-
return rows;
|
|
222
|
-
}
|
|
223
|
-
//#endregion
|
|
224
|
-
//#region extensions/slack/src/monitor/commands.ts
|
|
225
|
-
/**
|
|
226
|
-
* Strip Slack mentions (<@U123>, <@U123|name>) so command detection works on
|
|
227
|
-
* normalized text. Use in both prepare and debounce gate for consistency.
|
|
228
|
-
*/
|
|
229
|
-
function stripSlackMentionsForCommandDetection(text) {
|
|
230
|
-
return (text ?? "").replace(/<@[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
231
|
-
}
|
|
232
|
-
function normalizeSlackSlashCommandName(raw) {
|
|
233
|
-
return raw.replace(/^\/+/, "");
|
|
234
|
-
}
|
|
235
|
-
function resolveSlackSlashCommandConfig(raw) {
|
|
236
|
-
const name = normalizeSlackSlashCommandName(raw?.name?.trim() || "moldclaw") || "moldclaw";
|
|
237
|
-
return {
|
|
238
|
-
enabled: raw?.enabled === true,
|
|
239
|
-
name,
|
|
240
|
-
sessionPrefix: raw?.sessionPrefix?.trim() || "slack:slash",
|
|
241
|
-
ephemeral: raw?.ephemeral !== false
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
function buildSlackSlashCommandMatcher(name) {
|
|
245
|
-
const escaped = normalizeSlackSlashCommandName(name).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
246
|
-
return new RegExp(`^/?${escaped}$`);
|
|
247
|
-
}
|
|
248
|
-
//#endregion
|
|
249
|
-
//#region extensions/slack/src/monitor/policy.ts
|
|
250
|
-
function isSlackChannelAllowedByPolicy(params) {
|
|
251
|
-
return evaluateGroupRouteAccessForPolicy({
|
|
252
|
-
groupPolicy: params.groupPolicy,
|
|
253
|
-
routeAllowlistConfigured: params.channelAllowlistConfigured,
|
|
254
|
-
routeMatched: params.channelAllowed
|
|
255
|
-
}).allowed;
|
|
256
|
-
}
|
|
257
|
-
//#endregion
|
|
258
|
-
//#region extensions/slack/src/monitor/channel-config.ts
|
|
259
|
-
function firstDefined(...values) {
|
|
260
|
-
for (const value of values) if (typeof value !== "undefined") return value;
|
|
261
|
-
}
|
|
262
|
-
function resolveSlackChannelLabel(params) {
|
|
263
|
-
const channelName = params.channelName?.trim();
|
|
264
|
-
if (channelName) return `#${normalizeSlackSlug(channelName) || channelName}`;
|
|
265
|
-
const channelId = params.channelId?.trim();
|
|
266
|
-
return channelId ? `#${channelId}` : "unknown channel";
|
|
267
|
-
}
|
|
268
|
-
function resolveSlackChannelConfig(params) {
|
|
269
|
-
const { channelId, channelName, channels, channelKeys, defaultRequireMention, allowNameMatching } = params;
|
|
270
|
-
const entries = channels ?? {};
|
|
271
|
-
const keys = channelKeys ?? Object.keys(entries);
|
|
272
|
-
const normalizedName = channelName ? normalizeSlackSlug(channelName) : "";
|
|
273
|
-
const directName = channelName ? channelName.trim() : "";
|
|
274
|
-
const channelIdLower = channelId.toLowerCase();
|
|
275
|
-
const channelIdUpper = channelId.toUpperCase();
|
|
276
|
-
const match = resolveChannelEntryMatchWithFallback({
|
|
277
|
-
entries,
|
|
278
|
-
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),
|
|
279
|
-
wildcardKey: "*"
|
|
280
|
-
});
|
|
281
|
-
const { entry: matched, wildcardEntry: fallback } = match;
|
|
282
|
-
const requireMentionDefault = defaultRequireMention ?? true;
|
|
283
|
-
if (keys.length === 0) return {
|
|
284
|
-
allowed: true,
|
|
285
|
-
requireMention: requireMentionDefault
|
|
286
|
-
};
|
|
287
|
-
if (!matched && !fallback) return {
|
|
288
|
-
allowed: false,
|
|
289
|
-
requireMention: requireMentionDefault
|
|
290
|
-
};
|
|
291
|
-
const resolved = matched ?? fallback ?? {};
|
|
292
|
-
return applyChannelMatchMeta({
|
|
293
|
-
allowed: firstDefined(resolved.enabled, resolved.allow, fallback?.enabled, fallback?.allow, true) ?? true,
|
|
294
|
-
requireMention: firstDefined(resolved.requireMention, fallback?.requireMention, requireMentionDefault) ?? requireMentionDefault,
|
|
295
|
-
allowBots: firstDefined(resolved.allowBots, fallback?.allowBots),
|
|
296
|
-
users: firstDefined(resolved.users, fallback?.users),
|
|
297
|
-
skills: firstDefined(resolved.skills, fallback?.skills),
|
|
298
|
-
systemPrompt: firstDefined(resolved.systemPrompt, fallback?.systemPrompt)
|
|
299
|
-
}, match);
|
|
300
|
-
}
|
|
301
|
-
//#endregion
|
|
302
|
-
//#region extensions/slack/src/monitor/channel-type.ts
|
|
303
|
-
function inferSlackChannelType(channelId) {
|
|
304
|
-
const trimmed = channelId?.trim();
|
|
305
|
-
if (!trimmed) return;
|
|
306
|
-
if (trimmed.startsWith("D")) return "im";
|
|
307
|
-
if (trimmed.startsWith("C")) return "channel";
|
|
308
|
-
if (trimmed.startsWith("G")) return "group";
|
|
309
|
-
}
|
|
310
|
-
function normalizeSlackChannelType(channelType, channelId) {
|
|
311
|
-
const normalized = channelType?.trim().toLowerCase();
|
|
312
|
-
const inferred = inferSlackChannelType(channelId);
|
|
313
|
-
if (normalized === "im" || normalized === "mpim" || normalized === "channel" || normalized === "group") {
|
|
314
|
-
if (inferred === "im" && normalized !== "im") return "im";
|
|
315
|
-
return normalized;
|
|
316
|
-
}
|
|
317
|
-
return inferred ?? "channel";
|
|
318
|
-
}
|
|
319
|
-
//#endregion
|
|
320
|
-
//#region extensions/slack/src/monitor/context.ts
|
|
321
|
-
function createSlackMonitorContext(params) {
|
|
322
|
-
const channelHistories = /* @__PURE__ */ new Map();
|
|
323
|
-
const logger = getChildLogger({ module: "slack-auto-reply" });
|
|
324
|
-
const channelCache = /* @__PURE__ */ new Map();
|
|
325
|
-
const userCache = /* @__PURE__ */ new Map();
|
|
326
|
-
const seenMessages = createDedupeCache({
|
|
327
|
-
ttlMs: 6e4,
|
|
328
|
-
maxSize: 500
|
|
329
|
-
});
|
|
330
|
-
const allowFrom = normalizeAllowList(params.allowFrom);
|
|
331
|
-
const groupDmChannels = normalizeAllowList(params.groupDmChannels);
|
|
332
|
-
const groupDmChannelsLower = normalizeAllowListLower(groupDmChannels);
|
|
333
|
-
const defaultRequireMention = params.defaultRequireMention ?? true;
|
|
334
|
-
const hasChannelAllowlistConfig = Object.keys(params.channelsConfig ?? {}).length > 0;
|
|
335
|
-
const channelsConfigKeys = Object.keys(params.channelsConfig ?? {});
|
|
336
|
-
const markMessageSeen = (channelId, ts) => {
|
|
337
|
-
if (!channelId || !ts) return false;
|
|
338
|
-
return seenMessages.check(`${channelId}:${ts}`);
|
|
339
|
-
};
|
|
340
|
-
const resolveSlackSystemEventSessionKey = (p) => {
|
|
341
|
-
const channelId = p.channelId?.trim() ?? "";
|
|
342
|
-
if (!channelId) return params.mainKey;
|
|
343
|
-
const channelType = normalizeSlackChannelType(p.channelType, channelId);
|
|
344
|
-
const isDirectMessage = channelType === "im";
|
|
345
|
-
const isGroup = channelType === "mpim";
|
|
346
|
-
const from = isDirectMessage ? `slack:${channelId}` : isGroup ? `slack:group:${channelId}` : `slack:channel:${channelId}`;
|
|
347
|
-
const chatType = isDirectMessage ? "direct" : isGroup ? "group" : "channel";
|
|
348
|
-
const senderId = p.senderId?.trim() ?? "";
|
|
349
|
-
try {
|
|
350
|
-
const peerKind = isDirectMessage ? "direct" : isGroup ? "group" : "channel";
|
|
351
|
-
const peerId = isDirectMessage ? senderId : channelId;
|
|
352
|
-
if (peerId) return resolveAgentRoute({
|
|
353
|
-
cfg: params.cfg,
|
|
354
|
-
channel: "slack",
|
|
355
|
-
accountId: params.accountId,
|
|
356
|
-
teamId: params.teamId,
|
|
357
|
-
peer: {
|
|
358
|
-
kind: peerKind,
|
|
359
|
-
id: peerId
|
|
360
|
-
}
|
|
361
|
-
}).sessionKey;
|
|
362
|
-
} catch {}
|
|
363
|
-
return resolveSessionKey(params.sessionScope, {
|
|
364
|
-
From: from,
|
|
365
|
-
ChatType: chatType,
|
|
366
|
-
Provider: "slack"
|
|
367
|
-
}, params.mainKey);
|
|
368
|
-
};
|
|
369
|
-
const resolveChannelName = async (channelId) => {
|
|
370
|
-
const cached = channelCache.get(channelId);
|
|
371
|
-
if (cached) return cached;
|
|
372
|
-
try {
|
|
373
|
-
const info = await params.app.client.conversations.info({
|
|
374
|
-
token: params.botToken,
|
|
375
|
-
channel: channelId
|
|
376
|
-
});
|
|
377
|
-
const name = info.channel && "name" in info.channel ? info.channel.name : void 0;
|
|
378
|
-
const channel = info.channel ?? void 0;
|
|
379
|
-
const entry = {
|
|
380
|
-
name,
|
|
381
|
-
type: channel?.is_im ? "im" : channel?.is_mpim ? "mpim" : channel?.is_channel ? "channel" : channel?.is_group ? "group" : void 0,
|
|
382
|
-
topic: channel && "topic" in channel ? channel.topic?.value ?? void 0 : void 0,
|
|
383
|
-
purpose: channel && "purpose" in channel ? channel.purpose?.value ?? void 0 : void 0
|
|
384
|
-
};
|
|
385
|
-
channelCache.set(channelId, entry);
|
|
386
|
-
return entry;
|
|
387
|
-
} catch {
|
|
388
|
-
return {};
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
const resolveUserName = async (userId) => {
|
|
392
|
-
const cached = userCache.get(userId);
|
|
393
|
-
if (cached) return cached;
|
|
394
|
-
try {
|
|
395
|
-
const info = await params.app.client.users.info({
|
|
396
|
-
token: params.botToken,
|
|
397
|
-
user: userId
|
|
398
|
-
});
|
|
399
|
-
const profile = info.user?.profile;
|
|
400
|
-
const entry = { name: profile?.display_name || profile?.real_name || info.user?.name || void 0 };
|
|
401
|
-
userCache.set(userId, entry);
|
|
402
|
-
return entry;
|
|
403
|
-
} catch {
|
|
404
|
-
return {};
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
|
-
const setSlackThreadStatus = async (p) => {
|
|
408
|
-
if (!p.threadTs) return;
|
|
409
|
-
const payload = {
|
|
410
|
-
token: params.botToken,
|
|
411
|
-
channel_id: p.channelId,
|
|
412
|
-
thread_ts: p.threadTs,
|
|
413
|
-
status: p.status
|
|
414
|
-
};
|
|
415
|
-
const client = params.app.client;
|
|
416
|
-
try {
|
|
417
|
-
if (client.assistant?.threads?.setStatus) {
|
|
418
|
-
await client.assistant.threads.setStatus(payload);
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
if (typeof client.apiCall === "function") await client.apiCall("assistant.threads.setStatus", payload);
|
|
422
|
-
} catch (err) {
|
|
423
|
-
logVerbose(`slack status update failed for channel ${p.channelId}: ${String(err)}`);
|
|
424
|
-
}
|
|
425
|
-
};
|
|
426
|
-
const isChannelAllowed = (p) => {
|
|
427
|
-
const channelType = normalizeSlackChannelType(p.channelType, p.channelId);
|
|
428
|
-
const isDirectMessage = channelType === "im";
|
|
429
|
-
const isGroupDm = channelType === "mpim";
|
|
430
|
-
const isRoom = channelType === "channel" || channelType === "group";
|
|
431
|
-
if (isDirectMessage && !params.dmEnabled) return false;
|
|
432
|
-
if (isGroupDm && !params.groupDmEnabled) return false;
|
|
433
|
-
if (isGroupDm && groupDmChannels.length > 0) {
|
|
434
|
-
const candidates = [
|
|
435
|
-
p.channelId,
|
|
436
|
-
p.channelName ? `#${p.channelName}` : void 0,
|
|
437
|
-
p.channelName,
|
|
438
|
-
p.channelName ? normalizeSlackSlug(p.channelName) : void 0
|
|
439
|
-
].filter((value) => Boolean(value)).map((value) => value.toLowerCase());
|
|
440
|
-
if (!(groupDmChannelsLower.includes("*") || candidates.some((candidate) => groupDmChannelsLower.includes(candidate)))) return false;
|
|
441
|
-
}
|
|
442
|
-
if (isRoom && p.channelId) {
|
|
443
|
-
const channelConfig = resolveSlackChannelConfig({
|
|
444
|
-
channelId: p.channelId,
|
|
445
|
-
channelName: p.channelName,
|
|
446
|
-
channels: params.channelsConfig,
|
|
447
|
-
channelKeys: channelsConfigKeys,
|
|
448
|
-
defaultRequireMention,
|
|
449
|
-
allowNameMatching: params.allowNameMatching
|
|
450
|
-
});
|
|
451
|
-
const channelMatchMeta = formatAllowlistMatchMeta(channelConfig);
|
|
452
|
-
const channelAllowed = channelConfig?.allowed !== false;
|
|
453
|
-
const channelAllowlistConfigured = hasChannelAllowlistConfig;
|
|
454
|
-
if (!isSlackChannelAllowedByPolicy({
|
|
455
|
-
groupPolicy: params.groupPolicy,
|
|
456
|
-
channelAllowlistConfigured,
|
|
457
|
-
channelAllowed
|
|
458
|
-
})) {
|
|
459
|
-
logVerbose(`slack: drop channel ${p.channelId} (groupPolicy=${params.groupPolicy}, ${channelMatchMeta})`);
|
|
460
|
-
return false;
|
|
461
|
-
}
|
|
462
|
-
const hasExplicitConfig = Boolean(channelConfig?.matchSource);
|
|
463
|
-
if (!channelAllowed && (params.groupPolicy !== "open" || hasExplicitConfig)) {
|
|
464
|
-
logVerbose(`slack: drop channel ${p.channelId} (${channelMatchMeta})`);
|
|
465
|
-
return false;
|
|
466
|
-
}
|
|
467
|
-
logVerbose(`slack: allow channel ${p.channelId} (${channelMatchMeta})`);
|
|
468
|
-
}
|
|
469
|
-
return true;
|
|
470
|
-
};
|
|
471
|
-
const shouldDropMismatchedSlackEvent = (body) => {
|
|
472
|
-
if (!body || typeof body !== "object") return false;
|
|
473
|
-
const raw = body;
|
|
474
|
-
const incomingApiAppId = typeof raw.api_app_id === "string" ? raw.api_app_id : "";
|
|
475
|
-
const incomingTeamId = typeof raw.team_id === "string" ? raw.team_id : typeof raw.team?.id === "string" ? raw.team.id : "";
|
|
476
|
-
if (params.apiAppId && incomingApiAppId && incomingApiAppId !== params.apiAppId) {
|
|
477
|
-
logVerbose(`slack: drop event with api_app_id=${incomingApiAppId} (expected ${params.apiAppId})`);
|
|
478
|
-
return true;
|
|
479
|
-
}
|
|
480
|
-
if (params.teamId && incomingTeamId && incomingTeamId !== params.teamId) {
|
|
481
|
-
logVerbose(`slack: drop event with team_id=${incomingTeamId} (expected ${params.teamId})`);
|
|
482
|
-
return true;
|
|
483
|
-
}
|
|
484
|
-
return false;
|
|
485
|
-
};
|
|
486
|
-
return {
|
|
487
|
-
cfg: params.cfg,
|
|
488
|
-
accountId: params.accountId,
|
|
489
|
-
botToken: params.botToken,
|
|
490
|
-
app: params.app,
|
|
491
|
-
runtime: params.runtime,
|
|
492
|
-
botUserId: params.botUserId,
|
|
493
|
-
teamId: params.teamId,
|
|
494
|
-
apiAppId: params.apiAppId,
|
|
495
|
-
historyLimit: params.historyLimit,
|
|
496
|
-
channelHistories,
|
|
497
|
-
sessionScope: params.sessionScope,
|
|
498
|
-
mainKey: params.mainKey,
|
|
499
|
-
dmEnabled: params.dmEnabled,
|
|
500
|
-
dmPolicy: params.dmPolicy,
|
|
501
|
-
allowFrom,
|
|
502
|
-
allowNameMatching: params.allowNameMatching,
|
|
503
|
-
groupDmEnabled: params.groupDmEnabled,
|
|
504
|
-
groupDmChannels,
|
|
505
|
-
defaultRequireMention,
|
|
506
|
-
channelsConfig: params.channelsConfig,
|
|
507
|
-
channelsConfigKeys,
|
|
508
|
-
groupPolicy: params.groupPolicy,
|
|
509
|
-
useAccessGroups: params.useAccessGroups,
|
|
510
|
-
reactionMode: params.reactionMode,
|
|
511
|
-
reactionAllowlist: params.reactionAllowlist,
|
|
512
|
-
replyToMode: params.replyToMode,
|
|
513
|
-
threadHistoryScope: params.threadHistoryScope,
|
|
514
|
-
threadInheritParent: params.threadInheritParent,
|
|
515
|
-
slashCommand: params.slashCommand,
|
|
516
|
-
textLimit: params.textLimit,
|
|
517
|
-
ackReactionScope: params.ackReactionScope,
|
|
518
|
-
typingReaction: params.typingReaction,
|
|
519
|
-
mediaMaxBytes: params.mediaMaxBytes,
|
|
520
|
-
removeAckAfterReply: params.removeAckAfterReply,
|
|
521
|
-
logger,
|
|
522
|
-
markMessageSeen,
|
|
523
|
-
shouldDropMismatchedSlackEvent,
|
|
524
|
-
resolveSlackSystemEventSessionKey,
|
|
525
|
-
isChannelAllowed,
|
|
526
|
-
resolveChannelName,
|
|
527
|
-
resolveUserName,
|
|
528
|
-
setSlackThreadStatus
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
//#endregion
|
|
532
|
-
//#region extensions/slack/src/channel-migration.ts
|
|
533
|
-
function resolveAccountChannels(cfg, accountId) {
|
|
534
|
-
if (!accountId) return {};
|
|
535
|
-
const normalized = normalizeAccountId(accountId);
|
|
536
|
-
const accounts = cfg.channels?.slack?.accounts;
|
|
537
|
-
if (!accounts || typeof accounts !== "object") return {};
|
|
538
|
-
const exact = accounts[normalized];
|
|
539
|
-
if (exact?.channels) return { channels: exact.channels };
|
|
540
|
-
const matchKey = Object.keys(accounts).find((key) => key.toLowerCase() === normalized.toLowerCase());
|
|
541
|
-
return { channels: matchKey ? accounts[matchKey]?.channels : void 0 };
|
|
542
|
-
}
|
|
543
|
-
function migrateSlackChannelsInPlace(channels, oldChannelId, newChannelId) {
|
|
544
|
-
if (!channels) return {
|
|
545
|
-
migrated: false,
|
|
546
|
-
skippedExisting: false
|
|
547
|
-
};
|
|
548
|
-
if (oldChannelId === newChannelId) return {
|
|
549
|
-
migrated: false,
|
|
550
|
-
skippedExisting: false
|
|
551
|
-
};
|
|
552
|
-
if (!Object.hasOwn(channels, oldChannelId)) return {
|
|
553
|
-
migrated: false,
|
|
554
|
-
skippedExisting: false
|
|
555
|
-
};
|
|
556
|
-
if (Object.hasOwn(channels, newChannelId)) return {
|
|
557
|
-
migrated: false,
|
|
558
|
-
skippedExisting: true
|
|
559
|
-
};
|
|
560
|
-
channels[newChannelId] = channels[oldChannelId];
|
|
561
|
-
delete channels[oldChannelId];
|
|
562
|
-
return {
|
|
563
|
-
migrated: true,
|
|
564
|
-
skippedExisting: false
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
function migrateSlackChannelConfig(params) {
|
|
568
|
-
const scopes = [];
|
|
569
|
-
let migrated = false;
|
|
570
|
-
let skippedExisting = false;
|
|
571
|
-
const accountChannels = resolveAccountChannels(params.cfg, params.accountId).channels;
|
|
572
|
-
if (accountChannels) {
|
|
573
|
-
const result = migrateSlackChannelsInPlace(accountChannels, params.oldChannelId, params.newChannelId);
|
|
574
|
-
if (result.migrated) {
|
|
575
|
-
migrated = true;
|
|
576
|
-
scopes.push("account");
|
|
577
|
-
}
|
|
578
|
-
if (result.skippedExisting) skippedExisting = true;
|
|
579
|
-
}
|
|
580
|
-
const globalChannels = params.cfg.channels?.slack?.channels;
|
|
581
|
-
if (globalChannels) {
|
|
582
|
-
const result = migrateSlackChannelsInPlace(globalChannels, params.oldChannelId, params.newChannelId);
|
|
583
|
-
if (result.migrated) {
|
|
584
|
-
migrated = true;
|
|
585
|
-
scopes.push("global");
|
|
586
|
-
}
|
|
587
|
-
if (result.skippedExisting) skippedExisting = true;
|
|
588
|
-
}
|
|
589
|
-
return {
|
|
590
|
-
migrated,
|
|
591
|
-
skippedExisting,
|
|
592
|
-
scopes
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
//#endregion
|
|
596
|
-
//#region extensions/slack/src/monitor/events/channels.ts
|
|
597
|
-
function registerSlackChannelEvents(params) {
|
|
598
|
-
const { ctx, trackEvent } = params;
|
|
599
|
-
const enqueueChannelSystemEvent = (params) => {
|
|
600
|
-
if (!ctx.isChannelAllowed({
|
|
601
|
-
channelId: params.channelId,
|
|
602
|
-
channelName: params.channelName,
|
|
603
|
-
channelType: "channel"
|
|
604
|
-
})) return;
|
|
605
|
-
const label = resolveSlackChannelLabel({
|
|
606
|
-
channelId: params.channelId,
|
|
607
|
-
channelName: params.channelName
|
|
608
|
-
});
|
|
609
|
-
const sessionKey = ctx.resolveSlackSystemEventSessionKey({
|
|
610
|
-
channelId: params.channelId,
|
|
611
|
-
channelType: "channel"
|
|
612
|
-
});
|
|
613
|
-
enqueueSystemEvent(`Slack channel ${params.kind}: ${label}.`, {
|
|
614
|
-
sessionKey,
|
|
615
|
-
contextKey: `slack:channel:${params.kind}:${params.channelId ?? params.channelName ?? "unknown"}`
|
|
616
|
-
});
|
|
617
|
-
};
|
|
618
|
-
ctx.app.event("channel_created", async ({ event, body }) => {
|
|
619
|
-
try {
|
|
620
|
-
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
|
621
|
-
trackEvent?.();
|
|
622
|
-
const payload = event;
|
|
623
|
-
const channelId = payload.channel?.id;
|
|
624
|
-
const channelName = payload.channel?.name;
|
|
625
|
-
enqueueChannelSystemEvent({
|
|
626
|
-
kind: "created",
|
|
627
|
-
channelId,
|
|
628
|
-
channelName
|
|
629
|
-
});
|
|
630
|
-
} catch (err) {
|
|
631
|
-
ctx.runtime.error?.(danger(`slack channel created handler failed: ${String(err)}`));
|
|
632
|
-
}
|
|
633
|
-
});
|
|
634
|
-
ctx.app.event("channel_rename", async ({ event, body }) => {
|
|
635
|
-
try {
|
|
636
|
-
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
|
637
|
-
trackEvent?.();
|
|
638
|
-
const payload = event;
|
|
639
|
-
const channelId = payload.channel?.id;
|
|
640
|
-
enqueueChannelSystemEvent({
|
|
641
|
-
kind: "renamed",
|
|
642
|
-
channelId,
|
|
643
|
-
channelName: payload.channel?.name_normalized ?? payload.channel?.name
|
|
644
|
-
});
|
|
645
|
-
} catch (err) {
|
|
646
|
-
ctx.runtime.error?.(danger(`slack channel rename handler failed: ${String(err)}`));
|
|
647
|
-
}
|
|
648
|
-
});
|
|
649
|
-
ctx.app.event("channel_id_changed", async ({ event, body }) => {
|
|
650
|
-
try {
|
|
651
|
-
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
|
652
|
-
trackEvent?.();
|
|
653
|
-
const payload = event;
|
|
654
|
-
const oldChannelId = payload.old_channel_id;
|
|
655
|
-
const newChannelId = payload.new_channel_id;
|
|
656
|
-
if (!oldChannelId || !newChannelId) return;
|
|
657
|
-
const label = resolveSlackChannelLabel({
|
|
658
|
-
channelId: newChannelId,
|
|
659
|
-
channelName: (await ctx.resolveChannelName(newChannelId))?.name
|
|
660
|
-
});
|
|
661
|
-
ctx.runtime.log?.(warn(`[slack] Channel ID changed: ${oldChannelId} → ${newChannelId} (${label})`));
|
|
662
|
-
if (!resolveChannelConfigWrites({
|
|
663
|
-
cfg: ctx.cfg,
|
|
664
|
-
channelId: "slack",
|
|
665
|
-
accountId: ctx.accountId
|
|
666
|
-
})) {
|
|
667
|
-
ctx.runtime.log?.(warn("[slack] Config writes disabled; skipping channel config migration."));
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
const currentConfig = loadConfig();
|
|
671
|
-
const migration = migrateSlackChannelConfig({
|
|
672
|
-
cfg: currentConfig,
|
|
673
|
-
accountId: ctx.accountId,
|
|
674
|
-
oldChannelId,
|
|
675
|
-
newChannelId
|
|
676
|
-
});
|
|
677
|
-
if (migration.migrated) {
|
|
678
|
-
migrateSlackChannelConfig({
|
|
679
|
-
cfg: ctx.cfg,
|
|
680
|
-
accountId: ctx.accountId,
|
|
681
|
-
oldChannelId,
|
|
682
|
-
newChannelId
|
|
683
|
-
});
|
|
684
|
-
await writeConfigFile(currentConfig);
|
|
685
|
-
ctx.runtime.log?.(warn("[slack] Channel config migrated and saved successfully."));
|
|
686
|
-
} else if (migration.skippedExisting) ctx.runtime.log?.(warn(`[slack] Channel config already exists for ${newChannelId}; leaving ${oldChannelId} unchanged`));
|
|
687
|
-
else ctx.runtime.log?.(warn(`[slack] No config found for old channel ID ${oldChannelId}; migration logged only`));
|
|
688
|
-
} catch (err) {
|
|
689
|
-
ctx.runtime.error?.(danger(`slack channel_id_changed handler failed: ${String(err)}`));
|
|
690
|
-
}
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
//#endregion
|
|
694
|
-
//#region extensions/slack/src/monitor/auth.ts
|
|
695
|
-
let slackAllowFromCache = /* @__PURE__ */ new WeakMap();
|
|
696
|
-
const DEFAULT_PAIRING_ALLOW_FROM_CACHE_TTL_MS = 5e3;
|
|
697
|
-
function getPairingAllowFromCacheTtlMs() {
|
|
698
|
-
const raw = process.env.MOLDCLAW_SLACK_PAIRING_ALLOWFROM_CACHE_TTL_MS?.trim();
|
|
699
|
-
if (!raw) return DEFAULT_PAIRING_ALLOW_FROM_CACHE_TTL_MS;
|
|
700
|
-
const parsed = Number(raw);
|
|
701
|
-
if (!Number.isFinite(parsed)) return DEFAULT_PAIRING_ALLOW_FROM_CACHE_TTL_MS;
|
|
702
|
-
return Math.max(0, Math.floor(parsed));
|
|
703
|
-
}
|
|
704
|
-
function getAllowFromCacheState(ctx) {
|
|
705
|
-
const existing = slackAllowFromCache.get(ctx);
|
|
706
|
-
if (existing) return existing;
|
|
707
|
-
const next = {};
|
|
708
|
-
slackAllowFromCache.set(ctx, next);
|
|
709
|
-
return next;
|
|
710
|
-
}
|
|
711
|
-
function buildBaseAllowFrom(ctx) {
|
|
712
|
-
const allowFrom = normalizeAllowList(ctx.allowFrom);
|
|
713
|
-
return {
|
|
714
|
-
allowFrom,
|
|
715
|
-
allowFromLower: normalizeAllowListLower(allowFrom)
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
async function resolveSlackEffectiveAllowFrom(ctx, options) {
|
|
719
|
-
const includePairingStore = options?.includePairingStore === true;
|
|
720
|
-
const cache = getAllowFromCacheState(ctx);
|
|
721
|
-
const baseSignature = JSON.stringify(ctx.allowFrom);
|
|
722
|
-
if (cache.baseSignature !== baseSignature || !cache.base) {
|
|
723
|
-
cache.baseSignature = baseSignature;
|
|
724
|
-
cache.base = buildBaseAllowFrom(ctx);
|
|
725
|
-
cache.pairing = void 0;
|
|
726
|
-
cache.pairingKey = void 0;
|
|
727
|
-
cache.pairingExpiresAtMs = void 0;
|
|
728
|
-
cache.pairingPending = void 0;
|
|
729
|
-
}
|
|
730
|
-
if (!includePairingStore) return cache.base;
|
|
731
|
-
const ttlMs = getPairingAllowFromCacheTtlMs();
|
|
732
|
-
const nowMs = Date.now();
|
|
733
|
-
const pairingKey = `${ctx.accountId}:${ctx.dmPolicy}`;
|
|
734
|
-
if (ttlMs > 0 && cache.pairing && cache.pairingKey === pairingKey && (cache.pairingExpiresAtMs ?? 0) >= nowMs) return cache.pairing;
|
|
735
|
-
if (cache.pairingPending && cache.pairingKey === pairingKey) return await cache.pairingPending;
|
|
736
|
-
const pairingPending = (async () => {
|
|
737
|
-
let storeAllowFrom = [];
|
|
738
|
-
try {
|
|
739
|
-
const resolved = await readStoreAllowFromForDmPolicy({
|
|
740
|
-
provider: "slack",
|
|
741
|
-
accountId: ctx.accountId,
|
|
742
|
-
dmPolicy: ctx.dmPolicy
|
|
743
|
-
});
|
|
744
|
-
storeAllowFrom = Array.isArray(resolved) ? resolved : [];
|
|
745
|
-
} catch {
|
|
746
|
-
storeAllowFrom = [];
|
|
747
|
-
}
|
|
748
|
-
const allowFrom = normalizeAllowList([...cache.base?.allowFrom ?? [], ...storeAllowFrom]);
|
|
749
|
-
return {
|
|
750
|
-
allowFrom,
|
|
751
|
-
allowFromLower: normalizeAllowListLower(allowFrom)
|
|
752
|
-
};
|
|
753
|
-
})();
|
|
754
|
-
cache.pairingKey = pairingKey;
|
|
755
|
-
cache.pairingPending = pairingPending;
|
|
756
|
-
try {
|
|
757
|
-
const resolved = await pairingPending;
|
|
758
|
-
if (ttlMs > 0) {
|
|
759
|
-
cache.pairing = resolved;
|
|
760
|
-
cache.pairingExpiresAtMs = nowMs + ttlMs;
|
|
761
|
-
} else {
|
|
762
|
-
cache.pairing = void 0;
|
|
763
|
-
cache.pairingExpiresAtMs = void 0;
|
|
764
|
-
}
|
|
765
|
-
return resolved;
|
|
766
|
-
} finally {
|
|
767
|
-
if (cache.pairingPending === pairingPending) cache.pairingPending = void 0;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
function isSlackSenderAllowListed(params) {
|
|
771
|
-
const { allowListLower, senderId, senderName, allowNameMatching } = params;
|
|
772
|
-
return allowListLower.length === 0 || allowListMatches({
|
|
773
|
-
allowList: allowListLower,
|
|
774
|
-
id: senderId,
|
|
775
|
-
name: senderName,
|
|
776
|
-
allowNameMatching
|
|
777
|
-
});
|
|
778
|
-
}
|
|
779
|
-
async function authorizeSlackSystemEventSender(params) {
|
|
780
|
-
const senderId = params.senderId?.trim();
|
|
781
|
-
if (!senderId) return {
|
|
782
|
-
allowed: false,
|
|
783
|
-
reason: "missing-sender"
|
|
784
|
-
};
|
|
785
|
-
const expectedSenderId = params.expectedSenderId?.trim();
|
|
786
|
-
if (expectedSenderId && expectedSenderId !== senderId) return {
|
|
787
|
-
allowed: false,
|
|
788
|
-
reason: "sender-mismatch"
|
|
789
|
-
};
|
|
790
|
-
const channelId = params.channelId?.trim();
|
|
791
|
-
let channelType = normalizeSlackChannelType(params.channelType, channelId);
|
|
792
|
-
let channelName;
|
|
793
|
-
if (channelId) {
|
|
794
|
-
const info = await params.ctx.resolveChannelName(channelId).catch(() => ({}));
|
|
795
|
-
channelName = info.name;
|
|
796
|
-
channelType = normalizeSlackChannelType(params.channelType ?? info.type, channelId);
|
|
797
|
-
if (!params.ctx.isChannelAllowed({
|
|
798
|
-
channelId,
|
|
799
|
-
channelName,
|
|
800
|
-
channelType
|
|
801
|
-
})) return {
|
|
802
|
-
allowed: false,
|
|
803
|
-
reason: "channel-not-allowed",
|
|
804
|
-
channelType,
|
|
805
|
-
channelName
|
|
806
|
-
};
|
|
807
|
-
}
|
|
808
|
-
const senderName = (await params.ctx.resolveUserName(senderId).catch(() => ({}))).name;
|
|
809
|
-
const resolveAllowFromLower = async (includePairingStore = false) => (await resolveSlackEffectiveAllowFrom(params.ctx, { includePairingStore })).allowFromLower;
|
|
810
|
-
if (channelType === "im") {
|
|
811
|
-
if (!params.ctx.dmEnabled || params.ctx.dmPolicy === "disabled") return {
|
|
812
|
-
allowed: false,
|
|
813
|
-
reason: "dm-disabled",
|
|
814
|
-
channelType,
|
|
815
|
-
channelName
|
|
816
|
-
};
|
|
817
|
-
if (params.ctx.dmPolicy !== "open") {
|
|
818
|
-
if (!isSlackSenderAllowListed({
|
|
819
|
-
allowListLower: await resolveAllowFromLower(true),
|
|
820
|
-
senderId,
|
|
821
|
-
senderName,
|
|
822
|
-
allowNameMatching: params.ctx.allowNameMatching
|
|
823
|
-
})) return {
|
|
824
|
-
allowed: false,
|
|
825
|
-
reason: "sender-not-allowlisted",
|
|
826
|
-
channelType,
|
|
827
|
-
channelName
|
|
828
|
-
};
|
|
829
|
-
}
|
|
830
|
-
} else if (!channelId) {
|
|
831
|
-
const allowFromLower = await resolveAllowFromLower(false);
|
|
832
|
-
if (allowFromLower.length > 0) {
|
|
833
|
-
if (!isSlackSenderAllowListed({
|
|
834
|
-
allowListLower: allowFromLower,
|
|
835
|
-
senderId,
|
|
836
|
-
senderName,
|
|
837
|
-
allowNameMatching: params.ctx.allowNameMatching
|
|
838
|
-
})) return {
|
|
839
|
-
allowed: false,
|
|
840
|
-
reason: "sender-not-allowlisted"
|
|
841
|
-
};
|
|
842
|
-
}
|
|
843
|
-
} else {
|
|
844
|
-
const channelConfig = resolveSlackChannelConfig({
|
|
845
|
-
channelId,
|
|
846
|
-
channelName,
|
|
847
|
-
channels: params.ctx.channelsConfig,
|
|
848
|
-
channelKeys: params.ctx.channelsConfigKeys,
|
|
849
|
-
defaultRequireMention: params.ctx.defaultRequireMention,
|
|
850
|
-
allowNameMatching: params.ctx.allowNameMatching
|
|
851
|
-
});
|
|
852
|
-
if (Array.isArray(channelConfig?.users) && channelConfig.users.length > 0) {
|
|
853
|
-
if (!resolveSlackUserAllowed({
|
|
854
|
-
allowList: channelConfig?.users,
|
|
855
|
-
userId: senderId,
|
|
856
|
-
userName: senderName,
|
|
857
|
-
allowNameMatching: params.ctx.allowNameMatching
|
|
858
|
-
})) return {
|
|
859
|
-
allowed: false,
|
|
860
|
-
reason: "sender-not-channel-allowed",
|
|
861
|
-
channelType,
|
|
862
|
-
channelName
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
return {
|
|
867
|
-
allowed: true,
|
|
868
|
-
channelType,
|
|
869
|
-
channelName
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
//#endregion
|
|
873
|
-
//#region extensions/slack/src/monitor/mrkdwn.ts
|
|
874
|
-
function escapeSlackMrkdwn(value) {
|
|
875
|
-
return value.replaceAll("\\", "\\\\").replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replace(/([*_`~])/g, "\\$1");
|
|
876
|
-
}
|
|
877
|
-
//#endregion
|
|
878
|
-
//#region extensions/slack/src/monitor/events/interactions.block-actions.ts
|
|
879
|
-
function readOptionValues(options) {
|
|
880
|
-
if (!Array.isArray(options)) return;
|
|
881
|
-
const values = options.map((option) => option && typeof option === "object" ? option.value : null).filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
882
|
-
return values.length > 0 ? values : void 0;
|
|
883
|
-
}
|
|
884
|
-
function readOptionLabels(options) {
|
|
885
|
-
if (!Array.isArray(options)) return;
|
|
886
|
-
const labels = options.map((option) => option && typeof option === "object" ? option.text?.text ?? null : null).filter((label) => typeof label === "string" && label.trim().length > 0);
|
|
887
|
-
return labels.length > 0 ? labels : void 0;
|
|
888
|
-
}
|
|
889
|
-
function uniqueNonEmptyStrings(values) {
|
|
890
|
-
const unique = [];
|
|
891
|
-
const seen = /* @__PURE__ */ new Set();
|
|
892
|
-
for (const entry of values) {
|
|
893
|
-
if (typeof entry !== "string") continue;
|
|
894
|
-
const trimmed = entry.trim();
|
|
895
|
-
if (!trimmed || seen.has(trimmed)) continue;
|
|
896
|
-
seen.add(trimmed);
|
|
897
|
-
unique.push(trimmed);
|
|
898
|
-
}
|
|
899
|
-
return unique;
|
|
900
|
-
}
|
|
901
|
-
function collectRichTextFragments(value, out) {
|
|
902
|
-
if (!value || typeof value !== "object") return;
|
|
903
|
-
const typed = value;
|
|
904
|
-
if (typeof typed.text === "string" && typed.text.trim().length > 0) out.push(typed.text.trim());
|
|
905
|
-
if (Array.isArray(typed.elements)) for (const child of typed.elements) collectRichTextFragments(child, out);
|
|
906
|
-
}
|
|
907
|
-
function summarizeRichTextPreview(value) {
|
|
908
|
-
const fragments = [];
|
|
909
|
-
collectRichTextFragments(value, fragments);
|
|
910
|
-
if (fragments.length === 0) return;
|
|
911
|
-
const joined = fragments.join(" ").replace(/\s+/g, " ").trim();
|
|
912
|
-
if (!joined) return;
|
|
913
|
-
const max = 120;
|
|
914
|
-
return joined.length <= max ? joined : `${joined.slice(0, max - 1)}…`;
|
|
915
|
-
}
|
|
916
|
-
function readInteractionAction(raw) {
|
|
917
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
918
|
-
return raw;
|
|
919
|
-
}
|
|
920
|
-
function summarizeAction(action) {
|
|
921
|
-
const typed = action;
|
|
922
|
-
const actionType = typed.type;
|
|
923
|
-
const selectedUsers = uniqueNonEmptyStrings([...typed.selected_user ? [typed.selected_user] : [], ...Array.isArray(typed.selected_users) ? typed.selected_users : []]);
|
|
924
|
-
const selectedChannels = uniqueNonEmptyStrings([...typed.selected_channel ? [typed.selected_channel] : [], ...Array.isArray(typed.selected_channels) ? typed.selected_channels : []]);
|
|
925
|
-
const selectedConversations = uniqueNonEmptyStrings([...typed.selected_conversation ? [typed.selected_conversation] : [], ...Array.isArray(typed.selected_conversations) ? typed.selected_conversations : []]);
|
|
926
|
-
const selectedValues = uniqueNonEmptyStrings([
|
|
927
|
-
...typed.selected_option?.value ? [typed.selected_option.value] : [],
|
|
928
|
-
...readOptionValues(typed.selected_options) ?? [],
|
|
929
|
-
...selectedUsers,
|
|
930
|
-
...selectedChannels,
|
|
931
|
-
...selectedConversations
|
|
932
|
-
]);
|
|
933
|
-
const selectedLabels = uniqueNonEmptyStrings([...typed.selected_option?.text?.text ? [typed.selected_option.text.text] : [], ...readOptionLabels(typed.selected_options) ?? []]);
|
|
934
|
-
const inputValue = typeof typed.value === "string" ? typed.value : void 0;
|
|
935
|
-
const inputNumber = actionType === "number_input" && inputValue != null ? Number.parseFloat(inputValue) : void 0;
|
|
936
|
-
const parsedNumber = Number.isFinite(inputNumber) ? inputNumber : void 0;
|
|
937
|
-
const inputEmail = actionType === "email_text_input" && inputValue?.includes("@") ? inputValue : void 0;
|
|
938
|
-
let inputUrl;
|
|
939
|
-
if (actionType === "url_text_input" && inputValue) try {
|
|
940
|
-
inputUrl = new URL(inputValue).toString();
|
|
941
|
-
} catch {
|
|
942
|
-
inputUrl = void 0;
|
|
943
|
-
}
|
|
944
|
-
const richTextValue = actionType === "rich_text_input" ? typed.rich_text_value : void 0;
|
|
945
|
-
const richTextPreview = summarizeRichTextPreview(richTextValue);
|
|
946
|
-
return {
|
|
947
|
-
actionType,
|
|
948
|
-
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,
|
|
949
|
-
value: typed.value,
|
|
950
|
-
selectedValues: selectedValues.length > 0 ? selectedValues : void 0,
|
|
951
|
-
selectedUsers: selectedUsers.length > 0 ? selectedUsers : void 0,
|
|
952
|
-
selectedChannels: selectedChannels.length > 0 ? selectedChannels : void 0,
|
|
953
|
-
selectedConversations: selectedConversations.length > 0 ? selectedConversations : void 0,
|
|
954
|
-
selectedLabels: selectedLabels.length > 0 ? selectedLabels : void 0,
|
|
955
|
-
selectedDate: typed.selected_date,
|
|
956
|
-
selectedTime: typed.selected_time,
|
|
957
|
-
selectedDateTime: typeof typed.selected_date_time === "number" ? typed.selected_date_time : void 0,
|
|
958
|
-
inputValue,
|
|
959
|
-
inputNumber: parsedNumber,
|
|
960
|
-
inputEmail,
|
|
961
|
-
inputUrl,
|
|
962
|
-
richTextValue,
|
|
963
|
-
richTextPreview,
|
|
964
|
-
workflowTriggerUrl: typed.workflow?.trigger_url,
|
|
965
|
-
workflowId: typed.workflow?.workflow_id
|
|
966
|
-
};
|
|
967
|
-
}
|
|
968
|
-
function isBulkActionsBlock(block) {
|
|
969
|
-
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_"));
|
|
970
|
-
}
|
|
971
|
-
function formatInteractionSelectionLabel(params) {
|
|
972
|
-
if (params.summary.actionType === "button" && params.buttonText?.trim()) return params.buttonText.trim();
|
|
973
|
-
if (params.summary.selectedLabels?.length) {
|
|
974
|
-
if (params.summary.selectedLabels.length <= 3) return params.summary.selectedLabels.join(", ");
|
|
975
|
-
return `${params.summary.selectedLabels.slice(0, 3).join(", ")} +${params.summary.selectedLabels.length - 3}`;
|
|
976
|
-
}
|
|
977
|
-
if (params.summary.selectedValues?.length) {
|
|
978
|
-
if (params.summary.selectedValues.length <= 3) return params.summary.selectedValues.join(", ");
|
|
979
|
-
return `${params.summary.selectedValues.slice(0, 3).join(", ")} +${params.summary.selectedValues.length - 3}`;
|
|
980
|
-
}
|
|
981
|
-
if (params.summary.selectedDate) return params.summary.selectedDate;
|
|
982
|
-
if (params.summary.selectedTime) return params.summary.selectedTime;
|
|
983
|
-
if (typeof params.summary.selectedDateTime === "number") return (/* @__PURE__ */ new Date(params.summary.selectedDateTime * 1e3)).toISOString();
|
|
984
|
-
if (params.summary.richTextPreview) return params.summary.richTextPreview;
|
|
985
|
-
if (params.summary.value?.trim()) return params.summary.value.trim();
|
|
986
|
-
return params.actionId;
|
|
987
|
-
}
|
|
988
|
-
function formatInteractionConfirmationText(params) {
|
|
989
|
-
const actor = params.userId?.trim() ? ` by <@${params.userId.trim()}>` : "";
|
|
990
|
-
return `:white_check_mark: *${escapeSlackMrkdwn(params.selectedLabel)}* selected${actor}`;
|
|
991
|
-
}
|
|
992
|
-
function buildSlackPluginInteractionData(params) {
|
|
993
|
-
const actionId = params.actionId.trim();
|
|
994
|
-
if (!actionId) return null;
|
|
995
|
-
const payload = params.summary.value?.trim() || params.summary.selectedValues?.map((value) => value.trim()).find(Boolean) || "";
|
|
996
|
-
if (actionId === "moldclaw:reply_button" || actionId === "moldclaw:reply_select") return payload || null;
|
|
997
|
-
return payload ? `${actionId}:${payload}` : actionId;
|
|
998
|
-
}
|
|
999
|
-
function buildSlackPluginInteractionId(params) {
|
|
1000
|
-
const primaryValue = params.summary.value?.trim() || params.summary.selectedValues?.map((value) => value.trim()).find(Boolean) || "";
|
|
1001
|
-
return [
|
|
1002
|
-
params.userId?.trim() || "",
|
|
1003
|
-
params.channelId?.trim() || "",
|
|
1004
|
-
params.messageTs?.trim() || "",
|
|
1005
|
-
params.triggerId?.trim() || "",
|
|
1006
|
-
params.actionId.trim(),
|
|
1007
|
-
primaryValue
|
|
1008
|
-
].join(":");
|
|
1009
|
-
}
|
|
1010
|
-
function parseSlackBlockAction(params) {
|
|
1011
|
-
const typedBody = params.body;
|
|
1012
|
-
const typedAction = readInteractionAction(params.action);
|
|
1013
|
-
if (!typedAction) {
|
|
1014
|
-
params.log?.(`slack:interaction malformed action payload channel=${typedBody.channel?.id ?? typedBody.container?.channel_id ?? "unknown"} user=${typedBody.user?.id ?? "unknown"}`);
|
|
1015
|
-
return null;
|
|
1016
|
-
}
|
|
1017
|
-
const typedActionWithText = typedAction;
|
|
1018
|
-
return {
|
|
1019
|
-
typedBody,
|
|
1020
|
-
typedAction,
|
|
1021
|
-
typedActionWithText,
|
|
1022
|
-
actionId: typeof typedActionWithText.action_id === "string" ? typedActionWithText.action_id : "unknown",
|
|
1023
|
-
blockId: typedActionWithText.block_id,
|
|
1024
|
-
userId: typedBody.user?.id ?? "unknown",
|
|
1025
|
-
channelId: typedBody.channel?.id ?? typedBody.container?.channel_id,
|
|
1026
|
-
messageTs: typedBody.message?.ts ?? typedBody.container?.message_ts,
|
|
1027
|
-
threadTs: typedBody.container?.thread_ts,
|
|
1028
|
-
actionSummary: summarizeAction(typedAction)
|
|
1029
|
-
};
|
|
1030
|
-
}
|
|
1031
|
-
async function respondEphemeral(respond, text) {
|
|
1032
|
-
if (!respond) return;
|
|
1033
|
-
try {
|
|
1034
|
-
await respond({
|
|
1035
|
-
text,
|
|
1036
|
-
response_type: "ephemeral"
|
|
1037
|
-
});
|
|
1038
|
-
} catch {}
|
|
1039
|
-
}
|
|
1040
|
-
async function updateSlackInteractionMessage(params) {
|
|
1041
|
-
if (!params.channelId || !params.messageTs) return;
|
|
1042
|
-
await params.ctx.app.client.chat.update({
|
|
1043
|
-
channel: params.channelId,
|
|
1044
|
-
ts: params.messageTs,
|
|
1045
|
-
text: params.text,
|
|
1046
|
-
...params.blocks ? { blocks: params.blocks } : {}
|
|
1047
|
-
});
|
|
1048
|
-
}
|
|
1049
|
-
async function authorizeSlackBlockAction(params) {
|
|
1050
|
-
const auth = await authorizeSlackSystemEventSender({
|
|
1051
|
-
ctx: params.ctx,
|
|
1052
|
-
senderId: params.parsed.userId,
|
|
1053
|
-
channelId: params.parsed.channelId
|
|
1054
|
-
});
|
|
1055
|
-
if (auth.allowed) return auth;
|
|
1056
|
-
params.ctx.runtime.log?.(`slack:interaction drop action=${params.parsed.actionId} user=${params.parsed.userId} channel=${params.parsed.channelId ?? "unknown"} reason=${auth.reason ?? "unauthorized"}`);
|
|
1057
|
-
await respondEphemeral(params.respond, "You are not authorized to use this control.");
|
|
1058
|
-
return { allowed: false };
|
|
1059
|
-
}
|
|
1060
|
-
async function handleSlackPluginBindingApproval(params) {
|
|
1061
|
-
const pluginBindingApproval = parsePluginBindingApprovalCustomId(params.pluginInteractionData);
|
|
1062
|
-
if (!pluginBindingApproval) return false;
|
|
1063
|
-
const resolved = await resolvePluginConversationBindingApproval({
|
|
1064
|
-
approvalId: pluginBindingApproval.approvalId,
|
|
1065
|
-
decision: pluginBindingApproval.decision,
|
|
1066
|
-
senderId: params.parsed.userId
|
|
1067
|
-
});
|
|
1068
|
-
try {
|
|
1069
|
-
await updateSlackInteractionMessage({
|
|
1070
|
-
ctx: params.ctx,
|
|
1071
|
-
channelId: params.parsed.channelId,
|
|
1072
|
-
messageTs: params.parsed.messageTs,
|
|
1073
|
-
text: params.parsed.typedBody.message?.text ?? "",
|
|
1074
|
-
blocks: []
|
|
1075
|
-
});
|
|
1076
|
-
} catch {}
|
|
1077
|
-
await respondEphemeral(params.respond, buildPluginBindingResolvedText(resolved));
|
|
1078
|
-
return true;
|
|
1079
|
-
}
|
|
1080
|
-
async function dispatchSlackPluginInteraction(params) {
|
|
1081
|
-
const pluginInteractionId = buildSlackPluginInteractionId({
|
|
1082
|
-
userId: params.parsed.userId,
|
|
1083
|
-
channelId: params.parsed.channelId,
|
|
1084
|
-
messageTs: params.parsed.messageTs,
|
|
1085
|
-
triggerId: params.parsed.typedBody.trigger_id,
|
|
1086
|
-
actionId: params.parsed.actionId,
|
|
1087
|
-
summary: params.parsed.actionSummary
|
|
1088
|
-
});
|
|
1089
|
-
if (await handleSlackPluginBindingApproval({
|
|
1090
|
-
ctx: params.ctx,
|
|
1091
|
-
parsed: params.parsed,
|
|
1092
|
-
pluginInteractionData: params.pluginInteractionData,
|
|
1093
|
-
respond: params.respond
|
|
1094
|
-
})) return true;
|
|
1095
|
-
const pluginResult = await dispatchPluginInteractiveHandler({
|
|
1096
|
-
channel: "slack",
|
|
1097
|
-
data: params.pluginInteractionData,
|
|
1098
|
-
interactionId: pluginInteractionId,
|
|
1099
|
-
ctx: {
|
|
1100
|
-
accountId: params.ctx.accountId,
|
|
1101
|
-
interactionId: pluginInteractionId,
|
|
1102
|
-
conversationId: params.parsed.channelId ?? "",
|
|
1103
|
-
parentConversationId: void 0,
|
|
1104
|
-
threadId: params.parsed.threadTs,
|
|
1105
|
-
senderId: params.parsed.userId,
|
|
1106
|
-
senderUsername: void 0,
|
|
1107
|
-
auth: params.auth,
|
|
1108
|
-
interaction: {
|
|
1109
|
-
kind: params.parsed.actionSummary.actionType === "button" ? "button" : "select",
|
|
1110
|
-
actionId: params.parsed.actionId,
|
|
1111
|
-
blockId: params.parsed.blockId,
|
|
1112
|
-
messageTs: params.parsed.messageTs,
|
|
1113
|
-
threadTs: params.parsed.threadTs,
|
|
1114
|
-
value: params.parsed.actionSummary.value,
|
|
1115
|
-
selectedValues: params.parsed.actionSummary.selectedValues,
|
|
1116
|
-
selectedLabels: params.parsed.actionSummary.selectedLabels,
|
|
1117
|
-
triggerId: params.parsed.typedBody.trigger_id,
|
|
1118
|
-
responseUrl: params.parsed.typedBody.response_url
|
|
1119
|
-
}
|
|
1120
|
-
},
|
|
1121
|
-
respond: {
|
|
1122
|
-
acknowledge: async () => {},
|
|
1123
|
-
reply: async ({ text, responseType }) => {
|
|
1124
|
-
if (!text) return;
|
|
1125
|
-
await params.respond?.({
|
|
1126
|
-
text,
|
|
1127
|
-
response_type: responseType ?? "ephemeral"
|
|
1128
|
-
});
|
|
1129
|
-
},
|
|
1130
|
-
followUp: async ({ text, responseType }) => {
|
|
1131
|
-
if (!text) return;
|
|
1132
|
-
await params.respond?.({
|
|
1133
|
-
text,
|
|
1134
|
-
response_type: responseType ?? "ephemeral"
|
|
1135
|
-
});
|
|
1136
|
-
},
|
|
1137
|
-
editMessage: async ({ text, blocks }) => {
|
|
1138
|
-
await updateSlackInteractionMessage({
|
|
1139
|
-
ctx: params.ctx,
|
|
1140
|
-
channelId: params.parsed.channelId,
|
|
1141
|
-
messageTs: params.parsed.messageTs,
|
|
1142
|
-
text: text ?? params.parsed.typedBody.message?.text ?? "",
|
|
1143
|
-
blocks: Array.isArray(blocks) ? blocks : void 0
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
});
|
|
1148
|
-
return pluginResult.matched && pluginResult.handled;
|
|
1149
|
-
}
|
|
1150
|
-
function enqueueSlackBlockActionEvent(params) {
|
|
1151
|
-
const eventPayload = {
|
|
1152
|
-
interactionType: "block_action",
|
|
1153
|
-
actionId: params.parsed.actionId,
|
|
1154
|
-
blockId: params.parsed.blockId,
|
|
1155
|
-
...params.parsed.actionSummary,
|
|
1156
|
-
userId: params.parsed.userId,
|
|
1157
|
-
teamId: params.parsed.typedBody.team?.id,
|
|
1158
|
-
triggerId: params.parsed.typedBody.trigger_id,
|
|
1159
|
-
responseUrl: params.parsed.typedBody.response_url,
|
|
1160
|
-
channelId: params.parsed.channelId,
|
|
1161
|
-
messageTs: params.parsed.messageTs,
|
|
1162
|
-
threadTs: params.parsed.threadTs
|
|
1163
|
-
};
|
|
1164
|
-
params.ctx.runtime.log?.(`slack:interaction action=${params.parsed.actionId} type=${params.parsed.actionSummary.actionType ?? "unknown"} user=${params.parsed.userId} channel=${params.parsed.channelId}`);
|
|
1165
|
-
const sessionKey = params.ctx.resolveSlackSystemEventSessionKey({
|
|
1166
|
-
channelId: params.parsed.channelId,
|
|
1167
|
-
channelType: params.auth.channelType,
|
|
1168
|
-
senderId: params.parsed.userId
|
|
1169
|
-
});
|
|
1170
|
-
const contextParts = [
|
|
1171
|
-
"slack:interaction",
|
|
1172
|
-
params.parsed.channelId,
|
|
1173
|
-
params.parsed.messageTs,
|
|
1174
|
-
params.parsed.actionId
|
|
1175
|
-
].filter(Boolean);
|
|
1176
|
-
enqueueSystemEvent(params.formatSystemEvent(eventPayload), {
|
|
1177
|
-
sessionKey,
|
|
1178
|
-
contextKey: contextParts.join(":")
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1181
|
-
function buildSlackConfirmationBlocks(params) {
|
|
1182
|
-
const selectedLabel = formatInteractionSelectionLabel({
|
|
1183
|
-
actionId: params.parsed.actionId,
|
|
1184
|
-
summary: params.parsed.actionSummary,
|
|
1185
|
-
buttonText: params.parsed.typedActionWithText.text?.text
|
|
1186
|
-
});
|
|
1187
|
-
let updatedBlocks = params.originalBlocks.map((block) => {
|
|
1188
|
-
const typedBlock = block;
|
|
1189
|
-
if (typedBlock.type === "actions" && typedBlock.block_id === params.parsed.blockId) return {
|
|
1190
|
-
type: "context",
|
|
1191
|
-
elements: [{
|
|
1192
|
-
type: "mrkdwn",
|
|
1193
|
-
text: formatInteractionConfirmationText({
|
|
1194
|
-
selectedLabel,
|
|
1195
|
-
userId: params.parsed.userId
|
|
1196
|
-
})
|
|
1197
|
-
}]
|
|
1198
|
-
};
|
|
1199
|
-
return block;
|
|
1200
|
-
});
|
|
1201
|
-
if (!updatedBlocks.some((block) => {
|
|
1202
|
-
const typedBlock = block;
|
|
1203
|
-
return typedBlock.type === "actions" && !isBulkActionsBlock(typedBlock);
|
|
1204
|
-
})) updatedBlocks = updatedBlocks.filter((block, index) => {
|
|
1205
|
-
const typedBlock = block;
|
|
1206
|
-
if (isBulkActionsBlock(typedBlock)) return false;
|
|
1207
|
-
if (typedBlock.type !== "divider") return true;
|
|
1208
|
-
const next = updatedBlocks[index + 1];
|
|
1209
|
-
return !next || !isBulkActionsBlock(next);
|
|
1210
|
-
});
|
|
1211
|
-
return updatedBlocks;
|
|
1212
|
-
}
|
|
1213
|
-
async function updateSlackLegacyBlockAction(params) {
|
|
1214
|
-
const originalBlocks = params.parsed.typedBody.message?.blocks;
|
|
1215
|
-
if (!Array.isArray(originalBlocks) || !params.parsed.channelId || !params.parsed.messageTs || !params.parsed.blockId) return;
|
|
1216
|
-
try {
|
|
1217
|
-
await updateSlackInteractionMessage({
|
|
1218
|
-
ctx: params.ctx,
|
|
1219
|
-
channelId: params.parsed.channelId,
|
|
1220
|
-
messageTs: params.parsed.messageTs,
|
|
1221
|
-
text: params.parsed.typedBody.message?.text ?? "",
|
|
1222
|
-
blocks: buildSlackConfirmationBlocks({
|
|
1223
|
-
parsed: params.parsed,
|
|
1224
|
-
originalBlocks
|
|
1225
|
-
})
|
|
1226
|
-
});
|
|
1227
|
-
} catch {
|
|
1228
|
-
await respondEphemeral(params.respond, `Button "${params.parsed.actionId}" clicked!`);
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
async function handleSlackBlockAction(params) {
|
|
1232
|
-
const { ack, body, action, respond } = params.args;
|
|
1233
|
-
await ack();
|
|
1234
|
-
if (params.ctx.shouldDropMismatchedSlackEvent?.(body)) {
|
|
1235
|
-
params.ctx.runtime.log?.("slack:interaction drop block action payload (mismatched app/team)");
|
|
1236
|
-
return;
|
|
1237
|
-
}
|
|
1238
|
-
const parsed = parseSlackBlockAction({
|
|
1239
|
-
body,
|
|
1240
|
-
action,
|
|
1241
|
-
log: params.ctx.runtime.log
|
|
1242
|
-
});
|
|
1243
|
-
if (!parsed) return;
|
|
1244
|
-
const auth = await authorizeSlackBlockAction({
|
|
1245
|
-
ctx: params.ctx,
|
|
1246
|
-
parsed,
|
|
1247
|
-
respond
|
|
1248
|
-
});
|
|
1249
|
-
if (!auth.allowed) return;
|
|
1250
|
-
const pluginInteractionData = buildSlackPluginInteractionData({
|
|
1251
|
-
actionId: parsed.actionId,
|
|
1252
|
-
summary: parsed.actionSummary
|
|
1253
|
-
});
|
|
1254
|
-
if (pluginInteractionData) {
|
|
1255
|
-
if (await dispatchSlackPluginInteraction({
|
|
1256
|
-
ctx: params.ctx,
|
|
1257
|
-
parsed,
|
|
1258
|
-
pluginInteractionData,
|
|
1259
|
-
auth: { isAuthorizedSender: true },
|
|
1260
|
-
respond
|
|
1261
|
-
})) return;
|
|
1262
|
-
}
|
|
1263
|
-
enqueueSlackBlockActionEvent({
|
|
1264
|
-
ctx: params.ctx,
|
|
1265
|
-
parsed,
|
|
1266
|
-
auth,
|
|
1267
|
-
formatSystemEvent: params.formatSystemEvent
|
|
1268
|
-
});
|
|
1269
|
-
await updateSlackLegacyBlockAction({
|
|
1270
|
-
ctx: params.ctx,
|
|
1271
|
-
parsed,
|
|
1272
|
-
respond
|
|
1273
|
-
});
|
|
1274
|
-
}
|
|
1275
|
-
function registerSlackBlockActionHandler(params) {
|
|
1276
|
-
if (typeof params.ctx.app.action !== "function") return;
|
|
1277
|
-
params.ctx.app.action(/.+/, async (args) => {
|
|
1278
|
-
await handleSlackBlockAction({
|
|
1279
|
-
ctx: params.ctx,
|
|
1280
|
-
args,
|
|
1281
|
-
formatSystemEvent: params.formatSystemEvent
|
|
1282
|
-
});
|
|
1283
|
-
});
|
|
1284
|
-
}
|
|
1285
|
-
//#endregion
|
|
1286
|
-
//#region extensions/slack/src/modal-metadata.ts
|
|
1287
|
-
function normalizeString(value) {
|
|
1288
|
-
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
1289
|
-
}
|
|
1290
|
-
function parseSlackModalPrivateMetadata(raw) {
|
|
1291
|
-
if (typeof raw !== "string" || raw.trim().length === 0) return {};
|
|
1292
|
-
try {
|
|
1293
|
-
const parsed = JSON.parse(raw);
|
|
1294
|
-
return {
|
|
1295
|
-
sessionKey: normalizeString(parsed.sessionKey),
|
|
1296
|
-
channelId: normalizeString(parsed.channelId),
|
|
1297
|
-
channelType: normalizeString(parsed.channelType),
|
|
1298
|
-
userId: normalizeString(parsed.userId)
|
|
1299
|
-
};
|
|
1300
|
-
} catch {
|
|
1301
|
-
return {};
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
//#endregion
|
|
1305
|
-
//#region extensions/slack/src/monitor/events/interactions.modal.ts
|
|
1306
|
-
function resolveModalSessionRouting(params) {
|
|
1307
|
-
const metadata = params.metadata;
|
|
1308
|
-
if (metadata.sessionKey) return {
|
|
1309
|
-
sessionKey: metadata.sessionKey,
|
|
1310
|
-
channelId: metadata.channelId,
|
|
1311
|
-
channelType: metadata.channelType
|
|
1312
|
-
};
|
|
1313
|
-
if (metadata.channelId) return {
|
|
1314
|
-
sessionKey: params.ctx.resolveSlackSystemEventSessionKey({
|
|
1315
|
-
channelId: metadata.channelId,
|
|
1316
|
-
channelType: metadata.channelType,
|
|
1317
|
-
senderId: params.userId
|
|
1318
|
-
}),
|
|
1319
|
-
channelId: metadata.channelId,
|
|
1320
|
-
channelType: metadata.channelType
|
|
1321
|
-
};
|
|
1322
|
-
return { sessionKey: params.ctx.resolveSlackSystemEventSessionKey({}) };
|
|
1323
|
-
}
|
|
1324
|
-
function summarizeSlackViewLifecycleContext(view) {
|
|
1325
|
-
const rootViewId = view.root_view_id;
|
|
1326
|
-
const previousViewId = view.previous_view_id;
|
|
1327
|
-
return {
|
|
1328
|
-
rootViewId,
|
|
1329
|
-
previousViewId,
|
|
1330
|
-
externalId: view.external_id,
|
|
1331
|
-
viewHash: view.hash,
|
|
1332
|
-
isStackedView: Boolean(previousViewId)
|
|
1333
|
-
};
|
|
1334
|
-
}
|
|
1335
|
-
function resolveSlackModalEventBase(params) {
|
|
1336
|
-
const metadata = parseSlackModalPrivateMetadata(params.body.view?.private_metadata);
|
|
1337
|
-
const callbackId = params.body.view?.callback_id ?? "unknown";
|
|
1338
|
-
const userId = params.body.user?.id ?? "unknown";
|
|
1339
|
-
const viewId = params.body.view?.id;
|
|
1340
|
-
const inputs = params.summarizeViewState(params.body.view?.state?.values);
|
|
1341
|
-
const sessionRouting = resolveModalSessionRouting({
|
|
1342
|
-
ctx: params.ctx,
|
|
1343
|
-
metadata,
|
|
1344
|
-
userId
|
|
1345
|
-
});
|
|
1346
|
-
return {
|
|
1347
|
-
callbackId,
|
|
1348
|
-
userId,
|
|
1349
|
-
expectedUserId: metadata.userId,
|
|
1350
|
-
viewId,
|
|
1351
|
-
sessionRouting,
|
|
1352
|
-
payload: {
|
|
1353
|
-
actionId: `view:${callbackId}`,
|
|
1354
|
-
callbackId,
|
|
1355
|
-
viewId,
|
|
1356
|
-
userId,
|
|
1357
|
-
teamId: params.body.team?.id,
|
|
1358
|
-
...summarizeSlackViewLifecycleContext({
|
|
1359
|
-
root_view_id: params.body.view?.root_view_id,
|
|
1360
|
-
previous_view_id: params.body.view?.previous_view_id,
|
|
1361
|
-
external_id: params.body.view?.external_id,
|
|
1362
|
-
hash: params.body.view?.hash
|
|
1363
|
-
}),
|
|
1364
|
-
privateMetadata: params.body.view?.private_metadata,
|
|
1365
|
-
routedChannelId: sessionRouting.channelId,
|
|
1366
|
-
routedChannelType: sessionRouting.channelType,
|
|
1367
|
-
inputs
|
|
1368
|
-
}
|
|
1369
|
-
};
|
|
1370
|
-
}
|
|
1371
|
-
async function emitSlackModalLifecycleEvent(params) {
|
|
1372
|
-
const { callbackId, userId, expectedUserId, viewId, sessionRouting, payload } = resolveSlackModalEventBase({
|
|
1373
|
-
ctx: params.ctx,
|
|
1374
|
-
body: params.body,
|
|
1375
|
-
summarizeViewState: params.summarizeViewState
|
|
1376
|
-
});
|
|
1377
|
-
const isViewClosed = params.interactionType === "view_closed";
|
|
1378
|
-
const isCleared = params.body.is_cleared === true;
|
|
1379
|
-
const eventPayload = isViewClosed ? {
|
|
1380
|
-
interactionType: params.interactionType,
|
|
1381
|
-
...payload,
|
|
1382
|
-
isCleared
|
|
1383
|
-
} : {
|
|
1384
|
-
interactionType: params.interactionType,
|
|
1385
|
-
...payload
|
|
1386
|
-
};
|
|
1387
|
-
if (isViewClosed) params.ctx.runtime.log?.(`slack:interaction view_closed callback=${callbackId} user=${userId} cleared=${isCleared}`);
|
|
1388
|
-
else params.ctx.runtime.log?.(`slack:interaction view_submission callback=${callbackId} user=${userId} inputs=${payload.inputs.length}`);
|
|
1389
|
-
if (!expectedUserId) {
|
|
1390
|
-
params.ctx.runtime.log?.(`slack:interaction drop modal callback=${callbackId} user=${userId} reason=missing-expected-user`);
|
|
1391
|
-
return;
|
|
1392
|
-
}
|
|
1393
|
-
const auth = await authorizeSlackSystemEventSender({
|
|
1394
|
-
ctx: params.ctx,
|
|
1395
|
-
senderId: userId,
|
|
1396
|
-
channelId: sessionRouting.channelId,
|
|
1397
|
-
channelType: sessionRouting.channelType,
|
|
1398
|
-
expectedSenderId: expectedUserId
|
|
1399
|
-
});
|
|
1400
|
-
if (!auth.allowed) {
|
|
1401
|
-
params.ctx.runtime.log?.(`slack:interaction drop modal callback=${callbackId} user=${userId} reason=${auth.reason ?? "unauthorized"}`);
|
|
1402
|
-
return;
|
|
1403
|
-
}
|
|
1404
|
-
enqueueSystemEvent(params.formatSystemEvent(eventPayload), {
|
|
1405
|
-
sessionKey: sessionRouting.sessionKey,
|
|
1406
|
-
contextKey: [
|
|
1407
|
-
params.contextPrefix,
|
|
1408
|
-
callbackId,
|
|
1409
|
-
viewId,
|
|
1410
|
-
userId
|
|
1411
|
-
].filter(Boolean).join(":")
|
|
1412
|
-
});
|
|
1413
|
-
}
|
|
1414
|
-
function registerModalLifecycleHandler(params) {
|
|
1415
|
-
params.register(params.matcher, async ({ ack, body }) => {
|
|
1416
|
-
await ack();
|
|
1417
|
-
if (params.ctx.shouldDropMismatchedSlackEvent?.(body)) {
|
|
1418
|
-
params.ctx.runtime.log?.(`slack:interaction drop ${params.interactionType} payload (mismatched app/team)`);
|
|
1419
|
-
return;
|
|
1420
|
-
}
|
|
1421
|
-
await emitSlackModalLifecycleEvent({
|
|
1422
|
-
ctx: params.ctx,
|
|
1423
|
-
body,
|
|
1424
|
-
interactionType: params.interactionType,
|
|
1425
|
-
contextPrefix: params.contextPrefix,
|
|
1426
|
-
summarizeViewState: params.summarizeViewState,
|
|
1427
|
-
formatSystemEvent: params.formatSystemEvent
|
|
1428
|
-
});
|
|
1429
|
-
});
|
|
1430
|
-
}
|
|
1431
|
-
//#endregion
|
|
1432
|
-
//#region extensions/slack/src/monitor/events/interactions.ts
|
|
1433
|
-
const MOLDCLAW_ACTION_PREFIX = "moldclaw:";
|
|
1434
|
-
const SLACK_INTERACTION_EVENT_PREFIX = "Slack interaction: ";
|
|
1435
|
-
const REDACTED_INTERACTION_VALUE = "[redacted]";
|
|
1436
|
-
const SLACK_INTERACTION_EVENT_MAX_CHARS = 2400;
|
|
1437
|
-
const SLACK_INTERACTION_STRING_MAX_CHARS = 160;
|
|
1438
|
-
const SLACK_INTERACTION_ARRAY_MAX_ITEMS = 64;
|
|
1439
|
-
const SLACK_INTERACTION_COMPACT_INPUTS_MAX_ITEMS = 3;
|
|
1440
|
-
const SLACK_INTERACTION_REDACTED_KEYS = new Set([
|
|
1441
|
-
"triggerId",
|
|
1442
|
-
"responseUrl",
|
|
1443
|
-
"workflowTriggerUrl",
|
|
1444
|
-
"privateMetadata",
|
|
1445
|
-
"viewHash"
|
|
1446
|
-
]);
|
|
1447
|
-
function sanitizeSlackInteractionPayloadValue(value, key) {
|
|
1448
|
-
if (value === void 0) return;
|
|
1449
|
-
if (key && SLACK_INTERACTION_REDACTED_KEYS.has(key)) {
|
|
1450
|
-
if (typeof value !== "string" || value.trim().length === 0) return;
|
|
1451
|
-
return REDACTED_INTERACTION_VALUE;
|
|
1452
|
-
}
|
|
1453
|
-
if (typeof value === "string") return truncateSlackText(value, SLACK_INTERACTION_STRING_MAX_CHARS);
|
|
1454
|
-
if (Array.isArray(value)) {
|
|
1455
|
-
const sanitized = value.slice(0, SLACK_INTERACTION_ARRAY_MAX_ITEMS).map((entry) => sanitizeSlackInteractionPayloadValue(entry)).filter((entry) => entry !== void 0);
|
|
1456
|
-
if (value.length > SLACK_INTERACTION_ARRAY_MAX_ITEMS) sanitized.push(`…+${value.length - SLACK_INTERACTION_ARRAY_MAX_ITEMS} more`);
|
|
1457
|
-
return sanitized;
|
|
1458
|
-
}
|
|
1459
|
-
if (!value || typeof value !== "object") return value;
|
|
1460
|
-
const output = {};
|
|
1461
|
-
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
1462
|
-
const sanitized = sanitizeSlackInteractionPayloadValue(entryValue, entryKey);
|
|
1463
|
-
if (sanitized === void 0) continue;
|
|
1464
|
-
if (typeof sanitized === "string" && sanitized.length === 0) continue;
|
|
1465
|
-
if (Array.isArray(sanitized) && sanitized.length === 0) continue;
|
|
1466
|
-
output[entryKey] = sanitized;
|
|
1467
|
-
}
|
|
1468
|
-
return output;
|
|
1469
|
-
}
|
|
1470
|
-
function buildCompactSlackInteractionPayload(payload) {
|
|
1471
|
-
const rawInputs = Array.isArray(payload.inputs) ? payload.inputs : [];
|
|
1472
|
-
const compactInputs = rawInputs.slice(0, SLACK_INTERACTION_COMPACT_INPUTS_MAX_ITEMS).flatMap((entry) => {
|
|
1473
|
-
if (!entry || typeof entry !== "object") return [];
|
|
1474
|
-
const typed = entry;
|
|
1475
|
-
return [{
|
|
1476
|
-
actionId: typed.actionId,
|
|
1477
|
-
blockId: typed.blockId,
|
|
1478
|
-
actionType: typed.actionType,
|
|
1479
|
-
inputKind: typed.inputKind,
|
|
1480
|
-
selectedValues: typed.selectedValues,
|
|
1481
|
-
selectedLabels: typed.selectedLabels,
|
|
1482
|
-
inputValue: typed.inputValue,
|
|
1483
|
-
inputNumber: typed.inputNumber,
|
|
1484
|
-
selectedDate: typed.selectedDate,
|
|
1485
|
-
selectedTime: typed.selectedTime,
|
|
1486
|
-
selectedDateTime: typed.selectedDateTime,
|
|
1487
|
-
richTextPreview: typed.richTextPreview
|
|
1488
|
-
}];
|
|
1489
|
-
});
|
|
1490
|
-
return {
|
|
1491
|
-
interactionType: payload.interactionType,
|
|
1492
|
-
actionId: payload.actionId,
|
|
1493
|
-
callbackId: payload.callbackId,
|
|
1494
|
-
actionType: payload.actionType,
|
|
1495
|
-
userId: payload.userId,
|
|
1496
|
-
teamId: payload.teamId,
|
|
1497
|
-
channelId: payload.channelId ?? payload.routedChannelId,
|
|
1498
|
-
messageTs: payload.messageTs,
|
|
1499
|
-
threadTs: payload.threadTs,
|
|
1500
|
-
viewId: payload.viewId,
|
|
1501
|
-
isCleared: payload.isCleared,
|
|
1502
|
-
selectedValues: payload.selectedValues,
|
|
1503
|
-
selectedLabels: payload.selectedLabels,
|
|
1504
|
-
selectedDate: payload.selectedDate,
|
|
1505
|
-
selectedTime: payload.selectedTime,
|
|
1506
|
-
selectedDateTime: payload.selectedDateTime,
|
|
1507
|
-
workflowId: payload.workflowId,
|
|
1508
|
-
routedChannelType: payload.routedChannelType,
|
|
1509
|
-
inputs: compactInputs.length > 0 ? compactInputs : void 0,
|
|
1510
|
-
inputsOmitted: rawInputs.length > SLACK_INTERACTION_COMPACT_INPUTS_MAX_ITEMS ? rawInputs.length - SLACK_INTERACTION_COMPACT_INPUTS_MAX_ITEMS : void 0,
|
|
1511
|
-
payloadTruncated: true
|
|
1512
|
-
};
|
|
1513
|
-
}
|
|
1514
|
-
function formatSlackInteractionSystemEvent(payload) {
|
|
1515
|
-
const toEventText = (value) => `${SLACK_INTERACTION_EVENT_PREFIX}${JSON.stringify(value)}`;
|
|
1516
|
-
const sanitizedPayload = sanitizeSlackInteractionPayloadValue(payload) ?? {};
|
|
1517
|
-
let eventText = toEventText(sanitizedPayload);
|
|
1518
|
-
if (eventText.length <= SLACK_INTERACTION_EVENT_MAX_CHARS) return eventText;
|
|
1519
|
-
eventText = toEventText(sanitizeSlackInteractionPayloadValue(buildCompactSlackInteractionPayload(sanitizedPayload)));
|
|
1520
|
-
if (eventText.length <= SLACK_INTERACTION_EVENT_MAX_CHARS) return eventText;
|
|
1521
|
-
return toEventText({
|
|
1522
|
-
interactionType: sanitizedPayload.interactionType,
|
|
1523
|
-
actionId: sanitizedPayload.actionId ?? "unknown",
|
|
1524
|
-
userId: sanitizedPayload.userId,
|
|
1525
|
-
channelId: sanitizedPayload.channelId ?? sanitizedPayload.routedChannelId,
|
|
1526
|
-
payloadTruncated: true
|
|
1527
|
-
});
|
|
1528
|
-
}
|
|
1529
|
-
function summarizeViewState(values) {
|
|
1530
|
-
if (!values || typeof values !== "object") return [];
|
|
1531
|
-
const entries = [];
|
|
1532
|
-
for (const [blockId, blockValue] of Object.entries(values)) {
|
|
1533
|
-
if (!blockValue || typeof blockValue !== "object") continue;
|
|
1534
|
-
for (const [actionId, rawAction] of Object.entries(blockValue)) {
|
|
1535
|
-
if (!rawAction || typeof rawAction !== "object") continue;
|
|
1536
|
-
const actionSummary = summarizeAction(rawAction);
|
|
1537
|
-
entries.push({
|
|
1538
|
-
blockId,
|
|
1539
|
-
actionId,
|
|
1540
|
-
...actionSummary
|
|
1541
|
-
});
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
|
-
return entries;
|
|
1545
|
-
}
|
|
1546
|
-
function registerSlackInteractionEvents(params) {
|
|
1547
|
-
const { ctx } = params;
|
|
1548
|
-
registerSlackBlockActionHandler({
|
|
1549
|
-
ctx,
|
|
1550
|
-
formatSystemEvent: formatSlackInteractionSystemEvent
|
|
1551
|
-
});
|
|
1552
|
-
if (typeof ctx.app.view !== "function") return;
|
|
1553
|
-
const modalMatcher = new RegExp(`^${MOLDCLAW_ACTION_PREFIX}`);
|
|
1554
|
-
registerModalLifecycleHandler({
|
|
1555
|
-
register: (matcher, handler) => ctx.app.view(matcher, handler),
|
|
1556
|
-
matcher: modalMatcher,
|
|
1557
|
-
ctx,
|
|
1558
|
-
interactionType: "view_submission",
|
|
1559
|
-
contextPrefix: "slack:interaction:view",
|
|
1560
|
-
summarizeViewState,
|
|
1561
|
-
formatSystemEvent: formatSlackInteractionSystemEvent
|
|
1562
|
-
});
|
|
1563
|
-
const viewClosed = ctx.app.viewClosed;
|
|
1564
|
-
if (typeof viewClosed !== "function") return;
|
|
1565
|
-
registerModalLifecycleHandler({
|
|
1566
|
-
register: viewClosed,
|
|
1567
|
-
matcher: modalMatcher,
|
|
1568
|
-
ctx,
|
|
1569
|
-
interactionType: "view_closed",
|
|
1570
|
-
contextPrefix: "slack:interaction:view-closed",
|
|
1571
|
-
summarizeViewState,
|
|
1572
|
-
formatSystemEvent: formatSlackInteractionSystemEvent
|
|
1573
|
-
});
|
|
1574
|
-
}
|
|
1575
|
-
//#endregion
|
|
1576
|
-
//#region extensions/slack/src/monitor/events/system-event-context.ts
|
|
1577
|
-
async function authorizeAndResolveSlackSystemEventContext(params) {
|
|
1578
|
-
const { ctx, senderId, channelId, channelType, eventKind } = params;
|
|
1579
|
-
const auth = await authorizeSlackSystemEventSender({
|
|
1580
|
-
ctx,
|
|
1581
|
-
senderId,
|
|
1582
|
-
channelId,
|
|
1583
|
-
channelType
|
|
1584
|
-
});
|
|
1585
|
-
if (!auth.allowed) {
|
|
1586
|
-
logVerbose(`slack: drop ${eventKind} sender ${senderId ?? "unknown"} channel=${channelId ?? "unknown"} reason=${auth.reason ?? "unauthorized"}`);
|
|
1587
|
-
return;
|
|
1588
|
-
}
|
|
1589
|
-
return {
|
|
1590
|
-
channelLabel: resolveSlackChannelLabel({
|
|
1591
|
-
channelId,
|
|
1592
|
-
channelName: auth.channelName
|
|
1593
|
-
}),
|
|
1594
|
-
sessionKey: ctx.resolveSlackSystemEventSessionKey({
|
|
1595
|
-
channelId,
|
|
1596
|
-
channelType: auth.channelType,
|
|
1597
|
-
senderId
|
|
1598
|
-
})
|
|
1599
|
-
};
|
|
1600
|
-
}
|
|
1601
|
-
//#endregion
|
|
1602
|
-
//#region extensions/slack/src/monitor/events/members.ts
|
|
1603
|
-
function registerSlackMemberEvents(params) {
|
|
1604
|
-
const { ctx, trackEvent } = params;
|
|
1605
|
-
const handleMemberChannelEvent = async (params) => {
|
|
1606
|
-
try {
|
|
1607
|
-
if (ctx.shouldDropMismatchedSlackEvent(params.body)) return;
|
|
1608
|
-
trackEvent?.();
|
|
1609
|
-
const payload = params.event;
|
|
1610
|
-
const channelId = payload.channel;
|
|
1611
|
-
const channelInfo = channelId ? await ctx.resolveChannelName(channelId) : {};
|
|
1612
|
-
const channelType = payload.channel_type ?? channelInfo?.type;
|
|
1613
|
-
const ingressContext = await authorizeAndResolveSlackSystemEventContext({
|
|
1614
|
-
ctx,
|
|
1615
|
-
senderId: payload.user,
|
|
1616
|
-
channelId,
|
|
1617
|
-
channelType,
|
|
1618
|
-
eventKind: `member-${params.verb}`
|
|
1619
|
-
});
|
|
1620
|
-
if (!ingressContext) return;
|
|
1621
|
-
enqueueSystemEvent(`Slack: ${(payload.user ? await ctx.resolveUserName(payload.user) : {})?.name ?? payload.user ?? "someone"} ${params.verb} ${ingressContext.channelLabel}.`, {
|
|
1622
|
-
sessionKey: ingressContext.sessionKey,
|
|
1623
|
-
contextKey: `slack:member:${params.verb}:${channelId ?? "unknown"}:${payload.user ?? "unknown"}`
|
|
1624
|
-
});
|
|
1625
|
-
} catch (err) {
|
|
1626
|
-
ctx.runtime.error?.(danger(`slack ${params.verb} handler failed: ${String(err)}`));
|
|
1627
|
-
}
|
|
1628
|
-
};
|
|
1629
|
-
ctx.app.event("member_joined_channel", async ({ event, body }) => {
|
|
1630
|
-
await handleMemberChannelEvent({
|
|
1631
|
-
verb: "joined",
|
|
1632
|
-
event,
|
|
1633
|
-
body
|
|
1634
|
-
});
|
|
1635
|
-
});
|
|
1636
|
-
ctx.app.event("member_left_channel", async ({ event, body }) => {
|
|
1637
|
-
await handleMemberChannelEvent({
|
|
1638
|
-
verb: "left",
|
|
1639
|
-
event,
|
|
1640
|
-
body
|
|
1641
|
-
});
|
|
1642
|
-
});
|
|
1643
|
-
}
|
|
1644
|
-
//#endregion
|
|
1645
|
-
//#region extensions/slack/src/monitor/events/message-subtype-handlers.ts
|
|
1646
|
-
const SUBTYPE_HANDLER_REGISTRY = {
|
|
1647
|
-
message_changed: {
|
|
1648
|
-
subtype: "message_changed",
|
|
1649
|
-
eventKind: "message_changed",
|
|
1650
|
-
describe: (channelLabel) => `Slack message edited in ${channelLabel}.`,
|
|
1651
|
-
contextKey: (event) => {
|
|
1652
|
-
const changed = event;
|
|
1653
|
-
return `slack:message:changed:${changed.channel ?? "unknown"}:${changed.message?.ts ?? changed.previous_message?.ts ?? changed.event_ts ?? "unknown"}`;
|
|
1654
|
-
},
|
|
1655
|
-
resolveSenderId: (event) => {
|
|
1656
|
-
const changed = event;
|
|
1657
|
-
return changed.message?.user ?? changed.previous_message?.user ?? changed.message?.bot_id ?? changed.previous_message?.bot_id;
|
|
1658
|
-
},
|
|
1659
|
-
resolveChannelId: (event) => event.channel,
|
|
1660
|
-
resolveChannelType: () => void 0
|
|
1661
|
-
},
|
|
1662
|
-
message_deleted: {
|
|
1663
|
-
subtype: "message_deleted",
|
|
1664
|
-
eventKind: "message_deleted",
|
|
1665
|
-
describe: (channelLabel) => `Slack message deleted in ${channelLabel}.`,
|
|
1666
|
-
contextKey: (event) => {
|
|
1667
|
-
const deleted = event;
|
|
1668
|
-
return `slack:message:deleted:${deleted.channel ?? "unknown"}:${deleted.deleted_ts ?? deleted.event_ts ?? "unknown"}`;
|
|
1669
|
-
},
|
|
1670
|
-
resolveSenderId: (event) => {
|
|
1671
|
-
const deleted = event;
|
|
1672
|
-
return deleted.previous_message?.user ?? deleted.previous_message?.bot_id;
|
|
1673
|
-
},
|
|
1674
|
-
resolveChannelId: (event) => event.channel,
|
|
1675
|
-
resolveChannelType: () => void 0
|
|
1676
|
-
},
|
|
1677
|
-
thread_broadcast: {
|
|
1678
|
-
subtype: "thread_broadcast",
|
|
1679
|
-
eventKind: "thread_broadcast",
|
|
1680
|
-
describe: (channelLabel) => `Slack thread reply broadcast in ${channelLabel}.`,
|
|
1681
|
-
contextKey: (event) => {
|
|
1682
|
-
const thread = event;
|
|
1683
|
-
return `slack:thread:broadcast:${thread.channel ?? "unknown"}:${thread.message?.ts ?? thread.event_ts ?? "unknown"}`;
|
|
1684
|
-
},
|
|
1685
|
-
resolveSenderId: (event) => {
|
|
1686
|
-
const thread = event;
|
|
1687
|
-
return thread.user ?? thread.message?.user ?? thread.message?.bot_id;
|
|
1688
|
-
},
|
|
1689
|
-
resolveChannelId: (event) => event.channel,
|
|
1690
|
-
resolveChannelType: () => void 0
|
|
1691
|
-
}
|
|
1692
|
-
};
|
|
1693
|
-
function resolveSlackMessageSubtypeHandler(event) {
|
|
1694
|
-
const subtype = event.subtype;
|
|
1695
|
-
if (subtype !== "message_changed" && subtype !== "message_deleted" && subtype !== "thread_broadcast") return;
|
|
1696
|
-
return SUBTYPE_HANDLER_REGISTRY[subtype];
|
|
1697
|
-
}
|
|
1698
|
-
//#endregion
|
|
1699
|
-
//#region extensions/slack/src/monitor/events/messages.ts
|
|
1700
|
-
function registerSlackMessageEvents(params) {
|
|
1701
|
-
const { ctx, handleSlackMessage } = params;
|
|
1702
|
-
const handleIncomingMessageEvent = async ({ event, body }) => {
|
|
1703
|
-
try {
|
|
1704
|
-
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
|
1705
|
-
const message = event;
|
|
1706
|
-
const subtypeHandler = resolveSlackMessageSubtypeHandler(message);
|
|
1707
|
-
if (subtypeHandler) {
|
|
1708
|
-
const channelId = subtypeHandler.resolveChannelId(message);
|
|
1709
|
-
const ingressContext = await authorizeAndResolveSlackSystemEventContext({
|
|
1710
|
-
ctx,
|
|
1711
|
-
senderId: subtypeHandler.resolveSenderId(message),
|
|
1712
|
-
channelId,
|
|
1713
|
-
channelType: subtypeHandler.resolveChannelType(message),
|
|
1714
|
-
eventKind: subtypeHandler.eventKind
|
|
1715
|
-
});
|
|
1716
|
-
if (!ingressContext) return;
|
|
1717
|
-
enqueueSystemEvent(subtypeHandler.describe(ingressContext.channelLabel), {
|
|
1718
|
-
sessionKey: ingressContext.sessionKey,
|
|
1719
|
-
contextKey: subtypeHandler.contextKey(message)
|
|
1720
|
-
});
|
|
1721
|
-
return;
|
|
1722
|
-
}
|
|
1723
|
-
await handleSlackMessage(message, { source: "message" });
|
|
1724
|
-
} catch (err) {
|
|
1725
|
-
ctx.runtime.error?.(danger(`slack handler failed: ${String(err)}`));
|
|
1726
|
-
}
|
|
1727
|
-
};
|
|
1728
|
-
ctx.app.event("message", async ({ event, body }) => {
|
|
1729
|
-
await handleIncomingMessageEvent({
|
|
1730
|
-
event,
|
|
1731
|
-
body
|
|
1732
|
-
});
|
|
1733
|
-
});
|
|
1734
|
-
ctx.app.event("app_mention", async ({ event, body }) => {
|
|
1735
|
-
try {
|
|
1736
|
-
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
|
1737
|
-
const mention = event;
|
|
1738
|
-
const channelType = normalizeSlackChannelType(mention.channel_type, mention.channel);
|
|
1739
|
-
if (channelType === "im" || channelType === "mpim") return;
|
|
1740
|
-
await handleSlackMessage(mention, {
|
|
1741
|
-
source: "app_mention",
|
|
1742
|
-
wasMentioned: true
|
|
1743
|
-
});
|
|
1744
|
-
} catch (err) {
|
|
1745
|
-
ctx.runtime.error?.(danger(`slack mention handler failed: ${String(err)}`));
|
|
1746
|
-
}
|
|
1747
|
-
});
|
|
1748
|
-
}
|
|
1749
|
-
//#endregion
|
|
1750
|
-
//#region extensions/slack/src/monitor/events/pins.ts
|
|
1751
|
-
async function handleSlackPinEvent(params) {
|
|
1752
|
-
const { ctx, trackEvent, body, event, action, contextKeySuffix, errorLabel } = params;
|
|
1753
|
-
try {
|
|
1754
|
-
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
|
1755
|
-
trackEvent?.();
|
|
1756
|
-
const payload = event;
|
|
1757
|
-
const channelId = payload.channel_id;
|
|
1758
|
-
const ingressContext = await authorizeAndResolveSlackSystemEventContext({
|
|
1759
|
-
ctx,
|
|
1760
|
-
senderId: payload.user,
|
|
1761
|
-
channelId,
|
|
1762
|
-
eventKind: "pin"
|
|
1763
|
-
});
|
|
1764
|
-
if (!ingressContext) return;
|
|
1765
|
-
const userLabel = (payload.user ? await ctx.resolveUserName(payload.user) : {})?.name ?? payload.user ?? "someone";
|
|
1766
|
-
const itemType = payload.item?.type ?? "item";
|
|
1767
|
-
const messageId = payload.item?.message?.ts ?? payload.event_ts;
|
|
1768
|
-
enqueueSystemEvent(`Slack: ${userLabel} ${action} a ${itemType} in ${ingressContext.channelLabel}.`, {
|
|
1769
|
-
sessionKey: ingressContext.sessionKey,
|
|
1770
|
-
contextKey: `slack:pin:${contextKeySuffix}:${channelId ?? "unknown"}:${messageId ?? "unknown"}`
|
|
1771
|
-
});
|
|
1772
|
-
} catch (err) {
|
|
1773
|
-
ctx.runtime.error?.(danger(`slack ${errorLabel} handler failed: ${String(err)}`));
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
function registerSlackPinEvents(params) {
|
|
1777
|
-
const { ctx, trackEvent } = params;
|
|
1778
|
-
ctx.app.event("pin_added", async ({ event, body }) => {
|
|
1779
|
-
await handleSlackPinEvent({
|
|
1780
|
-
ctx,
|
|
1781
|
-
trackEvent,
|
|
1782
|
-
body,
|
|
1783
|
-
event,
|
|
1784
|
-
action: "pinned",
|
|
1785
|
-
contextKeySuffix: "added",
|
|
1786
|
-
errorLabel: "pin added"
|
|
1787
|
-
});
|
|
1788
|
-
});
|
|
1789
|
-
ctx.app.event("pin_removed", async ({ event, body }) => {
|
|
1790
|
-
await handleSlackPinEvent({
|
|
1791
|
-
ctx,
|
|
1792
|
-
trackEvent,
|
|
1793
|
-
body,
|
|
1794
|
-
event,
|
|
1795
|
-
action: "unpinned",
|
|
1796
|
-
contextKeySuffix: "removed",
|
|
1797
|
-
errorLabel: "pin removed"
|
|
1798
|
-
});
|
|
1799
|
-
});
|
|
1800
|
-
}
|
|
1801
|
-
//#endregion
|
|
1802
|
-
//#region extensions/slack/src/monitor/events/reactions.ts
|
|
1803
|
-
function registerSlackReactionEvents(params) {
|
|
1804
|
-
const { ctx, trackEvent } = params;
|
|
1805
|
-
const handleReactionEvent = async (event, action) => {
|
|
1806
|
-
try {
|
|
1807
|
-
const item = event.item;
|
|
1808
|
-
if (!item || item.type !== "message") return;
|
|
1809
|
-
trackEvent?.();
|
|
1810
|
-
const ingressContext = await authorizeAndResolveSlackSystemEventContext({
|
|
1811
|
-
ctx,
|
|
1812
|
-
senderId: event.user,
|
|
1813
|
-
channelId: item.channel,
|
|
1814
|
-
eventKind: "reaction"
|
|
1815
|
-
});
|
|
1816
|
-
if (!ingressContext) return;
|
|
1817
|
-
const actorInfoPromise = event.user ? ctx.resolveUserName(event.user) : Promise.resolve(void 0);
|
|
1818
|
-
const authorInfoPromise = event.item_user ? ctx.resolveUserName(event.item_user) : Promise.resolve(void 0);
|
|
1819
|
-
const [actorInfo, authorInfo] = await Promise.all([actorInfoPromise, authorInfoPromise]);
|
|
1820
|
-
const actorLabel = actorInfo?.name ?? event.user;
|
|
1821
|
-
const emojiLabel = event.reaction ?? "emoji";
|
|
1822
|
-
const authorLabel = authorInfo?.name ?? event.item_user;
|
|
1823
|
-
const baseText = `Slack reaction ${action}: :${emojiLabel}: by ${actorLabel} in ${ingressContext.channelLabel} msg ${item.ts}`;
|
|
1824
|
-
enqueueSystemEvent(authorLabel ? `${baseText} from ${authorLabel}` : baseText, {
|
|
1825
|
-
sessionKey: ingressContext.sessionKey,
|
|
1826
|
-
contextKey: `slack:reaction:${action}:${item.channel}:${item.ts}:${event.user}:${emojiLabel}`
|
|
1827
|
-
});
|
|
1828
|
-
} catch (err) {
|
|
1829
|
-
ctx.runtime.error?.(danger(`slack reaction handler failed: ${String(err)}`));
|
|
1830
|
-
}
|
|
1831
|
-
};
|
|
1832
|
-
ctx.app.event("reaction_added", async ({ event, body }) => {
|
|
1833
|
-
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
|
1834
|
-
await handleReactionEvent(event, "added");
|
|
1835
|
-
});
|
|
1836
|
-
ctx.app.event("reaction_removed", async ({ event, body }) => {
|
|
1837
|
-
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
|
1838
|
-
await handleReactionEvent(event, "removed");
|
|
1839
|
-
});
|
|
1840
|
-
}
|
|
1841
|
-
//#endregion
|
|
1842
|
-
//#region extensions/slack/src/monitor/events.ts
|
|
1843
|
-
function registerSlackMonitorEvents(params) {
|
|
1844
|
-
registerSlackMessageEvents({
|
|
1845
|
-
ctx: params.ctx,
|
|
1846
|
-
handleSlackMessage: params.handleSlackMessage
|
|
1847
|
-
});
|
|
1848
|
-
registerSlackReactionEvents({
|
|
1849
|
-
ctx: params.ctx,
|
|
1850
|
-
trackEvent: params.trackEvent
|
|
1851
|
-
});
|
|
1852
|
-
registerSlackMemberEvents({
|
|
1853
|
-
ctx: params.ctx,
|
|
1854
|
-
trackEvent: params.trackEvent
|
|
1855
|
-
});
|
|
1856
|
-
registerSlackChannelEvents({
|
|
1857
|
-
ctx: params.ctx,
|
|
1858
|
-
trackEvent: params.trackEvent
|
|
1859
|
-
});
|
|
1860
|
-
registerSlackPinEvents({
|
|
1861
|
-
ctx: params.ctx,
|
|
1862
|
-
trackEvent: params.trackEvent
|
|
1863
|
-
});
|
|
1864
|
-
registerSlackInteractionEvents({ ctx: params.ctx });
|
|
1865
|
-
}
|
|
1866
|
-
//#endregion
|
|
1867
|
-
//#region extensions/slack/src/draft-stream.ts
|
|
1868
|
-
const SLACK_STREAM_MAX_CHARS = 4e3;
|
|
1869
|
-
const DEFAULT_THROTTLE_MS = 1e3;
|
|
1870
|
-
function createSlackDraftStream(params) {
|
|
1871
|
-
const maxChars = Math.min(params.maxChars ?? SLACK_STREAM_MAX_CHARS, SLACK_STREAM_MAX_CHARS);
|
|
1872
|
-
const throttleMs = Math.max(250, params.throttleMs ?? DEFAULT_THROTTLE_MS);
|
|
1873
|
-
const send = params.send ?? sendMessageSlack;
|
|
1874
|
-
const edit = params.edit ?? editSlackMessage;
|
|
1875
|
-
const remove = params.remove ?? deleteSlackMessage;
|
|
1876
|
-
let streamMessageId;
|
|
1877
|
-
let streamChannelId;
|
|
1878
|
-
let lastSentText = "";
|
|
1879
|
-
let stopped = false;
|
|
1880
|
-
const sendOrEditStreamMessage = async (text) => {
|
|
1881
|
-
if (stopped) return;
|
|
1882
|
-
const trimmed = text.trimEnd();
|
|
1883
|
-
if (!trimmed) return;
|
|
1884
|
-
if (trimmed.length > maxChars) {
|
|
1885
|
-
stopped = true;
|
|
1886
|
-
params.warn?.(`slack stream preview stopped (text length ${trimmed.length} > ${maxChars})`);
|
|
1887
|
-
return;
|
|
1888
|
-
}
|
|
1889
|
-
if (trimmed === lastSentText) return;
|
|
1890
|
-
lastSentText = trimmed;
|
|
1891
|
-
try {
|
|
1892
|
-
if (streamChannelId && streamMessageId) {
|
|
1893
|
-
await edit(streamChannelId, streamMessageId, trimmed, {
|
|
1894
|
-
token: params.token,
|
|
1895
|
-
accountId: params.accountId
|
|
1896
|
-
});
|
|
1897
|
-
return;
|
|
1898
|
-
}
|
|
1899
|
-
const sent = await send(params.target, trimmed, {
|
|
1900
|
-
token: params.token,
|
|
1901
|
-
accountId: params.accountId,
|
|
1902
|
-
threadTs: params.resolveThreadTs?.()
|
|
1903
|
-
});
|
|
1904
|
-
streamChannelId = sent.channelId || streamChannelId;
|
|
1905
|
-
streamMessageId = sent.messageId || streamMessageId;
|
|
1906
|
-
if (!streamChannelId || !streamMessageId) {
|
|
1907
|
-
stopped = true;
|
|
1908
|
-
params.warn?.("slack stream preview stopped (missing identifiers from sendMessage)");
|
|
1909
|
-
return;
|
|
1910
|
-
}
|
|
1911
|
-
params.onMessageSent?.();
|
|
1912
|
-
} catch (err) {
|
|
1913
|
-
stopped = true;
|
|
1914
|
-
params.warn?.(`slack stream preview failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1915
|
-
}
|
|
1916
|
-
};
|
|
1917
|
-
const loop = createDraftStreamLoop({
|
|
1918
|
-
throttleMs,
|
|
1919
|
-
isStopped: () => stopped,
|
|
1920
|
-
sendOrEditStreamMessage
|
|
1921
|
-
});
|
|
1922
|
-
const stop = () => {
|
|
1923
|
-
stopped = true;
|
|
1924
|
-
loop.stop();
|
|
1925
|
-
};
|
|
1926
|
-
const clear = async () => {
|
|
1927
|
-
stop();
|
|
1928
|
-
await loop.waitForInFlight();
|
|
1929
|
-
const channelId = streamChannelId;
|
|
1930
|
-
const messageId = streamMessageId;
|
|
1931
|
-
streamChannelId = void 0;
|
|
1932
|
-
streamMessageId = void 0;
|
|
1933
|
-
lastSentText = "";
|
|
1934
|
-
if (!channelId || !messageId) return;
|
|
1935
|
-
try {
|
|
1936
|
-
await remove(channelId, messageId, {
|
|
1937
|
-
token: params.token,
|
|
1938
|
-
accountId: params.accountId
|
|
1939
|
-
});
|
|
1940
|
-
} catch (err) {
|
|
1941
|
-
params.warn?.(`slack stream preview cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1942
|
-
}
|
|
1943
|
-
};
|
|
1944
|
-
const forceNewMessage = () => {
|
|
1945
|
-
streamMessageId = void 0;
|
|
1946
|
-
streamChannelId = void 0;
|
|
1947
|
-
lastSentText = "";
|
|
1948
|
-
loop.resetPending();
|
|
1949
|
-
};
|
|
1950
|
-
params.log?.(`slack stream preview ready (maxChars=${maxChars}, throttleMs=${throttleMs})`);
|
|
1951
|
-
return {
|
|
1952
|
-
update: loop.update,
|
|
1953
|
-
flush: loop.flush,
|
|
1954
|
-
clear,
|
|
1955
|
-
stop,
|
|
1956
|
-
forceNewMessage,
|
|
1957
|
-
messageId: () => streamMessageId,
|
|
1958
|
-
channelId: () => streamChannelId
|
|
1959
|
-
};
|
|
1960
|
-
}
|
|
1961
|
-
//#endregion
|
|
1962
|
-
//#region extensions/slack/src/stream-mode.ts
|
|
1963
|
-
function resolveSlackStreamingConfig(params) {
|
|
1964
|
-
const mode = resolveSlackStreamingMode(params);
|
|
1965
|
-
return {
|
|
1966
|
-
mode,
|
|
1967
|
-
nativeStreaming: resolveSlackNativeStreaming(params),
|
|
1968
|
-
draftMode: mapStreamingModeToSlackLegacyDraftStreamMode(mode)
|
|
1969
|
-
};
|
|
1970
|
-
}
|
|
1971
|
-
function applyAppendOnlyStreamUpdate(params) {
|
|
1972
|
-
const incoming = params.incoming.trimEnd();
|
|
1973
|
-
if (!incoming) return {
|
|
1974
|
-
rendered: params.rendered,
|
|
1975
|
-
source: params.source,
|
|
1976
|
-
changed: false
|
|
1977
|
-
};
|
|
1978
|
-
if (!params.rendered) return {
|
|
1979
|
-
rendered: incoming,
|
|
1980
|
-
source: incoming,
|
|
1981
|
-
changed: true
|
|
1982
|
-
};
|
|
1983
|
-
if (incoming === params.source) return {
|
|
1984
|
-
rendered: params.rendered,
|
|
1985
|
-
source: params.source,
|
|
1986
|
-
changed: false
|
|
1987
|
-
};
|
|
1988
|
-
if (incoming.startsWith(params.source) || incoming.startsWith(params.rendered)) return {
|
|
1989
|
-
rendered: incoming,
|
|
1990
|
-
source: incoming,
|
|
1991
|
-
changed: incoming !== params.rendered
|
|
1992
|
-
};
|
|
1993
|
-
if (params.source.startsWith(incoming)) return {
|
|
1994
|
-
rendered: params.rendered,
|
|
1995
|
-
source: params.source,
|
|
1996
|
-
changed: false
|
|
1997
|
-
};
|
|
1998
|
-
const separator = params.rendered.endsWith("\n") ? "" : "\n";
|
|
1999
|
-
return {
|
|
2000
|
-
rendered: `${params.rendered}${separator}${incoming}`,
|
|
2001
|
-
source: incoming,
|
|
2002
|
-
changed: true
|
|
2003
|
-
};
|
|
2004
|
-
}
|
|
2005
|
-
function buildStatusFinalPreviewText(updateCount) {
|
|
2006
|
-
return `Status: thinking${".".repeat(Math.max(1, updateCount) % 3 + 1)}`;
|
|
2007
|
-
}
|
|
2008
|
-
//#endregion
|
|
2009
|
-
//#region extensions/slack/src/streaming.ts
|
|
2010
|
-
/**
|
|
2011
|
-
* Start a new Slack text stream.
|
|
2012
|
-
*
|
|
2013
|
-
* Returns a {@link SlackStreamSession} that should be passed to
|
|
2014
|
-
* {@link appendSlackStream} and {@link stopSlackStream}.
|
|
2015
|
-
*
|
|
2016
|
-
* The first chunk of text can optionally be included via `text`.
|
|
2017
|
-
*/
|
|
2018
|
-
async function startSlackStream(params) {
|
|
2019
|
-
const { client, channel, threadTs, text, teamId, userId } = params;
|
|
2020
|
-
logVerbose(`slack-stream: starting stream in ${channel} thread=${threadTs}${teamId ? ` team=${teamId}` : ""}${userId ? ` user=${userId}` : ""}`);
|
|
2021
|
-
const streamer = client.chatStream({
|
|
2022
|
-
channel,
|
|
2023
|
-
thread_ts: threadTs,
|
|
2024
|
-
...teamId ? { recipient_team_id: teamId } : {},
|
|
2025
|
-
...userId ? { recipient_user_id: userId } : {}
|
|
2026
|
-
});
|
|
2027
|
-
const session = {
|
|
2028
|
-
streamer,
|
|
2029
|
-
channel,
|
|
2030
|
-
threadTs,
|
|
2031
|
-
stopped: false
|
|
2032
|
-
};
|
|
2033
|
-
if (text) {
|
|
2034
|
-
await streamer.append({ markdown_text: text });
|
|
2035
|
-
logVerbose(`slack-stream: appended initial text (${text.length} chars)`);
|
|
2036
|
-
}
|
|
2037
|
-
return session;
|
|
2038
|
-
}
|
|
2039
|
-
/**
|
|
2040
|
-
* Append markdown text to an active Slack stream.
|
|
2041
|
-
*/
|
|
2042
|
-
async function appendSlackStream(params) {
|
|
2043
|
-
const { session, text } = params;
|
|
2044
|
-
if (session.stopped) {
|
|
2045
|
-
logVerbose("slack-stream: attempted to append to a stopped stream, ignoring");
|
|
2046
|
-
return;
|
|
2047
|
-
}
|
|
2048
|
-
if (!text) return;
|
|
2049
|
-
await session.streamer.append({ markdown_text: text });
|
|
2050
|
-
logVerbose(`slack-stream: appended ${text.length} chars`);
|
|
2051
|
-
}
|
|
2052
|
-
/**
|
|
2053
|
-
* Stop (finalize) a Slack stream.
|
|
2054
|
-
*
|
|
2055
|
-
* After calling this the stream message becomes a normal Slack message.
|
|
2056
|
-
* Optionally include final text to append before stopping.
|
|
2057
|
-
*/
|
|
2058
|
-
async function stopSlackStream(params) {
|
|
2059
|
-
const { session, text } = params;
|
|
2060
|
-
if (session.stopped) {
|
|
2061
|
-
logVerbose("slack-stream: stream already stopped, ignoring duplicate stop");
|
|
2062
|
-
return;
|
|
2063
|
-
}
|
|
2064
|
-
session.stopped = true;
|
|
2065
|
-
logVerbose(`slack-stream: stopping stream in ${session.channel} thread=${session.threadTs}${text ? ` (final text: ${text.length} chars)` : ""}`);
|
|
2066
|
-
await session.streamer.stop(text ? { markdown_text: text } : void 0);
|
|
2067
|
-
logVerbose("slack-stream: stream stopped");
|
|
2068
|
-
}
|
|
2069
|
-
//#endregion
|
|
2070
|
-
//#region extensions/slack/src/threading.ts
|
|
2071
|
-
function resolveSlackThreadContext(params) {
|
|
2072
|
-
const incomingThreadTs = params.message.thread_ts;
|
|
2073
|
-
const eventTs = params.message.event_ts;
|
|
2074
|
-
const messageTs = params.message.ts ?? eventTs;
|
|
2075
|
-
const isThreadReply = typeof incomingThreadTs === "string" && incomingThreadTs.length > 0 && (incomingThreadTs !== messageTs || Boolean(params.message.parent_user_id));
|
|
2076
|
-
return {
|
|
2077
|
-
incomingThreadTs,
|
|
2078
|
-
messageTs,
|
|
2079
|
-
isThreadReply,
|
|
2080
|
-
replyToId: incomingThreadTs ?? messageTs,
|
|
2081
|
-
messageThreadId: isThreadReply ? incomingThreadTs : params.replyToMode === "all" ? messageTs : void 0
|
|
2082
|
-
};
|
|
2083
|
-
}
|
|
2084
|
-
/**
|
|
2085
|
-
* Resolves Slack thread targeting for replies and status indicators.
|
|
2086
|
-
*
|
|
2087
|
-
* @returns replyThreadTs - Thread timestamp for reply messages
|
|
2088
|
-
* @returns statusThreadTs - Thread timestamp for status indicators (typing, etc.)
|
|
2089
|
-
* @returns isThreadReply - true if this is a genuine user reply in a thread,
|
|
2090
|
-
* false if thread_ts comes from a bot status message (e.g. typing indicator)
|
|
2091
|
-
*/
|
|
2092
|
-
function resolveSlackThreadTargets(params) {
|
|
2093
|
-
const { incomingThreadTs, messageTs, isThreadReply } = resolveSlackThreadContext(params);
|
|
2094
|
-
const replyThreadTs = isThreadReply ? incomingThreadTs : params.replyToMode === "all" ? messageTs : void 0;
|
|
2095
|
-
return {
|
|
2096
|
-
replyThreadTs,
|
|
2097
|
-
statusThreadTs: replyThreadTs,
|
|
2098
|
-
isThreadReply
|
|
2099
|
-
};
|
|
2100
|
-
}
|
|
2101
|
-
//#endregion
|
|
2102
|
-
//#region extensions/slack/src/monitor/message-handler/dispatch.ts
|
|
2103
|
-
function hasMedia(payload) {
|
|
2104
|
-
return Boolean(payload.mediaUrl) || (payload.mediaUrls?.length ?? 0) > 0;
|
|
2105
|
-
}
|
|
2106
|
-
function isSlackStreamingEnabled(params) {
|
|
2107
|
-
if (params.mode !== "partial") return false;
|
|
2108
|
-
return params.nativeStreaming;
|
|
2109
|
-
}
|
|
2110
|
-
function resolveSlackStreamingThreadHint(params) {
|
|
2111
|
-
return resolveSlackThreadTs({
|
|
2112
|
-
replyToMode: params.replyToMode,
|
|
2113
|
-
incomingThreadTs: params.incomingThreadTs,
|
|
2114
|
-
messageTs: params.messageTs,
|
|
2115
|
-
hasReplied: false,
|
|
2116
|
-
isThreadReply: params.isThreadReply
|
|
2117
|
-
});
|
|
2118
|
-
}
|
|
2119
|
-
function shouldUseStreaming(params) {
|
|
2120
|
-
if (!params.streamingEnabled) return false;
|
|
2121
|
-
if (!params.threadTs) {
|
|
2122
|
-
logVerbose("slack-stream: streaming disabled — no reply thread target available");
|
|
2123
|
-
return false;
|
|
2124
|
-
}
|
|
2125
|
-
return true;
|
|
2126
|
-
}
|
|
2127
|
-
async function dispatchPreparedSlackMessage(prepared) {
|
|
2128
|
-
const { ctx, account, message, route } = prepared;
|
|
2129
|
-
const cfg = ctx.cfg;
|
|
2130
|
-
const runtime = ctx.runtime;
|
|
2131
|
-
const outboundIdentity = resolveAgentOutboundIdentity(cfg, route.agentId);
|
|
2132
|
-
const slackIdentity = outboundIdentity ? {
|
|
2133
|
-
username: outboundIdentity.name,
|
|
2134
|
-
iconUrl: outboundIdentity.avatarUrl,
|
|
2135
|
-
iconEmoji: outboundIdentity.emoji
|
|
2136
|
-
} : void 0;
|
|
2137
|
-
if (prepared.isDirectMessage) {
|
|
2138
|
-
const sessionCfg = cfg.session;
|
|
2139
|
-
const storePath = resolveStorePath(sessionCfg?.store, { agentId: route.agentId });
|
|
2140
|
-
const pinnedMainDmOwner = resolvePinnedMainDmOwnerFromAllowlist({
|
|
2141
|
-
dmScope: cfg.session?.dmScope,
|
|
2142
|
-
allowFrom: ctx.allowFrom,
|
|
2143
|
-
normalizeEntry: normalizeSlackAllowOwnerEntry
|
|
2144
|
-
});
|
|
2145
|
-
const senderRecipient = message.user?.trim().toLowerCase();
|
|
2146
|
-
if (pinnedMainDmOwner && senderRecipient && pinnedMainDmOwner.trim().toLowerCase() !== senderRecipient) logVerbose(`slack: skip main-session last route for ${senderRecipient} (pinned owner ${pinnedMainDmOwner})`);
|
|
2147
|
-
else await updateLastRoute({
|
|
2148
|
-
storePath,
|
|
2149
|
-
sessionKey: route.mainSessionKey,
|
|
2150
|
-
deliveryContext: {
|
|
2151
|
-
channel: "slack",
|
|
2152
|
-
to: `user:${message.user}`,
|
|
2153
|
-
accountId: route.accountId,
|
|
2154
|
-
threadId: prepared.ctxPayload.MessageThreadId
|
|
2155
|
-
},
|
|
2156
|
-
ctx: prepared.ctxPayload
|
|
2157
|
-
});
|
|
2158
|
-
}
|
|
2159
|
-
const { statusThreadTs, isThreadReply } = resolveSlackThreadTargets({
|
|
2160
|
-
message,
|
|
2161
|
-
replyToMode: prepared.replyToMode
|
|
2162
|
-
});
|
|
2163
|
-
const messageTs = message.ts ?? message.event_ts;
|
|
2164
|
-
const incomingThreadTs = message.thread_ts;
|
|
2165
|
-
let didSetStatus = false;
|
|
2166
|
-
const hasRepliedRef = { value: false };
|
|
2167
|
-
const replyPlan = createSlackReplyDeliveryPlan({
|
|
2168
|
-
replyToMode: prepared.replyToMode,
|
|
2169
|
-
incomingThreadTs,
|
|
2170
|
-
messageTs,
|
|
2171
|
-
hasRepliedRef,
|
|
2172
|
-
isThreadReply
|
|
2173
|
-
});
|
|
2174
|
-
const typingTarget = statusThreadTs ? `${message.channel}/${statusThreadTs}` : message.channel;
|
|
2175
|
-
const typingReaction = ctx.typingReaction;
|
|
2176
|
-
const typingCallbacks = createTypingCallbacks({
|
|
2177
|
-
start: async () => {
|
|
2178
|
-
didSetStatus = true;
|
|
2179
|
-
await ctx.setSlackThreadStatus({
|
|
2180
|
-
channelId: message.channel,
|
|
2181
|
-
threadTs: statusThreadTs,
|
|
2182
|
-
status: "is typing..."
|
|
2183
|
-
});
|
|
2184
|
-
if (typingReaction && message.ts) await reactSlackMessage(message.channel, message.ts, typingReaction, {
|
|
2185
|
-
token: ctx.botToken,
|
|
2186
|
-
client: ctx.app.client
|
|
2187
|
-
}).catch(() => {});
|
|
2188
|
-
},
|
|
2189
|
-
stop: async () => {
|
|
2190
|
-
if (!didSetStatus) return;
|
|
2191
|
-
didSetStatus = false;
|
|
2192
|
-
await ctx.setSlackThreadStatus({
|
|
2193
|
-
channelId: message.channel,
|
|
2194
|
-
threadTs: statusThreadTs,
|
|
2195
|
-
status: ""
|
|
2196
|
-
});
|
|
2197
|
-
if (typingReaction && message.ts) await removeSlackReaction(message.channel, message.ts, typingReaction, {
|
|
2198
|
-
token: ctx.botToken,
|
|
2199
|
-
client: ctx.app.client
|
|
2200
|
-
}).catch(() => {});
|
|
2201
|
-
},
|
|
2202
|
-
onStartError: (err) => {
|
|
2203
|
-
logTypingFailure({
|
|
2204
|
-
log: (message) => runtime.error?.(danger(message)),
|
|
2205
|
-
channel: "slack",
|
|
2206
|
-
action: "start",
|
|
2207
|
-
target: typingTarget,
|
|
2208
|
-
error: err
|
|
2209
|
-
});
|
|
2210
|
-
},
|
|
2211
|
-
onStopError: (err) => {
|
|
2212
|
-
logTypingFailure({
|
|
2213
|
-
log: (message) => runtime.error?.(danger(message)),
|
|
2214
|
-
channel: "slack",
|
|
2215
|
-
action: "stop",
|
|
2216
|
-
target: typingTarget,
|
|
2217
|
-
error: err
|
|
2218
|
-
});
|
|
2219
|
-
}
|
|
2220
|
-
});
|
|
2221
|
-
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
|
|
2222
|
-
cfg,
|
|
2223
|
-
agentId: route.agentId,
|
|
2224
|
-
channel: "slack",
|
|
2225
|
-
accountId: route.accountId
|
|
2226
|
-
});
|
|
2227
|
-
const slackStreaming = resolveSlackStreamingConfig({
|
|
2228
|
-
streaming: account.config.streaming,
|
|
2229
|
-
streamMode: account.config.streamMode,
|
|
2230
|
-
nativeStreaming: account.config.nativeStreaming
|
|
2231
|
-
});
|
|
2232
|
-
const previewStreamingEnabled = slackStreaming.mode !== "off";
|
|
2233
|
-
const useStreaming = shouldUseStreaming({
|
|
2234
|
-
streamingEnabled: isSlackStreamingEnabled({
|
|
2235
|
-
mode: slackStreaming.mode,
|
|
2236
|
-
nativeStreaming: slackStreaming.nativeStreaming
|
|
2237
|
-
}),
|
|
2238
|
-
threadTs: resolveSlackStreamingThreadHint({
|
|
2239
|
-
replyToMode: prepared.replyToMode,
|
|
2240
|
-
incomingThreadTs,
|
|
2241
|
-
messageTs,
|
|
2242
|
-
isThreadReply
|
|
2243
|
-
})
|
|
2244
|
-
});
|
|
2245
|
-
let streamSession = null;
|
|
2246
|
-
let streamFailed = false;
|
|
2247
|
-
let usedReplyThreadTs;
|
|
2248
|
-
const deliverNormally = async (payload, forcedThreadTs) => {
|
|
2249
|
-
const replyThreadTs = forcedThreadTs ?? replyPlan.nextThreadTs();
|
|
2250
|
-
await deliverReplies({
|
|
2251
|
-
replies: [payload],
|
|
2252
|
-
target: prepared.replyTarget,
|
|
2253
|
-
token: ctx.botToken,
|
|
2254
|
-
accountId: account.accountId,
|
|
2255
|
-
runtime,
|
|
2256
|
-
textLimit: ctx.textLimit,
|
|
2257
|
-
replyThreadTs,
|
|
2258
|
-
replyToMode: prepared.replyToMode,
|
|
2259
|
-
...slackIdentity ? { identity: slackIdentity } : {}
|
|
2260
|
-
});
|
|
2261
|
-
if (replyThreadTs) usedReplyThreadTs ??= replyThreadTs;
|
|
2262
|
-
replyPlan.markSent();
|
|
2263
|
-
};
|
|
2264
|
-
const deliverWithStreaming = async (payload) => {
|
|
2265
|
-
if (streamFailed || hasMedia(payload) || readSlackReplyBlocks(payload)?.length || !payload.text?.trim()) {
|
|
2266
|
-
await deliverNormally(payload, streamSession?.threadTs);
|
|
2267
|
-
return;
|
|
2268
|
-
}
|
|
2269
|
-
const text = payload.text.trim();
|
|
2270
|
-
let plannedThreadTs;
|
|
2271
|
-
try {
|
|
2272
|
-
if (!streamSession) {
|
|
2273
|
-
const streamThreadTs = replyPlan.nextThreadTs();
|
|
2274
|
-
plannedThreadTs = streamThreadTs;
|
|
2275
|
-
if (!streamThreadTs) {
|
|
2276
|
-
logVerbose("slack-stream: no reply thread target for stream start, falling back to normal delivery");
|
|
2277
|
-
streamFailed = true;
|
|
2278
|
-
await deliverNormally(payload);
|
|
2279
|
-
return;
|
|
2280
|
-
}
|
|
2281
|
-
streamSession = await startSlackStream({
|
|
2282
|
-
client: ctx.app.client,
|
|
2283
|
-
channel: message.channel,
|
|
2284
|
-
threadTs: streamThreadTs,
|
|
2285
|
-
text,
|
|
2286
|
-
teamId: ctx.teamId,
|
|
2287
|
-
userId: message.user
|
|
2288
|
-
});
|
|
2289
|
-
usedReplyThreadTs ??= streamThreadTs;
|
|
2290
|
-
replyPlan.markSent();
|
|
2291
|
-
return;
|
|
2292
|
-
}
|
|
2293
|
-
await appendSlackStream({
|
|
2294
|
-
session: streamSession,
|
|
2295
|
-
text: "\n" + text
|
|
2296
|
-
});
|
|
2297
|
-
} catch (err) {
|
|
2298
|
-
runtime.error?.(danger(`slack-stream: streaming API call failed: ${String(err)}, falling back`));
|
|
2299
|
-
streamFailed = true;
|
|
2300
|
-
await deliverNormally(payload, streamSession?.threadTs ?? plannedThreadTs);
|
|
2301
|
-
}
|
|
2302
|
-
};
|
|
2303
|
-
const { dispatcher, replyOptions, markDispatchIdle } = createReplyDispatcherWithTyping({
|
|
2304
|
-
...prefixOptions,
|
|
2305
|
-
humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
|
|
2306
|
-
typingCallbacks,
|
|
2307
|
-
deliver: async (payload) => {
|
|
2308
|
-
if (useStreaming) {
|
|
2309
|
-
await deliverWithStreaming(payload);
|
|
2310
|
-
return;
|
|
2311
|
-
}
|
|
2312
|
-
const mediaCount = payload.mediaUrls?.length ?? (payload.mediaUrl ? 1 : 0);
|
|
2313
|
-
const slackBlocks = readSlackReplyBlocks(payload);
|
|
2314
|
-
const draftMessageId = draftStream?.messageId();
|
|
2315
|
-
const draftChannelId = draftStream?.channelId();
|
|
2316
|
-
const trimmedFinalText = (payload.text ?? "").trim();
|
|
2317
|
-
if (previewStreamingEnabled && streamMode !== "status_final" && mediaCount === 0 && !payload.isError && (trimmedFinalText.length > 0 || Boolean(slackBlocks?.length)) && typeof draftMessageId === "string" && typeof draftChannelId === "string") {
|
|
2318
|
-
draftStream?.stop();
|
|
2319
|
-
try {
|
|
2320
|
-
await editSlackMessage(draftChannelId, draftMessageId, normalizeSlackOutboundText(trimmedFinalText), {
|
|
2321
|
-
token: ctx.botToken,
|
|
2322
|
-
accountId: account.accountId,
|
|
2323
|
-
client: ctx.app.client,
|
|
2324
|
-
...slackBlocks?.length ? { blocks: slackBlocks } : {}
|
|
2325
|
-
});
|
|
2326
|
-
return;
|
|
2327
|
-
} catch (err) {
|
|
2328
|
-
logVerbose(`slack: preview final edit failed; falling back to standard send (${String(err)})`);
|
|
2329
|
-
}
|
|
2330
|
-
} else if (previewStreamingEnabled && streamMode === "status_final" && hasStreamedMessage) try {
|
|
2331
|
-
const statusChannelId = draftStream?.channelId();
|
|
2332
|
-
const statusMessageId = draftStream?.messageId();
|
|
2333
|
-
if (statusChannelId && statusMessageId) await ctx.app.client.chat.update({
|
|
2334
|
-
token: ctx.botToken,
|
|
2335
|
-
channel: statusChannelId,
|
|
2336
|
-
ts: statusMessageId,
|
|
2337
|
-
text: "Status: complete. Final answer posted below."
|
|
2338
|
-
});
|
|
2339
|
-
} catch (err) {
|
|
2340
|
-
logVerbose(`slack: status_final completion update failed (${String(err)})`);
|
|
2341
|
-
}
|
|
2342
|
-
else if (mediaCount > 0) {
|
|
2343
|
-
await draftStream?.clear();
|
|
2344
|
-
hasStreamedMessage = false;
|
|
2345
|
-
}
|
|
2346
|
-
await deliverNormally(payload);
|
|
2347
|
-
},
|
|
2348
|
-
onError: (err, info) => {
|
|
2349
|
-
runtime.error?.(danger(`slack ${info.kind} reply failed: ${String(err)}`));
|
|
2350
|
-
typingCallbacks.onIdle?.();
|
|
2351
|
-
}
|
|
2352
|
-
});
|
|
2353
|
-
const draftStream = createSlackDraftStream({
|
|
2354
|
-
target: prepared.replyTarget,
|
|
2355
|
-
token: ctx.botToken,
|
|
2356
|
-
accountId: account.accountId,
|
|
2357
|
-
maxChars: Math.min(ctx.textLimit, 4e3),
|
|
2358
|
-
resolveThreadTs: () => {
|
|
2359
|
-
const ts = replyPlan.nextThreadTs();
|
|
2360
|
-
if (ts) usedReplyThreadTs ??= ts;
|
|
2361
|
-
return ts;
|
|
2362
|
-
},
|
|
2363
|
-
onMessageSent: () => replyPlan.markSent(),
|
|
2364
|
-
log: logVerbose,
|
|
2365
|
-
warn: logVerbose
|
|
2366
|
-
});
|
|
2367
|
-
let hasStreamedMessage = false;
|
|
2368
|
-
const streamMode = slackStreaming.draftMode;
|
|
2369
|
-
let appendRenderedText = "";
|
|
2370
|
-
let appendSourceText = "";
|
|
2371
|
-
let statusUpdateCount = 0;
|
|
2372
|
-
const updateDraftFromPartial = (text) => {
|
|
2373
|
-
const trimmed = text?.trimEnd();
|
|
2374
|
-
if (!trimmed) return;
|
|
2375
|
-
if (streamMode === "append") {
|
|
2376
|
-
const next = applyAppendOnlyStreamUpdate({
|
|
2377
|
-
incoming: trimmed,
|
|
2378
|
-
rendered: appendRenderedText,
|
|
2379
|
-
source: appendSourceText
|
|
2380
|
-
});
|
|
2381
|
-
appendRenderedText = next.rendered;
|
|
2382
|
-
appendSourceText = next.source;
|
|
2383
|
-
if (!next.changed) return;
|
|
2384
|
-
draftStream.update(next.rendered);
|
|
2385
|
-
hasStreamedMessage = true;
|
|
2386
|
-
return;
|
|
2387
|
-
}
|
|
2388
|
-
if (streamMode === "status_final") {
|
|
2389
|
-
statusUpdateCount += 1;
|
|
2390
|
-
if (statusUpdateCount > 1 && statusUpdateCount % 4 !== 0) return;
|
|
2391
|
-
draftStream.update(buildStatusFinalPreviewText(statusUpdateCount));
|
|
2392
|
-
hasStreamedMessage = true;
|
|
2393
|
-
return;
|
|
2394
|
-
}
|
|
2395
|
-
draftStream.update(trimmed);
|
|
2396
|
-
hasStreamedMessage = true;
|
|
2397
|
-
};
|
|
2398
|
-
const onDraftBoundary = useStreaming || !previewStreamingEnabled ? void 0 : async () => {
|
|
2399
|
-
if (hasStreamedMessage) {
|
|
2400
|
-
draftStream.forceNewMessage();
|
|
2401
|
-
hasStreamedMessage = false;
|
|
2402
|
-
appendRenderedText = "";
|
|
2403
|
-
appendSourceText = "";
|
|
2404
|
-
statusUpdateCount = 0;
|
|
2405
|
-
}
|
|
2406
|
-
};
|
|
2407
|
-
const { queuedFinal, counts } = await dispatchInboundMessage({
|
|
2408
|
-
ctx: prepared.ctxPayload,
|
|
2409
|
-
cfg,
|
|
2410
|
-
dispatcher,
|
|
2411
|
-
replyOptions: {
|
|
2412
|
-
...replyOptions,
|
|
2413
|
-
skillFilter: prepared.channelConfig?.skills,
|
|
2414
|
-
hasRepliedRef,
|
|
2415
|
-
disableBlockStreaming: useStreaming ? true : typeof account.config.blockStreaming === "boolean" ? !account.config.blockStreaming : void 0,
|
|
2416
|
-
onModelSelected,
|
|
2417
|
-
onPartialReply: useStreaming ? void 0 : !previewStreamingEnabled ? void 0 : async (payload) => {
|
|
2418
|
-
updateDraftFromPartial(payload.text);
|
|
2419
|
-
},
|
|
2420
|
-
onAssistantMessageStart: onDraftBoundary,
|
|
2421
|
-
onReasoningEnd: onDraftBoundary
|
|
2422
|
-
}
|
|
2423
|
-
});
|
|
2424
|
-
await draftStream.flush();
|
|
2425
|
-
draftStream.stop();
|
|
2426
|
-
markDispatchIdle();
|
|
2427
|
-
const finalStream = streamSession;
|
|
2428
|
-
if (finalStream && !finalStream.stopped) try {
|
|
2429
|
-
await stopSlackStream({ session: finalStream });
|
|
2430
|
-
} catch (err) {
|
|
2431
|
-
runtime.error?.(danger(`slack-stream: failed to stop stream: ${String(err)}`));
|
|
2432
|
-
}
|
|
2433
|
-
const anyReplyDelivered = queuedFinal || (counts.block ?? 0) > 0 || (counts.final ?? 0) > 0;
|
|
2434
|
-
const participationThreadTs = usedReplyThreadTs ?? statusThreadTs;
|
|
2435
|
-
if (anyReplyDelivered && participationThreadTs) recordSlackThreadParticipation(account.accountId, message.channel, participationThreadTs);
|
|
2436
|
-
if (!anyReplyDelivered) {
|
|
2437
|
-
await draftStream.clear();
|
|
2438
|
-
if (prepared.isRoomish) clearHistoryEntriesIfEnabled({
|
|
2439
|
-
historyMap: ctx.channelHistories,
|
|
2440
|
-
historyKey: prepared.historyKey,
|
|
2441
|
-
limit: ctx.historyLimit
|
|
2442
|
-
});
|
|
2443
|
-
return;
|
|
2444
|
-
}
|
|
2445
|
-
if (shouldLogVerbose()) {
|
|
2446
|
-
const finalCount = counts.final;
|
|
2447
|
-
logVerbose(`slack: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${prepared.replyTarget}`);
|
|
2448
|
-
}
|
|
2449
|
-
removeAckReactionAfterReply({
|
|
2450
|
-
removeAfterReply: ctx.removeAckAfterReply,
|
|
2451
|
-
ackReactionPromise: prepared.ackReactionPromise,
|
|
2452
|
-
ackReactionValue: prepared.ackReactionValue,
|
|
2453
|
-
remove: () => removeSlackReaction(message.channel, prepared.ackReactionMessageTs ?? "", prepared.ackReactionValue, {
|
|
2454
|
-
token: ctx.botToken,
|
|
2455
|
-
client: ctx.app.client
|
|
2456
|
-
}),
|
|
2457
|
-
onError: (err) => {
|
|
2458
|
-
logAckFailure({
|
|
2459
|
-
log: logVerbose,
|
|
2460
|
-
channel: "slack",
|
|
2461
|
-
target: `${message.channel}/${message.ts}`,
|
|
2462
|
-
error: err
|
|
2463
|
-
});
|
|
2464
|
-
}
|
|
2465
|
-
});
|
|
2466
|
-
if (prepared.isRoomish) clearHistoryEntriesIfEnabled({
|
|
2467
|
-
historyMap: ctx.channelHistories,
|
|
2468
|
-
historyKey: prepared.historyKey,
|
|
2469
|
-
limit: ctx.historyLimit
|
|
2470
|
-
});
|
|
2471
|
-
}
|
|
2472
|
-
//#endregion
|
|
2473
|
-
//#region extensions/slack/src/monitor/dm-auth.ts
|
|
2474
|
-
async function authorizeSlackDirectMessage(params) {
|
|
2475
|
-
if (!params.ctx.dmEnabled || params.ctx.dmPolicy === "disabled") {
|
|
2476
|
-
await params.onDisabled();
|
|
2477
|
-
return false;
|
|
2478
|
-
}
|
|
2479
|
-
if (params.ctx.dmPolicy === "open") return true;
|
|
2480
|
-
const senderName = (await params.resolveSenderName(params.senderId))?.name ?? void 0;
|
|
2481
|
-
const allowMatch = resolveSlackAllowListMatch({
|
|
2482
|
-
allowList: params.allowFromLower,
|
|
2483
|
-
id: params.senderId,
|
|
2484
|
-
name: senderName,
|
|
2485
|
-
allowNameMatching: params.ctx.allowNameMatching
|
|
2486
|
-
});
|
|
2487
|
-
const allowMatchMeta = formatAllowlistMatchMeta(allowMatch);
|
|
2488
|
-
if (allowMatch.allowed) return true;
|
|
2489
|
-
if (params.ctx.dmPolicy === "pairing") {
|
|
2490
|
-
await issuePairingChallenge({
|
|
2491
|
-
channel: "slack",
|
|
2492
|
-
senderId: params.senderId,
|
|
2493
|
-
senderIdLine: `Your Slack user id: ${params.senderId}`,
|
|
2494
|
-
meta: { name: senderName },
|
|
2495
|
-
upsertPairingRequest: async ({ id, meta }) => await upsertChannelPairingRequest({
|
|
2496
|
-
channel: "slack",
|
|
2497
|
-
id,
|
|
2498
|
-
accountId: params.accountId,
|
|
2499
|
-
meta
|
|
2500
|
-
}),
|
|
2501
|
-
sendPairingReply: params.sendPairingReply,
|
|
2502
|
-
onCreated: () => {
|
|
2503
|
-
params.log(`slack pairing request sender=${params.senderId} name=${senderName ?? "unknown"} (${allowMatchMeta})`);
|
|
2504
|
-
},
|
|
2505
|
-
onReplyError: (err) => {
|
|
2506
|
-
params.log(`slack pairing reply failed for ${params.senderId}: ${String(err)}`);
|
|
2507
|
-
}
|
|
2508
|
-
});
|
|
2509
|
-
return false;
|
|
2510
|
-
}
|
|
2511
|
-
await params.onUnauthorized({
|
|
2512
|
-
allowMatchMeta,
|
|
2513
|
-
senderName
|
|
2514
|
-
});
|
|
2515
|
-
return false;
|
|
2516
|
-
}
|
|
2517
|
-
//#endregion
|
|
2518
|
-
//#region extensions/slack/src/monitor/room-context.ts
|
|
2519
|
-
function resolveSlackRoomContextHints(params) {
|
|
2520
|
-
if (!params.isRoomish) return {};
|
|
2521
|
-
const untrustedChannelMetadata = buildUntrustedChannelMetadata({
|
|
2522
|
-
source: "slack",
|
|
2523
|
-
label: "Slack channel description",
|
|
2524
|
-
entries: [params.channelInfo?.topic, params.channelInfo?.purpose]
|
|
2525
|
-
});
|
|
2526
|
-
const systemPromptParts = [params.channelConfig?.systemPrompt?.trim() || null].filter((entry) => Boolean(entry));
|
|
2527
|
-
return {
|
|
2528
|
-
untrustedChannelMetadata,
|
|
2529
|
-
groupSystemPrompt: systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : void 0
|
|
2530
|
-
};
|
|
2531
|
-
}
|
|
2532
|
-
//#endregion
|
|
2533
|
-
//#region extensions/slack/src/monitor/message-handler/prepare-content.ts
|
|
2534
|
-
function filterInheritedParentFiles(params) {
|
|
2535
|
-
const { files, isThreadReply, threadStarter } = params;
|
|
2536
|
-
if (!isThreadReply || !files?.length) return files;
|
|
2537
|
-
if (!threadStarter?.files?.length) return files;
|
|
2538
|
-
const starterFileIds = new Set(threadStarter.files.map((file) => file.id));
|
|
2539
|
-
const filtered = files.filter((file) => !file.id || !starterFileIds.has(file.id));
|
|
2540
|
-
if (filtered.length < files.length) logVerbose(`slack: filtered ${files.length - filtered.length} inherited parent file(s) from thread reply`);
|
|
2541
|
-
return filtered.length > 0 ? filtered : void 0;
|
|
2542
|
-
}
|
|
2543
|
-
async function resolveSlackMessageContent(params) {
|
|
2544
|
-
const ownFiles = filterInheritedParentFiles({
|
|
2545
|
-
files: params.message.files,
|
|
2546
|
-
isThreadReply: params.isThreadReply,
|
|
2547
|
-
threadStarter: params.threadStarter
|
|
2548
|
-
});
|
|
2549
|
-
const media = await resolveSlackMedia({
|
|
2550
|
-
files: ownFiles,
|
|
2551
|
-
token: params.botToken,
|
|
2552
|
-
maxBytes: params.mediaMaxBytes
|
|
2553
|
-
});
|
|
2554
|
-
const attachmentContent = await resolveSlackAttachmentContent({
|
|
2555
|
-
attachments: params.message.attachments,
|
|
2556
|
-
token: params.botToken,
|
|
2557
|
-
maxBytes: params.mediaMaxBytes
|
|
2558
|
-
});
|
|
2559
|
-
const mergedMedia = [...media ?? [], ...attachmentContent?.media ?? []];
|
|
2560
|
-
const effectiveDirectMedia = mergedMedia.length > 0 ? mergedMedia : null;
|
|
2561
|
-
const mediaPlaceholder = effectiveDirectMedia ? effectiveDirectMedia.map((item) => item.placeholder).join(" ") : void 0;
|
|
2562
|
-
const fallbackFiles = ownFiles ?? [];
|
|
2563
|
-
const fileOnlyFallback = !mediaPlaceholder && fallbackFiles.length > 0 ? fallbackFiles.slice(0, 8).map((file) => file.name?.trim() || "file").join(", ") : void 0;
|
|
2564
|
-
const fileOnlyPlaceholder = fileOnlyFallback ? `[Slack file: ${fileOnlyFallback}]` : void 0;
|
|
2565
|
-
const botAttachmentText = params.isBotMessage && !attachmentContent?.text ? (params.message.attachments ?? []).map((attachment) => attachment.text?.trim() || attachment.fallback?.trim()).filter(Boolean).join("\n") : void 0;
|
|
2566
|
-
const rawBody = [
|
|
2567
|
-
(params.message.text ?? "").trim(),
|
|
2568
|
-
attachmentContent?.text,
|
|
2569
|
-
botAttachmentText,
|
|
2570
|
-
mediaPlaceholder,
|
|
2571
|
-
fileOnlyPlaceholder
|
|
2572
|
-
].filter(Boolean).join("\n") || "";
|
|
2573
|
-
if (!rawBody) return null;
|
|
2574
|
-
return {
|
|
2575
|
-
rawBody,
|
|
2576
|
-
effectiveDirectMedia
|
|
2577
|
-
};
|
|
2578
|
-
}
|
|
2579
|
-
//#endregion
|
|
2580
|
-
//#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
|
|
2581
|
-
async function resolveSlackThreadContextData(params) {
|
|
2582
|
-
let threadStarterBody;
|
|
2583
|
-
let threadHistoryBody;
|
|
2584
|
-
let threadSessionPreviousTimestamp;
|
|
2585
|
-
let threadLabel;
|
|
2586
|
-
let threadStarterMedia = null;
|
|
2587
|
-
if (!params.isThreadReply || !params.threadTs) return {
|
|
2588
|
-
threadStarterBody,
|
|
2589
|
-
threadHistoryBody,
|
|
2590
|
-
threadSessionPreviousTimestamp,
|
|
2591
|
-
threadLabel,
|
|
2592
|
-
threadStarterMedia
|
|
2593
|
-
};
|
|
2594
|
-
const starter = params.threadStarter;
|
|
2595
|
-
if (starter?.text) {
|
|
2596
|
-
threadStarterBody = starter.text;
|
|
2597
|
-
const snippet = starter.text.replace(/\s+/g, " ").slice(0, 80);
|
|
2598
|
-
threadLabel = `Slack thread ${params.roomLabel}${snippet ? `: ${snippet}` : ""}`;
|
|
2599
|
-
if (!params.effectiveDirectMedia && starter.files && starter.files.length > 0) {
|
|
2600
|
-
threadStarterMedia = await resolveSlackMedia({
|
|
2601
|
-
files: starter.files,
|
|
2602
|
-
token: params.ctx.botToken,
|
|
2603
|
-
maxBytes: params.ctx.mediaMaxBytes
|
|
2604
|
-
});
|
|
2605
|
-
if (threadStarterMedia) logVerbose(`slack: hydrated thread starter file ${threadStarterMedia.map((item) => item.placeholder).join(", ")} from root message`);
|
|
2606
|
-
}
|
|
2607
|
-
} else threadLabel = `Slack thread ${params.roomLabel}`;
|
|
2608
|
-
const threadInitialHistoryLimit = params.account.config?.thread?.initialHistoryLimit ?? 20;
|
|
2609
|
-
threadSessionPreviousTimestamp = readSessionUpdatedAt({
|
|
2610
|
-
storePath: params.storePath,
|
|
2611
|
-
sessionKey: params.sessionKey
|
|
2612
|
-
});
|
|
2613
|
-
if (threadInitialHistoryLimit > 0 && !threadSessionPreviousTimestamp) {
|
|
2614
|
-
const threadHistory = await resolveSlackThreadHistory({
|
|
2615
|
-
channelId: params.message.channel,
|
|
2616
|
-
threadTs: params.threadTs,
|
|
2617
|
-
client: params.ctx.app.client,
|
|
2618
|
-
currentMessageTs: params.message.ts,
|
|
2619
|
-
limit: threadInitialHistoryLimit
|
|
2620
|
-
});
|
|
2621
|
-
if (threadHistory.length > 0) {
|
|
2622
|
-
const uniqueUserIds = [...new Set(threadHistory.map((item) => item.userId).filter((id) => Boolean(id)))];
|
|
2623
|
-
const userMap = /* @__PURE__ */ new Map();
|
|
2624
|
-
await Promise.all(uniqueUserIds.map(async (id) => {
|
|
2625
|
-
const user = await params.ctx.resolveUserName(id);
|
|
2626
|
-
if (user) userMap.set(id, user);
|
|
2627
|
-
}));
|
|
2628
|
-
const historyParts = [];
|
|
2629
|
-
for (const historyMsg of threadHistory) {
|
|
2630
|
-
const msgSenderName = (historyMsg.userId ? userMap.get(historyMsg.userId) : null)?.name ?? (historyMsg.botId ? `Bot (${historyMsg.botId})` : "Unknown");
|
|
2631
|
-
const role = Boolean(historyMsg.botId) ? "assistant" : "user";
|
|
2632
|
-
const msgWithId = `${historyMsg.text}\n[slack message id: ${historyMsg.ts ?? "unknown"} channel: ${params.message.channel}]`;
|
|
2633
|
-
historyParts.push(formatInboundEnvelope({
|
|
2634
|
-
channel: "Slack",
|
|
2635
|
-
from: `${msgSenderName} (${role})`,
|
|
2636
|
-
timestamp: historyMsg.ts ? Math.round(Number(historyMsg.ts) * 1e3) : void 0,
|
|
2637
|
-
body: msgWithId,
|
|
2638
|
-
chatType: "channel",
|
|
2639
|
-
envelope: params.envelopeOptions
|
|
2640
|
-
}));
|
|
2641
|
-
}
|
|
2642
|
-
threadHistoryBody = historyParts.join("\n\n");
|
|
2643
|
-
logVerbose(`slack: populated thread history with ${threadHistory.length} messages for new session`);
|
|
2644
|
-
}
|
|
2645
|
-
}
|
|
2646
|
-
return {
|
|
2647
|
-
threadStarterBody,
|
|
2648
|
-
threadHistoryBody,
|
|
2649
|
-
threadSessionPreviousTimestamp,
|
|
2650
|
-
threadLabel,
|
|
2651
|
-
threadStarterMedia
|
|
2652
|
-
};
|
|
2653
|
-
}
|
|
2654
|
-
//#endregion
|
|
2655
|
-
//#region extensions/slack/src/monitor/message-handler/prepare.ts
|
|
2656
|
-
const mentionRegexCache = /* @__PURE__ */ new WeakMap();
|
|
2657
|
-
function resolveCachedMentionRegexes(ctx, agentId) {
|
|
2658
|
-
const key = agentId?.trim() || "__default__";
|
|
2659
|
-
let byAgent = mentionRegexCache.get(ctx);
|
|
2660
|
-
if (!byAgent) {
|
|
2661
|
-
byAgent = /* @__PURE__ */ new Map();
|
|
2662
|
-
mentionRegexCache.set(ctx, byAgent);
|
|
2663
|
-
}
|
|
2664
|
-
const cached = byAgent.get(key);
|
|
2665
|
-
if (cached) return cached;
|
|
2666
|
-
const built = buildMentionRegexes(ctx.cfg, agentId);
|
|
2667
|
-
byAgent.set(key, built);
|
|
2668
|
-
return built;
|
|
2669
|
-
}
|
|
2670
|
-
async function resolveSlackConversationContext(params) {
|
|
2671
|
-
const { ctx, account, message } = params;
|
|
2672
|
-
const cfg = ctx.cfg;
|
|
2673
|
-
let channelInfo = {};
|
|
2674
|
-
let resolvedChannelType = normalizeSlackChannelType(message.channel_type, message.channel);
|
|
2675
|
-
if (resolvedChannelType !== "im" && (!message.channel_type || message.channel_type !== "im")) {
|
|
2676
|
-
channelInfo = await ctx.resolveChannelName(message.channel);
|
|
2677
|
-
resolvedChannelType = normalizeSlackChannelType(message.channel_type ?? channelInfo.type, message.channel);
|
|
2678
|
-
}
|
|
2679
|
-
const channelName = channelInfo?.name;
|
|
2680
|
-
const isDirectMessage = resolvedChannelType === "im";
|
|
2681
|
-
const isGroupDm = resolvedChannelType === "mpim";
|
|
2682
|
-
const isRoom = resolvedChannelType === "channel" || resolvedChannelType === "group";
|
|
2683
|
-
const isRoomish = isRoom || isGroupDm;
|
|
2684
|
-
const channelConfig = isRoom ? resolveSlackChannelConfig({
|
|
2685
|
-
channelId: message.channel,
|
|
2686
|
-
channelName,
|
|
2687
|
-
channels: ctx.channelsConfig,
|
|
2688
|
-
channelKeys: ctx.channelsConfigKeys,
|
|
2689
|
-
defaultRequireMention: ctx.defaultRequireMention,
|
|
2690
|
-
allowNameMatching: ctx.allowNameMatching
|
|
2691
|
-
}) : null;
|
|
2692
|
-
const allowBots = channelConfig?.allowBots ?? account.config?.allowBots ?? cfg.channels?.slack?.allowBots ?? false;
|
|
2693
|
-
return {
|
|
2694
|
-
channelInfo,
|
|
2695
|
-
channelName,
|
|
2696
|
-
resolvedChannelType,
|
|
2697
|
-
isDirectMessage,
|
|
2698
|
-
isGroupDm,
|
|
2699
|
-
isRoom,
|
|
2700
|
-
isRoomish,
|
|
2701
|
-
channelConfig,
|
|
2702
|
-
allowBots,
|
|
2703
|
-
isBotMessage: Boolean(message.bot_id)
|
|
2704
|
-
};
|
|
2705
|
-
}
|
|
2706
|
-
async function authorizeSlackInboundMessage(params) {
|
|
2707
|
-
const { ctx, account, message, conversation } = params;
|
|
2708
|
-
const { isDirectMessage, channelName, resolvedChannelType, isBotMessage, allowBots } = conversation;
|
|
2709
|
-
if (isBotMessage) {
|
|
2710
|
-
if (message.user && ctx.botUserId && message.user === ctx.botUserId) return null;
|
|
2711
|
-
if (!allowBots) {
|
|
2712
|
-
logVerbose(`slack: drop bot message ${message.bot_id ?? "unknown"} (allowBots=false)`);
|
|
2713
|
-
return null;
|
|
2714
|
-
}
|
|
2715
|
-
}
|
|
2716
|
-
if (isDirectMessage && !message.user) {
|
|
2717
|
-
logVerbose("slack: drop dm message (missing user id)");
|
|
2718
|
-
return null;
|
|
2719
|
-
}
|
|
2720
|
-
const senderId = message.user ?? (isBotMessage ? message.bot_id : void 0);
|
|
2721
|
-
if (!senderId) {
|
|
2722
|
-
logVerbose("slack: drop message (missing sender id)");
|
|
2723
|
-
return null;
|
|
2724
|
-
}
|
|
2725
|
-
if (!ctx.isChannelAllowed({
|
|
2726
|
-
channelId: message.channel,
|
|
2727
|
-
channelName,
|
|
2728
|
-
channelType: resolvedChannelType
|
|
2729
|
-
})) {
|
|
2730
|
-
logVerbose("slack: drop message (channel not allowed)");
|
|
2731
|
-
return null;
|
|
2732
|
-
}
|
|
2733
|
-
const { allowFromLower } = await resolveSlackEffectiveAllowFrom(ctx, { includePairingStore: isDirectMessage });
|
|
2734
|
-
if (isDirectMessage) {
|
|
2735
|
-
const directUserId = message.user;
|
|
2736
|
-
if (!directUserId) {
|
|
2737
|
-
logVerbose("slack: drop dm message (missing user id)");
|
|
2738
|
-
return null;
|
|
2739
|
-
}
|
|
2740
|
-
if (!await authorizeSlackDirectMessage({
|
|
2741
|
-
ctx,
|
|
2742
|
-
accountId: account.accountId,
|
|
2743
|
-
senderId: directUserId,
|
|
2744
|
-
allowFromLower,
|
|
2745
|
-
resolveSenderName: ctx.resolveUserName,
|
|
2746
|
-
sendPairingReply: async (text) => {
|
|
2747
|
-
await sendMessageSlack(message.channel, text, {
|
|
2748
|
-
token: ctx.botToken,
|
|
2749
|
-
client: ctx.app.client,
|
|
2750
|
-
accountId: account.accountId
|
|
2751
|
-
});
|
|
2752
|
-
},
|
|
2753
|
-
onDisabled: () => {
|
|
2754
|
-
logVerbose("slack: drop dm (dms disabled)");
|
|
2755
|
-
},
|
|
2756
|
-
onUnauthorized: ({ allowMatchMeta }) => {
|
|
2757
|
-
logVerbose(`Blocked unauthorized slack sender ${message.user} (dmPolicy=${ctx.dmPolicy}, ${allowMatchMeta})`);
|
|
2758
|
-
},
|
|
2759
|
-
log: logVerbose
|
|
2760
|
-
})) return null;
|
|
2761
|
-
}
|
|
2762
|
-
return {
|
|
2763
|
-
senderId,
|
|
2764
|
-
allowFromLower
|
|
2765
|
-
};
|
|
2766
|
-
}
|
|
2767
|
-
function resolveSlackRoutingContext(params) {
|
|
2768
|
-
const { ctx, account, message, isDirectMessage, isGroupDm, isRoom, isRoomish } = params;
|
|
2769
|
-
const route = resolveAgentRoute({
|
|
2770
|
-
cfg: ctx.cfg,
|
|
2771
|
-
channel: "slack",
|
|
2772
|
-
accountId: account.accountId,
|
|
2773
|
-
teamId: ctx.teamId || void 0,
|
|
2774
|
-
peer: {
|
|
2775
|
-
kind: isDirectMessage ? "direct" : isRoom ? "channel" : "group",
|
|
2776
|
-
id: isDirectMessage ? message.user ?? "unknown" : message.channel
|
|
2777
|
-
}
|
|
2778
|
-
});
|
|
2779
|
-
const chatType = isDirectMessage ? "direct" : isGroupDm ? "group" : "channel";
|
|
2780
|
-
const replyToMode = resolveSlackReplyToMode(account, chatType);
|
|
2781
|
-
const threadContext = resolveSlackThreadContext({
|
|
2782
|
-
message,
|
|
2783
|
-
replyToMode
|
|
2784
|
-
});
|
|
2785
|
-
const threadTs = threadContext.incomingThreadTs;
|
|
2786
|
-
const isThreadReply = threadContext.isThreadReply;
|
|
2787
|
-
const autoThreadId = !isThreadReply && replyToMode === "all" && threadContext.messageTs ? threadContext.messageTs : void 0;
|
|
2788
|
-
const canonicalThreadId = isRoomish ? isThreadReply && threadTs ? threadTs : void 0 : isThreadReply ? threadTs : autoThreadId;
|
|
2789
|
-
const threadKeys = resolveThreadSessionKeys({
|
|
2790
|
-
baseSessionKey: route.sessionKey,
|
|
2791
|
-
threadId: canonicalThreadId,
|
|
2792
|
-
parentSessionKey: canonicalThreadId && ctx.threadInheritParent ? route.sessionKey : void 0
|
|
2793
|
-
});
|
|
2794
|
-
const sessionKey = threadKeys.sessionKey;
|
|
2795
|
-
return {
|
|
2796
|
-
route,
|
|
2797
|
-
chatType,
|
|
2798
|
-
replyToMode,
|
|
2799
|
-
threadContext,
|
|
2800
|
-
threadTs,
|
|
2801
|
-
isThreadReply,
|
|
2802
|
-
threadKeys,
|
|
2803
|
-
sessionKey,
|
|
2804
|
-
historyKey: isThreadReply && ctx.threadHistoryScope === "thread" ? sessionKey : message.channel
|
|
2805
|
-
};
|
|
2806
|
-
}
|
|
2807
|
-
async function prepareSlackMessage(params) {
|
|
2808
|
-
const { ctx, account, message, opts } = params;
|
|
2809
|
-
const cfg = ctx.cfg;
|
|
2810
|
-
const conversation = await resolveSlackConversationContext({
|
|
2811
|
-
ctx,
|
|
2812
|
-
account,
|
|
2813
|
-
message
|
|
2814
|
-
});
|
|
2815
|
-
const { channelInfo, channelName, isDirectMessage, isGroupDm, isRoom, isRoomish, channelConfig, isBotMessage } = conversation;
|
|
2816
|
-
const authorization = await authorizeSlackInboundMessage({
|
|
2817
|
-
ctx,
|
|
2818
|
-
account,
|
|
2819
|
-
message,
|
|
2820
|
-
conversation
|
|
2821
|
-
});
|
|
2822
|
-
if (!authorization) return null;
|
|
2823
|
-
const { senderId, allowFromLower } = authorization;
|
|
2824
|
-
const { route, replyToMode, threadContext, threadTs, isThreadReply, threadKeys, sessionKey, historyKey } = resolveSlackRoutingContext({
|
|
2825
|
-
ctx,
|
|
2826
|
-
account,
|
|
2827
|
-
message,
|
|
2828
|
-
isDirectMessage,
|
|
2829
|
-
isGroupDm,
|
|
2830
|
-
isRoom,
|
|
2831
|
-
isRoomish
|
|
2832
|
-
});
|
|
2833
|
-
const mentionRegexes = resolveCachedMentionRegexes(ctx, route.agentId);
|
|
2834
|
-
const hasAnyMention = /<@[^>]+>/.test(message.text ?? "");
|
|
2835
|
-
const explicitlyMentioned = Boolean(ctx.botUserId && message.text?.includes(`<@${ctx.botUserId}>`));
|
|
2836
|
-
const wasMentioned = opts.wasMentioned ?? (!isDirectMessage && matchesMentionWithExplicit({
|
|
2837
|
-
text: message.text ?? "",
|
|
2838
|
-
mentionRegexes,
|
|
2839
|
-
explicit: {
|
|
2840
|
-
hasAnyMention,
|
|
2841
|
-
isExplicitlyMentioned: explicitlyMentioned,
|
|
2842
|
-
canResolveExplicit: Boolean(ctx.botUserId)
|
|
2843
|
-
}
|
|
2844
|
-
}));
|
|
2845
|
-
const implicitMention = Boolean(!isDirectMessage && ctx.botUserId && message.thread_ts && (message.parent_user_id === ctx.botUserId || hasSlackThreadParticipation(account.accountId, message.channel, message.thread_ts)));
|
|
2846
|
-
let resolvedSenderName = message.username?.trim() || void 0;
|
|
2847
|
-
const resolveSenderName = async () => {
|
|
2848
|
-
if (resolvedSenderName) return resolvedSenderName;
|
|
2849
|
-
if (message.user) {
|
|
2850
|
-
const normalized = (await ctx.resolveUserName(message.user))?.name?.trim();
|
|
2851
|
-
if (normalized) {
|
|
2852
|
-
resolvedSenderName = normalized;
|
|
2853
|
-
return resolvedSenderName;
|
|
2854
|
-
}
|
|
2855
|
-
}
|
|
2856
|
-
resolvedSenderName = message.user ?? message.bot_id ?? "unknown";
|
|
2857
|
-
return resolvedSenderName;
|
|
2858
|
-
};
|
|
2859
|
-
const senderNameForAuth = ctx.allowNameMatching ? await resolveSenderName() : void 0;
|
|
2860
|
-
const channelUserAuthorized = isRoom ? resolveSlackUserAllowed({
|
|
2861
|
-
allowList: channelConfig?.users,
|
|
2862
|
-
userId: senderId,
|
|
2863
|
-
userName: senderNameForAuth,
|
|
2864
|
-
allowNameMatching: ctx.allowNameMatching
|
|
2865
|
-
}) : true;
|
|
2866
|
-
if (isRoom && !channelUserAuthorized) {
|
|
2867
|
-
logVerbose(`Blocked unauthorized slack sender ${senderId} (not in channel users)`);
|
|
2868
|
-
return null;
|
|
2869
|
-
}
|
|
2870
|
-
const allowTextCommands = shouldHandleTextCommands({
|
|
2871
|
-
cfg,
|
|
2872
|
-
surface: "slack"
|
|
2873
|
-
});
|
|
2874
|
-
const textForCommandDetection = stripSlackMentionsForCommandDetection(message.text ?? "");
|
|
2875
|
-
const hasControlCommandInMessage = hasControlCommand(textForCommandDetection, cfg);
|
|
2876
|
-
const ownerAuthorized = resolveSlackAllowListMatch({
|
|
2877
|
-
allowList: allowFromLower,
|
|
2878
|
-
id: senderId,
|
|
2879
|
-
name: senderNameForAuth,
|
|
2880
|
-
allowNameMatching: ctx.allowNameMatching
|
|
2881
|
-
}).allowed;
|
|
2882
|
-
const channelUsersAllowlistConfigured = isRoom && Array.isArray(channelConfig?.users) && channelConfig.users.length > 0;
|
|
2883
|
-
const channelCommandAuthorized = isRoom && channelUsersAllowlistConfigured ? resolveSlackUserAllowed({
|
|
2884
|
-
allowList: channelConfig?.users,
|
|
2885
|
-
userId: senderId,
|
|
2886
|
-
userName: senderNameForAuth,
|
|
2887
|
-
allowNameMatching: ctx.allowNameMatching
|
|
2888
|
-
}) : false;
|
|
2889
|
-
const commandGate = resolveControlCommandGate({
|
|
2890
|
-
useAccessGroups: ctx.useAccessGroups,
|
|
2891
|
-
authorizers: [{
|
|
2892
|
-
configured: allowFromLower.length > 0,
|
|
2893
|
-
allowed: ownerAuthorized
|
|
2894
|
-
}, {
|
|
2895
|
-
configured: channelUsersAllowlistConfigured,
|
|
2896
|
-
allowed: channelCommandAuthorized
|
|
2897
|
-
}],
|
|
2898
|
-
allowTextCommands,
|
|
2899
|
-
hasControlCommand: hasControlCommandInMessage
|
|
2900
|
-
});
|
|
2901
|
-
const commandAuthorized = commandGate.commandAuthorized;
|
|
2902
|
-
if (isRoomish && commandGate.shouldBlock) {
|
|
2903
|
-
logInboundDrop({
|
|
2904
|
-
log: logVerbose,
|
|
2905
|
-
channel: "slack",
|
|
2906
|
-
reason: "control command (unauthorized)",
|
|
2907
|
-
target: senderId
|
|
2908
|
-
});
|
|
2909
|
-
return null;
|
|
2910
|
-
}
|
|
2911
|
-
const shouldRequireMention = isRoom ? channelConfig?.requireMention ?? ctx.defaultRequireMention : false;
|
|
2912
|
-
const canDetectMention = Boolean(ctx.botUserId) || mentionRegexes.length > 0;
|
|
2913
|
-
const mentionGate = resolveMentionGatingWithBypass({
|
|
2914
|
-
isGroup: isRoom,
|
|
2915
|
-
requireMention: Boolean(shouldRequireMention),
|
|
2916
|
-
canDetectMention,
|
|
2917
|
-
wasMentioned,
|
|
2918
|
-
implicitMention,
|
|
2919
|
-
hasAnyMention,
|
|
2920
|
-
allowTextCommands,
|
|
2921
|
-
hasControlCommand: hasControlCommandInMessage,
|
|
2922
|
-
commandAuthorized
|
|
2923
|
-
});
|
|
2924
|
-
const effectiveWasMentioned = mentionGate.effectiveWasMentioned;
|
|
2925
|
-
if (isRoom && shouldRequireMention && mentionGate.shouldSkip) {
|
|
2926
|
-
ctx.logger.info({
|
|
2927
|
-
channel: message.channel,
|
|
2928
|
-
reason: "no-mention"
|
|
2929
|
-
}, "skipping channel message");
|
|
2930
|
-
const pendingText = (message.text ?? "").trim();
|
|
2931
|
-
const fallbackFile = message.files?.[0]?.name ? `[Slack file: ${message.files[0].name}]` : message.files?.length ? "[Slack file]" : "";
|
|
2932
|
-
const pendingBody = pendingText || fallbackFile;
|
|
2933
|
-
recordPendingHistoryEntryIfEnabled({
|
|
2934
|
-
historyMap: ctx.channelHistories,
|
|
2935
|
-
historyKey,
|
|
2936
|
-
limit: ctx.historyLimit,
|
|
2937
|
-
entry: pendingBody ? {
|
|
2938
|
-
sender: await resolveSenderName(),
|
|
2939
|
-
body: pendingBody,
|
|
2940
|
-
timestamp: message.ts ? Math.round(Number(message.ts) * 1e3) : void 0,
|
|
2941
|
-
messageId: message.ts
|
|
2942
|
-
} : null
|
|
2943
|
-
});
|
|
2944
|
-
return null;
|
|
2945
|
-
}
|
|
2946
|
-
const threadStarter = isThreadReply && threadTs ? await resolveSlackThreadStarter({
|
|
2947
|
-
channelId: message.channel,
|
|
2948
|
-
threadTs,
|
|
2949
|
-
client: ctx.app.client
|
|
2950
|
-
}) : null;
|
|
2951
|
-
const resolvedMessageContent = await resolveSlackMessageContent({
|
|
2952
|
-
message,
|
|
2953
|
-
isThreadReply,
|
|
2954
|
-
threadStarter,
|
|
2955
|
-
isBotMessage,
|
|
2956
|
-
botToken: ctx.botToken,
|
|
2957
|
-
mediaMaxBytes: ctx.mediaMaxBytes
|
|
2958
|
-
});
|
|
2959
|
-
if (!resolvedMessageContent) return null;
|
|
2960
|
-
const { rawBody, effectiveDirectMedia } = resolvedMessageContent;
|
|
2961
|
-
const ackReaction = resolveAckReaction(cfg, route.agentId, {
|
|
2962
|
-
channel: "slack",
|
|
2963
|
-
accountId: account.accountId
|
|
2964
|
-
});
|
|
2965
|
-
const ackReactionValue = ackReaction ?? "";
|
|
2966
|
-
const shouldAckReaction$1 = () => Boolean(ackReaction && shouldAckReaction({
|
|
2967
|
-
scope: ctx.ackReactionScope,
|
|
2968
|
-
isDirect: isDirectMessage,
|
|
2969
|
-
isGroup: isRoomish,
|
|
2970
|
-
isMentionableGroup: isRoom,
|
|
2971
|
-
requireMention: Boolean(shouldRequireMention),
|
|
2972
|
-
canDetectMention,
|
|
2973
|
-
effectiveWasMentioned,
|
|
2974
|
-
shouldBypassMention: mentionGate.shouldBypassMention
|
|
2975
|
-
}));
|
|
2976
|
-
const ackReactionMessageTs = message.ts;
|
|
2977
|
-
const ackReactionPromise = shouldAckReaction$1() && ackReactionMessageTs && ackReactionValue ? reactSlackMessage(message.channel, ackReactionMessageTs, ackReactionValue, {
|
|
2978
|
-
token: ctx.botToken,
|
|
2979
|
-
client: ctx.app.client
|
|
2980
|
-
}).then(() => true, (err) => {
|
|
2981
|
-
logVerbose(`slack react failed for channel ${message.channel}: ${String(err)}`);
|
|
2982
|
-
return false;
|
|
2983
|
-
}) : null;
|
|
2984
|
-
const roomLabel = channelName ? `#${channelName}` : `#${message.channel}`;
|
|
2985
|
-
const senderName = await resolveSenderName();
|
|
2986
|
-
const preview = rawBody.replace(/\s+/g, " ").slice(0, 160);
|
|
2987
|
-
const inboundLabel = isDirectMessage ? `Slack DM from ${senderName}` : `Slack message in ${roomLabel} from ${senderName}`;
|
|
2988
|
-
const slackFrom = isDirectMessage ? `slack:${message.user}` : isRoom ? `slack:channel:${message.channel}` : `slack:group:${message.channel}`;
|
|
2989
|
-
enqueueSystemEvent(`${inboundLabel}: ${preview}`, {
|
|
2990
|
-
sessionKey,
|
|
2991
|
-
contextKey: `slack:message:${message.channel}:${message.ts ?? "unknown"}`
|
|
2992
|
-
});
|
|
2993
|
-
const envelopeFrom = resolveConversationLabel({
|
|
2994
|
-
ChatType: isDirectMessage ? "direct" : "channel",
|
|
2995
|
-
SenderName: senderName,
|
|
2996
|
-
GroupSubject: isRoomish ? roomLabel : void 0,
|
|
2997
|
-
From: slackFrom
|
|
2998
|
-
}) ?? (isDirectMessage ? senderName : roomLabel);
|
|
2999
|
-
const threadInfo = isThreadReply && threadTs ? ` thread_ts: ${threadTs}${message.parent_user_id ? ` parent_user_id: ${message.parent_user_id}` : ""}` : "";
|
|
3000
|
-
const textWithId = `${rawBody}\n[slack message id: ${message.ts} channel: ${message.channel}${threadInfo}]`;
|
|
3001
|
-
const storePath = resolveStorePath(ctx.cfg.session?.store, { agentId: route.agentId });
|
|
3002
|
-
const envelopeOptions = resolveEnvelopeFormatOptions(ctx.cfg);
|
|
3003
|
-
const previousTimestamp = readSessionUpdatedAt({
|
|
3004
|
-
storePath,
|
|
3005
|
-
sessionKey
|
|
3006
|
-
});
|
|
3007
|
-
let combinedBody = formatInboundEnvelope({
|
|
3008
|
-
channel: "Slack",
|
|
3009
|
-
from: envelopeFrom,
|
|
3010
|
-
timestamp: message.ts ? Math.round(Number(message.ts) * 1e3) : void 0,
|
|
3011
|
-
body: textWithId,
|
|
3012
|
-
chatType: isDirectMessage ? "direct" : "channel",
|
|
3013
|
-
sender: {
|
|
3014
|
-
name: senderName,
|
|
3015
|
-
id: senderId
|
|
3016
|
-
},
|
|
3017
|
-
previousTimestamp,
|
|
3018
|
-
envelope: envelopeOptions
|
|
3019
|
-
});
|
|
3020
|
-
if (isRoomish && ctx.historyLimit > 0) combinedBody = buildPendingHistoryContextFromMap({
|
|
3021
|
-
historyMap: ctx.channelHistories,
|
|
3022
|
-
historyKey,
|
|
3023
|
-
limit: ctx.historyLimit,
|
|
3024
|
-
currentMessage: combinedBody,
|
|
3025
|
-
formatEntry: (entry) => formatInboundEnvelope({
|
|
3026
|
-
channel: "Slack",
|
|
3027
|
-
from: roomLabel,
|
|
3028
|
-
timestamp: entry.timestamp,
|
|
3029
|
-
body: `${entry.body}${entry.messageId ? ` [id:${entry.messageId} channel:${message.channel}]` : ""}`,
|
|
3030
|
-
chatType: "channel",
|
|
3031
|
-
senderLabel: entry.sender,
|
|
3032
|
-
envelope: envelopeOptions
|
|
3033
|
-
})
|
|
3034
|
-
});
|
|
3035
|
-
const slackTo = isDirectMessage ? `user:${message.user}` : `channel:${message.channel}`;
|
|
3036
|
-
const { untrustedChannelMetadata, groupSystemPrompt } = resolveSlackRoomContextHints({
|
|
3037
|
-
isRoomish,
|
|
3038
|
-
channelInfo,
|
|
3039
|
-
channelConfig
|
|
3040
|
-
});
|
|
3041
|
-
const { threadStarterBody, threadHistoryBody, threadSessionPreviousTimestamp, threadLabel, threadStarterMedia } = await resolveSlackThreadContextData({
|
|
3042
|
-
ctx,
|
|
3043
|
-
account,
|
|
3044
|
-
message,
|
|
3045
|
-
isThreadReply,
|
|
3046
|
-
threadTs,
|
|
3047
|
-
threadStarter,
|
|
3048
|
-
roomLabel,
|
|
3049
|
-
storePath,
|
|
3050
|
-
sessionKey,
|
|
3051
|
-
envelopeOptions,
|
|
3052
|
-
effectiveDirectMedia
|
|
3053
|
-
});
|
|
3054
|
-
const effectiveMedia = effectiveDirectMedia ?? threadStarterMedia;
|
|
3055
|
-
const firstMedia = effectiveMedia?.[0];
|
|
3056
|
-
const inboundHistory = isRoomish && ctx.historyLimit > 0 ? (ctx.channelHistories.get(historyKey) ?? []).map((entry) => ({
|
|
3057
|
-
sender: entry.sender,
|
|
3058
|
-
body: entry.body,
|
|
3059
|
-
timestamp: entry.timestamp
|
|
3060
|
-
})) : void 0;
|
|
3061
|
-
const commandBody = textForCommandDetection.trim();
|
|
3062
|
-
const ctxPayload = finalizeInboundContext({
|
|
3063
|
-
Body: combinedBody,
|
|
3064
|
-
BodyForAgent: rawBody,
|
|
3065
|
-
InboundHistory: inboundHistory,
|
|
3066
|
-
RawBody: rawBody,
|
|
3067
|
-
CommandBody: commandBody,
|
|
3068
|
-
BodyForCommands: commandBody,
|
|
3069
|
-
From: slackFrom,
|
|
3070
|
-
To: slackTo,
|
|
3071
|
-
SessionKey: sessionKey,
|
|
3072
|
-
AccountId: route.accountId,
|
|
3073
|
-
ChatType: isDirectMessage ? "direct" : "channel",
|
|
3074
|
-
ConversationLabel: envelopeFrom,
|
|
3075
|
-
GroupSubject: isRoomish ? roomLabel : void 0,
|
|
3076
|
-
GroupSystemPrompt: isRoomish ? groupSystemPrompt : void 0,
|
|
3077
|
-
UntrustedContext: untrustedChannelMetadata ? [untrustedChannelMetadata] : void 0,
|
|
3078
|
-
SenderName: senderName,
|
|
3079
|
-
SenderId: senderId,
|
|
3080
|
-
Provider: "slack",
|
|
3081
|
-
Surface: "slack",
|
|
3082
|
-
MessageSid: message.ts,
|
|
3083
|
-
ReplyToId: threadContext.replyToId,
|
|
3084
|
-
MessageThreadId: threadContext.messageThreadId,
|
|
3085
|
-
ParentSessionKey: threadKeys.parentSessionKey,
|
|
3086
|
-
ThreadStarterBody: !threadSessionPreviousTimestamp ? threadStarterBody : void 0,
|
|
3087
|
-
ThreadHistoryBody: threadHistoryBody,
|
|
3088
|
-
IsFirstThreadTurn: isThreadReply && threadTs && !threadSessionPreviousTimestamp ? true : void 0,
|
|
3089
|
-
ThreadLabel: threadLabel,
|
|
3090
|
-
Timestamp: message.ts ? Math.round(Number(message.ts) * 1e3) : void 0,
|
|
3091
|
-
WasMentioned: isRoomish ? effectiveWasMentioned : void 0,
|
|
3092
|
-
MediaPath: firstMedia?.path,
|
|
3093
|
-
MediaType: firstMedia?.contentType,
|
|
3094
|
-
MediaUrl: firstMedia?.path,
|
|
3095
|
-
MediaPaths: effectiveMedia && effectiveMedia.length > 0 ? effectiveMedia.map((m) => m.path) : void 0,
|
|
3096
|
-
MediaUrls: effectiveMedia && effectiveMedia.length > 0 ? effectiveMedia.map((m) => m.path) : void 0,
|
|
3097
|
-
MediaTypes: effectiveMedia && effectiveMedia.length > 0 ? effectiveMedia.map((m) => m.contentType ?? "") : void 0,
|
|
3098
|
-
CommandAuthorized: commandAuthorized,
|
|
3099
|
-
OriginatingChannel: "slack",
|
|
3100
|
-
OriginatingTo: slackTo,
|
|
3101
|
-
NativeChannelId: message.channel
|
|
3102
|
-
});
|
|
3103
|
-
const pinnedMainDmOwner = isDirectMessage ? resolvePinnedMainDmOwnerFromAllowlist({
|
|
3104
|
-
dmScope: cfg.session?.dmScope,
|
|
3105
|
-
allowFrom: ctx.allowFrom,
|
|
3106
|
-
normalizeEntry: normalizeSlackAllowOwnerEntry
|
|
3107
|
-
}) : null;
|
|
3108
|
-
await recordInboundSession({
|
|
3109
|
-
storePath,
|
|
3110
|
-
sessionKey,
|
|
3111
|
-
ctx: ctxPayload,
|
|
3112
|
-
updateLastRoute: isDirectMessage ? {
|
|
3113
|
-
sessionKey: route.mainSessionKey,
|
|
3114
|
-
channel: "slack",
|
|
3115
|
-
to: `user:${message.user}`,
|
|
3116
|
-
accountId: route.accountId,
|
|
3117
|
-
threadId: threadContext.messageThreadId,
|
|
3118
|
-
mainDmOwnerPin: pinnedMainDmOwner && message.user ? {
|
|
3119
|
-
ownerRecipient: pinnedMainDmOwner,
|
|
3120
|
-
senderRecipient: message.user.toLowerCase(),
|
|
3121
|
-
onSkip: ({ ownerRecipient, senderRecipient }) => {
|
|
3122
|
-
logVerbose(`slack: skip main-session last route for ${senderRecipient} (pinned owner ${ownerRecipient})`);
|
|
3123
|
-
}
|
|
3124
|
-
} : void 0
|
|
3125
|
-
} : void 0,
|
|
3126
|
-
onRecordError: (err) => {
|
|
3127
|
-
ctx.logger.warn({
|
|
3128
|
-
error: String(err),
|
|
3129
|
-
storePath,
|
|
3130
|
-
sessionKey
|
|
3131
|
-
}, "failed updating session meta");
|
|
3132
|
-
}
|
|
3133
|
-
});
|
|
3134
|
-
const replyTarget = ctxPayload.To ?? void 0;
|
|
3135
|
-
if (!replyTarget) return null;
|
|
3136
|
-
if (shouldLogVerbose()) logVerbose(`slack inbound: channel=${message.channel} from=${slackFrom} preview="${preview}"`);
|
|
3137
|
-
return {
|
|
3138
|
-
ctx,
|
|
3139
|
-
account,
|
|
3140
|
-
message,
|
|
3141
|
-
route,
|
|
3142
|
-
channelConfig,
|
|
3143
|
-
replyTarget,
|
|
3144
|
-
ctxPayload,
|
|
3145
|
-
replyToMode,
|
|
3146
|
-
isDirectMessage,
|
|
3147
|
-
isRoomish,
|
|
3148
|
-
historyKey,
|
|
3149
|
-
preview,
|
|
3150
|
-
ackReactionMessageTs,
|
|
3151
|
-
ackReactionValue,
|
|
3152
|
-
ackReactionPromise
|
|
3153
|
-
};
|
|
3154
|
-
}
|
|
3155
|
-
//#endregion
|
|
3156
|
-
//#region extensions/slack/src/monitor/thread-resolution.ts
|
|
3157
|
-
const DEFAULT_THREAD_TS_CACHE_TTL_MS = 6e4;
|
|
3158
|
-
const DEFAULT_THREAD_TS_CACHE_MAX = 500;
|
|
3159
|
-
const normalizeThreadTs = (threadTs) => {
|
|
3160
|
-
const trimmed = threadTs?.trim();
|
|
3161
|
-
return trimmed ? trimmed : void 0;
|
|
3162
|
-
};
|
|
3163
|
-
async function resolveThreadTsFromHistory(params) {
|
|
3164
|
-
try {
|
|
3165
|
-
const response = await params.client.conversations.history({
|
|
3166
|
-
channel: params.channelId,
|
|
3167
|
-
latest: params.messageTs,
|
|
3168
|
-
oldest: params.messageTs,
|
|
3169
|
-
inclusive: true,
|
|
3170
|
-
limit: 1
|
|
3171
|
-
});
|
|
3172
|
-
return normalizeThreadTs((response.messages?.find((entry) => entry.ts === params.messageTs) ?? response.messages?.[0])?.thread_ts);
|
|
3173
|
-
} catch (err) {
|
|
3174
|
-
if (shouldLogVerbose()) logVerbose(`slack inbound: failed to resolve thread_ts via conversations.history for channel=${params.channelId} ts=${params.messageTs}: ${String(err)}`);
|
|
3175
|
-
return;
|
|
3176
|
-
}
|
|
3177
|
-
}
|
|
3178
|
-
function createSlackThreadTsResolver(params) {
|
|
3179
|
-
const ttlMs = Math.max(0, params.cacheTtlMs ?? DEFAULT_THREAD_TS_CACHE_TTL_MS);
|
|
3180
|
-
const maxSize = Math.max(0, params.maxSize ?? DEFAULT_THREAD_TS_CACHE_MAX);
|
|
3181
|
-
const cache = /* @__PURE__ */ new Map();
|
|
3182
|
-
const inflight = /* @__PURE__ */ new Map();
|
|
3183
|
-
const getCached = (key, now) => {
|
|
3184
|
-
const entry = cache.get(key);
|
|
3185
|
-
if (!entry) return;
|
|
3186
|
-
if (ttlMs > 0 && now - entry.updatedAt > ttlMs) {
|
|
3187
|
-
cache.delete(key);
|
|
3188
|
-
return;
|
|
3189
|
-
}
|
|
3190
|
-
cache.delete(key);
|
|
3191
|
-
cache.set(key, {
|
|
3192
|
-
...entry,
|
|
3193
|
-
updatedAt: now
|
|
3194
|
-
});
|
|
3195
|
-
return entry.threadTs;
|
|
3196
|
-
};
|
|
3197
|
-
const setCached = (key, threadTs, now) => {
|
|
3198
|
-
cache.delete(key);
|
|
3199
|
-
cache.set(key, {
|
|
3200
|
-
threadTs,
|
|
3201
|
-
updatedAt: now
|
|
3202
|
-
});
|
|
3203
|
-
pruneMapToMaxSize(cache, maxSize);
|
|
3204
|
-
};
|
|
3205
|
-
return { resolve: async (request) => {
|
|
3206
|
-
const { message } = request;
|
|
3207
|
-
if (!message.parent_user_id || message.thread_ts || !message.ts) return message;
|
|
3208
|
-
const cacheKey = `${message.channel}:${message.ts}`;
|
|
3209
|
-
const cached = getCached(cacheKey, Date.now());
|
|
3210
|
-
if (cached !== void 0) return cached ? {
|
|
3211
|
-
...message,
|
|
3212
|
-
thread_ts: cached
|
|
3213
|
-
} : message;
|
|
3214
|
-
if (shouldLogVerbose()) logVerbose(`slack inbound: missing thread_ts for thread reply channel=${message.channel} ts=${message.ts} source=${request.source}`);
|
|
3215
|
-
let pending = inflight.get(cacheKey);
|
|
3216
|
-
if (!pending) {
|
|
3217
|
-
pending = resolveThreadTsFromHistory({
|
|
3218
|
-
client: params.client,
|
|
3219
|
-
channelId: message.channel,
|
|
3220
|
-
messageTs: message.ts
|
|
3221
|
-
});
|
|
3222
|
-
inflight.set(cacheKey, pending);
|
|
3223
|
-
}
|
|
3224
|
-
let resolved;
|
|
3225
|
-
try {
|
|
3226
|
-
resolved = await pending;
|
|
3227
|
-
} finally {
|
|
3228
|
-
inflight.delete(cacheKey);
|
|
3229
|
-
}
|
|
3230
|
-
setCached(cacheKey, resolved ?? null, Date.now());
|
|
3231
|
-
if (resolved) {
|
|
3232
|
-
if (shouldLogVerbose()) logVerbose(`slack inbound: resolved missing thread_ts channel=${message.channel} ts=${message.ts} -> thread_ts=${resolved}`);
|
|
3233
|
-
return {
|
|
3234
|
-
...message,
|
|
3235
|
-
thread_ts: resolved
|
|
3236
|
-
};
|
|
3237
|
-
}
|
|
3238
|
-
if (shouldLogVerbose()) logVerbose(`slack inbound: could not resolve missing thread_ts channel=${message.channel} ts=${message.ts}`);
|
|
3239
|
-
return message;
|
|
3240
|
-
} };
|
|
3241
|
-
}
|
|
3242
|
-
//#endregion
|
|
3243
|
-
//#region extensions/slack/src/monitor/message-handler.ts
|
|
3244
|
-
const APP_MENTION_RETRY_TTL_MS = 6e4;
|
|
3245
|
-
function resolveSlackSenderId(message) {
|
|
3246
|
-
return message.user ?? message.bot_id ?? null;
|
|
3247
|
-
}
|
|
3248
|
-
function isSlackDirectMessageChannel(channelId) {
|
|
3249
|
-
return channelId.startsWith("D");
|
|
3250
|
-
}
|
|
3251
|
-
function isTopLevelSlackMessage(message) {
|
|
3252
|
-
return !message.thread_ts && !message.parent_user_id;
|
|
3253
|
-
}
|
|
3254
|
-
function buildTopLevelSlackConversationKey(message, accountId) {
|
|
3255
|
-
if (!isTopLevelSlackMessage(message)) return null;
|
|
3256
|
-
const senderId = resolveSlackSenderId(message);
|
|
3257
|
-
if (!senderId) return null;
|
|
3258
|
-
return `slack:${accountId}:${message.channel}:${senderId}`;
|
|
3259
|
-
}
|
|
3260
|
-
function shouldDebounceSlackMessage(message, cfg) {
|
|
3261
|
-
return shouldDebounceTextInbound({
|
|
3262
|
-
text: stripSlackMentionsForCommandDetection(message.text ?? ""),
|
|
3263
|
-
cfg,
|
|
3264
|
-
hasMedia: Boolean(message.files && message.files.length > 0)
|
|
3265
|
-
});
|
|
3266
|
-
}
|
|
3267
|
-
function buildSeenMessageKey(channelId, ts) {
|
|
3268
|
-
if (!channelId || !ts) return null;
|
|
3269
|
-
return `${channelId}:${ts}`;
|
|
3270
|
-
}
|
|
3271
|
-
/**
|
|
3272
|
-
* Build a debounce key that isolates messages by thread (or by message timestamp
|
|
3273
|
-
* for top-level non-DM channel messages). Without per-message scoping, concurrent
|
|
3274
|
-
* top-level messages from the same sender can share a key and get merged
|
|
3275
|
-
* into a single reply on the wrong thread.
|
|
3276
|
-
*
|
|
3277
|
-
* DMs intentionally stay channel-scoped to preserve short-message batching.
|
|
3278
|
-
*/
|
|
3279
|
-
function buildSlackDebounceKey(message, accountId) {
|
|
3280
|
-
const senderId = resolveSlackSenderId(message);
|
|
3281
|
-
if (!senderId) return null;
|
|
3282
|
-
const messageTs = message.ts ?? message.event_ts;
|
|
3283
|
-
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}`;
|
|
3284
|
-
}
|
|
3285
|
-
function createSlackMessageHandler(params) {
|
|
3286
|
-
const { ctx, account, trackEvent } = params;
|
|
3287
|
-
const { debounceMs, debouncer } = createChannelInboundDebouncer({
|
|
3288
|
-
cfg: ctx.cfg,
|
|
3289
|
-
channel: "slack",
|
|
3290
|
-
buildKey: (entry) => buildSlackDebounceKey(entry.message, ctx.accountId),
|
|
3291
|
-
shouldDebounce: (entry) => shouldDebounceSlackMessage(entry.message, ctx.cfg),
|
|
3292
|
-
onFlush: async (entries) => {
|
|
3293
|
-
const last = entries.at(-1);
|
|
3294
|
-
if (!last) return;
|
|
3295
|
-
const flushedKey = buildSlackDebounceKey(last.message, ctx.accountId);
|
|
3296
|
-
const topLevelConversationKey = buildTopLevelSlackConversationKey(last.message, ctx.accountId);
|
|
3297
|
-
if (flushedKey && topLevelConversationKey) {
|
|
3298
|
-
const pendingKeys = pendingTopLevelDebounceKeys.get(topLevelConversationKey);
|
|
3299
|
-
if (pendingKeys) {
|
|
3300
|
-
pendingKeys.delete(flushedKey);
|
|
3301
|
-
if (pendingKeys.size === 0) pendingTopLevelDebounceKeys.delete(topLevelConversationKey);
|
|
3302
|
-
}
|
|
3303
|
-
}
|
|
3304
|
-
const combinedText = entries.length === 1 ? last.message.text ?? "" : entries.map((entry) => entry.message.text ?? "").filter(Boolean).join("\n");
|
|
3305
|
-
const combinedMentioned = entries.some((entry) => Boolean(entry.opts.wasMentioned));
|
|
3306
|
-
const prepared = await prepareSlackMessage({
|
|
3307
|
-
ctx,
|
|
3308
|
-
account,
|
|
3309
|
-
message: {
|
|
3310
|
-
...last.message,
|
|
3311
|
-
text: combinedText
|
|
3312
|
-
},
|
|
3313
|
-
opts: {
|
|
3314
|
-
...last.opts,
|
|
3315
|
-
wasMentioned: combinedMentioned || last.opts.wasMentioned
|
|
3316
|
-
}
|
|
3317
|
-
});
|
|
3318
|
-
const seenMessageKey = buildSeenMessageKey(last.message.channel, last.message.ts);
|
|
3319
|
-
if (!prepared) return;
|
|
3320
|
-
if (seenMessageKey) {
|
|
3321
|
-
pruneAppMentionRetryKeys(Date.now());
|
|
3322
|
-
if (last.opts.source === "app_mention") appMentionDispatchedKeys.set(seenMessageKey, Date.now() + APP_MENTION_RETRY_TTL_MS);
|
|
3323
|
-
else if (last.opts.source === "message" && appMentionDispatchedKeys.has(seenMessageKey)) {
|
|
3324
|
-
appMentionDispatchedKeys.delete(seenMessageKey);
|
|
3325
|
-
appMentionRetryKeys.delete(seenMessageKey);
|
|
3326
|
-
return;
|
|
3327
|
-
}
|
|
3328
|
-
appMentionRetryKeys.delete(seenMessageKey);
|
|
3329
|
-
}
|
|
3330
|
-
if (entries.length > 1) {
|
|
3331
|
-
const ids = entries.map((entry) => entry.message.ts).filter(Boolean);
|
|
3332
|
-
if (ids.length > 0) {
|
|
3333
|
-
prepared.ctxPayload.MessageSids = ids;
|
|
3334
|
-
prepared.ctxPayload.MessageSidFirst = ids[0];
|
|
3335
|
-
prepared.ctxPayload.MessageSidLast = ids[ids.length - 1];
|
|
3336
|
-
}
|
|
3337
|
-
}
|
|
3338
|
-
await dispatchPreparedSlackMessage(prepared);
|
|
3339
|
-
},
|
|
3340
|
-
onError: (err) => {
|
|
3341
|
-
ctx.runtime.error?.(`slack inbound debounce flush failed: ${String(err)}`);
|
|
3342
|
-
}
|
|
3343
|
-
});
|
|
3344
|
-
const threadTsResolver = createSlackThreadTsResolver({ client: ctx.app.client });
|
|
3345
|
-
const pendingTopLevelDebounceKeys = /* @__PURE__ */ new Map();
|
|
3346
|
-
const appMentionRetryKeys = /* @__PURE__ */ new Map();
|
|
3347
|
-
const appMentionDispatchedKeys = /* @__PURE__ */ new Map();
|
|
3348
|
-
const pruneAppMentionRetryKeys = (now) => {
|
|
3349
|
-
for (const [key, expiresAt] of appMentionRetryKeys) if (expiresAt <= now) appMentionRetryKeys.delete(key);
|
|
3350
|
-
for (const [key, expiresAt] of appMentionDispatchedKeys) if (expiresAt <= now) appMentionDispatchedKeys.delete(key);
|
|
3351
|
-
};
|
|
3352
|
-
const rememberAppMentionRetryKey = (key) => {
|
|
3353
|
-
const now = Date.now();
|
|
3354
|
-
pruneAppMentionRetryKeys(now);
|
|
3355
|
-
appMentionRetryKeys.set(key, now + APP_MENTION_RETRY_TTL_MS);
|
|
3356
|
-
};
|
|
3357
|
-
const consumeAppMentionRetryKey = (key) => {
|
|
3358
|
-
pruneAppMentionRetryKeys(Date.now());
|
|
3359
|
-
if (!appMentionRetryKeys.has(key)) return false;
|
|
3360
|
-
appMentionRetryKeys.delete(key);
|
|
3361
|
-
return true;
|
|
3362
|
-
};
|
|
3363
|
-
return async (message, opts) => {
|
|
3364
|
-
if (opts.source === "message" && message.type !== "message") return;
|
|
3365
|
-
if (opts.source === "message" && message.subtype && message.subtype !== "file_share" && message.subtype !== "bot_message") return;
|
|
3366
|
-
const seenMessageKey = buildSeenMessageKey(message.channel, message.ts);
|
|
3367
|
-
const wasSeen = seenMessageKey ? ctx.markMessageSeen(message.channel, message.ts) : false;
|
|
3368
|
-
if (seenMessageKey && opts.source === "message" && !wasSeen) rememberAppMentionRetryKey(seenMessageKey);
|
|
3369
|
-
if (seenMessageKey && wasSeen) {
|
|
3370
|
-
if (opts.source !== "app_mention" || !consumeAppMentionRetryKey(seenMessageKey)) return;
|
|
3371
|
-
}
|
|
3372
|
-
trackEvent?.();
|
|
3373
|
-
const resolvedMessage = await threadTsResolver.resolve({
|
|
3374
|
-
message,
|
|
3375
|
-
source: opts.source
|
|
3376
|
-
});
|
|
3377
|
-
const debounceKey = buildSlackDebounceKey(resolvedMessage, ctx.accountId);
|
|
3378
|
-
const conversationKey = buildTopLevelSlackConversationKey(resolvedMessage, ctx.accountId);
|
|
3379
|
-
const canDebounce = debounceMs > 0 && shouldDebounceSlackMessage(resolvedMessage, ctx.cfg);
|
|
3380
|
-
if (!canDebounce && conversationKey) {
|
|
3381
|
-
const pendingKeys = pendingTopLevelDebounceKeys.get(conversationKey);
|
|
3382
|
-
if (pendingKeys && pendingKeys.size > 0) {
|
|
3383
|
-
const keysToFlush = Array.from(pendingKeys);
|
|
3384
|
-
for (const pendingKey of keysToFlush) await debouncer.flushKey(pendingKey);
|
|
3385
|
-
}
|
|
3386
|
-
}
|
|
3387
|
-
if (canDebounce && debounceKey && conversationKey) {
|
|
3388
|
-
const pendingKeys = pendingTopLevelDebounceKeys.get(conversationKey) ?? /* @__PURE__ */ new Set();
|
|
3389
|
-
pendingKeys.add(debounceKey);
|
|
3390
|
-
pendingTopLevelDebounceKeys.set(conversationKey, pendingKeys);
|
|
3391
|
-
}
|
|
3392
|
-
await debouncer.enqueue({
|
|
3393
|
-
message: resolvedMessage,
|
|
3394
|
-
opts
|
|
3395
|
-
});
|
|
3396
|
-
};
|
|
3397
|
-
}
|
|
3398
|
-
//#endregion
|
|
3399
|
-
//#region extensions/slack/src/monitor/reconnect-policy.ts
|
|
3400
|
-
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;
|
|
3401
|
-
const SLACK_SOCKET_RECONNECT_POLICY = {
|
|
3402
|
-
initialMs: 2e3,
|
|
3403
|
-
maxMs: 3e4,
|
|
3404
|
-
factor: 1.8,
|
|
3405
|
-
jitter: .25,
|
|
3406
|
-
maxAttempts: 12
|
|
3407
|
-
};
|
|
3408
|
-
function getSocketEmitter(app) {
|
|
3409
|
-
const receiver = app.receiver;
|
|
3410
|
-
const client = receiver && typeof receiver === "object" ? receiver.client : void 0;
|
|
3411
|
-
if (!client || typeof client !== "object") return null;
|
|
3412
|
-
const on = client.on;
|
|
3413
|
-
const off = client.off;
|
|
3414
|
-
if (typeof on !== "function" || typeof off !== "function") return null;
|
|
3415
|
-
return {
|
|
3416
|
-
on: (event, listener) => on.call(client, event, listener),
|
|
3417
|
-
off: (event, listener) => off.call(client, event, listener)
|
|
3418
|
-
};
|
|
3419
|
-
}
|
|
3420
|
-
function waitForSlackSocketDisconnect(app, abortSignal) {
|
|
3421
|
-
return new Promise((resolve) => {
|
|
3422
|
-
const emitter = getSocketEmitter(app);
|
|
3423
|
-
if (!emitter) {
|
|
3424
|
-
abortSignal?.addEventListener("abort", () => resolve({ event: "disconnect" }), { once: true });
|
|
3425
|
-
return;
|
|
3426
|
-
}
|
|
3427
|
-
const disconnectListener = () => resolveOnce({ event: "disconnect" });
|
|
3428
|
-
const startFailListener = (error) => resolveOnce({
|
|
3429
|
-
event: "unable_to_socket_mode_start",
|
|
3430
|
-
error
|
|
3431
|
-
});
|
|
3432
|
-
const errorListener = (error) => resolveOnce({
|
|
3433
|
-
event: "error",
|
|
3434
|
-
error
|
|
3435
|
-
});
|
|
3436
|
-
const abortListener = () => resolveOnce({ event: "disconnect" });
|
|
3437
|
-
const cleanup = () => {
|
|
3438
|
-
emitter.off("disconnected", disconnectListener);
|
|
3439
|
-
emitter.off("unable_to_socket_mode_start", startFailListener);
|
|
3440
|
-
emitter.off("error", errorListener);
|
|
3441
|
-
abortSignal?.removeEventListener("abort", abortListener);
|
|
3442
|
-
};
|
|
3443
|
-
const resolveOnce = (value) => {
|
|
3444
|
-
cleanup();
|
|
3445
|
-
resolve(value);
|
|
3446
|
-
};
|
|
3447
|
-
emitter.on("disconnected", disconnectListener);
|
|
3448
|
-
emitter.on("unable_to_socket_mode_start", startFailListener);
|
|
3449
|
-
emitter.on("error", errorListener);
|
|
3450
|
-
abortSignal?.addEventListener("abort", abortListener, { once: true });
|
|
3451
|
-
});
|
|
3452
|
-
}
|
|
3453
|
-
/**
|
|
3454
|
-
* Detect non-recoverable Slack API / auth errors that should NOT be retried.
|
|
3455
|
-
* These indicate permanent credential problems (revoked bot, deactivated account, etc.)
|
|
3456
|
-
* and retrying will never succeed — continuing to retry blocks the entire gateway.
|
|
3457
|
-
*/
|
|
3458
|
-
function isNonRecoverableSlackAuthError(error) {
|
|
3459
|
-
const msg = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
3460
|
-
return SLACK_AUTH_ERROR_RE.test(msg);
|
|
3461
|
-
}
|
|
3462
|
-
function formatUnknownError(error) {
|
|
3463
|
-
if (error instanceof Error) return error.message;
|
|
3464
|
-
if (typeof error === "string") return error;
|
|
3465
|
-
try {
|
|
3466
|
-
return JSON.stringify(error);
|
|
3467
|
-
} catch {
|
|
3468
|
-
return "unknown error";
|
|
3469
|
-
}
|
|
3470
|
-
}
|
|
3471
|
-
//#endregion
|
|
3472
|
-
//#region extensions/slack/src/monitor/external-arg-menu-store.ts
|
|
3473
|
-
const SLACK_EXTERNAL_ARG_MENU_TOKEN_BYTES = 18;
|
|
3474
|
-
const SLACK_EXTERNAL_ARG_MENU_TOKEN_LENGTH = Math.ceil(SLACK_EXTERNAL_ARG_MENU_TOKEN_BYTES * 8 / 6);
|
|
3475
|
-
const SLACK_EXTERNAL_ARG_MENU_TOKEN_PATTERN = new RegExp(`^[A-Za-z0-9_-]{${SLACK_EXTERNAL_ARG_MENU_TOKEN_LENGTH}}$`);
|
|
3476
|
-
const SLACK_EXTERNAL_ARG_MENU_TTL_MS = 600 * 1e3;
|
|
3477
|
-
const SLACK_EXTERNAL_ARG_MENU_PREFIX = "moldclaw_cmdarg_ext:";
|
|
3478
|
-
function pruneSlackExternalArgMenuStore(store, now) {
|
|
3479
|
-
for (const [token, entry] of store.entries()) if (entry.expiresAt <= now) store.delete(token);
|
|
3480
|
-
}
|
|
3481
|
-
function createSlackExternalArgMenuToken(store) {
|
|
3482
|
-
let token = "";
|
|
3483
|
-
do
|
|
3484
|
-
token = generateSecureToken(SLACK_EXTERNAL_ARG_MENU_TOKEN_BYTES);
|
|
3485
|
-
while (store.has(token));
|
|
3486
|
-
return token;
|
|
3487
|
-
}
|
|
3488
|
-
function createSlackExternalArgMenuStore() {
|
|
3489
|
-
const store = /* @__PURE__ */ new Map();
|
|
3490
|
-
return {
|
|
3491
|
-
create(params, now = Date.now()) {
|
|
3492
|
-
pruneSlackExternalArgMenuStore(store, now);
|
|
3493
|
-
const token = createSlackExternalArgMenuToken(store);
|
|
3494
|
-
store.set(token, {
|
|
3495
|
-
choices: params.choices,
|
|
3496
|
-
userId: params.userId,
|
|
3497
|
-
expiresAt: now + SLACK_EXTERNAL_ARG_MENU_TTL_MS
|
|
3498
|
-
});
|
|
3499
|
-
return token;
|
|
3500
|
-
},
|
|
3501
|
-
readToken(raw) {
|
|
3502
|
-
if (typeof raw !== "string" || !raw.startsWith("moldclaw_cmdarg_ext:")) return;
|
|
3503
|
-
const token = raw.slice(20).trim();
|
|
3504
|
-
return SLACK_EXTERNAL_ARG_MENU_TOKEN_PATTERN.test(token) ? token : void 0;
|
|
3505
|
-
},
|
|
3506
|
-
get(token, now = Date.now()) {
|
|
3507
|
-
pruneSlackExternalArgMenuStore(store, now);
|
|
3508
|
-
return store.get(token);
|
|
3509
|
-
}
|
|
3510
|
-
};
|
|
3511
|
-
}
|
|
3512
|
-
//#endregion
|
|
3513
|
-
//#region extensions/slack/src/monitor/slash.ts
|
|
3514
|
-
const SLACK_COMMAND_ARG_ACTION_ID = "moldclaw_cmdarg";
|
|
3515
|
-
const SLACK_COMMAND_ARG_VALUE_PREFIX = "cmdarg";
|
|
3516
|
-
const SLACK_COMMAND_ARG_BUTTON_ROW_SIZE = 5;
|
|
3517
|
-
const SLACK_COMMAND_ARG_OVERFLOW_MIN = 3;
|
|
3518
|
-
const SLACK_COMMAND_ARG_OVERFLOW_MAX = 5;
|
|
3519
|
-
const SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX = 100;
|
|
3520
|
-
const SLACK_COMMAND_ARG_SELECT_OPTION_VALUE_MAX = 75;
|
|
3521
|
-
const SLACK_HEADER_TEXT_MAX = 150;
|
|
3522
|
-
let slashCommandsRuntimePromise = null;
|
|
3523
|
-
let slashDispatchRuntimePromise = null;
|
|
3524
|
-
let slashSkillCommandsRuntimePromise = null;
|
|
3525
|
-
function loadSlashCommandsRuntime() {
|
|
3526
|
-
slashCommandsRuntimePromise ??= import("./slash-commands.runtime-BK88kgds.js");
|
|
3527
|
-
return slashCommandsRuntimePromise;
|
|
3528
|
-
}
|
|
3529
|
-
function loadSlashDispatchRuntime() {
|
|
3530
|
-
slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-COGywwJE.js");
|
|
3531
|
-
return slashDispatchRuntimePromise;
|
|
3532
|
-
}
|
|
3533
|
-
function loadSlashSkillCommandsRuntime() {
|
|
3534
|
-
slashSkillCommandsRuntimePromise ??= import("./slash-skill-commands.runtime-Ti4brxgh.js");
|
|
3535
|
-
return slashSkillCommandsRuntimePromise;
|
|
3536
|
-
}
|
|
3537
|
-
const slackExternalArgMenuStore = createSlackExternalArgMenuStore();
|
|
3538
|
-
function buildSlackArgMenuConfirm(params) {
|
|
3539
|
-
return {
|
|
3540
|
-
title: {
|
|
3541
|
-
type: "plain_text",
|
|
3542
|
-
text: "Confirm selection"
|
|
3543
|
-
},
|
|
3544
|
-
text: {
|
|
3545
|
-
type: "mrkdwn",
|
|
3546
|
-
text: `Run */${escapeSlackMrkdwn(params.command)}* with *${escapeSlackMrkdwn(params.arg)}* set to this value?`
|
|
3547
|
-
},
|
|
3548
|
-
confirm: {
|
|
3549
|
-
type: "plain_text",
|
|
3550
|
-
text: "Run command"
|
|
3551
|
-
},
|
|
3552
|
-
deny: {
|
|
3553
|
-
type: "plain_text",
|
|
3554
|
-
text: "Cancel"
|
|
3555
|
-
}
|
|
3556
|
-
};
|
|
3557
|
-
}
|
|
3558
|
-
function storeSlackExternalArgMenu(params) {
|
|
3559
|
-
return slackExternalArgMenuStore.create({
|
|
3560
|
-
choices: params.choices,
|
|
3561
|
-
userId: params.userId
|
|
3562
|
-
});
|
|
3563
|
-
}
|
|
3564
|
-
function readSlackExternalArgMenuToken(raw) {
|
|
3565
|
-
return slackExternalArgMenuStore.readToken(raw);
|
|
3566
|
-
}
|
|
3567
|
-
function encodeSlackCommandArgValue(parts) {
|
|
3568
|
-
return [
|
|
3569
|
-
SLACK_COMMAND_ARG_VALUE_PREFIX,
|
|
3570
|
-
encodeURIComponent(parts.command),
|
|
3571
|
-
encodeURIComponent(parts.arg),
|
|
3572
|
-
encodeURIComponent(parts.value),
|
|
3573
|
-
encodeURIComponent(parts.userId)
|
|
3574
|
-
].join("|");
|
|
3575
|
-
}
|
|
3576
|
-
function parseSlackCommandArgValue(raw) {
|
|
3577
|
-
if (!raw) return null;
|
|
3578
|
-
const parts = raw.split("|");
|
|
3579
|
-
if (parts.length !== 5 || parts[0] !== SLACK_COMMAND_ARG_VALUE_PREFIX) return null;
|
|
3580
|
-
const [, command, arg, value, userId] = parts;
|
|
3581
|
-
if (!command || !arg || !value || !userId) return null;
|
|
3582
|
-
const decode = (text) => {
|
|
3583
|
-
try {
|
|
3584
|
-
return decodeURIComponent(text);
|
|
3585
|
-
} catch {
|
|
3586
|
-
return null;
|
|
3587
|
-
}
|
|
3588
|
-
};
|
|
3589
|
-
const decodedCommand = decode(command);
|
|
3590
|
-
const decodedArg = decode(arg);
|
|
3591
|
-
const decodedValue = decode(value);
|
|
3592
|
-
const decodedUserId = decode(userId);
|
|
3593
|
-
if (!decodedCommand || !decodedArg || !decodedValue || !decodedUserId) return null;
|
|
3594
|
-
return {
|
|
3595
|
-
command: decodedCommand,
|
|
3596
|
-
arg: decodedArg,
|
|
3597
|
-
value: decodedValue,
|
|
3598
|
-
userId: decodedUserId
|
|
3599
|
-
};
|
|
3600
|
-
}
|
|
3601
|
-
function buildSlackArgMenuOptions(choices) {
|
|
3602
|
-
return choices.map((choice) => ({
|
|
3603
|
-
text: {
|
|
3604
|
-
type: "plain_text",
|
|
3605
|
-
text: choice.label.slice(0, 75)
|
|
3606
|
-
},
|
|
3607
|
-
value: choice.value
|
|
3608
|
-
}));
|
|
3609
|
-
}
|
|
3610
|
-
function buildSlackCommandArgMenuBlocks(params) {
|
|
3611
|
-
const encodedChoices = params.choices.map((choice) => ({
|
|
3612
|
-
label: choice.label,
|
|
3613
|
-
value: encodeSlackCommandArgValue({
|
|
3614
|
-
command: params.command,
|
|
3615
|
-
arg: params.arg,
|
|
3616
|
-
value: choice.value,
|
|
3617
|
-
userId: params.userId
|
|
3618
|
-
})
|
|
3619
|
-
}));
|
|
3620
|
-
const canUseStaticSelect = encodedChoices.every((choice) => choice.value.length <= SLACK_COMMAND_ARG_SELECT_OPTION_VALUE_MAX);
|
|
3621
|
-
const canUseOverflow = canUseStaticSelect && encodedChoices.length >= SLACK_COMMAND_ARG_OVERFLOW_MIN && encodedChoices.length <= SLACK_COMMAND_ARG_OVERFLOW_MAX;
|
|
3622
|
-
const canUseExternalSelect = params.supportsExternalSelect && canUseStaticSelect && encodedChoices.length > SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX;
|
|
3623
|
-
const rows = canUseOverflow ? [{
|
|
3624
|
-
type: "actions",
|
|
3625
|
-
elements: [{
|
|
3626
|
-
type: "overflow",
|
|
3627
|
-
action_id: SLACK_COMMAND_ARG_ACTION_ID,
|
|
3628
|
-
confirm: buildSlackArgMenuConfirm({
|
|
3629
|
-
command: params.command,
|
|
3630
|
-
arg: params.arg
|
|
3631
|
-
}),
|
|
3632
|
-
options: buildSlackArgMenuOptions(encodedChoices)
|
|
3633
|
-
}]
|
|
3634
|
-
}] : canUseExternalSelect ? [{
|
|
3635
|
-
type: "actions",
|
|
3636
|
-
block_id: `${SLACK_EXTERNAL_ARG_MENU_PREFIX}${params.createExternalMenuToken(encodedChoices)}`,
|
|
3637
|
-
elements: [{
|
|
3638
|
-
type: "external_select",
|
|
3639
|
-
action_id: SLACK_COMMAND_ARG_ACTION_ID,
|
|
3640
|
-
confirm: buildSlackArgMenuConfirm({
|
|
3641
|
-
command: params.command,
|
|
3642
|
-
arg: params.arg
|
|
3643
|
-
}),
|
|
3644
|
-
min_query_length: 0,
|
|
3645
|
-
placeholder: {
|
|
3646
|
-
type: "plain_text",
|
|
3647
|
-
text: `Search ${params.arg}`
|
|
3648
|
-
}
|
|
3649
|
-
}]
|
|
3650
|
-
}] : encodedChoices.length <= SLACK_COMMAND_ARG_BUTTON_ROW_SIZE || !canUseStaticSelect ? chunkItems(encodedChoices, SLACK_COMMAND_ARG_BUTTON_ROW_SIZE).map((choices) => ({
|
|
3651
|
-
type: "actions",
|
|
3652
|
-
elements: choices.map((choice) => ({
|
|
3653
|
-
type: "button",
|
|
3654
|
-
action_id: SLACK_COMMAND_ARG_ACTION_ID,
|
|
3655
|
-
text: {
|
|
3656
|
-
type: "plain_text",
|
|
3657
|
-
text: choice.label
|
|
3658
|
-
},
|
|
3659
|
-
value: choice.value,
|
|
3660
|
-
confirm: buildSlackArgMenuConfirm({
|
|
3661
|
-
command: params.command,
|
|
3662
|
-
arg: params.arg
|
|
3663
|
-
})
|
|
3664
|
-
}))
|
|
3665
|
-
})) : chunkItems(encodedChoices, SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX).map((choices, index) => ({
|
|
3666
|
-
type: "actions",
|
|
3667
|
-
elements: [{
|
|
3668
|
-
type: "static_select",
|
|
3669
|
-
action_id: SLACK_COMMAND_ARG_ACTION_ID,
|
|
3670
|
-
confirm: buildSlackArgMenuConfirm({
|
|
3671
|
-
command: params.command,
|
|
3672
|
-
arg: params.arg
|
|
3673
|
-
}),
|
|
3674
|
-
placeholder: {
|
|
3675
|
-
type: "plain_text",
|
|
3676
|
-
text: index === 0 ? `Choose ${params.arg}` : `Choose ${params.arg} (${index + 1})`
|
|
3677
|
-
},
|
|
3678
|
-
options: buildSlackArgMenuOptions(choices)
|
|
3679
|
-
}]
|
|
3680
|
-
}));
|
|
3681
|
-
const headerText = truncateSlackText(`/${params.command}: choose ${params.arg}`, SLACK_HEADER_TEXT_MAX);
|
|
3682
|
-
const sectionText = truncateSlackText(params.title, 3e3);
|
|
3683
|
-
const contextText = truncateSlackText(`Select one option to continue /${params.command} (${params.arg})`, 3e3);
|
|
3684
|
-
return [
|
|
3685
|
-
{
|
|
3686
|
-
type: "header",
|
|
3687
|
-
text: {
|
|
3688
|
-
type: "plain_text",
|
|
3689
|
-
text: headerText
|
|
3690
|
-
}
|
|
3691
|
-
},
|
|
3692
|
-
{
|
|
3693
|
-
type: "section",
|
|
3694
|
-
text: {
|
|
3695
|
-
type: "mrkdwn",
|
|
3696
|
-
text: sectionText
|
|
3697
|
-
}
|
|
3698
|
-
},
|
|
3699
|
-
{
|
|
3700
|
-
type: "context",
|
|
3701
|
-
elements: [{
|
|
3702
|
-
type: "mrkdwn",
|
|
3703
|
-
text: contextText
|
|
3704
|
-
}]
|
|
3705
|
-
},
|
|
3706
|
-
...rows
|
|
3707
|
-
];
|
|
3708
|
-
}
|
|
3709
|
-
async function registerSlackMonitorSlashCommands(params) {
|
|
3710
|
-
const { ctx, account } = params;
|
|
3711
|
-
const cfg = ctx.cfg;
|
|
3712
|
-
const runtime = ctx.runtime;
|
|
3713
|
-
const supportsInteractiveArgMenus = typeof ctx.app.action === "function";
|
|
3714
|
-
let supportsExternalArgMenus = typeof ctx.app.options === "function";
|
|
3715
|
-
const slashCommand = resolveSlackSlashCommandConfig(ctx.slashCommand ?? account.config.slashCommand);
|
|
3716
|
-
const handleSlashCommand = async (p) => {
|
|
3717
|
-
const { command, ack, respond, body, prompt, commandArgs, commandDefinition } = p;
|
|
3718
|
-
try {
|
|
3719
|
-
if (ctx.shouldDropMismatchedSlackEvent?.(body)) {
|
|
3720
|
-
await ack();
|
|
3721
|
-
runtime.log?.(`slack: drop slash command from user=${command.user_id ?? "unknown"} channel=${command.channel_id ?? "unknown"} (mismatched app/team)`);
|
|
3722
|
-
return;
|
|
3723
|
-
}
|
|
3724
|
-
if (!prompt.trim()) {
|
|
3725
|
-
await ack({
|
|
3726
|
-
text: "Message required.",
|
|
3727
|
-
response_type: "ephemeral"
|
|
3728
|
-
});
|
|
3729
|
-
return;
|
|
3730
|
-
}
|
|
3731
|
-
await ack();
|
|
3732
|
-
if (ctx.botUserId && command.user_id === ctx.botUserId) return;
|
|
3733
|
-
const channelInfo = await ctx.resolveChannelName(command.channel_id);
|
|
3734
|
-
const channelType = normalizeSlackChannelType(channelInfo?.type ?? (command.channel_name === "directmessage" ? "im" : void 0), command.channel_id);
|
|
3735
|
-
const isDirectMessage = channelType === "im";
|
|
3736
|
-
const isGroupDm = channelType === "mpim";
|
|
3737
|
-
const isRoom = channelType === "channel" || channelType === "group";
|
|
3738
|
-
const isRoomish = isRoom || isGroupDm;
|
|
3739
|
-
if (!ctx.isChannelAllowed({
|
|
3740
|
-
channelId: command.channel_id,
|
|
3741
|
-
channelName: channelInfo?.name,
|
|
3742
|
-
channelType
|
|
3743
|
-
})) {
|
|
3744
|
-
await respond({
|
|
3745
|
-
text: "This channel is not allowed.",
|
|
3746
|
-
response_type: "ephemeral"
|
|
3747
|
-
});
|
|
3748
|
-
return;
|
|
3749
|
-
}
|
|
3750
|
-
const { allowFromLower: effectiveAllowFromLower } = await resolveSlackEffectiveAllowFrom(ctx, { includePairingStore: isDirectMessage });
|
|
3751
|
-
let commandAuthorized = false;
|
|
3752
|
-
let channelConfig = null;
|
|
3753
|
-
if (isDirectMessage) {
|
|
3754
|
-
if (!await authorizeSlackDirectMessage({
|
|
3755
|
-
ctx,
|
|
3756
|
-
accountId: ctx.accountId,
|
|
3757
|
-
senderId: command.user_id,
|
|
3758
|
-
allowFromLower: effectiveAllowFromLower,
|
|
3759
|
-
resolveSenderName: ctx.resolveUserName,
|
|
3760
|
-
sendPairingReply: async (text) => {
|
|
3761
|
-
await respond({
|
|
3762
|
-
text,
|
|
3763
|
-
response_type: "ephemeral"
|
|
3764
|
-
});
|
|
3765
|
-
},
|
|
3766
|
-
onDisabled: async () => {
|
|
3767
|
-
await respond({
|
|
3768
|
-
text: "Slack DMs are disabled.",
|
|
3769
|
-
response_type: "ephemeral"
|
|
3770
|
-
});
|
|
3771
|
-
},
|
|
3772
|
-
onUnauthorized: async ({ allowMatchMeta }) => {
|
|
3773
|
-
logVerbose(`slack: blocked slash sender ${command.user_id} (dmPolicy=${ctx.dmPolicy}, ${allowMatchMeta})`);
|
|
3774
|
-
await respond({
|
|
3775
|
-
text: "You are not authorized to use this command.",
|
|
3776
|
-
response_type: "ephemeral"
|
|
3777
|
-
});
|
|
3778
|
-
},
|
|
3779
|
-
log: logVerbose
|
|
3780
|
-
})) return;
|
|
3781
|
-
}
|
|
3782
|
-
if (isRoom) {
|
|
3783
|
-
channelConfig = resolveSlackChannelConfig({
|
|
3784
|
-
channelId: command.channel_id,
|
|
3785
|
-
channelName: channelInfo?.name,
|
|
3786
|
-
channels: ctx.channelsConfig,
|
|
3787
|
-
channelKeys: ctx.channelsConfigKeys,
|
|
3788
|
-
defaultRequireMention: ctx.defaultRequireMention,
|
|
3789
|
-
allowNameMatching: ctx.allowNameMatching
|
|
3790
|
-
});
|
|
3791
|
-
if (ctx.useAccessGroups) {
|
|
3792
|
-
const channelAllowlistConfigured = (ctx.channelsConfigKeys?.length ?? 0) > 0;
|
|
3793
|
-
const channelAllowed = channelConfig?.allowed !== false;
|
|
3794
|
-
if (!isSlackChannelAllowedByPolicy({
|
|
3795
|
-
groupPolicy: ctx.groupPolicy,
|
|
3796
|
-
channelAllowlistConfigured,
|
|
3797
|
-
channelAllowed
|
|
3798
|
-
})) {
|
|
3799
|
-
await respond({
|
|
3800
|
-
text: "This channel is not allowed.",
|
|
3801
|
-
response_type: "ephemeral"
|
|
3802
|
-
});
|
|
3803
|
-
return;
|
|
3804
|
-
}
|
|
3805
|
-
const hasExplicitConfig = Boolean(channelConfig?.matchSource);
|
|
3806
|
-
if (!channelAllowed && (ctx.groupPolicy !== "open" || hasExplicitConfig)) {
|
|
3807
|
-
await respond({
|
|
3808
|
-
text: "This channel is not allowed.",
|
|
3809
|
-
response_type: "ephemeral"
|
|
3810
|
-
});
|
|
3811
|
-
return;
|
|
3812
|
-
}
|
|
3813
|
-
}
|
|
3814
|
-
}
|
|
3815
|
-
const senderName = (await ctx.resolveUserName(command.user_id))?.name ?? command.user_name ?? command.user_id;
|
|
3816
|
-
const channelUsersAllowlistConfigured = isRoom && Array.isArray(channelConfig?.users) && channelConfig.users.length > 0;
|
|
3817
|
-
const channelUserAllowed = channelUsersAllowlistConfigured ? resolveSlackUserAllowed({
|
|
3818
|
-
allowList: channelConfig?.users,
|
|
3819
|
-
userId: command.user_id,
|
|
3820
|
-
userName: senderName,
|
|
3821
|
-
allowNameMatching: ctx.allowNameMatching
|
|
3822
|
-
}) : false;
|
|
3823
|
-
if (channelUsersAllowlistConfigured && !channelUserAllowed) {
|
|
3824
|
-
await respond({
|
|
3825
|
-
text: "You are not authorized to use this command here.",
|
|
3826
|
-
response_type: "ephemeral"
|
|
3827
|
-
});
|
|
3828
|
-
return;
|
|
3829
|
-
}
|
|
3830
|
-
const ownerAllowed = resolveSlackAllowListMatch({
|
|
3831
|
-
allowList: effectiveAllowFromLower,
|
|
3832
|
-
id: command.user_id,
|
|
3833
|
-
name: senderName,
|
|
3834
|
-
allowNameMatching: ctx.allowNameMatching
|
|
3835
|
-
}).allowed;
|
|
3836
|
-
commandAuthorized = resolveCommandAuthorizedFromAuthorizers({
|
|
3837
|
-
useAccessGroups: ctx.useAccessGroups,
|
|
3838
|
-
authorizers: [{
|
|
3839
|
-
configured: effectiveAllowFromLower.length > 0,
|
|
3840
|
-
allowed: ownerAllowed
|
|
3841
|
-
}],
|
|
3842
|
-
modeWhenAccessGroupsOff: "configured"
|
|
3843
|
-
});
|
|
3844
|
-
if (isRoomish) {
|
|
3845
|
-
commandAuthorized = resolveCommandAuthorizedFromAuthorizers({
|
|
3846
|
-
useAccessGroups: ctx.useAccessGroups,
|
|
3847
|
-
authorizers: [{
|
|
3848
|
-
configured: effectiveAllowFromLower.length > 0,
|
|
3849
|
-
allowed: ownerAllowed
|
|
3850
|
-
}, {
|
|
3851
|
-
configured: channelUsersAllowlistConfigured,
|
|
3852
|
-
allowed: channelUserAllowed
|
|
3853
|
-
}],
|
|
3854
|
-
modeWhenAccessGroupsOff: "configured"
|
|
3855
|
-
});
|
|
3856
|
-
if (ctx.useAccessGroups && !commandAuthorized) {
|
|
3857
|
-
await respond({
|
|
3858
|
-
text: "You are not authorized to use this command.",
|
|
3859
|
-
response_type: "ephemeral"
|
|
3860
|
-
});
|
|
3861
|
-
return;
|
|
3862
|
-
}
|
|
3863
|
-
}
|
|
3864
|
-
if (commandDefinition && supportsInteractiveArgMenus) {
|
|
3865
|
-
const { resolveCommandArgMenu } = await loadSlashCommandsRuntime();
|
|
3866
|
-
const menu = resolveCommandArgMenu({
|
|
3867
|
-
command: commandDefinition,
|
|
3868
|
-
args: commandArgs,
|
|
3869
|
-
cfg
|
|
3870
|
-
});
|
|
3871
|
-
if (menu) {
|
|
3872
|
-
const commandLabel = commandDefinition.nativeName ?? commandDefinition.key;
|
|
3873
|
-
const title = menu.title ?? `Choose ${menu.arg.description || menu.arg.name} for /${commandLabel}.`;
|
|
3874
|
-
await respond({
|
|
3875
|
-
text: title,
|
|
3876
|
-
blocks: buildSlackCommandArgMenuBlocks({
|
|
3877
|
-
title,
|
|
3878
|
-
command: commandLabel,
|
|
3879
|
-
arg: menu.arg.name,
|
|
3880
|
-
choices: menu.choices,
|
|
3881
|
-
userId: command.user_id,
|
|
3882
|
-
supportsExternalSelect: supportsExternalArgMenus,
|
|
3883
|
-
createExternalMenuToken: (choices) => storeSlackExternalArgMenu({
|
|
3884
|
-
choices,
|
|
3885
|
-
userId: command.user_id
|
|
3886
|
-
})
|
|
3887
|
-
}),
|
|
3888
|
-
response_type: "ephemeral"
|
|
3889
|
-
});
|
|
3890
|
-
return;
|
|
3891
|
-
}
|
|
3892
|
-
}
|
|
3893
|
-
const channelName = channelInfo?.name;
|
|
3894
|
-
const roomLabel = channelName ? `#${channelName}` : `#${command.channel_id}`;
|
|
3895
|
-
const { createReplyPrefixOptions, deliverSlackSlashReplies, dispatchReplyWithDispatcher, finalizeInboundContext, recordInboundSessionMetaSafe, resolveAgentRoute, resolveChunkMode, resolveConversationLabel, resolveMarkdownTableMode } = await loadSlashDispatchRuntime();
|
|
3896
|
-
const route = resolveAgentRoute({
|
|
3897
|
-
cfg,
|
|
3898
|
-
channel: "slack",
|
|
3899
|
-
accountId: account.accountId,
|
|
3900
|
-
teamId: ctx.teamId || void 0,
|
|
3901
|
-
peer: {
|
|
3902
|
-
kind: isDirectMessage ? "direct" : isRoom ? "channel" : "group",
|
|
3903
|
-
id: isDirectMessage ? command.user_id : command.channel_id
|
|
3904
|
-
}
|
|
3905
|
-
});
|
|
3906
|
-
const { untrustedChannelMetadata, groupSystemPrompt } = resolveSlackRoomContextHints({
|
|
3907
|
-
isRoomish,
|
|
3908
|
-
channelInfo,
|
|
3909
|
-
channelConfig
|
|
3910
|
-
});
|
|
3911
|
-
const { sessionKey, commandTargetSessionKey } = resolveNativeCommandSessionTargets({
|
|
3912
|
-
agentId: route.agentId,
|
|
3913
|
-
sessionPrefix: slashCommand.sessionPrefix,
|
|
3914
|
-
userId: command.user_id,
|
|
3915
|
-
targetSessionKey: route.sessionKey,
|
|
3916
|
-
lowercaseSessionKey: true
|
|
3917
|
-
});
|
|
3918
|
-
const ctxPayload = finalizeInboundContext({
|
|
3919
|
-
Body: prompt,
|
|
3920
|
-
BodyForAgent: prompt,
|
|
3921
|
-
RawBody: prompt,
|
|
3922
|
-
CommandBody: prompt,
|
|
3923
|
-
CommandArgs: commandArgs,
|
|
3924
|
-
From: isDirectMessage ? `slack:${command.user_id}` : isRoom ? `slack:channel:${command.channel_id}` : `slack:group:${command.channel_id}`,
|
|
3925
|
-
To: `slash:${command.user_id}`,
|
|
3926
|
-
ChatType: isDirectMessage ? "direct" : "channel",
|
|
3927
|
-
ConversationLabel: resolveConversationLabel({
|
|
3928
|
-
ChatType: isDirectMessage ? "direct" : "channel",
|
|
3929
|
-
SenderName: senderName,
|
|
3930
|
-
GroupSubject: isRoomish ? roomLabel : void 0,
|
|
3931
|
-
From: isDirectMessage ? `slack:${command.user_id}` : isRoom ? `slack:channel:${command.channel_id}` : `slack:group:${command.channel_id}`
|
|
3932
|
-
}) ?? (isDirectMessage ? senderName : roomLabel),
|
|
3933
|
-
GroupSubject: isRoomish ? roomLabel : void 0,
|
|
3934
|
-
GroupSystemPrompt: isRoomish ? groupSystemPrompt : void 0,
|
|
3935
|
-
UntrustedContext: untrustedChannelMetadata ? [untrustedChannelMetadata] : void 0,
|
|
3936
|
-
SenderName: senderName,
|
|
3937
|
-
SenderId: command.user_id,
|
|
3938
|
-
Provider: "slack",
|
|
3939
|
-
Surface: "slack",
|
|
3940
|
-
WasMentioned: true,
|
|
3941
|
-
MessageSid: command.trigger_id,
|
|
3942
|
-
Timestamp: Date.now(),
|
|
3943
|
-
SessionKey: sessionKey,
|
|
3944
|
-
CommandTargetSessionKey: commandTargetSessionKey,
|
|
3945
|
-
AccountId: route.accountId,
|
|
3946
|
-
CommandSource: "native",
|
|
3947
|
-
CommandAuthorized: commandAuthorized,
|
|
3948
|
-
OriginatingChannel: "slack",
|
|
3949
|
-
OriginatingTo: `user:${command.user_id}`
|
|
3950
|
-
});
|
|
3951
|
-
await recordInboundSessionMetaSafe({
|
|
3952
|
-
cfg,
|
|
3953
|
-
agentId: route.agentId,
|
|
3954
|
-
sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
|
|
3955
|
-
ctx: ctxPayload,
|
|
3956
|
-
onError: (err) => runtime.error?.(danger(`slack slash: failed updating session meta: ${String(err)}`))
|
|
3957
|
-
});
|
|
3958
|
-
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
|
|
3959
|
-
cfg,
|
|
3960
|
-
agentId: route.agentId,
|
|
3961
|
-
channel: "slack",
|
|
3962
|
-
accountId: route.accountId
|
|
3963
|
-
});
|
|
3964
|
-
const deliverSlashPayloads = async (replies) => {
|
|
3965
|
-
await deliverSlackSlashReplies({
|
|
3966
|
-
replies,
|
|
3967
|
-
respond,
|
|
3968
|
-
ephemeral: slashCommand.ephemeral,
|
|
3969
|
-
textLimit: ctx.textLimit,
|
|
3970
|
-
chunkMode: resolveChunkMode(cfg, "slack", route.accountId),
|
|
3971
|
-
tableMode: resolveMarkdownTableMode({
|
|
3972
|
-
cfg,
|
|
3973
|
-
channel: "slack",
|
|
3974
|
-
accountId: route.accountId
|
|
3975
|
-
})
|
|
3976
|
-
});
|
|
3977
|
-
};
|
|
3978
|
-
const { counts } = await dispatchReplyWithDispatcher({
|
|
3979
|
-
ctx: ctxPayload,
|
|
3980
|
-
cfg,
|
|
3981
|
-
dispatcherOptions: {
|
|
3982
|
-
...prefixOptions,
|
|
3983
|
-
deliver: async (payload) => deliverSlashPayloads([payload]),
|
|
3984
|
-
onError: (err, info) => {
|
|
3985
|
-
runtime.error?.(danger(`slack slash ${info.kind} reply failed: ${String(err)}`));
|
|
3986
|
-
}
|
|
3987
|
-
},
|
|
3988
|
-
replyOptions: {
|
|
3989
|
-
skillFilter: channelConfig?.skills,
|
|
3990
|
-
onModelSelected
|
|
3991
|
-
}
|
|
3992
|
-
});
|
|
3993
|
-
if (counts.final + counts.tool + counts.block === 0) await deliverSlashPayloads([]);
|
|
3994
|
-
} catch (err) {
|
|
3995
|
-
runtime.error?.(danger(`slack slash handler failed: ${String(err)}`));
|
|
3996
|
-
await respond({
|
|
3997
|
-
text: "Sorry, something went wrong handling that command.",
|
|
3998
|
-
response_type: "ephemeral"
|
|
3999
|
-
});
|
|
4000
|
-
}
|
|
4001
|
-
};
|
|
4002
|
-
const nativeEnabled = resolveNativeCommandsEnabled({
|
|
4003
|
-
providerId: "slack",
|
|
4004
|
-
providerSetting: account.config.commands?.native,
|
|
4005
|
-
globalSetting: cfg.commands?.native
|
|
4006
|
-
});
|
|
4007
|
-
const nativeSkillsEnabled = resolveNativeSkillsEnabled({
|
|
4008
|
-
providerId: "slack",
|
|
4009
|
-
providerSetting: account.config.commands?.nativeSkills,
|
|
4010
|
-
globalSetting: cfg.commands?.nativeSkills
|
|
4011
|
-
});
|
|
4012
|
-
let nativeCommands = [];
|
|
4013
|
-
let slashCommandsRuntime = null;
|
|
4014
|
-
if (nativeEnabled) {
|
|
4015
|
-
slashCommandsRuntime = await loadSlashCommandsRuntime();
|
|
4016
|
-
const skillCommands = nativeSkillsEnabled ? (await loadSlashSkillCommandsRuntime()).listSkillCommandsForAgents({ cfg }) : [];
|
|
4017
|
-
nativeCommands = slashCommandsRuntime.listNativeCommandSpecsForConfig(cfg, {
|
|
4018
|
-
skillCommands,
|
|
4019
|
-
provider: "slack"
|
|
4020
|
-
});
|
|
4021
|
-
}
|
|
4022
|
-
if (nativeCommands.length > 0) {
|
|
4023
|
-
if (!slashCommandsRuntime) throw new Error("Missing commands runtime for native Slack commands.");
|
|
4024
|
-
for (const command of nativeCommands) ctx.app.command(`/${command.name}`, async ({ command: cmd, ack, respond, body }) => {
|
|
4025
|
-
const commandDefinition = slashCommandsRuntime.findCommandByNativeName(command.name, "slack");
|
|
4026
|
-
const rawText = cmd.text?.trim() ?? "";
|
|
4027
|
-
const commandArgs = commandDefinition ? slashCommandsRuntime.parseCommandArgs(commandDefinition, rawText) : rawText ? { raw: rawText } : void 0;
|
|
4028
|
-
await handleSlashCommand({
|
|
4029
|
-
command: cmd,
|
|
4030
|
-
ack,
|
|
4031
|
-
respond,
|
|
4032
|
-
body,
|
|
4033
|
-
prompt: commandDefinition ? slashCommandsRuntime.buildCommandTextFromArgs(commandDefinition, commandArgs) : rawText ? `/${command.name} ${rawText}` : `/${command.name}`,
|
|
4034
|
-
commandArgs,
|
|
4035
|
-
commandDefinition: commandDefinition ?? void 0
|
|
4036
|
-
});
|
|
4037
|
-
});
|
|
4038
|
-
} else if (slashCommand.enabled) ctx.app.command(buildSlackSlashCommandMatcher(slashCommand.name), async ({ command, ack, respond, body }) => {
|
|
4039
|
-
await handleSlashCommand({
|
|
4040
|
-
command,
|
|
4041
|
-
ack,
|
|
4042
|
-
respond,
|
|
4043
|
-
body,
|
|
4044
|
-
prompt: command.text?.trim() ?? ""
|
|
4045
|
-
});
|
|
4046
|
-
});
|
|
4047
|
-
else logVerbose("slack: slash commands disabled");
|
|
4048
|
-
if (nativeCommands.length === 0 || !supportsInteractiveArgMenus) return;
|
|
4049
|
-
const registerArgOptions = () => {
|
|
4050
|
-
const appWithOptions = ctx.app;
|
|
4051
|
-
if (typeof appWithOptions.options !== "function") return;
|
|
4052
|
-
appWithOptions.options(SLACK_COMMAND_ARG_ACTION_ID, async ({ ack, body }) => {
|
|
4053
|
-
if (ctx.shouldDropMismatchedSlackEvent?.(body)) {
|
|
4054
|
-
await ack({ options: [] });
|
|
4055
|
-
runtime.log?.("slack: drop slash arg options payload (mismatched app/team)");
|
|
4056
|
-
return;
|
|
4057
|
-
}
|
|
4058
|
-
const typedBody = body;
|
|
4059
|
-
const token = readSlackExternalArgMenuToken(typedBody.actions?.[0]?.block_id ?? typedBody.block_id);
|
|
4060
|
-
if (!token) {
|
|
4061
|
-
await ack({ options: [] });
|
|
4062
|
-
return;
|
|
4063
|
-
}
|
|
4064
|
-
const entry = slackExternalArgMenuStore.get(token);
|
|
4065
|
-
if (!entry) {
|
|
4066
|
-
await ack({ options: [] });
|
|
4067
|
-
return;
|
|
4068
|
-
}
|
|
4069
|
-
const requesterUserId = typedBody.user?.id?.trim();
|
|
4070
|
-
if (!requesterUserId || requesterUserId !== entry.userId) {
|
|
4071
|
-
await ack({ options: [] });
|
|
4072
|
-
return;
|
|
4073
|
-
}
|
|
4074
|
-
const query = typedBody.value?.trim().toLowerCase() ?? "";
|
|
4075
|
-
await ack({ options: entry.choices.filter((choice) => !query || choice.label.toLowerCase().includes(query)).slice(0, SLACK_COMMAND_ARG_SELECT_OPTIONS_MAX).map((choice) => ({
|
|
4076
|
-
text: {
|
|
4077
|
-
type: "plain_text",
|
|
4078
|
-
text: choice.label.slice(0, 75)
|
|
4079
|
-
},
|
|
4080
|
-
value: choice.value
|
|
4081
|
-
})) });
|
|
4082
|
-
});
|
|
4083
|
-
};
|
|
4084
|
-
try {
|
|
4085
|
-
registerArgOptions();
|
|
4086
|
-
} catch (err) {
|
|
4087
|
-
supportsExternalArgMenus = false;
|
|
4088
|
-
logVerbose(`slack: external arg-menu registration failed, falling back to static menus: ${String(err)}`);
|
|
4089
|
-
}
|
|
4090
|
-
const registerArgAction = (actionId) => {
|
|
4091
|
-
ctx.app.action(actionId, async (args) => {
|
|
4092
|
-
const { ack, body, respond } = args;
|
|
4093
|
-
const action = args.action;
|
|
4094
|
-
await ack();
|
|
4095
|
-
if (ctx.shouldDropMismatchedSlackEvent?.(body)) {
|
|
4096
|
-
runtime.log?.("slack: drop slash arg action payload (mismatched app/team)");
|
|
4097
|
-
return;
|
|
4098
|
-
}
|
|
4099
|
-
const respondFn = respond ?? (async (payload) => {
|
|
4100
|
-
if (!body.channel?.id || !body.user?.id) return;
|
|
4101
|
-
await ctx.app.client.chat.postEphemeral({
|
|
4102
|
-
token: ctx.botToken,
|
|
4103
|
-
channel: body.channel.id,
|
|
4104
|
-
user: body.user.id,
|
|
4105
|
-
text: payload.text,
|
|
4106
|
-
blocks: payload.blocks
|
|
4107
|
-
});
|
|
4108
|
-
});
|
|
4109
|
-
const parsed = parseSlackCommandArgValue(action?.value ?? action?.selected_option?.value);
|
|
4110
|
-
if (!parsed) {
|
|
4111
|
-
await respondFn({
|
|
4112
|
-
text: "Sorry, that button is no longer valid.",
|
|
4113
|
-
response_type: "ephemeral"
|
|
4114
|
-
});
|
|
4115
|
-
return;
|
|
4116
|
-
}
|
|
4117
|
-
if (body.user?.id && parsed.userId !== body.user.id) {
|
|
4118
|
-
await respondFn({
|
|
4119
|
-
text: "That menu is for another user.",
|
|
4120
|
-
response_type: "ephemeral"
|
|
4121
|
-
});
|
|
4122
|
-
return;
|
|
4123
|
-
}
|
|
4124
|
-
const { buildCommandTextFromArgs, findCommandByNativeName } = await loadSlashCommandsRuntime();
|
|
4125
|
-
const commandDefinition = findCommandByNativeName(parsed.command, "slack");
|
|
4126
|
-
const commandArgs = { values: { [parsed.arg]: parsed.value } };
|
|
4127
|
-
const prompt = commandDefinition ? buildCommandTextFromArgs(commandDefinition, commandArgs) : `/${parsed.command} ${parsed.value}`;
|
|
4128
|
-
const user = body.user;
|
|
4129
|
-
const userName = user && "name" in user && user.name ? user.name : user && "username" in user && user.username ? user.username : user?.id ?? "";
|
|
4130
|
-
const triggerId = "trigger_id" in body ? body.trigger_id : void 0;
|
|
4131
|
-
await handleSlashCommand({
|
|
4132
|
-
command: {
|
|
4133
|
-
user_id: user?.id ?? "",
|
|
4134
|
-
user_name: userName,
|
|
4135
|
-
channel_id: body.channel?.id ?? "",
|
|
4136
|
-
channel_name: body.channel?.name ?? body.channel?.id ?? "",
|
|
4137
|
-
trigger_id: triggerId
|
|
4138
|
-
},
|
|
4139
|
-
ack: async () => {},
|
|
4140
|
-
respond: respondFn,
|
|
4141
|
-
body,
|
|
4142
|
-
prompt,
|
|
4143
|
-
commandArgs,
|
|
4144
|
-
commandDefinition: commandDefinition ?? void 0
|
|
4145
|
-
});
|
|
4146
|
-
});
|
|
4147
|
-
};
|
|
4148
|
-
registerArgAction(SLACK_COMMAND_ARG_ACTION_ID);
|
|
4149
|
-
}
|
|
4150
|
-
//#endregion
|
|
4151
|
-
//#region extensions/slack/src/monitor/provider.ts
|
|
4152
|
-
function isConstructorFunction(value) {
|
|
4153
|
-
return typeof value === "function";
|
|
4154
|
-
}
|
|
4155
|
-
function resolveSlackBoltModule(value) {
|
|
4156
|
-
if (!value || typeof value !== "object") return null;
|
|
4157
|
-
const app = Reflect.get(value, "App");
|
|
4158
|
-
const httpReceiver = Reflect.get(value, "HTTPReceiver");
|
|
4159
|
-
if (!isConstructorFunction(app) || !isConstructorFunction(httpReceiver)) return null;
|
|
4160
|
-
return {
|
|
4161
|
-
App: app,
|
|
4162
|
-
HTTPReceiver: httpReceiver
|
|
4163
|
-
};
|
|
4164
|
-
}
|
|
4165
|
-
function resolveSlackBoltInterop(params) {
|
|
4166
|
-
const { defaultImport, namespaceImport } = params;
|
|
4167
|
-
const nestedDefault = defaultImport && typeof defaultImport === "object" ? Reflect.get(defaultImport, "default") : void 0;
|
|
4168
|
-
const namespaceDefault = namespaceImport && typeof namespaceImport === "object" ? Reflect.get(namespaceImport, "default") : void 0;
|
|
4169
|
-
const namespaceReceiver = namespaceImport && typeof namespaceImport === "object" ? Reflect.get(namespaceImport, "HTTPReceiver") : void 0;
|
|
4170
|
-
const directModule = resolveSlackBoltModule(defaultImport) ?? resolveSlackBoltModule(nestedDefault) ?? resolveSlackBoltModule(namespaceDefault) ?? resolveSlackBoltModule(namespaceImport);
|
|
4171
|
-
if (directModule) return directModule;
|
|
4172
|
-
if (isConstructorFunction(defaultImport) && isConstructorFunction(namespaceReceiver)) return {
|
|
4173
|
-
App: defaultImport,
|
|
4174
|
-
HTTPReceiver: namespaceReceiver
|
|
4175
|
-
};
|
|
4176
|
-
throw new TypeError("Unable to resolve @slack/bolt App/HTTPReceiver exports");
|
|
4177
|
-
}
|
|
4178
|
-
const { App, HTTPReceiver } = resolveSlackBoltInterop({
|
|
4179
|
-
defaultImport: SlackBolt,
|
|
4180
|
-
namespaceImport: SlackBoltNamespace
|
|
4181
|
-
});
|
|
4182
|
-
const SLACK_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
|
|
4183
|
-
const SLACK_WEBHOOK_BODY_TIMEOUT_MS = 3e4;
|
|
4184
|
-
function parseApiAppIdFromAppToken(raw) {
|
|
4185
|
-
const token = raw?.trim();
|
|
4186
|
-
if (!token) return;
|
|
4187
|
-
return /^xapp-\d-([a-z0-9]+)-/i.exec(token)?.[1]?.toUpperCase();
|
|
4188
|
-
}
|
|
4189
|
-
function publishSlackConnectedStatus(setStatus) {
|
|
4190
|
-
if (!setStatus) return;
|
|
4191
|
-
setStatus({
|
|
4192
|
-
...createConnectedChannelStatusPatch(Date.now()),
|
|
4193
|
-
lastError: null
|
|
4194
|
-
});
|
|
4195
|
-
}
|
|
4196
|
-
function publishSlackDisconnectedStatus(setStatus, error) {
|
|
4197
|
-
if (!setStatus) return;
|
|
4198
|
-
const at = Date.now();
|
|
4199
|
-
const message = error ? formatUnknownError(error) : void 0;
|
|
4200
|
-
setStatus({
|
|
4201
|
-
connected: false,
|
|
4202
|
-
lastDisconnect: message ? {
|
|
4203
|
-
at,
|
|
4204
|
-
error: message
|
|
4205
|
-
} : { at },
|
|
4206
|
-
lastError: message ?? null
|
|
4207
|
-
});
|
|
4208
|
-
}
|
|
4209
|
-
async function monitorSlackProvider(opts = {}) {
|
|
4210
|
-
const cfg = opts.config ?? loadConfig();
|
|
4211
|
-
const runtime = opts.runtime ?? createNonExitingRuntime();
|
|
4212
|
-
let account = resolveSlackAccount({
|
|
4213
|
-
cfg,
|
|
4214
|
-
accountId: opts.accountId
|
|
4215
|
-
});
|
|
4216
|
-
if (!account.enabled) {
|
|
4217
|
-
runtime.log?.(`[${account.accountId}] slack account disabled; monitor startup skipped`);
|
|
4218
|
-
if (opts.abortSignal?.aborted) return;
|
|
4219
|
-
await new Promise((resolve) => {
|
|
4220
|
-
opts.abortSignal?.addEventListener("abort", () => resolve(), { once: true });
|
|
4221
|
-
});
|
|
4222
|
-
return;
|
|
4223
|
-
}
|
|
4224
|
-
const historyLimit = Math.max(0, account.config.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? 50);
|
|
4225
|
-
const sessionCfg = cfg.session;
|
|
4226
|
-
const sessionScope = sessionCfg?.scope ?? "per-sender";
|
|
4227
|
-
const mainKey = normalizeMainKey(sessionCfg?.mainKey);
|
|
4228
|
-
const slackMode = opts.mode ?? account.config.mode ?? "socket";
|
|
4229
|
-
const slackWebhookPath = normalizeSlackWebhookPath(account.config.webhookPath);
|
|
4230
|
-
const signingSecret = normalizeResolvedSecretInputString({
|
|
4231
|
-
value: account.config.signingSecret,
|
|
4232
|
-
path: `channels.slack.accounts.${account.accountId}.signingSecret`
|
|
4233
|
-
});
|
|
4234
|
-
const botToken = resolveSlackBotToken(opts.botToken ?? account.botToken);
|
|
4235
|
-
const appToken = resolveSlackAppToken(opts.appToken ?? account.appToken);
|
|
4236
|
-
if (!botToken || slackMode !== "http" && !appToken) {
|
|
4237
|
-
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).`;
|
|
4238
|
-
throw new Error(missing);
|
|
4239
|
-
}
|
|
4240
|
-
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).`);
|
|
4241
|
-
const slackCfg = account.config;
|
|
4242
|
-
const dmConfig = slackCfg.dm;
|
|
4243
|
-
const dmEnabled = dmConfig?.enabled ?? true;
|
|
4244
|
-
const dmPolicy = slackCfg.dmPolicy ?? dmConfig?.policy ?? "pairing";
|
|
4245
|
-
let allowFrom = slackCfg.allowFrom ?? dmConfig?.allowFrom;
|
|
4246
|
-
const groupDmEnabled = dmConfig?.groupEnabled ?? false;
|
|
4247
|
-
const groupDmChannels = dmConfig?.groupChannels;
|
|
4248
|
-
let channelsConfig = slackCfg.channels;
|
|
4249
|
-
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
|
4250
|
-
const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy({
|
|
4251
|
-
providerConfigPresent: cfg.channels?.slack !== void 0,
|
|
4252
|
-
groupPolicy: slackCfg.groupPolicy,
|
|
4253
|
-
defaultGroupPolicy
|
|
4254
|
-
});
|
|
4255
|
-
warnMissingProviderGroupPolicyFallbackOnce({
|
|
4256
|
-
providerMissingFallbackApplied,
|
|
4257
|
-
providerKey: "slack",
|
|
4258
|
-
accountId: account.accountId,
|
|
4259
|
-
log: (message) => runtime.log?.(warn(message))
|
|
4260
|
-
});
|
|
4261
|
-
const resolveToken = account.userToken || botToken;
|
|
4262
|
-
const useAccessGroups = cfg.commands?.useAccessGroups !== false;
|
|
4263
|
-
const reactionMode = slackCfg.reactionNotifications ?? "own";
|
|
4264
|
-
const reactionAllowlist = slackCfg.reactionAllowlist ?? [];
|
|
4265
|
-
const replyToMode = slackCfg.replyToMode ?? "off";
|
|
4266
|
-
const threadHistoryScope = slackCfg.thread?.historyScope ?? "thread";
|
|
4267
|
-
const threadInheritParent = slackCfg.thread?.inheritParent ?? false;
|
|
4268
|
-
const slashCommand = resolveSlackSlashCommandConfig(opts.slashCommand ?? slackCfg.slashCommand);
|
|
4269
|
-
const textLimit = resolveTextChunkLimit(cfg, "slack", account.accountId);
|
|
4270
|
-
const ackReactionScope = cfg.messages?.ackReactionScope ?? "group-mentions";
|
|
4271
|
-
const typingReaction = slackCfg.typingReaction?.trim() ?? "";
|
|
4272
|
-
const mediaMaxBytes = (opts.mediaMaxMb ?? slackCfg.mediaMaxMb ?? 20) * 1024 * 1024;
|
|
4273
|
-
const removeAckAfterReply = cfg.messages?.removeAckAfterReply ?? false;
|
|
4274
|
-
const receiver = slackMode === "http" ? new HTTPReceiver({
|
|
4275
|
-
signingSecret: signingSecret ?? "",
|
|
4276
|
-
endpoints: slackWebhookPath
|
|
4277
|
-
}) : null;
|
|
4278
|
-
const clientOptions = resolveSlackWebClientOptions();
|
|
4279
|
-
const app = new App(slackMode === "socket" ? {
|
|
4280
|
-
token: botToken,
|
|
4281
|
-
appToken,
|
|
4282
|
-
socketMode: true,
|
|
4283
|
-
clientOptions
|
|
4284
|
-
} : {
|
|
4285
|
-
token: botToken,
|
|
4286
|
-
receiver: receiver ?? void 0,
|
|
4287
|
-
clientOptions
|
|
4288
|
-
});
|
|
4289
|
-
const slackHttpHandler = slackMode === "http" && receiver ? async (req, res) => {
|
|
4290
|
-
const guard = installRequestBodyLimitGuard(req, res, {
|
|
4291
|
-
maxBytes: SLACK_WEBHOOK_MAX_BODY_BYTES,
|
|
4292
|
-
timeoutMs: SLACK_WEBHOOK_BODY_TIMEOUT_MS,
|
|
4293
|
-
responseFormat: "text"
|
|
4294
|
-
});
|
|
4295
|
-
if (guard.isTripped()) return;
|
|
4296
|
-
try {
|
|
4297
|
-
await Promise.resolve(receiver.requestListener(req, res));
|
|
4298
|
-
} catch (err) {
|
|
4299
|
-
if (!guard.isTripped()) throw err;
|
|
4300
|
-
} finally {
|
|
4301
|
-
guard.dispose();
|
|
4302
|
-
}
|
|
4303
|
-
} : null;
|
|
4304
|
-
let unregisterHttpHandler = null;
|
|
4305
|
-
let botUserId = "";
|
|
4306
|
-
let teamId = "";
|
|
4307
|
-
let apiAppId = "";
|
|
4308
|
-
const expectedApiAppIdFromAppToken = parseApiAppIdFromAppToken(appToken);
|
|
4309
|
-
try {
|
|
4310
|
-
const auth = await app.client.auth.test({ token: botToken });
|
|
4311
|
-
botUserId = auth.user_id ?? "";
|
|
4312
|
-
teamId = auth.team_id ?? "";
|
|
4313
|
-
apiAppId = auth.api_app_id ?? "";
|
|
4314
|
-
} catch {}
|
|
4315
|
-
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}`);
|
|
4316
|
-
const ctx = createSlackMonitorContext({
|
|
4317
|
-
cfg,
|
|
4318
|
-
accountId: account.accountId,
|
|
4319
|
-
botToken,
|
|
4320
|
-
app,
|
|
4321
|
-
runtime,
|
|
4322
|
-
botUserId,
|
|
4323
|
-
teamId,
|
|
4324
|
-
apiAppId,
|
|
4325
|
-
historyLimit,
|
|
4326
|
-
sessionScope,
|
|
4327
|
-
mainKey,
|
|
4328
|
-
dmEnabled,
|
|
4329
|
-
dmPolicy,
|
|
4330
|
-
allowFrom,
|
|
4331
|
-
allowNameMatching: isDangerousNameMatchingEnabled(slackCfg),
|
|
4332
|
-
groupDmEnabled,
|
|
4333
|
-
groupDmChannels,
|
|
4334
|
-
defaultRequireMention: slackCfg.requireMention,
|
|
4335
|
-
channelsConfig,
|
|
4336
|
-
groupPolicy,
|
|
4337
|
-
useAccessGroups,
|
|
4338
|
-
reactionMode,
|
|
4339
|
-
reactionAllowlist,
|
|
4340
|
-
replyToMode,
|
|
4341
|
-
threadHistoryScope,
|
|
4342
|
-
threadInheritParent,
|
|
4343
|
-
slashCommand,
|
|
4344
|
-
textLimit,
|
|
4345
|
-
ackReactionScope,
|
|
4346
|
-
typingReaction,
|
|
4347
|
-
mediaMaxBytes,
|
|
4348
|
-
removeAckAfterReply
|
|
4349
|
-
});
|
|
4350
|
-
const trackEvent = opts.setStatus ? () => {
|
|
4351
|
-
opts.setStatus({
|
|
4352
|
-
lastEventAt: Date.now(),
|
|
4353
|
-
lastInboundAt: Date.now()
|
|
4354
|
-
});
|
|
4355
|
-
} : void 0;
|
|
4356
|
-
registerSlackMonitorEvents({
|
|
4357
|
-
ctx,
|
|
4358
|
-
account,
|
|
4359
|
-
handleSlackMessage: createSlackMessageHandler({
|
|
4360
|
-
ctx,
|
|
4361
|
-
account,
|
|
4362
|
-
trackEvent
|
|
4363
|
-
}),
|
|
4364
|
-
trackEvent
|
|
4365
|
-
});
|
|
4366
|
-
await registerSlackMonitorSlashCommands({
|
|
4367
|
-
ctx,
|
|
4368
|
-
account
|
|
4369
|
-
});
|
|
4370
|
-
if (slackMode === "http" && slackHttpHandler) unregisterHttpHandler = registerSlackHttpHandler({
|
|
4371
|
-
path: slackWebhookPath,
|
|
4372
|
-
handler: slackHttpHandler,
|
|
4373
|
-
log: runtime.log,
|
|
4374
|
-
accountId: account.accountId
|
|
4375
|
-
});
|
|
4376
|
-
if (resolveToken) (async () => {
|
|
4377
|
-
if (opts.abortSignal?.aborted) return;
|
|
4378
|
-
if (channelsConfig && Object.keys(channelsConfig).length > 0) try {
|
|
4379
|
-
const entries = Object.keys(channelsConfig).filter((key) => key !== "*");
|
|
4380
|
-
if (entries.length > 0) {
|
|
4381
|
-
const resolved = await resolveSlackChannelAllowlist({
|
|
4382
|
-
token: resolveToken,
|
|
4383
|
-
entries
|
|
4384
|
-
});
|
|
4385
|
-
const nextChannels = { ...channelsConfig };
|
|
4386
|
-
const mapping = [];
|
|
4387
|
-
const unresolved = [];
|
|
4388
|
-
for (const entry of resolved) {
|
|
4389
|
-
const source = channelsConfig?.[entry.input];
|
|
4390
|
-
if (!source) continue;
|
|
4391
|
-
if (!entry.resolved || !entry.id) {
|
|
4392
|
-
unresolved.push(entry.input);
|
|
4393
|
-
continue;
|
|
4394
|
-
}
|
|
4395
|
-
mapping.push(`${entry.input}→${entry.id}${entry.archived ? " (archived)" : ""}`);
|
|
4396
|
-
const existing = nextChannels[entry.id] ?? {};
|
|
4397
|
-
nextChannels[entry.id] = {
|
|
4398
|
-
...source,
|
|
4399
|
-
...existing
|
|
4400
|
-
};
|
|
4401
|
-
}
|
|
4402
|
-
channelsConfig = nextChannels;
|
|
4403
|
-
ctx.channelsConfig = nextChannels;
|
|
4404
|
-
summarizeMapping("slack channels", mapping, unresolved, runtime);
|
|
4405
|
-
}
|
|
4406
|
-
} catch (err) {
|
|
4407
|
-
runtime.log?.(`slack channel resolve failed; using config entries. ${String(err)}`);
|
|
4408
|
-
}
|
|
4409
|
-
const allowEntries = normalizeStringEntries(allowFrom).filter((entry) => entry !== "*");
|
|
4410
|
-
if (allowEntries.length > 0) try {
|
|
4411
|
-
const { mapping, unresolved, additions } = buildAllowlistResolutionSummary(await resolveSlackUserAllowlist({
|
|
4412
|
-
token: resolveToken,
|
|
4413
|
-
entries: allowEntries
|
|
4414
|
-
}), { formatResolved: (entry) => {
|
|
4415
|
-
const note = entry.note ? ` (${entry.note})` : "";
|
|
4416
|
-
return `${entry.input}→${entry.id}${note}`;
|
|
4417
|
-
} });
|
|
4418
|
-
allowFrom = mergeAllowlist({
|
|
4419
|
-
existing: allowFrom,
|
|
4420
|
-
additions
|
|
4421
|
-
});
|
|
4422
|
-
ctx.allowFrom = normalizeAllowList(allowFrom);
|
|
4423
|
-
summarizeMapping("slack users", mapping, unresolved, runtime);
|
|
4424
|
-
} catch (err) {
|
|
4425
|
-
runtime.log?.(`slack user resolve failed; using config entries. ${String(err)}`);
|
|
4426
|
-
}
|
|
4427
|
-
if (channelsConfig && Object.keys(channelsConfig).length > 0) {
|
|
4428
|
-
const userEntries = /* @__PURE__ */ new Set();
|
|
4429
|
-
for (const channel of Object.values(channelsConfig)) addAllowlistUserEntriesFromConfigEntry(userEntries, channel);
|
|
4430
|
-
if (userEntries.size > 0) try {
|
|
4431
|
-
const { resolvedMap, mapping, unresolved } = buildAllowlistResolutionSummary(await resolveSlackUserAllowlist({
|
|
4432
|
-
token: resolveToken,
|
|
4433
|
-
entries: Array.from(userEntries)
|
|
4434
|
-
}));
|
|
4435
|
-
const nextChannels = patchAllowlistUsersInConfigEntries({
|
|
4436
|
-
entries: channelsConfig,
|
|
4437
|
-
resolvedMap
|
|
4438
|
-
});
|
|
4439
|
-
channelsConfig = nextChannels;
|
|
4440
|
-
ctx.channelsConfig = nextChannels;
|
|
4441
|
-
summarizeMapping("slack channel users", mapping, unresolved, runtime);
|
|
4442
|
-
} catch (err) {
|
|
4443
|
-
runtime.log?.(`slack channel user resolve failed; using config entries. ${String(err)}`);
|
|
4444
|
-
}
|
|
4445
|
-
}
|
|
4446
|
-
})();
|
|
4447
|
-
const stopOnAbort = () => {
|
|
4448
|
-
if (opts.abortSignal?.aborted && slackMode === "socket") app.stop();
|
|
4449
|
-
};
|
|
4450
|
-
opts.abortSignal?.addEventListener("abort", stopOnAbort, { once: true });
|
|
4451
|
-
try {
|
|
4452
|
-
if (slackMode === "socket") {
|
|
4453
|
-
let reconnectAttempts = 0;
|
|
4454
|
-
while (!opts.abortSignal?.aborted) {
|
|
4455
|
-
try {
|
|
4456
|
-
await app.start();
|
|
4457
|
-
reconnectAttempts = 0;
|
|
4458
|
-
publishSlackConnectedStatus(opts.setStatus);
|
|
4459
|
-
runtime.log?.("slack socket mode connected");
|
|
4460
|
-
} catch (err) {
|
|
4461
|
-
if (isNonRecoverableSlackAuthError(err)) {
|
|
4462
|
-
runtime.error?.(`slack socket mode failed to start due to non-recoverable auth error — skipping channel (${formatUnknownError(err)})`);
|
|
4463
|
-
throw err;
|
|
4464
|
-
}
|
|
4465
|
-
reconnectAttempts += 1;
|
|
4466
|
-
if (SLACK_SOCKET_RECONNECT_POLICY.maxAttempts > 0 && reconnectAttempts >= SLACK_SOCKET_RECONNECT_POLICY.maxAttempts) throw err;
|
|
4467
|
-
const delayMs = computeBackoff(SLACK_SOCKET_RECONNECT_POLICY, reconnectAttempts);
|
|
4468
|
-
runtime.error?.(`slack socket mode failed to start. retry ${reconnectAttempts}/${SLACK_SOCKET_RECONNECT_POLICY.maxAttempts || "∞"} in ${Math.round(delayMs / 1e3)}s (${formatUnknownError(err)})`);
|
|
4469
|
-
try {
|
|
4470
|
-
await sleepWithAbort(delayMs, opts.abortSignal);
|
|
4471
|
-
} catch {
|
|
4472
|
-
break;
|
|
4473
|
-
}
|
|
4474
|
-
continue;
|
|
4475
|
-
}
|
|
4476
|
-
if (opts.abortSignal?.aborted) break;
|
|
4477
|
-
const disconnect = await waitForSlackSocketDisconnect(app, opts.abortSignal);
|
|
4478
|
-
if (opts.abortSignal?.aborted) break;
|
|
4479
|
-
publishSlackDisconnectedStatus(opts.setStatus, disconnect.error);
|
|
4480
|
-
if (disconnect.error && isNonRecoverableSlackAuthError(disconnect.error)) {
|
|
4481
|
-
runtime.error?.(`slack socket mode disconnected due to non-recoverable auth error — skipping channel (${formatUnknownError(disconnect.error)})`);
|
|
4482
|
-
throw disconnect.error instanceof Error ? disconnect.error : new Error(formatUnknownError(disconnect.error));
|
|
4483
|
-
}
|
|
4484
|
-
reconnectAttempts += 1;
|
|
4485
|
-
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}`);
|
|
4486
|
-
const delayMs = computeBackoff(SLACK_SOCKET_RECONNECT_POLICY, reconnectAttempts);
|
|
4487
|
-
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)})` : ""}`);
|
|
4488
|
-
await app.stop().catch(() => void 0);
|
|
4489
|
-
try {
|
|
4490
|
-
await sleepWithAbort(delayMs, opts.abortSignal);
|
|
4491
|
-
} catch {
|
|
4492
|
-
break;
|
|
4493
|
-
}
|
|
4494
|
-
}
|
|
4495
|
-
} else {
|
|
4496
|
-
runtime.log?.(`slack http mode listening at ${slackWebhookPath}`);
|
|
4497
|
-
if (!opts.abortSignal?.aborted) await new Promise((resolve) => {
|
|
4498
|
-
opts.abortSignal?.addEventListener("abort", () => resolve(), { once: true });
|
|
4499
|
-
});
|
|
4500
|
-
}
|
|
4501
|
-
} finally {
|
|
4502
|
-
opts.abortSignal?.removeEventListener("abort", stopOnAbort);
|
|
4503
|
-
unregisterHttpHandler?.();
|
|
4504
|
-
await app.stop().catch(() => void 0);
|
|
4505
|
-
}
|
|
4506
|
-
}
|
|
4507
|
-
//#endregion
|
|
4508
|
-
//#region extensions/slack/src/probe.ts
|
|
4509
|
-
async function probeSlack(token, timeoutMs = 2500) {
|
|
4510
|
-
const client = createSlackWebClient(token);
|
|
4511
|
-
const start = Date.now();
|
|
4512
|
-
try {
|
|
4513
|
-
const result = await withTimeout(client.auth.test(), timeoutMs);
|
|
4514
|
-
if (!result.ok) return {
|
|
4515
|
-
ok: false,
|
|
4516
|
-
status: 200,
|
|
4517
|
-
error: result.error ?? "unknown",
|
|
4518
|
-
elapsedMs: Date.now() - start
|
|
4519
|
-
};
|
|
4520
|
-
return {
|
|
4521
|
-
ok: true,
|
|
4522
|
-
status: 200,
|
|
4523
|
-
elapsedMs: Date.now() - start,
|
|
4524
|
-
bot: {
|
|
4525
|
-
id: result.user_id,
|
|
4526
|
-
name: result.user
|
|
4527
|
-
},
|
|
4528
|
-
team: {
|
|
4529
|
-
id: result.team_id,
|
|
4530
|
-
name: result.team
|
|
4531
|
-
}
|
|
4532
|
-
};
|
|
4533
|
-
} catch (err) {
|
|
4534
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
4535
|
-
return {
|
|
4536
|
-
ok: false,
|
|
4537
|
-
status: typeof err.status === "number" ? err.status : null,
|
|
4538
|
-
error: message,
|
|
4539
|
-
elapsedMs: Date.now() - start
|
|
4540
|
-
};
|
|
4541
|
-
}
|
|
4542
|
-
}
|
|
4543
|
-
//#endregion
|
|
4544
|
-
//#region src/plugins/runtime/runtime-slack-ops.runtime.ts
|
|
4545
|
-
const runtimeSlackOps = {
|
|
4546
|
-
listDirectoryGroupsLive: listSlackDirectoryGroupsLive,
|
|
4547
|
-
listDirectoryPeersLive: listSlackDirectoryPeersLive,
|
|
4548
|
-
probeSlack,
|
|
4549
|
-
resolveChannelAllowlist: resolveSlackChannelAllowlist,
|
|
4550
|
-
resolveUserAllowlist: resolveSlackUserAllowlist,
|
|
4551
|
-
sendMessageSlack,
|
|
4552
|
-
monitorSlackProvider,
|
|
4553
|
-
handleSlackAction
|
|
4554
|
-
};
|
|
4555
|
-
//#endregion
|
|
4556
|
-
export { runtimeSlackOps };
|