@openclaw/slack 2026.10.10 → 2026.11.5-beta.1
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.
- package/dist/.setup/{account-inspect-Bc7TVU8F.mjs → account-inspect-DgQgj0wk.mjs} +11 -8
- package/dist/.setup/{accounts-CEgdyqoW.mjs → accounts-EaPP0fFY.mjs} +10 -1
- package/dist/.setup/{action-runtime-DPoxYao5.mjs → action-runtime-CK9QZgQl.mjs} +38 -45
- package/dist/.setup/action-runtime.runtime-CpZktret.mjs +1 -0
- package/dist/.setup/{actions-PF2cP6to.mjs → actions-s3CcFO45.mjs} +89 -76
- package/dist/.setup/{approval-handler.runtime-BtRklemy.mjs → approval-handler.runtime-Ch7BecYK.mjs} +26 -30
- package/dist/.setup/{approval-message-updates-CpwbsEj6.mjs → approval-message-updates-CqcRoa28.mjs} +1 -1
- package/dist/.setup/{channel-B6bRmYmX.mjs → channel-CAvo_WNZ.mjs} +566 -338
- package/dist/.setup/{channel-type-Dh4vwh1Y.mjs → channel-type-9Lw6yLf2.mjs} +4 -8
- package/dist/.setup/{channel.setup-BBzT6uKA.mjs → channel.setup-BlgXNuCJ.mjs} +13 -13
- package/dist/.setup/{config-schema-Cz3uId-c.mjs → config-schema-Bx1tCZB0.mjs} +3 -7
- package/dist/.setup/cursor-pages-jVn3Vkfi.mjs +309 -0
- package/dist/.setup/{directory-config-BEPra1E2.mjs → directory-config-CGvKYg9F.mjs} +3 -3
- package/dist/.setup/{directory-live-BkUwPrS0.mjs → directory-live-DNdw97LA.mjs} +4 -4
- package/dist/.setup/{doctor-contract-HK8aedwU.mjs → doctor-contract-BB2KJ9Ni.mjs} +37 -45
- package/dist/.setup/{message-tool-api-DeiQJoRV.mjs → message-tool-api-B38BpEaE.mjs} +4 -3
- package/dist/.setup/{monitor-DsZUQtTM.mjs → monitor-BD86SotB.mjs} +3397 -3672
- package/dist/.setup/{outbound-adapter-C0R5jz6S.mjs → outbound-adapter-BH6iPWBj.mjs} +30 -36
- package/dist/.setup/paths-CQZI_5WY.mjs +7 -0
- package/dist/.setup/{pipeline.runtime-CIFVL0kf.mjs → pipeline.runtime-C4ELxaRU.mjs} +415 -534
- package/dist/.setup/{policy-Bp1FvE6m.mjs → policy-C6_DdlSf.mjs} +1 -2
- package/dist/.setup/{session-status-CHkEn3HM.mjs → probe-B_Ax1vS1.mjs} +985 -622
- package/dist/.setup/question-delivery-D3qAIwuv.mjs +38 -0
- package/dist/.setup/{relay-source-DFBDTA5Z.mjs → relay-source-a8jodKuO.mjs} +11 -4
- package/dist/.setup/{replies-7DRN46EL.mjs → replies-Bq7n0gfM.mjs} +34 -19
- package/dist/.setup/{reply-blocks-DOjiDr44.mjs → reply-blocks-DyYYRrP_.mjs} +200 -205
- package/dist/.setup/{resolve-channels-BNQ1KZXh.mjs → resolve-channels-CF2JME9q.mjs} +4 -7
- package/dist/.setup/{resolve-users-C4dC8Z7D.mjs → resolve-users-DDcCAjH1.mjs} +8 -16
- package/dist/.setup/{scopes-C1aeCBa6.mjs → scopes-CzHC_dwV.mjs} +2 -1
- package/dist/.setup/{secret-contract-D0UY2JWF.mjs → secret-contract-CqVqR7MP.mjs} +4 -2
- package/dist/.setup/{secret-contract-api-BhzGpUb0.mjs → secret-contract-api-DaCXZ47j.mjs} +1 -1
- package/dist/.setup/{send-C5uXkmZV.mjs → send-WJZ73VSf.mjs} +297 -333
- package/dist/.setup/{send-results-D-o6PgtS.mjs → send-results-Bm6V1ekL.mjs} +3 -4
- package/dist/.setup/setup-plugin-api-DJXIB8CF.mjs +2 -0
- package/dist/.setup/{setup-surface-CtM1pm-9.mjs → setup-surface-uIqP2B0Y.mjs} +5 -6
- package/dist/.setup/{slash-dispatch.runtime-Bnn9a5qB.mjs → slash-dispatch.runtime-Cwuwv7FL.mjs} +2 -2
- package/dist/.setup/{target-parsing-0pRZtahO.mjs → target-parsing-DwYAP3ts.mjs} +1 -2
- package/dist/.setup/{targets-BqXz7CpG.mjs → targets-eeOxA_b_.mjs} +1 -1
- package/dist/.source-module-urls/c5ffd0957b234c7c12090e7de4eca2482ef339b8561e078da55b734211b0ba58.json +1 -0
- package/dist/account-inspect-api.js +1 -1
- package/dist/api.js +16 -17
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/doctor-contract-api.js +1 -1
- package/dist/index.js +0 -6
- package/dist/message-tool-api.js +1 -1
- package/dist/runtime-api.js +10 -12
- package/dist/secret-contract-api.js +1 -1
- package/dist/setup-entry.js +2 -2
- package/dist/setup-plugin-api.js +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/package.json +2 -2
- package/node_modules/@slack/bolt/node_modules/@types/node/util.d.ts +6 -3
- package/node_modules/@slack/bolt/node_modules/body-parser/README.md +13 -17
- package/node_modules/@slack/bolt/node_modules/body-parser/lib/read.js +57 -74
- package/node_modules/@slack/bolt/node_modules/body-parser/lib/types/json.js +11 -11
- package/node_modules/@slack/bolt/node_modules/body-parser/lib/types/urlencoded.js +5 -5
- package/node_modules/@slack/bolt/node_modules/body-parser/lib/utils.js +15 -0
- package/node_modules/@slack/bolt/node_modules/body-parser/package.json +12 -7
- package/node_modules/@slack/bolt/node_modules/content-disposition/README.md +105 -35
- package/node_modules/@slack/bolt/node_modules/content-disposition/dist/index.d.ts +72 -0
- package/node_modules/@slack/bolt/node_modules/content-disposition/dist/index.js +388 -0
- package/node_modules/@slack/bolt/node_modules/content-disposition/dist/index.js.map +1 -0
- package/node_modules/@slack/bolt/node_modules/content-disposition/package.json +31 -19
- package/node_modules/@slack/bolt/node_modules/content-type/README.md +34 -57
- package/node_modules/@slack/bolt/node_modules/content-type/package.json +35 -25
- package/node_modules/@slack/bolt/node_modules/express/Readme.md +15 -9
- package/node_modules/@slack/bolt/node_modules/express/lib/application.js +2 -2
- package/node_modules/@slack/bolt/node_modules/express/lib/request.js +32 -19
- package/node_modules/@slack/bolt/node_modules/express/lib/response.js +27 -30
- package/node_modules/@slack/bolt/node_modules/express/package.json +10 -10
- package/node_modules/@slack/bolt/node_modules/qs/.github/THREAT_MODEL.md +4 -2
- package/node_modules/@slack/bolt/node_modules/qs/CHANGELOG.md +38 -14
- package/node_modules/@slack/bolt/node_modules/qs/README.md +31 -3
- package/node_modules/@slack/bolt/node_modules/qs/dist/qs.js +20 -20
- package/node_modules/@slack/bolt/node_modules/qs/lib/parse.js +94 -4
- package/node_modules/@slack/bolt/node_modules/qs/lib/stringify.js +4 -4
- package/node_modules/@slack/bolt/node_modules/qs/lib/utils.js +130 -6
- package/node_modules/@slack/bolt/node_modules/qs/package.json +4 -4
- package/node_modules/@slack/bolt/node_modules/qs/test/parse.js +174 -0
- package/node_modules/@slack/bolt/node_modules/qs/test/stringify.js +263 -33
- package/node_modules/@slack/bolt/node_modules/qs/test/utils.js +184 -0
- package/node_modules/@slack/web-api/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/package.json +2 -2
- package/node_modules/@slack/web-api/node_modules/@types/node/util.d.ts +6 -3
- package/node_modules/typebox/build/format/duration.d.mts +2 -2
- package/node_modules/typebox/build/format/duration.mjs +2 -2
- package/node_modules/typebox/build/format/idn_email.d.mts +1 -0
- package/node_modules/typebox/build/format/idn_email.mjs +1 -0
- package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -1
- package/node_modules/typebox/build/format/idn_hostname.mjs +1 -1
- package/node_modules/typebox/build/format/ipv4.d.mts +2 -2
- package/node_modules/typebox/build/format/ipv4.mjs +2 -2
- package/node_modules/typebox/build/format/ipv6.d.mts +1 -1
- package/node_modules/typebox/build/format/ipv6.mjs +1 -1
- package/node_modules/typebox/build/format/relative_json_pointer.d.mts +2 -2
- package/node_modules/typebox/build/format/relative_json_pointer.mjs +2 -2
- package/node_modules/typebox/build/schema/build.mjs +1 -0
- package/node_modules/typebox/build/schema/engine/_unique.d.mts +4 -2
- package/node_modules/typebox/build/schema/engine/_unique.mjs +7 -3
- package/node_modules/typebox/build/schema/engine/additionalItems.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/additionalProperties.d.mts +0 -3
- package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +15 -17
- package/node_modules/typebox/build/schema/engine/contains.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/index.d.mts +1 -0
- package/node_modules/typebox/build/schema/engine/index.mjs +1 -0
- package/node_modules/typebox/build/schema/engine/items.mjs +7 -4
- package/node_modules/typebox/build/schema/engine/maxContains.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/minContains.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/oneOf.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/patternProperties.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/unevaluatedItems.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/unevaluatedProperties.mjs +2 -2
- package/node_modules/typebox/build/system/hashing/hash.mjs +63 -34
- package/node_modules/typebox/build/system/locale/en_US.mjs +2 -1
- package/node_modules/typebox/build/system/memory/assign.d.mts +2 -8
- package/node_modules/typebox/build/system/memory/assign.mjs +13 -6
- package/node_modules/typebox/build/system/memory/clone.d.mts +4 -5
- package/node_modules/typebox/build/system/memory/clone.mjs +8 -96
- package/node_modules/typebox/build/system/memory/create.d.mts +1 -5
- package/node_modules/typebox/build/system/memory/create.mjs +18 -13
- package/node_modules/typebox/build/system/memory/deep.d.mts +2 -0
- package/node_modules/typebox/build/system/memory/deep.mjs +108 -0
- package/node_modules/typebox/build/system/memory/discard.d.mts +1 -1
- package/node_modules/typebox/build/system/memory/discard.mjs +9 -8
- package/node_modules/typebox/build/system/memory/finalize.d.mts +4 -0
- package/node_modules/typebox/build/system/memory/finalize.mjs +30 -0
- package/node_modules/typebox/build/system/memory/keys.d.mts +2 -0
- package/node_modules/typebox/build/system/memory/keys.mjs +8 -0
- package/node_modules/typebox/build/system/memory/update.d.mts +1 -4
- package/node_modules/typebox/build/system/memory/update.mjs +43 -16
- package/node_modules/typebox/build/type/engine/call/index.d.mts +1 -0
- package/node_modules/typebox/build/type/engine/call/index.mjs +1 -0
- package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +2 -24
- package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +2 -29
- package/node_modules/typebox/build/type/engine/evaluate/index.d.mts +1 -0
- package/node_modules/typebox/build/type/engine/evaluate/index.mjs +1 -0
- package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +5 -1
- package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +6 -1
- package/node_modules/typebox/build/type/engine/evaluate/property.d.mts +30 -0
- package/node_modules/typebox/build/type/engine/evaluate/property.mjs +29 -0
- package/node_modules/typebox/build/type/engine/immutable/instantiate_add.d.mts +1 -3
- package/node_modules/typebox/build/type/engine/immutable/instantiate_add.mjs +1 -4
- package/node_modules/typebox/build/type/engine/object/from_intersect.d.mts +2 -2
- package/node_modules/typebox/build/type/engine/object/from_intersect.mjs +2 -2
- package/node_modules/typebox/build/type/engine/object/from_union.d.mts +2 -2
- package/node_modules/typebox/build/type/engine/object/from_union.mjs +2 -2
- package/node_modules/typebox/build/type/engine/optional/instantiate_add.d.mts +1 -3
- package/node_modules/typebox/build/type/engine/optional/instantiate_add.mjs +1 -4
- package/node_modules/typebox/build/type/engine/readonly/instantiate_add.d.mts +1 -3
- package/node_modules/typebox/build/type/engine/readonly/instantiate_add.mjs +1 -4
- package/node_modules/typebox/build/type/engine/this/index.d.mts +1 -0
- package/node_modules/typebox/build/type/engine/this/index.mjs +1 -0
- package/node_modules/typebox/build/value/clone/clone.d.mts +4 -3
- package/node_modules/typebox/build/value/clone/clone.mjs +5 -7
- package/node_modules/typebox/build/value/repair/from_cyclic.d.mts +2 -0
- package/node_modules/typebox/build/value/repair/from_cyclic.mjs +6 -0
- package/node_modules/typebox/build/value/shared/union_priority_sort.mjs +25 -14
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +1 -1
- package/node_modules/undici/docs/docs/api/Interceptors.md +15 -2
- package/node_modules/undici/lib/dispatcher/client-h2.js +78 -3
- package/node_modules/undici/lib/dispatcher/pool-base.js +20 -3
- package/node_modules/undici/package.json +3 -3
- package/node_modules/undici/types/api.d.ts +1 -1
- package/node_modules/undici/types/utility.d.ts +4 -4
- package/node_modules/zod/package.json +1 -1
- package/node_modules/zod/src/v4/classic/tests/array.test.ts +27 -0
- package/node_modules/zod/src/v4/classic/tests/map.test.ts +7 -0
- package/node_modules/zod/src/v4/classic/tests/set.test.ts +6 -0
- package/node_modules/zod/src/v4/core/compile.ts +9 -0
- package/node_modules/zod/src/v4/core/schemas.ts +35 -1
- package/node_modules/zod/src/v4/core/util.ts +13 -0
- package/node_modules/zod/src/v4/core/versions.ts +1 -1
- package/node_modules/zod/v4/core/compile.cjs +8 -0
- package/node_modules/zod/v4/core/compile.js +8 -0
- package/node_modules/zod/v4/core/schemas.cjs +30 -0
- package/node_modules/zod/v4/core/schemas.d.cts +3 -1
- package/node_modules/zod/v4/core/schemas.d.ts +3 -1
- package/node_modules/zod/v4/core/schemas.js +30 -0
- package/node_modules/zod/v4/core/util.cjs +15 -0
- package/node_modules/zod/v4/core/util.d.cts +2 -0
- package/node_modules/zod/v4/core/util.d.ts +2 -0
- package/node_modules/zod/v4/core/util.js +14 -0
- package/node_modules/zod/v4/core/versions.cjs +1 -1
- package/node_modules/zod/v4/core/versions.js +1 -1
- package/openclaw.plugin.json +6 -30
- package/package.json +7 -7
- package/dist/.setup/action-runtime.runtime-DWKWaKgC.mjs +0 -1
- package/dist/.setup/http-GpAVKwSX.mjs +0 -2
- package/dist/.setup/plugin-routes-lXsy_bNV.mjs +0 -21
- package/dist/.setup/probe-C5VWf3sz.mjs +0 -686
- package/dist/.setup/registry-1G0f1zuZ.mjs +0 -34
- package/dist/.setup/setup-plugin-api-D7DoueEh.mjs +0 -2
- package/dist/.setup/shared-CohTHxM1.mjs +0 -255
- package/dist/.source-module-urls/562d694877afe9d9dcbb3b32a8883c16974738f2aa892c48d45572cdc9a62529.json +0 -1
- package/dist/http-routes-api.js +0 -1
- package/node_modules/@slack/bolt/node_modules/body-parser/node_modules/content-type/LICENSE +0 -22
- package/node_modules/@slack/bolt/node_modules/body-parser/node_modules/content-type/README.md +0 -71
- package/node_modules/@slack/bolt/node_modules/body-parser/node_modules/content-type/package.json +0 -52
- package/node_modules/@slack/bolt/node_modules/content-disposition/index.js +0 -536
- package/node_modules/@slack/bolt/node_modules/content-type/HISTORY.md +0 -29
- package/node_modules/@slack/bolt/node_modules/content-type/index.js +0 -225
- package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/LICENSE +0 -22
- package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/README.md +0 -71
- package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +0 -46
- package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.js +0 -176
- package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/dist/index.js.map +0 -1
- package/node_modules/@slack/bolt/node_modules/negotiator/node_modules/content-type/package.json +0 -52
- package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/LICENSE +0 -22
- package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/README.md +0 -71
- package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/dist/index.d.ts +0 -46
- package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/dist/index.js +0 -176
- package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/dist/index.js.map +0 -1
- package/node_modules/@slack/bolt/node_modules/type-is/node_modules/content-type/package.json +0 -52
- package/node_modules/typebox/build/system/memory/freeze.d.mts +0 -4
- package/node_modules/typebox/build/system/memory/freeze.mjs +0 -6
- /package/node_modules/@slack/bolt/node_modules/{body-parser/node_modules/content-type → content-type}/dist/index.d.ts +0 -0
- /package/node_modules/@slack/bolt/node_modules/{body-parser/node_modules/content-type → content-type}/dist/index.js +0 -0
- /package/node_modules/@slack/bolt/node_modules/{body-parser/node_modules/content-type → content-type}/dist/index.js.map +0 -0
|
@@ -1,30 +1,36 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-nwUcYXjP.mjs";
|
|
1
2
|
import { r as isSlackPluginAccountConfigured } from "./account-configured-35y0-Hso.mjs";
|
|
2
|
-
import { a as mergeSlackAccountConfig, c as resolveSlackAccountAllowFrom, i as listSlackAccountIds, o as resolveDefaultSlackAccountId, s as resolveSlackAccount } from "./accounts-
|
|
3
|
-
import { a as parseSlackTarget, n as formatSlackTarget, t as canonicalizeSlackApiTargetId } from "./target-parsing-
|
|
4
|
-
import { t as slackContextTargetsMatch } from "./targets-
|
|
5
|
-
import {
|
|
6
|
-
import { resolveGlobalMap } from "openclaw/plugin-sdk/global-singleton";
|
|
3
|
+
import { a as mergeSlackAccountConfig, c as resolveSlackAccountAllowFrom, i as listSlackAccountIds, m as formatSlackBotTokenIdentityWarning, o as resolveDefaultSlackAccountId, s as resolveSlackAccount } from "./accounts-EaPP0fFY.mjs";
|
|
4
|
+
import { a as parseSlackTarget, n as formatSlackTarget, t as canonicalizeSlackApiTargetId } from "./target-parsing-DwYAP3ts.mjs";
|
|
5
|
+
import { t as slackContextTargetsMatch } from "./targets-eeOxA_b_.mjs";
|
|
6
|
+
import { _ as createSlackProbeDispatcher, i as createSlackReadClient } from "./cursor-pages-jVn3Vkfi.mjs";
|
|
7
7
|
import { normalizeAccountId } from "openclaw/plugin-sdk/account-resolution";
|
|
8
|
-
import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeStringifiedOptionalString, readNonBlankString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
8
|
+
import { asOptionalRecord, isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringifiedOptionalString, readNonBlankString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
9
9
|
import { normalizeMessageChannel, resolveAgentRoute } from "openclaw/plugin-sdk/routing";
|
|
10
10
|
import { defaultRuntime, logVerbose, warn } from "openclaw/plugin-sdk/runtime-env";
|
|
11
11
|
import { FormatCapabilityProfile, chunkTextForOutbound, markdownToIR, renderMarkdownIRChunksWithinLimit, renderMarkdownWithMarkers } from "openclaw/plugin-sdk/text-chunking";
|
|
12
|
-
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
12
|
+
import { runChannelProbe, sliceUtf16Safe, truncateUtf16Safe, truncateUtf8Prefix } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
13
13
|
import { isSingleUseReplyToMode } from "openclaw/plugin-sdk/reply-reference";
|
|
14
14
|
import { buildChannelKeyCandidates } from "openclaw/plugin-sdk/channel-targets";
|
|
15
15
|
import { createNativeApprovalChannelRouteGatesAsync, doesApprovalRequestSelectChannelAccountAsync, resolveApprovalKind, resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
|
|
16
16
|
import { createChannelApprovalAuth, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
17
|
-
import { normalizeHyphenSlug, normalizeStringEntriesLower } from "openclaw/plugin-sdk/string-normalization-runtime";
|
|
17
|
+
import { normalizeHyphenSlug, normalizeStringEntriesLower, truncateWithMarker } from "openclaw/plugin-sdk/string-normalization-runtime";
|
|
18
18
|
import { createChannelExecApprovalProfileAsync, isChannelExecApprovalClientEnabledFromConfig, isChannelExecApprovalTargetRecipient, matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
|
|
19
|
+
import { resolveGlobalMap } from "openclaw/plugin-sdk/global-singleton";
|
|
20
|
+
import { compileAllowlist, resolveCompiledAllowlistMatch } from "openclaw/plugin-sdk/allow-from";
|
|
21
|
+
import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
|
|
19
22
|
import { channelRouteTargetsMatchExact, stringifyRouteThreadId } from "openclaw/plugin-sdk/channel-route";
|
|
20
23
|
import { normalizeMessagePresentation, renderMessagePresentationChartFallbackText, renderMessagePresentationFallbackText, renderMessagePresentationTableFallbackText } from "openclaw/plugin-sdk/interactive-runtime";
|
|
21
|
-
import { buildApprovalResolutionRef } from "openclaw/plugin-sdk/approval-reference-runtime";
|
|
22
24
|
import { eastAsianWidthType } from "get-east-asian-width";
|
|
23
25
|
import { resolveIntegerOption } from "openclaw/plugin-sdk/number-runtime";
|
|
26
|
+
import { isStringArray } from "openclaw/plugin-sdk/collection-input-runtime";
|
|
27
|
+
import { buildApprovalResolutionRef } from "openclaw/plugin-sdk/approval-reference-runtime";
|
|
24
28
|
import { questionGatewayRuntime } from "openclaw/plugin-sdk/question-gateway-runtime";
|
|
25
29
|
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
26
30
|
import { inspectRuntimeConversationBindingRoute, resolveConfiguredBindingRoute, resolveRuntimeConversationBindingRouteAsync } from "openclaw/plugin-sdk/conversation-binding-runtime";
|
|
31
|
+
import { redactSensitiveText } from "openclaw/plugin-sdk/logging-core";
|
|
27
32
|
import { resolveScopeRequireMention, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
|
|
33
|
+
import { defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
28
34
|
// extensions/slack/src/action-threading.ts
|
|
29
35
|
const SLACK_PRIVATE_ACTION_DELIVERY_RESULT = Symbol("slack.action.delivery-result");
|
|
30
36
|
function resolveSlackAutoThreadId(params) {
|
|
@@ -39,6 +45,16 @@ function resolveSlackAutoThreadId(params) {
|
|
|
39
45
|
if (isSingleUseReplyToMode(context.replyToMode ?? "off") && context.hasRepliedRef?.value) return;
|
|
40
46
|
return context.currentThreadTs;
|
|
41
47
|
}
|
|
48
|
+
function resolveSlackThreadTsFromContext(explicitThreadTs, targetChannel, context, opts) {
|
|
49
|
+
if (explicitThreadTs) return explicitThreadTs;
|
|
50
|
+
if (opts?.suppressImplicitThread) return;
|
|
51
|
+
const threadTs = resolveSlackAutoThreadId({
|
|
52
|
+
to: targetChannel,
|
|
53
|
+
toolContext: context
|
|
54
|
+
});
|
|
55
|
+
if (isSingleUseReplyToMode(context?.replyToMode ?? "off") && !context?.hasRepliedRef) return;
|
|
56
|
+
return threadTs;
|
|
57
|
+
}
|
|
42
58
|
// extensions/slack/src/approval-plugin-policy.ts
|
|
43
59
|
/** Returns undefined only when Slack plugin approvals retain legacy account authorization. */
|
|
44
60
|
function resolvePluginApprovalSlackApprovers(cfg, request) {
|
|
@@ -136,6 +152,103 @@ function getSlackInstallationTeamId(accountId) {
|
|
|
136
152
|
function isSlackWorkspaceInstallation(accountId) {
|
|
137
153
|
return getSlackInstallationKind(accountId) === "workspace";
|
|
138
154
|
}
|
|
155
|
+
// extensions/slack/src/monitor/allow-list.ts
|
|
156
|
+
const SLACK_SLUG_CACHE_MAX = 512;
|
|
157
|
+
const SLACK_STABLE_USER_ID_RE = /^[ubw][a-z0-9]+$/;
|
|
158
|
+
const slackSlugCache = /* @__PURE__ */ new Map();
|
|
159
|
+
function normalizeSlackSlug(raw) {
|
|
160
|
+
const key = raw ?? "";
|
|
161
|
+
const cached = slackSlugCache.get(key);
|
|
162
|
+
if (cached !== void 0) return cached;
|
|
163
|
+
const normalized = normalizeHyphenSlug(raw);
|
|
164
|
+
slackSlugCache.set(key, normalized);
|
|
165
|
+
pruneMapToMaxSize(slackSlugCache, SLACK_SLUG_CACHE_MAX);
|
|
166
|
+
return normalized;
|
|
167
|
+
}
|
|
168
|
+
function normalizeSlackAllowOwnerEntry(entry) {
|
|
169
|
+
const trimmed = normalizeOptionalLowercaseString(entry);
|
|
170
|
+
if (!trimmed || trimmed === "*") return;
|
|
171
|
+
try {
|
|
172
|
+
const target = parseSlackTarget(trimmed);
|
|
173
|
+
if (target?.kind === "user" && target.teamId) return target.id.toLowerCase();
|
|
174
|
+
} catch {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const withoutPrefix = trimmed.replace(/^(slack:|user:)/, "");
|
|
178
|
+
return SLACK_STABLE_USER_ID_RE.test(withoutPrefix) ? withoutPrefix : void 0;
|
|
179
|
+
}
|
|
180
|
+
function resolveSlackAllowListMatch(params) {
|
|
181
|
+
const compiledAllowList = compileAllowlist(params.allowList);
|
|
182
|
+
const teamId = normalizeOptionalLowercaseString(params.teamId);
|
|
183
|
+
const id = normalizeOptionalLowercaseString(params.id);
|
|
184
|
+
const name = normalizeOptionalLowercaseString(params.name);
|
|
185
|
+
const slug = normalizeSlackSlug(name);
|
|
186
|
+
const scopedCandidates = [{
|
|
187
|
+
value: teamId && id ? `team:${teamId}:user:${id}` : void 0,
|
|
188
|
+
source: "workspace-id"
|
|
189
|
+
}];
|
|
190
|
+
const unscopedCandidates = [
|
|
191
|
+
{
|
|
192
|
+
value: id,
|
|
193
|
+
source: "id"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
value: id ? `slack:${id}` : void 0,
|
|
197
|
+
source: "prefixed-id"
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
value: id ? `user:${id}` : void 0,
|
|
201
|
+
source: "prefixed-user"
|
|
202
|
+
},
|
|
203
|
+
...params.allowNameMatching === true ? [
|
|
204
|
+
{
|
|
205
|
+
value: name,
|
|
206
|
+
source: "name"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
value: name ? `slack:${name}` : void 0,
|
|
210
|
+
source: "prefixed-name"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
value: slug,
|
|
214
|
+
source: "slug"
|
|
215
|
+
}
|
|
216
|
+
] : []
|
|
217
|
+
];
|
|
218
|
+
return resolveCompiledAllowlistMatch({
|
|
219
|
+
compiledAllowlist: compiledAllowList,
|
|
220
|
+
candidates: [...scopedCandidates, ...unscopedCandidates]
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
function allowListMatches(params) {
|
|
224
|
+
return resolveSlackAllowListMatch(params).allowed;
|
|
225
|
+
}
|
|
226
|
+
function resolveSlackUserAllowed(params) {
|
|
227
|
+
const allowList = normalizeStringEntriesLower(params.allowList);
|
|
228
|
+
if (allowList.length === 0) return true;
|
|
229
|
+
return allowListMatches({
|
|
230
|
+
allowList,
|
|
231
|
+
teamId: params.teamId,
|
|
232
|
+
id: params.userId,
|
|
233
|
+
name: params.userName,
|
|
234
|
+
allowNameMatching: params.allowNameMatching
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function resolveSlackUserAllowListForTeam(params) {
|
|
238
|
+
const allowList = normalizeStringEntriesLower(params.allowList);
|
|
239
|
+
const teamId = normalizeOptionalLowercaseString(params.teamId);
|
|
240
|
+
return allowList.flatMap((entry) => {
|
|
241
|
+
if (entry === "*") return [entry];
|
|
242
|
+
if (!entry.startsWith("team:")) return [entry];
|
|
243
|
+
try {
|
|
244
|
+
const target = parseSlackTarget(entry);
|
|
245
|
+
if (target?.kind === "user" && target.teamId?.toLowerCase() === teamId) return [entry];
|
|
246
|
+
return params.preserveUnmatchedScopedEntries ? [entry] : [];
|
|
247
|
+
} catch {
|
|
248
|
+
return params.preserveUnmatchedScopedEntries ? [entry] : [];
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
139
252
|
// extensions/slack/src/approval-auth.ts
|
|
140
253
|
function resolveSlackApprovalInputs(params) {
|
|
141
254
|
const account = resolveSlackAccount(params).config;
|
|
@@ -414,199 +527,451 @@ async function shouldHandleSlackNativeApprovalRequest(params) {
|
|
|
414
527
|
approvalKind: "exec"
|
|
415
528
|
});
|
|
416
529
|
}
|
|
417
|
-
// extensions/slack/src/blocks-input.ts
|
|
418
|
-
const SLACK_MAX_BLOCKS = 50;
|
|
419
|
-
function parseBlocksJson(raw) {
|
|
420
|
-
try {
|
|
421
|
-
return JSON.parse(raw);
|
|
422
|
-
} catch {
|
|
423
|
-
throw new Error("blocks must be valid JSON");
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
function validateSlackBlocksArray(raw) {
|
|
427
|
-
if (!Array.isArray(raw)) throw new Error("blocks must be an array");
|
|
428
|
-
if (raw.length === 0) throw new Error("blocks must contain at least one block");
|
|
429
|
-
if (raw.length > 50) throw new Error(`blocks cannot exceed 50 items`);
|
|
430
|
-
for (const block of raw) {
|
|
431
|
-
if (!block || typeof block !== "object" || Array.isArray(block)) throw new Error("each block must be an object");
|
|
432
|
-
const type = block.type;
|
|
433
|
-
if (typeof type !== "string" || type.trim().length === 0) throw new Error("each block must include a non-empty string type");
|
|
434
|
-
}
|
|
435
|
-
return raw;
|
|
436
|
-
}
|
|
437
|
-
function parseSlackBlocksInput(raw) {
|
|
438
|
-
if (raw == null) return;
|
|
439
|
-
return validateSlackBlocksArray(typeof raw === "string" ? parseBlocksJson(raw) : raw);
|
|
440
|
-
}
|
|
441
|
-
const SLACK_BUTTON_VALUE_MAX = 2e3;
|
|
442
|
-
const SLACK_SECTION_TEXT_MAX = 3e3;
|
|
443
|
-
const SLACK_PRESENTATION_CAPABILITIES = {
|
|
444
|
-
supported: true,
|
|
445
|
-
buttons: true,
|
|
446
|
-
selects: true,
|
|
447
|
-
context: true,
|
|
448
|
-
divider: true,
|
|
449
|
-
charts: true,
|
|
450
|
-
tables: true,
|
|
451
|
-
limits: {
|
|
452
|
-
actions: {
|
|
453
|
-
maxActionsPerRow: 25,
|
|
454
|
-
maxValueBytes: SLACK_BUTTON_VALUE_MAX,
|
|
455
|
-
supportsStyles: true
|
|
456
|
-
},
|
|
457
|
-
selects: {
|
|
458
|
-
maxOptions: 100,
|
|
459
|
-
maxValueBytes: 150
|
|
460
|
-
},
|
|
461
|
-
text: {
|
|
462
|
-
encoding: "characters",
|
|
463
|
-
markdownDialect: "slack-mrkdwn",
|
|
464
|
-
supportsEdit: true
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
|
-
// extensions/slack/src/approval-actions.ts
|
|
469
|
-
const SLACK_APPROVAL_VALUE_PREFIX = "openclaw:approval:v1:";
|
|
470
|
-
const SLACK_APPROVAL_HEADER_BLOCK_ID = "openclaw_approval_header";
|
|
471
|
-
function isApprovalDecision(value) {
|
|
472
|
-
return value === "allow-once" || value === "allow-always" || value === "deny";
|
|
473
|
-
}
|
|
474
|
-
/** Encode portable approval facts without exposing a slash command to Slack callbacks. */
|
|
475
|
-
function encodeSlackApprovalAction(action) {
|
|
476
|
-
const encode = (approvalId) => `${SLACK_APPROVAL_VALUE_PREFIX}${JSON.stringify({
|
|
477
|
-
approvalId,
|
|
478
|
-
approvalKind: action.approvalKind,
|
|
479
|
-
decision: action.decision
|
|
480
|
-
})}`;
|
|
481
|
-
const exact = encode(action.approvalId);
|
|
482
|
-
return exact.length <= 2e3 ? exact : encode(buildApprovalResolutionRef({
|
|
483
|
-
approvalId: action.approvalId,
|
|
484
|
-
approvalKind: action.approvalKind
|
|
485
|
-
}));
|
|
486
|
-
}
|
|
487
|
-
/** Decode only the exact Slack-owned approval envelope. Malformed callbacks fail closed. */
|
|
488
|
-
function decodeSlackApprovalAction(value) {
|
|
489
|
-
if (typeof value !== "string" || !value.startsWith(SLACK_APPROVAL_VALUE_PREFIX)) return null;
|
|
490
|
-
try {
|
|
491
|
-
const decoded = JSON.parse(value.slice(21));
|
|
492
|
-
if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return null;
|
|
493
|
-
const record = decoded;
|
|
494
|
-
if (Object.keys(record).length !== 3 || typeof record.approvalId !== "string" || record.approvalId.length === 0 || record.approvalKind !== "exec" && record.approvalKind !== "plugin" && record.approvalKind !== "system-agent" || !isApprovalDecision(record.decision)) return null;
|
|
495
|
-
return {
|
|
496
|
-
type: "approval",
|
|
497
|
-
approvalId: record.approvalId,
|
|
498
|
-
approvalKind: record.approvalKind,
|
|
499
|
-
decision: record.decision
|
|
500
|
-
};
|
|
501
|
-
} catch {
|
|
502
|
-
return null;
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
530
|
// extensions/slack/src/monitor/mrkdwn.ts
|
|
506
531
|
function escapeSlackMrkdwn(value) {
|
|
507
532
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
508
533
|
}
|
|
509
|
-
// extensions/slack/src/
|
|
510
|
-
const
|
|
511
|
-
function
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
if (
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
if (escaped.yLabel) escaped.yLabel = escapeSlackMrkdwn(escaped.yLabel);
|
|
534
|
+
// extensions/slack/src/format.ts
|
|
535
|
+
const SLACK_ANGLE_TOKEN_RE = /<[^>\n]+>/g;
|
|
536
|
+
function isAllowedSlackAngleToken(token) {
|
|
537
|
+
const inner = token.slice(1, -1);
|
|
538
|
+
return inner.startsWith("@") || inner.startsWith("#") || inner.startsWith("!") || inner.startsWith("mailto:") || inner.startsWith("tel:") || inner.startsWith("http://") || inner.startsWith("https://") || inner.startsWith("slack://");
|
|
539
|
+
}
|
|
540
|
+
function escapeSlackMrkdwnContent(text, mentions) {
|
|
541
|
+
if (mentions === "escape") return escapeSlackMrkdwn(text);
|
|
542
|
+
if (!text.includes("&") && !text.includes("<") && !text.includes(">")) return text;
|
|
543
|
+
const out = [];
|
|
544
|
+
let lastIndex = 0;
|
|
545
|
+
for (const match of text.matchAll(SLACK_ANGLE_TOKEN_RE)) {
|
|
546
|
+
const matchIndex = match.index;
|
|
547
|
+
out.push(escapeSlackMrkdwn(text.slice(lastIndex, matchIndex)));
|
|
548
|
+
const token = match[0];
|
|
549
|
+
const escapeControl = mentions === "escape-controls" && /^<[@#!]/u.test(token);
|
|
550
|
+
out.push(isAllowedSlackAngleToken(token) && !escapeControl ? token : escapeSlackMrkdwn(token));
|
|
551
|
+
lastIndex = matchIndex + token.length;
|
|
528
552
|
}
|
|
529
|
-
|
|
553
|
+
out.push(escapeSlackMrkdwn(text.slice(lastIndex)));
|
|
554
|
+
return out.join("");
|
|
555
|
+
}
|
|
556
|
+
function escapeSlackMrkdwnText(text, mentions) {
|
|
557
|
+
if (!text.includes("&") && !text.includes("<") && !text.includes(">")) return text;
|
|
558
|
+
return text.split("\n").map((line) => {
|
|
559
|
+
if (line.startsWith("> ")) return `> ${escapeSlackMrkdwnContent(line.slice(2), mentions)}`;
|
|
560
|
+
return escapeSlackMrkdwnContent(line, mentions);
|
|
561
|
+
}).join("\n");
|
|
530
562
|
}
|
|
531
|
-
function
|
|
563
|
+
function buildSlackLink(link, text) {
|
|
564
|
+
const href = link.href.trim();
|
|
565
|
+
if (!href || /^[@#!]/u.test(href)) return null;
|
|
566
|
+
const trimmedLabel = text.slice(link.start, link.end).trim();
|
|
567
|
+
const comparableHref = href.startsWith("mailto:") ? href.slice(7) : href;
|
|
568
|
+
if (!(trimmedLabel.length > 0 && trimmedLabel !== href && trimmedLabel !== comparableHref)) return null;
|
|
569
|
+
const safeHref = escapeSlackMrkdwn(href);
|
|
532
570
|
return {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
571
|
+
start: link.start,
|
|
572
|
+
end: link.end,
|
|
573
|
+
open: `<${safeHref}|`,
|
|
574
|
+
close: ">"
|
|
537
575
|
};
|
|
538
576
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
...button.webApp ? { webApp: { url: escapeSlackMrkdwn(button.webApp.url) } } : {},
|
|
553
|
-
...button.web_app ? { web_app: { url: escapeSlackMrkdwn(button.web_app.url) } } : {},
|
|
554
|
-
...commandAction ? { action: {
|
|
555
|
-
...commandAction,
|
|
556
|
-
command: commandAction.command.replaceAll("`", "[backtick]")
|
|
557
|
-
} } : {}
|
|
558
|
-
};
|
|
559
|
-
})
|
|
560
|
-
};
|
|
561
|
-
if (block.type === "select") return {
|
|
562
|
-
...block,
|
|
563
|
-
...block.placeholder ? { placeholder: escapeSlackMrkdwn(block.placeholder) } : {},
|
|
564
|
-
options: block.options.map((option) => ({
|
|
565
|
-
...option,
|
|
566
|
-
label: escapeSlackMrkdwn(option.label)
|
|
567
|
-
}))
|
|
568
|
-
};
|
|
569
|
-
return block;
|
|
577
|
+
const SLACK_MRKDWN_WORD_CHARACTER_RE = /[\p{L}\p{M}\p{N}_]/u;
|
|
578
|
+
const SLACK_MRKDWN_PUNCTUATION_RE = /\p{P}/u;
|
|
579
|
+
const SLACK_MRKDWN_SYMBOL_RE = /\p{S}/u;
|
|
580
|
+
const SLACK_MRKDWN_CJK_SCRIPT_RE = /[\p{Script_Extensions=Han}\p{Script_Extensions=Hiragana}\p{Script_Extensions=Katakana}\p{Script_Extensions=Hangul}]/u;
|
|
581
|
+
const SLACK_MRKDWN_EMOJI_PRESENTATION_RE = /\p{Emoji_Presentation}/u;
|
|
582
|
+
function getCodePointBefore(text, index) {
|
|
583
|
+
if (index <= 0) return "";
|
|
584
|
+
const lastCodeUnit = text.charCodeAt(index - 1);
|
|
585
|
+
if (lastCodeUnit >= 56320 && lastCodeUnit <= 57343 && index > 1) {
|
|
586
|
+
const previousCodeUnit = text.charCodeAt(index - 2);
|
|
587
|
+
if (previousCodeUnit >= 55296 && previousCodeUnit <= 56319) return text.slice(index - 2, index);
|
|
588
|
+
}
|
|
589
|
+
return text[index - 1] ?? "";
|
|
570
590
|
}
|
|
571
|
-
function
|
|
572
|
-
|
|
591
|
+
function getCodePointAt(text, index) {
|
|
592
|
+
const codePoint = text.codePointAt(index);
|
|
593
|
+
return codePoint === void 0 ? "" : String.fromCodePoint(codePoint);
|
|
573
594
|
}
|
|
574
|
-
function
|
|
575
|
-
|
|
595
|
+
function isSlackCjkPunctuation(character) {
|
|
596
|
+
if (!SLACK_MRKDWN_PUNCTUATION_RE.test(character)) return false;
|
|
597
|
+
if (SLACK_MRKDWN_CJK_SCRIPT_RE.test(character)) return true;
|
|
598
|
+
const codePoint = character.codePointAt(0);
|
|
599
|
+
if (codePoint === void 0) return false;
|
|
600
|
+
const width = eastAsianWidthType(codePoint);
|
|
601
|
+
return width === "fullwidth" || width === "halfwidth" || width === "wide" && !SLACK_MRKDWN_EMOJI_PRESENTATION_RE.test(character);
|
|
576
602
|
}
|
|
577
|
-
function
|
|
578
|
-
if (
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
blocks: params.presentation.blocks.map(escapeSlackPresentationFallbackBlock)
|
|
583
|
-
};
|
|
584
|
-
return renderMessagePresentationFallbackText({
|
|
585
|
-
...params,
|
|
586
|
-
presentation
|
|
587
|
-
});
|
|
603
|
+
function isUnsafeSlackEmphasisBoundary(character) {
|
|
604
|
+
if (SLACK_MRKDWN_WORD_CHARACTER_RE.test(character)) return true;
|
|
605
|
+
const codePoint = character.codePointAt(0);
|
|
606
|
+
if (codePoint === void 0 || codePoint <= 127) return false;
|
|
607
|
+
return SLACK_MRKDWN_SYMBOL_RE.test(character) || isSlackCjkPunctuation(character);
|
|
588
608
|
}
|
|
589
|
-
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
if (!Array.isArray(value)) return "";
|
|
598
|
-
const table = mode === "table";
|
|
599
|
-
const read = table ? readNonBlankString : normalizeOptionalString;
|
|
600
|
-
const literal = table ? (text) => text : escapeSlackMrkdwn;
|
|
601
|
-
const reference = mode === "escaped" ? escapeSlackMrkdwn : (text) => text;
|
|
602
|
-
const formatReference = (raw, prefix, suffix) => {
|
|
603
|
-
const text = read(raw);
|
|
604
|
-
return text ? reference(`${prefix}${text}${suffix}`) : "";
|
|
609
|
+
function makeSlackEmphasisStylesSafe(ir) {
|
|
610
|
+
const styles = ir.styles.filter((span) => {
|
|
611
|
+
if (span.style !== "italic" && span.style !== "bold") return true;
|
|
612
|
+
return !isUnsafeSlackEmphasisBoundary(getCodePointBefore(ir.text, span.start)) && !isUnsafeSlackEmphasisBoundary(getCodePointAt(ir.text, span.end));
|
|
613
|
+
});
|
|
614
|
+
return styles.length === ir.styles.length ? ir : {
|
|
615
|
+
...ir,
|
|
616
|
+
styles
|
|
605
617
|
};
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
618
|
+
}
|
|
619
|
+
const SLACK_FORMAT_PROFILE = FormatCapabilityProfile.define({
|
|
620
|
+
mechanism: "markdown",
|
|
621
|
+
constructs: {
|
|
622
|
+
underline: "strip",
|
|
623
|
+
spoiler: "fallback",
|
|
624
|
+
codeLanguage: "fallback",
|
|
625
|
+
heading: "fallback",
|
|
626
|
+
bulletList: "fallback",
|
|
627
|
+
orderedList: "fallback",
|
|
628
|
+
taskList: "fallback",
|
|
629
|
+
table: "fallback",
|
|
630
|
+
image: "fallback"
|
|
631
|
+
},
|
|
632
|
+
chunk: {
|
|
633
|
+
limit: 4e3,
|
|
634
|
+
unit: "chars",
|
|
635
|
+
hardCap: 4e4
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
const SLACK_ASSISTANT_TRANSCRIPT_PREFIX = "`Assistant:` ";
|
|
639
|
+
function tokenizeSlackMrkdwn(text, graphemes) {
|
|
640
|
+
const tokens = [];
|
|
641
|
+
for (let index = 0; index < text.length;) {
|
|
642
|
+
if (text.startsWith("```", index)) {
|
|
643
|
+
tokens.push("```");
|
|
644
|
+
index += 3;
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
const entity = text[index] === "&" ? [
|
|
648
|
+
"&",
|
|
649
|
+
"<",
|
|
650
|
+
">"
|
|
651
|
+
].find((candidate) => text.startsWith(candidate, index)) : void 0;
|
|
652
|
+
if (entity) {
|
|
653
|
+
tokens.push(entity);
|
|
654
|
+
index += entity.length;
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
if (text[index] === "<") {
|
|
658
|
+
const end = text.indexOf(">", index + 1);
|
|
659
|
+
const angleToken = end >= 0 ? text.slice(index, end + 1) : void 0;
|
|
660
|
+
if (angleToken && !angleToken.includes("\n") && isAllowedSlackAngleToken(angleToken)) {
|
|
661
|
+
tokens.push(angleToken);
|
|
662
|
+
index += angleToken.length;
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
const codePoint = text.codePointAt(index);
|
|
667
|
+
if (codePoint === void 0) break;
|
|
668
|
+
const grapheme = graphemes?.containing(index + (codePoint > 65535 ? 1 : 0));
|
|
669
|
+
const character = grapheme && text.slice(index, grapheme.index + grapheme.segment.length).match(/^[^`*_~<>&]+/u)?.[0] || String.fromCodePoint(codePoint);
|
|
670
|
+
index += character.length;
|
|
671
|
+
tokens.push(character);
|
|
672
|
+
}
|
|
673
|
+
return tokens;
|
|
674
|
+
}
|
|
675
|
+
function resolveSlackCodeMarkerTransition(active, token) {
|
|
676
|
+
if ((token === "`" || token === "```") && (active === void 0 || active === token)) return active === token ? void 0 : token;
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
function maskSlackExcludedText(text) {
|
|
680
|
+
return text.split("\n").map((line) => line.trim() ? `x${" ".repeat(Math.max(0, line.length - 1))}` : " ".repeat(line.length)).join("\n");
|
|
681
|
+
}
|
|
682
|
+
function maskSlackExcludedRanges(projection) {
|
|
683
|
+
let masked = "";
|
|
684
|
+
let cursor = 0;
|
|
685
|
+
for (const range of projection.excludedRanges) {
|
|
686
|
+
masked += projection.text.slice(cursor, range.start);
|
|
687
|
+
masked += maskSlackExcludedText(projection.text.slice(range.start, range.end));
|
|
688
|
+
cursor = range.end;
|
|
689
|
+
}
|
|
690
|
+
return masked + projection.text.slice(cursor);
|
|
691
|
+
}
|
|
692
|
+
function slackProjectionHasRoleHeader(projection) {
|
|
693
|
+
return Boolean(markdownToIR(maskSlackExcludedRanges(projection), {
|
|
694
|
+
assistantTranscriptRoleHeaders: true,
|
|
695
|
+
autolink: false,
|
|
696
|
+
blockquotePrefix: "",
|
|
697
|
+
headingStyle: "none",
|
|
698
|
+
linkify: false,
|
|
699
|
+
tableMode: "off"
|
|
700
|
+
}).annotations?.some((annotation) => annotation.type === "assistant_transcript_role"));
|
|
701
|
+
}
|
|
702
|
+
function decodeSlackMrkdwnEntities(text) {
|
|
703
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
704
|
+
}
|
|
705
|
+
function projectSlackAngleToken(token, dateDisplay) {
|
|
706
|
+
const inner = token.slice(1, -1);
|
|
707
|
+
if (inner.startsWith("!date^")) {
|
|
708
|
+
const fallbackSeparator = inner.indexOf("|");
|
|
709
|
+
const tokenString = (fallbackSeparator === -1 ? inner : inner.slice(0, fallbackSeparator)).split("^")[2] ?? "";
|
|
710
|
+
const fallback = fallbackSeparator === -1 ? "" : inner.slice(fallbackSeparator + 1);
|
|
711
|
+
return decodeSlackMrkdwnEntities(dateDisplay === "fallback" ? fallback || tokenString : tokenString || fallback);
|
|
712
|
+
}
|
|
713
|
+
const labelSeparator = inner.indexOf("|");
|
|
714
|
+
if (labelSeparator >= 0) return decodeSlackMrkdwnEntities(inner.slice(labelSeparator + 1));
|
|
715
|
+
const prefix = inner.charAt(0);
|
|
716
|
+
if (prefix === "@" || prefix === "#" || prefix === "!") return prefix;
|
|
717
|
+
return decodeSlackMrkdwnEntities(inner);
|
|
718
|
+
}
|
|
719
|
+
function appendSlackVisibleProjection(projection, visible, excluded) {
|
|
720
|
+
if (!visible) return;
|
|
721
|
+
const start = projection.text.length;
|
|
722
|
+
projection.text += visible;
|
|
723
|
+
if (!excluded) return;
|
|
724
|
+
const previous = projection.excludedRanges.at(-1);
|
|
725
|
+
if (previous?.end === start) previous.end = projection.text.length;
|
|
726
|
+
else projection.excludedRanges.push({
|
|
727
|
+
start,
|
|
728
|
+
end: projection.text.length
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
function projectSlackMrkdwnVisibleText(text, dateDisplay) {
|
|
732
|
+
const projection = {
|
|
733
|
+
text: "",
|
|
734
|
+
excludedRanges: []
|
|
735
|
+
};
|
|
736
|
+
let activeMarker;
|
|
737
|
+
let lineHasVisibleContent = false;
|
|
738
|
+
for (const token of tokenizeSlackMrkdwn(text)) {
|
|
739
|
+
const transition = resolveSlackCodeMarkerTransition(activeMarker, token);
|
|
740
|
+
if (transition !== null) {
|
|
741
|
+
activeMarker = transition;
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
let visible = token;
|
|
745
|
+
if (isAllowedSlackAngleToken(token)) visible = activeMarker ? token : projectSlackAngleToken(token, dateDisplay);
|
|
746
|
+
else if (token === "&" || token === "<" || token === ">") visible = decodeSlackMrkdwnEntities(token);
|
|
747
|
+
else if (!activeMarker && (token === "*" || token === "_" || token === "~")) visible = "";
|
|
748
|
+
else if (!activeMarker && token === ">" && !lineHasVisibleContent) visible = "";
|
|
749
|
+
appendSlackVisibleProjection(projection, visible, activeMarker !== void 0);
|
|
750
|
+
for (const character of visible) if (character === "\n") lineHasVisibleContent = false;
|
|
751
|
+
else if (character !== " " && character !== " " && character !== "\r") lineHasVisibleContent = true;
|
|
752
|
+
}
|
|
753
|
+
return projection;
|
|
754
|
+
}
|
|
755
|
+
function protectSlackAssistantTranscriptRoleHeaders(text) {
|
|
756
|
+
if (text.startsWith(SLACK_ASSISTANT_TRANSCRIPT_PREFIX)) return text;
|
|
757
|
+
if (!slackProjectionHasRoleHeader(projectSlackMrkdwnVisibleText(text, "token")) && (!text.includes("<!date^") || !slackProjectionHasRoleHeader(projectSlackMrkdwnVisibleText(text, "fallback")))) return text;
|
|
758
|
+
return `${SLACK_ASSISTANT_TRANSCRIPT_PREFIX}${text}`;
|
|
759
|
+
}
|
|
760
|
+
function buildSlackRenderOptions({ enclosingStyle, mentions } = {}) {
|
|
761
|
+
return {
|
|
762
|
+
annotationMarkers: { assistant_transcript_role: {
|
|
763
|
+
open: "`",
|
|
764
|
+
close: "`",
|
|
765
|
+
suppressNestedFormatting: true
|
|
766
|
+
} },
|
|
767
|
+
styleMarkers: {
|
|
768
|
+
...enclosingStyle !== "bold" ? { bold: {
|
|
769
|
+
open: "*",
|
|
770
|
+
close: "*"
|
|
771
|
+
} } : {},
|
|
772
|
+
...enclosingStyle !== "italic" ? { italic: {
|
|
773
|
+
open: "_",
|
|
774
|
+
close: "_"
|
|
775
|
+
} } : {},
|
|
776
|
+
strikethrough: {
|
|
777
|
+
open: "~",
|
|
778
|
+
close: "~"
|
|
779
|
+
},
|
|
780
|
+
code: {
|
|
781
|
+
open: "`",
|
|
782
|
+
close: "`"
|
|
783
|
+
},
|
|
784
|
+
code_block: {
|
|
785
|
+
open: "```\n",
|
|
786
|
+
close: "```"
|
|
787
|
+
}
|
|
788
|
+
},
|
|
789
|
+
escapeText: (text) => escapeSlackMrkdwnText(text, mentions),
|
|
790
|
+
buildLink: buildSlackLink
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
function prepareSlackMarkdownIR(markdown, options) {
|
|
794
|
+
return makeSlackEmphasisStylesSafe(markdownToIR(markdown ?? "", {
|
|
795
|
+
assistantTranscriptRoleHeaders: true,
|
|
796
|
+
linkify: false,
|
|
797
|
+
autolink: false,
|
|
798
|
+
headingStyle: "rich",
|
|
799
|
+
blockquotePrefix: "> ",
|
|
800
|
+
tableMode: options.tableMode
|
|
801
|
+
}));
|
|
802
|
+
}
|
|
803
|
+
function normalizeSlackOutboundText(markdown, options = {}) {
|
|
804
|
+
const ir = prepareSlackMarkdownIR(markdown, options);
|
|
805
|
+
return protectSlackAssistantTranscriptRoleHeaders(renderMarkdownWithMarkers(ir, buildSlackRenderOptions(options), SLACK_FORMAT_PROFILE));
|
|
806
|
+
}
|
|
807
|
+
/** Chunk already-rendered Slack mrkdwn without splitting entities or code markers. */
|
|
808
|
+
function chunkSlackMrkdwnText(text, limit) {
|
|
809
|
+
if (text.length <= limit) return [text];
|
|
810
|
+
if (!(text.includes("`") || text.includes("&") || text.includes("<") || text.includes(">") || (text.match(/<[^>\n]+>/gu)?.some(isAllowedSlackAngleToken) ?? false))) return chunkTextForOutbound(text, limit, { preserveWhitespace: true });
|
|
811
|
+
const chunks = [];
|
|
812
|
+
let activeMarker;
|
|
813
|
+
let content = "";
|
|
814
|
+
const wrapper = (marker) => marker && limit > marker.length * 2 ? marker : void 0;
|
|
815
|
+
const capacity = (marker) => limit - (wrapper(marker)?.length ?? 0);
|
|
816
|
+
const flush = () => {
|
|
817
|
+
const marker = wrapper(activeMarker);
|
|
818
|
+
if (content && content !== marker) chunks.push(marker ? `${content}${marker}` : content);
|
|
819
|
+
content = "";
|
|
820
|
+
};
|
|
821
|
+
for (const token of tokenizeSlackMrkdwn(text, new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(text))) {
|
|
822
|
+
const transition = resolveSlackCodeMarkerTransition(activeMarker, token);
|
|
823
|
+
const nextMarker = transition === null ? activeMarker : transition;
|
|
824
|
+
const sourceMarker = token === "`" || token === "```" ? token : void 0;
|
|
825
|
+
if (transition !== null && sourceMarker && !wrapper(sourceMarker)) {
|
|
826
|
+
activeMarker = nextMarker;
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
if (content && content.length + token.length > capacity(nextMarker)) flush();
|
|
830
|
+
activeMarker = nextMarker;
|
|
831
|
+
if (!content && transition === void 0) continue;
|
|
832
|
+
content ||= transition === null ? wrapper(activeMarker) ?? "" : "";
|
|
833
|
+
const contentLimit = capacity(activeMarker) - (wrapper(activeMarker)?.length ?? 0);
|
|
834
|
+
if (token.length > contentLimit) {
|
|
835
|
+
flush();
|
|
836
|
+
const marker = wrapper(activeMarker);
|
|
837
|
+
if (activeMarker) {
|
|
838
|
+
const fragments = chunkTextForOutbound(marker ? token : escapeSlackMrkdwn(token), Math.max(1, Math.floor(marker ? contentLimit : limit)), { preserveWhitespace: true });
|
|
839
|
+
chunks.push(...marker ? fragments.map((fragment) => `${marker}${fragment}${marker}`) : fragments);
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
chunks.push(...token.length <= limit ? [token] : chunkTextForOutbound(token, limit));
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
content += token;
|
|
846
|
+
}
|
|
847
|
+
flush();
|
|
848
|
+
return chunks;
|
|
849
|
+
}
|
|
850
|
+
function markdownToSlackMrkdwnChunks(markdown, limit, options = {}) {
|
|
851
|
+
const ir = prepareSlackMarkdownIR(markdown, options);
|
|
852
|
+
const renderOptions = buildSlackRenderOptions();
|
|
853
|
+
const normalizedLimit = limit === Number.POSITIVE_INFINITY ? limit : resolveIntegerOption(limit, 1, { min: 1 });
|
|
854
|
+
return renderMarkdownIRChunksWithinLimit({
|
|
855
|
+
ir,
|
|
856
|
+
limit: normalizedLimit,
|
|
857
|
+
renderChunk: (chunk) => {
|
|
858
|
+
const rendered = renderMarkdownWithMarkers(chunk, renderOptions, SLACK_FORMAT_PROFILE);
|
|
859
|
+
return rendered.length > normalizedLimit ? rendered : protectSlackAssistantTranscriptRoleHeaders(rendered);
|
|
860
|
+
},
|
|
861
|
+
measureRendered: (rendered) => rendered.length
|
|
862
|
+
}).map(({ rendered }) => rendered.length > normalizedLimit ? protectSlackAssistantTranscriptRoleHeaders(rendered) : rendered);
|
|
863
|
+
}
|
|
864
|
+
// extensions/slack/src/limits.ts
|
|
865
|
+
const SLACK_TEXT_LIMIT = 8e3;
|
|
866
|
+
const SLACK_MESSAGE_TEXT_RECOMMENDED_LIMIT = 4e3;
|
|
867
|
+
const SLACK_EDIT_TEXT_MAX_BYTES = 4e3;
|
|
868
|
+
const SLACK_MESSAGE_TEXT_HARD_LIMIT = 4e4;
|
|
869
|
+
// extensions/slack/src/presentation-fallback.ts
|
|
870
|
+
const SLACK_UNCOPYABLE_COMMAND_WARNING = "not copyable: contains backtick";
|
|
871
|
+
function escapeSlackPresentationFallbackBlock(block) {
|
|
872
|
+
if (block.type === "chart") return {
|
|
873
|
+
type: "text",
|
|
874
|
+
text: renderSlackMessagePresentationChartFallbackText(block)
|
|
875
|
+
};
|
|
876
|
+
if (block.type === "table") return {
|
|
877
|
+
type: "text",
|
|
878
|
+
text: renderSlackMessagePresentationTableFallbackText(block)
|
|
879
|
+
};
|
|
880
|
+
if (block.type === "buttons") return {
|
|
881
|
+
...block,
|
|
882
|
+
buttons: block.buttons.map((button) => {
|
|
883
|
+
const commandAction = button.action?.type === "command" ? button.action : void 0;
|
|
884
|
+
const label = commandAction?.command.includes("`") ? `${button.label} [${SLACK_UNCOPYABLE_COMMAND_WARNING}]` : button.label;
|
|
885
|
+
return {
|
|
886
|
+
...button,
|
|
887
|
+
label: escapeSlackMrkdwn(label),
|
|
888
|
+
...button.value ? { value: escapeSlackMrkdwn(button.value) } : {},
|
|
889
|
+
...button.url ? { url: escapeSlackMrkdwn(button.url) } : {},
|
|
890
|
+
...button.webApp ? { webApp: { url: escapeSlackMrkdwn(button.webApp.url) } } : {},
|
|
891
|
+
...button.web_app ? { web_app: { url: escapeSlackMrkdwn(button.web_app.url) } } : {},
|
|
892
|
+
...commandAction ? { action: {
|
|
893
|
+
...commandAction,
|
|
894
|
+
command: commandAction.command.replaceAll("`", "[backtick]")
|
|
895
|
+
} } : {}
|
|
896
|
+
};
|
|
897
|
+
})
|
|
898
|
+
};
|
|
899
|
+
if (block.type === "select") return {
|
|
900
|
+
...block,
|
|
901
|
+
...block.placeholder ? { placeholder: escapeSlackMrkdwn(block.placeholder) } : {},
|
|
902
|
+
options: block.options.map((option) => ({
|
|
903
|
+
...option,
|
|
904
|
+
label: escapeSlackMrkdwn(option.label)
|
|
905
|
+
}))
|
|
906
|
+
};
|
|
907
|
+
return block;
|
|
908
|
+
}
|
|
909
|
+
function renderSlackMessagePresentationChartFallbackText(block) {
|
|
910
|
+
return escapeSlackMrkdwn(renderMessagePresentationChartFallbackText(block));
|
|
911
|
+
}
|
|
912
|
+
function renderSlackMessagePresentationTableFallbackText(block) {
|
|
913
|
+
return escapeSlackMrkdwn(renderMessagePresentationTableFallbackText(block));
|
|
914
|
+
}
|
|
915
|
+
function renderSlackMessagePresentationFallbackText(params) {
|
|
916
|
+
if (!params.presentation) return renderMessagePresentationFallbackText(params);
|
|
917
|
+
const presentation = {
|
|
918
|
+
...params.presentation,
|
|
919
|
+
...params.presentation.title ? { title: escapeSlackMrkdwn(params.presentation.title) } : {},
|
|
920
|
+
blocks: params.presentation.blocks.map(escapeSlackPresentationFallbackBlock)
|
|
921
|
+
};
|
|
922
|
+
return renderMessagePresentationFallbackText({
|
|
923
|
+
...params,
|
|
924
|
+
presentation
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
const SLACK_BUTTON_VALUE_MAX = 2e3;
|
|
928
|
+
const SLACK_SECTION_TEXT_MAX = 3e3;
|
|
929
|
+
const SLACK_PRESENTATION_CAPABILITIES = {
|
|
930
|
+
supported: true,
|
|
931
|
+
buttons: true,
|
|
932
|
+
selects: true,
|
|
933
|
+
context: true,
|
|
934
|
+
divider: true,
|
|
935
|
+
charts: true,
|
|
936
|
+
tables: true,
|
|
937
|
+
limits: {
|
|
938
|
+
actions: {
|
|
939
|
+
maxActionsPerRow: 25,
|
|
940
|
+
maxValueBytes: SLACK_BUTTON_VALUE_MAX,
|
|
941
|
+
supportsStyles: true
|
|
942
|
+
},
|
|
943
|
+
selects: {
|
|
944
|
+
maxOptions: 100,
|
|
945
|
+
maxValueBytes: 150
|
|
946
|
+
},
|
|
947
|
+
text: {
|
|
948
|
+
encoding: "characters",
|
|
949
|
+
markdownDialect: "slack-mrkdwn",
|
|
950
|
+
supportsEdit: true
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
// extensions/slack/src/rich-text.ts
|
|
955
|
+
const RICH_TEXT_CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
956
|
+
"rich_text_section",
|
|
957
|
+
"rich_text_preformatted",
|
|
958
|
+
"rich_text_quote",
|
|
959
|
+
"rich_text_list"
|
|
960
|
+
]);
|
|
961
|
+
function renderSlackRichText(value, mode, separator = "") {
|
|
962
|
+
if (!Array.isArray(value)) return "";
|
|
963
|
+
const table = mode === "table";
|
|
964
|
+
const read = table ? readNonBlankString : normalizeOptionalString;
|
|
965
|
+
const literal = table ? (text) => text : escapeSlackMrkdwn;
|
|
966
|
+
const reference = mode === "escaped" ? escapeSlackMrkdwn : (text) => text;
|
|
967
|
+
const formatReference = (raw, prefix, suffix) => {
|
|
968
|
+
const text = read(raw);
|
|
969
|
+
return text ? reference(`${prefix}${text}${suffix}`) : "";
|
|
970
|
+
};
|
|
971
|
+
return value.map((rawElement) => {
|
|
972
|
+
const element = asOptionalRecord(rawElement);
|
|
973
|
+
if (!element) return "";
|
|
974
|
+
if (Array.isArray(element.elements) && (table || RICH_TEXT_CONTAINER_TYPES.has(element.type))) return renderSlackRichText(element.elements, mode, element.type === "rich_text_list" ? "\n" : "");
|
|
610
975
|
if (element.type === "text" && typeof element.text === "string") return literal(element.text);
|
|
611
976
|
if (table) {
|
|
612
977
|
const text = readNonBlankString(element.text);
|
|
@@ -708,13 +1073,13 @@ function countSlackDataTableBlocksCellCharacters(blocks) {
|
|
|
708
1073
|
function buildSlackDataTableBlock(block, options = {}) {
|
|
709
1074
|
const cellCharacterCountOffset = options.cellCharacterCountOffset ?? 0;
|
|
710
1075
|
if (!Number.isSafeInteger(cellCharacterCountOffset) || cellCharacterCountOffset < 0) return;
|
|
711
|
-
if (
|
|
1076
|
+
if (block.caption.trim().length === 0 || block.headers.length < 1 || block.headers.length > SLACK_DATA_TABLE_COLUMNS_MAX || block.rows.length < 1 || block.rows.length > SLACK_DATA_TABLE_ROWS_MAX || new Set(block.headers).size !== block.headers.length || !block.headers.every((header) => header.trim().length > 0) || block.rowHeaderColumnIndex !== void 0 && (!Number.isInteger(block.rowHeaderColumnIndex) || block.rowHeaderColumnIndex < 0 || block.rowHeaderColumnIndex >= block.headers.length)) return;
|
|
712
1077
|
const rows = [Array.from(block.headers, (text) => ({
|
|
713
1078
|
type: "raw_text",
|
|
714
1079
|
text
|
|
715
1080
|
}))];
|
|
716
1081
|
for (const row of block.rows) {
|
|
717
|
-
if (
|
|
1082
|
+
if (row.length !== block.headers.length) return;
|
|
718
1083
|
const cells = [];
|
|
719
1084
|
for (const cell of row) {
|
|
720
1085
|
if (typeof cell === "number") {
|
|
@@ -726,7 +1091,7 @@ function buildSlackDataTableBlock(block, options = {}) {
|
|
|
726
1091
|
});
|
|
727
1092
|
continue;
|
|
728
1093
|
}
|
|
729
|
-
if (
|
|
1094
|
+
if (cell.trim().length === 0) return;
|
|
730
1095
|
cells.push({
|
|
731
1096
|
type: "raw_text",
|
|
732
1097
|
text: cell
|
|
@@ -784,7 +1149,7 @@ function hasSlackDataVisualizationBlock(blocks) {
|
|
|
784
1149
|
return blocks?.some((block) => asOptionalRecord(block)?.type === "data_visualization") ?? false;
|
|
785
1150
|
}
|
|
786
1151
|
function isStringWithin(value, maxLength) {
|
|
787
|
-
return
|
|
1152
|
+
return value.trim().length > 0 && Array.from(value).length <= maxLength;
|
|
788
1153
|
}
|
|
789
1154
|
function hasUniqueStrings(values) {
|
|
790
1155
|
return new Set(values).size === values.length;
|
|
@@ -798,7 +1163,7 @@ function buildSlackDataVisualizationBlock(block) {
|
|
|
798
1163
|
title: block.title,
|
|
799
1164
|
chart: {
|
|
800
1165
|
type: "pie",
|
|
801
|
-
segments: block.segments
|
|
1166
|
+
segments: block.segments
|
|
802
1167
|
}
|
|
803
1168
|
};
|
|
804
1169
|
}
|
|
@@ -816,7 +1181,7 @@ function buildSlackDataVisualizationBlock(block) {
|
|
|
816
1181
|
}))
|
|
817
1182
|
})),
|
|
818
1183
|
axis_config: {
|
|
819
|
-
categories:
|
|
1184
|
+
categories: block.categories,
|
|
820
1185
|
...block.xLabel ? { x_label: block.xLabel } : {},
|
|
821
1186
|
...block.yLabel ? { y_label: block.yLabel } : {}
|
|
822
1187
|
}
|
|
@@ -850,7 +1215,7 @@ function parseSlackDataVisualizationBlock(block) {
|
|
|
850
1215
|
if (chart.type !== "bar" && chart.type !== "area" && chart.type !== "line") return;
|
|
851
1216
|
const axisConfig = asOptionalRecord(chart.axis_config);
|
|
852
1217
|
const categories = axisConfig?.categories;
|
|
853
|
-
if (!
|
|
1218
|
+
if (!isStringArray(categories)) return;
|
|
854
1219
|
if (!Array.isArray(chart.series)) return;
|
|
855
1220
|
const series = chart.series.map((rawSeries) => {
|
|
856
1221
|
const seriesRecord = asOptionalRecord(rawSeries);
|
|
@@ -859,360 +1224,167 @@ function parseSlackDataVisualizationBlock(block) {
|
|
|
859
1224
|
if (data.some((datum) => !datum) || data.length !== categories.length) return;
|
|
860
1225
|
const dataByLabel = new Map(data.map((datum) => [datum.label, datum.value]));
|
|
861
1226
|
if (dataByLabel.size !== data.length || categories.some((category) => !dataByLabel.has(category))) return;
|
|
862
|
-
return {
|
|
863
|
-
name: seriesRecord.name,
|
|
864
|
-
values: categories.map((category) => dataByLabel.get(category))
|
|
865
|
-
};
|
|
866
|
-
});
|
|
867
|
-
if (series.some((entry) => !entry)) return;
|
|
868
|
-
return {
|
|
869
|
-
type: "chart",
|
|
870
|
-
chartType: chart.type,
|
|
871
|
-
title,
|
|
872
|
-
categories,
|
|
873
|
-
series,
|
|
874
|
-
xLabel: axisConfig?.x_label,
|
|
875
|
-
yLabel: axisConfig?.y_label
|
|
876
|
-
};
|
|
877
|
-
}
|
|
878
|
-
/** Extract an accessible summary, escaping mrkdwn control tokens when requested. */
|
|
879
|
-
function renderSlackDataVisualizationFallbackText(value, mrkdwnSafe = false) {
|
|
880
|
-
const block = asOptionalRecord(value);
|
|
881
|
-
if (block?.type !== "data_visualization") return;
|
|
882
|
-
const parsed = normalizeMessagePresentation({ blocks: [parseSlackDataVisualizationBlock(block)] })?.blocks[0];
|
|
883
|
-
if (parsed?.type === "chart") return mrkdwnSafe ? renderSlackMessagePresentationChartFallbackText(parsed) : renderMessagePresentationChartFallbackText(parsed);
|
|
884
|
-
const title = typeof block.title === "string" ? block.title.trim() : "";
|
|
885
|
-
return title ? mrkdwnSafe ? escapeSlackMrkdwn(title) : title : void 0;
|
|
886
|
-
}
|
|
887
|
-
// extensions/slack/src/format.ts
|
|
888
|
-
const SLACK_ANGLE_TOKEN_RE = /<[^>\n]+>/g;
|
|
889
|
-
function isAllowedSlackAngleToken(token) {
|
|
890
|
-
const inner = token.slice(1, -1);
|
|
891
|
-
return inner.startsWith("@") || inner.startsWith("#") || inner.startsWith("!") || inner.startsWith("mailto:") || inner.startsWith("tel:") || inner.startsWith("http://") || inner.startsWith("https://") || inner.startsWith("slack://");
|
|
892
|
-
}
|
|
893
|
-
function escapeSlackMrkdwnContent(text, mentions) {
|
|
894
|
-
if (mentions === "escape") return escapeSlackMrkdwn(text);
|
|
895
|
-
if (!text.includes("&") && !text.includes("<") && !text.includes(">")) return text;
|
|
896
|
-
const out = [];
|
|
897
|
-
let lastIndex = 0;
|
|
898
|
-
for (const match of text.matchAll(SLACK_ANGLE_TOKEN_RE)) {
|
|
899
|
-
const matchIndex = match.index;
|
|
900
|
-
out.push(escapeSlackMrkdwn(text.slice(lastIndex, matchIndex)));
|
|
901
|
-
const token = match[0];
|
|
902
|
-
const escapeControl = mentions === "escape-controls" && /^<[@#!]/u.test(token);
|
|
903
|
-
out.push(isAllowedSlackAngleToken(token) && !escapeControl ? token : escapeSlackMrkdwn(token));
|
|
904
|
-
lastIndex = matchIndex + token.length;
|
|
905
|
-
}
|
|
906
|
-
out.push(escapeSlackMrkdwn(text.slice(lastIndex)));
|
|
907
|
-
return out.join("");
|
|
908
|
-
}
|
|
909
|
-
function escapeSlackMrkdwnText(text, mentions) {
|
|
910
|
-
if (!text.includes("&") && !text.includes("<") && !text.includes(">")) return text;
|
|
911
|
-
return text.split("\n").map((line) => {
|
|
912
|
-
if (line.startsWith("> ")) return `> ${escapeSlackMrkdwnContent(line.slice(2), mentions)}`;
|
|
913
|
-
return escapeSlackMrkdwnContent(line, mentions);
|
|
914
|
-
}).join("\n");
|
|
915
|
-
}
|
|
916
|
-
function buildSlackLink(link, text) {
|
|
917
|
-
const href = link.href.trim();
|
|
918
|
-
if (!href || /^[@#!]/u.test(href)) return null;
|
|
919
|
-
const trimmedLabel = text.slice(link.start, link.end).trim();
|
|
920
|
-
const comparableHref = href.startsWith("mailto:") ? href.slice(7) : href;
|
|
921
|
-
if (!(trimmedLabel.length > 0 && trimmedLabel !== href && trimmedLabel !== comparableHref)) return null;
|
|
922
|
-
const safeHref = escapeSlackMrkdwn(href);
|
|
1227
|
+
return {
|
|
1228
|
+
name: seriesRecord.name,
|
|
1229
|
+
values: categories.map((category) => dataByLabel.get(category))
|
|
1230
|
+
};
|
|
1231
|
+
});
|
|
1232
|
+
if (series.some((entry) => !entry)) return;
|
|
923
1233
|
return {
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1234
|
+
type: "chart",
|
|
1235
|
+
chartType: chart.type,
|
|
1236
|
+
title,
|
|
1237
|
+
categories,
|
|
1238
|
+
series,
|
|
1239
|
+
xLabel: axisConfig?.x_label,
|
|
1240
|
+
yLabel: axisConfig?.y_label
|
|
928
1241
|
};
|
|
929
1242
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
const
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
if (lastCodeUnit >= 56320 && lastCodeUnit <= 57343 && index > 1) {
|
|
939
|
-
const previousCodeUnit = text.charCodeAt(index - 2);
|
|
940
|
-
if (previousCodeUnit >= 55296 && previousCodeUnit <= 56319) return text.slice(index - 2, index);
|
|
941
|
-
}
|
|
942
|
-
return text[index - 1] ?? "";
|
|
943
|
-
}
|
|
944
|
-
function getCodePointAt(text, index) {
|
|
945
|
-
const codePoint = text.codePointAt(index);
|
|
946
|
-
return codePoint === void 0 ? "" : String.fromCodePoint(codePoint);
|
|
947
|
-
}
|
|
948
|
-
function isSlackCjkPunctuation(character) {
|
|
949
|
-
if (!SLACK_MRKDWN_PUNCTUATION_RE.test(character)) return false;
|
|
950
|
-
if (SLACK_MRKDWN_CJK_SCRIPT_RE.test(character)) return true;
|
|
951
|
-
const codePoint = character.codePointAt(0);
|
|
952
|
-
if (codePoint === void 0) return false;
|
|
953
|
-
const width = eastAsianWidthType(codePoint);
|
|
954
|
-
return width === "fullwidth" || width === "halfwidth" || width === "wide" && !SLACK_MRKDWN_EMOJI_PRESENTATION_RE.test(character);
|
|
1243
|
+
/** Extract an accessible summary, escaping mrkdwn control tokens when requested. */
|
|
1244
|
+
function renderSlackDataVisualizationFallbackText(value, mrkdwnSafe = false) {
|
|
1245
|
+
const block = asOptionalRecord(value);
|
|
1246
|
+
if (block?.type !== "data_visualization") return;
|
|
1247
|
+
const parsed = normalizeMessagePresentation({ blocks: [parseSlackDataVisualizationBlock(block)] })?.blocks[0];
|
|
1248
|
+
if (parsed?.type === "chart") return mrkdwnSafe ? renderSlackMessagePresentationChartFallbackText(parsed) : renderMessagePresentationChartFallbackText(parsed);
|
|
1249
|
+
const title = typeof block.title === "string" ? block.title.trim() : "";
|
|
1250
|
+
return title ? mrkdwnSafe ? escapeSlackMrkdwn(title) : title : void 0;
|
|
955
1251
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1252
|
+
// extensions/slack/src/blocks-fallback.ts
|
|
1253
|
+
const SLACK_SELECT_ELEMENT_TYPES = /* @__PURE__ */ new Set([
|
|
1254
|
+
"static_select",
|
|
1255
|
+
"multi_static_select",
|
|
1256
|
+
"external_select",
|
|
1257
|
+
"multi_external_select",
|
|
1258
|
+
"users_select",
|
|
1259
|
+
"multi_users_select",
|
|
1260
|
+
"conversations_select",
|
|
1261
|
+
"multi_conversations_select",
|
|
1262
|
+
"channels_select",
|
|
1263
|
+
"multi_channels_select"
|
|
1264
|
+
]);
|
|
1265
|
+
function readTextObject(value, options = {}) {
|
|
1266
|
+
const record = asOptionalRecord(value);
|
|
1267
|
+
const text = normalizeOptionalString(record?.text);
|
|
1268
|
+
if (!text) return;
|
|
1269
|
+
return record?.type === "plain_text" && options.nativeDataFormat !== "plain" ? escapeSlackMrkdwn(text) : text;
|
|
961
1270
|
}
|
|
962
|
-
function
|
|
963
|
-
const
|
|
964
|
-
|
|
965
|
-
return !isUnsafeSlackEmphasisBoundary(getCodePointBefore(ir.text, span.start)) && !isUnsafeSlackEmphasisBoundary(getCodePointAt(ir.text, span.end));
|
|
966
|
-
});
|
|
967
|
-
return styles.length === ir.styles.length ? ir : {
|
|
968
|
-
...ir,
|
|
969
|
-
styles
|
|
970
|
-
};
|
|
1271
|
+
function readAltText(value) {
|
|
1272
|
+
const text = normalizeOptionalString(value);
|
|
1273
|
+
return text ? escapeSlackMrkdwn(text) : void 0;
|
|
971
1274
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
orderedList: "fallback",
|
|
981
|
-
taskList: "fallback",
|
|
982
|
-
table: "fallback",
|
|
983
|
-
image: "fallback"
|
|
984
|
-
},
|
|
985
|
-
chunk: {
|
|
986
|
-
limit: 4e3,
|
|
987
|
-
unit: "chars",
|
|
988
|
-
hardCap: 4e4
|
|
1275
|
+
function readControlElementText(value, options = {}) {
|
|
1276
|
+
const element = asOptionalRecord(value);
|
|
1277
|
+
const type = normalizeOptionalString(element?.type);
|
|
1278
|
+
if (type === "button" || type === "workflow_button") return normalizeOptionalString(element?.text) ?? readTextObject(element?.text, options);
|
|
1279
|
+
if (type && (SLACK_SELECT_ELEMENT_TYPES.has(type) || type === "overflow")) {
|
|
1280
|
+
if (!options.includeSelectOptions) return readTextObject(element?.placeholder, options);
|
|
1281
|
+
const choices = Array.isArray(element?.options) ? element.options : [];
|
|
1282
|
+
return [readTextObject(element?.placeholder, options), ...choices.map((choice) => readTextObject(asOptionalRecord(choice)?.text, options))].filter(Boolean).join("\n");
|
|
989
1283
|
}
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
function
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1284
|
+
}
|
|
1285
|
+
/** Read only user-visible text from one Slack block. */
|
|
1286
|
+
function renderSlackBlockFallbackText(raw, options = {}) {
|
|
1287
|
+
const block = asOptionalRecord(raw);
|
|
1288
|
+
if (!block) return;
|
|
1289
|
+
switch (block.type) {
|
|
1290
|
+
case "rich_text": return normalizeOptionalString(renderSlackRichText(block.elements, options.nativeReferenceFormat === "plain" ? "native-reference" : "escaped", "\n"));
|
|
1291
|
+
case "header": return readTextObject(block.text, options);
|
|
1292
|
+
case "section":
|
|
1293
|
+
case "context": {
|
|
1294
|
+
const context = block.type === "context";
|
|
1295
|
+
return (context ? (Array.isArray(block.elements) ? block.elements : []).map((element) => {
|
|
1296
|
+
const record = asOptionalRecord(element);
|
|
1297
|
+
return readTextObject(record, options) ?? readAltText(record?.alt_text);
|
|
1298
|
+
}) : [
|
|
1299
|
+
readTextObject(block.text, options),
|
|
1300
|
+
...Array.isArray(block.fields) ? block.fields.map((field) => readTextObject(field, options)) : [],
|
|
1301
|
+
readControlElementText(block.accessory, options)
|
|
1302
|
+
]).filter(Boolean).join(context ? " " : "\n") || void 0;
|
|
1009
1303
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
}
|
|
1304
|
+
case "image": return readAltText(block.alt_text) ?? readTextObject(block.title) ?? "Shared an image";
|
|
1305
|
+
case "video": return readTextObject(block.title, options) ?? readAltText(block.alt_text) ?? "Shared a video";
|
|
1306
|
+
case "file": return "Shared a file";
|
|
1307
|
+
case "actions": {
|
|
1308
|
+
if (!Array.isArray(block.elements)) return;
|
|
1309
|
+
const labels = block.elements.map((value) => readControlElementText(value, options)).filter(Boolean);
|
|
1310
|
+
return [...new Set(labels)].join("\n") || void 0;
|
|
1018
1311
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
index += character.length;
|
|
1024
|
-
tokens.push(character);
|
|
1312
|
+
case "data_visualization": return renderSlackDataVisualizationFallbackText(block, options.nativeDataFormat !== "plain");
|
|
1313
|
+
case "data_table": return renderSlackDataTableFallbackText(block, options.nativeDataFormat !== "plain");
|
|
1314
|
+
case "table": return renderSlackTableFallbackText(block, options.nativeDataFormat !== "plain");
|
|
1315
|
+
default: return;
|
|
1025
1316
|
}
|
|
1026
|
-
return tokens;
|
|
1027
1317
|
}
|
|
1028
|
-
function
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
function maskSlackExcludedText(text) {
|
|
1033
|
-
return text.split("\n").map((line) => line.trim() ? `x${" ".repeat(Math.max(0, line.length - 1))}` : " ".repeat(line.length)).join("\n");
|
|
1034
|
-
}
|
|
1035
|
-
function maskSlackExcludedRanges(projection) {
|
|
1036
|
-
let masked = "";
|
|
1037
|
-
let cursor = 0;
|
|
1038
|
-
for (const range of projection.excludedRanges) {
|
|
1039
|
-
masked += projection.text.slice(cursor, range.start);
|
|
1040
|
-
masked += maskSlackExcludedText(projection.text.slice(range.start, range.end));
|
|
1041
|
-
cursor = range.end;
|
|
1318
|
+
function buildSlackBlocksFallbackText(blocks) {
|
|
1319
|
+
for (const block of blocks) {
|
|
1320
|
+
const text = renderSlackBlockFallbackText(block);
|
|
1321
|
+
if (text) return text;
|
|
1042
1322
|
}
|
|
1043
|
-
return
|
|
1044
|
-
}
|
|
1045
|
-
function slackProjectionHasRoleHeader(projection) {
|
|
1046
|
-
return Boolean(markdownToIR(maskSlackExcludedRanges(projection), {
|
|
1047
|
-
assistantTranscriptRoleHeaders: true,
|
|
1048
|
-
autolink: false,
|
|
1049
|
-
blockquotePrefix: "",
|
|
1050
|
-
headingStyle: "none",
|
|
1051
|
-
linkify: false,
|
|
1052
|
-
tableMode: "off"
|
|
1053
|
-
}).annotations?.some((annotation) => annotation.type === "assistant_transcript_role"));
|
|
1323
|
+
return "Shared a Block Kit message";
|
|
1054
1324
|
}
|
|
1055
|
-
function
|
|
1056
|
-
return
|
|
1325
|
+
function buildSlackCompleteBlocksFallbackText(blocks, options = {}) {
|
|
1326
|
+
return blocks.map((block) => renderSlackBlockFallbackText(block, options)).filter(Boolean).join("\n\n").trim() || buildSlackBlocksFallbackText(blocks);
|
|
1057
1327
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1328
|
+
// extensions/slack/src/blocks-input.ts
|
|
1329
|
+
const SLACK_MAX_BLOCKS = 50;
|
|
1330
|
+
function parseBlocksJson(raw) {
|
|
1331
|
+
try {
|
|
1332
|
+
return JSON.parse(raw);
|
|
1333
|
+
} catch {
|
|
1334
|
+
throw new Error("blocks must be valid JSON");
|
|
1065
1335
|
}
|
|
1066
|
-
const labelSeparator = inner.indexOf("|");
|
|
1067
|
-
if (labelSeparator >= 0) return decodeSlackMrkdwnEntities(inner.slice(labelSeparator + 1));
|
|
1068
|
-
const prefix = inner.charAt(0);
|
|
1069
|
-
if (prefix === "@" || prefix === "#" || prefix === "!") return prefix;
|
|
1070
|
-
return decodeSlackMrkdwnEntities(inner);
|
|
1071
|
-
}
|
|
1072
|
-
function appendSlackVisibleProjection(projection, visible, excluded) {
|
|
1073
|
-
if (!visible) return;
|
|
1074
|
-
const start = projection.text.length;
|
|
1075
|
-
projection.text += visible;
|
|
1076
|
-
if (!excluded) return;
|
|
1077
|
-
const previous = projection.excludedRanges.at(-1);
|
|
1078
|
-
if (previous?.end === start) previous.end = projection.text.length;
|
|
1079
|
-
else projection.excludedRanges.push({
|
|
1080
|
-
start,
|
|
1081
|
-
end: projection.text.length
|
|
1082
|
-
});
|
|
1083
1336
|
}
|
|
1084
|
-
function
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
const transition = resolveSlackCodeMarkerTransition(activeMarker, token);
|
|
1093
|
-
if (transition !== null) {
|
|
1094
|
-
activeMarker = transition;
|
|
1095
|
-
continue;
|
|
1096
|
-
}
|
|
1097
|
-
let visible = token;
|
|
1098
|
-
if (isAllowedSlackAngleToken(token)) visible = activeMarker ? token : projectSlackAngleToken(token, dateDisplay);
|
|
1099
|
-
else if (token === "&" || token === "<" || token === ">") visible = decodeSlackMrkdwnEntities(token);
|
|
1100
|
-
else if (!activeMarker && (token === "*" || token === "_" || token === "~")) visible = "";
|
|
1101
|
-
else if (!activeMarker && token === ">" && !lineHasVisibleContent) visible = "";
|
|
1102
|
-
appendSlackVisibleProjection(projection, visible, activeMarker !== void 0);
|
|
1103
|
-
for (const character of visible) if (character === "\n") lineHasVisibleContent = false;
|
|
1104
|
-
else if (character !== " " && character !== " " && character !== "\r") lineHasVisibleContent = true;
|
|
1337
|
+
function validateSlackBlocksArray(raw) {
|
|
1338
|
+
if (!Array.isArray(raw)) throw new Error("blocks must be an array");
|
|
1339
|
+
if (raw.length === 0) throw new Error("blocks must contain at least one block");
|
|
1340
|
+
if (raw.length > 50) throw new Error(`blocks cannot exceed 50 items`);
|
|
1341
|
+
for (const block of raw) {
|
|
1342
|
+
if (!block || typeof block !== "object" || Array.isArray(block)) throw new Error("each block must be an object");
|
|
1343
|
+
const type = block.type;
|
|
1344
|
+
if (typeof type !== "string" || type.trim().length === 0) throw new Error("each block must include a non-empty string type");
|
|
1105
1345
|
}
|
|
1106
|
-
return
|
|
1346
|
+
return raw;
|
|
1107
1347
|
}
|
|
1108
|
-
function
|
|
1109
|
-
if (
|
|
1110
|
-
|
|
1111
|
-
return `${SLACK_ASSISTANT_TRANSCRIPT_PREFIX}${text}`;
|
|
1348
|
+
function parseSlackBlocksInput(raw) {
|
|
1349
|
+
if (raw == null) return;
|
|
1350
|
+
return validateSlackBlocksArray(typeof raw === "string" ? parseBlocksJson(raw) : raw);
|
|
1112
1351
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
suppressNestedFormatting: true
|
|
1119
|
-
} },
|
|
1120
|
-
styleMarkers: {
|
|
1121
|
-
...enclosingStyle !== "bold" ? { bold: {
|
|
1122
|
-
open: "*",
|
|
1123
|
-
close: "*"
|
|
1124
|
-
} } : {},
|
|
1125
|
-
...enclosingStyle !== "italic" ? { italic: {
|
|
1126
|
-
open: "_",
|
|
1127
|
-
close: "_"
|
|
1128
|
-
} } : {},
|
|
1129
|
-
strikethrough: {
|
|
1130
|
-
open: "~",
|
|
1131
|
-
close: "~"
|
|
1132
|
-
},
|
|
1133
|
-
code: {
|
|
1134
|
-
open: "`",
|
|
1135
|
-
close: "`"
|
|
1136
|
-
},
|
|
1137
|
-
code_block: {
|
|
1138
|
-
open: "```\n",
|
|
1139
|
-
close: "```"
|
|
1140
|
-
}
|
|
1141
|
-
},
|
|
1142
|
-
escapeText: (text) => escapeSlackMrkdwnText(text, mentions),
|
|
1143
|
-
buildLink: buildSlackLink
|
|
1144
|
-
};
|
|
1352
|
+
// extensions/slack/src/approval-actions.ts
|
|
1353
|
+
const SLACK_APPROVAL_VALUE_PREFIX = "openclaw:approval:v1:";
|
|
1354
|
+
const SLACK_APPROVAL_HEADER_BLOCK_ID = "openclaw_approval_header";
|
|
1355
|
+
function isApprovalDecision(value) {
|
|
1356
|
+
return value === "allow-once" || value === "allow-always" || value === "deny";
|
|
1145
1357
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1358
|
+
/** Encode portable approval facts without exposing a slash command to Slack callbacks. */
|
|
1359
|
+
function encodeSlackApprovalAction(action) {
|
|
1360
|
+
const encode = (approvalId) => `${SLACK_APPROVAL_VALUE_PREFIX}${JSON.stringify({
|
|
1361
|
+
approvalId,
|
|
1362
|
+
approvalKind: action.approvalKind,
|
|
1363
|
+
decision: action.decision
|
|
1364
|
+
})}`;
|
|
1365
|
+
const exact = encode(action.approvalId);
|
|
1366
|
+
return exact.length <= 2e3 ? exact : encode(buildApprovalResolutionRef({
|
|
1367
|
+
approvalId: action.approvalId,
|
|
1368
|
+
approvalKind: action.approvalKind
|
|
1154
1369
|
}));
|
|
1155
1370
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
content = "";
|
|
1173
|
-
};
|
|
1174
|
-
for (const token of tokenizeSlackMrkdwn(text, new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(text))) {
|
|
1175
|
-
const transition = resolveSlackCodeMarkerTransition(activeMarker, token);
|
|
1176
|
-
const nextMarker = transition === null ? activeMarker : transition;
|
|
1177
|
-
const sourceMarker = token === "`" || token === "```" ? token : void 0;
|
|
1178
|
-
if (transition !== null && sourceMarker && !wrapper(sourceMarker)) {
|
|
1179
|
-
activeMarker = nextMarker;
|
|
1180
|
-
continue;
|
|
1181
|
-
}
|
|
1182
|
-
if (content && content.length + token.length > capacity(nextMarker)) flush();
|
|
1183
|
-
activeMarker = nextMarker;
|
|
1184
|
-
if (!content && transition === void 0) continue;
|
|
1185
|
-
content ||= transition === null ? wrapper(activeMarker) ?? "" : "";
|
|
1186
|
-
const contentLimit = capacity(activeMarker) - (wrapper(activeMarker)?.length ?? 0);
|
|
1187
|
-
if (token.length > contentLimit) {
|
|
1188
|
-
flush();
|
|
1189
|
-
const marker = wrapper(activeMarker);
|
|
1190
|
-
if (activeMarker) {
|
|
1191
|
-
const fragments = chunkTextForOutbound(marker ? token : escapeSlackMrkdwn(token), Math.max(1, Math.floor(marker ? contentLimit : limit)), { preserveWhitespace: true });
|
|
1192
|
-
chunks.push(...marker ? fragments.map((fragment) => `${marker}${fragment}${marker}`) : fragments);
|
|
1193
|
-
continue;
|
|
1194
|
-
}
|
|
1195
|
-
chunks.push(...token.length <= limit ? [token] : chunkTextForOutbound(token, limit));
|
|
1196
|
-
continue;
|
|
1197
|
-
}
|
|
1198
|
-
content += token;
|
|
1371
|
+
/** Decode only the exact Slack-owned approval envelope. Malformed callbacks fail closed. */
|
|
1372
|
+
function decodeSlackApprovalAction(value) {
|
|
1373
|
+
if (typeof value !== "string" || !value.startsWith(SLACK_APPROVAL_VALUE_PREFIX)) return null;
|
|
1374
|
+
try {
|
|
1375
|
+
const decoded = JSON.parse(value.slice(21));
|
|
1376
|
+
if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return null;
|
|
1377
|
+
const record = decoded;
|
|
1378
|
+
if (Object.keys(record).length !== 3 || typeof record.approvalId !== "string" || record.approvalId.length === 0 || record.approvalKind !== "exec" && record.approvalKind !== "plugin" && record.approvalKind !== "system-agent" || !isApprovalDecision(record.decision)) return null;
|
|
1379
|
+
return {
|
|
1380
|
+
type: "approval",
|
|
1381
|
+
approvalId: record.approvalId,
|
|
1382
|
+
approvalKind: record.approvalKind,
|
|
1383
|
+
decision: record.decision
|
|
1384
|
+
};
|
|
1385
|
+
} catch {
|
|
1386
|
+
return null;
|
|
1199
1387
|
}
|
|
1200
|
-
flush();
|
|
1201
|
-
return chunks;
|
|
1202
|
-
}
|
|
1203
|
-
function markdownToSlackMrkdwnChunks(markdown, limit, options = {}) {
|
|
1204
|
-
const ir = prepareSlackMarkdownIR(markdown, options);
|
|
1205
|
-
const renderOptions = buildSlackRenderOptions();
|
|
1206
|
-
const normalizedLimit = limit === Number.POSITIVE_INFINITY ? limit : resolveIntegerOption(limit, 1, { min: 1 });
|
|
1207
|
-
return renderMarkdownIRChunksWithinLimit({
|
|
1208
|
-
ir,
|
|
1209
|
-
limit: normalizedLimit,
|
|
1210
|
-
renderChunk: (chunk) => {
|
|
1211
|
-
const rendered = renderMarkdownWithMarkers(chunk, renderOptions, SLACK_FORMAT_PROFILE);
|
|
1212
|
-
return rendered.length > normalizedLimit ? rendered : protectSlackAssistantTranscriptRoleHeaders(rendered);
|
|
1213
|
-
},
|
|
1214
|
-
measureRendered: (rendered) => rendered.length
|
|
1215
|
-
}).map(({ rendered }) => rendered.length > normalizedLimit ? protectSlackAssistantTranscriptRoleHeaders(rendered) : rendered);
|
|
1216
1388
|
}
|
|
1217
1389
|
// extensions/slack/src/question-actions.ts
|
|
1218
1390
|
const SLACK_QUESTION_VALUE_PREFIX = "slq1:";
|
|
@@ -1232,7 +1404,7 @@ function decodeSlackQuestionAction(value) {
|
|
|
1232
1404
|
async function resolveSlackQuestionAction(params) {
|
|
1233
1405
|
let result;
|
|
1234
1406
|
try {
|
|
1235
|
-
result = await
|
|
1407
|
+
result = await questionGatewayRuntime.resolveOption({
|
|
1236
1408
|
cfg: params.cfg,
|
|
1237
1409
|
questionId: params.action.questionId,
|
|
1238
1410
|
optionIndex: params.action.optionIndex,
|
|
@@ -1269,11 +1441,14 @@ function createSlackActionIdMatcher(...prefixes) {
|
|
|
1269
1441
|
const isSlackQuestionActionId = createSlackActionIdMatcher(SLACK_BUTTON_ACTION_IDS.question);
|
|
1270
1442
|
const isSlackApprovalActionId = createSlackActionIdMatcher(SLACK_BUTTON_ACTION_IDS.approval, SLACK_SELECT_ACTION_IDS.approval);
|
|
1271
1443
|
const isSlackCallbackActionId = createSlackActionIdMatcher(SLACK_BUTTON_ACTION_IDS.callback, SLACK_SELECT_ACTION_IDS.callback);
|
|
1444
|
+
function isSlackQuestionActionForQuestion(action, questionId) {
|
|
1445
|
+
return Boolean(action.action_id && isSlackQuestionActionId(action.action_id) && decodeSlackQuestionAction(action.value)?.questionId === questionId);
|
|
1446
|
+
}
|
|
1272
1447
|
/** Read only question control identities from the blocks actually sent to Slack. */
|
|
1273
|
-
function resolveSlackQuestionActionIds(blocks) {
|
|
1448
|
+
function resolveSlackQuestionActionIds(blocks, questionId) {
|
|
1274
1449
|
return (blocks ?? []).flatMap((block) => {
|
|
1275
1450
|
if (block.type !== "actions") return [];
|
|
1276
|
-
return (block.elements ?? []).flatMap((
|
|
1451
|
+
return (block.elements ?? []).flatMap((element) => element.action_id && (questionId ? isSlackQuestionActionForQuestion(element, questionId) : isSlackQuestionActionId(element.action_id)) ? [element.action_id] : []);
|
|
1277
1452
|
});
|
|
1278
1453
|
}
|
|
1279
1454
|
// extensions/slack/src/truncate.ts
|
|
@@ -1281,7 +1456,11 @@ function truncateSlackText(value, max, whitespace = "trim") {
|
|
|
1281
1456
|
const text = whitespace === "preserve" ? value : value.trim();
|
|
1282
1457
|
if (text.length <= max) return text;
|
|
1283
1458
|
if (max <= 1) return sliceUtf16Safe(text, 0, max);
|
|
1284
|
-
return
|
|
1459
|
+
return truncateWithMarker(text, max, {
|
|
1460
|
+
marker: "…",
|
|
1461
|
+
reserve: 1,
|
|
1462
|
+
trimEnd: false
|
|
1463
|
+
});
|
|
1285
1464
|
}
|
|
1286
1465
|
function countSlackTextUtf8Bytes(value) {
|
|
1287
1466
|
return Buffer.byteLength(value, "utf8");
|
|
@@ -1294,98 +1473,9 @@ function truncateSlackTextByUtf8Bytes(value, maxBytes) {
|
|
|
1294
1473
|
const suffix = "…";
|
|
1295
1474
|
const suffixBytes = countSlackTextUtf8Bytes(suffix);
|
|
1296
1475
|
const prefixBudget = maxBytes >= suffixBytes ? maxBytes - suffixBytes : maxBytes;
|
|
1297
|
-
|
|
1298
|
-
let prefixBytes = 0;
|
|
1299
|
-
for (const character of trimmed) {
|
|
1300
|
-
const characterBytes = countSlackTextUtf8Bytes(character);
|
|
1301
|
-
if (prefixBytes + characterBytes > prefixBudget) break;
|
|
1302
|
-
prefix += character;
|
|
1303
|
-
prefixBytes += characterBytes;
|
|
1304
|
-
}
|
|
1476
|
+
const prefix = truncateUtf8Prefix(trimmed, prefixBudget);
|
|
1305
1477
|
return maxBytes >= suffixBytes ? `${prefix}${suffix}` : prefix;
|
|
1306
1478
|
}
|
|
1307
|
-
// extensions/slack/src/limits.ts
|
|
1308
|
-
const SLACK_TEXT_LIMIT = 8e3;
|
|
1309
|
-
const SLACK_MESSAGE_TEXT_RECOMMENDED_LIMIT = 4e3;
|
|
1310
|
-
const SLACK_EDIT_TEXT_MAX_BYTES = 4e3;
|
|
1311
|
-
const SLACK_MESSAGE_TEXT_HARD_LIMIT = 4e4;
|
|
1312
|
-
// extensions/slack/src/blocks-fallback.ts
|
|
1313
|
-
const SLACK_SELECT_ELEMENT_TYPES = /* @__PURE__ */ new Set([
|
|
1314
|
-
"static_select",
|
|
1315
|
-
"multi_static_select",
|
|
1316
|
-
"external_select",
|
|
1317
|
-
"multi_external_select",
|
|
1318
|
-
"users_select",
|
|
1319
|
-
"multi_users_select",
|
|
1320
|
-
"conversations_select",
|
|
1321
|
-
"multi_conversations_select",
|
|
1322
|
-
"channels_select",
|
|
1323
|
-
"multi_channels_select"
|
|
1324
|
-
]);
|
|
1325
|
-
function readTextObject(value, options = {}) {
|
|
1326
|
-
const record = asOptionalRecord(value);
|
|
1327
|
-
const text = normalizeOptionalString(record?.text);
|
|
1328
|
-
if (!text) return;
|
|
1329
|
-
return record?.type === "plain_text" && options.nativeDataFormat !== "plain" ? escapeSlackMrkdwn(text) : text;
|
|
1330
|
-
}
|
|
1331
|
-
function readAltText(value) {
|
|
1332
|
-
const text = normalizeOptionalString(value);
|
|
1333
|
-
return text ? escapeSlackMrkdwn(text) : void 0;
|
|
1334
|
-
}
|
|
1335
|
-
function readControlElementText(value, options = {}) {
|
|
1336
|
-
const element = asOptionalRecord(value);
|
|
1337
|
-
const type = normalizeOptionalString(element?.type);
|
|
1338
|
-
if (type === "button" || type === "workflow_button") return normalizeOptionalString(element?.text) ?? readTextObject(element?.text, options);
|
|
1339
|
-
if (type && (SLACK_SELECT_ELEMENT_TYPES.has(type) || type === "overflow")) {
|
|
1340
|
-
if (!options.includeSelectOptions) return readTextObject(element?.placeholder, options);
|
|
1341
|
-
const choices = Array.isArray(element?.options) ? element.options : [];
|
|
1342
|
-
return [readTextObject(element?.placeholder, options), ...choices.map((choice) => readTextObject(asOptionalRecord(choice)?.text, options))].filter(Boolean).join("\n");
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
function readControlElementsText(values, options = {}) {
|
|
1346
|
-
const labels = values.map((value) => readControlElementText(value, options)).filter(Boolean);
|
|
1347
|
-
return [...new Set(labels)].join("\n") || void 0;
|
|
1348
|
-
}
|
|
1349
|
-
function readTextLayout(block, options = {}) {
|
|
1350
|
-
const context = block.type === "context";
|
|
1351
|
-
return (context ? (Array.isArray(block.elements) ? block.elements : []).map((element) => {
|
|
1352
|
-
const record = asOptionalRecord(element);
|
|
1353
|
-
return readTextObject(record, options) ?? readAltText(record?.alt_text);
|
|
1354
|
-
}) : [
|
|
1355
|
-
readTextObject(block.text, options),
|
|
1356
|
-
...Array.isArray(block.fields) ? block.fields.map((field) => readTextObject(field, options)) : [],
|
|
1357
|
-
readControlElementText(block.accessory, options)
|
|
1358
|
-
]).filter(Boolean).join(context ? " " : "\n") || void 0;
|
|
1359
|
-
}
|
|
1360
|
-
/** Read only user-visible text from one Slack block. */
|
|
1361
|
-
function renderSlackBlockFallbackText(raw, options = {}) {
|
|
1362
|
-
const block = asOptionalRecord(raw);
|
|
1363
|
-
if (!block) return;
|
|
1364
|
-
switch (block.type) {
|
|
1365
|
-
case "rich_text": return normalizeOptionalString(renderSlackRichText(block.elements, options.nativeReferenceFormat === "plain" ? "native-reference" : "escaped", "\n"));
|
|
1366
|
-
case "header": return readTextObject(block.text, options);
|
|
1367
|
-
case "section":
|
|
1368
|
-
case "context": return readTextLayout(block, options);
|
|
1369
|
-
case "image": return readAltText(block.alt_text) ?? readTextObject(block.title) ?? "Shared an image";
|
|
1370
|
-
case "video": return readTextObject(block.title, options) ?? readAltText(block.alt_text) ?? "Shared a video";
|
|
1371
|
-
case "file": return "Shared a file";
|
|
1372
|
-
case "actions": return Array.isArray(block.elements) ? readControlElementsText(block.elements, options) : void 0;
|
|
1373
|
-
case "data_visualization": return renderSlackDataVisualizationFallbackText(block, options.nativeDataFormat !== "plain");
|
|
1374
|
-
case "data_table": return renderSlackDataTableFallbackText(block, options.nativeDataFormat !== "plain");
|
|
1375
|
-
case "table": return renderSlackTableFallbackText(block, options.nativeDataFormat !== "plain");
|
|
1376
|
-
default: return;
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
function buildSlackBlocksFallbackText(blocks) {
|
|
1380
|
-
for (const block of blocks) {
|
|
1381
|
-
const text = renderSlackBlockFallbackText(block);
|
|
1382
|
-
if (text) return text;
|
|
1383
|
-
}
|
|
1384
|
-
return "Shared a Block Kit message";
|
|
1385
|
-
}
|
|
1386
|
-
function buildSlackCompleteBlocksFallbackText(blocks, options = {}) {
|
|
1387
|
-
return blocks.map((block) => renderSlackBlockFallbackText(block, options)).filter(Boolean).join("\n\n").trim() || buildSlackBlocksFallbackText(blocks);
|
|
1388
|
-
}
|
|
1389
1479
|
// extensions/slack/src/native-data-blocks.ts
|
|
1390
1480
|
const SLACK_MALFORMED_NATIVE_DATA_FALLBACK = "Slack could not render this chart or table data.";
|
|
1391
1481
|
const SLACK_RESPONSE_URL_BODY_LIMIT_BYTES = 16384;
|
|
@@ -1515,7 +1605,7 @@ function normalizeSlackRouteBindingConfig(cfg) {
|
|
|
1515
1605
|
const bindings = cfg.bindings;
|
|
1516
1606
|
const cached = slackRouteBindingConfigCache.get(cfg);
|
|
1517
1607
|
if (cached && cached.bindingsRef === bindings) return cached.normalizedCfg;
|
|
1518
|
-
if (!
|
|
1608
|
+
if (!bindings) return cfg;
|
|
1519
1609
|
let changed = false;
|
|
1520
1610
|
const normalizedBindings = bindings.map((binding) => {
|
|
1521
1611
|
if (binding.type === "acp" || binding.match.channel.trim().toLowerCase() !== "slack") return binding;
|
|
@@ -1665,6 +1755,113 @@ function qualifySlackRoutePeerId(params) {
|
|
|
1665
1755
|
function qualifySlackConversationId(conversationId, eventScope) {
|
|
1666
1756
|
return eventScope ? `team:${encodeURIComponent(eventScope.teamId)}:${conversationId}` : conversationId;
|
|
1667
1757
|
}
|
|
1758
|
+
// extensions/slack/src/errors.ts
|
|
1759
|
+
const NO_ERROR_DETAIL = "no error detail";
|
|
1760
|
+
const MAX_ERROR_CAUSE_DEPTH = 32;
|
|
1761
|
+
function getSlackWebApiErrorData(error) {
|
|
1762
|
+
if (!(error instanceof Error)) return;
|
|
1763
|
+
const data = error.data;
|
|
1764
|
+
return data && typeof data === "object" ? data : void 0;
|
|
1765
|
+
}
|
|
1766
|
+
function addStringDetail(details, label, value) {
|
|
1767
|
+
if (typeof value !== "string") return;
|
|
1768
|
+
const trimmed = redactSensitiveText(value.trim());
|
|
1769
|
+
if (trimmed) details.push(label ? `${label}: ${trimmed}` : trimmed);
|
|
1770
|
+
}
|
|
1771
|
+
function addScalarDetail(details, label, value) {
|
|
1772
|
+
if (typeof value === "string") {
|
|
1773
|
+
addStringDetail(details, label, value);
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
if (typeof value === "number" || typeof value === "boolean") details.push(`${label}: ${String(value)}`);
|
|
1777
|
+
}
|
|
1778
|
+
function addStringListDetail(details, label, value) {
|
|
1779
|
+
if (!Array.isArray(value)) return;
|
|
1780
|
+
const entries = [];
|
|
1781
|
+
for (const entry of value) addStringDetail(entries, "", entry);
|
|
1782
|
+
if (entries.length) details.push(`${label}: ${entries.join(", ")}`);
|
|
1783
|
+
}
|
|
1784
|
+
function safeStringify(value) {
|
|
1785
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
1786
|
+
try {
|
|
1787
|
+
const result = JSON.stringify(value, (_key, nested) => {
|
|
1788
|
+
if (typeof nested !== "object" || nested === null) return nested;
|
|
1789
|
+
if (seen.has(nested)) return "[Circular]";
|
|
1790
|
+
seen.add(nested);
|
|
1791
|
+
return nested;
|
|
1792
|
+
});
|
|
1793
|
+
return result ? redactSensitiveText(result) : void 0;
|
|
1794
|
+
} catch {
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
function addSlackResponseMetadata(details, value) {
|
|
1799
|
+
if (!isRecord(value)) return;
|
|
1800
|
+
addStringListDetail(details, "scopes", value.scopes);
|
|
1801
|
+
addStringListDetail(details, "accepted", value.acceptedScopes);
|
|
1802
|
+
for (const [key, label] of [["messages", "slack message"], ["warnings", "slack warning"]]) {
|
|
1803
|
+
const entries = value[key];
|
|
1804
|
+
if (Array.isArray(entries)) for (const entry of entries) addStringDetail(details, label, entry);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
function addSlackDataDetails(details, value) {
|
|
1808
|
+
if (!isRecord(value)) return;
|
|
1809
|
+
addScalarDetail(details, "slack error", value.error);
|
|
1810
|
+
addScalarDetail(details, "needed", value.needed);
|
|
1811
|
+
addScalarDetail(details, "provided", value.provided);
|
|
1812
|
+
addSlackResponseMetadata(details, value.response_metadata);
|
|
1813
|
+
}
|
|
1814
|
+
function addRecordDetails(details, value) {
|
|
1815
|
+
addScalarDetail(details, "code", value.code);
|
|
1816
|
+
addScalarDetail(details, "status", value.status);
|
|
1817
|
+
addScalarDetail(details, "statusCode", value.statusCode);
|
|
1818
|
+
addScalarDetail(details, "statusMessage", value.statusMessage);
|
|
1819
|
+
addScalarDetail(details, "retryAfter", value.retryAfter);
|
|
1820
|
+
addScalarDetail(details, "errno", value.errno);
|
|
1821
|
+
addScalarDetail(details, "syscall", value.syscall);
|
|
1822
|
+
addScalarDetail(details, "hostname", value.hostname);
|
|
1823
|
+
addScalarDetail(details, "type", value.type);
|
|
1824
|
+
addStringDetail(details, "statusText", value.statusText);
|
|
1825
|
+
addStringDetail(details, "body", value.body);
|
|
1826
|
+
addSlackDataDetails(details, value.data);
|
|
1827
|
+
if (isRecord(value.response)) {
|
|
1828
|
+
addScalarDetail(details, "response status", value.response.status);
|
|
1829
|
+
addStringDetail(details, "response statusText", value.response.statusText);
|
|
1830
|
+
addSlackDataDetails(details, value.response.data);
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
function collectSlackErrorDetails(error, seen) {
|
|
1834
|
+
const details = [];
|
|
1835
|
+
if (error === void 0 || error === null) return details;
|
|
1836
|
+
if (typeof error === "string") {
|
|
1837
|
+
addStringDetail(details, "", error);
|
|
1838
|
+
return details;
|
|
1839
|
+
}
|
|
1840
|
+
if (error instanceof Error) {
|
|
1841
|
+
addStringDetail(details, "", error.message || error.name);
|
|
1842
|
+
if (error.cause !== void 0) {
|
|
1843
|
+
const cause = formatSlackErrorWithCauses(error.cause, "", seen);
|
|
1844
|
+
if (cause) details.push(`cause: ${cause}`);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
if (isRecord(error)) addRecordDetails(details, error);
|
|
1848
|
+
return details;
|
|
1849
|
+
}
|
|
1850
|
+
function formatSlackErrorWithCauses(error, fallback, seen) {
|
|
1851
|
+
if (error instanceof Error) {
|
|
1852
|
+
if (seen.has(error)) return "[Circular]";
|
|
1853
|
+
if (seen.size >= MAX_ERROR_CAUSE_DEPTH) return "[Cause chain truncated]";
|
|
1854
|
+
seen.add(error);
|
|
1855
|
+
}
|
|
1856
|
+
const details = collectSlackErrorDetails(error, seen);
|
|
1857
|
+
if (details.length > 0) return details.join("; ");
|
|
1858
|
+
if (error === void 0 || error === null) return fallback;
|
|
1859
|
+
if (typeof error === "string" && !error.trim()) return fallback;
|
|
1860
|
+
return safeStringify(error) ?? fallback;
|
|
1861
|
+
}
|
|
1862
|
+
function formatSlackError(error, fallback = NO_ERROR_DETAIL) {
|
|
1863
|
+
return formatSlackErrorWithCauses(error, fallback, /* @__PURE__ */ new Set());
|
|
1864
|
+
}
|
|
1668
1865
|
// extensions/slack/src/channel-policy-state.ts
|
|
1669
1866
|
const slackChannelPolicyBindings = resolveGlobalMap(Symbol.for("openclaw.slack.channel-policy-bindings"), "close-and-restart");
|
|
1670
1867
|
function setSlackChannelPolicyBindings(accountId, bindings) {
|
|
@@ -1676,6 +1873,15 @@ function getSlackChannelPolicyKeys(accountId, channelId) {
|
|
|
1676
1873
|
return [...slackChannelPolicyBindings.get(normalizeAccountId(accountId)) ?? []].filter(([, resolvedId]) => resolvedId === id).map(([key]) => key);
|
|
1677
1874
|
}
|
|
1678
1875
|
// extensions/slack/src/group-policy.ts
|
|
1876
|
+
function hasPotentialSlackNamedPolicy(params) {
|
|
1877
|
+
if (params.allowNameMatching !== true) return false;
|
|
1878
|
+
return Object.entries(params.channels ?? {}).some(([key, entry]) => {
|
|
1879
|
+
if (entry == null || key === "*") return false;
|
|
1880
|
+
const named = !/^(?:channel:)?[CDG][A-Z0-9]+$/i.test(key);
|
|
1881
|
+
const entryAllows = entry.enabled !== false;
|
|
1882
|
+
return named && (params.decision === "allow" ? entryAllows : !entryAllows);
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1679
1885
|
function buildSlackChannelIdCandidates(channelId, teamId, options) {
|
|
1680
1886
|
const trimmedId = channelId?.trim();
|
|
1681
1887
|
if (!trimmedId) return [];
|
|
@@ -1727,6 +1933,103 @@ function resolveSlackGroupToolPolicy(params) {
|
|
|
1727
1933
|
senderE164: params.senderE164
|
|
1728
1934
|
});
|
|
1729
1935
|
}
|
|
1936
|
+
// extensions/slack/src/monitor/ingress-identity.ts
|
|
1937
|
+
const SLACK_USER_NAME_KIND = "plugin:slack-user-name";
|
|
1938
|
+
const SLACK_WORKSPACE_USER_ID_KIND = "plugin:slack-workspace-user-id";
|
|
1939
|
+
function normalizeSlackUserId(raw) {
|
|
1940
|
+
const value = (raw ?? "").trim().toLowerCase();
|
|
1941
|
+
if (!value) return "";
|
|
1942
|
+
const mention = value.match(/^<@([a-z0-9_]+)>$/i);
|
|
1943
|
+
if (mention?.[1]) return mention[1];
|
|
1944
|
+
return value.replace(/^(slack:|user:)/, "");
|
|
1945
|
+
}
|
|
1946
|
+
function normalizeSlackWorkspaceUserEntry(entry) {
|
|
1947
|
+
const normalized = entry.trim().toLowerCase();
|
|
1948
|
+
if (!normalized) return null;
|
|
1949
|
+
try {
|
|
1950
|
+
const target = parseSlackTarget(normalized);
|
|
1951
|
+
if (target?.kind === "user" && target.teamId) return target.normalized;
|
|
1952
|
+
} catch {
|
|
1953
|
+
return null;
|
|
1954
|
+
}
|
|
1955
|
+
return null;
|
|
1956
|
+
}
|
|
1957
|
+
function normalizeSlackBareUserEntry(entry) {
|
|
1958
|
+
const normalized = entry.trim().toLowerCase();
|
|
1959
|
+
if (!normalized || normalizeSlackWorkspaceUserEntry(normalized)) return null;
|
|
1960
|
+
const userId = normalizeSlackUserId(normalized);
|
|
1961
|
+
return /^[ubw][a-z0-9_]+$/i.test(userId) ? userId : null;
|
|
1962
|
+
}
|
|
1963
|
+
function normalizeSlackStableEntry(entry) {
|
|
1964
|
+
return normalizeSlackBareUserEntry(entry) ?? normalizeSlackWorkspaceUserEntry(entry);
|
|
1965
|
+
}
|
|
1966
|
+
function normalizeSlackNameEntry(entry) {
|
|
1967
|
+
const normalized = entry.trim().toLowerCase();
|
|
1968
|
+
if (!normalized || normalizeSlackStableEntry(normalized)) return null;
|
|
1969
|
+
return normalized.replace(/^slack:/, "") || null;
|
|
1970
|
+
}
|
|
1971
|
+
function normalizeSlackNameSubject(value) {
|
|
1972
|
+
return value.trim().toLowerCase() || null;
|
|
1973
|
+
}
|
|
1974
|
+
function normalizeSlackNameSlugEntry(entry) {
|
|
1975
|
+
const name = normalizeSlackNameEntry(entry);
|
|
1976
|
+
if (!name) return null;
|
|
1977
|
+
return normalizeSlackSlug(name) || null;
|
|
1978
|
+
}
|
|
1979
|
+
const slackIngressIdentity = defineStableChannelIngressIdentity({
|
|
1980
|
+
resolveParticipant: (subject) => {
|
|
1981
|
+
const qualified = subject.aliases?.workspaceSenderId;
|
|
1982
|
+
if (typeof qualified !== "string") return;
|
|
1983
|
+
const normalized = normalizeSlackWorkspaceUserEntry(qualified);
|
|
1984
|
+
const target = normalized ? parseSlackTarget(normalized) : void 0;
|
|
1985
|
+
return target?.teamId ? {
|
|
1986
|
+
domain: target.teamId,
|
|
1987
|
+
idKind: target.id.startsWith("b") ? "bot-id" : "user-id",
|
|
1988
|
+
id: target.id
|
|
1989
|
+
} : void 0;
|
|
1990
|
+
},
|
|
1991
|
+
key: "senderId",
|
|
1992
|
+
kind: "stable-id",
|
|
1993
|
+
authentication: "asserted",
|
|
1994
|
+
normalizeEntry: normalizeSlackBareUserEntry,
|
|
1995
|
+
normalizeSubject: normalizeSlackUserId,
|
|
1996
|
+
sensitivity: "pii",
|
|
1997
|
+
aliases: [{
|
|
1998
|
+
key: "workspaceSenderId",
|
|
1999
|
+
kind: SLACK_WORKSPACE_USER_ID_KIND,
|
|
2000
|
+
authentication: "asserted",
|
|
2001
|
+
normalizeEntry: normalizeSlackWorkspaceUserEntry,
|
|
2002
|
+
normalizeSubject: normalizeSlackWorkspaceUserEntry,
|
|
2003
|
+
sensitivity: "pii"
|
|
2004
|
+
}, ...[["senderName", normalizeSlackNameEntry], ["senderNameSlug", normalizeSlackNameSlugEntry]].map(([key, normalizeEntry]) => ({
|
|
2005
|
+
key,
|
|
2006
|
+
kind: SLACK_USER_NAME_KIND,
|
|
2007
|
+
normalizeEntry,
|
|
2008
|
+
normalizeSubject: normalizeSlackNameSubject,
|
|
2009
|
+
authentication: "mutable",
|
|
2010
|
+
sensitivity: "pii"
|
|
2011
|
+
}))]
|
|
2012
|
+
});
|
|
2013
|
+
function createSlackIngressSubject(params) {
|
|
2014
|
+
const senderId = normalizeSlackUserId(params.senderId);
|
|
2015
|
+
const teamId = normalizeOptionalLowercaseString(params.teamId);
|
|
2016
|
+
const senderName = params.senderName?.trim().toLowerCase();
|
|
2017
|
+
const senderNameSlug = senderName ? normalizeSlackSlug(senderName) : void 0;
|
|
2018
|
+
return {
|
|
2019
|
+
stableId: params.senderId,
|
|
2020
|
+
authentication: {
|
|
2021
|
+
senderId: params.senderAuthentication ?? "asserted",
|
|
2022
|
+
workspaceSenderId: params.senderAuthentication ?? "asserted",
|
|
2023
|
+
senderName: "mutable",
|
|
2024
|
+
senderNameSlug: "mutable"
|
|
2025
|
+
},
|
|
2026
|
+
aliases: {
|
|
2027
|
+
workspaceSenderId: teamId && senderId ? `team:${teamId}:user:${senderId}` : void 0,
|
|
2028
|
+
senderName,
|
|
2029
|
+
senderNameSlug
|
|
2030
|
+
}
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
1730
2033
|
// extensions/slack/src/session-status.ts
|
|
1731
2034
|
const MISSING_STOP_SUBSCRIPTION = "missing_agent_session_stopped_event_subscription";
|
|
1732
2035
|
let warnedMissingStopSubscription = false;
|
|
@@ -1768,4 +2071,64 @@ async function renameSlackSession(params) {
|
|
|
1768
2071
|
return false;
|
|
1769
2072
|
}
|
|
1770
2073
|
}
|
|
1771
|
-
|
|
2074
|
+
// extensions/slack/src/probe.ts
|
|
2075
|
+
var probe_exports = /* @__PURE__ */ __exportAll({ probeSlack: () => probeSlack });
|
|
2076
|
+
async function probeSlack(token, timeoutMs = 2500, opts) {
|
|
2077
|
+
const dispatcher = createSlackProbeDispatcher(timeoutMs);
|
|
2078
|
+
const client = createSlackReadClient(token, {
|
|
2079
|
+
rejectRateLimitedCalls: true,
|
|
2080
|
+
retryConfig: { retries: 0 },
|
|
2081
|
+
timeout: timeoutMs
|
|
2082
|
+
}, dispatcher);
|
|
2083
|
+
const probeResult = runChannelProbe(timeoutMs, async () => {
|
|
2084
|
+
const result = await client.auth.test();
|
|
2085
|
+
if (!result.ok) return {
|
|
2086
|
+
ok: false,
|
|
2087
|
+
status: 200,
|
|
2088
|
+
error: result.error ?? "unknown"
|
|
2089
|
+
};
|
|
2090
|
+
const userIdentity = opts?.identity === "user";
|
|
2091
|
+
let userId = result.user_id;
|
|
2092
|
+
if (userIdentity) {
|
|
2093
|
+
if (result.bot_id?.trim()) return {
|
|
2094
|
+
ok: false,
|
|
2095
|
+
status: 200,
|
|
2096
|
+
error: "Slack auth.test identified a bot token; user identity requires a user OAuth token"
|
|
2097
|
+
};
|
|
2098
|
+
userId = result.user_id?.trim();
|
|
2099
|
+
if (!userId) return {
|
|
2100
|
+
ok: false,
|
|
2101
|
+
status: 200,
|
|
2102
|
+
error: "Slack auth.test returned no human user_id for user identity"
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
const warning = userIdentity ? void 0 : formatSlackBotTokenIdentityWarning({
|
|
2106
|
+
auth: result,
|
|
2107
|
+
accountId: opts?.accountId
|
|
2108
|
+
});
|
|
2109
|
+
const authIdentity = {
|
|
2110
|
+
id: userId,
|
|
2111
|
+
name: result.user
|
|
2112
|
+
};
|
|
2113
|
+
return {
|
|
2114
|
+
ok: true,
|
|
2115
|
+
status: 200,
|
|
2116
|
+
...userIdentity ? { user: authIdentity } : { bot: authIdentity },
|
|
2117
|
+
team: {
|
|
2118
|
+
id: result.team_id,
|
|
2119
|
+
name: result.team
|
|
2120
|
+
},
|
|
2121
|
+
...warning ? { warning } : {}
|
|
2122
|
+
};
|
|
2123
|
+
}, (error) => ({
|
|
2124
|
+
ok: false,
|
|
2125
|
+
status: typeof error.statusCode === "number" ? error.statusCode : null,
|
|
2126
|
+
error: formatSlackError(error)
|
|
2127
|
+
}));
|
|
2128
|
+
try {
|
|
2129
|
+
return await probeResult;
|
|
2130
|
+
} finally {
|
|
2131
|
+
await dispatcher.destroy().catch(() => void 0);
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
export { decodeSlackApprovalAction as $, resolveSlackExecApprovalTarget as $t, isSlackInvalidBlocksResponse as A, resolveSessionSlackOriginTarget as At, SLACK_REPLY_LINK_ACTION_ID as B, allowListMatches as Bt, SLACK_MALFORMED_NATIVE_DATA_FALLBACK as C, markdownToSlackMrkdwnChunks as Ct, createSlackNativeDataBaseTextConsumer as D, isSlackPluginApprovalRequest as Dt, buildSlackNativeDataAccessibilityText as E, isSlackAnyNativeApprovalClientEnabled as Et, truncateSlackText as F, shouldHandleSlackPluginViaForwardingSession as Ft, isSlackCallbackActionId as G, resolveSlackUserAllowed as Gt, SLACK_SELECT_ACTION_IDS as H, normalizeSlackSlug as Ht, truncateSlackTextByUtf8Bytes as I, slackTargetsMatch as It, resolveSlackQuestionActionIds as J, registerSlackInstallationState as Jt, isSlackQuestionActionForQuestion as K, getSlackInstallationKind as Kt, SLACK_BUTTON_ACTION_IDS as L, getSlackApprovalApproversForTeam as Lt, renderSlackNativeDataPlainTextBlock as M, resolveSlackFallbackOriginTarget as Mt, stripSlackNativeDataBlocks as N, resolveTurnSourceSlackOriginTarget as Nt, hasSlackNativeDataBlock as O, normalizeSlackForwardTarget as Ot, countSlackTextUtf8Bytes as P, shouldHandleSlackNativeApprovalRequest as Pt, SLACK_APPROVAL_HEADER_BLOCK_ID as Q, normalizeSlackApproverId as Qt, SLACK_QUESTION_FINALIZATION_BLOCKS as R, isSlackPluginApprovalAuthorizedSender as Rt, assertSlackDetachedTargetAllowed as S, chunkSlackMrkdwnText as St, appendSlackNativeDataPlainTextFallback as T, escapeSlackMrkdwn as Tt, SLACK_SESSION_LINK_ACTION_ID as U, resolveSlackAllowListMatch as Ut, SLACK_REPLY_SELECT_ACTION_ID as V, normalizeSlackAllowOwnerEntry as Vt, isSlackApprovalActionId as W, resolveSlackUserAllowListForTeam as Wt, encodeSlackQuestionAction as X, isSlackExecApprovalAuthorizedSender as Xt, decodeSlackQuestionAction as Y, getSlackExecApprovalApprovers as Yt, resolveSlackQuestionAction as Z, isSlackExecApprovalClientEnabled as Zt, qualifySlackRoutePeerId as _, renderSlackMessagePresentationFallbackText as _t, SLACK_USER_NAME_KIND as a, buildSlackCompleteBlocksFallbackText as at, resolveSlackConversationBindingRoute as b, SLACK_MESSAGE_TEXT_RECOMMENDED_LIMIT as bt, buildSlackChannelIdCandidates as c, hasSlackDataVisualizationBlock as ct, resolveSlackGroupRequireMention as d, countSlackDataTableBlocksCellCharacters as dt, shouldSuppressLocalSlackExecApprovalPrompt as en, encodeSlackApprovalAction as et, resolveSlackGroupToolPolicy as f, countSlackDataTableCellCharacters as ft, qualifySlackConversationId as g, renderSlackMessagePresentationChartFallbackText as gt, getSlackWebApiErrorData as h, SLACK_SECTION_TEXT_MAX as ht, setSlackSessionStatus as i, resolveSlackThreadTsFromContext as in, buildSlackBlocksFallbackText as it, isSlackNativeResponseUrlRejection as j, resolveSlackApproverDmTargets as jt, isSlackInvalidBlocksError as k, normalizeSlackOriginTarget as kt, buildSlackChannelPolicyScope as l, SLACK_DATA_TABLE_AGGREGATE_CELL_CHARACTERS_MAX as lt, formatSlackError as m, SLACK_PRESENTATION_CAPABILITIES as mt, probe_exports as n, SLACK_PRIVATE_ACTION_DELIVERY_RESULT as nn, parseSlackBlocksInput as nt, createSlackIngressSubject as o, renderSlackBlockFallbackText as ot, setSlackChannelPolicyBindings as p, SLACK_BUTTON_VALUE_MAX as pt, isSlackQuestionActionId as q, isSlackWorkspaceInstallation as qt, renameSlackSession as r, resolveSlackAutoThreadId as rn, validateSlackBlocksArray as rt, slackIngressIdentity as s, buildSlackDataVisualizationBlock as st, probeSlack as t, resolvePluginApprovalSlackApprovers as tn, SLACK_MAX_BLOCKS as tt, hasPotentialSlackNamedPolicy as u, buildSlackDataTableBlock as ut, resolveSlackAgentRoute as v, SLACK_EDIT_TEXT_MAX_BYTES as vt, appendSlackNativeDataFallbackText as w, normalizeSlackOutboundText as wt, resolveSlackConversationBindingRouteAsync as x, SLACK_TEXT_LIMIT as xt, normalizeSlackRouteBindingConfig as y, SLACK_MESSAGE_TEXT_HARD_LIMIT as yt, SLACK_REPLY_BUTTON_ACTION_ID as z, resolveSlackApprovalTeamId as zt };
|