@openclaw/slack 2026.7.2-beta.2 → 2026.7.2-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/{account-configured-DzC407uv.js → account-configured-BwfXBo_F.js} +1 -1
  2. package/dist/{account-inspect-Di7ditqZ.js → account-inspect-Bvbtu3iH.js} +9 -2
  3. package/dist/account-inspect-api.js +1 -1
  4. package/dist/{accounts-BOJJiHSr.js → accounts-DQEbYKh9.js} +9 -0
  5. package/dist/accounts.runtime-BIKmJdtJ.js +2 -0
  6. package/dist/{action-runtime-Cee2ih0s.js → action-runtime-DqkRwO3Z.js} +10 -16
  7. package/dist/action-runtime.runtime-DEWOcFM4.js +2 -0
  8. package/dist/{actions-BZiuSTIq.js → actions-CF0tUY5q.js} +4 -4
  9. package/dist/{actions.runtime-DoD0-gxw.js → actions.runtime-hCbx4RXg.js} +1 -1
  10. package/dist/api.js +13 -14
  11. package/dist/{approval-handler.runtime-CUPtIM4k.js → approval-handler.runtime-Em8I1-8-.js} +2 -2
  12. package/dist/{channel-DaIiGUOp.js → channel-4-TRi1D6.js} +72 -40
  13. package/dist/channel-config-api.js +1 -1
  14. package/dist/channel-plugin-api.js +1 -1
  15. package/dist/{channel-type-Cfm6_6n1.js → channel-type-Cu1yjNg2.js} +3 -3
  16. package/dist/{channel.setup-C-mLz50O.js → channel.setup-CgkADtZg.js} +6 -4
  17. package/dist/{config-schema-xPSnK2VK.js → config-schema-XjJGfwQC.js} +6 -5
  18. package/dist/{directory-config-DRkFujwG.js → directory-config-Bv0lcUlH.js} +1 -1
  19. package/dist/directory-contract-api.js +1 -1
  20. package/dist/{directory-live-D_yZhjgp.js → directory-live-CzUIB7th.js} +2 -2
  21. package/dist/{doctor-contract-DddfkMlF.js → doctor-contract-DKpe5VBr.js} +80 -12
  22. package/dist/doctor-contract-api.js +1 -1
  23. package/dist/{group-policy-85UWiriS.js → group-policy-Bw-65m3V.js} +67 -121
  24. package/dist/{interactive-replies-BilWUdcI.js → interactive-replies-Dm_t2R9S.js} +1 -1
  25. package/dist/interactive-replies-api.js +1 -1
  26. package/dist/{message-tool-api-CA9jZrCY.js → message-tool-api-C2pcf5du.js} +3 -3
  27. package/dist/message-tool-api.js +1 -1
  28. package/dist/{monitor-CalGciTs.js → monitor-CNFeF30h.js} +3 -3
  29. package/dist/{outbound-adapter-BiLKcnyH.js → outbound-adapter-CVi2uok0.js} +49 -5
  30. package/dist/{pipeline.runtime-DofAvTtb.js → pipeline.runtime-CTvokbty.js} +391 -279
  31. package/dist/{policy-BVs4LiSa.js → policy-b_dOPDCq.js} +1 -1
  32. package/dist/probe-Borhn9PQ.js +340 -0
  33. package/dist/{provider-CmeoUiIM.js → provider-B-HVL5ZE.js} +573 -306
  34. package/dist/{relay-source-DMmgtvg4.js → relay-source-Ea_P9VcO.js} +6 -11
  35. package/dist/{replies-CJJCp6qz.js → replies-B4bOdrYb.js} +3 -3
  36. package/dist/{resolve-channels-Rfts6POK.js → resolve-channels-KTc568SU.js} +1 -1
  37. package/dist/{resolve-users-y6wAxR8E.js → resolve-users-DeGJf1yp.js} +1 -1
  38. package/dist/{runtime-api-BGDyOt1D.js → runtime-api-BFfNpzDe.js} +1 -1
  39. package/dist/runtime-api.js +11 -11
  40. package/dist/{scopes-CuxNQZ6v.js → scopes-lx08L56K.js} +1 -2
  41. package/dist/{secret-contract-CMohXFWS.js → secret-contract-DxmvbxBF.js} +1 -0
  42. package/dist/secret-contract-api.js +1 -1
  43. package/dist/{send-DYp8ca-l.js → send-KuTDky7i.js} +28 -11
  44. package/dist/send.runtime-CXyxws1D.js +2 -0
  45. package/dist/{setup-core-CkwVLzFO.js → setup-core-CeiAVwQ3.js} +218 -53
  46. package/dist/setup-plugin-api.js +1 -1
  47. package/dist/{setup-surface-oSd3NX9J.js → setup-surface-DLA0vHXp.js} +23 -13
  48. package/dist/{shared-D9yz5Otg.js → shared-C4yVB0Q-.js} +55 -9
  49. package/dist/{slash-dispatch.runtime-CSja9VuD.js → slash-dispatch.runtime-BT-mMm-f.js} +1 -1
  50. package/npm-shrinkwrap.json +3 -3
  51. package/openclaw.plugin.json +66 -10
  52. package/package.json +4 -4
  53. package/dist/accounts.runtime-KvftRRek.js +0 -2
  54. package/dist/action-runtime.runtime-DZfd7IFr.js +0 -2
  55. package/dist/client-WIPAS3Yr.js +0 -160
  56. package/dist/probe-CN0ssfLN.js +0 -48
  57. package/dist/send.runtime-CJLxAt26.js +0 -2
@@ -1,20 +1,20 @@
1
- import { u as resolveSlackReplyToMode } from "./accounts-BOJJiHSr.js";
1
+ import { u as resolveSlackReplyToMode } from "./accounts-DQEbYKh9.js";
2
2
  import { i as parseSlackTarget } from "./target-parsing-N6lHogYZ.js";
3
3
  import "./targets-KmjP2E8s.js";
4
- import { A as buildSlackBlocksFallbackText, B as truncateSlackText, I as SLACK_TEXT_LIMIT, P as SLACK_EDIT_TEXT_LIMIT, c as normalizeAllowListLower, d as resolveSlackAllowListMatch, i as formatSlackError, k as normalizeSlackOutboundText, l as normalizeSlackAllowOwnerEntry, q as escapeSlackMrkdwn, v as resolveSlackReplyRenderPlan, w as hasSlackNativeDataBlock } from "./group-policy-85UWiriS.js";
5
- import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-BilWUdcI.js";
4
+ import { C as hasSlackNativeDataBlock, F as SLACK_TEXT_LIMIT, N as SLACK_EDIT_TEXT_LIMIT, O as normalizeSlackOutboundText, Y as escapeSlackMrkdwn, _ as resolveSlackReplyRenderPlan, c as normalizeSlackAllowOwnerEntry, k as buildSlackBlocksFallbackText, s as normalizeAllowListLower, u as resolveSlackAllowListMatch, z as truncateSlackText } from "./group-policy-Bw-65m3V.js";
5
+ import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-Dm_t2R9S.js";
6
+ import { r as formatSlackError } from "./probe-Borhn9PQ.js";
6
7
  import { n as resolveSlackNativeStreaming, r as resolveSlackStreamingMode, t as mapStreamingModeToSlackLegacyDraftStreamMode } from "./streaming-compat-3_X-21wW.js";
7
- import { S as buildSlackEditTextPayload, _ as resolveSlackThreadHistory, b as resolveSlackBlocksText, f as removeSlackReaction, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage, v as resolveSlackThreadStarter, x as formatSlackFileReference, y as chooseSlackPrimaryText } from "./actions-BZiuSTIq.js";
8
- import { c as recordSlackThreadParticipation, l as buildSlackNativeDataDeliveryPlan, r as sendMessageSlack, s as hasSlackThreadParticipationWithPersistence } from "./send-DYp8ca-l.js";
9
- import { S as updateLastRoute, _ as resolveSlackChatType, b as resolveChannelResetConfig, d as authorizeSlackBotRoomMessage, f as resolveSlackCommandIngress, g as normalizeSlackChannelType, h as parseSlackAssistantThreadMetadata, l as recordInboundSession, m as buildSlackAssistantThreadMetadata, o as authorizeSlackDirectMessage, p as resolveSlackEffectiveAllowFrom, r as resolveSlackRoomContextHints, u as resolveConversationLabel$1, v as readSessionUpdatedAt, w as stripSlackMentionsForCommandDetection, x as resolveStorePath, y as resolveChannelContextVisibilityMode } from "./provider-CmeoUiIM.js";
10
- import { n as resolveSlackChannelConfig } from "./policy-BVs4LiSa.js";
11
- import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, s as emitSlackMessageSentHooks, t as createSlackReplyDeliveryPlan } from "./replies-CJJCp6qz.js";
8
+ import { S as buildSlackEditTextPayload, _ as resolveSlackThreadHistory, b as resolveSlackBlocksText, f as removeSlackReaction, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage, v as resolveSlackThreadStarter, x as formatSlackFileReference, y as chooseSlackPrimaryText } from "./actions-CF0tUY5q.js";
9
+ import { c as hasSlackThreadParticipationWithPersistence, l as recordSlackThreadParticipation, r as sendMessageSlack, u as buildSlackNativeDataDeliveryPlan } from "./send-KuTDky7i.js";
10
+ import { C as stripSlackMentionsForCommandDetection, _ as readSessionUpdatedAt, b as resolveStorePath, d as resolveSlackCommandIngress, f as resolveSlackEffectiveAllowFrom, g as resolveSlackChatType, h as normalizeSlackChannelType, l as resolveConversationLabel$1, m as parseSlackAssistantThreadMetadata, o as authorizeSlackDirectMessage, p as buildSlackAssistantThreadMetadata, r as resolveSlackRoomContextHints, u as authorizeSlackBotRoomMessage, v as resolveChannelContextVisibilityMode, x as updateLastRoute, y as resolveChannelResetConfig } from "./provider-B-HVL5ZE.js";
11
+ import { n as resolveSlackChannelConfig } from "./policy-b_dOPDCq.js";
12
+ import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, s as emitSlackMessageSentHooks, t as createSlackReplyDeliveryPlan } from "./replies-B4bOdrYb.js";
12
13
  import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
13
14
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
14
- import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelMessageReplyPipeline, createChannelProgressDraftGate, createDraftStreamLoop, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, formatChannelProgressDraftText, formatPlanChecklistLines, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelProgressDraftMaxLines, resolveChannelProgressDraftRender, resolveChannelStreamingBlockEnabled, resolveChannelStreamingNativeTransport, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingProgressCommentary, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
15
+ import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createChannelProgressReceiptTracker, createDraftStreamLoop, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, formatChannelProgressDraftText, formatPlanChecklistLines, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelProgressDraftMaxLines, resolveChannelProgressDraftRender, resolveChannelStreamingBlockEnabled, resolveChannelStreamingNativeTransport, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
15
16
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
16
17
  import { danger, logVerbose, shouldLogVerbose, sleep } from "openclaw/plugin-sdk/runtime-env";
17
- import { stripReasoningTagsFromText } from "openclaw/plugin-sdk/text-chunking";
18
18
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
19
19
  import { createHash } from "node:crypto";
20
20
  import fs from "node:fs/promises";
@@ -27,9 +27,9 @@ import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
27
27
  import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
28
28
  import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
29
29
  import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
30
- import { buildChannelInboundEventContext, buildMentionRegexes, classifyChannelInboundEvent, dispatchChannelInboundReply, formatInboundEnvelope, hasVisibleInboundReplyDispatch, implicitMentionKindWhen, logInboundDrop, matchesMentionWithExplicit, recordDroppedChannelInboundHistory, resolveEnvelopeFormatOptions, resolveInboundMentionDecision, resolveUnmentionedGroupInboundPolicy, toInboundMediaFacts } from "openclaw/plugin-sdk/channel-inbound";
30
+ import { resolveChannelImplicitMentions } from "openclaw/plugin-sdk/channel-ingress-runtime";
31
+ import { buildChannelInboundEventContext, buildMentionRegexes, classifyChannelInboundEvent, dispatchChannelInboundTurn, formatInboundEnvelope, hasVisibleInboundReplyDispatch, implicitMentionKindWhen, logInboundDrop, matchesMentionWithExplicit, recordDroppedChannelInboundHistory, resolveEnvelopeFormatOptions, resolveInboundMentionDecision, resolveInboundSupplementalSenderAllowed, resolveUnmentionedGroupInboundPolicy, toInboundMediaFacts } from "openclaw/plugin-sdk/channel-inbound";
31
32
  import { DEFAULT_TIMING, createStatusReactionController, logAckFailure, logTypingFailure, removeAckReactionAfterReply, resolveAckReaction, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
32
- import { dispatchReplyWithBufferedBlockDispatcher } from "openclaw/plugin-sdk/reply-runtime";
33
33
  import { hasControlCommand } from "openclaw/plugin-sdk/command-detection";
34
34
  import { isAbortRequestText } from "openclaw/plugin-sdk/command-primitives-runtime";
35
35
  import { shouldHandleTextCommands } from "openclaw/plugin-sdk/command-surface";
@@ -216,9 +216,24 @@ function lineTaskStatus(line) {
216
216
  function slugTaskIdPart(value) {
217
217
  return value?.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "task";
218
218
  }
219
- function stableTaskIdPart(value) {
219
+ function stableTaskIdPart(value, slugValue = value) {
220
220
  const suffix = createHash("sha256").update(value).digest("hex").slice(0, 8);
221
- return `${slugTaskIdPart(value)}_${suffix}`;
221
+ return `${slugTaskIdPart(slugValue).slice(0, 48)}_${suffix}`;
222
+ }
223
+ function resolveLineTaskIdentity(line) {
224
+ if (line.id?.trim()) return {
225
+ id: stableTaskIdPart(line.id),
226
+ contentDerived: false
227
+ };
228
+ return {
229
+ id: stableTaskIdPart([
230
+ line.kind,
231
+ line.toolName,
232
+ line.label,
233
+ line.text
234
+ ].join("\0"), line.toolName ?? line.kind ?? line.label),
235
+ contentDerived: true
236
+ };
222
237
  }
223
238
  function buildPlanTasks(params) {
224
239
  if (params.plan) return params.plan.slice(-50).map((entry, index) => ({
@@ -227,11 +242,23 @@ function buildPlanTasks(params) {
227
242
  status: entry.status === "completed" ? "complete" : entry.status
228
243
  }));
229
244
  const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_TASK_DETAIL_MAX_CHARS);
230
- return params.lines.slice(-50).map((line, index) => ({
231
- id: line.id ? stableTaskIdPart(line.id) : `${slugTaskIdPart(line.toolName ?? line.kind ?? line.label)}_${index + 1}`,
232
- title: lineTaskTitle(line, maxLineChars),
233
- status: lineTaskStatus(line)
234
- }));
245
+ const lines = params.lines.slice(-50);
246
+ const identities = lines.map(resolveLineTaskIdentity);
247
+ const contentIdOccurrences = /* @__PURE__ */ new Map();
248
+ return lines.map((line, index) => {
249
+ const identity = identities[index];
250
+ let id = identity.id;
251
+ if (identity.contentDerived) {
252
+ const occurrence = (contentIdOccurrences.get(id) ?? 0) + 1;
253
+ contentIdOccurrences.set(id, occurrence);
254
+ id = `${id}_${occurrence}`;
255
+ }
256
+ return {
257
+ id,
258
+ title: lineTaskTitle(line, maxLineChars),
259
+ status: lineTaskStatus(line)
260
+ };
261
+ });
235
262
  }
236
263
  function resolvePlanTitle(params) {
237
264
  return compactChunkText(params.title?.trim() || params.label?.trim() || params.tasks.at(-1)?.title || SLACK_PROGRESS_PLAN_FALLBACK_TITLE);
@@ -242,7 +269,13 @@ function buildSlackProgressStreamChunks(params) {
242
269
  plan: params.plan,
243
270
  maxLineChars: params.maxLineChars
244
271
  });
245
- if (tasks.length === 0) return;
272
+ if (tasks.length === 0) {
273
+ const title = params.title?.trim() || params.label?.trim();
274
+ return title ? [{
275
+ type: "plan_update",
276
+ title: compactChunkText(title)
277
+ }] : void 0;
278
+ }
246
279
  return [{
247
280
  type: "plan_update",
248
281
  title: resolvePlanTitle({
@@ -710,15 +743,6 @@ function resolveSlackTimestampMs(ts) {
710
743
  }
711
744
  //#endregion
712
745
  //#region extensions/slack/src/monitor/message-handler/dispatch.ts
713
- const SLACK_REASONING_TAG_RE = /<\s*(\/?)\s*(?:(?:antml:|mm:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>/gi;
714
- const SLACK_REASONING_LABEL_PREFIX_RE = /^\s*(?:>\s*)?Reasoning:\s*/iu;
715
- const SLACK_THINKING_LABEL_PREFIX_RE = /^\s*(?:>\s*)?Thinking\.{0,3}(?=\s*(?:\n|_))/iu;
716
- const SLACK_THREAD_LOADING_MESSAGES = [
717
- "Reading the thread...",
718
- "Checking context...",
719
- "Working through the request...",
720
- "Putting it all together..."
721
- ];
722
746
  function resolveSlackMessageTimestampMs(message) {
723
747
  return resolveSlackTimestampMs(message.event_ts ?? message.ts);
724
748
  }
@@ -820,43 +844,6 @@ function rememberSlackStreamRecipientTeam(params) {
820
844
  if (oldest) cache.delete(oldest);
821
845
  }
822
846
  }
823
- function normalizeSlackReasoningProgressLine(text) {
824
- return (extractSlackReasoningTagText(text) || stripReasoningTagsFromText(text, {
825
- mode: "strict",
826
- trim: "both"
827
- })).replace(SLACK_REASONING_LABEL_PREFIX_RE, "").replace(SLACK_THINKING_LABEL_PREFIX_RE, "").split(/\r?\n/u).map((line) => stripSimpleItalicMarkers(line)).filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
828
- }
829
- function extractSlackReasoningTagText(text) {
830
- if (!text) return "";
831
- let result = "";
832
- let lastIndex = 0;
833
- let inReasoning = false;
834
- SLACK_REASONING_TAG_RE.lastIndex = 0;
835
- for (const match of text.matchAll(SLACK_REASONING_TAG_RE)) {
836
- const index = match.index ?? 0;
837
- if (inReasoning) result += text.slice(lastIndex, index);
838
- inReasoning = match[1] !== "/";
839
- lastIndex = index + match[0].length;
840
- }
841
- if (inReasoning) result += text.slice(lastIndex);
842
- return result.trim();
843
- }
844
- function stripSimpleItalicMarkers(line) {
845
- const trimmed = line.trim();
846
- if (trimmed.length >= 2 && trimmed.startsWith("_") && trimmed.endsWith("_")) return trimmed.slice(1, -1).trim();
847
- return trimmed;
848
- }
849
- function mergeSlackReasoningProgressText(current, incoming, options) {
850
- if (!current) return incoming;
851
- const normalizedCurrent = normalizeSlackReasoningProgressLine(current);
852
- const normalizedIncoming = normalizeSlackReasoningProgressLine(incoming);
853
- if (!normalizedIncoming || normalizedIncoming === normalizedCurrent) return current;
854
- if (options?.snapshot === true || isSlackReasoningSnapshotText(incoming) || normalizedIncoming.startsWith(normalizedCurrent)) return incoming;
855
- return `${current}${incoming}`;
856
- }
857
- function isSlackReasoningSnapshotText(text) {
858
- return SLACK_REASONING_LABEL_PREFIX_RE.test(text) || SLACK_THINKING_LABEL_PREFIX_RE.test(text);
859
- }
860
847
  function createSlackEventDeliveryTracker() {
861
848
  const deliveredKeys = /* @__PURE__ */ new Set();
862
849
  return {
@@ -1036,7 +1023,6 @@ async function dispatchPreparedSlackMessage(prepared) {
1036
1023
  channelId: message.channel,
1037
1024
  threadTs: statusThreadTs,
1038
1025
  status: "is typing...",
1039
- loadingMessages: SLACK_THREAD_LOADING_MESSAGES,
1040
1026
  eventScope: prepared.eventScope
1041
1027
  });
1042
1028
  if (typingReaction && message.ts) await reactSlackMessage(message.channel, message.ts, typingReaction, {
@@ -1156,6 +1142,11 @@ async function dispatchPreparedSlackMessage(prepared) {
1156
1142
  delivery.outcome = "success";
1157
1143
  }
1158
1144
  };
1145
+ const acknowledgeStoppedStreamedDeliveries = (session, messageId) => {
1146
+ refreshStreamedAcknowledgements(session);
1147
+ for (const delivery of streamedDeliveries) delivery.acknowledged = true;
1148
+ emitAcknowledgedStreamedDeliveries(messageId);
1149
+ };
1159
1150
  const emitFailedPendingStreamedDeliveries = (error) => {
1160
1151
  for (const delivery of streamedDeliveries) {
1161
1152
  if (delivery.acknowledged || delivery.outcome) continue;
@@ -1209,8 +1200,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1209
1200
  session,
1210
1201
  ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
1211
1202
  });
1212
- for (const delivery of streamedDeliveries) delivery.acknowledged = true;
1213
- emitAcknowledgedStreamedDeliveries(stopResult.messageId);
1203
+ acknowledgeStoppedStreamedDeliveries(session, stopResult.messageId);
1214
1204
  observedReplyDelivery = true;
1215
1205
  usedReplyThreadTs ??= session.threadTs;
1216
1206
  return true;
@@ -1319,6 +1309,24 @@ async function dispatchPreparedSlackMessage(prepared) {
1319
1309
  rememberDeliveredThreadTs(params.kind, params.session.threadTs);
1320
1310
  return true;
1321
1311
  };
1312
+ const appendNativeProgressCompletion = async (isError) => {
1313
+ const session = streamSession;
1314
+ if (isError) nativeProgressTerminalStatus = "error";
1315
+ if (!session || nativeProgressCompletionSent) return;
1316
+ const chunks = buildNativeProgressCompletionChunks(isError ? "error" : "complete");
1317
+ if (!chunks?.length) return;
1318
+ try {
1319
+ await appendSlackStream({
1320
+ session,
1321
+ chunks
1322
+ });
1323
+ nativeProgressCompletionSent = true;
1324
+ observedReplyDelivery ||= session.delivered;
1325
+ } catch (err) {
1326
+ streamFailed = true;
1327
+ runtime.error?.(danger(`slack-stream: native progress completion failed: ${formatSlackError(err)}`));
1328
+ }
1329
+ };
1322
1330
  const deliverWithStreaming = async (params) => {
1323
1331
  if (params.payload.isReasoning === true) return;
1324
1332
  const reply = resolveSendableOutboundReplyParts(params.payload);
@@ -1344,13 +1352,6 @@ async function dispatchPreparedSlackMessage(prepared) {
1344
1352
  });
1345
1353
  return;
1346
1354
  }
1347
- if (useNativeProgressStreaming && !streamSession) {
1348
- await deliverNormally({
1349
- payload: params.payload,
1350
- kind: params.kind
1351
- });
1352
- return;
1353
- }
1354
1355
  if (!streamSession) {
1355
1356
  const streamThreadTs = replyPlan.nextThreadTs();
1356
1357
  plannedThreadTs = streamThreadTs;
@@ -1411,40 +1412,12 @@ async function dispatchPreparedSlackMessage(prepared) {
1411
1412
  logVerbose("slack-stream: suppressed duplicate append payload");
1412
1413
  return;
1413
1414
  }
1414
- const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent && (previewToolProgressLines.length > 0 || Boolean(latestPlan?.length) || Boolean(latestPlanExplanation) || [...nativeTaskState.values()].some((task) => task.status !== "complete" && task.status !== "error")) ? reconcileSlackNativeTaskChunks({
1415
- previousTasks: nativeTaskState,
1416
- chunks: buildSlackProgressStreamCompletionChunks({
1417
- title: resolveNativeProgressTitle(),
1418
- lines: resolveNativeProgressLines(),
1419
- plan: resolveNativeProgressPlan(),
1420
- maxLineChars: progressDraftMaxLineChars,
1421
- finalInProgressStatus: params.payload.isError ? "error" : "complete"
1422
- })
1423
- }).chunks : void 0;
1424
- if (useNativeProgressStreaming) {
1425
- if (completionChunks?.length) {
1426
- await appendSlackStream({
1427
- session: streamSession,
1428
- chunks: completionChunks
1429
- });
1430
- nativeProgressCompletionSent = true;
1431
- if (streamSession.delivered) observedReplyDelivery = true;
1432
- }
1433
- await deliverNormally({
1434
- payload: params.payload,
1435
- kind: params.kind,
1436
- forcedThreadTs: streamSession.threadTs
1437
- });
1438
- return;
1439
- }
1440
1415
  if (text) recordStreamedDelivery(params.kind, text);
1441
1416
  await appendSlackStream({
1442
1417
  session: streamSession,
1443
- text: "\n" + text,
1444
- chunks: completionChunks
1418
+ text: "\n" + text
1445
1419
  });
1446
1420
  refreshStreamedAcknowledgements(streamSession);
1447
- if (completionChunks?.length) nativeProgressCompletionSent = true;
1448
1421
  if (streamSession.delivered) {
1449
1422
  observedReplyDelivery = true;
1450
1423
  if (params.kind === "final") observedFinalReplyDelivery = true;
@@ -1491,13 +1464,59 @@ async function dispatchPreparedSlackMessage(prepared) {
1491
1464
  await deliverNormally({
1492
1465
  payload: params.payload,
1493
1466
  kind: params.kind,
1494
- forcedThreadTs: streamSession?.threadTs ?? plannedThreadTs
1467
+ forcedThreadTs: streamSession?.threadTs ?? nativeProgressStreamThreadTs ?? plannedThreadTs
1495
1468
  });
1496
1469
  }
1497
1470
  };
1498
1471
  let draftPreviewCommitted = false;
1499
1472
  const deliverSlackPayload = async (payload, info) => {
1500
1473
  if (payload.isReasoning === true) return { visibleReplySent: false };
1474
+ if (info.kind === "final" && slackStreaming.mode === "progress" && streamMode === "status_final") {
1475
+ const hadProgressDraft = progressDraft.hasStarted;
1476
+ progressDraft.markFinalReplyStarted();
1477
+ if (useNativeProgressStreaming) {
1478
+ await waitForNativeProgressStreamStart();
1479
+ const finalThreadTs = streamSession?.threadTs ?? nativeProgressStreamThreadTs;
1480
+ await deliverNormally({
1481
+ payload,
1482
+ kind: info.kind,
1483
+ forcedThreadTs: finalThreadTs
1484
+ });
1485
+ await appendNativeProgressCompletion(payload.isError === true);
1486
+ progressDraft.markFinalReplyDelivered();
1487
+ if (!payload.isError && hadProgressDraft && streamSession) pendingNativeProgressReceipt = progressReceipt.buildSummaryLine();
1488
+ return;
1489
+ }
1490
+ if (hadProgressDraft) try {
1491
+ await draftStream?.flush();
1492
+ } catch (err) {
1493
+ logVerbose(`slack: progress draft flush before final failed (${formatSlackError(err)})`);
1494
+ }
1495
+ const receiptChannelId = hadProgressDraft ? draftStream?.channelId() : void 0;
1496
+ const receiptMessageId = hadProgressDraft ? draftStream?.messageId() : void 0;
1497
+ const draftThreadTs = hadProgressDraft ? usedReplyThreadTs ?? statusThreadTs : void 0;
1498
+ await deliverNormally({
1499
+ payload,
1500
+ kind: info.kind,
1501
+ ...draftThreadTs ? { forcedThreadTs: draftThreadTs } : {}
1502
+ });
1503
+ progressDraft.markFinalReplyDelivered();
1504
+ if (!payload.isError && receiptChannelId && receiptMessageId && !progressReceiptCollapsed) await collapseProgressReceipt({
1505
+ channelId: receiptChannelId,
1506
+ messageId: receiptMessageId,
1507
+ text: progressReceipt.buildSummaryLine(),
1508
+ threadTs: usedReplyThreadTs ?? statusThreadTs
1509
+ });
1510
+ return;
1511
+ }
1512
+ if (useNativeProgressStreaming) {
1513
+ await deliverNormally({
1514
+ payload,
1515
+ kind: info.kind,
1516
+ forcedThreadTs: streamSession?.threadTs ?? nativeProgressStreamThreadTs
1517
+ });
1518
+ return;
1519
+ }
1501
1520
  if (useStreaming) {
1502
1521
  await deliverWithStreaming({
1503
1522
  payload,
@@ -1688,8 +1707,6 @@ async function dispatchPreparedSlackMessage(prepared) {
1688
1707
  const useNativeProgressStreaming = useStreaming && slackStreaming.mode === "progress";
1689
1708
  const progressDraftActive = Boolean(draftStream) || useNativeProgressStreaming;
1690
1709
  const previewToolProgressEnabled = progressDraftActive && resolveChannelStreamingPreviewToolProgress(account.config);
1691
- const commentaryProgressEnabled = progressDraftActive && resolveChannelStreamingProgressCommentary(account.config);
1692
- const commentaryDraftEnabled = progressDraftActive && (slackStreaming.mode === "progress" ? resolveChannelStreamingProgressCommentary(account.config, previewToolProgressEnabled) : previewToolProgressEnabled);
1693
1710
  let shouldYieldDraftProgress = () => false;
1694
1711
  const suppressDefaultToolProgressMessages = resolveChannelStreamingSuppressDefaultToolProgressMessages(account.config, {
1695
1712
  draftStreamActive: Boolean(draftStream) || useNativeProgressStreaming,
@@ -1697,46 +1714,20 @@ async function dispatchPreparedSlackMessage(prepared) {
1697
1714
  previewStreamingEnabled
1698
1715
  });
1699
1716
  let previewToolProgressSuppressed = false;
1700
- let previewToolProgressLines = [];
1701
- let lastNonEmptyPreviewToolProgressLines = [];
1702
- let latestPlan;
1703
- let latestPlanExplanation;
1717
+ let legacyPreviewToolProgressLines = [];
1704
1718
  let nativeTaskState = /* @__PURE__ */ new Map();
1705
1719
  let appendRenderedText = "";
1706
1720
  let appendSourceText = "";
1707
- let reasoningProgressRawText = "";
1708
- let statusUpdateCount = 0;
1709
1721
  let nativeProgressCompletionSent = false;
1722
+ let nativeProgressTerminalStatus = "complete";
1710
1723
  let nativeProgressChunkKey;
1724
+ const progressReceipt = createChannelProgressReceiptTracker();
1725
+ let progressReceiptCollapsed = false;
1726
+ let pendingNativeProgressReceipt;
1711
1727
  const progressSeed = `${account.accountId}:${message.channel}`;
1712
1728
  const useRichProgressDraft = streamMode === "status_final" && resolveChannelProgressDraftRender(account.config) === "rich";
1713
1729
  const explicitProgressTitle = resolveExplicitSlackProgressTitle(account.config);
1714
1730
  const progressDraftMaxLineChars = resolveChannelProgressDraftMaxLineChars(account.config);
1715
- const renderProgressDraft = () => {
1716
- if (!draftStream || streamMode !== "status_final") return;
1717
- const progressLines = previewToolProgressLines.length === 0 ? lastNonEmptyPreviewToolProgressLines : previewToolProgressLines;
1718
- const previewText = formatChannelProgressDraftText({
1719
- entry: account.config,
1720
- lines: progressLines,
1721
- seed: progressSeed,
1722
- formatLine: escapeSlackMrkdwn,
1723
- narration: latestPlanExplanation,
1724
- plan: latestPlan
1725
- });
1726
- if (!previewText) return;
1727
- const richProgressBlocks = useRichProgressDraft ? buildSlackProgressDraftBlocks({
1728
- title: explicitProgressTitle,
1729
- lines: progressLines,
1730
- plan: latestPlan,
1731
- narration: latestPlanExplanation,
1732
- maxLineChars: resolveChannelProgressDraftMaxLineChars(account.config)
1733
- }) : void 0;
1734
- draftStream.update(useRichProgressDraft && richProgressBlocks ? {
1735
- text: previewText,
1736
- blocks: richProgressBlocks
1737
- } : previewText);
1738
- hasStreamedMessage = true;
1739
- };
1740
1731
  const waitForNativeProgressStreamStart = async () => {
1741
1732
  if (streamSession || !nativeProgressStreamStartPromise) return true;
1742
1733
  try {
@@ -1747,26 +1738,40 @@ async function dispatchPreparedSlackMessage(prepared) {
1747
1738
  }
1748
1739
  return !streamFailed;
1749
1740
  };
1750
- const resolveNativeProgressPlan = () => latestPlan?.length ? latestPlan : void 0;
1751
- const resolveNativeProgressLines = () => {
1752
- if (latestPlan?.length || !latestPlanExplanation) return previewToolProgressLines;
1741
+ const resolveStructuredProgressLines = (lines) => lines.map((line) => {
1742
+ if (typeof line !== "string") return line;
1743
+ const reasoning = line.startsWith("🧠 ");
1744
+ const text = line.replace(/^(?:🧠|💬)\s+/u, "").replace(/^_(.*)_$/su, "$1").trim();
1745
+ return {
1746
+ ...reasoning ? { id: "reasoning" } : {},
1747
+ kind: "item",
1748
+ text,
1749
+ label: reasoning ? "Reasoning" : "Update",
1750
+ prefix: false
1751
+ };
1752
+ });
1753
+ const resolveNativeProgressPlan = (snapshot) => snapshot.plan?.length ? snapshot.plan : void 0;
1754
+ const resolveNativeProgressLines = (snapshot) => {
1755
+ const lines = resolveStructuredProgressLines(snapshot.lines);
1756
+ if (snapshot.plan?.length || !snapshot.planExplanation) return lines;
1753
1757
  const explanationLine = buildChannelProgressDraftLine({
1754
1758
  event: "plan",
1755
1759
  phase: "update",
1756
- explanation: latestPlanExplanation
1760
+ explanation: snapshot.planExplanation
1757
1761
  });
1758
- return explanationLine ? [...previewToolProgressLines, explanationLine] : previewToolProgressLines;
1762
+ return explanationLine ? [...lines, explanationLine] : lines;
1759
1763
  };
1760
- const resolveNativeProgressTitle = () => explicitProgressTitle && latestPlanExplanation ? `${explicitProgressTitle} — ${latestPlanExplanation}` : explicitProgressTitle ?? latestPlanExplanation;
1761
- const buildNativeProgressChunks = () => streamSession ? buildSlackProgressStreamUpdateChunks({
1762
- title: resolveNativeProgressTitle(),
1763
- lines: resolveNativeProgressLines(),
1764
- plan: resolveNativeProgressPlan(),
1764
+ const combineProgressHeadlineAndExplanation = (headline, explanation) => headline && explanation && headline !== explanation ? `${headline} — ${explanation}` : headline ?? explanation;
1765
+ const resolveNativeProgressTitle = (snapshot) => combineProgressHeadlineAndExplanation(explicitProgressTitle ?? snapshot.statusHeadline, snapshot.planExplanation);
1766
+ const buildNativeProgressChunks = (snapshot) => streamSession ? buildSlackProgressStreamUpdateChunks({
1767
+ title: resolveNativeProgressTitle(snapshot),
1768
+ lines: resolveNativeProgressLines(snapshot),
1769
+ plan: resolveNativeProgressPlan(snapshot),
1765
1770
  maxLineChars: progressDraftMaxLineChars
1766
1771
  }) : buildSlackProgressStreamStartChunks({
1767
- title: resolveNativeProgressTitle(),
1768
- lines: resolveNativeProgressLines(),
1769
- plan: resolveNativeProgressPlan(),
1772
+ title: resolveNativeProgressTitle(snapshot),
1773
+ lines: resolveNativeProgressLines(snapshot),
1774
+ plan: resolveNativeProgressPlan(snapshot),
1770
1775
  maxLineChars: progressDraftMaxLineChars
1771
1776
  });
1772
1777
  const markNativeProgressDelivered = (session, threadTs) => {
@@ -1824,12 +1829,14 @@ async function dispatchPreparedSlackMessage(prepared) {
1824
1829
  nativeProgressChunkKey = chunkKey;
1825
1830
  };
1826
1831
  const updateNativeProgressStream = async () => {
1832
+ const snapshot = progressDraft.getSnapshot();
1833
+ const progressLines = resolveNativeProgressLines(snapshot);
1827
1834
  const hasRetirableNativeTasks = [...nativeTaskState.values()].some((task) => task.status !== "complete" && task.status !== "error");
1828
- if (!useNativeProgressStreaming || streamFailed || previewToolProgressLines.length === 0 && !latestPlan?.length && !latestPlanExplanation && !hasRetirableNativeTasks) return;
1835
+ if (!useNativeProgressStreaming || streamFailed || progressLines.length === 0 && !snapshot.plan?.length && !snapshot.statusHeadline && !explicitProgressTitle && !hasRetirableNativeTasks) return;
1829
1836
  if (!await waitForNativeProgressStreamStart()) return;
1830
1837
  const reconciled = reconcileSlackNativeTaskChunks({
1831
1838
  previousTasks: nativeTaskState,
1832
- chunks: buildNativeProgressChunks()
1839
+ chunks: buildNativeProgressChunks(snapshot)
1833
1840
  });
1834
1841
  const chunks = reconciled.chunks;
1835
1842
  if (!chunks?.length) return;
@@ -1844,85 +1851,154 @@ async function dispatchPreparedSlackMessage(prepared) {
1844
1851
  streamFailed = true;
1845
1852
  }
1846
1853
  };
1847
- const progressDraftGate = createChannelProgressDraftGate({ onStart: useNativeProgressStreaming ? updateNativeProgressStream : renderProgressDraft });
1848
- const pushPlanProgress = async (steps, explanation) => {
1849
- if (previewToolProgressSuppressed) return;
1850
- latestPlan = steps ? steps.map((entry) => ({ ...entry })) : void 0;
1851
- latestPlanExplanation = explanation?.replace(/\s+/g, " ").trim() || void 0;
1852
- if (!draftStream && !useNativeProgressStreaming) return;
1853
- if (streamMode !== "status_final" && !useNativeProgressStreaming) {
1854
- const text = formatChannelProgressDraftText({
1855
- entry: account.config,
1856
- lines: previewToolProgressLines,
1857
- seed: progressSeed,
1858
- formatLine: escapeSlackMrkdwn,
1859
- narration: latestPlanExplanation,
1860
- plan: latestPlan
1854
+ const resetProgressTurnState = () => {
1855
+ progressReceipt.reset();
1856
+ progressReceiptCollapsed = false;
1857
+ pendingNativeProgressReceipt = void 0;
1858
+ };
1859
+ const collapseProgressReceipt = async (params) => {
1860
+ const { botToken: token } = ctx;
1861
+ try {
1862
+ await finalizeSlackPreviewEdit({
1863
+ client: slackClient,
1864
+ token,
1865
+ accountId: account.accountId,
1866
+ ...params
1861
1867
  });
1862
- if (text) {
1863
- draftStream?.update(text);
1864
- hasStreamedMessage = true;
1868
+ progressReceiptCollapsed = true;
1869
+ } catch (err) {
1870
+ logVerbose(`slack: progress receipt edit failed (${formatSlackError(err)})`);
1871
+ }
1872
+ };
1873
+ const progressDraft = createChannelProgressDraftCompositor({
1874
+ entry: account.config,
1875
+ mode: slackStreaming.mode,
1876
+ active: progressDraftActive && streamMode === "status_final",
1877
+ seed: progressSeed,
1878
+ formatLine: escapeSlackMrkdwn,
1879
+ reasoningLinePrefix: "🧠 ",
1880
+ commentaryLinePrefix: "💬 ",
1881
+ reasoningGate: previewToolProgressEnabled,
1882
+ commentaryItalics: false,
1883
+ updateOnLineChange: useNativeProgressStreaming || useRichProgressDraft,
1884
+ update: async (previewText, options) => {
1885
+ if (useNativeProgressStreaming) {
1886
+ await updateNativeProgressStream();
1887
+ return;
1865
1888
  }
1866
- return;
1889
+ if (!draftStream) return;
1890
+ const snapshot = progressDraft.getSnapshot();
1891
+ const structuredLines = resolveStructuredProgressLines(options?.lines ?? snapshot.lines);
1892
+ const richNarration = combineProgressHeadlineAndExplanation(snapshot.statusHeadline, snapshot.planExplanation);
1893
+ const richProgressBlocks = useRichProgressDraft ? buildSlackProgressDraftBlocks({
1894
+ title: explicitProgressTitle,
1895
+ lines: structuredLines,
1896
+ plan: snapshot.plan,
1897
+ narration: richNarration,
1898
+ maxLineChars: progressDraftMaxLineChars
1899
+ }) : void 0;
1900
+ draftStream.update(useRichProgressDraft && richProgressBlocks ? {
1901
+ text: previewText,
1902
+ blocks: richProgressBlocks
1903
+ } : previewText);
1904
+ hasStreamedMessage = true;
1905
+ if (options?.flush) await draftStream.flush();
1867
1906
  }
1868
- const alreadyStarted = progressDraftGate.hasStarted;
1869
- await progressDraftGate.startNow();
1870
- if (alreadyStarted && progressDraftGate.hasStarted) await refreshStartedProgressDraft();
1907
+ });
1908
+ const commentaryProgressEnabled = progressDraft.commentaryProgressEnabled;
1909
+ const buildNativeProgressCompletionChunks = (finalInProgressStatus) => {
1910
+ const snapshot = progressDraft.getSnapshot();
1911
+ const lines = resolveNativeProgressLines(snapshot);
1912
+ const hasRetirableNativeTasks = [...nativeTaskState.values()].some((task) => task.status !== "complete" && task.status !== "error");
1913
+ if (lines.length === 0 && !snapshot.plan?.length && !hasRetirableNativeTasks) return;
1914
+ return reconcileSlackNativeTaskChunks({
1915
+ previousTasks: nativeTaskState,
1916
+ chunks: buildSlackProgressStreamCompletionChunks({
1917
+ title: resolveNativeProgressTitle(snapshot),
1918
+ lines,
1919
+ plan: resolveNativeProgressPlan(snapshot),
1920
+ maxLineChars: progressDraftMaxLineChars,
1921
+ finalInProgressStatus
1922
+ })
1923
+ }).chunks;
1871
1924
  };
1872
- const refreshStartedProgressDraft = async () => {
1873
- if (useNativeProgressStreaming) await updateNativeProgressStream();
1874
- else renderProgressDraft();
1925
+ const finishNativeProgressTurn = async (completionChunks) => {
1926
+ if (nativeProgressStreamStartPromise) await nativeProgressStreamStartPromise.catch(() => null);
1927
+ const session = streamSession;
1928
+ if (session && !session.stopped) try {
1929
+ if (completionChunks?.length) nativeProgressCompletionSent = true;
1930
+ const stopResult = await stopSlackStream({
1931
+ session,
1932
+ ...completionChunks?.length ? { chunks: completionChunks } : {},
1933
+ ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
1934
+ });
1935
+ acknowledgeStoppedStreamedDeliveries(session, stopResult?.messageId);
1936
+ if (pendingNativeProgressReceipt && stopResult?.messageId) await collapseProgressReceipt({
1937
+ channelId: session.channel,
1938
+ messageId: stopResult.messageId,
1939
+ text: pendingNativeProgressReceipt,
1940
+ threadTs: session.threadTs
1941
+ });
1942
+ } catch (err) {
1943
+ const error = formatSlackError(err);
1944
+ emitAcknowledgedStreamedDeliveries();
1945
+ emitFailedPendingStreamedDeliveries(error);
1946
+ logVerbose(`slack-stream: failed to rotate native progress stream (${error})`);
1947
+ }
1948
+ streamSession = null;
1949
+ nativeProgressStreamStartPromise = null;
1950
+ nativeProgressStreamThreadTs = void 0;
1951
+ streamFailed = false;
1875
1952
  };
1876
- const pushPreviewProgress = async (line, options) => {
1877
- const lane = options?.lane ?? "tool";
1878
- const progressEnabled = lane === "commentary" ? commentaryDraftEnabled : previewToolProgressEnabled;
1879
- if (!draftStream && !useNativeProgressStreaming) return;
1880
- if (lane === "commentary" && commentaryProgressEnabled && shouldYieldDraftProgress()) return;
1881
- if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
1882
- const normalized = line?.text.replace(/\s+/g, " ").trim();
1883
- if (!line || !normalized) {
1884
- if (streamMode !== "status_final") return;
1885
- const alreadyStarted = progressDraftGate.hasStarted;
1886
- const progressActive = await progressDraftGate.noteWork();
1887
- if ((alreadyStarted || progressActive) && progressDraftGate.hasStarted) await refreshStartedProgressDraft();
1953
+ const pushPlanProgress = async (steps, explanation) => {
1954
+ if (streamMode === "status_final") {
1955
+ await progressDraft.pushPlanProgress(steps, { explanation });
1888
1956
  return;
1889
1957
  }
1890
- if (streamMode !== "status_final") {
1891
- if (!progressEnabled || previewToolProgressSuppressed) return;
1892
- const nextLines = mergeChannelProgressDraftLine(previewToolProgressLines, line, { maxLines: resolveChannelProgressDraftMaxLines(account.config) });
1893
- if (nextLines === previewToolProgressLines) return;
1894
- previewToolProgressLines = nextLines;
1895
- draftStream?.update(formatChannelProgressDraftText({
1896
- entry: account.config,
1897
- lines: previewToolProgressLines,
1898
- seed: progressSeed,
1899
- formatLine: escapeSlackMrkdwn
1900
- }));
1958
+ if (previewToolProgressSuppressed || !draftStream) return;
1959
+ const text = formatChannelProgressDraftText({
1960
+ entry: account.config,
1961
+ lines: legacyPreviewToolProgressLines,
1962
+ seed: progressSeed,
1963
+ formatLine: escapeSlackMrkdwn,
1964
+ narration: explanation,
1965
+ plan: steps
1966
+ });
1967
+ if (text) {
1968
+ draftStream.update(text);
1901
1969
  hasStreamedMessage = true;
1902
- return;
1903
- }
1904
- if (progressEnabled && !previewToolProgressSuppressed) {
1905
- previewToolProgressLines = mergeChannelProgressDraftLine(previewToolProgressLines, line, { maxLines: resolveChannelProgressDraftMaxLines(account.config) });
1906
- if (previewToolProgressLines.length > 0) lastNonEmptyPreviewToolProgressLines = previewToolProgressLines;
1907
1970
  }
1908
- if (useNativeProgressStreaming) {
1909
- if (progressDraftGate.hasStarted) await updateNativeProgressStream();
1910
- else {
1911
- await progressDraftGate.startNow();
1912
- if (progressDraftGate.hasStarted) await updateNativeProgressStream();
1971
+ };
1972
+ const pushPreviewProgress = async (line, options) => {
1973
+ if (!draftStream && !useNativeProgressStreaming) return;
1974
+ if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
1975
+ const normalized = line?.text.replace(/\s+/g, " ").trim();
1976
+ if (streamMode === "status_final") {
1977
+ if (!line || !normalized) {
1978
+ await progressDraft.noteActivity();
1979
+ return;
1913
1980
  }
1981
+ await progressDraft.pushToolProgress(line, options);
1914
1982
  return;
1915
1983
  }
1916
- const alreadyStarted = progressDraftGate.hasStarted;
1917
- const progressActive = await progressDraftGate.noteWork();
1918
- if ((alreadyStarted || progressActive) && progressDraftGate.hasStarted) await refreshStartedProgressDraft();
1984
+ if (!line || !normalized || !draftStream || !previewToolProgressEnabled || previewToolProgressSuppressed) return;
1985
+ const nextLines = mergeChannelProgressDraftLine(legacyPreviewToolProgressLines, line, { maxLines: resolveChannelProgressDraftMaxLines(account.config) });
1986
+ if (nextLines === legacyPreviewToolProgressLines) return;
1987
+ legacyPreviewToolProgressLines = nextLines;
1988
+ draftStream.update(formatChannelProgressDraftText({
1989
+ entry: account.config,
1990
+ lines: legacyPreviewToolProgressLines,
1991
+ seed: progressSeed,
1992
+ formatLine: escapeSlackMrkdwn
1993
+ }));
1994
+ hasStreamedMessage = true;
1919
1995
  };
1920
1996
  const updateDraftFromPartial = (text) => {
1921
1997
  const trimmed = text?.trimEnd();
1922
1998
  if (!trimmed) return;
1923
1999
  if (streamMode === "append") {
1924
2000
  previewToolProgressSuppressed = true;
1925
- previewToolProgressLines = [];
2001
+ legacyPreviewToolProgressLines = [];
1926
2002
  const next = applyAppendOnlyStreamUpdate({
1927
2003
  incoming: trimmed,
1928
2004
  rendered: appendRenderedText,
@@ -1935,74 +2011,86 @@ async function dispatchPreparedSlackMessage(prepared) {
1935
2011
  hasStreamedMessage = true;
1936
2012
  return;
1937
2013
  }
1938
- if (streamMode === "status_final") {
1939
- if (!progressDraftGate.hasStarted) return;
1940
- statusUpdateCount += 1;
1941
- if (statusUpdateCount > 1 && statusUpdateCount % 4 !== 0) return;
1942
- renderProgressDraft();
1943
- return;
1944
- }
2014
+ if (streamMode === "status_final") return;
1945
2015
  previewToolProgressSuppressed = true;
1946
- previewToolProgressLines = [];
2016
+ legacyPreviewToolProgressLines = [];
1947
2017
  draftStream?.update(trimmed);
1948
2018
  hasStreamedMessage = true;
1949
2019
  };
1950
2020
  const pushReasoningProgress = async (payload) => {
1951
2021
  if (!payload?.text) return;
1952
- reasoningProgressRawText = mergeSlackReasoningProgressText(reasoningProgressRawText, payload.text, { snapshot: payload.isReasoningSnapshot === true });
1953
- const normalized = normalizeSlackReasoningProgressLine(reasoningProgressRawText);
1954
- if (!normalized) return;
1955
- await pushPreviewProgress({
1956
- id: "reasoning",
1957
- kind: "item",
1958
- text: normalized,
1959
- label: "Reasoning"
1960
- });
2022
+ if (streamMode !== "status_final") {
2023
+ const normalized = progressDraft.mergeReasoningProgress(payload.text, { snapshot: payload.isReasoningSnapshot === true }).replace(/^_(.*)_$/su, "$1").trim();
2024
+ if (!normalized) return;
2025
+ await pushPreviewProgress({
2026
+ id: "reasoning",
2027
+ kind: "item",
2028
+ text: normalized,
2029
+ label: "Reasoning"
2030
+ });
2031
+ return;
2032
+ }
2033
+ progressReceipt.noteReasoning();
2034
+ await progressDraft.pushReasoningProgress(payload.text, { snapshot: payload.isReasoningSnapshot === true });
1961
2035
  };
1962
2036
  const resetDraftDeliveryState = () => {
1963
2037
  hasStreamedMessage = false;
1964
2038
  appendRenderedText = "";
1965
2039
  appendSourceText = "";
1966
- statusUpdateCount = 0;
1967
2040
  };
1968
2041
  const resetDraftProgressState = () => {
1969
- reasoningProgressRawText = "";
2042
+ progressDraft.resetReasoningProgress();
1970
2043
  previewToolProgressSuppressed = false;
1971
- previewToolProgressLines = [];
2044
+ legacyPreviewToolProgressLines = [];
2045
+ };
2046
+ const beginNewProgressTurn = async (options) => {
2047
+ const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent ? buildNativeProgressCompletionChunks(nativeProgressTerminalStatus) : void 0;
2048
+ if (!progressDraft.beginNewTurn(options)) return false;
2049
+ if (useNativeProgressStreaming) await finishNativeProgressTurn(completionChunks);
2050
+ else draftStream?.forceNewMessage();
2051
+ resetProgressTurnState();
2052
+ nativeTaskState = /* @__PURE__ */ new Map();
2053
+ nativeProgressCompletionSent = false;
2054
+ nativeProgressTerminalStatus = "complete";
2055
+ nativeProgressChunkKey = void 0;
2056
+ draftPreviewCommitted = false;
2057
+ observedFinalReplyDelivery = false;
2058
+ deliveryTracker = createSlackEventDeliveryTracker();
2059
+ progressReceiptCollapsed = false;
2060
+ return true;
1972
2061
  };
1973
- const onDraftBoundary = !shouldUseDraftStream ? void 0 : async () => {
1974
- if (hasStreamedMessage && streamMode !== "status_final") {
1975
- draftStream?.forceNewMessage();
1976
- resetDraftDeliveryState();
2062
+ const onDraftBoundary = !shouldUseDraftStream && !useNativeProgressStreaming ? void 0 : async () => {
2063
+ if (streamMode === "status_final") {
2064
+ await beginNewProgressTurn();
2065
+ return;
1977
2066
  }
2067
+ if (hasStreamedMessage) draftStream?.forceNewMessage();
2068
+ resetDraftDeliveryState();
1978
2069
  resetDraftProgressState();
1979
2070
  };
1980
- const onQueuedFollowupAdmitted = !shouldUseDraftStream ? void 0 : async () => {
2071
+ const onQueuedFollowupAdmitted = !shouldUseDraftStream && !useNativeProgressStreaming ? void 0 : async () => {
1981
2072
  await draftStream?.flush();
1982
- draftStream?.forceNewMessage();
1983
2073
  draftPreviewCommitted = false;
1984
2074
  observedFinalReplyDelivery = false;
2075
+ if (streamMode === "status_final") await beginNewProgressTurn({ force: true });
2076
+ else draftStream?.forceNewMessage();
1985
2077
  deliveryTracker = createSlackEventDeliveryTracker();
1986
2078
  resetDraftDeliveryState();
1987
2079
  resetDraftProgressState();
1988
- latestPlan = void 0;
1989
- latestPlanExplanation = void 0;
1990
- nativeTaskState = /* @__PURE__ */ new Map();
1991
2080
  };
1992
2081
  let dispatchError;
1993
2082
  let queuedFinal = false;
1994
2083
  let counts = {};
1995
2084
  try {
1996
- const turnResult = await dispatchChannelInboundReply({
2085
+ const turnResult = await dispatchChannelInboundTurn({
1997
2086
  cfg,
1998
2087
  channel: "slack",
1999
2088
  accountId: route.accountId,
2000
- agentId: route.agentId,
2001
- routeSessionKey: route.sessionKey,
2002
- storePath: prepared.turn.storePath,
2089
+ route: {
2090
+ agentId: route.agentId,
2091
+ sessionKey: route.sessionKey
2092
+ },
2003
2093
  ctxPayload: prepared.ctxPayload,
2004
- recordInboundSession,
2005
- dispatchReplyWithBufferedBlockDispatcher,
2006
2094
  dispatcherOptions: {
2007
2095
  ...replyPipeline,
2008
2096
  humanDelay: resolveHumanDelayConfig(cfg, route.agentId)
@@ -2015,6 +2103,7 @@ async function dispatchPreparedSlackMessage(prepared) {
2015
2103
  history: prepared.turn.history,
2016
2104
  botLoopProtection: resolveSlackBotLoopProtection(prepared),
2017
2105
  replyOptions: {
2106
+ ...prepared.turnAdoptionLifecycle ? { turnAdoptionLifecycle: prepared.turnAdoptionLifecycle } : {},
2018
2107
  skillFilter: prepared.channelConfig?.skills,
2019
2108
  sourceReplyDeliveryMode,
2020
2109
  suppressTyping: suppressRoomEventTyping ? true : void 0,
@@ -2023,6 +2112,8 @@ async function dispatchPreparedSlackMessage(prepared) {
2023
2112
  onModelSelected,
2024
2113
  suppressDefaultToolProgressMessages: suppressDefaultToolProgressMessages ? true : void 0,
2025
2114
  commentaryProgressEnabled: commentaryProgressEnabled ? true : void 0,
2115
+ progressPreambleEnabled: progressDraftActive && slackStreaming.mode === "progress" ? true : void 0,
2116
+ commentaryPayloadsEnabled: commentaryProgressEnabled ? true : void 0,
2026
2117
  onVerboseProgressVisibility: commentaryProgressEnabled ? (isActive) => {
2027
2118
  shouldYieldDraftProgress = isActive;
2028
2119
  } : void 0,
@@ -2032,7 +2123,10 @@ async function dispatchPreparedSlackMessage(prepared) {
2032
2123
  updateDraftFromPartial(payload.text);
2033
2124
  },
2034
2125
  onAssistantMessageStart: onDraftBoundary,
2035
- onReasoningEnd: onDraftBoundary,
2126
+ onReasoningEnd: async () => {
2127
+ progressReceipt.closeReasoning();
2128
+ await onDraftBoundary?.();
2129
+ },
2036
2130
  onQueuedFollowupAdmitted,
2037
2131
  onReasoningStream: statusReactionsEnabled || previewToolProgressEnabled ? async (payload) => {
2038
2132
  await pushReasoningProgress(payload);
@@ -2041,6 +2135,7 @@ async function dispatchPreparedSlackMessage(prepared) {
2041
2135
  } : void 0,
2042
2136
  onToolStart: async (payload) => {
2043
2137
  if (statusReactionsEnabled) await statusReactions.setTool(payload.name);
2138
+ if (payload.phase === "start") progressReceipt.noteToolCall(payload.name);
2044
2139
  await pushPreviewProgress(buildChannelProgressDraftLineForEntry(account.config, {
2045
2140
  event: "tool",
2046
2141
  itemId: payload.itemId,
@@ -2051,6 +2146,14 @@ async function dispatchPreparedSlackMessage(prepared) {
2051
2146
  }, payload.detailMode ? { detailMode: payload.detailMode } : void 0), { toolName: payload.name });
2052
2147
  },
2053
2148
  onItemEvent: async (payload) => {
2149
+ if (streamMode === "status_final" && payload.kind === "preamble") {
2150
+ if (shouldYieldDraftProgress()) return;
2151
+ await progressDraft.pushPreambleHeadline(payload.progressText, { itemId: payload.itemId });
2152
+ if (commentaryProgressEnabled) {
2153
+ if (await progressDraft.pushCommentaryProgress(payload.progressText, { itemId: payload.itemId })) progressReceipt.noteCommentary(payload.itemId, payload.progressText);
2154
+ }
2155
+ return;
2156
+ }
2054
2157
  await pushPreviewProgress(buildChannelProgressDraftLineForEntry(account.config, {
2055
2158
  event: "item",
2056
2159
  itemId: payload.itemId,
@@ -2063,11 +2166,11 @@ async function dispatchPreparedSlackMessage(prepared) {
2063
2166
  summary: payload.summary,
2064
2167
  progressText: payload.progressText,
2065
2168
  meta: payload.meta
2066
- }), payload.kind === "preamble" ? { lane: "commentary" } : void 0);
2169
+ }));
2067
2170
  },
2068
2171
  onPlanUpdate: async (payload) => {
2069
2172
  if (payload.phase !== "update") return;
2070
- await pushPlanProgress(payload.planSteps, payload.explanation);
2173
+ await pushPlanProgress(payload.steps, payload.explanation);
2071
2174
  },
2072
2175
  onApprovalEvent: async (payload) => {
2073
2176
  if (payload.phase !== "requested") return;
@@ -2118,30 +2221,26 @@ async function dispatchPreparedSlackMessage(prepared) {
2118
2221
  } catch (err) {
2119
2222
  dispatchError = err;
2120
2223
  } finally {
2121
- progressDraftGate.cancel();
2224
+ progressDraft.cancel();
2122
2225
  await draftStream?.discardPending();
2123
2226
  }
2124
2227
  let streamFallbackDelivered = false;
2125
2228
  const finalStream = streamSession;
2126
2229
  if (finalStream && !finalStream.stopped) try {
2127
- const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent && (previewToolProgressLines.length > 0 || Boolean(latestPlan?.length) || Boolean(latestPlanExplanation) || [...nativeTaskState.values()].some((task) => task.status !== "complete" && task.status !== "error")) ? reconcileSlackNativeTaskChunks({
2128
- previousTasks: nativeTaskState,
2129
- chunks: buildSlackProgressStreamCompletionChunks({
2130
- title: resolveNativeProgressTitle(),
2131
- lines: resolveNativeProgressLines(),
2132
- plan: resolveNativeProgressPlan(),
2133
- maxLineChars: progressDraftMaxLineChars,
2134
- finalInProgressStatus: dispatchError ? "error" : "complete"
2135
- })
2136
- }).chunks : void 0;
2230
+ const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent ? buildNativeProgressCompletionChunks(dispatchError ? "error" : nativeProgressTerminalStatus) : void 0;
2137
2231
  if (completionChunks?.length) nativeProgressCompletionSent = true;
2138
2232
  const stopResult = await stopSlackStream({
2139
2233
  session: finalStream,
2140
2234
  ...completionChunks?.length ? { chunks: completionChunks } : {},
2141
2235
  ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
2142
2236
  });
2143
- for (const delivery of streamedDeliveries) delivery.acknowledged = true;
2144
- emitAcknowledgedStreamedDeliveries(stopResult?.messageId);
2237
+ acknowledgeStoppedStreamedDeliveries(finalStream, stopResult?.messageId);
2238
+ if (pendingNativeProgressReceipt && stopResult?.messageId && !progressReceiptCollapsed) await collapseProgressReceipt({
2239
+ channelId: finalStream.channel,
2240
+ messageId: stopResult.messageId,
2241
+ text: pendingNativeProgressReceipt,
2242
+ threadTs: finalStream.threadTs
2243
+ });
2145
2244
  } catch (err) {
2146
2245
  if (err instanceof SlackStreamNotDeliveredError) {
2147
2246
  streamFallbackDelivered = await deliverPendingStreamFallback(finalStream, err);
@@ -2321,7 +2420,7 @@ async function discardSlackPreflightMedia(media) {
2321
2420
  const SLACK_MENTION_RESOLUTION_CONCURRENCY = 4;
2322
2421
  const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
2323
2422
  const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
2324
- const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-BZiuSTIq.js").then((n) => n.g));
2423
+ const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-CF0tUY5q.js").then((n) => n.g));
2325
2424
  function collectUniqueSlackMentionIds$1(texts) {
2326
2425
  const seen = /* @__PURE__ */ new Set();
2327
2426
  const mentionIds = [];
@@ -2743,7 +2842,7 @@ function formatSlackThreadLabelSnippet(text) {
2743
2842
  }
2744
2843
  //#endregion
2745
2844
  //#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
2746
- const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-BZiuSTIq.js").then((n) => n.g));
2845
+ const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-CF0tUY5q.js").then((n) => n.g));
2747
2846
  const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
2748
2847
  function resolveSlackThreadSessionFreshness(params) {
2749
2848
  return params.ctx.channelRuntime?.session?.resolveEntryResetFreshness?.({
@@ -2758,14 +2857,21 @@ function resolveSlackThreadSessionFreshness(params) {
2758
2857
  });
2759
2858
  }
2760
2859
  function isSlackThreadContextSenderAllowed(params) {
2761
- if (params.allowFromLower.length === 0 || params.botId) return true;
2762
- if (!params.userId) return false;
2763
- return resolveSlackAllowListMatch({
2764
- allowList: params.allowFromLower,
2765
- id: params.userId,
2766
- name: params.userName,
2767
- allowNameMatching: params.allowNameMatching
2768
- }).allowed;
2860
+ return resolveInboundSupplementalSenderAllowed({
2861
+ isGroup: true,
2862
+ groupPolicy: params.allowFromLower.length === 0 ? "open" : "allowlist",
2863
+ allowFrom: params.allowFromLower,
2864
+ isSenderAllowed: (allowFrom) => {
2865
+ if (params.botId) return true;
2866
+ if (!params.userId) return false;
2867
+ return resolveSlackAllowListMatch({
2868
+ allowList: allowFrom,
2869
+ id: params.userId,
2870
+ name: params.userName,
2871
+ allowNameMatching: params.allowNameMatching
2872
+ }).allowed;
2873
+ }
2874
+ });
2769
2875
  }
2770
2876
  async function resolveSlackThreadUserMap(params) {
2771
2877
  const uniqueUserIds = [];
@@ -3458,7 +3564,7 @@ async function prepareSlackMessage(params) {
3458
3564
  name: void 0
3459
3565
  });
3460
3566
  let implicitMentionKinds = [];
3461
- if (!isDirectMessage && ctx.botUserId && message.thread_ts && !ctx.threadRequireExplicitMention && !wasMentioned) {
3567
+ if (!isDirectMessage && ctx.botUserId && message.thread_ts && !wasMentioned) {
3462
3568
  const replyToBotKinds = implicitMentionKindWhen("reply_to_bot", message.parent_user_id === ctx.botUserId);
3463
3569
  implicitMentionKinds = replyToBotKinds.length > 0 ? replyToBotKinds : implicitMentionKindWhen("bot_thread_participant", await hasSlackThreadParticipationWithPersistence({
3464
3570
  accountId: account.accountId,
@@ -3554,6 +3660,11 @@ async function prepareSlackMessage(params) {
3554
3660
  surface: "slack"
3555
3661
  });
3556
3662
  const shouldRequireMention = isRoom ? channelConfig?.requireMention ?? ctx.defaultRequireMention : false;
3663
+ const implicitMentions = resolveChannelImplicitMentions({
3664
+ cfg,
3665
+ channel: "slack",
3666
+ accountId: account.accountId
3667
+ });
3557
3668
  if (message["_ambiguousThreadReply"]) {
3558
3669
  ctx.logger.info({
3559
3670
  channel: message.channel,
@@ -3586,7 +3697,7 @@ async function prepareSlackMessage(params) {
3586
3697
  activation: {
3587
3698
  requireMention: shouldRequireMention,
3588
3699
  allowTextCommands,
3589
- ...ctx.threadRequireExplicitMention ? { allowedImplicitMentionKinds: [] } : {}
3700
+ implicitMentions
3590
3701
  }
3591
3702
  });
3592
3703
  const senderGate = messageIngress.senderAccess.gate;
@@ -3698,7 +3809,7 @@ async function prepareSlackMessage(params) {
3698
3809
  allowTextCommands,
3699
3810
  hasControlCommand: hasControlCommandInMessage,
3700
3811
  commandAuthorized,
3701
- ...ctx.threadRequireExplicitMention ? { allowedImplicitMentionKinds: [] } : {}
3812
+ implicitMentions
3702
3813
  }
3703
3814
  });
3704
3815
  const effectiveWasMentioned = mentionDecision.effectiveWasMentioned;
@@ -3926,6 +4037,7 @@ async function prepareSlackMessage(params) {
3926
4037
  },
3927
4038
  route: {
3928
4039
  agentId: route.agentId,
4040
+ dmScope: route.dmScope,
3929
4041
  accountId: route.accountId,
3930
4042
  routeSessionKey: sessionKey,
3931
4043
  parentSessionKey: threadKeys.parentSessionKey