@openclaw/slack 2026.7.2-beta.1 → 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 (61) 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-p6M2ULpv.js → action-runtime-DqkRwO3Z.js} +14 -17
  7. package/dist/action-runtime.runtime-DEWOcFM4.js +2 -0
  8. package/dist/{actions-DkTcWmpT.js → actions-CF0tUY5q.js} +5 -9
  9. package/dist/{actions.runtime-BSxpwgUc.js → actions.runtime-hCbx4RXg.js} +1 -1
  10. package/dist/api.js +13 -14
  11. package/dist/{approval-handler.runtime-UGbnz9OC.js → approval-handler.runtime-Em8I1-8-.js} +2 -2
  12. package/dist/{channel-B0_j6C6z.js → channel-4-TRi1D6.js} +75 -41
  13. package/dist/channel-config-api.js +1 -1
  14. package/dist/channel-plugin-api.js +1 -1
  15. package/dist/{channel-type-BpeTezY4.js → channel-type-Cu1yjNg2.js} +3 -3
  16. package/dist/{channel.setup-BQFut21T.js → channel.setup-CgkADtZg.js} +6 -4
  17. package/dist/{config-schema-DYI01PV2.js → config-schema-XjJGfwQC.js} +18 -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-DX7yFsM-.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-C-b2ZnSl.js → group-policy-Bw-65m3V.js} +94 -135
  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-BN_uuvrQ.js → monitor-CNFeF30h.js} +3 -3
  29. package/dist/{outbound-adapter-ECl0go7d.js → outbound-adapter-CVi2uok0.js} +49 -5
  30. package/dist/{pipeline.runtime-f0eJJoJL.js → pipeline.runtime-CTvokbty.js} +485 -251
  31. package/dist/{policy-f8b0edSi.js → policy-b_dOPDCq.js} +7 -7
  32. package/dist/probe-Borhn9PQ.js +340 -0
  33. package/dist/{provider-Ds-y-imP.js → provider-B-HVL5ZE.js} +831 -329
  34. package/dist/{relay-source-B-6LFUdz.js → relay-source-Ea_P9VcO.js} +6 -11
  35. package/dist/{replies-BMvf3ihj.js → replies-B4bOdrYb.js} +3 -3
  36. package/dist/{resolve-channels-DOMgNCWb.js → resolve-channels-KTc568SU.js} +1 -1
  37. package/dist/{resolve-users-aL6-Ssee.js → resolve-users-DeGJf1yp.js} +1 -1
  38. package/dist/{runtime-CauEvswP.js → runtime-JSVZSWAj.js} +2 -2
  39. package/dist/{runtime-api-CVb6CQjX.js → runtime-api-BFfNpzDe.js} +1 -1
  40. package/dist/runtime-api.js +12 -12
  41. package/dist/runtime-setter-api.js +1 -1
  42. package/dist/{scopes-Dw8wbU0j.js → scopes-lx08L56K.js} +1 -2
  43. package/dist/{secret-contract-CMohXFWS.js → secret-contract-DxmvbxBF.js} +1 -0
  44. package/dist/secret-contract-api.js +1 -1
  45. package/dist/{send-NEfJ0GvM.js → send-KuTDky7i.js} +29 -12
  46. package/dist/send.runtime-CXyxws1D.js +2 -0
  47. package/dist/setup-core-CeiAVwQ3.js +502 -0
  48. package/dist/setup-plugin-api.js +1 -1
  49. package/dist/{setup-surface-BN2WeG5c.js → setup-surface-DLA0vHXp.js} +23 -13
  50. package/dist/{shared-BLnB9R1T.js → shared-C4yVB0Q-.js} +64 -41
  51. package/dist/{slash-dispatch.runtime-C0Otuf_T.js → slash-dispatch.runtime-BT-mMm-f.js} +1 -1
  52. package/npm-shrinkwrap.json +3 -3
  53. package/openclaw.plugin.json +134 -10
  54. package/package.json +5 -7
  55. package/dist/accounts.runtime-KvftRRek.js +0 -2
  56. package/dist/action-runtime.runtime-DQstDkHG.js +0 -2
  57. package/dist/client-D2F6m8CP.js +0 -152
  58. package/dist/configured-state.js +0 -11
  59. package/dist/probe-CfALk0l4.js +0 -48
  60. package/dist/send.runtime-BkKpitPu.js +0 -2
  61. package/dist/setup-core-fGd6Gquy.js +0 -343
@@ -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 { C as hasSlackNativeDataBlock, F as SLACK_TEXT_LIMIT, K as escapeSlackMrkdwn, N as SLACK_EDIT_TEXT_LIMIT, O as normalizeSlackOutboundText, _ as resolveSlackReplyRenderPlan, c as normalizeSlackAllowOwnerEntry, k as buildSlackBlocksFallbackText, r as formatSlackError, s as normalizeAllowListLower, u as resolveSlackAllowListMatch, z as truncateSlackText } from "./group-policy-C-b2ZnSl.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 { C as buildSlackEditTextPayload, S as formatSlackFileReference, _ as resolveSlackThreadHistory, b as resolveSlackBlocksText, f as removeSlackReaction, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage, v as resolveSlackThreadStarter, y as chooseSlackPrimaryText } from "./actions-DkTcWmpT.js";
8
- import { c as recordSlackThreadParticipation, l as buildSlackNativeDataDeliveryPlan, r as sendMessageSlack, s as hasSlackThreadParticipationWithPersistence } from "./send-NEfJ0GvM.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-Ds-y-imP.js";
10
- import { n as resolveSlackChannelConfig } from "./policy-f8b0edSi.js";
11
- import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, s as emitSlackMessageSentHooks, t as createSlackReplyDeliveryPlan } from "./replies-BMvf3ihj.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, 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,17 +216,49 @@ 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
- const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_TASK_DETAIL_MAX_CHARS);
225
- return params.lines.slice(-50).map((line, index) => ({
226
- id: line.id ? stableTaskIdPart(line.id) : `${slugTaskIdPart(line.toolName ?? line.kind ?? line.label)}_${index + 1}`,
227
- title: lineTaskTitle(line, maxLineChars),
228
- status: lineTaskStatus(line)
239
+ if (params.plan) return params.plan.slice(-50).map((entry, index) => ({
240
+ id: `plan_step_${index + 1}`,
241
+ title: compactTitle(entry.step),
242
+ status: entry.status === "completed" ? "complete" : entry.status
229
243
  }));
244
+ const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_TASK_DETAIL_MAX_CHARS);
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
+ });
230
262
  }
231
263
  function resolvePlanTitle(params) {
232
264
  return compactChunkText(params.title?.trim() || params.label?.trim() || params.tasks.at(-1)?.title || SLACK_PROGRESS_PLAN_FALLBACK_TITLE);
@@ -234,9 +266,16 @@ function resolvePlanTitle(params) {
234
266
  function buildSlackProgressStreamChunks(params) {
235
267
  const tasks = buildPlanTasks({
236
268
  lines: params.lines,
269
+ plan: params.plan,
237
270
  maxLineChars: params.maxLineChars
238
271
  });
239
- 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
+ }
240
279
  return [{
241
280
  type: "plan_update",
242
281
  title: resolvePlanTitle({
@@ -254,15 +293,63 @@ function buildSlackProgressStreamChunks(params) {
254
293
  function buildSlackProgressDraftBlocks(params) {
255
294
  const label = params.label?.trim() || params.title?.trim();
256
295
  const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS);
257
- const renderedBlocks = [...label ? [{
258
- type: "section",
259
- text: field(`*${escapeSlackMrkdwn(label)}*`)
260
- }] : [], ...params.lines.map((line) => ({
296
+ const planLines = formatPlanChecklistLines(params.plan ?? [], {
297
+ maxLines: 50,
298
+ maxLineChars
299
+ });
300
+ const narration = params.narration?.replace(/\s+/g, " ").trim();
301
+ const headBlocks = [
302
+ ...label ? [{
303
+ type: "section",
304
+ text: field(`*${escapeSlackMrkdwn(label)}*`)
305
+ }] : [],
306
+ ...narration ? [{
307
+ type: "section",
308
+ text: field(`_${escapeSlackMrkdwn(narration)}_`)
309
+ }] : [],
310
+ ...planLines.length > 0 ? [{
311
+ type: "section",
312
+ text: field(planLines.map((line) => escapeSlackMrkdwn(line)).join("\n"))
313
+ }] : []
314
+ ].slice(0, 50);
315
+ const lineBudget = Math.max(0, 50 - headBlocks.length);
316
+ const renderedBlocks = [...headBlocks, ...params.lines.slice(-lineBudget).map((line) => ({
261
317
  type: "section",
262
318
  fields: [field(legacyLineTitle(line)), field(legacyLineDetail(line, maxLineChars))]
263
- }))].slice(-50);
319
+ }))];
264
320
  return renderedBlocks.length ? renderedBlocks : void 0;
265
321
  }
322
+ /**
323
+ * Slack native streams key task rows by persistent id with no removal chunk.
324
+ * When the task source switches representation (tool lines <-> typed plan) or
325
+ * a snapshot drops ids, previously emitted non-terminal rows must receive a
326
+ * final update or they linger in_progress forever.
327
+ */
328
+ function reconcileSlackNativeTaskChunks(params) {
329
+ const nextTasks = /* @__PURE__ */ new Map();
330
+ for (const chunk of params.chunks ?? []) if (chunk.type === "task_update") nextTasks.set(chunk.id, {
331
+ title: chunk.title,
332
+ status: chunk.status
333
+ });
334
+ const terminalized = [...params.previousTasks].filter(([id, task]) => !nextTasks.has(id) && task.status !== "complete" && task.status !== "error").map(([id, task]) => {
335
+ const entry = {
336
+ title: task.title,
337
+ status: "complete"
338
+ };
339
+ nextTasks.set(id, entry);
340
+ return {
341
+ type: "task_update",
342
+ id,
343
+ title: task.title,
344
+ status: "complete"
345
+ };
346
+ });
347
+ for (const [id, task] of params.previousTasks) if (!nextTasks.has(id)) nextTasks.set(id, task);
348
+ return {
349
+ chunks: params.chunks?.length ? [...params.chunks, ...terminalized] : terminalized.length ? terminalized : params.chunks,
350
+ tasks: nextTasks
351
+ };
352
+ }
266
353
  function buildSlackProgressStreamStartChunks(params) {
267
354
  return buildSlackProgressStreamChunks(params);
268
355
  }
@@ -656,15 +743,6 @@ function resolveSlackTimestampMs(ts) {
656
743
  }
657
744
  //#endregion
658
745
  //#region extensions/slack/src/monitor/message-handler/dispatch.ts
659
- const SLACK_REASONING_TAG_RE = /<\s*(\/?)\s*(?:(?:antml:|mm:)?(?:think(?:ing)?|thought)|antthinking)\b[^<>]*>/gi;
660
- const SLACK_REASONING_LABEL_PREFIX_RE = /^\s*(?:>\s*)?Reasoning:\s*/iu;
661
- const SLACK_THINKING_LABEL_PREFIX_RE = /^\s*(?:>\s*)?Thinking\.{0,3}(?=\s*(?:\n|_))/iu;
662
- const SLACK_THREAD_LOADING_MESSAGES = [
663
- "Reading the thread...",
664
- "Checking context...",
665
- "Working through the request...",
666
- "Putting it all together..."
667
- ];
668
746
  function resolveSlackMessageTimestampMs(message) {
669
747
  return resolveSlackTimestampMs(message.event_ts ?? message.ts);
670
748
  }
@@ -766,43 +844,6 @@ function rememberSlackStreamRecipientTeam(params) {
766
844
  if (oldest) cache.delete(oldest);
767
845
  }
768
846
  }
769
- function normalizeSlackReasoningProgressLine(text) {
770
- return (extractSlackReasoningTagText(text) || stripReasoningTagsFromText(text, {
771
- mode: "strict",
772
- trim: "both"
773
- })).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();
774
- }
775
- function extractSlackReasoningTagText(text) {
776
- if (!text) return "";
777
- let result = "";
778
- let lastIndex = 0;
779
- let inReasoning = false;
780
- SLACK_REASONING_TAG_RE.lastIndex = 0;
781
- for (const match of text.matchAll(SLACK_REASONING_TAG_RE)) {
782
- const index = match.index ?? 0;
783
- if (inReasoning) result += text.slice(lastIndex, index);
784
- inReasoning = match[1] !== "/";
785
- lastIndex = index + match[0].length;
786
- }
787
- if (inReasoning) result += text.slice(lastIndex);
788
- return result.trim();
789
- }
790
- function stripSimpleItalicMarkers(line) {
791
- const trimmed = line.trim();
792
- if (trimmed.length >= 2 && trimmed.startsWith("_") && trimmed.endsWith("_")) return trimmed.slice(1, -1).trim();
793
- return trimmed;
794
- }
795
- function mergeSlackReasoningProgressText(current, incoming, options) {
796
- if (!current) return incoming;
797
- const normalizedCurrent = normalizeSlackReasoningProgressLine(current);
798
- const normalizedIncoming = normalizeSlackReasoningProgressLine(incoming);
799
- if (!normalizedIncoming || normalizedIncoming === normalizedCurrent) return current;
800
- if (options?.snapshot === true || isSlackReasoningSnapshotText(incoming) || normalizedIncoming.startsWith(normalizedCurrent)) return incoming;
801
- return `${current}${incoming}`;
802
- }
803
- function isSlackReasoningSnapshotText(text) {
804
- return SLACK_REASONING_LABEL_PREFIX_RE.test(text) || SLACK_THINKING_LABEL_PREFIX_RE.test(text);
805
- }
806
847
  function createSlackEventDeliveryTracker() {
807
848
  const deliveredKeys = /* @__PURE__ */ new Set();
808
849
  return {
@@ -982,7 +1023,6 @@ async function dispatchPreparedSlackMessage(prepared) {
982
1023
  channelId: message.channel,
983
1024
  threadTs: statusThreadTs,
984
1025
  status: "is typing...",
985
- loadingMessages: SLACK_THREAD_LOADING_MESSAGES,
986
1026
  eventScope: prepared.eventScope
987
1027
  });
988
1028
  if (typingReaction && message.ts) await reactSlackMessage(message.channel, message.ts, typingReaction, {
@@ -1102,6 +1142,11 @@ async function dispatchPreparedSlackMessage(prepared) {
1102
1142
  delivery.outcome = "success";
1103
1143
  }
1104
1144
  };
1145
+ const acknowledgeStoppedStreamedDeliveries = (session, messageId) => {
1146
+ refreshStreamedAcknowledgements(session);
1147
+ for (const delivery of streamedDeliveries) delivery.acknowledged = true;
1148
+ emitAcknowledgedStreamedDeliveries(messageId);
1149
+ };
1105
1150
  const emitFailedPendingStreamedDeliveries = (error) => {
1106
1151
  for (const delivery of streamedDeliveries) {
1107
1152
  if (delivery.acknowledged || delivery.outcome) continue;
@@ -1130,7 +1175,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1130
1175
  delivery.outcome = "success";
1131
1176
  }
1132
1177
  };
1133
- const deliveryTracker = createSlackEventDeliveryTracker();
1178
+ let deliveryTracker = createSlackEventDeliveryTracker();
1134
1179
  const markPreviewPayloadDelivered = (params) => {
1135
1180
  deliveryTracker.markDelivered(params);
1136
1181
  const nextThreadTs = replyPlan.peekThreadTs();
@@ -1155,8 +1200,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1155
1200
  session,
1156
1201
  ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
1157
1202
  });
1158
- for (const delivery of streamedDeliveries) delivery.acknowledged = true;
1159
- emitAcknowledgedStreamedDeliveries(stopResult.messageId);
1203
+ acknowledgeStoppedStreamedDeliveries(session, stopResult.messageId);
1160
1204
  observedReplyDelivery = true;
1161
1205
  usedReplyThreadTs ??= session.threadTs;
1162
1206
  return true;
@@ -1265,6 +1309,24 @@ async function dispatchPreparedSlackMessage(prepared) {
1265
1309
  rememberDeliveredThreadTs(params.kind, params.session.threadTs);
1266
1310
  return true;
1267
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
+ };
1268
1330
  const deliverWithStreaming = async (params) => {
1269
1331
  if (params.payload.isReasoning === true) return;
1270
1332
  const reply = resolveSendableOutboundReplyParts(params.payload);
@@ -1290,13 +1352,6 @@ async function dispatchPreparedSlackMessage(prepared) {
1290
1352
  });
1291
1353
  return;
1292
1354
  }
1293
- if (useNativeProgressStreaming && !streamSession) {
1294
- await deliverNormally({
1295
- payload: params.payload,
1296
- kind: params.kind
1297
- });
1298
- return;
1299
- }
1300
1355
  if (!streamSession) {
1301
1356
  const streamThreadTs = replyPlan.nextThreadTs();
1302
1357
  plannedThreadTs = streamThreadTs;
@@ -1357,36 +1412,12 @@ async function dispatchPreparedSlackMessage(prepared) {
1357
1412
  logVerbose("slack-stream: suppressed duplicate append payload");
1358
1413
  return;
1359
1414
  }
1360
- const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent && previewToolProgressLines.length > 0 ? buildSlackProgressStreamCompletionChunks({
1361
- title: explicitProgressTitle,
1362
- lines: previewToolProgressLines,
1363
- maxLineChars: progressDraftMaxLineChars,
1364
- finalInProgressStatus: params.payload.isError ? "error" : "complete"
1365
- }) : void 0;
1366
- if (useNativeProgressStreaming) {
1367
- if (completionChunks?.length) {
1368
- await appendSlackStream({
1369
- session: streamSession,
1370
- chunks: completionChunks
1371
- });
1372
- nativeProgressCompletionSent = true;
1373
- if (streamSession.delivered) observedReplyDelivery = true;
1374
- }
1375
- await deliverNormally({
1376
- payload: params.payload,
1377
- kind: params.kind,
1378
- forcedThreadTs: streamSession.threadTs
1379
- });
1380
- return;
1381
- }
1382
1415
  if (text) recordStreamedDelivery(params.kind, text);
1383
1416
  await appendSlackStream({
1384
1417
  session: streamSession,
1385
- text: "\n" + text,
1386
- chunks: completionChunks
1418
+ text: "\n" + text
1387
1419
  });
1388
1420
  refreshStreamedAcknowledgements(streamSession);
1389
- if (completionChunks?.length) nativeProgressCompletionSent = true;
1390
1421
  if (streamSession.delivered) {
1391
1422
  observedReplyDelivery = true;
1392
1423
  if (params.kind === "final") observedFinalReplyDelivery = true;
@@ -1433,13 +1464,59 @@ async function dispatchPreparedSlackMessage(prepared) {
1433
1464
  await deliverNormally({
1434
1465
  payload: params.payload,
1435
1466
  kind: params.kind,
1436
- forcedThreadTs: streamSession?.threadTs ?? plannedThreadTs
1467
+ forcedThreadTs: streamSession?.threadTs ?? nativeProgressStreamThreadTs ?? plannedThreadTs
1437
1468
  });
1438
1469
  }
1439
1470
  };
1440
1471
  let draftPreviewCommitted = false;
1441
1472
  const deliverSlackPayload = async (payload, info) => {
1442
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
+ }
1443
1520
  if (useStreaming) {
1444
1521
  await deliverWithStreaming({
1445
1522
  payload,
@@ -1630,8 +1707,6 @@ async function dispatchPreparedSlackMessage(prepared) {
1630
1707
  const useNativeProgressStreaming = useStreaming && slackStreaming.mode === "progress";
1631
1708
  const progressDraftActive = Boolean(draftStream) || useNativeProgressStreaming;
1632
1709
  const previewToolProgressEnabled = progressDraftActive && resolveChannelStreamingPreviewToolProgress(account.config);
1633
- const commentaryProgressEnabled = progressDraftActive && resolveChannelStreamingProgressCommentary(account.config);
1634
- const commentaryDraftEnabled = progressDraftActive && (slackStreaming.mode === "progress" ? resolveChannelStreamingProgressCommentary(account.config, previewToolProgressEnabled) : previewToolProgressEnabled);
1635
1710
  let shouldYieldDraftProgress = () => false;
1636
1711
  const suppressDefaultToolProgressMessages = resolveChannelStreamingSuppressDefaultToolProgressMessages(account.config, {
1637
1712
  draftStreamActive: Boolean(draftStream) || useNativeProgressStreaming,
@@ -1639,39 +1714,20 @@ async function dispatchPreparedSlackMessage(prepared) {
1639
1714
  previewStreamingEnabled
1640
1715
  });
1641
1716
  let previewToolProgressSuppressed = false;
1642
- let previewToolProgressLines = [];
1643
- let lastNonEmptyPreviewToolProgressLines = [];
1717
+ let legacyPreviewToolProgressLines = [];
1718
+ let nativeTaskState = /* @__PURE__ */ new Map();
1644
1719
  let appendRenderedText = "";
1645
1720
  let appendSourceText = "";
1646
- let reasoningProgressRawText = "";
1647
- let statusUpdateCount = 0;
1648
1721
  let nativeProgressCompletionSent = false;
1722
+ let nativeProgressTerminalStatus = "complete";
1649
1723
  let nativeProgressChunkKey;
1724
+ const progressReceipt = createChannelProgressReceiptTracker();
1725
+ let progressReceiptCollapsed = false;
1726
+ let pendingNativeProgressReceipt;
1650
1727
  const progressSeed = `${account.accountId}:${message.channel}`;
1651
1728
  const useRichProgressDraft = streamMode === "status_final" && resolveChannelProgressDraftRender(account.config) === "rich";
1652
1729
  const explicitProgressTitle = resolveExplicitSlackProgressTitle(account.config);
1653
1730
  const progressDraftMaxLineChars = resolveChannelProgressDraftMaxLineChars(account.config);
1654
- const renderProgressDraft = () => {
1655
- if (!draftStream || streamMode !== "status_final") return;
1656
- const progressLines = previewToolProgressLines.length === 0 ? lastNonEmptyPreviewToolProgressLines : previewToolProgressLines;
1657
- const previewText = formatChannelProgressDraftText({
1658
- entry: account.config,
1659
- lines: progressLines,
1660
- seed: progressSeed,
1661
- formatLine: escapeSlackMrkdwn
1662
- });
1663
- if (!previewText) return;
1664
- const richProgressBlocks = useRichProgressDraft ? buildSlackProgressDraftBlocks({
1665
- title: explicitProgressTitle,
1666
- lines: progressLines,
1667
- maxLineChars: resolveChannelProgressDraftMaxLineChars(account.config)
1668
- }) : void 0;
1669
- draftStream.update(useRichProgressDraft && richProgressBlocks ? {
1670
- text: previewText,
1671
- blocks: richProgressBlocks
1672
- } : previewText);
1673
- hasStreamedMessage = true;
1674
- };
1675
1731
  const waitForNativeProgressStreamStart = async () => {
1676
1732
  if (streamSession || !nativeProgressStreamStartPromise) return true;
1677
1733
  try {
@@ -1682,13 +1738,40 @@ async function dispatchPreparedSlackMessage(prepared) {
1682
1738
  }
1683
1739
  return !streamFailed;
1684
1740
  };
1685
- const buildNativeProgressChunks = () => streamSession ? buildSlackProgressStreamUpdateChunks({
1686
- title: explicitProgressTitle,
1687
- lines: 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;
1757
+ const explanationLine = buildChannelProgressDraftLine({
1758
+ event: "plan",
1759
+ phase: "update",
1760
+ explanation: snapshot.planExplanation
1761
+ });
1762
+ return explanationLine ? [...lines, explanationLine] : lines;
1763
+ };
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),
1688
1770
  maxLineChars: progressDraftMaxLineChars
1689
1771
  }) : buildSlackProgressStreamStartChunks({
1690
- title: explicitProgressTitle,
1691
- lines: previewToolProgressLines,
1772
+ title: resolveNativeProgressTitle(snapshot),
1773
+ lines: resolveNativeProgressLines(snapshot),
1774
+ plan: resolveNativeProgressPlan(snapshot),
1692
1775
  maxLineChars: progressDraftMaxLineChars
1693
1776
  });
1694
1777
  const markNativeProgressDelivered = (session, threadTs) => {
@@ -1746,78 +1829,176 @@ async function dispatchPreparedSlackMessage(prepared) {
1746
1829
  nativeProgressChunkKey = chunkKey;
1747
1830
  };
1748
1831
  const updateNativeProgressStream = async () => {
1749
- if (!useNativeProgressStreaming || streamFailed || previewToolProgressLines.length === 0) return;
1832
+ const snapshot = progressDraft.getSnapshot();
1833
+ const progressLines = resolveNativeProgressLines(snapshot);
1834
+ const hasRetirableNativeTasks = [...nativeTaskState.values()].some((task) => task.status !== "complete" && task.status !== "error");
1835
+ if (!useNativeProgressStreaming || streamFailed || progressLines.length === 0 && !snapshot.plan?.length && !snapshot.statusHeadline && !explicitProgressTitle && !hasRetirableNativeTasks) return;
1750
1836
  if (!await waitForNativeProgressStreamStart()) return;
1751
- const chunks = buildNativeProgressChunks();
1837
+ const reconciled = reconcileSlackNativeTaskChunks({
1838
+ previousTasks: nativeTaskState,
1839
+ chunks: buildNativeProgressChunks(snapshot)
1840
+ });
1841
+ const chunks = reconciled.chunks;
1752
1842
  if (!chunks?.length) return;
1753
1843
  const chunkKey = JSON.stringify(chunks);
1754
1844
  if (chunkKey === nativeProgressChunkKey) return;
1755
1845
  try {
1756
- if (!streamSession) {
1757
- await startNativeProgressStream(chunks, chunkKey);
1758
- return;
1759
- }
1760
- await appendNativeProgressStream(chunks, chunkKey);
1846
+ if (!streamSession) await startNativeProgressStream(chunks, chunkKey);
1847
+ else await appendNativeProgressStream(chunks, chunkKey);
1848
+ nativeTaskState = reconciled.tasks;
1761
1849
  } catch (err) {
1762
1850
  runtime.error?.(danger(`slack-stream: native progress stream failed: ${formatSlackError(err)}, falling back`));
1763
1851
  streamFailed = true;
1764
1852
  }
1765
1853
  };
1766
- const progressDraftGate = createChannelProgressDraftGate({ onStart: useNativeProgressStreaming ? updateNativeProgressStream : renderProgressDraft });
1767
- const refreshStartedProgressDraft = async () => {
1768
- if (useNativeProgressStreaming) await updateNativeProgressStream();
1769
- else renderProgressDraft();
1854
+ const resetProgressTurnState = () => {
1855
+ progressReceipt.reset();
1856
+ progressReceiptCollapsed = false;
1857
+ pendingNativeProgressReceipt = void 0;
1770
1858
  };
1771
- const pushPreviewProgress = async (line, options) => {
1772
- const lane = options?.lane ?? "tool";
1773
- const progressEnabled = lane === "commentary" ? commentaryDraftEnabled : previewToolProgressEnabled;
1774
- if (!draftStream && !useNativeProgressStreaming) return;
1775
- if (lane === "commentary" && commentaryProgressEnabled && shouldYieldDraftProgress()) return;
1776
- if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
1777
- const normalized = line?.text.replace(/\s+/g, " ").trim();
1778
- if (!line || !normalized) {
1779
- if (streamMode !== "status_final") return;
1780
- const alreadyStarted = progressDraftGate.hasStarted;
1781
- const progressActive = await progressDraftGate.noteWork();
1782
- if ((alreadyStarted || progressActive) && progressDraftGate.hasStarted) await refreshStartedProgressDraft();
1783
- return;
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
1867
+ });
1868
+ progressReceiptCollapsed = true;
1869
+ } catch (err) {
1870
+ logVerbose(`slack: progress receipt edit failed (${formatSlackError(err)})`);
1784
1871
  }
1785
- if (streamMode !== "status_final") {
1786
- if (!progressEnabled || previewToolProgressSuppressed) return;
1787
- const nextLines = mergeChannelProgressDraftLine(previewToolProgressLines, line, { maxLines: resolveChannelProgressDraftMaxLines(account.config) });
1788
- if (nextLines === previewToolProgressLines) return;
1789
- previewToolProgressLines = nextLines;
1790
- draftStream?.update(formatChannelProgressDraftText({
1791
- entry: account.config,
1792
- lines: previewToolProgressLines,
1793
- seed: progressSeed,
1794
- formatLine: escapeSlackMrkdwn
1795
- }));
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;
1888
+ }
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);
1796
1904
  hasStreamedMessage = true;
1905
+ if (options?.flush) await draftStream.flush();
1906
+ }
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;
1924
+ };
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;
1952
+ };
1953
+ const pushPlanProgress = async (steps, explanation) => {
1954
+ if (streamMode === "status_final") {
1955
+ await progressDraft.pushPlanProgress(steps, { explanation });
1797
1956
  return;
1798
1957
  }
1799
- if (progressEnabled && !previewToolProgressSuppressed) {
1800
- previewToolProgressLines = mergeChannelProgressDraftLine(previewToolProgressLines, line, { maxLines: resolveChannelProgressDraftMaxLines(account.config) });
1801
- if (previewToolProgressLines.length > 0) lastNonEmptyPreviewToolProgressLines = previewToolProgressLines;
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);
1969
+ hasStreamedMessage = true;
1802
1970
  }
1803
- if (useNativeProgressStreaming) {
1804
- if (progressDraftGate.hasStarted) await updateNativeProgressStream();
1805
- else {
1806
- await progressDraftGate.startNow();
1807
- 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;
1808
1980
  }
1981
+ await progressDraft.pushToolProgress(line, options);
1809
1982
  return;
1810
1983
  }
1811
- const alreadyStarted = progressDraftGate.hasStarted;
1812
- const progressActive = await progressDraftGate.noteWork();
1813
- 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;
1814
1995
  };
1815
1996
  const updateDraftFromPartial = (text) => {
1816
1997
  const trimmed = text?.trimEnd();
1817
1998
  if (!trimmed) return;
1818
1999
  if (streamMode === "append") {
1819
2000
  previewToolProgressSuppressed = true;
1820
- previewToolProgressLines = [];
2001
+ legacyPreviewToolProgressLines = [];
1821
2002
  const next = applyAppendOnlyStreamUpdate({
1822
2003
  incoming: trimmed,
1823
2004
  rendered: appendRenderedText,
@@ -1830,56 +2011,86 @@ async function dispatchPreparedSlackMessage(prepared) {
1830
2011
  hasStreamedMessage = true;
1831
2012
  return;
1832
2013
  }
1833
- if (streamMode === "status_final") {
1834
- if (!progressDraftGate.hasStarted) return;
1835
- statusUpdateCount += 1;
1836
- if (statusUpdateCount > 1 && statusUpdateCount % 4 !== 0) return;
1837
- renderProgressDraft();
1838
- return;
1839
- }
2014
+ if (streamMode === "status_final") return;
1840
2015
  previewToolProgressSuppressed = true;
1841
- previewToolProgressLines = [];
2016
+ legacyPreviewToolProgressLines = [];
1842
2017
  draftStream?.update(trimmed);
1843
2018
  hasStreamedMessage = true;
1844
2019
  };
1845
2020
  const pushReasoningProgress = async (payload) => {
1846
2021
  if (!payload?.text) return;
1847
- reasoningProgressRawText = mergeSlackReasoningProgressText(reasoningProgressRawText, payload.text, { snapshot: payload.isReasoningSnapshot === true });
1848
- const normalized = normalizeSlackReasoningProgressLine(reasoningProgressRawText);
1849
- if (!normalized) return;
1850
- await pushPreviewProgress({
1851
- id: "reasoning",
1852
- kind: "item",
1853
- text: normalized,
1854
- label: "Reasoning"
1855
- });
1856
- };
1857
- const onDraftBoundary = !shouldUseDraftStream ? void 0 : async () => {
1858
- if (hasStreamedMessage && streamMode !== "status_final") {
1859
- draftStream?.forceNewMessage();
1860
- hasStreamedMessage = false;
1861
- appendRenderedText = "";
1862
- appendSourceText = "";
1863
- statusUpdateCount = 0;
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;
1864
2032
  }
1865
- reasoningProgressRawText = "";
2033
+ progressReceipt.noteReasoning();
2034
+ await progressDraft.pushReasoningProgress(payload.text, { snapshot: payload.isReasoningSnapshot === true });
2035
+ };
2036
+ const resetDraftDeliveryState = () => {
2037
+ hasStreamedMessage = false;
2038
+ appendRenderedText = "";
2039
+ appendSourceText = "";
2040
+ };
2041
+ const resetDraftProgressState = () => {
2042
+ progressDraft.resetReasoningProgress();
1866
2043
  previewToolProgressSuppressed = false;
1867
- 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;
2061
+ };
2062
+ const onDraftBoundary = !shouldUseDraftStream && !useNativeProgressStreaming ? void 0 : async () => {
2063
+ if (streamMode === "status_final") {
2064
+ await beginNewProgressTurn();
2065
+ return;
2066
+ }
2067
+ if (hasStreamedMessage) draftStream?.forceNewMessage();
2068
+ resetDraftDeliveryState();
2069
+ resetDraftProgressState();
2070
+ };
2071
+ const onQueuedFollowupAdmitted = !shouldUseDraftStream && !useNativeProgressStreaming ? void 0 : async () => {
2072
+ await draftStream?.flush();
2073
+ draftPreviewCommitted = false;
2074
+ observedFinalReplyDelivery = false;
2075
+ if (streamMode === "status_final") await beginNewProgressTurn({ force: true });
2076
+ else draftStream?.forceNewMessage();
2077
+ deliveryTracker = createSlackEventDeliveryTracker();
2078
+ resetDraftDeliveryState();
2079
+ resetDraftProgressState();
1868
2080
  };
1869
2081
  let dispatchError;
1870
2082
  let queuedFinal = false;
1871
2083
  let counts = {};
1872
2084
  try {
1873
- const turnResult = await dispatchChannelInboundReply({
2085
+ const turnResult = await dispatchChannelInboundTurn({
1874
2086
  cfg,
1875
2087
  channel: "slack",
1876
2088
  accountId: route.accountId,
1877
- agentId: route.agentId,
1878
- routeSessionKey: route.sessionKey,
1879
- storePath: prepared.turn.storePath,
2089
+ route: {
2090
+ agentId: route.agentId,
2091
+ sessionKey: route.sessionKey
2092
+ },
1880
2093
  ctxPayload: prepared.ctxPayload,
1881
- recordInboundSession,
1882
- dispatchReplyWithBufferedBlockDispatcher,
1883
2094
  dispatcherOptions: {
1884
2095
  ...replyPipeline,
1885
2096
  humanDelay: resolveHumanDelayConfig(cfg, route.agentId)
@@ -1892,6 +2103,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1892
2103
  history: prepared.turn.history,
1893
2104
  botLoopProtection: resolveSlackBotLoopProtection(prepared),
1894
2105
  replyOptions: {
2106
+ ...prepared.turnAdoptionLifecycle ? { turnAdoptionLifecycle: prepared.turnAdoptionLifecycle } : {},
1895
2107
  skillFilter: prepared.channelConfig?.skills,
1896
2108
  sourceReplyDeliveryMode,
1897
2109
  suppressTyping: suppressRoomEventTyping ? true : void 0,
@@ -1900,6 +2112,8 @@ async function dispatchPreparedSlackMessage(prepared) {
1900
2112
  onModelSelected,
1901
2113
  suppressDefaultToolProgressMessages: suppressDefaultToolProgressMessages ? true : void 0,
1902
2114
  commentaryProgressEnabled: commentaryProgressEnabled ? true : void 0,
2115
+ progressPreambleEnabled: progressDraftActive && slackStreaming.mode === "progress" ? true : void 0,
2116
+ commentaryPayloadsEnabled: commentaryProgressEnabled ? true : void 0,
1903
2117
  onVerboseProgressVisibility: commentaryProgressEnabled ? (isActive) => {
1904
2118
  shouldYieldDraftProgress = isActive;
1905
2119
  } : void 0,
@@ -1909,7 +2123,11 @@ async function dispatchPreparedSlackMessage(prepared) {
1909
2123
  updateDraftFromPartial(payload.text);
1910
2124
  },
1911
2125
  onAssistantMessageStart: onDraftBoundary,
1912
- onReasoningEnd: onDraftBoundary,
2126
+ onReasoningEnd: async () => {
2127
+ progressReceipt.closeReasoning();
2128
+ await onDraftBoundary?.();
2129
+ },
2130
+ onQueuedFollowupAdmitted,
1913
2131
  onReasoningStream: statusReactionsEnabled || previewToolProgressEnabled ? async (payload) => {
1914
2132
  await pushReasoningProgress(payload);
1915
2133
  if (!statusReactionsEnabled) return;
@@ -1917,6 +2135,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1917
2135
  } : void 0,
1918
2136
  onToolStart: async (payload) => {
1919
2137
  if (statusReactionsEnabled) await statusReactions.setTool(payload.name);
2138
+ if (payload.phase === "start") progressReceipt.noteToolCall(payload.name);
1920
2139
  await pushPreviewProgress(buildChannelProgressDraftLineForEntry(account.config, {
1921
2140
  event: "tool",
1922
2141
  itemId: payload.itemId,
@@ -1927,6 +2146,14 @@ async function dispatchPreparedSlackMessage(prepared) {
1927
2146
  }, payload.detailMode ? { detailMode: payload.detailMode } : void 0), { toolName: payload.name });
1928
2147
  },
1929
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
+ }
1930
2157
  await pushPreviewProgress(buildChannelProgressDraftLineForEntry(account.config, {
1931
2158
  event: "item",
1932
2159
  itemId: payload.itemId,
@@ -1939,17 +2166,11 @@ async function dispatchPreparedSlackMessage(prepared) {
1939
2166
  summary: payload.summary,
1940
2167
  progressText: payload.progressText,
1941
2168
  meta: payload.meta
1942
- }), payload.kind === "preamble" ? { lane: "commentary" } : void 0);
2169
+ }));
1943
2170
  },
1944
2171
  onPlanUpdate: async (payload) => {
1945
2172
  if (payload.phase !== "update") return;
1946
- await pushPreviewProgress(buildChannelProgressDraftLine({
1947
- event: "plan",
1948
- phase: payload.phase,
1949
- title: payload.title,
1950
- explanation: payload.explanation,
1951
- steps: payload.steps
1952
- }));
2173
+ await pushPlanProgress(payload.steps, payload.explanation);
1953
2174
  },
1954
2175
  onApprovalEvent: async (payload) => {
1955
2176
  if (payload.phase !== "requested") return;
@@ -2000,26 +2221,26 @@ async function dispatchPreparedSlackMessage(prepared) {
2000
2221
  } catch (err) {
2001
2222
  dispatchError = err;
2002
2223
  } finally {
2003
- progressDraftGate.cancel();
2224
+ progressDraft.cancel();
2004
2225
  await draftStream?.discardPending();
2005
2226
  }
2006
2227
  let streamFallbackDelivered = false;
2007
2228
  const finalStream = streamSession;
2008
2229
  if (finalStream && !finalStream.stopped) try {
2009
- const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent && previewToolProgressLines.length > 0 ? buildSlackProgressStreamCompletionChunks({
2010
- title: explicitProgressTitle,
2011
- lines: previewToolProgressLines,
2012
- maxLineChars: progressDraftMaxLineChars,
2013
- finalInProgressStatus: dispatchError ? "error" : "complete"
2014
- }) : void 0;
2230
+ const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent ? buildNativeProgressCompletionChunks(dispatchError ? "error" : nativeProgressTerminalStatus) : void 0;
2015
2231
  if (completionChunks?.length) nativeProgressCompletionSent = true;
2016
2232
  const stopResult = await stopSlackStream({
2017
2233
  session: finalStream,
2018
2234
  ...completionChunks?.length ? { chunks: completionChunks } : {},
2019
2235
  ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
2020
2236
  });
2021
- for (const delivery of streamedDeliveries) delivery.acknowledged = true;
2022
- 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
+ });
2023
2244
  } catch (err) {
2024
2245
  if (err instanceof SlackStreamNotDeliveredError) {
2025
2246
  streamFallbackDelivered = await deliverPendingStreamFallback(finalStream, err);
@@ -2199,7 +2420,7 @@ async function discardSlackPreflightMedia(media) {
2199
2420
  const SLACK_MENTION_RESOLUTION_CONCURRENCY = 4;
2200
2421
  const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
2201
2422
  const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
2202
- const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-DkTcWmpT.js").then((n) => n.g));
2423
+ const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-CF0tUY5q.js").then((n) => n.g));
2203
2424
  function collectUniqueSlackMentionIds$1(texts) {
2204
2425
  const seen = /* @__PURE__ */ new Set();
2205
2426
  const mentionIds = [];
@@ -2621,7 +2842,7 @@ function formatSlackThreadLabelSnippet(text) {
2621
2842
  }
2622
2843
  //#endregion
2623
2844
  //#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
2624
- const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-DkTcWmpT.js").then((n) => n.g));
2845
+ const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-CF0tUY5q.js").then((n) => n.g));
2625
2846
  const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
2626
2847
  function resolveSlackThreadSessionFreshness(params) {
2627
2848
  return params.ctx.channelRuntime?.session?.resolveEntryResetFreshness?.({
@@ -2636,14 +2857,21 @@ function resolveSlackThreadSessionFreshness(params) {
2636
2857
  });
2637
2858
  }
2638
2859
  function isSlackThreadContextSenderAllowed(params) {
2639
- if (params.allowFromLower.length === 0 || params.botId) return true;
2640
- if (!params.userId) return false;
2641
- return resolveSlackAllowListMatch({
2642
- allowList: params.allowFromLower,
2643
- id: params.userId,
2644
- name: params.userName,
2645
- allowNameMatching: params.allowNameMatching
2646
- }).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
+ });
2647
2875
  }
2648
2876
  async function resolveSlackThreadUserMap(params) {
2649
2877
  const uniqueUserIds = [];
@@ -3336,7 +3564,7 @@ async function prepareSlackMessage(params) {
3336
3564
  name: void 0
3337
3565
  });
3338
3566
  let implicitMentionKinds = [];
3339
- if (!isDirectMessage && ctx.botUserId && message.thread_ts && !ctx.threadRequireExplicitMention && !wasMentioned) {
3567
+ if (!isDirectMessage && ctx.botUserId && message.thread_ts && !wasMentioned) {
3340
3568
  const replyToBotKinds = implicitMentionKindWhen("reply_to_bot", message.parent_user_id === ctx.botUserId);
3341
3569
  implicitMentionKinds = replyToBotKinds.length > 0 ? replyToBotKinds : implicitMentionKindWhen("bot_thread_participant", await hasSlackThreadParticipationWithPersistence({
3342
3570
  accountId: account.accountId,
@@ -3432,6 +3660,11 @@ async function prepareSlackMessage(params) {
3432
3660
  surface: "slack"
3433
3661
  });
3434
3662
  const shouldRequireMention = isRoom ? channelConfig?.requireMention ?? ctx.defaultRequireMention : false;
3663
+ const implicitMentions = resolveChannelImplicitMentions({
3664
+ cfg,
3665
+ channel: "slack",
3666
+ accountId: account.accountId
3667
+ });
3435
3668
  if (message["_ambiguousThreadReply"]) {
3436
3669
  ctx.logger.info({
3437
3670
  channel: message.channel,
@@ -3464,7 +3697,7 @@ async function prepareSlackMessage(params) {
3464
3697
  activation: {
3465
3698
  requireMention: shouldRequireMention,
3466
3699
  allowTextCommands,
3467
- ...ctx.threadRequireExplicitMention ? { allowedImplicitMentionKinds: [] } : {}
3700
+ implicitMentions
3468
3701
  }
3469
3702
  });
3470
3703
  const senderGate = messageIngress.senderAccess.gate;
@@ -3576,7 +3809,7 @@ async function prepareSlackMessage(params) {
3576
3809
  allowTextCommands,
3577
3810
  hasControlCommand: hasControlCommandInMessage,
3578
3811
  commandAuthorized,
3579
- ...ctx.threadRequireExplicitMention ? { allowedImplicitMentionKinds: [] } : {}
3812
+ implicitMentions
3580
3813
  }
3581
3814
  });
3582
3815
  const effectiveWasMentioned = mentionDecision.effectiveWasMentioned;
@@ -3804,6 +4037,7 @@ async function prepareSlackMessage(params) {
3804
4037
  },
3805
4038
  route: {
3806
4039
  agentId: route.agentId,
4040
+ dmScope: route.dmScope,
3807
4041
  accountId: route.accountId,
3808
4042
  routeSessionKey: sessionKey,
3809
4043
  parentSessionKey: threadKeys.parentSessionKey