@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,12 +1,14 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
- import { a as resolveSlackAccount, f as resolveSlackBotToken } from "./accounts-Dm_H77gH.js";
3
- import { K as truncateSlackTextByUtf8Bytes, L as SLACK_EDIT_TEXT_MAX_BYTES, M as stripSlackNativeDataBlocks, N as buildSlackBlocksFallbackText, O as hasSlackNativeDataBlock, P as buildSlackCompleteBlocksFallbackText, Rt as SLACK_PRIVATE_ACTION_DELIVERY_RESULT, T as appendSlackNativeDataPlainTextFallback, W as countSlackTextUtf8Bytes, ct as escapeSlackMrkdwn, g as assertSlackDetachedTargetAllowed, ht as validateSlackBlocksArray, k as isSlackInvalidBlocksError, ot as normalizeSlackOutboundText, w as appendSlackNativeDataFallbackText } from "./group-policy-CU1tEGpS.js";
4
- import { d as getSlackWriteClient, i as createSlackLookupClient } from "./probe-4_aHtVT3.js";
5
- import { c as resolveSlackMessageText, i as hasSlackMessageTableBlock, n as fetchWithRuntimeDispatcher, r as saveRemoteMedia, t as isGovSlackClient } from "./slack-client-kind-hSfjXyUM.js";
6
- import { m as buildSlackNativeDataDeliveryPlan, r as sendMessageSlack } from "./send-DtjEnlLh.js";
2
+ import { a as resolveSlackAccount, f as resolveSlackBotToken, i as resolveDefaultSlackAccountId } from "./accounts-Dm_H77gH.js";
3
+ import { A as countSlackTextUtf8Bytes, C as buildSlackBlocksFallbackText, E as SLACK_EDIT_TEXT_MAX_BYTES, M as truncateSlackTextByUtf8Bytes, S as stripSlackNativeDataBlocks, T as renderSlackBlockFallbackText, Y as normalizeSlackOutboundText, at as escapeSlackMrkdwn, ct as validateSlackBlocksArray, f as assertSlackDetachedTargetAllowed, h as appendSlackNativeDataPlainTextFallback, jt as SLACK_PRIVATE_ACTION_DELIVERY_RESULT, m as appendSlackNativeDataFallbackText, v as hasSlackNativeDataBlock, w as buildSlackCompleteBlocksFallbackText, y as isSlackInvalidBlocksError } from "./session-status-BynwcZSO.js";
4
+ import { n as formatSlackTarget } from "./target-parsing-PbxGDbr9.js";
5
+ import { m as getSlackWriteClient, s as createSlackLookupClient } from "./probe-Biy--yhL.js";
6
+ import { m as buildSlackNativeDataDeliveryPlan, r as sendMessageSlack } from "./send-DElUcgIs.js";
7
7
  import { normalizeAccountId } from "openclaw/plugin-sdk/account-resolution";
8
8
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
9
- import { logVerbose, logVerbose as logVerbose$1 } from "openclaw/plugin-sdk/runtime-env";
9
+ import { createSubsystemLogger, logVerbose, logVerbose as logVerbose$1 } from "openclaw/plugin-sdk/runtime-env";
10
+ import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime";
11
+ import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
10
12
  import { z } from "zod";
11
13
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
12
14
  import fs from "node:fs/promises";
@@ -14,6 +16,31 @@ import { runTasksWithConcurrency } from "openclaw/plugin-sdk/concurrency-runtime
14
16
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
15
17
  import { normalizeHostname } from "openclaw/plugin-sdk/host-runtime";
16
18
  import { resolveRequestUrl } from "openclaw/plugin-sdk/request-url";
19
+ import { fetchWithRuntimeDispatcher } from "openclaw/plugin-sdk/runtime-fetch";
20
+ import { saveRemoteMedia } from "openclaw/plugin-sdk/media-runtime";
21
+ //#region extensions/slack/src/conversation-open.ts
22
+ function parseSlackConversationOpenInput(userIds, teamId) {
23
+ const users = z.array(z.string().trim().regex(/^[UW][A-Z0-9]+$/)).min(1).max(8).parse(userIds);
24
+ if (new Set(users).size !== users.length) throw new Error("Slack conversation-open requires distinct userIds.");
25
+ return {
26
+ users,
27
+ teamId: teamId === void 0 ? void 0 : z.string().trim().regex(/^T[A-Z0-9]+$/i).parse(teamId)
28
+ };
29
+ }
30
+ async function openSlackConversationWithClient(client, input) {
31
+ const channelId = (await client.conversations.open({ users: input.users.join(",") })).channel?.id?.trim();
32
+ if (!channelId || !/^[CDG][A-Z0-9]+$/i.test(channelId)) throw new Error("Slack conversations.open did not return a valid conversation ID.");
33
+ return {
34
+ channelId,
35
+ target: formatSlackTarget({
36
+ teamId: input.teamId,
37
+ kind: "channel",
38
+ id: channelId,
39
+ explicitKind: true
40
+ })
41
+ };
42
+ }
43
+ //#endregion
17
44
  //#region extensions/slack/src/edit-text.ts
18
45
  function buildSlackEditTextPayload(content, blocks) {
19
46
  const trimmedContent = content.trim();
@@ -21,12 +48,92 @@ function buildSlackEditTextPayload(content, blocks) {
21
48
  if (trimmedContent && !blockText) return trimmedContent;
22
49
  if (trimmedContent) {
23
50
  const nativePlainText = blocks?.length ? appendSlackNativeDataPlainTextFallback("", blocks).trim() : "";
24
- return truncateSlackTextByUtf8Bytes(Boolean(blockText && trimmedContent.includes(blockText)) || Boolean(nativePlainText && trimmedContent.includes(nativePlainText)) ? trimmedContent : blockText && !blockText.includes(trimmedContent) ? `${trimmedContent}\n\n${blockText}` : blockText || trimmedContent, SLACK_EDIT_TEXT_MAX_BYTES);
51
+ const fallbackText = Boolean(blockText && trimmedContent.includes(blockText)) || Boolean(nativePlainText && trimmedContent.includes(nativePlainText)) ? trimmedContent : blockText && !blockText.includes(trimmedContent) ? `${trimmedContent}\n\n${blockText}` : blockText || trimmedContent;
52
+ return truncateSlackTextByUtf8Bytes(fallbackText, SLACK_EDIT_TEXT_MAX_BYTES);
25
53
  }
26
54
  if (blockText) return truncateSlackTextByUtf8Bytes(blockText, SLACK_EDIT_TEXT_MAX_BYTES);
27
55
  return " ";
28
56
  }
29
57
  //#endregion
58
+ //#region extensions/slack/src/monitor/block-text.ts
59
+ function readSlackBlockType(block) {
60
+ return block && typeof block === "object" && !Array.isArray(block) ? block.type : void 0;
61
+ }
62
+ function hasSlackTableBlock(blocks) {
63
+ return blocks?.some((block) => readSlackBlockType(block) === "table") ?? false;
64
+ }
65
+ function hasSlackMessageTableBlock(message) {
66
+ return hasSlackTableBlock(message.blocks) || message.attachments?.some((attachment) => !isSlackUnfurlAttachment(attachment) && hasSlackTableBlock(attachment.blocks)) === true;
67
+ }
68
+ function isSlackUnfurlAttachment(attachment) {
69
+ return attachment.is_msg_unfurl === true || attachment.is_app_unfurl === true || typeof attachment.app_unfurl_url === "string" && attachment.app_unfurl_url.trim().length > 0;
70
+ }
71
+ function resolveSlackBlocksText(blocks) {
72
+ if (!blocks?.length) return;
73
+ const parts = [];
74
+ let hasRichText = false;
75
+ let hasNativeData = false;
76
+ for (const block of blocks) {
77
+ const blockType = readSlackBlockType(block);
78
+ hasRichText ||= blockType === "rich_text";
79
+ hasNativeData ||= blockType === "data_visualization" || blockType === "data_table" || blockType === "table";
80
+ const text = renderSlackBlockFallbackText(block, {
81
+ nativeDataFormat: "plain",
82
+ nativeReferenceFormat: "plain"
83
+ });
84
+ if (text) parts.push(text);
85
+ }
86
+ return parts.length > 0 ? {
87
+ text: parts.join("\n"),
88
+ hasRichText,
89
+ hasNativeData
90
+ } : void 0;
91
+ }
92
+ function appendDistinctSlackText(base, addition) {
93
+ if (!base) return addition;
94
+ const comparableBase = `\n${base.replace(/\r\n?/g, "\n")}\n`;
95
+ const comparableAddition = `\n${addition.replace(/\r\n?/g, "\n")}\n`;
96
+ if (comparableBase.includes(comparableAddition)) return base;
97
+ return `${base}\n${addition}`;
98
+ }
99
+ function resolveSlackAttachmentTableTexts(attachments) {
100
+ const seen = /* @__PURE__ */ new Set();
101
+ const texts = [];
102
+ for (const attachment of attachments ?? []) {
103
+ if (isSlackUnfurlAttachment(attachment)) continue;
104
+ for (const block of attachment.blocks ?? []) {
105
+ if (readSlackBlockType(block) !== "table") continue;
106
+ const text = renderSlackBlockFallbackText(block, { nativeDataFormat: "plain" });
107
+ if (!text || seen.has(text)) continue;
108
+ seen.add(text);
109
+ texts.push(text);
110
+ }
111
+ }
112
+ return texts;
113
+ }
114
+ /** Resolve agent-visible Slack text while admitting only native tables from attachments. */
115
+ function resolveSlackMessageText(message, options = {}) {
116
+ let resolved = chooseSlackPrimaryText({
117
+ messageText: options.preserveMessageTextWhitespace ? typeof message.text === "string" && message.text.trim().length > 0 ? message.text : void 0 : normalizeOptionalString(message.text),
118
+ blocksText: resolveSlackBlocksText(message.blocks)
119
+ });
120
+ for (const tableText of resolveSlackAttachmentTableTexts(message.attachments)) resolved = appendDistinctSlackText(resolved, tableText);
121
+ return resolved;
122
+ }
123
+ function chooseSlackPrimaryText(params) {
124
+ const { messageText, blocksText } = params;
125
+ if (!blocksText) return messageText;
126
+ if (!messageText) return blocksText.text;
127
+ if (blocksText.hasNativeData) {
128
+ const comparableMessageText = messageText.replace(/\s+/g, " ").trim();
129
+ const comparableBlocksText = blocksText.text.replace(/\s+/g, " ").trim();
130
+ if (comparableMessageText.includes(comparableBlocksText)) return messageText;
131
+ return comparableBlocksText.startsWith(comparableMessageText) ? blocksText.text : `${messageText}\n${blocksText.text}`;
132
+ }
133
+ if (blocksText.hasRichText && blocksText.text.length > messageText.length) return blocksText.text;
134
+ return blocksText.text.length > messageText.length && blocksText.text.startsWith(messageText) ? blocksText.text : messageText;
135
+ }
136
+ //#endregion
30
137
  //#region extensions/slack/src/file-reference.ts
31
138
  function formatSlackFileReference(file) {
32
139
  const name = normalizeOptionalString(file?.name) ?? "file";
@@ -45,6 +152,21 @@ function formatSlackFileReferenceList(files) {
45
152
  return files.map((file) => formatSlackFileReference(file)).join(", ");
46
153
  }
47
154
  //#endregion
155
+ //#region extensions/slack/src/monitor/media.runtime.ts
156
+ const slackMediaLog = createSubsystemLogger("gateway/channels/slack").child("media");
157
+ //#endregion
158
+ //#region extensions/slack/src/monitor/slack-client-kind.ts
159
+ /** Detects the isolated GovSlack API plane without consulting mutable config. */
160
+ function isGovSlackClient(client) {
161
+ if (!client?.slackApiUrl) return false;
162
+ try {
163
+ const apiUrl = new URL(client.slackApiUrl);
164
+ return apiUrl.protocol === "https:" && !apiUrl.port && normalizeHostname(apiUrl.hostname) === "slack-gov.com";
165
+ } catch {
166
+ return false;
167
+ }
168
+ }
169
+ //#endregion
48
170
  //#region extensions/slack/src/monitor/media.ts
49
171
  var media_exports = /* @__PURE__ */ __exportAll({
50
172
  SLACK_MEDIA_READ_IDLE_TIMEOUT_MS: () => SLACK_MEDIA_READ_IDLE_TIMEOUT_MS,
@@ -180,6 +302,10 @@ async function looksLikeHtmlFile(filePath) {
180
302
  }
181
303
  const MAX_SLACK_MEDIA_CONCURRENCY = 3;
182
304
  const MAX_SLACK_FORWARDED_ATTACHMENTS = 8;
305
+ const SLACK_MEDIA_LIMIT_REASON = `omitted: 8-file limit`;
306
+ function formatSlackMediaFailure(error) {
307
+ return redactToolPayloadText(formatErrorMessage(error)).replace(/\s+/g, " ").slice(0, 200);
308
+ }
183
309
  async function fetchFreshSlackFileUrl(params) {
184
310
  if (!params.file.id || !params.client) return null;
185
311
  try {
@@ -221,7 +347,7 @@ async function downloadSlackMediaFile(params) {
221
347
  if (!(fileMime === "text/html" || fileName.endsWith(".html") || fileName.endsWith(".htm"))) {
222
348
  if (normalizeOptionalLowercaseString(saved.contentType?.split(";")[0]) === "text/html" || await looksLikeHtmlFile(saved.path)) {
223
349
  await fs.rm(saved.path, { force: true }).catch(() => void 0);
224
- return null;
350
+ throw new Error("blocked: unexpected HTML content");
225
351
  }
226
352
  }
227
353
  const effectiveMime = resolveSlackMediaMimetype(params.file, saved.contentType);
@@ -259,6 +385,7 @@ async function resolveSlackMedia(params) {
259
385
  const govSlack = isGovSlackClient(params.client);
260
386
  const files = params.files ?? [];
261
387
  const limitedFiles = files.length > 8 ? files.slice(0, 8) : files;
388
+ for (const file of files.slice(8)) params.unavailableFiles?.set(file, SLACK_MEDIA_LIMIT_REASON);
262
389
  const refreshFileUrl = (file) => fetchFreshSlackFileUrl({
263
390
  file,
264
391
  client: params.client,
@@ -269,40 +396,30 @@ async function resolveSlackMedia(params) {
269
396
  const preloaded = params.preloadedMedia?.get(file);
270
397
  if (preloaded) return preloaded;
271
398
  const eventUrl = file.url_private_download ?? file.url_private;
272
- const url = eventUrl ?? await refreshFileUrl(file);
273
- if (!url) return null;
274
- const result = await downloadSlackMediaFile({
275
- file,
276
- url,
277
- token: params.token,
278
- maxBytes: params.maxBytes,
279
- readIdleTimeoutMs: params.readIdleTimeoutMs,
280
- totalTimeoutMs: params.totalTimeoutMs,
281
- abortSignal: params.abortSignal,
282
- govSlack
283
- }).catch(() => null);
284
- if (result || !eventUrl) return result;
285
- const freshUrl = await refreshFileUrl(file);
286
- if (!freshUrl) return null;
287
- return await downloadSlackMediaFile({
288
- file,
289
- url: freshUrl,
290
- token: params.token,
291
- maxBytes: params.maxBytes,
292
- readIdleTimeoutMs: params.readIdleTimeoutMs,
293
- totalTimeoutMs: params.totalTimeoutMs,
294
- abortSignal: params.abortSignal,
295
- govSlack
296
- }).catch(() => null);
399
+ let url = eventUrl ?? await refreshFileUrl(file);
400
+ let reason = "no private download URL";
401
+ for (let attempt = 0; url && attempt < 2; attempt += 1) {
402
+ try {
403
+ return await downloadSlackMediaFile({
404
+ ...params,
405
+ file,
406
+ url,
407
+ govSlack
408
+ });
409
+ } catch (error) {
410
+ reason = formatSlackMediaFailure(error);
411
+ }
412
+ url = attempt === 0 && eventUrl ? await refreshFileUrl(file) : null;
413
+ }
414
+ params.unavailableFiles?.set(file, reason);
415
+ slackMediaLog.warn(`slack: file ${formatSlackFileReference(file)} unavailable (${reason})`);
416
+ return null;
297
417
  }),
298
418
  limit: MAX_SLACK_MEDIA_CONCURRENCY,
299
419
  errorMode: "stop",
300
420
  throwOnError: true
301
421
  });
302
- const resolved = results.filter((result, index) => {
303
- if (result) params.resolvedFiles?.add(limitedFiles[index]);
304
- return result !== null;
305
- });
422
+ const resolved = results.filter((result) => result !== null);
306
423
  return resolved.length > 0 ? resolved : null;
307
424
  }
308
425
  /** Extracts text and media from forwarded-message attachments. Returns null when empty. */
@@ -317,7 +434,8 @@ async function resolveSlackAttachmentContent(params) {
317
434
  if (fileIds.has(fileId)) return false;
318
435
  fileIds.add(fileId);
319
436
  return true;
320
- }).slice(0, 8).map((file) => {
437
+ }).map((file, index) => {
438
+ if (index >= 8) return file;
321
439
  const fileId = normalizeOptionalString(file.id);
322
440
  const preloaded = fileId && candidates.find((candidate) => normalizeOptionalString(candidate.id) === fileId && params.preloadedMedia?.has(candidate));
323
441
  if (preloaded) return preloaded;
@@ -325,11 +443,11 @@ async function resolveSlackAttachmentContent(params) {
325
443
  const downloadable = candidates.find((candidate) => normalizeOptionalString(candidate.id) === fileId && (candidate.url_private_download || candidate.url_private));
326
444
  return downloadable ? Object.assign({}, file, downloadable) : file;
327
445
  });
328
- const pendingFiles = new Map(allFiles.map((file) => [normalizeOptionalString(file.id) ?? file, file]));
329
- const resolvedFiles = /* @__PURE__ */ new Set();
446
+ const pendingFiles = new Map(allFiles.slice(0, 8).map((file) => [normalizeOptionalString(file.id) ?? file, file]));
447
+ const unavailableFileReasons = /* @__PURE__ */ new Map();
330
448
  const resolveFiles = (files) => resolveSlackMedia({
331
449
  ...params,
332
- resolvedFiles,
450
+ unavailableFiles: unavailableFileReasons,
333
451
  files: files?.flatMap((file) => {
334
452
  const key = normalizeOptionalString(file.id) ?? file;
335
453
  const selected = pendingFiles.get(key);
@@ -340,7 +458,7 @@ async function resolveSlackAttachmentContent(params) {
340
458
  const directMediaPromise = resolveFiles(params.files);
341
459
  const textBlocks = [];
342
460
  const attachmentMedia = [];
343
- let unavailableImageCount = 0;
461
+ let unavailableMediaCount = 0;
344
462
  const govSlack = isGovSlackClient(params.client);
345
463
  for (const att of forwardedAttachments) {
346
464
  const text = att.text?.trim() || att.fallback?.trim();
@@ -371,19 +489,27 @@ async function resolveSlackAttachmentContent(params) {
371
489
  ...saved.fileName ? { fileName: saved.fileName } : {},
372
490
  placeholder: `[Forwarded image: ${label}]`
373
491
  });
374
- } catch {
375
- unavailableImageCount += 1;
492
+ } catch (error) {
493
+ unavailableMediaCount += 1;
494
+ slackMediaLog.warn(`slack: forwarded image unavailable (${formatSlackMediaFailure(error)})`);
376
495
  }
377
496
  attachmentMedia.push(...await resolveFiles(att.files) ?? []);
378
497
  }
379
498
  const allMedia = [...await directMediaPromise ?? [], ...attachmentMedia];
380
- const unavailableFiles = allFiles.filter((file) => !resolvedFiles.has(file));
499
+ const unavailableFiles = allFiles.flatMap((file, index) => {
500
+ const reason = index >= 8 ? SLACK_MEDIA_LIMIT_REASON : unavailableFileReasons.get(file);
501
+ return reason ? [{
502
+ ...file,
503
+ reason
504
+ }] : [];
505
+ });
506
+ unavailableMediaCount += unavailableFiles.length;
381
507
  const combinedText = textBlocks.join("\n\n");
382
- if (!combinedText && allMedia.length === 0 && allFiles.length === 0 && unavailableImageCount === 0) return null;
508
+ if (!combinedText && allMedia.length === 0 && allFiles.length === 0 && unavailableMediaCount === 0) return null;
383
509
  return {
384
510
  text: combinedText,
385
511
  media: allMedia,
386
- unavailableImageCount,
512
+ unavailableMediaCount,
387
513
  ...unavailableFiles.length > 0 ? { files: unavailableFiles } : {}
388
514
  };
389
515
  }
@@ -398,6 +524,7 @@ var actions_exports = /* @__PURE__ */ __exportAll({
398
524
  listSlackEmojis: () => listSlackEmojis,
399
525
  listSlackPins: () => listSlackPins,
400
526
  listSlackReactions: () => listSlackReactions,
527
+ openSlackConversation: () => openSlackConversation,
401
528
  pinSlackMessage: () => pinSlackMessage,
402
529
  reactSlackMessage: () => reactSlackMessage,
403
530
  readSlackMessages: () => readSlackMessages,
@@ -421,8 +548,9 @@ function resolveToken(explicit, accountId, cfg) {
421
548
  if (token) return token;
422
549
  }
423
550
  if (!cfg) throw new Error("Slack actions requires a resolved runtime config. Load and resolve config at the command or gateway boundary, then pass cfg through the runtime path.");
551
+ const resolvedCfg = requireRuntimeConfig(cfg, "Slack actions");
424
552
  const account = resolveSlackAccount({
425
- cfg: requireRuntimeConfig(cfg, "Slack actions"),
553
+ cfg: resolvedCfg,
426
554
  accountId
427
555
  });
428
556
  const token = resolveSlackBotToken(account.botToken ?? void 0);
@@ -509,10 +637,11 @@ function hasSlackPlatformError(err, code) {
509
637
  }
510
638
  async function getClient(opts = {}, mode = "read") {
511
639
  if (opts.client) return opts.client;
512
- assertSlackDetachedTargetAllowed(opts.cfg ? resolveSlackAccount({
640
+ const accountId = opts.cfg ? resolveSlackAccount({
513
641
  cfg: requireRuntimeConfig(opts.cfg, "Slack actions"),
514
642
  accountId: opts.accountId
515
- }).accountId : normalizeAccountId(opts.accountId), opts.teamId);
643
+ }).accountId : normalizeAccountId(opts.accountId);
644
+ assertSlackDetachedTargetAllowed(accountId, opts.teamId);
516
645
  const token = resolveToken(opts.token, opts.accountId, opts.cfg);
517
646
  if (mode === "write") return getSlackWriteClient(token, { teamId: opts.teamId });
518
647
  return createSlackLookupClient(token, { teamId: opts.teamId });
@@ -597,7 +726,13 @@ async function sendSlackMessage(to, content, opts) {
597
726
  });
598
727
  }
599
728
  async function editSlackMessage(channelId, messageId, content, opts = {}) {
600
- await editSlackRenderedMessage(channelId, messageId, normalizeSlackOutboundText(content), opts);
729
+ const accountId = opts.accountId ?? (opts.cfg ? resolveDefaultSlackAccountId(opts.cfg) : void 0);
730
+ const tableMode = resolveMarkdownTableMode({
731
+ cfg: opts.cfg,
732
+ channel: "slack",
733
+ accountId
734
+ });
735
+ await editSlackRenderedMessage(channelId, messageId, normalizeSlackOutboundText(content, { tableMode }), opts);
601
736
  }
602
737
  async function editSlackRenderedMessage(channelId, messageId, content, opts = {}) {
603
738
  const client = await getClient(opts, "write");
@@ -617,10 +752,12 @@ async function editSlackRenderedMessage(channelId, messageId, content, opts = {}
617
752
  } catch (error) {
618
753
  if (!hasSlackNativeDataBlock(blocks) || !isSlackInvalidBlocksError(error)) throw error;
619
754
  logVerbose("slack edit: native data block rejected, retrying with text fallback");
620
- const survivorText = buildSlackBlocksFallbackText(stripSlackNativeDataBlocks(blocks)) ?? "";
755
+ const survivorBlocks = stripSlackNativeDataBlocks(blocks);
756
+ const survivorText = buildSlackBlocksFallbackText(survivorBlocks) ?? "";
621
757
  const authoredEditText = content.trim();
758
+ const baseText = authoredEditText && !survivorText.includes(authoredEditText) ? authoredEditText : "";
622
759
  const fallbackPlan = buildSlackNativeDataDeliveryPlan({
623
- baseText: authoredEditText && !survivorText.includes(authoredEditText) ? authoredEditText : "",
760
+ baseText,
624
761
  blocks: blocks ?? []
625
762
  });
626
763
  if (fallbackPlan.fallbackMessages.length !== 1) throw new Error("Slack native chart or table edit fallback requires multiple messages. Send a new message instead.", { cause: error });
@@ -642,6 +779,13 @@ async function deleteSlackMessage(channelId, messageId, opts = {}) {
642
779
  ts: messageId
643
780
  });
644
781
  }
782
+ async function openSlackConversation(userIds, opts = {}) {
783
+ const input = parseSlackConversationOpenInput(userIds, opts.teamId);
784
+ return await openSlackConversationWithClient(await getClient({
785
+ ...opts,
786
+ teamId: input.teamId
787
+ }, "write"), input);
788
+ }
645
789
  async function resolveSlackConversationName(channelId, opts = {}) {
646
790
  return (await (await getClient(opts, "read")).conversations.info({ channel: channelId })).channel?.name?.trim() || void 0;
647
791
  }
@@ -786,4 +930,4 @@ async function downloadSlackFile(fileId, opts) {
786
930
  }))?.[0] ?? null;
787
931
  }
788
932
  //#endregion
789
- export { media_exports as _, editSlackRenderedMessage as a, formatSlackFileReferenceList as b, listSlackPins as c, reactSlackMessage as d, readSlackMessages as f, unpinSlackMessage as g, sendSlackMessage as h, editSlackMessage as i, listSlackReactions as l, removeSlackReaction as m, deleteSlackMessage as n, getSlackMemberInfo as o, removeOwnSlackReactions as p, downloadSlackFile as r, listSlackEmojis as s, actions_exports as t, pinSlackMessage as u, logVerbose$1 as v, buildSlackEditTextPayload as x, formatSlackFileReference as y };
933
+ export { hasSlackMessageTableBlock as C, resolveSlackMessageText as D, resolveSlackBlocksText as E, buildSlackEditTextPayload as O, formatSlackFileReferenceList as S, isSlackUnfurlAttachment as T, media_exports as _, editSlackRenderedMessage as a, saveRemoteMedia as b, listSlackPins as c, reactSlackMessage as d, readSlackMessages as f, unpinSlackMessage as g, sendSlackMessage as h, editSlackMessage as i, listSlackReactions as l, removeSlackReaction as m, deleteSlackMessage as n, getSlackMemberInfo as o, removeOwnSlackReactions as p, downloadSlackFile as r, listSlackEmojis as s, actions_exports as t, pinSlackMessage as u, logVerbose$1 as v, hasSlackTableBlock as w, formatSlackFileReference as x, isGovSlackClient as y };
package/dist/api.js CHANGED
@@ -1,18 +1,18 @@
1
1
  import { n as registerSlackHttpHandler, r as normalizeSlackWebhookPath, t as handleSlackHttpRequest } from "./registry-bbjH7IHX.js";
2
2
  import { a as resolveSlackAccount, i as resolveDefaultSlackAccountId, n as listSlackAccountIds, r as mergeSlackAccountConfig, t as listEnabledSlackAccounts } from "./accounts-Dm_H77gH.js";
3
- import { r as resolveSlackReplyToMode } from "./conversation-binding-route-DFVLUdYF.js";
4
- import { t as inspectSlackAccount } from "./account-inspect-CN2hBsim.js";
5
- import { a as parseSlackTarget, i as normalizeSlackMessagingTarget, o as resolveSlackChannelId, r as looksLikeSlackTargetId } from "./target-parsing-BnMD2ZqZ.js";
6
- import "./targets-Cx5W_n3W.js";
7
- import { H as buildSlackPresentationBlocks, V as buildSlackInteractiveBlocks, c as allowListMatches, d as normalizeSlackAllowOwnerEntry, f as normalizeSlackSlug, h as resolveSlackUserAllowed, ht as validateSlackBlocksArray, i as resolveSlackGroupToolPolicy, l as normalizeAllowList, mt as parseSlackBlocksInput, p as resolveSlackAllowListMatch, pt as SLACK_MAX_BLOCKS, r as resolveSlackGroupRequireMention, u as normalizeAllowListLower, zt as resolveSlackAutoThreadId } from "./group-policy-CU1tEGpS.js";
8
- import { n as buildSlackThreadingToolContext, t as slackPlugin } from "./channel-C_vnTgxo.js";
9
- import { n as extractSlackToolSend, r as listSlackMessageActions } from "./message-tool-api-CWosHSi5.js";
10
- import { _ as resolveSlackWriteClientOptions, c as createSlackWebClient, d as getSlackWriteClient, f as SLACK_DEFAULT_RETRY_OPTIONS, g as resolveSlackWebClientOptions, l as createSlackWriteClient, p as SLACK_WRITE_RETRY_OPTIONS, s as createSlackTokenCacheKey, t as probeSlack } from "./probe-4_aHtVT3.js";
11
- import { n as resolveSlackChannelType } from "./channel-type-j-2lalcC.js";
12
- import { t as slackSetupPlugin } from "./channel.setup-aal8tokM.js";
13
- import { c as listSlackPins, d as reactSlackMessage, f as readSlackMessages, g as unpinSlackMessage, h as sendSlackMessage, i as editSlackMessage, l as listSlackReactions, m as removeSlackReaction, n as deleteSlackMessage, o as getSlackMemberInfo, p as removeOwnSlackReactions, r as downloadSlackFile, s as listSlackEmojis, u as pinSlackMessage } from "./actions-XdQyjG_p.js";
14
- import { f as recordSlackThreadParticipation, l as hasSlackThreadParticipation, s as clearSlackThreadParticipationCache } from "./send-DtjEnlLh.js";
15
- import { n as listSlackDirectoryGroupsFromConfig, r as listSlackDirectoryPeersFromConfig } from "./directory-config-DqfQ42OV.js";
3
+ import { Mt as resolveSlackAutoThreadId, Nt as resolveSlackReplyToMode, a as resolveSlackGroupRequireMention, ct as validateSlackBlocksArray, o as resolveSlackGroupToolPolicy, ot as SLACK_MAX_BLOCKS, st as parseSlackBlocksInput } from "./session-status-BynwcZSO.js";
4
+ import { t as inspectSlackAccount } from "./account-inspect-C3sePHWa.js";
5
+ import { a as parseSlackTarget, i as normalizeSlackMessagingTarget, o as resolveSlackChannelId, r as looksLikeSlackTargetId } from "./target-parsing-PbxGDbr9.js";
6
+ import "./targets-7FexXJ62.js";
7
+ import { C as normalizeAllowListLower, E as resolveSlackAllowListMatch, O as resolveSlackUserAllowed, S as normalizeAllowList, T as normalizeSlackSlug, b as resolveSlackWriteClientOptions, d as createSlackWebClient, f as createSlackWriteClient, g as SLACK_WRITE_RETRY_OPTIONS, h as SLACK_DEFAULT_RETRY_OPTIONS, m as getSlackWriteClient, t as probeSlack, u as createSlackTokenCacheKey, w as normalizeSlackAllowOwnerEntry, x as allowListMatches, y as resolveSlackWebClientOptions } from "./probe-Biy--yhL.js";
8
+ import { n as buildSlackThreadingToolContext, t as slackPlugin } from "./channel-CmOuxbOD.js";
9
+ import { c as buildSlackInteractiveBlocks, l as buildSlackPresentationBlocks } from "./reply-blocks-PRkhyAN7.js";
10
+ import { n as extractSlackToolSend, r as listSlackMessageActions } from "./message-tool-api-uNEfQYGY.js";
11
+ import { n as resolveSlackChannelType } from "./channel-type-BWt2CRaI.js";
12
+ import { t as slackSetupPlugin } from "./channel.setup-D5z1ReaT.js";
13
+ import { c as listSlackPins, d as reactSlackMessage, f as readSlackMessages, g as unpinSlackMessage, h as sendSlackMessage, i as editSlackMessage, l as listSlackReactions, m as removeSlackReaction, n as deleteSlackMessage, o as getSlackMemberInfo, p as removeOwnSlackReactions, r as downloadSlackFile, s as listSlackEmojis, u as pinSlackMessage } from "./actions-CFkvEv1d.js";
14
+ import { f as recordSlackThreadParticipation, l as hasSlackThreadParticipation, s as clearSlackThreadParticipationCache } from "./send-DElUcgIs.js";
15
+ import { n as listSlackDirectoryGroupsFromConfig, r as listSlackDirectoryPeersFromConfig } from "./directory-config-Bmw4ykSy.js";
16
16
  import { t as collectSlackSecurityAuditFindings } from "./security-audit-B_yKr7MW.js";
17
- import { n as resolveSlackRuntimeGroupPolicy } from "./provider-D4nDgxCh.js";
17
+ import { n as resolveSlackRuntimeGroupPolicy } from "./provider-DoaUvAM4.js";
18
18
  export { SLACK_DEFAULT_RETRY_OPTIONS, SLACK_MAX_BLOCKS, SLACK_WRITE_RETRY_OPTIONS, allowListMatches, buildSlackInteractiveBlocks, buildSlackPresentationBlocks, buildSlackThreadingToolContext, clearSlackThreadParticipationCache, collectSlackSecurityAuditFindings, createSlackTokenCacheKey, createSlackWebClient, createSlackWriteClient, deleteSlackMessage, downloadSlackFile, editSlackMessage, extractSlackToolSend, getSlackMemberInfo, getSlackWriteClient, handleSlackHttpRequest, hasSlackThreadParticipation, inspectSlackAccount, listEnabledSlackAccounts, listSlackAccountIds, listSlackDirectoryGroupsFromConfig, listSlackDirectoryPeersFromConfig, listSlackEmojis, listSlackMessageActions, listSlackPins, listSlackReactions, looksLikeSlackTargetId, mergeSlackAccountConfig, normalizeAllowList, normalizeAllowListLower, normalizeSlackAllowOwnerEntry, normalizeSlackMessagingTarget, normalizeSlackSlug, normalizeSlackWebhookPath, parseSlackBlocksInput, parseSlackTarget, pinSlackMessage, probeSlack, reactSlackMessage, readSlackMessages, recordSlackThreadParticipation, registerSlackHttpHandler, removeOwnSlackReactions, removeSlackReaction, resolveDefaultSlackAccountId, resolveSlackAccount, resolveSlackAllowListMatch, resolveSlackAutoThreadId, resolveSlackChannelId, resolveSlackChannelType, resolveSlackGroupRequireMention, resolveSlackGroupToolPolicy, resolveSlackReplyToMode, resolveSlackRuntimeGroupPolicy, resolveSlackUserAllowed, resolveSlackWebClientOptions, resolveSlackWriteClientOptions, sendSlackMessage, slackPlugin, slackSetupPlugin, unpinSlackMessage, validateSlackBlocksArray };
@@ -1,6 +1,8 @@
1
- import { a as parseSlackTarget } from "./target-parsing-BnMD2ZqZ.js";
2
- import { Ft as normalizeSlackApproverId, K as truncateSlackTextByUtf8Bytes, L as SLACK_EDIT_TEXT_MAX_BYTES, _t as isSlackAnyNativeApprovalClientEnabled, b as resolveSlackReplyBlocks, lt as SLACK_APPROVAL_HEADER_BLOCK_ID, wt as shouldHandleSlackNativeApprovalRequest } from "./group-policy-CU1tEGpS.js";
3
- import { r as sendMessageSlack } from "./send-DtjEnlLh.js";
1
+ import { E as SLACK_EDIT_TEXT_MAX_BYTES, M as truncateSlackTextByUtf8Bytes, Ot as normalizeSlackApproverId, _t as shouldHandleSlackNativeApprovalRequest, n as setSlackSessionStatus, ut as isSlackAnyNativeApprovalClientEnabled } from "./session-status-BynwcZSO.js";
2
+ import { a as parseSlackTarget } from "./target-parsing-PbxGDbr9.js";
3
+ import { p as getSlackListenerWriteClient } from "./probe-Biy--yhL.js";
4
+ import { i as resolveSlackReplyBlocks, p as SLACK_APPROVAL_HEADER_BLOCK_ID } from "./reply-blocks-PRkhyAN7.js";
5
+ import { r as sendMessageSlack } from "./send-DElUcgIs.js";
4
6
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
7
  import { buildChannelApprovalNativeTargetKey } from "openclaw/plugin-sdk/approval-native-runtime";
6
8
  import { buildApprovalPresentationFromActionDescriptors } from "openclaw/plugin-sdk/approval-reply-runtime";
@@ -46,7 +48,7 @@ function buildSlackMetadataLines(metadata) {
46
48
  }
47
49
  function buildSlackMetadataContextElements(metadata) {
48
50
  const lines = buildSlackMetadataLines(metadata);
49
- const visibleLineCount = lines.length > SLACK_CONTEXT_ELEMENTS_MAX ? SLACK_CONTEXT_ELEMENTS_MAX - 1 : lines.length;
51
+ const visibleLineCount = lines.length > SLACK_CONTEXT_ELEMENTS_MAX ? 9 : lines.length;
50
52
  const elements = [];
51
53
  for (let index = 0; index < visibleLineCount; index += 1) {
52
54
  const line = lines[index];
@@ -93,14 +95,15 @@ function buildSlackPluginRequestBlocks(view) {
93
95
  function buildSlackApprovalPayload(input) {
94
96
  const { phase, view } = input;
95
97
  const isPlugin = view.approvalKind === "plugin";
96
- const approvalName = isPlugin ? "Plugin" : "Exec";
98
+ const isSystemAgent = view.approvalKind === "system-agent";
99
+ const approvalName = isPlugin ? "Plugin" : isSystemAgent ? "OpenClaw change" : "Exec";
97
100
  let heading;
98
101
  let description;
99
102
  if (phase === "pending") {
100
103
  heading = `*${approvalName} approval required*`;
101
- description = view.approvalKind === "plugin" ? resolveSlackPluginDescription(view) : "A command needs your approval.";
104
+ description = view.approvalKind === "plugin" ? resolveSlackPluginDescription(view) : isSystemAgent ? "An OpenClaw change needs your approval." : "A command needs your approval.";
102
105
  } else if (phase === "resolved") {
103
- heading = `*${approvalName} approval: ${resolveSlackApprovalDecisionLabel(view.decision)}*`;
106
+ heading = `*${approvalName} approval: ${isSystemAgent && view.terminalStatus === "cancelled" ? "Cancelled" : isSystemAgent && view.applicationStatus === "applied" ? "Applied" : isSystemAgent && view.applicationStatus === "not-applied" ? "Not applied" : resolveSlackApprovalDecisionLabel(view.decision)}*`;
104
107
  const resolvedBy = formatSlackApprover(view.resolvedBy);
105
108
  description = resolvedBy ? `Resolved by ${resolvedBy}.` : "Resolved.";
106
109
  } else {
@@ -108,7 +111,7 @@ function buildSlackApprovalPayload(input) {
108
111
  description = "This approval request expired before it was resolved.";
109
112
  }
110
113
  const metadata = isPlugin ? buildSlackPluginMetadata(view) : view.metadata;
111
- const bodyLabel = isPlugin ? "*Request*" : "*Command*";
114
+ const bodyLabel = isPlugin ? "*Request*" : isSystemAgent ? "*Change*" : "*Command*";
112
115
  const bodyText = isPlugin ? view.title : buildSlackCodeBlock(view.commandText);
113
116
  const text = [
114
117
  heading,
@@ -130,7 +133,7 @@ function buildSlackApprovalPayload(input) {
130
133
  type: "section",
131
134
  text: {
132
135
  type: "mrkdwn",
133
- text: `*Command*\n${buildSlackCodeBlock(truncateSlackMrkdwn(view.commandText, 2600))}`
136
+ text: `${bodyLabel}\n${buildSlackCodeBlock(truncateSlackMrkdwn(view.commandText, 2600))}`
134
137
  }
135
138
  }, ...phase === "pending" ? buildSlackMetadataContextBlocks(view.metadata) : []]];
136
139
  if (phase === "pending") blocks.push(...resolveSlackReplyBlocks({
@@ -155,7 +158,11 @@ async function updateMessage(params) {
155
158
  }
156
159
  }
157
160
  const slackApprovalNativeRuntime = createChannelApprovalNativeRuntimeAdapter({
158
- eventKinds: ["exec", "plugin"],
161
+ eventKinds: [
162
+ "exec",
163
+ "plugin",
164
+ "system-agent"
165
+ ],
159
166
  availability: {
160
167
  isConfigured: (params) => {
161
168
  const resolved = resolveHandlerContext(params);
@@ -217,7 +224,15 @@ const slackApprovalNativeRuntime = createChannelApprovalNativeRuntimeAdapter({
217
224
  if (!resolved) return null;
218
225
  const client = resolveApprovalClient(resolved.context, preparedTarget.teamId);
219
226
  const to = await resolveApprovalChannel(client, preparedTarget.to, preparedTarget.teamId);
220
- const eventScope = resolveApprovalEventScope(client, preparedTarget.teamId);
227
+ const eventScope = preparedTarget.teamId ? {
228
+ teamId: preparedTarget.teamId,
229
+ client,
230
+ writeClient: getSlackListenerWriteClient({
231
+ listenerClient: client,
232
+ teamId: preparedTarget.teamId,
233
+ clientOptions: resolved.context.app.webClientOptions
234
+ })
235
+ } : void 0;
221
236
  const message = await sendMessageSlack(to, pendingPayload.text, {
222
237
  cfg,
223
238
  accountId: resolved.accountId,
@@ -226,26 +241,39 @@ const slackApprovalNativeRuntime = createChannelApprovalNativeRuntimeAdapter({
226
241
  client,
227
242
  eventScope
228
243
  });
244
+ await setSlackSessionStatus({
245
+ client,
246
+ channelId: message.channelId,
247
+ threadTs: preparedTarget.threadTs,
248
+ status: "suspended"
249
+ });
229
250
  return {
230
251
  channelId: message.channelId,
231
252
  messageTs: message.messageId,
253
+ threadTs: preparedTarget.threadTs,
232
254
  teamId: preparedTarget.teamId
233
255
  };
234
256
  },
235
- updateEntry: async ({ cfg, accountId, context, entry, payload }) => {
257
+ updateEntry: async ({ cfg, accountId, context, entry, payload, phase }) => {
236
258
  const resolved = resolveHandlerContext({
237
259
  cfg,
238
260
  accountId,
239
261
  context
240
262
  });
241
263
  if (!resolved) return;
242
- const nextPayload = payload;
264
+ const client = resolveApprovalClient(resolved.context, entry.teamId);
243
265
  await updateMessage({
244
- client: resolveApprovalClient(resolved.context, entry.teamId),
266
+ client,
245
267
  channelId: entry.channelId,
246
268
  messageTs: entry.messageTs,
247
- text: nextPayload.text,
248
- blocks: nextPayload.blocks
269
+ text: payload.text,
270
+ blocks: payload.blocks
271
+ });
272
+ await setSlackSessionStatus({
273
+ client,
274
+ channelId: entry.channelId,
275
+ threadTs: entry.threadTs,
276
+ status: phase === "resolved" ? "processing" : "active"
249
277
  });
250
278
  }
251
279
  },
@@ -260,22 +288,16 @@ function resolveApprovalClient(context, teamId) {
260
288
  if (!client) throw new Error("Slack Enterprise Grid approval client is unavailable");
261
289
  return client;
262
290
  }
263
- function resolveApprovalEventScope(client, teamId) {
264
- if (!teamId) return;
265
- return {
266
- teamId,
267
- client
268
- };
269
- }
270
291
  async function resolveApprovalChannel(client, target, teamId) {
271
292
  if (!teamId) return target;
272
293
  const parsed = parseSlackTarget(target, { defaultKind: "channel" });
273
294
  if (!parsed) throw new Error("Slack approval delivery target is missing");
274
295
  if (parsed.kind === "channel") return `channel:${parsed.id}`;
275
- const channelId = normalizeOptionalString((await client.conversations.open({
296
+ const opened = await client.conversations.open({
276
297
  users: parsed.id,
277
298
  return_im: true
278
- })).channel?.id);
299
+ });
300
+ const channelId = normalizeOptionalString(opened.channel?.id);
279
301
  if (!channelId) throw new Error("Slack Enterprise Grid approval DM did not return a channel id");
280
302
  return `channel:${channelId}`;
281
303
  }