@openclaw/slack 2026.5.27 → 2026.5.28-beta.2
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-inspect-vVg3pT03.js → account-inspect-CdGk6R7l.js} +1 -1
- package/dist/account-inspect-api.js +1 -1
- package/dist/{accounts-BnLQ3fe2.js → accounts-f6Xcv9Vi.js} +42 -1
- package/dist/accounts.runtime-BJt1IxOS.js +2 -0
- package/dist/{action-runtime-JVb7KyQs.js → action-runtime-BOEgcnv6.js} +7 -8
- package/dist/action-runtime.runtime-BXQYV0yA.js +2 -0
- package/dist/{actions-1o9nMIY8.js → actions-zfVWcIY6.js} +4 -4
- package/dist/{actions.runtime-qQtdNww-.js → actions.runtime-CoijPN8g.js} +1 -1
- package/dist/api.js +14 -17
- package/dist/{approval-handler.runtime-Ba8nwnYi.js → approval-handler.runtime-CWz3XLfN.js} +12 -69
- package/dist/{channel-Bd7eept6.js → channel-D8p_1twn.js} +25 -28
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-WNNVmCm0.js → channel.setup-oGp4gSTP.js} +4 -4
- package/dist/{client-Dc2Ji2YN.js → client-DowBk5k0.js} +6 -24
- package/dist/{config-schema-CUiDK8HV.js → config-schema-C0RewpJQ.js} +4 -0
- package/dist/contract-api.js +1 -1
- package/dist/{directory-config-9_djLTOK.js → directory-config-8UPAEyNg.js} +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-Cl83cGLZ.js → directory-live-BFB1pSax.js} +2 -2
- package/dist/http-routes-api.js +1 -1
- package/dist/index.js +3 -7
- package/dist/{interactive-replies-DtYu4Sf5.js → interactive-replies-DrBq4Mld.js} +1 -1
- package/dist/interactive-replies-api.js +1 -1
- package/dist/{message-tool-api-BieXX5lk.js → message-tool-api-B9M0zzlQ.js} +2 -2
- package/dist/message-tool-api.js +1 -1
- package/dist/{monitor-Cn7LUDFL.js → monitor-D7jGKmQk.js} +3 -4
- package/dist/{outbound-adapter-ChuR4_0v.js → outbound-adapter-BHZMgblN.js} +4 -7
- package/dist/pipeline.runtime-xM6ppqQZ.js +3492 -0
- package/dist/plugin-routes-B9PvcDQJ.js +22 -0
- package/dist/{policy-pu8tsF8_.js → policy-BBDU-PQK.js} +1 -1
- package/dist/{probe-cBVPhCKZ.js → probe-Djes9Fy6.js} +1 -1
- package/dist/{provider-Bccng2Wp.js → provider-CxMP_s2o.js} +828 -28
- package/dist/{replies-jiEDV6lH.js → replies-DkmWK7JW.js} +2 -4
- package/dist/{resolve-channels-CT4oiz_9.js → resolve-channels-zXt5f47h.js} +1 -1
- package/dist/{resolve-users-o5S-Ijks.js → resolve-users-BLfGAz1v.js} +1 -1
- package/dist/{runtime-api-CyPE-EvY.js → runtime-api-DvpUD2hw.js} +2 -2
- package/dist/runtime-api.js +12 -12
- package/dist/{scopes-CnyhLIPT.js → scopes-DiiHsqh1.js} +1 -1
- package/dist/{send-DvfE8LVm.js → send-BURYyCXI.js} +4 -4
- package/dist/send.runtime-CKaMG3s-.js +2 -0
- package/dist/{setup-core-DKe7Ug3V.js → setup-core-POfI_bgP.js} +2 -2
- package/dist/setup-entry.js +8 -1
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-gjRthuZA.js → setup-surface-DJTHAguz.js} +5 -5
- package/dist/{shared-D8A7iVVH.js → shared-D9WMYymo.js} +5 -5
- package/dist/{slash-dispatch.runtime-BBhdJHb8.js → slash-dispatch.runtime-lsyTm_q5.js} +1 -1
- package/dist/thread-ts-NSVqWybn.js +646 -0
- package/node_modules/semver/classes/range.js +7 -0
- package/node_modules/semver/package.json +1 -1
- package/node_modules/semver/ranges/subset.js +2 -2
- package/npm-shrinkwrap.json +6 -29
- package/openclaw.plugin.json +10 -0
- package/package.json +8 -7
- package/dist/accounts.runtime-BVdtQeuq.js +0 -2
- package/dist/action-runtime.runtime-BZa5VDjs.js +0 -2
- package/dist/allow-list-B1lkGjwl.js +0 -82
- package/dist/blocks-render-CNC4vQnd.js +0 -232
- package/dist/inbound-contract-test-api.js +0 -3
- package/dist/magic-string.es-BPXBBMwL.js +0 -1011
- package/dist/outbound-payload-test-api.js +0 -2
- package/dist/outbound-payload.test-harness-8MnHFgR1.js +0 -13551
- package/dist/pipeline.runtime-BVK8yXw_.js +0 -1473
- package/dist/plugin-routes-DRR3ijKM.js +0 -20
- package/dist/prepare-Bl5WcC3f.js +0 -1713
- package/dist/prepare.test-helpers-BcAo4KMw.js +0 -49
- package/dist/reply-blocks-BlOURkUm.js +0 -290
- package/dist/room-context-BmNTBiw5.js +0 -816
- package/dist/send.runtime-Dv6ajTGK.js +0 -2
- package/dist/send.runtime-v3TSw9xY.js +0 -2
- package/dist/test-api.js +0 -8
- package/dist/thread-ts-ks-O8cEG.js +0 -52
- package/node_modules/agent-base/LICENSE +0 -22
- package/node_modules/agent-base/README.md +0 -69
- package/node_modules/agent-base/dist/helpers.d.ts +0 -10
- package/node_modules/agent-base/dist/helpers.d.ts.map +0 -1
- package/node_modules/agent-base/dist/helpers.js +0 -37
- package/node_modules/agent-base/dist/helpers.js.map +0 -1
- package/node_modules/agent-base/dist/index.d.ts +0 -37
- package/node_modules/agent-base/dist/index.d.ts.map +0 -1
- package/node_modules/agent-base/dist/index.js +0 -146
- package/node_modules/agent-base/dist/index.js.map +0 -1
- package/node_modules/agent-base/package.json +0 -46
- package/node_modules/https-proxy-agent/LICENSE +0 -22
- package/node_modules/https-proxy-agent/README.md +0 -70
- package/node_modules/https-proxy-agent/dist/index.d.ts +0 -43
- package/node_modules/https-proxy-agent/dist/index.d.ts.map +0 -1
- package/node_modules/https-proxy-agent/dist/index.js +0 -150
- package/node_modules/https-proxy-agent/dist/index.js.map +0 -1
- package/node_modules/https-proxy-agent/dist/parse-proxy-response.d.ts +0 -12
- package/node_modules/https-proxy-agent/dist/parse-proxy-response.d.ts.map +0 -1
- package/node_modules/https-proxy-agent/dist/parse-proxy-response.js +0 -94
- package/node_modules/https-proxy-agent/dist/parse-proxy-response.js.map +0 -1
- package/node_modules/https-proxy-agent/package.json +0 -50
|
@@ -1,816 +0,0 @@
|
|
|
1
|
-
import { a as normalizeSlackSlug, i as normalizeSlackAllowOwnerEntry, n as normalizeAllowList, o as resolveSlackAllowListMatch, r as normalizeAllowListLower, t as allowListMatches } from "./allow-list-B1lkGjwl.js";
|
|
2
|
-
import { t as formatSlackError } from "./errors-CZtmv-h0.js";
|
|
3
|
-
import { n as resolveSlackChannelConfig, t as isSlackChannelAllowedByPolicy } from "./policy-pu8tsF8_.js";
|
|
4
|
-
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
5
|
-
import { resolveAgentRoute, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
|
|
6
|
-
import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
|
|
7
|
-
import { formatAllowlistMatchMeta } from "openclaw/plugin-sdk/allow-from";
|
|
8
|
-
import { getChildLogger, logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
9
|
-
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
10
|
-
import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
|
|
11
|
-
import { buildPluginBindingResolvedText, parsePluginBindingApprovalCustomId, recordInboundSession, resolveConversationLabel as resolveConversationLabel$1, resolvePluginConversationBindingApproval, resolveRuntimeConversationBindingRoute, upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
|
|
12
|
-
import { getRuntimeConfig as getRuntimeConfig$1 } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
13
|
-
import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
|
|
14
|
-
import { readSessionUpdatedAt, resolveSessionKey, resolveStorePath as resolveStorePath$1, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
|
|
15
|
-
import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
|
|
16
|
-
import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy as resolveOpenProviderRuntimeGroupPolicy$1, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
|
|
17
|
-
import { resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
|
|
18
|
-
import { createChannelIngressResolver, defineStableChannelIngressIdentity, readChannelIngressStoreAllowFromForDmPolicy } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
19
|
-
import { buildUntrustedChannelMetadata } from "openclaw/plugin-sdk/security-runtime";
|
|
20
|
-
//#region extensions/slack/src/monitor/commands.ts
|
|
21
|
-
/**
|
|
22
|
-
* Strip Slack mentions (<@U123>, <@U123|name>) so command detection works on
|
|
23
|
-
* normalized text. Use in both prepare and debounce gate for consistency.
|
|
24
|
-
*/
|
|
25
|
-
function stripSlackMentionsForCommandDetection(text) {
|
|
26
|
-
return (text ?? "").replace(/<@[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
27
|
-
}
|
|
28
|
-
function normalizeSlackSlashCommandName(raw) {
|
|
29
|
-
return raw.replace(/^\/+/, "");
|
|
30
|
-
}
|
|
31
|
-
function resolveSlackSlashCommandConfig(raw) {
|
|
32
|
-
const name = normalizeSlackSlashCommandName(normalizeOptionalString(raw?.name) ?? "openclaw") || "openclaw";
|
|
33
|
-
return {
|
|
34
|
-
enabled: raw?.enabled === true,
|
|
35
|
-
name,
|
|
36
|
-
sessionPrefix: normalizeOptionalString(raw?.sessionPrefix) ?? "slack:slash",
|
|
37
|
-
ephemeral: raw?.ephemeral !== false
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
function buildSlackSlashCommandMatcher(name) {
|
|
41
|
-
const escaped = normalizeSlackSlashCommandName(name).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
42
|
-
return new RegExp(`^/?${escaped}$`);
|
|
43
|
-
}
|
|
44
|
-
//#endregion
|
|
45
|
-
//#region extensions/slack/src/monitor/channel-type.ts
|
|
46
|
-
function inferSlackChannelType(channelId) {
|
|
47
|
-
const trimmed = channelId?.trim();
|
|
48
|
-
if (!trimmed) return;
|
|
49
|
-
if (trimmed.startsWith("D")) return "im";
|
|
50
|
-
if (trimmed.startsWith("C")) return "channel";
|
|
51
|
-
if (trimmed.startsWith("G")) return "group";
|
|
52
|
-
}
|
|
53
|
-
function normalizeSlackChannelType(channelType, channelId) {
|
|
54
|
-
const normalized = normalizeOptionalLowercaseString(channelType);
|
|
55
|
-
const inferred = inferSlackChannelType(channelId);
|
|
56
|
-
if (normalized === "im" || normalized === "mpim" || normalized === "channel" || normalized === "group") {
|
|
57
|
-
if (inferred === "im" && normalized !== "im") return "im";
|
|
58
|
-
return normalized;
|
|
59
|
-
}
|
|
60
|
-
return inferred ?? "channel";
|
|
61
|
-
}
|
|
62
|
-
function resolveSlackChatType(channelType) {
|
|
63
|
-
if (channelType === "im") return "direct";
|
|
64
|
-
if (channelType === "mpim") return "group";
|
|
65
|
-
return "channel";
|
|
66
|
-
}
|
|
67
|
-
//#endregion
|
|
68
|
-
//#region extensions/slack/src/monitor/context.ts
|
|
69
|
-
const SLACK_ASSISTANT_THREAD_CONTEXT_METADATA_EVENT = "assistant_thread_context";
|
|
70
|
-
function buildSlackAssistantThreadMetadata(context) {
|
|
71
|
-
const eventPayload = {};
|
|
72
|
-
if (context.channelId) eventPayload.channel_id = context.channelId;
|
|
73
|
-
if (context.teamId) eventPayload.team_id = context.teamId;
|
|
74
|
-
if (context.enterpriseId) eventPayload.enterprise_id = context.enterpriseId;
|
|
75
|
-
return {
|
|
76
|
-
event_type: SLACK_ASSISTANT_THREAD_CONTEXT_METADATA_EVENT,
|
|
77
|
-
event_payload: eventPayload
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
function parseSlackAssistantThreadMetadata(value) {
|
|
81
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
82
|
-
const metadata = value;
|
|
83
|
-
if (metadata.event_type !== "assistant_thread_context") return;
|
|
84
|
-
const payload = metadata.event_payload;
|
|
85
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return;
|
|
86
|
-
const record = payload;
|
|
87
|
-
const stringField = (key) => {
|
|
88
|
-
const raw = record[key];
|
|
89
|
-
return typeof raw === "string" && raw.trim() ? raw.trim() : void 0;
|
|
90
|
-
};
|
|
91
|
-
return {
|
|
92
|
-
channelId: stringField("channel_id"),
|
|
93
|
-
teamId: stringField("team_id"),
|
|
94
|
-
enterpriseId: stringField("enterprise_id")
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
const SLACK_ASSISTANT_CONTEXT_TTL_MS = 1440 * 60 * 1e3;
|
|
98
|
-
const SLACK_ASSISTANT_CONTEXT_CLEANUP_INTERVAL_MS = 600 * 1e3;
|
|
99
|
-
function createSlackMonitorContext(params) {
|
|
100
|
-
const channelHistories = /* @__PURE__ */ new Map();
|
|
101
|
-
const logger = getChildLogger({ module: "slack-auto-reply" });
|
|
102
|
-
const channelCache = /* @__PURE__ */ new Map();
|
|
103
|
-
const userCache = /* @__PURE__ */ new Map();
|
|
104
|
-
const seenMessages = createDedupeCache({
|
|
105
|
-
ttlMs: 6e4,
|
|
106
|
-
maxSize: 500
|
|
107
|
-
});
|
|
108
|
-
const assistantThreadContexts = /* @__PURE__ */ new Map();
|
|
109
|
-
let lastAssistantContextCleanupAt = Date.now();
|
|
110
|
-
const allowFrom = normalizeAllowList(params.allowFrom);
|
|
111
|
-
const groupDmChannels = normalizeAllowList(params.groupDmChannels);
|
|
112
|
-
const groupDmChannelsLower = normalizeAllowListLower(groupDmChannels);
|
|
113
|
-
const defaultRequireMention = params.defaultRequireMention ?? true;
|
|
114
|
-
const hasChannelAllowlistConfig = Object.keys(params.channelsConfig ?? {}).length > 0;
|
|
115
|
-
const channelsConfigKeys = Object.keys(params.channelsConfig ?? {});
|
|
116
|
-
const markMessageSeen = (channelId, ts) => {
|
|
117
|
-
if (!channelId || !ts) return false;
|
|
118
|
-
return seenMessages.check(`${channelId}:${ts}`);
|
|
119
|
-
};
|
|
120
|
-
const releaseSeenMessage = (channelId, ts) => {
|
|
121
|
-
if (!channelId || !ts) return;
|
|
122
|
-
seenMessages.delete(`${channelId}:${ts}`);
|
|
123
|
-
};
|
|
124
|
-
const assistantContextKey = (channelId, threadTs) => `${channelId}:${threadTs}`;
|
|
125
|
-
const cleanupAssistantThreadContexts = () => {
|
|
126
|
-
const now = Date.now();
|
|
127
|
-
if (now - lastAssistantContextCleanupAt < SLACK_ASSISTANT_CONTEXT_CLEANUP_INTERVAL_MS) return;
|
|
128
|
-
lastAssistantContextCleanupAt = now;
|
|
129
|
-
const cutoff = now - SLACK_ASSISTANT_CONTEXT_TTL_MS;
|
|
130
|
-
for (const [key, entry] of assistantThreadContexts) if (entry.updatedAt < cutoff) assistantThreadContexts.delete(key);
|
|
131
|
-
};
|
|
132
|
-
const getSlackAssistantThreadContext = (channelId, threadTs) => {
|
|
133
|
-
if (!channelId || !threadTs) return;
|
|
134
|
-
const key = assistantContextKey(channelId, threadTs);
|
|
135
|
-
const entry = assistantThreadContexts.get(key);
|
|
136
|
-
if (!entry) return;
|
|
137
|
-
if (Date.now() - entry.updatedAt > SLACK_ASSISTANT_CONTEXT_TTL_MS) {
|
|
138
|
-
assistantThreadContexts.delete(key);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
return entry;
|
|
142
|
-
};
|
|
143
|
-
const saveSlackAssistantThreadContext = (context) => {
|
|
144
|
-
cleanupAssistantThreadContexts();
|
|
145
|
-
assistantThreadContexts.set(assistantContextKey(context.assistantChannelId, context.threadTs), {
|
|
146
|
-
...context,
|
|
147
|
-
updatedAt: Date.now()
|
|
148
|
-
});
|
|
149
|
-
};
|
|
150
|
-
const resolveSlackSystemEventSessionKey = (p) => {
|
|
151
|
-
const channelId = normalizeOptionalString(p.channelId) ?? "";
|
|
152
|
-
if (!channelId) return params.mainKey;
|
|
153
|
-
const channelType = normalizeSlackChannelType(p.channelType, channelId);
|
|
154
|
-
const isDirectMessage = channelType === "im";
|
|
155
|
-
const isGroup = channelType === "mpim";
|
|
156
|
-
const from = isDirectMessage ? `slack:${channelId}` : isGroup ? `slack:group:${channelId}` : `slack:channel:${channelId}`;
|
|
157
|
-
const chatType = isDirectMessage ? "direct" : isGroup ? "group" : "channel";
|
|
158
|
-
const senderId = normalizeOptionalString(p.senderId) ?? "";
|
|
159
|
-
try {
|
|
160
|
-
const peerKind = isDirectMessage ? "direct" : isGroup ? "group" : "channel";
|
|
161
|
-
const peerId = isDirectMessage ? senderId : channelId;
|
|
162
|
-
if (peerId) {
|
|
163
|
-
const route = resolveAgentRoute({
|
|
164
|
-
cfg: params.cfg,
|
|
165
|
-
channel: "slack",
|
|
166
|
-
accountId: params.accountId,
|
|
167
|
-
teamId: params.teamId,
|
|
168
|
-
peer: {
|
|
169
|
-
kind: peerKind,
|
|
170
|
-
id: peerId
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
const threadTs = normalizeOptionalString(p.threadTs);
|
|
174
|
-
const baseConversationId = isDirectMessage ? `user:${senderId}` : channelId;
|
|
175
|
-
const threadBindingRoute = threadTs ? resolveRuntimeConversationBindingRoute({
|
|
176
|
-
route,
|
|
177
|
-
conversation: {
|
|
178
|
-
channel: "slack",
|
|
179
|
-
accountId: params.accountId,
|
|
180
|
-
conversationId: threadTs,
|
|
181
|
-
parentConversationId: baseConversationId
|
|
182
|
-
}
|
|
183
|
-
}) : null;
|
|
184
|
-
const runtimeRoute = threadBindingRoute?.boundSessionKey || threadBindingRoute?.bindingRecord ? threadBindingRoute : resolveRuntimeConversationBindingRoute({
|
|
185
|
-
route,
|
|
186
|
-
conversation: {
|
|
187
|
-
channel: "slack",
|
|
188
|
-
accountId: params.accountId,
|
|
189
|
-
conversationId: baseConversationId
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
if (runtimeRoute.boundSessionKey) return runtimeRoute.route.sessionKey;
|
|
193
|
-
return resolveThreadSessionKeys({
|
|
194
|
-
baseSessionKey: runtimeRoute.route.sessionKey,
|
|
195
|
-
threadId: threadTs,
|
|
196
|
-
parentSessionKey: threadTs && params.threadInheritParent ? runtimeRoute.route.sessionKey : void 0
|
|
197
|
-
}).sessionKey;
|
|
198
|
-
}
|
|
199
|
-
} catch {}
|
|
200
|
-
const legacySessionKey = resolveSessionKey(params.sessionScope, {
|
|
201
|
-
From: from,
|
|
202
|
-
ChatType: chatType,
|
|
203
|
-
Provider: "slack"
|
|
204
|
-
}, params.mainKey, resolveDefaultAgentId(params.cfg));
|
|
205
|
-
return resolveThreadSessionKeys({
|
|
206
|
-
baseSessionKey: legacySessionKey,
|
|
207
|
-
threadId: normalizeOptionalString(p.threadTs),
|
|
208
|
-
parentSessionKey: normalizeOptionalString(p.threadTs) && params.threadInheritParent ? legacySessionKey : void 0
|
|
209
|
-
}).sessionKey;
|
|
210
|
-
};
|
|
211
|
-
const resolveChannelName = async (channelId) => {
|
|
212
|
-
const cached = channelCache.get(channelId);
|
|
213
|
-
if (cached) return cached;
|
|
214
|
-
try {
|
|
215
|
-
const info = await params.app.client.conversations.info({
|
|
216
|
-
token: params.botToken,
|
|
217
|
-
channel: channelId
|
|
218
|
-
});
|
|
219
|
-
const name = info.channel && "name" in info.channel ? info.channel.name : void 0;
|
|
220
|
-
const channel = info.channel ?? void 0;
|
|
221
|
-
const entry = {
|
|
222
|
-
name,
|
|
223
|
-
type: channel?.is_im ? "im" : channel?.is_mpim ? "mpim" : channel?.is_channel ? "channel" : channel?.is_group ? "group" : void 0,
|
|
224
|
-
topic: channel && "topic" in channel ? channel.topic?.value ?? void 0 : void 0,
|
|
225
|
-
purpose: channel && "purpose" in channel ? channel.purpose?.value ?? void 0 : void 0
|
|
226
|
-
};
|
|
227
|
-
channelCache.set(channelId, entry);
|
|
228
|
-
return entry;
|
|
229
|
-
} catch {
|
|
230
|
-
return {};
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
const resolveUserName = async (userId) => {
|
|
234
|
-
const cached = userCache.get(userId);
|
|
235
|
-
if (cached) return cached;
|
|
236
|
-
try {
|
|
237
|
-
const info = await params.app.client.users.info({
|
|
238
|
-
token: params.botToken,
|
|
239
|
-
user: userId
|
|
240
|
-
});
|
|
241
|
-
const profile = info.user?.profile;
|
|
242
|
-
const entry = { name: profile?.display_name || profile?.real_name || info.user?.name || void 0 };
|
|
243
|
-
userCache.set(userId, entry);
|
|
244
|
-
return entry;
|
|
245
|
-
} catch {
|
|
246
|
-
return {};
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
const setSlackThreadStatus = async (p) => {
|
|
250
|
-
if (!p.threadTs) return;
|
|
251
|
-
try {
|
|
252
|
-
await params.app.client.assistant.threads.setStatus({
|
|
253
|
-
token: params.botToken,
|
|
254
|
-
channel_id: p.channelId,
|
|
255
|
-
thread_ts: p.threadTs,
|
|
256
|
-
status: p.status
|
|
257
|
-
});
|
|
258
|
-
} catch (err) {
|
|
259
|
-
logVerbose(`slack status update failed for channel ${p.channelId}: ${formatSlackError(err)}`);
|
|
260
|
-
}
|
|
261
|
-
};
|
|
262
|
-
const setSlackAssistantSuggestedPrompts = async (p) => {
|
|
263
|
-
const prompts = p.prompts.map((prompt) => ({
|
|
264
|
-
title: prompt.title.trim(),
|
|
265
|
-
message: prompt.message.trim()
|
|
266
|
-
})).filter((prompt) => prompt.title && prompt.message).slice(0, 4);
|
|
267
|
-
if (prompts.length === 0) return false;
|
|
268
|
-
try {
|
|
269
|
-
await params.app.client.assistant.threads.setSuggestedPrompts({
|
|
270
|
-
token: params.botToken,
|
|
271
|
-
channel_id: p.channelId,
|
|
272
|
-
thread_ts: p.threadTs,
|
|
273
|
-
...p.title?.trim() ? { title: p.title.trim() } : {},
|
|
274
|
-
prompts
|
|
275
|
-
});
|
|
276
|
-
return true;
|
|
277
|
-
} catch (err) {
|
|
278
|
-
logVerbose(`slack suggested prompts update failed for channel ${p.channelId}: ${formatSlackError(err)}`);
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
};
|
|
282
|
-
const isChannelAllowed = (p) => {
|
|
283
|
-
const channelType = normalizeSlackChannelType(p.channelType, p.channelId);
|
|
284
|
-
const isDirectMessage = channelType === "im";
|
|
285
|
-
const isGroupDm = channelType === "mpim";
|
|
286
|
-
const isRoom = channelType === "channel" || channelType === "group";
|
|
287
|
-
if (isDirectMessage && !params.dmEnabled) return false;
|
|
288
|
-
if (isGroupDm && !params.groupDmEnabled) return false;
|
|
289
|
-
if (isGroupDm && groupDmChannels.length > 0) {
|
|
290
|
-
const candidates = [
|
|
291
|
-
p.channelId,
|
|
292
|
-
p.channelName ? `#${p.channelName}` : void 0,
|
|
293
|
-
p.channelName,
|
|
294
|
-
p.channelName ? normalizeSlackSlug(p.channelName) : void 0
|
|
295
|
-
].filter((value) => Boolean(value)).map((value) => normalizeLowercaseStringOrEmpty(value));
|
|
296
|
-
if (!(groupDmChannelsLower.includes("*") || candidates.some((candidate) => groupDmChannelsLower.includes(candidate)))) return false;
|
|
297
|
-
}
|
|
298
|
-
if (isRoom && p.channelId) {
|
|
299
|
-
const channelConfig = resolveSlackChannelConfig({
|
|
300
|
-
channelId: p.channelId,
|
|
301
|
-
channelName: p.channelName,
|
|
302
|
-
channels: params.channelsConfig,
|
|
303
|
-
channelKeys: channelsConfigKeys,
|
|
304
|
-
defaultRequireMention,
|
|
305
|
-
allowNameMatching: params.allowNameMatching
|
|
306
|
-
});
|
|
307
|
-
const channelMatchMeta = formatAllowlistMatchMeta(channelConfig);
|
|
308
|
-
const channelAllowed = channelConfig?.allowed !== false;
|
|
309
|
-
const channelAllowlistConfigured = hasChannelAllowlistConfig;
|
|
310
|
-
if (!isSlackChannelAllowedByPolicy({
|
|
311
|
-
groupPolicy: params.groupPolicy,
|
|
312
|
-
channelAllowlistConfigured,
|
|
313
|
-
channelAllowed
|
|
314
|
-
})) {
|
|
315
|
-
logVerbose(`slack: drop channel ${p.channelId} (groupPolicy=${params.groupPolicy}, ${channelMatchMeta})`);
|
|
316
|
-
return false;
|
|
317
|
-
}
|
|
318
|
-
const hasExplicitConfig = Boolean(channelConfig?.matchSource);
|
|
319
|
-
if (!channelAllowed && (params.groupPolicy !== "open" || hasExplicitConfig)) {
|
|
320
|
-
logVerbose(`slack: drop channel ${p.channelId} (${channelMatchMeta})`);
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
323
|
-
logVerbose(`slack: allow channel ${p.channelId} (${channelMatchMeta})`);
|
|
324
|
-
}
|
|
325
|
-
return true;
|
|
326
|
-
};
|
|
327
|
-
const shouldDropMismatchedSlackEvent = (body) => {
|
|
328
|
-
if (!body || typeof body !== "object") return false;
|
|
329
|
-
const raw = body;
|
|
330
|
-
const incomingApiAppId = typeof raw.api_app_id === "string" ? raw.api_app_id : "";
|
|
331
|
-
const incomingTeamId = typeof raw.team_id === "string" ? raw.team_id : typeof raw.team?.id === "string" ? raw.team.id : "";
|
|
332
|
-
if (params.apiAppId && incomingApiAppId && incomingApiAppId !== params.apiAppId) {
|
|
333
|
-
logVerbose(`slack: drop event with api_app_id=${incomingApiAppId} (expected ${params.apiAppId})`);
|
|
334
|
-
return true;
|
|
335
|
-
}
|
|
336
|
-
if (params.teamId && incomingTeamId && incomingTeamId !== params.teamId) {
|
|
337
|
-
logVerbose(`slack: drop event with team_id=${incomingTeamId} (expected ${params.teamId})`);
|
|
338
|
-
return true;
|
|
339
|
-
}
|
|
340
|
-
return false;
|
|
341
|
-
};
|
|
342
|
-
return {
|
|
343
|
-
cfg: params.cfg,
|
|
344
|
-
accountId: params.accountId,
|
|
345
|
-
botToken: params.botToken,
|
|
346
|
-
app: params.app,
|
|
347
|
-
runtime: params.runtime,
|
|
348
|
-
botUserId: params.botUserId,
|
|
349
|
-
botId: params.botId,
|
|
350
|
-
teamId: params.teamId,
|
|
351
|
-
apiAppId: params.apiAppId,
|
|
352
|
-
historyLimit: params.historyLimit,
|
|
353
|
-
dmHistoryLimit: Math.max(0, params.dmHistoryLimit ?? 0),
|
|
354
|
-
channelHistories,
|
|
355
|
-
sessionScope: params.sessionScope,
|
|
356
|
-
mainKey: params.mainKey,
|
|
357
|
-
dmEnabled: params.dmEnabled,
|
|
358
|
-
dmPolicy: params.dmPolicy,
|
|
359
|
-
allowFrom,
|
|
360
|
-
allowNameMatching: params.allowNameMatching,
|
|
361
|
-
groupDmEnabled: params.groupDmEnabled,
|
|
362
|
-
groupDmChannels,
|
|
363
|
-
defaultRequireMention,
|
|
364
|
-
channelsConfig: params.channelsConfig,
|
|
365
|
-
channelsConfigKeys,
|
|
366
|
-
groupPolicy: params.groupPolicy,
|
|
367
|
-
useAccessGroups: params.useAccessGroups,
|
|
368
|
-
reactionMode: params.reactionMode,
|
|
369
|
-
reactionAllowlist: params.reactionAllowlist,
|
|
370
|
-
replyToMode: params.replyToMode,
|
|
371
|
-
threadHistoryScope: params.threadHistoryScope,
|
|
372
|
-
threadInheritParent: params.threadInheritParent,
|
|
373
|
-
threadRequireExplicitMention: params.threadRequireExplicitMention,
|
|
374
|
-
slashCommand: params.slashCommand,
|
|
375
|
-
textLimit: params.textLimit,
|
|
376
|
-
ackReactionScope: params.ackReactionScope,
|
|
377
|
-
typingReaction: params.typingReaction,
|
|
378
|
-
mediaMaxBytes: params.mediaMaxBytes,
|
|
379
|
-
removeAckAfterReply: params.removeAckAfterReply,
|
|
380
|
-
logger,
|
|
381
|
-
markMessageSeen,
|
|
382
|
-
releaseSeenMessage,
|
|
383
|
-
shouldDropMismatchedSlackEvent,
|
|
384
|
-
resolveSlackSystemEventSessionKey,
|
|
385
|
-
isChannelAllowed,
|
|
386
|
-
resolveChannelName,
|
|
387
|
-
resolveUserName,
|
|
388
|
-
setSlackThreadStatus,
|
|
389
|
-
getSlackAssistantThreadContext,
|
|
390
|
-
saveSlackAssistantThreadContext,
|
|
391
|
-
setSlackAssistantSuggestedPrompts
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
//#endregion
|
|
395
|
-
//#region extensions/slack/src/monitor/auth.ts
|
|
396
|
-
let slackChannelMembersCache = /* @__PURE__ */ new WeakMap();
|
|
397
|
-
const DEFAULT_CHANNEL_MEMBERS_CACHE_TTL_MS = 6e4;
|
|
398
|
-
const CHANNEL_MEMBERS_CACHE_MAX = 512;
|
|
399
|
-
const SLACK_CHANNEL_ID = "slack";
|
|
400
|
-
const SLACK_USER_NAME_KIND = "plugin:slack-user-name";
|
|
401
|
-
function normalizeSlackUserId(raw) {
|
|
402
|
-
const value = (raw ?? "").trim().toLowerCase();
|
|
403
|
-
if (!value) return "";
|
|
404
|
-
const mention = value.match(/^<@([a-z0-9_]+)>$/i);
|
|
405
|
-
if (mention?.[1]) return mention[1];
|
|
406
|
-
return value.replace(/^(slack:|user:)/, "");
|
|
407
|
-
}
|
|
408
|
-
function isSlackStableUserId(value) {
|
|
409
|
-
return /^[ubw][a-z0-9_]+$/i.test(value);
|
|
410
|
-
}
|
|
411
|
-
function normalizeSlackStableEntry(entry) {
|
|
412
|
-
const normalized = entry.trim().toLowerCase();
|
|
413
|
-
if (!normalized) return null;
|
|
414
|
-
const userId = normalizeSlackUserId(normalized);
|
|
415
|
-
return isSlackStableUserId(userId) ? userId : null;
|
|
416
|
-
}
|
|
417
|
-
function normalizeSlackNameEntry(entry) {
|
|
418
|
-
const normalized = entry.trim().toLowerCase();
|
|
419
|
-
if (!normalized || normalizeSlackStableEntry(normalized)) return null;
|
|
420
|
-
return normalized.replace(/^slack:/, "") || null;
|
|
421
|
-
}
|
|
422
|
-
function normalizeSlackNameSubject(value) {
|
|
423
|
-
return value.trim().toLowerCase() || null;
|
|
424
|
-
}
|
|
425
|
-
function normalizeSlackNameSlugEntry(entry) {
|
|
426
|
-
const name = normalizeSlackNameEntry(entry);
|
|
427
|
-
if (!name) return null;
|
|
428
|
-
const slug = normalizeSlackSlug(name);
|
|
429
|
-
return slug && slug !== name ? slug : null;
|
|
430
|
-
}
|
|
431
|
-
const slackIngressIdentity = defineStableChannelIngressIdentity({
|
|
432
|
-
key: "senderId",
|
|
433
|
-
kind: "stable-id",
|
|
434
|
-
normalizeEntry: normalizeSlackStableEntry,
|
|
435
|
-
normalizeSubject: normalizeSlackUserId,
|
|
436
|
-
sensitivity: "pii",
|
|
437
|
-
aliases: [["senderName", normalizeSlackNameEntry], ["senderNameSlug", normalizeSlackNameSlugEntry]].map(([key, normalizeEntry]) => ({
|
|
438
|
-
key,
|
|
439
|
-
kind: SLACK_USER_NAME_KIND,
|
|
440
|
-
normalizeEntry,
|
|
441
|
-
normalizeSubject: normalizeSlackNameSubject,
|
|
442
|
-
dangerous: true,
|
|
443
|
-
sensitivity: "pii"
|
|
444
|
-
}))
|
|
445
|
-
});
|
|
446
|
-
function createSlackIngressSubject(params) {
|
|
447
|
-
const senderId = normalizeSlackUserId(params.senderId);
|
|
448
|
-
const senderName = params.senderName?.trim().toLowerCase();
|
|
449
|
-
return {
|
|
450
|
-
stableId: senderId,
|
|
451
|
-
aliases: {
|
|
452
|
-
senderName,
|
|
453
|
-
senderNameSlug: senderName ? normalizeSlackSlug(senderName) : void 0
|
|
454
|
-
}
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
function createSlackIngressResolver(ctx) {
|
|
458
|
-
return createChannelIngressResolver({
|
|
459
|
-
channelId: SLACK_CHANNEL_ID,
|
|
460
|
-
accountId: ctx.accountId,
|
|
461
|
-
identity: slackIngressIdentity,
|
|
462
|
-
cfg: ctx.cfg
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
function readSlackCacheTtlMs(envName, fallback) {
|
|
466
|
-
const raw = process.env[envName]?.trim();
|
|
467
|
-
if (!raw) return fallback;
|
|
468
|
-
const parsed = Number(raw);
|
|
469
|
-
return Number.isFinite(parsed) ? Math.max(0, Math.floor(parsed)) : fallback;
|
|
470
|
-
}
|
|
471
|
-
function getChannelMembersCache(ctx) {
|
|
472
|
-
const existing = slackChannelMembersCache.get(ctx);
|
|
473
|
-
if (existing) return existing;
|
|
474
|
-
const next = /* @__PURE__ */ new Map();
|
|
475
|
-
slackChannelMembersCache.set(ctx, next);
|
|
476
|
-
return next;
|
|
477
|
-
}
|
|
478
|
-
function pruneChannelMembersCache(cache) {
|
|
479
|
-
while (cache.size > CHANNEL_MEMBERS_CACHE_MAX) {
|
|
480
|
-
const oldest = cache.keys().next();
|
|
481
|
-
if (oldest.done) return;
|
|
482
|
-
cache.delete(oldest.value);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
function buildBaseAllowFrom(ctx) {
|
|
486
|
-
return normalizeAllowListLower(normalizeAllowList(ctx.allowFrom));
|
|
487
|
-
}
|
|
488
|
-
async function resolveSlackEffectiveAllowFrom(ctx, options) {
|
|
489
|
-
const base = buildBaseAllowFrom(ctx);
|
|
490
|
-
if (options?.includePairingStore !== true) return base;
|
|
491
|
-
let storeAllowFrom = [];
|
|
492
|
-
try {
|
|
493
|
-
const resolved = await readChannelIngressStoreAllowFromForDmPolicy({
|
|
494
|
-
provider: "slack",
|
|
495
|
-
accountId: ctx.accountId,
|
|
496
|
-
dmPolicy: ctx.dmPolicy
|
|
497
|
-
});
|
|
498
|
-
storeAllowFrom = Array.isArray(resolved) ? resolved : [];
|
|
499
|
-
} catch {
|
|
500
|
-
storeAllowFrom = [];
|
|
501
|
-
}
|
|
502
|
-
return normalizeAllowListLower([...base, ...storeAllowFrom]);
|
|
503
|
-
}
|
|
504
|
-
async function fetchSlackChannelMemberIds(ctx, channelId) {
|
|
505
|
-
const members = /* @__PURE__ */ new Set();
|
|
506
|
-
let cursor;
|
|
507
|
-
do {
|
|
508
|
-
const response = await ctx.app.client.conversations.members({
|
|
509
|
-
token: ctx.botToken,
|
|
510
|
-
channel: channelId,
|
|
511
|
-
limit: 999,
|
|
512
|
-
...cursor ? { cursor } : {}
|
|
513
|
-
});
|
|
514
|
-
for (const member of normalizeAllowListLower(response.members)) members.add(member);
|
|
515
|
-
const nextCursor = response.response_metadata?.next_cursor?.trim();
|
|
516
|
-
cursor = nextCursor ? nextCursor : void 0;
|
|
517
|
-
} while (cursor);
|
|
518
|
-
return members;
|
|
519
|
-
}
|
|
520
|
-
async function resolveSlackChannelMemberIds(ctx, channelId) {
|
|
521
|
-
const cache = getChannelMembersCache(ctx);
|
|
522
|
-
const key = `${ctx.accountId}:${channelId}`;
|
|
523
|
-
const ttlMs = readSlackCacheTtlMs("OPENCLAW_SLACK_CHANNEL_MEMBERS_CACHE_TTL_MS", DEFAULT_CHANNEL_MEMBERS_CACHE_TTL_MS);
|
|
524
|
-
const nowMs = Date.now();
|
|
525
|
-
const cached = cache.get(key);
|
|
526
|
-
if (ttlMs > 0 && cached?.members && cached.expiresAtMs >= nowMs) return cached.members;
|
|
527
|
-
if (cached?.pending) return await cached.pending;
|
|
528
|
-
const pending = fetchSlackChannelMemberIds(ctx, channelId);
|
|
529
|
-
cache.set(key, {
|
|
530
|
-
expiresAtMs: ttlMs > 0 ? nowMs + ttlMs : 0,
|
|
531
|
-
pending
|
|
532
|
-
});
|
|
533
|
-
pruneChannelMembersCache(cache);
|
|
534
|
-
try {
|
|
535
|
-
const members = await pending;
|
|
536
|
-
if (ttlMs > 0) {
|
|
537
|
-
cache.set(key, {
|
|
538
|
-
expiresAtMs: Date.now() + ttlMs,
|
|
539
|
-
members
|
|
540
|
-
});
|
|
541
|
-
pruneChannelMembersCache(cache);
|
|
542
|
-
} else cache.delete(key);
|
|
543
|
-
return members;
|
|
544
|
-
} finally {
|
|
545
|
-
if (cache.get(key)?.pending === pending) cache.delete(key);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
function resolveExplicitSlackOwnerIds(allowFromLower) {
|
|
549
|
-
const ownerIds = /* @__PURE__ */ new Set();
|
|
550
|
-
for (const entry of allowFromLower) {
|
|
551
|
-
const ownerId = normalizeSlackAllowOwnerEntry(entry);
|
|
552
|
-
if (ownerId) ownerIds.add(ownerId);
|
|
553
|
-
}
|
|
554
|
-
return [...ownerIds];
|
|
555
|
-
}
|
|
556
|
-
async function authorizeSlackBotRoomMessage(params) {
|
|
557
|
-
const channelUserAllowList = normalizeAllowListLower(params.channelUsers).filter((entry) => entry !== "*");
|
|
558
|
-
if (channelUserAllowList.length > 0 && allowListMatches({
|
|
559
|
-
allowList: channelUserAllowList,
|
|
560
|
-
id: params.senderId,
|
|
561
|
-
name: params.senderName,
|
|
562
|
-
allowNameMatching: params.ctx.allowNameMatching
|
|
563
|
-
})) return true;
|
|
564
|
-
const explicitOwnerIds = resolveExplicitSlackOwnerIds(params.allowFromLower);
|
|
565
|
-
if (explicitOwnerIds.length === 0) {
|
|
566
|
-
logVerbose(`slack: drop bot message ${params.senderId} in ${params.channelId} (no explicit owner id for presence check)`);
|
|
567
|
-
return false;
|
|
568
|
-
}
|
|
569
|
-
try {
|
|
570
|
-
const channelMemberIds = await resolveSlackChannelMemberIds(params.ctx, params.channelId);
|
|
571
|
-
if (explicitOwnerIds.some((ownerId) => channelMemberIds.has(ownerId))) return true;
|
|
572
|
-
logVerbose(`slack: drop bot message ${params.senderId} in ${params.channelId} (no owner present)`);
|
|
573
|
-
} catch (error) {
|
|
574
|
-
logVerbose(`slack: drop bot message ${params.senderId} in ${params.channelId} (owner presence lookup failed: ${formatErrorMessage(error)})`);
|
|
575
|
-
}
|
|
576
|
-
return false;
|
|
577
|
-
}
|
|
578
|
-
function wildcardWhenOpen(entries) {
|
|
579
|
-
return entries.length > 0 ? [...entries] : ["*"];
|
|
580
|
-
}
|
|
581
|
-
function slackIngressConversationKind(channelType) {
|
|
582
|
-
return channelType === "im" ? "direct" : channelType === "mpim" ? "group" : "channel";
|
|
583
|
-
}
|
|
584
|
-
async function resolveSlackCommandIngress(params) {
|
|
585
|
-
const isDirectMessage = params.channelType === "im";
|
|
586
|
-
const channelUsers = normalizeAllowListLower(params.channelUsers);
|
|
587
|
-
const channelUsersConfigured = !isDirectMessage && channelUsers.length > 0;
|
|
588
|
-
return await createSlackIngressResolver(params.ctx).message({
|
|
589
|
-
subject: createSlackIngressSubject({
|
|
590
|
-
senderId: params.senderId,
|
|
591
|
-
senderName: params.senderName
|
|
592
|
-
}),
|
|
593
|
-
conversation: {
|
|
594
|
-
kind: slackIngressConversationKind(params.channelType),
|
|
595
|
-
id: params.channelId
|
|
596
|
-
},
|
|
597
|
-
event: {
|
|
598
|
-
kind: params.eventKind ?? "message",
|
|
599
|
-
authMode: "inbound",
|
|
600
|
-
mayPair: false
|
|
601
|
-
},
|
|
602
|
-
dmPolicy: isDirectMessage ? "open" : "disabled",
|
|
603
|
-
groupPolicy: channelUsersConfigured ? "allowlist" : "open",
|
|
604
|
-
policy: {
|
|
605
|
-
groupAllowFromFallbackToAllowFrom: false,
|
|
606
|
-
mutableIdentifierMatching: params.ctx.allowNameMatching ? "enabled" : "disabled",
|
|
607
|
-
...params.activation ? { activation: params.activation } : {}
|
|
608
|
-
},
|
|
609
|
-
mentionFacts: params.mentionFacts,
|
|
610
|
-
allowFrom: isDirectMessage ? ["*"] : params.ownerAllowFromLower,
|
|
611
|
-
groupAllowFrom: channelUsersConfigured ? channelUsers : [],
|
|
612
|
-
command: {
|
|
613
|
-
allowTextCommands: params.allowTextCommands,
|
|
614
|
-
hasControlCommand: params.hasControlCommand,
|
|
615
|
-
modeWhenAccessGroupsOff: params.modeWhenAccessGroupsOff,
|
|
616
|
-
...isDirectMessage ? { commandOwnerAllowFrom: params.ownerAllowFromLower } : {}
|
|
617
|
-
}
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
async function decideSlackSystemIngress(params) {
|
|
621
|
-
const isDirectMessage = params.channelType === "im";
|
|
622
|
-
const channelUsers = normalizeAllowListLower(params.channelUsers);
|
|
623
|
-
const channelUsersConfigured = !isDirectMessage && channelUsers.length > 0;
|
|
624
|
-
const ownerAllowFrom = params.interactiveEvent && channelUsersConfigured ? params.ownerAllowFromLower.filter((entry) => entry !== "*") : params.ownerAllowFromLower;
|
|
625
|
-
const hasAnyCommandAllowlist = ownerAllowFrom.length > 0 || channelUsersConfigured;
|
|
626
|
-
const groupAllowFrom = (() => {
|
|
627
|
-
if (isDirectMessage) return [];
|
|
628
|
-
if (params.interactiveEvent && hasAnyCommandAllowlist) return channelUsersConfigured ? channelUsers : [];
|
|
629
|
-
if (channelUsersConfigured) return channelUsers;
|
|
630
|
-
return params.channelId ? ["*"] : wildcardWhenOpen(params.ownerAllowFromLower);
|
|
631
|
-
})();
|
|
632
|
-
return (await createSlackIngressResolver(params.ctx).message({
|
|
633
|
-
subject: createSlackIngressSubject({
|
|
634
|
-
senderId: params.senderId,
|
|
635
|
-
senderName: params.senderName
|
|
636
|
-
}),
|
|
637
|
-
conversation: {
|
|
638
|
-
kind: slackIngressConversationKind(params.channelType),
|
|
639
|
-
id: params.channelId ?? "slack-system"
|
|
640
|
-
},
|
|
641
|
-
event: {
|
|
642
|
-
kind: params.interactiveEvent ? "button" : "system",
|
|
643
|
-
authMode: params.interactiveEvent && hasAnyCommandAllowlist ? "command" : "inbound",
|
|
644
|
-
mayPair: false
|
|
645
|
-
},
|
|
646
|
-
dmPolicy: isDirectMessage ? "open" : "disabled",
|
|
647
|
-
groupPolicy: params.interactiveEvent && hasAnyCommandAllowlist ? "open" : channelUsersConfigured || !params.channelId && params.ownerAllowFromLower.length > 0 ? "allowlist" : "open",
|
|
648
|
-
policy: {
|
|
649
|
-
groupAllowFromFallbackToAllowFrom: false,
|
|
650
|
-
mutableIdentifierMatching: params.ctx.allowNameMatching ? "enabled" : "disabled"
|
|
651
|
-
},
|
|
652
|
-
allowFrom: isDirectMessage ? wildcardWhenOpen(params.ownerAllowFromLower) : ownerAllowFrom,
|
|
653
|
-
groupAllowFrom,
|
|
654
|
-
command: params.interactiveEvent && hasAnyCommandAllowlist ? {
|
|
655
|
-
useAccessGroups: true,
|
|
656
|
-
allowTextCommands: true,
|
|
657
|
-
modeWhenAccessGroupsOff: "configured",
|
|
658
|
-
commandOwnerAllowFrom: ownerAllowFrom
|
|
659
|
-
} : void 0
|
|
660
|
-
})).ingress;
|
|
661
|
-
}
|
|
662
|
-
async function authorizeSlackSystemEventSender(params) {
|
|
663
|
-
const senderId = params.senderId?.trim();
|
|
664
|
-
if (!senderId) return {
|
|
665
|
-
allowed: false,
|
|
666
|
-
reason: "missing-sender"
|
|
667
|
-
};
|
|
668
|
-
const expectedSenderId = params.expectedSenderId?.trim();
|
|
669
|
-
if (expectedSenderId && expectedSenderId !== senderId) return {
|
|
670
|
-
allowed: false,
|
|
671
|
-
reason: "sender-mismatch"
|
|
672
|
-
};
|
|
673
|
-
if (params.interactiveEvent && !expectedSenderId) return {
|
|
674
|
-
allowed: false,
|
|
675
|
-
reason: "missing-expected-sender"
|
|
676
|
-
};
|
|
677
|
-
const channelId = params.channelId?.trim();
|
|
678
|
-
let channelType = normalizeSlackChannelType(params.channelType, channelId);
|
|
679
|
-
let channelName;
|
|
680
|
-
if (channelId) {
|
|
681
|
-
const info = await params.ctx.resolveChannelName(channelId).catch(() => ({}));
|
|
682
|
-
channelName = info.name;
|
|
683
|
-
const resolvedTypeSource = params.channelType ?? info.type;
|
|
684
|
-
channelType = normalizeSlackChannelType(resolvedTypeSource, channelId);
|
|
685
|
-
if (!params.ctx.isChannelAllowed({
|
|
686
|
-
channelId,
|
|
687
|
-
channelName,
|
|
688
|
-
channelType
|
|
689
|
-
})) return {
|
|
690
|
-
allowed: false,
|
|
691
|
-
reason: "channel-not-allowed",
|
|
692
|
-
channelType,
|
|
693
|
-
channelName
|
|
694
|
-
};
|
|
695
|
-
if (params.interactiveEvent) {
|
|
696
|
-
const inferredFromId = inferSlackChannelType(channelId);
|
|
697
|
-
const sourceNormalized = typeof resolvedTypeSource === "string" ? resolvedTypeSource.toLowerCase().trim() : void 0;
|
|
698
|
-
if (inferredFromId === void 0 && !(sourceNormalized === "im" || sourceNormalized === "mpim" || sourceNormalized === "channel" || sourceNormalized === "group")) return {
|
|
699
|
-
allowed: false,
|
|
700
|
-
reason: "ambiguous-channel-type",
|
|
701
|
-
channelType,
|
|
702
|
-
channelName
|
|
703
|
-
};
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
const senderName = (await params.ctx.resolveUserName(senderId).catch(() => ({}))).name;
|
|
707
|
-
const ingressChannelType = channelType ?? "channel";
|
|
708
|
-
if (ingressChannelType === "im") {
|
|
709
|
-
if (!params.ctx.dmEnabled || params.ctx.dmPolicy === "disabled") return {
|
|
710
|
-
allowed: false,
|
|
711
|
-
reason: "dm-disabled",
|
|
712
|
-
channelType,
|
|
713
|
-
channelName
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
const allowFromLower = await resolveSlackEffectiveAllowFrom(params.ctx, { includePairingStore: ingressChannelType === "im" });
|
|
717
|
-
const channelConfig = channelId ? resolveSlackChannelConfig({
|
|
718
|
-
channelId,
|
|
719
|
-
channelName,
|
|
720
|
-
channels: params.ctx.channelsConfig,
|
|
721
|
-
channelKeys: params.ctx.channelsConfigKeys,
|
|
722
|
-
defaultRequireMention: params.ctx.defaultRequireMention,
|
|
723
|
-
allowNameMatching: params.ctx.allowNameMatching
|
|
724
|
-
}) : null;
|
|
725
|
-
const channelUsersAllowlistConfigured = Array.isArray(channelConfig?.users) && channelConfig.users.length > 0;
|
|
726
|
-
if ((await decideSlackSystemIngress({
|
|
727
|
-
ctx: params.ctx,
|
|
728
|
-
senderId,
|
|
729
|
-
senderName,
|
|
730
|
-
channelType: ingressChannelType,
|
|
731
|
-
channelId,
|
|
732
|
-
ownerAllowFromLower: allowFromLower,
|
|
733
|
-
channelUsers: channelConfig?.users,
|
|
734
|
-
interactiveEvent: params.interactiveEvent === true
|
|
735
|
-
})).decision === "allow") return {
|
|
736
|
-
allowed: true,
|
|
737
|
-
channelType,
|
|
738
|
-
channelName
|
|
739
|
-
};
|
|
740
|
-
if (channelType === "im" || !channelId) return {
|
|
741
|
-
allowed: false,
|
|
742
|
-
reason: "sender-not-allowlisted",
|
|
743
|
-
...channelId ? {
|
|
744
|
-
channelType,
|
|
745
|
-
channelName
|
|
746
|
-
} : {}
|
|
747
|
-
};
|
|
748
|
-
return {
|
|
749
|
-
allowed: false,
|
|
750
|
-
reason: params.interactiveEvent && channelUsersAllowlistConfigured && allowFromLower.length > 0 ? "sender-not-authorized" : channelUsersAllowlistConfigured ? "sender-not-channel-allowed" : "sender-not-allowlisted",
|
|
751
|
-
channelType,
|
|
752
|
-
channelName
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
//#endregion
|
|
756
|
-
//#region extensions/slack/src/monitor/dm-auth.ts
|
|
757
|
-
async function authorizeSlackDirectMessage(params) {
|
|
758
|
-
if (!params.ctx.dmEnabled || params.ctx.dmPolicy === "disabled") {
|
|
759
|
-
await params.onDisabled();
|
|
760
|
-
return false;
|
|
761
|
-
}
|
|
762
|
-
if (params.ctx.dmPolicy === "open" && params.allowFromLower.includes("*")) return true;
|
|
763
|
-
const senderName = (await params.resolveSenderName(params.senderId))?.name ?? void 0;
|
|
764
|
-
const allowMatch = resolveSlackAllowListMatch({
|
|
765
|
-
allowList: params.allowFromLower,
|
|
766
|
-
id: params.senderId,
|
|
767
|
-
name: senderName,
|
|
768
|
-
allowNameMatching: params.ctx.allowNameMatching
|
|
769
|
-
});
|
|
770
|
-
const allowMatchMeta = formatAllowlistMatchMeta(allowMatch);
|
|
771
|
-
if (allowMatch.allowed) return true;
|
|
772
|
-
if (params.ctx.dmPolicy === "pairing") {
|
|
773
|
-
await createChannelPairingChallengeIssuer({
|
|
774
|
-
channel: "slack",
|
|
775
|
-
upsertPairingRequest: async ({ id, meta }) => await upsertChannelPairingRequest({
|
|
776
|
-
channel: "slack",
|
|
777
|
-
id,
|
|
778
|
-
accountId: params.accountId,
|
|
779
|
-
meta
|
|
780
|
-
})
|
|
781
|
-
})({
|
|
782
|
-
senderId: params.senderId,
|
|
783
|
-
senderIdLine: `Your Slack user id: ${params.senderId}`,
|
|
784
|
-
meta: { name: senderName },
|
|
785
|
-
sendPairingReply: params.sendPairingReply,
|
|
786
|
-
onCreated: () => {
|
|
787
|
-
params.log(`slack pairing request sender=${params.senderId} name=${senderName ?? "unknown"} (${allowMatchMeta})`);
|
|
788
|
-
},
|
|
789
|
-
onReplyError: (err) => {
|
|
790
|
-
params.log(`slack pairing reply failed for ${params.senderId}: ${formatErrorMessage(err)}`);
|
|
791
|
-
}
|
|
792
|
-
});
|
|
793
|
-
return false;
|
|
794
|
-
}
|
|
795
|
-
await params.onUnauthorized({
|
|
796
|
-
allowMatchMeta,
|
|
797
|
-
senderName
|
|
798
|
-
});
|
|
799
|
-
return false;
|
|
800
|
-
}
|
|
801
|
-
//#endregion
|
|
802
|
-
//#region extensions/slack/src/monitor/room-context.ts
|
|
803
|
-
function resolveSlackRoomContextHints(params) {
|
|
804
|
-
const untrustedChannelMetadata = params.isRoomish ? buildUntrustedChannelMetadata({
|
|
805
|
-
source: "slack",
|
|
806
|
-
label: "Slack channel description",
|
|
807
|
-
entries: [params.channelInfo?.topic, params.channelInfo?.purpose]
|
|
808
|
-
}) : void 0;
|
|
809
|
-
const systemPromptParts = [params.isRoomish ? normalizeOptionalString(params.channelConfig?.systemPrompt) ?? null : null].filter((entry) => Boolean(entry));
|
|
810
|
-
return {
|
|
811
|
-
untrustedChannelMetadata,
|
|
812
|
-
groupSystemPrompt: systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : void 0
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
//#endregion
|
|
816
|
-
export { resolveStorePath$1 as C, resolveSlackSlashCommandConfig as D, buildSlackSlashCommandMatcher as E, stripSlackMentionsForCommandDetection as O, resolveOpenProviderRuntimeGroupPolicy$1 as S, warnMissingProviderGroupPolicyFallbackOnce as T, getRuntimeConfig$1 as _, recordInboundSession as a, resolveChannelContextVisibilityMode as b, authorizeSlackBotRoomMessage as c, resolveSlackEffectiveAllowFrom as d, buildSlackAssistantThreadMetadata as f, resolveSlackChatType as g, normalizeSlackChannelType as h, parsePluginBindingApprovalCustomId as i, authorizeSlackSystemEventSender as l, parseSlackAssistantThreadMetadata as m, authorizeSlackDirectMessage as n, resolveConversationLabel$1 as o, createSlackMonitorContext as p, buildPluginBindingResolvedText as r, resolvePluginConversationBindingApproval as s, resolveSlackRoomContextHints as t, resolveSlackCommandIngress as u, isDangerousNameMatchingEnabled as v, updateLastRoute as w, resolveDefaultGroupPolicy as x, readSessionUpdatedAt as y };
|