@openclaw/slack 2026.5.27 → 2026.5.28-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/{account-inspect-vVg3pT03.js → account-inspect-CdGk6R7l.js} +1 -1
  2. package/dist/account-inspect-api.js +1 -1
  3. package/dist/{accounts-BnLQ3fe2.js → accounts-f6Xcv9Vi.js} +42 -1
  4. package/dist/accounts.runtime-BJt1IxOS.js +2 -0
  5. package/dist/{action-runtime-JVb7KyQs.js → action-runtime-BOEgcnv6.js} +7 -8
  6. package/dist/action-runtime.runtime-BXQYV0yA.js +2 -0
  7. package/dist/{actions-1o9nMIY8.js → actions-zfVWcIY6.js} +4 -4
  8. package/dist/{actions.runtime-qQtdNww-.js → actions.runtime-CoijPN8g.js} +1 -1
  9. package/dist/api.js +14 -17
  10. package/dist/{approval-handler.runtime-Ba8nwnYi.js → approval-handler.runtime-CWz3XLfN.js} +12 -69
  11. package/dist/{channel-Bd7eept6.js → channel-B9uavFQM.js} +25 -28
  12. package/dist/channel-config-api.js +1 -1
  13. package/dist/channel-plugin-api.js +1 -1
  14. package/dist/{channel.setup-WNNVmCm0.js → channel.setup-oGp4gSTP.js} +4 -4
  15. package/dist/{client-Dc2Ji2YN.js → client-DowBk5k0.js} +6 -24
  16. package/dist/{config-schema-CUiDK8HV.js → config-schema-C0RewpJQ.js} +4 -0
  17. package/dist/contract-api.js +1 -1
  18. package/dist/{directory-config-9_djLTOK.js → directory-config-8UPAEyNg.js} +1 -1
  19. package/dist/directory-contract-api.js +1 -1
  20. package/dist/{directory-live-Cl83cGLZ.js → directory-live-BFB1pSax.js} +2 -2
  21. package/dist/http-routes-api.js +1 -1
  22. package/dist/index.js +3 -7
  23. package/dist/{interactive-replies-DtYu4Sf5.js → interactive-replies-DrBq4Mld.js} +1 -1
  24. package/dist/interactive-replies-api.js +1 -1
  25. package/dist/{message-tool-api-BieXX5lk.js → message-tool-api-B9M0zzlQ.js} +2 -2
  26. package/dist/message-tool-api.js +1 -1
  27. package/dist/{monitor-Cn7LUDFL.js → monitor-BzzGqB63.js} +3 -4
  28. package/dist/{outbound-adapter-ChuR4_0v.js → outbound-adapter-BHZMgblN.js} +4 -7
  29. package/dist/{prepare-Bl5WcC3f.js → pipeline.runtime-XK36f1W3.js} +1769 -47
  30. package/dist/plugin-routes-B9PvcDQJ.js +22 -0
  31. package/dist/{policy-pu8tsF8_.js → policy-BBDU-PQK.js} +1 -1
  32. package/dist/{probe-cBVPhCKZ.js → probe-Djes9Fy6.js} +1 -1
  33. package/dist/{provider-Bccng2Wp.js → provider-CfZypaNV.js} +826 -26
  34. package/dist/{replies-jiEDV6lH.js → replies-DkmWK7JW.js} +2 -4
  35. package/dist/{resolve-channels-CT4oiz_9.js → resolve-channels-zXt5f47h.js} +1 -1
  36. package/dist/{resolve-users-o5S-Ijks.js → resolve-users-BLfGAz1v.js} +1 -1
  37. package/dist/{runtime-api-CyPE-EvY.js → runtime-api-DvpUD2hw.js} +2 -2
  38. package/dist/runtime-api.js +12 -12
  39. package/dist/{scopes-CnyhLIPT.js → scopes-DiiHsqh1.js} +1 -1
  40. package/dist/{send-DvfE8LVm.js → send-BURYyCXI.js} +4 -4
  41. package/dist/send.runtime-CKaMG3s-.js +2 -0
  42. package/dist/{setup-core-DKe7Ug3V.js → setup-core-POfI_bgP.js} +2 -2
  43. package/dist/setup-entry.js +8 -1
  44. package/dist/setup-plugin-api.js +1 -1
  45. package/dist/{setup-surface-gjRthuZA.js → setup-surface-DJTHAguz.js} +5 -5
  46. package/dist/{shared-D8A7iVVH.js → shared-D9WMYymo.js} +5 -5
  47. package/dist/{slash-dispatch.runtime-BBhdJHb8.js → slash-dispatch.runtime-lsyTm_q5.js} +1 -1
  48. package/dist/thread-ts-NSVqWybn.js +646 -0
  49. package/node_modules/semver/classes/range.js +7 -0
  50. package/node_modules/semver/package.json +1 -1
  51. package/node_modules/semver/ranges/subset.js +2 -2
  52. package/npm-shrinkwrap.json +6 -29
  53. package/openclaw.plugin.json +10 -0
  54. package/package.json +8 -7
  55. package/dist/accounts.runtime-BVdtQeuq.js +0 -2
  56. package/dist/action-runtime.runtime-BZa5VDjs.js +0 -2
  57. package/dist/allow-list-B1lkGjwl.js +0 -82
  58. package/dist/blocks-render-CNC4vQnd.js +0 -232
  59. package/dist/inbound-contract-test-api.js +0 -3
  60. package/dist/magic-string.es-BPXBBMwL.js +0 -1011
  61. package/dist/outbound-payload-test-api.js +0 -2
  62. package/dist/outbound-payload.test-harness-8MnHFgR1.js +0 -13551
  63. package/dist/pipeline.runtime-BVK8yXw_.js +0 -1473
  64. package/dist/plugin-routes-DRR3ijKM.js +0 -20
  65. package/dist/prepare.test-helpers-BcAo4KMw.js +0 -49
  66. package/dist/reply-blocks-BlOURkUm.js +0 -290
  67. package/dist/room-context-BmNTBiw5.js +0 -816
  68. package/dist/send.runtime-Dv6ajTGK.js +0 -2
  69. package/dist/send.runtime-v3TSw9xY.js +0 -2
  70. package/dist/test-api.js +0 -8
  71. package/dist/thread-ts-ks-O8cEG.js +0 -52
  72. package/node_modules/agent-base/LICENSE +0 -22
  73. package/node_modules/agent-base/README.md +0 -69
  74. package/node_modules/agent-base/dist/helpers.d.ts +0 -10
  75. package/node_modules/agent-base/dist/helpers.d.ts.map +0 -1
  76. package/node_modules/agent-base/dist/helpers.js +0 -37
  77. package/node_modules/agent-base/dist/helpers.js.map +0 -1
  78. package/node_modules/agent-base/dist/index.d.ts +0 -37
  79. package/node_modules/agent-base/dist/index.d.ts.map +0 -1
  80. package/node_modules/agent-base/dist/index.js +0 -146
  81. package/node_modules/agent-base/dist/index.js.map +0 -1
  82. package/node_modules/agent-base/package.json +0 -46
  83. package/node_modules/https-proxy-agent/LICENSE +0 -22
  84. package/node_modules/https-proxy-agent/README.md +0 -70
  85. package/node_modules/https-proxy-agent/dist/index.d.ts +0 -43
  86. package/node_modules/https-proxy-agent/dist/index.d.ts.map +0 -1
  87. package/node_modules/https-proxy-agent/dist/index.js +0 -150
  88. package/node_modules/https-proxy-agent/dist/index.js.map +0 -1
  89. package/node_modules/https-proxy-agent/dist/parse-proxy-response.d.ts +0 -12
  90. package/node_modules/https-proxy-agent/dist/parse-proxy-response.d.ts.map +0 -1
  91. package/node_modules/https-proxy-agent/dist/parse-proxy-response.js +0 -94
  92. package/node_modules/https-proxy-agent/dist/parse-proxy-response.js.map +0 -1
  93. package/node_modules/https-proxy-agent/package.json +0 -50
@@ -1,36 +1,37 @@
1
- import { a as resolveSlackAccount, d as resolveSlackBotToken, o as resolveSlackAccountAllowFrom, s as resolveSlackAccountDmPolicy, u as resolveSlackAppToken } from "./accounts-BnLQ3fe2.js";
2
- import { c as isSlackApprovalAuthorizedSender, i as isSlackAnyNativeApprovalClientEnabled, u as isSlackExecApprovalAuthorizedSender } from "./reply-blocks-BlOURkUm.js";
3
- import "./blocks-render-CNC4vQnd.js";
4
- import { o as SLACK_TEXT_LIMIT, s as truncateSlackText } from "./thread-ts-ks-O8cEG.js";
5
- import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-DtYu4Sf5.js";
6
- import { c as resolveSlackWebClientOptions } from "./client-Dc2Ji2YN.js";
7
- import { n as normalizeAllowList, r as normalizeAllowListLower, t as allowListMatches } from "./allow-list-B1lkGjwl.js";
8
- import { t as getOptionalSlackRuntime } from "./runtime-BOk7xkOl.js";
9
1
  import { n as registerSlackHttpHandler, r as normalizeSlackWebhookPath } from "./registry-bbjH7IHX.js";
2
+ import { a as resolveSlackAccount, d as resolveSlackBotToken, o as resolveSlackAccountAllowFrom, s as resolveSlackAccountDmPolicy, u as resolveSlackAppToken } from "./accounts-f6Xcv9Vi.js";
3
+ import { C as isSlackAnyNativeApprovalClientEnabled, D as isSlackApprovalAuthorizedSender, b as truncateSlackText, d as normalizeAllowList, f as normalizeAllowListLower, h as resolveSlackAllowListMatch, k as isSlackExecApprovalAuthorizedSender, m as normalizeSlackSlug, p as normalizeSlackAllowOwnerEntry, s as SLACK_TEXT_LIMIT, u as allowListMatches } from "./thread-ts-NSVqWybn.js";
4
+ import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-DrBq4Mld.js";
5
+ import { c as resolveSlackWebClientOptions } from "./client-DowBk5k0.js";
6
+ import { t as getOptionalSlackRuntime } from "./runtime-BOk7xkOl.js";
10
7
  import { t as formatSlackError } from "./errors-CZtmv-h0.js";
11
- import { t as resolveSlackChannelAllowlist } from "./resolve-channels-CT4oiz_9.js";
12
- import { t as resolveSlackUserAllowlist } from "./resolve-users-o5S-Ijks.js";
13
- import { D as resolveSlackSlashCommandConfig, E as buildSlackSlashCommandMatcher, O as stripSlackMentionsForCommandDetection, S as resolveOpenProviderRuntimeGroupPolicy, T as warnMissingProviderGroupPolicyFallbackOnce, _ as getRuntimeConfig$1, d as resolveSlackEffectiveAllowFrom, f as buildSlackAssistantThreadMetadata, g as resolveSlackChatType, h as normalizeSlackChannelType, i as parsePluginBindingApprovalCustomId, l as authorizeSlackSystemEventSender, n as authorizeSlackDirectMessage, p as createSlackMonitorContext, r as buildPluginBindingResolvedText, s as resolvePluginConversationBindingApproval, t as resolveSlackRoomContextHints, u as resolveSlackCommandIngress, v as isDangerousNameMatchingEnabled, x as resolveDefaultGroupPolicy } from "./room-context-BmNTBiw5.js";
14
- import { n as resolveSlackChannelConfig, r as resolveSlackChannelLabel, t as isSlackChannelAllowedByPolicy } from "./policy-pu8tsF8_.js";
8
+ import { t as resolveSlackChannelAllowlist } from "./resolve-channels-zXt5f47h.js";
9
+ import { t as resolveSlackUserAllowlist } from "./resolve-users-BLfGAz1v.js";
10
+ import { n as resolveSlackChannelConfig, r as resolveSlackChannelLabel, t as isSlackChannelAllowedByPolicy } from "./policy-BBDU-PQK.js";
15
11
  import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
16
- import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringEntries, normalizeStringEntriesLower, normalizeUniqueTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
17
- import { normalizeAccountId, normalizeMainKey } from "openclaw/plugin-sdk/routing";
12
+ import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries, normalizeStringEntriesLower, normalizeUniqueTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
13
+ import { normalizeAccountId, normalizeMainKey, resolveAgentRoute, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
18
14
  import { createChannelMessageReplyPipeline } from "openclaw/plugin-sdk/channel-outbound";
15
+ import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
19
16
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
20
- import { addAllowlistUserEntriesFromConfigEntry, buildAllowlistResolutionSummary, mergeAllowlist, patchAllowlistUsersInConfigEntries, summarizeMapping } from "openclaw/plugin-sdk/allow-from";
21
- import { computeBackoff, createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, sleepWithAbort, warn } from "openclaw/plugin-sdk/runtime-env";
17
+ import { addAllowlistUserEntriesFromConfigEntry, buildAllowlistResolutionSummary, formatAllowlistMatchMeta, mergeAllowlist, patchAllowlistUsersInConfigEntries, summarizeMapping } from "openclaw/plugin-sdk/allow-from";
18
+ import { computeBackoff, createNonExitingRuntime, danger, getChildLogger, logVerbose, shouldLogVerbose, sleepWithAbort, warn } from "openclaw/plugin-sdk/runtime-env";
22
19
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
23
20
  import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
24
21
  import { resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
25
22
  import { chunkItems } from "openclaw/plugin-sdk/text-chunking";
26
- import { resolveGlobalDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
23
+ import { createDedupeCache, resolveGlobalDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
24
+ import { buildPluginBindingResolvedText, parsePluginBindingApprovalCustomId, recordInboundSession, resolveConversationLabel as resolveConversationLabel$1, resolvePluginConversationBindingApproval, resolveRuntimeConversationBindingRoute, upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
27
25
  import { resolveNativeCommandsEnabled, resolveNativeSkillsEnabled } from "openclaw/plugin-sdk/native-command-config-runtime";
28
26
  import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
29
27
  import { DEFAULT_GROUP_HISTORY_LIMIT } from "openclaw/plugin-sdk/reply-history";
30
28
  import { installRequestBodyLimitGuard } from "openclaw/plugin-sdk/webhook-request-guards";
31
- import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
32
- import { loadSessionStore, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
33
- import { resolveDefaultModelForAgent } from "openclaw/plugin-sdk/agent-runtime";
29
+ import { getRuntimeConfig, getRuntimeConfig as getRuntimeConfig$1 } from "openclaw/plugin-sdk/runtime-config-snapshot";
30
+ import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
31
+ import { loadSessionStore, readSessionUpdatedAt, resolveSessionKey, resolveStorePath, resolveStorePath as resolveStorePath$1, updateLastRoute } from "openclaw/plugin-sdk/session-store-runtime";
32
+ import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
33
+ import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy as resolveOpenProviderRuntimeGroupPolicy$1, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
34
+ import { resolveDefaultAgentId, resolveDefaultModelForAgent } from "openclaw/plugin-sdk/agent-runtime";
34
35
  import { resolveChannelConfigWrites } from "openclaw/plugin-sdk/channel-config-writes";
35
36
  import { mutateConfigFile } from "openclaw/plugin-sdk/config-mutation";
36
37
  import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
@@ -38,9 +39,387 @@ import { resolveApprovalOverGateway } from "openclaw/plugin-sdk/approval-gateway
38
39
  import { parseExecApprovalCommandText } from "openclaw/plugin-sdk/approval-reply-runtime";
39
40
  import { formatCommandArgMenuTitle, resolveCommandAuthorization, resolveNativeCommandSessionTargets, resolveStoredModelOverride } from "openclaw/plugin-sdk/command-auth-native";
40
41
  import { requestHeartbeat } from "openclaw/plugin-sdk/heartbeat-runtime";
42
+ import { parseStrictFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
41
43
  import { createInteractiveConversationBindingHelpers, dispatchPluginInteractiveHandler } from "openclaw/plugin-sdk/plugin-runtime";
44
+ import { createChannelIngressResolver, defineStableChannelIngressIdentity, readChannelIngressStoreAllowFromForDmPolicy } from "openclaw/plugin-sdk/channel-ingress-runtime";
42
45
  import { createChannelInboundDebouncer, shouldDebounceTextInbound } from "openclaw/plugin-sdk/channel-inbound";
43
46
  import { generateSecureToken } from "openclaw/plugin-sdk/secure-random-runtime";
47
+ import { buildUntrustedChannelMetadata } from "openclaw/plugin-sdk/security-runtime";
48
+ //#region extensions/slack/src/monitor/commands.ts
49
+ /**
50
+ * Strip Slack mentions (<@U123>, <@U123|name>) so command detection works on
51
+ * normalized text. Use in both prepare and debounce gate for consistency.
52
+ */
53
+ function stripSlackMentionsForCommandDetection(text) {
54
+ return (text ?? "").replace(/<@[^>]+>/g, " ").replace(/\s+/g, " ").trim();
55
+ }
56
+ function normalizeSlackSlashCommandName(raw) {
57
+ return raw.replace(/^\/+/, "");
58
+ }
59
+ function resolveSlackSlashCommandConfig(raw) {
60
+ const name = normalizeSlackSlashCommandName(normalizeOptionalString(raw?.name) ?? "openclaw") || "openclaw";
61
+ return {
62
+ enabled: raw?.enabled === true,
63
+ name,
64
+ sessionPrefix: normalizeOptionalString(raw?.sessionPrefix) ?? "slack:slash",
65
+ ephemeral: raw?.ephemeral !== false
66
+ };
67
+ }
68
+ function buildSlackSlashCommandMatcher(name) {
69
+ const escaped = normalizeSlackSlashCommandName(name).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
70
+ return new RegExp(`^/?${escaped}$`);
71
+ }
72
+ //#endregion
73
+ //#region extensions/slack/src/monitor/channel-type.ts
74
+ function inferSlackChannelType(channelId) {
75
+ const trimmed = channelId?.trim();
76
+ if (!trimmed) return;
77
+ if (trimmed.startsWith("D")) return "im";
78
+ if (trimmed.startsWith("C")) return "channel";
79
+ if (trimmed.startsWith("G")) return "group";
80
+ }
81
+ function normalizeSlackChannelType(channelType, channelId) {
82
+ const normalized = normalizeOptionalLowercaseString(channelType);
83
+ const inferred = inferSlackChannelType(channelId);
84
+ if (normalized === "im" || normalized === "mpim" || normalized === "channel" || normalized === "group") {
85
+ if (inferred === "im" && normalized !== "im") return "im";
86
+ return normalized;
87
+ }
88
+ return inferred ?? "channel";
89
+ }
90
+ function resolveSlackChatType(channelType) {
91
+ if (channelType === "im") return "direct";
92
+ if (channelType === "mpim") return "group";
93
+ return "channel";
94
+ }
95
+ //#endregion
96
+ //#region extensions/slack/src/monitor/context.ts
97
+ const SLACK_ASSISTANT_THREAD_CONTEXT_METADATA_EVENT = "assistant_thread_context";
98
+ function buildSlackAssistantThreadMetadata(context) {
99
+ const eventPayload = {};
100
+ if (context.channelId) eventPayload.channel_id = context.channelId;
101
+ if (context.teamId) eventPayload.team_id = context.teamId;
102
+ if (context.enterpriseId) eventPayload.enterprise_id = context.enterpriseId;
103
+ return {
104
+ event_type: SLACK_ASSISTANT_THREAD_CONTEXT_METADATA_EVENT,
105
+ event_payload: eventPayload
106
+ };
107
+ }
108
+ function parseSlackAssistantThreadMetadata(value) {
109
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
110
+ const metadata = value;
111
+ if (metadata.event_type !== "assistant_thread_context") return;
112
+ const payload = metadata.event_payload;
113
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return;
114
+ const record = payload;
115
+ const stringField = (key) => {
116
+ const raw = record[key];
117
+ return typeof raw === "string" && raw.trim() ? raw.trim() : void 0;
118
+ };
119
+ return {
120
+ channelId: stringField("channel_id"),
121
+ teamId: stringField("team_id"),
122
+ enterpriseId: stringField("enterprise_id")
123
+ };
124
+ }
125
+ const SLACK_ASSISTANT_CONTEXT_TTL_MS = 1440 * 60 * 1e3;
126
+ const SLACK_ASSISTANT_CONTEXT_CLEANUP_INTERVAL_MS = 600 * 1e3;
127
+ function createSlackMonitorContext(params) {
128
+ const channelHistories = /* @__PURE__ */ new Map();
129
+ const logger = getChildLogger({ module: "slack-auto-reply" });
130
+ const channelCache = /* @__PURE__ */ new Map();
131
+ const userCache = /* @__PURE__ */ new Map();
132
+ const seenMessages = createDedupeCache({
133
+ ttlMs: 6e4,
134
+ maxSize: 500
135
+ });
136
+ const assistantThreadContexts = /* @__PURE__ */ new Map();
137
+ let lastAssistantContextCleanupAt = Date.now();
138
+ const allowFrom = normalizeAllowList(params.allowFrom);
139
+ const groupDmChannels = normalizeAllowList(params.groupDmChannels);
140
+ const groupDmChannelsLower = normalizeAllowListLower(groupDmChannels);
141
+ const defaultRequireMention = params.defaultRequireMention ?? true;
142
+ const hasChannelAllowlistConfig = Object.keys(params.channelsConfig ?? {}).length > 0;
143
+ const channelsConfigKeys = Object.keys(params.channelsConfig ?? {});
144
+ const markMessageSeen = (channelId, ts) => {
145
+ if (!channelId || !ts) return false;
146
+ return seenMessages.check(`${channelId}:${ts}`);
147
+ };
148
+ const releaseSeenMessage = (channelId, ts) => {
149
+ if (!channelId || !ts) return;
150
+ seenMessages.delete(`${channelId}:${ts}`);
151
+ };
152
+ const assistantContextKey = (channelId, threadTs) => `${channelId}:${threadTs}`;
153
+ const cleanupAssistantThreadContexts = () => {
154
+ const now = Date.now();
155
+ if (now - lastAssistantContextCleanupAt < SLACK_ASSISTANT_CONTEXT_CLEANUP_INTERVAL_MS) return;
156
+ lastAssistantContextCleanupAt = now;
157
+ const cutoff = now - SLACK_ASSISTANT_CONTEXT_TTL_MS;
158
+ for (const [key, entry] of assistantThreadContexts) if (entry.updatedAt < cutoff) assistantThreadContexts.delete(key);
159
+ };
160
+ const getSlackAssistantThreadContext = (channelId, threadTs) => {
161
+ if (!channelId || !threadTs) return;
162
+ const key = assistantContextKey(channelId, threadTs);
163
+ const entry = assistantThreadContexts.get(key);
164
+ if (!entry) return;
165
+ if (Date.now() - entry.updatedAt > SLACK_ASSISTANT_CONTEXT_TTL_MS) {
166
+ assistantThreadContexts.delete(key);
167
+ return;
168
+ }
169
+ return entry;
170
+ };
171
+ const saveSlackAssistantThreadContext = (context) => {
172
+ cleanupAssistantThreadContexts();
173
+ assistantThreadContexts.set(assistantContextKey(context.assistantChannelId, context.threadTs), {
174
+ ...context,
175
+ updatedAt: Date.now()
176
+ });
177
+ };
178
+ const resolveSlackSystemEventSessionKey = (p) => {
179
+ const channelId = normalizeOptionalString(p.channelId) ?? "";
180
+ if (!channelId) return params.mainKey;
181
+ const channelType = normalizeSlackChannelType(p.channelType, channelId);
182
+ const isDirectMessage = channelType === "im";
183
+ const isGroup = channelType === "mpim";
184
+ const from = isDirectMessage ? `slack:${channelId}` : isGroup ? `slack:group:${channelId}` : `slack:channel:${channelId}`;
185
+ const chatType = isDirectMessage ? "direct" : isGroup ? "group" : "channel";
186
+ const senderId = normalizeOptionalString(p.senderId) ?? "";
187
+ try {
188
+ const peerKind = isDirectMessage ? "direct" : isGroup ? "group" : "channel";
189
+ const peerId = isDirectMessage ? senderId : channelId;
190
+ if (peerId) {
191
+ const route = resolveAgentRoute({
192
+ cfg: params.cfg,
193
+ channel: "slack",
194
+ accountId: params.accountId,
195
+ teamId: params.teamId,
196
+ peer: {
197
+ kind: peerKind,
198
+ id: peerId
199
+ }
200
+ });
201
+ const threadTs = normalizeOptionalString(p.threadTs);
202
+ const baseConversationId = isDirectMessage ? `user:${senderId}` : channelId;
203
+ const threadBindingRoute = threadTs ? resolveRuntimeConversationBindingRoute({
204
+ route,
205
+ conversation: {
206
+ channel: "slack",
207
+ accountId: params.accountId,
208
+ conversationId: threadTs,
209
+ parentConversationId: baseConversationId
210
+ }
211
+ }) : null;
212
+ const runtimeRoute = threadBindingRoute?.boundSessionKey || threadBindingRoute?.bindingRecord ? threadBindingRoute : resolveRuntimeConversationBindingRoute({
213
+ route,
214
+ conversation: {
215
+ channel: "slack",
216
+ accountId: params.accountId,
217
+ conversationId: baseConversationId
218
+ }
219
+ });
220
+ if (runtimeRoute.boundSessionKey) return runtimeRoute.route.sessionKey;
221
+ return resolveThreadSessionKeys({
222
+ baseSessionKey: runtimeRoute.route.sessionKey,
223
+ threadId: threadTs,
224
+ parentSessionKey: threadTs && params.threadInheritParent ? runtimeRoute.route.sessionKey : void 0
225
+ }).sessionKey;
226
+ }
227
+ } catch {}
228
+ const legacySessionKey = resolveSessionKey(params.sessionScope, {
229
+ From: from,
230
+ ChatType: chatType,
231
+ Provider: "slack"
232
+ }, params.mainKey, resolveDefaultAgentId(params.cfg));
233
+ return resolveThreadSessionKeys({
234
+ baseSessionKey: legacySessionKey,
235
+ threadId: normalizeOptionalString(p.threadTs),
236
+ parentSessionKey: normalizeOptionalString(p.threadTs) && params.threadInheritParent ? legacySessionKey : void 0
237
+ }).sessionKey;
238
+ };
239
+ const resolveChannelName = async (channelId) => {
240
+ const cached = channelCache.get(channelId);
241
+ if (cached) return cached;
242
+ try {
243
+ const info = await params.app.client.conversations.info({
244
+ token: params.botToken,
245
+ channel: channelId
246
+ });
247
+ const name = info.channel && "name" in info.channel ? info.channel.name : void 0;
248
+ const channel = info.channel ?? void 0;
249
+ const entry = {
250
+ name,
251
+ type: channel?.is_im ? "im" : channel?.is_mpim ? "mpim" : channel?.is_channel ? "channel" : channel?.is_group ? "group" : void 0,
252
+ topic: channel && "topic" in channel ? channel.topic?.value ?? void 0 : void 0,
253
+ purpose: channel && "purpose" in channel ? channel.purpose?.value ?? void 0 : void 0
254
+ };
255
+ channelCache.set(channelId, entry);
256
+ return entry;
257
+ } catch {
258
+ return {};
259
+ }
260
+ };
261
+ const resolveUserName = async (userId) => {
262
+ const cached = userCache.get(userId);
263
+ if (cached) return cached;
264
+ try {
265
+ const info = await params.app.client.users.info({
266
+ token: params.botToken,
267
+ user: userId
268
+ });
269
+ const profile = info.user?.profile;
270
+ const entry = { name: profile?.display_name || profile?.real_name || info.user?.name || void 0 };
271
+ userCache.set(userId, entry);
272
+ return entry;
273
+ } catch {
274
+ return {};
275
+ }
276
+ };
277
+ const setSlackThreadStatus = async (p) => {
278
+ if (!p.threadTs) return;
279
+ try {
280
+ await params.app.client.assistant.threads.setStatus({
281
+ token: params.botToken,
282
+ channel_id: p.channelId,
283
+ thread_ts: p.threadTs,
284
+ status: p.status
285
+ });
286
+ } catch (err) {
287
+ logVerbose(`slack status update failed for channel ${p.channelId}: ${formatSlackError(err)}`);
288
+ }
289
+ };
290
+ const setSlackAssistantSuggestedPrompts = async (p) => {
291
+ const prompts = p.prompts.map((prompt) => ({
292
+ title: prompt.title.trim(),
293
+ message: prompt.message.trim()
294
+ })).filter((prompt) => prompt.title && prompt.message).slice(0, 4);
295
+ if (prompts.length === 0) return false;
296
+ try {
297
+ await params.app.client.assistant.threads.setSuggestedPrompts({
298
+ token: params.botToken,
299
+ channel_id: p.channelId,
300
+ thread_ts: p.threadTs,
301
+ ...p.title?.trim() ? { title: p.title.trim() } : {},
302
+ prompts
303
+ });
304
+ return true;
305
+ } catch (err) {
306
+ logVerbose(`slack suggested prompts update failed for channel ${p.channelId}: ${formatSlackError(err)}`);
307
+ return false;
308
+ }
309
+ };
310
+ const isChannelAllowed = (p) => {
311
+ const channelType = normalizeSlackChannelType(p.channelType, p.channelId);
312
+ const isDirectMessage = channelType === "im";
313
+ const isGroupDm = channelType === "mpim";
314
+ const isRoom = channelType === "channel" || channelType === "group";
315
+ if (isDirectMessage && !params.dmEnabled) return false;
316
+ if (isGroupDm && !params.groupDmEnabled) return false;
317
+ if (isGroupDm && groupDmChannels.length > 0) {
318
+ const candidates = [
319
+ p.channelId,
320
+ p.channelName ? `#${p.channelName}` : void 0,
321
+ p.channelName,
322
+ p.channelName ? normalizeSlackSlug(p.channelName) : void 0
323
+ ].filter((value) => Boolean(value)).map((value) => normalizeLowercaseStringOrEmpty(value));
324
+ if (!(groupDmChannelsLower.includes("*") || candidates.some((candidate) => groupDmChannelsLower.includes(candidate)))) return false;
325
+ }
326
+ if (isRoom && p.channelId) {
327
+ const channelConfig = resolveSlackChannelConfig({
328
+ channelId: p.channelId,
329
+ channelName: p.channelName,
330
+ channels: params.channelsConfig,
331
+ channelKeys: channelsConfigKeys,
332
+ defaultRequireMention,
333
+ allowNameMatching: params.allowNameMatching
334
+ });
335
+ const channelMatchMeta = formatAllowlistMatchMeta(channelConfig);
336
+ const channelAllowed = channelConfig?.allowed !== false;
337
+ const channelAllowlistConfigured = hasChannelAllowlistConfig;
338
+ if (!isSlackChannelAllowedByPolicy({
339
+ groupPolicy: params.groupPolicy,
340
+ channelAllowlistConfigured,
341
+ channelAllowed
342
+ })) {
343
+ logVerbose(`slack: drop channel ${p.channelId} (groupPolicy=${params.groupPolicy}, ${channelMatchMeta})`);
344
+ return false;
345
+ }
346
+ const hasExplicitConfig = Boolean(channelConfig?.matchSource);
347
+ if (!channelAllowed && (params.groupPolicy !== "open" || hasExplicitConfig)) {
348
+ logVerbose(`slack: drop channel ${p.channelId} (${channelMatchMeta})`);
349
+ return false;
350
+ }
351
+ logVerbose(`slack: allow channel ${p.channelId} (${channelMatchMeta})`);
352
+ }
353
+ return true;
354
+ };
355
+ const shouldDropMismatchedSlackEvent = (body) => {
356
+ if (!body || typeof body !== "object") return false;
357
+ const raw = body;
358
+ const incomingApiAppId = typeof raw.api_app_id === "string" ? raw.api_app_id : "";
359
+ const incomingTeamId = typeof raw.team_id === "string" ? raw.team_id : typeof raw.team?.id === "string" ? raw.team.id : "";
360
+ if (params.apiAppId && incomingApiAppId && incomingApiAppId !== params.apiAppId) {
361
+ logVerbose(`slack: drop event with api_app_id=${incomingApiAppId} (expected ${params.apiAppId})`);
362
+ return true;
363
+ }
364
+ if (params.teamId && incomingTeamId && incomingTeamId !== params.teamId) {
365
+ logVerbose(`slack: drop event with team_id=${incomingTeamId} (expected ${params.teamId})`);
366
+ return true;
367
+ }
368
+ return false;
369
+ };
370
+ return {
371
+ cfg: params.cfg,
372
+ accountId: params.accountId,
373
+ botToken: params.botToken,
374
+ app: params.app,
375
+ runtime: params.runtime,
376
+ botUserId: params.botUserId,
377
+ botId: params.botId,
378
+ teamId: params.teamId,
379
+ apiAppId: params.apiAppId,
380
+ historyLimit: params.historyLimit,
381
+ dmHistoryLimit: Math.max(0, params.dmHistoryLimit ?? 0),
382
+ channelHistories,
383
+ sessionScope: params.sessionScope,
384
+ mainKey: params.mainKey,
385
+ dmEnabled: params.dmEnabled,
386
+ dmPolicy: params.dmPolicy,
387
+ allowFrom,
388
+ allowNameMatching: params.allowNameMatching,
389
+ groupDmEnabled: params.groupDmEnabled,
390
+ groupDmChannels,
391
+ defaultRequireMention,
392
+ channelsConfig: params.channelsConfig,
393
+ channelsConfigKeys,
394
+ groupPolicy: params.groupPolicy,
395
+ useAccessGroups: params.useAccessGroups,
396
+ reactionMode: params.reactionMode,
397
+ reactionAllowlist: params.reactionAllowlist,
398
+ replyToMode: params.replyToMode,
399
+ threadHistoryScope: params.threadHistoryScope,
400
+ threadInheritParent: params.threadInheritParent,
401
+ threadRequireExplicitMention: params.threadRequireExplicitMention,
402
+ slashCommand: params.slashCommand,
403
+ textLimit: params.textLimit,
404
+ ackReactionScope: params.ackReactionScope,
405
+ typingReaction: params.typingReaction,
406
+ mediaMaxBytes: params.mediaMaxBytes,
407
+ removeAckAfterReply: params.removeAckAfterReply,
408
+ logger,
409
+ markMessageSeen,
410
+ releaseSeenMessage,
411
+ shouldDropMismatchedSlackEvent,
412
+ resolveSlackSystemEventSessionKey,
413
+ isChannelAllowed,
414
+ resolveChannelName,
415
+ resolveUserName,
416
+ setSlackThreadStatus,
417
+ getSlackAssistantThreadContext,
418
+ saveSlackAssistantThreadContext,
419
+ setSlackAssistantSuggestedPrompts
420
+ };
421
+ }
422
+ //#endregion
44
423
  //#region extensions/slack/src/monitor/events/assistant.ts
45
424
  const DEFAULT_ASSISTANT_PROMPTS = [
46
425
  {
@@ -399,6 +778,367 @@ async function dispatchSlackPluginInteractiveHandler(params) {
399
778
  });
400
779
  }
401
780
  //#endregion
781
+ //#region extensions/slack/src/monitor/auth.ts
782
+ let slackChannelMembersCache = /* @__PURE__ */ new WeakMap();
783
+ const DEFAULT_CHANNEL_MEMBERS_CACHE_TTL_MS = 6e4;
784
+ const CHANNEL_MEMBERS_CACHE_MAX = 512;
785
+ const SLACK_CHANNEL_ID = "slack";
786
+ const SLACK_USER_NAME_KIND = "plugin:slack-user-name";
787
+ function normalizeSlackUserId(raw) {
788
+ const value = (raw ?? "").trim().toLowerCase();
789
+ if (!value) return "";
790
+ const mention = value.match(/^<@([a-z0-9_]+)>$/i);
791
+ if (mention?.[1]) return mention[1];
792
+ return value.replace(/^(slack:|user:)/, "");
793
+ }
794
+ function isSlackStableUserId(value) {
795
+ return /^[ubw][a-z0-9_]+$/i.test(value);
796
+ }
797
+ function normalizeSlackStableEntry(entry) {
798
+ const normalized = entry.trim().toLowerCase();
799
+ if (!normalized) return null;
800
+ const userId = normalizeSlackUserId(normalized);
801
+ return isSlackStableUserId(userId) ? userId : null;
802
+ }
803
+ function normalizeSlackNameEntry(entry) {
804
+ const normalized = entry.trim().toLowerCase();
805
+ if (!normalized || normalizeSlackStableEntry(normalized)) return null;
806
+ return normalized.replace(/^slack:/, "") || null;
807
+ }
808
+ function normalizeSlackNameSubject(value) {
809
+ return value.trim().toLowerCase() || null;
810
+ }
811
+ function normalizeSlackNameSlugEntry(entry) {
812
+ const name = normalizeSlackNameEntry(entry);
813
+ if (!name) return null;
814
+ const slug = normalizeSlackSlug(name);
815
+ return slug && slug !== name ? slug : null;
816
+ }
817
+ const slackIngressIdentity = defineStableChannelIngressIdentity({
818
+ key: "senderId",
819
+ kind: "stable-id",
820
+ normalizeEntry: normalizeSlackStableEntry,
821
+ normalizeSubject: normalizeSlackUserId,
822
+ sensitivity: "pii",
823
+ aliases: [["senderName", normalizeSlackNameEntry], ["senderNameSlug", normalizeSlackNameSlugEntry]].map(([key, normalizeEntry]) => ({
824
+ key,
825
+ kind: SLACK_USER_NAME_KIND,
826
+ normalizeEntry,
827
+ normalizeSubject: normalizeSlackNameSubject,
828
+ dangerous: true,
829
+ sensitivity: "pii"
830
+ }))
831
+ });
832
+ function createSlackIngressSubject(params) {
833
+ const senderId = normalizeSlackUserId(params.senderId);
834
+ const senderName = params.senderName?.trim().toLowerCase();
835
+ return {
836
+ stableId: senderId,
837
+ aliases: {
838
+ senderName,
839
+ senderNameSlug: senderName ? normalizeSlackSlug(senderName) : void 0
840
+ }
841
+ };
842
+ }
843
+ function createSlackIngressResolver(ctx) {
844
+ return createChannelIngressResolver({
845
+ channelId: SLACK_CHANNEL_ID,
846
+ accountId: ctx.accountId,
847
+ identity: slackIngressIdentity,
848
+ cfg: ctx.cfg
849
+ });
850
+ }
851
+ function readSlackCacheTtlMs(envName, fallback) {
852
+ const raw = process.env[envName]?.trim();
853
+ if (!raw) return fallback;
854
+ const parsed = /^\d+$/.test(raw) ? Number(raw) : NaN;
855
+ return Number.isSafeInteger(parsed) ? parsed : fallback;
856
+ }
857
+ function getChannelMembersCache(ctx) {
858
+ const existing = slackChannelMembersCache.get(ctx);
859
+ if (existing) return existing;
860
+ const next = /* @__PURE__ */ new Map();
861
+ slackChannelMembersCache.set(ctx, next);
862
+ return next;
863
+ }
864
+ function pruneChannelMembersCache(cache) {
865
+ while (cache.size > CHANNEL_MEMBERS_CACHE_MAX) {
866
+ const oldest = cache.keys().next();
867
+ if (oldest.done) return;
868
+ cache.delete(oldest.value);
869
+ }
870
+ }
871
+ function buildBaseAllowFrom(ctx) {
872
+ return normalizeAllowListLower(normalizeAllowList(ctx.allowFrom));
873
+ }
874
+ async function resolveSlackEffectiveAllowFrom(ctx, options) {
875
+ const base = buildBaseAllowFrom(ctx);
876
+ if (options?.includePairingStore !== true) return base;
877
+ let storeAllowFrom = [];
878
+ try {
879
+ const resolved = await readChannelIngressStoreAllowFromForDmPolicy({
880
+ provider: "slack",
881
+ accountId: ctx.accountId,
882
+ dmPolicy: ctx.dmPolicy
883
+ });
884
+ storeAllowFrom = Array.isArray(resolved) ? resolved : [];
885
+ } catch {
886
+ storeAllowFrom = [];
887
+ }
888
+ return normalizeAllowListLower([...base, ...storeAllowFrom]);
889
+ }
890
+ async function fetchSlackChannelMemberIds(ctx, channelId) {
891
+ const members = /* @__PURE__ */ new Set();
892
+ let cursor;
893
+ do {
894
+ const response = await ctx.app.client.conversations.members({
895
+ token: ctx.botToken,
896
+ channel: channelId,
897
+ limit: 999,
898
+ ...cursor ? { cursor } : {}
899
+ });
900
+ for (const member of normalizeAllowListLower(response.members)) members.add(member);
901
+ const nextCursor = response.response_metadata?.next_cursor?.trim();
902
+ cursor = nextCursor ? nextCursor : void 0;
903
+ } while (cursor);
904
+ return members;
905
+ }
906
+ async function resolveSlackChannelMemberIds(ctx, channelId) {
907
+ const cache = getChannelMembersCache(ctx);
908
+ const key = `${ctx.accountId}:${channelId}`;
909
+ const ttlMs = readSlackCacheTtlMs("OPENCLAW_SLACK_CHANNEL_MEMBERS_CACHE_TTL_MS", DEFAULT_CHANNEL_MEMBERS_CACHE_TTL_MS);
910
+ const nowMs = Date.now();
911
+ const cached = cache.get(key);
912
+ if (ttlMs > 0 && cached?.members && cached.expiresAtMs >= nowMs) return cached.members;
913
+ if (cached?.pending) return await cached.pending;
914
+ const pending = fetchSlackChannelMemberIds(ctx, channelId);
915
+ cache.set(key, {
916
+ expiresAtMs: ttlMs > 0 ? nowMs + ttlMs : 0,
917
+ pending
918
+ });
919
+ pruneChannelMembersCache(cache);
920
+ try {
921
+ const members = await pending;
922
+ if (ttlMs > 0) {
923
+ cache.set(key, {
924
+ expiresAtMs: Date.now() + ttlMs,
925
+ members
926
+ });
927
+ pruneChannelMembersCache(cache);
928
+ } else cache.delete(key);
929
+ return members;
930
+ } finally {
931
+ if (cache.get(key)?.pending === pending) cache.delete(key);
932
+ }
933
+ }
934
+ function resolveExplicitSlackOwnerIds(allowFromLower) {
935
+ const ownerIds = /* @__PURE__ */ new Set();
936
+ for (const entry of allowFromLower) {
937
+ const ownerId = normalizeSlackAllowOwnerEntry(entry);
938
+ if (ownerId) ownerIds.add(ownerId);
939
+ }
940
+ return [...ownerIds];
941
+ }
942
+ async function authorizeSlackBotRoomMessage(params) {
943
+ const channelUserAllowList = normalizeAllowListLower(params.channelUsers).filter((entry) => entry !== "*");
944
+ if (channelUserAllowList.length > 0 && allowListMatches({
945
+ allowList: channelUserAllowList,
946
+ id: params.senderId,
947
+ name: params.senderName,
948
+ allowNameMatching: params.ctx.allowNameMatching
949
+ })) return true;
950
+ const explicitOwnerIds = resolveExplicitSlackOwnerIds(params.allowFromLower);
951
+ if (explicitOwnerIds.length === 0) {
952
+ logVerbose(`slack: drop bot message ${params.senderId} in ${params.channelId} (no explicit owner id for presence check)`);
953
+ return false;
954
+ }
955
+ try {
956
+ const channelMemberIds = await resolveSlackChannelMemberIds(params.ctx, params.channelId);
957
+ if (explicitOwnerIds.some((ownerId) => channelMemberIds.has(ownerId))) return true;
958
+ logVerbose(`slack: drop bot message ${params.senderId} in ${params.channelId} (no owner present)`);
959
+ } catch (error) {
960
+ logVerbose(`slack: drop bot message ${params.senderId} in ${params.channelId} (owner presence lookup failed: ${formatErrorMessage(error)})`);
961
+ }
962
+ return false;
963
+ }
964
+ function wildcardWhenOpen(entries) {
965
+ return entries.length > 0 ? [...entries] : ["*"];
966
+ }
967
+ function slackIngressConversationKind(channelType) {
968
+ return channelType === "im" ? "direct" : channelType === "mpim" ? "group" : "channel";
969
+ }
970
+ async function resolveSlackCommandIngress(params) {
971
+ const isDirectMessage = params.channelType === "im";
972
+ const channelUsers = normalizeAllowListLower(params.channelUsers);
973
+ const channelUsersConfigured = !isDirectMessage && channelUsers.length > 0;
974
+ return await createSlackIngressResolver(params.ctx).message({
975
+ subject: createSlackIngressSubject({
976
+ senderId: params.senderId,
977
+ senderName: params.senderName
978
+ }),
979
+ conversation: {
980
+ kind: slackIngressConversationKind(params.channelType),
981
+ id: params.channelId
982
+ },
983
+ event: {
984
+ kind: params.eventKind ?? "message",
985
+ authMode: "inbound",
986
+ mayPair: false
987
+ },
988
+ dmPolicy: isDirectMessage ? "open" : "disabled",
989
+ groupPolicy: channelUsersConfigured ? "allowlist" : "open",
990
+ policy: {
991
+ groupAllowFromFallbackToAllowFrom: false,
992
+ mutableIdentifierMatching: params.ctx.allowNameMatching ? "enabled" : "disabled",
993
+ ...params.activation ? { activation: params.activation } : {}
994
+ },
995
+ mentionFacts: params.mentionFacts,
996
+ allowFrom: isDirectMessage ? ["*"] : params.ownerAllowFromLower,
997
+ groupAllowFrom: channelUsersConfigured ? channelUsers : [],
998
+ command: {
999
+ allowTextCommands: params.allowTextCommands,
1000
+ hasControlCommand: params.hasControlCommand,
1001
+ modeWhenAccessGroupsOff: params.modeWhenAccessGroupsOff,
1002
+ ...isDirectMessage ? { commandOwnerAllowFrom: params.ownerAllowFromLower } : {}
1003
+ }
1004
+ });
1005
+ }
1006
+ async function decideSlackSystemIngress(params) {
1007
+ const isDirectMessage = params.channelType === "im";
1008
+ const channelUsers = normalizeAllowListLower(params.channelUsers);
1009
+ const channelUsersConfigured = !isDirectMessage && channelUsers.length > 0;
1010
+ const ownerAllowFrom = params.interactiveEvent && channelUsersConfigured ? params.ownerAllowFromLower.filter((entry) => entry !== "*") : params.ownerAllowFromLower;
1011
+ const hasAnyCommandAllowlist = ownerAllowFrom.length > 0 || channelUsersConfigured;
1012
+ const groupAllowFrom = (() => {
1013
+ if (isDirectMessage) return [];
1014
+ if (params.interactiveEvent && hasAnyCommandAllowlist) return channelUsersConfigured ? channelUsers : [];
1015
+ if (channelUsersConfigured) return channelUsers;
1016
+ return params.channelId ? ["*"] : wildcardWhenOpen(params.ownerAllowFromLower);
1017
+ })();
1018
+ return (await createSlackIngressResolver(params.ctx).message({
1019
+ subject: createSlackIngressSubject({
1020
+ senderId: params.senderId,
1021
+ senderName: params.senderName
1022
+ }),
1023
+ conversation: {
1024
+ kind: slackIngressConversationKind(params.channelType),
1025
+ id: params.channelId ?? "slack-system"
1026
+ },
1027
+ event: {
1028
+ kind: params.interactiveEvent ? "button" : "system",
1029
+ authMode: params.interactiveEvent && hasAnyCommandAllowlist ? "command" : "inbound",
1030
+ mayPair: false
1031
+ },
1032
+ dmPolicy: isDirectMessage ? "open" : "disabled",
1033
+ groupPolicy: params.interactiveEvent && hasAnyCommandAllowlist ? "open" : channelUsersConfigured || !params.channelId && params.ownerAllowFromLower.length > 0 ? "allowlist" : "open",
1034
+ policy: {
1035
+ groupAllowFromFallbackToAllowFrom: false,
1036
+ mutableIdentifierMatching: params.ctx.allowNameMatching ? "enabled" : "disabled"
1037
+ },
1038
+ allowFrom: isDirectMessage ? wildcardWhenOpen(params.ownerAllowFromLower) : ownerAllowFrom,
1039
+ groupAllowFrom,
1040
+ command: params.interactiveEvent && hasAnyCommandAllowlist ? {
1041
+ useAccessGroups: true,
1042
+ allowTextCommands: true,
1043
+ modeWhenAccessGroupsOff: "configured",
1044
+ commandOwnerAllowFrom: ownerAllowFrom
1045
+ } : void 0
1046
+ })).ingress;
1047
+ }
1048
+ async function authorizeSlackSystemEventSender(params) {
1049
+ const senderId = params.senderId?.trim();
1050
+ if (!senderId) return {
1051
+ allowed: false,
1052
+ reason: "missing-sender"
1053
+ };
1054
+ const expectedSenderId = params.expectedSenderId?.trim();
1055
+ if (expectedSenderId && expectedSenderId !== senderId) return {
1056
+ allowed: false,
1057
+ reason: "sender-mismatch"
1058
+ };
1059
+ if (params.interactiveEvent && !expectedSenderId) return {
1060
+ allowed: false,
1061
+ reason: "missing-expected-sender"
1062
+ };
1063
+ const channelId = params.channelId?.trim();
1064
+ let channelType = normalizeSlackChannelType(params.channelType, channelId);
1065
+ let channelName;
1066
+ if (channelId) {
1067
+ const info = await params.ctx.resolveChannelName(channelId).catch(() => ({}));
1068
+ channelName = info.name;
1069
+ const resolvedTypeSource = params.channelType ?? info.type;
1070
+ channelType = normalizeSlackChannelType(resolvedTypeSource, channelId);
1071
+ if (!params.ctx.isChannelAllowed({
1072
+ channelId,
1073
+ channelName,
1074
+ channelType
1075
+ })) return {
1076
+ allowed: false,
1077
+ reason: "channel-not-allowed",
1078
+ channelType,
1079
+ channelName
1080
+ };
1081
+ if (params.interactiveEvent) {
1082
+ const inferredFromId = inferSlackChannelType(channelId);
1083
+ const sourceNormalized = typeof resolvedTypeSource === "string" ? resolvedTypeSource.toLowerCase().trim() : void 0;
1084
+ if (inferredFromId === void 0 && !(sourceNormalized === "im" || sourceNormalized === "mpim" || sourceNormalized === "channel" || sourceNormalized === "group")) return {
1085
+ allowed: false,
1086
+ reason: "ambiguous-channel-type",
1087
+ channelType,
1088
+ channelName
1089
+ };
1090
+ }
1091
+ }
1092
+ const senderName = (await params.ctx.resolveUserName(senderId).catch(() => ({}))).name;
1093
+ const ingressChannelType = channelType ?? "channel";
1094
+ if (ingressChannelType === "im") {
1095
+ if (!params.ctx.dmEnabled || params.ctx.dmPolicy === "disabled") return {
1096
+ allowed: false,
1097
+ reason: "dm-disabled",
1098
+ channelType,
1099
+ channelName
1100
+ };
1101
+ }
1102
+ const allowFromLower = await resolveSlackEffectiveAllowFrom(params.ctx, { includePairingStore: ingressChannelType === "im" });
1103
+ const channelConfig = channelId ? resolveSlackChannelConfig({
1104
+ channelId,
1105
+ channelName,
1106
+ channels: params.ctx.channelsConfig,
1107
+ channelKeys: params.ctx.channelsConfigKeys,
1108
+ defaultRequireMention: params.ctx.defaultRequireMention,
1109
+ allowNameMatching: params.ctx.allowNameMatching
1110
+ }) : null;
1111
+ const channelUsersAllowlistConfigured = Array.isArray(channelConfig?.users) && channelConfig.users.length > 0;
1112
+ if ((await decideSlackSystemIngress({
1113
+ ctx: params.ctx,
1114
+ senderId,
1115
+ senderName,
1116
+ channelType: ingressChannelType,
1117
+ channelId,
1118
+ ownerAllowFromLower: allowFromLower,
1119
+ channelUsers: channelConfig?.users,
1120
+ interactiveEvent: params.interactiveEvent === true
1121
+ })).decision === "allow") return {
1122
+ allowed: true,
1123
+ channelType,
1124
+ channelName
1125
+ };
1126
+ if (channelType === "im" || !channelId) return {
1127
+ allowed: false,
1128
+ reason: "sender-not-allowlisted",
1129
+ ...channelId ? {
1130
+ channelType,
1131
+ channelName
1132
+ } : {}
1133
+ };
1134
+ return {
1135
+ allowed: false,
1136
+ reason: params.interactiveEvent && channelUsersAllowlistConfigured && allowFromLower.length > 0 ? "sender-not-authorized" : channelUsersAllowlistConfigured ? "sender-not-channel-allowed" : "sender-not-allowlisted",
1137
+ channelType,
1138
+ channelName
1139
+ };
1140
+ }
1141
+ //#endregion
402
1142
  //#region extensions/slack/src/monitor/mrkdwn.ts
403
1143
  function escapeSlackMrkdwn(value) {
404
1144
  return value.replaceAll("\\", "\\\\").replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replace(/([*_`~])/g, "\\$1");
@@ -452,7 +1192,7 @@ function summarizeAction(action) {
452
1192
  ]);
453
1193
  const selectedLabels = uniqueNonEmptyStrings([...typed.selected_option?.text?.text ? [typed.selected_option.text.text] : [], ...readOptionLabels(typed.selected_options) ?? []]);
454
1194
  const inputValue = typeof typed.value === "string" ? typed.value : void 0;
455
- const inputNumber = actionType === "number_input" && inputValue != null ? Number.parseFloat(inputValue) : void 0;
1195
+ const inputNumber = actionType === "number_input" && inputValue != null ? parseStrictFiniteNumber(inputValue) : void 0;
456
1196
  const parsedNumber = Number.isFinite(inputNumber) ? inputNumber : void 0;
457
1197
  const inputEmail = actionType === "email_text_input" && inputValue?.includes("@") ? inputValue : void 0;
458
1198
  let inputUrl;
@@ -1942,7 +2682,7 @@ function createSlackThreadTsResolver(params) {
1942
2682
  //#region extensions/slack/src/monitor/message-handler.ts
1943
2683
  let slackMessagePipelinePromise;
1944
2684
  function loadSlackMessagePipeline() {
1945
- slackMessagePipelinePromise ??= import("./pipeline.runtime-BVK8yXw_.js");
2685
+ slackMessagePipelinePromise ??= import("./pipeline.runtime-XK36f1W3.js");
1946
2686
  return slackMessagePipelinePromise;
1947
2687
  }
1948
2688
  const APP_MENTION_RETRY_TTL_MS = 6e4;
@@ -2433,6 +3173,52 @@ function formatSlackUserResolved(entry) {
2433
3173
  });
2434
3174
  }
2435
3175
  //#endregion
3176
+ //#region extensions/slack/src/monitor/dm-auth.ts
3177
+ async function authorizeSlackDirectMessage(params) {
3178
+ if (!params.ctx.dmEnabled || params.ctx.dmPolicy === "disabled") {
3179
+ await params.onDisabled();
3180
+ return false;
3181
+ }
3182
+ if (params.ctx.dmPolicy === "open" && params.allowFromLower.includes("*")) return true;
3183
+ const senderName = (await params.resolveSenderName(params.senderId))?.name ?? void 0;
3184
+ const allowMatch = resolveSlackAllowListMatch({
3185
+ allowList: params.allowFromLower,
3186
+ id: params.senderId,
3187
+ name: senderName,
3188
+ allowNameMatching: params.ctx.allowNameMatching
3189
+ });
3190
+ const allowMatchMeta = formatAllowlistMatchMeta(allowMatch);
3191
+ if (allowMatch.allowed) return true;
3192
+ if (params.ctx.dmPolicy === "pairing") {
3193
+ await createChannelPairingChallengeIssuer({
3194
+ channel: "slack",
3195
+ upsertPairingRequest: async ({ id, meta }) => await upsertChannelPairingRequest({
3196
+ channel: "slack",
3197
+ id,
3198
+ accountId: params.accountId,
3199
+ meta
3200
+ })
3201
+ })({
3202
+ senderId: params.senderId,
3203
+ senderIdLine: `Your Slack user id: ${params.senderId}`,
3204
+ meta: { name: senderName },
3205
+ sendPairingReply: params.sendPairingReply,
3206
+ onCreated: () => {
3207
+ params.log(`slack pairing request sender=${params.senderId} name=${senderName ?? "unknown"} (${allowMatchMeta})`);
3208
+ },
3209
+ onReplyError: (err) => {
3210
+ params.log(`slack pairing reply failed for ${params.senderId}: ${formatErrorMessage(err)}`);
3211
+ }
3212
+ });
3213
+ return false;
3214
+ }
3215
+ await params.onUnauthorized({
3216
+ allowMatchMeta,
3217
+ senderName
3218
+ });
3219
+ return false;
3220
+ }
3221
+ //#endregion
2436
3222
  //#region extensions/slack/src/monitor/external-arg-menu-store.ts
2437
3223
  const SLACK_EXTERNAL_ARG_MENU_TOKEN_BYTES = 18;
2438
3224
  const SLACK_EXTERNAL_ARG_MENU_TOKEN_PATTERN = new RegExp(`^[A-Za-z0-9_-]{${Math.ceil(SLACK_EXTERNAL_ARG_MENU_TOKEN_BYTES * 8 / 6)}}$`);
@@ -2473,6 +3259,20 @@ function createSlackExternalArgMenuStore() {
2473
3259
  };
2474
3260
  }
2475
3261
  //#endregion
3262
+ //#region extensions/slack/src/monitor/room-context.ts
3263
+ function resolveSlackRoomContextHints(params) {
3264
+ const untrustedChannelMetadata = params.isRoomish ? buildUntrustedChannelMetadata({
3265
+ source: "slack",
3266
+ label: "Slack channel description",
3267
+ entries: [params.channelInfo?.topic, params.channelInfo?.purpose]
3268
+ }) : void 0;
3269
+ const systemPromptParts = [params.isRoomish ? normalizeOptionalString(params.channelConfig?.systemPrompt) ?? null : null].filter((entry) => Boolean(entry));
3270
+ return {
3271
+ untrustedChannelMetadata,
3272
+ groupSystemPrompt: systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : void 0
3273
+ };
3274
+ }
3275
+ //#endregion
2476
3276
  //#region extensions/slack/src/monitor/slash.ts
2477
3277
  const SLACK_COMMAND_ARG_ACTION_ID = "openclaw_cmdarg";
2478
3278
  const SLACK_COMMAND_ARG_ACTION_LISTENER = /^openclaw_cmdarg/;
@@ -2497,7 +3297,7 @@ function loadSlashCommandsRuntime() {
2497
3297
  return slashCommandsRuntimePromise;
2498
3298
  }
2499
3299
  function loadSlashDispatchRuntime() {
2500
- slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-BBhdJHb8.js");
3300
+ slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-lsyTm_q5.js");
2501
3301
  return slashDispatchRuntimePromise;
2502
3302
  }
2503
3303
  function loadSlackPluginCommandsRuntime() {
@@ -3282,7 +4082,7 @@ async function monitorSlackProvider(opts = {}) {
3282
4082
  const groupDmChannels = dmConfig?.groupChannels;
3283
4083
  let channelsConfig = slackCfg.channels;
3284
4084
  const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
3285
- const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy({
4085
+ const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy$1({
3286
4086
  providerConfigPresent: cfg.channels?.slack !== void 0,
3287
4087
  groupPolicy: slackCfg.groupPolicy,
3288
4088
  defaultGroupPolicy
@@ -3612,6 +4412,6 @@ async function monitorSlackProvider(opts = {}) {
3612
4412
  await gracefulStop();
3613
4413
  }
3614
4414
  }
3615
- const resolveSlackRuntimeGroupPolicy = resolveOpenProviderRuntimeGroupPolicy;
4415
+ const resolveSlackRuntimeGroupPolicy = resolveOpenProviderRuntimeGroupPolicy$1;
3616
4416
  //#endregion
3617
- export { resolveSlackRuntimeGroupPolicy as n, escapeSlackMrkdwn as r, monitorSlackProvider as t };
4417
+ export { resolveStorePath$1 as _, escapeSlackMrkdwn as a, stripSlackMentionsForCommandDetection as b, authorizeSlackBotRoomMessage as c, buildSlackAssistantThreadMetadata as d, parseSlackAssistantThreadMetadata as f, resolveChannelContextVisibilityMode as g, readSessionUpdatedAt as h, authorizeSlackDirectMessage as i, resolveSlackCommandIngress as l, resolveSlackChatType as m, resolveSlackRuntimeGroupPolicy as n, recordInboundSession as o, normalizeSlackChannelType as p, resolveSlackRoomContextHints as r, resolveConversationLabel$1 as s, monitorSlackProvider as t, resolveSlackEffectiveAllowFrom as u, updateLastRoute as v, buildSlackSlashCommandMatcher as y };