@openclaw/slack 2026.6.6 → 2026.6.8-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.
- package/dist/api.js +3 -3
- package/dist/{channel-i43aT1rv.js → channel-CdZdGdU5.js} +3 -3
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-DrCTObMp.js → channel.setup-W5YnieWd.js} +1 -1
- package/dist/{monitor-Szm2LjsM.js → monitor-DwDmBYfl.js} +2 -2
- package/dist/{pipeline.runtime-Dvqs0AT-.js → pipeline.runtime-1FYOQOt_.js} +182 -22
- package/dist/{provider-LH_j-bLA.js → provider-ChhYGpXx.js} +10 -3
- package/dist/replies-Bt4nWmSd.js +285 -0
- package/dist/runtime-api.js +2 -2
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-BB_ivsoR.js → setup-surface-NNMjGpXc.js} +1 -1
- package/dist/{shared-Bviszgb6.js → shared-Bkkmro6q.js} +88 -26
- package/dist/{slash-dispatch.runtime-CoFAysiw.js → slash-dispatch.runtime-Dvvs747S.js} +1 -1
- package/npm-shrinkwrap.json +3 -3
- package/package.json +4 -4
- package/dist/replies-FEOdgSlE.js +0 -142
package/dist/api.js
CHANGED
|
@@ -3,16 +3,16 @@ import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, l as resol
|
|
|
3
3
|
import { t as inspectSlackAccount } from "./account-inspect-CdGk6R7l.js";
|
|
4
4
|
import { i as resolveSlackChannelId, n as normalizeSlackMessagingTarget, r as parseSlackTarget, t as looksLikeSlackTargetId } from "./target-parsing-C7eeWg7M.js";
|
|
5
5
|
import "./targets-nUqxHGgg.js";
|
|
6
|
-
import { a as resolveSlackAutoThreadId, i as resolveSlackChannelType, n as buildSlackThreadingToolContext, r as resetSlackChannelTypeCacheForTest, t as slackPlugin } from "./channel-
|
|
6
|
+
import { a as resolveSlackAutoThreadId, i as resolveSlackChannelType, n as buildSlackThreadingToolContext, r as resetSlackChannelTypeCacheForTest, t as slackPlugin } from "./channel-CdZdGdU5.js";
|
|
7
7
|
import { _ as buildSlackInteractiveBlocks, a as parseSlackBlocksInput, c as resolveSlackGroupRequireMention, d as normalizeAllowList, f as normalizeAllowListLower, g as resolveSlackUserAllowed, h as resolveSlackAllowListMatch, i as SLACK_MAX_BLOCKS, l as resolveSlackGroupToolPolicy, m as normalizeSlackSlug, o as validateSlackBlocksArray, p as normalizeSlackAllowOwnerEntry, u as allowListMatches, v as buildSlackPresentationBlocks } from "./thread-ts-Cffag8e2.js";
|
|
8
8
|
import { a as getSlackWriteClient, c as resolveSlackWebClientOptions, i as createSlackWriteClient, l as resolveSlackWriteClientOptions, n as createSlackTokenCacheKey, o as SLACK_DEFAULT_RETRY_OPTIONS, r as createSlackWebClient, s as SLACK_WRITE_RETRY_OPTIONS, t as clearSlackWriteClientCacheForTest } from "./client-Cn2WwpcA.js";
|
|
9
9
|
import { n as extractSlackToolSend, r as listSlackMessageActions } from "./message-tool-api-B9M0zzlQ.js";
|
|
10
10
|
import { n as isSlackInteractiveRepliesEnabled, r as parseSlackOptionsLine, t as compileSlackInteractiveReplies } from "./interactive-replies-DrBq4Mld.js";
|
|
11
|
-
import { t as slackSetupPlugin } from "./channel.setup-
|
|
11
|
+
import { t as slackSetupPlugin } from "./channel.setup-W5YnieWd.js";
|
|
12
12
|
import { a as recordSlackThreadParticipation, n as clearSlackThreadParticipationCache, r as hasSlackThreadParticipation } from "./send-rekB-Xjp.js";
|
|
13
13
|
import { a as listSlackEmojis, c as pinSlackMessage, d as removeOwnSlackReactions, f as removeSlackReaction, i as getSlackMemberInfo, l as reactSlackMessage, m as unpinSlackMessage, n as downloadSlackFile, o as listSlackPins, p as sendSlackMessage, r as editSlackMessage, s as listSlackReactions, t as deleteSlackMessage, u as readSlackMessages } from "./actions-YkhEE5f1.js";
|
|
14
14
|
import { n as listSlackDirectoryGroupsFromConfig, r as listSlackDirectoryPeersFromConfig } from "./directory-config-8UPAEyNg.js";
|
|
15
15
|
import { t as probeSlack } from "./probe-BTKzLT2u.js";
|
|
16
16
|
import { t as collectSlackSecurityAuditFindings } from "./security-audit-CikQhBUY.js";
|
|
17
|
-
import { n as resolveSlackRuntimeGroupPolicy } from "./provider-
|
|
17
|
+
import { n as resolveSlackRuntimeGroupPolicy } from "./provider-ChhYGpXx.js";
|
|
18
18
|
export { SLACK_DEFAULT_RETRY_OPTIONS, SLACK_MAX_BLOCKS, SLACK_WRITE_RETRY_OPTIONS, resetSlackChannelTypeCacheForTest as __resetSlackChannelTypeCacheForTest, resetSlackChannelTypeCacheForTest, allowListMatches, buildSlackInteractiveBlocks, buildSlackPresentationBlocks, buildSlackThreadingToolContext, clearSlackThreadParticipationCache, clearSlackWriteClientCacheForTest, collectSlackSecurityAuditFindings, compileSlackInteractiveReplies, createSlackTokenCacheKey, createSlackWebClient, createSlackWriteClient, deleteSlackMessage, downloadSlackFile, editSlackMessage, extractSlackToolSend, getSlackMemberInfo, getSlackWriteClient, handleSlackHttpRequest, hasSlackThreadParticipation, inspectSlackAccount, isSlackInteractiveRepliesEnabled, listEnabledSlackAccounts, listSlackAccountIds, listSlackDirectoryGroupsFromConfig, listSlackDirectoryPeersFromConfig, listSlackEmojis, listSlackMessageActions, listSlackPins, listSlackReactions, looksLikeSlackTargetId, mergeSlackAccountConfig, normalizeAllowList, normalizeAllowListLower, normalizeSlackAllowOwnerEntry, normalizeSlackMessagingTarget, normalizeSlackSlug, normalizeSlackWebhookPath, parseSlackBlocksInput, parseSlackOptionsLine, parseSlackTarget, pinSlackMessage, probeSlack, reactSlackMessage, readSlackMessages, recordSlackThreadParticipation, registerSlackHttpHandler, removeOwnSlackReactions, removeSlackReaction, resolveDefaultSlackAccountId, resolveSlackAccount, resolveSlackAllowListMatch, resolveSlackAutoThreadId, resolveSlackChannelId, resolveSlackChannelType, resolveSlackGroupRequireMention, resolveSlackGroupToolPolicy, resolveSlackReplyToMode, resolveSlackRuntimeGroupPolicy, resolveSlackUserAllowed, resolveSlackWebClientOptions, resolveSlackWriteClientOptions, sendSlackMessage, slackPlugin, slackSetupPlugin, unpinSlackMessage, validateSlackBlocksArray };
|
|
@@ -6,7 +6,7 @@ import { d as PAIRING_APPROVED_MESSAGE, h as isSlackPluginAccountConfigured, m a
|
|
|
6
6
|
import { n as extractSlackToolSend, t as describeSlackMessageTool } from "./message-tool-api-B9M0zzlQ.js";
|
|
7
7
|
import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-DrBq4Mld.js";
|
|
8
8
|
import { t as getOptionalSlackRuntime } from "./runtime-BOk7xkOl.js";
|
|
9
|
-
import { n as slackConfigAdapter, r as slackSecurityAdapter, t as createSlackPluginBase } from "./shared-
|
|
9
|
+
import { n as slackConfigAdapter, r as slackSecurityAdapter, t as createSlackPluginBase } from "./shared-Bkkmro6q.js";
|
|
10
10
|
import { i as SLACK_CHANNEL, n as createSlackSetupWizardProxy, r as slackSetupAdapter } from "./setup-core-POfI_bgP.js";
|
|
11
11
|
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
12
12
|
import { adaptScopedAccountAccessor } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
@@ -476,7 +476,7 @@ const EXTENSION_SHARED_MODULE_ID = "openclaw/plugin-sdk/extension-shared";
|
|
|
476
476
|
const TARGET_RESOLVER_RUNTIME_MODULE_ID = "openclaw/plugin-sdk/target-resolver-runtime";
|
|
477
477
|
const loadExtensionSharedSdk = createLazyRuntimeModule(() => import(EXTENSION_SHARED_MODULE_ID));
|
|
478
478
|
const loadTargetResolverRuntimeSdk = createLazyRuntimeModule(() => import(TARGET_RESOLVER_RUNTIME_MODULE_ID));
|
|
479
|
-
const loadSlackSetupSurfaceModule = createLazyRuntimeModule(() => import("./setup-surface-
|
|
479
|
+
const loadSlackSetupSurfaceModule = createLazyRuntimeModule(() => import("./setup-surface-NNMjGpXc.js"));
|
|
480
480
|
const loadSlackScopesModule = createLazyRuntimeModule(() => import("./scopes-CErQL0td.js"));
|
|
481
481
|
const loadSlackOutboundAdapterModule = createLazyRuntimeModule(() => import("./outbound-adapter-DvXnHfDP.js"));
|
|
482
482
|
async function resolveSlackHandleAction() {
|
|
@@ -514,7 +514,7 @@ async function loadSlackProbeModule() {
|
|
|
514
514
|
return await slackProbeModulePromise;
|
|
515
515
|
}
|
|
516
516
|
async function loadSlackMonitorModule() {
|
|
517
|
-
slackMonitorModulePromise ??= import("./monitor-
|
|
517
|
+
slackMonitorModulePromise ??= import("./monitor-DwDmBYfl.js").then((n) => n.t);
|
|
518
518
|
return await slackMonitorModulePromise;
|
|
519
519
|
}
|
|
520
520
|
async function loadSlackDirectoryLiveModule() {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as slackPlugin } from "./channel-
|
|
1
|
+
import { t as slackPlugin } from "./channel-CdZdGdU5.js";
|
|
2
2
|
export { slackPlugin };
|
|
@@ -4,7 +4,7 @@ import { t as SlackChannelConfigSchema } from "./config-schema-BjAjs8_6.js";
|
|
|
4
4
|
import { adaptScopedAccountAccessor, createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
|
|
5
5
|
import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
|
|
6
6
|
//#region extensions/slack/src/channel.setup.ts
|
|
7
|
-
const slackSetupWizard = createSlackSetupWizardProxy(async () => ({ slackSetupWizard: (await import("./setup-surface-
|
|
7
|
+
const slackSetupWizard = createSlackSetupWizardProxy(async () => ({ slackSetupWizard: (await import("./setup-surface-NNMjGpXc.js")).slackSetupWizard }));
|
|
8
8
|
const slackSetupConfigAdapter = createScopedChannelConfigAdapter({
|
|
9
9
|
sectionKey: SLACK_CHANNEL,
|
|
10
10
|
listAccountIds: listSlackAccountIds,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { t as monitorSlackProvider, y as buildSlackSlashCommandMatcher } from "./provider-
|
|
2
|
+
import { t as monitorSlackProvider, y as buildSlackSlashCommandMatcher } from "./provider-ChhYGpXx.js";
|
|
3
3
|
import { t as isSlackChannelAllowedByPolicy } from "./policy-DT3mRh58.js";
|
|
4
|
-
import { o as resolveSlackThreadTs } from "./replies-
|
|
4
|
+
import { o as resolveSlackThreadTs } from "./replies-Bt4nWmSd.js";
|
|
5
5
|
//#region extensions/slack/src/monitor.ts
|
|
6
6
|
var monitor_exports = /* @__PURE__ */ __exportAll({
|
|
7
7
|
buildSlackSlashCommandMatcher: () => buildSlackSlashCommandMatcher,
|
|
@@ -7,9 +7,9 @@ import { n as resolveSlackNativeStreaming, r as resolveSlackStreamingMode, t as
|
|
|
7
7
|
import { a as recordSlackThreadParticipation, i as hasSlackThreadParticipationWithPersistence, s as normalizeSlackOutboundText, t as sendMessageSlack } from "./send-rekB-Xjp.js";
|
|
8
8
|
import { _ as resolveSlackThreadStarter, b as buildSlackEditTextPayload, f as removeSlackReaction, g as resolveSlackThreadHistory, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage, y as formatSlackFileReference } from "./actions-YkhEE5f1.js";
|
|
9
9
|
import { t as formatSlackError } from "./errors-CZtmv-h0.js";
|
|
10
|
-
import { _ as resolveStorePath, a as escapeSlackMrkdwn, b as stripSlackMentionsForCommandDetection, c as authorizeSlackBotRoomMessage, d as buildSlackAssistantThreadMetadata, f as parseSlackAssistantThreadMetadata, g as resolveChannelContextVisibilityMode, h as readSessionUpdatedAt, i as authorizeSlackDirectMessage, l as resolveSlackCommandIngress, m as resolveSlackChatType, o as recordInboundSession, p as normalizeSlackChannelType, r as resolveSlackRoomContextHints, s as resolveConversationLabel$1, u as resolveSlackEffectiveAllowFrom, v as updateLastRoute } from "./provider-
|
|
10
|
+
import { _ as resolveStorePath, a as escapeSlackMrkdwn, b as stripSlackMentionsForCommandDetection, c as authorizeSlackBotRoomMessage, d as buildSlackAssistantThreadMetadata, f as parseSlackAssistantThreadMetadata, g as resolveChannelContextVisibilityMode, h as readSessionUpdatedAt, i as authorizeSlackDirectMessage, l as resolveSlackCommandIngress, m as resolveSlackChatType, o as recordInboundSession, p as normalizeSlackChannelType, r as resolveSlackRoomContextHints, s as resolveConversationLabel$1, u as resolveSlackEffectiveAllowFrom, v as updateLastRoute } from "./provider-ChhYGpXx.js";
|
|
11
11
|
import { n as resolveSlackChannelConfig } from "./policy-DT3mRh58.js";
|
|
12
|
-
import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, t as createSlackReplyDeliveryPlan } from "./replies-
|
|
12
|
+
import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, s as emitSlackMessageSentHooks, t as createSlackReplyDeliveryPlan } from "./replies-Bt4nWmSd.js";
|
|
13
13
|
import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
14
14
|
import { resolveAgentRoute, resolveInboundLastRouteSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
|
|
15
15
|
import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelMessageReplyPipeline, createChannelProgressDraftGate, createDraftStreamLoop, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelProgressDraftMaxLines, resolveChannelProgressDraftRender, resolveChannelStreamingBlockEnabled, resolveChannelStreamingNativeTransport, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
|
|
@@ -415,36 +415,41 @@ async function appendSlackStream(params) {
|
|
|
415
415
|
* text so the caller can deliver it through the normal Slack reply path.
|
|
416
416
|
*
|
|
417
417
|
* All other errors propagate unchanged.
|
|
418
|
+
*
|
|
419
|
+
* On success, returns the finalized message's Slack `ts` (when reported) so the
|
|
420
|
+
* caller can emit the `message_sent` hook with a populated `messageId`.
|
|
418
421
|
*/
|
|
419
422
|
async function stopSlackStream(params) {
|
|
420
423
|
const { session, text, chunks, metadata } = params;
|
|
421
424
|
if (session.stopped) {
|
|
422
425
|
logVerbose("slack-stream: stream already stopped, ignoring duplicate stop");
|
|
423
|
-
return;
|
|
426
|
+
return {};
|
|
424
427
|
}
|
|
425
428
|
session.stopped = true;
|
|
426
429
|
if (text) session.pendingText += text;
|
|
427
430
|
logVerbose(`slack-stream: stopping stream in ${session.channel} thread=${session.threadTs}${text ? ` (final text: ${text.length} chars)` : ""}`);
|
|
428
431
|
try {
|
|
429
|
-
await session.streamer.stop(text || chunks?.length || metadata ? {
|
|
432
|
+
const stopResponse = await session.streamer.stop(text || chunks?.length || metadata ? {
|
|
430
433
|
...text ? { markdown_text: text } : {},
|
|
431
434
|
...chunks?.length ? { chunks } : {},
|
|
432
435
|
...metadata ? { metadata } : {}
|
|
433
436
|
} : void 0);
|
|
434
437
|
session.delivered = true;
|
|
435
438
|
session.pendingText = "";
|
|
439
|
+
logVerbose("slack-stream: stream stopped");
|
|
440
|
+
const messageId = stopResponse?.ts ?? stopResponse?.message?.ts;
|
|
441
|
+
return messageId ? { messageId } : {};
|
|
436
442
|
} catch (err) {
|
|
437
443
|
if (isBenignSlackFinalizeError(err)) {
|
|
438
444
|
const code = extractSlackErrorCode(err) ?? "unknown";
|
|
439
445
|
if (session.pendingText) throw new SlackStreamNotDeliveredError(session.pendingText, code);
|
|
440
446
|
if (session.delivered) {
|
|
441
447
|
logVerbose(`slack-stream: finalize rejected by Slack (${code}); prior appends delivered, treating stream as stopped`);
|
|
442
|
-
return;
|
|
448
|
+
return {};
|
|
443
449
|
}
|
|
444
450
|
}
|
|
445
451
|
throw err;
|
|
446
452
|
}
|
|
447
|
-
logVerbose("slack-stream: stream stopped");
|
|
448
453
|
}
|
|
449
454
|
/**
|
|
450
455
|
* Slack API error codes that indicate `chat.stopStream` (or the
|
|
@@ -473,10 +478,10 @@ function extractSlackErrorCode(err) {
|
|
|
473
478
|
return (typeof record.message === "string" ? record.message : "").match(/An API error occurred:\s*([a-z_][a-z0-9_]*)/i)?.[1];
|
|
474
479
|
}
|
|
475
480
|
function markSlackStreamFallbackDelivered(session) {
|
|
476
|
-
const
|
|
481
|
+
const nativeStreamWasStarted = session.delivered;
|
|
477
482
|
session.pendingText = "";
|
|
478
|
-
session.
|
|
479
|
-
|
|
483
|
+
session.streamer.buffer = "";
|
|
484
|
+
session.stopped = !nativeStreamWasStarted;
|
|
480
485
|
}
|
|
481
486
|
//#endregion
|
|
482
487
|
//#region extensions/slack/src/threading.ts
|
|
@@ -848,6 +853,16 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
848
853
|
ctx: prepared.ctxPayload
|
|
849
854
|
});
|
|
850
855
|
const sourceRepliesAreToolOnly = sourceReplyDeliveryMode === "message_tool_only";
|
|
856
|
+
const messageSentHookTarget = prepared.ctxPayload.OriginatingTo ?? prepared.ctxPayload.To ?? prepared.replyTarget;
|
|
857
|
+
const messageSentHookContext = {
|
|
858
|
+
sessionKeyForInternalHooks: prepared.ctxPayload.SessionKey ?? route.sessionKey,
|
|
859
|
+
isGroup: prepared.isRoomish,
|
|
860
|
+
groupId: prepared.isRoomish ? message.channel : void 0
|
|
861
|
+
};
|
|
862
|
+
const messageSentDeliveryHookContext = {
|
|
863
|
+
...messageSentHookContext,
|
|
864
|
+
messageSentHookTarget
|
|
865
|
+
};
|
|
851
866
|
const reactionMessageTs = prepared.ackReactionMessageTs;
|
|
852
867
|
const messageTs = message.ts ?? message.event_ts;
|
|
853
868
|
const incomingThreadTs = message.thread_ts;
|
|
@@ -998,7 +1013,79 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
998
1013
|
let usedBlockReplyThreadTs;
|
|
999
1014
|
let observedReplyDelivery = false;
|
|
1000
1015
|
let observedFinalReplyDelivery = false;
|
|
1016
|
+
const streamedDeliveries = [];
|
|
1017
|
+
const streamedFailuresOwnedByDispatcher = {
|
|
1018
|
+
tool: 0,
|
|
1019
|
+
block: 0,
|
|
1020
|
+
final: 0
|
|
1021
|
+
};
|
|
1022
|
+
const refreshStreamedAcknowledgements = (session) => {
|
|
1023
|
+
if (session.pendingText.length === 0) for (const delivery of streamedDeliveries) delivery.acknowledged = true;
|
|
1024
|
+
};
|
|
1025
|
+
const recordStreamedDelivery = (kind, content) => {
|
|
1026
|
+
const delivery = {
|
|
1027
|
+
kind,
|
|
1028
|
+
content,
|
|
1029
|
+
acknowledged: false
|
|
1030
|
+
};
|
|
1031
|
+
streamedDeliveries.push(delivery);
|
|
1032
|
+
return delivery;
|
|
1033
|
+
};
|
|
1034
|
+
const rememberStreamedDelivery = (kind, content, session) => {
|
|
1035
|
+
recordStreamedDelivery(kind, content);
|
|
1036
|
+
refreshStreamedAcknowledgements(session);
|
|
1037
|
+
};
|
|
1038
|
+
const emitAcknowledgedStreamedDeliveries = (messageId) => {
|
|
1039
|
+
for (const delivery of streamedDeliveries) {
|
|
1040
|
+
if (!delivery.acknowledged || delivery.outcome) continue;
|
|
1041
|
+
emitSlackMessageSentHooks({
|
|
1042
|
+
...messageSentHookContext,
|
|
1043
|
+
to: messageSentHookTarget,
|
|
1044
|
+
accountId: account.accountId,
|
|
1045
|
+
content: delivery.content,
|
|
1046
|
+
success: true,
|
|
1047
|
+
...messageId ? { messageId } : {}
|
|
1048
|
+
});
|
|
1049
|
+
delivery.outcome = "success";
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
const emitFailedPendingStreamedDeliveries = (error) => {
|
|
1053
|
+
for (const delivery of streamedDeliveries) {
|
|
1054
|
+
if (delivery.acknowledged || delivery.outcome) continue;
|
|
1055
|
+
emitSlackMessageSentHooks({
|
|
1056
|
+
...messageSentHookContext,
|
|
1057
|
+
to: messageSentHookTarget,
|
|
1058
|
+
accountId: account.accountId,
|
|
1059
|
+
content: delivery.content,
|
|
1060
|
+
success: false,
|
|
1061
|
+
error
|
|
1062
|
+
});
|
|
1063
|
+
delivery.outcome = "failure";
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
const emitSuccessfulPendingStreamedDeliveries = (messageId) => {
|
|
1067
|
+
for (const delivery of streamedDeliveries) {
|
|
1068
|
+
if (delivery.acknowledged || delivery.outcome) continue;
|
|
1069
|
+
emitSlackMessageSentHooks({
|
|
1070
|
+
...messageSentHookContext,
|
|
1071
|
+
to: messageSentHookTarget,
|
|
1072
|
+
accountId: account.accountId,
|
|
1073
|
+
content: delivery.content,
|
|
1074
|
+
success: true,
|
|
1075
|
+
...messageId ? { messageId } : {}
|
|
1076
|
+
});
|
|
1077
|
+
delivery.outcome = "success";
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1001
1080
|
const deliveryTracker = createSlackEventDeliveryTracker();
|
|
1081
|
+
const markPreviewPayloadDelivered = (params) => {
|
|
1082
|
+
deliveryTracker.markDelivered(params);
|
|
1083
|
+
const nextThreadTs = replyPlan.peekThreadTs();
|
|
1084
|
+
if (nextThreadTs !== params.threadTs) deliveryTracker.markDelivered({
|
|
1085
|
+
...params,
|
|
1086
|
+
threadTs: nextThreadTs
|
|
1087
|
+
});
|
|
1088
|
+
};
|
|
1002
1089
|
const resolveDeliveryThreadTs = (params) => {
|
|
1003
1090
|
const plannedThreadTs = params.forcedThreadTs ? void 0 : replyPlan.nextThreadTs();
|
|
1004
1091
|
return params.forcedThreadTs ?? plannedThreadTs ?? (params.kind === "block" ? usedBlockReplyThreadTs : void 0);
|
|
@@ -1009,7 +1096,23 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1009
1096
|
if (kind === "block") usedBlockReplyThreadTs = deliveredThreadTs;
|
|
1010
1097
|
};
|
|
1011
1098
|
const deliverPendingStreamFallback = async (session, err) => {
|
|
1012
|
-
|
|
1099
|
+
let fallbackError = err;
|
|
1100
|
+
if (!session.stopped) try {
|
|
1101
|
+
const stopResult = await stopSlackStream({
|
|
1102
|
+
session,
|
|
1103
|
+
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
|
|
1104
|
+
});
|
|
1105
|
+
for (const delivery of streamedDeliveries) delivery.acknowledged = true;
|
|
1106
|
+
emitAcknowledgedStreamedDeliveries(stopResult.messageId);
|
|
1107
|
+
observedReplyDelivery = true;
|
|
1108
|
+
usedReplyThreadTs ??= session.threadTs;
|
|
1109
|
+
return true;
|
|
1110
|
+
} catch (stopErr) {
|
|
1111
|
+
if (stopErr instanceof SlackStreamNotDeliveredError) fallbackError = stopErr;
|
|
1112
|
+
else runtime.error?.(danger(`slack-stream: failed to finalize buffered text before fallback: ${formatSlackError(stopErr)}`));
|
|
1113
|
+
}
|
|
1114
|
+
emitAcknowledgedStreamedDeliveries();
|
|
1115
|
+
const fallbackText = fallbackError.pendingText.trim();
|
|
1013
1116
|
if (!fallbackText) return false;
|
|
1014
1117
|
try {
|
|
1015
1118
|
await deliverReplies({
|
|
@@ -1023,15 +1126,27 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1023
1126
|
replyThreadTs: session.threadTs,
|
|
1024
1127
|
replyToMode: replyDeliveryMode,
|
|
1025
1128
|
...slackIdentity ? { identity: slackIdentity } : {},
|
|
1026
|
-
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
|
|
1129
|
+
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
|
|
1130
|
+
...messageSentDeliveryHookContext,
|
|
1131
|
+
deferMessageSentHooks: true
|
|
1027
1132
|
});
|
|
1028
1133
|
markSlackStreamFallbackDelivered(session);
|
|
1134
|
+
if (!session.stopped) try {
|
|
1135
|
+
await stopSlackStream({
|
|
1136
|
+
session,
|
|
1137
|
+
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
|
|
1138
|
+
});
|
|
1139
|
+
} catch (finalizeErr) {
|
|
1140
|
+
runtime.error?.(danger(`slack-stream: failed to finalize native stream after fallback delivery: ${formatSlackError(finalizeErr)}`));
|
|
1141
|
+
}
|
|
1142
|
+
emitSuccessfulPendingStreamedDeliveries();
|
|
1029
1143
|
observedReplyDelivery = true;
|
|
1030
1144
|
usedReplyThreadTs ??= session.threadTs;
|
|
1031
|
-
logVerbose(`slack-stream: streamed delivery failed (${
|
|
1145
|
+
logVerbose(`slack-stream: streamed delivery failed (${fallbackError.slackCode}); delivered ${fallbackText.length} chars via deliverReplies fallback`);
|
|
1032
1146
|
return true;
|
|
1033
1147
|
} catch (postErr) {
|
|
1034
|
-
|
|
1148
|
+
emitFailedPendingStreamedDeliveries(formatErrorMessage(postErr));
|
|
1149
|
+
runtime.error?.(danger(`slack-stream: fallback deliverReplies failed after ${fallbackError.slackCode}: ${formatErrorMessage(postErr)}`));
|
|
1035
1150
|
return false;
|
|
1036
1151
|
}
|
|
1037
1152
|
};
|
|
@@ -1045,7 +1160,7 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1045
1160
|
threadTs: deliveryReplyThreadTs
|
|
1046
1161
|
})) {
|
|
1047
1162
|
logVerbose("slack: suppressed duplicate normal delivery within the same turn");
|
|
1048
|
-
return;
|
|
1163
|
+
return deliveryReplyThreadTs;
|
|
1049
1164
|
}
|
|
1050
1165
|
await deliverReplies({
|
|
1051
1166
|
cfg: ctx.cfg,
|
|
@@ -1058,7 +1173,8 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1058
1173
|
replyThreadTs: deliveryReplyThreadTs,
|
|
1059
1174
|
replyToMode: replyDeliveryMode,
|
|
1060
1175
|
...slackIdentity ? { identity: slackIdentity } : {},
|
|
1061
|
-
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
|
|
1176
|
+
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
|
|
1177
|
+
...messageSentDeliveryHookContext
|
|
1062
1178
|
});
|
|
1063
1179
|
observedReplyDelivery = true;
|
|
1064
1180
|
if (params.kind === "final") observedFinalReplyDelivery = true;
|
|
@@ -1074,9 +1190,13 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1074
1190
|
payload: params.payload,
|
|
1075
1191
|
threadTs: deliveryReplyThreadTs
|
|
1076
1192
|
});
|
|
1193
|
+
return deliveryReplyThreadTs;
|
|
1077
1194
|
};
|
|
1078
1195
|
const deliverBufferedStreamFallback = async (params) => {
|
|
1079
|
-
if (!await deliverPendingStreamFallback(params.session, params.err))
|
|
1196
|
+
if (!await deliverPendingStreamFallback(params.session, params.err)) {
|
|
1197
|
+
streamedFailuresOwnedByDispatcher[params.kind] += 1;
|
|
1198
|
+
return false;
|
|
1199
|
+
}
|
|
1080
1200
|
replyPlan.markSent();
|
|
1081
1201
|
if (params.kind === "final") observedFinalReplyDelivery = true;
|
|
1082
1202
|
deliveryTracker.markDelivered({
|
|
@@ -1152,10 +1272,12 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1152
1272
|
}),
|
|
1153
1273
|
userId: message.user
|
|
1154
1274
|
});
|
|
1275
|
+
refreshStreamedAcknowledgements(streamSession);
|
|
1155
1276
|
if (streamSession.delivered) {
|
|
1156
1277
|
observedReplyDelivery = true;
|
|
1157
1278
|
if (params.kind === "final") observedFinalReplyDelivery = true;
|
|
1158
1279
|
}
|
|
1280
|
+
if (text) rememberStreamedDelivery(params.kind, text, streamSession);
|
|
1159
1281
|
rememberDeliveredThreadTs(params.kind, streamThreadTs);
|
|
1160
1282
|
replyPlan.markSent();
|
|
1161
1283
|
deliveryTracker.markDelivered({
|
|
@@ -1197,11 +1319,13 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1197
1319
|
});
|
|
1198
1320
|
return;
|
|
1199
1321
|
}
|
|
1322
|
+
if (text) recordStreamedDelivery(params.kind, text);
|
|
1200
1323
|
await appendSlackStream({
|
|
1201
1324
|
session: streamSession,
|
|
1202
1325
|
text: "\n" + text,
|
|
1203
1326
|
chunks: completionChunks
|
|
1204
1327
|
});
|
|
1328
|
+
refreshStreamedAcknowledgements(streamSession);
|
|
1205
1329
|
if (completionChunks?.length) nativeProgressCompletionSent = true;
|
|
1206
1330
|
if (streamSession.delivered) {
|
|
1207
1331
|
observedReplyDelivery = true;
|
|
@@ -1244,6 +1368,7 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1244
1368
|
kind: params.kind,
|
|
1245
1369
|
textOverride: text
|
|
1246
1370
|
})) return;
|
|
1371
|
+
throw err;
|
|
1247
1372
|
}
|
|
1248
1373
|
await deliverNormally({
|
|
1249
1374
|
payload: params.payload,
|
|
@@ -1313,7 +1438,7 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1313
1438
|
kind: info.kind,
|
|
1314
1439
|
forcedThreadTs: finalThreadTs
|
|
1315
1440
|
});
|
|
1316
|
-
|
|
1441
|
+
markPreviewPayloadDelivered({
|
|
1317
1442
|
kind: info.kind,
|
|
1318
1443
|
payload,
|
|
1319
1444
|
threadTs: finalThreadTs
|
|
@@ -1363,6 +1488,14 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1363
1488
|
...edit.blocks?.length ? { blocks: edit.blocks } : {},
|
|
1364
1489
|
threadTs: edit.threadTs
|
|
1365
1490
|
});
|
|
1491
|
+
if (!ttsSupplement) emitSlackMessageSentHooks({
|
|
1492
|
+
...messageSentHookContext,
|
|
1493
|
+
to: messageSentHookTarget,
|
|
1494
|
+
accountId: account.accountId,
|
|
1495
|
+
content: trimmedFinalText,
|
|
1496
|
+
success: true,
|
|
1497
|
+
messageId: preview.messageId
|
|
1498
|
+
});
|
|
1366
1499
|
draftPreviewCommitted = true;
|
|
1367
1500
|
observedFinalReplyDelivery = true;
|
|
1368
1501
|
},
|
|
@@ -1372,7 +1505,7 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1372
1505
|
const finalThreadTs = usedReplyThreadTs ?? statusThreadTs;
|
|
1373
1506
|
observedReplyDelivery = true;
|
|
1374
1507
|
replyPlan.markSent();
|
|
1375
|
-
|
|
1508
|
+
if (!ttsSupplement) markPreviewPayloadDelivered({
|
|
1376
1509
|
kind: info.kind,
|
|
1377
1510
|
payload,
|
|
1378
1511
|
threadTs: finalThreadTs
|
|
@@ -1380,9 +1513,16 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1380
1513
|
},
|
|
1381
1514
|
buildSupplementalPayload: () => ttsSupplement ? buildTtsSupplementMediaPayload(payload) : void 0,
|
|
1382
1515
|
deliverSupplemental: async (supplementalPayload) => {
|
|
1383
|
-
|
|
1516
|
+
const previewThreadTs = usedReplyThreadTs ?? statusThreadTs;
|
|
1517
|
+
const supplementalThreadTs = await deliverNormally({
|
|
1384
1518
|
payload: supplementalPayload,
|
|
1385
|
-
kind: info.kind
|
|
1519
|
+
kind: info.kind,
|
|
1520
|
+
forcedThreadTs: previewThreadTs
|
|
1521
|
+
});
|
|
1522
|
+
markPreviewPayloadDelivered({
|
|
1523
|
+
kind: info.kind,
|
|
1524
|
+
payload,
|
|
1525
|
+
threadTs: supplementalThreadTs
|
|
1386
1526
|
});
|
|
1387
1527
|
},
|
|
1388
1528
|
logPreviewEditFailure: (err) => {
|
|
@@ -1792,15 +1932,35 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
1792
1932
|
finalInProgressStatus: dispatchError ? "error" : "complete"
|
|
1793
1933
|
}) : void 0;
|
|
1794
1934
|
if (completionChunks?.length) nativeProgressCompletionSent = true;
|
|
1795
|
-
await stopSlackStream({
|
|
1935
|
+
const stopResult = await stopSlackStream({
|
|
1796
1936
|
session: finalStream,
|
|
1797
1937
|
...completionChunks?.length ? { chunks: completionChunks } : {},
|
|
1798
1938
|
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
|
|
1799
1939
|
});
|
|
1940
|
+
for (const delivery of streamedDeliveries) delivery.acknowledged = true;
|
|
1941
|
+
emitAcknowledgedStreamedDeliveries(stopResult?.messageId);
|
|
1800
1942
|
} catch (err) {
|
|
1801
1943
|
if (err instanceof SlackStreamNotDeliveredError) streamFallbackDelivered = await deliverPendingStreamFallback(finalStream, err);
|
|
1802
|
-
else
|
|
1944
|
+
else {
|
|
1945
|
+
const error = formatSlackError(err);
|
|
1946
|
+
emitAcknowledgedStreamedDeliveries();
|
|
1947
|
+
emitFailedPendingStreamedDeliveries(error);
|
|
1948
|
+
runtime.error?.(danger(`slack-stream: failed to stop stream: ${error}`));
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
for (const kind of [
|
|
1952
|
+
"tool",
|
|
1953
|
+
"block",
|
|
1954
|
+
"final"
|
|
1955
|
+
]) {
|
|
1956
|
+
const failedStreamedCount = streamedDeliveries.filter((delivery) => delivery.kind === kind && delivery.outcome === "failure").length;
|
|
1957
|
+
const additionalFailedStreamed = Math.max(0, failedStreamedCount - streamedFailuresOwnedByDispatcher[kind]);
|
|
1958
|
+
if (additionalFailedStreamed > 0) counts = {
|
|
1959
|
+
...counts,
|
|
1960
|
+
[kind]: Math.max(0, (counts[kind] ?? 0) - additionalFailedStreamed)
|
|
1961
|
+
};
|
|
1803
1962
|
}
|
|
1963
|
+
queuedFinal = queuedFinal && (counts.final ?? 0) > 0;
|
|
1804
1964
|
const anyReplyDelivered = hasVisibleInboundReplyDispatch({
|
|
1805
1965
|
queuedFinal,
|
|
1806
1966
|
counts
|
|
@@ -2699,7 +2699,7 @@ function createSlackThreadTsResolver(params) {
|
|
|
2699
2699
|
//#region extensions/slack/src/monitor/message-handler.ts
|
|
2700
2700
|
let slackMessagePipelinePromise;
|
|
2701
2701
|
function loadSlackMessagePipeline() {
|
|
2702
|
-
slackMessagePipelinePromise ??= import("./pipeline.runtime-
|
|
2702
|
+
slackMessagePipelinePromise ??= import("./pipeline.runtime-1FYOQOt_.js");
|
|
2703
2703
|
return slackMessagePipelinePromise;
|
|
2704
2704
|
}
|
|
2705
2705
|
const APP_MENTION_RETRY_TTL_MS = 6e4;
|
|
@@ -3341,7 +3341,7 @@ function loadSlashCommandsRuntime() {
|
|
|
3341
3341
|
return slashCommandsRuntimePromise;
|
|
3342
3342
|
}
|
|
3343
3343
|
function loadSlashDispatchRuntime() {
|
|
3344
|
-
slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-
|
|
3344
|
+
slashDispatchRuntimePromise ??= import("./slash-dispatch.runtime-Dvvs747S.js");
|
|
3345
3345
|
return slashDispatchRuntimePromise;
|
|
3346
3346
|
}
|
|
3347
3347
|
function loadSlackPluginCommandsRuntime() {
|
|
@@ -3777,6 +3777,7 @@ async function registerSlackMonitorSlashCommands(params) {
|
|
|
3777
3777
|
targetSessionKey: route.sessionKey,
|
|
3778
3778
|
lowercaseSessionKey: true
|
|
3779
3779
|
});
|
|
3780
|
+
const slashReplyTarget = !slashCommand.ephemeral && isRoomish ? `channel:${command.channel_id}` : `user:${command.user_id}`;
|
|
3780
3781
|
const ctxPayload = finalizeInboundContext({
|
|
3781
3782
|
Body: prompt,
|
|
3782
3783
|
BodyForAgent: prompt,
|
|
@@ -3809,7 +3810,7 @@ async function registerSlackMonitorSlashCommands(params) {
|
|
|
3809
3810
|
CommandSource: "native",
|
|
3810
3811
|
CommandAuthorized: commandAuthorized,
|
|
3811
3812
|
OriginatingChannel: "slack",
|
|
3812
|
-
OriginatingTo:
|
|
3813
|
+
OriginatingTo: slashReplyTarget
|
|
3813
3814
|
});
|
|
3814
3815
|
await recordInboundSessionMetaSafe({
|
|
3815
3816
|
cfg,
|
|
@@ -3831,12 +3832,18 @@ async function registerSlackMonitorSlashCommands(params) {
|
|
|
3831
3832
|
}) ? compileSlackInteractiveReplies(payload) : payload;
|
|
3832
3833
|
}
|
|
3833
3834
|
});
|
|
3835
|
+
const messageSentHookTarget = ctxPayload.OriginatingTo ?? ctxPayload.To ?? slashReplyTarget;
|
|
3834
3836
|
const deliverSlashPayloads = async (replies) => {
|
|
3835
3837
|
await deliverSlackSlashReplies({
|
|
3836
3838
|
replies,
|
|
3837
3839
|
respond,
|
|
3838
3840
|
ephemeral: slashCommand.ephemeral,
|
|
3839
3841
|
textLimit: ctx.textLimit,
|
|
3842
|
+
messageSentHookTarget,
|
|
3843
|
+
accountId: route.accountId,
|
|
3844
|
+
sessionKeyForInternalHooks: ctxPayload.SessionKey ?? route.sessionKey,
|
|
3845
|
+
isGroup: isRoomish,
|
|
3846
|
+
groupId: isRoomish ? command.channel_id : void 0,
|
|
3840
3847
|
chunkMode: resolveChunkMode(cfg, "slack", route.accountId),
|
|
3841
3848
|
tableMode: resolveMarkdownTableMode({
|
|
3842
3849
|
cfg,
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { r as resolveSlackReplyBlocks, s as SLACK_TEXT_LIMIT } from "./thread-ts-Cffag8e2.js";
|
|
2
|
+
import { o as markdownToSlackMrkdwnChunks, t as sendMessageSlack } from "./send-rekB-Xjp.js";
|
|
3
|
+
import { createReplyReferencePlanner } from "openclaw/plugin-sdk/reply-reference";
|
|
4
|
+
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
5
|
+
import { SILENT_REPLY_TOKEN, chunkMarkdownTextWithMode, isSilentReplyText } from "openclaw/plugin-sdk/reply-chunking";
|
|
6
|
+
import { deliverTextOrMediaReply, getReplyPayloadTtsSupplement, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
7
|
+
import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
|
|
8
|
+
import { buildCanonicalSentMessageHookContext, createInternalHookEvent, fireAndForgetHook, toInternalMessageSentContext, toPluginMessageContext, toPluginMessageSentEvent, triggerInternalHook } from "openclaw/plugin-sdk/hook-runtime";
|
|
9
|
+
//#region extensions/slack/src/message-sent-hook.ts
|
|
10
|
+
/**
|
|
11
|
+
* Slack-side emission of the `message_sent` plugin hook.
|
|
12
|
+
*
|
|
13
|
+
* Mirrors the Telegram pattern in `extensions/telegram/src/bot/delivery.replies.ts`
|
|
14
|
+
* (`buildTelegramSentHookContext`, `emitMessageSentHooks`, `emitTelegramMessageSentHooks`).
|
|
15
|
+
*
|
|
16
|
+
* Without this, plugins observing `message_sent` see Telegram outbound but not
|
|
17
|
+
* Slack outbound — even though `docs/plugins/hooks.md` documents the hook as
|
|
18
|
+
* firing for all successful outbound deliveries.
|
|
19
|
+
*/
|
|
20
|
+
function buildSlackSentHookContext(params) {
|
|
21
|
+
return buildCanonicalSentMessageHookContext({
|
|
22
|
+
to: params.to,
|
|
23
|
+
content: params.content,
|
|
24
|
+
success: params.success,
|
|
25
|
+
error: params.error,
|
|
26
|
+
channelId: "slack",
|
|
27
|
+
accountId: params.accountId ?? void 0,
|
|
28
|
+
conversationId: params.to,
|
|
29
|
+
sessionKey: params.sessionKeyForInternalHooks,
|
|
30
|
+
messageId: params.messageId,
|
|
31
|
+
isGroup: params.isGroup,
|
|
32
|
+
groupId: params.groupId
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function emitInternalSlackMessageSentHook(params) {
|
|
36
|
+
if (!params.sessionKeyForInternalHooks) return;
|
|
37
|
+
const canonical = buildSlackSentHookContext(params);
|
|
38
|
+
fireAndForgetHook(triggerInternalHook(createInternalHookEvent("message", "sent", params.sessionKeyForInternalHooks, toInternalMessageSentContext(canonical))), "slack: message:sent internal hook failed");
|
|
39
|
+
}
|
|
40
|
+
function emitMessageSentHooks(params) {
|
|
41
|
+
if (!params.enabled && !params.sessionKeyForInternalHooks) return;
|
|
42
|
+
const canonical = buildSlackSentHookContext(params);
|
|
43
|
+
if (params.enabled) fireAndForgetHook(Promise.resolve(params.hookRunner.runMessageSent(toPluginMessageSentEvent(canonical), toPluginMessageContext(canonical))), "slack: message_sent plugin hook failed");
|
|
44
|
+
emitInternalSlackMessageSentHook(params);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Fire both the plugin `message_sent` hook and (if a session key is supplied)
|
|
48
|
+
* the internal `message:sent` hook for a successful or failed Slack outbound
|
|
49
|
+
* delivery.
|
|
50
|
+
*
|
|
51
|
+
* Safe to call after every `chat.postMessage` — the function self-gates on
|
|
52
|
+
* `hookRunner.hasHooks("message_sent")` so plugins not observing the hook
|
|
53
|
+
* incur no cost.
|
|
54
|
+
*/
|
|
55
|
+
function emitSlackMessageSentHooks(params) {
|
|
56
|
+
const hookRunner = getGlobalHookRunner();
|
|
57
|
+
emitMessageSentHooks({
|
|
58
|
+
...params,
|
|
59
|
+
hookRunner,
|
|
60
|
+
enabled: hookRunner?.hasHooks("message_sent") ?? false
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region extensions/slack/src/monitor/replies.ts
|
|
65
|
+
function readSlackReplyBlocks(payload) {
|
|
66
|
+
return resolveSlackReplyBlocks(payload);
|
|
67
|
+
}
|
|
68
|
+
function resolveSlackMediaHookSpokenText(payload) {
|
|
69
|
+
return (getReplyPayloadTtsSupplement(payload)?.spokenText ?? payload.spokenText)?.trim() || void 0;
|
|
70
|
+
}
|
|
71
|
+
function resolveDeliveredSlackReplyThreadTs(params) {
|
|
72
|
+
return (params.replyToMode === "off" ? void 0 : params.payloadReplyToId) ?? params.replyThreadTs;
|
|
73
|
+
}
|
|
74
|
+
async function deliverReplies(params) {
|
|
75
|
+
let latestResult;
|
|
76
|
+
for (const payload of params.replies) {
|
|
77
|
+
if (payload.isReasoning === true) continue;
|
|
78
|
+
const threadTs = resolveDeliveredSlackReplyThreadTs({
|
|
79
|
+
replyToMode: params.replyToMode,
|
|
80
|
+
payloadReplyToId: payload.replyToId,
|
|
81
|
+
replyThreadTs: params.replyThreadTs
|
|
82
|
+
});
|
|
83
|
+
const reply = resolveSendableOutboundReplyParts(payload);
|
|
84
|
+
const slackBlocks = readSlackReplyBlocks(payload);
|
|
85
|
+
if (!reply.hasContent && !slackBlocks?.length) continue;
|
|
86
|
+
const emitSent = (content, result) => {
|
|
87
|
+
if (params.deferMessageSentHooks) return;
|
|
88
|
+
emitSlackMessageSentHooks({
|
|
89
|
+
sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
|
|
90
|
+
to: params.messageSentHookTarget ?? params.target,
|
|
91
|
+
accountId: params.accountId,
|
|
92
|
+
content,
|
|
93
|
+
success: true,
|
|
94
|
+
messageId: result?.messageId,
|
|
95
|
+
isGroup: params.isGroup,
|
|
96
|
+
groupId: params.groupId
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
const emitFailed = (content, error) => {
|
|
100
|
+
if (params.deferMessageSentHooks) return;
|
|
101
|
+
emitSlackMessageSentHooks({
|
|
102
|
+
sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
|
|
103
|
+
to: params.messageSentHookTarget ?? params.target,
|
|
104
|
+
accountId: params.accountId,
|
|
105
|
+
content,
|
|
106
|
+
success: false,
|
|
107
|
+
error: formatErrorMessage(error),
|
|
108
|
+
isGroup: params.isGroup,
|
|
109
|
+
groupId: params.groupId
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
if (!reply.hasMedia && slackBlocks?.length) {
|
|
113
|
+
const trimmed = reply.trimmedText;
|
|
114
|
+
if (!trimmed && !slackBlocks?.length) continue;
|
|
115
|
+
if (trimmed && isSilentReplyText(trimmed, SILENT_REPLY_TOKEN)) continue;
|
|
116
|
+
let result;
|
|
117
|
+
try {
|
|
118
|
+
result = await sendMessageSlack(params.target, trimmed, {
|
|
119
|
+
cfg: params.cfg,
|
|
120
|
+
token: params.token,
|
|
121
|
+
threadTs,
|
|
122
|
+
accountId: params.accountId,
|
|
123
|
+
...slackBlocks?.length ? { blocks: slackBlocks } : {},
|
|
124
|
+
...params.identity ? { identity: params.identity } : {},
|
|
125
|
+
...params.metadata ? { metadata: params.metadata } : {}
|
|
126
|
+
});
|
|
127
|
+
} catch (error) {
|
|
128
|
+
emitFailed(trimmed, error);
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
emitSent(trimmed, result);
|
|
132
|
+
latestResult = result;
|
|
133
|
+
params.runtime.log?.(`delivered reply to ${params.target}`);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const spokenText = resolveSlackMediaHookSpokenText(payload);
|
|
137
|
+
const mediaHookContent = reply.hasText ? reply.text : spokenText || reply.text;
|
|
138
|
+
const hookContent = reply.hasMedia ? mediaHookContent : reply.trimmedText;
|
|
139
|
+
let lastResult;
|
|
140
|
+
let delivered;
|
|
141
|
+
try {
|
|
142
|
+
delivered = await deliverTextOrMediaReply({
|
|
143
|
+
payload,
|
|
144
|
+
text: reply.text,
|
|
145
|
+
chunkText: !reply.hasMedia ? (value) => {
|
|
146
|
+
const trimmed = value.trim();
|
|
147
|
+
if (!trimmed || isSilentReplyText(trimmed, SILENT_REPLY_TOKEN)) return [];
|
|
148
|
+
return [trimmed];
|
|
149
|
+
} : void 0,
|
|
150
|
+
sendText: async (trimmed) => {
|
|
151
|
+
lastResult = await sendMessageSlack(params.target, trimmed, {
|
|
152
|
+
cfg: params.cfg,
|
|
153
|
+
token: params.token,
|
|
154
|
+
threadTs,
|
|
155
|
+
accountId: params.accountId,
|
|
156
|
+
...params.identity ? { identity: params.identity } : {},
|
|
157
|
+
...params.metadata ? { metadata: params.metadata } : {}
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
sendMedia: async ({ mediaUrl, caption }) => {
|
|
161
|
+
lastResult = await sendMessageSlack(params.target, caption ?? "", {
|
|
162
|
+
cfg: params.cfg,
|
|
163
|
+
token: params.token,
|
|
164
|
+
mediaUrl,
|
|
165
|
+
threadTs,
|
|
166
|
+
accountId: params.accountId,
|
|
167
|
+
...params.identity ? { identity: params.identity } : {},
|
|
168
|
+
...params.metadata ? { metadata: params.metadata } : {}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
} catch (error) {
|
|
173
|
+
emitFailed(hookContent, error);
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
if (delivered !== "empty") {
|
|
177
|
+
emitSent(hookContent, reply.hasMedia ? void 0 : lastResult);
|
|
178
|
+
latestResult = lastResult;
|
|
179
|
+
params.runtime.log?.(`delivered reply to ${params.target}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return latestResult;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Compute effective threadTs for a Slack reply based on replyToMode.
|
|
186
|
+
* - "off": stay in thread if already in one, otherwise main channel
|
|
187
|
+
* - "first": first reply goes to thread, subsequent replies to main channel
|
|
188
|
+
* - "all": all replies go to thread
|
|
189
|
+
*/
|
|
190
|
+
function resolveSlackThreadTs(params) {
|
|
191
|
+
return createSlackReplyReferencePlanner({
|
|
192
|
+
replyToMode: params.replyToMode,
|
|
193
|
+
incomingThreadTs: params.incomingThreadTs,
|
|
194
|
+
messageTs: params.messageTs,
|
|
195
|
+
hasReplied: params.hasReplied,
|
|
196
|
+
isThreadReply: params.isThreadReply
|
|
197
|
+
}).use();
|
|
198
|
+
}
|
|
199
|
+
function createSlackReplyReferencePlanner(params) {
|
|
200
|
+
return createReplyReferencePlanner({
|
|
201
|
+
replyToMode: params.isThreadReply ?? Boolean(params.incomingThreadTs && params.incomingThreadTs !== params.messageTs) ? "all" : params.replyToMode,
|
|
202
|
+
existingId: params.incomingThreadTs,
|
|
203
|
+
startId: params.messageTs,
|
|
204
|
+
hasReplied: params.hasReplied
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function createSlackReplyDeliveryPlan(params) {
|
|
208
|
+
const replyReference = createSlackReplyReferencePlanner({
|
|
209
|
+
replyToMode: params.replyToMode,
|
|
210
|
+
incomingThreadTs: params.incomingThreadTs,
|
|
211
|
+
messageTs: params.messageTs,
|
|
212
|
+
hasReplied: params.hasRepliedRef.value,
|
|
213
|
+
isThreadReply: params.isThreadReply
|
|
214
|
+
});
|
|
215
|
+
return {
|
|
216
|
+
peekThreadTs: () => replyReference.peek(),
|
|
217
|
+
nextThreadTs: () => replyReference.use(),
|
|
218
|
+
markSent: () => {
|
|
219
|
+
replyReference.markSent();
|
|
220
|
+
params.hasRepliedRef.value = replyReference.hasReplied();
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async function deliverSlackSlashReplies(params) {
|
|
225
|
+
const deliveries = [];
|
|
226
|
+
const chunkLimit = Math.min(params.textLimit, SLACK_TEXT_LIMIT);
|
|
227
|
+
for (const payload of params.replies) {
|
|
228
|
+
if (payload.isReasoning === true) continue;
|
|
229
|
+
const reply = resolveSendableOutboundReplyParts(payload);
|
|
230
|
+
const slackBlocks = readSlackReplyBlocks(payload);
|
|
231
|
+
const text = reply.hasText && !isSilentReplyText(reply.trimmedText, SILENT_REPLY_TOKEN) ? reply.trimmedText : void 0;
|
|
232
|
+
if (slackBlocks?.length && !reply.hasMedia) {
|
|
233
|
+
deliveries.push({
|
|
234
|
+
hookContent: text ?? "",
|
|
235
|
+
messages: [{
|
|
236
|
+
text: text ?? "",
|
|
237
|
+
blocks: slackBlocks
|
|
238
|
+
}]
|
|
239
|
+
});
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
const combined = [text ?? "", ...reply.mediaUrls].filter(Boolean).join("\n");
|
|
243
|
+
if (!combined) continue;
|
|
244
|
+
const chunkMode = params.chunkMode ?? "length";
|
|
245
|
+
const chunks = (chunkMode === "newline" ? chunkMarkdownTextWithMode(combined, chunkLimit, chunkMode) : [combined]).flatMap((markdown) => markdownToSlackMrkdwnChunks(markdown, chunkLimit, { tableMode: params.tableMode }));
|
|
246
|
+
if (!chunks.length && combined) chunks.push(combined);
|
|
247
|
+
deliveries.push({
|
|
248
|
+
hookContent: text ?? resolveSlackMediaHookSpokenText(payload) ?? combined,
|
|
249
|
+
messages: chunks.map((chunk) => ({ text: chunk }))
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
if (deliveries.length === 0) return;
|
|
253
|
+
const responseType = params.ephemeral ? "ephemeral" : "in_channel";
|
|
254
|
+
for (const delivery of deliveries) {
|
|
255
|
+
try {
|
|
256
|
+
for (const message of delivery.messages) await params.respond({
|
|
257
|
+
...message,
|
|
258
|
+
response_type: responseType
|
|
259
|
+
});
|
|
260
|
+
} catch (error) {
|
|
261
|
+
if (params.messageSentHookTarget) emitSlackMessageSentHooks({
|
|
262
|
+
sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
|
|
263
|
+
to: params.messageSentHookTarget,
|
|
264
|
+
accountId: params.accountId,
|
|
265
|
+
content: delivery.hookContent,
|
|
266
|
+
success: false,
|
|
267
|
+
error: formatErrorMessage(error),
|
|
268
|
+
isGroup: params.isGroup,
|
|
269
|
+
groupId: params.groupId
|
|
270
|
+
});
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
if (params.messageSentHookTarget) emitSlackMessageSentHooks({
|
|
274
|
+
sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
|
|
275
|
+
to: params.messageSentHookTarget,
|
|
276
|
+
accountId: params.accountId,
|
|
277
|
+
content: delivery.hookContent,
|
|
278
|
+
success: true,
|
|
279
|
+
isGroup: params.isGroup,
|
|
280
|
+
groupId: params.groupId
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
//#endregion
|
|
285
|
+
export { resolveDeliveredSlackReplyThreadTs as a, readSlackReplyBlocks as i, deliverReplies as n, resolveSlackThreadTs as o, deliverSlackSlashReplies as r, emitSlackMessageSentHooks as s, createSlackReplyDeliveryPlan as t };
|
package/dist/runtime-api.js
CHANGED
|
@@ -7,8 +7,8 @@ import { a as listSlackEmojis, c as pinSlackMessage, d as removeOwnSlackReaction
|
|
|
7
7
|
import { t as probeSlack } from "./probe-BTKzLT2u.js";
|
|
8
8
|
import { t as resolveSlackChannelAllowlist } from "./resolve-channels-DX2GSx9c.js";
|
|
9
9
|
import { t as resolveSlackUserAllowlist } from "./resolve-users-DOULgUwy.js";
|
|
10
|
-
import { t as monitorSlackProvider } from "./provider-
|
|
10
|
+
import { t as monitorSlackProvider } from "./provider-ChhYGpXx.js";
|
|
11
11
|
import { n as slackActionRuntime, t as handleSlackAction } from "./action-runtime-CgrHdqkC.js";
|
|
12
12
|
import { n as listSlackDirectoryGroupsLive, r as listSlackDirectoryPeersLive } from "./directory-live-C1acgXKJ.js";
|
|
13
|
-
import "./monitor-
|
|
13
|
+
import "./monitor-DwDmBYfl.js";
|
|
14
14
|
export { deleteSlackMessage, editSlackMessage, getSlackMemberInfo, handleSlackAction, listEnabledSlackAccounts, listSlackAccountIds, listSlackDirectoryGroupsLive, listSlackDirectoryPeersLive, listSlackEmojis, listSlackPins, listSlackReactions, monitorSlackProvider, pinSlackMessage, probeSlack, reactSlackMessage, readSlackMessages, registerSlackPluginHttpRoutes, removeOwnSlackReactions, removeSlackReaction, resolveDefaultSlackAccountId, resolveSlackAccount, resolveSlackAppToken, resolveSlackBotToken, resolveSlackChannelAllowlist, resolveSlackGroupRequireMention, resolveSlackGroupToolPolicy, resolveSlackUserAllowlist, sendMessageSlack, sendSlackMessage, setSlackRuntime, slackActionRuntime, unpinSlackMessage };
|
package/dist/setup-plugin-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as slackSetupPlugin } from "./channel.setup-
|
|
1
|
+
import { t as slackSetupPlugin } from "./channel.setup-W5YnieWd.js";
|
|
2
2
|
export { slackSetupPlugin };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, o as resolveSlackAccountAllowFrom } from "./accounts-f6Xcv9Vi.js";
|
|
2
|
-
import "./shared-
|
|
2
|
+
import "./shared-Bkkmro6q.js";
|
|
3
3
|
import { i as SLACK_CHANNEL, t as createSlackSetupWizardBase } from "./setup-core-POfI_bgP.js";
|
|
4
4
|
import { t as resolveSlackChannelAllowlist } from "./resolve-channels-DX2GSx9c.js";
|
|
5
5
|
import { t as resolveSlackUserAllowlist } from "./resolve-users-DOULgUwy.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as resolveSlackAccount, c as resolveSlackConfigAccessorAccount, i as resolveDefaultSlackAccountId, n as listSlackAccountIds, o as resolveSlackAccountAllowFrom, s as resolveSlackAccountDmPolicy } from "./accounts-f6Xcv9Vi.js";
|
|
1
|
+
import { a as resolveSlackAccount, c as resolveSlackConfigAccessorAccount, i as resolveDefaultSlackAccountId, n as listSlackAccountIds, o as resolveSlackAccountAllowFrom, r as mergeSlackAccountConfig, s as resolveSlackAccountDmPolicy } from "./accounts-f6Xcv9Vi.js";
|
|
2
2
|
import { t as inspectSlackAccount } from "./account-inspect-CdGk6R7l.js";
|
|
3
3
|
import { f as getChatChannelMeta, h as isSlackPluginAccountConfigured } from "./client-Cn2WwpcA.js";
|
|
4
4
|
import { n as isSlackInteractiveRepliesEnabled } from "./interactive-replies-DrBq4Mld.js";
|
|
@@ -71,6 +71,92 @@ function isSlackMutableAllowEntry(raw) {
|
|
|
71
71
|
function asObjectRecord(value) {
|
|
72
72
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
73
73
|
}
|
|
74
|
+
const collectSlackMutableAllowlistWarnings = createDangerousNameMatchingMutableAllowlistWarningCollector({
|
|
75
|
+
channel: "slack",
|
|
76
|
+
detector: isSlackMutableAllowEntry,
|
|
77
|
+
collectLists: (scope) => {
|
|
78
|
+
const lists = [{
|
|
79
|
+
pathLabel: `${scope.prefix}.allowFrom`,
|
|
80
|
+
list: scope.account.allowFrom
|
|
81
|
+
}];
|
|
82
|
+
const dm = asObjectRecord(scope.account.dm);
|
|
83
|
+
if (dm) lists.push({
|
|
84
|
+
pathLabel: `${scope.prefix}.dm.allowFrom`,
|
|
85
|
+
list: dm.allowFrom
|
|
86
|
+
});
|
|
87
|
+
const channels = asObjectRecord(scope.account.channels);
|
|
88
|
+
if (channels) for (const [channelKey, channelRaw] of Object.entries(channels)) {
|
|
89
|
+
const channel = asObjectRecord(channelRaw);
|
|
90
|
+
if (!channel) continue;
|
|
91
|
+
lists.push({
|
|
92
|
+
pathLabel: `${scope.prefix}.channels.${channelKey}.users`,
|
|
93
|
+
list: channel.users
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return lists;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
const SLACK_CANONICAL_CHANNEL_ID_RE = /^[CG][A-Z0-9]{8,}$/;
|
|
100
|
+
const SLACK_LOWERCASE_CHANNEL_ID_RE = /^[cg][0-9][a-z0-9]{7,}$/;
|
|
101
|
+
const SLACK_PREFIXED_CANONICAL_CHANNEL_ID_RE = /^channel:[CG][A-Z0-9]{8,}$/;
|
|
102
|
+
const SLACK_PREFIXED_LOWERCASE_CHANNEL_ID_RE = /^channel:[cg][0-9][a-z0-9]{7,}$/;
|
|
103
|
+
const SLACK_CANONICAL_DM_ID_RE = /^(?:channel:)?D[A-Z0-9]{8,}$/;
|
|
104
|
+
const SLACK_PREFIXED_LOWERCASE_DM_ID_RE = /^channel:d[a-z0-9]{8,}$/;
|
|
105
|
+
const SLACK_AMBIGUOUS_LOWERCASE_DM_ID_RE = /^d[a-z0-9]{8,}$/;
|
|
106
|
+
const SLACK_AMBIGUOUS_LOWERCASE_CHANNEL_ID_RE = /^(?:channel:)?[cgd][a-z][a-z0-9]{7,}$/;
|
|
107
|
+
const SLACK_CHANNEL_NAME_RE = /^[\p{L}\p{M}\p{N}_-]{1,80}$/u;
|
|
108
|
+
const SLACK_CHANNEL_NAME_ALPHANUMERIC_RE = /[\p{L}\p{N}]/u;
|
|
109
|
+
function looksLikeSlackChannelId(channelKey) {
|
|
110
|
+
return SLACK_CANONICAL_CHANNEL_ID_RE.test(channelKey) || SLACK_LOWERCASE_CHANNEL_ID_RE.test(channelKey) || SLACK_PREFIXED_CANONICAL_CHANNEL_ID_RE.test(channelKey) || SLACK_PREFIXED_LOWERCASE_CHANNEL_ID_RE.test(channelKey);
|
|
111
|
+
}
|
|
112
|
+
function looksLikeSlackDmId(channelKey) {
|
|
113
|
+
return SLACK_CANONICAL_DM_ID_RE.test(channelKey) || SLACK_PREFIXED_LOWERCASE_DM_ID_RE.test(channelKey);
|
|
114
|
+
}
|
|
115
|
+
function looksLikeSlackChannelNameKey(channelKey) {
|
|
116
|
+
const name = channelKey.startsWith("#") ? channelKey.slice(1) : channelKey;
|
|
117
|
+
return name === name.toLowerCase() && SLACK_CHANNEL_NAME_RE.test(name) && SLACK_CHANNEL_NAME_ALPHANUMERIC_RE.test(name);
|
|
118
|
+
}
|
|
119
|
+
function collectSlackNameKeyedChannelWarnings({ cfg }) {
|
|
120
|
+
const warnings = /* @__PURE__ */ new Set();
|
|
121
|
+
const slackCfg = asObjectRecord(asObjectRecord(cfg.channels)?.slack);
|
|
122
|
+
const providerChannels = asObjectRecord(slackCfg?.channels);
|
|
123
|
+
const accounts = asObjectRecord(slackCfg?.accounts);
|
|
124
|
+
for (const accountId of listSlackAccountIds(cfg)) {
|
|
125
|
+
const account = asObjectRecord(mergeSlackAccountConfig(cfg, accountId));
|
|
126
|
+
if (!account || slackCfg?.enabled === false || account.enabled === false) continue;
|
|
127
|
+
const effectiveGroupPolicy = (typeof account.groupPolicy === "string" ? account.groupPolicy : void 0) ?? "allowlist";
|
|
128
|
+
const rawAccount = asObjectRecord(accounts?.[accountId]);
|
|
129
|
+
const accountPrefix = rawAccount ? `channels.slack.accounts.${accountId}` : "channels.slack";
|
|
130
|
+
const accountChannels = asObjectRecord(rawAccount?.channels);
|
|
131
|
+
const channels = accountChannels ?? providerChannels;
|
|
132
|
+
if (!channels) continue;
|
|
133
|
+
const channelsPrefix = accountChannels ? `channels.slack.accounts.${accountId}` : "channels.slack";
|
|
134
|
+
const fallbackDescription = Object.hasOwn(channels, "*") ? `${channelsPrefix}.channels."*" applies instead and this entry's overrides are ignored` : effectiveGroupPolicy === "open" ? "this entry's overrides are ignored and the channel remains allowed by groupPolicy: \"open\"" : "messages from the channel are dropped";
|
|
135
|
+
for (const channelKey of Object.keys(channels)) {
|
|
136
|
+
if (channelKey === "*") continue;
|
|
137
|
+
if (looksLikeSlackDmId(channelKey)) {
|
|
138
|
+
warnings.add(`${channelsPrefix}.channels."${channelKey}" is a Slack DM conversation ID, but ${channelsPrefix}.channels only configures channel and group rooms. Configure DM access with ${accountPrefix}.dmPolicy and ${accountPrefix}.allowFrom instead.`);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (SLACK_AMBIGUOUS_LOWERCASE_DM_ID_RE.test(channelKey)) {
|
|
142
|
+
if (account.dangerouslyAllowNameMatching === true && looksLikeSlackChannelNameKey(channelKey)) continue;
|
|
143
|
+
warnings.add(`${channelsPrefix}.channels."${channelKey}" is ambiguous: it may be a lowercase Slack DM conversation ID or a channel name. Configure DMs with ${accountPrefix}.dmPolicy and ${accountPrefix}.allowFrom; otherwise re-key the room with its stable C/G ID.`);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (effectiveGroupPolicy === "disabled") continue;
|
|
147
|
+
const channelConfig = asObjectRecord(channels[channelKey]);
|
|
148
|
+
if (effectiveGroupPolicy === "open" && Object.keys(channelConfig ?? {}).length === 0) continue;
|
|
149
|
+
if (looksLikeSlackChannelId(channelKey)) continue;
|
|
150
|
+
if (account.dangerouslyAllowNameMatching === true && looksLikeSlackChannelNameKey(channelKey)) continue;
|
|
151
|
+
if (SLACK_AMBIGUOUS_LOWERCASE_CHANNEL_ID_RE.test(channelKey)) {
|
|
152
|
+
warnings.add(`${channelsPrefix}.channels."${channelKey}" is ambiguous: it may be a lowercase Slack channel ID or a channel name. If it is a channel name, inbound routing will not match it and ${fallbackDescription}. Re-key it with the channel's stable ID (e.g. C0123ABCD, from the channel's About details or conversations.info).`);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
warnings.add(`${channelsPrefix}.channels."${channelKey}" is keyed by a channel name or non-canonical ID form, not a routable Slack channel ID; under groupPolicy: "${effectiveGroupPolicy}" inbound routing does not match this entry, so ${fallbackDescription}. Re-key it with the channel's ID (e.g. C0123ABCD, from the channel's About details or conversations.info).`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return [...warnings];
|
|
159
|
+
}
|
|
74
160
|
const slackDoctor = {
|
|
75
161
|
dmAllowFromMode: "topOnly",
|
|
76
162
|
groupModel: "route",
|
|
@@ -78,31 +164,7 @@ const slackDoctor = {
|
|
|
78
164
|
warnOnEmptyGroupSenderAllowlist: false,
|
|
79
165
|
legacyConfigRules,
|
|
80
166
|
normalizeCompatibilityConfig,
|
|
81
|
-
collectMutableAllowlistWarnings:
|
|
82
|
-
channel: "slack",
|
|
83
|
-
detector: isSlackMutableAllowEntry,
|
|
84
|
-
collectLists: (scope) => {
|
|
85
|
-
const lists = [{
|
|
86
|
-
pathLabel: `${scope.prefix}.allowFrom`,
|
|
87
|
-
list: scope.account.allowFrom
|
|
88
|
-
}];
|
|
89
|
-
const dm = asObjectRecord(scope.account.dm);
|
|
90
|
-
if (dm) lists.push({
|
|
91
|
-
pathLabel: `${scope.prefix}.dm.allowFrom`,
|
|
92
|
-
list: dm.allowFrom
|
|
93
|
-
});
|
|
94
|
-
const channels = asObjectRecord(scope.account.channels);
|
|
95
|
-
if (channels) for (const [channelKey, channelRaw] of Object.entries(channels)) {
|
|
96
|
-
const channel = asObjectRecord(channelRaw);
|
|
97
|
-
if (!channel) continue;
|
|
98
|
-
lists.push({
|
|
99
|
-
pathLabel: `${scope.prefix}.channels.${channelKey}.users`,
|
|
100
|
-
list: channel.users
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
return lists;
|
|
104
|
-
}
|
|
105
|
-
})
|
|
167
|
+
collectMutableAllowlistWarnings: ({ cfg }) => [...collectSlackMutableAllowlistWarnings({ cfg }), ...collectSlackNameKeyedChannelWarnings({ cfg })]
|
|
106
168
|
};
|
|
107
169
|
//#endregion
|
|
108
170
|
//#region extensions/slack/src/shared.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as deliverSlackSlashReplies$1 } from "./replies-
|
|
1
|
+
import { r as deliverSlackSlashReplies$1 } from "./replies-Bt4nWmSd.js";
|
|
2
2
|
import { resolveAgentRoute as resolveAgentRoute$1 } from "openclaw/plugin-sdk/routing";
|
|
3
3
|
import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
4
4
|
import { recordInboundSessionMetaSafe as recordInboundSessionMetaSafe$1, resolveConversationLabel as resolveConversationLabel$1 } from "openclaw/plugin-sdk/conversation-runtime";
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/slack",
|
|
3
|
-
"version": "2026.6.
|
|
3
|
+
"version": "2026.6.8-beta.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/slack",
|
|
9
|
-
"version": "2026.6.
|
|
9
|
+
"version": "2026.6.8-beta.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@slack/bolt": "4.7.3",
|
|
12
12
|
"@slack/types": "2.21.1",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"zod": "4.4.3"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"openclaw": ">=2026.6.
|
|
18
|
+
"openclaw": ">=2026.6.8-beta.1"
|
|
19
19
|
},
|
|
20
20
|
"peerDependenciesMeta": {
|
|
21
21
|
"openclaw": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/slack",
|
|
3
|
-
"version": "2026.6.
|
|
3
|
+
"version": "2026.6.8-beta.1",
|
|
4
4
|
"description": "OpenClaw Slack channel plugin for channels, DMs, commands, and app events.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"zod": "4.4.3"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"openclaw": ">=2026.6.
|
|
18
|
+
"openclaw": ">=2026.6.8-beta.1"
|
|
19
19
|
},
|
|
20
20
|
"peerDependenciesMeta": {
|
|
21
21
|
"openclaw": {
|
|
@@ -60,13 +60,13 @@
|
|
|
60
60
|
"allowInvalidConfigRecovery": true
|
|
61
61
|
},
|
|
62
62
|
"compat": {
|
|
63
|
-
"pluginApi": ">=2026.6.
|
|
63
|
+
"pluginApi": ">=2026.6.8-beta.1"
|
|
64
64
|
},
|
|
65
65
|
"startup": {
|
|
66
66
|
"deferConfiguredChannelFullLoadUntilAfterListen": true
|
|
67
67
|
},
|
|
68
68
|
"build": {
|
|
69
|
-
"openclawVersion": "2026.6.
|
|
69
|
+
"openclawVersion": "2026.6.8-beta.1",
|
|
70
70
|
"bundledDist": false
|
|
71
71
|
},
|
|
72
72
|
"release": {
|
package/dist/replies-FEOdgSlE.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { r as resolveSlackReplyBlocks, s as SLACK_TEXT_LIMIT } from "./thread-ts-Cffag8e2.js";
|
|
2
|
-
import { o as markdownToSlackMrkdwnChunks, t as sendMessageSlack } from "./send-rekB-Xjp.js";
|
|
3
|
-
import { createReplyReferencePlanner } from "openclaw/plugin-sdk/reply-reference";
|
|
4
|
-
import { SILENT_REPLY_TOKEN, chunkMarkdownTextWithMode, isSilentReplyText } from "openclaw/plugin-sdk/reply-chunking";
|
|
5
|
-
import { deliverTextOrMediaReply, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
6
|
-
//#region extensions/slack/src/monitor/replies.ts
|
|
7
|
-
function readSlackReplyBlocks(payload) {
|
|
8
|
-
return resolveSlackReplyBlocks(payload);
|
|
9
|
-
}
|
|
10
|
-
function resolveDeliveredSlackReplyThreadTs(params) {
|
|
11
|
-
return (params.replyToMode === "off" ? void 0 : params.payloadReplyToId) ?? params.replyThreadTs;
|
|
12
|
-
}
|
|
13
|
-
async function deliverReplies(params) {
|
|
14
|
-
for (const payload of params.replies) {
|
|
15
|
-
if (payload.isReasoning === true) continue;
|
|
16
|
-
const threadTs = resolveDeliveredSlackReplyThreadTs({
|
|
17
|
-
replyToMode: params.replyToMode,
|
|
18
|
-
payloadReplyToId: payload.replyToId,
|
|
19
|
-
replyThreadTs: params.replyThreadTs
|
|
20
|
-
});
|
|
21
|
-
const reply = resolveSendableOutboundReplyParts(payload);
|
|
22
|
-
const slackBlocks = readSlackReplyBlocks(payload);
|
|
23
|
-
if (!reply.hasContent && !slackBlocks?.length) continue;
|
|
24
|
-
if (!reply.hasMedia && slackBlocks?.length) {
|
|
25
|
-
const trimmed = reply.trimmedText;
|
|
26
|
-
if (!trimmed && !slackBlocks?.length) continue;
|
|
27
|
-
if (trimmed && isSilentReplyText(trimmed, SILENT_REPLY_TOKEN)) continue;
|
|
28
|
-
await sendMessageSlack(params.target, trimmed, {
|
|
29
|
-
cfg: params.cfg,
|
|
30
|
-
token: params.token,
|
|
31
|
-
threadTs,
|
|
32
|
-
accountId: params.accountId,
|
|
33
|
-
...slackBlocks?.length ? { blocks: slackBlocks } : {},
|
|
34
|
-
...params.identity ? { identity: params.identity } : {},
|
|
35
|
-
...params.metadata ? { metadata: params.metadata } : {}
|
|
36
|
-
});
|
|
37
|
-
params.runtime.log?.(`delivered reply to ${params.target}`);
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (await deliverTextOrMediaReply({
|
|
41
|
-
payload,
|
|
42
|
-
text: reply.text,
|
|
43
|
-
chunkText: !reply.hasMedia ? (value) => {
|
|
44
|
-
const trimmed = value.trim();
|
|
45
|
-
if (!trimmed || isSilentReplyText(trimmed, SILENT_REPLY_TOKEN)) return [];
|
|
46
|
-
return [trimmed];
|
|
47
|
-
} : void 0,
|
|
48
|
-
sendText: async (trimmed) => {
|
|
49
|
-
await sendMessageSlack(params.target, trimmed, {
|
|
50
|
-
cfg: params.cfg,
|
|
51
|
-
token: params.token,
|
|
52
|
-
threadTs,
|
|
53
|
-
accountId: params.accountId,
|
|
54
|
-
...params.identity ? { identity: params.identity } : {},
|
|
55
|
-
...params.metadata ? { metadata: params.metadata } : {}
|
|
56
|
-
});
|
|
57
|
-
},
|
|
58
|
-
sendMedia: async ({ mediaUrl, caption }) => {
|
|
59
|
-
await sendMessageSlack(params.target, caption ?? "", {
|
|
60
|
-
cfg: params.cfg,
|
|
61
|
-
token: params.token,
|
|
62
|
-
mediaUrl,
|
|
63
|
-
threadTs,
|
|
64
|
-
accountId: params.accountId,
|
|
65
|
-
...params.identity ? { identity: params.identity } : {},
|
|
66
|
-
...params.metadata ? { metadata: params.metadata } : {}
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}) !== "empty") params.runtime.log?.(`delivered reply to ${params.target}`);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Compute effective threadTs for a Slack reply based on replyToMode.
|
|
74
|
-
* - "off": stay in thread if already in one, otherwise main channel
|
|
75
|
-
* - "first": first reply goes to thread, subsequent replies to main channel
|
|
76
|
-
* - "all": all replies go to thread
|
|
77
|
-
*/
|
|
78
|
-
function resolveSlackThreadTs(params) {
|
|
79
|
-
return createSlackReplyReferencePlanner({
|
|
80
|
-
replyToMode: params.replyToMode,
|
|
81
|
-
incomingThreadTs: params.incomingThreadTs,
|
|
82
|
-
messageTs: params.messageTs,
|
|
83
|
-
hasReplied: params.hasReplied,
|
|
84
|
-
isThreadReply: params.isThreadReply
|
|
85
|
-
}).use();
|
|
86
|
-
}
|
|
87
|
-
function createSlackReplyReferencePlanner(params) {
|
|
88
|
-
return createReplyReferencePlanner({
|
|
89
|
-
replyToMode: params.isThreadReply ?? Boolean(params.incomingThreadTs && params.incomingThreadTs !== params.messageTs) ? "all" : params.replyToMode,
|
|
90
|
-
existingId: params.incomingThreadTs,
|
|
91
|
-
startId: params.messageTs,
|
|
92
|
-
hasReplied: params.hasReplied
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
function createSlackReplyDeliveryPlan(params) {
|
|
96
|
-
const replyReference = createSlackReplyReferencePlanner({
|
|
97
|
-
replyToMode: params.replyToMode,
|
|
98
|
-
incomingThreadTs: params.incomingThreadTs,
|
|
99
|
-
messageTs: params.messageTs,
|
|
100
|
-
hasReplied: params.hasRepliedRef.value,
|
|
101
|
-
isThreadReply: params.isThreadReply
|
|
102
|
-
});
|
|
103
|
-
return {
|
|
104
|
-
peekThreadTs: () => replyReference.peek(),
|
|
105
|
-
nextThreadTs: () => replyReference.use(),
|
|
106
|
-
markSent: () => {
|
|
107
|
-
replyReference.markSent();
|
|
108
|
-
params.hasRepliedRef.value = replyReference.hasReplied();
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
async function deliverSlackSlashReplies(params) {
|
|
113
|
-
const messages = [];
|
|
114
|
-
const chunkLimit = Math.min(params.textLimit, SLACK_TEXT_LIMIT);
|
|
115
|
-
for (const payload of params.replies) {
|
|
116
|
-
if (payload.isReasoning === true) continue;
|
|
117
|
-
const reply = resolveSendableOutboundReplyParts(payload);
|
|
118
|
-
const slackBlocks = readSlackReplyBlocks(payload);
|
|
119
|
-
const text = reply.hasText && !isSilentReplyText(reply.trimmedText, SILENT_REPLY_TOKEN) ? reply.trimmedText : void 0;
|
|
120
|
-
if (slackBlocks?.length && !reply.hasMedia) {
|
|
121
|
-
messages.push({
|
|
122
|
-
text: text ?? "",
|
|
123
|
-
blocks: slackBlocks
|
|
124
|
-
});
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
const combined = [text ?? "", ...reply.mediaUrls].filter(Boolean).join("\n");
|
|
128
|
-
if (!combined) continue;
|
|
129
|
-
const chunkMode = params.chunkMode ?? "length";
|
|
130
|
-
const chunks = (chunkMode === "newline" ? chunkMarkdownTextWithMode(combined, chunkLimit, chunkMode) : [combined]).flatMap((markdown) => markdownToSlackMrkdwnChunks(markdown, chunkLimit, { tableMode: params.tableMode }));
|
|
131
|
-
if (!chunks.length && combined) chunks.push(combined);
|
|
132
|
-
for (const chunk of chunks) messages.push({ text: chunk });
|
|
133
|
-
}
|
|
134
|
-
if (messages.length === 0) return;
|
|
135
|
-
const responseType = params.ephemeral ? "ephemeral" : "in_channel";
|
|
136
|
-
for (const message of messages) await params.respond({
|
|
137
|
-
...message,
|
|
138
|
-
response_type: responseType
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
//#endregion
|
|
142
|
-
export { resolveDeliveredSlackReplyThreadTs as a, readSlackReplyBlocks as i, deliverReplies as n, resolveSlackThreadTs as o, deliverSlackSlashReplies as r, createSlackReplyDeliveryPlan as t };
|