@openclaw/slack 2026.5.14-beta.2 → 2026.5.16-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 (32) hide show
  1. package/dist/{action-runtime-CyE2jfW-.js → action-runtime-p39JLqwf.js} +1 -1
  2. package/dist/action-runtime.runtime-BzrPV3EA.js +2 -0
  3. package/dist/{actions-BibhOXpJ.js → actions-BCRbHv1Q.js} +3 -2
  4. package/dist/{actions.runtime-B9XQG6X4.js → actions.runtime-CpywQR3D.js} +1 -1
  5. package/dist/api.js +5 -5
  6. package/dist/{approval-handler.runtime-BjzVRaXN.js → approval-handler.runtime-DXrdRbkT.js} +1 -1
  7. package/dist/{channel-Dh07mU_K.js → channel-CVSopl66.js} +9 -9
  8. package/dist/channel-plugin-api.js +1 -1
  9. package/dist/{channel.setup-CmG37S2W.js → channel.setup-DknBgufI.js} +2 -2
  10. package/dist/inbound-contract-test-api.js +1 -1
  11. package/dist/{monitor-DDE5AI2O.js → monitor-CdVxsuHi.js} +2 -2
  12. package/dist/{outbound-adapter-BluPNDNi.js → outbound-adapter-CHm6e-0Q.js} +1 -1
  13. package/dist/outbound-payload-test-api.js +1 -1
  14. package/dist/{outbound-payload.test-harness-BNxnP6MC.js → outbound-payload.test-harness-C0CW7_CE.js} +1 -1
  15. package/dist/{pipeline.runtime-Dft2-QU4.js → pipeline.runtime-CakcaQh9.js} +113 -136
  16. package/dist/{prepare-D0tMg4dt.js → prepare-DSRUr44d.js} +220 -81
  17. package/dist/{provider-C6WxaFFf.js → provider-bKg1hkf5.js} +139 -7
  18. package/dist/{replies-D0QXXSPP.js → replies-Fg1T3ZzU.js} +2 -2
  19. package/dist/runtime-api.js +5 -5
  20. package/dist/{send-Dg9zcyYT.js → send-CxXFbqN1.js} +1 -1
  21. package/dist/send.runtime-BHCPpSj_.js +2 -0
  22. package/dist/send.runtime-CDG5AgU3.js +2 -0
  23. package/dist/{setup-core-WWQl-cE9.js → setup-core-B7pou7oe.js} +23 -22
  24. package/dist/setup-plugin-api.js +1 -1
  25. package/dist/{setup-surface-BLoTgna4.js → setup-surface-D6LLzeRz.js} +14 -13
  26. package/dist/{shared-GoB-OuUq.js → shared-7Vi9j4aV.js} +1 -1
  27. package/dist/{slash-dispatch.runtime-Bz_OkRcR.js → slash-dispatch.runtime-Cg7uU92H.js} +1 -1
  28. package/dist/test-api.js +5 -5
  29. package/package.json +4 -4
  30. package/dist/action-runtime.runtime-DLhfKw4B.js +0 -2
  31. package/dist/send.runtime-CjjQ9StM.js +0 -2
  32. package/dist/send.runtime-E47jGN-2.js +0 -2
@@ -2,25 +2,26 @@ import { l as resolveSlackReplyToMode } from "./accounts-yk5K3wQU.js";
2
2
  import { r as parseSlackTarget } from "./target-parsing-CQmv-iSm.js";
3
3
  import "./targets-B1tYCAr6.js";
4
4
  import { i as normalizeSlackAllowOwnerEntry, o as resolveSlackAllowListMatch, r as normalizeAllowListLower } from "./allow-list-nwXs_eCP.js";
5
- import { i as hasSlackThreadParticipationWithPersistence, t as sendMessageSlack } from "./send-Dg9zcyYT.js";
6
- import { _ as resolveSlackThreadStarter, g as resolveSlackThreadHistory, l as reactSlackMessage, y as formatSlackFileReference } from "./actions-BibhOXpJ.js";
5
+ import { i as hasSlackThreadParticipationWithPersistence, t as sendMessageSlack } from "./send-CxXFbqN1.js";
6
+ import { _ as resolveSlackThreadStarter, g as resolveSlackThreadHistory, l as reactSlackMessage, y as formatSlackFileReference } from "./actions-BCRbHv1Q.js";
7
7
  import { t as formatSlackError } from "./errors-C_sW0Zgl.js";
8
8
  import { b as readSessionUpdatedAt, c as authorizeSlackBotRoomMessage, d as resolveSlackEffectiveAllowFrom, g as resolveSlackChannelConfig, h as resolveSlackChatType, k as stripSlackMentionsForCommandDetection, m as normalizeSlackChannelType, n as authorizeSlackDirectMessage, o as resolveConversationLabel$1, t as resolveSlackRoomContextHints, u as resolveSlackCommandIngress, w as resolveStorePath, x as resolveChannelContextVisibilityMode } from "./room-context-Cd8jFpS-.js";
9
- import "./send.runtime-CjjQ9StM.js";
9
+ import "./send.runtime-BHCPpSj_.js";
10
10
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
11
11
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
12
12
  import { resolveChannelMessageSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-message";
13
13
  import { logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
14
14
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
15
15
  import { ensureConfiguredBindingRouteReady, resolveConfiguredBindingRoute, resolveRuntimeConversationBindingRoute } from "openclaw/plugin-sdk/conversation-runtime";
16
- import { buildPendingHistoryContextFromMap, recordPendingHistoryEntryIfEnabled } from "openclaw/plugin-sdk/reply-history";
16
+ import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
17
17
  import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
18
- import { buildMentionRegexes, formatInboundEnvelope, implicitMentionKindWhen, logInboundDrop, matchesMentionWithExplicit, resolveEnvelopeFormatOptions } from "openclaw/plugin-sdk/channel-inbound";
18
+ import { buildChannelTurnContext, buildMentionRegexes, formatInboundEnvelope, implicitMentionKindWhen, logInboundDrop, matchesMentionWithExplicit, resolveEnvelopeFormatOptions, toInboundMediaFacts } from "openclaw/plugin-sdk/channel-inbound";
19
19
  import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist, shouldIncludeSupplementalContext } from "openclaw/plugin-sdk/security-runtime";
20
20
  import { resolveAckReaction, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
21
21
  import { hasControlCommand } from "openclaw/plugin-sdk/command-detection";
22
22
  import { shouldHandleTextCommands } from "openclaw/plugin-sdk/command-surface";
23
- import { finalizeInboundContext } from "openclaw/plugin-sdk/reply-dispatch-runtime";
23
+ import { recordDroppedChannelTurnHistory } from "openclaw/plugin-sdk/inbound-reply-dispatch";
24
+ import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
24
25
  import { runTasksWithConcurrency } from "openclaw/plugin-sdk/concurrency-runtime";
25
26
  //#region extensions/slack/src/monitor/message-handler/prepare-content.ts
26
27
  const SLACK_MENTION_RESOLUTION_CONCURRENCY = 4;
@@ -28,7 +29,7 @@ const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
28
29
  const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
29
30
  let slackMediaModulePromise$1;
30
31
  function loadSlackMediaModule$1() {
31
- slackMediaModulePromise$1 ??= import("./actions-BibhOXpJ.js").then((n) => n.h);
32
+ slackMediaModulePromise$1 ??= import("./actions-BCRbHv1Q.js").then((n) => n.h);
32
33
  return slackMediaModulePromise$1;
33
34
  }
34
35
  function collectUniqueSlackMentionIds$1(texts) {
@@ -190,13 +191,19 @@ async function resolveSlackMessageContent(params) {
190
191
  files: ownFiles,
191
192
  client: params.client,
192
193
  token: params.botToken,
193
- maxBytes: params.mediaMaxBytes
194
+ maxBytes: params.mediaMaxBytes,
195
+ readIdleTimeoutMs: params.mediaReadIdleTimeoutMs,
196
+ totalTimeoutMs: params.mediaTotalTimeoutMs,
197
+ abortSignal: params.abortSignal
194
198
  })) : Promise.resolve(null);
195
199
  const attachmentContentPromise = params.message.attachments && params.message.attachments.length > 0 ? loadSlackMediaModule$1().then(({ resolveSlackAttachmentContent }) => resolveSlackAttachmentContent({
196
200
  attachments: params.message.attachments,
197
201
  client: params.client,
198
202
  token: params.botToken,
199
- maxBytes: params.mediaMaxBytes
203
+ maxBytes: params.mediaMaxBytes,
204
+ readIdleTimeoutMs: params.mediaReadIdleTimeoutMs,
205
+ totalTimeoutMs: params.mediaTotalTimeoutMs,
206
+ abortSignal: params.abortSignal
200
207
  })) : Promise.resolve(null);
201
208
  const [media, attachmentContent] = await Promise.all([mediaPromise, attachmentContentPromise]);
202
209
  const mergedMedia = [...media ?? [], ...attachmentContent?.media ?? []];
@@ -569,7 +576,7 @@ function formatSlackBotStarterThreadLabel(params) {
569
576
  //#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
570
577
  let slackMediaModulePromise;
571
578
  function loadSlackMediaModule() {
572
- slackMediaModulePromise ??= import("./actions-BibhOXpJ.js").then((n) => n.h);
579
+ slackMediaModulePromise ??= import("./actions-BCRbHv1Q.js").then((n) => n.h);
573
580
  return slackMediaModulePromise;
574
581
  }
575
582
  const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
@@ -837,6 +844,10 @@ const SLACK_ANY_MENTION_RE = /<@[^>]+>|<!subteam\^[^>]+>/;
837
844
  const SLACK_USER_MENTION_RE = /<@([^>|]+)(?:\|[^>]+)?>/g;
838
845
  const SLACK_SUBTEAM_MENTION_RE = /<!subteam\^([^>|]+)(?:\|[^>]+)?>/g;
839
846
  const SLACK_SUBTEAM_MENTION_MARKER = "<!subteam^";
847
+ const SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS = 4;
848
+ const SLACK_HISTORY_MEDIA_MAX_BYTES = 10 * 1024 * 1024;
849
+ const SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS = 1e3;
850
+ const SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS = 3e3;
840
851
  function resolveCachedMentionRegexes(ctx, agentId) {
841
852
  const key = normalizeOptionalString(agentId) ?? "__default__";
842
853
  let byAgent = mentionRegexCache.get(ctx);
@@ -850,6 +861,60 @@ function resolveCachedMentionRegexes(ctx, agentId) {
850
861
  byAgent.set(key, built);
851
862
  return built;
852
863
  }
864
+ function isSlackImageFileCandidate(file) {
865
+ if ((file.mimetype?.split(";")[0]?.trim().toLowerCase())?.startsWith("image/")) return true;
866
+ return Boolean(mimeTypeFromFilePath(file.name)?.startsWith("image/"));
867
+ }
868
+ function sliceSlackImageFileCandidates(files, limit) {
869
+ if (limit <= 0 || !files?.length) return [];
870
+ return files.filter(isSlackImageFileCandidate).slice(0, limit);
871
+ }
872
+ function sliceSlackHistoryAttachmentCandidates(attachments, limit) {
873
+ if (limit <= 0 || !attachments?.length) return [];
874
+ const out = [];
875
+ let remaining = limit;
876
+ for (const attachment of attachments) {
877
+ if (attachment.is_share !== true) continue;
878
+ const hasImageUrl = Boolean(normalizeOptionalString(attachment.image_url));
879
+ const files = sliceSlackImageFileCandidates(attachment.files, remaining - (hasImageUrl ? 1 : 0));
880
+ if (!hasImageUrl && files.length === 0) continue;
881
+ out.push({
882
+ ...attachment,
883
+ files
884
+ });
885
+ remaining -= (hasImageUrl ? 1 : 0) + files.length;
886
+ if (remaining <= 0) break;
887
+ }
888
+ return out;
889
+ }
890
+ function buildSlackHistoryMediaCandidateMessage(message) {
891
+ const files = sliceSlackImageFileCandidates(message.files, SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS);
892
+ const attachments = sliceSlackHistoryAttachmentCandidates(message.attachments, Math.max(0, SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS - files.length));
893
+ if (files.length === 0 && attachments.length === 0) return null;
894
+ return {
895
+ ...message,
896
+ files,
897
+ attachments
898
+ };
899
+ }
900
+ async function resolveSlackHistoryMediaForPendingRecord(params) {
901
+ const mediaMessage = buildSlackHistoryMediaCandidateMessage(params.message);
902
+ if (!mediaMessage) return [];
903
+ return toInboundMediaFacts((await resolveSlackMessageContent({
904
+ message: mediaMessage,
905
+ isThreadReply: params.isThreadReply,
906
+ threadStarter: params.threadStarter,
907
+ isBotMessage: params.isBotMessage,
908
+ client: params.ctx.app.client,
909
+ botToken: params.ctx.botToken,
910
+ mediaMaxBytes: Math.min(params.ctx.mediaMaxBytes, SLACK_HISTORY_MEDIA_MAX_BYTES),
911
+ mediaReadIdleTimeoutMs: SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS,
912
+ mediaTotalTimeoutMs: SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS
913
+ }))?.effectiveDirectMedia, {
914
+ kind: "image",
915
+ messageId: params.message.ts
916
+ });
917
+ }
853
918
  function collectUniqueSlackMentionIds(text, regex) {
854
919
  const ids = [];
855
920
  regex.lastIndex = 0;
@@ -1198,18 +1263,52 @@ async function prepareSlackMessage(params) {
1198
1263
  reason: "no-mention"
1199
1264
  }, "skipping channel message");
1200
1265
  const pendingText = (message.text ?? "").trim();
1266
+ const historyMediaCandidate = buildSlackHistoryMediaCandidateMessage(message);
1201
1267
  const fallbackFile = message.files?.length ? `[Slack file: ${formatSlackFileReference(message.files[0])}]` : "";
1202
- const pendingBody = pendingText || fallbackFile;
1203
- recordPendingHistoryEntryIfEnabled({
1204
- historyMap: ctx.channelHistories,
1205
- historyKey,
1206
- limit: ctx.historyLimit,
1207
- entry: pendingBody ? {
1208
- sender: await resolveSenderName(),
1209
- body: pendingBody,
1210
- timestamp: message.ts ? Math.round(Number(message.ts) * 1e3) : void 0,
1211
- messageId: message.ts
1212
- } : null
1268
+ const pendingBody = pendingText || fallbackFile || (!fallbackFile && historyMediaCandidate ? "[Slack media attachment]" : "");
1269
+ const skippedThreadStarter = historyMediaCandidate && isThreadReply && threadTs ? await resolveSlackThreadStarter({
1270
+ channelId: message.channel,
1271
+ threadTs,
1272
+ client: ctx.app.client
1273
+ }) : null;
1274
+ const timestamp = message.ts ? Math.round(Number(message.ts) * 1e3) : void 0;
1275
+ const senderName = pendingBody ? await resolveSenderName() : void 0;
1276
+ await recordDroppedChannelTurnHistory({
1277
+ input: {
1278
+ id: message.ts ?? `${message.channel}:${Date.now()}`,
1279
+ timestamp,
1280
+ rawText: pendingBody,
1281
+ textForAgent: pendingBody,
1282
+ raw: message
1283
+ },
1284
+ admission: {
1285
+ kind: "drop",
1286
+ reason: "slack-no-mention",
1287
+ recordHistory: true
1288
+ },
1289
+ preflight: {
1290
+ message: pendingBody ? {
1291
+ rawBody: pendingBody,
1292
+ body: pendingBody,
1293
+ bodyForAgent: pendingBody,
1294
+ senderLabel: senderName,
1295
+ envelopeFrom: senderName
1296
+ } : void 0,
1297
+ history: {
1298
+ key: historyKey,
1299
+ historyMap: ctx.channelHistories,
1300
+ limit: ctx.historyLimit,
1301
+ recordOnDrop: true,
1302
+ mediaLimit: SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS
1303
+ },
1304
+ media: () => resolveSlackHistoryMediaForPendingRecord({
1305
+ ctx,
1306
+ message,
1307
+ isThreadReply,
1308
+ threadStarter: skippedThreadStarter,
1309
+ isBotMessage
1310
+ })
1311
+ }
1213
1312
  });
1214
1313
  return null;
1215
1314
  }
@@ -1270,6 +1369,7 @@ async function prepareSlackMessage(params) {
1270
1369
  enqueueSystemEvent(`${inboundLabel}: ${preview}`, {
1271
1370
  sessionKey,
1272
1371
  contextKey: `slack:message:${message.channel}:${message.ts ?? "unknown"}`,
1372
+ forceSenderIsOwnerFalse: true,
1273
1373
  trusted: false
1274
1374
  });
1275
1375
  const envelopeFrom = resolveConversationLabel$1({
@@ -1286,6 +1386,7 @@ async function prepareSlackMessage(params) {
1286
1386
  storePath,
1287
1387
  sessionKey
1288
1388
  });
1389
+ const channelHistory = createChannelHistoryWindow({ historyMap: ctx.channelHistories });
1289
1390
  const dmHistoryLimit = isDirectMessage ? resolveSlackDmHistoryLimit({
1290
1391
  account,
1291
1392
  userId: message.user,
@@ -1315,8 +1416,7 @@ async function prepareSlackMessage(params) {
1315
1416
  inboundHistory: void 0
1316
1417
  };
1317
1418
  if (dmHistoryContext.body) combinedBody = `${dmHistoryContext.body}\n\n${combinedBody}`;
1318
- if (isRoomish && ctx.historyLimit > 0) combinedBody = buildPendingHistoryContextFromMap({
1319
- historyMap: ctx.channelHistories,
1419
+ if (isRoomish && ctx.historyLimit > 0) combinedBody = channelHistory.buildPendingContext({
1320
1420
  historyKey,
1321
1421
  limit: ctx.historyLimit,
1322
1422
  currentMessage: combinedBody,
@@ -1353,65 +1453,98 @@ async function prepareSlackMessage(params) {
1353
1453
  effectiveDirectMedia
1354
1454
  });
1355
1455
  const effectiveMedia = effectiveDirectMedia ?? threadStarterMedia;
1356
- const firstMedia = effectiveMedia?.[0];
1357
- const inboundHistory = isRoomish && ctx.historyLimit > 0 ? (ctx.channelHistories.get(historyKey) ?? []).map((entry) => ({
1358
- sender: entry.sender,
1359
- body: entry.body,
1360
- timestamp: entry.timestamp
1361
- })) : dmHistoryContext.inboundHistory;
1456
+ const inboundHistory = isRoomish && ctx.historyLimit > 0 ? channelHistory.buildInboundHistory({
1457
+ historyKey,
1458
+ limit: ctx.historyLimit
1459
+ }) : dmHistoryContext.inboundHistory;
1362
1460
  const commandBody = textForCommandDetection.trim();
1363
- const ctxPayload = finalizeInboundContext({
1364
- Body: combinedBody,
1365
- BodyForAgent: rawBody,
1366
- InboundHistory: inboundHistory,
1367
- RawBody: rawBody,
1368
- CommandBody: commandBody,
1369
- BodyForCommands: commandBody,
1370
- From: slackFrom,
1371
- To: slackTo,
1372
- SessionKey: sessionKey,
1373
- AccountId: route.accountId,
1374
- ChatType: chatType,
1375
- ConversationLabel: envelopeFrom,
1376
- GroupSubject: isRoomish ? roomLabel : void 0,
1377
- GroupSpace: ctx.teamId || void 0,
1378
- GroupSystemPrompt: groupSystemPrompt,
1379
- UntrustedContext: untrustedChannelMetadata ? [untrustedChannelMetadata] : void 0,
1380
- SenderName: senderName,
1381
- SenderId: senderId,
1382
- Provider: "slack",
1383
- Surface: "slack",
1384
- MessageSid: message.ts,
1385
- ReplyToId: threadContext.replyToId,
1386
- MessageThreadId: threadContext.messageThreadId,
1387
- ParentSessionKey: threadKeys.parentSessionKey,
1388
- ThreadStarterBody: !threadSessionPreviousTimestamp ? threadStarterBody : void 0,
1389
- ThreadHistoryBody: threadHistoryBody,
1390
- IsFirstThreadTurn: isThreadReply && threadTs && !threadSessionPreviousTimestamp ? true : void 0,
1391
- ThreadLabel: threadLabel,
1392
- Timestamp: message.ts ? Math.round(Number(message.ts) * 1e3) : void 0,
1393
- ...buildSlackMentionContextPayload({
1394
- isRoomish,
1395
- effectiveWasMentioned,
1396
- explicitlyMentioned,
1397
- mentionedUserIds,
1398
- mentionedSubteamIds,
1399
- matchedImplicitMentionKinds,
1400
- mentionSource
1401
- }),
1402
- MediaPath: firstMedia?.path,
1403
- MediaType: firstMedia?.contentType,
1404
- MediaUrl: firstMedia?.path,
1405
- MediaPaths: effectiveMedia && effectiveMedia.length > 0 ? effectiveMedia.map((m) => m.path) : void 0,
1406
- MediaUrls: effectiveMedia && effectiveMedia.length > 0 ? effectiveMedia.map((m) => m.path) : void 0,
1407
- MediaTypes: effectiveMedia && effectiveMedia.length > 0 ? effectiveMedia.map((m) => m.contentType ?? "") : void 0,
1408
- CommandAuthorized: commandAuthorized,
1409
- OriginatingChannel: "slack",
1410
- OriginatingTo: slackTo,
1411
- NativeChannelId: message.channel
1461
+ const ctxPayload = buildChannelTurnContext({
1462
+ channel: "slack",
1463
+ provider: "slack",
1464
+ surface: "slack",
1465
+ accountId: route.accountId,
1466
+ messageId: message.ts,
1467
+ timestamp: message.ts ? Math.round(Number(message.ts) * 1e3) : void 0,
1468
+ from: slackFrom,
1469
+ sender: {
1470
+ id: senderId,
1471
+ name: senderName,
1472
+ displayLabel: senderName
1473
+ },
1474
+ conversation: {
1475
+ kind: chatType,
1476
+ id: message.channel,
1477
+ label: envelopeFrom,
1478
+ spaceId: ctx.teamId || void 0,
1479
+ threadId: threadContext.messageThreadId,
1480
+ nativeChannelId: message.channel,
1481
+ routePeer: {
1482
+ kind: chatType,
1483
+ id: message.channel
1484
+ }
1485
+ },
1486
+ route: {
1487
+ agentId: route.agentId,
1488
+ accountId: route.accountId,
1489
+ routeSessionKey: sessionKey,
1490
+ parentSessionKey: threadKeys.parentSessionKey
1491
+ },
1492
+ reply: {
1493
+ to: slackTo,
1494
+ originatingTo: slackTo,
1495
+ replyToId: threadContext.replyToId,
1496
+ messageThreadId: threadContext.messageThreadId,
1497
+ nativeChannelId: message.channel
1498
+ },
1499
+ message: {
1500
+ body: combinedBody,
1501
+ bodyForAgent: rawBody,
1502
+ rawBody,
1503
+ commandBody,
1504
+ envelopeFrom,
1505
+ inboundHistory
1506
+ },
1507
+ access: {
1508
+ mentions: {
1509
+ canDetectMention: isRoomish,
1510
+ wasMentioned: effectiveWasMentioned,
1511
+ hasAnyMention: explicitlyMentioned || mentionedSubteamIds.length > 0,
1512
+ implicitMentionKinds: matchedImplicitMentionKinds,
1513
+ requireMention: shouldRequireMention,
1514
+ effectiveWasMentioned
1515
+ },
1516
+ commands: {
1517
+ authorized: commandAuthorized,
1518
+ allowTextCommands,
1519
+ useAccessGroups: false,
1520
+ authorizers: []
1521
+ }
1522
+ },
1523
+ media: toInboundMediaFacts(effectiveMedia),
1524
+ supplemental: {
1525
+ thread: {
1526
+ starterBody: !threadSessionPreviousTimestamp ? threadStarterBody : void 0,
1527
+ historyBody: threadHistoryBody,
1528
+ label: threadLabel
1529
+ },
1530
+ groupSystemPrompt
1531
+ },
1532
+ extra: {
1533
+ GroupSubject: isRoomish ? roomLabel : void 0,
1534
+ UntrustedContext: untrustedChannelMetadata ? [untrustedChannelMetadata] : void 0,
1535
+ IsFirstThreadTurn: isThreadReply && threadTs && !threadSessionPreviousTimestamp ? true : void 0,
1536
+ ...buildSlackMentionContextPayload({
1537
+ isRoomish,
1538
+ effectiveWasMentioned,
1539
+ explicitlyMentioned,
1540
+ mentionedUserIds,
1541
+ mentionedSubteamIds,
1542
+ matchedImplicitMentionKinds,
1543
+ mentionSource
1544
+ })
1545
+ }
1412
1546
  });
1413
- if (isRoomish && !shouldRequireMention) recordPendingHistoryEntryIfEnabled({
1414
- historyMap: ctx.channelHistories,
1547
+ if (isRoomish && !shouldRequireMention) channelHistory.record({
1415
1548
  historyKey,
1416
1549
  limit: ctx.historyLimit,
1417
1550
  entry: {
@@ -1464,7 +1597,13 @@ async function prepareSlackMessage(params) {
1464
1597
  sessionKey
1465
1598
  }, "failed updating session meta");
1466
1599
  }
1467
- }
1600
+ },
1601
+ history: isRoomish && shouldRequireMention ? {
1602
+ isGroup: true,
1603
+ historyKey,
1604
+ historyMap: ctx.channelHistories,
1605
+ limit: ctx.historyLimit
1606
+ } : void 0
1468
1607
  },
1469
1608
  replyToMode,
1470
1609
  requireMention: shouldRequireMention,
@@ -121,6 +121,7 @@ function registerSlackChannelEvents(params) {
121
121
  enqueueSystemEvent(`Slack channel ${params.kind}: ${label}.`, {
122
122
  sessionKey,
123
123
  contextKey: `slack:channel:${params.kind}:${params.channelId ?? params.channelName ?? "unknown"}`,
124
+ forceSenderIsOwnerFalse: true,
124
125
  trusted: false
125
126
  });
126
127
  };
@@ -700,6 +701,7 @@ function enqueueSlackBlockActionEvent(params) {
700
701
  accountId: params.ctx.accountId,
701
702
  threadId: params.parsed.threadTs
702
703
  },
704
+ forceSenderIsOwnerFalse: true,
703
705
  trusted: false
704
706
  })) requestHeartbeat({
705
707
  source: "hook",
@@ -845,7 +847,8 @@ function parseSlackModalPrivateMetadata(raw) {
845
847
  sessionKey: normalizeOptionalString(parsed.sessionKey),
846
848
  channelId: normalizeOptionalString(parsed.channelId),
847
849
  channelType: normalizeOptionalString(parsed.channelType),
848
- userId: normalizeOptionalString(parsed.userId)
850
+ userId: normalizeOptionalString(parsed.userId),
851
+ pluginInteractiveData: normalizeOptionalString(parsed.pluginInteractiveData)
849
852
  };
850
853
  } catch {
851
854
  return {};
@@ -853,6 +856,35 @@ function parseSlackModalPrivateMetadata(raw) {
853
856
  }
854
857
  //#endregion
855
858
  //#region extensions/slack/src/monitor/events/interactions.modal.ts
859
+ const OPENCLAW_MODAL_CALLBACK_PREFIX = "openclaw:";
860
+ function resolveSlackModalPluginInteractiveData(params) {
861
+ const metadataData = params.metadata.pluginInteractiveData?.trim();
862
+ if (metadataData) return metadataData;
863
+ if (!params.callbackId.startsWith(OPENCLAW_MODAL_CALLBACK_PREFIX)) return;
864
+ return params.callbackId.slice(9).trim() || void 0;
865
+ }
866
+ function shouldHandleSlackModalLifecycleBody(body) {
867
+ const typed = body;
868
+ if ((typed.view?.callback_id ?? "").startsWith(OPENCLAW_MODAL_CALLBACK_PREFIX)) return true;
869
+ const metadata = parseSlackModalPrivateMetadata(typed.view?.private_metadata);
870
+ return Boolean(metadata.pluginInteractiveData?.trim());
871
+ }
872
+ function resolveSlackModalPluginNamespace(data) {
873
+ if (!data) return;
874
+ const separatorIndex = data.indexOf(":");
875
+ return separatorIndex >= 0 ? data.slice(0, separatorIndex) : data;
876
+ }
877
+ function resolveSlackPluginSystemEventPayload(result) {
878
+ if (!result || typeof result !== "object") return;
879
+ const systemEvent = result.systemEvent;
880
+ if (!systemEvent || typeof systemEvent !== "object") return;
881
+ const typed = systemEvent;
882
+ const output = {};
883
+ if (typeof typed.summary === "string" && typed.summary.trim()) output.summary = typed.summary;
884
+ if (typeof typed.reference === "string" && typed.reference.trim()) output.reference = typed.reference;
885
+ if (typed.data && typeof typed.data === "object" && !Array.isArray(typed.data)) output.data = typed.data;
886
+ return Object.keys(output).length > 0 ? output : void 0;
887
+ }
856
888
  function resolveModalSessionRouting(params) {
857
889
  const metadata = params.metadata;
858
890
  if (metadata.sessionKey) return {
@@ -899,6 +931,7 @@ function resolveSlackModalEventBase(params) {
899
931
  expectedUserId: metadata.userId,
900
932
  viewId,
901
933
  sessionRouting,
934
+ stateValues: params.body.view?.state?.values,
902
935
  payload: {
903
936
  actionId: `view:${callbackId}`,
904
937
  callbackId,
@@ -918,12 +951,68 @@ function resolveSlackModalEventBase(params) {
918
951
  }
919
952
  };
920
953
  }
954
+ async function dispatchSlackModalPluginInteractiveHandler(params) {
955
+ if (!params.data) return {
956
+ matched: false,
957
+ handled: false,
958
+ duplicate: false
959
+ };
960
+ const isViewClosed = params.interactionType === "view_closed";
961
+ const interactionId = [
962
+ params.interactionType,
963
+ params.payload.callbackId,
964
+ params.payload.viewId,
965
+ params.payload.userId
966
+ ].filter(Boolean).join(":");
967
+ const result = await dispatchSlackPluginInteractiveHandler({
968
+ data: params.data,
969
+ interactionId,
970
+ ctx: {
971
+ accountId: params.ctx.accountId,
972
+ interactionId,
973
+ conversationId: params.sessionRouting.channelId ?? "",
974
+ parentConversationId: void 0,
975
+ threadId: void 0,
976
+ senderId: params.payload.userId,
977
+ senderUsername: void 0,
978
+ auth: params.auth,
979
+ interaction: {
980
+ kind: params.interactionType,
981
+ callbackId: params.payload.callbackId,
982
+ viewId: params.payload.viewId,
983
+ rootViewId: params.payload.rootViewId,
984
+ previousViewId: params.payload.previousViewId,
985
+ externalId: params.payload.externalId,
986
+ isStackedView: params.payload.isStackedView,
987
+ isCleared: isViewClosed ? params.body.is_cleared === true : void 0,
988
+ inputs: params.payload.inputs,
989
+ stateValues: params.stateValues,
990
+ triggerId: params.body.trigger_id
991
+ }
992
+ },
993
+ respond: {
994
+ acknowledge: async () => {},
995
+ reply: async () => {},
996
+ followUp: async () => {},
997
+ editMessage: async () => {}
998
+ }
999
+ });
1000
+ return {
1001
+ ...result,
1002
+ namespace: result.matched ? resolveSlackModalPluginNamespace(params.data) : void 0,
1003
+ systemEvent: result.matched ? resolveSlackPluginSystemEventPayload(result.result) : void 0
1004
+ };
1005
+ }
921
1006
  async function emitSlackModalLifecycleEvent(params) {
922
- const { callbackId, userId, expectedUserId, viewId, sessionRouting, payload } = resolveSlackModalEventBase({
1007
+ const { callbackId, userId, expectedUserId, viewId, sessionRouting, stateValues, payload } = resolveSlackModalEventBase({
923
1008
  ctx: params.ctx,
924
1009
  body: params.body,
925
1010
  summarizeViewState: params.summarizeViewState
926
1011
  });
1012
+ const pluginInteractiveData = resolveSlackModalPluginInteractiveData({
1013
+ callbackId,
1014
+ metadata: parseSlackModalPrivateMetadata(params.body.view?.private_metadata)
1015
+ });
927
1016
  const isViewClosed = params.interactionType === "view_closed";
928
1017
  const isCleared = params.body.is_cleared === true;
929
1018
  const eventPayload = isViewClosed ? {
@@ -937,6 +1026,20 @@ async function emitSlackModalLifecycleEvent(params) {
937
1026
  if (isViewClosed) params.ctx.runtime.log?.(`slack:interaction view_closed callback=${callbackId} user=${userId} cleared=${isCleared}`);
938
1027
  else params.ctx.runtime.log?.(`slack:interaction view_submission callback=${callbackId} user=${userId} inputs=${payload.inputs.length}`);
939
1028
  if (!expectedUserId) {
1029
+ if (pluginInteractiveData) try {
1030
+ await dispatchSlackModalPluginInteractiveHandler({
1031
+ ctx: params.ctx,
1032
+ body: params.body,
1033
+ interactionType: params.interactionType,
1034
+ data: pluginInteractiveData,
1035
+ auth: { isAuthorizedSender: false },
1036
+ payload,
1037
+ stateValues,
1038
+ sessionRouting
1039
+ });
1040
+ } catch (error) {
1041
+ params.ctx.runtime.log?.(`slack:interaction modal plugin dispatch failed callback=${callbackId} error=${error instanceof Error ? error.message : String(error)}`);
1042
+ }
940
1043
  params.ctx.runtime.log?.(`slack:interaction drop modal callback=${callbackId} user=${userId} reason=missing-expected-user`);
941
1044
  return;
942
1045
  }
@@ -952,7 +1055,31 @@ async function emitSlackModalLifecycleEvent(params) {
952
1055
  params.ctx.runtime.log?.(`slack:interaction drop modal callback=${callbackId} user=${userId} reason=${auth.reason ?? "unauthorized"}`);
953
1056
  return;
954
1057
  }
955
- enqueueSystemEvent(params.formatSystemEvent(eventPayload), {
1058
+ let pluginDispatch;
1059
+ try {
1060
+ pluginDispatch = await dispatchSlackModalPluginInteractiveHandler({
1061
+ ctx: params.ctx,
1062
+ body: params.body,
1063
+ interactionType: params.interactionType,
1064
+ data: pluginInteractiveData,
1065
+ auth: { isAuthorizedSender: auth.allowed },
1066
+ payload,
1067
+ stateValues,
1068
+ sessionRouting
1069
+ });
1070
+ } catch (error) {
1071
+ params.ctx.runtime.log?.(`slack:interaction modal plugin dispatch failed callback=${callbackId} error=${error instanceof Error ? error.message : String(error)}`);
1072
+ }
1073
+ const pluginEventFields = pluginDispatch?.matched === true ? {
1074
+ pluginHandled: pluginDispatch.handled,
1075
+ pluginNamespace: pluginDispatch.namespace,
1076
+ pluginDuplicate: pluginDispatch.duplicate || void 0,
1077
+ pluginSystemEvent: pluginDispatch.systemEvent
1078
+ } : {};
1079
+ enqueueSystemEvent(params.formatSystemEvent({
1080
+ ...eventPayload,
1081
+ ...pluginEventFields
1082
+ }), {
956
1083
  sessionKey: sessionRouting.sessionKey,
957
1084
  contextKey: [
958
1085
  params.contextPrefix,
@@ -964,6 +1091,7 @@ async function emitSlackModalLifecycleEvent(params) {
964
1091
  }
965
1092
  function registerModalLifecycleHandler(params) {
966
1093
  params.register(params.matcher, async ({ ack, body }) => {
1094
+ if (!shouldHandleSlackModalLifecycleBody(body)) return;
967
1095
  await ack();
968
1096
  if (params.ctx.shouldDropMismatchedSlackEvent?.(body)) {
969
1097
  params.ctx.runtime.log?.(`slack:interaction drop ${params.interactionType} payload (mismatched app/team)`);
@@ -982,7 +1110,6 @@ function registerModalLifecycleHandler(params) {
982
1110
  }
983
1111
  //#endregion
984
1112
  //#region extensions/slack/src/monitor/events/interactions.ts
985
- const OPENCLAW_ACTION_PREFIX = "openclaw:";
986
1113
  const SLACK_INTERACTION_EVENT_PREFIX = "Slack interaction: ";
987
1114
  const REDACTED_INTERACTION_VALUE = "[redacted]";
988
1115
  const SLACK_INTERACTION_EVENT_MAX_CHARS = 2400;
@@ -1058,6 +1185,10 @@ function buildCompactSlackInteractionPayload(payload) {
1058
1185
  selectedDateTime: payload.selectedDateTime,
1059
1186
  workflowId: payload.workflowId,
1060
1187
  routedChannelType: payload.routedChannelType,
1188
+ pluginHandled: payload.pluginHandled,
1189
+ pluginNamespace: payload.pluginNamespace,
1190
+ pluginDuplicate: payload.pluginDuplicate,
1191
+ pluginSystemEvent: payload.pluginSystemEvent,
1061
1192
  inputs: compactInputs.length > 0 ? compactInputs : void 0,
1062
1193
  inputsOmitted: rawInputs.length > SLACK_INTERACTION_COMPACT_INPUTS_MAX_ITEMS ? rawInputs.length - SLACK_INTERACTION_COMPACT_INPUTS_MAX_ITEMS : void 0,
1063
1194
  payloadTruncated: true
@@ -1103,7 +1234,7 @@ function registerSlackInteractionEvents(params) {
1103
1234
  formatSystemEvent: formatSlackInteractionSystemEvent
1104
1235
  });
1105
1236
  if (typeof ctx.app.view !== "function") return;
1106
- const modalMatcher = new RegExp(`^${OPENCLAW_ACTION_PREFIX}`);
1237
+ const modalMatcher = /.*/;
1107
1238
  registerModalLifecycleHandler({
1108
1239
  register: (matcher, handler) => ctx.app.view(matcher, handler),
1109
1240
  matcher: modalMatcher,
@@ -1464,6 +1595,7 @@ function registerSlackReactionEvents(params) {
1464
1595
  enqueueSystemEvent(authorLabel ? `${baseText} from ${authorLabel}` : baseText, {
1465
1596
  sessionKey: ingressContext.sessionKey,
1466
1597
  contextKey: `slack:reaction:${action}:${item.channel}:${item.ts}:${event.user}:${emojiLabel}`,
1598
+ forceSenderIsOwnerFalse: true,
1467
1599
  trusted: false
1468
1600
  });
1469
1601
  } catch (err) {
@@ -1629,7 +1761,7 @@ function createSlackThreadTsResolver(params) {
1629
1761
  //#region extensions/slack/src/monitor/message-handler.ts
1630
1762
  let slackMessagePipelinePromise;
1631
1763
  function loadSlackMessagePipeline() {
1632
- slackMessagePipelinePromise ??= import("./pipeline.runtime-Dft2-QU4.js");
1764
+ slackMessagePipelinePromise ??= import("./pipeline.runtime-CakcaQh9.js");
1633
1765
  return slackMessagePipelinePromise;
1634
1766
  }
1635
1767
  const APP_MENTION_RETRY_TTL_MS = 6e4;
@@ -2171,7 +2303,7 @@ function loadSlashCommandsRuntime() {
2171
2303
  return slashCommandsRuntimePromise;
2172
2304
  }
2173
2305
  function loadSlashDispatchRuntime() {
2174
- slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-Bz_OkRcR.js");
2306
+ slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-Cg7uU92H.js");
2175
2307
  return slashDispatchRuntimePromise;
2176
2308
  }
2177
2309
  function loadSlackPluginCommandsRuntime() {
@@ -1,7 +1,7 @@
1
1
  import { t as resolveSlackReplyBlocks } from "./reply-blocks-BFaJ_ejG.js";
2
2
  import { o as SLACK_TEXT_LIMIT } from "./thread-ts-As_dcNbD.js";
3
- import { o as markdownToSlackMrkdwnChunks, t as sendMessageSlack } from "./send-Dg9zcyYT.js";
4
- import "./send.runtime-CjjQ9StM.js";
3
+ import { o as markdownToSlackMrkdwnChunks, t as sendMessageSlack } from "./send-CxXFbqN1.js";
4
+ import "./send.runtime-BHCPpSj_.js";
5
5
  import { createReplyReferencePlanner } from "openclaw/plugin-sdk/reply-reference";
6
6
  import { SILENT_REPLY_TOKEN, chunkMarkdownTextWithMode, isSilentReplyText } from "openclaw/plugin-sdk/reply-chunking";
7
7
  import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";