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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/{account-configured-DzC407uv.js → account-configured-BwfXBo_F.js} +1 -1
  2. package/dist/account-inspect-api.js +1 -1
  3. package/dist/{account-inspect-Di7ditqZ.js → account-inspect-jn1xLaVi.js} +9 -2
  4. package/dist/account-reply-mode-DrUXogvO.js +14 -0
  5. package/dist/{accounts-BOJJiHSr.js → accounts-BSr252c_.js} +13 -25
  6. package/dist/accounts.runtime-CjULMLTa.js +2 -0
  7. package/dist/{action-runtime-Cee2ih0s.js → action-runtime-CFU52n4W.js} +11 -17
  8. package/dist/action-runtime.runtime-9-17zvKH.js +2 -0
  9. package/dist/{actions-BZiuSTIq.js → actions-D51foiwR.js} +5 -5
  10. package/dist/{actions.runtime-DoD0-gxw.js → actions.runtime-BMJwtf5C.js} +1 -1
  11. package/dist/api.js +15 -15
  12. package/dist/{approval-handler.runtime-CUPtIM4k.js → approval-handler.runtime-3b4-wxMl.js} +2 -2
  13. package/dist/{channel-DaIiGUOp.js → channel-Ds8NKvfr.js} +75 -41
  14. package/dist/channel-config-api.js +1 -1
  15. package/dist/channel-plugin-api.js +1 -1
  16. package/dist/{channel-type-Cfm6_6n1.js → channel-type-DXkUajFP.js} +3 -3
  17. package/dist/{channel.setup-C-mLz50O.js → channel.setup-DOSTEDfA.js} +8 -5
  18. package/dist/{config-schema-xPSnK2VK.js → config-schema-B4j4tCOI.js} +236 -30
  19. package/dist/{directory-config-DRkFujwG.js → directory-config-B08pm5ZT.js} +1 -1
  20. package/dist/directory-contract-api.js +1 -1
  21. package/dist/{directory-live-D_yZhjgp.js → directory-live-DpPKzPrn.js} +2 -2
  22. package/dist/doctor-contract-U6oVntbM.js +173 -0
  23. package/dist/doctor-contract-api.js +1 -1
  24. package/dist/{group-policy-85UWiriS.js → group-policy-CdnQzQBI.js} +98 -125
  25. package/dist/{interactive-replies-BilWUdcI.js → interactive-replies-C3ffk6oN.js} +1 -1
  26. package/dist/interactive-replies-api.js +1 -1
  27. package/dist/{message-tool-api-CA9jZrCY.js → message-tool-api-ByVyIVOh.js} +3 -3
  28. package/dist/message-tool-api.js +1 -1
  29. package/dist/{monitor-CalGciTs.js → monitor-CnB_WKvF.js} +3 -3
  30. package/dist/{outbound-adapter-BiLKcnyH.js → outbound-adapter-DuPz2HHt.js} +49 -5
  31. package/dist/{pipeline.runtime-DofAvTtb.js → pipeline.runtime-5rRYZuX5.js} +490 -345
  32. package/dist/{policy-BVs4LiSa.js → policy-C31RYSuT.js} +1 -1
  33. package/dist/probe-CVRuArHA.js +340 -0
  34. package/dist/{provider-CmeoUiIM.js → provider-htuIJGFD.js} +791 -359
  35. package/dist/{relay-source-DMmgtvg4.js → relay-source-Df9EaiTF.js} +6 -11
  36. package/dist/{replies-CJJCp6qz.js → replies-Cgr2Gli4.js} +3 -3
  37. package/dist/{resolve-channels-Rfts6POK.js → resolve-channels-DodP2TFx.js} +1 -1
  38. package/dist/{resolve-users-y6wAxR8E.js → resolve-users-M9eF8891.js} +1 -1
  39. package/dist/{runtime-api-BGDyOt1D.js → runtime-api-D5XYMMsf.js} +3 -2
  40. package/dist/runtime-api.js +11 -11
  41. package/dist/{scopes-CuxNQZ6v.js → scopes-CJRpHkDC.js} +1 -2
  42. package/dist/{secret-contract-CMohXFWS.js → secret-contract-DxmvbxBF.js} +1 -0
  43. package/dist/secret-contract-api.js +1 -1
  44. package/dist/{security-audit-CikQhBUY.js → security-audit-Cz6erklv.js} +0 -10
  45. package/dist/security-contract-api.js +1 -1
  46. package/dist/{send-DYp8ca-l.js → send-BZ1tPv7-.js} +28 -11
  47. package/dist/send.runtime-a2pilFor.js +2 -0
  48. package/dist/setup-core-tPxMuFez.js +600 -0
  49. package/dist/setup-plugin-api.js +1 -1
  50. package/dist/{setup-surface-oSd3NX9J.js → setup-surface-wV-S1dE_.js} +58 -32
  51. package/dist/{shared-D9yz5Otg.js → shared-BfMskaMx.js} +57 -10
  52. package/dist/{slash-dispatch.runtime-CSja9VuD.js → slash-dispatch.runtime-lfeIsXTX.js} +1 -1
  53. package/node_modules/@types/node/README.md +1 -1
  54. package/node_modules/@types/node/fs/promises.d.ts +16 -6
  55. package/node_modules/@types/node/package.json +2 -2
  56. package/node_modules/agent-base/README.md +145 -0
  57. package/node_modules/agent-base/dist/src/index.d.ts +78 -0
  58. package/node_modules/agent-base/dist/src/index.js +203 -0
  59. package/node_modules/agent-base/dist/src/index.js.map +1 -0
  60. package/node_modules/agent-base/dist/src/promisify.d.ts +4 -0
  61. package/node_modules/agent-base/dist/src/promisify.js +18 -0
  62. package/node_modules/agent-base/dist/src/promisify.js.map +1 -0
  63. package/node_modules/agent-base/package.json +64 -0
  64. package/node_modules/agent-base/src/index.ts +345 -0
  65. package/node_modules/agent-base/src/promisify.ts +33 -0
  66. package/node_modules/axios/CHANGELOG.md +197 -1
  67. package/node_modules/axios/README.md +424 -256
  68. package/node_modules/axios/dist/axios.js +589 -204
  69. package/node_modules/axios/dist/axios.min.js +3 -3
  70. package/node_modules/axios/dist/axios.min.js.map +1 -1
  71. package/node_modules/axios/dist/browser/axios.cjs +630 -185
  72. package/node_modules/axios/dist/esm/axios.js +630 -185
  73. package/node_modules/axios/dist/esm/axios.min.js +2 -2
  74. package/node_modules/axios/dist/esm/axios.min.js.map +1 -1
  75. package/node_modules/axios/dist/node/axios.cjs +949 -301
  76. package/node_modules/axios/index.d.cts +28 -5
  77. package/node_modules/axios/index.d.ts +24 -3
  78. package/node_modules/axios/lib/adapters/adapters.js +1 -1
  79. package/node_modules/axios/lib/adapters/fetch.js +223 -49
  80. package/node_modules/axios/lib/adapters/http.js +408 -193
  81. package/node_modules/axios/lib/adapters/xhr.js +3 -1
  82. package/node_modules/axios/lib/core/Axios.js +4 -2
  83. package/node_modules/axios/lib/core/AxiosError.js +13 -1
  84. package/node_modules/axios/lib/core/AxiosHeaders.js +13 -42
  85. package/node_modules/axios/lib/core/buildFullPath.js +29 -1
  86. package/node_modules/axios/lib/core/mergeConfig.js +35 -0
  87. package/node_modules/axios/lib/defaults/transitional.js +2 -0
  88. package/node_modules/axios/lib/env/data.js +1 -1
  89. package/node_modules/axios/lib/helpers/AxiosURLSearchParams.js +1 -3
  90. package/node_modules/axios/lib/helpers/Http2Sessions.js +119 -0
  91. package/node_modules/axios/lib/helpers/buildURL.js +7 -4
  92. package/node_modules/axios/lib/helpers/composeSignals.js +48 -47
  93. package/node_modules/axios/lib/helpers/cookies.js +5 -1
  94. package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +16 -11
  95. package/node_modules/axios/lib/helpers/formDataToJSON.js +26 -4
  96. package/node_modules/axios/lib/helpers/formDataToStream.js +2 -2
  97. package/node_modules/axios/lib/helpers/fromDataURI.js +20 -5
  98. package/node_modules/axios/lib/helpers/progressEventReducer.js +3 -0
  99. package/node_modules/axios/lib/helpers/resolveConfig.js +26 -13
  100. package/node_modules/axios/lib/helpers/sanitizeHeaderValue.js +60 -0
  101. package/node_modules/axios/lib/helpers/shouldBypassProxy.js +33 -1
  102. package/node_modules/axios/lib/helpers/toFormData.js +48 -12
  103. package/node_modules/axios/lib/helpers/validator.js +1 -1
  104. package/node_modules/axios/lib/utils.js +105 -19
  105. package/node_modules/axios/package.json +31 -13
  106. package/node_modules/https-proxy-agent/README.md +137 -0
  107. package/node_modules/https-proxy-agent/dist/agent.d.ts +30 -0
  108. package/node_modules/https-proxy-agent/dist/agent.js +177 -0
  109. package/node_modules/https-proxy-agent/dist/agent.js.map +1 -0
  110. package/node_modules/https-proxy-agent/dist/index.d.ts +23 -0
  111. package/node_modules/https-proxy-agent/dist/index.js +14 -0
  112. package/node_modules/https-proxy-agent/dist/index.js.map +1 -0
  113. package/node_modules/https-proxy-agent/dist/parse-proxy-response.d.ts +7 -0
  114. package/node_modules/https-proxy-agent/dist/parse-proxy-response.js +66 -0
  115. package/node_modules/https-proxy-agent/dist/parse-proxy-response.js.map +1 -0
  116. package/node_modules/https-proxy-agent/package.json +56 -0
  117. package/node_modules/iconv-lite/encodings/sbcs-data.js +2 -0
  118. package/node_modules/iconv-lite/encodings/utf32.js +10 -3
  119. package/node_modules/iconv-lite/package.json +2 -2
  120. package/node_modules/iconv-lite/types/encodings.d.ts +2 -0
  121. package/node_modules/path-to-regexp/Readme.md +3 -3
  122. package/node_modules/path-to-regexp/dist/index.js +135 -158
  123. package/node_modules/path-to-regexp/dist/index.js.map +1 -1
  124. package/node_modules/path-to-regexp/package.json +2 -2
  125. package/node_modules/qs/CHANGELOG.md +11 -0
  126. package/node_modules/qs/dist/qs.js +17 -17
  127. package/node_modules/qs/lib/parse.js +19 -8
  128. package/node_modules/qs/lib/utils.js +47 -8
  129. package/node_modules/qs/package.json +6 -5
  130. package/node_modules/qs/test/parse.js +230 -0
  131. package/node_modules/qs/test/utils.js +194 -0
  132. package/node_modules/typebox/build/compile/validator.d.mts +1 -4
  133. package/node_modules/typebox/build/guard/guard.d.mts +1 -1
  134. package/node_modules/typebox/build/guard/guard.mjs +5 -2
  135. package/node_modules/typebox/build/guard/string.mjs +16 -4
  136. package/node_modules/typebox/build/schema/engine/_functions.mjs +34 -15
  137. package/node_modules/typebox/build/type/action/readonly_object.d.mts +5 -0
  138. package/node_modules/typebox/build/type/action/readonly_object.mjs +8 -0
  139. package/node_modules/typebox/build/typebox.d.mts +1 -1
  140. package/node_modules/typebox/build/typebox.mjs +1 -1
  141. package/node_modules/typebox/package.json +1 -1
  142. package/node_modules/typebox/readme.md +29 -32
  143. package/node_modules/ws/lib/receiver.js +15 -32
  144. package/node_modules/ws/lib/websocket-server.js +4 -4
  145. package/node_modules/ws/lib/websocket.js +4 -4
  146. package/node_modules/ws/package.json +5 -1
  147. package/npm-shrinkwrap.json +54 -27
  148. package/openclaw.plugin.json +1331 -1424
  149. package/package.json +92 -14
  150. package/dist/accounts.runtime-KvftRRek.js +0 -2
  151. package/dist/action-runtime.runtime-DZfd7IFr.js +0 -2
  152. package/dist/client-WIPAS3Yr.js +0 -160
  153. package/dist/config-api-Df9a1B8C.js +0 -3
  154. package/dist/doctor-contract-DddfkMlF.js +0 -134
  155. package/dist/probe-CN0ssfLN.js +0 -48
  156. package/dist/send.runtime-CJLxAt26.js +0 -2
  157. package/dist/setup-core-CkwVLzFO.js +0 -337
  158. package/node_modules/axios/dist/axios.js.map +0 -1
  159. package/node_modules/axios/dist/browser/axios.cjs.map +0 -1
  160. package/node_modules/axios/dist/esm/axios.js.map +0 -1
  161. package/node_modules/axios/dist/node/axios.cjs.map +0 -1
@@ -1,20 +1,20 @@
1
- import { u as resolveSlackReplyToMode } from "./accounts-BOJJiHSr.js";
1
+ import { t as resolveSlackReplyToMode } from "./account-reply-mode-DrUXogvO.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-CdnQzQBI.js";
5
+ import { n as isSlackInteractiveRepliesEnabled, t as compileSlackInteractiveReplies } from "./interactive-replies-C3ffk6oN.js";
6
+ import { r as formatSlackError } from "./probe-CVRuArHA.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-D51foiwR.js";
9
+ import { c as hasSlackThreadParticipationWithPersistence, l as recordSlackThreadParticipation, r as sendMessageSlack, u as buildSlackNativeDataDeliveryPlan } from "./send-BZ1tPv7-.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-htuIJGFD.js";
11
+ import { n as resolveSlackChannelConfig } from "./policy-C31RYSuT.js";
12
+ import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, s as emitSlackMessageSentHooks, t as createSlackReplyDeliveryPlan } from "./replies-Cgr2Gli4.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
- import { danger, logVerbose, shouldLogVerbose, sleep } from "openclaw/plugin-sdk/runtime-env";
17
- import { stripReasoningTagsFromText } from "openclaw/plugin-sdk/text-chunking";
17
+ import { danger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
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";
31
- import { DEFAULT_TIMING, createStatusReactionController, logAckFailure, logTypingFailure, removeAckReactionAfterReply, resolveAckReaction, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
32
- import { dispatchReplyWithBufferedBlockDispatcher } from "openclaw/plugin-sdk/reply-runtime";
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";
32
+ import { DEFAULT_TIMING, createStatusReactionController, logAckFailure, logTypingFailure, resolveAckReaction, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
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 {
@@ -967,36 +954,31 @@ async function dispatchPreparedSlackMessage(prepared) {
967
954
  const incomingThreadTs = message.thread_ts;
968
955
  let didSetStatus = false;
969
956
  const statusReactionsEnabled = prepared.ctxPayload.InboundEventKind !== "room_event" && Boolean(prepared.ackReactionPromise) && Boolean(reactionMessageTs) && cfg.messages?.statusReactions?.enabled === true;
970
- const slackStatusAdapter = {
971
- setReaction: async (emoji) => {
972
- await reactSlackMessage(message.channel, reactionMessageTs ?? "", emoji, {
973
- token: ctx.botToken,
974
- client: slackClient
975
- }).catch((err) => {
976
- if (formatErrorMessage(err).includes("already_reacted")) return;
977
- throw err;
978
- });
979
- },
980
- removeReaction: async (emoji) => {
981
- await removeSlackReaction(message.channel, reactionMessageTs ?? "", emoji, {
982
- token: ctx.botToken,
983
- client: slackClient
984
- }).catch((err) => {
985
- if (formatErrorMessage(err).includes("no_reaction")) return;
986
- throw err;
987
- });
988
- }
989
- };
990
- const statusReactionTiming = {
991
- ...DEFAULT_TIMING,
992
- ...cfg.messages?.statusReactions?.timing
993
- };
994
957
  const statusReactions = createStatusReactionController({
995
958
  enabled: statusReactionsEnabled,
996
- adapter: slackStatusAdapter,
959
+ adapter: {
960
+ setReaction: async (emoji) => {
961
+ await reactSlackMessage(message.channel, reactionMessageTs ?? "", emoji, {
962
+ token: ctx.botToken,
963
+ client: slackClient
964
+ }).catch((err) => {
965
+ if (formatErrorMessage(err).includes("already_reacted")) return;
966
+ throw err;
967
+ });
968
+ },
969
+ removeReaction: async (emoji) => {
970
+ await removeSlackReaction(message.channel, reactionMessageTs ?? "", emoji, {
971
+ token: ctx.botToken,
972
+ client: slackClient
973
+ }).catch((err) => {
974
+ if (formatErrorMessage(err).includes("no_reaction")) return;
975
+ throw err;
976
+ });
977
+ }
978
+ },
997
979
  initialEmoji: prepared.ackReactionValue || "eyes",
998
- emojis: cfg.messages?.statusReactions?.emojis,
999
- timing: cfg.messages?.statusReactions?.timing,
980
+ emojis: void 0,
981
+ timing: DEFAULT_TIMING,
1000
982
  onError: (err) => {
1001
983
  logAckFailure({
1002
984
  log: logVerbose,
@@ -1036,7 +1018,6 @@ async function dispatchPreparedSlackMessage(prepared) {
1036
1018
  channelId: message.channel,
1037
1019
  threadTs: statusThreadTs,
1038
1020
  status: "is typing...",
1039
- loadingMessages: SLACK_THREAD_LOADING_MESSAGES,
1040
1021
  eventScope: prepared.eventScope
1041
1022
  });
1042
1023
  if (typingReaction && message.ts) await reactSlackMessage(message.channel, message.ts, typingReaction, {
@@ -1156,6 +1137,11 @@ async function dispatchPreparedSlackMessage(prepared) {
1156
1137
  delivery.outcome = "success";
1157
1138
  }
1158
1139
  };
1140
+ const acknowledgeStoppedStreamedDeliveries = (session, messageId) => {
1141
+ refreshStreamedAcknowledgements(session);
1142
+ for (const delivery of streamedDeliveries) delivery.acknowledged = true;
1143
+ emitAcknowledgedStreamedDeliveries(messageId);
1144
+ };
1159
1145
  const emitFailedPendingStreamedDeliveries = (error) => {
1160
1146
  for (const delivery of streamedDeliveries) {
1161
1147
  if (delivery.acknowledged || delivery.outcome) continue;
@@ -1209,8 +1195,7 @@ async function dispatchPreparedSlackMessage(prepared) {
1209
1195
  session,
1210
1196
  ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
1211
1197
  });
1212
- for (const delivery of streamedDeliveries) delivery.acknowledged = true;
1213
- emitAcknowledgedStreamedDeliveries(stopResult.messageId);
1198
+ acknowledgeStoppedStreamedDeliveries(session, stopResult.messageId);
1214
1199
  observedReplyDelivery = true;
1215
1200
  usedReplyThreadTs ??= session.threadTs;
1216
1201
  return true;
@@ -1319,6 +1304,24 @@ async function dispatchPreparedSlackMessage(prepared) {
1319
1304
  rememberDeliveredThreadTs(params.kind, params.session.threadTs);
1320
1305
  return true;
1321
1306
  };
1307
+ const appendNativeProgressCompletion = async (isError) => {
1308
+ const session = streamSession;
1309
+ if (isError) nativeProgressTerminalStatus = "error";
1310
+ if (!session || nativeProgressCompletionSent) return;
1311
+ const chunks = buildNativeProgressCompletionChunks(isError ? "error" : "complete");
1312
+ if (!chunks?.length) return;
1313
+ try {
1314
+ await appendSlackStream({
1315
+ session,
1316
+ chunks
1317
+ });
1318
+ nativeProgressCompletionSent = true;
1319
+ observedReplyDelivery ||= session.delivered;
1320
+ } catch (err) {
1321
+ streamFailed = true;
1322
+ runtime.error?.(danger(`slack-stream: native progress completion failed: ${formatSlackError(err)}`));
1323
+ }
1324
+ };
1322
1325
  const deliverWithStreaming = async (params) => {
1323
1326
  if (params.payload.isReasoning === true) return;
1324
1327
  const reply = resolveSendableOutboundReplyParts(params.payload);
@@ -1344,13 +1347,6 @@ async function dispatchPreparedSlackMessage(prepared) {
1344
1347
  });
1345
1348
  return;
1346
1349
  }
1347
- if (useNativeProgressStreaming && !streamSession) {
1348
- await deliverNormally({
1349
- payload: params.payload,
1350
- kind: params.kind
1351
- });
1352
- return;
1353
- }
1354
1350
  if (!streamSession) {
1355
1351
  const streamThreadTs = replyPlan.nextThreadTs();
1356
1352
  plannedThreadTs = streamThreadTs;
@@ -1411,40 +1407,12 @@ async function dispatchPreparedSlackMessage(prepared) {
1411
1407
  logVerbose("slack-stream: suppressed duplicate append payload");
1412
1408
  return;
1413
1409
  }
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
1410
  if (text) recordStreamedDelivery(params.kind, text);
1441
1411
  await appendSlackStream({
1442
1412
  session: streamSession,
1443
- text: "\n" + text,
1444
- chunks: completionChunks
1413
+ text: "\n" + text
1445
1414
  });
1446
1415
  refreshStreamedAcknowledgements(streamSession);
1447
- if (completionChunks?.length) nativeProgressCompletionSent = true;
1448
1416
  if (streamSession.delivered) {
1449
1417
  observedReplyDelivery = true;
1450
1418
  if (params.kind === "final") observedFinalReplyDelivery = true;
@@ -1491,13 +1459,59 @@ async function dispatchPreparedSlackMessage(prepared) {
1491
1459
  await deliverNormally({
1492
1460
  payload: params.payload,
1493
1461
  kind: params.kind,
1494
- forcedThreadTs: streamSession?.threadTs ?? plannedThreadTs
1462
+ forcedThreadTs: streamSession?.threadTs ?? nativeProgressStreamThreadTs ?? plannedThreadTs
1495
1463
  });
1496
1464
  }
1497
1465
  };
1498
1466
  let draftPreviewCommitted = false;
1499
1467
  const deliverSlackPayload = async (payload, info) => {
1500
1468
  if (payload.isReasoning === true) return { visibleReplySent: false };
1469
+ if (info.kind === "final" && slackStreaming.mode === "progress" && streamMode === "status_final") {
1470
+ const hadProgressDraft = progressDraft.hasStarted;
1471
+ progressDraft.markFinalReplyStarted();
1472
+ if (useNativeProgressStreaming) {
1473
+ await waitForNativeProgressStreamStart();
1474
+ const finalThreadTs = streamSession?.threadTs ?? nativeProgressStreamThreadTs;
1475
+ await deliverNormally({
1476
+ payload,
1477
+ kind: info.kind,
1478
+ forcedThreadTs: finalThreadTs
1479
+ });
1480
+ await appendNativeProgressCompletion(payload.isError === true);
1481
+ progressDraft.markFinalReplyDelivered();
1482
+ if (!payload.isError && hadProgressDraft && streamSession) pendingNativeProgressReceipt = progressReceipt.buildSummaryLine();
1483
+ return;
1484
+ }
1485
+ if (hadProgressDraft) try {
1486
+ await draftStream?.flush();
1487
+ } catch (err) {
1488
+ logVerbose(`slack: progress draft flush before final failed (${formatSlackError(err)})`);
1489
+ }
1490
+ const receiptChannelId = hadProgressDraft ? draftStream?.channelId() : void 0;
1491
+ const receiptMessageId = hadProgressDraft ? draftStream?.messageId() : void 0;
1492
+ const draftThreadTs = hadProgressDraft ? usedReplyThreadTs ?? statusThreadTs : void 0;
1493
+ await deliverNormally({
1494
+ payload,
1495
+ kind: info.kind,
1496
+ ...draftThreadTs ? { forcedThreadTs: draftThreadTs } : {}
1497
+ });
1498
+ progressDraft.markFinalReplyDelivered();
1499
+ if (!payload.isError && receiptChannelId && receiptMessageId && !progressReceiptCollapsed) await collapseProgressReceipt({
1500
+ channelId: receiptChannelId,
1501
+ messageId: receiptMessageId,
1502
+ text: progressReceipt.buildSummaryLine(),
1503
+ threadTs: usedReplyThreadTs ?? statusThreadTs
1504
+ });
1505
+ return;
1506
+ }
1507
+ if (useNativeProgressStreaming) {
1508
+ await deliverNormally({
1509
+ payload,
1510
+ kind: info.kind,
1511
+ forcedThreadTs: streamSession?.threadTs ?? nativeProgressStreamThreadTs
1512
+ });
1513
+ return;
1514
+ }
1501
1515
  if (useStreaming) {
1502
1516
  await deliverWithStreaming({
1503
1517
  payload,
@@ -1688,8 +1702,6 @@ async function dispatchPreparedSlackMessage(prepared) {
1688
1702
  const useNativeProgressStreaming = useStreaming && slackStreaming.mode === "progress";
1689
1703
  const progressDraftActive = Boolean(draftStream) || useNativeProgressStreaming;
1690
1704
  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
1705
  let shouldYieldDraftProgress = () => false;
1694
1706
  const suppressDefaultToolProgressMessages = resolveChannelStreamingSuppressDefaultToolProgressMessages(account.config, {
1695
1707
  draftStreamActive: Boolean(draftStream) || useNativeProgressStreaming,
@@ -1697,46 +1709,20 @@ async function dispatchPreparedSlackMessage(prepared) {
1697
1709
  previewStreamingEnabled
1698
1710
  });
1699
1711
  let previewToolProgressSuppressed = false;
1700
- let previewToolProgressLines = [];
1701
- let lastNonEmptyPreviewToolProgressLines = [];
1702
- let latestPlan;
1703
- let latestPlanExplanation;
1712
+ let legacyPreviewToolProgressLines = [];
1704
1713
  let nativeTaskState = /* @__PURE__ */ new Map();
1705
1714
  let appendRenderedText = "";
1706
1715
  let appendSourceText = "";
1707
- let reasoningProgressRawText = "";
1708
- let statusUpdateCount = 0;
1709
1716
  let nativeProgressCompletionSent = false;
1717
+ let nativeProgressTerminalStatus = "complete";
1710
1718
  let nativeProgressChunkKey;
1719
+ const progressReceipt = createChannelProgressReceiptTracker();
1720
+ let progressReceiptCollapsed = false;
1721
+ let pendingNativeProgressReceipt;
1711
1722
  const progressSeed = `${account.accountId}:${message.channel}`;
1712
1723
  const useRichProgressDraft = streamMode === "status_final" && resolveChannelProgressDraftRender(account.config) === "rich";
1713
1724
  const explicitProgressTitle = resolveExplicitSlackProgressTitle(account.config);
1714
1725
  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
1726
  const waitForNativeProgressStreamStart = async () => {
1741
1727
  if (streamSession || !nativeProgressStreamStartPromise) return true;
1742
1728
  try {
@@ -1747,26 +1733,40 @@ async function dispatchPreparedSlackMessage(prepared) {
1747
1733
  }
1748
1734
  return !streamFailed;
1749
1735
  };
1750
- const resolveNativeProgressPlan = () => latestPlan?.length ? latestPlan : void 0;
1751
- const resolveNativeProgressLines = () => {
1752
- if (latestPlan?.length || !latestPlanExplanation) return previewToolProgressLines;
1736
+ const resolveStructuredProgressLines = (lines) => lines.map((line) => {
1737
+ if (typeof line !== "string") return line;
1738
+ const reasoning = line.startsWith("🧠 ");
1739
+ const text = line.replace(/^(?:🧠|💬)\s+/u, "").replace(/^_(.*)_$/su, "$1").trim();
1740
+ return {
1741
+ ...reasoning ? { id: "reasoning" } : {},
1742
+ kind: "item",
1743
+ text,
1744
+ label: reasoning ? "Reasoning" : "Update",
1745
+ prefix: false
1746
+ };
1747
+ });
1748
+ const resolveNativeProgressPlan = (snapshot) => snapshot.plan?.length ? snapshot.plan : void 0;
1749
+ const resolveNativeProgressLines = (snapshot) => {
1750
+ const lines = resolveStructuredProgressLines(snapshot.lines);
1751
+ if (snapshot.plan?.length || !snapshot.planExplanation) return lines;
1753
1752
  const explanationLine = buildChannelProgressDraftLine({
1754
1753
  event: "plan",
1755
1754
  phase: "update",
1756
- explanation: latestPlanExplanation
1755
+ explanation: snapshot.planExplanation
1757
1756
  });
1758
- return explanationLine ? [...previewToolProgressLines, explanationLine] : previewToolProgressLines;
1757
+ return explanationLine ? [...lines, explanationLine] : lines;
1759
1758
  };
1760
- const resolveNativeProgressTitle = () => explicitProgressTitle && latestPlanExplanation ? `${explicitProgressTitle} — ${latestPlanExplanation}` : explicitProgressTitle ?? latestPlanExplanation;
1761
- const buildNativeProgressChunks = () => streamSession ? buildSlackProgressStreamUpdateChunks({
1762
- title: resolveNativeProgressTitle(),
1763
- lines: resolveNativeProgressLines(),
1764
- plan: resolveNativeProgressPlan(),
1759
+ const combineProgressHeadlineAndExplanation = (headline, explanation) => headline && explanation && headline !== explanation ? `${headline} — ${explanation}` : headline ?? explanation;
1760
+ const resolveNativeProgressTitle = (snapshot) => combineProgressHeadlineAndExplanation(explicitProgressTitle ?? snapshot.statusHeadline, snapshot.planExplanation);
1761
+ const buildNativeProgressChunks = (snapshot) => streamSession ? buildSlackProgressStreamUpdateChunks({
1762
+ title: resolveNativeProgressTitle(snapshot),
1763
+ lines: resolveNativeProgressLines(snapshot),
1764
+ plan: resolveNativeProgressPlan(snapshot),
1765
1765
  maxLineChars: progressDraftMaxLineChars
1766
1766
  }) : buildSlackProgressStreamStartChunks({
1767
- title: resolveNativeProgressTitle(),
1768
- lines: resolveNativeProgressLines(),
1769
- plan: resolveNativeProgressPlan(),
1767
+ title: resolveNativeProgressTitle(snapshot),
1768
+ lines: resolveNativeProgressLines(snapshot),
1769
+ plan: resolveNativeProgressPlan(snapshot),
1770
1770
  maxLineChars: progressDraftMaxLineChars
1771
1771
  });
1772
1772
  const markNativeProgressDelivered = (session, threadTs) => {
@@ -1824,12 +1824,14 @@ async function dispatchPreparedSlackMessage(prepared) {
1824
1824
  nativeProgressChunkKey = chunkKey;
1825
1825
  };
1826
1826
  const updateNativeProgressStream = async () => {
1827
+ const snapshot = progressDraft.getSnapshot();
1828
+ const progressLines = resolveNativeProgressLines(snapshot);
1827
1829
  const hasRetirableNativeTasks = [...nativeTaskState.values()].some((task) => task.status !== "complete" && task.status !== "error");
1828
- if (!useNativeProgressStreaming || streamFailed || previewToolProgressLines.length === 0 && !latestPlan?.length && !latestPlanExplanation && !hasRetirableNativeTasks) return;
1830
+ if (!useNativeProgressStreaming || streamFailed || progressLines.length === 0 && !snapshot.plan?.length && !snapshot.statusHeadline && !explicitProgressTitle && !hasRetirableNativeTasks) return;
1829
1831
  if (!await waitForNativeProgressStreamStart()) return;
1830
1832
  const reconciled = reconcileSlackNativeTaskChunks({
1831
1833
  previousTasks: nativeTaskState,
1832
- chunks: buildNativeProgressChunks()
1834
+ chunks: buildNativeProgressChunks(snapshot)
1833
1835
  });
1834
1836
  const chunks = reconciled.chunks;
1835
1837
  if (!chunks?.length) return;
@@ -1844,85 +1846,154 @@ async function dispatchPreparedSlackMessage(prepared) {
1844
1846
  streamFailed = true;
1845
1847
  }
1846
1848
  };
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
1849
+ const resetProgressTurnState = () => {
1850
+ progressReceipt.reset();
1851
+ progressReceiptCollapsed = false;
1852
+ pendingNativeProgressReceipt = void 0;
1853
+ };
1854
+ const collapseProgressReceipt = async (params) => {
1855
+ const { botToken: token } = ctx;
1856
+ try {
1857
+ await finalizeSlackPreviewEdit({
1858
+ client: slackClient,
1859
+ token,
1860
+ accountId: account.accountId,
1861
+ ...params
1861
1862
  });
1862
- if (text) {
1863
- draftStream?.update(text);
1864
- hasStreamedMessage = true;
1863
+ progressReceiptCollapsed = true;
1864
+ } catch (err) {
1865
+ logVerbose(`slack: progress receipt edit failed (${formatSlackError(err)})`);
1866
+ }
1867
+ };
1868
+ const progressDraft = createChannelProgressDraftCompositor({
1869
+ entry: account.config,
1870
+ mode: slackStreaming.mode,
1871
+ active: progressDraftActive && streamMode === "status_final",
1872
+ seed: progressSeed,
1873
+ formatLine: escapeSlackMrkdwn,
1874
+ reasoningLinePrefix: "🧠 ",
1875
+ commentaryLinePrefix: "💬 ",
1876
+ reasoningGate: previewToolProgressEnabled,
1877
+ commentaryItalics: false,
1878
+ updateOnLineChange: useNativeProgressStreaming || useRichProgressDraft,
1879
+ update: async (previewText, options) => {
1880
+ if (useNativeProgressStreaming) {
1881
+ await updateNativeProgressStream();
1882
+ return;
1865
1883
  }
1866
- return;
1884
+ if (!draftStream) return;
1885
+ const snapshot = progressDraft.getSnapshot();
1886
+ const structuredLines = resolveStructuredProgressLines(options?.lines ?? snapshot.lines);
1887
+ const richNarration = combineProgressHeadlineAndExplanation(snapshot.statusHeadline, snapshot.planExplanation);
1888
+ const richProgressBlocks = useRichProgressDraft ? buildSlackProgressDraftBlocks({
1889
+ title: explicitProgressTitle,
1890
+ lines: structuredLines,
1891
+ plan: snapshot.plan,
1892
+ narration: richNarration,
1893
+ maxLineChars: progressDraftMaxLineChars
1894
+ }) : void 0;
1895
+ draftStream.update(useRichProgressDraft && richProgressBlocks ? {
1896
+ text: previewText,
1897
+ blocks: richProgressBlocks
1898
+ } : previewText);
1899
+ hasStreamedMessage = true;
1900
+ if (options?.flush) await draftStream.flush();
1867
1901
  }
1868
- const alreadyStarted = progressDraftGate.hasStarted;
1869
- await progressDraftGate.startNow();
1870
- if (alreadyStarted && progressDraftGate.hasStarted) await refreshStartedProgressDraft();
1902
+ });
1903
+ const commentaryProgressEnabled = progressDraft.commentaryProgressEnabled;
1904
+ const buildNativeProgressCompletionChunks = (finalInProgressStatus) => {
1905
+ const snapshot = progressDraft.getSnapshot();
1906
+ const lines = resolveNativeProgressLines(snapshot);
1907
+ const hasRetirableNativeTasks = [...nativeTaskState.values()].some((task) => task.status !== "complete" && task.status !== "error");
1908
+ if (lines.length === 0 && !snapshot.plan?.length && !hasRetirableNativeTasks) return;
1909
+ return reconcileSlackNativeTaskChunks({
1910
+ previousTasks: nativeTaskState,
1911
+ chunks: buildSlackProgressStreamCompletionChunks({
1912
+ title: resolveNativeProgressTitle(snapshot),
1913
+ lines,
1914
+ plan: resolveNativeProgressPlan(snapshot),
1915
+ maxLineChars: progressDraftMaxLineChars,
1916
+ finalInProgressStatus
1917
+ })
1918
+ }).chunks;
1871
1919
  };
1872
- const refreshStartedProgressDraft = async () => {
1873
- if (useNativeProgressStreaming) await updateNativeProgressStream();
1874
- else renderProgressDraft();
1920
+ const finishNativeProgressTurn = async (completionChunks) => {
1921
+ if (nativeProgressStreamStartPromise) await nativeProgressStreamStartPromise.catch(() => null);
1922
+ const session = streamSession;
1923
+ if (session && !session.stopped) try {
1924
+ if (completionChunks?.length) nativeProgressCompletionSent = true;
1925
+ const stopResult = await stopSlackStream({
1926
+ session,
1927
+ ...completionChunks?.length ? { chunks: completionChunks } : {},
1928
+ ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
1929
+ });
1930
+ acknowledgeStoppedStreamedDeliveries(session, stopResult?.messageId);
1931
+ if (pendingNativeProgressReceipt && stopResult?.messageId) await collapseProgressReceipt({
1932
+ channelId: session.channel,
1933
+ messageId: stopResult.messageId,
1934
+ text: pendingNativeProgressReceipt,
1935
+ threadTs: session.threadTs
1936
+ });
1937
+ } catch (err) {
1938
+ const error = formatSlackError(err);
1939
+ emitAcknowledgedStreamedDeliveries();
1940
+ emitFailedPendingStreamedDeliveries(error);
1941
+ logVerbose(`slack-stream: failed to rotate native progress stream (${error})`);
1942
+ }
1943
+ streamSession = null;
1944
+ nativeProgressStreamStartPromise = null;
1945
+ nativeProgressStreamThreadTs = void 0;
1946
+ streamFailed = false;
1875
1947
  };
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();
1948
+ const pushPlanProgress = async (steps, explanation) => {
1949
+ if (streamMode === "status_final") {
1950
+ await progressDraft.pushPlanProgress(steps, { explanation });
1888
1951
  return;
1889
1952
  }
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
- }));
1953
+ if (previewToolProgressSuppressed || !draftStream) return;
1954
+ const text = formatChannelProgressDraftText({
1955
+ entry: account.config,
1956
+ lines: legacyPreviewToolProgressLines,
1957
+ seed: progressSeed,
1958
+ formatLine: escapeSlackMrkdwn,
1959
+ narration: explanation,
1960
+ plan: steps
1961
+ });
1962
+ if (text) {
1963
+ draftStream.update(text);
1901
1964
  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
1965
  }
1908
- if (useNativeProgressStreaming) {
1909
- if (progressDraftGate.hasStarted) await updateNativeProgressStream();
1910
- else {
1911
- await progressDraftGate.startNow();
1912
- if (progressDraftGate.hasStarted) await updateNativeProgressStream();
1966
+ };
1967
+ const pushPreviewProgress = async (line, options) => {
1968
+ if (!draftStream && !useNativeProgressStreaming) return;
1969
+ if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
1970
+ const normalized = line?.text.replace(/\s+/g, " ").trim();
1971
+ if (streamMode === "status_final") {
1972
+ if (!line || !normalized) {
1973
+ await progressDraft.noteActivity();
1974
+ return;
1913
1975
  }
1976
+ await progressDraft.pushToolProgress(line, options);
1914
1977
  return;
1915
1978
  }
1916
- const alreadyStarted = progressDraftGate.hasStarted;
1917
- const progressActive = await progressDraftGate.noteWork();
1918
- if ((alreadyStarted || progressActive) && progressDraftGate.hasStarted) await refreshStartedProgressDraft();
1979
+ if (!line || !normalized || !draftStream || !previewToolProgressEnabled || previewToolProgressSuppressed) return;
1980
+ const nextLines = mergeChannelProgressDraftLine(legacyPreviewToolProgressLines, line, { maxLines: resolveChannelProgressDraftMaxLines(account.config) });
1981
+ if (nextLines === legacyPreviewToolProgressLines) return;
1982
+ legacyPreviewToolProgressLines = nextLines;
1983
+ draftStream.update(formatChannelProgressDraftText({
1984
+ entry: account.config,
1985
+ lines: legacyPreviewToolProgressLines,
1986
+ seed: progressSeed,
1987
+ formatLine: escapeSlackMrkdwn
1988
+ }));
1989
+ hasStreamedMessage = true;
1919
1990
  };
1920
1991
  const updateDraftFromPartial = (text) => {
1921
1992
  const trimmed = text?.trimEnd();
1922
1993
  if (!trimmed) return;
1923
1994
  if (streamMode === "append") {
1924
1995
  previewToolProgressSuppressed = true;
1925
- previewToolProgressLines = [];
1996
+ legacyPreviewToolProgressLines = [];
1926
1997
  const next = applyAppendOnlyStreamUpdate({
1927
1998
  incoming: trimmed,
1928
1999
  rendered: appendRenderedText,
@@ -1935,74 +2006,86 @@ async function dispatchPreparedSlackMessage(prepared) {
1935
2006
  hasStreamedMessage = true;
1936
2007
  return;
1937
2008
  }
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
- }
2009
+ if (streamMode === "status_final") return;
1945
2010
  previewToolProgressSuppressed = true;
1946
- previewToolProgressLines = [];
2011
+ legacyPreviewToolProgressLines = [];
1947
2012
  draftStream?.update(trimmed);
1948
2013
  hasStreamedMessage = true;
1949
2014
  };
1950
2015
  const pushReasoningProgress = async (payload) => {
1951
2016
  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
- });
2017
+ if (streamMode !== "status_final") {
2018
+ const normalized = progressDraft.mergeReasoningProgress(payload.text, { snapshot: payload.isReasoningSnapshot === true }).replace(/^_(.*)_$/su, "$1").trim();
2019
+ if (!normalized) return;
2020
+ await pushPreviewProgress({
2021
+ id: "reasoning",
2022
+ kind: "item",
2023
+ text: normalized,
2024
+ label: "Reasoning"
2025
+ });
2026
+ return;
2027
+ }
2028
+ progressReceipt.noteReasoning();
2029
+ await progressDraft.pushReasoningProgress(payload.text, { snapshot: payload.isReasoningSnapshot === true });
1961
2030
  };
1962
2031
  const resetDraftDeliveryState = () => {
1963
2032
  hasStreamedMessage = false;
1964
2033
  appendRenderedText = "";
1965
2034
  appendSourceText = "";
1966
- statusUpdateCount = 0;
1967
2035
  };
1968
2036
  const resetDraftProgressState = () => {
1969
- reasoningProgressRawText = "";
2037
+ progressDraft.resetReasoningProgress();
1970
2038
  previewToolProgressSuppressed = false;
1971
- previewToolProgressLines = [];
2039
+ legacyPreviewToolProgressLines = [];
2040
+ };
2041
+ const beginNewProgressTurn = async (options) => {
2042
+ const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent ? buildNativeProgressCompletionChunks(nativeProgressTerminalStatus) : void 0;
2043
+ if (!progressDraft.beginNewTurn(options)) return false;
2044
+ if (useNativeProgressStreaming) await finishNativeProgressTurn(completionChunks);
2045
+ else draftStream?.forceNewMessage();
2046
+ resetProgressTurnState();
2047
+ nativeTaskState = /* @__PURE__ */ new Map();
2048
+ nativeProgressCompletionSent = false;
2049
+ nativeProgressTerminalStatus = "complete";
2050
+ nativeProgressChunkKey = void 0;
2051
+ draftPreviewCommitted = false;
2052
+ observedFinalReplyDelivery = false;
2053
+ deliveryTracker = createSlackEventDeliveryTracker();
2054
+ progressReceiptCollapsed = false;
2055
+ return true;
1972
2056
  };
1973
- const onDraftBoundary = !shouldUseDraftStream ? void 0 : async () => {
1974
- if (hasStreamedMessage && streamMode !== "status_final") {
1975
- draftStream?.forceNewMessage();
1976
- resetDraftDeliveryState();
2057
+ const onDraftBoundary = !shouldUseDraftStream && !useNativeProgressStreaming ? void 0 : async () => {
2058
+ if (streamMode === "status_final") {
2059
+ await beginNewProgressTurn();
2060
+ return;
1977
2061
  }
2062
+ if (hasStreamedMessage) draftStream?.forceNewMessage();
2063
+ resetDraftDeliveryState();
1978
2064
  resetDraftProgressState();
1979
2065
  };
1980
- const onQueuedFollowupAdmitted = !shouldUseDraftStream ? void 0 : async () => {
2066
+ const onQueuedFollowupAdmitted = !shouldUseDraftStream && !useNativeProgressStreaming ? void 0 : async () => {
1981
2067
  await draftStream?.flush();
1982
- draftStream?.forceNewMessage();
1983
2068
  draftPreviewCommitted = false;
1984
2069
  observedFinalReplyDelivery = false;
2070
+ if (streamMode === "status_final") await beginNewProgressTurn({ force: true });
2071
+ else draftStream?.forceNewMessage();
1985
2072
  deliveryTracker = createSlackEventDeliveryTracker();
1986
2073
  resetDraftDeliveryState();
1987
2074
  resetDraftProgressState();
1988
- latestPlan = void 0;
1989
- latestPlanExplanation = void 0;
1990
- nativeTaskState = /* @__PURE__ */ new Map();
1991
2075
  };
1992
2076
  let dispatchError;
1993
2077
  let queuedFinal = false;
1994
2078
  let counts = {};
1995
2079
  try {
1996
- const turnResult = await dispatchChannelInboundReply({
2080
+ const turnResult = await dispatchChannelInboundTurn({
1997
2081
  cfg,
1998
2082
  channel: "slack",
1999
2083
  accountId: route.accountId,
2000
- agentId: route.agentId,
2001
- routeSessionKey: route.sessionKey,
2002
- storePath: prepared.turn.storePath,
2084
+ route: {
2085
+ agentId: route.agentId,
2086
+ sessionKey: route.sessionKey
2087
+ },
2003
2088
  ctxPayload: prepared.ctxPayload,
2004
- recordInboundSession,
2005
- dispatchReplyWithBufferedBlockDispatcher,
2006
2089
  dispatcherOptions: {
2007
2090
  ...replyPipeline,
2008
2091
  humanDelay: resolveHumanDelayConfig(cfg, route.agentId)
@@ -2015,6 +2098,7 @@ async function dispatchPreparedSlackMessage(prepared) {
2015
2098
  history: prepared.turn.history,
2016
2099
  botLoopProtection: resolveSlackBotLoopProtection(prepared),
2017
2100
  replyOptions: {
2101
+ ...prepared.turnAdoptionLifecycle ? { turnAdoptionLifecycle: prepared.turnAdoptionLifecycle } : {},
2018
2102
  skillFilter: prepared.channelConfig?.skills,
2019
2103
  sourceReplyDeliveryMode,
2020
2104
  suppressTyping: suppressRoomEventTyping ? true : void 0,
@@ -2023,6 +2107,8 @@ async function dispatchPreparedSlackMessage(prepared) {
2023
2107
  onModelSelected,
2024
2108
  suppressDefaultToolProgressMessages: suppressDefaultToolProgressMessages ? true : void 0,
2025
2109
  commentaryProgressEnabled: commentaryProgressEnabled ? true : void 0,
2110
+ progressPreambleEnabled: progressDraftActive && slackStreaming.mode === "progress" ? true : void 0,
2111
+ commentaryPayloadsEnabled: commentaryProgressEnabled ? true : void 0,
2026
2112
  onVerboseProgressVisibility: commentaryProgressEnabled ? (isActive) => {
2027
2113
  shouldYieldDraftProgress = isActive;
2028
2114
  } : void 0,
@@ -2032,7 +2118,10 @@ async function dispatchPreparedSlackMessage(prepared) {
2032
2118
  updateDraftFromPartial(payload.text);
2033
2119
  },
2034
2120
  onAssistantMessageStart: onDraftBoundary,
2035
- onReasoningEnd: onDraftBoundary,
2121
+ onReasoningEnd: async () => {
2122
+ progressReceipt.closeReasoning();
2123
+ await onDraftBoundary?.();
2124
+ },
2036
2125
  onQueuedFollowupAdmitted,
2037
2126
  onReasoningStream: statusReactionsEnabled || previewToolProgressEnabled ? async (payload) => {
2038
2127
  await pushReasoningProgress(payload);
@@ -2041,6 +2130,7 @@ async function dispatchPreparedSlackMessage(prepared) {
2041
2130
  } : void 0,
2042
2131
  onToolStart: async (payload) => {
2043
2132
  if (statusReactionsEnabled) await statusReactions.setTool(payload.name);
2133
+ if (payload.phase === "start") progressReceipt.noteToolCall(payload.name);
2044
2134
  await pushPreviewProgress(buildChannelProgressDraftLineForEntry(account.config, {
2045
2135
  event: "tool",
2046
2136
  itemId: payload.itemId,
@@ -2051,6 +2141,14 @@ async function dispatchPreparedSlackMessage(prepared) {
2051
2141
  }, payload.detailMode ? { detailMode: payload.detailMode } : void 0), { toolName: payload.name });
2052
2142
  },
2053
2143
  onItemEvent: async (payload) => {
2144
+ if (streamMode === "status_final" && payload.kind === "preamble") {
2145
+ if (shouldYieldDraftProgress()) return;
2146
+ await progressDraft.pushPreambleHeadline(payload.progressText, { itemId: payload.itemId });
2147
+ if (commentaryProgressEnabled) {
2148
+ if (await progressDraft.pushCommentaryProgress(payload.progressText, { itemId: payload.itemId })) progressReceipt.noteCommentary(payload.itemId, payload.progressText);
2149
+ }
2150
+ return;
2151
+ }
2054
2152
  await pushPreviewProgress(buildChannelProgressDraftLineForEntry(account.config, {
2055
2153
  event: "item",
2056
2154
  itemId: payload.itemId,
@@ -2063,11 +2161,11 @@ async function dispatchPreparedSlackMessage(prepared) {
2063
2161
  summary: payload.summary,
2064
2162
  progressText: payload.progressText,
2065
2163
  meta: payload.meta
2066
- }), payload.kind === "preamble" ? { lane: "commentary" } : void 0);
2164
+ }));
2067
2165
  },
2068
2166
  onPlanUpdate: async (payload) => {
2069
2167
  if (payload.phase !== "update") return;
2070
- await pushPlanProgress(payload.planSteps, payload.explanation);
2168
+ await pushPlanProgress(payload.steps, payload.explanation);
2071
2169
  },
2072
2170
  onApprovalEvent: async (payload) => {
2073
2171
  if (payload.phase !== "requested") return;
@@ -2118,30 +2216,26 @@ async function dispatchPreparedSlackMessage(prepared) {
2118
2216
  } catch (err) {
2119
2217
  dispatchError = err;
2120
2218
  } finally {
2121
- progressDraftGate.cancel();
2219
+ progressDraft.cancel();
2122
2220
  await draftStream?.discardPending();
2123
2221
  }
2124
2222
  let streamFallbackDelivered = false;
2125
2223
  const finalStream = streamSession;
2126
2224
  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;
2225
+ const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent ? buildNativeProgressCompletionChunks(dispatchError ? "error" : nativeProgressTerminalStatus) : void 0;
2137
2226
  if (completionChunks?.length) nativeProgressCompletionSent = true;
2138
2227
  const stopResult = await stopSlackStream({
2139
2228
  session: finalStream,
2140
2229
  ...completionChunks?.length ? { chunks: completionChunks } : {},
2141
2230
  ...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
2142
2231
  });
2143
- for (const delivery of streamedDeliveries) delivery.acknowledged = true;
2144
- emitAcknowledgedStreamedDeliveries(stopResult?.messageId);
2232
+ acknowledgeStoppedStreamedDeliveries(finalStream, stopResult?.messageId);
2233
+ if (pendingNativeProgressReceipt && stopResult?.messageId && !progressReceiptCollapsed) await collapseProgressReceipt({
2234
+ channelId: finalStream.channel,
2235
+ messageId: stopResult.messageId,
2236
+ text: pendingNativeProgressReceipt,
2237
+ threadTs: finalStream.threadTs
2238
+ });
2145
2239
  } catch (err) {
2146
2240
  if (err instanceof SlackStreamNotDeliveredError) {
2147
2241
  streamFallbackDelivered = await deliverPendingStreamFallback(finalStream, err);
@@ -2174,19 +2268,10 @@ async function dispatchPreparedSlackMessage(prepared) {
2174
2268
  observedReplyDelivery,
2175
2269
  fallbackDelivered: streamFallbackDelivered
2176
2270
  });
2177
- if (statusReactionsEnabled) if (dispatchError) {
2178
- await statusReactions.setError();
2179
- if (ctx.removeAckAfterReply) (async () => {
2180
- await sleep(statusReactionTiming.errorHoldMs);
2181
- if (anyReplyDelivered) await statusReactions.clear();
2182
- })();
2183
- } else if (anyReplyDelivered) {
2271
+ if (statusReactionsEnabled) if (dispatchError) await statusReactions.setError();
2272
+ else if (anyReplyDelivered) {
2184
2273
  await statusReactions.setDone();
2185
- if (ctx.removeAckAfterReply) (async () => {
2186
- await sleep(statusReactionTiming.doneHoldMs);
2187
- await statusReactions.clear();
2188
- })();
2189
- else statusReactions.restoreInitial();
2274
+ statusReactions.restoreInitial();
2190
2275
  } else await statusReactions.restoreInitial();
2191
2276
  const participationThreadTs = usedReplyThreadTs ?? statusThreadTs;
2192
2277
  if (anyReplyDelivered && participationThreadTs) recordSlackThreadParticipation(account.accountId, message.channel, participationThreadTs, {
@@ -2202,23 +2287,6 @@ async function dispatchPreparedSlackMessage(prepared) {
2202
2287
  const finalCount = counts.final;
2203
2288
  logVerbose(`slack: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${prepared.replyTarget}`);
2204
2289
  }
2205
- if (!statusReactionsEnabled) removeAckReactionAfterReply({
2206
- removeAfterReply: ctx.removeAckAfterReply && anyReplyDelivered,
2207
- ackReactionPromise: prepared.ackReactionPromise,
2208
- ackReactionValue: prepared.ackReactionValue,
2209
- remove: () => removeSlackReaction(message.channel, prepared.ackReactionMessageTs ?? "", prepared.ackReactionValue, {
2210
- token: ctx.botToken,
2211
- client: slackClient
2212
- }),
2213
- onError: (err) => {
2214
- logAckFailure({
2215
- log: logVerbose,
2216
- channel: "slack",
2217
- target: `${message.channel}/${message.ts}`,
2218
- error: err
2219
- });
2220
- }
2221
- });
2222
2290
  }
2223
2291
  function toLintErrorObject(value, fallbackMessage) {
2224
2292
  if (value instanceof Error) return value;
@@ -2228,6 +2296,51 @@ function toLintErrorObject(value, fallbackMessage) {
2228
2296
  return error;
2229
2297
  }
2230
2298
  //#endregion
2299
+ //#region extensions/slack/src/agent-context.ts
2300
+ function asRecord(value) {
2301
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
2302
+ }
2303
+ function trimmedString(value) {
2304
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
2305
+ }
2306
+ function normalizeEntity(value) {
2307
+ const entity = asRecord(value);
2308
+ const type = trimmedString(entity?.type);
2309
+ if (!entity || !type) return;
2310
+ const teamId = trimmedString(entity.team_id);
2311
+ if (type === "slack#/types/channel_id" || type === "slack#/types/canvas_id" || type === "slack#/types/list_id") {
2312
+ const entityValue = trimmedString(entity.value);
2313
+ return entityValue ? {
2314
+ type,
2315
+ value: entityValue,
2316
+ ...teamId ? { team_id: teamId } : {}
2317
+ } : void 0;
2318
+ }
2319
+ if (type !== "slack#/types/message_context") return;
2320
+ const message = asRecord(entity.value);
2321
+ const channelId = trimmedString(message?.channel_id);
2322
+ const messageTs = trimmedString(message?.message_ts);
2323
+ return channelId && messageTs ? {
2324
+ type,
2325
+ value: {
2326
+ channel_id: channelId,
2327
+ message_ts: messageTs
2328
+ },
2329
+ ...teamId ? { team_id: teamId } : {}
2330
+ } : void 0;
2331
+ }
2332
+ function isSlackAppContext(value) {
2333
+ return Boolean(asRecord(value));
2334
+ }
2335
+ function normalizeSlackAppContextEntities(value) {
2336
+ const context = asRecord(value);
2337
+ if (!Array.isArray(context?.entities)) return [];
2338
+ return context.entities.flatMap((entity) => {
2339
+ const normalized = normalizeEntity(entity);
2340
+ return normalized ? [normalized] : [];
2341
+ });
2342
+ }
2343
+ //#endregion
2231
2344
  //#region extensions/slack/src/monitor/message-handler/preflight-audio.ts
2232
2345
  const SLACK_DEFAULT_ECHO_TRANSCRIPT_FORMAT = "📝 \"{transcript}\"";
2233
2346
  const loadSlackPreflightAudioRuntime = createLazyRuntimeModule(() => import("./preflight-audio.runtime-D5Bgags0.js"));
@@ -2267,8 +2380,7 @@ async function resolveSlackPreflightAudioTranscript(params) {
2267
2380
  const { transcribeFirstAudio } = await loadSlackPreflightAudioRuntime();
2268
2381
  const transcript = await transcribeFirstAudio({
2269
2382
  ctx: {
2270
- MediaPaths: params.media.map((entry) => entry.path),
2271
- MediaTypes: params.media.map((entry) => entry.contentType ?? ""),
2383
+ media: [...params.media],
2272
2384
  Provider: "slack",
2273
2385
  Surface: "slack",
2274
2386
  OriginatingChannel: "slack",
@@ -2321,7 +2433,7 @@ async function discardSlackPreflightMedia(media) {
2321
2433
  const SLACK_MENTION_RESOLUTION_CONCURRENCY = 4;
2322
2434
  const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
2323
2435
  const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
2324
- const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-BZiuSTIq.js").then((n) => n.g));
2436
+ const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-D51foiwR.js").then((n) => n.g));
2325
2437
  function collectUniqueSlackMentionIds$1(texts) {
2326
2438
  const seen = /* @__PURE__ */ new Set();
2327
2439
  const mentionIds = [];
@@ -2600,7 +2712,7 @@ function resolveSlackInitialAgentRoute(params) {
2600
2712
  };
2601
2713
  }
2602
2714
  function resolveSlackRoutingContext(params) {
2603
- const { ctx, account, message, isDirectMessage, isGroupDm, isRoom, isRoomish, channelConfig, seedTopLevelRoomThread, assistantThreadTs, eventScope } = params;
2715
+ const { ctx, account, message, isDirectMessage, isGroupDm, isRoom, isRoomish, channelConfig, seedTopLevelRoomThread, assistantThreadTs, agentViewThreadTs, eventScope } = params;
2604
2716
  let route = resolveSlackInitialAgentRoute({
2605
2717
  ctx,
2606
2718
  account,
@@ -2620,7 +2732,7 @@ function resolveSlackRoutingContext(params) {
2620
2732
  const isThreadReply = threadContext.isThreadReply;
2621
2733
  const autoThreadId = !isThreadReply && replyToMode === "all" && threadContext.messageTs ? threadContext.messageTs : void 0;
2622
2734
  const seedCandidateThreadId = threadContext.incomingThreadTs ?? threadContext.messageTs;
2623
- const routedThreadId = (isDirectMessage ? assistantThreadTs : isRoomish ? isThreadReply && threadTs ? threadTs : void 0 : isThreadReply ? threadTs : autoThreadId) ?? (isRoomish ? !isThreadReply && isRoom && seedTopLevelRoomThread && replyToMode !== "off" && seedCandidateThreadId ? seedCandidateThreadId : void 0 : void 0);
2735
+ const routedThreadId = (isDirectMessage ? assistantThreadTs ?? agentViewThreadTs : isRoomish ? isThreadReply && threadTs ? threadTs : void 0 : isThreadReply ? threadTs : autoThreadId) ?? (isRoomish ? !isThreadReply && isRoom && seedTopLevelRoomThread && replyToMode !== "off" && seedCandidateThreadId ? seedCandidateThreadId : void 0 : void 0);
2624
2736
  const baseConversationId = resolveSlackBaseConversationId({
2625
2737
  message,
2626
2738
  isDirectMessage,
@@ -2743,7 +2855,7 @@ function formatSlackThreadLabelSnippet(text) {
2743
2855
  }
2744
2856
  //#endregion
2745
2857
  //#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
2746
- const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-BZiuSTIq.js").then((n) => n.g));
2858
+ const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-D51foiwR.js").then((n) => n.g));
2747
2859
  const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
2748
2860
  function resolveSlackThreadSessionFreshness(params) {
2749
2861
  return params.ctx.channelRuntime?.session?.resolveEntryResetFreshness?.({
@@ -2758,14 +2870,21 @@ function resolveSlackThreadSessionFreshness(params) {
2758
2870
  });
2759
2871
  }
2760
2872
  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;
2873
+ return resolveInboundSupplementalSenderAllowed({
2874
+ isGroup: true,
2875
+ groupPolicy: params.allowFromLower.length === 0 ? "open" : "allowlist",
2876
+ allowFrom: params.allowFromLower,
2877
+ isSenderAllowed: (allowFrom) => {
2878
+ if (params.botId) return true;
2879
+ if (!params.userId) return false;
2880
+ return resolveSlackAllowListMatch({
2881
+ allowList: allowFrom,
2882
+ id: params.userId,
2883
+ name: params.userName,
2884
+ allowNameMatching: params.allowNameMatching
2885
+ }).allowed;
2886
+ }
2887
+ });
2769
2888
  }
2770
2889
  async function resolveSlackThreadUserMap(params) {
2771
2890
  const uniqueUserIds = [];
@@ -3389,6 +3508,14 @@ async function prepareSlackMessage(params) {
3389
3508
  const assistantThreadContext = mergeSlackAssistantThreadContext(messageAssistantThreadContext, cachedAssistantThreadContext ?? restoredAssistantThreadContext);
3390
3509
  const assistantThreadContextToCache = messageAssistantThreadContext || restoredAssistantThreadContext ? assistantThreadContext : void 0;
3391
3510
  if (assistantThreadContextToCache) ctx.saveSlackAssistantThreadContext(assistantThreadContextToCache, opts.eventScope);
3511
+ const hasAgentViewMessageSignal = isDirectMessage && !opts.eventScope && !assistantThreadContext && isSlackAppContext(message.app_context);
3512
+ if (hasAgentViewMessageSignal) await ctx.recordSlackAgentView();
3513
+ const managedViewThreadTs = message.thread_ts;
3514
+ const isManagedViewRoot = isDirectMessage && !opts.eventScope && !assistantThreadContext && !message.parent_user_id && Boolean(managedViewThreadTs && message.ts && managedViewThreadTs === message.ts);
3515
+ if (isManagedViewRoot && managedViewThreadTs) await ctx.recordSlackManagedViewThread(message.channel, managedViewThreadTs);
3516
+ const hasManagedViewThread = isDirectMessage && !opts.eventScope && !assistantThreadContext && Boolean(managedViewThreadTs && await ctx.isSlackManagedViewThread(message.channel, managedViewThreadTs));
3517
+ const isAgentViewMessage = isDirectMessage && !opts.eventScope && !assistantThreadContext && (hasAgentViewMessageSignal || isManagedViewRoot || hasManagedViewThread || await ctx.isSlackAgentView());
3518
+ const agentViewThreadTs = isAgentViewMessage ? message.thread_ts ?? message.ts : void 0;
3392
3519
  const channelReplyToMode = channelConfig?.replyToMode ?? resolveSlackReplyToMode(account, channelChatType);
3393
3520
  const willImplicitlyThreadReply = isRoom && !channelRequireMention && channelReplyToMode !== "off";
3394
3521
  const seedTopLevelRoomThreadBySource = opts.source === "app_mention" || opts.wasMentioned === true || explicitlyMentioned || willImplicitlyThreadReply;
@@ -3403,6 +3530,7 @@ async function prepareSlackMessage(params) {
3403
3530
  channelConfig,
3404
3531
  seedTopLevelRoomThread: seedTopLevelRoomThreadBySource,
3405
3532
  assistantThreadTs: assistantThreadContext?.threadTs,
3533
+ agentViewThreadTs,
3406
3534
  eventScope: opts.eventScope
3407
3535
  });
3408
3536
  let mentionCheckTranscript;
@@ -3428,6 +3556,7 @@ async function prepareSlackMessage(params) {
3428
3556
  const { configuredBinding, configuredBindingSessionKey } = routing;
3429
3557
  const isAssistantThreadMessage = Boolean(isDirectMessage && messageAssistantThreadContext);
3430
3558
  const forcedAssistantReplyThreadTs = Boolean(assistantThreadContext?.threadTs && (isThreadReply || isAssistantThreadMessage || replyToMode !== "off")) ? assistantThreadContext?.threadTs : void 0;
3559
+ const forcedReplyThreadTs = agentViewThreadTs ?? forcedAssistantReplyThreadTs;
3431
3560
  if (runtimeBinding && shouldLogVerbose()) logVerbose(`slack: routed via bound conversation ${runtimeBinding.conversation.conversationId} -> ${runtimeBinding.targetSessionKey}`);
3432
3561
  if (configuredBinding) {
3433
3562
  const ensured = await ensureConfiguredBindingRouteReady({
@@ -3458,7 +3587,7 @@ async function prepareSlackMessage(params) {
3458
3587
  name: void 0
3459
3588
  });
3460
3589
  let implicitMentionKinds = [];
3461
- if (!isDirectMessage && ctx.botUserId && message.thread_ts && !ctx.threadRequireExplicitMention && !wasMentioned) {
3590
+ if (!isDirectMessage && ctx.botUserId && message.thread_ts && !wasMentioned) {
3462
3591
  const replyToBotKinds = implicitMentionKindWhen("reply_to_bot", message.parent_user_id === ctx.botUserId);
3463
3592
  implicitMentionKinds = replyToBotKinds.length > 0 ? replyToBotKinds : implicitMentionKindWhen("bot_thread_participant", await hasSlackThreadParticipationWithPersistence({
3464
3593
  accountId: account.accountId,
@@ -3554,6 +3683,11 @@ async function prepareSlackMessage(params) {
3554
3683
  surface: "slack"
3555
3684
  });
3556
3685
  const shouldRequireMention = isRoom ? channelConfig?.requireMention ?? ctx.defaultRequireMention : false;
3686
+ const implicitMentions = resolveChannelImplicitMentions({
3687
+ cfg,
3688
+ channel: "slack",
3689
+ accountId: account.accountId
3690
+ });
3557
3691
  if (message["_ambiguousThreadReply"]) {
3558
3692
  ctx.logger.info({
3559
3693
  channel: message.channel,
@@ -3586,7 +3720,7 @@ async function prepareSlackMessage(params) {
3586
3720
  activation: {
3587
3721
  requireMention: shouldRequireMention,
3588
3722
  allowTextCommands,
3589
- ...ctx.threadRequireExplicitMention ? { allowedImplicitMentionKinds: [] } : {}
3723
+ implicitMentions
3590
3724
  }
3591
3725
  });
3592
3726
  const senderGate = messageIngress.senderAccess.gate;
@@ -3633,6 +3767,7 @@ async function prepareSlackMessage(params) {
3633
3767
  channelConfig,
3634
3768
  seedTopLevelRoomThread: true,
3635
3769
  assistantThreadTs: assistantThreadContext?.threadTs,
3770
+ agentViewThreadTs,
3636
3771
  eventScope: opts.eventScope
3637
3772
  });
3638
3773
  return seededMentionRouting;
@@ -3684,7 +3819,7 @@ async function prepareSlackMessage(params) {
3684
3819
  preflightAudioTranscript = void 0;
3685
3820
  preflightAudioMedia = void 0;
3686
3821
  }
3687
- const directThreadRoutedToDmSession = !assistantThreadContext && isDirectMessage && isThreadReply && threadTs && runtimeBinding?.conversation.conversationId !== threadTs;
3822
+ const directThreadRoutedToDmSession = !assistantThreadContext && !agentViewThreadTs && isDirectMessage && isThreadReply && threadTs && runtimeBinding?.conversation.conversationId !== threadTs;
3688
3823
  const mentionDecision = resolveInboundMentionDecision({
3689
3824
  facts: {
3690
3825
  canDetectMention,
@@ -3698,7 +3833,7 @@ async function prepareSlackMessage(params) {
3698
3833
  allowTextCommands,
3699
3834
  hasControlCommand: hasControlCommandInMessage,
3700
3835
  commandAuthorized,
3701
- ...ctx.threadRequireExplicitMention ? { allowedImplicitMentionKinds: [] } : {}
3836
+ implicitMentions
3702
3837
  }
3703
3838
  });
3704
3839
  const effectiveWasMentioned = mentionDecision.effectiveWasMentioned;
@@ -3845,7 +3980,7 @@ async function prepareSlackMessage(params) {
3845
3980
  previousTimestamp,
3846
3981
  envelope: envelopeOptions
3847
3982
  });
3848
- const dmHistoryContext = isDirectMessage && !isThreadReply && dmHistoryLimit > 0 && !previousTimestamp ? await resolveSlackDmHistoryContext({
3983
+ const dmHistoryContext = isDirectMessage && !assistantThreadContext && !agentViewThreadTs && !isThreadReply && dmHistoryLimit > 0 && !previousTimestamp ? await resolveSlackDmHistoryContext({
3849
3984
  ctx,
3850
3985
  channelId: message.channel,
3851
3986
  currentMessageTs: message.ts,
@@ -3903,7 +4038,8 @@ async function prepareSlackMessage(params) {
3903
4038
  }) : dmHistoryContext.inboundHistory;
3904
4039
  const commandBody = textForCommandDetection.trim();
3905
4040
  const supplementalThreadHistoryBody = directThreadRoutedToDmSession && !threadHistoryBody ? threadStarterBody : threadHistoryBody;
3906
- const effectiveMessageThreadId = assistantThreadContext?.threadTs ?? threadContext.messageThreadId;
4041
+ const effectiveMessageThreadId = assistantThreadContext?.threadTs ?? agentViewThreadTs ?? threadContext.messageThreadId;
4042
+ const agentContextEntities = isAgentViewMessage ? normalizeSlackAppContextEntities(message.app_context) : [];
3907
4043
  const ctxPayload = buildChannelInboundEventContext({
3908
4044
  channel: "slack",
3909
4045
  accountId: route.accountId,
@@ -3926,6 +4062,7 @@ async function prepareSlackMessage(params) {
3926
4062
  },
3927
4063
  route: {
3928
4064
  agentId: route.agentId,
4065
+ dmScope: route.dmScope,
3929
4066
  accountId: route.accountId,
3930
4067
  routeSessionKey: sessionKey,
3931
4068
  parentSessionKey: threadKeys.parentSessionKey
@@ -3944,6 +4081,7 @@ async function prepareSlackMessage(params) {
3944
4081
  commandBody,
3945
4082
  inboundHistory
3946
4083
  },
4084
+ sessionTranscript: { historyLimit: isRoomish ? ctx.historyLimit : dmHistoryLimit },
3947
4085
  access: {
3948
4086
  mentions: {
3949
4087
  canDetectMention: isRoomish,
@@ -3967,8 +4105,15 @@ async function prepareSlackMessage(params) {
3967
4105
  extra: {
3968
4106
  GroupSubject: isRoomish ? roomLabel : void 0,
3969
4107
  UntrustedContext: untrustedChannelMetadata ? [untrustedChannelMetadata] : void 0,
4108
+ UntrustedStructuredContext: agentContextEntities.length > 0 ? [{
4109
+ label: "Slack active context",
4110
+ source: "slack",
4111
+ type: "active_view",
4112
+ payload: { entities: agentContextEntities }
4113
+ }] : void 0,
3970
4114
  TransportThreadId: directThreadRoutedToDmSession ? threadContext.messageThreadId : void 0,
3971
4115
  SlackAssistantThread: assistantThreadContext ? true : void 0,
4116
+ SlackAgentThread: agentViewThreadTs ? true : void 0,
3972
4117
  SlackAssistantThreadContextChannelId: assistantThreadContext?.channelId,
3973
4118
  SlackAssistantThreadContextTeamId: assistantThreadContext?.teamId,
3974
4119
  SlackAssistantThreadContextEnterpriseId: assistantThreadContext?.enterpriseId ?? void 0,
@@ -4058,7 +4203,7 @@ async function prepareSlackMessage(params) {
4058
4203
  } : void 0
4059
4204
  },
4060
4205
  replyToMode,
4061
- ...forcedAssistantReplyThreadTs ? { forcedReplyThreadTs: forcedAssistantReplyThreadTs } : {},
4206
+ ...forcedReplyThreadTs ? { forcedReplyThreadTs } : {},
4062
4207
  ...assistantThreadContext ? { slackMessageMetadata: buildSlackAssistantThreadMetadata(assistantThreadContext) } : {},
4063
4208
  requireMention: shouldRequireMention,
4064
4209
  isDirectMessage,