@openclaw/slack 2026.5.16-beta.2 → 2026.5.16-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/{action-runtime-p39JLqwf.js → action-runtime-0RLkDKyA.js} +1 -1
  2. package/dist/action-runtime.runtime-DpzOXqtk.js +2 -0
  3. package/dist/{actions-BCRbHv1Q.js → actions-Chs6DbrP.js} +1 -1
  4. package/dist/{actions.runtime-CpywQR3D.js → actions.runtime-BsbIDsmT.js} +1 -1
  5. package/dist/api.js +5 -5
  6. package/dist/{approval-handler.runtime-DXrdRbkT.js → approval-handler.runtime-BmCbRrbT.js} +1 -1
  7. package/dist/{channel-CVSopl66.js → channel-DUS8ZO45.js} +16 -12
  8. package/dist/channel-plugin-api.js +1 -1
  9. package/dist/{channel.setup-DknBgufI.js → channel.setup-qGOC05UK.js} +2 -2
  10. package/dist/inbound-contract-test-api.js +2 -2
  11. package/dist/{monitor-CdVxsuHi.js → monitor-BhEzWuv0.js} +3 -3
  12. package/dist/{outbound-adapter-CHm6e-0Q.js → outbound-adapter-BoDXPksS.js} +1 -1
  13. package/dist/outbound-payload-test-api.js +1 -1
  14. package/dist/{outbound-payload.test-harness-C0CW7_CE.js → outbound-payload.test-harness-C7Izm95Q.js} +1 -1
  15. package/dist/{pipeline.runtime-CakcaQh9.js → pipeline.runtime-Bq754VH8.js} +41 -26
  16. package/dist/{prepare-DSRUr44d.js → prepare-BcznR9ok.js} +136 -24
  17. package/dist/{prepare.test-helpers-CU1qB54Q.js → prepare.test-helpers-D807wdul.js} +1 -1
  18. package/dist/{provider-bKg1hkf5.js → provider-BFnE2bgI.js} +118 -4
  19. package/dist/{replies-Fg1T3ZzU.js → replies-2ve_YcHy.js} +8 -5
  20. package/dist/{room-context-Cd8jFpS-.js → room-context-BI26wVBb.js} +83 -2
  21. package/dist/runtime-api.js +5 -5
  22. package/dist/{send-CxXFbqN1.js → send-C5PzphgC.js} +5 -0
  23. package/dist/send.runtime-Bgf0P22e.js +2 -0
  24. package/dist/send.runtime-DsEXD6MR.js +2 -0
  25. package/dist/{setup-core-B7pou7oe.js → setup-core-az0LCrNr.js} +21 -2
  26. package/dist/setup-plugin-api.js +1 -1
  27. package/dist/{setup-surface-D6LLzeRz.js → setup-surface-Cdq_mfjx.js} +2 -2
  28. package/dist/{shared-7Vi9j4aV.js → shared-BuNoOmas.js} +6 -2
  29. package/dist/{slash-dispatch.runtime-Cg7uU92H.js → slash-dispatch.runtime-CcbE1HtP.js} +1 -1
  30. package/dist/test-api.js +6 -6
  31. package/package.json +4 -4
  32. package/dist/action-runtime.runtime-BzrPV3EA.js +0 -2
  33. package/dist/send.runtime-BHCPpSj_.js +0 -2
  34. package/dist/send.runtime-CDG5AgU3.js +0 -2
@@ -2,11 +2,11 @@ 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-CxXFbqN1.js";
6
- import { _ as resolveSlackThreadStarter, g as resolveSlackThreadHistory, l as reactSlackMessage, y as formatSlackFileReference } from "./actions-BCRbHv1Q.js";
5
+ import { i as hasSlackThreadParticipationWithPersistence, t as sendMessageSlack } from "./send-C5PzphgC.js";
6
+ import { _ as resolveSlackThreadStarter, g as resolveSlackThreadHistory, l as reactSlackMessage, y as formatSlackFileReference } from "./actions-Chs6DbrP.js";
7
7
  import { t as formatSlackError } from "./errors-C_sW0Zgl.js";
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-BHCPpSj_.js";
8
+ import { C as resolveChannelContextVisibilityMode, E as resolveStorePath, S as readSessionUpdatedAt, _ as resolveSlackChatType, c as authorizeSlackBotRoomMessage, d as resolveSlackEffectiveAllowFrom, f as buildSlackAssistantThreadMetadata, g as normalizeSlackChannelType, j as stripSlackMentionsForCommandDetection, m as parseSlackAssistantThreadMetadata, n as authorizeSlackDirectMessage, o as resolveConversationLabel$1, t as resolveSlackRoomContextHints, u as resolveSlackCommandIngress, v as resolveSlackChannelConfig } from "./room-context-BI26wVBb.js";
9
+ import "./send.runtime-DsEXD6MR.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";
@@ -15,10 +15,11 @@ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
15
15
  import { ensureConfiguredBindingRouteReady, resolveConfiguredBindingRoute, resolveRuntimeConversationBindingRoute } from "openclaw/plugin-sdk/conversation-runtime";
16
16
  import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
17
17
  import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
18
- import { buildChannelTurnContext, buildMentionRegexes, formatInboundEnvelope, implicitMentionKindWhen, logInboundDrop, matchesMentionWithExplicit, resolveEnvelopeFormatOptions, toInboundMediaFacts } from "openclaw/plugin-sdk/channel-inbound";
18
+ import { buildChannelInboundEventContext, buildMentionRegexes, classifyChannelInboundEvent, formatInboundEnvelope, implicitMentionKindWhen, logInboundDrop, matchesMentionWithExplicit, resolveEnvelopeFormatOptions, resolveUnmentionedGroupInboundPolicy, 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
+ import { isAbortRequestText } from "openclaw/plugin-sdk/command-primitives-runtime";
22
23
  import { shouldHandleTextCommands } from "openclaw/plugin-sdk/command-surface";
23
24
  import { recordDroppedChannelTurnHistory } from "openclaw/plugin-sdk/inbound-reply-dispatch";
24
25
  import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
@@ -29,7 +30,7 @@ const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
29
30
  const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
30
31
  let slackMediaModulePromise$1;
31
32
  function loadSlackMediaModule$1() {
32
- slackMediaModulePromise$1 ??= import("./actions-BCRbHv1Q.js").then((n) => n.h);
33
+ slackMediaModulePromise$1 ??= import("./actions-Chs6DbrP.js").then((n) => n.h);
33
34
  return slackMediaModulePromise$1;
34
35
  }
35
36
  function collectUniqueSlackMentionIds$1(texts) {
@@ -442,7 +443,7 @@ function resolveSlackInitialAgentRoute(params) {
442
443
  });
443
444
  }
444
445
  function resolveSlackRoutingContext(params) {
445
- const { ctx, account, message, isDirectMessage, isGroupDm, isRoom, isRoomish, seedTopLevelRoomThread } = params;
446
+ const { ctx, account, message, isDirectMessage, isGroupDm, isRoom, isRoomish, seedTopLevelRoomThread, assistantThreadTs } = params;
446
447
  let route = resolveSlackInitialAgentRoute({
447
448
  ctx,
448
449
  account,
@@ -460,17 +461,18 @@ function resolveSlackRoutingContext(params) {
460
461
  const isThreadReply = threadContext.isThreadReply;
461
462
  const autoThreadId = !isThreadReply && replyToMode === "all" && threadContext.messageTs ? threadContext.messageTs : void 0;
462
463
  const seedCandidateThreadId = threadContext.incomingThreadTs ?? threadContext.messageTs;
463
- const routedThreadId = (isDirectMessage ? isThreadReply ? threadTs : void 0 : isRoomish ? isThreadReply && threadTs ? threadTs : void 0 : isThreadReply ? threadTs : autoThreadId) ?? (isRoomish ? !isThreadReply && isRoom && seedTopLevelRoomThread && replyToMode !== "off" && seedCandidateThreadId ? seedCandidateThreadId : void 0 : void 0);
464
+ const routedThreadId = (isDirectMessage ? assistantThreadTs : isRoomish ? isThreadReply && threadTs ? threadTs : void 0 : isThreadReply ? threadTs : autoThreadId) ?? (isRoomish ? !isThreadReply && isRoom && seedTopLevelRoomThread && replyToMode !== "off" && seedCandidateThreadId ? seedCandidateThreadId : void 0 : void 0);
464
465
  const baseConversationId = resolveSlackBaseConversationId({
465
466
  message,
466
467
  isDirectMessage
467
468
  });
468
- const boundThreadRoute = routedThreadId ? resolveRuntimeConversationBindingRoute({
469
+ const runtimeBindingThreadId = routedThreadId ?? (isDirectMessage && isThreadReply ? threadTs : void 0);
470
+ const boundThreadRoute = runtimeBindingThreadId ? resolveRuntimeConversationBindingRoute({
469
471
  route,
470
472
  conversation: {
471
473
  channel: "slack",
472
474
  accountId: account.accountId,
473
- conversationId: routedThreadId,
475
+ conversationId: runtimeBindingThreadId,
474
476
  parentConversationId: baseConversationId
475
477
  }
476
478
  }) : null;
@@ -576,7 +578,7 @@ function formatSlackBotStarterThreadLabel(params) {
576
578
  //#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
577
579
  let slackMediaModulePromise;
578
580
  function loadSlackMediaModule() {
579
- slackMediaModulePromise ??= import("./actions-BCRbHv1Q.js").then((n) => n.h);
581
+ slackMediaModulePromise ??= import("./actions-Chs6DbrP.js").then((n) => n.h);
580
582
  return slackMediaModulePromise;
581
583
  }
582
584
  const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
@@ -686,7 +688,7 @@ async function resolveSlackThreadContextData(params) {
686
688
  logVerbose("slack: retained current-bot thread starter as assistant root context");
687
689
  }
688
690
  const threadInitialHistoryLimit = params.account.config?.thread?.initialHistoryLimit ?? 20;
689
- if (threadInitialHistoryLimit > 0 && !threadSessionPreviousTimestamp) {
691
+ if (threadInitialHistoryLimit > 0 && (!threadSessionPreviousTimestamp || params.forceInitialHistory)) {
690
692
  const currentBotRootTs = starter?.ts ?? params.threadTs;
691
693
  const threadHistoryWithBotRoot = ensureSlackThreadHistoryHasBotRoot({
692
694
  history: await resolveSlackThreadHistory({
@@ -848,6 +850,76 @@ const SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS = 4;
848
850
  const SLACK_HISTORY_MEDIA_MAX_BYTES = 10 * 1024 * 1024;
849
851
  const SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS = 1e3;
850
852
  const SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS = 3e3;
853
+ function asRecord(value) {
854
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
855
+ }
856
+ function recordString(record, key) {
857
+ return normalizeOptionalString(record?.[key]);
858
+ }
859
+ function recordNullableString(record, key) {
860
+ if (!record || !(key in record)) return;
861
+ if (record[key] === null) return null;
862
+ return normalizeOptionalString(record[key]);
863
+ }
864
+ function mergeSlackAssistantThreadContext(primary, fallback) {
865
+ if (!primary) return fallback;
866
+ if (!fallback) return primary;
867
+ return {
868
+ assistantChannelId: primary.assistantChannelId || fallback.assistantChannelId,
869
+ threadTs: primary.threadTs || fallback.threadTs,
870
+ userId: primary.userId ?? fallback.userId,
871
+ channelId: primary.channelId ?? fallback.channelId,
872
+ teamId: primary.teamId ?? fallback.teamId,
873
+ enterpriseId: primary.enterpriseId !== void 0 ? primary.enterpriseId : fallback.enterpriseId
874
+ };
875
+ }
876
+ function hasSlackAssistantThreadMetadata(context) {
877
+ return Boolean(context?.channelId || context?.teamId || context?.enterpriseId !== void 0);
878
+ }
879
+ function resolveSlackMessageAssistantThreadContext(message) {
880
+ const thread = asRecord(message.assistant_thread);
881
+ if (!thread) return;
882
+ const context = asRecord(thread.context);
883
+ const assistantChannelId = recordString(thread, "channel_id") ?? message.channel;
884
+ const threadTs = recordString(thread, "thread_ts") ?? message.thread_ts ?? message.ts;
885
+ if (!assistantChannelId || !threadTs) return;
886
+ return {
887
+ assistantChannelId,
888
+ threadTs,
889
+ userId: recordString(thread, "user_id") ?? message.user,
890
+ channelId: recordString(context, "channel_id"),
891
+ teamId: recordString(context, "team_id"),
892
+ enterpriseId: recordNullableString(context, "enterprise_id")
893
+ };
894
+ }
895
+ async function restoreSlackAssistantThreadContextFromMetadata(params) {
896
+ const threadTs = params.message.thread_ts;
897
+ const parentUserId = params.message.parent_user_id?.trim();
898
+ if (!params.message.channel || !threadTs || !parentUserId || parentUserId !== params.ctx.botUserId && parentUserId !== params.ctx.botId) return;
899
+ try {
900
+ const response = await params.ctx.app.client.conversations.replies({
901
+ channel: params.message.channel,
902
+ ts: threadTs,
903
+ oldest: threadTs,
904
+ include_all_metadata: true,
905
+ limit: 4
906
+ });
907
+ for (const message of response.messages ?? []) {
908
+ const context = parseSlackAssistantThreadMetadata(message.metadata);
909
+ if (!context) continue;
910
+ return {
911
+ assistantChannelId: params.message.channel,
912
+ threadTs,
913
+ userId: params.message.user,
914
+ channelId: context.channelId,
915
+ teamId: context.teamId,
916
+ enterpriseId: context.enterpriseId
917
+ };
918
+ }
919
+ } catch (err) {
920
+ logVerbose(`slack assistant context restore failed channel=${params.message.channel} ts=${threadTs}: ${formatErrorMessage(err)}`);
921
+ }
922
+ }
851
923
  function resolveCachedMentionRegexes(ctx, agentId) {
852
924
  const key = normalizeOptionalString(agentId) ?? "__default__";
853
925
  let byAgent = mentionRegexCache.get(ctx);
@@ -1092,7 +1164,19 @@ async function prepareSlackMessage(params) {
1092
1164
  source: opts.source
1093
1165
  });
1094
1166
  const channelRequireMention = channelConfig?.requireMention ?? ctx.defaultRequireMention ?? true;
1095
- const willImplicitlyThreadReply = isRoom && !channelRequireMention && resolveSlackReplyToMode(account, isDirectMessage ? "direct" : isGroupDm ? "group" : "channel") !== "off";
1167
+ const channelChatType = isDirectMessage ? "direct" : isGroupDm ? "group" : "channel";
1168
+ const messageAssistantThreadContext = resolveSlackMessageAssistantThreadContext(message);
1169
+ const assistantContextLookupChannelId = messageAssistantThreadContext?.assistantChannelId ?? message.channel;
1170
+ const assistantContextLookupThreadTs = messageAssistantThreadContext?.threadTs ?? message.thread_ts ?? message.ts;
1171
+ const cachedAssistantThreadContext = isDirectMessage ? ctx.getSlackAssistantThreadContext(assistantContextLookupChannelId, assistantContextLookupThreadTs) : void 0;
1172
+ const restoredAssistantThreadContext = isDirectMessage && !cachedAssistantThreadContext && !hasSlackAssistantThreadMetadata(messageAssistantThreadContext) ? await restoreSlackAssistantThreadContextFromMetadata({
1173
+ ctx,
1174
+ message
1175
+ }) : void 0;
1176
+ const assistantThreadContext = mergeSlackAssistantThreadContext(messageAssistantThreadContext, cachedAssistantThreadContext ?? restoredAssistantThreadContext);
1177
+ const assistantThreadContextToCache = messageAssistantThreadContext || restoredAssistantThreadContext ? assistantThreadContext : void 0;
1178
+ if (assistantThreadContextToCache) ctx.saveSlackAssistantThreadContext(assistantThreadContextToCache);
1179
+ const willImplicitlyThreadReply = isRoom && !channelRequireMention && resolveSlackReplyToMode(account, channelChatType) !== "off";
1096
1180
  const seedTopLevelRoomThreadBySource = opts.source === "app_mention" || opts.wasMentioned === true || explicitlyMentioned || willImplicitlyThreadReply;
1097
1181
  let routing = resolveSlackRoutingContext({
1098
1182
  ctx,
@@ -1102,7 +1186,8 @@ async function prepareSlackMessage(params) {
1102
1186
  isGroupDm,
1103
1187
  isRoom,
1104
1188
  isRoomish,
1105
- seedTopLevelRoomThread: seedTopLevelRoomThreadBySource
1189
+ seedTopLevelRoomThread: seedTopLevelRoomThreadBySource,
1190
+ assistantThreadTs: assistantThreadContext?.threadTs
1106
1191
  });
1107
1192
  const resolveWasMentioned = (mentionRegexes) => opts.wasMentioned ?? (!isDirectMessage && matchesMentionWithExplicit({
1108
1193
  text: messageText,
@@ -1125,7 +1210,8 @@ async function prepareSlackMessage(params) {
1125
1210
  isGroupDm,
1126
1211
  isRoom,
1127
1212
  isRoomish,
1128
- seedTopLevelRoomThread: true
1213
+ seedTopLevelRoomThread: true,
1214
+ assistantThreadTs: assistantThreadContext?.threadTs
1129
1215
  });
1130
1216
  mentionRegexes = resolveCachedMentionRegexes(ctx, routing.route.agentId);
1131
1217
  wasMentioned = resolveWasMentioned(mentionRegexes);
@@ -1150,6 +1236,7 @@ async function prepareSlackMessage(params) {
1150
1236
  return null;
1151
1237
  }
1152
1238
  }
1239
+ const directThreadRoutedToDmSession = !assistantThreadContext && isDirectMessage && isThreadReply && threadTs && runtimeBinding?.conversation.conversationId !== threadTs;
1153
1240
  let implicitMentionKinds = [];
1154
1241
  if (!isDirectMessage && ctx.botUserId && message.thread_ts && !ctx.threadRequireExplicitMention && !wasMentioned) {
1155
1242
  const replyToBotKinds = implicitMentionKindWhen("reply_to_bot", message.parent_user_id === ctx.botUserId);
@@ -1189,6 +1276,7 @@ async function prepareSlackMessage(params) {
1189
1276
  const canDetectMention = Boolean(ctx.botUserId) || mentionRegexes.length > 0;
1190
1277
  const textForCommandDetection = stripSlackMentionsForCommandDetection(message.text ?? "");
1191
1278
  const hasControlCommandInMessage = hasControlCommand(textForCommandDetection, cfg);
1279
+ const hasAbortRequest = isAbortRequestText(textForCommandDetection);
1192
1280
  const channelUsersAllowlistConfigured = isRoom && Array.isArray(channelConfig?.users) && channelConfig.users.length > 0;
1193
1281
  const messageIngress = await resolveSlackCommandIngress({
1194
1282
  ctx,
@@ -1330,6 +1418,16 @@ async function prepareSlackMessage(params) {
1330
1418
  if (!resolvedMessageContent) return null;
1331
1419
  const { rawBody, effectiveDirectMedia } = resolvedMessageContent;
1332
1420
  const chatType = resolveSlackChatType(conversation.resolvedChannelType);
1421
+ const inboundEventKind = classifyChannelInboundEvent({
1422
+ conversation: { kind: chatType },
1423
+ unmentionedGroupPolicy: resolveUnmentionedGroupInboundPolicy({
1424
+ cfg,
1425
+ agentId: route.agentId
1426
+ }),
1427
+ wasMentioned: effectiveWasMentioned,
1428
+ hasControlCommand: hasControlCommandInMessage,
1429
+ hasAbortRequest
1430
+ });
1333
1431
  const ackReaction = resolveAckReaction(cfg, route.agentId, {
1334
1432
  channel: "slack",
1335
1433
  accountId: account.accountId
@@ -1337,7 +1435,10 @@ async function prepareSlackMessage(params) {
1337
1435
  const ackReactionValue = ackReaction ?? "";
1338
1436
  const sourceRepliesAreToolOnly = resolveChannelMessageSourceReplyDeliveryMode({
1339
1437
  cfg,
1340
- ctx: { ChatType: chatType }
1438
+ ctx: {
1439
+ ChatType: chatType,
1440
+ InboundEventKind: inboundEventKind
1441
+ }
1341
1442
  }) === "message_tool_only";
1342
1443
  const statusReactionsExplicitlyEnabled = cfg.messages?.statusReactions?.enabled === true;
1343
1444
  const shouldAckReaction$1 = () => Boolean(ackReaction && shouldAckReaction({
@@ -1446,6 +1547,7 @@ async function prepareSlackMessage(params) {
1446
1547
  roomLabel,
1447
1548
  storePath,
1448
1549
  sessionKey,
1550
+ forceInitialHistory: Boolean(directThreadRoutedToDmSession),
1449
1551
  allowFromLower: threadContextAllowFromLower,
1450
1552
  allowNameMatching: ctx.allowNameMatching,
1451
1553
  contextVisibilityMode,
@@ -1458,7 +1560,9 @@ async function prepareSlackMessage(params) {
1458
1560
  limit: ctx.historyLimit
1459
1561
  }) : dmHistoryContext.inboundHistory;
1460
1562
  const commandBody = textForCommandDetection.trim();
1461
- const ctxPayload = buildChannelTurnContext({
1563
+ const supplementalThreadHistoryBody = directThreadRoutedToDmSession && !threadHistoryBody ? threadStarterBody : threadHistoryBody;
1564
+ const effectiveMessageThreadId = assistantThreadContext?.threadTs ?? threadContext.messageThreadId;
1565
+ const ctxPayload = buildChannelInboundEventContext({
1462
1566
  channel: "slack",
1463
1567
  provider: "slack",
1464
1568
  surface: "slack",
@@ -1476,7 +1580,7 @@ async function prepareSlackMessage(params) {
1476
1580
  id: message.channel,
1477
1581
  label: envelopeFrom,
1478
1582
  spaceId: ctx.teamId || void 0,
1479
- threadId: threadContext.messageThreadId,
1583
+ threadId: directThreadRoutedToDmSession ? void 0 : effectiveMessageThreadId,
1480
1584
  nativeChannelId: message.channel,
1481
1585
  routePeer: {
1482
1586
  kind: chatType,
@@ -1493,10 +1597,11 @@ async function prepareSlackMessage(params) {
1493
1597
  to: slackTo,
1494
1598
  originatingTo: slackTo,
1495
1599
  replyToId: threadContext.replyToId,
1496
- messageThreadId: threadContext.messageThreadId,
1600
+ messageThreadId: directThreadRoutedToDmSession ? void 0 : effectiveMessageThreadId,
1497
1601
  nativeChannelId: message.channel
1498
1602
  },
1499
1603
  message: {
1604
+ inboundEventKind,
1500
1605
  body: combinedBody,
1501
1606
  bodyForAgent: rawBody,
1502
1607
  rawBody,
@@ -1523,16 +1628,21 @@ async function prepareSlackMessage(params) {
1523
1628
  media: toInboundMediaFacts(effectiveMedia),
1524
1629
  supplemental: {
1525
1630
  thread: {
1526
- starterBody: !threadSessionPreviousTimestamp ? threadStarterBody : void 0,
1527
- historyBody: threadHistoryBody,
1528
- label: threadLabel
1631
+ starterBody: !directThreadRoutedToDmSession && !threadSessionPreviousTimestamp ? threadStarterBody : void 0,
1632
+ historyBody: supplementalThreadHistoryBody,
1633
+ label: directThreadRoutedToDmSession ? void 0 : threadLabel
1529
1634
  },
1530
1635
  groupSystemPrompt
1531
1636
  },
1532
1637
  extra: {
1533
1638
  GroupSubject: isRoomish ? roomLabel : void 0,
1534
1639
  UntrustedContext: untrustedChannelMetadata ? [untrustedChannelMetadata] : void 0,
1535
- IsFirstThreadTurn: isThreadReply && threadTs && !threadSessionPreviousTimestamp ? true : void 0,
1640
+ TransportThreadId: directThreadRoutedToDmSession ? threadContext.messageThreadId : void 0,
1641
+ SlackAssistantThread: assistantThreadContext ? true : void 0,
1642
+ SlackAssistantThreadContextChannelId: assistantThreadContext?.channelId,
1643
+ SlackAssistantThreadContextTeamId: assistantThreadContext?.teamId,
1644
+ SlackAssistantThreadContextEnterpriseId: assistantThreadContext?.enterpriseId ?? void 0,
1645
+ IsFirstThreadTurn: isThreadReply && threadTs && !directThreadRoutedToDmSession && !threadSessionPreviousTimestamp ? true : void 0,
1536
1646
  ...buildSlackMentionContextPayload({
1537
1647
  isRoomish,
1538
1648
  effectiveWasMentioned,
@@ -1581,7 +1691,7 @@ async function prepareSlackMessage(params) {
1581
1691
  channel: "slack",
1582
1692
  to: `user:${message.user}`,
1583
1693
  accountId: route.accountId,
1584
- threadId: threadContext.messageThreadId,
1694
+ threadId: effectiveMessageThreadId,
1585
1695
  mainDmOwnerPin: pinnedMainDmOwner && message.user ? {
1586
1696
  ownerRecipient: pinnedMainDmOwner,
1587
1697
  senderRecipient: normalizeLowercaseStringOrEmpty(message.user),
@@ -1606,6 +1716,8 @@ async function prepareSlackMessage(params) {
1606
1716
  } : void 0
1607
1717
  },
1608
1718
  replyToMode,
1719
+ ...assistantThreadContext?.threadTs ? { forcedReplyThreadTs: assistantThreadContext.threadTs } : {},
1720
+ ...assistantThreadContext ? { slackMessageMetadata: buildSlackAssistantThreadMetadata(assistantThreadContext) } : {},
1609
1721
  requireMention: shouldRequireMention,
1610
1722
  isDirectMessage,
1611
1723
  isRoomish,
@@ -1,4 +1,4 @@
1
- import { f as createSlackMonitorContext } from "./room-context-Cd8jFpS-.js";
1
+ import { p as createSlackMonitorContext } from "./room-context-BI26wVBb.js";
2
2
  import "openclaw/plugin-sdk/temp-path";
3
3
  //#region extensions/slack/src/monitor/message-handler/prepare.test-helpers.ts
4
4
  function createInboundSlackTestContext(params) {
@@ -8,7 +8,7 @@ import { n as registerSlackHttpHandler, r as normalizeSlackWebhookPath } from ".
8
8
  import { t as formatSlackError } from "./errors-C_sW0Zgl.js";
9
9
  import { t as resolveSlackChannelAllowlist } from "./resolve-channels-B_eKaOkE.js";
10
10
  import { t as resolveSlackUserAllowlist } from "./resolve-users-BzBAJwvq.js";
11
- import { C as resolveOpenProviderRuntimeGroupPolicy, D as buildSlackSlashCommandMatcher, E as warnMissingProviderGroupPolicyFallbackOnce, O as resolveSlackSlashCommandConfig, S as resolveDefaultGroupPolicy, _ as resolveSlackChannelLabel, d as resolveSlackEffectiveAllowFrom, f as createSlackMonitorContext, g as resolveSlackChannelConfig, h as resolveSlackChatType, i as parsePluginBindingApprovalCustomId, k as stripSlackMentionsForCommandDetection, l as authorizeSlackSystemEventSender, m as normalizeSlackChannelType, n as authorizeSlackDirectMessage, p as isSlackChannelAllowedByPolicy, r as buildPluginBindingResolvedText, s as resolvePluginConversationBindingApproval, t as resolveSlackRoomContextHints, u as resolveSlackCommandIngress, v as getRuntimeConfig$1, y as isDangerousNameMatchingEnabled } from "./room-context-Cd8jFpS-.js";
11
+ import { A as resolveSlackSlashCommandConfig, O as warnMissingProviderGroupPolicyFallbackOnce, T as resolveOpenProviderRuntimeGroupPolicy, _ as resolveSlackChatType, b as getRuntimeConfig$1, d as resolveSlackEffectiveAllowFrom, f as buildSlackAssistantThreadMetadata, g as normalizeSlackChannelType, h as isSlackChannelAllowedByPolicy, i as parsePluginBindingApprovalCustomId, j as stripSlackMentionsForCommandDetection, k as buildSlackSlashCommandMatcher, l as authorizeSlackSystemEventSender, n as authorizeSlackDirectMessage, p as createSlackMonitorContext, r as buildPluginBindingResolvedText, s as resolvePluginConversationBindingApproval, t as resolveSlackRoomContextHints, u as resolveSlackCommandIngress, v as resolveSlackChannelConfig, w as resolveDefaultGroupPolicy, x as isDangerousNameMatchingEnabled, y as resolveSlackChannelLabel } from "./room-context-BI26wVBb.js";
12
12
  import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
13
13
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
14
14
  import { normalizeAccountId, normalizeMainKey } from "openclaw/plugin-sdk/routing";
@@ -37,6 +37,112 @@ import { requestHeartbeat } from "openclaw/plugin-sdk/heartbeat-runtime";
37
37
  import { createInteractiveConversationBindingHelpers, dispatchPluginInteractiveHandler } from "openclaw/plugin-sdk/plugin-runtime";
38
38
  import { createChannelInboundDebouncer, shouldDebounceTextInbound } from "openclaw/plugin-sdk/channel-inbound";
39
39
  import { generateSecureToken } from "openclaw/plugin-sdk/secure-random-runtime";
40
+ //#region extensions/slack/src/monitor/events/assistant.ts
41
+ const DEFAULT_ASSISTANT_PROMPTS = [
42
+ {
43
+ title: "What can you do?",
44
+ message: "What can you help me with?"
45
+ },
46
+ {
47
+ title: "Summarize this channel",
48
+ message: "Summarize the recent activity in this channel."
49
+ },
50
+ {
51
+ title: "Draft a reply",
52
+ message: "Help me draft a reply."
53
+ }
54
+ ];
55
+ function normalizeAssistantThread(event, getPrevious) {
56
+ const thread = event.assistant_thread;
57
+ if (!thread) return null;
58
+ const channelId = thread.channel_id?.trim();
59
+ const threadTs = thread.thread_ts?.trim();
60
+ if (!channelId || !threadTs) return null;
61
+ const previous = getPrevious?.(channelId, threadTs);
62
+ const threadContext = thread.context;
63
+ const eventContext = event.context;
64
+ const resolveContextString = (key, previousValue) => threadContext?.[key]?.trim() || eventContext?.[key]?.trim() || previousValue;
65
+ const enterpriseId = (() => {
66
+ if (threadContext && "enterprise_id" in threadContext) return threadContext.enterprise_id === null ? null : threadContext.enterprise_id?.trim() || previous?.enterpriseId;
67
+ if (eventContext && "enterprise_id" in eventContext) return eventContext.enterprise_id === null ? null : eventContext.enterprise_id?.trim() || previous?.enterpriseId;
68
+ return previous?.enterpriseId;
69
+ })();
70
+ return {
71
+ assistantChannelId: channelId,
72
+ threadTs,
73
+ userId: thread.user_id?.trim() || previous?.userId,
74
+ channelId: resolveContextString("channel_id", previous?.channelId),
75
+ teamId: resolveContextString("team_id", previous?.teamId),
76
+ enterpriseId
77
+ };
78
+ }
79
+ async function persistAssistantThreadMetadata(params) {
80
+ const { ctx, assistantThread } = params;
81
+ try {
82
+ const initialMessage = ((await ctx.app.client.conversations.replies({
83
+ token: ctx.botToken,
84
+ channel: assistantThread.assistantChannelId,
85
+ ts: assistantThread.threadTs,
86
+ oldest: assistantThread.threadTs,
87
+ include_all_metadata: true,
88
+ limit: 4
89
+ })).messages ?? []).find((message) => !message.subtype && message.user === ctx.botUserId && message.ts);
90
+ if (!initialMessage?.ts) return;
91
+ await ctx.app.client.chat.update({
92
+ token: ctx.botToken,
93
+ channel: assistantThread.assistantChannelId,
94
+ ts: initialMessage.ts,
95
+ text: initialMessage.text ?? "",
96
+ blocks: Array.isArray(initialMessage.blocks) ? initialMessage.blocks : [],
97
+ metadata: buildSlackAssistantThreadMetadata(assistantThread)
98
+ });
99
+ } catch (err) {
100
+ logVerbose(`slack assistant thread metadata persist failed for channel ${assistantThread.assistantChannelId}: ${formatErrorMessage(err)}`);
101
+ }
102
+ }
103
+ function registerSlackAssistantEvents(params) {
104
+ const { ctx, trackEvent } = params;
105
+ const slackApp = ctx.app;
106
+ slackApp.event("assistant_thread_started", async ({ event, body }) => {
107
+ try {
108
+ if (ctx.shouldDropMismatchedSlackEvent(body)) return;
109
+ trackEvent?.();
110
+ const assistantThread = normalizeAssistantThread(event, ctx.getSlackAssistantThreadContext);
111
+ if (!assistantThread) {
112
+ logVerbose("slack assistant_thread_started dropped: missing assistant thread channel/thread");
113
+ return;
114
+ }
115
+ ctx.saveSlackAssistantThreadContext(assistantThread);
116
+ await ctx.setSlackAssistantSuggestedPrompts({
117
+ channelId: assistantThread.assistantChannelId,
118
+ threadTs: assistantThread.threadTs,
119
+ title: "Try asking",
120
+ prompts: DEFAULT_ASSISTANT_PROMPTS
121
+ });
122
+ } catch (err) {
123
+ ctx.runtime.error?.(danger(`slack assistant_thread_started handler failed: ${formatErrorMessage(err)}`));
124
+ }
125
+ });
126
+ slackApp.event("assistant_thread_context_changed", async ({ event, body }) => {
127
+ try {
128
+ if (ctx.shouldDropMismatchedSlackEvent(body)) return;
129
+ trackEvent?.();
130
+ const assistantThread = normalizeAssistantThread(event, ctx.getSlackAssistantThreadContext);
131
+ if (!assistantThread) {
132
+ logVerbose("slack assistant_thread_context_changed dropped: missing assistant thread channel/thread");
133
+ return;
134
+ }
135
+ ctx.saveSlackAssistantThreadContext(assistantThread);
136
+ await persistAssistantThreadMetadata({
137
+ ctx,
138
+ assistantThread
139
+ });
140
+ } catch (err) {
141
+ ctx.runtime.error?.(danger(`slack assistant_thread_context_changed handler failed: ${formatErrorMessage(err)}`));
142
+ }
143
+ });
144
+ }
145
+ //#endregion
40
146
  //#region extensions/slack/src/channel-migration.ts
41
147
  function resolveAccountChannels(cfg, accountId) {
42
148
  if (!accountId) return {};
@@ -1418,7 +1524,10 @@ function resolveAssistantMessageChangedInbound(params) {
1418
1524
  message,
1419
1525
  botUserId: params.ctx.botUserId
1420
1526
  });
1421
- if (!senderId) return;
1527
+ if (!senderId) {
1528
+ if (shouldLogVerbose()) logVerbose(`slack: assistant_app_thread message_changed in DM channel=${changed.channel} dropped: no sender resolved from metadata`);
1529
+ return;
1530
+ }
1422
1531
  return {
1423
1532
  type: "message",
1424
1533
  channel: changed.channel ?? params.event.channel,
@@ -1428,6 +1537,7 @@ function resolveAssistantMessageChangedInbound(params) {
1428
1537
  ts: asString(message.ts) ?? asString(changed.event_ts),
1429
1538
  thread_ts: asString(message.thread_ts),
1430
1539
  event_ts: changed.event_ts,
1540
+ assistant_thread: asRecord$1(message.assistant_thread) ?? asRecord$1(changed.assistant_thread),
1431
1541
  files: Array.isArray(message.files) ? message.files : void 0,
1432
1542
  attachments: Array.isArray(message.attachments) ? message.attachments : void 0
1433
1543
  };
@@ -1642,6 +1752,10 @@ function registerSlackMonitorEvents(params) {
1642
1752
  ctx: params.ctx,
1643
1753
  trackEvent: params.trackEvent
1644
1754
  });
1755
+ registerSlackAssistantEvents({
1756
+ ctx: params.ctx,
1757
+ trackEvent: params.trackEvent
1758
+ });
1645
1759
  }
1646
1760
  //#endregion
1647
1761
  //#region extensions/slack/src/monitor/message-handler/debounce-key.ts
@@ -1761,7 +1875,7 @@ function createSlackThreadTsResolver(params) {
1761
1875
  //#region extensions/slack/src/monitor/message-handler.ts
1762
1876
  let slackMessagePipelinePromise;
1763
1877
  function loadSlackMessagePipeline() {
1764
- slackMessagePipelinePromise ??= import("./pipeline.runtime-CakcaQh9.js");
1878
+ slackMessagePipelinePromise ??= import("./pipeline.runtime-Bq754VH8.js");
1765
1879
  return slackMessagePipelinePromise;
1766
1880
  }
1767
1881
  const APP_MENTION_RETRY_TTL_MS = 6e4;
@@ -2303,7 +2417,7 @@ function loadSlashCommandsRuntime() {
2303
2417
  return slashCommandsRuntimePromise;
2304
2418
  }
2305
2419
  function loadSlashDispatchRuntime() {
2306
- slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-Cg7uU92H.js");
2420
+ slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-CcbE1HtP.js");
2307
2421
  return slashDispatchRuntimePromise;
2308
2422
  }
2309
2423
  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-CxXFbqN1.js";
4
- import "./send.runtime-BHCPpSj_.js";
3
+ import { o as markdownToSlackMrkdwnChunks, t as sendMessageSlack } from "./send-C5PzphgC.js";
4
+ import "./send.runtime-DsEXD6MR.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";
@@ -32,7 +32,8 @@ async function deliverReplies(params) {
32
32
  threadTs,
33
33
  accountId: params.accountId,
34
34
  ...slackBlocks?.length ? { blocks: slackBlocks } : {},
35
- ...params.identity ? { identity: params.identity } : {}
35
+ ...params.identity ? { identity: params.identity } : {},
36
+ ...params.metadata ? { metadata: params.metadata } : {}
36
37
  });
37
38
  params.runtime.log?.(`delivered reply to ${params.target}`);
38
39
  continue;
@@ -51,7 +52,8 @@ async function deliverReplies(params) {
51
52
  token: params.token,
52
53
  threadTs,
53
54
  accountId: params.accountId,
54
- ...params.identity ? { identity: params.identity } : {}
55
+ ...params.identity ? { identity: params.identity } : {},
56
+ ...params.metadata ? { metadata: params.metadata } : {}
55
57
  });
56
58
  },
57
59
  sendMedia: async ({ mediaUrl, caption }) => {
@@ -61,7 +63,8 @@ async function deliverReplies(params) {
61
63
  mediaUrl,
62
64
  threadTs,
63
65
  accountId: params.accountId,
64
- ...params.identity ? { identity: params.identity } : {}
66
+ ...params.identity ? { identity: params.identity } : {},
67
+ ...params.metadata ? { metadata: params.metadata } : {}
65
68
  });
66
69
  }
67
70
  }) !== "empty") params.runtime.log?.(`delivered reply to ${params.target}`);