@openclaw/slack 2026.9.1-beta.1 → 2026.9.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/{account-inspect-CN2hBsim.js → account-inspect-C3sePHWa.js} +3 -3
- package/dist/account-inspect-api.js +1 -1
- package/dist/{action-runtime-BoSeX09X.js → action-runtime-DND4Pyp2.js} +84 -66
- package/dist/action-runtime.runtime-CEhbmS-Z.js +2 -0
- package/dist/{actions-XdQyjG_p.js → actions-fh66A6lc.js} +198 -54
- package/dist/api.js +14 -14
- package/dist/{approval-handler.runtime-DxXfGX_Z.js → approval-handler.runtime-D7d8sKM0.js} +18 -12
- package/dist/{channel-C_vnTgxo.js → channel-4m8IAdMD.js} +70 -128
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-type-j-2lalcC.js → channel-type-3-SqEMVc.js} +2 -2
- package/dist/{channel.setup-aal8tokM.js → channel.setup-t_ghLGb_.js} +23 -22
- package/dist/{config-schema-CN3XZRX5.js → config-schema-bP3eV2ra.js} +25 -16
- package/dist/{conversation-binding-route-DFVLUdYF.js → conversation-binding-route-Vee8Dgbf.js} +2 -2
- package/dist/{cursor-pages-eX7p8Wwt.js → cursor-pages-ByFwo-Py.js} +7 -1
- package/dist/{directory-config-DqfQ42OV.js → directory-config-Bmw4ykSy.js} +4 -3
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-lC6X_Ru-.js → directory-live-q32AW7N1.js} +3 -8
- package/dist/{doctor-contract-BAy7-EUQ.js → doctor-contract-BNjw9bxb.js} +5 -4
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{group-policy-CU1tEGpS.js → group-policy-BKjMqT-8.js} +161 -260
- package/dist/{message-tool-api-CWosHSi5.js → message-tool-api-uNEfQYGY.js} +19 -2
- package/dist/message-tool-api.js +1 -1
- package/dist/{monitor-DrNCGneD.js → monitor-Chwu_SiZ.js} +3 -3
- package/dist/{outbound-adapter-DkT2vUE9.js → outbound-adapter-BB8wAjm5.js} +95 -91
- package/dist/{pipeline.runtime-CRRng_Nw.js → pipeline.runtime-BklS_NEj.js} +347 -505
- package/dist/{policy-BoP6t88v.js → policy-D7PKkiet.js} +7 -4
- package/dist/{probe-4_aHtVT3.js → probe-gj5yQgN2.js} +210 -3
- package/dist/{provider-D4nDgxCh.js → provider-Do50wMWn.js} +168 -153
- package/dist/{relay-source-CEmze11m.js → relay-source-DWuduiJ2.js} +6 -4
- package/dist/{replies-D9XMM_S0.js → replies-Dc6WVh_Q.js} +20 -16
- package/dist/{resolve-channels-DSamDgVs.js → resolve-channels-CU7Bg5Ka.js} +6 -10
- package/dist/{resolve-users-CIzwbaJR.js → resolve-users-DrhttvRQ.js} +5 -4
- package/dist/runtime-api.js +10 -10
- package/dist/{scopes-BBrUzpBH.js → scopes-B9HRguDe.js} +1 -1
- package/dist/{send-DtjEnlLh.js → send-Djswy6lf.js} +24 -16
- package/dist/send-results-eeOu_HYm.js +23 -0
- package/dist/{send.runtime-DQeNKa2J.js → send.runtime-__WVTWLQ.js} +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-CsBCM4rP.js → setup-surface-DO0e-MMS.js} +5 -5
- package/dist/{shared-D9GeeR9K.js → shared-DEQBEKBL.js} +7 -5
- package/dist/{slash-dispatch.runtime-Chr3YHnT.js → slash-dispatch.runtime-C3B4h5ZM.js} +2 -2
- package/dist/{target-parsing-BnMD2ZqZ.js → target-parsing-PbxGDbr9.js} +11 -6
- package/dist/{targets-Cx5W_n3W.js → targets-7FexXJ62.js} +1 -1
- package/dist/{thread-ts-DUGhaYKq.js → thread-ts-CukseRVL.js} +5 -1
- package/node_modules/@slack/bolt/node_modules/negotiator/lib/accept.js +54 -0
- package/node_modules/@slack/bolt/node_modules/negotiator/lib/charset.js +10 -34
- package/node_modules/@slack/bolt/node_modules/negotiator/lib/encoding.js +9 -30
- package/node_modules/@slack/bolt/node_modules/negotiator/lib/language.js +15 -36
- package/node_modules/@slack/bolt/node_modules/negotiator/lib/mediaType.js +16 -134
- package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
- package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/README.md +71 -0
- package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
- package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
- package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
- package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/package.json +52 -0
- package/node_modules/@slack/bolt/node_modules/negotiator/package.json +16 -10
- package/node_modules/typebox/build/format/email.mjs +1 -1
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
- package/node_modules/typebox/package.json +1 -1
- package/openclaw.plugin.json +15 -7
- package/package.json +5 -5
- package/dist/action-runtime.runtime-Dpa16J87.js +0 -2
- package/dist/slack-client-kind-hSfjXyUM.js +0 -94
- package/node_modules/@slack/bolt/node_modules/negotiator/HISTORY.md +0 -114
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { n as resolveSlackConversationBindingRoute, r as resolveSlackReplyToMode, t as normalizeSlackRouteBindingConfig } from "./conversation-binding-route-
|
|
2
|
-
import { n as formatSlackTarget } from "./target-parsing-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { n as resolveSlackConversationBindingRoute, r as resolveSlackReplyToMode, t as normalizeSlackRouteBindingConfig } from "./conversation-binding-route-Vee8Dgbf.js";
|
|
2
|
+
import { n as formatSlackTarget } from "./target-parsing-PbxGDbr9.js";
|
|
3
|
+
import { I as countSlackTextUtf8Bytes, L as truncateSlackText, M as SLACK_TEXT_LIMIT, Q as normalizeSlackOutboundText, R as truncateSlackTextByUtf8Bytes, T as buildSlackBlocksFallbackText, U as SLACK_SESSION_LINK_ACTION_ID, a as qualifySlackConversationId, b as hasSlackNativeDataBlock, et as escapeSlackMrkdwn, k as SLACK_EDIT_TEXT_MAX_BYTES, m as resolveSlackReplyRenderPlan, o as qualifySlackRoutePeerId, s as resolveSlackEnterpriseMainDmSessionKey } from "./group-policy-BKjMqT-8.js";
|
|
4
|
+
import { C as normalizeAllowListLower, E as resolveSlackAllowListMatch, o as formatSlackError, w as normalizeSlackAllowOwnerEntry } from "./probe-gj5yQgN2.js";
|
|
5
|
+
import { n as resolveSlackReplyThreadTs } from "./thread-ts-CukseRVL.js";
|
|
5
6
|
import { n as resolveSlackStreamingMode, t as resolveSlackNativeStreaming } from "./streaming-compat-B-e0mqM0.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { c as
|
|
9
|
-
import {
|
|
10
|
-
import { n as
|
|
11
|
-
import { a as resolveDeliveredSlackReplyThreadTs, c as emitSlackMessageSentHooks, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, s as sanitizeSlackMonitorReplyPayload, t as createSlackReplyDeliveryPlan } from "./replies-D9XMM_S0.js";
|
|
7
|
+
import { D as resolveSlackMessageText$1, E as resolveSlackBlocksText, O as buildSlackEditTextPayload, S as formatSlackFileReferenceList, T as isSlackUnfurlAttachment, a as editSlackRenderedMessage, d as reactSlackMessage, i as editSlackMessage, m as removeSlackReaction, n as deleteSlackMessage, v as logVerbose$1, w as hasSlackTableBlock, x as formatSlackFileReference } from "./actions-fh66A6lc.js";
|
|
8
|
+
import { c as hasSlackThreadFailureNotice, d as recordSlackThreadFailureNotice, f as recordSlackThreadParticipation, l as hasSlackThreadParticipation, m as buildSlackNativeDataDeliveryPlan, o as clearSlackThreadFailureNotice, r as sendMessageSlack, u as hasSlackThreadParticipationWithPersistence } from "./send-Djswy6lf.js";
|
|
9
|
+
import { C as resolveStorePath, E as stripSlackMentionsForCommandDetection, S as resolveChannelResetConfig, _ as resolveSlackChatType, b as readSessionUpdatedAt, c as resolveSlackThreadTargets, d as authorizeSlackBotRoomMessage, f as resolveSlackCommandIngress, g as normalizeSlackChannelType, l as trackSlackDraftMessage, o as authorizeSlackDirectMessage, p as resolveSlackEffectiveAllowFrom, r as resolveSlackRoomContextHints, s as resolveSlackThreadContext, u as resolveConversationLabel$1, v as buildSlackAssistantThreadMetadata, w as updateLastRoute, x as resolveChannelContextVisibilityMode, y as parseSlackAssistantThreadMetadata } from "./provider-Do50wMWn.js";
|
|
10
|
+
import { n as resolveSlackChannelConfig } from "./policy-D7PKkiet.js";
|
|
11
|
+
import { a as resolveSlackThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as sanitizeSlackMonitorReplyPayload, s as emitSlackMessageSentHooks, t as createSlackReplyDeliveryPlan } from "./replies-Dc6WVh_Q.js";
|
|
12
12
|
import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
13
13
|
import { resolveAgentRoute, resolveInboundLastRouteSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
|
|
14
|
-
import {
|
|
14
|
+
import { createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createDraftStreamLoop, createFinalizableDraftStreamControlsForState, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
|
|
15
15
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
16
16
|
import { danger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
17
17
|
import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
|
|
18
|
+
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
|
|
19
|
+
import { buildTtsSupplementMediaPayload, getReplyPayloadTtsSupplement, isReplyPayloadNonTerminalToolErrorWarning, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
18
20
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
19
|
-
import { createHash } from "node:crypto";
|
|
20
21
|
import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
|
|
21
22
|
import { ensureConfiguredBindingRouteReady } from "openclaw/plugin-sdk/conversation-runtime";
|
|
23
|
+
import { resolveChannelImplicitMentions } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
22
24
|
import fs from "node:fs/promises";
|
|
23
25
|
import { runTasksWithConcurrency } from "openclaw/plugin-sdk/concurrency-runtime";
|
|
24
26
|
import { formatErrorMessage, toErrorObject } from "openclaw/plugin-sdk/error-runtime";
|
|
25
|
-
import { buildTtsSupplementMediaPayload, getReplyPayloadTtsSupplement, isReplyPayloadNonTerminalToolErrorWarning, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
26
27
|
import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist, shouldIncludeSupplementalContext } from "openclaw/plugin-sdk/security-runtime";
|
|
27
28
|
import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
|
28
29
|
import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
29
30
|
import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
|
|
30
31
|
import { enqueueRoutedSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
|
|
31
|
-
import { resolveChannelImplicitMentions } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
32
32
|
import { asDateTimestampMs, asFiniteNumberInRange, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
33
33
|
import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
|
|
34
|
-
import { buildChannelInboundEventContext, buildMentionRegexes, classifyChannelInboundEvent, dispatchChannelInboundTurn, formatInboundEnvelope, formatInboundMediaUnavailableText, hasVisibleInboundReplyDispatch, implicitMentionKindWhen,
|
|
34
|
+
import { buildChannelInboundEventContext, buildMentionRegexes, classifyChannelInboundEvent, dispatchChannelInboundTurn, formatInboundEnvelope, formatInboundMediaUnavailableText, hasVisibleInboundReplyDispatch, implicitMentionKindWhen, matchesMentionWithExplicit, readAgentRunTerminalOutcome, recordDroppedChannelInboundHistory, resolveEnvelopeFormatOptions, resolveInboundMentionDecision, resolveInboundReplyDispatchCounts, resolveInboundSupplementalSenderAllowed, resolveUnmentionedGroupInboundPolicy, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
|
|
35
35
|
import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
|
|
36
36
|
import { resolveGatewayPublicOrigin } from "openclaw/plugin-sdk/config-contracts";
|
|
37
37
|
import { buildControlUiSessionPath } from "openclaw/plugin-sdk/session-discussion";
|
|
38
|
-
import {
|
|
38
|
+
import { createStatusReactionController, logAckFailure, logTypingFailure, resolveAckReaction, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
|
|
39
39
|
import { hasControlCommand } from "openclaw/plugin-sdk/command-detection";
|
|
40
40
|
import { isAbortRequestText } from "openclaw/plugin-sdk/command-primitives-runtime";
|
|
41
41
|
import { shouldHandleTextCommands } from "openclaw/plugin-sdk/command-surface";
|
|
@@ -93,7 +93,7 @@ async function startSlackStream(params) {
|
|
|
93
93
|
try {
|
|
94
94
|
const result = await streamer.append({
|
|
95
95
|
...text ? { markdown_text: text } : {},
|
|
96
|
-
...chunks
|
|
96
|
+
...chunks ? { chunks } : {}
|
|
97
97
|
});
|
|
98
98
|
if (result) {
|
|
99
99
|
session.delivered = true;
|
|
@@ -121,7 +121,7 @@ async function appendSlackStream(params) {
|
|
|
121
121
|
try {
|
|
122
122
|
const result = await session.streamer.append({
|
|
123
123
|
...text ? { markdown_text: text } : {},
|
|
124
|
-
...chunks
|
|
124
|
+
...chunks ? { chunks } : {}
|
|
125
125
|
});
|
|
126
126
|
if (result) {
|
|
127
127
|
session.delivered = true;
|
|
@@ -269,7 +269,8 @@ function resolveExplicitSlackProgressTitle(entry) {
|
|
|
269
269
|
return trimmed && trimmed.toLowerCase() !== "auto" ? trimmed : void 0;
|
|
270
270
|
}
|
|
271
271
|
function resolveSlackProgressStyle(entry) {
|
|
272
|
-
|
|
272
|
+
const progress = entry?.streaming?.progress;
|
|
273
|
+
return progress?.style ?? (progress?.toolProgress === false ? "compact" : "card");
|
|
273
274
|
}
|
|
274
275
|
function resolveSlackNativeProgressTaskCards(entry) {
|
|
275
276
|
if (resolveSlackProgressStyle(entry) === "compact") return false;
|
|
@@ -479,6 +480,7 @@ function createSlackDraftStream(params) {
|
|
|
479
480
|
};
|
|
480
481
|
const { loop, update, discardPending, seal } = createFinalizableDraftStreamControlsForState({
|
|
481
482
|
throttleMs,
|
|
483
|
+
coalesceInFlight: true,
|
|
482
484
|
state: streamState,
|
|
483
485
|
sendOrEditStreamMessage,
|
|
484
486
|
emptyValue: "",
|
|
@@ -572,64 +574,11 @@ function createSlackDraftStream(params) {
|
|
|
572
574
|
};
|
|
573
575
|
}
|
|
574
576
|
//#endregion
|
|
575
|
-
//#region extensions/slack/src/stream-mode.ts
|
|
576
|
-
function resolveSlackStreamingConfig(params) {
|
|
577
|
-
return {
|
|
578
|
-
mode: resolveSlackStreamingMode(params),
|
|
579
|
-
nativeStreaming: resolveSlackNativeStreaming(params)
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
function applyAppendOnlyStreamUpdate(params) {
|
|
583
|
-
const incoming = params.incoming.trimEnd();
|
|
584
|
-
if (!incoming) return {
|
|
585
|
-
rendered: params.rendered,
|
|
586
|
-
source: params.source,
|
|
587
|
-
changed: false
|
|
588
|
-
};
|
|
589
|
-
if (!params.rendered) return {
|
|
590
|
-
rendered: incoming,
|
|
591
|
-
source: incoming,
|
|
592
|
-
changed: true
|
|
593
|
-
};
|
|
594
|
-
if (incoming === params.source) return {
|
|
595
|
-
rendered: params.rendered,
|
|
596
|
-
source: params.source,
|
|
597
|
-
changed: false
|
|
598
|
-
};
|
|
599
|
-
if (incoming.startsWith(params.rendered)) return {
|
|
600
|
-
rendered: incoming,
|
|
601
|
-
source: incoming,
|
|
602
|
-
changed: incoming !== params.rendered
|
|
603
|
-
};
|
|
604
|
-
if (incoming.startsWith(params.source)) {
|
|
605
|
-
const delta = incoming.slice(params.source.length);
|
|
606
|
-
return {
|
|
607
|
-
rendered: `${params.rendered}${delta}`,
|
|
608
|
-
source: incoming,
|
|
609
|
-
changed: delta.length > 0
|
|
610
|
-
};
|
|
611
|
-
}
|
|
612
|
-
if (params.source.startsWith(incoming)) return {
|
|
613
|
-
rendered: params.rendered,
|
|
614
|
-
source: params.source,
|
|
615
|
-
changed: false
|
|
616
|
-
};
|
|
617
|
-
const separator = params.separator ?? (params.rendered.endsWith("\n") ? "" : "\n");
|
|
618
|
-
return {
|
|
619
|
-
rendered: `${params.rendered}${separator}${incoming}`,
|
|
620
|
-
source: incoming,
|
|
621
|
-
changed: true
|
|
622
|
-
};
|
|
623
|
-
}
|
|
624
|
-
//#endregion
|
|
625
577
|
//#region extensions/slack/src/progress-blocks.ts
|
|
626
578
|
const SLACK_PROGRESS_FIELD_MAX = 1800;
|
|
627
579
|
const DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS = 120;
|
|
628
|
-
const DEFAULT_SLACK_PROGRESS_TASK_DETAIL_MAX_CHARS = 48;
|
|
629
580
|
const SLACK_PROGRESS_CHUNK_TEXT_MAX = 256;
|
|
630
581
|
const SLACK_PROGRESS_TASK_TITLE_MAX = 120;
|
|
631
|
-
const SLACK_PROGRESS_PLAN_FALLBACK_TITLE = "Thinking";
|
|
632
|
-
const SLACK_PROGRESS_LINE_DELTA_RE = /(?:^|\s)\+(\d+)\s+[−-](\d+)(?=\s|$)/u;
|
|
633
582
|
function buildSessionSources(url) {
|
|
634
583
|
return [{
|
|
635
584
|
type: "url_source",
|
|
@@ -637,12 +586,6 @@ function buildSessionSources(url) {
|
|
|
637
586
|
text: "Open in OpenClaw"
|
|
638
587
|
}];
|
|
639
588
|
}
|
|
640
|
-
function field(text) {
|
|
641
|
-
return {
|
|
642
|
-
type: "mrkdwn",
|
|
643
|
-
text: truncateSlackText(text, SLACK_PROGRESS_FIELD_MAX)
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
589
|
function resolveMaxLineChars(value, fallback) {
|
|
647
590
|
return value && value > 0 ? Math.floor(value) : fallback;
|
|
648
591
|
}
|
|
@@ -661,141 +604,58 @@ function compactTitle(value) {
|
|
|
661
604
|
function compactChunkText(value) {
|
|
662
605
|
return truncateSlackText(value.replace(/\s+/g, " ").trim(), SLACK_PROGRESS_CHUNK_TEXT_MAX);
|
|
663
606
|
}
|
|
664
|
-
function lineDetailParts(line) {
|
|
665
|
-
return [line.detail, line.status && line.status !== "completed" && !line.detail?.includes(line.status) ? line.status : void 0].map((part) => part?.trim()).filter((part) => Boolean(part));
|
|
666
|
-
}
|
|
667
|
-
function legacyLineTitle(line) {
|
|
668
|
-
return `${line.icon ?? "•"} *${escapeSlackMrkdwn(line.label)}*`;
|
|
669
|
-
}
|
|
670
607
|
function isAuthoredProgressLine(line) {
|
|
671
608
|
return line.id === "reasoning" || line.id?.startsWith("commentary:") === true;
|
|
672
609
|
}
|
|
673
|
-
function
|
|
674
|
-
const
|
|
675
|
-
if (
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
function lineTaskTitle(line) {
|
|
680
|
-
const label = (line.kind === "command-output" ? line.toolName : void 0) || line.label.replace(/\s+/g, " ").trim() || line.toolName || line.kind || "Update";
|
|
681
|
-
const fallback = line.text.replace(/\s+/g, " ").trim();
|
|
682
|
-
if (fallback && fallback !== label) return compactTitle(lineDetailParts(line).length > 0 || line.status ? label : fallback);
|
|
683
|
-
return compactTitle(label);
|
|
684
|
-
}
|
|
685
|
-
function lineTaskDetails(line, maxLineChars) {
|
|
686
|
-
const detail = line.detail?.replace(SLACK_PROGRESS_LINE_DELTA_RE, "").replace(/\s+·\s*$/u, "").trim();
|
|
687
|
-
return detail && detail !== line.status?.trim() ? compactDetail(detail, maxLineChars) : void 0;
|
|
688
|
-
}
|
|
689
|
-
function lineTaskOutput(line) {
|
|
690
|
-
const match = line.detail ? SLACK_PROGRESS_LINE_DELTA_RE.exec(line.detail) : null;
|
|
691
|
-
if (match) return `+${match[1]} −${match[2]}`;
|
|
692
|
-
const status = line.status?.replace(/\s+/g, " ").trim();
|
|
693
|
-
return status && lineTaskStatus(line) === "error" ? status : void 0;
|
|
694
|
-
}
|
|
695
|
-
function lineTaskStatus(line) {
|
|
696
|
-
const normalized = line.status?.replace(/\s+/g, " ").trim().toLowerCase();
|
|
697
|
-
if (!normalized) return "in_progress";
|
|
698
|
-
if (normalized === "complete" || normalized === "completed" || normalized === "done" || normalized === "ok" || normalized === "success" || normalized === "succeeded" || normalized === "successful" || normalized === "exit 0") return "complete";
|
|
699
|
-
if (normalized === "error" || normalized === "failed" || normalized === "failure" || normalized.startsWith("exit ")) return normalized === "exit 0" ? "complete" : "error";
|
|
700
|
-
return "in_progress";
|
|
701
|
-
}
|
|
702
|
-
function slugTaskIdPart(value) {
|
|
703
|
-
return value?.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "task";
|
|
704
|
-
}
|
|
705
|
-
function stableTaskIdPart(value, slugValue = value) {
|
|
706
|
-
const suffix = createHash("sha256").update(value).digest("hex").slice(0, 8);
|
|
707
|
-
return `${slugTaskIdPart(slugValue).slice(0, 48)}_${suffix}`;
|
|
708
|
-
}
|
|
709
|
-
function resolveLineTaskIdentity(line) {
|
|
710
|
-
if (line.id?.trim()) return {
|
|
711
|
-
id: stableTaskIdPart(line.id),
|
|
712
|
-
contentDerived: false
|
|
610
|
+
function resolveProgressAttention(lines) {
|
|
611
|
+
const approval = lines.findLast((line) => line.kind === "approval" && line.status === "requested");
|
|
612
|
+
if (approval) return {
|
|
613
|
+
id: "openclaw_attention",
|
|
614
|
+
title: compactTitle(`Approval required: ${approval.detail || approval.label}`),
|
|
615
|
+
status: "pending"
|
|
713
616
|
};
|
|
617
|
+
const failure = lines.findLast((line) => {
|
|
618
|
+
const status = line.status?.trim().toLowerCase();
|
|
619
|
+
return status === "error" || status === "failed" || status === "failure" || status?.startsWith("exit ") && status !== "exit 0";
|
|
620
|
+
});
|
|
621
|
+
if (!failure) return;
|
|
714
622
|
return {
|
|
715
|
-
id:
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
].join("
|
|
721
|
-
|
|
623
|
+
id: "openclaw_attention",
|
|
624
|
+
title: compactTitle([...new Set([
|
|
625
|
+
failure.label,
|
|
626
|
+
failure.detail,
|
|
627
|
+
failure.status
|
|
628
|
+
].filter(Boolean))].join(" — ")),
|
|
629
|
+
status: "error"
|
|
722
630
|
};
|
|
723
631
|
}
|
|
724
|
-
function
|
|
725
|
-
|
|
632
|
+
function buildSlackProgressStreamChunks(params) {
|
|
633
|
+
const title = compactChunkText(params.title?.trim() || params.label?.trim() || "Working");
|
|
634
|
+
if (!params.title && !params.label && !params.lines.length && !params.plan?.length && !params.sessionUrl) return;
|
|
635
|
+
const tasks = params.plan?.length ? params.plan.slice(-50).map((entry, index) => ({
|
|
726
636
|
id: `plan_step_${index + 1}`,
|
|
727
637
|
title: compactTitle(entry.step),
|
|
728
638
|
status: entry.status === "completed" ? "complete" : entry.status
|
|
729
|
-
}))
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
}
|
|
742
|
-
const details = lineTaskDetails(line, maxLineChars);
|
|
743
|
-
const output = lineTaskOutput(line);
|
|
744
|
-
const task = {
|
|
745
|
-
id,
|
|
746
|
-
title: lineTaskTitle(line),
|
|
747
|
-
status: lineTaskStatus(line)
|
|
748
|
-
};
|
|
749
|
-
if (details) task.details = details;
|
|
750
|
-
if (output) task.output = output;
|
|
751
|
-
return task;
|
|
752
|
-
});
|
|
753
|
-
}
|
|
754
|
-
function resolvePlanTitle(params) {
|
|
755
|
-
return compactChunkText(params.title?.trim() || params.label?.trim() || (params.tasks.at(-1)?.details ? `${params.tasks.at(-1)?.title} — ${params.tasks.at(-1)?.details}` : params.tasks.at(-1)?.title) || SLACK_PROGRESS_PLAN_FALLBACK_TITLE);
|
|
756
|
-
}
|
|
757
|
-
function buildSlackProgressStreamChunks(params) {
|
|
758
|
-
const tasks = buildPlanTasks({
|
|
759
|
-
lines: params.lines,
|
|
760
|
-
plan: params.plan,
|
|
761
|
-
maxLineChars: params.maxLineChars
|
|
762
|
-
});
|
|
763
|
-
if (tasks.length === 0) {
|
|
764
|
-
const title = params.title?.trim() || params.label?.trim();
|
|
765
|
-
if (!title) return;
|
|
766
|
-
if (!params.sessionUrl && !params.diffStat) return [{
|
|
767
|
-
type: "plan_update",
|
|
768
|
-
title: compactChunkText(title)
|
|
769
|
-
}];
|
|
770
|
-
return [{
|
|
771
|
-
type: "plan_update",
|
|
772
|
-
title: compactChunkText(title)
|
|
773
|
-
}, {
|
|
774
|
-
type: "task_update",
|
|
775
|
-
id: "openclaw_summary",
|
|
776
|
-
title: "Completed",
|
|
777
|
-
status: "complete",
|
|
778
|
-
...formatTaskDiffOutput(params.diffStat) ? { output: formatTaskDiffOutput(params.diffStat) } : {},
|
|
779
|
-
...params.sessionUrl ? { sources: buildSessionSources(params.sessionUrl) } : {}
|
|
780
|
-
}];
|
|
781
|
-
}
|
|
782
|
-
const title = resolvePlanTitle({
|
|
783
|
-
label: params.label,
|
|
784
|
-
title: params.title,
|
|
785
|
-
tasks
|
|
786
|
-
});
|
|
639
|
+
})) : [{
|
|
640
|
+
id: "openclaw_summary",
|
|
641
|
+
title: compactTitle(title),
|
|
642
|
+
status: "in_progress"
|
|
643
|
+
}];
|
|
644
|
+
let attention = resolveProgressAttention(params.lines);
|
|
645
|
+
if (params.finalInProgressStatus === "error" && !tasks.some((task) => task.status === "in_progress")) attention = {
|
|
646
|
+
id: "openclaw_attention",
|
|
647
|
+
title: attention?.status === "error" ? attention.title : "Failed",
|
|
648
|
+
status: "error"
|
|
649
|
+
};
|
|
650
|
+
if (attention && !(params.finalInProgressStatus && attention.status === "pending")) tasks.push(attention);
|
|
787
651
|
const finalTaskIndex = tasks.length - 1;
|
|
788
|
-
const diffOutput = formatTaskDiffOutput(params.diffStat);
|
|
789
652
|
const taskChunks = tasks.map((task, index) => {
|
|
790
653
|
const chunk = {
|
|
791
654
|
type: "task_update",
|
|
792
655
|
id: task.id,
|
|
793
656
|
title: task.title,
|
|
794
|
-
status: task.status === "in_progress" ? params.finalInProgressStatus ??
|
|
657
|
+
status: task.status === "in_progress" ? params.finalInProgressStatus ?? task.status : task.status
|
|
795
658
|
};
|
|
796
|
-
if (task.details) chunk.details = task.details;
|
|
797
|
-
if (task.output) chunk.output = task.output;
|
|
798
|
-
if (index === finalTaskIndex && diffOutput) chunk.output = [task.output, diffOutput].filter(Boolean).join(" · ");
|
|
799
659
|
if (index === finalTaskIndex && params.sessionUrl) chunk.sources = buildSessionSources(params.sessionUrl);
|
|
800
660
|
return chunk;
|
|
801
661
|
});
|
|
@@ -804,113 +664,50 @@ function buildSlackProgressStreamChunks(params) {
|
|
|
804
664
|
title
|
|
805
665
|
}, ...taskChunks];
|
|
806
666
|
}
|
|
807
|
-
function formatDiffStat(diffStat) {
|
|
808
|
-
if (!diffStat || diffStat.files === 0 && diffStat.added === 0 && diffStat.removed === 0) return;
|
|
809
|
-
return [
|
|
810
|
-
`📝 ${diffStat.files} files`,
|
|
811
|
-
...diffStat.added > 0 ? [`+${diffStat.added}`] : [],
|
|
812
|
-
...diffStat.removed > 0 ? [`−${diffStat.removed}`] : []
|
|
813
|
-
].join(" ");
|
|
814
|
-
}
|
|
815
|
-
function formatTaskDiffOutput(diffStat) {
|
|
816
|
-
return diffStat && (diffStat.added > 0 || diffStat.removed > 0) ? `+${diffStat.added} −${diffStat.removed}` : void 0;
|
|
817
|
-
}
|
|
818
|
-
function buildActivityText(lines, maxLineChars) {
|
|
819
|
-
const rendered = [];
|
|
820
|
-
let length = 0;
|
|
821
|
-
for (const line of lines.slice(-50).toReversed()) {
|
|
822
|
-
const row = `${legacyLineTitle(line)} — ${legacyLineDetail(line, maxLineChars)}`;
|
|
823
|
-
const nextLength = length + row.length + (rendered.length > 0 ? 1 : 0);
|
|
824
|
-
if (nextLength > SLACK_PROGRESS_FIELD_MAX) break;
|
|
825
|
-
rendered.push(row);
|
|
826
|
-
length = nextLength;
|
|
827
|
-
}
|
|
828
|
-
return rendered.toReversed().join("\n");
|
|
829
|
-
}
|
|
830
667
|
function buildSlackProgressCardBlocks(params) {
|
|
831
668
|
const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS);
|
|
832
|
-
const
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
const narration = params.narration?.replace(/\s+/g, " ").trim();
|
|
837
|
-
const activityText = buildActivityText(params.lines, maxLineChars);
|
|
838
|
-
const diffStat = formatDiffStat(params.diffStat);
|
|
839
|
-
const workingFooter = [
|
|
840
|
-
...params.toolCalls && params.toolCalls > 0 ? [`🛠️ ${params.toolCalls} tools`] : [],
|
|
841
|
-
...diffStat ? [diffStat] : [],
|
|
842
|
-
...params.elapsedSeconds && params.elapsedSeconds > 0 ? [`⏱ ${params.elapsedSeconds}s`] : []
|
|
843
|
-
].join(" · ");
|
|
844
|
-
const footer = params.state === "working" ? workingFooter : diffStat;
|
|
845
|
-
return [
|
|
846
|
-
{
|
|
847
|
-
type: "section",
|
|
848
|
-
text: field(`${params.state === "working" ? "🔄" : params.state === "success" ? "✅" : "❌"} *${escapeSlackMrkdwn(params.title.trim() || "Working")}*`)
|
|
849
|
-
},
|
|
850
|
-
...narration ? [{
|
|
851
|
-
type: "section",
|
|
852
|
-
text: field(`_${escapeSlackMrkdwn(narration)}_`)
|
|
853
|
-
}] : [],
|
|
854
|
-
...planLines.length > 0 ? [{
|
|
855
|
-
type: "section",
|
|
856
|
-
text: field(planLines.map((line) => escapeSlackMrkdwn(line)).join("\n"))
|
|
857
|
-
}] : [],
|
|
858
|
-
...activityText ? [{
|
|
859
|
-
type: "section",
|
|
860
|
-
text: field(activityText)
|
|
861
|
-
}] : [],
|
|
862
|
-
...footer ? [{
|
|
863
|
-
type: "context",
|
|
864
|
-
elements: [field(footer)]
|
|
865
|
-
}] : [],
|
|
866
|
-
...params.state !== "working" && params.sessionUrl ? [{
|
|
867
|
-
type: "actions",
|
|
868
|
-
elements: [{
|
|
869
|
-
type: "button",
|
|
870
|
-
action_id: SLACK_SESSION_LINK_ACTION_ID,
|
|
871
|
-
text: {
|
|
872
|
-
type: "plain_text",
|
|
873
|
-
text: "Open in OpenClaw"
|
|
874
|
-
},
|
|
875
|
-
url: params.sessionUrl
|
|
876
|
-
}]
|
|
877
|
-
}] : []
|
|
878
|
-
].slice(0, 50);
|
|
879
|
-
}
|
|
880
|
-
const EMPTY_SLACK_NATIVE_STREAM_SNAPSHOT = { tasks: /* @__PURE__ */ new Map() };
|
|
881
|
-
const SLACK_TASK_FIELD_SEPARATOR = " · ";
|
|
882
|
-
function resolveTaskFieldDelta(previous, incoming) {
|
|
883
|
-
if (!incoming) return {
|
|
884
|
-
field: previous,
|
|
885
|
-
delta: void 0
|
|
669
|
+
const statusLabels = {
|
|
670
|
+
completed: "Completed",
|
|
671
|
+
in_progress: "In progress",
|
|
672
|
+
pending: "Pending"
|
|
886
673
|
};
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
674
|
+
const planLines = (params.plan ?? []).slice(-50).map((entry) => `${statusLabels[entry.status]}: ${compactDetail(entry.step, maxLineChars)}`);
|
|
675
|
+
const narration = params.narration?.replace(/\s+/g, " ").trim();
|
|
676
|
+
const authoredText = params.lines.filter(isAuthoredProgressLine).map((line) => line.text.trim()).filter((text, index, values) => text && text !== narration && values.indexOf(text) === index).map((text) => normalizeSlackOutboundText(compactDetail(text, maxLineChars))).join("\n");
|
|
677
|
+
const attention = resolveProgressAttention(params.lines);
|
|
678
|
+
const blocks = [
|
|
679
|
+
`${params.state === "success" ? "Completed: " : params.state === "error" ? "Failed: " : ""}*${escapeSlackMrkdwn(params.title.trim() || "Working")}*`,
|
|
680
|
+
narration ? `_${escapeSlackMrkdwn(narration)}_` : "",
|
|
681
|
+
planLines.map((line) => escapeSlackMrkdwn(line)).join("\n"),
|
|
682
|
+
authoredText,
|
|
683
|
+
attention && !(params.state !== "working" && attention.status === "pending") ? escapeSlackMrkdwn(attention.title) : ""
|
|
684
|
+
].filter(Boolean).map((text) => ({
|
|
685
|
+
type: "section",
|
|
686
|
+
text: {
|
|
687
|
+
type: "mrkdwn",
|
|
688
|
+
text: truncateSlackText(text, SLACK_PROGRESS_FIELD_MAX)
|
|
689
|
+
}
|
|
690
|
+
}));
|
|
691
|
+
if (params.state !== "working" && params.sessionUrl) blocks.push({
|
|
692
|
+
type: "actions",
|
|
693
|
+
elements: [{
|
|
694
|
+
type: "button",
|
|
695
|
+
action_id: SLACK_SESSION_LINK_ACTION_ID,
|
|
696
|
+
text: {
|
|
697
|
+
type: "plain_text",
|
|
698
|
+
text: "Open in OpenClaw"
|
|
699
|
+
},
|
|
700
|
+
url: params.sessionUrl
|
|
701
|
+
}]
|
|
899
702
|
});
|
|
900
|
-
|
|
901
|
-
return {
|
|
902
|
-
field: {
|
|
903
|
-
rendered: next.rendered,
|
|
904
|
-
source: next.source
|
|
905
|
-
},
|
|
906
|
-
delta
|
|
907
|
-
};
|
|
703
|
+
return blocks.slice(0, 50);
|
|
908
704
|
}
|
|
705
|
+
const EMPTY_SLACK_NATIVE_STREAM_SNAPSHOT = { tasks: /* @__PURE__ */ new Map() };
|
|
909
706
|
/**
|
|
910
707
|
* Turns a full task snapshot into the delta Slack must receive. Native streams
|
|
911
708
|
* key rows by persistent id with no removal chunk: unchanged rows are omitted,
|
|
912
|
-
* changed
|
|
913
|
-
* (plan shrinks,
|
|
709
|
+
* changed titles/statuses replace in place, and rows that dropped out
|
|
710
|
+
* (plan shrinks, summary <-> plan source switches) get a final complete
|
|
914
711
|
* update or they linger in_progress forever.
|
|
915
712
|
*/
|
|
916
713
|
function reconcileSlackNativeTaskChunks(params) {
|
|
@@ -931,26 +728,20 @@ function reconcileSlackNativeTaskChunks(params) {
|
|
|
931
728
|
}
|
|
932
729
|
const previousRow = params.previous.tasks.get(chunk.id);
|
|
933
730
|
const status = chunk.status;
|
|
934
|
-
const details = resolveTaskFieldDelta(previousRow?.details, chunk.details);
|
|
935
|
-
const output = resolveTaskFieldDelta(previousRow?.output, chunk.output);
|
|
936
731
|
const sourcesChanged = Boolean(chunk.sources) && !previousRow?.sourcesSent;
|
|
937
732
|
const row = {
|
|
938
733
|
title: chunk.title,
|
|
939
734
|
status
|
|
940
735
|
};
|
|
941
|
-
if (details.field) row.details = details.field;
|
|
942
|
-
if (output.field) row.output = output.field;
|
|
943
736
|
if (sourcesChanged || previousRow?.sourcesSent) row.sourcesSent = true;
|
|
944
737
|
nextTasks.set(chunk.id, row);
|
|
945
|
-
if (!(!previousRow || previousRow.title !== chunk.title || previousRow.status !== status ||
|
|
738
|
+
if (!(!previousRow || previousRow.title !== chunk.title || previousRow.status !== status || sourcesChanged)) continue;
|
|
946
739
|
const update = {
|
|
947
740
|
type: "task_update",
|
|
948
741
|
id: chunk.id,
|
|
949
742
|
title: chunk.title,
|
|
950
743
|
status
|
|
951
744
|
};
|
|
952
|
-
if (details.delta) update.details = details.delta;
|
|
953
|
-
if (output.delta) update.output = output.delta;
|
|
954
745
|
if (sourcesChanged) update.sources = chunk.sources;
|
|
955
746
|
emitted.push(update);
|
|
956
747
|
}
|
|
@@ -979,40 +770,63 @@ function reconcileSlackNativeTaskChunks(params) {
|
|
|
979
770
|
}
|
|
980
771
|
};
|
|
981
772
|
}
|
|
982
|
-
function buildSlackProgressStreamCompletionChunks(params) {
|
|
983
|
-
return buildSlackProgressStreamChunks({
|
|
984
|
-
...params,
|
|
985
|
-
completeInProgress: true
|
|
986
|
-
});
|
|
987
|
-
}
|
|
988
773
|
//#endregion
|
|
989
|
-
//#region extensions/slack/src/
|
|
990
|
-
function
|
|
991
|
-
return
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
774
|
+
//#region extensions/slack/src/stream-mode.ts
|
|
775
|
+
function resolveSlackStreamingConfig(params) {
|
|
776
|
+
return {
|
|
777
|
+
mode: resolveSlackStreamingMode(params),
|
|
778
|
+
nativeStreaming: resolveSlackNativeStreaming(params)
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
function applyAppendOnlyStreamUpdate(params) {
|
|
782
|
+
const incoming = params.incoming.trimEnd();
|
|
783
|
+
if (!incoming) return {
|
|
784
|
+
rendered: params.rendered,
|
|
785
|
+
source: params.source,
|
|
786
|
+
changed: false
|
|
787
|
+
};
|
|
788
|
+
if (!params.rendered) return {
|
|
789
|
+
rendered: incoming,
|
|
790
|
+
source: incoming,
|
|
791
|
+
changed: true
|
|
792
|
+
};
|
|
793
|
+
if (incoming === params.source) return {
|
|
794
|
+
rendered: params.rendered,
|
|
795
|
+
source: params.source,
|
|
796
|
+
changed: false
|
|
797
|
+
};
|
|
798
|
+
if (incoming.startsWith(params.rendered)) return {
|
|
799
|
+
rendered: incoming,
|
|
800
|
+
source: incoming,
|
|
801
|
+
changed: incoming !== params.rendered
|
|
802
|
+
};
|
|
803
|
+
if (incoming.startsWith(params.source)) {
|
|
804
|
+
const delta = incoming.slice(params.source.length);
|
|
995
805
|
return {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
label: reasoning ? "Reasoning" : "Update",
|
|
1000
|
-
prefix: false
|
|
806
|
+
rendered: `${params.rendered}${delta}`,
|
|
807
|
+
source: incoming,
|
|
808
|
+
changed: delta.length > 0
|
|
1001
809
|
};
|
|
1002
|
-
}
|
|
810
|
+
}
|
|
811
|
+
if (params.source.startsWith(incoming)) return {
|
|
812
|
+
rendered: params.rendered,
|
|
813
|
+
source: params.source,
|
|
814
|
+
changed: false
|
|
815
|
+
};
|
|
816
|
+
const separator = params.separator ?? (params.rendered.endsWith("\n") ? "" : "\n");
|
|
817
|
+
return {
|
|
818
|
+
rendered: `${params.rendered}${separator}${incoming}`,
|
|
819
|
+
source: incoming,
|
|
820
|
+
changed: true
|
|
821
|
+
};
|
|
1003
822
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
823
|
+
//#endregion
|
|
824
|
+
//#region extensions/slack/src/monitor/message-handler/dispatch-progress-render.ts
|
|
825
|
+
function resolveStructuredProgressLines(lines) {
|
|
826
|
+
return lines.filter((line) => typeof line !== "string");
|
|
1006
827
|
}
|
|
1007
828
|
function resolveNativeProgressLines(snapshot) {
|
|
1008
|
-
|
|
1009
|
-
if (snapshot.plan?.length || !snapshot.planExplanation) return lines;
|
|
1010
|
-
const explanationLine = buildChannelProgressDraftLine({
|
|
1011
|
-
event: "plan",
|
|
1012
|
-
phase: "update",
|
|
1013
|
-
explanation: snapshot.planExplanation
|
|
1014
|
-
});
|
|
1015
|
-
return explanationLine ? [...lines, explanationLine] : lines;
|
|
829
|
+
return resolveStructuredProgressLines(snapshot.lines).filter((line) => line.id !== "reasoning" && line.id?.startsWith("commentary:") !== true);
|
|
1016
830
|
}
|
|
1017
831
|
function resolveNativeProgressNarration(snapshot) {
|
|
1018
832
|
const paragraphs = resolveStructuredProgressLines(snapshot.lines).filter((line) => line.id === "reasoning" || line.id?.startsWith("commentary:") === true).map((line) => line.text.trim()).filter((text, index, values) => Boolean(text) && values.indexOf(text) === index);
|
|
@@ -1021,14 +835,6 @@ function resolveNativeProgressNarration(snapshot) {
|
|
|
1021
835
|
function combineProgressHeadlineAndExplanation(headline, explanation) {
|
|
1022
836
|
return headline && explanation && headline !== explanation ? `${headline} — ${explanation}` : headline ?? explanation;
|
|
1023
837
|
}
|
|
1024
|
-
function buildNativeProgressChunks(params) {
|
|
1025
|
-
return buildSlackProgressStreamChunks({
|
|
1026
|
-
title: params.title,
|
|
1027
|
-
lines: resolveNativeProgressLines(params.snapshot),
|
|
1028
|
-
plan: resolveNativeProgressPlan(params.snapshot),
|
|
1029
|
-
maxLineChars: params.maxLineChars
|
|
1030
|
-
});
|
|
1031
|
-
}
|
|
1032
838
|
//#endregion
|
|
1033
839
|
//#region extensions/slack/src/monitor/message-handler/preview-finalize.ts
|
|
1034
840
|
function buildExpectedSlackEditText(params) {
|
|
@@ -1066,7 +872,8 @@ function blocksMatch(expected, actual) {
|
|
|
1066
872
|
const fallbackBlocks = fallbackPlan.fallbackMessages.flatMap((message) => message.blocks ?? []);
|
|
1067
873
|
if (JSON.stringify(fallbackBlocks) === JSON.stringify(actual)) return true;
|
|
1068
874
|
const fallbackText = fallbackPlan.fallbackMessages.map((message) => message.text).filter(Boolean).join("\n\n");
|
|
1069
|
-
|
|
875
|
+
const actualText = buildSlackBlocksFallbackText(actual);
|
|
876
|
+
return normalizeSlackOutboundText(actualText) === normalizeSlackOutboundText(fallbackText);
|
|
1070
877
|
} catch {
|
|
1071
878
|
return false;
|
|
1072
879
|
}
|
|
@@ -1157,6 +964,7 @@ function createSlackDraftProgressCardRuntime(params) {
|
|
|
1157
964
|
return url.toString();
|
|
1158
965
|
};
|
|
1159
966
|
const resolveText = (snapshot) => latestFallbackText || formatChannelProgressDraftText({
|
|
967
|
+
presentation: "summary",
|
|
1160
968
|
entry: account.config,
|
|
1161
969
|
lines: [...snapshot.lines],
|
|
1162
970
|
seed: params.progressSeed,
|
|
@@ -1173,11 +981,7 @@ function createSlackDraftProgressCardRuntime(params) {
|
|
|
1173
981
|
plan: snapshot.plan,
|
|
1174
982
|
lines: resolveStructuredProgressLines(snapshot.lines),
|
|
1175
983
|
maxLineChars: params.maxLineChars,
|
|
1176
|
-
|
|
1177
|
-
...state === "working" ? {
|
|
1178
|
-
toolCalls: params.progressWorkCounter.toolCalls,
|
|
1179
|
-
elapsedSeconds: params.progressWorkCounter.elapsedSeconds
|
|
1180
|
-
} : { sessionUrl: resolveSessionUrl() }
|
|
984
|
+
...state !== "working" ? { sessionUrl: resolveSessionUrl() } : {}
|
|
1181
985
|
});
|
|
1182
986
|
};
|
|
1183
987
|
const finalize = async (status, snapshot = params.getSnapshot(), fallbackText = resolveText(snapshot)) => {
|
|
@@ -1363,7 +1167,6 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1363
1167
|
nativeStreamOrder = run.catch(() => void 0);
|
|
1364
1168
|
return run;
|
|
1365
1169
|
};
|
|
1366
|
-
const progressWorkCounter = createChannelProgressWorkCounter();
|
|
1367
1170
|
const progressSeed = `${account.accountId}:${message.channel}`;
|
|
1368
1171
|
const slackProgressStyle = resolveSlackProgressStyle(account.config);
|
|
1369
1172
|
const useDraftProgressCard = Boolean(draftStream) && isProgressMode && slackProgressStyle === "card";
|
|
@@ -1379,7 +1182,6 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1379
1182
|
},
|
|
1380
1183
|
draftStream,
|
|
1381
1184
|
enabled: useDraftProgressCard,
|
|
1382
|
-
progressWorkCounter,
|
|
1383
1185
|
progressSeed,
|
|
1384
1186
|
explicitTitle: explicitProgressTitle,
|
|
1385
1187
|
maxLineChars: progressDraftMaxLineChars,
|
|
@@ -1399,12 +1201,17 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1399
1201
|
if (isError) nativeProgressTerminalStatus = "error";
|
|
1400
1202
|
if (!session || nativeProgressCompletionSent) return;
|
|
1401
1203
|
const chunks = buildNativeProgressCompletionChunks(isError ? "error" : "complete");
|
|
1402
|
-
|
|
1204
|
+
const narrationUpdate = resolveNarrationUpdate(resolveNativeProgressNarration(progressDraft.getSnapshot()));
|
|
1205
|
+
if (!chunks?.length && !narrationUpdate.delta) return;
|
|
1403
1206
|
try {
|
|
1404
1207
|
await appendSlackStream({
|
|
1405
1208
|
session,
|
|
1406
1209
|
chunks
|
|
1407
1210
|
});
|
|
1211
|
+
if (narrationUpdate.next.changed) {
|
|
1212
|
+
nativeNarrationRenderedText = narrationUpdate.next.rendered;
|
|
1213
|
+
nativeNarrationSourceText = narrationUpdate.next.source;
|
|
1214
|
+
}
|
|
1408
1215
|
nativeProgressCompletionSent = true;
|
|
1409
1216
|
delivery.observedReplyDelivery ||= session.delivered;
|
|
1410
1217
|
} catch (err) {
|
|
@@ -1413,11 +1220,6 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1413
1220
|
}
|
|
1414
1221
|
};
|
|
1415
1222
|
const resolveNativeProgressTitle = (snapshot) => combineProgressHeadlineAndExplanation(explicitProgressTitle ?? snapshot.statusHeadline, snapshot.planExplanation);
|
|
1416
|
-
const buildNativeProgressChunks$1 = (snapshot) => buildNativeProgressChunks({
|
|
1417
|
-
snapshot,
|
|
1418
|
-
title: resolveNativeProgressTitle(snapshot),
|
|
1419
|
-
maxLineChars: progressDraftMaxLineChars
|
|
1420
|
-
});
|
|
1421
1223
|
const normalizeProgressText = (text) => text?.replace(/\s+/gu, " ").trim() ?? "";
|
|
1422
1224
|
const isRenderedAsProgressTitle = (text) => {
|
|
1423
1225
|
const candidate = normalizeProgressText(text);
|
|
@@ -1436,17 +1238,18 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1436
1238
|
delta: next.changed ? next.rendered.slice(nativeNarrationRenderedText.length) : ""
|
|
1437
1239
|
};
|
|
1438
1240
|
};
|
|
1439
|
-
const updateNativeProgressStream = () => withNativeStreamOrder(updateNativeProgressStreamNow);
|
|
1440
1241
|
const updateNativeProgressStreamNow = async () => {
|
|
1441
1242
|
const snapshot = progressDraft.getSnapshot();
|
|
1442
|
-
const progressLines = resolveNativeProgressLines(snapshot);
|
|
1443
1243
|
const narrationUpdate = resolveNarrationUpdate(resolveNativeProgressNarration(snapshot));
|
|
1444
|
-
|
|
1445
|
-
if (!useNativeProgressStreaming || delivery.streamFailed || progressLines.length === 0 && !snapshot.plan?.length && !snapshot.statusHeadline && !explicitProgressTitle && !hasRetirableNativeTasks && !narrationUpdate.delta) return false;
|
|
1244
|
+
if (!useNativeProgressStreaming || delivery.streamFailed || nativeUpdatesStopped) return false;
|
|
1446
1245
|
if (!await nativeTransport.waitForStart()) return false;
|
|
1447
1246
|
const reconciled = reconcileSlackNativeTaskChunks({
|
|
1448
1247
|
previous: nativeStreamSnapshot,
|
|
1449
|
-
chunks:
|
|
1248
|
+
chunks: buildSlackProgressStreamChunks({
|
|
1249
|
+
title: resolveNativeProgressTitle(snapshot) ?? "Working",
|
|
1250
|
+
lines: resolveNativeProgressLines(snapshot),
|
|
1251
|
+
plan: snapshot.plan
|
|
1252
|
+
})
|
|
1450
1253
|
});
|
|
1451
1254
|
const chunks = reconciled.chunks;
|
|
1452
1255
|
if (!chunks?.length && !narrationUpdate.delta) return false;
|
|
@@ -1470,6 +1273,21 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1470
1273
|
return false;
|
|
1471
1274
|
}
|
|
1472
1275
|
};
|
|
1276
|
+
let nativeUpdatesStopped = false;
|
|
1277
|
+
const nativeUpdates = createDraftStreamLoop({
|
|
1278
|
+
throttleMs: 1e3,
|
|
1279
|
+
coalesceInFlight: true,
|
|
1280
|
+
emptyValue: false,
|
|
1281
|
+
isEmpty: (pending) => !pending,
|
|
1282
|
+
isStopped: () => nativeUpdatesStopped,
|
|
1283
|
+
sendOrEditStreamMessage: () => withNativeStreamOrder(updateNativeProgressStreamNow),
|
|
1284
|
+
onBackgroundFlushError: (err) => runtime.error?.(danger(`slack-stream: progress update failed: ${formatSlackError(err)}`))
|
|
1285
|
+
});
|
|
1286
|
+
const cancelNativeUpdates = async () => {
|
|
1287
|
+
nativeUpdatesStopped = true;
|
|
1288
|
+
nativeUpdates.stop();
|
|
1289
|
+
await nativeUpdates.waitForInFlight();
|
|
1290
|
+
};
|
|
1473
1291
|
const appendNativeNarration = (payload, kind) => withNativeStreamOrder(() => appendNativeNarrationNow(payload, kind));
|
|
1474
1292
|
const appendNativeNarrationNow = async (payload, kind) => {
|
|
1475
1293
|
if (isRenderedAsProgressTitle(payload.text)) return false;
|
|
@@ -1489,24 +1307,28 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1489
1307
|
return true;
|
|
1490
1308
|
};
|
|
1491
1309
|
const resetProgressTurnState = () => {
|
|
1492
|
-
progressWorkCounter.reset();
|
|
1493
1310
|
nativeNarrationRenderedText = "";
|
|
1494
1311
|
nativeNarrationSourceText = "";
|
|
1495
1312
|
};
|
|
1496
1313
|
const progressDraft = createChannelProgressDraftCompositor({
|
|
1314
|
+
presentation: isProgressMode && slackProgressStyle === "card" ? "summary" : void 0,
|
|
1497
1315
|
entry: account.config,
|
|
1498
1316
|
mode: slackStreaming.mode,
|
|
1499
1317
|
active: progressDraftActive,
|
|
1500
1318
|
seed: progressSeed,
|
|
1501
1319
|
formatLine: formatSlackProgressDraftLine,
|
|
1502
1320
|
reasoningLinePrefix: "🧠 ",
|
|
1503
|
-
commentaryLinePrefix: "",
|
|
1504
1321
|
reasoningGate: previewToolProgressEnabled,
|
|
1505
|
-
commentaryItalics: true,
|
|
1506
|
-
buildProgressEventLine: (input, options) => input.event === "tool" || input.event === "item" || input.event === "command-output" ? buildChannelProgressDraftLineForEntry(account.config, input, options) : buildChannelProgressDraftLine(input, options),
|
|
1507
1322
|
updateOnLineChange: useNativeProgressStreaming || useDraftProgressCard,
|
|
1508
1323
|
update: async (previewText, options) => {
|
|
1509
|
-
if (useNativeProgressStreaming)
|
|
1324
|
+
if (useNativeProgressStreaming) {
|
|
1325
|
+
const priorSnapshot = nativeStreamSnapshot;
|
|
1326
|
+
const priorNarration = nativeNarrationRenderedText;
|
|
1327
|
+
nativeUpdates.update(true);
|
|
1328
|
+
if (options?.flush) await nativeUpdates.flush();
|
|
1329
|
+
else await nativeUpdates.waitForInFlight();
|
|
1330
|
+
return priorSnapshot !== nativeStreamSnapshot || priorNarration !== nativeNarrationRenderedText;
|
|
1331
|
+
}
|
|
1510
1332
|
if (!draftStream) return false;
|
|
1511
1333
|
const snapshot = progressDraft.getSnapshot();
|
|
1512
1334
|
progressCard.setFallbackText(previewText);
|
|
@@ -1519,7 +1341,11 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1519
1341
|
}
|
|
1520
1342
|
});
|
|
1521
1343
|
const commentaryProgressEnabled = progressDraft.commentaryProgressEnabled;
|
|
1522
|
-
const deliverNativeFinal = (payload, kind) =>
|
|
1344
|
+
const deliverNativeFinal = async (payload, kind) => {
|
|
1345
|
+
progressDraft.markFinalReplyStarted();
|
|
1346
|
+
await cancelNativeUpdates();
|
|
1347
|
+
await withNativeStreamOrder(() => deliverNativeFinalNow(payload, kind));
|
|
1348
|
+
};
|
|
1523
1349
|
const deliverNativeFinalNow = async (payload, kind) => {
|
|
1524
1350
|
progressDraft.markFinalReplyStarted();
|
|
1525
1351
|
const streamReady = await nativeTransport.waitForStart();
|
|
@@ -1544,20 +1370,23 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1544
1370
|
const snapshot = progressDraft.getSnapshot();
|
|
1545
1371
|
const lines = resolveNativeProgressLines(snapshot);
|
|
1546
1372
|
const sessionUrl = progressCard.resolveSessionUrl();
|
|
1373
|
+
const narrationUpdate = resolveNarrationUpdate(resolveNativeProgressNarration(snapshot));
|
|
1547
1374
|
const hasRetirableNativeTasks = [...nativeStreamSnapshot.tasks.values()].some((task) => task.status !== "complete" && task.status !== "error");
|
|
1548
|
-
if (lines.length === 0 && !snapshot.plan?.length && !hasRetirableNativeTasks && !
|
|
1549
|
-
|
|
1375
|
+
if (lines.length === 0 && !snapshot.plan?.length && !hasRetirableNativeTasks && !narrationUpdate.delta && !sessionUrl) return;
|
|
1376
|
+
const completion = reconcileSlackNativeTaskChunks({
|
|
1550
1377
|
previous: nativeStreamSnapshot,
|
|
1551
|
-
chunks:
|
|
1378
|
+
chunks: buildSlackProgressStreamChunks({
|
|
1552
1379
|
title: resolveNativeProgressTitle(snapshot) ?? (lines.length === 0 && !snapshot.plan?.length ? "Working" : void 0),
|
|
1553
1380
|
lines,
|
|
1554
|
-
plan:
|
|
1555
|
-
maxLineChars: progressDraftMaxLineChars,
|
|
1381
|
+
plan: snapshot.plan,
|
|
1556
1382
|
finalInProgressStatus,
|
|
1557
|
-
diffStat: snapshot.diffStat,
|
|
1558
1383
|
sessionUrl
|
|
1559
1384
|
})
|
|
1560
1385
|
}).chunks;
|
|
1386
|
+
return narrationUpdate.delta ? [{
|
|
1387
|
+
type: "markdown_text",
|
|
1388
|
+
text: narrationUpdate.delta
|
|
1389
|
+
}, ...completion ?? []] : completion;
|
|
1561
1390
|
};
|
|
1562
1391
|
const finishNativeProgressTurn = async (completionChunks) => {
|
|
1563
1392
|
if (delivery.nativeProgressStreamStartPromise) await delivery.nativeProgressStreamStartPromise.catch(() => null);
|
|
@@ -1582,6 +1411,7 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1582
1411
|
delivery.streamFailed = false;
|
|
1583
1412
|
};
|
|
1584
1413
|
const pushPlanProgress = async (steps, explanation) => {
|
|
1414
|
+
if (!previewToolProgressEnabled) return false;
|
|
1585
1415
|
if (isProgressMode) {
|
|
1586
1416
|
if (slackProgressStyle === "compact") return false;
|
|
1587
1417
|
return await progressDraft.pushPlanProgress(steps, { explanation });
|
|
@@ -1660,6 +1490,10 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1660
1490
|
progressDraft.reset();
|
|
1661
1491
|
};
|
|
1662
1492
|
const beginNewProgressTurn = async (options) => {
|
|
1493
|
+
if (useNativeProgressStreaming) {
|
|
1494
|
+
if (!nativeUpdatesStopped && options?.force !== true) return false;
|
|
1495
|
+
await cancelNativeUpdates();
|
|
1496
|
+
}
|
|
1663
1497
|
const priorSnapshot = progressDraft.getSnapshot();
|
|
1664
1498
|
const priorFallbackText = progressCard.resolveText(priorSnapshot);
|
|
1665
1499
|
const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent ? buildNativeProgressCompletionChunks(nativeProgressTerminalStatus) : void 0;
|
|
@@ -1674,6 +1508,8 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1674
1508
|
nativeStreamSnapshot = EMPTY_SLACK_NATIVE_STREAM_SNAPSHOT;
|
|
1675
1509
|
nativeProgressCompletionSent = false;
|
|
1676
1510
|
nativeProgressTerminalStatus = "complete";
|
|
1511
|
+
nativeUpdatesStopped = false;
|
|
1512
|
+
nativeUpdates.resetThrottleWindow();
|
|
1677
1513
|
progressCard.reset();
|
|
1678
1514
|
resetPreviewDeliveryState();
|
|
1679
1515
|
delivery.resetDeliveryTracker();
|
|
@@ -1697,7 +1533,14 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1697
1533
|
resetDraftDeliveryState();
|
|
1698
1534
|
resetDraftProgressState();
|
|
1699
1535
|
};
|
|
1700
|
-
const onQueuedFollowupSettled = !useDraftProgressCard ? void 0 : async () => {
|
|
1536
|
+
const onQueuedFollowupSettled = !useDraftProgressCard && !useNativeProgressStreaming ? void 0 : async () => {
|
|
1537
|
+
if (useNativeProgressStreaming) {
|
|
1538
|
+
progressDraft.markFinalReplyStarted();
|
|
1539
|
+
await cancelNativeUpdates();
|
|
1540
|
+
await finishNativeProgressTurn(nativeProgressCompletionSent ? void 0 : buildNativeProgressCompletionChunks(nativeProgressTerminalStatus));
|
|
1541
|
+
progressDraft.markFinalReplyDelivered();
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1701
1544
|
if (!progressCard.hasTerminalized) await draftStream?.clear();
|
|
1702
1545
|
await dropDetachedProgressCards();
|
|
1703
1546
|
};
|
|
@@ -1711,7 +1554,10 @@ function createSlackProgressRuntime(runtimeParams) {
|
|
|
1711
1554
|
suppressDefaultToolProgressMessages,
|
|
1712
1555
|
progressDraft,
|
|
1713
1556
|
commentaryProgressEnabled,
|
|
1714
|
-
|
|
1557
|
+
async cancel() {
|
|
1558
|
+
progressDraft.cancel();
|
|
1559
|
+
await cancelNativeUpdates();
|
|
1560
|
+
},
|
|
1715
1561
|
get nativeProgressCompletionSent() {
|
|
1716
1562
|
return nativeProgressCompletionSent;
|
|
1717
1563
|
},
|
|
@@ -1816,24 +1662,17 @@ async function createSlackDispatchSetup(prepared) {
|
|
|
1816
1662
|
await reactSlackMessage(message.channel, reactionMessageTs ?? "", emoji, {
|
|
1817
1663
|
token: ctx.botToken,
|
|
1818
1664
|
client: slackClient
|
|
1819
|
-
}).catch((err) => {
|
|
1820
|
-
if (formatErrorMessage(err).includes("already_reacted")) return;
|
|
1821
|
-
throw err;
|
|
1822
1665
|
});
|
|
1823
1666
|
},
|
|
1824
1667
|
removeReaction: async (emoji) => {
|
|
1825
1668
|
await removeSlackReaction(message.channel, reactionMessageTs ?? "", emoji, {
|
|
1826
1669
|
token: ctx.botToken,
|
|
1827
1670
|
client: slackClient
|
|
1828
|
-
}).catch((err) => {
|
|
1829
|
-
if (formatErrorMessage(err).includes("no_reaction")) return;
|
|
1830
|
-
throw err;
|
|
1831
1671
|
});
|
|
1832
1672
|
}
|
|
1833
1673
|
},
|
|
1834
1674
|
initialEmoji: prepared.ackReactionValue || "eyes",
|
|
1835
|
-
|
|
1836
|
-
timing: DEFAULT_TIMING,
|
|
1675
|
+
presentation: "acknowledgement",
|
|
1837
1676
|
onError: (err) => {
|
|
1838
1677
|
logAckFailure({
|
|
1839
1678
|
log: logVerbose,
|
|
@@ -2174,10 +2013,11 @@ function createSlackStreamingDeliveryRuntime(setup) {
|
|
|
2174
2013
|
});
|
|
2175
2014
|
state.observedReplyDelivery = true;
|
|
2176
2015
|
if (params.kind === "final") state.observedFinalReplyDelivery = true;
|
|
2177
|
-
const deliveredThreadTs =
|
|
2016
|
+
const deliveredThreadTs = resolveSlackReplyThreadTs({
|
|
2178
2017
|
replyToMode: replyDeliveryMode,
|
|
2179
|
-
|
|
2180
|
-
|
|
2018
|
+
replyToId: params.payload.replyToId,
|
|
2019
|
+
threadId: deliveryReplyThreadTs,
|
|
2020
|
+
replyToCurrent: params.payload.replyToCurrent
|
|
2181
2021
|
});
|
|
2182
2022
|
rememberDeliveredThreadTs(params.kind, deliveredThreadTs);
|
|
2183
2023
|
replyPlan.markSent();
|
|
@@ -2219,6 +2059,7 @@ function createSlackStreamingDeliveryRuntime(setup) {
|
|
|
2219
2059
|
}
|
|
2220
2060
|
const text = params.streamText ?? reply.trimmedText;
|
|
2221
2061
|
const hookContent = reply.trimmedText;
|
|
2062
|
+
const chunks = params.kind === "final" ? [] : void 0;
|
|
2222
2063
|
let plannedThreadTs;
|
|
2223
2064
|
try {
|
|
2224
2065
|
if (!state.streamSession && state.nativeProgressStreamStartPromise) await state.nativeProgressStreamStartPromise;
|
|
@@ -2256,6 +2097,7 @@ function createSlackStreamingDeliveryRuntime(setup) {
|
|
|
2256
2097
|
channel: message.channel,
|
|
2257
2098
|
threadTs: streamThreadTs,
|
|
2258
2099
|
text,
|
|
2100
|
+
chunks,
|
|
2259
2101
|
...params.taskDisplayMode ? { taskDisplayMode: params.taskDisplayMode } : {},
|
|
2260
2102
|
...slackIdentity ? { identity: slackIdentity } : {},
|
|
2261
2103
|
teamId: await resolveSlackStreamRecipientTeamId({
|
|
@@ -2294,7 +2136,8 @@ function createSlackStreamingDeliveryRuntime(setup) {
|
|
|
2294
2136
|
if (hookContent) recordStreamedDelivery(params.kind, hookContent);
|
|
2295
2137
|
await appendSlackStream({
|
|
2296
2138
|
session: state.streamSession,
|
|
2297
|
-
text: `${params.appendSeparator === false ? "" : "\n"}${text}
|
|
2139
|
+
text: `${params.appendSeparator === false ? "" : "\n"}${text}`,
|
|
2140
|
+
chunks
|
|
2298
2141
|
});
|
|
2299
2142
|
refreshStreamedAcknowledgements(state.streamSession);
|
|
2300
2143
|
if (state.streamSession.delivered && state.streamSession.pendingText.length === 0) {
|
|
@@ -2459,7 +2302,11 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
2459
2302
|
const slackBlocks = replyRenderPlan.mode === "single" ? replyRenderPlan.blocks : replyRenderPlan.blockPart?.blocks;
|
|
2460
2303
|
const requiresSeparateFallbackDelivery = replyRenderPlan.mode === "split";
|
|
2461
2304
|
const trimmedFinalText = replyRenderPlan.mode === "single" ? replyRenderPlan.text.trim() : replyRenderPlan.fallbackText.trim();
|
|
2462
|
-
const previewFinalText = replyRenderPlan.mode === "single" && replyRenderPlan.textIsSlackMrkdwn ? trimmedFinalText : normalizeSlackOutboundText((replySourceText ?? "").trim()
|
|
2305
|
+
const previewFinalText = replyRenderPlan.mode === "single" && replyRenderPlan.textIsSlackMrkdwn ? trimmedFinalText : normalizeSlackOutboundText((replySourceText ?? "").trim(), { tableMode: resolveMarkdownTableMode({
|
|
2306
|
+
cfg,
|
|
2307
|
+
channel: "slack",
|
|
2308
|
+
accountId: account.accountId
|
|
2309
|
+
}) });
|
|
2463
2310
|
const previewFinalTextFitsEdit = countSlackTextUtf8Bytes(previewFinalText) <= SLACK_EDIT_TEXT_MAX_BYTES;
|
|
2464
2311
|
const shouldRestoreTtsSupplementTextForPreviewFallback = Boolean(ttsSupplement) && ttsSupplement?.visibleTextAlreadyDelivered !== true && Boolean(draftStream) && !draftPreviewCommitted.value && !delivery.observedFinalReplyDelivery && previewStreamingEnabled && !payload.text?.trim();
|
|
2465
2312
|
let ttsPreviewFinalization;
|
|
@@ -2630,10 +2477,10 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
2630
2477
|
} : void 0,
|
|
2631
2478
|
onToolStart: async (payload) => {
|
|
2632
2479
|
if (statusReactionsEnabled) await statusReactions.setTool(payload.name);
|
|
2633
|
-
if (payload.phase === "start") progress.progressWorkCounter.noteToolCall(payload.name);
|
|
2634
2480
|
return await progress.progressDraft.pushToolEvent(payload);
|
|
2635
2481
|
},
|
|
2636
2482
|
onItemEvent: async (payload) => {
|
|
2483
|
+
if (payload.kind === "preamble" && (payload.phase === "start" || payload.phase === "update") && !draftStream?.messageId() && !delivery.streamSession?.delivered) return false;
|
|
2637
2484
|
if (progress.isProgressMode && payload.kind === "preamble") {
|
|
2638
2485
|
if (progress.shouldYieldDraftProgress()) return false;
|
|
2639
2486
|
const headlineVisible = await progress.progressDraft.pushPreambleHeadline(payload.progressText, { itemId: payload.itemId });
|
|
@@ -2672,7 +2519,7 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
2672
2519
|
} catch (err) {
|
|
2673
2520
|
dispatchError = err;
|
|
2674
2521
|
} finally {
|
|
2675
|
-
progress.
|
|
2522
|
+
await progress.cancel();
|
|
2676
2523
|
if (!progress.useDraftProgressCard) await draftStream?.discardPending();
|
|
2677
2524
|
}
|
|
2678
2525
|
let streamFallbackDelivered = false;
|
|
@@ -2705,13 +2552,15 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
2705
2552
|
if (pendingFailureNotice && anyReplyDelivered) recordSlackThreadFailureNotice(pendingFailureNotice);
|
|
2706
2553
|
if (dispatchError || agentRunFailed) await progress.finalizeDraftProgressCard("error");
|
|
2707
2554
|
await progress.dropDetachedProgressCards();
|
|
2708
|
-
if (statusReactionsEnabled)
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2555
|
+
if (statusReactionsEnabled) {
|
|
2556
|
+
if (dispatchError || agentRunFailed) {
|
|
2557
|
+
await statusReactions.setError();
|
|
2558
|
+
statusReactions.restoreInitial();
|
|
2559
|
+
} else if (anyReplyDelivered) {
|
|
2560
|
+
await statusReactions.setDone();
|
|
2561
|
+
statusReactions.restoreInitial();
|
|
2562
|
+
} else await statusReactions.restoreInitial();
|
|
2563
|
+
}
|
|
2715
2564
|
const participationThreadTs = delivery.usedReplyThreadTs ?? statusThreadTs;
|
|
2716
2565
|
if (anyReplyDelivered && participationThreadTs) recordSlackThreadParticipation(account.accountId, message.channel, participationThreadTs, {
|
|
2717
2566
|
agentId: route.agentId,
|
|
@@ -2770,7 +2619,7 @@ function normalizeSlackAppContextEntities(value) {
|
|
|
2770
2619
|
//#endregion
|
|
2771
2620
|
//#region extensions/slack/src/monitor/thread.ts
|
|
2772
2621
|
const THREAD_STARTER_CACHE = /* @__PURE__ */ new Map();
|
|
2773
|
-
const THREAD_STARTER_CACHE_TTL_MS =
|
|
2622
|
+
const THREAD_STARTER_CACHE_TTL_MS = 216e5;
|
|
2774
2623
|
const THREAD_STARTER_CACHE_MAX = 2e3;
|
|
2775
2624
|
function evictThreadStarterCache() {
|
|
2776
2625
|
const now = asDateTimestampMs(Date.now());
|
|
@@ -2965,7 +2814,24 @@ async function discardSlackPreflightMedia(media) {
|
|
|
2965
2814
|
const SLACK_MENTION_RESOLUTION_CONCURRENCY = 4;
|
|
2966
2815
|
const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
|
|
2967
2816
|
const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
|
|
2968
|
-
const
|
|
2817
|
+
const MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS = 2e3;
|
|
2818
|
+
const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-fh66A6lc.js").then((n) => n._));
|
|
2819
|
+
function formatSlackUnavailableMedia(params) {
|
|
2820
|
+
let fileReferences = params.files?.map((file) => `${formatSlackFileReference(file)} unavailable (${file.reason})`).join(", ");
|
|
2821
|
+
if (fileReferences && fileReferences.length > MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS) fileReferences = `${truncateUtf16Safe(fileReferences, MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS)}; … (file references truncated)`;
|
|
2822
|
+
const fileBlock = fileReferences ? `[Slack file: ${fileReferences}]` : void 0;
|
|
2823
|
+
const body = [params.body, fileBlock].filter(Boolean).join("\n");
|
|
2824
|
+
if (params.unavailableMediaCount === 0) return body;
|
|
2825
|
+
const notice = `[slack ${params.unavailableMediaCount > 1 ? `${params.unavailableMediaCount} attachments` : "attachment"} unavailable]`;
|
|
2826
|
+
return params.prependUnavailable ? [
|
|
2827
|
+
notice,
|
|
2828
|
+
fileBlock,
|
|
2829
|
+
params.body
|
|
2830
|
+
].filter(Boolean).join("\n") : formatInboundMediaUnavailableText({
|
|
2831
|
+
body,
|
|
2832
|
+
notice
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2969
2835
|
function collectUniqueSlackMentionIds$1(texts) {
|
|
2970
2836
|
const seen = /* @__PURE__ */ new Set();
|
|
2971
2837
|
const mentionIds = [];
|
|
@@ -3016,7 +2882,6 @@ async function resolveSlackMessageContent(params) {
|
|
|
3016
2882
|
})) : null;
|
|
3017
2883
|
const effectiveDirectMedia = attachmentContent?.media.length ? attachmentContent.media : null;
|
|
3018
2884
|
const mediaPlaceholder = effectiveDirectMedia?.map((item) => item.placeholder).join(" ");
|
|
3019
|
-
const fileOnlyFallback = attachmentContent?.files?.map(formatSlackFileReference).join(", ");
|
|
3020
2885
|
let botAttachmentText;
|
|
3021
2886
|
if (params.isBotMessage && !attachmentContent?.text) {
|
|
3022
2887
|
const botAttachmentTextParts = [];
|
|
@@ -3040,7 +2905,8 @@ async function resolveSlackMessageContent(params) {
|
|
|
3040
2905
|
if (skippedLookups > 0) logVerbose(`slack: skipping ${skippedLookups} mention lookup(s) beyond per-message cap (${SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE})`);
|
|
3041
2906
|
const { results } = await runTasksWithConcurrency({
|
|
3042
2907
|
tasks: lookupIds.map((userId) => async () => {
|
|
3043
|
-
const
|
|
2908
|
+
const user = await resolveUserName(userId);
|
|
2909
|
+
const renderedName = normalizeOptionalString(user?.name);
|
|
3044
2910
|
return {
|
|
3045
2911
|
userId,
|
|
3046
2912
|
rendered: renderedName ? `<@${userId}> (${renderedName})` : null
|
|
@@ -3053,17 +2919,15 @@ async function resolveSlackMessageContent(params) {
|
|
|
3053
2919
|
renderedMentions.set(result.userId, result.rendered);
|
|
3054
2920
|
}
|
|
3055
2921
|
}
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
body: rawBody,
|
|
3066
|
-
notice: `[slack ${unavailableImageCount > 1 ? `${unavailableImageCount} forwarded images` : "forwarded image"} unavailable]`
|
|
2922
|
+
const rawBody = formatSlackUnavailableMedia({
|
|
2923
|
+
body: [
|
|
2924
|
+
renderSlackUserMentions(textParts[0], renderedMentions),
|
|
2925
|
+
renderSlackUserMentions(textParts[1], renderedMentions),
|
|
2926
|
+
renderSlackUserMentions(textParts[2], renderedMentions),
|
|
2927
|
+
mediaPlaceholder
|
|
2928
|
+
].filter(Boolean).join("\n") || "",
|
|
2929
|
+
files: attachmentContent?.files,
|
|
2930
|
+
unavailableMediaCount: attachmentContent?.unavailableMediaCount ?? 0
|
|
3067
2931
|
});
|
|
3068
2932
|
return rawBody ? {
|
|
3069
2933
|
rawBody,
|
|
@@ -3145,7 +3009,8 @@ async function resolveSlackDmHistoryContext(params) {
|
|
|
3145
3009
|
//#endregion
|
|
3146
3010
|
//#region extensions/slack/src/monitor/message-handler/prepare-routing.ts
|
|
3147
3011
|
function resolveSlackBaseConversationId(params) {
|
|
3148
|
-
|
|
3012
|
+
const raw = params.isDirectMessage ? `user:${params.message.user ?? "unknown"}` : params.message.channel;
|
|
3013
|
+
return qualifySlackConversationId(raw, params.eventScope);
|
|
3149
3014
|
}
|
|
3150
3015
|
function resolveSlackInitialAgentRoute(params) {
|
|
3151
3016
|
const route = resolveAgentRoute({
|
|
@@ -3296,7 +3161,7 @@ function formatSlackThreadLabelSnippet(text) {
|
|
|
3296
3161
|
}
|
|
3297
3162
|
//#endregion
|
|
3298
3163
|
//#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
|
|
3299
|
-
const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-
|
|
3164
|
+
const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-fh66A6lc.js").then((n) => n._));
|
|
3300
3165
|
const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
|
|
3301
3166
|
function resolveSlackThreadSessionFreshness(params) {
|
|
3302
3167
|
return params.ctx.channelRuntime?.session?.resolveEntryResetFreshness?.({
|
|
@@ -3408,14 +3273,24 @@ async function resolveSlackThreadContextData(params) {
|
|
|
3408
3273
|
const snippet = formatSlackThreadLabelSnippet(starter.text);
|
|
3409
3274
|
threadLabel = `Slack thread ${params.roomLabel}${snippet ? `: ${snippet}` : ""}`;
|
|
3410
3275
|
if (shouldSeedInitialThreadContext && !params.effectiveDirectMedia && starter.files && starter.files.length > 0) {
|
|
3411
|
-
const {
|
|
3412
|
-
|
|
3276
|
+
const { resolveSlackAttachmentContent } = await loadSlackMediaModule();
|
|
3277
|
+
const attachmentContent = await resolveSlackAttachmentContent({
|
|
3413
3278
|
files: starter.files,
|
|
3414
3279
|
client: params.eventScope?.client ?? params.ctx.app.client,
|
|
3415
3280
|
token: params.ctx.botToken,
|
|
3416
3281
|
maxBytes: params.ctx.mediaMaxBytes
|
|
3417
3282
|
});
|
|
3418
|
-
|
|
3283
|
+
threadStarterMedia = attachmentContent?.media.length ? attachmentContent.media : null;
|
|
3284
|
+
if (attachmentContent) threadStarterBody = formatSlackUnavailableMedia({
|
|
3285
|
+
body: threadStarterBody,
|
|
3286
|
+
files: attachmentContent.files,
|
|
3287
|
+
unavailableMediaCount: attachmentContent.unavailableMediaCount,
|
|
3288
|
+
prependUnavailable: true
|
|
3289
|
+
});
|
|
3290
|
+
if (threadStarterMedia) {
|
|
3291
|
+
const starterPlaceholders = threadStarterMedia.map((item) => item.placeholder).join(", ");
|
|
3292
|
+
logVerbose(`slack: hydrated thread starter file ${starterPlaceholders} from root message`);
|
|
3293
|
+
}
|
|
3419
3294
|
}
|
|
3420
3295
|
} else threadLabel = `Slack thread ${params.roomLabel}`;
|
|
3421
3296
|
const includeBotStarterAsRootContext = shouldIncludeBotThreadStarterContext({
|
|
@@ -3435,14 +3310,20 @@ async function resolveSlackThreadContextData(params) {
|
|
|
3435
3310
|
const threadInitialHistoryLimit = params.account.config?.thread?.initialHistoryLimit ?? 20;
|
|
3436
3311
|
if (threadInitialHistoryLimit > 0 && shouldLoadInitialThreadHistory) {
|
|
3437
3312
|
const currentBotRootTs = starter?.ts ?? params.threadTs;
|
|
3313
|
+
const threadHistory = await resolveSlackThreadHistory({
|
|
3314
|
+
channelId: params.message.channel,
|
|
3315
|
+
threadTs: params.threadTs,
|
|
3316
|
+
client: params.eventScope?.client ?? params.ctx.app.client,
|
|
3317
|
+
currentMessageTs: params.message.ts,
|
|
3318
|
+
limit: threadInitialHistoryLimit
|
|
3319
|
+
});
|
|
3320
|
+
const enrichedStarter = starter && threadStarterBody && threadStarterBody !== starter.text ? {
|
|
3321
|
+
...starter,
|
|
3322
|
+
text: threadStarterBody,
|
|
3323
|
+
ts: currentBotRootTs
|
|
3324
|
+
} : null;
|
|
3438
3325
|
const threadHistoryWithBotRoot = ensureSlackThreadHistoryHasBotRoot({
|
|
3439
|
-
history:
|
|
3440
|
-
channelId: params.message.channel,
|
|
3441
|
-
threadTs: params.threadTs,
|
|
3442
|
-
client: params.eventScope?.client ?? params.ctx.app.client,
|
|
3443
|
-
currentMessageTs: params.message.ts,
|
|
3444
|
-
limit: threadInitialHistoryLimit
|
|
3445
|
-
}),
|
|
3326
|
+
history: enrichedStarter && !threadHistory.some((entry) => entry.ts === currentBotRootTs) ? [enrichedStarter, ...threadHistory.length >= threadInitialHistoryLimit ? threadHistory.slice(1) : threadHistory] : threadHistory,
|
|
3446
3327
|
includeBotStarterAsRootContext,
|
|
3447
3328
|
threadStarter: starter ? {
|
|
3448
3329
|
...starter,
|
|
@@ -3501,7 +3382,7 @@ async function resolveSlackThreadContextData(params) {
|
|
|
3501
3382
|
});
|
|
3502
3383
|
const role = isCurrentBot || isOtherBot || Boolean(historyMsg.botId) ? "assistant" : "user";
|
|
3503
3384
|
const msgSenderName = isCurrentBot ? "Bot (this assistant)" : msgUser?.name ?? (historyMsg.botId ? `Bot (${historyMsg.botId})` : "Unknown");
|
|
3504
|
-
const msgWithId = `${historyMsg.text}\n[slack message id: ${historyMsg.ts ?? "unknown"} channel: ${params.message.channel}]`;
|
|
3385
|
+
const msgWithId = `${historyMsg.ts === currentBotRootTs && threadStarterBody ? threadStarterBody : historyMsg.text}\n[slack message id: ${historyMsg.ts ?? "unknown"} channel: ${params.message.channel}]`;
|
|
3505
3386
|
historyParts.push(formatInboundEnvelope({
|
|
3506
3387
|
channel: "Slack",
|
|
3507
3388
|
from: `${msgSenderName} (${role})`,
|
|
@@ -3528,7 +3409,7 @@ async function resolveSlackThreadContextData(params) {
|
|
|
3528
3409
|
//#endregion
|
|
3529
3410
|
//#region extensions/slack/src/monitor/message-handler/subteam-mentions.ts
|
|
3530
3411
|
const SUBTEAM_MENTION_RE = /<!subteam\^([A-Z0-9]+)(?:\|[^>]*)?>/gi;
|
|
3531
|
-
const SUBTEAM_MEMBER_CACHE_TTL_MS =
|
|
3412
|
+
const SUBTEAM_MEMBER_CACHE_TTL_MS = 3e5;
|
|
3532
3413
|
const subteamMemberCache = /* @__PURE__ */ new WeakMap();
|
|
3533
3414
|
function normalizeSlackId(value) {
|
|
3534
3415
|
return typeof value === "string" && value.trim() ? value.trim().toUpperCase() : void 0;
|
|
@@ -3599,7 +3480,7 @@ const SLACK_USER_MENTION_RE = /<@([^>|]+)(?:\|[^>]+)?>/g;
|
|
|
3599
3480
|
const SLACK_SUBTEAM_MENTION_RE = /<!subteam\^([^>|]+)(?:\|[^>]+)?>/g;
|
|
3600
3481
|
const SLACK_SUBTEAM_MENTION_MARKER = "<!subteam^";
|
|
3601
3482
|
const SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS = 4;
|
|
3602
|
-
const SLACK_HISTORY_MEDIA_MAX_BYTES =
|
|
3483
|
+
const SLACK_HISTORY_MEDIA_MAX_BYTES = 10485760;
|
|
3603
3484
|
const SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS = 1e3;
|
|
3604
3485
|
const SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS = 3e3;
|
|
3605
3486
|
const SLACK_CHANNEL_ACCESS_DOCS_URL = "https://docs.openclaw.ai/channels/slack#access-control-and-routing";
|
|
@@ -3733,7 +3614,7 @@ function buildSlackHistoryMediaCandidateMessage(message) {
|
|
|
3733
3614
|
async function resolveSlackHistoryMediaForPendingRecord(params) {
|
|
3734
3615
|
const mediaMessage = buildSlackHistoryMediaCandidateMessage(params.message);
|
|
3735
3616
|
if (!mediaMessage) return [];
|
|
3736
|
-
|
|
3617
|
+
const content = await resolveSlackMessageContent({
|
|
3737
3618
|
message: mediaMessage,
|
|
3738
3619
|
isThreadReply: params.isThreadReply,
|
|
3739
3620
|
threadStarter: params.threadStarter,
|
|
@@ -3743,7 +3624,8 @@ async function resolveSlackHistoryMediaForPendingRecord(params) {
|
|
|
3743
3624
|
mediaMaxBytes: Math.min(params.ctx.mediaMaxBytes, SLACK_HISTORY_MEDIA_MAX_BYTES),
|
|
3744
3625
|
mediaReadIdleTimeoutMs: SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS,
|
|
3745
3626
|
mediaTotalTimeoutMs: SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS
|
|
3746
|
-
})
|
|
3627
|
+
});
|
|
3628
|
+
return await toInboundMediaFactsWithMetadata(content?.effectiveDirectMedia, {
|
|
3747
3629
|
kind: "image",
|
|
3748
3630
|
messageId: params.message.ts
|
|
3749
3631
|
});
|
|
@@ -3839,24 +3721,15 @@ async function resolveSlackConversationContext(params) {
|
|
|
3839
3721
|
};
|
|
3840
3722
|
}
|
|
3841
3723
|
async function authorizeSlackInboundMessage(params) {
|
|
3842
|
-
const { ctx, account, message, conversation } = params;
|
|
3724
|
+
const { ctx, account, message, conversation, drop } = params;
|
|
3843
3725
|
const { isDirectMessage, channelName, resolvedChannelType, isBotMessage, allowBotsMode } = conversation;
|
|
3844
3726
|
if (isBotMessage) {
|
|
3845
3727
|
if (message.user && ctx.botUserId && message.user === ctx.botUserId) return null;
|
|
3846
|
-
if (allowBotsMode === "off")
|
|
3847
|
-
logVerbose(`slack: drop bot message ${message.bot_id ?? "unknown"} (allowBots=false)`);
|
|
3848
|
-
return null;
|
|
3849
|
-
}
|
|
3850
|
-
}
|
|
3851
|
-
if (isDirectMessage && !message.user) {
|
|
3852
|
-
logVerbose("slack: drop dm message (missing user id)");
|
|
3853
|
-
return null;
|
|
3728
|
+
if (allowBotsMode === "off") return drop("bot-disabled");
|
|
3854
3729
|
}
|
|
3730
|
+
if (isDirectMessage && !message.user) return drop("missing-user");
|
|
3855
3731
|
const senderId = message.user ?? (isBotMessage ? message.bot_id : void 0);
|
|
3856
|
-
if (!senderId)
|
|
3857
|
-
logVerbose("slack: drop message (missing sender id)");
|
|
3858
|
-
return null;
|
|
3859
|
-
}
|
|
3732
|
+
if (!senderId) return drop("missing-sender");
|
|
3860
3733
|
if (!ctx.isChannelAllowed({
|
|
3861
3734
|
teamId: params.eventScope?.teamId ?? ctx.teamId,
|
|
3862
3735
|
channelId: message.channel,
|
|
@@ -3866,7 +3739,8 @@ async function authorizeSlackInboundMessage(params) {
|
|
|
3866
3739
|
if (conversation.isRoom && ctx.groupPolicy === "allowlist" && params.explicitBotMention && !isBotMessage && message.user) {
|
|
3867
3740
|
let subject = "This OpenClaw bot";
|
|
3868
3741
|
if (ctx.botUserId) try {
|
|
3869
|
-
const
|
|
3742
|
+
const botIdentity = await ctx.resolveUserName(ctx.botUserId, params.eventScope);
|
|
3743
|
+
const botName = normalizeOptionalString(botIdentity?.name);
|
|
3870
3744
|
if (botName) subject = escapeSlackMrkdwn(botName);
|
|
3871
3745
|
} catch (error) {
|
|
3872
3746
|
logVerbose(`slack allowlist denial bot-name lookup failed: ${formatSlackError(error)}`);
|
|
@@ -3883,8 +3757,7 @@ async function authorizeSlackInboundMessage(params) {
|
|
|
3883
3757
|
ctx.runtime.error?.(`slack allowlist denial notice failed for channel ${message.channel}: ${formatSlackError(error)}`);
|
|
3884
3758
|
}
|
|
3885
3759
|
}
|
|
3886
|
-
|
|
3887
|
-
return null;
|
|
3760
|
+
return drop("channel-not-allowed");
|
|
3888
3761
|
}
|
|
3889
3762
|
const allowFromLower = await resolveSlackEffectiveAllowFrom(ctx, {
|
|
3890
3763
|
includePairingStore: isDirectMessage,
|
|
@@ -3892,10 +3765,8 @@ async function authorizeSlackInboundMessage(params) {
|
|
|
3892
3765
|
});
|
|
3893
3766
|
if (isDirectMessage) {
|
|
3894
3767
|
const directUserId = message.user;
|
|
3895
|
-
if (!directUserId)
|
|
3896
|
-
|
|
3897
|
-
return null;
|
|
3898
|
-
}
|
|
3768
|
+
if (!directUserId) return drop("missing-user");
|
|
3769
|
+
let dropReason = "dm-unauthorized";
|
|
3899
3770
|
if (!await authorizeSlackDirectMessage({
|
|
3900
3771
|
ctx,
|
|
3901
3772
|
accountId: account.accountId,
|
|
@@ -3913,13 +3784,13 @@ async function authorizeSlackInboundMessage(params) {
|
|
|
3913
3784
|
});
|
|
3914
3785
|
},
|
|
3915
3786
|
onDisabled: () => {
|
|
3916
|
-
|
|
3787
|
+
dropReason = "dm-disabled";
|
|
3917
3788
|
},
|
|
3918
3789
|
onUnauthorized: ({ allowMatchMeta }) => {
|
|
3919
3790
|
logVerbose(`Blocked unauthorized slack sender ${message.user} (dmPolicy=${ctx.dmPolicy}, ${allowMatchMeta})`);
|
|
3920
3791
|
},
|
|
3921
3792
|
log: logVerbose
|
|
3922
|
-
})) return
|
|
3793
|
+
})) return drop(dropReason);
|
|
3923
3794
|
}
|
|
3924
3795
|
return {
|
|
3925
3796
|
senderId,
|
|
@@ -3928,6 +3799,19 @@ async function authorizeSlackInboundMessage(params) {
|
|
|
3928
3799
|
}
|
|
3929
3800
|
async function prepareSlackMessage(params) {
|
|
3930
3801
|
const { ctx, account, message, opts } = params;
|
|
3802
|
+
const drop = (reason, parentUserId) => {
|
|
3803
|
+
ctx.logger.info({
|
|
3804
|
+
provider: "slack",
|
|
3805
|
+
accountId: account.accountId,
|
|
3806
|
+
teamId: opts.eventScope?.teamId ?? ctx.teamId,
|
|
3807
|
+
channelId: message.channel,
|
|
3808
|
+
messageTs: message.ts,
|
|
3809
|
+
source: opts.source,
|
|
3810
|
+
reason,
|
|
3811
|
+
...parentUserId ? { parentUserId } : {}
|
|
3812
|
+
}, "Slack inbound event rejected during preparation");
|
|
3813
|
+
return null;
|
|
3814
|
+
};
|
|
3931
3815
|
const slackClient = opts.eventScope?.client ?? ctx.app.client;
|
|
3932
3816
|
const threadStarterWorkspaceScope = {
|
|
3933
3817
|
accountId: account.accountId,
|
|
@@ -3951,7 +3835,8 @@ async function prepareSlackMessage(params) {
|
|
|
3951
3835
|
conversation,
|
|
3952
3836
|
explicitBotMention: opts.source === "app_mention" || Boolean(normalizedBotUserId && mentionMetadata.mentionedUserIds.includes(normalizedBotUserId)),
|
|
3953
3837
|
eventScope: opts.eventScope,
|
|
3954
|
-
onVisibleDrop: opts.onVisibleDrop
|
|
3838
|
+
onVisibleDrop: opts.onVisibleDrop,
|
|
3839
|
+
drop
|
|
3955
3840
|
});
|
|
3956
3841
|
if (!authorization) return null;
|
|
3957
3842
|
const { senderId, allowFromLower } = authorization;
|
|
@@ -3959,7 +3844,8 @@ async function prepareSlackMessage(params) {
|
|
|
3959
3844
|
const resolveSenderName = async () => {
|
|
3960
3845
|
if (resolvedSenderName) return resolvedSenderName;
|
|
3961
3846
|
if (message.user) {
|
|
3962
|
-
const
|
|
3847
|
+
const sender = await ctx.resolveUserName(message.user, opts.eventScope);
|
|
3848
|
+
const normalized = normalizeOptionalString(sender?.name);
|
|
3963
3849
|
if (normalized) {
|
|
3964
3850
|
resolvedSenderName = normalized;
|
|
3965
3851
|
return resolvedSenderName;
|
|
@@ -4055,13 +3941,7 @@ async function prepareSlackMessage(params) {
|
|
|
4055
3941
|
if (shouldLogVerbose()) logVerbose(`slack: using configured ACP binding for ${configuredBinding.record.conversation.conversationId} -> ${configuredBindingSessionKey}`);
|
|
4056
3942
|
} else {
|
|
4057
3943
|
if (shouldLogVerbose()) logVerbose(`slack: configured ACP binding unavailable for ${configuredBinding.record.conversation.conversationId}: ${ensured.error}`);
|
|
4058
|
-
|
|
4059
|
-
log: logVerbose,
|
|
4060
|
-
channel: "slack",
|
|
4061
|
-
reason: "configured ACP binding unavailable",
|
|
4062
|
-
target: configuredBinding.record.conversation.conversationId
|
|
4063
|
-
});
|
|
4064
|
-
return null;
|
|
3944
|
+
return drop("configured-binding-unavailable");
|
|
4065
3945
|
}
|
|
4066
3946
|
}
|
|
4067
3947
|
const senderNameForAuthPromise = ctx.allowNameMatching ? resolveSenderName().then((name) => ({
|
|
@@ -4176,14 +4056,7 @@ async function prepareSlackMessage(params) {
|
|
|
4176
4056
|
channel: "slack",
|
|
4177
4057
|
accountId: account.accountId
|
|
4178
4058
|
});
|
|
4179
|
-
if (message["_ambiguousThreadReply"])
|
|
4180
|
-
ctx.logger.info({
|
|
4181
|
-
channel: message.channel,
|
|
4182
|
-
ts: message.ts,
|
|
4183
|
-
parentUserId: message.parent_user_id
|
|
4184
|
-
}, "skipping ambiguous slack thread reply");
|
|
4185
|
-
return null;
|
|
4186
|
-
}
|
|
4059
|
+
if (message["_ambiguousThreadReply"]) return drop("ambiguous-thread", message.parent_user_id);
|
|
4187
4060
|
let canDetectMention = Boolean(ctx.botUserId) || mentionRegexes.length > 0;
|
|
4188
4061
|
const textForCommandDetection = stripSlackMentionsForCommandDetection(message.text ?? "");
|
|
4189
4062
|
const hasControlCommandInMessage = hasControlCommand(textForCommandDetection, cfg);
|
|
@@ -4216,10 +4089,7 @@ async function prepareSlackMessage(params) {
|
|
|
4216
4089
|
});
|
|
4217
4090
|
let messageIngress = await resolveMessageIngress();
|
|
4218
4091
|
const senderGate = messageIngress.senderAccess.gate;
|
|
4219
|
-
if (isRoomish && senderGate?.allowed === false)
|
|
4220
|
-
logVerbose(`Blocked unauthorized slack sender ${senderId} (not in sender allowlist)`);
|
|
4221
|
-
return null;
|
|
4222
|
-
}
|
|
4092
|
+
if (isRoomish && senderGate?.allowed === false) return drop("unauthorized-sender");
|
|
4223
4093
|
if (isRoom && isBotMessage && allowBotsMode !== "off" && !await authorizeSlackBotRoomMessage({
|
|
4224
4094
|
ctx,
|
|
4225
4095
|
channelId: message.channel,
|
|
@@ -4228,7 +4098,7 @@ async function prepareSlackMessage(params) {
|
|
|
4228
4098
|
channelUsers: channelConfig?.users,
|
|
4229
4099
|
allowFromLower,
|
|
4230
4100
|
eventScope: opts.eventScope
|
|
4231
|
-
})) return
|
|
4101
|
+
})) return drop("unauthorized-bot");
|
|
4232
4102
|
const threadContextAllowFromLower = isRoom ? channelUsersAllowlistConfigured ? normalizeAllowListLower(channelConfig?.users) : [] : isDirectMessage ? allowFromLower : [];
|
|
4233
4103
|
const contextVisibilityMode = resolveChannelContextVisibilityMode({
|
|
4234
4104
|
cfg: ctx.cfg,
|
|
@@ -4248,15 +4118,7 @@ async function prepareSlackMessage(params) {
|
|
|
4248
4118
|
explicitKind: true
|
|
4249
4119
|
});
|
|
4250
4120
|
const commandAuthorized = messageIngress.commandAccess.authorized;
|
|
4251
|
-
if (isRoomish && messageIngress.commandAccess.shouldBlockControlCommand)
|
|
4252
|
-
logInboundDrop({
|
|
4253
|
-
log: logVerbose,
|
|
4254
|
-
channel: "slack",
|
|
4255
|
-
reason: "control command (unauthorized)",
|
|
4256
|
-
target: senderId
|
|
4257
|
-
});
|
|
4258
|
-
return null;
|
|
4259
|
-
}
|
|
4121
|
+
if (isRoomish && messageIngress.commandAccess.shouldBlockControlCommand) return drop("control-command-unauthorized");
|
|
4260
4122
|
const canSeedMentionedRoomThread = !seedTopLevelRoomThreadBySource && isRoom && !routing.isThreadReply && !hasBoundSession;
|
|
4261
4123
|
let seededMentionRouting;
|
|
4262
4124
|
const getSeededMentionRouting = () => {
|
|
@@ -4351,37 +4213,20 @@ async function prepareSlackMessage(params) {
|
|
|
4351
4213
|
wasMentioned
|
|
4352
4214
|
});
|
|
4353
4215
|
if (isBotMessage && allowBotsMode === "mentions") {
|
|
4354
|
-
if (!(isDirectMessage || effectiveWasMentioned || shouldBypassMention))
|
|
4355
|
-
logVerbose("slack: drop bot message (allowBots=mentions, missing mention)");
|
|
4356
|
-
return null;
|
|
4357
|
-
}
|
|
4216
|
+
if (!(isDirectMessage || effectiveWasMentioned || shouldBypassMention)) return drop("bot-missing-mention");
|
|
4358
4217
|
}
|
|
4359
4218
|
if (isRoom && shouldRequireMention && !canDetectMention && !effectiveWasMentioned) {
|
|
4360
|
-
ctx.logger.info({
|
|
4361
|
-
channel: message.channel,
|
|
4362
|
-
reason: "mention-detection-unavailable"
|
|
4363
|
-
}, "skipping channel message");
|
|
4364
4219
|
await recordDroppedHistory("slack-mention-detection-unavailable");
|
|
4365
|
-
return
|
|
4220
|
+
return drop("mention-detection-unavailable");
|
|
4366
4221
|
}
|
|
4367
4222
|
const ignoreOtherMentions = channelConfig?.ignoreOtherMentions ?? false;
|
|
4368
4223
|
if (isRoom && ignoreOtherMentions && Boolean(ctx.botUserId) && hasAnyMention && !wasMentioned) {
|
|
4369
|
-
logInboundDrop({
|
|
4370
|
-
log: logVerbose,
|
|
4371
|
-
channel: "slack",
|
|
4372
|
-
reason: "other-mention",
|
|
4373
|
-
target: senderId
|
|
4374
|
-
});
|
|
4375
4224
|
await recordDroppedHistory("slack-other-mention");
|
|
4376
|
-
return
|
|
4225
|
+
return drop("other-mention");
|
|
4377
4226
|
}
|
|
4378
4227
|
if (isRoom && shouldRequireMention && mentionDecision.shouldSkip) {
|
|
4379
|
-
ctx.logger.info({
|
|
4380
|
-
channel: message.channel,
|
|
4381
|
-
reason: "no-mention"
|
|
4382
|
-
}, "skipping channel message");
|
|
4383
4228
|
await recordDroppedHistory("slack-no-mention");
|
|
4384
|
-
return
|
|
4229
|
+
return drop("missing-mention");
|
|
4385
4230
|
}
|
|
4386
4231
|
const chatType = resolveSlackChatType(conversation.resolvedChannelType);
|
|
4387
4232
|
const inboundEventKind = classifyChannelInboundEvent({
|
|
@@ -4396,7 +4241,7 @@ async function prepareSlackMessage(params) {
|
|
|
4396
4241
|
});
|
|
4397
4242
|
const threadStarter = await getThreadStarter();
|
|
4398
4243
|
const resolvedMessageContent = await getMessageContent();
|
|
4399
|
-
if (!resolvedMessageContent) return
|
|
4244
|
+
if (!resolvedMessageContent) return drop("empty-content");
|
|
4400
4245
|
const { rawBody, effectiveDirectMedia } = resolvedMessageContent;
|
|
4401
4246
|
const bodyForAgent = preflightAudioTranscript ? formatSlackAudioTranscriptForAgent({
|
|
4402
4247
|
transcript: preflightAudioTranscript,
|
|
@@ -4559,10 +4404,7 @@ async function prepareSlackMessage(params) {
|
|
|
4559
4404
|
messageId: threadContext.messageTs,
|
|
4560
4405
|
inboundEventKind
|
|
4561
4406
|
}, boundMessageThreadId);
|
|
4562
|
-
if (messageIngress.ingress.admission !== "dispatch")
|
|
4563
|
-
logVerbose(`Blocked slack sender ${senderId} after final route binding`);
|
|
4564
|
-
return null;
|
|
4565
|
-
}
|
|
4407
|
+
if (messageIngress.ingress.admission !== "dispatch") return drop("final-route-denied");
|
|
4566
4408
|
const agentContextEntities = isAgentViewMessage ? normalizeSlackAppContextEntities(message.app_context) : [];
|
|
4567
4409
|
const ctxPayload = (ctx.buildContext ?? buildChannelInboundEventContext)({
|
|
4568
4410
|
channelIngress: messageIngress,
|
|
@@ -4647,6 +4489,7 @@ async function prepareSlackMessage(params) {
|
|
|
4647
4489
|
payload: { entities: agentContextEntities }
|
|
4648
4490
|
}] : void 0,
|
|
4649
4491
|
TransportThreadId: directThreadRoutedToDmSession ? threadContext.messageThreadId : void 0,
|
|
4492
|
+
ReplyThreading: isThreadReply ? { implicitCurrentMessage: "deny" } : void 0,
|
|
4650
4493
|
SlackAssistantThread: assistantThreadContext ? true : void 0,
|
|
4651
4494
|
SlackAgentThread: agentViewThreadTs ? true : void 0,
|
|
4652
4495
|
SlackAssistantThreadContextChannelId: assistantThreadContext?.channelId,
|
|
@@ -4682,7 +4525,6 @@ async function prepareSlackMessage(params) {
|
|
|
4682
4525
|
normalizeEntry: normalizeSlackAllowOwnerEntry
|
|
4683
4526
|
}) : null;
|
|
4684
4527
|
const replyTarget = `channel:${message.channel}`;
|
|
4685
|
-
if (!replyTarget) return null;
|
|
4686
4528
|
if (preflightAudioTranscript) await sendSlackPreflightAudioTranscriptEcho({
|
|
4687
4529
|
transcript: preflightAudioTranscript,
|
|
4688
4530
|
cfg,
|