@openclaw/slack 2026.9.1-beta.1 → 2026.9.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 (99) 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-BoSeX09X.js → action-runtime-DftQWpI4.js} +84 -66
  4. package/dist/action-runtime.runtime-rHI3UAh4.js +2 -0
  5. package/dist/{actions-XdQyjG_p.js → actions-CFkvEv1d.js} +198 -54
  6. package/dist/api.js +14 -14
  7. package/dist/{approval-handler.runtime-DxXfGX_Z.js → approval-handler.runtime-BeILKdw4.js} +47 -25
  8. package/dist/{channel-C_vnTgxo.js → channel-CmOuxbOD.js} +76 -133
  9. package/dist/channel-config-api.js +1 -1
  10. package/dist/channel-plugin-api.js +1 -1
  11. package/dist/{channel-type-j-2lalcC.js → channel-type-BWt2CRaI.js} +2 -2
  12. package/dist/{channel.setup-aal8tokM.js → channel.setup-D5z1ReaT.js} +29 -23
  13. package/dist/{config-schema-CN3XZRX5.js → config-schema-JDx_f9Zb.js} +26 -17
  14. package/dist/{cursor-pages-eX7p8Wwt.js → cursor-pages-ByFwo-Py.js} +7 -1
  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-lC6X_Ru-.js → directory-live-BuBPHmju.js} +3 -8
  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/{message-tool-api-CWosHSi5.js → message-tool-api-uNEfQYGY.js} +19 -2
  21. package/dist/message-tool-api.js +1 -1
  22. package/dist/{monitor-DrNCGneD.js → monitor-DI07pfJl.js} +3 -3
  23. package/dist/{outbound-adapter-DkT2vUE9.js → outbound-adapter-B5QyTRpJ.js} +96 -91
  24. package/dist/{pipeline.runtime-CRRng_Nw.js → pipeline.runtime-BdIwZgm4.js} +596 -920
  25. package/dist/{policy-BoP6t88v.js → policy-Bh5TOqII.js} +7 -4
  26. package/dist/{probe-4_aHtVT3.js → probe-Biy--yhL.js} +251 -12
  27. package/dist/{provider-D4nDgxCh.js → provider-DoaUvAM4.js} +5621 -4944
  28. package/dist/{relay-source-CEmze11m.js → relay-source-D4uecp68.js} +6 -4
  29. package/dist/{replies-D9XMM_S0.js → replies-DENuTcPO.js} +21 -16
  30. package/dist/reply-blocks-PRkhyAN7.js +790 -0
  31. package/dist/{resolve-channels-DSamDgVs.js → resolve-channels-DQGV2VrE.js} +6 -10
  32. package/dist/{resolve-users-CIzwbaJR.js → resolve-users-DRGt_Q4I.js} +5 -4
  33. package/dist/runtime-api.js +10 -10
  34. package/dist/{scopes-BBrUzpBH.js → scopes-CZzeGdWV.js} +1 -1
  35. package/dist/{send-DtjEnlLh.js → send-DElUcgIs.js} +32 -24
  36. package/dist/send-results-eeOu_HYm.js +23 -0
  37. package/dist/{send.runtime-DQeNKa2J.js → send.runtime-C8D6cKGm.js} +1 -1
  38. package/dist/{group-policy-CU1tEGpS.js → session-status-BynwcZSO.js} +284 -1013
  39. package/dist/setup-plugin-api.js +1 -1
  40. package/dist/{setup-surface-CsBCM4rP.js → setup-surface-B1_gpGBl.js} +5 -5
  41. package/dist/{shared-D9GeeR9K.js → shared-CNQsWJT7.js} +7 -5
  42. package/dist/{slash-dispatch.runtime-Chr3YHnT.js → slash-dispatch.runtime-DhsXavSY.js} +2 -2
  43. package/dist/{target-parsing-BnMD2ZqZ.js → target-parsing-PbxGDbr9.js} +11 -6
  44. package/dist/{targets-Cx5W_n3W.js → targets-7FexXJ62.js} +1 -1
  45. package/dist/{thread-ts-DUGhaYKq.js → thread-ts-CukseRVL.js} +5 -1
  46. package/node_modules/@slack/bolt/node_modules/negotiator/lib/accept.js +54 -0
  47. package/node_modules/@slack/bolt/node_modules/negotiator/lib/charset.js +10 -34
  48. package/node_modules/@slack/bolt/node_modules/negotiator/lib/encoding.js +9 -30
  49. package/node_modules/@slack/bolt/node_modules/negotiator/lib/language.js +15 -36
  50. package/node_modules/@slack/bolt/node_modules/negotiator/lib/mediaType.js +16 -134
  51. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
  52. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/README.md +71 -0
  53. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
  54. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
  55. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
  56. package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/package.json +52 -0
  57. package/node_modules/@slack/bolt/node_modules/negotiator/package.json +16 -10
  58. package/node_modules/typebox/build/format/email.mjs +1 -1
  59. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
  60. package/node_modules/typebox/build/format/json_pointer.d.mts +2 -2
  61. package/node_modules/typebox/build/format/json_pointer.mjs +2 -2
  62. package/node_modules/typebox/build/format/json_pointer_uri_fragment.d.mts +2 -2
  63. package/node_modules/typebox/build/format/json_pointer_uri_fragment.mjs +2 -2
  64. package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
  65. package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
  66. package/node_modules/typebox/build/system/memory/assign.mjs +2 -1
  67. package/node_modules/typebox/build/system/memory/create.mjs +3 -4
  68. package/node_modules/typebox/build/system/memory/discard.mjs +2 -2
  69. package/node_modules/typebox/build/system/memory/freeze.d.mts +4 -0
  70. package/node_modules/typebox/build/system/memory/freeze.mjs +6 -0
  71. package/node_modules/typebox/build/system/memory/update.mjs +3 -2
  72. package/node_modules/typebox/package.json +1 -1
  73. package/node_modules/undici/lib/dispatcher/balanced-pool.js +4 -2
  74. package/node_modules/undici/lib/dispatcher/client-h1.js +12 -4
  75. package/node_modules/undici/lib/dispatcher/client-h2.js +70 -14
  76. package/node_modules/undici/lib/dispatcher/client.js +6 -2
  77. package/node_modules/undici/lib/handler/cache-handler.js +21 -5
  78. package/node_modules/undici/lib/handler/retry-handler.js +42 -7
  79. package/node_modules/undici/lib/interceptor/cache.js +20 -1
  80. package/node_modules/undici/lib/interceptor/decompress.js +146 -14
  81. package/node_modules/undici/lib/interceptor/dump.js +10 -23
  82. package/node_modules/undici/lib/web/eventsource/eventsource-stream.js +245 -150
  83. package/node_modules/undici/lib/web/websocket/connection.js +1 -1
  84. package/node_modules/undici/lib/web/websocket/permessage-deflate.js +5 -0
  85. package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +8 -10
  86. package/node_modules/undici/package.json +1 -1
  87. package/node_modules/undici/types/interceptors.d.ts +2 -0
  88. package/openclaw.plugin.json +17 -9
  89. package/package.json +6 -6
  90. package/skills/_vendor/slack-skills-plugin.LICENSE +21 -0
  91. package/skills/_vendor/slack-skills-plugin.json +31 -0
  92. package/skills/block-kit/SKILL.md +37 -0
  93. package/skills/block-kit/references/official-block-kit.md +257 -0
  94. package/skills/block-kit/references/official-common-patterns.md +385 -0
  95. package/skills/slack/SKILL.md +1 -1
  96. package/dist/action-runtime.runtime-Dpa16J87.js +0 -2
  97. package/dist/conversation-binding-route-DFVLUdYF.js +0 -115
  98. package/dist/slack-client-kind-hSfjXyUM.js +0 -94
  99. package/node_modules/@slack/bolt/node_modules/negotiator/HISTORY.md +0 -114
@@ -1,28 +1,40 @@
1
1
  import { n as isSlackPluginAccountConfigured } from "./account-configured-sUohAxZr.js";
2
2
  import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, n as listSlackAccountIds, o as resolveSlackAccountAllowFrom, r as mergeSlackAccountConfig } from "./accounts-Dm_H77gH.js";
3
- import { a as parseSlackTarget, n as formatSlackTarget, t as canonicalizeSlackApiTargetId } from "./target-parsing-BnMD2ZqZ.js";
4
- import { t as slackContextTargetsMatch } from "./targets-Cx5W_n3W.js";
3
+ import { a as parseSlackTarget, n as formatSlackTarget, t as canonicalizeSlackApiTargetId } from "./target-parsing-PbxGDbr9.js";
4
+ import { t as slackContextTargetsMatch } from "./targets-7FexXJ62.js";
5
+ import { C as normalizeAllowListLower, D as resolveSlackUserAllowListForTeam, E as resolveSlackAllowListMatch, o as formatSlackError } from "./probe-Biy--yhL.js";
5
6
  import { normalizeAccountId } from "openclaw/plugin-sdk/account-resolution";
6
- import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringifiedOptionalString, readNonBlankString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
+ import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringifiedOptionalString, readNonBlankString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
8
  import { normalizeMessageChannel } from "openclaw/plugin-sdk/routing";
9
+ import { defaultRuntime, logVerbose, warn } from "openclaw/plugin-sdk/runtime-env";
8
10
  import { FormatCapabilityProfile, chunkTextForOutbound, markdownToIR, renderMarkdownIRChunksWithinLimit, renderMarkdownWithMarkers } from "openclaw/plugin-sdk/text-chunking";
9
11
  import { isSingleUseReplyToMode } from "openclaw/plugin-sdk/reply-reference";
10
12
  import { buildChannelKeyCandidates } from "openclaw/plugin-sdk/channel-targets";
11
13
  import { createNativeApprovalChannelRouteGates, doesApprovalRequestSelectChannelAccount, resolveApprovalKind, resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
12
14
  import { createChannelApprovalAuth, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
13
15
  import { createChannelExecApprovalProfile, isChannelExecApprovalClientEnabledFromConfig, isChannelExecApprovalTargetRecipient, matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
16
+ import { normalizeHyphenSlug } from "openclaw/plugin-sdk/string-normalization-runtime";
14
17
  import { channelRouteTargetsMatchExact, stringifyRouteThreadId } from "openclaw/plugin-sdk/channel-route";
15
18
  import { resolveGlobalMap } from "openclaw/plugin-sdk/global-singleton";
16
- import { legacyInteractiveReplyToPresentation, normalizeMessagePresentation, renderMessagePresentationChartFallbackText, renderMessagePresentationFallbackText, renderMessagePresentationTableFallbackText, resolveMessagePresentationButtonAction, resolveMessagePresentationOptionAction } from "openclaw/plugin-sdk/interactive-runtime";
17
- import { parseExecApprovalCommandText } from "openclaw/plugin-sdk/approval-reply-runtime";
18
- import { buildApprovalResolutionRef } from "openclaw/plugin-sdk/approval-reference-runtime";
19
+ import { normalizeMessagePresentation, renderMessagePresentationChartFallbackText, renderMessagePresentationFallbackText, renderMessagePresentationTableFallbackText } from "openclaw/plugin-sdk/interactive-runtime";
19
20
  import { eastAsianWidthType } from "get-east-asian-width";
20
- import { questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
21
- import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
21
+ import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
22
22
  import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
23
- import { compileAllowlist, resolveCompiledAllowlistMatch } from "openclaw/plugin-sdk/allow-from";
24
- import { normalizeHyphenSlug, normalizeStringEntries as normalizeStringEntries$1, normalizeStringEntriesLower } from "openclaw/plugin-sdk/string-normalization-runtime";
23
+ import { resolveConfiguredBindingRoute, resolveRuntimeConversationBindingRoute } from "openclaw/plugin-sdk/conversation-runtime";
25
24
  import { resolveScopeRequireMention, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
25
+ //#region extensions/slack/src/account-reply-mode.ts
26
+ function normalizeSlackChatType(raw) {
27
+ const value = raw?.trim().toLowerCase();
28
+ if (!value) return;
29
+ if (value === "direct" || value === "dm") return "direct";
30
+ if (value === "group" || value === "channel") return value;
31
+ }
32
+ function resolveSlackReplyToMode(account, chatType) {
33
+ const normalized = normalizeSlackChatType(chatType ?? void 0);
34
+ if (normalized && account.replyToModeByChatType?.[normalized] !== void 0) return account.replyToModeByChatType[normalized] ?? "off";
35
+ return account.replyToMode ?? "off";
36
+ }
37
+ //#endregion
26
38
  //#region extensions/slack/src/action-threading.ts
27
39
  const SLACK_PRIVATE_ACTION_DELIVERY_RESULT = Symbol("slack.action.delivery-result");
28
40
  function resolveSlackAutoThreadId(params) {
@@ -43,14 +55,24 @@ function normalizeSlackUserLikeId(value) {
43
55
  const upper = value.toUpperCase();
44
56
  return /^[UW][A-Z0-9]+$/.test(upper) ? upper : void 0;
45
57
  }
46
- function normalizeSlackApproverId(value) {
58
+ function normalizeSlackApproverTarget(value) {
47
59
  const trimmed = normalizeStringifiedOptionalString(value);
48
60
  if (!trimmed) return;
49
- const prefixed = trimmed.match(/^(?:slack|user):([A-Z0-9]+)$/i);
50
- if (prefixed?.[1]) return normalizeSlackUserLikeId(prefixed[1]);
51
- const mention = trimmed.match(/^<@([A-Z0-9]+)>$/i);
52
- if (mention?.[1]) return normalizeSlackUserLikeId(mention[1]);
53
- return normalizeSlackUserLikeId(trimmed);
61
+ try {
62
+ const target = parseSlackTarget(trimmed, { defaultKind: "user" });
63
+ const id = target?.kind === "user" ? normalizeSlackUserLikeId(target.id) : void 0;
64
+ return target?.teamId && id ? formatSlackTarget({
65
+ kind: "user",
66
+ id,
67
+ teamId: target.teamId.toUpperCase()
68
+ }) : id;
69
+ } catch {
70
+ return;
71
+ }
72
+ }
73
+ function normalizeSlackApproverId(value) {
74
+ const target = normalizeSlackApproverTarget(value);
75
+ return target?.startsWith("team:") ? void 0 : target;
54
76
  }
55
77
  function resolveSlackOwnerApprovers(cfg) {
56
78
  const ownerAllowFrom = cfg.commands?.ownerAllowFrom;
@@ -87,27 +109,40 @@ const resolveSlackExecApprovalTarget = slackExecApprovalProfile.resolveTarget;
87
109
  const shouldSuppressLocalSlackExecApprovalPrompt = slackExecApprovalProfile.shouldSuppressLocalPrompt;
88
110
  //#endregion
89
111
  //#region extensions/slack/src/approval-auth.ts
112
+ function resolveSlackApprovalInputs(params) {
113
+ const account = resolveSlackAccount(params).config;
114
+ return {
115
+ allowFrom: resolveSlackAccountAllowFrom(params),
116
+ defaultTo: account.defaultTo
117
+ };
118
+ }
119
+ function slackApprovalTargetMatches(senderId, approvers) {
120
+ const sender = parseSlackTarget(senderId, { defaultKind: "user" });
121
+ return sender?.kind === "user" && resolveSlackAllowListMatch({
122
+ allowList: normalizeAllowListLower([...approvers]),
123
+ teamId: sender.teamId,
124
+ id: sender.id
125
+ }).allowed;
126
+ }
90
127
  const slackApproval = createChannelApprovalAuth({
91
128
  channelLabel: "Slack",
92
- resolveInputs: ({ cfg, accountId }) => {
93
- const account = resolveSlackAccount({
94
- cfg,
95
- accountId
96
- }).config;
97
- return {
98
- allowFrom: resolveSlackAccountAllowFrom({
99
- cfg,
100
- accountId
101
- }),
102
- defaultTo: account.defaultTo
103
- };
104
- },
105
- normalizeApprover: normalizeSlackApproverId,
106
- normalizeDefaultTo: normalizeSlackApproverId,
107
- isWildcardAuthorized: ({ purpose, senderId, inputs, approvers }) => purpose === "sender" && Boolean(senderId) && approvers.length === 0 && inputs.allowFrom?.some((entry) => String(entry).trim() === "*") === true
129
+ resolveInputs: resolveSlackApprovalInputs,
130
+ normalizeApprover: normalizeSlackApproverTarget,
131
+ normalizeDefaultTo: normalizeSlackApproverTarget,
132
+ normalizeSenderId: normalizeSlackApproverTarget,
133
+ isWildcardAuthorized: ({ purpose, senderId, inputs, approvers }) => Boolean(senderId && (slackApprovalTargetMatches(senderId, approvers) || purpose === "sender" && approvers.length === 0 && inputs.allowFrom?.some((entry) => String(entry).trim() === "*")))
108
134
  });
109
135
  const getSlackApprovalApprovers = slackApproval.resolveApprovers;
110
136
  const isSlackApprovalAuthorizedSender = slackApproval.isAuthorizedSender;
137
+ function getSlackApprovalApproversForTeam(params) {
138
+ return resolveApprovalApprovers({
139
+ allowFrom: resolveSlackUserAllowListForTeam({
140
+ allowList: getSlackApprovalApprovers(params),
141
+ teamId: params.teamId
142
+ }),
143
+ normalizeApprover: normalizeSlackApproverTarget
144
+ });
145
+ }
111
146
  //#endregion
112
147
  //#region extensions/slack/src/installation-identity-state.ts
113
148
  const slackInstallationStates = resolveGlobalMap(Symbol.for("openclaw.slack.installation-identities"), "close-and-restart");
@@ -142,7 +177,8 @@ const DEFAULT_APPROVAL_FORWARDING_MODE = "session";
142
177
  const SLACK_DM_CHANNEL_ID_RE = /^D[A-Z0-9]{8,}$/i;
143
178
  const SLACK_USER_ID_RE = /^[UW][A-Z0-9]{8,}$/i;
144
179
  function isSlackApprovalTransportEnabled(params) {
145
- return isSlackPluginAccountConfigured(resolveSlackAccount(params));
180
+ const account = resolveSlackAccount(params);
181
+ return isSlackPluginAccountConfigured(account);
146
182
  }
147
183
  function resolveSlackNativeApprovalConfig(params) {
148
184
  return resolveSlackAccount(params).config.execApprovals;
@@ -158,7 +194,8 @@ function normalizeComparableTarget(value) {
158
194
  }
159
195
  function extractSlackSessionKind(sessionKey) {
160
196
  if (!sessionKey) return null;
161
- const kind = normalizeLowercaseStringOrEmpty(sessionKey.match(/slack:(direct|channel|group):/i)?.[1]);
197
+ const match = sessionKey.match(/slack:(direct|channel|group):/i);
198
+ const kind = normalizeLowercaseStringOrEmpty(match?.[1]);
162
199
  return kind ? kind : null;
163
200
  }
164
201
  function resolveSlackTurnSourceDefaultKind(params) {
@@ -169,9 +206,10 @@ function resolveTurnSourceSlackOriginTarget(request) {
169
206
  const turnSourceChannel = normalizeLowercaseStringOrEmpty(request.request.turnSourceChannel);
170
207
  const turnSourceTo = normalizeOptionalString(request.request.turnSourceTo) ?? "";
171
208
  if (turnSourceChannel !== "slack" || !turnSourceTo) return null;
209
+ const sessionKind = extractSlackSessionKind(request.request.sessionKey ?? void 0);
172
210
  const parsed = parseSlackTarget(turnSourceTo, { defaultKind: resolveSlackTurnSourceDefaultKind({
173
211
  turnSourceTo,
174
- sessionKind: extractSlackSessionKind(request.request.sessionKey ?? void 0)
212
+ sessionKind
175
213
  }) });
176
214
  if (!parsed) return null;
177
215
  return {
@@ -263,11 +301,12 @@ const { canApprovalPotentiallyRouteToChannel: canApprovalPotentiallyRouteToSlack
263
301
  targetsMatch: slackTargetsMatch
264
302
  });
265
303
  function hasSlackPluginApprovers(params) {
266
- return getSlackApprovalApprovers(params).length > 0;
304
+ return getSlackApprovalApproversForTeam(params).length > 0;
267
305
  }
268
306
  function isSlackPluginNativeApprovalClientConfigEnabled(params) {
307
+ const slackNativeConfig = resolveSlackNativeApprovalConfig(params);
269
308
  return isChannelExecApprovalClientEnabledFromConfig({
270
- enabled: resolveSlackNativeApprovalConfig(params)?.enabled,
309
+ enabled: slackNativeConfig?.enabled,
271
310
  approverCount: getSlackApprovalApprovers(params).length
272
311
  });
273
312
  }
@@ -304,7 +343,10 @@ function matchesSlackNativeApprovalFilters(params) {
304
343
  }
305
344
  function isSlackNativeApprovalAccountEligible(params) {
306
345
  const config = resolveSlackNativeApprovalConfig(params);
307
- const approverCount = params.approvalKind === "exec" ? getSlackExecApprovalApprovers(params).length : getSlackApprovalApprovers(params).length;
346
+ const approverCount = params.approvalKind === "exec" ? getSlackExecApprovalApprovers(params).length : getSlackApprovalApproversForTeam({
347
+ ...params,
348
+ teamId: resolveEnterpriseApprovalTeamId(params.request)
349
+ }).length;
308
350
  return isSlackApprovalTransportEnabled(params) && isChannelExecApprovalClientEnabledFromConfig({
309
351
  enabled: config?.enabled,
310
352
  approverCount
@@ -408,75 +450,10 @@ function parseSlackBlocksInput(raw) {
408
450
  if (raw == null) return;
409
451
  return validateSlackBlocksArray(typeof raw === "string" ? parseBlocksJson(raw) : raw);
410
452
  }
411
- const SLACK_BUTTON_VALUE_MAX = 2e3;
412
- const SLACK_SECTION_TEXT_MAX = 3e3;
413
- const SLACK_PRESENTATION_CAPABILITIES = {
414
- supported: true,
415
- buttons: true,
416
- selects: true,
417
- context: true,
418
- divider: true,
419
- charts: true,
420
- tables: true,
421
- limits: {
422
- actions: {
423
- maxActionsPerRow: 25,
424
- maxValueBytes: SLACK_BUTTON_VALUE_MAX,
425
- supportsStyles: true
426
- },
427
- selects: {
428
- maxOptions: 100,
429
- maxValueBytes: 150
430
- },
431
- text: {
432
- encoding: "characters",
433
- markdownDialect: "slack-mrkdwn",
434
- supportsEdit: true
435
- }
436
- }
437
- };
438
- //#endregion
439
- //#region extensions/slack/src/approval-actions.ts
440
- const SLACK_APPROVAL_VALUE_PREFIX = "openclaw:approval:v1:";
441
- const SLACK_APPROVAL_HEADER_BLOCK_ID = "openclaw_approval_header";
442
- function isApprovalDecision(value) {
443
- return value === "allow-once" || value === "allow-always" || value === "deny";
444
- }
445
- /** Encode portable approval facts without exposing a slash command to Slack callbacks. */
446
- function encodeSlackApprovalAction(action) {
447
- const encode = (approvalId) => `${SLACK_APPROVAL_VALUE_PREFIX}${JSON.stringify({
448
- approvalId,
449
- approvalKind: action.approvalKind,
450
- decision: action.decision
451
- })}`;
452
- const exact = encode(action.approvalId);
453
- return exact.length <= 2e3 ? exact : encode(buildApprovalResolutionRef({
454
- approvalId: action.approvalId,
455
- approvalKind: action.approvalKind
456
- }));
457
- }
458
- /** Decode only the exact Slack-owned approval envelope. Malformed callbacks fail closed. */
459
- function decodeSlackApprovalAction(value) {
460
- if (typeof value !== "string" || !value.startsWith(SLACK_APPROVAL_VALUE_PREFIX)) return null;
461
- try {
462
- const decoded = JSON.parse(value.slice(21));
463
- if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return null;
464
- const record = decoded;
465
- if (Object.keys(record).length !== 3 || typeof record.approvalId !== "string" || record.approvalId.length === 0 || record.approvalKind !== "exec" && record.approvalKind !== "plugin" || !isApprovalDecision(record.decision)) return null;
466
- return {
467
- type: "approval",
468
- approvalId: record.approvalId,
469
- approvalKind: record.approvalKind,
470
- decision: record.decision
471
- };
472
- } catch {
473
- return null;
474
- }
475
- }
476
453
  //#endregion
477
454
  //#region extensions/slack/src/monitor/mrkdwn.ts
478
455
  function escapeSlackMrkdwn(value) {
479
- return value.replaceAll("\\", "\\\\").replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replace(/([*_`~])/g, "\\$1");
456
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
480
457
  }
481
458
  //#endregion
482
459
  //#region extensions/slack/src/presentation-fallback.ts
@@ -568,58 +545,56 @@ function renderSlackMessagePresentationFallbackText(params) {
568
545
  });
569
546
  }
570
547
  //#endregion
548
+ //#region extensions/slack/src/rich-text.ts
549
+ const RICH_TEXT_CONTAINER_TYPES = /* @__PURE__ */ new Set([
550
+ "rich_text_section",
551
+ "rich_text_preformatted",
552
+ "rich_text_quote",
553
+ "rich_text_list"
554
+ ]);
555
+ function renderSlackRichText(value, mode, separator = "") {
556
+ if (!Array.isArray(value)) return "";
557
+ const table = mode === "table";
558
+ const read = table ? readNonBlankString : normalizeOptionalString;
559
+ const literal = table ? (text) => text : escapeSlackMrkdwn;
560
+ const reference = mode === "escaped" ? escapeSlackMrkdwn : (text) => text;
561
+ const formatReference = (raw, prefix, suffix) => {
562
+ const text = read(raw);
563
+ return text ? reference(`${prefix}${text}${suffix}`) : "";
564
+ };
565
+ return value.map((rawElement) => {
566
+ const element = asOptionalRecord(rawElement);
567
+ if (!element) return "";
568
+ if (Array.isArray(element.elements) && (table || RICH_TEXT_CONTAINER_TYPES.has(element.type))) return renderSlackRichText(element.elements, mode, element.type === "rich_text_list" ? "\n" : "");
569
+ if (table) {
570
+ if (element.type === "text" && typeof element.text === "string") return element.text;
571
+ const text = readNonBlankString(element.text);
572
+ if (text) return text;
573
+ }
574
+ switch (element.type) {
575
+ case "text": return typeof element.text === "string" ? literal(element.text) : "";
576
+ case "link": return literal(read(element.text) ?? read(element.url) ?? "");
577
+ case "user": return formatReference(element.user_id, "<@", ">");
578
+ case "channel": return formatReference(element.channel_id, "<#", ">");
579
+ case "usergroup": return formatReference(element.usergroup_id, "<!subteam^", ">");
580
+ case "broadcast": return formatReference(element.range, "<!", ">");
581
+ case "emoji": {
582
+ const name = read(element.name);
583
+ return name ? `:${name}:` : "";
584
+ }
585
+ case "date": return literal(read(element.fallback) ?? "");
586
+ default: return "";
587
+ }
588
+ }).filter(Boolean).join(separator);
589
+ }
590
+ //#endregion
571
591
  //#region extensions/slack/src/data-table.ts
572
592
  const SLACK_DATA_TABLE_COLUMNS_MAX = 20;
573
593
  const SLACK_DATA_TABLE_ROWS_MAX = 100;
594
+ const SLACK_DATA_TABLE_AGGREGATE_CELL_CHARACTERS_MAX = 1e4;
574
595
  function countCharacters(value) {
575
596
  return Array.from(value).length;
576
597
  }
577
- function readRichTextLeaf(record) {
578
- if (record.type === "text" && typeof record.text === "string") return record.text;
579
- const text = readNonBlankString(record.text);
580
- if (text) return text;
581
- switch (record.type) {
582
- case "link": return readNonBlankString(record.url) ?? "";
583
- case "user": {
584
- const userId = readNonBlankString(record.user_id);
585
- return userId ? `<@${userId}>` : "";
586
- }
587
- case "channel": {
588
- const channelId = readNonBlankString(record.channel_id);
589
- return channelId ? `<#${channelId}>` : "";
590
- }
591
- case "usergroup": {
592
- const usergroupId = readNonBlankString(record.usergroup_id);
593
- return usergroupId ? `<!subteam^${usergroupId}>` : "";
594
- }
595
- case "broadcast": {
596
- const range = readNonBlankString(record.range);
597
- return range ? `<!${range}>` : "";
598
- }
599
- case "emoji": {
600
- const name = readNonBlankString(record.name);
601
- return name ? `:${name}:` : "";
602
- }
603
- case "date": return readNonBlankString(record.fallback) ?? "";
604
- default: return "";
605
- }
606
- }
607
- function readRichTextElements(value, separator = "") {
608
- if (!Array.isArray(value)) return "";
609
- const parts = [];
610
- for (const rawElement of value) {
611
- const element = asOptionalRecord(rawElement);
612
- if (!element) continue;
613
- if (Array.isArray(element.elements)) {
614
- const rendered = readRichTextElements(element.elements, element.type === "rich_text_list" ? "\n" : "");
615
- if (rendered) parts.push(rendered);
616
- continue;
617
- }
618
- const rendered = readRichTextLeaf(element);
619
- if (rendered) parts.push(rendered);
620
- }
621
- return parts.join(separator);
622
- }
623
598
  function readSlackBasicTableCell(value) {
624
599
  const cell = asOptionalRecord(value);
625
600
  if (!cell) return "";
@@ -629,7 +604,7 @@ function readSlackBasicTableCell(value) {
629
604
  if (typeof cell.value === "number" && Number.isFinite(cell.value)) return String(cell.value);
630
605
  return typeof cell.value === "string" ? cell.value : "";
631
606
  }
632
- return cell.type === "rich_text" ? readRichTextElements(cell.elements, "\n") : "";
607
+ return cell.type === "rich_text" ? renderSlackRichText(cell.elements, "table", "\n") : "";
633
608
  }
634
609
  function parseSlackBasicTableRows(value) {
635
610
  const block = asOptionalRecord(value);
@@ -651,7 +626,7 @@ function readSlackDataTableCell(value, allowRichText) {
651
626
  if (!cell) return;
652
627
  if (cell.type === "raw_text") return readNonBlankString(cell.text);
653
628
  if (cell.type === "raw_number") return typeof cell.value === "number" && Number.isFinite(cell.value) ? readNonBlankString(cell.text) : void 0;
654
- if (allowRichText && cell.type === "rich_text") return readNonBlankString(readRichTextElements(cell.elements));
629
+ if (allowRichText && cell.type === "rich_text") return readNonBlankString(renderSlackRichText(cell.elements, "table"));
655
630
  }
656
631
  function parseSlackDataTable(value, options = {}) {
657
632
  const block = asOptionalRecord(value);
@@ -758,7 +733,7 @@ function escapeCompactFallbackCell(value) {
758
733
  return value.replaceAll("\\", "\\\\").replaceAll(" ", "\\t").replaceAll("\r", "\\r").replaceAll("\n", "\\n");
759
734
  }
760
735
  function escapeSlackBasicTableCell(value, mrkdwnSafe) {
761
- return (mrkdwnSafe ? escapeSlackMrkdwn(value) : value.replaceAll("\\", "\\\\")).replaceAll(" ", "\\t").replaceAll("\r", "\\r").replaceAll("\n", "\\n");
736
+ return escapeCompactFallbackCell(mrkdwnSafe ? escapeSlackMrkdwn(value) : value);
762
737
  }
763
738
  function renderSlackBasicTableRows(value, mrkdwnSafe) {
764
739
  return parseSlackBasicTableRows(value)?.map((row) => row.map((cell) => escapeSlackBasicTableCell(cell, mrkdwnSafe)).join(" ")).join("\n");
@@ -925,16 +900,14 @@ function renderSlackDataVisualizationMrkdwnFallbackText(value) {
925
900
  }
926
901
  //#endregion
927
902
  //#region extensions/slack/src/format.ts
928
- function escapeSlackMrkdwnSegment(text) {
929
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
930
- }
931
903
  const SLACK_ANGLE_TOKEN_RE = /<[^>\n]+>/g;
932
904
  function isAllowedSlackAngleToken(token) {
933
905
  if (!token.startsWith("<") || !token.endsWith(">")) return false;
934
906
  const inner = token.slice(1, -1);
935
907
  return inner.startsWith("@") || inner.startsWith("#") || inner.startsWith("!") || inner.startsWith("mailto:") || inner.startsWith("tel:") || inner.startsWith("http://") || inner.startsWith("https://") || inner.startsWith("slack://");
936
908
  }
937
- function escapeSlackMrkdwnContent(text) {
909
+ function escapeSlackMrkdwnContent(text, mentions) {
910
+ if (mentions === "escape") return escapeSlackMrkdwn(text);
938
911
  if (!text) return "";
939
912
  if (!text.includes("&") && !text.includes("<") && !text.includes(">")) return text;
940
913
  SLACK_ANGLE_TOKEN_RE.lastIndex = 0;
@@ -942,20 +915,20 @@ function escapeSlackMrkdwnContent(text) {
942
915
  let lastIndex = 0;
943
916
  for (let match = SLACK_ANGLE_TOKEN_RE.exec(text); match; match = SLACK_ANGLE_TOKEN_RE.exec(text)) {
944
917
  const matchIndex = match.index ?? 0;
945
- out.push(escapeSlackMrkdwnSegment(text.slice(lastIndex, matchIndex)));
918
+ out.push(escapeSlackMrkdwn(text.slice(lastIndex, matchIndex)));
946
919
  const token = match[0] ?? "";
947
- out.push(isAllowedSlackAngleToken(token) ? token : escapeSlackMrkdwnSegment(token));
920
+ out.push(isAllowedSlackAngleToken(token) ? token : escapeSlackMrkdwn(token));
948
921
  lastIndex = matchIndex + token.length;
949
922
  }
950
- out.push(escapeSlackMrkdwnSegment(text.slice(lastIndex)));
923
+ out.push(escapeSlackMrkdwn(text.slice(lastIndex)));
951
924
  return out.join("");
952
925
  }
953
- function escapeSlackMrkdwnText(text) {
926
+ function escapeSlackMrkdwnText(text, mentions) {
954
927
  if (!text) return "";
955
928
  if (!text.includes("&") && !text.includes("<") && !text.includes(">")) return text;
956
929
  return text.split("\n").map((line) => {
957
- if (line.startsWith("> ")) return `> ${escapeSlackMrkdwnContent(line.slice(2))}`;
958
- return escapeSlackMrkdwnContent(line);
930
+ if (line.startsWith("> ")) return `> ${escapeSlackMrkdwnContent(line.slice(2), mentions)}`;
931
+ return escapeSlackMrkdwnContent(line, mentions);
959
932
  }).join("\n");
960
933
  }
961
934
  function buildSlackLink(link, text) {
@@ -964,7 +937,7 @@ function buildSlackLink(link, text) {
964
937
  const trimmedLabel = text.slice(link.start, link.end).trim();
965
938
  const comparableHref = href.startsWith("mailto:") ? href.slice(7) : href;
966
939
  if (!(trimmedLabel.length > 0 && trimmedLabel !== href && trimmedLabel !== comparableHref)) return null;
967
- const safeHref = escapeSlackMrkdwnSegment(href);
940
+ const safeHref = escapeSlackMrkdwn(href);
968
941
  return {
969
942
  start: link.start,
970
943
  end: link.end,
@@ -1163,12 +1136,10 @@ function projectSlackMrkdwnVisibleText(text, dateDisplay) {
1163
1136
  }
1164
1137
  function protectSlackAssistantTranscriptRoleHeaders(text) {
1165
1138
  if (text.startsWith(SLACK_ASSISTANT_TRANSCRIPT_PREFIX)) return text;
1166
- const tokenProjection = projectSlackMrkdwnVisibleText(text, "token");
1167
- const fallbackProjection = projectSlackMrkdwnVisibleText(text, "fallback");
1168
- if (!slackProjectionHasRoleHeader(tokenProjection) && !slackProjectionHasRoleHeader(fallbackProjection)) return text;
1139
+ if (!slackProjectionHasRoleHeader(projectSlackMrkdwnVisibleText(text, "token")) && (!text.includes("<!date^") || !slackProjectionHasRoleHeader(projectSlackMrkdwnVisibleText(text, "fallback")))) return text;
1169
1140
  return `${SLACK_ASSISTANT_TRANSCRIPT_PREFIX}${text}`;
1170
1141
  }
1171
- function buildSlackRenderOptions() {
1142
+ function buildSlackRenderOptions({ enclosingStyle, mentions } = {}) {
1172
1143
  return {
1173
1144
  annotationMarkers: { assistant_transcript_role: {
1174
1145
  open: "`",
@@ -1176,14 +1147,14 @@ function buildSlackRenderOptions() {
1176
1147
  suppressNestedFormatting: true
1177
1148
  } },
1178
1149
  styleMarkers: {
1179
- bold: {
1150
+ ...enclosingStyle !== "bold" ? { bold: {
1180
1151
  open: "*",
1181
1152
  close: "*"
1182
- },
1183
- italic: {
1153
+ } } : {},
1154
+ ...enclosingStyle !== "italic" ? { italic: {
1184
1155
  open: "_",
1185
1156
  close: "_"
1186
- },
1157
+ } } : {},
1187
1158
  strikethrough: {
1188
1159
  open: "~",
1189
1160
  close: "~"
@@ -1197,22 +1168,20 @@ function buildSlackRenderOptions() {
1197
1168
  close: "```"
1198
1169
  }
1199
1170
  },
1200
- escapeText: escapeSlackMrkdwnText,
1171
+ escapeText: (text) => escapeSlackMrkdwnText(text, mentions),
1201
1172
  buildLink: buildSlackLink
1202
1173
  };
1203
1174
  }
1204
- function markdownToSlackMrkdwn(markdown, options = {}) {
1205
- return renderMarkdownWithMarkers(makeSlackEmphasisStylesSafe(markdownToIR(markdown ?? "", {
1175
+ function normalizeSlackOutboundText(markdown, options = {}) {
1176
+ const ir = makeSlackEmphasisStylesSafe(markdownToIR(markdown ?? "", {
1206
1177
  assistantTranscriptRoleHeaders: true,
1207
1178
  linkify: false,
1208
1179
  autolink: false,
1209
1180
  headingStyle: "rich",
1210
1181
  blockquotePrefix: "> ",
1211
1182
  tableMode: options.tableMode
1212
- })), buildSlackRenderOptions(), SLACK_FORMAT_PROFILE);
1213
- }
1214
- function normalizeSlackOutboundText(markdown) {
1215
- return protectSlackAssistantTranscriptRoleHeaders(markdownToSlackMrkdwn(markdown ?? ""));
1183
+ }));
1184
+ return protectSlackAssistantTranscriptRoleHeaders(renderMarkdownWithMarkers(ir, buildSlackRenderOptions(options), SLACK_FORMAT_PROFILE));
1216
1185
  }
1217
1186
  /** Chunk already-rendered Slack mrkdwn without splitting entities or code markers. */
1218
1187
  function chunkSlackMrkdwnText(text, limit) {
@@ -1246,7 +1215,7 @@ function chunkSlackMrkdwnText(text, limit) {
1246
1215
  const marker = wrapper(activeMarker);
1247
1216
  if (activeMarker && isAllowedSlackAngleToken(token)) {
1248
1217
  if (marker) chunks.push(...chunkTextForOutbound(token, Math.max(1, Math.floor(contentLimit)), { preserveWhitespace: true }).map((fragment) => `${marker}${fragment}${marker}`));
1249
- else chunks.push(...chunkTextForOutbound(escapeSlackMrkdwnSegment(token), Math.max(1, Math.floor(limit)), { preserveWhitespace: true }));
1218
+ else chunks.push(...chunkTextForOutbound(escapeSlackMrkdwn(token), Math.max(1, Math.floor(limit)), { preserveWhitespace: true }));
1250
1219
  continue;
1251
1220
  }
1252
1221
  chunks.push(...token.length <= limit ? [token] : chunkTextForOutbound(token, limit));
@@ -1275,39 +1244,6 @@ function markdownToSlackMrkdwnChunks(markdown, limit, options = {}) {
1275
1244
  }).map(({ rendered }) => rendered);
1276
1245
  }
1277
1246
  //#endregion
1278
- //#region extensions/slack/src/question-actions.ts
1279
- const SLACK_QUESTION_VALUE_PREFIX = "slq1:";
1280
- const QUESTION_RECORD_ID_PATTERN = /^ask_[a-f0-9]{32}$/u;
1281
- function encodeSlackQuestionAction(action) {
1282
- if (!QUESTION_RECORD_ID_PATTERN.test(action.questionId) || !Number.isInteger(action.optionIndex) || action.optionIndex < 0 || action.optionIndex > 3) return;
1283
- const value = `${SLACK_QUESTION_VALUE_PREFIX}${action.questionId}:${action.optionIndex}`;
1284
- return value.length <= 2e3 ? value : void 0;
1285
- }
1286
- function decodeSlackQuestionAction(value) {
1287
- if (typeof value !== "string" || value.length > 2e3) return null;
1288
- const match = /^slq1:(ask_[a-f0-9]{32}):([0-3])$/u.exec(value);
1289
- return match?.[1] && match[2] ? {
1290
- questionId: match[1],
1291
- optionIndex: Number(match[2])
1292
- } : null;
1293
- }
1294
- async function resolveSlackQuestionAction(params) {
1295
- let result;
1296
- try {
1297
- result = await (params.resolveQuestion ?? questionGatewayRuntime.resolveOption)({
1298
- cfg: params.cfg,
1299
- questionId: params.action.questionId,
1300
- optionIndex: params.action.optionIndex,
1301
- senderId: params.userId,
1302
- clientDisplayName: `Slack question (${params.accountId})`
1303
- });
1304
- } catch {
1305
- await params.respond("Could not submit this answer.").catch(() => {});
1306
- return;
1307
- }
1308
- await params.respond(result.status === "answered" ? "Answer submitted." : "This question was already answered.").catch(() => {});
1309
- }
1310
- //#endregion
1311
1247
  //#region extensions/slack/src/reply-action-ids.ts
1312
1248
  const SLACK_REPLY_BUTTON_ACTION_ID = "openclaw:reply_button";
1313
1249
  const SLACK_REPLY_LINK_ACTION_ID = "openclaw:reply_link";
@@ -1365,368 +1301,12 @@ function truncateSlackTextByUtf8Bytes(value, maxBytes) {
1365
1301
  return maxBytes >= suffixBytes ? `${prefix}${suffix}` : prefix;
1366
1302
  }
1367
1303
  //#endregion
1368
- //#region extensions/slack/src/blocks-render.ts
1369
- const SLACK_BUTTON_URL_MAX = 3e3;
1370
- function buildSlackReplyButtonActionId(buttonIndex, choiceIndex) {
1371
- return `${SLACK_REPLY_BUTTON_ACTION_ID}:${String(buttonIndex)}:${String(choiceIndex + 1)}`;
1372
- }
1373
- function buildSlackReplyLinkActionId(buttonIndex, choiceIndex) {
1374
- return `${SLACK_REPLY_LINK_ACTION_ID}:${String(buttonIndex)}:${String(choiceIndex + 1)}`;
1375
- }
1376
- function buildSlackReplySelectActionId(selectIndex) {
1377
- return `${SLACK_REPLY_SELECT_ACTION_ID}:${String(selectIndex)}`;
1378
- }
1379
- function buildSlackApprovalButtonActionId(buttonIndex, choiceIndex) {
1380
- return `${SLACK_APPROVAL_BUTTON_ACTION_ID}:${String(buttonIndex)}:${String(choiceIndex + 1)}`;
1381
- }
1382
- function buildSlackApprovalSelectActionId(selectIndex) {
1383
- return `${SLACK_APPROVAL_SELECT_ACTION_ID}:${String(selectIndex)}`;
1384
- }
1385
- function buildSlackCallbackButtonActionId(buttonIndex, choiceIndex) {
1386
- return `${SLACK_CALLBACK_BUTTON_ACTION_ID}:${String(buttonIndex)}:${String(choiceIndex + 1)}`;
1387
- }
1388
- function buildSlackCallbackSelectActionId(selectIndex) {
1389
- return `${SLACK_CALLBACK_SELECT_ACTION_ID}:${String(selectIndex)}`;
1390
- }
1391
- function buildSlackQuestionButtonActionId(buttonIndex, choiceIndex) {
1392
- return `${SLACK_QUESTION_BUTTON_ACTION_ID}:${String(buttonIndex)}:${String(choiceIndex + 1)}`;
1393
- }
1394
- function resolveSlackButtonStyle(style) {
1395
- if (style === "primary" || style === "danger") return style;
1396
- if (style === "success") return "primary";
1397
- }
1398
- function resolveSlackActionTarget(action, optionIndex) {
1399
- if (!action) return;
1400
- if (action.type === "approval") return {
1401
- kind: "approval",
1402
- value: encodeSlackApprovalAction(action)
1403
- };
1404
- if (action.type === "question") {
1405
- const value = optionIndex === void 0 ? void 0 : encodeSlackQuestionAction({
1406
- questionId: action.questionId,
1407
- optionIndex
1408
- });
1409
- return value ? {
1410
- kind: "question",
1411
- value
1412
- } : void 0;
1413
- }
1414
- if (action.type === "url" || action.type === "web-app") {
1415
- const url = normalizeOptionalString(action.url);
1416
- return url ? {
1417
- kind: "link",
1418
- url
1419
- } : void 0;
1420
- }
1421
- if (action.type === "callback") {
1422
- const value = normalizeOptionalString(action.value);
1423
- return value ? {
1424
- kind: "callback",
1425
- value
1426
- } : void 0;
1427
- }
1428
- const command = normalizeOptionalString(action.command);
1429
- return command && parseExecApprovalCommandText(command) ? {
1430
- kind: "reply",
1431
- value: command
1432
- } : void 0;
1433
- }
1434
- function resolveSlackButtonTarget(button, optionIndex) {
1435
- if (button.action !== void 0) {
1436
- const action = resolveMessagePresentationButtonAction(button);
1437
- return action ? resolveSlackActionTarget(action, optionIndex) : void 0;
1438
- }
1439
- const legacyUrl = normalizeOptionalString(button.url ?? button.webApp?.url ?? button.web_app?.url);
1440
- if (legacyUrl && isWithinSlackLimit(legacyUrl, SLACK_BUTTON_URL_MAX)) return {
1441
- kind: "link",
1442
- url: legacyUrl
1443
- };
1444
- const legacyValue = normalizeOptionalString(button.value);
1445
- if (legacyValue) return {
1446
- kind: "reply",
1447
- value: legacyValue
1448
- };
1449
- return legacyUrl ? {
1450
- kind: "link",
1451
- url: legacyUrl
1452
- } : void 0;
1453
- }
1454
- function resolveSlackOptionTarget(option) {
1455
- if (option.action !== void 0) {
1456
- const action = resolveMessagePresentationOptionAction(option);
1457
- const target = action ? resolveSlackActionTarget(action) : void 0;
1458
- return target?.kind === "link" || target?.kind === "question" ? void 0 : target;
1459
- }
1460
- const value = normalizeOptionalString(option.value);
1461
- return value ? {
1462
- kind: "reply",
1463
- value
1464
- } : void 0;
1465
- }
1466
- function isWithinSlackLimit(value, maxLength) {
1467
- return value.length <= maxLength;
1468
- }
1469
- function isRenderableSlackOption(option) {
1470
- return isWithinSlackLimit(option.value, 150);
1471
- }
1472
- function readSlackBlockId(block) {
1473
- const value = block.block_id;
1474
- return typeof value === "string" ? value : void 0;
1475
- }
1476
- function readSlackOpenClawBlockIndex(blockId, prefix) {
1477
- if (!blockId.startsWith(prefix)) return;
1478
- const value = Number.parseInt(blockId.slice(prefix.length), 10);
1479
- return Number.isSafeInteger(value) && value > 0 ? value : void 0;
1480
- }
1481
- /** Resolve existing Block Kit indexes and native-data budgets before appending portable blocks. */
1482
- function resolveSlackBlockOffsets(blocks) {
1483
- let buttonIndexOffset = 0;
1484
- const dataTableCellCharacterCountOffset = countSlackDataTableBlocksCellCharacters(blocks) ?? 10001;
1485
- let dataVisualizationCountOffset = 0;
1486
- let selectIndexOffset = 0;
1487
- for (const block of blocks ?? []) {
1488
- if (hasSlackDataVisualizationBlock([block])) dataVisualizationCountOffset += 1;
1489
- const blockId = readSlackBlockId(block);
1490
- if (!blockId) continue;
1491
- buttonIndexOffset = Math.max(buttonIndexOffset, readSlackOpenClawBlockIndex(blockId, "openclaw_reply_buttons_") ?? 0);
1492
- selectIndexOffset = Math.max(selectIndexOffset, readSlackOpenClawBlockIndex(blockId, "openclaw_reply_select_") ?? 0);
1493
- }
1494
- return {
1495
- buttonIndexOffset,
1496
- dataTableCellCharacterCountOffset,
1497
- dataVisualizationCountOffset,
1498
- selectIndexOffset
1499
- };
1500
- }
1501
- /**
1502
- * @deprecated Use buildSlackPresentationBlocks with MessagePresentation.
1503
- */
1504
- function buildSlackInteractiveBlocks(interactive, options = {}) {
1505
- return buildSlackPresentationBlocks(interactive ? legacyInteractiveReplyToPresentation(interactive) : void 0, options);
1506
- }
1507
- /** Render portable presentation blocks as Slack Block Kit blocks. */
1508
- function buildSlackPresentationBlocks(presentation, options = {}) {
1509
- if (!presentation) return [];
1510
- const renderTablesNatively = canRenderSlackPresentationTables(presentation, options);
1511
- const blocks = [];
1512
- if (presentation.title) blocks.push({
1513
- type: "header",
1514
- text: {
1515
- type: "plain_text",
1516
- text: truncateSlackText(presentation.title, 150),
1517
- emoji: true
1518
- }
1519
- });
1520
- let buttonIndex = options.buttonIndexOffset ?? 0;
1521
- let dataTableCellCharacterCount = options.dataTableCellCharacterCountOffset ?? 0;
1522
- let dataVisualizationCount = options.dataVisualizationCountOffset ?? 0;
1523
- let selectIndex = options.selectIndexOffset ?? 0;
1524
- for (const block of presentation.blocks) {
1525
- if (block.type === "text" || block.type === "context") {
1526
- const text = block.text.trim();
1527
- if (!text) continue;
1528
- for (const chunk of chunkSlackMrkdwnText(text, SLACK_SECTION_TEXT_MAX)) blocks.push(block.type === "context" ? {
1529
- type: "context",
1530
- elements: [{
1531
- type: "mrkdwn",
1532
- text: chunk,
1533
- verbatim: true
1534
- }]
1535
- } : {
1536
- type: "section",
1537
- text: {
1538
- type: "mrkdwn",
1539
- text: chunk
1540
- }
1541
- });
1542
- continue;
1543
- }
1544
- if (block.type === "divider") {
1545
- blocks.push({ type: "divider" });
1546
- continue;
1547
- }
1548
- if (block.type === "buttons") {
1549
- const rendered = buildSlackPresentationButtonBlock(block, buttonIndex + 1);
1550
- if (rendered) {
1551
- buttonIndex += 1;
1552
- blocks.push(rendered);
1553
- }
1554
- continue;
1555
- }
1556
- if (block.type === "chart") {
1557
- const rendered = dataVisualizationCount < 2 ? buildSlackPresentationChartBlock(block) : void 0;
1558
- if (rendered) {
1559
- dataVisualizationCount += 1;
1560
- blocks.push(rendered);
1561
- } else {
1562
- const fallback = renderSlackMessagePresentationChartFallbackText(block);
1563
- blocks.push(...chunkTextForOutbound(fallback, SLACK_SECTION_TEXT_MAX).map((text) => ({
1564
- type: "context",
1565
- elements: [{
1566
- type: "mrkdwn",
1567
- text,
1568
- verbatim: true
1569
- }]
1570
- })));
1571
- }
1572
- continue;
1573
- }
1574
- if (block.type === "table") {
1575
- if (!renderTablesNatively) continue;
1576
- const rendered = buildSlackDataTableBlock(block, { cellCharacterCountOffset: dataTableCellCharacterCount });
1577
- if (rendered) {
1578
- dataTableCellCharacterCount += countSlackDataTableCellCharacters(rendered);
1579
- blocks.push(rendered);
1580
- }
1581
- continue;
1582
- }
1583
- if (block.type === "select") {
1584
- const rendered = buildSlackPresentationSelectBlock(block, selectIndex + 1);
1585
- if (rendered) {
1586
- selectIndex += 1;
1587
- blocks.push(rendered);
1588
- }
1589
- }
1590
- }
1591
- return blocks;
1592
- }
1593
- function buildSlackPresentationChartBlock(block) {
1594
- return buildSlackDataVisualizationBlock(block);
1595
- }
1596
- function buildSlackPresentationButtonBlock(block, buttonIndex) {
1597
- const elements = block.buttons.flatMap((button, choiceIndex) => {
1598
- const target = resolveSlackButtonTarget(button, choiceIndex);
1599
- if (!target || (target.kind === "link" ? !isWithinSlackLimit(target.url, SLACK_BUTTON_URL_MAX) : !isWithinSlackLimit(target.value, 2e3))) return [];
1600
- const style = resolveSlackButtonStyle(button.style);
1601
- return [{
1602
- type: "button",
1603
- action_id: target.kind === "link" ? buildSlackReplyLinkActionId(buttonIndex, choiceIndex) : target.kind === "approval" ? buildSlackApprovalButtonActionId(buttonIndex, choiceIndex) : target.kind === "callback" ? buildSlackCallbackButtonActionId(buttonIndex, choiceIndex) : target.kind === "question" ? buildSlackQuestionButtonActionId(buttonIndex, choiceIndex) : buildSlackReplyButtonActionId(buttonIndex, choiceIndex),
1604
- text: {
1605
- type: "plain_text",
1606
- text: truncateSlackText(button.label, 75),
1607
- emoji: true
1608
- },
1609
- ...target.kind === "link" ? { url: target.url } : { value: target.value },
1610
- ...style ? { style } : {}
1611
- }];
1612
- }).slice(0, 25);
1613
- return elements.length > 0 ? {
1614
- type: "actions",
1615
- block_id: `openclaw_reply_buttons_${buttonIndex}`,
1616
- elements
1617
- } : void 0;
1618
- }
1619
- /** True when every portable table fits Slack's native per-message table budget. */
1620
- function canRenderSlackPresentationTables(presentation, options = {}) {
1621
- let cellCharacterCount = options.dataTableCellCharacterCountOffset ?? 0;
1622
- for (const block of presentation.blocks) {
1623
- if (block.type !== "table") continue;
1624
- const rendered = buildSlackDataTableBlock(block, { cellCharacterCountOffset: cellCharacterCount });
1625
- if (!rendered) return false;
1626
- cellCharacterCount += countSlackDataTableCellCharacters(rendered);
1627
- }
1628
- return true;
1629
- }
1630
- /** True when native Slack rendering preserves every portable control. */
1631
- function canRenderSlackPresentation(presentation, options = {}) {
1632
- if (presentation.title && !isWithinSlackLimit(presentation.title.trim(), 150)) return false;
1633
- if (!canRenderSlackPresentationTables(presentation, options)) return false;
1634
- let dataVisualizationCount = options.dataVisualizationCountOffset ?? 0;
1635
- for (const block of presentation.blocks) {
1636
- if (block.type === "text" || block.type === "context") continue;
1637
- if (block.type === "buttons") {
1638
- if (!(block.buttons.length <= 25 && block.buttons.every((button, choiceIndex) => {
1639
- if (!isWithinSlackLimit(button.label, 75)) return false;
1640
- const target = resolveSlackButtonTarget(button, choiceIndex);
1641
- return target ? target.kind === "link" ? isWithinSlackLimit(target.url, SLACK_BUTTON_URL_MAX) : isWithinSlackLimit(target.value, 2e3) : false;
1642
- }))) return false;
1643
- continue;
1644
- }
1645
- if (block.type === "select") {
1646
- if (!(isWithinSlackLimit(normalizeOptionalString(block.placeholder) ?? "Choose an option", 75) && block.options.length <= 100 && (!block.placeholder || isWithinSlackLimit(block.placeholder, 75)) && block.options.every((option) => {
1647
- if (!isWithinSlackLimit(option.label, 75)) return false;
1648
- const target = resolveSlackOptionTarget(option);
1649
- return target ? isRenderableSlackOption({
1650
- label: option.label,
1651
- ...target
1652
- }) : false;
1653
- }) && new Set(block.options.map((option) => resolveSlackOptionTarget(option)?.kind)).size === 1)) return false;
1654
- continue;
1655
- }
1656
- if (block.type === "chart") {
1657
- if (dataVisualizationCount >= 2 || !canRenderSlackDataVisualization(block)) return false;
1658
- dataVisualizationCount += 1;
1659
- continue;
1660
- }
1661
- if (block.type === "table") continue;
1662
- }
1663
- return true;
1664
- }
1665
- function buildSlackPresentationSelectBlock(block, selectIndex) {
1666
- const options = block.options.flatMap((option) => {
1667
- const target = resolveSlackOptionTarget(option);
1668
- return target ? [{
1669
- label: option.label,
1670
- ...target
1671
- }] : [];
1672
- }).filter(isRenderableSlackOption).slice(0, 100);
1673
- const optionKinds = new Set(options.map((option) => option.kind));
1674
- return options.length > 0 && optionKinds.size === 1 ? {
1675
- type: "actions",
1676
- block_id: `openclaw_reply_select_${selectIndex}`,
1677
- elements: [{
1678
- type: "static_select",
1679
- action_id: options[0]?.kind === "approval" ? buildSlackApprovalSelectActionId(selectIndex) : options[0]?.kind === "callback" ? buildSlackCallbackSelectActionId(selectIndex) : buildSlackReplySelectActionId(selectIndex),
1680
- placeholder: {
1681
- type: "plain_text",
1682
- text: truncateSlackText(normalizeOptionalString(block.placeholder) ?? "Choose an option", 75),
1683
- emoji: true
1684
- },
1685
- options: options.map((option) => ({
1686
- text: {
1687
- type: "plain_text",
1688
- text: truncateSlackText(option.label, 75),
1689
- emoji: true
1690
- },
1691
- value: option.value
1692
- }))
1693
- }]
1694
- } : void 0;
1695
- }
1696
- //#endregion
1697
1304
  //#region extensions/slack/src/limits.ts
1698
1305
  const SLACK_TEXT_LIMIT = 8e3;
1699
1306
  const SLACK_MESSAGE_TEXT_RECOMMENDED_LIMIT = 4e3;
1700
1307
  const SLACK_EDIT_TEXT_MAX_BYTES = 4e3;
1701
1308
  const SLACK_MESSAGE_TEXT_HARD_LIMIT = 4e4;
1702
1309
  //#endregion
1703
- //#region extensions/slack/src/authored-text.ts
1704
- function normalizeComparableSlackText(text) {
1705
- return text.trim().replace(/\s+/g, " ");
1706
- }
1707
- function isSlackAuthoredTextRepresentedInInteractive(text, interactive) {
1708
- return isSlackAuthoredTextRepresentedInFragments(text, interactive?.blocks.flatMap((block) => block.type === "text" ? [block.text] : []) ?? []);
1709
- }
1710
- function isSlackAuthoredTextRepresentedInFragments(text, rawFragments) {
1711
- const target = normalizeComparableSlackText(text);
1712
- const fragments = rawFragments.map(normalizeComparableSlackText).filter(Boolean);
1713
- for (let start = 0; start < fragments.length; start += 1) {
1714
- let combined = "";
1715
- for (let end = start; end < fragments.length; end += 1) {
1716
- combined = normalizeComparableSlackText(`${combined} ${fragments[end]}`);
1717
- if (combined === target) return true;
1718
- if (combined.length > target.length) break;
1719
- }
1720
- }
1721
- return false;
1722
- }
1723
- /** Resolve placement from producer facts, before accessibility text changes the payload text. */
1724
- function resolveSlackAuthoredTextPlacement(params) {
1725
- const text = normalizeOptionalString(params.text);
1726
- if (!text) return "none";
1727
- return params.renderedInBlocks || isSlackAuthoredTextRepresentedInFragments(text, params.renderedTextFragments ?? []) || isSlackAuthoredTextRepresentedInInteractive(text, params.interactive) ? "blocks" : "outside-blocks";
1728
- }
1729
- //#endregion
1730
1310
  //#region extensions/slack/src/blocks-fallback.ts
1731
1311
  const SLACK_SELECT_ELEMENT_TYPES = /* @__PURE__ */ new Set([
1732
1312
  "static_select",
@@ -1750,49 +1330,6 @@ function readTextObject(value, options = {}) {
1750
1330
  function readTextValue(value, options = {}) {
1751
1331
  return normalizeOptionalString(value) ?? readTextObject(value, options);
1752
1332
  }
1753
- function renderSlackRichTextLeaf(element) {
1754
- switch (element.type) {
1755
- case "text": return typeof element.text === "string" ? escapeSlackMrkdwn(element.text) : "";
1756
- case "link": return escapeSlackMrkdwn(normalizeOptionalString(element.text) ?? normalizeOptionalString(element.url) ?? "");
1757
- case "user": {
1758
- const userId = normalizeOptionalString(element.user_id);
1759
- return userId ? escapeSlackMrkdwn(`<@${userId}>`) : "";
1760
- }
1761
- case "channel": {
1762
- const channelId = normalizeOptionalString(element.channel_id);
1763
- return channelId ? escapeSlackMrkdwn(`<#${channelId}>`) : "";
1764
- }
1765
- case "usergroup": {
1766
- const usergroupId = normalizeOptionalString(element.usergroup_id);
1767
- return usergroupId ? escapeSlackMrkdwn(`<!subteam^${usergroupId}>`) : "";
1768
- }
1769
- case "broadcast": {
1770
- const range = normalizeOptionalString(element.range);
1771
- return range ? escapeSlackMrkdwn(`<!${range}>`) : "";
1772
- }
1773
- case "emoji": {
1774
- const name = normalizeOptionalString(element.name);
1775
- return name ? `:${name}:` : "";
1776
- }
1777
- case "date": return escapeSlackMrkdwn(normalizeOptionalString(element.fallback) ?? "");
1778
- default: return "";
1779
- }
1780
- }
1781
- function renderSlackRichTextElement(value) {
1782
- const element = asOptionalRecord(value);
1783
- if (!element) return "";
1784
- switch (element.type) {
1785
- case "rich_text_section":
1786
- case "rich_text_preformatted":
1787
- case "rich_text_quote": return renderSlackRichTextElements(element.elements, "");
1788
- case "rich_text_list": return renderSlackRichTextElements(element.elements, "\n");
1789
- default: return renderSlackRichTextLeaf(element);
1790
- }
1791
- }
1792
- function renderSlackRichTextElements(value, separator) {
1793
- if (!Array.isArray(value)) return "";
1794
- return value.map(renderSlackRichTextElement).filter(Boolean).join(separator);
1795
- }
1796
1333
  function readImageText(block) {
1797
1334
  const altText = normalizeOptionalString(block.alt_text);
1798
1335
  return (altText ? escapeSlackMrkdwn(altText) : void 0) ?? readTextObject(block.title);
@@ -1842,7 +1379,7 @@ function renderSlackBlockFallbackText(raw, options = {}) {
1842
1379
  const block = asOptionalRecord(raw);
1843
1380
  if (!block) return;
1844
1381
  switch (block.type) {
1845
- case "rich_text": return normalizeOptionalString(renderSlackRichTextElements(block.elements, "\n"));
1382
+ case "rich_text": return normalizeOptionalString(renderSlackRichText(block.elements, options.nativeReferenceFormat === "plain" ? "native-reference" : "escaped", "\n"));
1846
1383
  case "header": return readTextObject(block.text, options);
1847
1384
  case "section": return readSectionText(block, options);
1848
1385
  case "image": return readImageText(block) ?? "Shared an image";
@@ -1869,7 +1406,7 @@ function buildSlackCompleteBlocksFallbackText(blocks) {
1869
1406
  //#endregion
1870
1407
  //#region extensions/slack/src/native-data-blocks.ts
1871
1408
  const SLACK_MALFORMED_NATIVE_DATA_FALLBACK = "Slack could not render this chart or table data.";
1872
- const SLACK_RESPONSE_URL_BODY_LIMIT_BYTES = 16 * 1024;
1409
+ const SLACK_RESPONSE_URL_BODY_LIMIT_BYTES = 16384;
1873
1410
  const SLACK_RESPONSE_URL_BODY_TIMEOUT_MS = 3e4;
1874
1411
  /** Detect a native Slack chart or table block. */
1875
1412
  function hasSlackNativeDataBlock(blocks) {
@@ -1987,418 +1524,109 @@ function appendSlackNativeDataPlainTextFallback(text, blocks) {
1987
1524
  return appendSlackNativeDataFallback(text, blocks, renderSlackNativeDataPlainTextBlock);
1988
1525
  }
1989
1526
  //#endregion
1990
- //#region extensions/slack/src/reply-blocks.ts
1991
- function parseSlackReplyBlockSegments(value) {
1992
- if (value === void 0) return;
1993
- if (!Array.isArray(value)) throw new Error("Slack rendered presentation segments must be an array");
1994
- return value.map((raw) => {
1995
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("Slack rendered presentation segment must be an object");
1996
- const segment = raw;
1997
- if (segment.kind === "text" && typeof segment.text === "string" && segment.mrkdwn === false) return {
1998
- kind: "text",
1999
- text: segment.text,
2000
- mrkdwn: false
2001
- };
2002
- if (segment.kind === "blocks") {
2003
- const blocks = parseSlackBlocksInput(segment.blocks);
2004
- if (blocks?.length) return {
2005
- kind: "blocks",
2006
- blocks
2007
- };
2008
- }
2009
- throw new Error("Slack rendered presentation segment is invalid");
2010
- });
2011
- }
2012
- /** Convert compiled segments into ordered sender calls without re-inferring text placement. */
2013
- function resolveSlackReplyDeliveryMessages(params) {
2014
- const messages = [];
2015
- let outsideText = params.authoredTextPlacement === "outside-blocks" ? params.text?.trim() ?? "" : "";
2016
- for (const segment of params.segments) {
2017
- if (segment.kind === "text") {
2018
- const text = [outsideText, segment.text].filter(Boolean).join("\n\n");
2019
- outsideText = "";
2020
- if (text) messages.push({
2021
- text,
2022
- textIsSlackPlainText: true
2023
- });
2024
- continue;
2025
- }
2026
- const baseText = outsideText;
2027
- outsideText = "";
2028
- const text = buildSlackNativeDataAccessibilityText(baseText, segment.blocks) || buildSlackBlocksFallbackText(segment.blocks);
2029
- const authoredTextPlacement = baseText ? "outside-blocks" : params.authoredTextPlacement === "blocks" ? "blocks" : "none";
2030
- messages.push({
2031
- text,
2032
- blocks: segment.blocks,
2033
- authoredTextPlacement,
2034
- ...baseText ? { nativeDataFallbackBaseText: baseText } : {}
2035
- });
2036
- }
2037
- if (outsideText) messages.push({ text: outsideText });
2038
- return messages;
2039
- }
2040
- function resolveSlackReplyText(payload, text = payload.text) {
2041
- const presentation = normalizeMessagePresentation(payload.presentation);
2042
- return presentation ? renderSlackMessagePresentationFallbackText({
2043
- text,
2044
- presentation
2045
- }) : text ?? "";
1527
+ //#region extensions/slack/src/detached-target-admission.ts
1528
+ function assertSlackDetachedTargetAllowed(accountId, teamId) {
1529
+ const installationKind = getSlackInstallationKind(accountId);
1530
+ if (installationKind && installationKind !== "workspace" && !teamId) throw new Error("unsupported_enterprise_slack_delivery: detached Slack operations require team:<team-id>:channel:<channel-id> or team:<team-id>:user:<user-id> until a workspace install is authenticated");
2046
1531
  }
2047
- function resolveSlackReplyRenderPlan(payload, text = payload.text) {
2048
- const hasStructuredContent = hasSlackReplyStructuredContent(payload);
2049
- const resolution = resolveSlackReplyBlockResolution({
2050
- ...payload,
2051
- text
2052
- }, { materializeAuthoredText: hasStructuredContent });
2053
- const messages = resolveSlackReplyDeliveryMessages({
2054
- authoredTextPlacement: resolution.authoredTextPlacement,
2055
- segments: resolution.segments,
2056
- text
2057
- });
2058
- if (messages.length <= 1) {
2059
- const [message] = messages;
2060
- const sourceText = text?.trim() ?? "";
2061
- const blocks = message?.authoredTextPlacement === "blocks" ? addPreviewVerbatimToAuthoredTextBlocks(message.blocks, sourceText) : message?.blocks;
2062
- let renderedText = message?.text ?? resolveSlackReplyText(payload, text);
2063
- let textIsSlackMrkdwn = Boolean(message && !message.textIsSlackPlainText && (message.authoredTextPlacement !== "outside-blocks" || message.nativeDataFallbackBaseText));
2064
- if (blocks?.length && sourceText) {
2065
- if (hasSlackNativeDataBlock(blocks)) {
2066
- renderedText = appendSlackNativeDataFallbackText(sourceText, blocks) || renderedText;
2067
- textIsSlackMrkdwn = true;
2068
- } else if (message?.authoredTextPlacement === "blocks") {
2069
- renderedText = sourceText;
2070
- textIsSlackMrkdwn = false;
2071
- }
1532
+ //#endregion
1533
+ //#region extensions/slack/src/conversation-binding-route.ts
1534
+ const slackRouteBindingConfigCache = /* @__PURE__ */ new WeakMap();
1535
+ function slackTargetDefaultKindForPeer(kind) {
1536
+ return kind === "direct" ? "user" : "channel";
1537
+ }
1538
+ function slackTargetKindMatchesPeer(peerKind, targetKind) {
1539
+ if (targetKind === "user") return peerKind === "direct";
1540
+ return peerKind === "channel" || peerKind === "group";
1541
+ }
1542
+ function normalizeSlackRouteBindingPeer(peer) {
1543
+ const rawId = peer.id.trim();
1544
+ if (!rawId || rawId === "*") return peer;
1545
+ const target = (() => {
1546
+ try {
1547
+ return parseSlackTarget(rawId, { defaultKind: slackTargetDefaultKindForPeer(peer.kind) });
1548
+ } catch {
1549
+ return;
2072
1550
  }
2073
- return {
2074
- mode: "single",
2075
- text: renderedText,
2076
- ...blocks ? { blocks } : {},
2077
- ...textIsSlackMrkdwn ? { textIsSlackMrkdwn: true } : {}
2078
- };
2079
- }
2080
- const blockPart = messages.find((message) => message.blocks?.length);
2081
- return {
2082
- mode: "split",
2083
- fallbackText: messages.map((message) => message.text).filter(Boolean).join("\n\n"),
2084
- ...blockPart ? { blockPart: {
2085
- text: blockPart.text,
2086
- blocks: blockPart.blocks
2087
- } } : {}
1551
+ })();
1552
+ if (!target || !slackTargetKindMatchesPeer(peer.kind, target.kind)) return peer;
1553
+ const normalizedId = target.teamId ? `team:${target.teamId}:${target.kind}:${target.id}` : target.id;
1554
+ return normalizedId === peer.id ? peer : {
1555
+ ...peer,
1556
+ id: normalizedId
2088
1557
  };
2089
1558
  }
2090
- function readSlackChannelBlocks(payload) {
2091
- const slackData = payload.channelData?.slack;
2092
- if (!slackData || typeof slackData !== "object" || Array.isArray(slackData)) return [];
2093
- return parseSlackBlocksInput(slackData.blocks) ?? [];
2094
- }
2095
- function hasSlackReplyStructuredContent(payload) {
2096
- return Boolean(readSlackChannelBlocks(payload).length || normalizeMessagePresentation(payload.presentation) || payload.interactive?.blocks.length);
2097
- }
2098
- function renderSlackAuthoredTextFragments(blocks) {
2099
- return blocks.flatMap((block) => {
2100
- if (block.type === "actions") return [];
2101
- const text = renderSlackBlockFallbackText(block, { nativeDataFormat: "plain" });
2102
- return text ? [text] : [];
2103
- });
2104
- }
2105
- function buildSlackAuthoredTextBlocks(text) {
2106
- return markdownToSlackMrkdwnChunks(text, SLACK_SECTION_TEXT_MAX).map((chunk) => ({
2107
- type: "section",
2108
- text: {
2109
- type: "mrkdwn",
2110
- text: chunk,
2111
- verbatim: true
2112
- }
2113
- }));
2114
- }
2115
- function addPreviewVerbatimToAuthoredTextBlocks(blocks, sourceText) {
2116
- if (!blocks?.length || !sourceText) return blocks;
2117
- const authoredChunks = new Set(markdownToSlackMrkdwnChunks(sourceText, SLACK_SECTION_TEXT_MAX));
2118
- return blocks.map((block) => {
2119
- const text = block.text;
2120
- if (block.type !== "section" || text?.type !== "mrkdwn" || typeof text.text !== "string" || !authoredChunks.has(text.text)) return block;
1559
+ function normalizeSlackRouteBindingConfig(cfg) {
1560
+ const bindings = cfg.bindings;
1561
+ const cached = slackRouteBindingConfigCache.get(cfg);
1562
+ if (cached && cached.bindingsRef === bindings) return cached.normalizedCfg;
1563
+ if (!Array.isArray(bindings)) return cfg;
1564
+ let changed = false;
1565
+ const normalizedBindings = bindings.map((binding) => {
1566
+ if (binding.type === "acp" || binding.match.channel.trim().toLowerCase() !== "slack") return binding;
1567
+ const peer = binding.match.peer;
1568
+ if (!peer) return binding;
1569
+ const normalizedPeer = normalizeSlackRouteBindingPeer(peer);
1570
+ if (normalizedPeer === peer) return binding;
1571
+ changed = true;
2121
1572
  return {
2122
- ...block,
2123
- text: {
2124
- ...text,
2125
- verbatim: true
1573
+ ...binding,
1574
+ match: {
1575
+ ...binding.match,
1576
+ peer: normalizedPeer
2126
1577
  }
2127
1578
  };
2128
1579
  });
2129
- }
2130
- function readLastBlockSegment(segments) {
2131
- const last = segments.at(-1);
2132
- return last?.kind === "blocks" ? last.blocks : [];
2133
- }
2134
- function readAllNativeBlocks(segments) {
2135
- return segments.flatMap((segment) => segment.kind === "blocks" ? segment.blocks : []);
2136
- }
2137
- function appendTextSegment(segments, text) {
2138
- const trimmed = text.trim();
2139
- if (!trimmed) return;
2140
- const last = segments.at(-1);
2141
- if (last?.kind === "text") {
2142
- last.text = `${last.text}\n\n${trimmed}`;
2143
- return;
2144
- }
2145
- segments.push({
2146
- kind: "text",
2147
- text: trimmed,
2148
- mrkdwn: false
2149
- });
2150
- }
2151
- function appendBlockSegment(segments, blocks, startNew = false) {
2152
- let shouldStartNew = startNew;
2153
- for (const block of blocks) {
2154
- const last = segments.at(-1);
2155
- if (!shouldStartNew && last?.kind === "blocks" && last.blocks.length < 50) last.blocks.push(block);
2156
- else segments.push({
2157
- kind: "blocks",
2158
- blocks: [block]
2159
- });
2160
- shouldStartNew = false;
2161
- }
2162
- }
2163
- function resolvePresentationRenderOptions(segments, mode) {
2164
- const allOffsets = resolveSlackBlockOffsets(readAllNativeBlocks(segments));
2165
- return {
2166
- ...mode === "current" ? resolveSlackBlockOffsets(readLastBlockSegment(segments)) : {},
2167
- buttonIndexOffset: allOffsets.buttonIndexOffset,
2168
- selectIndexOffset: allOffsets.selectIndexOffset
2169
- };
2170
- }
2171
- function renderNativePresentation(presentation, options) {
2172
- if (!canRenderSlackPresentation(presentation, options)) return;
2173
- const blocks = buildSlackPresentationBlocks(presentation, options);
2174
- return blocks.length > 0 ? blocks : void 0;
2175
- }
2176
- function appendPresentationPart(segments, presentation) {
2177
- const currentBlocks = readLastBlockSegment(segments);
2178
- const currentRendered = renderNativePresentation(presentation, resolvePresentationRenderOptions(segments, "current"));
2179
- if (currentRendered && currentBlocks.length + currentRendered.length <= 50) {
2180
- appendBlockSegment(segments, currentRendered);
2181
- return;
2182
- }
2183
- const freshRendered = renderNativePresentation(presentation, resolvePresentationRenderOptions(segments, "new-message"));
2184
- if (freshRendered) {
2185
- appendBlockSegment(segments, freshRendered, true);
2186
- return;
2187
- }
2188
- appendTextSegment(segments, renderMessagePresentationFallbackText({ presentation }));
2189
- }
2190
- const SLACK_BUTTON_CONTROL_ACTION_IDS = [
2191
- SLACK_APPROVAL_BUTTON_ACTION_ID,
2192
- SLACK_CALLBACK_BUTTON_ACTION_ID,
2193
- SLACK_QUESTION_BUTTON_ACTION_ID,
2194
- SLACK_REPLY_BUTTON_ACTION_ID,
2195
- SLACK_REPLY_LINK_ACTION_ID
2196
- ];
2197
- const SLACK_SELECT_CONTROL_ACTION_IDS = [
2198
- SLACK_APPROVAL_SELECT_ACTION_ID,
2199
- SLACK_CALLBACK_SELECT_ACTION_ID,
2200
- SLACK_REPLY_SELECT_ACTION_ID
2201
- ];
2202
- function readGeneratedSlackControlRowKey(block) {
2203
- const record = block;
2204
- if (record.type !== "actions" || typeof record.block_id !== "string") return;
2205
- const expectedElementType = /^openclaw_reply_buttons_[1-9]\d*$/.test(record.block_id) ? "button" : /^openclaw_reply_select_[1-9]\d*$/.test(record.block_id) ? "static_select" : void 0;
2206
- if (!expectedElementType || !Array.isArray(record.elements) || record.elements.length === 0) return;
2207
- const actionIds = expectedElementType === "button" ? SLACK_BUTTON_CONTROL_ACTION_IDS : SLACK_SELECT_CONTROL_ACTION_IDS;
2208
- const elements = record.elements.map((element) => {
2209
- if (!element || typeof element !== "object" || Array.isArray(element)) return;
2210
- const { action_id: actionId, ...content } = element;
2211
- const actionFamily = typeof actionId === "string" ? actionIds.find((candidate) => actionId.startsWith(`${candidate}:`)) : void 0;
2212
- return actionFamily && content.type === expectedElementType ? [actionFamily, content] : void 0;
2213
- });
2214
- return elements.some((element) => element === void 0) ? void 0 : JSON.stringify(elements);
2215
- }
2216
- function subtractMirroredSlackControlRows(params) {
2217
- const remainingMirrors = /* @__PURE__ */ new Map();
2218
- for (const block of params.presentationBlocks) {
2219
- const key = readGeneratedSlackControlRowKey(block);
2220
- if (key) remainingMirrors.set(key, (remainingMirrors.get(key) ?? 0) + 1);
2221
- }
2222
- return params.interactiveBlocks.filter((block) => {
2223
- const key = readGeneratedSlackControlRowKey(block);
2224
- const remaining = key ? remainingMirrors.get(key) ?? 0 : 0;
2225
- if (!key || remaining === 0) return true;
2226
- remainingMirrors.set(key, remaining - 1);
2227
- return false;
1580
+ const normalizedCfg = changed ? {
1581
+ ...cfg,
1582
+ bindings: normalizedBindings
1583
+ } : cfg;
1584
+ slackRouteBindingConfigCache.set(cfg, {
1585
+ bindingsRef: bindings,
1586
+ normalizedCfg
2228
1587
  });
1588
+ return normalizedCfg;
2229
1589
  }
2230
- /**
2231
- * Resolve reply content into transport-order segments. Each blocks segment is
2232
- * one Slack message; text segments carry complete fallback content between it.
2233
- */
2234
- function resolveSlackReplyBlockResolution(payload, options = {}) {
2235
- const segments = [];
2236
- const channelBlocks = readSlackChannelBlocks(payload);
2237
- let compiledChannelBlocks = channelBlocks;
2238
- let authoredTextKnownInBlocks = false;
2239
- if (options.materializeAuthoredText) {
2240
- const rawTextFragments = renderSlackAuthoredTextFragments(channelBlocks);
2241
- const initialPlacement = resolveSlackAuthoredTextPlacement({
2242
- text: payload.text,
2243
- interactive: payload.interactive,
2244
- renderedTextFragments: rawTextFragments
2245
- });
2246
- authoredTextKnownInBlocks = initialPlacement === "blocks";
2247
- const text = normalizeOptionalString(payload.text);
2248
- if (text && initialPlacement === "outside-blocks") {
2249
- const textBlocks = buildSlackAuthoredTextBlocks(text);
2250
- if (resolveSlackAuthoredTextPlacement({
2251
- text: renderSlackAuthoredTextFragments(textBlocks).join(" "),
2252
- renderedTextFragments: rawTextFragments
2253
- }) !== "blocks") compiledChannelBlocks = [...channelBlocks, ...textBlocks];
2254
- authoredTextKnownInBlocks = true;
1590
+ function resolveSlackConversationBindingRoute(params) {
1591
+ const boundThreadRoute = params.bindingsEnabled && params.runtimeBindingThreadId ? resolveRuntimeConversationBindingRoute({
1592
+ route: params.route,
1593
+ touchBinding: params.touchBinding,
1594
+ conversation: {
1595
+ channel: "slack",
1596
+ accountId: params.accountId,
1597
+ conversationId: params.runtimeBindingThreadId,
1598
+ parentConversationId: params.baseConversationId
1599
+ }
1600
+ }) : null;
1601
+ const runtimeRoute = !params.bindingsEnabled ? {
1602
+ bindingOwnerAvailable: true,
1603
+ route: params.route,
1604
+ bindingRecord: null,
1605
+ boundSessionKey: void 0
1606
+ } : boundThreadRoute?.boundSessionKey || boundThreadRoute?.bindingRecord ? boundThreadRoute : resolveRuntimeConversationBindingRoute({
1607
+ route: params.route,
1608
+ touchBinding: params.touchBinding,
1609
+ conversation: {
1610
+ channel: "slack",
1611
+ accountId: params.accountId,
1612
+ conversationId: params.baseConversationId
2255
1613
  }
2256
- }
2257
- if (compiledChannelBlocks.length > 0) appendBlockSegment(segments, compiledChannelBlocks);
2258
- const presentation = normalizeMessagePresentation(payload.presentation);
2259
- const presentationBlockOffset = readAllNativeBlocks(segments).length;
2260
- if (presentation?.title) appendPresentationPart(segments, {
2261
- title: presentation.title,
2262
- blocks: []
2263
- });
2264
- for (const block of presentation?.blocks ?? []) appendPresentationPart(segments, { blocks: [block] });
2265
- const renderedPresentationBlocks = readAllNativeBlocks(segments).slice(presentationBlockOffset);
2266
- appendBlockSegment(segments, subtractMirroredSlackControlRows({
2267
- interactiveBlocks: buildSlackInteractiveBlocks(payload.interactive, resolveSlackBlockOffsets(readAllNativeBlocks(segments))),
2268
- presentationBlocks: renderedPresentationBlocks
2269
- }));
2270
- const renderedTextFragments = segments.flatMap((segment) => {
2271
- if (segment.kind === "text") return [segment.text];
2272
- return renderSlackAuthoredTextFragments(segment.blocks);
2273
- });
2274
- const authoredTextPlacement = resolveSlackAuthoredTextPlacement({
2275
- text: payload.text,
2276
- interactive: payload.interactive,
2277
- renderedTextFragments
2278
1614
  });
1615
+ const configuredRoute = params.bindingsEnabled && !runtimeRoute.boundSessionKey && !runtimeRoute.bindingRecord ? resolveConfiguredBindingRoute({
1616
+ cfg: params.cfg,
1617
+ route: params.route,
1618
+ conversation: {
1619
+ channel: "slack",
1620
+ accountId: params.accountId,
1621
+ conversationId: params.baseConversationId
1622
+ }
1623
+ }) : null;
2279
1624
  return {
2280
- authoredTextPlacement: authoredTextKnownInBlocks ? "blocks" : authoredTextPlacement,
2281
- segments
1625
+ runtimeRoute,
1626
+ configuredRoute,
1627
+ route: runtimeRoute.boundSessionKey ? runtimeRoute.route : configuredRoute?.route ?? params.route
2282
1628
  };
2283
1629
  }
2284
- /** Return the single-message native shape when no ordered text fallback is required. */
2285
- function resolveSlackReplyBlocks(payload) {
2286
- const { segments } = resolveSlackReplyBlockResolution(payload);
2287
- return segments.length === 1 && segments[0]?.kind === "blocks" ? segments[0].blocks : void 0;
2288
- }
2289
- //#endregion
2290
- //#region extensions/slack/src/detached-target-admission.ts
2291
- function assertSlackDetachedTargetAllowed(accountId, teamId) {
2292
- const installationKind = getSlackInstallationKind(accountId);
2293
- if (installationKind && installationKind !== "workspace" && !teamId) throw new Error("unsupported_enterprise_slack_delivery: detached Slack operations require team:<team-id>:channel:<channel-id> or team:<team-id>:user:<user-id> until a workspace install is authenticated");
2294
- }
2295
- //#endregion
2296
- //#region extensions/slack/src/monitor/allow-list.ts
2297
- const SLACK_SLUG_CACHE_MAX = 512;
2298
- const SLACK_STABLE_USER_ID_RE = /^[ubw][a-z0-9]+$/;
2299
- const slackSlugCache = /* @__PURE__ */ new Map();
2300
- function normalizeSlackSlug(raw) {
2301
- const key = raw ?? "";
2302
- const cached = slackSlugCache.get(key);
2303
- if (cached !== void 0) return cached;
2304
- const normalized = normalizeHyphenSlug(raw);
2305
- slackSlugCache.set(key, normalized);
2306
- if (slackSlugCache.size > SLACK_SLUG_CACHE_MAX) {
2307
- const oldest = slackSlugCache.keys().next();
2308
- if (!oldest.done) slackSlugCache.delete(oldest.value);
2309
- }
2310
- return normalized;
2311
- }
2312
- function normalizeAllowList(list) {
2313
- return normalizeStringEntries$1(list);
2314
- }
2315
- function normalizeAllowListLower(list) {
2316
- return normalizeStringEntriesLower(list);
2317
- }
2318
- function normalizeSlackAllowOwnerEntry(entry) {
2319
- const trimmed = normalizeOptionalLowercaseString(entry);
2320
- if (!trimmed || trimmed === "*") return;
2321
- try {
2322
- const target = parseSlackTarget(trimmed);
2323
- if (target?.kind === "user" && target.teamId) return target.id.toLowerCase();
2324
- } catch {
2325
- return;
2326
- }
2327
- const withoutPrefix = trimmed.replace(/^(slack:|user:)/, "");
2328
- return SLACK_STABLE_USER_ID_RE.test(withoutPrefix) ? withoutPrefix : void 0;
2329
- }
2330
- function resolveSlackAllowListMatch(params) {
2331
- const compiledAllowList = compileAllowlist(params.allowList);
2332
- const teamId = normalizeOptionalLowercaseString(params.teamId);
2333
- const id = normalizeOptionalLowercaseString(params.id);
2334
- const name = normalizeOptionalLowercaseString(params.name);
2335
- const slug = normalizeSlackSlug(name);
2336
- const scopedCandidates = [{
2337
- value: teamId && id ? `team:${teamId}:user:${id}` : void 0,
2338
- source: "workspace-id"
2339
- }];
2340
- const unscopedCandidates = [
2341
- {
2342
- value: id,
2343
- source: "id"
2344
- },
2345
- {
2346
- value: id ? `slack:${id}` : void 0,
2347
- source: "prefixed-id"
2348
- },
2349
- {
2350
- value: id ? `user:${id}` : void 0,
2351
- source: "prefixed-user"
2352
- },
2353
- ...params.allowNameMatching === true ? [
2354
- {
2355
- value: name,
2356
- source: "name"
2357
- },
2358
- {
2359
- value: name ? `slack:${name}` : void 0,
2360
- source: "prefixed-name"
2361
- },
2362
- {
2363
- value: slug,
2364
- source: "slug"
2365
- }
2366
- ] : []
2367
- ];
2368
- return resolveCompiledAllowlistMatch({
2369
- compiledAllowlist: compiledAllowList,
2370
- candidates: [...scopedCandidates, ...unscopedCandidates]
2371
- });
2372
- }
2373
- function allowListMatches(params) {
2374
- return resolveSlackAllowListMatch(params).allowed;
2375
- }
2376
- function resolveSlackUserAllowed(params) {
2377
- const allowList = normalizeAllowListLower(params.allowList);
2378
- if (allowList.length === 0) return true;
2379
- return allowListMatches({
2380
- allowList,
2381
- teamId: params.teamId,
2382
- id: params.userId,
2383
- name: params.userName,
2384
- allowNameMatching: params.allowNameMatching
2385
- });
2386
- }
2387
- function resolveSlackUserAllowListForTeam(params) {
2388
- const allowList = normalizeAllowListLower(params.allowList);
2389
- const teamId = normalizeOptionalLowercaseString(params.teamId);
2390
- return allowList.flatMap((entry) => {
2391
- if (entry === "*") return [entry];
2392
- if (!entry.startsWith("team:")) return [entry];
2393
- try {
2394
- const target = parseSlackTarget(entry);
2395
- if (target?.kind === "user" && target.teamId?.toLowerCase() === teamId) return [entry];
2396
- return params.preserveUnmatchedScopedEntries ? [entry] : [];
2397
- } catch {
2398
- return params.preserveUnmatchedScopedEntries ? [entry] : [];
2399
- }
2400
- });
2401
- }
2402
1630
  //#endregion
2403
1631
  //#region extensions/slack/src/monitor/workspace-routing.ts
2404
1632
  function resolveSlackEnterpriseMainDmSessionKey(params) {
@@ -2455,8 +1683,9 @@ function resolveSlackGroupRequireMention(params) {
2455
1683
  return resolveScopeRequireMention(resolveSlackGroupPolicyScope(params));
2456
1684
  }
2457
1685
  function resolveSlackGroupToolPolicy(params) {
1686
+ const scope = resolveSlackGroupPolicyScope(params);
2458
1687
  return resolveScopeToolsPolicy({
2459
- ...resolveSlackGroupPolicyScope(params),
1688
+ ...scope,
2460
1689
  senderPolicyMode: params.senderPolicyMode,
2461
1690
  senderId: params.senderId,
2462
1691
  senderName: params.senderName,
@@ -2465,4 +1694,46 @@ function resolveSlackGroupToolPolicy(params) {
2465
1694
  });
2466
1695
  }
2467
1696
  //#endregion
2468
- export { isSlackCallbackActionId as $, isSlackInvalidBlocksResponse as A, getSlackApprovalApprovers as At, SLACK_TEXT_LIMIT as B, SLACK_MALFORMED_NATIVE_DATA_FALLBACK as C, resolveTurnSourceSlackOriginTarget as Ct, createSlackNativeDataBaseTextConsumer as D, getSlackInstallationKind as Dt, buildSlackNativeDataAccessibilityText as E, slackTargetsMatch as Et, renderSlackBlockFallbackText as F, normalizeSlackApproverId as Ft, truncateSlackText as G, buildSlackPresentationBlocks as H, resolveSlackAuthoredTextPlacement as I, resolveSlackExecApprovalTarget as It, SLACK_REPLY_BUTTON_ACTION_ID as J, truncateSlackTextByUtf8Bytes as K, SLACK_EDIT_TEXT_MAX_BYTES as L, shouldSuppressLocalSlackExecApprovalPrompt as Lt, stripSlackNativeDataBlocks as M, getSlackExecApprovalApprovers as Mt, buildSlackBlocksFallbackText as N, isSlackExecApprovalAuthorizedSender as Nt, hasSlackNativeDataBlock as O, isSlackWorkspaceInstallation as Ot, buildSlackCompleteBlocksFallbackText as P, isSlackExecApprovalClientEnabled as Pt, isSlackApprovalActionId as Q, SLACK_MESSAGE_TEXT_HARD_LIMIT as R, SLACK_PRIVATE_ACTION_DELIVERY_RESULT as Rt, resolveSlackReplyRenderPlan as S, resolveSlackFallbackOriginTarget as St, appendSlackNativeDataPlainTextFallback as T, shouldHandleSlackPluginViaForwardingSession as Tt, canRenderSlackPresentation as U, buildSlackInteractiveBlocks as V, countSlackTextUtf8Bytes as W, SLACK_REPLY_SELECT_ACTION_ID as X, SLACK_REPLY_LINK_ACTION_ID as Y, SLACK_SESSION_LINK_ACTION_ID as Z, hasSlackReplyStructuredContent as _, isSlackAnyNativeApprovalClientEnabled as _t, qualifySlackConversationId as a, markdownToSlackMrkdwnChunks as at, resolveSlackReplyBlocks as b, resolveEnterpriseApprovalTeamId as bt, allowListMatches as c, escapeSlackMrkdwn as ct, normalizeSlackAllowOwnerEntry as d, SLACK_PRESENTATION_CAPABILITIES as dt, isSlackQuestionActionId as et, normalizeSlackSlug as f, SLACK_SECTION_TEXT_MAX as ft, assertSlackDetachedTargetAllowed as g, hasSlackPluginApprovers as gt, resolveSlackUserAllowed as h, validateSlackBlocksArray as ht, resolveSlackGroupToolPolicy as i, chunkSlackMrkdwnText as it, isSlackNativeResponseUrlRejection as j, isSlackApprovalAuthorizedSender as jt, isSlackInvalidBlocksError as k, registerSlackInstallationState as kt, normalizeAllowList as l, SLACK_APPROVAL_HEADER_BLOCK_ID as lt, resolveSlackUserAllowListForTeam as m, parseSlackBlocksInput as mt, buildSlackChannelPolicyScope as n, decodeSlackQuestionAction as nt, qualifySlackRoutePeerId as o, normalizeSlackOutboundText as ot, resolveSlackAllowListMatch as p, SLACK_MAX_BLOCKS as pt, SLACK_QUESTION_FINALIZATION_BLOCKS as q, resolveSlackGroupRequireMention as r, resolveSlackQuestionAction as rt, resolveSlackEnterpriseMainDmSessionKey as s, renderSlackMessagePresentationFallbackText as st, buildSlackChannelIdCandidates as t, resolveSlackQuestionActionIds as tt, normalizeAllowListLower as u, decodeSlackApprovalAction as ut, parseSlackReplyBlockSegments as v, normalizeSlackForwardTarget as vt, appendSlackNativeDataFallbackText as w, shouldHandleSlackNativeApprovalRequest as wt, resolveSlackReplyDeliveryMessages as x, resolveSessionSlackOriginTarget as xt, resolveSlackReplyBlockResolution as y, normalizeSlackOriginTarget as yt, SLACK_MESSAGE_TEXT_RECOMMENDED_LIMIT as z, resolveSlackAutoThreadId as zt };
1697
+ //#region extensions/slack/src/session-status.ts
1698
+ const MISSING_STOP_SUBSCRIPTION = "missing_agent_session_stopped_event_subscription";
1699
+ let warnedMissingStopSubscription = false;
1700
+ async function setSlackSessionStatus(params) {
1701
+ if (!params.threadTs) return { ok: false };
1702
+ const request = {
1703
+ channel_id: params.channelId,
1704
+ thread_ts: params.threadTs,
1705
+ status: params.status,
1706
+ ...params.token ? { token: params.token } : {},
1707
+ ...params.title !== void 0 ? { title: truncateUtf16Safe(params.title, 200) } : {}
1708
+ };
1709
+ try {
1710
+ const response = await params.client.apiCall("agents.sessions.setStatus", request);
1711
+ if (!warnedMissingStopSubscription && (response.warning === MISSING_STOP_SUBSCRIPTION || response.response_metadata?.warnings?.includes(MISSING_STOP_SUBSCRIPTION))) {
1712
+ warnedMissingStopSubscription = true;
1713
+ (params.runtime ?? defaultRuntime).log?.(warn("Slack's Stop button is unavailable until the app subscribes to agent_session_stopped. See https://docs.openclaw.ai/channels/slack#additional-manifest-settings"));
1714
+ }
1715
+ return response.ok ? {
1716
+ ok: true,
1717
+ title: response.title
1718
+ } : { ok: false };
1719
+ } catch (error) {
1720
+ logVerbose(`slack status update failed for channel ${params.channelId}: ${formatSlackError(error)}`);
1721
+ return { ok: false };
1722
+ }
1723
+ }
1724
+ async function renameSlackSession(params) {
1725
+ const request = {
1726
+ channel_id: params.channelId,
1727
+ thread_ts: params.threadTs,
1728
+ title: truncateUtf16Safe(params.title, 200),
1729
+ ...params.token ? { token: params.token } : {}
1730
+ };
1731
+ try {
1732
+ return (await params.client.apiCall("agents.sessions.rename", request)).ok;
1733
+ } catch (error) {
1734
+ logVerbose(`slack session rename failed for channel ${params.channelId}: ${formatSlackError(error)}`);
1735
+ return false;
1736
+ }
1737
+ }
1738
+ //#endregion
1739
+ export { SLACK_DATA_TABLE_AGGREGATE_CELL_CHARACTERS_MAX as $, countSlackTextUtf8Bytes as A, shouldSuppressLocalSlackExecApprovalPrompt as At, SLACK_REPLY_LINK_ACTION_ID as B, buildSlackBlocksFallbackText as C, getSlackApprovalApproversForTeam as Ct, SLACK_MESSAGE_TEXT_HARD_LIMIT as D, isSlackExecApprovalClientEnabled as Dt, SLACK_EDIT_TEXT_MAX_BYTES as E, isSlackExecApprovalAuthorizedSender as Et, SLACK_CALLBACK_BUTTON_ACTION_ID as F, isSlackQuestionActionId as G, SLACK_SESSION_LINK_ACTION_ID as H, SLACK_CALLBACK_SELECT_ACTION_ID as I, markdownToSlackMrkdwnChunks as J, resolveSlackQuestionActionIds as K, SLACK_QUESTION_BUTTON_ACTION_ID as L, truncateSlackTextByUtf8Bytes as M, resolveSlackAutoThreadId as Mt, SLACK_APPROVAL_BUTTON_ACTION_ID as N, resolveSlackReplyToMode as Nt, SLACK_MESSAGE_TEXT_RECOMMENDED_LIMIT as O, normalizeSlackApproverId as Ot, SLACK_APPROVAL_SELECT_ACTION_ID as P, hasSlackDataVisualizationBlock as Q, SLACK_QUESTION_FINALIZATION_BLOCKS as R, stripSlackNativeDataBlocks as S, registerSlackInstallationState as St, renderSlackBlockFallbackText as T, getSlackExecApprovalApprovers as Tt, isSlackApprovalActionId as U, SLACK_REPLY_SELECT_ACTION_ID as V, isSlackCallbackActionId as W, buildSlackDataVisualizationBlock as X, normalizeSlackOutboundText as Y, canRenderSlackDataVisualization as Z, createSlackNativeDataBaseTextConsumer as _, shouldHandleSlackNativeApprovalRequest as _t, resolveSlackGroupRequireMention as a, escapeSlackMrkdwn as at, isSlackInvalidBlocksResponse as b, getSlackInstallationKind as bt, qualifySlackRoutePeerId as c, validateSlackBlocksArray as ct, resolveSlackConversationBindingRoute as d, normalizeSlackForwardTarget as dt, buildSlackDataTableBlock as et, assertSlackDetachedTargetAllowed as f, normalizeSlackOriginTarget as ft, buildSlackNativeDataAccessibilityText as g, resolveTurnSourceSlackOriginTarget as gt, appendSlackNativeDataPlainTextFallback as h, resolveSlackFallbackOriginTarget as ht, buildSlackChannelPolicyScope as i, renderSlackMessagePresentationFallbackText as it, truncateSlackText as j, SLACK_PRIVATE_ACTION_DELIVERY_RESULT as jt, SLACK_TEXT_LIMIT as k, resolveSlackExecApprovalTarget as kt, resolveSlackEnterpriseMainDmSessionKey as l, hasSlackPluginApprovers as lt, appendSlackNativeDataFallbackText as m, resolveSessionSlackOriginTarget as mt, setSlackSessionStatus as n, countSlackDataTableCellCharacters as nt, resolveSlackGroupToolPolicy as o, SLACK_MAX_BLOCKS as ot, SLACK_MALFORMED_NATIVE_DATA_FALLBACK as p, resolveEnterpriseApprovalTeamId as pt, chunkSlackMrkdwnText as q, buildSlackChannelIdCandidates as r, renderSlackMessagePresentationChartFallbackText as rt, qualifySlackConversationId as s, parseSlackBlocksInput as st, renameSlackSession as t, countSlackDataTableBlocksCellCharacters as tt, normalizeSlackRouteBindingConfig as u, isSlackAnyNativeApprovalClientEnabled as ut, hasSlackNativeDataBlock as v, shouldHandleSlackPluginViaForwardingSession as vt, buildSlackCompleteBlocksFallbackText as w, isSlackApprovalAuthorizedSender as wt, isSlackNativeResponseUrlRejection as x, isSlackWorkspaceInstallation as xt, isSlackInvalidBlocksError as y, slackTargetsMatch as yt, SLACK_REPLY_BUTTON_ACTION_ID as z };