@openclaw/slack 2026.8.1 → 2026.8.2

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 (60) hide show
  1. package/dist/{account-inspect-CN2hBsim.js → account-inspect-C3sePHWa.js} +3 -3
  2. package/dist/account-inspect-api.js +1 -1
  3. package/dist/{action-runtime-044ldvNL.js → action-runtime-B0kRJ69h.js} +50 -40
  4. package/dist/action-runtime.runtime-IuA14rev.js +2 -0
  5. package/dist/{actions-BI7JkfVm.js → actions-BOxiu2Vy.js} +18 -12
  6. package/dist/api.js +14 -14
  7. package/dist/{approval-handler.runtime-DYv0UIZS.js → approval-handler.runtime-l5_2JLqi.js} +7 -6
  8. package/dist/{channel-1vIJOzPx.js → channel-BguxhhNG.js} +39 -31
  9. package/dist/channel-config-api.js +1 -1
  10. package/dist/channel-plugin-api.js +1 -1
  11. package/dist/{channel-type-DlFSKeIZ.js → channel-type-ChkODKBW.js} +2 -2
  12. package/dist/{channel.setup-ofDi3_Pu.js → channel.setup-BJohRJha.js} +23 -22
  13. package/dist/{config-schema-BVIMyiDN.js → config-schema-B9GwoRlE.js} +5 -3
  14. package/dist/{conversation-binding-route-DFVLUdYF.js → conversation-binding-route-Vee8Dgbf.js} +2 -2
  15. package/dist/{directory-config-DqfQ42OV.js → directory-config-Bmw4ykSy.js} +4 -3
  16. package/dist/directory-contract-api.js +1 -1
  17. package/dist/{directory-live-BPaNHjmP.js → directory-live-BOILcXPo.js} +1 -1
  18. package/dist/{doctor-contract-BAy7-EUQ.js → doctor-contract-BNjw9bxb.js} +5 -4
  19. package/dist/doctor-contract-api.js +1 -1
  20. package/dist/{group-policy-BdwszjYn.js → group-policy-BYH74y50.js} +70 -39
  21. package/dist/{message-tool-api-CJ0kjcg5.js → message-tool-api-BsZbsulu.js} +1 -1
  22. package/dist/message-tool-api.js +1 -1
  23. package/dist/{monitor-Dq5Cmrvp.js → monitor-B72wWXWg.js} +3 -3
  24. package/dist/{outbound-adapter-DYaI8ThX.js → outbound-adapter-CPgYQ-BM.js} +3 -3
  25. package/dist/{pipeline.runtime-Bd22U0ga.js → pipeline.runtime-CcdujV99.js} +238 -393
  26. package/dist/{policy-CtQVbde1.js → policy-CUpBeOt9.js} +7 -5
  27. package/dist/{probe-0usmAb0i.js → probe-gj5yQgN2.js} +113 -112
  28. package/dist/{provider-CN_8hFv2.js → provider-DW9YC_no.js} +69 -44
  29. package/dist/{relay-source-BNGpsdwV.js → relay-source-BLMdBrAw.js} +6 -4
  30. package/dist/{replies-BSiRQOJ1.js → replies-DLjQ8RYU.js} +13 -8
  31. package/dist/{resolve-channels-Czheznc8.js → resolve-channels-Bv6IiGZ4.js} +4 -3
  32. package/dist/{resolve-users-BAOlNimr.js → resolve-users-BXw2pA7y.js} +4 -3
  33. package/dist/runtime-api.js +10 -10
  34. package/dist/{scopes-COSlmhnH.js → scopes-B9HRguDe.js} +1 -1
  35. package/dist/{send-DS9ICrBI.js → send-GUz4_v2E.js} +21 -13
  36. package/dist/{send.runtime-D_Bb6IuH.js → send.runtime-rScD-UJi.js} +1 -1
  37. package/dist/setup-plugin-api.js +1 -1
  38. package/dist/{setup-surface-BGnd2pSe.js → setup-surface-Cc3P-z35.js} +5 -5
  39. package/dist/{shared-D5B-5hBg.js → shared-CQGVZ_8a.js} +5 -5
  40. package/dist/{slash-dispatch.runtime-CilDmI0c.js → slash-dispatch.runtime-C2015r6s.js} +1 -1
  41. package/dist/{target-parsing-BnMD2ZqZ.js → target-parsing-PbxGDbr9.js} +11 -6
  42. package/dist/{targets-Cx5W_n3W.js → targets-7FexXJ62.js} +1 -1
  43. package/node_modules/@slack/bolt/node_modules/negotiator/lib/accept.js +54 -0
  44. package/node_modules/@slack/bolt/node_modules/negotiator/lib/charset.js +10 -34
  45. package/node_modules/@slack/bolt/node_modules/negotiator/lib/encoding.js +9 -30
  46. package/node_modules/@slack/bolt/node_modules/negotiator/lib/language.js +15 -36
  47. package/node_modules/@slack/bolt/node_modules/negotiator/lib/mediaType.js +16 -134
  48. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
  49. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/README.md +71 -0
  50. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
  51. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
  52. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
  53. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/package.json +52 -0
  54. package/node_modules/@slack/bolt/node_modules/negotiator/package.json +16 -10
  55. package/node_modules/typebox/build/format/email.mjs +1 -1
  56. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
  57. package/node_modules/typebox/package.json +1 -1
  58. package/package.json +5 -5
  59. package/dist/action-runtime.runtime-BL3KzX13.js +0 -2
  60. package/node_modules/@slack/bolt/node_modules/negotiator/HISTORY.md +0 -114
@@ -1,23 +1,22 @@
1
- import { n as resolveSlackConversationBindingRoute, r as resolveSlackReplyToMode, t as normalizeSlackRouteBindingConfig } from "./conversation-binding-route-DFVLUdYF.js";
2
- import { n as formatSlackTarget } from "./target-parsing-BnMD2ZqZ.js";
3
- import { I as countSlackTextUtf8Bytes, L as truncateSlackText, M as SLACK_TEXT_LIMIT, Q as normalizeSlackOutboundText, R as truncateSlackTextByUtf8Bytes, T as buildSlackBlocksFallbackText, U as SLACK_SESSION_LINK_ACTION_ID, a as qualifySlackConversationId, b as hasSlackNativeDataBlock, et as escapeSlackMrkdwn, k as SLACK_EDIT_TEXT_MAX_BYTES, m as resolveSlackReplyRenderPlan, o as qualifySlackRoutePeerId, s as resolveSlackEnterpriseMainDmSessionKey } from "./group-policy-BdwszjYn.js";
4
- import { f as resolveSlackAllowListMatch, l as normalizeAllowListLower, o as formatSlackError, u as normalizeSlackAllowOwnerEntry } from "./probe-0usmAb0i.js";
1
+ import { n as resolveSlackConversationBindingRoute, r as resolveSlackReplyToMode, t as normalizeSlackRouteBindingConfig } from "./conversation-binding-route-Vee8Dgbf.js";
2
+ import { n as formatSlackTarget } from "./target-parsing-PbxGDbr9.js";
3
+ import { I as countSlackTextUtf8Bytes, L as truncateSlackText, M as SLACK_TEXT_LIMIT, Q as normalizeSlackOutboundText, R as truncateSlackTextByUtf8Bytes, T as buildSlackBlocksFallbackText, U as SLACK_SESSION_LINK_ACTION_ID, a as qualifySlackConversationId, b as hasSlackNativeDataBlock, et as escapeSlackMrkdwn, k as SLACK_EDIT_TEXT_MAX_BYTES, m as resolveSlackReplyRenderPlan, o as qualifySlackRoutePeerId, s as resolveSlackEnterpriseMainDmSessionKey } from "./group-policy-BYH74y50.js";
4
+ import { C as normalizeAllowListLower, E as resolveSlackAllowListMatch, o as formatSlackError, w as normalizeSlackAllowOwnerEntry } from "./probe-gj5yQgN2.js";
5
5
  import { n as resolveSlackStreamingMode, t as resolveSlackNativeStreaming } from "./streaming-compat-B-e0mqM0.js";
6
- import { D as resolveSlackMessageText$1, E as resolveSlackBlocksText, O as buildSlackEditTextPayload, S as formatSlackFileReferenceList, T as isSlackUnfurlAttachment, a as editSlackRenderedMessage, d as reactSlackMessage, i as editSlackMessage, m as removeSlackReaction, n as deleteSlackMessage, v as logVerbose$1, w as hasSlackTableBlock, x as formatSlackFileReference } from "./actions-BI7JkfVm.js";
7
- import { c as hasSlackThreadFailureNotice, d as recordSlackThreadFailureNotice, f as recordSlackThreadParticipation, l as hasSlackThreadParticipation, m as buildSlackNativeDataDeliveryPlan, o as clearSlackThreadFailureNotice, r as sendMessageSlack, u as hasSlackThreadParticipationWithPersistence } from "./send-DS9ICrBI.js";
8
- import { C as resolveStorePath, E as stripSlackMentionsForCommandDetection, S as resolveChannelResetConfig, _ as resolveSlackChatType, b as readSessionUpdatedAt, c as resolveSlackThreadTargets, d as authorizeSlackBotRoomMessage, f as resolveSlackCommandIngress, g as normalizeSlackChannelType, l as trackSlackDraftMessage, o as authorizeSlackDirectMessage, p as resolveSlackEffectiveAllowFrom, r as resolveSlackRoomContextHints, s as resolveSlackThreadContext, u as resolveConversationLabel$1, v as buildSlackAssistantThreadMetadata, w as updateLastRoute, x as resolveChannelContextVisibilityMode, y as parseSlackAssistantThreadMetadata } from "./provider-CN_8hFv2.js";
9
- import { n as resolveSlackChannelConfig } from "./policy-CtQVbde1.js";
10
- import { a as resolveDeliveredSlackReplyThreadTs, c as emitSlackMessageSentHooks, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, s as sanitizeSlackMonitorReplyPayload, t as createSlackReplyDeliveryPlan } from "./replies-BSiRQOJ1.js";
6
+ import { D as resolveSlackMessageText$1, E as resolveSlackBlocksText, O as buildSlackEditTextPayload, S as formatSlackFileReferenceList, T as isSlackUnfurlAttachment, a as editSlackRenderedMessage, d as reactSlackMessage, i as editSlackMessage, m as removeSlackReaction, n as deleteSlackMessage, v as logVerbose$1, w as hasSlackTableBlock, x as formatSlackFileReference } from "./actions-BOxiu2Vy.js";
7
+ import { c as hasSlackThreadFailureNotice, d as recordSlackThreadFailureNotice, f as recordSlackThreadParticipation, l as hasSlackThreadParticipation, m as buildSlackNativeDataDeliveryPlan, o as clearSlackThreadFailureNotice, r as sendMessageSlack, u as hasSlackThreadParticipationWithPersistence } from "./send-GUz4_v2E.js";
8
+ import { C as resolveStorePath, E as stripSlackMentionsForCommandDetection, S as resolveChannelResetConfig, _ as resolveSlackChatType, b as readSessionUpdatedAt, c as resolveSlackThreadTargets, d as authorizeSlackBotRoomMessage, f as resolveSlackCommandIngress, g as normalizeSlackChannelType, l as trackSlackDraftMessage, o as authorizeSlackDirectMessage, p as resolveSlackEffectiveAllowFrom, r as resolveSlackRoomContextHints, s as resolveSlackThreadContext, u as resolveConversationLabel$1, v as buildSlackAssistantThreadMetadata, w as updateLastRoute, x as resolveChannelContextVisibilityMode, y as parseSlackAssistantThreadMetadata } from "./provider-DW9YC_no.js";
9
+ import { n as resolveSlackChannelConfig } from "./policy-CUpBeOt9.js";
10
+ import { a as resolveDeliveredSlackReplyThreadTs, c as emitSlackMessageSentHooks, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, s as sanitizeSlackMonitorReplyPayload, t as createSlackReplyDeliveryPlan } from "./replies-DLjQ8RYU.js";
11
11
  import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
12
12
  import { resolveAgentRoute, resolveInboundLastRouteSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
13
- import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createChannelProgressWorkCounter, createFinalizableDraftStreamControlsForState, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, formatChannelProgressDraftText, formatPlanChecklistLines, isChannelProgressDraftWorkToolName, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
13
+ import { createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createDraftStreamLoop, createFinalizableDraftStreamControlsForState, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
14
14
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
15
15
  import { danger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
16
16
  import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
17
17
  import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
18
18
  import { buildTtsSupplementMediaPayload, getReplyPayloadTtsSupplement, isReplyPayloadNonTerminalToolErrorWarning, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
19
19
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
20
- import { createHash } from "node:crypto";
21
20
  import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
22
21
  import { ensureConfiguredBindingRouteReady } from "openclaw/plugin-sdk/conversation-runtime";
23
22
  import { resolveChannelImplicitMentions } from "openclaw/plugin-sdk/channel-ingress-runtime";
@@ -35,7 +34,7 @@ import { buildChannelInboundEventContext, buildMentionRegexes, classifyChannelIn
35
34
  import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
36
35
  import { resolveGatewayPublicOrigin } from "openclaw/plugin-sdk/config-contracts";
37
36
  import { buildControlUiSessionPath } from "openclaw/plugin-sdk/session-discussion";
38
- import { DEFAULT_TIMING, createStatusReactionController, logAckFailure, logTypingFailure, resolveAckReaction, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
37
+ import { createStatusReactionController, logAckFailure, logTypingFailure, resolveAckReaction, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
39
38
  import { hasControlCommand } from "openclaw/plugin-sdk/command-detection";
40
39
  import { isAbortRequestText } from "openclaw/plugin-sdk/command-primitives-runtime";
41
40
  import { shouldHandleTextCommands } from "openclaw/plugin-sdk/command-surface";
@@ -479,6 +478,7 @@ function createSlackDraftStream(params) {
479
478
  };
480
479
  const { loop, update, discardPending, seal } = createFinalizableDraftStreamControlsForState({
481
480
  throttleMs,
481
+ coalesceInFlight: true,
482
482
  state: streamState,
483
483
  sendOrEditStreamMessage,
484
484
  emptyValue: "",
@@ -572,64 +572,11 @@ function createSlackDraftStream(params) {
572
572
  };
573
573
  }
574
574
  //#endregion
575
- //#region extensions/slack/src/stream-mode.ts
576
- function resolveSlackStreamingConfig(params) {
577
- return {
578
- mode: resolveSlackStreamingMode(params),
579
- nativeStreaming: resolveSlackNativeStreaming(params)
580
- };
581
- }
582
- function applyAppendOnlyStreamUpdate(params) {
583
- const incoming = params.incoming.trimEnd();
584
- if (!incoming) return {
585
- rendered: params.rendered,
586
- source: params.source,
587
- changed: false
588
- };
589
- if (!params.rendered) return {
590
- rendered: incoming,
591
- source: incoming,
592
- changed: true
593
- };
594
- if (incoming === params.source) return {
595
- rendered: params.rendered,
596
- source: params.source,
597
- changed: false
598
- };
599
- if (incoming.startsWith(params.rendered)) return {
600
- rendered: incoming,
601
- source: incoming,
602
- changed: incoming !== params.rendered
603
- };
604
- if (incoming.startsWith(params.source)) {
605
- const delta = incoming.slice(params.source.length);
606
- return {
607
- rendered: `${params.rendered}${delta}`,
608
- source: incoming,
609
- changed: delta.length > 0
610
- };
611
- }
612
- if (params.source.startsWith(incoming)) return {
613
- rendered: params.rendered,
614
- source: params.source,
615
- changed: false
616
- };
617
- const separator = params.separator ?? (params.rendered.endsWith("\n") ? "" : "\n");
618
- return {
619
- rendered: `${params.rendered}${separator}${incoming}`,
620
- source: incoming,
621
- changed: true
622
- };
623
- }
624
- //#endregion
625
575
  //#region extensions/slack/src/progress-blocks.ts
626
576
  const SLACK_PROGRESS_FIELD_MAX = 1800;
627
577
  const DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS = 120;
628
- const DEFAULT_SLACK_PROGRESS_TASK_DETAIL_MAX_CHARS = 48;
629
578
  const SLACK_PROGRESS_CHUNK_TEXT_MAX = 256;
630
579
  const SLACK_PROGRESS_TASK_TITLE_MAX = 120;
631
- const SLACK_PROGRESS_PLAN_FALLBACK_TITLE = "Thinking";
632
- const SLACK_PROGRESS_LINE_DELTA_RE = /(?:^|\s)\+(\d+)\s+[−-](\d+)(?=\s|$)/u;
633
580
  function buildSessionSources(url) {
634
581
  return [{
635
582
  type: "url_source",
@@ -637,12 +584,6 @@ function buildSessionSources(url) {
637
584
  text: "Open in OpenClaw"
638
585
  }];
639
586
  }
640
- function field(text) {
641
- return {
642
- type: "mrkdwn",
643
- text: truncateSlackText(text, SLACK_PROGRESS_FIELD_MAX)
644
- };
645
- }
646
587
  function resolveMaxLineChars(value, fallback) {
647
588
  return value && value > 0 ? Math.floor(value) : fallback;
648
589
  }
@@ -661,141 +602,58 @@ function compactTitle(value) {
661
602
  function compactChunkText(value) {
662
603
  return truncateSlackText(value.replace(/\s+/g, " ").trim(), SLACK_PROGRESS_CHUNK_TEXT_MAX);
663
604
  }
664
- function lineDetailParts(line) {
665
- return [line.detail, line.status && line.status !== "completed" && !line.detail?.includes(line.status) ? line.status : void 0].map((part) => part?.trim()).filter((part) => Boolean(part));
666
- }
667
- function legacyLineTitle(line) {
668
- return `${line.icon ?? "•"} *${escapeSlackMrkdwn(line.label)}*`;
669
- }
670
605
  function isAuthoredProgressLine(line) {
671
606
  return line.id === "reasoning" || line.id?.startsWith("commentary:") === true;
672
607
  }
673
- function legacyLineDetail(line, maxChars) {
674
- const detail = lineDetailParts(line).join(" · ");
675
- if (detail) return escapeSlackMrkdwn(compactDetail(detail, maxChars));
676
- if (isAuthoredProgressLine(line)) return normalizeSlackOutboundText(compactDetail(line.text.replace(/^(?:🧠|💬)\s+/u, ""), maxChars));
677
- return "—";
678
- }
679
- function lineTaskTitle(line) {
680
- const label = (line.kind === "command-output" ? line.toolName : void 0) || line.label.replace(/\s+/g, " ").trim() || line.toolName || line.kind || "Update";
681
- const fallback = line.text.replace(/\s+/g, " ").trim();
682
- if (fallback && fallback !== label) return compactTitle(lineDetailParts(line).length > 0 || line.status ? label : fallback);
683
- return compactTitle(label);
684
- }
685
- function lineTaskDetails(line, maxLineChars) {
686
- const detail = line.detail?.replace(SLACK_PROGRESS_LINE_DELTA_RE, "").replace(/\s+·\s*$/u, "").trim();
687
- return detail && detail !== line.status?.trim() ? compactDetail(detail, maxLineChars) : void 0;
688
- }
689
- function lineTaskOutput(line) {
690
- const match = line.detail ? SLACK_PROGRESS_LINE_DELTA_RE.exec(line.detail) : null;
691
- if (match) return `+${match[1]} −${match[2]}`;
692
- const status = line.status?.replace(/\s+/g, " ").trim();
693
- return status && lineTaskStatus(line) === "error" ? status : void 0;
694
- }
695
- function lineTaskStatus(line) {
696
- const normalized = line.status?.replace(/\s+/g, " ").trim().toLowerCase();
697
- if (!normalized) return "in_progress";
698
- if (normalized === "complete" || normalized === "completed" || normalized === "done" || normalized === "ok" || normalized === "success" || normalized === "succeeded" || normalized === "successful" || normalized === "exit 0") return "complete";
699
- if (normalized === "error" || normalized === "failed" || normalized === "failure" || normalized.startsWith("exit ")) return normalized === "exit 0" ? "complete" : "error";
700
- return "in_progress";
701
- }
702
- function slugTaskIdPart(value) {
703
- return value?.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "task";
704
- }
705
- function stableTaskIdPart(value, slugValue = value) {
706
- const suffix = createHash("sha256").update(value).digest("hex").slice(0, 8);
707
- return `${slugTaskIdPart(slugValue).slice(0, 48)}_${suffix}`;
708
- }
709
- function resolveLineTaskIdentity(line) {
710
- if (line.id?.trim()) return {
711
- id: stableTaskIdPart(line.id),
712
- contentDerived: false
608
+ function resolveProgressAttention(lines) {
609
+ const approval = lines.findLast((line) => line.kind === "approval" && line.status === "requested");
610
+ if (approval) return {
611
+ id: "openclaw_attention",
612
+ title: compactTitle(`Approval required: ${approval.detail || approval.label}`),
613
+ status: "pending"
713
614
  };
615
+ const failure = lines.findLast((line) => {
616
+ const status = line.status?.trim().toLowerCase();
617
+ return status === "error" || status === "failed" || status === "failure" || status?.startsWith("exit ") && status !== "exit 0";
618
+ });
619
+ if (!failure) return;
714
620
  return {
715
- id: stableTaskIdPart([
716
- line.kind,
717
- line.toolName,
718
- line.label,
719
- line.text
720
- ].join("\0"), line.toolName ?? line.kind ?? line.label),
721
- contentDerived: true
621
+ id: "openclaw_attention",
622
+ title: compactTitle([...new Set([
623
+ failure.label,
624
+ failure.detail,
625
+ failure.status
626
+ ].filter(Boolean))].join(" ")),
627
+ status: "error"
722
628
  };
723
629
  }
724
- function buildPlanTasks(params) {
725
- if (params.plan) return params.plan.slice(-50).map((entry, index) => ({
630
+ function buildSlackProgressStreamChunks(params) {
631
+ const title = compactChunkText(params.title?.trim() || params.label?.trim() || "Working");
632
+ if (!params.title && !params.label && !params.lines.length && !params.plan?.length && !params.sessionUrl) return;
633
+ const tasks = params.plan?.length ? params.plan.slice(-50).map((entry, index) => ({
726
634
  id: `plan_step_${index + 1}`,
727
635
  title: compactTitle(entry.step),
728
636
  status: entry.status === "completed" ? "complete" : entry.status
729
- }));
730
- const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_TASK_DETAIL_MAX_CHARS);
731
- const lines = params.lines.slice(-50);
732
- const identities = lines.map(resolveLineTaskIdentity);
733
- const contentIdOccurrences = /* @__PURE__ */ new Map();
734
- return lines.map((line, index) => {
735
- const identity = identities[index];
736
- let id = identity.id;
737
- if (identity.contentDerived) {
738
- const occurrence = (contentIdOccurrences.get(id) ?? 0) + 1;
739
- contentIdOccurrences.set(id, occurrence);
740
- id = `${id}_${occurrence}`;
741
- }
742
- const details = lineTaskDetails(line, maxLineChars);
743
- const output = lineTaskOutput(line);
744
- const task = {
745
- id,
746
- title: lineTaskTitle(line),
747
- status: lineTaskStatus(line)
748
- };
749
- if (details) task.details = details;
750
- if (output) task.output = output;
751
- return task;
752
- });
753
- }
754
- function resolvePlanTitle(params) {
755
- return compactChunkText(params.title?.trim() || params.label?.trim() || (params.tasks.at(-1)?.details ? `${params.tasks.at(-1)?.title} — ${params.tasks.at(-1)?.details}` : params.tasks.at(-1)?.title) || SLACK_PROGRESS_PLAN_FALLBACK_TITLE);
756
- }
757
- function buildSlackProgressStreamChunks(params) {
758
- const tasks = buildPlanTasks({
759
- lines: params.lines,
760
- plan: params.plan,
761
- maxLineChars: params.maxLineChars
762
- });
763
- if (tasks.length === 0) {
764
- const title = params.title?.trim() || params.label?.trim();
765
- if (!title) return;
766
- if (!params.sessionUrl && !params.diffStat) return [{
767
- type: "plan_update",
768
- title: compactChunkText(title)
769
- }];
770
- return [{
771
- type: "plan_update",
772
- title: compactChunkText(title)
773
- }, {
774
- type: "task_update",
775
- id: "openclaw_summary",
776
- title: "Completed",
777
- status: "complete",
778
- ...formatTaskDiffOutput(params.diffStat) ? { output: formatTaskDiffOutput(params.diffStat) } : {},
779
- ...params.sessionUrl ? { sources: buildSessionSources(params.sessionUrl) } : {}
780
- }];
781
- }
782
- const title = resolvePlanTitle({
783
- label: params.label,
784
- title: params.title,
785
- tasks
786
- });
637
+ })) : [{
638
+ id: "openclaw_summary",
639
+ title: compactTitle(title),
640
+ status: "in_progress"
641
+ }];
642
+ let attention = resolveProgressAttention(params.lines);
643
+ if (params.finalInProgressStatus === "error" && !tasks.some((task) => task.status === "in_progress")) attention = {
644
+ id: "openclaw_attention",
645
+ title: attention?.status === "error" ? attention.title : "Failed",
646
+ status: "error"
647
+ };
648
+ if (attention && !(params.finalInProgressStatus && attention.status === "pending")) tasks.push(attention);
787
649
  const finalTaskIndex = tasks.length - 1;
788
- const diffOutput = formatTaskDiffOutput(params.diffStat);
789
650
  const taskChunks = tasks.map((task, index) => {
790
651
  const chunk = {
791
652
  type: "task_update",
792
653
  id: task.id,
793
654
  title: task.title,
794
- status: task.status === "in_progress" ? params.finalInProgressStatus ?? (params.completeInProgress ? "complete" : task.status) : task.status
655
+ status: task.status === "in_progress" ? params.finalInProgressStatus ?? task.status : task.status
795
656
  };
796
- if (task.details) chunk.details = task.details;
797
- if (task.output) chunk.output = task.output;
798
- if (index === finalTaskIndex && diffOutput) chunk.output = [task.output, diffOutput].filter(Boolean).join(" · ");
799
657
  if (index === finalTaskIndex && params.sessionUrl) chunk.sources = buildSessionSources(params.sessionUrl);
800
658
  return chunk;
801
659
  });
@@ -804,113 +662,50 @@ function buildSlackProgressStreamChunks(params) {
804
662
  title
805
663
  }, ...taskChunks];
806
664
  }
807
- function formatDiffStat(diffStat) {
808
- if (!diffStat || diffStat.files === 0 && diffStat.added === 0 && diffStat.removed === 0) return;
809
- return [
810
- `📝 ${diffStat.files} files`,
811
- ...diffStat.added > 0 ? [`+${diffStat.added}`] : [],
812
- ...diffStat.removed > 0 ? [`−${diffStat.removed}`] : []
813
- ].join(" ");
814
- }
815
- function formatTaskDiffOutput(diffStat) {
816
- return diffStat && (diffStat.added > 0 || diffStat.removed > 0) ? `+${diffStat.added} −${diffStat.removed}` : void 0;
817
- }
818
- function buildActivityText(lines, maxLineChars) {
819
- const rendered = [];
820
- let length = 0;
821
- for (const line of lines.slice(-50).toReversed()) {
822
- const row = `${legacyLineTitle(line)} — ${legacyLineDetail(line, maxLineChars)}`;
823
- const nextLength = length + row.length + (rendered.length > 0 ? 1 : 0);
824
- if (nextLength > SLACK_PROGRESS_FIELD_MAX) break;
825
- rendered.push(row);
826
- length = nextLength;
827
- }
828
- return rendered.toReversed().join("\n");
829
- }
830
665
  function buildSlackProgressCardBlocks(params) {
831
666
  const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS);
832
- const planLines = formatPlanChecklistLines(params.plan ?? [], {
833
- maxLines: 50,
834
- maxLineChars
835
- });
836
- const narration = params.narration?.replace(/\s+/g, " ").trim();
837
- const activityText = buildActivityText(params.lines, maxLineChars);
838
- const diffStat = formatDiffStat(params.diffStat);
839
- const workingFooter = [
840
- ...params.toolCalls && params.toolCalls > 0 ? [`🛠️ ${params.toolCalls} tools`] : [],
841
- ...diffStat ? [diffStat] : [],
842
- ...params.elapsedSeconds && params.elapsedSeconds > 0 ? [`⏱ ${params.elapsedSeconds}s`] : []
843
- ].join(" · ");
844
- const footer = params.state === "working" ? workingFooter : diffStat;
845
- return [
846
- {
847
- type: "section",
848
- text: field(`${params.state === "working" ? "🔄" : params.state === "success" ? "✅" : "❌"} *${escapeSlackMrkdwn(params.title.trim() || "Working")}*`)
849
- },
850
- ...narration ? [{
851
- type: "section",
852
- text: field(`_${escapeSlackMrkdwn(narration)}_`)
853
- }] : [],
854
- ...planLines.length > 0 ? [{
855
- type: "section",
856
- text: field(planLines.map((line) => escapeSlackMrkdwn(line)).join("\n"))
857
- }] : [],
858
- ...activityText ? [{
859
- type: "section",
860
- text: field(activityText)
861
- }] : [],
862
- ...footer ? [{
863
- type: "context",
864
- elements: [field(footer)]
865
- }] : [],
866
- ...params.state !== "working" && params.sessionUrl ? [{
867
- type: "actions",
868
- elements: [{
869
- type: "button",
870
- action_id: SLACK_SESSION_LINK_ACTION_ID,
871
- text: {
872
- type: "plain_text",
873
- text: "Open in OpenClaw"
874
- },
875
- url: params.sessionUrl
876
- }]
877
- }] : []
878
- ].slice(0, 50);
879
- }
880
- const EMPTY_SLACK_NATIVE_STREAM_SNAPSHOT = { tasks: /* @__PURE__ */ new Map() };
881
- const SLACK_TASK_FIELD_SEPARATOR = " · ";
882
- function resolveTaskFieldDelta(previous, incoming) {
883
- if (!incoming) return {
884
- field: previous,
885
- delta: void 0
667
+ const statusLabels = {
668
+ completed: "Completed",
669
+ in_progress: "In progress",
670
+ pending: "Pending"
886
671
  };
887
- if (previous?.rendered.includes(incoming)) return {
888
- field: {
889
- rendered: previous.rendered,
890
- source: incoming
891
- },
892
- delta: void 0
893
- };
894
- const next = applyAppendOnlyStreamUpdate({
895
- incoming,
896
- rendered: previous?.rendered ?? "",
897
- source: previous?.source ?? "",
898
- separator: SLACK_TASK_FIELD_SEPARATOR
672
+ const planLines = (params.plan ?? []).slice(-50).map((entry) => `${statusLabels[entry.status]}: ${compactDetail(entry.step, maxLineChars)}`);
673
+ const narration = params.narration?.replace(/\s+/g, " ").trim();
674
+ const authoredText = params.lines.filter(isAuthoredProgressLine).map((line) => line.text.trim()).filter((text, index, values) => text && text !== narration && values.indexOf(text) === index).map((text) => normalizeSlackOutboundText(compactDetail(text, maxLineChars))).join("\n");
675
+ const attention = resolveProgressAttention(params.lines);
676
+ const blocks = [
677
+ `${params.state === "success" ? "Completed: " : params.state === "error" ? "Failed: " : ""}*${escapeSlackMrkdwn(params.title.trim() || "Working")}*`,
678
+ narration ? `_${escapeSlackMrkdwn(narration)}_` : "",
679
+ planLines.map((line) => escapeSlackMrkdwn(line)).join("\n"),
680
+ authoredText,
681
+ attention && !(params.state !== "working" && attention.status === "pending") ? escapeSlackMrkdwn(attention.title) : ""
682
+ ].filter(Boolean).map((text) => ({
683
+ type: "section",
684
+ text: {
685
+ type: "mrkdwn",
686
+ text: truncateSlackText(text, SLACK_PROGRESS_FIELD_MAX)
687
+ }
688
+ }));
689
+ if (params.state !== "working" && params.sessionUrl) blocks.push({
690
+ type: "actions",
691
+ elements: [{
692
+ type: "button",
693
+ action_id: SLACK_SESSION_LINK_ACTION_ID,
694
+ text: {
695
+ type: "plain_text",
696
+ text: "Open in OpenClaw"
697
+ },
698
+ url: params.sessionUrl
699
+ }]
899
700
  });
900
- const delta = next.changed ? next.rendered.slice(previous?.rendered.length ?? 0) : void 0;
901
- return {
902
- field: {
903
- rendered: next.rendered,
904
- source: next.source
905
- },
906
- delta
907
- };
701
+ return blocks.slice(0, 50);
908
702
  }
703
+ const EMPTY_SLACK_NATIVE_STREAM_SNAPSHOT = { tasks: /* @__PURE__ */ new Map() };
909
704
  /**
910
705
  * Turns a full task snapshot into the delta Slack must receive. Native streams
911
706
  * key rows by persistent id with no removal chunk: unchanged rows are omitted,
912
- * changed rows carry only their unsent field text, and rows that dropped out
913
- * (plan shrinks, tool-line <-> plan source switches) get a final complete
707
+ * changed titles/statuses replace in place, and rows that dropped out
708
+ * (plan shrinks, summary <-> plan source switches) get a final complete
914
709
  * update or they linger in_progress forever.
915
710
  */
916
711
  function reconcileSlackNativeTaskChunks(params) {
@@ -931,26 +726,20 @@ function reconcileSlackNativeTaskChunks(params) {
931
726
  }
932
727
  const previousRow = params.previous.tasks.get(chunk.id);
933
728
  const status = chunk.status;
934
- const details = resolveTaskFieldDelta(previousRow?.details, chunk.details);
935
- const output = resolveTaskFieldDelta(previousRow?.output, chunk.output);
936
729
  const sourcesChanged = Boolean(chunk.sources) && !previousRow?.sourcesSent;
937
730
  const row = {
938
731
  title: chunk.title,
939
732
  status
940
733
  };
941
- if (details.field) row.details = details.field;
942
- if (output.field) row.output = output.field;
943
734
  if (sourcesChanged || previousRow?.sourcesSent) row.sourcesSent = true;
944
735
  nextTasks.set(chunk.id, row);
945
- if (!(!previousRow || previousRow.title !== chunk.title || previousRow.status !== status || Boolean(details.delta) || Boolean(output.delta) || sourcesChanged)) continue;
736
+ if (!(!previousRow || previousRow.title !== chunk.title || previousRow.status !== status || sourcesChanged)) continue;
946
737
  const update = {
947
738
  type: "task_update",
948
739
  id: chunk.id,
949
740
  title: chunk.title,
950
741
  status
951
742
  };
952
- if (details.delta) update.details = details.delta;
953
- if (output.delta) update.output = output.delta;
954
743
  if (sourcesChanged) update.sources = chunk.sources;
955
744
  emitted.push(update);
956
745
  }
@@ -979,40 +768,63 @@ function reconcileSlackNativeTaskChunks(params) {
979
768
  }
980
769
  };
981
770
  }
982
- function buildSlackProgressStreamCompletionChunks(params) {
983
- return buildSlackProgressStreamChunks({
984
- ...params,
985
- completeInProgress: true
986
- });
987
- }
988
771
  //#endregion
989
- //#region extensions/slack/src/monitor/message-handler/dispatch-progress-render.ts
990
- function resolveStructuredProgressLines(lines) {
991
- return lines.map((line) => {
992
- if (typeof line !== "string") return line;
993
- const reasoning = line.startsWith("🧠 ");
994
- const text = line.replace(/^(?:🧠|💬)\s+/u, "").replace(/^_(.*)_$/su, "$1").trim();
772
+ //#region extensions/slack/src/stream-mode.ts
773
+ function resolveSlackStreamingConfig(params) {
774
+ return {
775
+ mode: resolveSlackStreamingMode(params),
776
+ nativeStreaming: resolveSlackNativeStreaming(params)
777
+ };
778
+ }
779
+ function applyAppendOnlyStreamUpdate(params) {
780
+ const incoming = params.incoming.trimEnd();
781
+ if (!incoming) return {
782
+ rendered: params.rendered,
783
+ source: params.source,
784
+ changed: false
785
+ };
786
+ if (!params.rendered) return {
787
+ rendered: incoming,
788
+ source: incoming,
789
+ changed: true
790
+ };
791
+ if (incoming === params.source) return {
792
+ rendered: params.rendered,
793
+ source: params.source,
794
+ changed: false
795
+ };
796
+ if (incoming.startsWith(params.rendered)) return {
797
+ rendered: incoming,
798
+ source: incoming,
799
+ changed: incoming !== params.rendered
800
+ };
801
+ if (incoming.startsWith(params.source)) {
802
+ const delta = incoming.slice(params.source.length);
995
803
  return {
996
- ...reasoning ? { id: "reasoning" } : {},
997
- kind: "item",
998
- text,
999
- label: reasoning ? "Reasoning" : "Update",
1000
- prefix: false
804
+ rendered: `${params.rendered}${delta}`,
805
+ source: incoming,
806
+ changed: delta.length > 0
1001
807
  };
1002
- });
808
+ }
809
+ if (params.source.startsWith(incoming)) return {
810
+ rendered: params.rendered,
811
+ source: params.source,
812
+ changed: false
813
+ };
814
+ const separator = params.separator ?? (params.rendered.endsWith("\n") ? "" : "\n");
815
+ return {
816
+ rendered: `${params.rendered}${separator}${incoming}`,
817
+ source: incoming,
818
+ changed: true
819
+ };
1003
820
  }
1004
- function resolveNativeProgressPlan(snapshot) {
1005
- return snapshot.plan?.length ? snapshot.plan : void 0;
821
+ //#endregion
822
+ //#region extensions/slack/src/monitor/message-handler/dispatch-progress-render.ts
823
+ function resolveStructuredProgressLines(lines) {
824
+ return lines.filter((line) => typeof line !== "string");
1006
825
  }
1007
826
  function resolveNativeProgressLines(snapshot) {
1008
- const lines = resolveStructuredProgressLines(snapshot.lines).filter((line) => line.id !== "reasoning" && line.id?.startsWith("commentary:") !== true);
1009
- if (snapshot.plan?.length || !snapshot.planExplanation) return lines;
1010
- const explanationLine = buildChannelProgressDraftLine({
1011
- event: "plan",
1012
- phase: "update",
1013
- explanation: snapshot.planExplanation
1014
- });
1015
- return explanationLine ? [...lines, explanationLine] : lines;
827
+ return resolveStructuredProgressLines(snapshot.lines).filter((line) => line.id !== "reasoning" && line.id?.startsWith("commentary:") !== true);
1016
828
  }
1017
829
  function resolveNativeProgressNarration(snapshot) {
1018
830
  const paragraphs = resolveStructuredProgressLines(snapshot.lines).filter((line) => line.id === "reasoning" || line.id?.startsWith("commentary:") === true).map((line) => line.text.trim()).filter((text, index, values) => Boolean(text) && values.indexOf(text) === index);
@@ -1021,14 +833,6 @@ function resolveNativeProgressNarration(snapshot) {
1021
833
  function combineProgressHeadlineAndExplanation(headline, explanation) {
1022
834
  return headline && explanation && headline !== explanation ? `${headline} — ${explanation}` : headline ?? explanation;
1023
835
  }
1024
- function buildNativeProgressChunks(params) {
1025
- return buildSlackProgressStreamChunks({
1026
- title: params.title,
1027
- lines: resolveNativeProgressLines(params.snapshot),
1028
- plan: resolveNativeProgressPlan(params.snapshot),
1029
- maxLineChars: params.maxLineChars
1030
- });
1031
- }
1032
836
  //#endregion
1033
837
  //#region extensions/slack/src/monitor/message-handler/preview-finalize.ts
1034
838
  function buildExpectedSlackEditText(params) {
@@ -1066,7 +870,8 @@ function blocksMatch(expected, actual) {
1066
870
  const fallbackBlocks = fallbackPlan.fallbackMessages.flatMap((message) => message.blocks ?? []);
1067
871
  if (JSON.stringify(fallbackBlocks) === JSON.stringify(actual)) return true;
1068
872
  const fallbackText = fallbackPlan.fallbackMessages.map((message) => message.text).filter(Boolean).join("\n\n");
1069
- return normalizeSlackOutboundText(buildSlackBlocksFallbackText(actual)) === normalizeSlackOutboundText(fallbackText);
873
+ const actualText = buildSlackBlocksFallbackText(actual);
874
+ return normalizeSlackOutboundText(actualText) === normalizeSlackOutboundText(fallbackText);
1070
875
  } catch {
1071
876
  return false;
1072
877
  }
@@ -1157,6 +962,7 @@ function createSlackDraftProgressCardRuntime(params) {
1157
962
  return url.toString();
1158
963
  };
1159
964
  const resolveText = (snapshot) => latestFallbackText || formatChannelProgressDraftText({
965
+ presentation: "summary",
1160
966
  entry: account.config,
1161
967
  lines: [...snapshot.lines],
1162
968
  seed: params.progressSeed,
@@ -1173,11 +979,7 @@ function createSlackDraftProgressCardRuntime(params) {
1173
979
  plan: snapshot.plan,
1174
980
  lines: resolveStructuredProgressLines(snapshot.lines),
1175
981
  maxLineChars: params.maxLineChars,
1176
- diffStat: snapshot.diffStat,
1177
- ...state === "working" ? {
1178
- toolCalls: params.progressWorkCounter.toolCalls,
1179
- elapsedSeconds: params.progressWorkCounter.elapsedSeconds
1180
- } : { sessionUrl: resolveSessionUrl() }
982
+ ...state !== "working" ? { sessionUrl: resolveSessionUrl() } : {}
1181
983
  });
1182
984
  };
1183
985
  const finalize = async (status, snapshot = params.getSnapshot(), fallbackText = resolveText(snapshot)) => {
@@ -1363,7 +1165,6 @@ function createSlackProgressRuntime(runtimeParams) {
1363
1165
  nativeStreamOrder = run.catch(() => void 0);
1364
1166
  return run;
1365
1167
  };
1366
- const progressWorkCounter = createChannelProgressWorkCounter();
1367
1168
  const progressSeed = `${account.accountId}:${message.channel}`;
1368
1169
  const slackProgressStyle = resolveSlackProgressStyle(account.config);
1369
1170
  const useDraftProgressCard = Boolean(draftStream) && isProgressMode && slackProgressStyle === "card";
@@ -1379,7 +1180,6 @@ function createSlackProgressRuntime(runtimeParams) {
1379
1180
  },
1380
1181
  draftStream,
1381
1182
  enabled: useDraftProgressCard,
1382
- progressWorkCounter,
1383
1183
  progressSeed,
1384
1184
  explicitTitle: explicitProgressTitle,
1385
1185
  maxLineChars: progressDraftMaxLineChars,
@@ -1399,12 +1199,17 @@ function createSlackProgressRuntime(runtimeParams) {
1399
1199
  if (isError) nativeProgressTerminalStatus = "error";
1400
1200
  if (!session || nativeProgressCompletionSent) return;
1401
1201
  const chunks = buildNativeProgressCompletionChunks(isError ? "error" : "complete");
1402
- if (!chunks?.length) return;
1202
+ const narrationUpdate = resolveNarrationUpdate(resolveNativeProgressNarration(progressDraft.getSnapshot()));
1203
+ if (!chunks?.length && !narrationUpdate.delta) return;
1403
1204
  try {
1404
1205
  await appendSlackStream({
1405
1206
  session,
1406
1207
  chunks
1407
1208
  });
1209
+ if (narrationUpdate.next.changed) {
1210
+ nativeNarrationRenderedText = narrationUpdate.next.rendered;
1211
+ nativeNarrationSourceText = narrationUpdate.next.source;
1212
+ }
1408
1213
  nativeProgressCompletionSent = true;
1409
1214
  delivery.observedReplyDelivery ||= session.delivered;
1410
1215
  } catch (err) {
@@ -1413,11 +1218,6 @@ function createSlackProgressRuntime(runtimeParams) {
1413
1218
  }
1414
1219
  };
1415
1220
  const resolveNativeProgressTitle = (snapshot) => combineProgressHeadlineAndExplanation(explicitProgressTitle ?? snapshot.statusHeadline, snapshot.planExplanation);
1416
- const buildNativeProgressChunks$1 = (snapshot) => buildNativeProgressChunks({
1417
- snapshot,
1418
- title: resolveNativeProgressTitle(snapshot),
1419
- maxLineChars: progressDraftMaxLineChars
1420
- });
1421
1221
  const normalizeProgressText = (text) => text?.replace(/\s+/gu, " ").trim() ?? "";
1422
1222
  const isRenderedAsProgressTitle = (text) => {
1423
1223
  const candidate = normalizeProgressText(text);
@@ -1436,17 +1236,18 @@ function createSlackProgressRuntime(runtimeParams) {
1436
1236
  delta: next.changed ? next.rendered.slice(nativeNarrationRenderedText.length) : ""
1437
1237
  };
1438
1238
  };
1439
- const updateNativeProgressStream = () => withNativeStreamOrder(updateNativeProgressStreamNow);
1440
1239
  const updateNativeProgressStreamNow = async () => {
1441
1240
  const snapshot = progressDraft.getSnapshot();
1442
- const progressLines = resolveNativeProgressLines(snapshot);
1443
1241
  const narrationUpdate = resolveNarrationUpdate(resolveNativeProgressNarration(snapshot));
1444
- const hasRetirableNativeTasks = [...nativeStreamSnapshot.tasks.values()].some((task) => task.status !== "complete" && task.status !== "error");
1445
- if (!useNativeProgressStreaming || delivery.streamFailed || progressLines.length === 0 && !snapshot.plan?.length && !snapshot.statusHeadline && !explicitProgressTitle && !hasRetirableNativeTasks && !narrationUpdate.delta) return false;
1242
+ if (!useNativeProgressStreaming || delivery.streamFailed || nativeUpdatesStopped) return false;
1446
1243
  if (!await nativeTransport.waitForStart()) return false;
1447
1244
  const reconciled = reconcileSlackNativeTaskChunks({
1448
1245
  previous: nativeStreamSnapshot,
1449
- chunks: buildNativeProgressChunks$1(snapshot)
1246
+ chunks: buildSlackProgressStreamChunks({
1247
+ title: resolveNativeProgressTitle(snapshot) ?? "Working",
1248
+ lines: resolveNativeProgressLines(snapshot),
1249
+ plan: snapshot.plan
1250
+ })
1450
1251
  });
1451
1252
  const chunks = reconciled.chunks;
1452
1253
  if (!chunks?.length && !narrationUpdate.delta) return false;
@@ -1470,6 +1271,21 @@ function createSlackProgressRuntime(runtimeParams) {
1470
1271
  return false;
1471
1272
  }
1472
1273
  };
1274
+ let nativeUpdatesStopped = false;
1275
+ const nativeUpdates = createDraftStreamLoop({
1276
+ throttleMs: 1e3,
1277
+ coalesceInFlight: true,
1278
+ emptyValue: false,
1279
+ isEmpty: (pending) => !pending,
1280
+ isStopped: () => nativeUpdatesStopped,
1281
+ sendOrEditStreamMessage: () => withNativeStreamOrder(updateNativeProgressStreamNow),
1282
+ onBackgroundFlushError: (err) => runtime.error?.(danger(`slack-stream: progress update failed: ${formatSlackError(err)}`))
1283
+ });
1284
+ const cancelNativeUpdates = async () => {
1285
+ nativeUpdatesStopped = true;
1286
+ nativeUpdates.stop();
1287
+ await nativeUpdates.waitForInFlight();
1288
+ };
1473
1289
  const appendNativeNarration = (payload, kind) => withNativeStreamOrder(() => appendNativeNarrationNow(payload, kind));
1474
1290
  const appendNativeNarrationNow = async (payload, kind) => {
1475
1291
  if (isRenderedAsProgressTitle(payload.text)) return false;
@@ -1489,24 +1305,28 @@ function createSlackProgressRuntime(runtimeParams) {
1489
1305
  return true;
1490
1306
  };
1491
1307
  const resetProgressTurnState = () => {
1492
- progressWorkCounter.reset();
1493
1308
  nativeNarrationRenderedText = "";
1494
1309
  nativeNarrationSourceText = "";
1495
1310
  };
1496
1311
  const progressDraft = createChannelProgressDraftCompositor({
1312
+ presentation: isProgressMode ? "summary" : void 0,
1497
1313
  entry: account.config,
1498
1314
  mode: slackStreaming.mode,
1499
1315
  active: progressDraftActive,
1500
1316
  seed: progressSeed,
1501
1317
  formatLine: formatSlackProgressDraftLine,
1502
1318
  reasoningLinePrefix: "🧠 ",
1503
- commentaryLinePrefix: "",
1504
1319
  reasoningGate: previewToolProgressEnabled,
1505
- commentaryItalics: true,
1506
- buildProgressEventLine: (input, options) => input.event === "tool" || input.event === "item" || input.event === "command-output" ? buildChannelProgressDraftLineForEntry(account.config, input, options) : buildChannelProgressDraftLine(input, options),
1507
1320
  updateOnLineChange: useNativeProgressStreaming || useDraftProgressCard,
1508
1321
  update: async (previewText, options) => {
1509
- if (useNativeProgressStreaming) return await updateNativeProgressStream();
1322
+ if (useNativeProgressStreaming) {
1323
+ const priorSnapshot = nativeStreamSnapshot;
1324
+ const priorNarration = nativeNarrationRenderedText;
1325
+ nativeUpdates.update(true);
1326
+ if (options?.flush) await nativeUpdates.flush();
1327
+ else await nativeUpdates.waitForInFlight();
1328
+ return priorSnapshot !== nativeStreamSnapshot || priorNarration !== nativeNarrationRenderedText;
1329
+ }
1510
1330
  if (!draftStream) return false;
1511
1331
  const snapshot = progressDraft.getSnapshot();
1512
1332
  progressCard.setFallbackText(previewText);
@@ -1519,7 +1339,11 @@ function createSlackProgressRuntime(runtimeParams) {
1519
1339
  }
1520
1340
  });
1521
1341
  const commentaryProgressEnabled = progressDraft.commentaryProgressEnabled;
1522
- const deliverNativeFinal = (payload, kind) => withNativeStreamOrder(() => deliverNativeFinalNow(payload, kind));
1342
+ const deliverNativeFinal = async (payload, kind) => {
1343
+ progressDraft.markFinalReplyStarted();
1344
+ await cancelNativeUpdates();
1345
+ await withNativeStreamOrder(() => deliverNativeFinalNow(payload, kind));
1346
+ };
1523
1347
  const deliverNativeFinalNow = async (payload, kind) => {
1524
1348
  progressDraft.markFinalReplyStarted();
1525
1349
  const streamReady = await nativeTransport.waitForStart();
@@ -1544,20 +1368,23 @@ function createSlackProgressRuntime(runtimeParams) {
1544
1368
  const snapshot = progressDraft.getSnapshot();
1545
1369
  const lines = resolveNativeProgressLines(snapshot);
1546
1370
  const sessionUrl = progressCard.resolveSessionUrl();
1371
+ const narrationUpdate = resolveNarrationUpdate(resolveNativeProgressNarration(snapshot));
1547
1372
  const hasRetirableNativeTasks = [...nativeStreamSnapshot.tasks.values()].some((task) => task.status !== "complete" && task.status !== "error");
1548
- if (lines.length === 0 && !snapshot.plan?.length && !hasRetirableNativeTasks && !snapshot.diffStat && !sessionUrl) return;
1549
- return reconcileSlackNativeTaskChunks({
1373
+ if (lines.length === 0 && !snapshot.plan?.length && !hasRetirableNativeTasks && !narrationUpdate.delta && !sessionUrl) return;
1374
+ const completion = reconcileSlackNativeTaskChunks({
1550
1375
  previous: nativeStreamSnapshot,
1551
- chunks: buildSlackProgressStreamCompletionChunks({
1376
+ chunks: buildSlackProgressStreamChunks({
1552
1377
  title: resolveNativeProgressTitle(snapshot) ?? (lines.length === 0 && !snapshot.plan?.length ? "Working" : void 0),
1553
1378
  lines,
1554
- plan: resolveNativeProgressPlan(snapshot),
1555
- maxLineChars: progressDraftMaxLineChars,
1379
+ plan: snapshot.plan,
1556
1380
  finalInProgressStatus,
1557
- diffStat: snapshot.diffStat,
1558
1381
  sessionUrl
1559
1382
  })
1560
1383
  }).chunks;
1384
+ return narrationUpdate.delta ? [{
1385
+ type: "markdown_text",
1386
+ text: narrationUpdate.delta
1387
+ }, ...completion ?? []] : completion;
1561
1388
  };
1562
1389
  const finishNativeProgressTurn = async (completionChunks) => {
1563
1390
  if (delivery.nativeProgressStreamStartPromise) await delivery.nativeProgressStreamStartPromise.catch(() => null);
@@ -1660,6 +1487,10 @@ function createSlackProgressRuntime(runtimeParams) {
1660
1487
  progressDraft.reset();
1661
1488
  };
1662
1489
  const beginNewProgressTurn = async (options) => {
1490
+ if (useNativeProgressStreaming) {
1491
+ if (!nativeUpdatesStopped && options?.force !== true) return false;
1492
+ await cancelNativeUpdates();
1493
+ }
1663
1494
  const priorSnapshot = progressDraft.getSnapshot();
1664
1495
  const priorFallbackText = progressCard.resolveText(priorSnapshot);
1665
1496
  const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent ? buildNativeProgressCompletionChunks(nativeProgressTerminalStatus) : void 0;
@@ -1674,6 +1505,8 @@ function createSlackProgressRuntime(runtimeParams) {
1674
1505
  nativeStreamSnapshot = EMPTY_SLACK_NATIVE_STREAM_SNAPSHOT;
1675
1506
  nativeProgressCompletionSent = false;
1676
1507
  nativeProgressTerminalStatus = "complete";
1508
+ nativeUpdatesStopped = false;
1509
+ nativeUpdates.resetThrottleWindow();
1677
1510
  progressCard.reset();
1678
1511
  resetPreviewDeliveryState();
1679
1512
  delivery.resetDeliveryTracker();
@@ -1697,7 +1530,14 @@ function createSlackProgressRuntime(runtimeParams) {
1697
1530
  resetDraftDeliveryState();
1698
1531
  resetDraftProgressState();
1699
1532
  };
1700
- const onQueuedFollowupSettled = !useDraftProgressCard ? void 0 : async () => {
1533
+ const onQueuedFollowupSettled = !useDraftProgressCard && !useNativeProgressStreaming ? void 0 : async () => {
1534
+ if (useNativeProgressStreaming) {
1535
+ progressDraft.markFinalReplyStarted();
1536
+ await cancelNativeUpdates();
1537
+ await finishNativeProgressTurn(nativeProgressCompletionSent ? void 0 : buildNativeProgressCompletionChunks(nativeProgressTerminalStatus));
1538
+ progressDraft.markFinalReplyDelivered();
1539
+ return;
1540
+ }
1701
1541
  if (!progressCard.hasTerminalized) await draftStream?.clear();
1702
1542
  await dropDetachedProgressCards();
1703
1543
  };
@@ -1711,7 +1551,10 @@ function createSlackProgressRuntime(runtimeParams) {
1711
1551
  suppressDefaultToolProgressMessages,
1712
1552
  progressDraft,
1713
1553
  commentaryProgressEnabled,
1714
- progressWorkCounter,
1554
+ async cancel() {
1555
+ progressDraft.cancel();
1556
+ await cancelNativeUpdates();
1557
+ },
1715
1558
  get nativeProgressCompletionSent() {
1716
1559
  return nativeProgressCompletionSent;
1717
1560
  },
@@ -1816,24 +1659,17 @@ async function createSlackDispatchSetup(prepared) {
1816
1659
  await reactSlackMessage(message.channel, reactionMessageTs ?? "", emoji, {
1817
1660
  token: ctx.botToken,
1818
1661
  client: slackClient
1819
- }).catch((err) => {
1820
- if (formatErrorMessage(err).includes("already_reacted")) return;
1821
- throw err;
1822
1662
  });
1823
1663
  },
1824
1664
  removeReaction: async (emoji) => {
1825
1665
  await removeSlackReaction(message.channel, reactionMessageTs ?? "", emoji, {
1826
1666
  token: ctx.botToken,
1827
1667
  client: slackClient
1828
- }).catch((err) => {
1829
- if (formatErrorMessage(err).includes("no_reaction")) return;
1830
- throw err;
1831
1668
  });
1832
1669
  }
1833
1670
  },
1834
1671
  initialEmoji: prepared.ackReactionValue || "eyes",
1835
- emojis: void 0,
1836
- timing: DEFAULT_TIMING,
1672
+ presentation: "acknowledgement",
1837
1673
  onError: (err) => {
1838
1674
  logAckFailure({
1839
1675
  log: logVerbose,
@@ -2634,7 +2470,6 @@ async function dispatchPreparedSlackMessage(prepared) {
2634
2470
  } : void 0,
2635
2471
  onToolStart: async (payload) => {
2636
2472
  if (statusReactionsEnabled) await statusReactions.setTool(payload.name);
2637
- if (payload.phase === "start") progress.progressWorkCounter.noteToolCall(payload.name);
2638
2473
  return await progress.progressDraft.pushToolEvent(payload);
2639
2474
  },
2640
2475
  onItemEvent: async (payload) => {
@@ -2676,7 +2511,7 @@ async function dispatchPreparedSlackMessage(prepared) {
2676
2511
  } catch (err) {
2677
2512
  dispatchError = err;
2678
2513
  } finally {
2679
- progress.progressDraft.cancel();
2514
+ await progress.cancel();
2680
2515
  if (!progress.useDraftProgressCard) await draftStream?.discardPending();
2681
2516
  }
2682
2517
  let streamFallbackDelivered = false;
@@ -2709,13 +2544,15 @@ async function dispatchPreparedSlackMessage(prepared) {
2709
2544
  if (pendingFailureNotice && anyReplyDelivered) recordSlackThreadFailureNotice(pendingFailureNotice);
2710
2545
  if (dispatchError || agentRunFailed) await progress.finalizeDraftProgressCard("error");
2711
2546
  await progress.dropDetachedProgressCards();
2712
- if (statusReactionsEnabled) if (dispatchError || agentRunFailed) {
2713
- await statusReactions.setError();
2714
- statusReactions.restoreInitial();
2715
- } else if (anyReplyDelivered) {
2716
- await statusReactions.setDone();
2717
- statusReactions.restoreInitial();
2718
- } else await statusReactions.restoreInitial();
2547
+ if (statusReactionsEnabled) {
2548
+ if (dispatchError || agentRunFailed) {
2549
+ await statusReactions.setError();
2550
+ statusReactions.restoreInitial();
2551
+ } else if (anyReplyDelivered) {
2552
+ await statusReactions.setDone();
2553
+ statusReactions.restoreInitial();
2554
+ } else await statusReactions.restoreInitial();
2555
+ }
2719
2556
  const participationThreadTs = delivery.usedReplyThreadTs ?? statusThreadTs;
2720
2557
  if (anyReplyDelivered && participationThreadTs) recordSlackThreadParticipation(account.accountId, message.channel, participationThreadTs, {
2721
2558
  agentId: route.agentId,
@@ -2774,7 +2611,7 @@ function normalizeSlackAppContextEntities(value) {
2774
2611
  //#endregion
2775
2612
  //#region extensions/slack/src/monitor/thread.ts
2776
2613
  const THREAD_STARTER_CACHE = /* @__PURE__ */ new Map();
2777
- const THREAD_STARTER_CACHE_TTL_MS = 360 * 6e4;
2614
+ const THREAD_STARTER_CACHE_TTL_MS = 216e5;
2778
2615
  const THREAD_STARTER_CACHE_MAX = 2e3;
2779
2616
  function evictThreadStarterCache() {
2780
2617
  const now = asDateTimestampMs(Date.now());
@@ -2970,7 +2807,7 @@ const SLACK_MENTION_RESOLUTION_CONCURRENCY = 4;
2970
2807
  const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
2971
2808
  const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
2972
2809
  const MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS = 2e3;
2973
- const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-BI7JkfVm.js").then((n) => n._));
2810
+ const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-BOxiu2Vy.js").then((n) => n._));
2974
2811
  function formatSlackUnavailableMedia(params) {
2975
2812
  let fileReferences = params.files?.map((file) => `${formatSlackFileReference(file)} unavailable (${file.reason})`).join(", ");
2976
2813
  if (fileReferences && fileReferences.length > MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS) fileReferences = `${truncateUtf16Safe(fileReferences, MAX_SLACK_UNAVAILABLE_FILE_TEXT_CHARS)}; … (file references truncated)`;
@@ -3060,7 +2897,8 @@ async function resolveSlackMessageContent(params) {
3060
2897
  if (skippedLookups > 0) logVerbose(`slack: skipping ${skippedLookups} mention lookup(s) beyond per-message cap (${SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE})`);
3061
2898
  const { results } = await runTasksWithConcurrency({
3062
2899
  tasks: lookupIds.map((userId) => async () => {
3063
- const renderedName = normalizeOptionalString((await resolveUserName(userId))?.name);
2900
+ const user = await resolveUserName(userId);
2901
+ const renderedName = normalizeOptionalString(user?.name);
3064
2902
  return {
3065
2903
  userId,
3066
2904
  rendered: renderedName ? `<@${userId}> (${renderedName})` : null
@@ -3163,7 +3001,8 @@ async function resolveSlackDmHistoryContext(params) {
3163
3001
  //#endregion
3164
3002
  //#region extensions/slack/src/monitor/message-handler/prepare-routing.ts
3165
3003
  function resolveSlackBaseConversationId(params) {
3166
- return qualifySlackConversationId(params.isDirectMessage ? `user:${params.message.user ?? "unknown"}` : params.message.channel, params.eventScope);
3004
+ const raw = params.isDirectMessage ? `user:${params.message.user ?? "unknown"}` : params.message.channel;
3005
+ return qualifySlackConversationId(raw, params.eventScope);
3167
3006
  }
3168
3007
  function resolveSlackInitialAgentRoute(params) {
3169
3008
  const route = resolveAgentRoute({
@@ -3314,7 +3153,7 @@ function formatSlackThreadLabelSnippet(text) {
3314
3153
  }
3315
3154
  //#endregion
3316
3155
  //#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
3317
- const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-BI7JkfVm.js").then((n) => n._));
3156
+ const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-BOxiu2Vy.js").then((n) => n._));
3318
3157
  const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
3319
3158
  function resolveSlackThreadSessionFreshness(params) {
3320
3159
  return params.ctx.channelRuntime?.session?.resolveEntryResetFreshness?.({
@@ -3440,7 +3279,10 @@ async function resolveSlackThreadContextData(params) {
3440
3279
  unavailableMediaCount: attachmentContent.unavailableMediaCount,
3441
3280
  prependUnavailable: true
3442
3281
  });
3443
- if (threadStarterMedia) logVerbose(`slack: hydrated thread starter file ${threadStarterMedia.map((item) => item.placeholder).join(", ")} from root message`);
3282
+ if (threadStarterMedia) {
3283
+ const starterPlaceholders = threadStarterMedia.map((item) => item.placeholder).join(", ");
3284
+ logVerbose(`slack: hydrated thread starter file ${starterPlaceholders} from root message`);
3285
+ }
3444
3286
  }
3445
3287
  } else threadLabel = `Slack thread ${params.roomLabel}`;
3446
3288
  const includeBotStarterAsRootContext = shouldIncludeBotThreadStarterContext({
@@ -3559,7 +3401,7 @@ async function resolveSlackThreadContextData(params) {
3559
3401
  //#endregion
3560
3402
  //#region extensions/slack/src/monitor/message-handler/subteam-mentions.ts
3561
3403
  const SUBTEAM_MENTION_RE = /<!subteam\^([A-Z0-9]+)(?:\|[^>]*)?>/gi;
3562
- const SUBTEAM_MEMBER_CACHE_TTL_MS = 300 * 1e3;
3404
+ const SUBTEAM_MEMBER_CACHE_TTL_MS = 3e5;
3563
3405
  const subteamMemberCache = /* @__PURE__ */ new WeakMap();
3564
3406
  function normalizeSlackId(value) {
3565
3407
  return typeof value === "string" && value.trim() ? value.trim().toUpperCase() : void 0;
@@ -3630,7 +3472,7 @@ const SLACK_USER_MENTION_RE = /<@([^>|]+)(?:\|[^>]+)?>/g;
3630
3472
  const SLACK_SUBTEAM_MENTION_RE = /<!subteam\^([^>|]+)(?:\|[^>]+)?>/g;
3631
3473
  const SLACK_SUBTEAM_MENTION_MARKER = "<!subteam^";
3632
3474
  const SLACK_HISTORY_MEDIA_MAX_ATTACHMENTS = 4;
3633
- const SLACK_HISTORY_MEDIA_MAX_BYTES = 10 * 1024 * 1024;
3475
+ const SLACK_HISTORY_MEDIA_MAX_BYTES = 10485760;
3634
3476
  const SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS = 1e3;
3635
3477
  const SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS = 3e3;
3636
3478
  const SLACK_CHANNEL_ACCESS_DOCS_URL = "https://docs.openclaw.ai/channels/slack#access-control-and-routing";
@@ -3764,7 +3606,7 @@ function buildSlackHistoryMediaCandidateMessage(message) {
3764
3606
  async function resolveSlackHistoryMediaForPendingRecord(params) {
3765
3607
  const mediaMessage = buildSlackHistoryMediaCandidateMessage(params.message);
3766
3608
  if (!mediaMessage) return [];
3767
- return await toInboundMediaFactsWithMetadata((await resolveSlackMessageContent({
3609
+ const content = await resolveSlackMessageContent({
3768
3610
  message: mediaMessage,
3769
3611
  isThreadReply: params.isThreadReply,
3770
3612
  threadStarter: params.threadStarter,
@@ -3774,7 +3616,8 @@ async function resolveSlackHistoryMediaForPendingRecord(params) {
3774
3616
  mediaMaxBytes: Math.min(params.ctx.mediaMaxBytes, SLACK_HISTORY_MEDIA_MAX_BYTES),
3775
3617
  mediaReadIdleTimeoutMs: SLACK_HISTORY_MEDIA_IDLE_TIMEOUT_MS,
3776
3618
  mediaTotalTimeoutMs: SLACK_HISTORY_MEDIA_TOTAL_TIMEOUT_MS
3777
- }))?.effectiveDirectMedia, {
3619
+ });
3620
+ return await toInboundMediaFactsWithMetadata(content?.effectiveDirectMedia, {
3778
3621
  kind: "image",
3779
3622
  messageId: params.message.ts
3780
3623
  });
@@ -3897,7 +3740,8 @@ async function authorizeSlackInboundMessage(params) {
3897
3740
  if (conversation.isRoom && ctx.groupPolicy === "allowlist" && params.explicitBotMention && !isBotMessage && message.user) {
3898
3741
  let subject = "This OpenClaw bot";
3899
3742
  if (ctx.botUserId) try {
3900
- const botName = normalizeOptionalString((await ctx.resolveUserName(ctx.botUserId, params.eventScope))?.name);
3743
+ const botIdentity = await ctx.resolveUserName(ctx.botUserId, params.eventScope);
3744
+ const botName = normalizeOptionalString(botIdentity?.name);
3901
3745
  if (botName) subject = escapeSlackMrkdwn(botName);
3902
3746
  } catch (error) {
3903
3747
  logVerbose(`slack allowlist denial bot-name lookup failed: ${formatSlackError(error)}`);
@@ -3990,7 +3834,8 @@ async function prepareSlackMessage(params) {
3990
3834
  const resolveSenderName = async () => {
3991
3835
  if (resolvedSenderName) return resolvedSenderName;
3992
3836
  if (message.user) {
3993
- const normalized = normalizeOptionalString((await ctx.resolveUserName(message.user, opts.eventScope))?.name);
3837
+ const sender = await ctx.resolveUserName(message.user, opts.eventScope);
3838
+ const normalized = normalizeOptionalString(sender?.name);
3994
3839
  if (normalized) {
3995
3840
  resolvedSenderName = normalized;
3996
3841
  return resolvedSenderName;