@openclaw/slack 2026.7.2-beta.2 → 2026.7.2-beta.4
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/{account-configured-DzC407uv.js → account-configured-BwfXBo_F.js} +1 -1
- package/dist/account-inspect-api.js +1 -1
- package/dist/{account-inspect-Di7ditqZ.js → account-inspect-jn1xLaVi.js} +9 -2
- package/dist/account-reply-mode-DrUXogvO.js +14 -0
- package/dist/{accounts-BOJJiHSr.js → accounts-BSr252c_.js} +13 -25
- package/dist/accounts.runtime-CjULMLTa.js +2 -0
- package/dist/{action-runtime-Cee2ih0s.js → action-runtime-CFU52n4W.js} +11 -17
- package/dist/action-runtime.runtime-9-17zvKH.js +2 -0
- package/dist/{actions-BZiuSTIq.js → actions-D51foiwR.js} +5 -5
- package/dist/{actions.runtime-DoD0-gxw.js → actions.runtime-BMJwtf5C.js} +1 -1
- package/dist/api.js +15 -15
- package/dist/{approval-handler.runtime-CUPtIM4k.js → approval-handler.runtime-3b4-wxMl.js} +2 -2
- package/dist/{channel-DaIiGUOp.js → channel-Ds8NKvfr.js} +75 -41
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-type-Cfm6_6n1.js → channel-type-DXkUajFP.js} +3 -3
- package/dist/{channel.setup-C-mLz50O.js → channel.setup-DOSTEDfA.js} +8 -5
- package/dist/{config-schema-xPSnK2VK.js → config-schema-B4j4tCOI.js} +236 -30
- package/dist/{directory-config-DRkFujwG.js → directory-config-B08pm5ZT.js} +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-D_yZhjgp.js → directory-live-DpPKzPrn.js} +2 -2
- package/dist/doctor-contract-U6oVntbM.js +173 -0
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{group-policy-85UWiriS.js → group-policy-CdnQzQBI.js} +98 -125
- package/dist/{interactive-replies-BilWUdcI.js → interactive-replies-C3ffk6oN.js} +1 -1
- package/dist/interactive-replies-api.js +1 -1
- package/dist/{message-tool-api-CA9jZrCY.js → message-tool-api-ByVyIVOh.js} +3 -3
- package/dist/message-tool-api.js +1 -1
- package/dist/{monitor-CalGciTs.js → monitor-CnB_WKvF.js} +3 -3
- package/dist/{outbound-adapter-BiLKcnyH.js → outbound-adapter-DuPz2HHt.js} +49 -5
- package/dist/{pipeline.runtime-DofAvTtb.js → pipeline.runtime-5rRYZuX5.js} +490 -345
- package/dist/{policy-BVs4LiSa.js → policy-C31RYSuT.js} +1 -1
- package/dist/probe-CVRuArHA.js +340 -0
- package/dist/{provider-CmeoUiIM.js → provider-htuIJGFD.js} +791 -359
- package/dist/{relay-source-DMmgtvg4.js → relay-source-Df9EaiTF.js} +6 -11
- package/dist/{replies-CJJCp6qz.js → replies-Cgr2Gli4.js} +3 -3
- package/dist/{resolve-channels-Rfts6POK.js → resolve-channels-DodP2TFx.js} +1 -1
- package/dist/{resolve-users-y6wAxR8E.js → resolve-users-M9eF8891.js} +1 -1
- package/dist/{runtime-api-BGDyOt1D.js → runtime-api-D5XYMMsf.js} +3 -2
- package/dist/runtime-api.js +11 -11
- package/dist/{scopes-CuxNQZ6v.js → scopes-CJRpHkDC.js} +1 -2
- package/dist/{secret-contract-CMohXFWS.js → secret-contract-DxmvbxBF.js} +1 -0
- package/dist/secret-contract-api.js +1 -1
- package/dist/{security-audit-CikQhBUY.js → security-audit-Cz6erklv.js} +0 -10
- package/dist/security-contract-api.js +1 -1
- package/dist/{send-DYp8ca-l.js → send-BZ1tPv7-.js} +28 -11
- package/dist/send.runtime-a2pilFor.js +2 -0
- package/dist/setup-core-tPxMuFez.js +600 -0
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-oSd3NX9J.js → setup-surface-wV-S1dE_.js} +58 -32
- package/dist/{shared-D9yz5Otg.js → shared-BfMskaMx.js} +57 -10
- package/dist/{slash-dispatch.runtime-CSja9VuD.js → slash-dispatch.runtime-lfeIsXTX.js} +1 -1
- package/node_modules/@types/node/README.md +1 -1
- package/node_modules/@types/node/fs/promises.d.ts +16 -6
- package/node_modules/@types/node/package.json +2 -2
- package/node_modules/agent-base/README.md +145 -0
- package/node_modules/agent-base/dist/src/index.d.ts +78 -0
- package/node_modules/agent-base/dist/src/index.js +203 -0
- package/node_modules/agent-base/dist/src/index.js.map +1 -0
- package/node_modules/agent-base/dist/src/promisify.d.ts +4 -0
- package/node_modules/agent-base/dist/src/promisify.js +18 -0
- package/node_modules/agent-base/dist/src/promisify.js.map +1 -0
- package/node_modules/agent-base/package.json +64 -0
- package/node_modules/agent-base/src/index.ts +345 -0
- package/node_modules/agent-base/src/promisify.ts +33 -0
- package/node_modules/axios/CHANGELOG.md +197 -1
- package/node_modules/axios/README.md +424 -256
- package/node_modules/axios/dist/axios.js +589 -204
- package/node_modules/axios/dist/axios.min.js +3 -3
- package/node_modules/axios/dist/axios.min.js.map +1 -1
- package/node_modules/axios/dist/browser/axios.cjs +630 -185
- package/node_modules/axios/dist/esm/axios.js +630 -185
- package/node_modules/axios/dist/esm/axios.min.js +2 -2
- package/node_modules/axios/dist/esm/axios.min.js.map +1 -1
- package/node_modules/axios/dist/node/axios.cjs +949 -301
- package/node_modules/axios/index.d.cts +28 -5
- package/node_modules/axios/index.d.ts +24 -3
- package/node_modules/axios/lib/adapters/adapters.js +1 -1
- package/node_modules/axios/lib/adapters/fetch.js +223 -49
- package/node_modules/axios/lib/adapters/http.js +408 -193
- package/node_modules/axios/lib/adapters/xhr.js +3 -1
- package/node_modules/axios/lib/core/Axios.js +4 -2
- package/node_modules/axios/lib/core/AxiosError.js +13 -1
- package/node_modules/axios/lib/core/AxiosHeaders.js +13 -42
- package/node_modules/axios/lib/core/buildFullPath.js +29 -1
- package/node_modules/axios/lib/core/mergeConfig.js +35 -0
- package/node_modules/axios/lib/defaults/transitional.js +2 -0
- package/node_modules/axios/lib/env/data.js +1 -1
- package/node_modules/axios/lib/helpers/AxiosURLSearchParams.js +1 -3
- package/node_modules/axios/lib/helpers/Http2Sessions.js +119 -0
- package/node_modules/axios/lib/helpers/buildURL.js +7 -4
- package/node_modules/axios/lib/helpers/composeSignals.js +48 -47
- package/node_modules/axios/lib/helpers/cookies.js +5 -1
- package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +16 -11
- package/node_modules/axios/lib/helpers/formDataToJSON.js +26 -4
- package/node_modules/axios/lib/helpers/formDataToStream.js +2 -2
- package/node_modules/axios/lib/helpers/fromDataURI.js +20 -5
- package/node_modules/axios/lib/helpers/progressEventReducer.js +3 -0
- package/node_modules/axios/lib/helpers/resolveConfig.js +26 -13
- package/node_modules/axios/lib/helpers/sanitizeHeaderValue.js +60 -0
- package/node_modules/axios/lib/helpers/shouldBypassProxy.js +33 -1
- package/node_modules/axios/lib/helpers/toFormData.js +48 -12
- package/node_modules/axios/lib/helpers/validator.js +1 -1
- package/node_modules/axios/lib/utils.js +105 -19
- package/node_modules/axios/package.json +31 -13
- package/node_modules/https-proxy-agent/README.md +137 -0
- package/node_modules/https-proxy-agent/dist/agent.d.ts +30 -0
- package/node_modules/https-proxy-agent/dist/agent.js +177 -0
- package/node_modules/https-proxy-agent/dist/agent.js.map +1 -0
- package/node_modules/https-proxy-agent/dist/index.d.ts +23 -0
- package/node_modules/https-proxy-agent/dist/index.js +14 -0
- package/node_modules/https-proxy-agent/dist/index.js.map +1 -0
- package/node_modules/https-proxy-agent/dist/parse-proxy-response.d.ts +7 -0
- package/node_modules/https-proxy-agent/dist/parse-proxy-response.js +66 -0
- package/node_modules/https-proxy-agent/dist/parse-proxy-response.js.map +1 -0
- package/node_modules/https-proxy-agent/package.json +56 -0
- package/node_modules/iconv-lite/encodings/sbcs-data.js +2 -0
- package/node_modules/iconv-lite/encodings/utf32.js +10 -3
- package/node_modules/iconv-lite/package.json +2 -2
- package/node_modules/iconv-lite/types/encodings.d.ts +2 -0
- package/node_modules/path-to-regexp/Readme.md +3 -3
- package/node_modules/path-to-regexp/dist/index.js +135 -158
- package/node_modules/path-to-regexp/dist/index.js.map +1 -1
- package/node_modules/path-to-regexp/package.json +2 -2
- package/node_modules/qs/CHANGELOG.md +11 -0
- package/node_modules/qs/dist/qs.js +17 -17
- package/node_modules/qs/lib/parse.js +19 -8
- package/node_modules/qs/lib/utils.js +47 -8
- package/node_modules/qs/package.json +6 -5
- package/node_modules/qs/test/parse.js +230 -0
- package/node_modules/qs/test/utils.js +194 -0
- package/node_modules/typebox/build/compile/validator.d.mts +1 -4
- package/node_modules/typebox/build/guard/guard.d.mts +1 -1
- package/node_modules/typebox/build/guard/guard.mjs +5 -2
- package/node_modules/typebox/build/guard/string.mjs +16 -4
- package/node_modules/typebox/build/schema/engine/_functions.mjs +34 -15
- package/node_modules/typebox/build/type/action/readonly_object.d.mts +5 -0
- package/node_modules/typebox/build/type/action/readonly_object.mjs +8 -0
- package/node_modules/typebox/build/typebox.d.mts +1 -1
- package/node_modules/typebox/build/typebox.mjs +1 -1
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +29 -32
- package/node_modules/ws/lib/receiver.js +15 -32
- package/node_modules/ws/lib/websocket-server.js +4 -4
- package/node_modules/ws/lib/websocket.js +4 -4
- package/node_modules/ws/package.json +5 -1
- package/npm-shrinkwrap.json +54 -27
- package/openclaw.plugin.json +1331 -1424
- package/package.json +92 -14
- package/dist/accounts.runtime-KvftRRek.js +0 -2
- package/dist/action-runtime.runtime-DZfd7IFr.js +0 -2
- package/dist/client-WIPAS3Yr.js +0 -160
- package/dist/config-api-Df9a1B8C.js +0 -3
- package/dist/doctor-contract-DddfkMlF.js +0 -134
- package/dist/probe-CN0ssfLN.js +0 -48
- package/dist/send.runtime-CJLxAt26.js +0 -2
- package/dist/setup-core-CkwVLzFO.js +0 -337
- package/node_modules/axios/dist/axios.js.map +0 -1
- package/node_modules/axios/dist/browser/axios.cjs.map +0 -1
- package/node_modules/axios/dist/esm/axios.js.map +0 -1
- package/node_modules/axios/dist/node/axios.cjs.map +0 -1
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { n as registerSlackHttpHandler, r as normalizeSlackWebhookPath } from "./registry-bbjH7IHX.js";
|
|
2
|
-
import { a as resolveSlackAccount, d as
|
|
3
|
-
import {
|
|
4
|
-
import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-
|
|
5
|
-
import {
|
|
2
|
+
import { a as resolveSlackAccount, d as resolveSlackAppToken, f as resolveSlackBotToken, i as resolveDefaultSlackAccountId, o as resolveSlackAccountAllowFrom, s as resolveSlackAccountDmPolicy, u as formatSlackBotTokenIdentityWarning } from "./accounts-BSr252c_.js";
|
|
3
|
+
import { F as SLACK_TEXT_LIMIT, G as isSlackQuestionActionId, K as decodeSlackQuestionAction, U as isSlackApprovalActionId, W as isSlackCallbackActionId, X as decodeSlackApprovalAction, Y as escapeSlackMrkdwn, a as allowListMatches, c as normalizeSlackAllowOwnerEntry, ft as isSlackApprovalAuthorizedSender, l as normalizeSlackSlug, mt as isSlackExecApprovalAuthorizedSender, nt as isSlackAnyNativeApprovalClientEnabled, o as normalizeAllowList, q as resolveSlackQuestionAction, s as normalizeAllowListLower, u as resolveSlackAllowListMatch, z as truncateSlackText } from "./group-policy-CdnQzQBI.js";
|
|
4
|
+
import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-C3ffk6oN.js";
|
|
5
|
+
import { m as resolveSlackWebClientOptions, o as createSlackStartupAuthClient, r as formatSlackError, u as getSlackListenerUploadCompletionClient } from "./probe-CVRuArHA.js";
|
|
6
6
|
import { n as getSlackRuntime, t as getOptionalSlackRuntime } from "./runtime-JSVZSWAj.js";
|
|
7
|
-
import { i as setSlackDefaultSendIdentity } from "./send-
|
|
8
|
-
import { t as resolveSlackChannelAllowlist } from "./resolve-channels-
|
|
9
|
-
import { t as resolveSlackUserAllowlist } from "./resolve-users-
|
|
10
|
-
import { n as resolveSlackChannelConfig, r as resolveSlackChannelLabel, t as isSlackChannelAllowedByPolicy } from "./policy-
|
|
7
|
+
import { i as setSlackDefaultSendIdentity } from "./send-BZ1tPv7-.js";
|
|
8
|
+
import { t as resolveSlackChannelAllowlist } from "./resolve-channels-DodP2TFx.js";
|
|
9
|
+
import { t as resolveSlackUserAllowlist } from "./resolve-users-M9eF8891.js";
|
|
10
|
+
import { n as resolveSlackChannelConfig, r as resolveSlackChannelLabel, t as isSlackChannelAllowedByPolicy } from "./policy-C31RYSuT.js";
|
|
11
11
|
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
12
12
|
import { asOptionalRecord, isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, normalizeStringEntriesLower, normalizeUniqueTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
13
13
|
import { normalizeAccountId, normalizeMainKey, resolveAgentRoute, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
|
|
14
|
-
import { createChannelMessageReplyPipeline } from "openclaw/plugin-sdk/channel-outbound";
|
|
14
|
+
import { createChannelIngressMonitor, createChannelMessageReplyPipeline } from "openclaw/plugin-sdk/channel-outbound";
|
|
15
15
|
import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
|
|
16
16
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
17
17
|
import { computeBackoff, createNonExitingRuntime, createSubsystemLogger, danger, getChildLogger, logVerbose, shouldLogVerbose, sleepWithAbort, warn } from "openclaw/plugin-sdk/runtime-env";
|
|
@@ -20,12 +20,12 @@ import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plu
|
|
|
20
20
|
import { parseExecApprovalCommandText } from "openclaw/plugin-sdk/approval-reply-runtime";
|
|
21
21
|
import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
|
|
22
22
|
import { addAllowlistUserEntriesFromConfigEntry, buildAllowlistResolutionSummary, formatAllowlistMatchMeta, mergeAllowlist, patchAllowlistUsersInConfigEntries, summarizeMapping } from "openclaw/plugin-sdk/allow-from";
|
|
23
|
-
import { collectErrorGraphCandidates, formatErrorMessage, isApprovalNotFoundError } from "openclaw/plugin-sdk/error-runtime";
|
|
23
|
+
import { collectErrorGraphCandidates, extractErrorCode, formatErrorMessage, isApprovalNotFoundError } from "openclaw/plugin-sdk/error-runtime";
|
|
24
24
|
import { asDateTimestampMs, parseFiniteNumber, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
25
25
|
import { resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
|
|
26
26
|
import { buildUntrustedChannelMetadata } from "openclaw/plugin-sdk/security-runtime";
|
|
27
|
-
import { createDedupeCache
|
|
28
|
-
import { buildPluginBindingResolvedText, parsePluginBindingApprovalCustomId,
|
|
27
|
+
import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
|
|
28
|
+
import { buildPluginBindingResolvedText, parsePluginBindingApprovalCustomId, resolveConversationLabel as resolveConversationLabel$1, resolvePluginConversationBindingApproval, resolveRuntimeConversationBindingRoute, upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
|
|
29
29
|
import { resolveNativeCommandsEnabled, resolveNativeSkillsEnabled } from "openclaw/plugin-sdk/native-command-config-runtime";
|
|
30
30
|
import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
|
|
31
31
|
import { DEFAULT_GROUP_HISTORY_LIMIT } from "openclaw/plugin-sdk/reply-history";
|
|
@@ -35,7 +35,7 @@ import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-na
|
|
|
35
35
|
import { getSessionEntry, readSessionUpdatedAt, resolveChannelResetConfig, resolveSessionKey, resolveStorePath, resolveStorePath as resolveStorePath$1, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
|
|
36
36
|
import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
|
|
37
37
|
import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy as resolveOpenProviderRuntimeGroupPolicy$1, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
38
|
-
import {
|
|
38
|
+
import { loadPreparedModelCatalog, resolveAgentDir, resolveDefaultAgentId, resolveDefaultModelForAgent } from "openclaw/plugin-sdk/agent-runtime";
|
|
39
39
|
import { resolveChannelConfigWrites } from "openclaw/plugin-sdk/channel-config-writes";
|
|
40
40
|
import { mutateConfigFile } from "openclaw/plugin-sdk/config-mutation";
|
|
41
41
|
import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
|
|
@@ -45,6 +45,7 @@ import { requestHeartbeat } from "openclaw/plugin-sdk/heartbeat-runtime";
|
|
|
45
45
|
import { createInteractiveConversationBindingHelpers, dispatchPluginInteractiveHandler } from "openclaw/plugin-sdk/plugin-runtime";
|
|
46
46
|
import { createChannelIngressResolver, defineStableChannelIngressIdentity, readChannelIngressStoreAllowFromForDmPolicy } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
47
47
|
import { createChannelInboundDebouncer, shouldDebounceTextInbound } from "openclaw/plugin-sdk/channel-inbound";
|
|
48
|
+
import { createChannelReplayGuard, runClaimableDedupeClaimLoop } from "openclaw/plugin-sdk/persistent-dedupe";
|
|
48
49
|
import { generateSecureToken } from "openclaw/plugin-sdk/secure-random-runtime";
|
|
49
50
|
//#region extensions/slack/src/monitor/commands.ts
|
|
50
51
|
/**
|
|
@@ -71,6 +72,151 @@ function buildSlackSlashCommandMatcher(name) {
|
|
|
71
72
|
return new RegExp(`^/?${escaped}$`);
|
|
72
73
|
}
|
|
73
74
|
//#endregion
|
|
75
|
+
//#region extensions/slack/src/monitor/agent-view-state.ts
|
|
76
|
+
const SLACK_AGENT_VIEW_STATE_NAMESPACE = "agent-view-workspaces";
|
|
77
|
+
const SLACK_AGENT_VIEW_THREAD_STATE_NAMESPACE = "agent-view-threads";
|
|
78
|
+
const SLACK_AGENT_VIEW_STATE_MAX_ENTRIES = 4096;
|
|
79
|
+
const SLACK_AGENT_VIEW_THREAD_STATE_MAX_ENTRIES = 4096;
|
|
80
|
+
const SLACK_MANAGED_THREAD_CACHE_MAX_ENTRIES = 4096;
|
|
81
|
+
function createSlackAgentViewState(params) {
|
|
82
|
+
let enabled = false;
|
|
83
|
+
let loaded = false;
|
|
84
|
+
let persisted = false;
|
|
85
|
+
let workspaceStore;
|
|
86
|
+
let threadStore;
|
|
87
|
+
let warned = false;
|
|
88
|
+
const managedThreads = /* @__PURE__ */ new Map();
|
|
89
|
+
const warnOnce = (action, error) => {
|
|
90
|
+
if (warned) return;
|
|
91
|
+
warned = true;
|
|
92
|
+
params.warn(action, error);
|
|
93
|
+
};
|
|
94
|
+
const openWorkspaceStore = () => {
|
|
95
|
+
if (workspaceStore) return workspaceStore;
|
|
96
|
+
const runtime = getOptionalSlackRuntime();
|
|
97
|
+
if (!runtime) return;
|
|
98
|
+
try {
|
|
99
|
+
workspaceStore = runtime.state.openKeyedStore({
|
|
100
|
+
namespace: SLACK_AGENT_VIEW_STATE_NAMESPACE,
|
|
101
|
+
maxEntries: SLACK_AGENT_VIEW_STATE_MAX_ENTRIES
|
|
102
|
+
});
|
|
103
|
+
return workspaceStore;
|
|
104
|
+
} catch (error) {
|
|
105
|
+
warnOnce("open", error);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const openThreadStore = () => {
|
|
110
|
+
if (threadStore) return threadStore;
|
|
111
|
+
const runtime = getOptionalSlackRuntime();
|
|
112
|
+
if (!runtime) return;
|
|
113
|
+
try {
|
|
114
|
+
threadStore = runtime.state.openKeyedStore({
|
|
115
|
+
namespace: SLACK_AGENT_VIEW_THREAD_STATE_NAMESPACE,
|
|
116
|
+
maxEntries: SLACK_AGENT_VIEW_THREAD_STATE_MAX_ENTRIES
|
|
117
|
+
});
|
|
118
|
+
return threadStore;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
warnOnce("open", error);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const workspaceStateKey = params.apiAppId ? JSON.stringify([
|
|
125
|
+
"workspace",
|
|
126
|
+
params.accountId,
|
|
127
|
+
params.teamId,
|
|
128
|
+
params.apiAppId
|
|
129
|
+
]) : void 0;
|
|
130
|
+
const record = async () => {
|
|
131
|
+
enabled = true;
|
|
132
|
+
loaded = true;
|
|
133
|
+
if (persisted || !workspaceStateKey) return;
|
|
134
|
+
const openedStore = openWorkspaceStore();
|
|
135
|
+
if (!openedStore) return;
|
|
136
|
+
try {
|
|
137
|
+
await openedStore.register(workspaceStateKey, {
|
|
138
|
+
experience: "agent",
|
|
139
|
+
observedAt: Date.now()
|
|
140
|
+
});
|
|
141
|
+
persisted = true;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
warnOnce("persist", error);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const isEnabled = async () => {
|
|
147
|
+
if (enabled) return true;
|
|
148
|
+
if (loaded) return false;
|
|
149
|
+
if (!workspaceStateKey) {
|
|
150
|
+
loaded = true;
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
const openedStore = openWorkspaceStore();
|
|
154
|
+
if (!openedStore) return false;
|
|
155
|
+
try {
|
|
156
|
+
const stored = await openedStore.lookup(workspaceStateKey);
|
|
157
|
+
loaded = true;
|
|
158
|
+
enabled = stored?.experience === "agent";
|
|
159
|
+
persisted = enabled;
|
|
160
|
+
return enabled;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
warnOnce("load", error);
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
const managedThreadKey = (channelId, threadTs) => JSON.stringify([channelId, threadTs]);
|
|
167
|
+
const managedThreadStateKey = (channelId, threadTs) => params.apiAppId ? JSON.stringify([
|
|
168
|
+
"thread",
|
|
169
|
+
params.accountId,
|
|
170
|
+
params.teamId,
|
|
171
|
+
params.apiAppId,
|
|
172
|
+
channelId,
|
|
173
|
+
threadTs
|
|
174
|
+
]) : void 0;
|
|
175
|
+
const rememberManagedThread = (key) => {
|
|
176
|
+
managedThreads.delete(key);
|
|
177
|
+
managedThreads.set(key, true);
|
|
178
|
+
if (managedThreads.size <= SLACK_MANAGED_THREAD_CACHE_MAX_ENTRIES) return;
|
|
179
|
+
const oldestKey = managedThreads.keys().next().value;
|
|
180
|
+
if (oldestKey !== void 0) managedThreads.delete(oldestKey);
|
|
181
|
+
};
|
|
182
|
+
const recordManagedThread = async (channelId, threadTs) => {
|
|
183
|
+
const key = managedThreadKey(channelId, threadTs);
|
|
184
|
+
rememberManagedThread(key);
|
|
185
|
+
const stateKey = managedThreadStateKey(channelId, threadTs);
|
|
186
|
+
const openedStore = stateKey ? openThreadStore() : void 0;
|
|
187
|
+
if (!openedStore || !stateKey) return;
|
|
188
|
+
try {
|
|
189
|
+
await openedStore.register(stateKey, {
|
|
190
|
+
experience: "managed-thread",
|
|
191
|
+
observedAt: Date.now()
|
|
192
|
+
});
|
|
193
|
+
} catch (error) {
|
|
194
|
+
warnOnce("persist", error);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const isManagedThread = async (channelId, threadTs) => {
|
|
198
|
+
const key = managedThreadKey(channelId, threadTs);
|
|
199
|
+
if (managedThreads.has(key)) return true;
|
|
200
|
+
const stateKey = managedThreadStateKey(channelId, threadTs);
|
|
201
|
+
const openedStore = stateKey ? openThreadStore() : void 0;
|
|
202
|
+
if (!openedStore || !stateKey) return false;
|
|
203
|
+
try {
|
|
204
|
+
const found = (await openedStore.lookup(stateKey))?.experience === "managed-thread";
|
|
205
|
+
if (found) rememberManagedThread(key);
|
|
206
|
+
return found;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
warnOnce("load", error);
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
return {
|
|
213
|
+
isEnabled,
|
|
214
|
+
isManagedThread,
|
|
215
|
+
record,
|
|
216
|
+
recordManagedThread
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
//#endregion
|
|
74
220
|
//#region extensions/slack/src/monitor/channel-type.ts
|
|
75
221
|
function inferSlackChannelType(channelId) {
|
|
76
222
|
const trimmed = channelId?.trim();
|
|
@@ -110,6 +256,42 @@ function writeLruMapEntry(cache, cacheKey, entry, maxEntries) {
|
|
|
110
256
|
pruneMapToMaxSize(cache, maxEntries);
|
|
111
257
|
}
|
|
112
258
|
//#endregion
|
|
259
|
+
//#region extensions/slack/src/monitor/suggested-prompts.ts
|
|
260
|
+
const DEFAULT_SLACK_SUGGESTED_PROMPTS = [
|
|
261
|
+
{
|
|
262
|
+
title: "What can you do?",
|
|
263
|
+
message: "What can you help me with?"
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
title: "Summarize this channel",
|
|
267
|
+
message: "Summarize the recent activity in this channel."
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
title: "Draft a reply",
|
|
271
|
+
message: "Help me draft a reply."
|
|
272
|
+
}
|
|
273
|
+
];
|
|
274
|
+
async function updateSlackSuggestedPrompts(params) {
|
|
275
|
+
const prompts = params.prompts.map((prompt) => ({
|
|
276
|
+
title: prompt.title.trim(),
|
|
277
|
+
message: prompt.message.trim()
|
|
278
|
+
})).filter((prompt) => prompt.title && prompt.message).slice(0, 4);
|
|
279
|
+
if (prompts.length === 0) return false;
|
|
280
|
+
try {
|
|
281
|
+
await params.client.assistant.threads.setSuggestedPrompts({
|
|
282
|
+
token: params.botToken,
|
|
283
|
+
channel_id: params.channelId,
|
|
284
|
+
...params.threadTs ? { thread_ts: params.threadTs } : {},
|
|
285
|
+
...params.title?.trim() ? { title: params.title.trim() } : {},
|
|
286
|
+
prompts
|
|
287
|
+
});
|
|
288
|
+
return true;
|
|
289
|
+
} catch (error) {
|
|
290
|
+
logVerbose(`slack suggested prompts update failed for channel ${params.channelId}: ${formatSlackError(error)}`);
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
//#endregion
|
|
113
295
|
//#region extensions/slack/src/monitor/context.ts
|
|
114
296
|
const SLACK_ASSISTANT_THREAD_CONTEXT_METADATA_EVENT = "assistant_thread_context";
|
|
115
297
|
const SLACK_CHANNEL_CACHE_MAX_ENTRIES = 1024;
|
|
@@ -150,16 +332,18 @@ function createSlackMonitorContext(params) {
|
|
|
150
332
|
const logger = getChildLogger({ module: "slack-auto-reply" });
|
|
151
333
|
const channelCache = /* @__PURE__ */ new Map();
|
|
152
334
|
const userCache = /* @__PURE__ */ new Map();
|
|
153
|
-
const seenMessages = createDedupeCache({
|
|
154
|
-
ttlMs: 6e4,
|
|
155
|
-
maxSize: 500
|
|
156
|
-
});
|
|
157
335
|
const channelDenialWarnings = createDedupeCache({
|
|
158
336
|
ttlMs: SLACK_CHANNEL_DENIAL_WARNING_TTL_MS,
|
|
159
337
|
maxSize: SLACK_CHANNEL_DENIAL_WARNING_MAX_ENTRIES
|
|
160
338
|
});
|
|
161
339
|
const assistantThreadContexts = /* @__PURE__ */ new Map();
|
|
162
340
|
let lastAssistantContextCleanupAt = Date.now();
|
|
341
|
+
const agentViewState = createSlackAgentViewState({
|
|
342
|
+
accountId: params.accountId,
|
|
343
|
+
teamId: params.teamId,
|
|
344
|
+
apiAppId: params.apiAppId,
|
|
345
|
+
warn: (action, error) => logger.warn({ error: formatSlackError(error) }, `Slack Agent View state failed to ${action}`)
|
|
346
|
+
});
|
|
163
347
|
const allowFrom = normalizeAllowList(params.allowFrom);
|
|
164
348
|
const groupDmChannels = normalizeAllowList(params.groupDmChannels);
|
|
165
349
|
const groupDmChannelsLower = new Set(normalizeAllowListLower(groupDmChannels).map((entry) => entry.replace(/^channel:/, "")));
|
|
@@ -187,14 +371,6 @@ function createSlackMonitorContext(params) {
|
|
|
187
371
|
const id = normalizeOptionalString(channelId);
|
|
188
372
|
return id ? readLruMapEntry(channelCache, scopedKey(id, eventScope))?.info.type : void 0;
|
|
189
373
|
};
|
|
190
|
-
const markMessageSeen = (channelId, ts, eventScope) => {
|
|
191
|
-
if (!channelId || !ts) return false;
|
|
192
|
-
return seenMessages.check(scopedKey(`${channelId}:${ts}`, eventScope));
|
|
193
|
-
};
|
|
194
|
-
const releaseSeenMessage = (channelId, ts, eventScope) => {
|
|
195
|
-
if (!channelId || !ts) return;
|
|
196
|
-
seenMessages.delete(scopedKey(`${channelId}:${ts}`, eventScope));
|
|
197
|
-
};
|
|
198
374
|
const assistantContextKey = (channelId, threadTs, eventScope) => scopedKey(`${channelId}:${threadTs}`, eventScope);
|
|
199
375
|
const cleanupAssistantThreadContexts = () => {
|
|
200
376
|
const now = Date.now();
|
|
@@ -342,26 +518,11 @@ function createSlackMonitorContext(params) {
|
|
|
342
518
|
logVerbose(`slack status update failed for channel ${p.channelId}: ${formatSlackError(err)}`);
|
|
343
519
|
}
|
|
344
520
|
};
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
if (prompts.length === 0) return false;
|
|
351
|
-
try {
|
|
352
|
-
await params.app.client.assistant.threads.setSuggestedPrompts({
|
|
353
|
-
token: params.botToken,
|
|
354
|
-
channel_id: p.channelId,
|
|
355
|
-
thread_ts: p.threadTs,
|
|
356
|
-
...p.title?.trim() ? { title: p.title.trim() } : {},
|
|
357
|
-
prompts
|
|
358
|
-
});
|
|
359
|
-
return true;
|
|
360
|
-
} catch (err) {
|
|
361
|
-
logVerbose(`slack suggested prompts update failed for channel ${p.channelId}: ${formatSlackError(err)}`);
|
|
362
|
-
return false;
|
|
363
|
-
}
|
|
364
|
-
};
|
|
521
|
+
const setSlackSuggestedPrompts = (input) => updateSlackSuggestedPrompts({
|
|
522
|
+
...input,
|
|
523
|
+
botToken: params.botToken,
|
|
524
|
+
client: params.app.client
|
|
525
|
+
});
|
|
365
526
|
const isChannelAllowed = (p) => {
|
|
366
527
|
const channelType = normalizeSlackChannelType(p.channelType, p.channelId);
|
|
367
528
|
const isDirectMessage = channelType === "im";
|
|
@@ -472,16 +633,12 @@ function createSlackMonitorContext(params) {
|
|
|
472
633
|
replyToMode: params.replyToMode,
|
|
473
634
|
threadHistoryScope: params.threadHistoryScope,
|
|
474
635
|
threadInheritParent: params.threadInheritParent,
|
|
475
|
-
threadRequireExplicitMention: params.threadRequireExplicitMention,
|
|
476
636
|
slashCommand: params.slashCommand,
|
|
477
637
|
textLimit: params.textLimit,
|
|
478
638
|
ackReactionScope: params.ackReactionScope,
|
|
479
639
|
typingReaction: params.typingReaction,
|
|
480
640
|
mediaMaxBytes: params.mediaMaxBytes,
|
|
481
|
-
removeAckAfterReply: params.removeAckAfterReply,
|
|
482
641
|
logger,
|
|
483
|
-
markMessageSeen,
|
|
484
|
-
releaseSeenMessage,
|
|
485
642
|
shouldDropMismatchedSlackEvent,
|
|
486
643
|
resolveSlackSystemEventSessionKey,
|
|
487
644
|
isChannelAllowed,
|
|
@@ -492,7 +649,11 @@ function createSlackMonitorContext(params) {
|
|
|
492
649
|
setSlackThreadStatus,
|
|
493
650
|
getSlackAssistantThreadContext,
|
|
494
651
|
saveSlackAssistantThreadContext,
|
|
495
|
-
|
|
652
|
+
setSlackSuggestedPrompts,
|
|
653
|
+
recordSlackAgentView: agentViewState.record,
|
|
654
|
+
isSlackAgentView: agentViewState.isEnabled,
|
|
655
|
+
recordSlackManagedViewThread: agentViewState.recordManagedThread,
|
|
656
|
+
isSlackManagedViewThread: agentViewState.isManagedThread
|
|
496
657
|
};
|
|
497
658
|
}
|
|
498
659
|
//#endregion
|
|
@@ -535,8 +696,8 @@ function assertEnterpriseSlackPolicyConfig(params) {
|
|
|
535
696
|
predicate: isStableSlackAllowlistUserEntry
|
|
536
697
|
});
|
|
537
698
|
assertStableEntries({
|
|
538
|
-
values: config.
|
|
539
|
-
path: `channels.slack.accounts.${accountId}.
|
|
699
|
+
values: config.allowFrom,
|
|
700
|
+
path: `channels.slack.accounts.${accountId}.allowFrom`,
|
|
540
701
|
predicate: isStableSlackAllowlistUserEntry
|
|
541
702
|
});
|
|
542
703
|
assertStableEntries({
|
|
@@ -625,21 +786,21 @@ function resolveSlackIdentityHealth(params) {
|
|
|
625
786
|
};
|
|
626
787
|
}
|
|
627
788
|
//#endregion
|
|
789
|
+
//#region extensions/slack/src/monitor/events/agent.ts
|
|
790
|
+
function registerSlackAgentEvents(params) {
|
|
791
|
+
const { ctx, trackEvent } = params;
|
|
792
|
+
ctx.app.event("app_context_changed", async ({ body }) => {
|
|
793
|
+
try {
|
|
794
|
+
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
|
795
|
+
trackEvent?.();
|
|
796
|
+
await ctx.recordSlackAgentView();
|
|
797
|
+
} catch (error) {
|
|
798
|
+
ctx.runtime.error?.(danger(`slack app_context_changed handler failed: ${formatErrorMessage(error)}`));
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
//#endregion
|
|
628
803
|
//#region extensions/slack/src/monitor/events/assistant.ts
|
|
629
|
-
const DEFAULT_ASSISTANT_PROMPTS = [
|
|
630
|
-
{
|
|
631
|
-
title: "What can you do?",
|
|
632
|
-
message: "What can you help me with?"
|
|
633
|
-
},
|
|
634
|
-
{
|
|
635
|
-
title: "Summarize this channel",
|
|
636
|
-
message: "Summarize the recent activity in this channel."
|
|
637
|
-
},
|
|
638
|
-
{
|
|
639
|
-
title: "Draft a reply",
|
|
640
|
-
message: "Help me draft a reply."
|
|
641
|
-
}
|
|
642
|
-
];
|
|
643
804
|
function normalizeAssistantThread(event, getPrevious) {
|
|
644
805
|
const thread = event.assistant_thread;
|
|
645
806
|
if (!thread) return null;
|
|
@@ -701,11 +862,11 @@ function registerSlackAssistantEvents(params) {
|
|
|
701
862
|
return;
|
|
702
863
|
}
|
|
703
864
|
ctx.saveSlackAssistantThreadContext(assistantThread);
|
|
704
|
-
await ctx.
|
|
865
|
+
await ctx.setSlackSuggestedPrompts({
|
|
705
866
|
channelId: assistantThread.assistantChannelId,
|
|
706
867
|
threadTs: assistantThread.threadTs,
|
|
707
868
|
title: "Try asking",
|
|
708
|
-
prompts:
|
|
869
|
+
prompts: DEFAULT_SLACK_SUGGESTED_PROMPTS
|
|
709
870
|
});
|
|
710
871
|
} catch (err) {
|
|
711
872
|
ctx.runtime.error?.(danger(`slack assistant_thread_started handler failed: ${formatErrorMessage(err)}`));
|
|
@@ -945,7 +1106,16 @@ function registerSlackHomeEvents(params) {
|
|
|
945
1106
|
if (ctx.shouldDropMismatchedSlackEvent(body)) return;
|
|
946
1107
|
trackEvent?.();
|
|
947
1108
|
const payload = event;
|
|
948
|
-
if (!payload.user
|
|
1109
|
+
if (!payload.user) return;
|
|
1110
|
+
if (payload.tab === "messages") {
|
|
1111
|
+
if (!payload.channel) return;
|
|
1112
|
+
if (await ctx.setSlackSuggestedPrompts({
|
|
1113
|
+
channelId: payload.channel,
|
|
1114
|
+
title: "Try asking",
|
|
1115
|
+
prompts: DEFAULT_SLACK_SUGGESTED_PROMPTS
|
|
1116
|
+
})) await ctx.recordSlackAgentView();
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
949
1119
|
await ctx.app.client.views.publish({
|
|
950
1120
|
token: ctx.botToken,
|
|
951
1121
|
user_id: payload.user,
|
|
@@ -1927,6 +2097,26 @@ async function handleSlackBlockAction(params) {
|
|
|
1927
2097
|
});
|
|
1928
2098
|
return;
|
|
1929
2099
|
}
|
|
2100
|
+
if (isSlackQuestionActionId(parsed.actionId)) {
|
|
2101
|
+
const question = decodeSlackQuestionAction(parsed.actionSummary.value);
|
|
2102
|
+
if (!question) {
|
|
2103
|
+
await respondEphemeral(respond, "This question action is invalid or expired.");
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
if (!(await authorizeSlackBlockAction({
|
|
2107
|
+
ctx: params.ctx,
|
|
2108
|
+
parsed,
|
|
2109
|
+
respond
|
|
2110
|
+
})).allowed) return;
|
|
2111
|
+
await resolveSlackQuestionAction({
|
|
2112
|
+
action: question,
|
|
2113
|
+
cfg: params.ctx.cfg,
|
|
2114
|
+
accountId: params.ctx.accountId,
|
|
2115
|
+
userId: parsed.userId,
|
|
2116
|
+
respond: async (text) => await respondEphemeral(respond, text)
|
|
2117
|
+
});
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
1930
2120
|
const pluginInteractionData = buildSlackPluginInteractionData({
|
|
1931
2121
|
actionId: parsed.actionId,
|
|
1932
2122
|
summary: parsed.actionSummary
|
|
@@ -2649,6 +2839,334 @@ function resolveSlackEventScope(params) {
|
|
|
2649
2839
|
};
|
|
2650
2840
|
}
|
|
2651
2841
|
//#endregion
|
|
2842
|
+
//#region extensions/slack/src/monitor/reconnect-policy.ts
|
|
2843
|
+
const SLACK_AUTH_ERROR_RE = /account_inactive|invalid_auth|token_revoked|token_expired|not_authed|org_login_required|team_access_not_granted|user_removed_from_team|team_disabled|missing_scope|cannot_find_service|invalid_token/i;
|
|
2844
|
+
const NO_ERROR_DETAIL = "no error detail";
|
|
2845
|
+
const SLACK_SOCKET_RECONNECT_POLICY = {
|
|
2846
|
+
initialMs: 2e3,
|
|
2847
|
+
maxMs: 3e4,
|
|
2848
|
+
factor: 1.8,
|
|
2849
|
+
jitter: .25
|
|
2850
|
+
};
|
|
2851
|
+
const SLACK_SOCKET_SHARED_CONNECTION_DOCS_URL = "https://docs.slack.dev/apis/events-api/using-socket-mode#using-multiple-connections";
|
|
2852
|
+
const SLACK_SOCKET_HELLO_MARKER = Buffer.from("\"hello\"");
|
|
2853
|
+
function getSocketEmitter(app) {
|
|
2854
|
+
const receiver = app.receiver;
|
|
2855
|
+
const client = receiver && typeof receiver === "object" ? receiver.client : void 0;
|
|
2856
|
+
if (!client || typeof client !== "object") return null;
|
|
2857
|
+
const on = client.on;
|
|
2858
|
+
const off = client.off;
|
|
2859
|
+
if (typeof on !== "function" || typeof off !== "function") return null;
|
|
2860
|
+
return {
|
|
2861
|
+
on: (event, listener) => on.call(client, event, listener),
|
|
2862
|
+
off: (event, listener) => off.call(client, event, listener)
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2865
|
+
function isBufferArray(value) {
|
|
2866
|
+
return Array.isArray(value) && value.every((entry) => Buffer.isBuffer(entry));
|
|
2867
|
+
}
|
|
2868
|
+
function resolveSlackSocketModeConnectionCount(message) {
|
|
2869
|
+
const buffer = Buffer.isBuffer(message) ? message : message instanceof ArrayBuffer ? Buffer.from(message) : isBufferArray(message) ? Buffer.concat(message) : void 0;
|
|
2870
|
+
if (!buffer?.includes(SLACK_SOCKET_HELLO_MARKER)) return;
|
|
2871
|
+
let payload;
|
|
2872
|
+
try {
|
|
2873
|
+
payload = JSON.parse(buffer.toString("utf8"));
|
|
2874
|
+
} catch {
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
const count = isRecord(payload) && payload.type === "hello" ? payload.num_connections : void 0;
|
|
2878
|
+
return typeof count === "number" && Number.isSafeInteger(count) && count >= 0 ? count : void 0;
|
|
2879
|
+
}
|
|
2880
|
+
function formatSlackSocketModeSharedConnectionWarning(activeConnections) {
|
|
2881
|
+
return [
|
|
2882
|
+
`slack socket mode reports ${activeConnections} active connections for this Slack app`,
|
|
2883
|
+
"Slack may deliver each event to any one connection",
|
|
2884
|
+
"ensure every OpenClaw gateway sharing this app has equivalent routing and authorization, or use a separate Slack app per gateway, one relay ingress, or HTTP Request URLs behind a load balancer",
|
|
2885
|
+
`See ${SLACK_SOCKET_SHARED_CONNECTION_DOCS_URL}`
|
|
2886
|
+
].join("; ");
|
|
2887
|
+
}
|
|
2888
|
+
function registerSlackSocketModeConnectionDiagnostics(params) {
|
|
2889
|
+
const emitter = getSocketEmitter(params.app);
|
|
2890
|
+
if (!emitter) return () => {};
|
|
2891
|
+
let hasWarned = false;
|
|
2892
|
+
const listener = (message, isBinary) => {
|
|
2893
|
+
if (isBinary === true || hasWarned) return;
|
|
2894
|
+
const activeConnections = resolveSlackSocketModeConnectionCount(message);
|
|
2895
|
+
if (activeConnections === void 0 || activeConnections <= 1) return;
|
|
2896
|
+
hasWarned = true;
|
|
2897
|
+
params.onSharedConnection(activeConnections);
|
|
2898
|
+
};
|
|
2899
|
+
emitter.on("ws_message", listener);
|
|
2900
|
+
return () => {
|
|
2901
|
+
emitter.off("ws_message", listener);
|
|
2902
|
+
};
|
|
2903
|
+
}
|
|
2904
|
+
function waitForSlackSocketDisconnect(app, abortSignal) {
|
|
2905
|
+
return new Promise((resolve) => {
|
|
2906
|
+
const emitter = getSocketEmitter(app);
|
|
2907
|
+
if (!emitter) {
|
|
2908
|
+
abortSignal?.addEventListener("abort", () => resolve({ event: "disconnect" }), { once: true });
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2911
|
+
const disconnectListener = () => resolveOnce({ event: "disconnect" });
|
|
2912
|
+
const startFailListener = (error) => resolveOnce({
|
|
2913
|
+
event: "unable_to_socket_mode_start",
|
|
2914
|
+
error
|
|
2915
|
+
});
|
|
2916
|
+
const errorListener = (error) => resolveOnce({
|
|
2917
|
+
event: "error",
|
|
2918
|
+
error
|
|
2919
|
+
});
|
|
2920
|
+
const abortListener = () => resolveOnce({ event: "disconnect" });
|
|
2921
|
+
const cleanup = () => {
|
|
2922
|
+
emitter.off("disconnected", disconnectListener);
|
|
2923
|
+
emitter.off("unable_to_socket_mode_start", startFailListener);
|
|
2924
|
+
emitter.off("error", errorListener);
|
|
2925
|
+
abortSignal?.removeEventListener("abort", abortListener);
|
|
2926
|
+
};
|
|
2927
|
+
const resolveOnce = (value) => {
|
|
2928
|
+
cleanup();
|
|
2929
|
+
resolve(value);
|
|
2930
|
+
};
|
|
2931
|
+
emitter.on("disconnected", disconnectListener);
|
|
2932
|
+
emitter.on("unable_to_socket_mode_start", startFailListener);
|
|
2933
|
+
emitter.on("error", errorListener);
|
|
2934
|
+
abortSignal?.addEventListener("abort", abortListener, { once: true });
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2937
|
+
/**
|
|
2938
|
+
* Detect permanent Slack account and credential failures.
|
|
2939
|
+
* Transient request and HTTP failures stay in OpenClaw's reconnect loop.
|
|
2940
|
+
*/
|
|
2941
|
+
function isNonRecoverableSlackAuthError(error) {
|
|
2942
|
+
return SLACK_AUTH_ERROR_RE.test(formatUnknownError(error, ""));
|
|
2943
|
+
}
|
|
2944
|
+
function formatUnknownError(error, fallback = NO_ERROR_DETAIL) {
|
|
2945
|
+
return formatSlackError(error, fallback);
|
|
2946
|
+
}
|
|
2947
|
+
//#endregion
|
|
2948
|
+
//#region extensions/slack/src/monitor/ingress.ts
|
|
2949
|
+
const SLACK_INGRESS_PAYLOAD_VERSION = 1;
|
|
2950
|
+
const SLACK_INGRESS_POLL_INTERVAL_MS = 1e3;
|
|
2951
|
+
const SLACK_INGRESS_PRUNE_INTERVAL_MS = 3600 * 1e3;
|
|
2952
|
+
const SLACK_INGRESS_COMPLETED_TTL_MS = 720 * 60 * 60 * 1e3;
|
|
2953
|
+
const SLACK_INGRESS_COMPLETED_MAX_ENTRIES = 2e4;
|
|
2954
|
+
const SLACK_INGRESS_FAILED_TTL_MS = 720 * 60 * 60 * 1e3;
|
|
2955
|
+
const SLACK_INGRESS_FAILED_MAX_ENTRIES = 2e4;
|
|
2956
|
+
const SLACK_BOLT_AUTHORIZATION_ERROR = "slack_bolt_authorization_error";
|
|
2957
|
+
const SLACK_INGRESS_LIFECYCLE_CONTEXT_KEY = "openclawIngressLifecycle";
|
|
2958
|
+
/** Logical message identity: mirrors the retired guard key (team:channel:ts). */
|
|
2959
|
+
function resolveSlackRelayIngressEventId(event) {
|
|
2960
|
+
const ts = event.message.ts?.trim();
|
|
2961
|
+
if (!event.message.channel?.trim() || !ts) return `relay:${event.deliveryId}`;
|
|
2962
|
+
const team = event.message.team?.trim();
|
|
2963
|
+
return `message:${team ? `${team}:` : ""}${event.message.channel.trim()}:${ts}`;
|
|
2964
|
+
}
|
|
2965
|
+
var SlackIngressPayloadError = class extends Error {
|
|
2966
|
+
constructor(message) {
|
|
2967
|
+
super(message);
|
|
2968
|
+
this.name = "SlackIngressPayloadError";
|
|
2969
|
+
}
|
|
2970
|
+
};
|
|
2971
|
+
function resolveSlackEventId(body) {
|
|
2972
|
+
const eventId = asOptionalRecord(body)?.event_id;
|
|
2973
|
+
return typeof eventId === "string" && eventId.trim() ? eventId.trim() : null;
|
|
2974
|
+
}
|
|
2975
|
+
function resolveSlackIngressLane(body, eventId) {
|
|
2976
|
+
const envelope = asOptionalRecord(body);
|
|
2977
|
+
const event = asOptionalRecord(envelope?.event);
|
|
2978
|
+
const item = asOptionalRecord(event?.item);
|
|
2979
|
+
const assistantThread = asOptionalRecord(event?.assistant_thread);
|
|
2980
|
+
const team = asOptionalRecord(envelope?.team);
|
|
2981
|
+
const teamId = [
|
|
2982
|
+
envelope?.team_id,
|
|
2983
|
+
team?.id,
|
|
2984
|
+
event?.team
|
|
2985
|
+
].find((value) => typeof value === "string" && value.trim())?.toString().trim() || "workspace";
|
|
2986
|
+
const channelId = [
|
|
2987
|
+
event?.channel,
|
|
2988
|
+
event?.channel_id,
|
|
2989
|
+
item?.channel,
|
|
2990
|
+
assistantThread?.channel_id
|
|
2991
|
+
].find((value) => typeof value === "string" && value.trim())?.toString().trim();
|
|
2992
|
+
if (channelId) return `team:${teamId}:conversation:${channelId}`;
|
|
2993
|
+
const userId = [event?.user, event?.user_id].find((value) => typeof value === "string" && value.trim())?.toString().trim();
|
|
2994
|
+
return userId ? `team:${teamId}:user:${userId}` : `event:${eventId}`;
|
|
2995
|
+
}
|
|
2996
|
+
function isSlackEventCallback(body) {
|
|
2997
|
+
return asOptionalRecord(body)?.type === "event_callback";
|
|
2998
|
+
}
|
|
2999
|
+
function decodeSlackIngressPayload(payload, eventId) {
|
|
3000
|
+
if (payload.kind === "relay") {
|
|
3001
|
+
if (!asOptionalRecord(payload.message)) throw new SlackIngressPayloadError(`Slack relay ingress payload ${eventId} was invalid.`);
|
|
3002
|
+
return {
|
|
3003
|
+
version: payload.version,
|
|
3004
|
+
body: payload
|
|
3005
|
+
};
|
|
3006
|
+
}
|
|
3007
|
+
if (!asOptionalRecord(payload.body) || resolveSlackEventId(payload.body) !== eventId) throw new SlackIngressPayloadError(`Slack ingress payload ${eventId} was invalid.`);
|
|
3008
|
+
return {
|
|
3009
|
+
version: payload.version,
|
|
3010
|
+
body: payload
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
3013
|
+
function inspectSlackIngress(raw) {
|
|
3014
|
+
if (raw.kind === "relay") {
|
|
3015
|
+
const eventId = resolveSlackRelayIngressEventId({
|
|
3016
|
+
deliveryId: raw.deliveryId,
|
|
3017
|
+
message: raw.message
|
|
3018
|
+
});
|
|
3019
|
+
return {
|
|
3020
|
+
eventId,
|
|
3021
|
+
laneKey: resolveSlackIngressLane({ event: raw.message }, eventId)
|
|
3022
|
+
};
|
|
3023
|
+
}
|
|
3024
|
+
const eventId = resolveSlackEventId(raw.body);
|
|
3025
|
+
if (!eventId) throw new SlackIngressPayloadError("Slack Events API envelope missing event_id.");
|
|
3026
|
+
return {
|
|
3027
|
+
eventId,
|
|
3028
|
+
laneKey: resolveSlackIngressLane(raw.body, eventId)
|
|
3029
|
+
};
|
|
3030
|
+
}
|
|
3031
|
+
function resolveSlackIngressNonRetryableFailure(error) {
|
|
3032
|
+
for (const candidate of collectErrorGraphCandidates(error, (current) => [
|
|
3033
|
+
current.cause,
|
|
3034
|
+
current.error,
|
|
3035
|
+
current.original
|
|
3036
|
+
])) {
|
|
3037
|
+
if (candidate instanceof SlackIngressPayloadError || candidate instanceof SyntaxError) return {
|
|
3038
|
+
reason: "invalid-event",
|
|
3039
|
+
message: formatErrorMessage(candidate)
|
|
3040
|
+
};
|
|
3041
|
+
if (extractErrorCode(candidate) === SLACK_BOLT_AUTHORIZATION_ERROR || isNonRecoverableSlackAuthError(candidate)) return {
|
|
3042
|
+
reason: "slack-auth",
|
|
3043
|
+
message: formatErrorMessage(candidate)
|
|
3044
|
+
};
|
|
3045
|
+
}
|
|
3046
|
+
return null;
|
|
3047
|
+
}
|
|
3048
|
+
function resolveSlackIngressTurnLifecycle(context) {
|
|
3049
|
+
const candidate = asOptionalRecord(context)?.[SLACK_INGRESS_LIFECYCLE_CONTEXT_KEY];
|
|
3050
|
+
if (!candidate || typeof candidate !== "object") return null;
|
|
3051
|
+
const lifecycle = candidate;
|
|
3052
|
+
return typeof lifecycle.onAdopted === "function" && lifecycle.abortSignal instanceof AbortSignal ? lifecycle : null;
|
|
3053
|
+
}
|
|
3054
|
+
function createSlackDurableIngress(options) {
|
|
3055
|
+
let app;
|
|
3056
|
+
let relayDispatch;
|
|
3057
|
+
const monitor = createChannelIngressMonitor({
|
|
3058
|
+
queue: options.queue ?? (() => getSlackRuntime().state.openChannelIngressQueue({ accountId: options.accountId })),
|
|
3059
|
+
inspect: inspectSlackIngress,
|
|
3060
|
+
payload: {
|
|
3061
|
+
version: SLACK_INGRESS_PAYLOAD_VERSION,
|
|
3062
|
+
serialize: (raw, { receivedAt }) => raw.kind === "relay" ? {
|
|
3063
|
+
kind: "relay",
|
|
3064
|
+
receivedAt,
|
|
3065
|
+
message: raw.message
|
|
3066
|
+
} : {
|
|
3067
|
+
kind: "events-api",
|
|
3068
|
+
receivedAt,
|
|
3069
|
+
body: raw.body,
|
|
3070
|
+
...raw.retryNum === void 0 ? {} : { retryNum: raw.retryNum },
|
|
3071
|
+
...raw.retryReason === void 0 ? {} : { retryReason: raw.retryReason }
|
|
3072
|
+
},
|
|
3073
|
+
deserialize: (body, { claim }) => body.kind === "relay" ? {
|
|
3074
|
+
kind: "relay",
|
|
3075
|
+
deliveryId: claim.id.startsWith("relay:") ? claim.id.slice(6) : claim.id,
|
|
3076
|
+
message: body.message
|
|
3077
|
+
} : {
|
|
3078
|
+
kind: "events-api",
|
|
3079
|
+
body: body.body,
|
|
3080
|
+
...body.retryNum === void 0 ? {} : { retryNum: body.retryNum },
|
|
3081
|
+
...body.retryReason === void 0 ? {} : { retryReason: body.retryReason }
|
|
3082
|
+
},
|
|
3083
|
+
encode: ({ body }) => ({
|
|
3084
|
+
version: SLACK_INGRESS_PAYLOAD_VERSION,
|
|
3085
|
+
...body
|
|
3086
|
+
}),
|
|
3087
|
+
decode: (payload, { claim }) => decodeSlackIngressPayload(payload, claim.id),
|
|
3088
|
+
createClaimError: (_kind, claim) => new SlackIngressPayloadError(`Slack ingress payload ${claim.id} was invalid.`)
|
|
3089
|
+
},
|
|
3090
|
+
onDurableAdmission: async (raw) => {
|
|
3091
|
+
if (raw.kind === "events-api") await raw.afterDurableAdmission?.();
|
|
3092
|
+
},
|
|
3093
|
+
deliver: async (raw, lifecycle) => {
|
|
3094
|
+
if (raw.kind === "relay") {
|
|
3095
|
+
if (!relayDispatch) throw new Error("Slack relay ingress dispatcher is not attached.");
|
|
3096
|
+
await relayDispatch(raw.message, lifecycle);
|
|
3097
|
+
return;
|
|
3098
|
+
}
|
|
3099
|
+
if (!app) throw new Error("Slack ingress receiver is not attached to a Bolt app.");
|
|
3100
|
+
await app.processEvent({
|
|
3101
|
+
body: raw.body,
|
|
3102
|
+
ack: async () => {},
|
|
3103
|
+
...raw.retryNum === void 0 ? {} : { retryNum: raw.retryNum },
|
|
3104
|
+
...raw.retryReason === void 0 ? {} : { retryReason: raw.retryReason },
|
|
3105
|
+
customProperties: { [SLACK_INGRESS_LIFECYCLE_CONTEXT_KEY]: lifecycle }
|
|
3106
|
+
});
|
|
3107
|
+
},
|
|
3108
|
+
pollIntervalMs: options.pollIntervalMs ?? SLACK_INGRESS_POLL_INTERVAL_MS,
|
|
3109
|
+
retention: {
|
|
3110
|
+
pruneIntervalMs: SLACK_INGRESS_PRUNE_INTERVAL_MS,
|
|
3111
|
+
completedTtlMs: SLACK_INGRESS_COMPLETED_TTL_MS,
|
|
3112
|
+
completedMaxEntries: SLACK_INGRESS_COMPLETED_MAX_ENTRIES,
|
|
3113
|
+
failedTtlMs: SLACK_INGRESS_FAILED_TTL_MS,
|
|
3114
|
+
failedMaxEntries: SLACK_INGRESS_FAILED_MAX_ENTRIES
|
|
3115
|
+
},
|
|
3116
|
+
appendRetryDelaysMs: [0],
|
|
3117
|
+
drain: {
|
|
3118
|
+
resolveNonRetryableFailure: resolveSlackIngressNonRetryableFailure,
|
|
3119
|
+
deriveLaneKey: (record) => record.payload.kind === "relay" ? resolveSlackIngressLane({ event: record.payload.message }, record.id) : resolveSlackIngressLane(record.payload.body, record.id),
|
|
3120
|
+
...options.adoptionStallTimeoutMs === void 0 ? {} : { adoptionStallTimeoutMs: options.adoptionStallTimeoutMs },
|
|
3121
|
+
...options.onLog ? { onLog: options.onLog } : {}
|
|
3122
|
+
},
|
|
3123
|
+
...options.abortSignal ? { abortSignal: options.abortSignal } : {},
|
|
3124
|
+
onError: (error) => options.onLog?.(`slack ingress drain failed: ${formatErrorMessage(error)}`)
|
|
3125
|
+
});
|
|
3126
|
+
const acceptReceiverEvent = async (event) => {
|
|
3127
|
+
if (!isSlackEventCallback(event.body)) {
|
|
3128
|
+
if (!app) throw new Error("Slack ingress receiver is not attached to a Bolt app.");
|
|
3129
|
+
await app.processEvent(event);
|
|
3130
|
+
return;
|
|
3131
|
+
}
|
|
3132
|
+
await monitor.admit({
|
|
3133
|
+
kind: "events-api",
|
|
3134
|
+
body: event.body,
|
|
3135
|
+
...event.retryNum === void 0 ? {} : { retryNum: event.retryNum },
|
|
3136
|
+
...event.retryReason === void 0 ? {} : { retryReason: event.retryReason },
|
|
3137
|
+
afterDurableAdmission: () => event.ack()
|
|
3138
|
+
});
|
|
3139
|
+
};
|
|
3140
|
+
const acceptRelayEvent = async (event) => {
|
|
3141
|
+
await monitor.admit({
|
|
3142
|
+
kind: "relay",
|
|
3143
|
+
deliveryId: event.deliveryId,
|
|
3144
|
+
message: event.message
|
|
3145
|
+
});
|
|
3146
|
+
};
|
|
3147
|
+
return {
|
|
3148
|
+
wrapReceiver: (receiver) => {
|
|
3149
|
+
const client = Reflect.get(receiver, "client");
|
|
3150
|
+
return {
|
|
3151
|
+
init: (nextApp) => {
|
|
3152
|
+
app = nextApp;
|
|
3153
|
+
receiver.init({ processEvent: acceptReceiverEvent });
|
|
3154
|
+
},
|
|
3155
|
+
start: (...args) => receiver.start(...args),
|
|
3156
|
+
stop: (...args) => receiver.stop(...args),
|
|
3157
|
+
...client === void 0 ? {} : { client }
|
|
3158
|
+
};
|
|
3159
|
+
},
|
|
3160
|
+
acceptRelayEvent,
|
|
3161
|
+
attachRelayDispatch: (dispatch) => {
|
|
3162
|
+
relayDispatch = dispatch;
|
|
3163
|
+
},
|
|
3164
|
+
start: monitor.start,
|
|
3165
|
+
stop: monitor.stop,
|
|
3166
|
+
waitForIdle: monitor.waitForIdle
|
|
3167
|
+
};
|
|
3168
|
+
}
|
|
3169
|
+
//#endregion
|
|
2652
3170
|
//#region extensions/slack/src/monitor/events/message-subtype-handlers.ts
|
|
2653
3171
|
const SUBTYPE_HANDLER_REGISTRY = {
|
|
2654
3172
|
message_changed: {
|
|
@@ -2776,6 +3294,7 @@ function registerSlackMessageEvents(params) {
|
|
|
2776
3294
|
return resolved.scope;
|
|
2777
3295
|
};
|
|
2778
3296
|
const handleIncomingMessageEvent = async ({ event, body, context, client }) => {
|
|
3297
|
+
const turnAdoptionLifecycle = resolveSlackIngressTurnLifecycle(context);
|
|
2779
3298
|
try {
|
|
2780
3299
|
const eventScope = resolveEventScope({
|
|
2781
3300
|
body,
|
|
@@ -2801,10 +3320,9 @@ function registerSlackMessageEvents(params) {
|
|
|
2801
3320
|
if (assistantChangedInbound) {
|
|
2802
3321
|
await handleSlackMessage(assistantChangedInbound, {
|
|
2803
3322
|
source: "message",
|
|
2804
|
-
...eventScope ? {
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
} : {}
|
|
3323
|
+
...eventScope ? { eventScope } : {},
|
|
3324
|
+
...turnAdoptionLifecycle ? { turnAdoptionLifecycle } : {},
|
|
3325
|
+
...eventScope || turnAdoptionLifecycle ? { awaitDispatch: true } : {}
|
|
2808
3326
|
});
|
|
2809
3327
|
return;
|
|
2810
3328
|
}
|
|
@@ -2832,12 +3350,12 @@ function registerSlackMessageEvents(params) {
|
|
|
2832
3350
|
}
|
|
2833
3351
|
await handleSlackMessage(message, {
|
|
2834
3352
|
source: "message",
|
|
2835
|
-
...eventScope ? {
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
} : {}
|
|
3353
|
+
...eventScope ? { eventScope } : {},
|
|
3354
|
+
...turnAdoptionLifecycle ? { turnAdoptionLifecycle } : {},
|
|
3355
|
+
...eventScope || turnAdoptionLifecycle ? { awaitDispatch: true } : {}
|
|
2839
3356
|
});
|
|
2840
3357
|
} catch (err) {
|
|
3358
|
+
if (turnAdoptionLifecycle) throw err;
|
|
2841
3359
|
ctx.runtime.error?.(danger(`slack handler failed: ${formatErrorMessage(err)}`));
|
|
2842
3360
|
}
|
|
2843
3361
|
};
|
|
@@ -2846,6 +3364,7 @@ function registerSlackMessageEvents(params) {
|
|
|
2846
3364
|
});
|
|
2847
3365
|
ctx.app.event("app_mention", async (args) => {
|
|
2848
3366
|
const { event, body, context, client } = args;
|
|
3367
|
+
const turnAdoptionLifecycle = resolveSlackIngressTurnLifecycle(context);
|
|
2849
3368
|
try {
|
|
2850
3369
|
const eventScope = resolveEventScope({
|
|
2851
3370
|
body,
|
|
@@ -2872,12 +3391,12 @@ function registerSlackMessageEvents(params) {
|
|
|
2872
3391
|
await handleSlackMessage(mention, {
|
|
2873
3392
|
source: "app_mention",
|
|
2874
3393
|
wasMentioned: true,
|
|
2875
|
-
...eventScope ? {
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
} : {}
|
|
3394
|
+
...eventScope ? { eventScope } : {},
|
|
3395
|
+
...turnAdoptionLifecycle ? { turnAdoptionLifecycle } : {},
|
|
3396
|
+
...eventScope || turnAdoptionLifecycle ? { awaitDispatch: true } : {}
|
|
2879
3397
|
});
|
|
2880
3398
|
} catch (err) {
|
|
3399
|
+
if (turnAdoptionLifecycle) throw err;
|
|
2881
3400
|
ctx.runtime.error?.(danger(`slack mention handler failed: ${formatErrorMessage(err)}`));
|
|
2882
3401
|
}
|
|
2883
3402
|
});
|
|
@@ -3026,6 +3545,10 @@ function registerSlackMonitorEvents(params) {
|
|
|
3026
3545
|
slashCommandName: params.appHomeSlashCommandName,
|
|
3027
3546
|
trackEvent: params.trackEvent
|
|
3028
3547
|
});
|
|
3548
|
+
registerSlackAgentEvents({
|
|
3549
|
+
ctx: params.ctx,
|
|
3550
|
+
trackEvent: params.trackEvent
|
|
3551
|
+
});
|
|
3029
3552
|
registerSlackInteractionEvents({
|
|
3030
3553
|
ctx: params.ctx,
|
|
3031
3554
|
trackEvent: params.trackEvent
|
|
@@ -3036,41 +3559,47 @@ function registerSlackMonitorEvents(params) {
|
|
|
3036
3559
|
});
|
|
3037
3560
|
}
|
|
3038
3561
|
//#endregion
|
|
3039
|
-
//#region extensions/slack/src/monitor/
|
|
3040
|
-
const
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3562
|
+
//#region extensions/slack/src/monitor/message-dispatch-dedupe.ts
|
|
3563
|
+
const SLACK_MESSAGE_DISPATCH_DEDUPE_TTL_MS = 1440 * 60 * 1e3;
|
|
3564
|
+
const SLACK_MESSAGE_DISPATCH_DEDUPE_MEMORY_MAX_ENTRIES = 2e4;
|
|
3565
|
+
const SLACK_MESSAGE_DISPATCH_DEDUPE_STATE_MAX_ENTRIES = 2e4;
|
|
3566
|
+
const SLACK_MESSAGE_DISPATCH_DEDUPE_NAMESPACE = "global";
|
|
3567
|
+
const SLACK_MESSAGE_DISPATCH_DEDUPE_NAMESPACE_PREFIX = "slack.message-dispatch-dedupe";
|
|
3568
|
+
const SLACK_MESSAGE_DISPATCH_DEDUPE_STATE_PLUGIN_ID = "slack-message-dispatch-dedupe";
|
|
3569
|
+
function buildSlackMessageDispatchReplayKey(params) {
|
|
3570
|
+
const channelId = params.channelId?.trim();
|
|
3571
|
+
const ts = params.ts?.trim();
|
|
3572
|
+
if (!channelId || !ts) return null;
|
|
3573
|
+
const teamId = params.teamId?.trim();
|
|
3574
|
+
return JSON.stringify([
|
|
3575
|
+
"message",
|
|
3576
|
+
params.accountId,
|
|
3577
|
+
teamId ?? "",
|
|
3578
|
+
channelId,
|
|
3579
|
+
ts
|
|
3580
|
+
]);
|
|
3581
|
+
}
|
|
3582
|
+
function createSlackMessageDispatchReplayGuard(params = {}) {
|
|
3583
|
+
return createChannelReplayGuard({
|
|
3584
|
+
dedupe: {
|
|
3585
|
+
ttlMs: SLACK_MESSAGE_DISPATCH_DEDUPE_TTL_MS,
|
|
3586
|
+
memoryMaxSize: SLACK_MESSAGE_DISPATCH_DEDUPE_MEMORY_MAX_ENTRIES,
|
|
3587
|
+
pluginId: SLACK_MESSAGE_DISPATCH_DEDUPE_STATE_PLUGIN_ID,
|
|
3588
|
+
namespacePrefix: SLACK_MESSAGE_DISPATCH_DEDUPE_NAMESPACE_PREFIX,
|
|
3589
|
+
stateMaxEntries: SLACK_MESSAGE_DISPATCH_DEDUPE_STATE_MAX_ENTRIES,
|
|
3590
|
+
...params.onDiskError ? { onDiskError: params.onDiskError } : {}
|
|
3591
|
+
},
|
|
3592
|
+
buildReplayKey: (event) => event.keys,
|
|
3593
|
+
namespace: () => SLACK_MESSAGE_DISPATCH_DEDUPE_NAMESPACE
|
|
3594
|
+
});
|
|
3595
|
+
}
|
|
3596
|
+
/** Claim one logical message key; an in-flight sibling claim settles to duplicate. */
|
|
3597
|
+
async function claimSlackMessageDispatchReplay(params) {
|
|
3598
|
+
const claim = await runClaimableDedupeClaimLoop(() => params.guard.claim({ keys: [params.key] }), (_error, rejectionCount) => rejectionCount <= 1);
|
|
3599
|
+
return claim.kind === "claimed" ? {
|
|
3600
|
+
kind: "claimed",
|
|
3601
|
+
handle: claim.handle
|
|
3602
|
+
} : { kind: "duplicate" };
|
|
3074
3603
|
}
|
|
3075
3604
|
//#endregion
|
|
3076
3605
|
//#region extensions/slack/src/monitor/message-handler/debounce-key.ts
|
|
@@ -3198,7 +3727,7 @@ function createSlackThreadTsResolver(params) {
|
|
|
3198
3727
|
}
|
|
3199
3728
|
//#endregion
|
|
3200
3729
|
//#region extensions/slack/src/monitor/message-handler.ts
|
|
3201
|
-
const loadSlackMessagePipeline = createLazyRuntimeModule(() => import("./pipeline.runtime-
|
|
3730
|
+
const loadSlackMessagePipeline = createLazyRuntimeModule(() => import("./pipeline.runtime-5rRYZuX5.js"));
|
|
3202
3731
|
function createSlackDispatchCompletion() {
|
|
3203
3732
|
let resolve;
|
|
3204
3733
|
let reject;
|
|
@@ -3211,7 +3740,6 @@ function createSlackDispatchCompletion() {
|
|
|
3211
3740
|
reject
|
|
3212
3741
|
};
|
|
3213
3742
|
}
|
|
3214
|
-
const APP_MENTION_RETRY_TTL_MS = 6e4;
|
|
3215
3743
|
const RETRYABLE_FLUSH_MAX_ATTEMPTS = 3;
|
|
3216
3744
|
const RETRYABLE_FLUSH_RETRY_DELAY_MS = 1e3;
|
|
3217
3745
|
const REPLY_SESSION_INIT_CONFLICT_MESSAGE_RE = /reply session initialization conflicted for \S+/u;
|
|
@@ -3225,12 +3753,9 @@ function shouldDebounceSlackMessage(message, cfg) {
|
|
|
3225
3753
|
hasMedia: Boolean(message.files && message.files.length > 0)
|
|
3226
3754
|
});
|
|
3227
3755
|
}
|
|
3228
|
-
function buildSeenMessageKey(channelId, ts, teamId) {
|
|
3229
|
-
if (!channelId || !ts) return null;
|
|
3230
|
-
return `${teamId ? `${teamId}:` : ""}${channelId}:${ts}`;
|
|
3231
|
-
}
|
|
3232
3756
|
function createSlackMessageHandler(params) {
|
|
3233
3757
|
const { ctx, account, trackEvent, onPrepared } = params;
|
|
3758
|
+
const dispatchReplayGuard = params.dispatchReplayGuard ?? createSlackMessageDispatchReplayGuard({ onDiskError: (error) => ctx.runtime.error?.(`slack message dispatch dedupe persistence failed: ${formatErrorMessage(error)}`) });
|
|
3234
3759
|
const { debounceMs, debouncer } = createChannelInboundDebouncer({
|
|
3235
3760
|
cfg: ctx.cfg,
|
|
3236
3761
|
channel: "slack",
|
|
@@ -3263,8 +3788,41 @@ function createSlackMessageHandler(params) {
|
|
|
3263
3788
|
const completions = entries.map((entry) => entry.opts.dispatchCompletion).filter((completion) => completion !== void 0);
|
|
3264
3789
|
try {
|
|
3265
3790
|
await (async () => {
|
|
3266
|
-
const
|
|
3267
|
-
|
|
3791
|
+
const claims = [];
|
|
3792
|
+
const claimedKeys = /* @__PURE__ */ new Set();
|
|
3793
|
+
const surviving = [];
|
|
3794
|
+
for (const entry of entries) {
|
|
3795
|
+
const replayKey = buildSlackMessageDispatchReplayKey({
|
|
3796
|
+
accountId: ctx.accountId,
|
|
3797
|
+
channelId: entry.message.channel,
|
|
3798
|
+
ts: entry.message.ts,
|
|
3799
|
+
teamId: entry.opts.eventScope?.teamId
|
|
3800
|
+
});
|
|
3801
|
+
if (!replayKey || claimedKeys.has(replayKey)) {
|
|
3802
|
+
surviving.push(entry);
|
|
3803
|
+
continue;
|
|
3804
|
+
}
|
|
3805
|
+
const claim = await claimSlackMessageDispatchReplay({
|
|
3806
|
+
guard: dispatchReplayGuard,
|
|
3807
|
+
key: replayKey
|
|
3808
|
+
});
|
|
3809
|
+
if (claim.kind === "claimed") {
|
|
3810
|
+
claims.push(claim.handle);
|
|
3811
|
+
claimedKeys.add(replayKey);
|
|
3812
|
+
surviving.push(entry);
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
const releaseClaims = (error) => {
|
|
3816
|
+
for (const handle of claims) handle.release(error === void 0 ? {} : { error });
|
|
3817
|
+
};
|
|
3818
|
+
const commitClaims = async () => {
|
|
3819
|
+
for (const handle of claims) await handle.commit();
|
|
3820
|
+
};
|
|
3821
|
+
const last = surviving.at(-1);
|
|
3822
|
+
if (!last) {
|
|
3823
|
+
releaseClaims();
|
|
3824
|
+
return;
|
|
3825
|
+
}
|
|
3268
3826
|
const teamId = last.opts.eventScope?.teamId;
|
|
3269
3827
|
const flushedKey = buildSlackDebounceKey(last.message, ctx.accountId, teamId);
|
|
3270
3828
|
const topLevelConversationKey = buildTopLevelSlackConversationKey(last.message, ctx.accountId, teamId);
|
|
@@ -3275,76 +3833,60 @@ function createSlackMessageHandler(params) {
|
|
|
3275
3833
|
if (pendingKeys.size === 0) pendingTopLevelDebounceKeys.delete(topLevelConversationKey);
|
|
3276
3834
|
}
|
|
3277
3835
|
}
|
|
3278
|
-
const combinedText =
|
|
3279
|
-
const combinedMentioned =
|
|
3836
|
+
const combinedText = surviving.length === 1 ? last.message.text ?? "" : surviving.map((entry) => entry.message.text ?? "").filter(Boolean).join("\n");
|
|
3837
|
+
const combinedMentioned = surviving.some((entry) => Boolean(entry.opts.wasMentioned));
|
|
3280
3838
|
const syntheticMessage = {
|
|
3281
3839
|
...last.message,
|
|
3282
3840
|
text: combinedText
|
|
3283
3841
|
};
|
|
3284
|
-
const
|
|
3842
|
+
const { prepareSlackMessage, dispatchPreparedSlackMessage } = await loadSlackMessagePipeline();
|
|
3843
|
+
const { dispatchCompletion: _completion, awaitDispatch: _awaitDispatch, turnAdoptionLifecycle, ...lastOpts } = last.opts;
|
|
3844
|
+
let prepared;
|
|
3845
|
+
let settlementHandedOff = false;
|
|
3285
3846
|
try {
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
ctx,
|
|
3294
|
-
account,
|
|
3295
|
-
message: syntheticMessage,
|
|
3296
|
-
opts: {
|
|
3297
|
-
...lastOpts,
|
|
3298
|
-
wasMentioned: combinedMentioned || last.opts.wasMentioned,
|
|
3299
|
-
...seenMessageKey && lastOpts.source === "message" ? { shouldRecordDroppedHistory: () => !appMentionPreparingKeys.has(seenMessageKey) && !appMentionDispatchedKeys.has(seenMessageKey) } : {}
|
|
3300
|
-
}
|
|
3301
|
-
});
|
|
3302
|
-
if (result && seenMessageKey) {
|
|
3303
|
-
pruneAppMentionRetryKeys(Date.now());
|
|
3304
|
-
if (last.opts.source === "app_mention") rememberExpiringAppMentionKey(appMentionDispatchedKeys, seenMessageKey);
|
|
3305
|
-
else if (last.opts.source === "message" && appMentionDispatchedKeys.has(seenMessageKey)) {
|
|
3306
|
-
appMentionDispatchedKeys.delete(seenMessageKey);
|
|
3307
|
-
appMentionRetryKeys.delete(seenMessageKey);
|
|
3308
|
-
return null;
|
|
3309
|
-
}
|
|
3310
|
-
appMentionRetryKeys.delete(seenMessageKey);
|
|
3311
|
-
}
|
|
3312
|
-
return result;
|
|
3313
|
-
} finally {
|
|
3314
|
-
if (appMentionRetryKey) appMentionPreparingKeys.delete(appMentionRetryKey);
|
|
3847
|
+
prepared = await prepareSlackMessage({
|
|
3848
|
+
ctx,
|
|
3849
|
+
account,
|
|
3850
|
+
message: syntheticMessage,
|
|
3851
|
+
opts: {
|
|
3852
|
+
...lastOpts,
|
|
3853
|
+
wasMentioned: combinedMentioned || last.opts.wasMentioned
|
|
3315
3854
|
}
|
|
3316
|
-
})
|
|
3317
|
-
if (!prepared)
|
|
3855
|
+
});
|
|
3856
|
+
if (!prepared) {
|
|
3857
|
+
releaseClaims();
|
|
3858
|
+
return;
|
|
3859
|
+
}
|
|
3860
|
+
prepared.turnAdoptionLifecycle = turnAdoptionLifecycle ? {
|
|
3861
|
+
...turnAdoptionLifecycle,
|
|
3862
|
+
onAdopted: async () => {
|
|
3863
|
+
settlementHandedOff = true;
|
|
3864
|
+
await commitClaims();
|
|
3865
|
+
await turnAdoptionLifecycle.onAdopted();
|
|
3866
|
+
},
|
|
3867
|
+
onDeferred: () => {
|
|
3868
|
+
settlementHandedOff = true;
|
|
3869
|
+
turnAdoptionLifecycle.onDeferred();
|
|
3870
|
+
},
|
|
3871
|
+
onAbandoned: () => {
|
|
3872
|
+
releaseClaims();
|
|
3873
|
+
turnAdoptionLifecycle.onAbandoned();
|
|
3874
|
+
}
|
|
3875
|
+
} : turnAdoptionLifecycle;
|
|
3318
3876
|
onPrepared?.(prepared);
|
|
3319
|
-
if (
|
|
3320
|
-
const ids =
|
|
3877
|
+
if (surviving.length > 1) {
|
|
3878
|
+
const ids = surviving.map((entry) => entry.message.ts).filter(Boolean);
|
|
3321
3879
|
if (ids.length > 0) {
|
|
3322
3880
|
prepared.ctxPayload.MessageSids = ids;
|
|
3323
3881
|
prepared.ctxPayload.MessageSidFirst = ids[0];
|
|
3324
3882
|
prepared.ctxPayload.MessageSidLast = ids[ids.length - 1];
|
|
3325
3883
|
}
|
|
3326
3884
|
}
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
accountId: ctx.accountId,
|
|
3331
|
-
...last.opts.eventScope ? { teamId: last.opts.eventScope.teamId } : {},
|
|
3332
|
-
messages: entries.map((entry) => entry.message)
|
|
3333
|
-
});
|
|
3334
|
-
} catch (error) {
|
|
3335
|
-
if (!isRetryableSlackInboundError(error)) await recordSlackInboundMessageDeliveries({
|
|
3336
|
-
accountId: ctx.accountId,
|
|
3337
|
-
...last.opts.eventScope ? { teamId: last.opts.eventScope.teamId } : {},
|
|
3338
|
-
messages: entries.map((entry) => entry.message)
|
|
3339
|
-
});
|
|
3340
|
-
throw error;
|
|
3341
|
-
}
|
|
3885
|
+
await dispatchPreparedSlackMessage(prepared);
|
|
3886
|
+
if (!turnAdoptionLifecycle) await commitClaims();
|
|
3887
|
+
else if (!settlementHandedOff) releaseClaims();
|
|
3342
3888
|
} catch (error) {
|
|
3343
|
-
|
|
3344
|
-
const entrySeenKey = buildSeenMessageKey(entry.message.channel, entry.message.ts, entry.opts.eventScope?.teamId);
|
|
3345
|
-
if (entrySeenKey) appMentionDispatchedKeys.delete(entrySeenKey);
|
|
3346
|
-
ctx.releaseSeenMessage(entry.message.channel, entry.message.ts, entry.opts.eventScope);
|
|
3347
|
-
}
|
|
3889
|
+
releaseClaims(error);
|
|
3348
3890
|
throw error;
|
|
3349
3891
|
}
|
|
3350
3892
|
})();
|
|
@@ -3361,51 +3903,10 @@ function createSlackMessageHandler(params) {
|
|
|
3361
3903
|
});
|
|
3362
3904
|
const threadTsResolver = createSlackThreadTsResolver({ client: ctx.app.client });
|
|
3363
3905
|
const pendingTopLevelDebounceKeys = /* @__PURE__ */ new Map();
|
|
3364
|
-
const appMentionRetryKeys = /* @__PURE__ */ new Map();
|
|
3365
|
-
const appMentionPreparingKeys = /* @__PURE__ */ new Set();
|
|
3366
|
-
const appMentionDispatchedKeys = /* @__PURE__ */ new Map();
|
|
3367
|
-
const pruneAppMentionRetryKeys = (rawNow) => {
|
|
3368
|
-
const now = asDateTimestampMs(rawNow);
|
|
3369
|
-
if (now === void 0) {
|
|
3370
|
-
appMentionRetryKeys.clear();
|
|
3371
|
-
appMentionDispatchedKeys.clear();
|
|
3372
|
-
return false;
|
|
3373
|
-
}
|
|
3374
|
-
for (const [key, expiresAt] of appMentionRetryKeys) if (asDateTimestampMs(expiresAt) === void 0 || expiresAt <= now) appMentionRetryKeys.delete(key);
|
|
3375
|
-
for (const [key, expiresAt] of appMentionDispatchedKeys) if (asDateTimestampMs(expiresAt) === void 0 || expiresAt <= now) appMentionDispatchedKeys.delete(key);
|
|
3376
|
-
return true;
|
|
3377
|
-
};
|
|
3378
|
-
const rememberExpiringAppMentionKey = (map, key) => {
|
|
3379
|
-
const now = Date.now();
|
|
3380
|
-
if (!pruneAppMentionRetryKeys(now)) return;
|
|
3381
|
-
const expiresAt = resolveExpiresAtMsFromDurationMs(APP_MENTION_RETRY_TTL_MS, { nowMs: now });
|
|
3382
|
-
if (expiresAt !== void 0) map.set(key, expiresAt);
|
|
3383
|
-
};
|
|
3384
|
-
const rememberAppMentionRetryKey = (key) => {
|
|
3385
|
-
rememberExpiringAppMentionKey(appMentionRetryKeys, key);
|
|
3386
|
-
};
|
|
3387
|
-
const consumeAppMentionRetryKey = (key) => {
|
|
3388
|
-
if (!pruneAppMentionRetryKeys(Date.now())) return false;
|
|
3389
|
-
if (!appMentionRetryKeys.has(key)) return false;
|
|
3390
|
-
appMentionRetryKeys.delete(key);
|
|
3391
|
-
return true;
|
|
3392
|
-
};
|
|
3393
3906
|
async function enqueueSlackMessage(message, opts) {
|
|
3394
3907
|
if (opts.source === "message" && message.type !== "message") return;
|
|
3395
3908
|
if (opts.source === "message" && message.subtype && message.subtype !== "file_share" && message.subtype !== "bot_message" && message.subtype !== "thread_broadcast") return;
|
|
3396
3909
|
ctx.rememberSlackChannelType(message.channel, message.channel_type, opts.eventScope);
|
|
3397
|
-
const seenMessageKey = buildSeenMessageKey(message.channel, message.ts, opts.eventScope?.teamId);
|
|
3398
|
-
if (seenMessageKey && await hasSlackInboundMessageDelivery({
|
|
3399
|
-
accountId: ctx.accountId,
|
|
3400
|
-
channelId: message.channel,
|
|
3401
|
-
ts: message.ts,
|
|
3402
|
-
...opts.eventScope ? { teamId: opts.eventScope.teamId } : {}
|
|
3403
|
-
})) return;
|
|
3404
|
-
const wasSeen = seenMessageKey ? ctx.markMessageSeen(message.channel, message.ts, opts.eventScope) : false;
|
|
3405
|
-
if (seenMessageKey && opts.source === "message" && !wasSeen) rememberAppMentionRetryKey(seenMessageKey);
|
|
3406
|
-
if (seenMessageKey && wasSeen) {
|
|
3407
|
-
if (opts.source !== "app_mention" || !consumeAppMentionRetryKey(seenMessageKey)) return;
|
|
3408
|
-
}
|
|
3409
3910
|
trackEvent?.();
|
|
3410
3911
|
const resolvedMessage = await (opts.eventScope ? createSlackThreadTsResolver({ client: opts.eventScope.client }) : threadTsResolver).resolve({
|
|
3411
3912
|
message,
|
|
@@ -3642,112 +4143,6 @@ function openSlackPresenceCooldownStore() {
|
|
|
3642
4143
|
});
|
|
3643
4144
|
}
|
|
3644
4145
|
//#endregion
|
|
3645
|
-
//#region extensions/slack/src/monitor/reconnect-policy.ts
|
|
3646
|
-
const SLACK_AUTH_ERROR_RE = /account_inactive|invalid_auth|token_revoked|token_expired|not_authed|org_login_required|team_access_not_granted|user_removed_from_team|team_disabled|missing_scope|cannot_find_service|invalid_token/i;
|
|
3647
|
-
const NO_ERROR_DETAIL = "no error detail";
|
|
3648
|
-
const SLACK_SOCKET_RECONNECT_POLICY = {
|
|
3649
|
-
initialMs: 2e3,
|
|
3650
|
-
maxMs: 3e4,
|
|
3651
|
-
factor: 1.8,
|
|
3652
|
-
jitter: .25
|
|
3653
|
-
};
|
|
3654
|
-
const SLACK_SOCKET_SHARED_CONNECTION_DOCS_URL = "https://docs.slack.dev/apis/events-api/using-socket-mode#using-multiple-connections";
|
|
3655
|
-
const SLACK_SOCKET_HELLO_MARKER = Buffer.from("\"hello\"");
|
|
3656
|
-
function getSocketEmitter(app) {
|
|
3657
|
-
const receiver = app.receiver;
|
|
3658
|
-
const client = receiver && typeof receiver === "object" ? receiver.client : void 0;
|
|
3659
|
-
if (!client || typeof client !== "object") return null;
|
|
3660
|
-
const on = client.on;
|
|
3661
|
-
const off = client.off;
|
|
3662
|
-
if (typeof on !== "function" || typeof off !== "function") return null;
|
|
3663
|
-
return {
|
|
3664
|
-
on: (event, listener) => on.call(client, event, listener),
|
|
3665
|
-
off: (event, listener) => off.call(client, event, listener)
|
|
3666
|
-
};
|
|
3667
|
-
}
|
|
3668
|
-
function isBufferArray(value) {
|
|
3669
|
-
return Array.isArray(value) && value.every((entry) => Buffer.isBuffer(entry));
|
|
3670
|
-
}
|
|
3671
|
-
function resolveSlackSocketModeConnectionCount(message) {
|
|
3672
|
-
const buffer = Buffer.isBuffer(message) ? message : message instanceof ArrayBuffer ? Buffer.from(message) : isBufferArray(message) ? Buffer.concat(message) : void 0;
|
|
3673
|
-
if (!buffer?.includes(SLACK_SOCKET_HELLO_MARKER)) return;
|
|
3674
|
-
let payload;
|
|
3675
|
-
try {
|
|
3676
|
-
payload = JSON.parse(buffer.toString("utf8"));
|
|
3677
|
-
} catch {
|
|
3678
|
-
return;
|
|
3679
|
-
}
|
|
3680
|
-
const count = isRecord(payload) && payload.type === "hello" ? payload.num_connections : void 0;
|
|
3681
|
-
return typeof count === "number" && Number.isSafeInteger(count) && count >= 0 ? count : void 0;
|
|
3682
|
-
}
|
|
3683
|
-
function formatSlackSocketModeSharedConnectionWarning(activeConnections) {
|
|
3684
|
-
return [
|
|
3685
|
-
`slack socket mode reports ${activeConnections} active connections for this Slack app`,
|
|
3686
|
-
"Slack may deliver each event to any one connection",
|
|
3687
|
-
"ensure every OpenClaw gateway sharing this app has equivalent routing and authorization, or use a separate Slack app per gateway, one relay ingress, or HTTP Request URLs behind a load balancer",
|
|
3688
|
-
`See ${SLACK_SOCKET_SHARED_CONNECTION_DOCS_URL}`
|
|
3689
|
-
].join("; ");
|
|
3690
|
-
}
|
|
3691
|
-
function registerSlackSocketModeConnectionDiagnostics(params) {
|
|
3692
|
-
const emitter = getSocketEmitter(params.app);
|
|
3693
|
-
if (!emitter) return () => {};
|
|
3694
|
-
let hasWarned = false;
|
|
3695
|
-
const listener = (message, isBinary) => {
|
|
3696
|
-
if (isBinary === true || hasWarned) return;
|
|
3697
|
-
const activeConnections = resolveSlackSocketModeConnectionCount(message);
|
|
3698
|
-
if (activeConnections === void 0 || activeConnections <= 1) return;
|
|
3699
|
-
hasWarned = true;
|
|
3700
|
-
params.onSharedConnection(activeConnections);
|
|
3701
|
-
};
|
|
3702
|
-
emitter.on("ws_message", listener);
|
|
3703
|
-
return () => {
|
|
3704
|
-
emitter.off("ws_message", listener);
|
|
3705
|
-
};
|
|
3706
|
-
}
|
|
3707
|
-
function waitForSlackSocketDisconnect(app, abortSignal) {
|
|
3708
|
-
return new Promise((resolve) => {
|
|
3709
|
-
const emitter = getSocketEmitter(app);
|
|
3710
|
-
if (!emitter) {
|
|
3711
|
-
abortSignal?.addEventListener("abort", () => resolve({ event: "disconnect" }), { once: true });
|
|
3712
|
-
return;
|
|
3713
|
-
}
|
|
3714
|
-
const disconnectListener = () => resolveOnce({ event: "disconnect" });
|
|
3715
|
-
const startFailListener = (error) => resolveOnce({
|
|
3716
|
-
event: "unable_to_socket_mode_start",
|
|
3717
|
-
error
|
|
3718
|
-
});
|
|
3719
|
-
const errorListener = (error) => resolveOnce({
|
|
3720
|
-
event: "error",
|
|
3721
|
-
error
|
|
3722
|
-
});
|
|
3723
|
-
const abortListener = () => resolveOnce({ event: "disconnect" });
|
|
3724
|
-
const cleanup = () => {
|
|
3725
|
-
emitter.off("disconnected", disconnectListener);
|
|
3726
|
-
emitter.off("unable_to_socket_mode_start", startFailListener);
|
|
3727
|
-
emitter.off("error", errorListener);
|
|
3728
|
-
abortSignal?.removeEventListener("abort", abortListener);
|
|
3729
|
-
};
|
|
3730
|
-
const resolveOnce = (value) => {
|
|
3731
|
-
cleanup();
|
|
3732
|
-
resolve(value);
|
|
3733
|
-
};
|
|
3734
|
-
emitter.on("disconnected", disconnectListener);
|
|
3735
|
-
emitter.on("unable_to_socket_mode_start", startFailListener);
|
|
3736
|
-
emitter.on("error", errorListener);
|
|
3737
|
-
abortSignal?.addEventListener("abort", abortListener, { once: true });
|
|
3738
|
-
});
|
|
3739
|
-
}
|
|
3740
|
-
/**
|
|
3741
|
-
* Detect permanent Slack account and credential failures.
|
|
3742
|
-
* Transient request and HTTP failures stay in OpenClaw's reconnect loop.
|
|
3743
|
-
*/
|
|
3744
|
-
function isNonRecoverableSlackAuthError(error) {
|
|
3745
|
-
return SLACK_AUTH_ERROR_RE.test(formatUnknownError(error, ""));
|
|
3746
|
-
}
|
|
3747
|
-
function formatUnknownError(error, fallback = NO_ERROR_DETAIL) {
|
|
3748
|
-
return formatSlackError(error, fallback);
|
|
3749
|
-
}
|
|
3750
|
-
//#endregion
|
|
3751
4146
|
//#region extensions/slack/src/monitor/provider-support.ts
|
|
3752
4147
|
const OPENCLAW_SLACK_CLIENT_PING_TIMEOUT_MS = 15e3;
|
|
3753
4148
|
const OPENCLAW_SLACK_SOCKET_START_FAILED_EVENT = "unable_to_socket_mode_start";
|
|
@@ -3910,27 +4305,28 @@ function createSlackBoltApp(params) {
|
|
|
3910
4305
|
const socketModeReceiverOptions = {
|
|
3911
4306
|
appToken: params.appToken ?? "",
|
|
3912
4307
|
autoReconnectEnabled: true,
|
|
3913
|
-
clientPingTimeout:
|
|
4308
|
+
clientPingTimeout: OPENCLAW_SLACK_CLIENT_PING_TIMEOUT_MS,
|
|
3914
4309
|
logger: socketModeLogger,
|
|
3915
|
-
installerOptions: { clientOptions: params.clientOptions }
|
|
4310
|
+
installerOptions: { clientOptions: params.clientOptions },
|
|
4311
|
+
...params.wrapReceiver ? { processEventErrorHandler: async () => false } : {}
|
|
3916
4312
|
};
|
|
3917
|
-
if (params.socketMode?.serverPingTimeout !== void 0) socketModeReceiverOptions.serverPingTimeout = params.socketMode.serverPingTimeout;
|
|
3918
|
-
if (params.socketMode?.pingPongLoggingEnabled !== void 0) socketModeReceiverOptions.pingPongLoggingEnabled = params.socketMode.pingPongLoggingEnabled;
|
|
3919
4313
|
let receiver;
|
|
3920
4314
|
if (params.slackMode === "socket") {
|
|
3921
4315
|
receiver = new params.interop.SocketModeReceiver(socketModeReceiverOptions);
|
|
3922
4316
|
installSlackNativeReconnectFailureObserver(receiver);
|
|
3923
4317
|
} else if (params.slackMode === "http") receiver = new params.interop.HTTPReceiver({
|
|
3924
4318
|
signingSecret: params.signingSecret ?? "",
|
|
3925
|
-
endpoints: params.slackWebhookPath
|
|
4319
|
+
endpoints: params.slackWebhookPath,
|
|
4320
|
+
...params.wrapReceiver ? { processEventErrorHandler: async () => false } : {}
|
|
3926
4321
|
});
|
|
3927
4322
|
else receiver = createSlackRelayReceiver();
|
|
4323
|
+
const appReceiver = receiver && params.wrapReceiver ? params.wrapReceiver(receiver) : receiver;
|
|
3928
4324
|
const app = new params.interop.App({
|
|
3929
|
-
token: params.
|
|
4325
|
+
token: params.token,
|
|
3930
4326
|
clientOptions: params.clientOptions,
|
|
3931
4327
|
ignoreSelf: false,
|
|
3932
4328
|
tokenVerificationEnabled: false,
|
|
3933
|
-
...
|
|
4329
|
+
...appReceiver ? { receiver: appReceiver } : {}
|
|
3934
4330
|
});
|
|
3935
4331
|
app.use(async (args) => {
|
|
3936
4332
|
if (shouldSkipOpenClawSlackSelfEvent(args)) return;
|
|
@@ -4178,7 +4574,7 @@ const SLACK_COMMAND_ARG_CONFIRM_TEXT_MAX = 300;
|
|
|
4178
4574
|
const SLACK_HEADER_TEXT_MAX = 150;
|
|
4179
4575
|
const SLACK_COMMAND_ARG_ACTION_BLOCKS_MAX = 47;
|
|
4180
4576
|
const loadSlashCommandsRuntime = createLazyRuntimeModule(() => import("./slash-commands.runtime-BTTMlmwy.js"));
|
|
4181
|
-
const loadSlashDispatchRuntime = createLazyRuntimeModule(() => import("./slash-dispatch.runtime-
|
|
4577
|
+
const loadSlashDispatchRuntime = createLazyRuntimeModule(() => import("./slash-dispatch.runtime-lfeIsXTX.js"));
|
|
4182
4578
|
const loadSlackPluginCommandsRuntime = createLazyRuntimeModule(() => import("./slash-plugin-commands.runtime-Dosfcx3X.js"));
|
|
4183
4579
|
const loadSlashSkillCommandsRuntime = createLazyRuntimeModule(() => import("./slash-skill-commands.runtime-CD1J2ODY.js"));
|
|
4184
4580
|
function resolveSlackCommandMenuModelContext(params) {
|
|
@@ -4571,8 +4967,12 @@ async function registerSlackMonitorSlashCommands(params) {
|
|
|
4571
4967
|
agentId: menuRoute.agentId,
|
|
4572
4968
|
sessionKey: menuRoute.sessionKey
|
|
4573
4969
|
}) : {};
|
|
4574
|
-
const menuModelCatalog = commandDefinition.key === "think" && menuNeedsModelContext ? await
|
|
4970
|
+
const menuModelCatalog = commandDefinition.key === "think" && menuNeedsModelContext ? await loadPreparedModelCatalog({
|
|
4575
4971
|
config: cfg,
|
|
4972
|
+
...menuRoute ? {
|
|
4973
|
+
agentId: menuRoute.agentId,
|
|
4974
|
+
agentDir: resolveAgentDir(cfg, menuRoute.agentId)
|
|
4975
|
+
} : {},
|
|
4576
4976
|
readOnly: true
|
|
4577
4977
|
}) : void 0;
|
|
4578
4978
|
const menu = resolveCommandArgMenu({
|
|
@@ -4630,7 +5030,7 @@ async function registerSlackMonitorSlashCommands(params) {
|
|
|
4630
5030
|
sessionPrefix: slashCommand.sessionPrefix,
|
|
4631
5031
|
userId: command.user_id,
|
|
4632
5032
|
targetSessionKey: route.sessionKey,
|
|
4633
|
-
|
|
5033
|
+
sessionKeyCase: "lowercase"
|
|
4634
5034
|
});
|
|
4635
5035
|
const slashReplyTarget = !slashCommand.ephemeral && isRoomish ? `channel:${command.channel_id}` : `user:${command.user_id}`;
|
|
4636
5036
|
const ctxPayload = finalizeInboundContext({
|
|
@@ -4709,12 +5109,17 @@ async function registerSlackMonitorSlashCommands(params) {
|
|
|
4709
5109
|
});
|
|
4710
5110
|
};
|
|
4711
5111
|
const pendingSlashReplies = [];
|
|
5112
|
+
const shouldDeliverBlockImmediately = commandDefinition?.key === "login";
|
|
4712
5113
|
const { counts } = await dispatchReplyWithDispatcher({
|
|
4713
5114
|
ctx: ctxPayload,
|
|
4714
5115
|
cfg,
|
|
4715
5116
|
dispatcherOptions: {
|
|
4716
5117
|
...replyPipeline,
|
|
4717
|
-
deliver: async (payload) => {
|
|
5118
|
+
deliver: async (payload, info) => {
|
|
5119
|
+
if (info.kind === "block" && shouldDeliverBlockImmediately) {
|
|
5120
|
+
await deliverSlashPayloads([payload]);
|
|
5121
|
+
return;
|
|
5122
|
+
}
|
|
4718
5123
|
pendingSlashReplies.push(payload);
|
|
4719
5124
|
},
|
|
4720
5125
|
onError: (err, info) => {
|
|
@@ -4918,7 +5323,7 @@ async function getSlackBoltInterop() {
|
|
|
4918
5323
|
}
|
|
4919
5324
|
return slackBoltInterop;
|
|
4920
5325
|
}
|
|
4921
|
-
const loadSlackRelaySource = createLazyRuntimeModule(() => import("./relay-source-
|
|
5326
|
+
const loadSlackRelaySource = createLazyRuntimeModule(() => import("./relay-source-Df9EaiTF.js"));
|
|
4922
5327
|
const SLACK_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
|
|
4923
5328
|
const SLACK_WEBHOOK_BODY_TIMEOUT_MS = 3e4;
|
|
4924
5329
|
function resolveStableSlackUserIdEntry(raw) {
|
|
@@ -5011,16 +5416,26 @@ async function monitorSlackProvider(opts = {}) {
|
|
|
5011
5416
|
path: `channels.slack.accounts.${account.accountId}.signingSecret`
|
|
5012
5417
|
});
|
|
5013
5418
|
const botToken = resolveSlackBotToken(opts.botToken ?? account.botToken);
|
|
5419
|
+
const userToken = account.userToken;
|
|
5014
5420
|
const appToken = resolveSlackAppToken(opts.appToken ?? account.appToken);
|
|
5015
5421
|
const relayConfig = slackMode === "relay" ? resolveSlackRelayConfig({
|
|
5016
5422
|
relay: account.config.relay,
|
|
5017
5423
|
accountId: account.accountId
|
|
5018
5424
|
}) : void 0;
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
throw new Error(missing);
|
|
5425
|
+
let token;
|
|
5426
|
+
if (account.identity === "user") {
|
|
5427
|
+
if (!userToken) throw new Error(`Slack user token missing for account "${account.accountId}" (set channels.slack.accounts.${account.accountId}.userToken or SLACK_USER_TOKEN for default).`);
|
|
5428
|
+
if (slackMode === "socket" && !appToken) throw new Error(`Slack app token missing for user-identity socket mode account "${account.accountId}" (set channels.slack.accounts.${account.accountId}.appToken or SLACK_APP_TOKEN for default).`);
|
|
5429
|
+
if (slackMode === "http" && !signingSecret) throw new Error(`Slack signing secret missing for user-identity HTTP mode account "${account.accountId}" (set channels.slack.signingSecret or channels.slack.accounts.${account.accountId}.signingSecret).`);
|
|
5430
|
+
token = userToken;
|
|
5431
|
+
} else {
|
|
5432
|
+
if (!botToken || slackMode === "socket" && !appToken) {
|
|
5433
|
+
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).` : slackMode === "relay" ? `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).`;
|
|
5434
|
+
throw new Error(missing);
|
|
5435
|
+
}
|
|
5436
|
+
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).`);
|
|
5437
|
+
token = botToken;
|
|
5022
5438
|
}
|
|
5023
|
-
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).`);
|
|
5024
5439
|
const slackCfg = account.config;
|
|
5025
5440
|
const dmConfig = slackCfg.dm;
|
|
5026
5441
|
const dmEnabled = dmConfig?.enabled ?? true;
|
|
@@ -5054,30 +5469,33 @@ async function monitorSlackProvider(opts = {}) {
|
|
|
5054
5469
|
log: (message) => runtime.log?.(warn(message))
|
|
5055
5470
|
});
|
|
5056
5471
|
const resolveToken = account.userToken || botToken;
|
|
5057
|
-
const useAccessGroups =
|
|
5472
|
+
const useAccessGroups = true;
|
|
5058
5473
|
const reactionMode = slackCfg.reactionNotifications ?? "own";
|
|
5059
5474
|
const reactionAllowlist = slackCfg.reactionAllowlist ?? [];
|
|
5060
5475
|
const replyToMode = slackCfg.replyToMode ?? "off";
|
|
5061
5476
|
const threadHistoryScope = slackCfg.thread?.historyScope ?? "thread";
|
|
5062
5477
|
const threadInheritParent = slackCfg.thread?.inheritParent ?? false;
|
|
5063
|
-
const threadRequireExplicitMention = slackCfg.thread?.requireExplicitMention ?? false;
|
|
5064
5478
|
const slashCommand = resolveSlackSlashCommandConfig(opts.slashCommand ?? slackCfg.slashCommand);
|
|
5065
5479
|
const allowNameMatching = isDangerousNameMatchingEnabled(slackCfg);
|
|
5066
5480
|
const textLimit = resolveTextChunkLimit(cfg, "slack", account.accountId, { fallbackLimit: SLACK_TEXT_LIMIT });
|
|
5067
5481
|
const ackReactionScope = cfg.messages?.ackReactionScope ?? "group-mentions";
|
|
5068
5482
|
const typingReaction = slackCfg.typingReaction?.trim() ?? "";
|
|
5069
5483
|
const mediaMaxBytes = (opts.mediaMaxMb ?? slackCfg.mediaMaxMb ?? 20) * 1024 * 1024;
|
|
5070
|
-
const removeAckAfterReply = cfg.messages?.removeAckAfterReply ?? false;
|
|
5071
5484
|
const clientOptions = resolveSlackWebClientOptions();
|
|
5485
|
+
const durableIngress = createSlackDurableIngress({
|
|
5486
|
+
accountId: account.accountId,
|
|
5487
|
+
...runtime.log ? { onLog: runtime.log } : {},
|
|
5488
|
+
...opts.abortSignal ? { abortSignal: opts.abortSignal } : {}
|
|
5489
|
+
});
|
|
5072
5490
|
const { app, receiver, socketModeLogger } = createSlackBoltApp({
|
|
5073
5491
|
interop: await getSlackBoltInterop(),
|
|
5074
5492
|
slackMode,
|
|
5075
|
-
|
|
5493
|
+
token,
|
|
5076
5494
|
appToken: slackMode === "socket" ? appToken ?? void 0 : void 0,
|
|
5077
5495
|
signingSecret: slackMode === "http" ? signingSecret ?? void 0 : void 0,
|
|
5078
5496
|
slackWebhookPath,
|
|
5079
5497
|
clientOptions,
|
|
5080
|
-
|
|
5498
|
+
wrapReceiver: durableIngress.wrapReceiver
|
|
5081
5499
|
});
|
|
5082
5500
|
const gracefulStop = async () => {
|
|
5083
5501
|
await gracefulStopSlackApp(app);
|
|
@@ -5112,12 +5530,12 @@ async function monitorSlackProvider(opts = {}) {
|
|
|
5112
5530
|
let authIdentityWarning;
|
|
5113
5531
|
let authTestIdentity;
|
|
5114
5532
|
try {
|
|
5115
|
-
const auth = await createSlackStartupAuthClient(
|
|
5533
|
+
const auth = await createSlackStartupAuthClient(token, clientOptions).auth.test();
|
|
5116
5534
|
const authUserId = normalizeOptionalString(auth.user_id) ?? "";
|
|
5117
5535
|
botId = normalizeOptionalString(auth.bot_id) ?? "";
|
|
5118
|
-
botUserId = botId ? authUserId : "";
|
|
5536
|
+
botUserId = account.identity === "user" ? authUserId : botId ? authUserId : "";
|
|
5119
5537
|
authTestIdentity = auth;
|
|
5120
|
-
authIdentityWarning = formatSlackBotTokenIdentityWarning({
|
|
5538
|
+
if (account.identity === "bot") authIdentityWarning = formatSlackBotTokenIdentityWarning({
|
|
5121
5539
|
auth,
|
|
5122
5540
|
accountId: account.accountId
|
|
5123
5541
|
});
|
|
@@ -5133,7 +5551,10 @@ async function monitorSlackProvider(opts = {}) {
|
|
|
5133
5551
|
});
|
|
5134
5552
|
const teamId = installationIdentity.kind === "workspace" ? installationIdentity.teamId : "";
|
|
5135
5553
|
const apiAppId = installationIdentity.kind === "degraded" ? "" : installationIdentity.apiAppId ?? "";
|
|
5136
|
-
if (authTestError !== void 0)
|
|
5554
|
+
if (authTestError !== void 0) {
|
|
5555
|
+
const identityFailureDetail = account.identity === "user" ? "explicit self-mention detection will be disabled until restart with a valid user token" : "explicit bot-mention detection will be disabled until restart with a valid bot token";
|
|
5556
|
+
runtime.log?.(warn(`[${account.accountId}] slack auth.test failed at boot (${authTestError}); ${identityFailureDetail}; required-mention channels will fail closed without another trusted activation signal`));
|
|
5557
|
+
}
|
|
5137
5558
|
if (authIdentityWarning) runtime.log?.(warn(authIdentityWarning));
|
|
5138
5559
|
const identityHealth = resolveSlackIdentityHealth({
|
|
5139
5560
|
installationIdentity,
|
|
@@ -5141,11 +5562,14 @@ async function monitorSlackProvider(opts = {}) {
|
|
|
5141
5562
|
authTestError,
|
|
5142
5563
|
authIdentityWarning
|
|
5143
5564
|
});
|
|
5144
|
-
if (apiAppId && expectedApiAppIdFromAppToken && apiAppId !== expectedApiAppIdFromAppToken)
|
|
5565
|
+
if (apiAppId && expectedApiAppIdFromAppToken && apiAppId !== expectedApiAppIdFromAppToken) {
|
|
5566
|
+
const identityTokenLabel = account.identity === "user" ? "user token" : "bot token";
|
|
5567
|
+
runtime.error?.(`slack token mismatch: ${identityTokenLabel} app_id=${apiAppId} but app token looks like app_id=${expectedApiAppIdFromAppToken}`);
|
|
5568
|
+
}
|
|
5145
5569
|
const ctx = createSlackMonitorContext({
|
|
5146
5570
|
cfg,
|
|
5147
5571
|
accountId: account.accountId,
|
|
5148
|
-
botToken,
|
|
5572
|
+
botToken: token,
|
|
5149
5573
|
app,
|
|
5150
5574
|
runtime,
|
|
5151
5575
|
channelRuntime: opts.channelRuntime,
|
|
@@ -5173,13 +5597,11 @@ async function monitorSlackProvider(opts = {}) {
|
|
|
5173
5597
|
replyToMode,
|
|
5174
5598
|
threadHistoryScope,
|
|
5175
5599
|
threadInheritParent,
|
|
5176
|
-
threadRequireExplicitMention,
|
|
5177
5600
|
slashCommand,
|
|
5178
5601
|
textLimit,
|
|
5179
5602
|
ackReactionScope,
|
|
5180
5603
|
typingReaction,
|
|
5181
|
-
mediaMaxBytes
|
|
5182
|
-
removeAckAfterReply
|
|
5604
|
+
mediaMaxBytes
|
|
5183
5605
|
});
|
|
5184
5606
|
const trackEvent = opts.setStatus ? () => {
|
|
5185
5607
|
opts.setStatus({
|
|
@@ -5232,6 +5654,7 @@ async function monitorSlackProvider(opts = {}) {
|
|
|
5232
5654
|
appHomeSlashCommandName: commandRegistration.mode === "single" ? commandRegistration.name : void 0,
|
|
5233
5655
|
trackEvent
|
|
5234
5656
|
});
|
|
5657
|
+
durableIngress.start();
|
|
5235
5658
|
presenceMonitor?.start();
|
|
5236
5659
|
if (slackMode === "http" && slackHttpHandler) unregisterHttpHandler = registerSlackHttpHandler({
|
|
5237
5660
|
path: slackWebhookPath,
|
|
@@ -5397,9 +5820,17 @@ async function monitorSlackProvider(opts = {}) {
|
|
|
5397
5820
|
}
|
|
5398
5821
|
} else if (slackMode === "relay" && relayConfig) {
|
|
5399
5822
|
runtime.log?.(`slack relay mode connecting to ${relayConfig.url} gateway_id:${relayConfig.gatewayId}`);
|
|
5823
|
+
durableIngress.attachRelayDispatch(async (message, turnAdoptionLifecycle) => {
|
|
5824
|
+
await handleSlackMessage(message, {
|
|
5825
|
+
source: "message",
|
|
5826
|
+
wasMentioned: true,
|
|
5827
|
+
awaitDispatch: true,
|
|
5828
|
+
turnAdoptionLifecycle
|
|
5829
|
+
});
|
|
5830
|
+
});
|
|
5400
5831
|
await (await loadSlackRelaySource()).monitorSlackRelaySource({
|
|
5401
5832
|
config: relayConfig,
|
|
5402
|
-
|
|
5833
|
+
acceptRelayEvent: durableIngress.acceptRelayEvent,
|
|
5403
5834
|
runtime,
|
|
5404
5835
|
abortSignal: opts.abortSignal,
|
|
5405
5836
|
setStatus: opts.setStatus,
|
|
@@ -5417,9 +5848,10 @@ async function monitorSlackProvider(opts = {}) {
|
|
|
5417
5848
|
opts.abortSignal?.removeEventListener("abort", stopOnAbort);
|
|
5418
5849
|
unregisterSocketModeConnectionDiagnostics();
|
|
5419
5850
|
unregisterHttpHandler?.();
|
|
5851
|
+
await durableIngress.stop();
|
|
5420
5852
|
await gracefulStop();
|
|
5421
5853
|
}
|
|
5422
5854
|
}
|
|
5423
5855
|
const resolveSlackRuntimeGroupPolicy = resolveOpenProviderRuntimeGroupPolicy$1;
|
|
5424
5856
|
//#endregion
|
|
5425
|
-
export {
|
|
5857
|
+
export { stripSlackMentionsForCommandDetection as C, buildSlackSlashCommandMatcher as S, readSessionUpdatedAt as _, createSlackResponseUrlBudget as a, resolveStorePath$1 as b, formatUnknownError as c, resolveSlackCommandIngress as d, resolveSlackEffectiveAllowFrom as f, resolveSlackChatType as g, normalizeSlackChannelType as h, SlackResponseAlreadyReportedError as i, resolveConversationLabel$1 as l, parseSlackAssistantThreadMetadata as m, resolveSlackRuntimeGroupPolicy as n, authorizeSlackDirectMessage as o, buildSlackAssistantThreadMetadata as p, resolveSlackRoomContextHints as r, SLACK_SOCKET_RECONNECT_POLICY as s, monitorSlackProvider as t, authorizeSlackBotRoomMessage as u, resolveChannelContextVisibilityMode as v, updateLastRoute as x, resolveChannelResetConfig as y };
|