@openclaw/slack 2026.7.2-beta.3 → 2026.7.2-beta.5
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/README.md +1 -1
- package/dist/account-inspect-api.js +1 -1
- package/dist/{account-inspect-Bvbtu3iH.js → account-inspect-jn1xLaVi.js} +2 -2
- package/dist/account-reply-mode-DrUXogvO.js +14 -0
- package/dist/{accounts-DQEbYKh9.js → accounts-BSr252c_.js} +6 -27
- package/dist/{accounts.runtime-BIKmJdtJ.js → accounts.runtime-CjULMLTa.js} +1 -1
- package/dist/{action-runtime-DqkRwO3Z.js → action-runtime-BO3HK_50.js} +7 -7
- package/dist/action-runtime.runtime-DbEcqEXC.js +2 -0
- package/dist/{actions-CF0tUY5q.js → actions-BkOaiDSX.js} +6 -6
- package/dist/{actions.runtime-hCbx4RXg.js → actions.runtime-Do-iOL51.js} +1 -1
- package/dist/api.js +15 -15
- package/dist/{approval-handler.runtime-Em8I1-8-.js → approval-handler.runtime-BAnGN7w0.js} +2 -2
- package/dist/{channel-4-TRi1D6.js → channel-Cnrlie9S.js} +26 -36
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-type-Cu1yjNg2.js → channel-type-CPDX8EV-.js} +4 -4
- package/dist/{channel.setup-CgkADtZg.js → channel.setup-DyZdLwUB.js} +6 -25
- package/dist/{setup-core-CeiAVwQ3.js → config-adapter-Bb8hn2tM.js} +141 -72
- package/dist/{config-schema-XjJGfwQC.js → config-schema-BajbuJGL.js} +229 -30
- package/dist/{directory-config-Bv0lcUlH.js → directory-config-CeixfVuC.js} +2 -2
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-CzUIB7th.js → directory-live-CUwGPX2d.js} +3 -3
- package/dist/doctor-contract-CsGH1Dfc.js +158 -0
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{group-policy-Bw-65m3V.js → group-policy-CthIXE2T.js} +48 -7
- package/dist/{message-tool-api-C2pcf5du.js → message-tool-api-CvKoWYYQ.js} +1 -6
- package/dist/message-tool-api.js +1 -1
- package/dist/{monitor-CNFeF30h.js → monitor-Cb5ZsOTY.js} +4 -4
- package/dist/{outbound-adapter-CVi2uok0.js → outbound-adapter-sGrozuRg.js} +11 -68
- package/dist/{pipeline.runtime-CTvokbty.js → pipeline.runtime-mFCCSllA.js} +2173 -1999
- package/dist/{policy-b_dOPDCq.js → policy-DVg6FyMP.js} +1 -1
- package/dist/{probe-Borhn9PQ.js → probe-BhQTDVBQ.js} +51 -58
- package/dist/{provider-B-HVL5ZE.js → provider-PSppCU_K.js} +435 -252
- package/dist/{relay-source-Ea_P9VcO.js → relay-source-D0mb_JBk.js} +1 -1
- package/dist/{replies-B4bOdrYb.js → replies-BkPPM2Ri.js} +50 -13
- package/dist/{resolve-channels-KTc568SU.js → resolve-channels-B3RlJKyV.js} +2 -2
- package/dist/{resolve-users-DeGJf1yp.js → resolve-users-DTm7PEMa.js} +2 -2
- package/dist/{rolldown-runtime-D7D4PA-g.js → rolldown-runtime-8H4AJuhK.js} +1 -0
- package/dist/{runtime-api-BFfNpzDe.js → runtime-api-jpn3vQ7e.js} +3 -2
- package/dist/runtime-api.js +11 -11
- package/dist/{scopes-lx08L56K.js → scopes-B2SqZ--4.js} +1 -1
- package/dist/{security-audit-CikQhBUY.js → security-audit-DR097212.js} +1 -11
- package/dist/security-contract-api.js +1 -1
- package/dist/{send-KuTDky7i.js → send-2IoDXPeB.js} +8 -22
- package/dist/{send.runtime-CXyxws1D.js → send.runtime-BKd_PAEw.js} +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-DLA0vHXp.js → setup-surface-Dyam58nO.js} +47 -39
- package/dist/{shared-C4yVB0Q-.js → shared-g55DV75U.js} +16 -37
- package/dist/{slash-dispatch.runtime-BT-mMm-f.js → slash-dispatch.runtime-BRQbvn2e.js} +10 -9
- package/node_modules/@slack/bolt/dist/App.d.ts +4 -19
- package/node_modules/@slack/bolt/dist/App.d.ts.map +1 -1
- package/node_modules/@slack/bolt/dist/App.js +21 -40
- package/node_modules/@slack/bolt/dist/App.js.map +1 -1
- package/node_modules/@slack/bolt/dist/context/create-respond.d.ts +3 -3
- package/node_modules/@slack/bolt/dist/context/create-respond.d.ts.map +1 -1
- package/node_modules/@slack/bolt/dist/context/create-respond.js +13 -2
- package/node_modules/@slack/bolt/dist/context/create-respond.js.map +1 -1
- package/node_modules/@slack/bolt/dist/errors.d.ts +6 -8
- package/node_modules/@slack/bolt/dist/errors.d.ts.map +1 -1
- package/node_modules/@slack/bolt/dist/errors.js +11 -11
- package/node_modules/@slack/bolt/dist/errors.js.map +1 -1
- package/node_modules/@slack/bolt/dist/helpers.d.ts.map +1 -1
- package/node_modules/@slack/bolt/dist/helpers.js +1 -2
- package/node_modules/@slack/bolt/dist/helpers.js.map +1 -1
- package/node_modules/@slack/bolt/dist/index.d.ts +0 -1
- package/node_modules/@slack/bolt/dist/index.d.ts.map +1 -1
- package/node_modules/@slack/bolt/dist/index.js +1 -3
- package/node_modules/@slack/bolt/dist/index.js.map +1 -1
- package/node_modules/@slack/bolt/dist/receivers/AwsLambdaReceiver.d.ts +10 -1
- package/node_modules/@slack/bolt/dist/receivers/AwsLambdaReceiver.d.ts.map +1 -1
- package/node_modules/@slack/bolt/dist/receivers/AwsLambdaReceiver.js +5 -1
- package/node_modules/@slack/bolt/dist/receivers/AwsLambdaReceiver.js.map +1 -1
- package/node_modules/@slack/bolt/dist/receivers/HTTPModuleFunctions.d.ts.map +1 -1
- package/node_modules/@slack/bolt/dist/receivers/HTTPModuleFunctions.js +10 -14
- package/node_modules/@slack/bolt/dist/receivers/HTTPModuleFunctions.js.map +1 -1
- package/node_modules/@slack/bolt/dist/receivers/SocketModeFunctions.d.ts.map +1 -1
- package/node_modules/@slack/bolt/dist/receivers/SocketModeFunctions.js +1 -1
- package/node_modules/@slack/bolt/dist/receivers/SocketModeFunctions.js.map +1 -1
- package/node_modules/@slack/bolt/dist/receivers/SocketModeReceiver.d.ts +3 -1
- package/node_modules/@slack/bolt/dist/receivers/SocketModeReceiver.d.ts.map +1 -1
- package/node_modules/@slack/bolt/dist/receivers/SocketModeReceiver.js +2 -1
- package/node_modules/@slack/bolt/dist/receivers/SocketModeReceiver.js.map +1 -1
- package/node_modules/@slack/bolt/dist/types/actions/index.d.ts +2 -4
- package/node_modules/@slack/bolt/dist/types/actions/index.d.ts.map +1 -1
- package/node_modules/@slack/bolt/dist/types/actions/index.js +0 -2
- package/node_modules/@slack/bolt/dist/types/actions/index.js.map +1 -1
- package/node_modules/@slack/bolt/dist/types/events/index.d.ts +2 -0
- package/node_modules/@slack/bolt/dist/types/events/index.d.ts.map +1 -1
- package/node_modules/@slack/bolt/dist/types/utilities.d.ts +2 -2
- package/node_modules/@slack/bolt/dist/types/utilities.d.ts.map +1 -1
- package/node_modules/@slack/bolt/dist/types/view/index.d.ts +1 -31
- package/node_modules/@slack/bolt/dist/types/view/index.d.ts.map +1 -1
- package/node_modules/@slack/bolt/package.json +10 -11
- package/node_modules/@slack/logger/README.md +1 -1
- package/node_modules/@slack/logger/dist/index.js +17 -13
- package/node_modules/@slack/logger/dist/index.js.map +1 -1
- package/node_modules/@slack/logger/package.json +6 -7
- package/node_modules/@slack/oauth/README.md +1 -1
- package/node_modules/@slack/oauth/dist/errors.d.ts +25 -14
- package/node_modules/@slack/oauth/dist/errors.d.ts.map +1 -1
- package/node_modules/@slack/oauth/dist/errors.js +24 -33
- package/node_modules/@slack/oauth/dist/errors.js.map +1 -1
- package/node_modules/@slack/oauth/dist/install-provider.js +58 -16
- package/node_modules/@slack/oauth/dist/install-provider.js.map +1 -1
- package/node_modules/@slack/oauth/dist/installation-stores/file-store.js +4 -2
- package/node_modules/@slack/oauth/dist/installation-stores/file-store.js.map +1 -1
- package/node_modules/@slack/oauth/dist/installation-stores/memory-store.js +3 -16
- package/node_modules/@slack/oauth/dist/installation-stores/memory-store.js.map +1 -1
- package/node_modules/@slack/oauth/dist/state-stores/clear-state-store.js +2 -0
- package/node_modules/@slack/oauth/dist/state-stores/clear-state-store.js.map +1 -1
- package/node_modules/@slack/oauth/dist/state-stores/file-state-store.js +3 -0
- package/node_modules/@slack/oauth/dist/state-stores/file-state-store.js.map +1 -1
- package/node_modules/@slack/oauth/package.json +8 -9
- package/node_modules/@slack/socket-mode/README.md +1 -1
- package/node_modules/@slack/socket-mode/dist/package.json +21 -13
- package/node_modules/@slack/socket-mode/dist/src/SlackWebSocket.d.ts +14 -8
- package/node_modules/@slack/socket-mode/dist/src/SlackWebSocket.d.ts.map +1 -1
- package/node_modules/@slack/socket-mode/dist/src/SlackWebSocket.js +136 -48
- package/node_modules/@slack/socket-mode/dist/src/SlackWebSocket.js.map +1 -1
- package/node_modules/@slack/socket-mode/dist/src/SocketModeClient.d.ts +12 -6
- package/node_modules/@slack/socket-mode/dist/src/SocketModeClient.d.ts.map +1 -1
- package/node_modules/@slack/socket-mode/dist/src/SocketModeClient.js +87 -38
- package/node_modules/@slack/socket-mode/dist/src/SocketModeClient.js.map +1 -1
- package/node_modules/@slack/socket-mode/dist/src/SocketModeOptions.d.ts +31 -1
- package/node_modules/@slack/socket-mode/dist/src/SocketModeOptions.d.ts.map +1 -1
- package/node_modules/@slack/socket-mode/dist/src/errors.d.ts +31 -37
- package/node_modules/@slack/socket-mode/dist/src/errors.d.ts.map +1 -1
- package/node_modules/@slack/socket-mode/dist/src/errors.js +43 -47
- package/node_modules/@slack/socket-mode/dist/src/errors.js.map +1 -1
- package/node_modules/@slack/socket-mode/dist/src/index.d.ts +2 -2
- package/node_modules/@slack/socket-mode/dist/src/index.d.ts.map +1 -1
- package/node_modules/@slack/socket-mode/dist/src/index.js +7 -1
- package/node_modules/@slack/socket-mode/dist/src/index.js.map +1 -1
- package/node_modules/@slack/socket-mode/package.json +21 -13
- package/node_modules/@slack/types/README.md +1 -1
- package/node_modules/@slack/types/dist/events/app.d.ts +28 -0
- package/node_modules/@slack/types/dist/events/app.d.ts.map +1 -1
- package/node_modules/@slack/types/dist/events/index.d.ts +2 -2
- package/node_modules/@slack/types/dist/events/index.d.ts.map +1 -1
- package/node_modules/@slack/types/dist/events/index.js.map +1 -1
- package/node_modules/@slack/types/dist/events/message.d.ts +2 -0
- package/node_modules/@slack/types/dist/events/message.d.ts.map +1 -1
- package/node_modules/@slack/types/package.json +3 -3
- package/node_modules/@slack/web-api/README.md +1 -1
- package/node_modules/@slack/web-api/dist/WebClient.d.ts +42 -71
- package/node_modules/@slack/web-api/dist/WebClient.d.ts.map +1 -1
- package/node_modules/@slack/web-api/dist/WebClient.js +180 -248
- package/node_modules/@slack/web-api/dist/WebClient.js.map +1 -1
- package/node_modules/@slack/web-api/dist/chat-stream.js +37 -22
- package/node_modules/@slack/web-api/dist/chat-stream.js.map +1 -1
- package/node_modules/@slack/web-api/dist/errors.d.ts +32 -51
- package/node_modules/@slack/web-api/dist/errors.d.ts.map +1 -1
- package/node_modules/@slack/web-api/dist/errors.js +64 -55
- package/node_modules/@slack/web-api/dist/errors.js.map +1 -1
- package/node_modules/@slack/web-api/dist/file-upload.d.ts +0 -9
- package/node_modules/@slack/web-api/dist/file-upload.d.ts.map +1 -1
- package/node_modules/@slack/web-api/dist/file-upload.js +41 -46
- package/node_modules/@slack/web-api/dist/file-upload.js.map +1 -1
- package/node_modules/@slack/web-api/dist/index.d.ts +2 -2
- package/node_modules/@slack/web-api/dist/index.d.ts.map +1 -1
- package/node_modules/@slack/web-api/dist/index.js +8 -1
- package/node_modules/@slack/web-api/dist/index.js.map +1 -1
- package/node_modules/@slack/web-api/dist/methods.d.ts +2 -38
- package/node_modules/@slack/web-api/dist/methods.d.ts.map +1 -1
- package/node_modules/@slack/web-api/dist/methods.js +1347 -1384
- package/node_modules/@slack/web-api/dist/methods.js.map +1 -1
- package/node_modules/@slack/web-api/dist/types/request/files.d.ts +0 -1
- package/node_modules/@slack/web-api/dist/types/request/files.d.ts.map +1 -1
- package/node_modules/@slack/web-api/dist/types/request/index.d.ts +3 -3
- package/node_modules/@slack/web-api/dist/types/request/index.d.ts.map +1 -1
- package/node_modules/@slack/web-api/dist/types/request/manifest.d.ts +66 -1
- package/node_modules/@slack/web-api/dist/types/request/manifest.d.ts.map +1 -1
- package/node_modules/@slack/web-api/dist/types/request/rtm.d.ts +1 -14
- package/node_modules/@slack/web-api/dist/types/request/rtm.d.ts.map +1 -1
- package/node_modules/@slack/web-api/dist/types/request/workflows.d.ts +0 -29
- package/node_modules/@slack/web-api/dist/types/request/workflows.d.ts.map +1 -1
- package/node_modules/@slack/web-api/package.json +6 -10
- package/node_modules/@types/node/README.md +1 -1
- package/node_modules/@types/node/fs/promises.d.ts +16 -6
- package/node_modules/@types/node/package.json +2 -2
- package/node_modules/@types/retry/LICENSE +21 -21
- package/node_modules/@types/retry/README.md +5 -4
- package/node_modules/@types/retry/index.d.ts +20 -13
- package/node_modules/@types/retry/package.json +9 -11
- package/node_modules/iconv-lite/encodings/sbcs-data.js +2 -0
- package/node_modules/iconv-lite/encodings/utf32.js +10 -3
- package/node_modules/iconv-lite/package.json +2 -2
- package/node_modules/iconv-lite/types/encodings.d.ts +2 -0
- package/node_modules/path-to-regexp/Readme.md +3 -3
- package/node_modules/path-to-regexp/dist/index.js +135 -158
- package/node_modules/path-to-regexp/dist/index.js.map +1 -1
- package/node_modules/path-to-regexp/package.json +2 -2
- package/node_modules/qs/CHANGELOG.md +11 -0
- package/node_modules/qs/dist/qs.js +17 -17
- package/node_modules/qs/lib/parse.js +19 -8
- package/node_modules/qs/lib/utils.js +47 -8
- package/node_modules/qs/package.json +6 -5
- package/node_modules/qs/test/parse.js +230 -0
- package/node_modules/qs/test/utils.js +194 -0
- package/node_modules/typebox/build/compile/validator.d.mts +1 -4
- package/node_modules/typebox/build/guard/guard.d.mts +1 -1
- package/node_modules/typebox/build/guard/guard.mjs +5 -2
- package/node_modules/typebox/build/guard/string.mjs +16 -4
- package/node_modules/typebox/build/schema/engine/_functions.mjs +34 -15
- package/node_modules/typebox/build/type/action/readonly_object.d.mts +5 -0
- package/node_modules/typebox/build/type/action/readonly_object.mjs +8 -0
- package/node_modules/typebox/build/typebox.d.mts +1 -1
- package/node_modules/typebox/build/typebox.mjs +1 -1
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +29 -32
- package/node_modules/{es-set-tostringtag → undici}/LICENSE +1 -1
- package/node_modules/undici/README.md +741 -0
- package/node_modules/undici/docs/docs/api/Agent.md +84 -0
- package/node_modules/undici/docs/docs/api/BalancedPool.md +99 -0
- package/node_modules/undici/docs/docs/api/CacheStorage.md +30 -0
- package/node_modules/undici/docs/docs/api/CacheStore.md +164 -0
- package/node_modules/undici/docs/docs/api/Client.md +288 -0
- package/node_modules/undici/docs/docs/api/ClientStats.md +27 -0
- package/node_modules/undici/docs/docs/api/Connector.md +115 -0
- package/node_modules/undici/docs/docs/api/ContentType.md +57 -0
- package/node_modules/undici/docs/docs/api/Cookies.md +128 -0
- package/node_modules/undici/docs/docs/api/Debug.md +62 -0
- package/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +315 -0
- package/node_modules/undici/docs/docs/api/Dispatcher.md +1392 -0
- package/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +159 -0
- package/node_modules/undici/docs/docs/api/Errors.md +49 -0
- package/node_modules/undici/docs/docs/api/EventSource.md +45 -0
- package/node_modules/undici/docs/docs/api/Fetch.md +60 -0
- package/node_modules/undici/docs/docs/api/GlobalInstallation.md +139 -0
- package/node_modules/undici/docs/docs/api/H2CClient.md +263 -0
- package/node_modules/undici/docs/docs/api/MockAgent.md +603 -0
- package/node_modules/undici/docs/docs/api/MockCallHistory.md +197 -0
- package/node_modules/undici/docs/docs/api/MockCallHistoryLog.md +43 -0
- package/node_modules/undici/docs/docs/api/MockClient.md +81 -0
- package/node_modules/undici/docs/docs/api/MockErrors.md +12 -0
- package/node_modules/undici/docs/docs/api/MockPool.md +555 -0
- package/node_modules/undici/docs/docs/api/Pool.md +84 -0
- package/node_modules/undici/docs/docs/api/PoolStats.md +35 -0
- package/node_modules/undici/docs/docs/api/ProxyAgent.md +229 -0
- package/node_modules/undici/docs/docs/api/RedirectHandler.md +93 -0
- package/node_modules/undici/docs/docs/api/RetryAgent.md +50 -0
- package/node_modules/undici/docs/docs/api/RetryHandler.md +118 -0
- package/node_modules/undici/docs/docs/api/RoundRobinPool.md +145 -0
- package/node_modules/undici/docs/docs/api/SnapshotAgent.md +616 -0
- package/node_modules/undici/docs/docs/api/Socks5ProxyAgent.md +275 -0
- package/node_modules/undici/docs/docs/api/Util.md +25 -0
- package/node_modules/undici/docs/docs/api/WebSocket.md +141 -0
- package/node_modules/undici/docs/docs/api/api-lifecycle.md +91 -0
- package/node_modules/undici/docs/docs/best-practices/client-certificate.md +64 -0
- package/node_modules/undici/docs/docs/best-practices/crawling.md +58 -0
- package/node_modules/undici/docs/docs/best-practices/mocking-request.md +190 -0
- package/node_modules/undici/docs/docs/best-practices/proxy.md +127 -0
- package/node_modules/undici/docs/docs/best-practices/undici-vs-builtin-fetch.md +224 -0
- package/node_modules/undici/docs/docs/best-practices/writing-tests.md +63 -0
- package/node_modules/undici/index-fetch.js +65 -0
- package/node_modules/undici/index.d.ts +3 -0
- package/node_modules/undici/index.js +234 -0
- package/node_modules/undici/lib/api/abort-signal.js +59 -0
- package/node_modules/undici/lib/api/api-connect.js +110 -0
- package/node_modules/undici/lib/api/api-pipeline.js +252 -0
- package/node_modules/undici/lib/api/api-request.js +214 -0
- package/node_modules/undici/lib/api/api-stream.js +209 -0
- package/node_modules/undici/lib/api/api-upgrade.js +111 -0
- package/node_modules/undici/lib/api/index.js +7 -0
- package/node_modules/undici/lib/api/readable.js +580 -0
- package/node_modules/undici/lib/cache/memory-cache-store.js +234 -0
- package/node_modules/undici/lib/cache/sqlite-cache-store.js +461 -0
- package/node_modules/undici/lib/core/connect.js +153 -0
- package/node_modules/undici/lib/core/constants.js +143 -0
- package/node_modules/undici/lib/core/diagnostics.js +227 -0
- package/node_modules/undici/lib/core/errors.js +477 -0
- package/node_modules/undici/lib/core/request.js +453 -0
- package/node_modules/undici/lib/core/socks5-client.js +422 -0
- package/node_modules/undici/lib/core/socks5-utils.js +212 -0
- package/node_modules/undici/lib/core/symbols.js +75 -0
- package/node_modules/undici/lib/core/tree.js +160 -0
- package/node_modules/undici/lib/core/util.js +992 -0
- package/node_modules/undici/lib/dispatcher/agent.js +158 -0
- package/node_modules/undici/lib/dispatcher/balanced-pool.js +219 -0
- package/node_modules/undici/lib/dispatcher/client-h1.js +1722 -0
- package/node_modules/undici/lib/dispatcher/client-h2.js +995 -0
- package/node_modules/undici/lib/dispatcher/client.js +664 -0
- package/node_modules/undici/lib/dispatcher/dispatcher-base.js +184 -0
- package/node_modules/undici/lib/dispatcher/dispatcher.js +48 -0
- package/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +146 -0
- package/node_modules/undici/lib/dispatcher/fixed-queue.js +135 -0
- package/node_modules/undici/lib/dispatcher/h2c-client.js +51 -0
- package/node_modules/undici/lib/dispatcher/pool-base.js +214 -0
- package/node_modules/undici/lib/dispatcher/pool.js +118 -0
- package/node_modules/undici/lib/dispatcher/proxy-agent.js +319 -0
- package/node_modules/undici/lib/dispatcher/retry-agent.js +35 -0
- package/node_modules/undici/lib/dispatcher/round-robin-pool.js +137 -0
- package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +260 -0
- package/node_modules/undici/lib/encoding/index.js +33 -0
- package/node_modules/undici/lib/global.js +59 -0
- package/node_modules/undici/lib/handler/cache-handler.js +578 -0
- package/node_modules/undici/lib/handler/cache-revalidation-handler.js +124 -0
- package/node_modules/undici/lib/handler/decorator-handler.js +67 -0
- package/node_modules/undici/lib/handler/deduplication-handler.js +460 -0
- package/node_modules/undici/lib/handler/redirect-handler.js +238 -0
- package/node_modules/undici/lib/handler/retry-handler.js +394 -0
- package/node_modules/undici/lib/handler/unwrap-handler.js +106 -0
- package/node_modules/undici/lib/handler/wrap-handler.js +105 -0
- package/node_modules/undici/lib/interceptor/cache.js +495 -0
- package/node_modules/undici/lib/interceptor/decompress.js +259 -0
- package/node_modules/undici/lib/interceptor/deduplicate.js +117 -0
- package/node_modules/undici/lib/interceptor/dns.js +571 -0
- package/node_modules/undici/lib/interceptor/dump.js +112 -0
- package/node_modules/undici/lib/interceptor/redirect.js +21 -0
- package/node_modules/undici/lib/interceptor/response-error.js +95 -0
- package/node_modules/undici/lib/interceptor/retry.js +19 -0
- package/node_modules/undici/lib/llhttp/.gitkeep +0 -0
- package/node_modules/undici/lib/llhttp/constants.d.ts +195 -0
- package/node_modules/undici/lib/llhttp/constants.js +531 -0
- package/node_modules/undici/lib/llhttp/llhttp-wasm.js +15 -0
- package/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +15 -0
- package/node_modules/undici/lib/llhttp/utils.d.ts +2 -0
- package/node_modules/undici/lib/llhttp/utils.js +12 -0
- package/node_modules/undici/lib/mock/mock-agent.js +232 -0
- package/node_modules/undici/lib/mock/mock-call-history.js +248 -0
- package/node_modules/undici/lib/mock/mock-client.js +68 -0
- package/node_modules/undici/lib/mock/mock-errors.js +29 -0
- package/node_modules/undici/lib/mock/mock-interceptor.js +209 -0
- package/node_modules/undici/lib/mock/mock-pool.js +68 -0
- package/node_modules/undici/lib/mock/mock-symbols.js +32 -0
- package/node_modules/undici/lib/mock/mock-utils.js +486 -0
- package/node_modules/undici/lib/mock/pending-interceptors-formatter.js +43 -0
- package/node_modules/undici/lib/mock/snapshot-agent.js +353 -0
- package/node_modules/undici/lib/mock/snapshot-recorder.js +588 -0
- package/node_modules/undici/lib/mock/snapshot-utils.js +158 -0
- package/node_modules/undici/lib/util/cache.js +414 -0
- package/node_modules/undici/lib/util/date.js +653 -0
- package/node_modules/undici/lib/util/promise.js +28 -0
- package/node_modules/undici/lib/util/runtime-features.js +124 -0
- package/node_modules/undici/lib/util/stats.js +32 -0
- package/node_modules/undici/lib/util/timers.js +425 -0
- package/node_modules/undici/lib/web/cache/cache.js +864 -0
- package/node_modules/undici/lib/web/cache/cachestorage.js +152 -0
- package/node_modules/undici/lib/web/cache/util.js +45 -0
- package/node_modules/undici/lib/web/cookies/constants.js +12 -0
- package/node_modules/undici/lib/web/cookies/index.js +199 -0
- package/node_modules/undici/lib/web/cookies/parse.js +314 -0
- package/node_modules/undici/lib/web/cookies/util.js +282 -0
- package/node_modules/undici/lib/web/eventsource/eventsource-stream.js +399 -0
- package/node_modules/undici/lib/web/eventsource/eventsource.js +501 -0
- package/node_modules/undici/lib/web/eventsource/util.js +29 -0
- package/node_modules/{has-tostringtag → undici/lib/web/fetch}/LICENSE +1 -1
- package/node_modules/undici/lib/web/fetch/body.js +509 -0
- package/node_modules/undici/lib/web/fetch/constants.js +131 -0
- package/node_modules/undici/lib/web/fetch/data-url.js +596 -0
- package/node_modules/undici/lib/web/fetch/formdata-parser.js +586 -0
- package/node_modules/undici/lib/web/fetch/formdata.js +259 -0
- package/node_modules/undici/lib/web/fetch/global.js +40 -0
- package/node_modules/undici/lib/web/fetch/headers.js +719 -0
- package/node_modules/undici/lib/web/fetch/index.js +2413 -0
- package/node_modules/undici/lib/web/fetch/request.js +1115 -0
- package/node_modules/undici/lib/web/fetch/response.js +641 -0
- package/node_modules/undici/lib/web/fetch/util.js +1522 -0
- package/node_modules/undici/lib/web/infra/index.js +229 -0
- package/node_modules/undici/lib/web/subresource-integrity/Readme.md +9 -0
- package/node_modules/undici/lib/web/subresource-integrity/subresource-integrity.js +307 -0
- package/node_modules/undici/lib/web/webidl/index.js +1006 -0
- package/node_modules/undici/lib/web/websocket/connection.js +329 -0
- package/node_modules/undici/lib/web/websocket/constants.js +126 -0
- package/node_modules/undici/lib/web/websocket/events.js +331 -0
- package/node_modules/undici/lib/web/websocket/frame.js +133 -0
- package/node_modules/undici/lib/web/websocket/permessage-deflate.js +100 -0
- package/node_modules/undici/lib/web/websocket/receiver.js +507 -0
- package/node_modules/undici/lib/web/websocket/sender.js +109 -0
- package/node_modules/undici/lib/web/websocket/stream/websocketerror.js +104 -0
- package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +498 -0
- package/node_modules/undici/lib/web/websocket/util.js +347 -0
- package/node_modules/undici/lib/web/websocket/websocket.js +758 -0
- package/node_modules/undici/package.json +152 -0
- package/node_modules/undici/scripts/strip-comments.js +10 -0
- package/node_modules/undici/types/README.md +6 -0
- package/node_modules/undici/types/agent.d.ts +32 -0
- package/node_modules/undici/types/api.d.ts +43 -0
- package/node_modules/undici/types/balanced-pool.d.ts +30 -0
- package/node_modules/undici/types/cache-interceptor.d.ts +179 -0
- package/node_modules/undici/types/cache.d.ts +36 -0
- package/node_modules/undici/types/client-stats.d.ts +15 -0
- package/node_modules/undici/types/client.d.ts +139 -0
- package/node_modules/undici/types/connector.d.ts +36 -0
- package/node_modules/undici/types/content-type.d.ts +21 -0
- package/node_modules/undici/types/cookies.d.ts +30 -0
- package/node_modules/undici/types/diagnostics-channel.d.ts +74 -0
- package/node_modules/undici/types/dispatcher.d.ts +275 -0
- package/node_modules/undici/types/env-http-proxy-agent.d.ts +22 -0
- package/node_modules/undici/types/errors.d.ts +177 -0
- package/node_modules/undici/types/eventsource.d.ts +66 -0
- package/node_modules/undici/types/fetch.d.ts +231 -0
- package/node_modules/undici/types/formdata.d.ts +114 -0
- package/node_modules/undici/types/global-dispatcher.d.ts +9 -0
- package/node_modules/undici/types/global-origin.d.ts +7 -0
- package/node_modules/undici/types/h2c-client.d.ts +73 -0
- package/node_modules/undici/types/handlers.d.ts +14 -0
- package/node_modules/undici/types/header.d.ts +160 -0
- package/node_modules/undici/types/index.d.ts +91 -0
- package/node_modules/undici/types/interceptors.d.ts +80 -0
- package/node_modules/undici/types/mock-agent.d.ts +68 -0
- package/node_modules/undici/types/mock-call-history.d.ts +111 -0
- package/node_modules/undici/types/mock-client.d.ts +27 -0
- package/node_modules/undici/types/mock-errors.d.ts +12 -0
- package/node_modules/undici/types/mock-interceptor.d.ts +94 -0
- package/node_modules/undici/types/mock-pool.d.ts +27 -0
- package/node_modules/undici/types/patch.d.ts +29 -0
- package/node_modules/undici/types/pool-stats.d.ts +19 -0
- package/node_modules/undici/types/pool.d.ts +41 -0
- package/node_modules/undici/types/proxy-agent.d.ts +29 -0
- package/node_modules/undici/types/readable.d.ts +68 -0
- package/node_modules/undici/types/retry-agent.d.ts +8 -0
- package/node_modules/undici/types/retry-handler.d.ts +125 -0
- package/node_modules/undici/types/round-robin-pool.d.ts +41 -0
- package/node_modules/undici/types/snapshot-agent.d.ts +109 -0
- package/node_modules/undici/types/socks5-proxy-agent.d.ts +25 -0
- package/node_modules/undici/types/util.d.ts +18 -0
- package/node_modules/undici/types/utility.d.ts +7 -0
- package/node_modules/undici/types/webidl.d.ts +347 -0
- package/node_modules/undici/types/websocket.d.ts +188 -0
- package/node_modules/ws/lib/receiver.js +15 -32
- package/node_modules/ws/lib/websocket-server.js +4 -4
- package/node_modules/ws/lib/websocket.js +4 -4
- package/node_modules/ws/package.json +5 -1
- package/openclaw.plugin.json +1064 -1243
- package/package.json +99 -18
- package/dist/action-runtime.runtime-DEWOcFM4.js +0 -2
- package/dist/config-api-Df9a1B8C.js +0 -3
- package/dist/doctor-contract-DKpe5VBr.js +0 -202
- package/dist/interactive-replies-Dm_t2R9S.js +0 -197
- package/dist/interactive-replies-api.js +0 -2
- package/node_modules/@slack/bolt/dist/WorkflowStep.d.ts +0 -159
- package/node_modules/@slack/bolt/dist/WorkflowStep.d.ts.map +0 -1
- package/node_modules/@slack/bolt/dist/WorkflowStep.js +0 -217
- package/node_modules/@slack/bolt/dist/WorkflowStep.js.map +0 -1
- package/node_modules/@slack/bolt/dist/types/actions/workflow-step-edit.d.ts +0 -49
- package/node_modules/@slack/bolt/dist/types/actions/workflow-step-edit.d.ts.map +0 -1
- package/node_modules/@slack/bolt/dist/types/actions/workflow-step-edit.js +0 -3
- package/node_modules/@slack/bolt/dist/types/actions/workflow-step-edit.js.map +0 -1
- package/node_modules/@types/ws/LICENSE +0 -21
- package/node_modules/@types/ws/README.md +0 -15
- package/node_modules/@types/ws/index.d.mts +0 -451
- package/node_modules/@types/ws/index.d.ts +0 -445
- package/node_modules/@types/ws/package.json +0 -72
- package/node_modules/asynckit/LICENSE +0 -21
- package/node_modules/asynckit/README.md +0 -233
- package/node_modules/asynckit/bench.js +0 -76
- package/node_modules/asynckit/index.js +0 -6
- package/node_modules/asynckit/lib/abort.js +0 -29
- package/node_modules/asynckit/lib/async.js +0 -34
- package/node_modules/asynckit/lib/defer.js +0 -26
- package/node_modules/asynckit/lib/iterate.js +0 -75
- package/node_modules/asynckit/lib/readable_asynckit.js +0 -91
- package/node_modules/asynckit/lib/readable_parallel.js +0 -25
- package/node_modules/asynckit/lib/readable_serial.js +0 -25
- package/node_modules/asynckit/lib/readable_serial_ordered.js +0 -29
- package/node_modules/asynckit/lib/state.js +0 -37
- package/node_modules/asynckit/lib/streamify.js +0 -141
- package/node_modules/asynckit/lib/terminator.js +0 -29
- package/node_modules/asynckit/package.json +0 -63
- package/node_modules/asynckit/parallel.js +0 -43
- package/node_modules/asynckit/serial.js +0 -17
- package/node_modules/asynckit/serialOrdered.js +0 -75
- package/node_modules/asynckit/stream.js +0 -21
- package/node_modules/axios/CHANGELOG.md +0 -1676
- package/node_modules/axios/LICENSE +0 -7
- package/node_modules/axios/MIGRATION_GUIDE.md +0 -877
- package/node_modules/axios/README.md +0 -2391
- package/node_modules/axios/dist/axios.js +0 -4864
- package/node_modules/axios/dist/axios.js.map +0 -1
- package/node_modules/axios/dist/axios.min.js +0 -5
- package/node_modules/axios/dist/axios.min.js.map +0 -1
- package/node_modules/axios/dist/browser/axios.cjs +0 -4701
- package/node_modules/axios/dist/browser/axios.cjs.map +0 -1
- package/node_modules/axios/dist/esm/axios.js +0 -4722
- package/node_modules/axios/dist/esm/axios.js.map +0 -1
- package/node_modules/axios/dist/esm/axios.min.js +0 -3
- package/node_modules/axios/dist/esm/axios.min.js.map +0 -1
- package/node_modules/axios/dist/node/axios.cjs +0 -5348
- package/node_modules/axios/dist/node/axios.cjs.map +0 -1
- package/node_modules/axios/index.d.cts +0 -715
- package/node_modules/axios/index.d.ts +0 -734
- package/node_modules/axios/index.js +0 -45
- package/node_modules/axios/lib/adapters/README.md +0 -36
- package/node_modules/axios/lib/adapters/adapters.js +0 -132
- package/node_modules/axios/lib/adapters/fetch.js +0 -469
- package/node_modules/axios/lib/adapters/http.js +0 -1202
- package/node_modules/axios/lib/adapters/xhr.js +0 -226
- package/node_modules/axios/lib/axios.js +0 -89
- package/node_modules/axios/lib/cancel/CancelToken.js +0 -135
- package/node_modules/axios/lib/cancel/CanceledError.js +0 -22
- package/node_modules/axios/lib/cancel/isCancel.js +0 -5
- package/node_modules/axios/lib/core/Axios.js +0 -281
- package/node_modules/axios/lib/core/AxiosError.js +0 -176
- package/node_modules/axios/lib/core/AxiosHeaders.js +0 -380
- package/node_modules/axios/lib/core/InterceptorManager.js +0 -72
- package/node_modules/axios/lib/core/README.md +0 -8
- package/node_modules/axios/lib/core/buildFullPath.js +0 -22
- package/node_modules/axios/lib/core/dispatchRequest.js +0 -89
- package/node_modules/axios/lib/core/mergeConfig.js +0 -124
- package/node_modules/axios/lib/core/settle.js +0 -27
- package/node_modules/axios/lib/core/transformData.js +0 -28
- package/node_modules/axios/lib/defaults/index.js +0 -177
- package/node_modules/axios/lib/defaults/transitional.js +0 -8
- package/node_modules/axios/lib/env/README.md +0 -3
- package/node_modules/axios/lib/env/classes/FormData.js +0 -2
- package/node_modules/axios/lib/env/data.js +0 -1
- package/node_modules/axios/lib/helpers/AxiosTransformStream.js +0 -156
- package/node_modules/axios/lib/helpers/AxiosURLSearchParams.js +0 -61
- package/node_modules/axios/lib/helpers/HttpStatusCode.js +0 -77
- package/node_modules/axios/lib/helpers/README.md +0 -7
- package/node_modules/axios/lib/helpers/ZlibHeaderTransformStream.js +0 -29
- package/node_modules/axios/lib/helpers/bind.js +0 -14
- package/node_modules/axios/lib/helpers/buildURL.js +0 -66
- package/node_modules/axios/lib/helpers/callbackify.js +0 -18
- package/node_modules/axios/lib/helpers/combineURLs.js +0 -15
- package/node_modules/axios/lib/helpers/composeSignals.js +0 -56
- package/node_modules/axios/lib/helpers/cookies.js +0 -60
- package/node_modules/axios/lib/helpers/deprecatedMethod.js +0 -31
- package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +0 -100
- package/node_modules/axios/lib/helpers/formDataToJSON.js +0 -97
- package/node_modules/axios/lib/helpers/formDataToStream.js +0 -119
- package/node_modules/axios/lib/helpers/fromDataURI.js +0 -53
- package/node_modules/axios/lib/helpers/isAbsoluteURL.js +0 -19
- package/node_modules/axios/lib/helpers/isAxiosError.js +0 -14
- package/node_modules/axios/lib/helpers/isURLSameOrigin.js +0 -16
- package/node_modules/axios/lib/helpers/null.js +0 -2
- package/node_modules/axios/lib/helpers/parseHeaders.js +0 -69
- package/node_modules/axios/lib/helpers/parseProtocol.js +0 -6
- package/node_modules/axios/lib/helpers/progressEventReducer.js +0 -51
- package/node_modules/axios/lib/helpers/readBlob.js +0 -15
- package/node_modules/axios/lib/helpers/resolveConfig.js +0 -106
- package/node_modules/axios/lib/helpers/shouldBypassProxy.js +0 -178
- package/node_modules/axios/lib/helpers/speedometer.js +0 -55
- package/node_modules/axios/lib/helpers/spread.js +0 -28
- package/node_modules/axios/lib/helpers/throttle.js +0 -44
- package/node_modules/axios/lib/helpers/toFormData.js +0 -249
- package/node_modules/axios/lib/helpers/toURLEncodedForm.js +0 -19
- package/node_modules/axios/lib/helpers/trackStream.js +0 -89
- package/node_modules/axios/lib/helpers/validator.js +0 -112
- package/node_modules/axios/lib/platform/browser/classes/Blob.js +0 -3
- package/node_modules/axios/lib/platform/browser/classes/FormData.js +0 -3
- package/node_modules/axios/lib/platform/browser/classes/URLSearchParams.js +0 -4
- package/node_modules/axios/lib/platform/browser/index.js +0 -13
- package/node_modules/axios/lib/platform/common/utils.js +0 -52
- package/node_modules/axios/lib/platform/index.js +0 -7
- package/node_modules/axios/lib/platform/node/classes/FormData.js +0 -3
- package/node_modules/axios/lib/platform/node/classes/URLSearchParams.js +0 -4
- package/node_modules/axios/lib/platform/node/index.js +0 -37
- package/node_modules/axios/lib/utils.js +0 -931
- package/node_modules/axios/package.json +0 -183
- package/node_modules/combined-stream/License +0 -19
- package/node_modules/combined-stream/Readme.md +0 -138
- package/node_modules/combined-stream/lib/combined_stream.js +0 -208
- package/node_modules/combined-stream/package.json +0 -25
- package/node_modules/delayed-stream/.npmignore +0 -1
- package/node_modules/delayed-stream/License +0 -19
- package/node_modules/delayed-stream/Makefile +0 -7
- package/node_modules/delayed-stream/Readme.md +0 -141
- package/node_modules/delayed-stream/lib/delayed_stream.js +0 -107
- package/node_modules/delayed-stream/package.json +0 -27
- package/node_modules/es-set-tostringtag/.eslintrc +0 -13
- package/node_modules/es-set-tostringtag/.nycrc +0 -9
- package/node_modules/es-set-tostringtag/CHANGELOG.md +0 -67
- package/node_modules/es-set-tostringtag/README.md +0 -53
- package/node_modules/es-set-tostringtag/index.d.ts +0 -10
- package/node_modules/es-set-tostringtag/index.js +0 -35
- package/node_modules/es-set-tostringtag/package.json +0 -78
- package/node_modules/es-set-tostringtag/test/index.js +0 -85
- package/node_modules/es-set-tostringtag/tsconfig.json +0 -9
- package/node_modules/follow-redirects/LICENSE +0 -18
- package/node_modules/follow-redirects/README.md +0 -157
- package/node_modules/follow-redirects/debug.js +0 -15
- package/node_modules/follow-redirects/http.js +0 -1
- package/node_modules/follow-redirects/https.js +0 -1
- package/node_modules/follow-redirects/index.js +0 -709
- package/node_modules/follow-redirects/package.json +0 -58
- package/node_modules/form-data/CHANGELOG.md +0 -669
- package/node_modules/form-data/License +0 -19
- package/node_modules/form-data/README.md +0 -350
- package/node_modules/form-data/index.d.ts +0 -51
- package/node_modules/form-data/lib/browser.js +0 -4
- package/node_modules/form-data/lib/form_data.js +0 -514
- package/node_modules/form-data/lib/populate.js +0 -10
- package/node_modules/form-data/node_modules/mime-db/HISTORY.md +0 -507
- package/node_modules/form-data/node_modules/mime-db/LICENSE +0 -23
- package/node_modules/form-data/node_modules/mime-db/README.md +0 -100
- package/node_modules/form-data/node_modules/mime-db/db.json +0 -8519
- package/node_modules/form-data/node_modules/mime-db/index.js +0 -12
- package/node_modules/form-data/node_modules/mime-db/package.json +0 -60
- package/node_modules/form-data/node_modules/mime-types/HISTORY.md +0 -397
- package/node_modules/form-data/node_modules/mime-types/LICENSE +0 -23
- package/node_modules/form-data/node_modules/mime-types/README.md +0 -113
- package/node_modules/form-data/node_modules/mime-types/index.js +0 -188
- package/node_modules/form-data/node_modules/mime-types/package.json +0 -44
- package/node_modules/form-data/package.json +0 -83
- package/node_modules/has-tostringtag/.eslintrc +0 -5
- package/node_modules/has-tostringtag/.github/FUNDING.yml +0 -12
- package/node_modules/has-tostringtag/.nycrc +0 -13
- package/node_modules/has-tostringtag/CHANGELOG.md +0 -42
- package/node_modules/has-tostringtag/README.md +0 -46
- package/node_modules/has-tostringtag/index.d.ts +0 -3
- package/node_modules/has-tostringtag/index.js +0 -8
- package/node_modules/has-tostringtag/package.json +0 -108
- package/node_modules/has-tostringtag/shams.d.ts +0 -3
- package/node_modules/has-tostringtag/shams.js +0 -8
- package/node_modules/has-tostringtag/test/index.js +0 -21
- package/node_modules/has-tostringtag/test/shams/core-js.js +0 -31
- package/node_modules/has-tostringtag/test/shams/get-own-property-symbols.js +0 -30
- package/node_modules/has-tostringtag/test/tests.js +0 -15
- package/node_modules/has-tostringtag/tsconfig.json +0 -49
- package/node_modules/is-electron/LICENSE +0 -21
- package/node_modules/is-electron/README.md +0 -25
- package/node_modules/is-electron/index.d.ts +0 -2
- package/node_modules/is-electron/index.js +0 -21
- package/node_modules/is-electron/package.json +0 -34
- package/node_modules/is-stream/index.d.ts +0 -79
- package/node_modules/is-stream/index.js +0 -28
- package/node_modules/is-stream/license +0 -9
- package/node_modules/is-stream/package.json +0 -42
- package/node_modules/is-stream/readme.md +0 -60
- package/node_modules/proxy-from-env/LICENSE +0 -20
- package/node_modules/proxy-from-env/README.md +0 -163
- package/node_modules/proxy-from-env/index.cjs +0 -105
- package/node_modules/proxy-from-env/index.js +0 -103
- package/node_modules/proxy-from-env/package.json +0 -43
- package/npm-shrinkwrap.json +0 -1440
|
@@ -1,2082 +1,2235 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as resolveSlackReplyToMode } from "./account-reply-mode-DrUXogvO.js";
|
|
2
2
|
import { i as parseSlackTarget } from "./target-parsing-N6lHogYZ.js";
|
|
3
3
|
import "./targets-KmjP2E8s.js";
|
|
4
|
-
import { C as hasSlackNativeDataBlock, F as
|
|
5
|
-
import {
|
|
6
|
-
import { r as formatSlackError } from "./probe-Borhn9PQ.js";
|
|
4
|
+
import { A as normalizeSlackOutboundText, C as hasSlackNativeDataBlock, F as SLACK_EDIT_TEXT_LIMIT, L as SLACK_TEXT_LIMIT, V as truncateSlackText, Z as escapeSlackMrkdwn, _ as resolveSlackReplyRenderPlan, c as normalizeSlackAllowOwnerEntry, j as buildSlackBlocksFallbackText, s as normalizeAllowListLower, u as resolveSlackAllowListMatch } from "./group-policy-CthIXE2T.js";
|
|
5
|
+
import { r as formatSlackError } from "./probe-BhQTDVBQ.js";
|
|
7
6
|
import { n as resolveSlackNativeStreaming, r as resolveSlackStreamingMode, t as mapStreamingModeToSlackLegacyDraftStreamMode } from "./streaming-compat-3_X-21wW.js";
|
|
8
|
-
import { S as buildSlackEditTextPayload, _ as resolveSlackThreadHistory, b as resolveSlackBlocksText, f as removeSlackReaction, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage, v as resolveSlackThreadStarter, x as formatSlackFileReference, y as chooseSlackPrimaryText } from "./actions-
|
|
9
|
-
import { c as hasSlackThreadParticipationWithPersistence, l as recordSlackThreadParticipation, r as sendMessageSlack, u as buildSlackNativeDataDeliveryPlan } from "./send-
|
|
10
|
-
import { C as stripSlackMentionsForCommandDetection, _ as readSessionUpdatedAt, b as resolveStorePath, d as resolveSlackCommandIngress, f as resolveSlackEffectiveAllowFrom, g as resolveSlackChatType, h as normalizeSlackChannelType, l as resolveConversationLabel$1, m as parseSlackAssistantThreadMetadata, o as authorizeSlackDirectMessage, p as buildSlackAssistantThreadMetadata, r as resolveSlackRoomContextHints, u as authorizeSlackBotRoomMessage, v as resolveChannelContextVisibilityMode, x as updateLastRoute, y as resolveChannelResetConfig } from "./provider-
|
|
11
|
-
import { n as resolveSlackChannelConfig } from "./policy-
|
|
12
|
-
import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, s as emitSlackMessageSentHooks, t as createSlackReplyDeliveryPlan } from "./replies-
|
|
7
|
+
import { S as buildSlackEditTextPayload, _ as resolveSlackThreadHistory, b as resolveSlackBlocksText, f as removeSlackReaction, l as reactSlackMessage, r as editSlackMessage, t as deleteSlackMessage, v as resolveSlackThreadStarter, x as formatSlackFileReference, y as chooseSlackPrimaryText } from "./actions-BkOaiDSX.js";
|
|
8
|
+
import { c as hasSlackThreadParticipationWithPersistence, l as recordSlackThreadParticipation, r as sendMessageSlack, u as buildSlackNativeDataDeliveryPlan } from "./send-2IoDXPeB.js";
|
|
9
|
+
import { C as stripSlackMentionsForCommandDetection, _ as readSessionUpdatedAt, b as resolveStorePath, d as resolveSlackCommandIngress, f as resolveSlackEffectiveAllowFrom, g as resolveSlackChatType, h as normalizeSlackChannelType, l as resolveConversationLabel$1, m as parseSlackAssistantThreadMetadata, o as authorizeSlackDirectMessage, p as buildSlackAssistantThreadMetadata, r as resolveSlackRoomContextHints, u as authorizeSlackBotRoomMessage, v as resolveChannelContextVisibilityMode, x as updateLastRoute, y as resolveChannelResetConfig } from "./provider-PSppCU_K.js";
|
|
10
|
+
import { n as resolveSlackChannelConfig } from "./policy-DVg6FyMP.js";
|
|
11
|
+
import { a as resolveDeliveredSlackReplyThreadTs, i as readSlackReplyBlocks, n as deliverReplies, o as resolveSlackThreadTs, s as emitSlackMessageSentHooks, t as createSlackReplyDeliveryPlan } from "./replies-BkPPM2Ri.js";
|
|
13
12
|
import { asOptionalRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
14
13
|
import { resolveAgentRoute, resolveInboundLastRouteSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
|
|
15
14
|
import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelMessageReplyPipeline, createChannelProgressDraftCompositor, createChannelProgressReceiptTracker, createDraftStreamLoop, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, formatChannelProgressDraftText, formatPlanChecklistLines, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, resolveAgentOutboundIdentity, resolveChannelMessageSourceReplyDeliveryMode, resolveChannelProgressDraftConfig, resolveChannelProgressDraftMaxLineChars, resolveChannelProgressDraftMaxLines, resolveChannelProgressDraftRender, resolveChannelStreamingBlockEnabled, resolveChannelStreamingNativeTransport, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
|
|
16
15
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
17
|
-
import { danger, logVerbose, shouldLogVerbose
|
|
16
|
+
import { danger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
18
17
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
19
18
|
import { createHash } from "node:crypto";
|
|
20
19
|
import fs from "node:fs/promises";
|
|
21
|
-
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
20
|
+
import { formatErrorMessage, toErrorObject } from "openclaw/plugin-sdk/error-runtime";
|
|
22
21
|
import { asDateTimestampMs, asFiniteNumberInRange, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
23
22
|
import { buildTtsSupplementMediaPayload, getReplyPayloadTtsSupplement, resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
24
23
|
import { filterSupplementalContextItems, resolvePinnedMainDmOwnerFromAllowlist, shouldIncludeSupplementalContext } from "openclaw/plugin-sdk/security-runtime";
|
|
25
24
|
import { ensureConfiguredBindingRouteReady, resolveConfiguredBindingRoute, resolveRuntimeConversationBindingRoute } from "openclaw/plugin-sdk/conversation-runtime";
|
|
26
25
|
import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
27
|
-
import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
|
|
26
|
+
import { resolveDefaultAgentId, resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
|
|
28
27
|
import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
|
|
29
28
|
import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
|
|
30
29
|
import { resolveChannelImplicitMentions } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
31
30
|
import { buildChannelInboundEventContext, buildMentionRegexes, classifyChannelInboundEvent, dispatchChannelInboundTurn, formatInboundEnvelope, hasVisibleInboundReplyDispatch, implicitMentionKindWhen, logInboundDrop, matchesMentionWithExplicit, recordDroppedChannelInboundHistory, resolveEnvelopeFormatOptions, resolveInboundMentionDecision, resolveInboundSupplementalSenderAllowed, resolveUnmentionedGroupInboundPolicy, toInboundMediaFacts } from "openclaw/plugin-sdk/channel-inbound";
|
|
32
|
-
import { DEFAULT_TIMING, createStatusReactionController, logAckFailure, logTypingFailure,
|
|
31
|
+
import { DEFAULT_TIMING, createStatusReactionController, logAckFailure, logTypingFailure, resolveAckReaction, shouldAckReaction } from "openclaw/plugin-sdk/channel-feedback";
|
|
33
32
|
import { hasControlCommand } from "openclaw/plugin-sdk/command-detection";
|
|
34
33
|
import { isAbortRequestText } from "openclaw/plugin-sdk/command-primitives-runtime";
|
|
35
34
|
import { shouldHandleTextCommands } from "openclaw/plugin-sdk/command-surface";
|
|
36
35
|
import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
|
37
36
|
import { runTasksWithConcurrency } from "openclaw/plugin-sdk/concurrency-runtime";
|
|
38
|
-
//#region extensions/slack/src/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const sent = await send(params.target, trimmed, {
|
|
78
|
-
cfg: params.cfg,
|
|
79
|
-
token: params.token,
|
|
80
|
-
accountId: params.accountId,
|
|
81
|
-
threadTs: params.resolveThreadTs?.(),
|
|
82
|
-
identity: params.identity,
|
|
83
|
-
...params.eventScope ? {
|
|
84
|
-
client: params.eventScope.client,
|
|
85
|
-
enterpriseEventScope: params.eventScope
|
|
86
|
-
} : {},
|
|
87
|
-
...params.metadata ? { metadata: params.metadata } : {},
|
|
88
|
-
...blocks ? { blocks } : {}
|
|
89
|
-
});
|
|
90
|
-
streamChannelId = sent.channelId || streamChannelId;
|
|
91
|
-
streamMessageId = sent.messageId || streamMessageId;
|
|
92
|
-
if (!streamChannelId || !streamMessageId) {
|
|
93
|
-
stopped = true;
|
|
94
|
-
params.warn?.("slack stream preview stopped (missing identifiers from sendMessage)");
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
params.onMessageSent?.();
|
|
98
|
-
} catch (err) {
|
|
99
|
-
stopped = true;
|
|
100
|
-
params.warn?.(`slack stream preview failed: ${formatSlackError(err)}`);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
const loop = createDraftStreamLoop({
|
|
104
|
-
throttleMs,
|
|
105
|
-
isStopped: () => stopped,
|
|
106
|
-
sendOrEditStreamMessage
|
|
37
|
+
//#region extensions/slack/src/streaming.ts
|
|
38
|
+
/**
|
|
39
|
+
* Thrown when Slack definitively rejects a stream flush/finalize while text
|
|
40
|
+
* remains buffered locally by the Slack SDK. Carries the pending text so the
|
|
41
|
+
* caller can deliver it via the normal Slack reply path.
|
|
42
|
+
*/
|
|
43
|
+
var SlackStreamNotDeliveredError = class extends Error {
|
|
44
|
+
constructor(pendingText, slackCode) {
|
|
45
|
+
super(`slack-stream: finalize failed with ${slackCode} before buffered text reached Slack (${pendingText.length} chars pending)`);
|
|
46
|
+
this.name = "SlackStreamNotDeliveredError";
|
|
47
|
+
this.pendingText = pendingText;
|
|
48
|
+
this.slackCode = slackCode;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Start a new Slack text stream.
|
|
53
|
+
*
|
|
54
|
+
* Returns a {@link SlackStreamSession} that should be passed to
|
|
55
|
+
* {@link appendSlackStream} and {@link stopSlackStream}.
|
|
56
|
+
*
|
|
57
|
+
* The first chunk of text can optionally be included via `text`.
|
|
58
|
+
*/
|
|
59
|
+
async function startSlackStream(params) {
|
|
60
|
+
const { client, channel, threadTs, text, chunks, taskDisplayMode, teamId, userId, identity } = params;
|
|
61
|
+
const identityPayload = identity?.iconUrl ? {
|
|
62
|
+
...identity.username ? { username: identity.username } : {},
|
|
63
|
+
icon_url: identity.iconUrl
|
|
64
|
+
} : identity?.iconEmoji ? {
|
|
65
|
+
...identity.username ? { username: identity.username } : {},
|
|
66
|
+
icon_emoji: identity.iconEmoji
|
|
67
|
+
} : identity?.username ? { username: identity.username } : {};
|
|
68
|
+
logVerbose(`slack-stream: starting stream in ${channel} thread=${threadTs}${teamId ? ` team=${teamId}` : ""}${userId ? ` user=${userId}` : ""}`);
|
|
69
|
+
const streamer = client.chatStream({
|
|
70
|
+
channel,
|
|
71
|
+
thread_ts: threadTs,
|
|
72
|
+
...taskDisplayMode ? { task_display_mode: taskDisplayMode } : {},
|
|
73
|
+
...teamId ? { recipient_team_id: teamId } : {},
|
|
74
|
+
...userId ? { recipient_user_id: userId } : {},
|
|
75
|
+
...identityPayload
|
|
107
76
|
});
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
77
|
+
const session = {
|
|
78
|
+
streamer,
|
|
79
|
+
channel,
|
|
80
|
+
threadTs,
|
|
81
|
+
stopped: false,
|
|
82
|
+
delivered: false,
|
|
83
|
+
pendingText: ""
|
|
115
84
|
};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const channelId = streamChannelId;
|
|
119
|
-
const messageId = streamMessageId;
|
|
120
|
-
streamChannelId = void 0;
|
|
121
|
-
streamMessageId = void 0;
|
|
122
|
-
lastSentKey = "";
|
|
123
|
-
pendingUpdate = void 0;
|
|
124
|
-
if (!channelId || !messageId) return;
|
|
85
|
+
if (text || chunks?.length) {
|
|
86
|
+
if (text) session.pendingText += text;
|
|
125
87
|
try {
|
|
126
|
-
await
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
...params.eventScope ? { client: params.eventScope.client } : {}
|
|
88
|
+
const result = await streamer.append({
|
|
89
|
+
...text ? { markdown_text: text } : {},
|
|
90
|
+
...chunks?.length ? { chunks } : {}
|
|
130
91
|
});
|
|
92
|
+
if (result) {
|
|
93
|
+
session.delivered = true;
|
|
94
|
+
session.pendingText = "";
|
|
95
|
+
}
|
|
96
|
+
logVerbose(`slack-stream: appended initial payload (${text?.length ?? 0} chars, ${chunks?.length ?? 0} chunks, ${result ? "flushed" : "buffered"})`);
|
|
131
97
|
} catch (err) {
|
|
132
|
-
|
|
98
|
+
if (isBenignSlackFinalizeError(err) && session.pendingText) throw new SlackStreamNotDeliveredError(session.pendingText, extractSlackErrorCode(err) ?? "unknown");
|
|
99
|
+
throw err;
|
|
133
100
|
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
streamMessageId = void 0;
|
|
137
|
-
streamChannelId = void 0;
|
|
138
|
-
lastSentKey = "";
|
|
139
|
-
pendingUpdate = void 0;
|
|
140
|
-
loop.resetPending();
|
|
141
|
-
};
|
|
142
|
-
params.log?.(`slack stream preview ready (maxChars=${maxChars}, throttleMs=${throttleMs})`);
|
|
143
|
-
return {
|
|
144
|
-
update: (update) => {
|
|
145
|
-
const normalized = normalizeUpdate(update);
|
|
146
|
-
pendingUpdate = update;
|
|
147
|
-
loop.update(normalized.text);
|
|
148
|
-
},
|
|
149
|
-
flush: loop.flush,
|
|
150
|
-
clear,
|
|
151
|
-
discardPending,
|
|
152
|
-
seal: discardPending,
|
|
153
|
-
stop,
|
|
154
|
-
forceNewMessage,
|
|
155
|
-
messageId: () => streamMessageId,
|
|
156
|
-
channelId: () => streamChannelId
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
//#endregion
|
|
160
|
-
//#region extensions/slack/src/progress-blocks.ts
|
|
161
|
-
const SLACK_PROGRESS_FIELD_MAX = 1800;
|
|
162
|
-
const DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS = 120;
|
|
163
|
-
const DEFAULT_SLACK_PROGRESS_TASK_DETAIL_MAX_CHARS = 48;
|
|
164
|
-
const SLACK_PROGRESS_CHUNK_TEXT_MAX = 256;
|
|
165
|
-
const SLACK_PROGRESS_TASK_TITLE_MAX = 120;
|
|
166
|
-
const SLACK_PROGRESS_PLAN_FALLBACK_TITLE = "Thinking";
|
|
167
|
-
function field(text) {
|
|
168
|
-
return {
|
|
169
|
-
type: "mrkdwn",
|
|
170
|
-
text: truncateSlackText(text, SLACK_PROGRESS_FIELD_MAX)
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
function resolveMaxLineChars(value, fallback) {
|
|
174
|
-
return value && value > 0 ? Math.floor(value) : fallback;
|
|
175
|
-
}
|
|
176
|
-
function compactDetail(value, maxChars) {
|
|
177
|
-
const normalized = value.replace(/\s+/g, " ").trim();
|
|
178
|
-
const chars = Array.from(normalized);
|
|
179
|
-
if (chars.length <= maxChars) return normalized;
|
|
180
|
-
if (maxChars <= 1) return "…";
|
|
181
|
-
const keepStart = Math.max(1, Math.ceil((maxChars - 1) * .45));
|
|
182
|
-
const keepEnd = Math.max(1, maxChars - keepStart - 1);
|
|
183
|
-
return `${chars.slice(0, keepStart).join("").trimEnd()}…${chars.slice(-keepEnd).join("").trimStart()}`;
|
|
184
|
-
}
|
|
185
|
-
function compactTitle(value) {
|
|
186
|
-
return truncateSlackText(value.replace(/\s+/g, " ").trim(), SLACK_PROGRESS_TASK_TITLE_MAX);
|
|
187
|
-
}
|
|
188
|
-
function compactChunkText(value) {
|
|
189
|
-
return truncateSlackText(value.replace(/\s+/g, " ").trim(), SLACK_PROGRESS_CHUNK_TEXT_MAX);
|
|
190
|
-
}
|
|
191
|
-
function lineDetailParts(line) {
|
|
192
|
-
return [line.detail, line.status && line.status !== "completed" && !line.detail?.includes(line.status) ? line.status : void 0].map((part) => part?.trim()).filter((part) => Boolean(part));
|
|
193
|
-
}
|
|
194
|
-
function legacyLineTitle(line) {
|
|
195
|
-
return `${line.icon ?? "•"} *${escapeSlackMrkdwn(line.label)}*`;
|
|
196
|
-
}
|
|
197
|
-
function legacyLineDetail(line, maxChars) {
|
|
198
|
-
const detail = lineDetailParts(line).join(" · ");
|
|
199
|
-
return detail ? escapeSlackMrkdwn(compactDetail(detail, maxChars)) : "—";
|
|
200
|
-
}
|
|
201
|
-
function lineTaskTitle(line, maxLineChars) {
|
|
202
|
-
const label = line.label.replace(/\s+/g, " ").trim() || line.toolName || line.kind || "Update";
|
|
203
|
-
const detail = lineDetailParts(line).join(" · ") || line.status?.trim();
|
|
204
|
-
const fallback = line.text.replace(/\s+/g, " ").trim();
|
|
205
|
-
if (detail) return compactTitle(`${label} — ${compactDetail(detail, maxLineChars)}`);
|
|
206
|
-
if (fallback && fallback !== label) return compactTitle(fallback);
|
|
207
|
-
return compactTitle(label);
|
|
101
|
+
}
|
|
102
|
+
return session;
|
|
208
103
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Append markdown text to an active Slack stream.
|
|
106
|
+
*/
|
|
107
|
+
async function appendSlackStream(params) {
|
|
108
|
+
const { session, text, chunks } = params;
|
|
109
|
+
if (session.stopped) {
|
|
110
|
+
logVerbose("slack-stream: attempted to append to a stopped stream, ignoring");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (!text && !chunks?.length) return;
|
|
114
|
+
if (text) session.pendingText += text;
|
|
115
|
+
try {
|
|
116
|
+
const result = await session.streamer.append({
|
|
117
|
+
...text ? { markdown_text: text } : {},
|
|
118
|
+
...chunks?.length ? { chunks } : {}
|
|
119
|
+
});
|
|
120
|
+
if (result) {
|
|
121
|
+
session.delivered = true;
|
|
122
|
+
session.pendingText = "";
|
|
123
|
+
}
|
|
124
|
+
logVerbose(`slack-stream: appended ${text?.length ?? 0} chars, ${chunks?.length ?? 0} chunks (${result ? "flushed" : "buffered"})`);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
if (isBenignSlackFinalizeError(err) && session.pendingText) throw new SlackStreamNotDeliveredError(session.pendingText, extractSlackErrorCode(err) ?? "unknown");
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
215
129
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
function
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
function
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Stop (finalize) a Slack stream.
|
|
132
|
+
*
|
|
133
|
+
* After calling this the stream message becomes a normal Slack message.
|
|
134
|
+
* Optionally include final text to append before stopping.
|
|
135
|
+
*
|
|
136
|
+
* If Slack's `chat.stopStream` responds with a definitive recipient/channel
|
|
137
|
+
* rejection while text is still buffered locally, this function throws a
|
|
138
|
+
* {@link SlackStreamNotDeliveredError} carrying that pending text so the caller
|
|
139
|
+
* can deliver it through the normal Slack reply path. Ambiguous failures
|
|
140
|
+
* propagate unchanged because Slack may have committed the request.
|
|
141
|
+
*
|
|
142
|
+
* If Slack responds with a known benign finalize error (see
|
|
143
|
+
* {@link BENIGN_SLACK_FINALIZE_ERROR_CODES}) after prior `append` calls already
|
|
144
|
+
* landed, the error is swallowed and the session is marked stopped - the
|
|
145
|
+
* already-delivered text stays visible.
|
|
146
|
+
*
|
|
147
|
+
* Errors without buffered text propagate unchanged.
|
|
148
|
+
*
|
|
149
|
+
* On success, returns the finalized message's Slack `ts` (when reported) so the
|
|
150
|
+
* caller can emit the `message_sent` hook with a populated `messageId`.
|
|
151
|
+
*/
|
|
152
|
+
async function stopSlackStream(params) {
|
|
153
|
+
const { session, text, chunks, metadata } = params;
|
|
154
|
+
if (session.stopped) {
|
|
155
|
+
logVerbose("slack-stream: stream already stopped, ignoring duplicate stop");
|
|
156
|
+
return {};
|
|
157
|
+
}
|
|
158
|
+
session.stopped = true;
|
|
159
|
+
if (text) session.pendingText += text;
|
|
160
|
+
logVerbose(`slack-stream: stopping stream in ${session.channel} thread=${session.threadTs}${text ? ` (final text: ${text.length} chars)` : ""}`);
|
|
161
|
+
try {
|
|
162
|
+
const stopResponse = await session.streamer.stop(text || chunks?.length || metadata ? {
|
|
163
|
+
...text ? { markdown_text: text } : {},
|
|
164
|
+
...chunks?.length ? { chunks } : {},
|
|
165
|
+
...metadata ? { metadata } : {}
|
|
166
|
+
} : void 0);
|
|
167
|
+
session.delivered = true;
|
|
168
|
+
session.pendingText = "";
|
|
169
|
+
logVerbose("slack-stream: stream stopped");
|
|
170
|
+
const messageId = stopResponse?.ts ?? stopResponse?.message?.ts;
|
|
171
|
+
return messageId ? { messageId } : {};
|
|
172
|
+
} catch (err) {
|
|
173
|
+
const code = extractSlackErrorCode(err) ?? "unknown";
|
|
174
|
+
const benignFinalizeError = isBenignSlackFinalizeError(err);
|
|
175
|
+
if (session.pendingText && (benignFinalizeError || code === "missing_scope")) throw new SlackStreamNotDeliveredError(session.pendingText, code);
|
|
176
|
+
if (benignFinalizeError) {
|
|
177
|
+
if (session.delivered) {
|
|
178
|
+
logVerbose(`slack-stream: finalize rejected by Slack (${code}); prior appends delivered, treating stream as stopped`);
|
|
179
|
+
return {};
|
|
180
|
+
}
|
|
255
181
|
}
|
|
256
|
-
|
|
257
|
-
id,
|
|
258
|
-
title: lineTaskTitle(line, maxLineChars),
|
|
259
|
-
status: lineTaskStatus(line)
|
|
260
|
-
};
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
function resolvePlanTitle(params) {
|
|
264
|
-
return compactChunkText(params.title?.trim() || params.label?.trim() || params.tasks.at(-1)?.title || SLACK_PROGRESS_PLAN_FALLBACK_TITLE);
|
|
265
|
-
}
|
|
266
|
-
function buildSlackProgressStreamChunks(params) {
|
|
267
|
-
const tasks = buildPlanTasks({
|
|
268
|
-
lines: params.lines,
|
|
269
|
-
plan: params.plan,
|
|
270
|
-
maxLineChars: params.maxLineChars
|
|
271
|
-
});
|
|
272
|
-
if (tasks.length === 0) {
|
|
273
|
-
const title = params.title?.trim() || params.label?.trim();
|
|
274
|
-
return title ? [{
|
|
275
|
-
type: "plan_update",
|
|
276
|
-
title: compactChunkText(title)
|
|
277
|
-
}] : void 0;
|
|
182
|
+
throw err;
|
|
278
183
|
}
|
|
279
|
-
return [{
|
|
280
|
-
type: "plan_update",
|
|
281
|
-
title: resolvePlanTitle({
|
|
282
|
-
label: params.label,
|
|
283
|
-
title: params.title,
|
|
284
|
-
tasks
|
|
285
|
-
})
|
|
286
|
-
}, ...tasks.map((task) => ({
|
|
287
|
-
type: "task_update",
|
|
288
|
-
id: task.id,
|
|
289
|
-
title: task.title,
|
|
290
|
-
status: task.status === "in_progress" ? params.finalInProgressStatus ?? (params.completeInProgress ? "complete" : task.status) : task.status
|
|
291
|
-
}))];
|
|
292
|
-
}
|
|
293
|
-
function buildSlackProgressDraftBlocks(params) {
|
|
294
|
-
const label = params.label?.trim() || params.title?.trim();
|
|
295
|
-
const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS);
|
|
296
|
-
const planLines = formatPlanChecklistLines(params.plan ?? [], {
|
|
297
|
-
maxLines: 50,
|
|
298
|
-
maxLineChars
|
|
299
|
-
});
|
|
300
|
-
const narration = params.narration?.replace(/\s+/g, " ").trim();
|
|
301
|
-
const headBlocks = [
|
|
302
|
-
...label ? [{
|
|
303
|
-
type: "section",
|
|
304
|
-
text: field(`*${escapeSlackMrkdwn(label)}*`)
|
|
305
|
-
}] : [],
|
|
306
|
-
...narration ? [{
|
|
307
|
-
type: "section",
|
|
308
|
-
text: field(`_${escapeSlackMrkdwn(narration)}_`)
|
|
309
|
-
}] : [],
|
|
310
|
-
...planLines.length > 0 ? [{
|
|
311
|
-
type: "section",
|
|
312
|
-
text: field(planLines.map((line) => escapeSlackMrkdwn(line)).join("\n"))
|
|
313
|
-
}] : []
|
|
314
|
-
].slice(0, 50);
|
|
315
|
-
const lineBudget = Math.max(0, 50 - headBlocks.length);
|
|
316
|
-
const renderedBlocks = [...headBlocks, ...params.lines.slice(-lineBudget).map((line) => ({
|
|
317
|
-
type: "section",
|
|
318
|
-
fields: [field(legacyLineTitle(line)), field(legacyLineDetail(line, maxLineChars))]
|
|
319
|
-
}))];
|
|
320
|
-
return renderedBlocks.length ? renderedBlocks : void 0;
|
|
321
184
|
}
|
|
322
185
|
/**
|
|
323
|
-
* Slack
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
186
|
+
* Slack API error codes that indicate `chat.stopStream` (or the
|
|
187
|
+
* `chat.startStream` call the SDK issues inside `stop()` when the buffer
|
|
188
|
+
* never flushed) cannot finalize the stream for the current recipient or
|
|
189
|
+
* team. Either the caller falls back to a normal message (see
|
|
190
|
+
* {@link SlackStreamNotDeliveredError}) or, if prior appends already
|
|
191
|
+
* delivered text, the error is logged verbosely and swallowed.
|
|
327
192
|
*/
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
status: "complete"
|
|
338
|
-
};
|
|
339
|
-
nextTasks.set(id, entry);
|
|
340
|
-
return {
|
|
341
|
-
type: "task_update",
|
|
342
|
-
id,
|
|
343
|
-
title: task.title,
|
|
344
|
-
status: "complete"
|
|
345
|
-
};
|
|
346
|
-
});
|
|
347
|
-
for (const [id, task] of params.previousTasks) if (!nextTasks.has(id)) nextTasks.set(id, task);
|
|
348
|
-
return {
|
|
349
|
-
chunks: params.chunks?.length ? [...params.chunks, ...terminalized] : terminalized.length ? terminalized : params.chunks,
|
|
350
|
-
tasks: nextTasks
|
|
351
|
-
};
|
|
193
|
+
const BENIGN_SLACK_FINALIZE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
194
|
+
"user_not_found",
|
|
195
|
+
"team_not_found",
|
|
196
|
+
"missing_recipient_user_id",
|
|
197
|
+
"method_not_supported_for_channel_type"
|
|
198
|
+
]);
|
|
199
|
+
function isBenignSlackFinalizeError(err) {
|
|
200
|
+
const code = extractSlackErrorCode(err);
|
|
201
|
+
return code !== void 0 && BENIGN_SLACK_FINALIZE_ERROR_CODES.has(code);
|
|
352
202
|
}
|
|
353
|
-
function
|
|
354
|
-
|
|
203
|
+
function extractSlackErrorCode(err) {
|
|
204
|
+
if (!err || typeof err !== "object") return;
|
|
205
|
+
const record = err;
|
|
206
|
+
if (record.data && typeof record.data === "object") {
|
|
207
|
+
const inner = record.data.error;
|
|
208
|
+
if (typeof inner === "string") return inner;
|
|
209
|
+
}
|
|
210
|
+
return (typeof record.message === "string" ? record.message : "").match(/An API error occurred:\s*([a-z_][a-z0-9_]*)/i)?.[1];
|
|
355
211
|
}
|
|
356
|
-
function
|
|
357
|
-
|
|
212
|
+
function markSlackStreamFallbackDelivered(session) {
|
|
213
|
+
const nativeStreamWasStarted = session.delivered;
|
|
214
|
+
session.pendingText = "";
|
|
215
|
+
session.streamer.buffer = "";
|
|
216
|
+
session.stopped = !nativeStreamWasStarted;
|
|
358
217
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region extensions/slack/src/monitor/message-handler/timestamp.ts
|
|
220
|
+
const SLACK_TIMESTAMP_RE = /^\d+(?:\.\d+)?$/;
|
|
221
|
+
const MAX_SAFE_SLACK_TIMESTAMP_SECONDS = Number.MAX_SAFE_INTEGER / 1e3;
|
|
222
|
+
function resolveSlackTimestampMs(ts) {
|
|
223
|
+
const trimmed = ts?.trim();
|
|
224
|
+
if (!trimmed || !SLACK_TIMESTAMP_RE.test(trimmed)) return;
|
|
225
|
+
const seconds = asFiniteNumberInRange(parseStrictFiniteNumber(trimmed), {
|
|
226
|
+
min: 0,
|
|
227
|
+
max: MAX_SAFE_SLACK_TIMESTAMP_SECONDS
|
|
363
228
|
});
|
|
229
|
+
return seconds === void 0 ? void 0 : Math.round(seconds * 1e3);
|
|
364
230
|
}
|
|
365
231
|
//#endregion
|
|
366
|
-
//#region extensions/slack/src/
|
|
367
|
-
function
|
|
368
|
-
|
|
232
|
+
//#region extensions/slack/src/monitor/message-handler/dispatch-helpers.ts
|
|
233
|
+
function resolveSlackMessageTimestampMs(message) {
|
|
234
|
+
return resolveSlackTimestampMs(message.event_ts ?? message.ts);
|
|
235
|
+
}
|
|
236
|
+
function resolveSlackBotLoopProtection(prepared) {
|
|
237
|
+
const senderBotId = prepared.message.bot_id;
|
|
238
|
+
if (!senderBotId) return;
|
|
239
|
+
const receiverBotId = prepared.ctx.botId || prepared.ctx.botUserId;
|
|
240
|
+
if (!receiverBotId || senderBotId === prepared.ctx.botId || prepared.message.user === prepared.ctx.botUserId) return;
|
|
369
241
|
return {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
242
|
+
scopeId: prepared.route.accountId,
|
|
243
|
+
conversationId: prepared.message.channel,
|
|
244
|
+
senderId: senderBotId,
|
|
245
|
+
receiverId: receiverBotId,
|
|
246
|
+
config: mergePairLoopGuardConfig(prepared.account.config.botLoopProtection, prepared.channelConfig?.botLoopProtection),
|
|
247
|
+
defaultsConfig: prepared.ctx.cfg.channels?.defaults?.botLoopProtection,
|
|
248
|
+
defaultEnabled: true,
|
|
249
|
+
nowMs: resolveSlackMessageTimestampMs(prepared.message)
|
|
373
250
|
};
|
|
374
251
|
}
|
|
375
|
-
function
|
|
376
|
-
|
|
377
|
-
if (
|
|
378
|
-
|
|
379
|
-
source: params.source,
|
|
380
|
-
changed: false
|
|
381
|
-
};
|
|
382
|
-
if (!params.rendered) return {
|
|
383
|
-
rendered: incoming,
|
|
384
|
-
source: incoming,
|
|
385
|
-
changed: true
|
|
386
|
-
};
|
|
387
|
-
if (incoming === params.source) return {
|
|
388
|
-
rendered: params.rendered,
|
|
389
|
-
source: params.source,
|
|
390
|
-
changed: false
|
|
391
|
-
};
|
|
392
|
-
if (incoming.startsWith(params.source) || incoming.startsWith(params.rendered)) return {
|
|
393
|
-
rendered: incoming,
|
|
394
|
-
source: incoming,
|
|
395
|
-
changed: incoming !== params.rendered
|
|
396
|
-
};
|
|
397
|
-
if (params.source.startsWith(incoming)) return {
|
|
398
|
-
rendered: params.rendered,
|
|
399
|
-
source: params.source,
|
|
400
|
-
changed: false
|
|
401
|
-
};
|
|
402
|
-
const separator = params.rendered.endsWith("\n") ? "" : "\n";
|
|
403
|
-
return {
|
|
404
|
-
rendered: `${params.rendered}${separator}${incoming}`,
|
|
405
|
-
source: incoming,
|
|
406
|
-
changed: true
|
|
407
|
-
};
|
|
252
|
+
function isSlackStreamingEnabled(params) {
|
|
253
|
+
if (params.mode === "partial") return params.nativeStreaming;
|
|
254
|
+
if (params.mode === "progress") return params.nativeStreaming && params.nativeProgressTaskCards === true;
|
|
255
|
+
return false;
|
|
408
256
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
...identity.username ? { username: identity.username } : {},
|
|
439
|
-
icon_emoji: identity.iconEmoji
|
|
440
|
-
} : identity?.username ? { username: identity.username } : {};
|
|
441
|
-
logVerbose(`slack-stream: starting stream in ${channel} thread=${threadTs}${teamId ? ` team=${teamId}` : ""}${userId ? ` user=${userId}` : ""}`);
|
|
442
|
-
const streamer = client.chatStream({
|
|
443
|
-
channel,
|
|
444
|
-
thread_ts: threadTs,
|
|
445
|
-
...taskDisplayMode ? { task_display_mode: taskDisplayMode } : {},
|
|
446
|
-
...teamId ? { recipient_team_id: teamId } : {},
|
|
447
|
-
...userId ? { recipient_user_id: userId } : {},
|
|
448
|
-
...identityPayload
|
|
257
|
+
function shouldEnableSlackPreviewStreaming(params) {
|
|
258
|
+
return params.mode !== "off";
|
|
259
|
+
}
|
|
260
|
+
function shouldInitializeSlackDraftStream(params) {
|
|
261
|
+
return params.previewStreamingEnabled && !params.useStreaming;
|
|
262
|
+
}
|
|
263
|
+
function resolveSlackDisableBlockStreaming(params) {
|
|
264
|
+
if (params.useStreaming || params.shouldUseDraftStream) return true;
|
|
265
|
+
return typeof params.blockStreamingEnabled === "boolean" ? !params.blockStreamingEnabled : void 0;
|
|
266
|
+
}
|
|
267
|
+
function resolveExplicitSlackProgressTitle(entry) {
|
|
268
|
+
const label = resolveChannelProgressDraftConfig(entry).label;
|
|
269
|
+
if (typeof label !== "string") return;
|
|
270
|
+
const trimmed = label.trim();
|
|
271
|
+
return trimmed && trimmed.toLowerCase() !== "auto" ? trimmed : void 0;
|
|
272
|
+
}
|
|
273
|
+
function resolveSlackNativeProgressTaskCards(entry) {
|
|
274
|
+
const streaming = entry?.streaming;
|
|
275
|
+
if (!streaming || typeof streaming !== "object" || Array.isArray(streaming)) return false;
|
|
276
|
+
const progressConfig = streaming.progress;
|
|
277
|
+
return Boolean(progressConfig) && typeof progressConfig === "object" && !Array.isArray(progressConfig) && progressConfig.nativeTaskCards === true;
|
|
278
|
+
}
|
|
279
|
+
function resolveSlackStreamingThreadHint(params) {
|
|
280
|
+
return resolveSlackThreadTs({
|
|
281
|
+
replyToMode: params.replyToMode,
|
|
282
|
+
incomingThreadTs: params.incomingThreadTs,
|
|
283
|
+
messageTs: params.messageTs,
|
|
284
|
+
hasReplied: false,
|
|
285
|
+
isThreadReply: params.isThreadReply
|
|
449
286
|
});
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
287
|
+
}
|
|
288
|
+
const SLACK_STREAM_RECIPIENT_TEAM_CACHE_MAX = 2e3;
|
|
289
|
+
const slackStreamRecipientTeamCaches = /* @__PURE__ */ new WeakMap();
|
|
290
|
+
function getSlackStreamRecipientTeamCache(client) {
|
|
291
|
+
const existing = slackStreamRecipientTeamCaches.get(client);
|
|
292
|
+
if (existing) return existing;
|
|
293
|
+
const cache = /* @__PURE__ */ new Map();
|
|
294
|
+
slackStreamRecipientTeamCaches.set(client, cache);
|
|
295
|
+
return cache;
|
|
296
|
+
}
|
|
297
|
+
function buildSlackEventDeliveryKey(params) {
|
|
298
|
+
const reply = resolveSendableOutboundReplyParts(params.payload, { text: params.textOverride });
|
|
299
|
+
const renderPlan = resolveSlackReplyRenderPlan(params.payload, params.textOverride ?? params.payload.text);
|
|
300
|
+
const plannedBlocks = renderPlan.mode === "single" ? renderPlan.blocks : renderPlan.blockPart?.blocks;
|
|
301
|
+
const slackBlocks = readSlackReplyBlocks(params.payload) ?? plannedBlocks;
|
|
302
|
+
const renderedText = renderPlan.mode === "single" ? renderPlan.text : renderPlan.fallbackText;
|
|
303
|
+
if (!reply.hasContent && !slackBlocks?.length && !renderedText.trim()) return null;
|
|
304
|
+
return JSON.stringify({
|
|
305
|
+
kind: params.kind,
|
|
306
|
+
threadTs: params.threadTs ?? "",
|
|
307
|
+
replyToId: params.payload.replyToId ?? null,
|
|
308
|
+
text: renderedText || reply.trimmedText,
|
|
309
|
+
mediaUrls: reply.mediaUrls,
|
|
310
|
+
blocks: slackBlocks ?? null
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
function readSlackStreamRecipientTeamCache(params) {
|
|
314
|
+
if (!params.fallbackTeamId || !params.userId) return;
|
|
315
|
+
const cacheKey = `${params.fallbackTeamId}:${params.userId}`;
|
|
316
|
+
const cache = getSlackStreamRecipientTeamCache(params.client);
|
|
317
|
+
const cached = cache.get(cacheKey);
|
|
318
|
+
if (!cached) return;
|
|
319
|
+
cache.delete(cacheKey);
|
|
320
|
+
cache.set(cacheKey, cached);
|
|
321
|
+
return cached;
|
|
322
|
+
}
|
|
323
|
+
function rememberSlackStreamRecipientTeam(params) {
|
|
324
|
+
if (!params.fallbackTeamId || !params.userId) return;
|
|
325
|
+
const cacheKey = `${params.fallbackTeamId}:${params.userId}`;
|
|
326
|
+
const cache = getSlackStreamRecipientTeamCache(params.client);
|
|
327
|
+
if (cache.has(cacheKey)) cache.delete(cacheKey);
|
|
328
|
+
cache.set(cacheKey, params.teamId);
|
|
329
|
+
if (cache.size > SLACK_STREAM_RECIPIENT_TEAM_CACHE_MAX) {
|
|
330
|
+
const oldest = cache.keys().next().value;
|
|
331
|
+
if (oldest) cache.delete(oldest);
|
|
474
332
|
}
|
|
475
|
-
return session;
|
|
476
333
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
334
|
+
function createSlackEventDeliveryTracker() {
|
|
335
|
+
const deliveredKeys = /* @__PURE__ */ new Set();
|
|
336
|
+
return {
|
|
337
|
+
hasDelivered(params) {
|
|
338
|
+
const key = buildSlackEventDeliveryKey(params);
|
|
339
|
+
return key ? deliveredKeys.has(key) : false;
|
|
340
|
+
},
|
|
341
|
+
markDelivered(params) {
|
|
342
|
+
const key = buildSlackEventDeliveryKey(params);
|
|
343
|
+
if (key) deliveredKeys.add(key);
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function shouldUseStreaming(params) {
|
|
348
|
+
if (!params.streamingEnabled) return false;
|
|
349
|
+
if (!params.threadTs) {
|
|
350
|
+
logVerbose("slack-stream: streaming disabled — no reply thread target available");
|
|
351
|
+
return false;
|
|
485
352
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
async function resolveSlackStreamRecipientTeamId(params) {
|
|
356
|
+
const cachedTeamId = readSlackStreamRecipientTeamCache(params);
|
|
357
|
+
if (cachedTeamId) return cachedTeamId;
|
|
358
|
+
if (params.userId) try {
|
|
359
|
+
const info = await params.client.users.info({
|
|
360
|
+
token: params.token,
|
|
361
|
+
user: params.userId
|
|
492
362
|
});
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
363
|
+
const teamId = info.user?.team_id ?? info.user?.profile?.team;
|
|
364
|
+
if (teamId) {
|
|
365
|
+
rememberSlackStreamRecipientTeam({
|
|
366
|
+
...params,
|
|
367
|
+
teamId
|
|
368
|
+
});
|
|
369
|
+
return teamId;
|
|
496
370
|
}
|
|
497
|
-
logVerbose(`slack-stream: appended ${text?.length ?? 0} chars, ${chunks?.length ?? 0} chunks (${result ? "flushed" : "buffered"})`);
|
|
498
371
|
} catch (err) {
|
|
499
|
-
|
|
500
|
-
throw err;
|
|
372
|
+
logVerbose(`slack-stream: users.info team lookup failed (${formatErrorMessage(err)})`);
|
|
501
373
|
}
|
|
374
|
+
return params.fallbackTeamId;
|
|
502
375
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region extensions/slack/src/draft-stream.ts
|
|
378
|
+
const DEFAULT_THROTTLE_MS = 1e3;
|
|
379
|
+
function createSlackDraftStream(params) {
|
|
380
|
+
const maxChars = Math.min(params.maxChars ?? 8e3, SLACK_TEXT_LIMIT);
|
|
381
|
+
const throttleMs = Math.max(250, params.throttleMs ?? DEFAULT_THROTTLE_MS);
|
|
382
|
+
const send = params.send ?? sendMessageSlack;
|
|
383
|
+
const edit = params.edit ?? editSlackMessage;
|
|
384
|
+
const remove = params.remove ?? deleteSlackMessage;
|
|
385
|
+
let streamMessageId;
|
|
386
|
+
let streamChannelId;
|
|
387
|
+
let lastSentKey = "";
|
|
388
|
+
let pendingUpdate;
|
|
389
|
+
let stopped = false;
|
|
390
|
+
const normalizeUpdate = (update) => typeof update === "string" ? { text: update } : update;
|
|
391
|
+
const sendOrEditStreamMessage = async (text) => {
|
|
392
|
+
if (stopped) return;
|
|
393
|
+
const trimmed = text.trimEnd();
|
|
394
|
+
if (!trimmed) return;
|
|
395
|
+
if (trimmed.length > maxChars) {
|
|
396
|
+
stopped = true;
|
|
397
|
+
params.warn?.(`slack stream preview stopped (text length ${trimmed.length} > ${maxChars})`);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const update = normalizeUpdate(pendingUpdate ?? text);
|
|
401
|
+
const blocks = update.text === text ? update.blocks : void 0;
|
|
402
|
+
const sentKey = `${trimmed}\n${blocks ? JSON.stringify(blocks) : ""}`;
|
|
403
|
+
if (sentKey === lastSentKey) return;
|
|
404
|
+
lastSentKey = sentKey;
|
|
405
|
+
try {
|
|
406
|
+
if (streamChannelId && streamMessageId) {
|
|
407
|
+
await edit(streamChannelId, streamMessageId, trimmed, {
|
|
408
|
+
cfg: params.cfg,
|
|
409
|
+
token: params.token,
|
|
410
|
+
accountId: params.accountId,
|
|
411
|
+
...params.eventScope ? { client: params.eventScope.client } : {},
|
|
412
|
+
...blocks ? { blocks } : {}
|
|
413
|
+
});
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const sent = await send(params.target, trimmed, {
|
|
417
|
+
cfg: params.cfg,
|
|
418
|
+
token: params.token,
|
|
419
|
+
accountId: params.accountId,
|
|
420
|
+
threadTs: params.resolveThreadTs?.(),
|
|
421
|
+
identity: params.identity,
|
|
422
|
+
...params.eventScope ? {
|
|
423
|
+
client: params.eventScope.client,
|
|
424
|
+
enterpriseEventScope: params.eventScope
|
|
425
|
+
} : {},
|
|
426
|
+
...params.metadata ? { metadata: params.metadata } : {},
|
|
427
|
+
...blocks ? { blocks } : {}
|
|
428
|
+
});
|
|
429
|
+
streamChannelId = sent.channelId || streamChannelId;
|
|
430
|
+
streamMessageId = sent.messageId || streamMessageId;
|
|
431
|
+
if (!streamChannelId || !streamMessageId) {
|
|
432
|
+
stopped = true;
|
|
433
|
+
params.warn?.("slack stream preview stopped (missing identifiers from sendMessage)");
|
|
434
|
+
return;
|
|
553
435
|
}
|
|
436
|
+
params.onMessageSent?.();
|
|
437
|
+
} catch (err) {
|
|
438
|
+
stopped = true;
|
|
439
|
+
params.warn?.(`slack stream preview failed: ${formatSlackError(err)}`);
|
|
554
440
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
const messageTs = params.message.ts ?? eventTs;
|
|
597
|
-
const hasThreadTs = typeof incomingThreadTs === "string" && incomingThreadTs.length > 0;
|
|
598
|
-
const isThreadReply = hasThreadTs && (incomingThreadTs !== messageTs || Boolean(params.message.parent_user_id));
|
|
599
|
-
const replyToId = isThreadReply ? incomingThreadTs : void 0;
|
|
600
|
-
const isAssistantDmThreadRoot = hasThreadTs && !isThreadReply && params.isDirectMessage === true;
|
|
441
|
+
};
|
|
442
|
+
const loop = createDraftStreamLoop({
|
|
443
|
+
throttleMs,
|
|
444
|
+
isStopped: () => stopped,
|
|
445
|
+
sendOrEditStreamMessage
|
|
446
|
+
});
|
|
447
|
+
const stop = () => {
|
|
448
|
+
stopped = true;
|
|
449
|
+
loop.stop();
|
|
450
|
+
};
|
|
451
|
+
const discardPending = async () => {
|
|
452
|
+
stop();
|
|
453
|
+
await loop.waitForInFlight();
|
|
454
|
+
};
|
|
455
|
+
const clear = async () => {
|
|
456
|
+
await discardPending();
|
|
457
|
+
const channelId = streamChannelId;
|
|
458
|
+
const messageId = streamMessageId;
|
|
459
|
+
streamChannelId = void 0;
|
|
460
|
+
streamMessageId = void 0;
|
|
461
|
+
lastSentKey = "";
|
|
462
|
+
pendingUpdate = void 0;
|
|
463
|
+
if (!channelId || !messageId) return;
|
|
464
|
+
try {
|
|
465
|
+
await remove(channelId, messageId, {
|
|
466
|
+
token: params.token,
|
|
467
|
+
accountId: params.accountId,
|
|
468
|
+
...params.eventScope ? { client: params.eventScope.client } : {}
|
|
469
|
+
});
|
|
470
|
+
} catch (err) {
|
|
471
|
+
params.warn?.(`slack stream preview cleanup failed: ${formatSlackError(err)}`);
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
const forceNewMessage = () => {
|
|
475
|
+
streamMessageId = void 0;
|
|
476
|
+
streamChannelId = void 0;
|
|
477
|
+
lastSentKey = "";
|
|
478
|
+
pendingUpdate = void 0;
|
|
479
|
+
loop.resetPending();
|
|
480
|
+
};
|
|
481
|
+
params.log?.(`slack stream preview ready (maxChars=${maxChars}, throttleMs=${throttleMs})`);
|
|
601
482
|
return {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
483
|
+
update: (update) => {
|
|
484
|
+
const normalized = normalizeUpdate(update);
|
|
485
|
+
pendingUpdate = update;
|
|
486
|
+
loop.update(normalized.text);
|
|
487
|
+
},
|
|
488
|
+
flush: loop.flush,
|
|
489
|
+
clear,
|
|
490
|
+
discardPending,
|
|
491
|
+
seal: discardPending,
|
|
492
|
+
stop,
|
|
493
|
+
forceNewMessage,
|
|
494
|
+
messageId: () => streamMessageId,
|
|
495
|
+
channelId: () => streamChannelId
|
|
607
496
|
};
|
|
608
497
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
function
|
|
618
|
-
const { incomingThreadTs, messageTs, isThreadReply } = resolveSlackThreadContext(params);
|
|
619
|
-
const replyThreadTs = isThreadReply ? incomingThreadTs : params.replyToMode === "all" ? messageTs : void 0;
|
|
498
|
+
//#endregion
|
|
499
|
+
//#region extensions/slack/src/progress-blocks.ts
|
|
500
|
+
const SLACK_PROGRESS_FIELD_MAX = 1800;
|
|
501
|
+
const DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS = 120;
|
|
502
|
+
const DEFAULT_SLACK_PROGRESS_TASK_DETAIL_MAX_CHARS = 48;
|
|
503
|
+
const SLACK_PROGRESS_CHUNK_TEXT_MAX = 256;
|
|
504
|
+
const SLACK_PROGRESS_TASK_TITLE_MAX = 120;
|
|
505
|
+
const SLACK_PROGRESS_PLAN_FALLBACK_TITLE = "Thinking";
|
|
506
|
+
function field(text) {
|
|
620
507
|
return {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
isThreadReply
|
|
508
|
+
type: "mrkdwn",
|
|
509
|
+
text: truncateSlackText(text, SLACK_PROGRESS_FIELD_MAX)
|
|
624
510
|
};
|
|
625
511
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
function buildExpectedSlackEditText(params) {
|
|
629
|
-
const trimmedText = params.text.trim();
|
|
630
|
-
if (trimmedText) return normalizeSlackOutboundText(trimmedText);
|
|
631
|
-
if (params.blocks?.length) return normalizeSlackOutboundText(buildSlackBlocksFallbackText(params.blocks));
|
|
632
|
-
return " ";
|
|
512
|
+
function resolveMaxLineChars(value, fallback) {
|
|
513
|
+
return value && value > 0 ? Math.floor(value) : fallback;
|
|
633
514
|
}
|
|
634
|
-
function
|
|
635
|
-
const
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
const fallbackPlan = buildSlackNativeDataDeliveryPlan({
|
|
643
|
-
baseText: params.text,
|
|
644
|
-
blocks: params.blocks
|
|
645
|
-
});
|
|
646
|
-
for (const message of fallbackPlan.fallbackMessages) texts.add(normalizeSlackOutboundText(message.text));
|
|
647
|
-
}
|
|
648
|
-
return texts;
|
|
515
|
+
function compactDetail(value, maxChars) {
|
|
516
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
517
|
+
const chars = Array.from(normalized);
|
|
518
|
+
if (chars.length <= maxChars) return normalized;
|
|
519
|
+
if (maxChars <= 1) return "…";
|
|
520
|
+
const keepStart = Math.max(1, Math.ceil((maxChars - 1) * .45));
|
|
521
|
+
const keepEnd = Math.max(1, maxChars - keepStart - 1);
|
|
522
|
+
return `${chars.slice(0, keepStart).join("").trimEnd()}…${chars.slice(-keepEnd).join("").trimStart()}`;
|
|
649
523
|
}
|
|
650
|
-
function
|
|
651
|
-
|
|
652
|
-
if (!actual?.length) {
|
|
653
|
-
if (!hasSlackNativeDataBlock(expected)) return false;
|
|
654
|
-
return buildSlackNativeDataDeliveryPlan({ blocks: expected }).fallbackMessages.every((message) => !message.blocks?.length);
|
|
655
|
-
}
|
|
656
|
-
if (JSON.stringify(expected) === JSON.stringify(actual)) return true;
|
|
657
|
-
if (!hasSlackNativeDataBlock(expected)) return false;
|
|
658
|
-
try {
|
|
659
|
-
const fallbackPlan = buildSlackNativeDataDeliveryPlan({ blocks: expected });
|
|
660
|
-
const fallbackBlocks = fallbackPlan.fallbackMessages.flatMap((message) => message.blocks ?? []);
|
|
661
|
-
if (JSON.stringify(fallbackBlocks) === JSON.stringify(actual)) return true;
|
|
662
|
-
const fallbackText = fallbackPlan.fallbackMessages.map((message) => message.text).filter(Boolean).join("\n\n");
|
|
663
|
-
return normalizeSlackOutboundText(buildSlackBlocksFallbackText(actual)) === normalizeSlackOutboundText(fallbackText);
|
|
664
|
-
} catch {
|
|
665
|
-
return false;
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
async function readSlackMessageAfterEditError(params) {
|
|
669
|
-
if (params.threadTs) return ((await params.client.conversations.replies({
|
|
670
|
-
token: params.token,
|
|
671
|
-
channel: params.channelId,
|
|
672
|
-
ts: params.threadTs,
|
|
673
|
-
latest: params.messageId,
|
|
674
|
-
inclusive: true,
|
|
675
|
-
limit: 100
|
|
676
|
-
})).messages ?? []).find((message) => message?.ts === params.messageId) ?? null;
|
|
677
|
-
const message = (await params.client.conversations.history({
|
|
678
|
-
token: params.token,
|
|
679
|
-
channel: params.channelId,
|
|
680
|
-
latest: params.messageId,
|
|
681
|
-
oldest: params.messageId,
|
|
682
|
-
inclusive: true,
|
|
683
|
-
limit: 1
|
|
684
|
-
})).messages?.[0];
|
|
685
|
-
if (!message?.ts || message.ts !== params.messageId) return null;
|
|
686
|
-
return message;
|
|
687
|
-
}
|
|
688
|
-
async function didSlackPreviewEditApplyAfterError(params) {
|
|
689
|
-
const readback = await readSlackMessageAfterEditError(params);
|
|
690
|
-
if (!readback) return false;
|
|
691
|
-
const expectedText = buildExpectedSlackEditText({
|
|
692
|
-
text: params.text,
|
|
693
|
-
blocks: params.blocks
|
|
694
|
-
});
|
|
695
|
-
const acceptedTexts = buildAcceptedSlackEditTexts({
|
|
696
|
-
text: params.text,
|
|
697
|
-
blocks: params.blocks
|
|
698
|
-
});
|
|
699
|
-
const actualText = normalizeSlackOutboundText((readback.text ?? "").trim());
|
|
700
|
-
if (params.blocks?.length) return acceptedTexts.has(actualText) && blocksMatch(params.blocks, readback.blocks);
|
|
701
|
-
return actualText === expectedText;
|
|
702
|
-
}
|
|
703
|
-
async function finalizeSlackPreviewEdit(params) {
|
|
704
|
-
try {
|
|
705
|
-
await editSlackMessage(params.channelId, params.messageId, params.text, {
|
|
706
|
-
token: params.token,
|
|
707
|
-
accountId: params.accountId,
|
|
708
|
-
client: params.client,
|
|
709
|
-
...params.blocks?.length ? { blocks: params.blocks } : {}
|
|
710
|
-
});
|
|
711
|
-
} catch (err) {
|
|
712
|
-
try {
|
|
713
|
-
if (await didSlackPreviewEditApplyAfterError({
|
|
714
|
-
client: params.client,
|
|
715
|
-
token: params.token,
|
|
716
|
-
channelId: params.channelId,
|
|
717
|
-
messageId: params.messageId,
|
|
718
|
-
text: params.text,
|
|
719
|
-
blocks: params.blocks,
|
|
720
|
-
threadTs: params.threadTs
|
|
721
|
-
})) {
|
|
722
|
-
logVerbose(`slack: preview final edit response failed but readback matched message ${params.channelId}/${params.messageId}; suppressing duplicate fallback send`);
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
} catch (readbackErr) {
|
|
726
|
-
logVerbose(`slack: preview final edit readback failed (${String(readbackErr)})`);
|
|
727
|
-
}
|
|
728
|
-
throw err;
|
|
729
|
-
}
|
|
524
|
+
function compactTitle(value) {
|
|
525
|
+
return truncateSlackText(value.replace(/\s+/g, " ").trim(), SLACK_PROGRESS_TASK_TITLE_MAX);
|
|
730
526
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
const SLACK_TIMESTAMP_RE = /^\d+(?:\.\d+)?$/;
|
|
734
|
-
const MAX_SAFE_SLACK_TIMESTAMP_SECONDS = Number.MAX_SAFE_INTEGER / 1e3;
|
|
735
|
-
function resolveSlackTimestampMs(ts) {
|
|
736
|
-
const trimmed = ts?.trim();
|
|
737
|
-
if (!trimmed || !SLACK_TIMESTAMP_RE.test(trimmed)) return;
|
|
738
|
-
const seconds = asFiniteNumberInRange(parseStrictFiniteNumber(trimmed), {
|
|
739
|
-
min: 0,
|
|
740
|
-
max: MAX_SAFE_SLACK_TIMESTAMP_SECONDS
|
|
741
|
-
});
|
|
742
|
-
return seconds === void 0 ? void 0 : Math.round(seconds * 1e3);
|
|
527
|
+
function compactChunkText(value) {
|
|
528
|
+
return truncateSlackText(value.replace(/\s+/g, " ").trim(), SLACK_PROGRESS_CHUNK_TEXT_MAX);
|
|
743
529
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
function resolveSlackMessageTimestampMs(message) {
|
|
747
|
-
return resolveSlackTimestampMs(message.event_ts ?? message.ts);
|
|
530
|
+
function lineDetailParts(line) {
|
|
531
|
+
return [line.detail, line.status && line.status !== "completed" && !line.detail?.includes(line.status) ? line.status : void 0].map((part) => part?.trim()).filter((part) => Boolean(part));
|
|
748
532
|
}
|
|
749
|
-
function
|
|
750
|
-
|
|
751
|
-
if (!senderBotId) return;
|
|
752
|
-
const receiverBotId = prepared.ctx.botId || prepared.ctx.botUserId;
|
|
753
|
-
if (!receiverBotId || senderBotId === prepared.ctx.botId || prepared.message.user === prepared.ctx.botUserId) return;
|
|
754
|
-
return {
|
|
755
|
-
scopeId: prepared.route.accountId,
|
|
756
|
-
conversationId: prepared.message.channel,
|
|
757
|
-
senderId: senderBotId,
|
|
758
|
-
receiverId: receiverBotId,
|
|
759
|
-
config: mergePairLoopGuardConfig(prepared.account.config.botLoopProtection, prepared.channelConfig?.botLoopProtection),
|
|
760
|
-
defaultsConfig: prepared.ctx.cfg.channels?.defaults?.botLoopProtection,
|
|
761
|
-
defaultEnabled: true,
|
|
762
|
-
nowMs: resolveSlackMessageTimestampMs(prepared.message)
|
|
763
|
-
};
|
|
533
|
+
function legacyLineTitle(line) {
|
|
534
|
+
return `${line.icon ?? "•"} *${escapeSlackMrkdwn(line.label)}*`;
|
|
764
535
|
}
|
|
765
|
-
function
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
return false;
|
|
536
|
+
function legacyLineDetail(line, maxChars) {
|
|
537
|
+
const detail = lineDetailParts(line).join(" · ");
|
|
538
|
+
return detail ? escapeSlackMrkdwn(compactDetail(detail, maxChars)) : "—";
|
|
769
539
|
}
|
|
770
|
-
function
|
|
771
|
-
|
|
540
|
+
function lineTaskTitle(line, maxLineChars) {
|
|
541
|
+
const label = line.label.replace(/\s+/g, " ").trim() || line.toolName || line.kind || "Update";
|
|
542
|
+
const detail = lineDetailParts(line).join(" · ") || line.status?.trim();
|
|
543
|
+
const fallback = line.text.replace(/\s+/g, " ").trim();
|
|
544
|
+
if (detail) return compactTitle(`${label} — ${compactDetail(detail, maxLineChars)}`);
|
|
545
|
+
if (fallback && fallback !== label) return compactTitle(fallback);
|
|
546
|
+
return compactTitle(label);
|
|
772
547
|
}
|
|
773
|
-
function
|
|
774
|
-
|
|
548
|
+
function lineTaskStatus(line) {
|
|
549
|
+
const normalized = line.status?.replace(/\s+/g, " ").trim().toLowerCase();
|
|
550
|
+
if (!normalized) return "in_progress";
|
|
551
|
+
if (normalized === "complete" || normalized === "completed" || normalized === "done" || normalized === "ok" || normalized === "success" || normalized === "succeeded" || normalized === "successful" || normalized === "exit 0") return "complete";
|
|
552
|
+
if (normalized === "error" || normalized === "failed" || normalized === "failure" || normalized.startsWith("exit ")) return normalized === "exit 0" ? "complete" : "error";
|
|
553
|
+
return "in_progress";
|
|
775
554
|
}
|
|
776
|
-
function
|
|
777
|
-
|
|
778
|
-
return typeof params.blockStreamingEnabled === "boolean" ? !params.blockStreamingEnabled : void 0;
|
|
555
|
+
function slugTaskIdPart(value) {
|
|
556
|
+
return value?.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "task";
|
|
779
557
|
}
|
|
780
|
-
function
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
const trimmed = label.trim();
|
|
784
|
-
return trimmed && trimmed.toLowerCase() !== "auto" ? trimmed : void 0;
|
|
558
|
+
function stableTaskIdPart(value, slugValue = value) {
|
|
559
|
+
const suffix = createHash("sha256").update(value).digest("hex").slice(0, 8);
|
|
560
|
+
return `${slugTaskIdPart(slugValue).slice(0, 48)}_${suffix}`;
|
|
785
561
|
}
|
|
786
|
-
function
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
562
|
+
function resolveLineTaskIdentity(line) {
|
|
563
|
+
if (line.id?.trim()) return {
|
|
564
|
+
id: stableTaskIdPart(line.id),
|
|
565
|
+
contentDerived: false
|
|
566
|
+
};
|
|
567
|
+
return {
|
|
568
|
+
id: stableTaskIdPart([
|
|
569
|
+
line.kind,
|
|
570
|
+
line.toolName,
|
|
571
|
+
line.label,
|
|
572
|
+
line.text
|
|
573
|
+
].join("\0"), line.toolName ?? line.kind ?? line.label),
|
|
574
|
+
contentDerived: true
|
|
575
|
+
};
|
|
791
576
|
}
|
|
792
|
-
function
|
|
793
|
-
return
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
577
|
+
function buildPlanTasks(params) {
|
|
578
|
+
if (params.plan) return params.plan.slice(-50).map((entry, index) => ({
|
|
579
|
+
id: `plan_step_${index + 1}`,
|
|
580
|
+
title: compactTitle(entry.step),
|
|
581
|
+
status: entry.status === "completed" ? "complete" : entry.status
|
|
582
|
+
}));
|
|
583
|
+
const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_TASK_DETAIL_MAX_CHARS);
|
|
584
|
+
const lines = params.lines.slice(-50);
|
|
585
|
+
const identities = lines.map(resolveLineTaskIdentity);
|
|
586
|
+
const contentIdOccurrences = /* @__PURE__ */ new Map();
|
|
587
|
+
return lines.map((line, index) => {
|
|
588
|
+
const identity = identities[index];
|
|
589
|
+
let id = identity.id;
|
|
590
|
+
if (identity.contentDerived) {
|
|
591
|
+
const occurrence = (contentIdOccurrences.get(id) ?? 0) + 1;
|
|
592
|
+
contentIdOccurrences.set(id, occurrence);
|
|
593
|
+
id = `${id}_${occurrence}`;
|
|
594
|
+
}
|
|
595
|
+
return {
|
|
596
|
+
id,
|
|
597
|
+
title: lineTaskTitle(line, maxLineChars),
|
|
598
|
+
status: lineTaskStatus(line)
|
|
599
|
+
};
|
|
799
600
|
});
|
|
800
601
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
function getSlackStreamRecipientTeamCache(client) {
|
|
804
|
-
const existing = slackStreamRecipientTeamCaches.get(client);
|
|
805
|
-
if (existing) return existing;
|
|
806
|
-
const cache = /* @__PURE__ */ new Map();
|
|
807
|
-
slackStreamRecipientTeamCaches.set(client, cache);
|
|
808
|
-
return cache;
|
|
602
|
+
function resolvePlanTitle(params) {
|
|
603
|
+
return compactChunkText(params.title?.trim() || params.label?.trim() || params.tasks.at(-1)?.title || SLACK_PROGRESS_PLAN_FALLBACK_TITLE);
|
|
809
604
|
}
|
|
810
|
-
function
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
const renderedText = renderPlan.mode === "single" ? renderPlan.text : renderPlan.fallbackText;
|
|
816
|
-
if (!reply.hasContent && !slackBlocks?.length && !renderedText.trim()) return null;
|
|
817
|
-
return JSON.stringify({
|
|
818
|
-
kind: params.kind,
|
|
819
|
-
threadTs: params.threadTs ?? "",
|
|
820
|
-
replyToId: params.payload.replyToId ?? null,
|
|
821
|
-
text: renderedText || reply.trimmedText,
|
|
822
|
-
mediaUrls: reply.mediaUrls,
|
|
823
|
-
blocks: slackBlocks ?? null
|
|
605
|
+
function buildSlackProgressStreamChunks(params) {
|
|
606
|
+
const tasks = buildPlanTasks({
|
|
607
|
+
lines: params.lines,
|
|
608
|
+
plan: params.plan,
|
|
609
|
+
maxLineChars: params.maxLineChars
|
|
824
610
|
});
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
if (!cached) return;
|
|
832
|
-
cache.delete(cacheKey);
|
|
833
|
-
cache.set(cacheKey, cached);
|
|
834
|
-
return cached;
|
|
835
|
-
}
|
|
836
|
-
function rememberSlackStreamRecipientTeam(params) {
|
|
837
|
-
if (!params.fallbackTeamId || !params.userId) return;
|
|
838
|
-
const cacheKey = `${params.fallbackTeamId}:${params.userId}`;
|
|
839
|
-
const cache = getSlackStreamRecipientTeamCache(params.client);
|
|
840
|
-
if (cache.has(cacheKey)) cache.delete(cacheKey);
|
|
841
|
-
cache.set(cacheKey, params.teamId);
|
|
842
|
-
if (cache.size > SLACK_STREAM_RECIPIENT_TEAM_CACHE_MAX) {
|
|
843
|
-
const oldest = cache.keys().next().value;
|
|
844
|
-
if (oldest) cache.delete(oldest);
|
|
611
|
+
if (tasks.length === 0) {
|
|
612
|
+
const title = params.title?.trim() || params.label?.trim();
|
|
613
|
+
return title ? [{
|
|
614
|
+
type: "plan_update",
|
|
615
|
+
title: compactChunkText(title)
|
|
616
|
+
}] : void 0;
|
|
845
617
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
618
|
+
return [{
|
|
619
|
+
type: "plan_update",
|
|
620
|
+
title: resolvePlanTitle({
|
|
621
|
+
label: params.label,
|
|
622
|
+
title: params.title,
|
|
623
|
+
tasks
|
|
624
|
+
})
|
|
625
|
+
}, ...tasks.map((task) => ({
|
|
626
|
+
type: "task_update",
|
|
627
|
+
id: task.id,
|
|
628
|
+
title: task.title,
|
|
629
|
+
status: task.status === "in_progress" ? params.finalInProgressStatus ?? (params.completeInProgress ? "complete" : task.status) : task.status
|
|
630
|
+
}))];
|
|
631
|
+
}
|
|
632
|
+
function buildSlackProgressDraftBlocks(params) {
|
|
633
|
+
const label = params.label?.trim() || params.title?.trim();
|
|
634
|
+
const maxLineChars = resolveMaxLineChars(params.maxLineChars, DEFAULT_SLACK_PROGRESS_DETAIL_MAX_CHARS);
|
|
635
|
+
const planLines = formatPlanChecklistLines(params.plan ?? [], {
|
|
636
|
+
maxLines: 50,
|
|
637
|
+
maxLineChars
|
|
638
|
+
});
|
|
639
|
+
const narration = params.narration?.replace(/\s+/g, " ").trim();
|
|
640
|
+
const headBlocks = [
|
|
641
|
+
...label ? [{
|
|
642
|
+
type: "section",
|
|
643
|
+
text: field(`*${escapeSlackMrkdwn(label)}*`)
|
|
644
|
+
}] : [],
|
|
645
|
+
...narration ? [{
|
|
646
|
+
type: "section",
|
|
647
|
+
text: field(`_${escapeSlackMrkdwn(narration)}_`)
|
|
648
|
+
}] : [],
|
|
649
|
+
...planLines.length > 0 ? [{
|
|
650
|
+
type: "section",
|
|
651
|
+
text: field(planLines.map((line) => escapeSlackMrkdwn(line)).join("\n"))
|
|
652
|
+
}] : []
|
|
653
|
+
].slice(0, 50);
|
|
654
|
+
const lineBudget = Math.max(0, 50 - headBlocks.length);
|
|
655
|
+
const renderedBlocks = [...headBlocks, ...params.lines.slice(-lineBudget).map((line) => ({
|
|
656
|
+
type: "section",
|
|
657
|
+
fields: [field(legacyLineTitle(line)), field(legacyLineDetail(line, maxLineChars))]
|
|
658
|
+
}))];
|
|
659
|
+
return renderedBlocks.length ? renderedBlocks : void 0;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Slack native streams key task rows by persistent id with no removal chunk.
|
|
663
|
+
* When the task source switches representation (tool lines <-> typed plan) or
|
|
664
|
+
* a snapshot drops ids, previously emitted non-terminal rows must receive a
|
|
665
|
+
* final update or they linger in_progress forever.
|
|
666
|
+
*/
|
|
667
|
+
function reconcileSlackNativeTaskChunks(params) {
|
|
668
|
+
const nextTasks = /* @__PURE__ */ new Map();
|
|
669
|
+
for (const chunk of params.chunks ?? []) if (chunk.type === "task_update") nextTasks.set(chunk.id, {
|
|
670
|
+
title: chunk.title,
|
|
671
|
+
status: chunk.status
|
|
672
|
+
});
|
|
673
|
+
const terminalized = [...params.previousTasks].filter(([id, task]) => !nextTasks.has(id) && task.status !== "complete" && task.status !== "error").map(([id, task]) => {
|
|
674
|
+
const entry = {
|
|
675
|
+
title: task.title,
|
|
676
|
+
status: "complete"
|
|
677
|
+
};
|
|
678
|
+
nextTasks.set(id, entry);
|
|
679
|
+
return {
|
|
680
|
+
type: "task_update",
|
|
681
|
+
id,
|
|
682
|
+
title: task.title,
|
|
683
|
+
status: "complete"
|
|
684
|
+
};
|
|
685
|
+
});
|
|
686
|
+
for (const [id, task] of params.previousTasks) if (!nextTasks.has(id)) nextTasks.set(id, task);
|
|
687
|
+
return {
|
|
688
|
+
chunks: params.chunks?.length ? [...params.chunks, ...terminalized] : terminalized.length ? terminalized : params.chunks,
|
|
689
|
+
tasks: nextTasks
|
|
858
690
|
};
|
|
859
691
|
}
|
|
860
|
-
function
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
692
|
+
function buildSlackProgressStreamStartChunks(params) {
|
|
693
|
+
return buildSlackProgressStreamChunks(params);
|
|
694
|
+
}
|
|
695
|
+
function buildSlackProgressStreamUpdateChunks(params) {
|
|
696
|
+
return buildSlackProgressStreamChunks(params);
|
|
697
|
+
}
|
|
698
|
+
function buildSlackProgressStreamCompletionChunks(params) {
|
|
699
|
+
return buildSlackProgressStreamChunks({
|
|
700
|
+
...params,
|
|
701
|
+
completeInProgress: true
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
//#endregion
|
|
705
|
+
//#region extensions/slack/src/stream-mode.ts
|
|
706
|
+
function resolveSlackStreamingConfig(params) {
|
|
707
|
+
const mode = resolveSlackStreamingMode(params);
|
|
708
|
+
return {
|
|
709
|
+
mode,
|
|
710
|
+
nativeStreaming: resolveSlackNativeStreaming(params),
|
|
711
|
+
draftMode: mapStreamingModeToSlackLegacyDraftStreamMode(mode)
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
function applyAppendOnlyStreamUpdate(params) {
|
|
715
|
+
const incoming = params.incoming.trimEnd();
|
|
716
|
+
if (!incoming) return {
|
|
717
|
+
rendered: params.rendered,
|
|
718
|
+
source: params.source,
|
|
719
|
+
changed: false
|
|
720
|
+
};
|
|
721
|
+
if (!params.rendered) return {
|
|
722
|
+
rendered: incoming,
|
|
723
|
+
source: incoming,
|
|
724
|
+
changed: true
|
|
725
|
+
};
|
|
726
|
+
if (incoming === params.source) return {
|
|
727
|
+
rendered: params.rendered,
|
|
728
|
+
source: params.source,
|
|
729
|
+
changed: false
|
|
730
|
+
};
|
|
731
|
+
if (incoming.startsWith(params.source) || incoming.startsWith(params.rendered)) return {
|
|
732
|
+
rendered: incoming,
|
|
733
|
+
source: incoming,
|
|
734
|
+
changed: incoming !== params.rendered
|
|
735
|
+
};
|
|
736
|
+
if (params.source.startsWith(incoming)) return {
|
|
737
|
+
rendered: params.rendered,
|
|
738
|
+
source: params.source,
|
|
739
|
+
changed: false
|
|
740
|
+
};
|
|
741
|
+
const separator = params.rendered.endsWith("\n") ? "" : "\n";
|
|
742
|
+
return {
|
|
743
|
+
rendered: `${params.rendered}${separator}${incoming}`,
|
|
744
|
+
source: incoming,
|
|
745
|
+
changed: true
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
//#endregion
|
|
749
|
+
//#region extensions/slack/src/monitor/message-handler/preview-finalize.ts
|
|
750
|
+
function buildExpectedSlackEditText(params) {
|
|
751
|
+
const trimmedText = params.text.trim();
|
|
752
|
+
if (trimmedText) return normalizeSlackOutboundText(trimmedText);
|
|
753
|
+
if (params.blocks?.length) return normalizeSlackOutboundText(buildSlackBlocksFallbackText(params.blocks));
|
|
754
|
+
return " ";
|
|
755
|
+
}
|
|
756
|
+
function buildAcceptedSlackEditTexts(params) {
|
|
757
|
+
const expected = buildExpectedSlackEditText(params);
|
|
758
|
+
const texts = /* @__PURE__ */ new Set([
|
|
759
|
+
expected,
|
|
760
|
+
normalizeSlackOutboundText(truncateSlackText(expected, SLACK_EDIT_TEXT_LIMIT)),
|
|
761
|
+
normalizeSlackOutboundText(buildSlackEditTextPayload(params.text, params.blocks))
|
|
762
|
+
]);
|
|
763
|
+
if (params.blocks?.length && hasSlackNativeDataBlock(params.blocks)) {
|
|
764
|
+
const fallbackPlan = buildSlackNativeDataDeliveryPlan({
|
|
765
|
+
baseText: params.text,
|
|
766
|
+
blocks: params.blocks
|
|
767
|
+
});
|
|
768
|
+
for (const message of fallbackPlan.fallbackMessages) texts.add(normalizeSlackOutboundText(message.text));
|
|
769
|
+
}
|
|
770
|
+
return texts;
|
|
771
|
+
}
|
|
772
|
+
function blocksMatch(expected, actual) {
|
|
773
|
+
if (!expected?.length) return !actual?.length;
|
|
774
|
+
if (!actual?.length) {
|
|
775
|
+
if (!hasSlackNativeDataBlock(expected)) return false;
|
|
776
|
+
return buildSlackNativeDataDeliveryPlan({ blocks: expected }).fallbackMessages.every((message) => !message.blocks?.length);
|
|
777
|
+
}
|
|
778
|
+
if (JSON.stringify(expected) === JSON.stringify(actual)) return true;
|
|
779
|
+
if (!hasSlackNativeDataBlock(expected)) return false;
|
|
780
|
+
try {
|
|
781
|
+
const fallbackPlan = buildSlackNativeDataDeliveryPlan({ blocks: expected });
|
|
782
|
+
const fallbackBlocks = fallbackPlan.fallbackMessages.flatMap((message) => message.blocks ?? []);
|
|
783
|
+
if (JSON.stringify(fallbackBlocks) === JSON.stringify(actual)) return true;
|
|
784
|
+
const fallbackText = fallbackPlan.fallbackMessages.map((message) => message.text).filter(Boolean).join("\n\n");
|
|
785
|
+
return normalizeSlackOutboundText(buildSlackBlocksFallbackText(actual)) === normalizeSlackOutboundText(fallbackText);
|
|
786
|
+
} catch {
|
|
864
787
|
return false;
|
|
865
788
|
}
|
|
866
|
-
return true;
|
|
867
789
|
}
|
|
868
|
-
async function
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
790
|
+
async function readSlackMessageAfterEditError(params) {
|
|
791
|
+
if (params.threadTs) return ((await params.client.conversations.replies({
|
|
792
|
+
token: params.token,
|
|
793
|
+
channel: params.channelId,
|
|
794
|
+
ts: params.threadTs,
|
|
795
|
+
latest: params.messageId,
|
|
796
|
+
inclusive: true,
|
|
797
|
+
limit: 100
|
|
798
|
+
})).messages ?? []).find((message) => message?.ts === params.messageId) ?? null;
|
|
799
|
+
const message = (await params.client.conversations.history({
|
|
800
|
+
token: params.token,
|
|
801
|
+
channel: params.channelId,
|
|
802
|
+
latest: params.messageId,
|
|
803
|
+
oldest: params.messageId,
|
|
804
|
+
inclusive: true,
|
|
805
|
+
limit: 1
|
|
806
|
+
})).messages?.[0];
|
|
807
|
+
if (!message?.ts || message.ts !== params.messageId) return null;
|
|
808
|
+
return message;
|
|
809
|
+
}
|
|
810
|
+
async function didSlackPreviewEditApplyAfterError(params) {
|
|
811
|
+
const readback = await readSlackMessageAfterEditError(params);
|
|
812
|
+
if (!readback) return false;
|
|
813
|
+
const expectedText = buildExpectedSlackEditText({
|
|
814
|
+
text: params.text,
|
|
815
|
+
blocks: params.blocks
|
|
816
|
+
});
|
|
817
|
+
const acceptedTexts = buildAcceptedSlackEditTexts({
|
|
818
|
+
text: params.text,
|
|
819
|
+
blocks: params.blocks
|
|
820
|
+
});
|
|
821
|
+
const actualText = normalizeSlackOutboundText((readback.text ?? "").trim());
|
|
822
|
+
if (params.blocks?.length) return acceptedTexts.has(actualText) && blocksMatch(params.blocks, readback.blocks);
|
|
823
|
+
return actualText === expectedText;
|
|
824
|
+
}
|
|
825
|
+
async function finalizeSlackPreviewEdit(params) {
|
|
826
|
+
try {
|
|
827
|
+
await editSlackMessage(params.channelId, params.messageId, params.text, {
|
|
873
828
|
token: params.token,
|
|
874
|
-
|
|
829
|
+
accountId: params.accountId,
|
|
830
|
+
client: params.client,
|
|
831
|
+
...params.blocks?.length ? { blocks: params.blocks } : {}
|
|
875
832
|
});
|
|
876
|
-
const teamId = info.user?.team_id ?? info.user?.profile?.team;
|
|
877
|
-
if (teamId) {
|
|
878
|
-
rememberSlackStreamRecipientTeam({
|
|
879
|
-
...params,
|
|
880
|
-
teamId
|
|
881
|
-
});
|
|
882
|
-
return teamId;
|
|
883
|
-
}
|
|
884
833
|
} catch (err) {
|
|
885
|
-
|
|
834
|
+
try {
|
|
835
|
+
if (await didSlackPreviewEditApplyAfterError({
|
|
836
|
+
client: params.client,
|
|
837
|
+
token: params.token,
|
|
838
|
+
channelId: params.channelId,
|
|
839
|
+
messageId: params.messageId,
|
|
840
|
+
text: params.text,
|
|
841
|
+
blocks: params.blocks,
|
|
842
|
+
threadTs: params.threadTs
|
|
843
|
+
})) {
|
|
844
|
+
logVerbose(`slack: preview final edit response failed but readback matched message ${params.channelId}/${params.messageId}; suppressing duplicate fallback send`);
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
} catch (readbackErr) {
|
|
848
|
+
logVerbose(`slack: preview final edit readback failed (${String(readbackErr)})`);
|
|
849
|
+
}
|
|
850
|
+
throw err;
|
|
886
851
|
}
|
|
887
|
-
return params.fallbackTeamId;
|
|
888
852
|
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
const
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
iconEmoji: outboundIdentity.emoji
|
|
900
|
-
} : prepared.relayIdentity;
|
|
901
|
-
if (prepared.isDirectMessage) {
|
|
902
|
-
const sessionCfg = cfg.session;
|
|
903
|
-
const storePath = resolveStorePath(sessionCfg?.store, { agentId: route.agentId });
|
|
904
|
-
const pinnedMainDmOwner = resolvePinnedMainDmOwnerFromAllowlist({
|
|
905
|
-
dmScope: cfg.session?.dmScope,
|
|
906
|
-
allowFrom: ctx.allowFrom,
|
|
907
|
-
normalizeEntry: normalizeSlackAllowOwnerEntry
|
|
908
|
-
});
|
|
909
|
-
const senderRecipient = normalizeOptionalLowercaseString(message.user);
|
|
910
|
-
const inboundLastRouteSessionKey = resolveInboundLastRouteSessionKey({
|
|
911
|
-
route,
|
|
912
|
-
sessionKey: prepared.ctxPayload.SessionKey ?? route.sessionKey
|
|
913
|
-
});
|
|
914
|
-
if (inboundLastRouteSessionKey === route.mainSessionKey && pinnedMainDmOwner && senderRecipient && normalizeOptionalLowercaseString(pinnedMainDmOwner) !== senderRecipient) logVerbose(`slack: skip main-session last route for ${senderRecipient} (pinned owner ${pinnedMainDmOwner})`);
|
|
915
|
-
else await updateLastRoute({
|
|
916
|
-
storePath,
|
|
917
|
-
sessionKey: inboundLastRouteSessionKey,
|
|
918
|
-
deliveryContext: {
|
|
919
|
-
channel: "slack",
|
|
920
|
-
to: `user:${message.user}`,
|
|
921
|
-
accountId: route.accountId,
|
|
922
|
-
threadId: prepared.ctxPayload.MessageThreadId ?? prepared.ctxPayload.TransportThreadId
|
|
923
|
-
},
|
|
924
|
-
ctx: prepared.ctxPayload
|
|
853
|
+
//#endregion
|
|
854
|
+
//#region extensions/slack/src/monitor/message-handler/dispatch-progress-io.ts
|
|
855
|
+
async function collapseSlackProgressReceipt(params) {
|
|
856
|
+
const { account, ctx, slackClient } = params.setup;
|
|
857
|
+
try {
|
|
858
|
+
await finalizeSlackPreviewEdit({
|
|
859
|
+
client: slackClient,
|
|
860
|
+
token: ctx.botToken,
|
|
861
|
+
accountId: account.accountId,
|
|
862
|
+
...params.edit
|
|
925
863
|
});
|
|
864
|
+
return true;
|
|
865
|
+
} catch (err) {
|
|
866
|
+
logVerbose(`slack: progress receipt edit failed (${formatSlackError(err)})`);
|
|
867
|
+
return false;
|
|
926
868
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
869
|
+
}
|
|
870
|
+
//#endregion
|
|
871
|
+
//#region extensions/slack/src/monitor/message-handler/dispatch-progress-render.ts
|
|
872
|
+
function resolveStructuredProgressLines(lines) {
|
|
873
|
+
return lines.map((line) => {
|
|
874
|
+
if (typeof line !== "string") return line;
|
|
875
|
+
const reasoning = line.startsWith("🧠 ");
|
|
876
|
+
const text = line.replace(/^(?:🧠|💬)\s+/u, "").replace(/^_(.*)_$/su, "$1").trim();
|
|
877
|
+
return {
|
|
878
|
+
...reasoning ? { id: "reasoning" } : {},
|
|
879
|
+
kind: "item",
|
|
880
|
+
text,
|
|
881
|
+
label: reasoning ? "Reasoning" : "Update",
|
|
882
|
+
prefix: false
|
|
883
|
+
};
|
|
930
884
|
});
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
const
|
|
937
|
-
|
|
938
|
-
|
|
885
|
+
}
|
|
886
|
+
function resolveNativeProgressPlan(snapshot) {
|
|
887
|
+
return snapshot.plan?.length ? snapshot.plan : void 0;
|
|
888
|
+
}
|
|
889
|
+
function resolveNativeProgressLines(snapshot) {
|
|
890
|
+
const lines = resolveStructuredProgressLines(snapshot.lines);
|
|
891
|
+
if (snapshot.plan?.length || !snapshot.planExplanation) return lines;
|
|
892
|
+
const explanationLine = buildChannelProgressDraftLine({
|
|
893
|
+
event: "plan",
|
|
894
|
+
phase: "update",
|
|
895
|
+
explanation: snapshot.planExplanation
|
|
939
896
|
});
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
897
|
+
return explanationLine ? [...lines, explanationLine] : lines;
|
|
898
|
+
}
|
|
899
|
+
function combineProgressHeadlineAndExplanation(headline, explanation) {
|
|
900
|
+
return headline && explanation && headline !== explanation ? `${headline} — ${explanation}` : headline ?? explanation;
|
|
901
|
+
}
|
|
902
|
+
function buildNativeProgressChunks(params) {
|
|
903
|
+
const input = {
|
|
904
|
+
title: params.title,
|
|
905
|
+
lines: resolveNativeProgressLines(params.snapshot),
|
|
906
|
+
plan: resolveNativeProgressPlan(params.snapshot),
|
|
907
|
+
maxLineChars: params.maxLineChars
|
|
951
908
|
};
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
const
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
909
|
+
return params.streamStarted ? buildSlackProgressStreamUpdateChunks(input) : buildSlackProgressStreamStartChunks(input);
|
|
910
|
+
}
|
|
911
|
+
//#endregion
|
|
912
|
+
//#region extensions/slack/src/monitor/message-handler/dispatch-progress.ts
|
|
913
|
+
function createSlackProgressRuntime(runtimeParams) {
|
|
914
|
+
const { setup, delivery, resetPreviewDeliveryState } = runtimeParams;
|
|
915
|
+
const { account, cfg, ctx, message, prepared, replyPlan, runtime, slackClient, slackIdentity, slackMessageMetadata, slackStreaming, slackStreamFallbackTeamId, shouldUseDraftStream, useStreaming, previewStreamingEnabled } = setup;
|
|
916
|
+
const draftStream = shouldUseDraftStream ? createSlackDraftStream({
|
|
917
|
+
target: prepared.replyTarget,
|
|
918
|
+
cfg,
|
|
919
|
+
token: ctx.botToken,
|
|
920
|
+
accountId: account.accountId,
|
|
921
|
+
...prepared.eventScope ? { eventScope: prepared.eventScope } : {},
|
|
922
|
+
identity: slackIdentity,
|
|
923
|
+
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
|
|
924
|
+
maxChars: Math.min(ctx.textLimit, SLACK_TEXT_LIMIT),
|
|
925
|
+
resolveThreadTs: () => {
|
|
926
|
+
const ts = replyPlan.peekThreadTs();
|
|
927
|
+
if (ts) delivery.usedReplyThreadTs ??= ts;
|
|
928
|
+
return ts;
|
|
966
929
|
},
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
930
|
+
log: logVerbose,
|
|
931
|
+
warn: logVerbose
|
|
932
|
+
}) : void 0;
|
|
933
|
+
let hasStreamedMessage = false;
|
|
934
|
+
const streamMode = slackStreaming.draftMode;
|
|
935
|
+
const useNativeProgressStreaming = useStreaming && slackStreaming.mode === "progress";
|
|
936
|
+
const progressDraftActive = Boolean(draftStream) || useNativeProgressStreaming;
|
|
937
|
+
const previewToolProgressEnabled = progressDraftActive && resolveChannelStreamingPreviewToolProgress(account.config);
|
|
938
|
+
let shouldYieldDraftProgress = () => false;
|
|
939
|
+
const suppressDefaultToolProgressMessages = resolveChannelStreamingSuppressDefaultToolProgressMessages(account.config, {
|
|
940
|
+
draftStreamActive: Boolean(draftStream) || useNativeProgressStreaming,
|
|
941
|
+
previewToolProgressEnabled,
|
|
942
|
+
previewStreamingEnabled
|
|
943
|
+
});
|
|
944
|
+
let previewToolProgressSuppressed = false;
|
|
945
|
+
let legacyPreviewToolProgressLines = [];
|
|
946
|
+
let nativeTaskState = /* @__PURE__ */ new Map();
|
|
947
|
+
let appendRenderedText = "";
|
|
948
|
+
let appendSourceText = "";
|
|
949
|
+
let nativeProgressCompletionSent = false;
|
|
950
|
+
let nativeProgressTerminalStatus = "complete";
|
|
951
|
+
let nativeProgressChunkKey;
|
|
952
|
+
const progressReceipt = createChannelProgressReceiptTracker();
|
|
953
|
+
let progressReceiptCollapsed = false;
|
|
954
|
+
let pendingNativeProgressReceipt;
|
|
955
|
+
const progressSeed = `${account.accountId}:${message.channel}`;
|
|
956
|
+
const useRichProgressDraft = streamMode === "status_final" && resolveChannelProgressDraftRender(account.config) === "rich";
|
|
957
|
+
const explicitProgressTitle = resolveExplicitSlackProgressTitle(account.config);
|
|
958
|
+
const progressDraftMaxLineChars = resolveChannelProgressDraftMaxLineChars(account.config);
|
|
959
|
+
const waitForNativeProgressStreamStart = async () => {
|
|
960
|
+
if (delivery.streamSession || !delivery.nativeProgressStreamStartPromise) return true;
|
|
961
|
+
try {
|
|
962
|
+
await delivery.nativeProgressStreamStartPromise;
|
|
963
|
+
} catch {
|
|
964
|
+
delivery.streamFailed = true;
|
|
965
|
+
return false;
|
|
975
966
|
}
|
|
967
|
+
return !delivery.streamFailed;
|
|
976
968
|
};
|
|
977
|
-
const
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
onError: (err) => {
|
|
988
|
-
logAckFailure({
|
|
989
|
-
log: logVerbose,
|
|
990
|
-
channel: "slack",
|
|
991
|
-
target: `${message.channel}/${message.ts}`,
|
|
992
|
-
error: err
|
|
969
|
+
const appendNativeProgressCompletion = async (isError) => {
|
|
970
|
+
const session = delivery.streamSession;
|
|
971
|
+
if (isError) nativeProgressTerminalStatus = "error";
|
|
972
|
+
if (!session || nativeProgressCompletionSent) return;
|
|
973
|
+
const chunks = buildNativeProgressCompletionChunks(isError ? "error" : "complete");
|
|
974
|
+
if (!chunks?.length) return;
|
|
975
|
+
try {
|
|
976
|
+
await appendSlackStream({
|
|
977
|
+
session,
|
|
978
|
+
chunks
|
|
993
979
|
});
|
|
980
|
+
nativeProgressCompletionSent = true;
|
|
981
|
+
delivery.observedReplyDelivery ||= session.delivered;
|
|
982
|
+
} catch (err) {
|
|
983
|
+
delivery.streamFailed = true;
|
|
984
|
+
runtime.error?.(danger(`slack-stream: native progress completion failed: ${formatSlackError(err)}`));
|
|
994
985
|
}
|
|
986
|
+
};
|
|
987
|
+
const buildNativeProgressChunks$1 = (snapshot) => buildNativeProgressChunks({
|
|
988
|
+
snapshot,
|
|
989
|
+
streamStarted: Boolean(delivery.streamSession),
|
|
990
|
+
title: combineProgressHeadlineAndExplanation(explicitProgressTitle ?? snapshot.statusHeadline, snapshot.planExplanation),
|
|
991
|
+
maxLineChars: progressDraftMaxLineChars
|
|
995
992
|
});
|
|
996
|
-
|
|
997
|
-
const
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
hasRepliedRef,
|
|
1003
|
-
isThreadReply: Boolean(forcedReplyThreadTs) || isThreadReply
|
|
1004
|
-
});
|
|
1005
|
-
const typingTarget = statusThreadTs ? `${message.channel}/${statusThreadTs}` : message.channel;
|
|
1006
|
-
const typingReaction = ctx.typingReaction;
|
|
1007
|
-
const { onModelSelected, ...replyPipeline } = createChannelMessageReplyPipeline({
|
|
1008
|
-
cfg,
|
|
1009
|
-
agentId: route.agentId,
|
|
1010
|
-
channel: "slack",
|
|
1011
|
-
accountId: route.accountId,
|
|
1012
|
-
transformReplyPayload: (payload) => {
|
|
1013
|
-
if (payload.isReasoning === true) return null;
|
|
1014
|
-
return isSlackInteractiveRepliesEnabled({
|
|
1015
|
-
cfg,
|
|
1016
|
-
accountId: route.accountId
|
|
1017
|
-
}) ? compileSlackInteractiveReplies(payload) : payload;
|
|
1018
|
-
},
|
|
1019
|
-
typing: {
|
|
1020
|
-
start: async () => {
|
|
1021
|
-
didSetStatus = true;
|
|
1022
|
-
await ctx.setSlackThreadStatus({
|
|
1023
|
-
channelId: message.channel,
|
|
1024
|
-
threadTs: statusThreadTs,
|
|
1025
|
-
status: "is typing...",
|
|
1026
|
-
eventScope: prepared.eventScope
|
|
1027
|
-
});
|
|
1028
|
-
if (typingReaction && message.ts) await reactSlackMessage(message.channel, message.ts, typingReaction, {
|
|
1029
|
-
token: ctx.botToken,
|
|
1030
|
-
client: slackClient
|
|
1031
|
-
}).catch((err) => {
|
|
1032
|
-
logVerbose(`slack send: typing reaction failed: ${formatSlackError(err)}`);
|
|
1033
|
-
});
|
|
1034
|
-
},
|
|
1035
|
-
stop: async () => {
|
|
1036
|
-
if (!didSetStatus) return;
|
|
1037
|
-
didSetStatus = false;
|
|
1038
|
-
await ctx.setSlackThreadStatus({
|
|
1039
|
-
channelId: message.channel,
|
|
1040
|
-
threadTs: statusThreadTs,
|
|
1041
|
-
status: "",
|
|
1042
|
-
eventScope: prepared.eventScope
|
|
1043
|
-
});
|
|
1044
|
-
if (typingReaction && message.ts) await removeSlackReaction(message.channel, message.ts, typingReaction, {
|
|
1045
|
-
token: ctx.botToken,
|
|
1046
|
-
client: slackClient
|
|
1047
|
-
}).catch((err) => {
|
|
1048
|
-
logVerbose(`slack send: typing reaction removal failed: ${formatSlackError(err)}`);
|
|
1049
|
-
});
|
|
1050
|
-
},
|
|
1051
|
-
onStartError: (err) => {
|
|
1052
|
-
logTypingFailure({
|
|
1053
|
-
log: (messageValue) => runtime.error?.(danger(messageValue)),
|
|
1054
|
-
channel: "slack",
|
|
1055
|
-
action: "start",
|
|
1056
|
-
target: typingTarget,
|
|
1057
|
-
error: err
|
|
1058
|
-
});
|
|
1059
|
-
},
|
|
1060
|
-
onStopError: (err) => {
|
|
1061
|
-
logTypingFailure({
|
|
1062
|
-
log: (messageLocal) => runtime.error?.(danger(messageLocal)),
|
|
1063
|
-
channel: "slack",
|
|
1064
|
-
action: "stop",
|
|
1065
|
-
target: typingTarget,
|
|
1066
|
-
error: err
|
|
1067
|
-
});
|
|
1068
|
-
}
|
|
993
|
+
const resolveNativeProgressTitle = (snapshot) => combineProgressHeadlineAndExplanation(explicitProgressTitle ?? snapshot.statusHeadline, snapshot.planExplanation);
|
|
994
|
+
const markNativeProgressDelivered = (session, threadTs) => {
|
|
995
|
+
if (session.delivered) delivery.observedReplyDelivery = true;
|
|
996
|
+
if (threadTs) {
|
|
997
|
+
delivery.usedReplyThreadTs ??= threadTs;
|
|
998
|
+
delivery.rememberDeliveredThreadTs("block", threadTs);
|
|
1069
999
|
}
|
|
1070
|
-
});
|
|
1071
|
-
const slackStreaming = resolveSlackStreamingConfig({
|
|
1072
|
-
streaming: account.config.streaming,
|
|
1073
|
-
nativeStreaming: resolveChannelStreamingNativeTransport(account.config)
|
|
1074
|
-
});
|
|
1075
|
-
const streamThreadHint = forcedReplyThreadTs ?? resolveSlackStreamingThreadHint({
|
|
1076
|
-
replyToMode: replyDeliveryMode,
|
|
1077
|
-
incomingThreadTs,
|
|
1078
|
-
messageTs,
|
|
1079
|
-
isThreadReply
|
|
1080
|
-
});
|
|
1081
|
-
const previewStreamingEnabled = !sourceRepliesAreToolOnly && shouldEnableSlackPreviewStreaming({ mode: slackStreaming.mode });
|
|
1082
|
-
const hasSlackCustomIdentity = Boolean(slackIdentity?.username || slackIdentity?.iconUrl || slackIdentity?.iconEmoji);
|
|
1083
|
-
const useStreaming = shouldUseStreaming({
|
|
1084
|
-
streamingEnabled: !sourceRepliesAreToolOnly && isSlackStreamingEnabled({
|
|
1085
|
-
mode: slackStreaming.mode,
|
|
1086
|
-
nativeStreaming: slackStreaming.nativeStreaming,
|
|
1087
|
-
nativeProgressTaskCards: resolveSlackNativeProgressTaskCards(account.config)
|
|
1088
|
-
}),
|
|
1089
|
-
threadTs: streamThreadHint
|
|
1090
|
-
});
|
|
1091
|
-
const shouldUseDraftStream = !hasSlackCustomIdentity && shouldInitializeSlackDraftStream({
|
|
1092
|
-
previewStreamingEnabled,
|
|
1093
|
-
useStreaming
|
|
1094
|
-
});
|
|
1095
|
-
const blockStreamingEnabled = resolveChannelStreamingBlockEnabled(account.config);
|
|
1096
|
-
const disableBlockStreaming = sourceRepliesAreToolOnly ? true : resolveSlackDisableBlockStreaming({
|
|
1097
|
-
useStreaming,
|
|
1098
|
-
shouldUseDraftStream,
|
|
1099
|
-
blockStreamingEnabled
|
|
1100
|
-
});
|
|
1101
|
-
let streamSession = null;
|
|
1102
|
-
let nativeProgressStreamStartPromise = null;
|
|
1103
|
-
let nativeProgressStreamThreadTs;
|
|
1104
|
-
let streamFailed = false;
|
|
1105
|
-
let usedReplyThreadTs;
|
|
1106
|
-
let usedBlockReplyThreadTs;
|
|
1107
|
-
let observedReplyDelivery = false;
|
|
1108
|
-
let observedFinalReplyDelivery = false;
|
|
1109
|
-
const streamedDeliveries = [];
|
|
1110
|
-
const streamedFailuresOwnedByDispatcher = {
|
|
1111
|
-
tool: 0,
|
|
1112
|
-
block: 0,
|
|
1113
|
-
final: 0
|
|
1114
|
-
};
|
|
1115
|
-
const refreshStreamedAcknowledgements = (session) => {
|
|
1116
|
-
if (session.pendingText.length === 0) for (const delivery of streamedDeliveries) delivery.acknowledged = true;
|
|
1117
1000
|
};
|
|
1118
|
-
const
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1001
|
+
const startNativeProgressStream = async (chunks, chunkKey) => {
|
|
1002
|
+
const streamThreadTs = replyPlan.nextThreadTs();
|
|
1003
|
+
if (!streamThreadTs) {
|
|
1004
|
+
logVerbose("slack-stream: no reply thread target for native progress stream start, falling back");
|
|
1005
|
+
delivery.streamFailed = true;
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
delivery.nativeProgressStreamThreadTs = streamThreadTs;
|
|
1009
|
+
const startPromise = (async () => {
|
|
1010
|
+
const session = await startSlackStream({
|
|
1011
|
+
client: slackClient,
|
|
1012
|
+
channel: message.channel,
|
|
1013
|
+
threadTs: streamThreadTs,
|
|
1014
|
+
chunks,
|
|
1015
|
+
taskDisplayMode: "plan",
|
|
1016
|
+
...slackIdentity ? { identity: slackIdentity } : {},
|
|
1017
|
+
teamId: await resolveSlackStreamRecipientTeamId({
|
|
1018
|
+
client: slackClient,
|
|
1019
|
+
token: ctx.botToken,
|
|
1020
|
+
userId: message.user,
|
|
1021
|
+
fallbackTeamId: slackStreamFallbackTeamId
|
|
1022
|
+
}),
|
|
1023
|
+
userId: message.user
|
|
1141
1024
|
});
|
|
1142
|
-
delivery.
|
|
1025
|
+
delivery.streamSession = session;
|
|
1026
|
+
return session;
|
|
1027
|
+
})();
|
|
1028
|
+
delivery.nativeProgressStreamStartPromise = startPromise;
|
|
1029
|
+
let startedSession;
|
|
1030
|
+
try {
|
|
1031
|
+
startedSession = await startPromise;
|
|
1032
|
+
} finally {
|
|
1033
|
+
if (delivery.nativeProgressStreamStartPromise === startPromise) delivery.nativeProgressStreamStartPromise = null;
|
|
1143
1034
|
}
|
|
1035
|
+
if (startedSession) markNativeProgressDelivered(startedSession, streamThreadTs);
|
|
1036
|
+
nativeProgressChunkKey = chunkKey;
|
|
1037
|
+
replyPlan.markSent();
|
|
1144
1038
|
};
|
|
1145
|
-
const
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1039
|
+
const appendNativeProgressStream = async (chunks, chunkKey) => {
|
|
1040
|
+
if (!delivery.streamSession) return;
|
|
1041
|
+
await appendSlackStream({
|
|
1042
|
+
session: delivery.streamSession,
|
|
1043
|
+
chunks
|
|
1044
|
+
});
|
|
1045
|
+
markNativeProgressDelivered(delivery.streamSession);
|
|
1046
|
+
nativeProgressChunkKey = chunkKey;
|
|
1149
1047
|
};
|
|
1150
|
-
const
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1048
|
+
const updateNativeProgressStream = async () => {
|
|
1049
|
+
const snapshot = progressDraft.getSnapshot();
|
|
1050
|
+
const progressLines = resolveNativeProgressLines(snapshot);
|
|
1051
|
+
const hasRetirableNativeTasks = [...nativeTaskState.values()].some((task) => task.status !== "complete" && task.status !== "error");
|
|
1052
|
+
if (!useNativeProgressStreaming || delivery.streamFailed || progressLines.length === 0 && !snapshot.plan?.length && !snapshot.statusHeadline && !explicitProgressTitle && !hasRetirableNativeTasks) return;
|
|
1053
|
+
if (!await waitForNativeProgressStreamStart()) return;
|
|
1054
|
+
const reconciled = reconcileSlackNativeTaskChunks({
|
|
1055
|
+
previousTasks: nativeTaskState,
|
|
1056
|
+
chunks: buildNativeProgressChunks$1(snapshot)
|
|
1057
|
+
});
|
|
1058
|
+
const chunks = reconciled.chunks;
|
|
1059
|
+
if (!chunks?.length) return;
|
|
1060
|
+
const chunkKey = JSON.stringify(chunks);
|
|
1061
|
+
if (chunkKey === nativeProgressChunkKey) return;
|
|
1062
|
+
try {
|
|
1063
|
+
if (!delivery.streamSession) await startNativeProgressStream(chunks, chunkKey);
|
|
1064
|
+
else await appendNativeProgressStream(chunks, chunkKey);
|
|
1065
|
+
nativeTaskState = reconciled.tasks;
|
|
1066
|
+
} catch (err) {
|
|
1067
|
+
runtime.error?.(danger(`slack-stream: native progress stream failed: ${formatSlackError(err)}, falling back`));
|
|
1068
|
+
delivery.streamFailed = true;
|
|
1162
1069
|
}
|
|
1163
1070
|
};
|
|
1164
|
-
const
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
...messageSentHookContext,
|
|
1169
|
-
to: messageSentHookTarget,
|
|
1170
|
-
accountId: account.accountId,
|
|
1171
|
-
content: delivery.content,
|
|
1172
|
-
success: true,
|
|
1173
|
-
...messageId ? { messageId } : {}
|
|
1174
|
-
});
|
|
1175
|
-
delivery.outcome = "success";
|
|
1176
|
-
}
|
|
1071
|
+
const resetProgressTurnState = () => {
|
|
1072
|
+
progressReceipt.reset();
|
|
1073
|
+
progressReceiptCollapsed = false;
|
|
1074
|
+
pendingNativeProgressReceipt = void 0;
|
|
1177
1075
|
};
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
if (nextThreadTs !== params.threadTs) deliveryTracker.markDelivered({
|
|
1183
|
-
...params,
|
|
1184
|
-
threadTs: nextThreadTs
|
|
1076
|
+
const collapseProgressReceipt = async (params) => {
|
|
1077
|
+
progressReceiptCollapsed = await collapseSlackProgressReceipt({
|
|
1078
|
+
setup,
|
|
1079
|
+
edit: params
|
|
1185
1080
|
});
|
|
1186
1081
|
};
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1082
|
+
const progressDraft = createChannelProgressDraftCompositor({
|
|
1083
|
+
entry: account.config,
|
|
1084
|
+
mode: slackStreaming.mode,
|
|
1085
|
+
active: progressDraftActive && streamMode === "status_final",
|
|
1086
|
+
seed: progressSeed,
|
|
1087
|
+
formatLine: escapeSlackMrkdwn,
|
|
1088
|
+
reasoningLinePrefix: "🧠 ",
|
|
1089
|
+
commentaryLinePrefix: "💬 ",
|
|
1090
|
+
reasoningGate: previewToolProgressEnabled,
|
|
1091
|
+
commentaryItalics: false,
|
|
1092
|
+
updateOnLineChange: useNativeProgressStreaming || useRichProgressDraft,
|
|
1093
|
+
update: async (previewText, options) => {
|
|
1094
|
+
if (useNativeProgressStreaming) {
|
|
1095
|
+
await updateNativeProgressStream();
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
if (!draftStream) return;
|
|
1099
|
+
const snapshot = progressDraft.getSnapshot();
|
|
1100
|
+
const structuredLines = resolveStructuredProgressLines(options?.lines ?? snapshot.lines);
|
|
1101
|
+
const richNarration = combineProgressHeadlineAndExplanation(snapshot.statusHeadline, snapshot.planExplanation);
|
|
1102
|
+
const richProgressBlocks = useRichProgressDraft ? buildSlackProgressDraftBlocks({
|
|
1103
|
+
title: explicitProgressTitle,
|
|
1104
|
+
lines: structuredLines,
|
|
1105
|
+
plan: snapshot.plan,
|
|
1106
|
+
narration: richNarration,
|
|
1107
|
+
maxLineChars: progressDraftMaxLineChars
|
|
1108
|
+
}) : void 0;
|
|
1109
|
+
draftStream.update(useRichProgressDraft && richProgressBlocks ? {
|
|
1110
|
+
text: previewText,
|
|
1111
|
+
blocks: richProgressBlocks
|
|
1112
|
+
} : previewText);
|
|
1113
|
+
hasStreamedMessage = true;
|
|
1114
|
+
if (options?.flush) await draftStream.flush();
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
const commentaryProgressEnabled = progressDraft.commentaryProgressEnabled;
|
|
1118
|
+
const buildNativeProgressCompletionChunks = (finalInProgressStatus) => {
|
|
1119
|
+
const snapshot = progressDraft.getSnapshot();
|
|
1120
|
+
const lines = resolveNativeProgressLines(snapshot);
|
|
1121
|
+
const hasRetirableNativeTasks = [...nativeTaskState.values()].some((task) => task.status !== "complete" && task.status !== "error");
|
|
1122
|
+
if (lines.length === 0 && !snapshot.plan?.length && !hasRetirableNativeTasks) return;
|
|
1123
|
+
return reconcileSlackNativeTaskChunks({
|
|
1124
|
+
previousTasks: nativeTaskState,
|
|
1125
|
+
chunks: buildSlackProgressStreamCompletionChunks({
|
|
1126
|
+
title: resolveNativeProgressTitle(snapshot),
|
|
1127
|
+
lines,
|
|
1128
|
+
plan: resolveNativeProgressPlan(snapshot),
|
|
1129
|
+
maxLineChars: progressDraftMaxLineChars,
|
|
1130
|
+
finalInProgressStatus
|
|
1131
|
+
})
|
|
1132
|
+
}).chunks;
|
|
1195
1133
|
};
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
|
|
1134
|
+
const finishNativeProgressTurn = async (completionChunks) => {
|
|
1135
|
+
if (delivery.nativeProgressStreamStartPromise) await delivery.nativeProgressStreamStartPromise.catch(() => null);
|
|
1136
|
+
const session = delivery.streamSession;
|
|
1137
|
+
if (session && !session.stopped) try {
|
|
1138
|
+
if (completionChunks?.length) nativeProgressCompletionSent = true;
|
|
1199
1139
|
const stopResult = await stopSlackStream({
|
|
1200
1140
|
session,
|
|
1141
|
+
...completionChunks?.length ? { chunks: completionChunks } : {},
|
|
1201
1142
|
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
|
|
1202
1143
|
});
|
|
1203
|
-
acknowledgeStoppedStreamedDeliveries(session, stopResult
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
else runtime.error?.(danger(`slack-stream: failed to finalize buffered text before fallback: ${formatSlackError(stopErr)}`));
|
|
1210
|
-
}
|
|
1211
|
-
emitAcknowledgedStreamedDeliveries();
|
|
1212
|
-
const fallbackText = fallbackError.pendingText.trim();
|
|
1213
|
-
if (!fallbackText) return false;
|
|
1214
|
-
try {
|
|
1215
|
-
await deliverReplies({
|
|
1216
|
-
cfg: ctx.cfg,
|
|
1217
|
-
replies: [{ text: fallbackText }],
|
|
1218
|
-
target: prepared.replyTarget,
|
|
1219
|
-
token: ctx.botToken,
|
|
1220
|
-
accountId: account.accountId,
|
|
1221
|
-
runtime,
|
|
1222
|
-
textLimit: ctx.textLimit,
|
|
1223
|
-
mediaMaxBytes: ctx.mediaMaxBytes,
|
|
1224
|
-
replyThreadTs: session.threadTs,
|
|
1225
|
-
replyToMode: replyDeliveryMode,
|
|
1226
|
-
...slackIdentity ? { identity: slackIdentity } : {},
|
|
1227
|
-
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
|
|
1228
|
-
...messageSentDeliveryHookContext,
|
|
1229
|
-
deferMessageSentHooks: true,
|
|
1230
|
-
...prepared.eventScope ? { eventScope: prepared.eventScope } : {}
|
|
1144
|
+
delivery.acknowledgeStoppedStreamedDeliveries(session, stopResult?.messageId);
|
|
1145
|
+
if (pendingNativeProgressReceipt && stopResult?.messageId) await collapseProgressReceipt({
|
|
1146
|
+
channelId: session.channel,
|
|
1147
|
+
messageId: stopResult.messageId,
|
|
1148
|
+
text: pendingNativeProgressReceipt,
|
|
1149
|
+
threadTs: session.threadTs
|
|
1231
1150
|
});
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
});
|
|
1238
|
-
} catch (finalizeErr) {
|
|
1239
|
-
runtime.error?.(danger(`slack-stream: failed to finalize native stream after fallback delivery: ${formatSlackError(finalizeErr)}`));
|
|
1240
|
-
}
|
|
1241
|
-
emitSuccessfulPendingStreamedDeliveries();
|
|
1242
|
-
observedReplyDelivery = true;
|
|
1243
|
-
usedReplyThreadTs ??= session.threadTs;
|
|
1244
|
-
logVerbose(`slack-stream: streamed delivery failed (${fallbackError.slackCode}); delivered ${fallbackText.length} chars via deliverReplies fallback`);
|
|
1245
|
-
return true;
|
|
1246
|
-
} catch (postErr) {
|
|
1247
|
-
emitFailedPendingStreamedDeliveries(formatErrorMessage(postErr));
|
|
1248
|
-
runtime.error?.(danger(`slack-stream: fallback deliverReplies failed after ${fallbackError.slackCode}: ${formatErrorMessage(postErr)}`));
|
|
1249
|
-
return false;
|
|
1151
|
+
} catch (err) {
|
|
1152
|
+
const error = formatSlackError(err);
|
|
1153
|
+
delivery.emitAcknowledgedStreamedDeliveries();
|
|
1154
|
+
delivery.emitFailedPendingStreamedDeliveries(error);
|
|
1155
|
+
logVerbose(`slack-stream: failed to rotate native progress stream (${error})`);
|
|
1250
1156
|
}
|
|
1157
|
+
delivery.streamSession = null;
|
|
1158
|
+
delivery.nativeProgressStreamStartPromise = null;
|
|
1159
|
+
delivery.nativeProgressStreamThreadTs = void 0;
|
|
1160
|
+
delivery.streamFailed = false;
|
|
1251
1161
|
};
|
|
1252
|
-
const
|
|
1253
|
-
if (
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
if (deliveryTracker.hasDelivered({
|
|
1257
|
-
kind: params.kind,
|
|
1258
|
-
payload: params.payload,
|
|
1259
|
-
threadTs: deliveryReplyThreadTs
|
|
1260
|
-
})) {
|
|
1261
|
-
logVerbose("slack: suppressed duplicate normal delivery within the same turn");
|
|
1262
|
-
return deliveryReplyThreadTs;
|
|
1162
|
+
const pushPlanProgress = async (steps, explanation) => {
|
|
1163
|
+
if (streamMode === "status_final") {
|
|
1164
|
+
await progressDraft.pushPlanProgress(steps, { explanation });
|
|
1165
|
+
return;
|
|
1263
1166
|
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
mediaMaxBytes: ctx.mediaMaxBytes,
|
|
1273
|
-
replyThreadTs: deliveryReplyThreadTs,
|
|
1274
|
-
replyToMode: replyDeliveryMode,
|
|
1275
|
-
...slackIdentity ? { identity: slackIdentity } : {},
|
|
1276
|
-
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
|
|
1277
|
-
...messageSentDeliveryHookContext,
|
|
1278
|
-
...prepared.eventScope ? { eventScope: prepared.eventScope } : {}
|
|
1279
|
-
});
|
|
1280
|
-
observedReplyDelivery = true;
|
|
1281
|
-
if (params.kind === "final") observedFinalReplyDelivery = true;
|
|
1282
|
-
const deliveredThreadTs = resolveDeliveredSlackReplyThreadTs({
|
|
1283
|
-
replyToMode: replyDeliveryMode,
|
|
1284
|
-
payloadReplyToId: params.payload.replyToId,
|
|
1285
|
-
replyThreadTs: deliveryReplyThreadTs
|
|
1286
|
-
});
|
|
1287
|
-
rememberDeliveredThreadTs(params.kind, deliveredThreadTs);
|
|
1288
|
-
replyPlan.markSent();
|
|
1289
|
-
deliveryTracker.markDelivered({
|
|
1290
|
-
kind: params.kind,
|
|
1291
|
-
payload: params.payload,
|
|
1292
|
-
threadTs: deliveryReplyThreadTs
|
|
1167
|
+
if (previewToolProgressSuppressed || !draftStream) return;
|
|
1168
|
+
const text = formatChannelProgressDraftText({
|
|
1169
|
+
entry: account.config,
|
|
1170
|
+
lines: legacyPreviewToolProgressLines,
|
|
1171
|
+
seed: progressSeed,
|
|
1172
|
+
formatLine: escapeSlackMrkdwn,
|
|
1173
|
+
narration: explanation,
|
|
1174
|
+
plan: steps
|
|
1293
1175
|
});
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
if (!await deliverPendingStreamFallback(params.session, params.err)) {
|
|
1298
|
-
streamedFailuresOwnedByDispatcher[params.kind] += 1;
|
|
1299
|
-
return false;
|
|
1176
|
+
if (text) {
|
|
1177
|
+
draftStream.update(text);
|
|
1178
|
+
hasStreamedMessage = true;
|
|
1300
1179
|
}
|
|
1301
|
-
replyPlan.markSent();
|
|
1302
|
-
if (params.kind === "final") observedFinalReplyDelivery = true;
|
|
1303
|
-
deliveryTracker.markDelivered({
|
|
1304
|
-
kind: params.kind,
|
|
1305
|
-
payload: params.payload,
|
|
1306
|
-
threadTs: params.session.threadTs,
|
|
1307
|
-
textOverride: params.textOverride
|
|
1308
|
-
});
|
|
1309
|
-
rememberDeliveredThreadTs(params.kind, params.session.threadTs);
|
|
1310
|
-
return true;
|
|
1311
1180
|
};
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1314
|
-
if (
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
nativeProgressCompletionSent = true;
|
|
1324
|
-
observedReplyDelivery ||= session.delivered;
|
|
1325
|
-
} catch (err) {
|
|
1326
|
-
streamFailed = true;
|
|
1327
|
-
runtime.error?.(danger(`slack-stream: native progress completion failed: ${formatSlackError(err)}`));
|
|
1181
|
+
const pushPreviewProgress = async (line, options) => {
|
|
1182
|
+
if (!draftStream && !useNativeProgressStreaming) return;
|
|
1183
|
+
if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
|
|
1184
|
+
const normalized = line?.text.replace(/\s+/g, " ").trim();
|
|
1185
|
+
if (streamMode === "status_final") {
|
|
1186
|
+
if (!line || !normalized) {
|
|
1187
|
+
await progressDraft.noteActivity();
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
await progressDraft.pushToolProgress(line, options);
|
|
1191
|
+
return;
|
|
1328
1192
|
}
|
|
1193
|
+
if (!line || !normalized || !draftStream || !previewToolProgressEnabled || previewToolProgressSuppressed) return;
|
|
1194
|
+
const nextLines = mergeChannelProgressDraftLine(legacyPreviewToolProgressLines, line, { maxLines: resolveChannelProgressDraftMaxLines(account.config) });
|
|
1195
|
+
if (nextLines === legacyPreviewToolProgressLines) return;
|
|
1196
|
+
legacyPreviewToolProgressLines = nextLines;
|
|
1197
|
+
draftStream.update(formatChannelProgressDraftText({
|
|
1198
|
+
entry: account.config,
|
|
1199
|
+
lines: legacyPreviewToolProgressLines,
|
|
1200
|
+
seed: progressSeed,
|
|
1201
|
+
formatLine: escapeSlackMrkdwn
|
|
1202
|
+
}));
|
|
1203
|
+
hasStreamedMessage = true;
|
|
1329
1204
|
};
|
|
1330
|
-
const
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1205
|
+
const updateDraftFromPartial = (text) => {
|
|
1206
|
+
const trimmed = text?.trimEnd();
|
|
1207
|
+
if (!trimmed) return;
|
|
1208
|
+
if (streamMode === "append") {
|
|
1209
|
+
previewToolProgressSuppressed = true;
|
|
1210
|
+
legacyPreviewToolProgressLines = [];
|
|
1211
|
+
const next = applyAppendOnlyStreamUpdate({
|
|
1212
|
+
incoming: trimmed,
|
|
1213
|
+
rendered: appendRenderedText,
|
|
1214
|
+
source: appendSourceText
|
|
1340
1215
|
});
|
|
1216
|
+
appendRenderedText = next.rendered;
|
|
1217
|
+
appendSourceText = next.source;
|
|
1218
|
+
if (!next.changed) return;
|
|
1219
|
+
draftStream?.update(next.rendered);
|
|
1220
|
+
hasStreamedMessage = true;
|
|
1341
1221
|
return;
|
|
1342
1222
|
}
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
await deliverNormally({
|
|
1349
|
-
payload: params.payload,
|
|
1350
|
-
kind: params.kind,
|
|
1351
|
-
forcedThreadTs: streamSession?.threadTs ?? nativeProgressStreamThreadTs
|
|
1352
|
-
});
|
|
1353
|
-
return;
|
|
1354
|
-
}
|
|
1355
|
-
if (!streamSession) {
|
|
1356
|
-
const streamThreadTs = replyPlan.nextThreadTs();
|
|
1357
|
-
plannedThreadTs = streamThreadTs;
|
|
1358
|
-
if (!streamThreadTs) {
|
|
1359
|
-
logVerbose("slack-stream: no reply thread target for stream start, falling back to normal delivery");
|
|
1360
|
-
streamFailed = true;
|
|
1361
|
-
await deliverNormally({
|
|
1362
|
-
payload: params.payload,
|
|
1363
|
-
kind: params.kind
|
|
1364
|
-
});
|
|
1365
|
-
return;
|
|
1366
|
-
}
|
|
1367
|
-
if (deliveryTracker.hasDelivered({
|
|
1368
|
-
kind: params.kind,
|
|
1369
|
-
payload: params.payload,
|
|
1370
|
-
threadTs: streamThreadTs,
|
|
1371
|
-
textOverride: text
|
|
1372
|
-
})) {
|
|
1373
|
-
logVerbose("slack-stream: suppressed duplicate stream start payload");
|
|
1374
|
-
return;
|
|
1375
|
-
}
|
|
1376
|
-
streamSession = await startSlackStream({
|
|
1377
|
-
client: slackClient,
|
|
1378
|
-
channel: message.channel,
|
|
1379
|
-
threadTs: streamThreadTs,
|
|
1380
|
-
text,
|
|
1381
|
-
...slackIdentity ? { identity: slackIdentity } : {},
|
|
1382
|
-
teamId: await resolveSlackStreamRecipientTeamId({
|
|
1383
|
-
client: slackClient,
|
|
1384
|
-
token: ctx.botToken,
|
|
1385
|
-
userId: message.user,
|
|
1386
|
-
fallbackTeamId: slackStreamFallbackTeamId
|
|
1387
|
-
}),
|
|
1388
|
-
userId: message.user
|
|
1389
|
-
});
|
|
1390
|
-
refreshStreamedAcknowledgements(streamSession);
|
|
1391
|
-
if (streamSession.delivered) {
|
|
1392
|
-
observedReplyDelivery = true;
|
|
1393
|
-
if (params.kind === "final") observedFinalReplyDelivery = true;
|
|
1394
|
-
}
|
|
1395
|
-
if (text) rememberStreamedDelivery(params.kind, text, streamSession);
|
|
1396
|
-
rememberDeliveredThreadTs(params.kind, streamThreadTs);
|
|
1397
|
-
replyPlan.markSent();
|
|
1398
|
-
deliveryTracker.markDelivered({
|
|
1399
|
-
kind: params.kind,
|
|
1400
|
-
payload: params.payload,
|
|
1401
|
-
threadTs: streamThreadTs,
|
|
1402
|
-
textOverride: text
|
|
1403
|
-
});
|
|
1404
|
-
return;
|
|
1405
|
-
}
|
|
1406
|
-
if (deliveryTracker.hasDelivered({
|
|
1407
|
-
kind: params.kind,
|
|
1408
|
-
payload: params.payload,
|
|
1409
|
-
threadTs: streamSession.threadTs,
|
|
1410
|
-
textOverride: text
|
|
1411
|
-
})) {
|
|
1412
|
-
logVerbose("slack-stream: suppressed duplicate append payload");
|
|
1413
|
-
return;
|
|
1414
|
-
}
|
|
1415
|
-
if (text) recordStreamedDelivery(params.kind, text);
|
|
1416
|
-
await appendSlackStream({
|
|
1417
|
-
session: streamSession,
|
|
1418
|
-
text: "\n" + text
|
|
1419
|
-
});
|
|
1420
|
-
refreshStreamedAcknowledgements(streamSession);
|
|
1421
|
-
if (streamSession.delivered) {
|
|
1422
|
-
observedReplyDelivery = true;
|
|
1423
|
-
if (params.kind === "final") observedFinalReplyDelivery = true;
|
|
1424
|
-
}
|
|
1425
|
-
deliveryTracker.markDelivered({
|
|
1426
|
-
kind: params.kind,
|
|
1427
|
-
payload: params.payload,
|
|
1428
|
-
threadTs: streamSession.threadTs,
|
|
1429
|
-
textOverride: text
|
|
1430
|
-
});
|
|
1431
|
-
} catch (err) {
|
|
1432
|
-
if (err instanceof SlackStreamNotDeliveredError) {
|
|
1433
|
-
streamFailed = true;
|
|
1434
|
-
if (streamSession) {
|
|
1435
|
-
if (await deliverBufferedStreamFallback({
|
|
1436
|
-
session: streamSession,
|
|
1437
|
-
err,
|
|
1438
|
-
payload: params.payload,
|
|
1439
|
-
kind: params.kind,
|
|
1440
|
-
textOverride: text
|
|
1441
|
-
})) return;
|
|
1442
|
-
throw err;
|
|
1443
|
-
}
|
|
1444
|
-
await deliverNormally({
|
|
1445
|
-
payload: params.payload,
|
|
1446
|
-
kind: params.kind,
|
|
1447
|
-
forcedThreadTs: plannedThreadTs
|
|
1448
|
-
});
|
|
1449
|
-
return;
|
|
1450
|
-
}
|
|
1451
|
-
runtime.error?.(danger(`slack-stream: streaming API call failed: ${formatSlackError(err)}, falling back`));
|
|
1452
|
-
streamFailed = true;
|
|
1453
|
-
if (streamSession && streamSession.pendingText) {
|
|
1454
|
-
const bufferedFallbackErr = new SlackStreamNotDeliveredError(streamSession.pendingText, "unknown");
|
|
1455
|
-
if (await deliverBufferedStreamFallback({
|
|
1456
|
-
session: streamSession,
|
|
1457
|
-
err: bufferedFallbackErr,
|
|
1458
|
-
payload: params.payload,
|
|
1459
|
-
kind: params.kind,
|
|
1460
|
-
textOverride: text
|
|
1461
|
-
})) return;
|
|
1462
|
-
throw err;
|
|
1463
|
-
}
|
|
1464
|
-
await deliverNormally({
|
|
1465
|
-
payload: params.payload,
|
|
1466
|
-
kind: params.kind,
|
|
1467
|
-
forcedThreadTs: streamSession?.threadTs ?? nativeProgressStreamThreadTs ?? plannedThreadTs
|
|
1468
|
-
});
|
|
1469
|
-
}
|
|
1223
|
+
if (streamMode === "status_final") return;
|
|
1224
|
+
previewToolProgressSuppressed = true;
|
|
1225
|
+
legacyPreviewToolProgressLines = [];
|
|
1226
|
+
draftStream?.update(trimmed);
|
|
1227
|
+
hasStreamedMessage = true;
|
|
1470
1228
|
};
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
if (
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
payload,
|
|
1482
|
-
kind: info.kind,
|
|
1483
|
-
forcedThreadTs: finalThreadTs
|
|
1484
|
-
});
|
|
1485
|
-
await appendNativeProgressCompletion(payload.isError === true);
|
|
1486
|
-
progressDraft.markFinalReplyDelivered();
|
|
1487
|
-
if (!payload.isError && hadProgressDraft && streamSession) pendingNativeProgressReceipt = progressReceipt.buildSummaryLine();
|
|
1488
|
-
return;
|
|
1489
|
-
}
|
|
1490
|
-
if (hadProgressDraft) try {
|
|
1491
|
-
await draftStream?.flush();
|
|
1492
|
-
} catch (err) {
|
|
1493
|
-
logVerbose(`slack: progress draft flush before final failed (${formatSlackError(err)})`);
|
|
1494
|
-
}
|
|
1495
|
-
const receiptChannelId = hadProgressDraft ? draftStream?.channelId() : void 0;
|
|
1496
|
-
const receiptMessageId = hadProgressDraft ? draftStream?.messageId() : void 0;
|
|
1497
|
-
const draftThreadTs = hadProgressDraft ? usedReplyThreadTs ?? statusThreadTs : void 0;
|
|
1498
|
-
await deliverNormally({
|
|
1499
|
-
payload,
|
|
1500
|
-
kind: info.kind,
|
|
1501
|
-
...draftThreadTs ? { forcedThreadTs: draftThreadTs } : {}
|
|
1502
|
-
});
|
|
1503
|
-
progressDraft.markFinalReplyDelivered();
|
|
1504
|
-
if (!payload.isError && receiptChannelId && receiptMessageId && !progressReceiptCollapsed) await collapseProgressReceipt({
|
|
1505
|
-
channelId: receiptChannelId,
|
|
1506
|
-
messageId: receiptMessageId,
|
|
1507
|
-
text: progressReceipt.buildSummaryLine(),
|
|
1508
|
-
threadTs: usedReplyThreadTs ?? statusThreadTs
|
|
1229
|
+
const pushReasoningProgress = async (payload) => {
|
|
1230
|
+
if (!payload?.text) return;
|
|
1231
|
+
if (streamMode !== "status_final") {
|
|
1232
|
+
const normalized = progressDraft.mergeReasoningProgress(payload.text, { snapshot: payload.isReasoningSnapshot === true }).replace(/^_(.*)_$/su, "$1").trim();
|
|
1233
|
+
if (!normalized) return;
|
|
1234
|
+
await pushPreviewProgress({
|
|
1235
|
+
id: "reasoning",
|
|
1236
|
+
kind: "item",
|
|
1237
|
+
text: normalized,
|
|
1238
|
+
label: "Reasoning"
|
|
1509
1239
|
});
|
|
1510
1240
|
return;
|
|
1511
1241
|
}
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1242
|
+
progressReceipt.noteReasoning();
|
|
1243
|
+
await progressDraft.pushReasoningProgress(payload.text, { snapshot: payload.isReasoningSnapshot === true });
|
|
1244
|
+
};
|
|
1245
|
+
const resetDraftDeliveryState = () => {
|
|
1246
|
+
hasStreamedMessage = false;
|
|
1247
|
+
appendRenderedText = "";
|
|
1248
|
+
appendSourceText = "";
|
|
1249
|
+
};
|
|
1250
|
+
const resetDraftProgressState = () => {
|
|
1251
|
+
progressDraft.resetReasoningProgress();
|
|
1252
|
+
previewToolProgressSuppressed = false;
|
|
1253
|
+
legacyPreviewToolProgressLines = [];
|
|
1254
|
+
};
|
|
1255
|
+
const beginNewProgressTurn = async (options) => {
|
|
1256
|
+
const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent ? buildNativeProgressCompletionChunks(nativeProgressTerminalStatus) : void 0;
|
|
1257
|
+
if (!progressDraft.beginNewTurn(options)) return false;
|
|
1258
|
+
if (useNativeProgressStreaming) await finishNativeProgressTurn(completionChunks);
|
|
1259
|
+
else draftStream?.forceNewMessage();
|
|
1260
|
+
resetProgressTurnState();
|
|
1261
|
+
nativeTaskState = /* @__PURE__ */ new Map();
|
|
1262
|
+
nativeProgressCompletionSent = false;
|
|
1263
|
+
nativeProgressTerminalStatus = "complete";
|
|
1264
|
+
nativeProgressChunkKey = void 0;
|
|
1265
|
+
resetPreviewDeliveryState();
|
|
1266
|
+
delivery.resetDeliveryTracker();
|
|
1267
|
+
progressReceiptCollapsed = false;
|
|
1268
|
+
return true;
|
|
1269
|
+
};
|
|
1270
|
+
return {
|
|
1271
|
+
draftStream,
|
|
1272
|
+
streamMode,
|
|
1273
|
+
useNativeProgressStreaming,
|
|
1274
|
+
progressDraftActive,
|
|
1275
|
+
previewToolProgressEnabled,
|
|
1276
|
+
suppressDefaultToolProgressMessages,
|
|
1277
|
+
progressDraft,
|
|
1278
|
+
commentaryProgressEnabled,
|
|
1279
|
+
progressReceipt,
|
|
1280
|
+
get progressReceiptCollapsed() {
|
|
1281
|
+
return progressReceiptCollapsed;
|
|
1282
|
+
},
|
|
1283
|
+
get pendingNativeProgressReceipt() {
|
|
1284
|
+
return pendingNativeProgressReceipt;
|
|
1285
|
+
},
|
|
1286
|
+
set pendingNativeProgressReceipt(value) {
|
|
1287
|
+
pendingNativeProgressReceipt = value;
|
|
1288
|
+
},
|
|
1289
|
+
get nativeProgressCompletionSent() {
|
|
1290
|
+
return nativeProgressCompletionSent;
|
|
1291
|
+
},
|
|
1292
|
+
set nativeProgressCompletionSent(value) {
|
|
1293
|
+
nativeProgressCompletionSent = value;
|
|
1294
|
+
},
|
|
1295
|
+
get nativeProgressTerminalStatus() {
|
|
1296
|
+
return nativeProgressTerminalStatus;
|
|
1297
|
+
},
|
|
1298
|
+
appendNativeProgressCompletion,
|
|
1299
|
+
beginNewProgressTurn,
|
|
1300
|
+
buildNativeProgressCompletionChunks,
|
|
1301
|
+
collapseProgressReceipt,
|
|
1302
|
+
onDraftBoundary: !shouldUseDraftStream && !useNativeProgressStreaming ? void 0 : async () => {
|
|
1303
|
+
if (streamMode === "status_final") {
|
|
1304
|
+
await beginNewProgressTurn();
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
if (hasStreamedMessage) draftStream?.forceNewMessage();
|
|
1308
|
+
resetDraftDeliveryState();
|
|
1309
|
+
resetDraftProgressState();
|
|
1310
|
+
},
|
|
1311
|
+
onQueuedFollowupAdmitted: !shouldUseDraftStream && !useNativeProgressStreaming ? void 0 : async () => {
|
|
1312
|
+
await draftStream?.flush();
|
|
1313
|
+
resetPreviewDeliveryState();
|
|
1314
|
+
if (streamMode === "status_final") await beginNewProgressTurn({ force: true });
|
|
1315
|
+
else draftStream?.forceNewMessage();
|
|
1316
|
+
delivery.resetDeliveryTracker();
|
|
1317
|
+
resetDraftDeliveryState();
|
|
1318
|
+
resetDraftProgressState();
|
|
1319
|
+
},
|
|
1320
|
+
pushPlanProgress,
|
|
1321
|
+
pushPreviewProgress,
|
|
1322
|
+
pushReasoningProgress,
|
|
1323
|
+
updateDraftFromPartial,
|
|
1324
|
+
waitForNativeProgressStreamStart,
|
|
1325
|
+
setShouldYieldDraftProgress: (value) => {
|
|
1326
|
+
shouldYieldDraftProgress = value;
|
|
1327
|
+
},
|
|
1328
|
+
shouldYieldDraftProgress: () => shouldYieldDraftProgress()
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
//#endregion
|
|
1332
|
+
//#region extensions/slack/src/threading.ts
|
|
1333
|
+
function resolveSlackThreadContext(params) {
|
|
1334
|
+
const incomingThreadTs = params.message.thread_ts;
|
|
1335
|
+
const eventTs = params.message.event_ts;
|
|
1336
|
+
const messageTs = params.message.ts ?? eventTs;
|
|
1337
|
+
const hasThreadTs = typeof incomingThreadTs === "string" && incomingThreadTs.length > 0;
|
|
1338
|
+
const isThreadReply = hasThreadTs && (incomingThreadTs !== messageTs || Boolean(params.message.parent_user_id));
|
|
1339
|
+
const replyToId = isThreadReply ? incomingThreadTs : void 0;
|
|
1340
|
+
const isAssistantDmThreadRoot = hasThreadTs && !isThreadReply && params.isDirectMessage === true;
|
|
1341
|
+
return {
|
|
1342
|
+
incomingThreadTs,
|
|
1343
|
+
messageTs,
|
|
1344
|
+
isThreadReply,
|
|
1345
|
+
replyToId,
|
|
1346
|
+
messageThreadId: isThreadReply || isAssistantDmThreadRoot ? incomingThreadTs : params.replyToMode === "all" ? messageTs : void 0
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Resolves Slack thread targeting for replies and status indicators.
|
|
1351
|
+
*
|
|
1352
|
+
* @returns replyThreadTs - Thread timestamp for reply messages
|
|
1353
|
+
* @returns statusThreadTs - Thread timestamp for status indicators (typing, etc.)
|
|
1354
|
+
* @returns isThreadReply - true if this is a genuine user reply in a thread,
|
|
1355
|
+
* false if thread_ts comes from a bot status message (e.g. typing indicator)
|
|
1356
|
+
*/
|
|
1357
|
+
function resolveSlackThreadTargets(params) {
|
|
1358
|
+
const { incomingThreadTs, messageTs, isThreadReply } = resolveSlackThreadContext(params);
|
|
1359
|
+
const replyThreadTs = isThreadReply ? incomingThreadTs : params.replyToMode === "all" ? messageTs : void 0;
|
|
1360
|
+
return {
|
|
1361
|
+
replyThreadTs,
|
|
1362
|
+
statusThreadTs: replyThreadTs,
|
|
1363
|
+
isThreadReply
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
//#endregion
|
|
1367
|
+
//#region extensions/slack/src/monitor/message-handler/dispatch-setup.ts
|
|
1368
|
+
async function createSlackDispatchSetup(prepared) {
|
|
1369
|
+
const { ctx, account, message, route } = prepared;
|
|
1370
|
+
const slackClient = prepared.eventScope?.client ?? ctx.app.client;
|
|
1371
|
+
const slackStreamFallbackTeamId = prepared.eventScope?.teamId ?? ctx.teamId;
|
|
1372
|
+
const cfg = ctx.cfg;
|
|
1373
|
+
const runtime = ctx.runtime;
|
|
1374
|
+
const outboundIdentity = resolveAgentOutboundIdentity(cfg, route.agentId);
|
|
1375
|
+
const slackIdentity = outboundIdentity ? {
|
|
1376
|
+
username: outboundIdentity.name,
|
|
1377
|
+
iconUrl: outboundIdentity.avatarUrl,
|
|
1378
|
+
iconEmoji: outboundIdentity.emoji
|
|
1379
|
+
} : prepared.relayIdentity;
|
|
1380
|
+
if (prepared.isDirectMessage) {
|
|
1381
|
+
const sessionCfg = cfg.session;
|
|
1382
|
+
const storePath = resolveStorePath(sessionCfg?.store, { agentId: route.agentId });
|
|
1383
|
+
const pinnedMainDmOwner = resolvePinnedMainDmOwnerFromAllowlist({
|
|
1384
|
+
dmScope: cfg.session?.dmScope,
|
|
1385
|
+
allowFrom: ctx.allowFrom,
|
|
1386
|
+
normalizeEntry: normalizeSlackAllowOwnerEntry
|
|
1387
|
+
});
|
|
1388
|
+
const senderRecipient = normalizeOptionalLowercaseString(message.user);
|
|
1389
|
+
const inboundLastRouteSessionKey = resolveInboundLastRouteSessionKey({
|
|
1390
|
+
route,
|
|
1391
|
+
sessionKey: prepared.ctxPayload.SessionKey ?? route.sessionKey
|
|
1392
|
+
});
|
|
1393
|
+
if (inboundLastRouteSessionKey === route.mainSessionKey && pinnedMainDmOwner && senderRecipient && normalizeOptionalLowercaseString(pinnedMainDmOwner) !== senderRecipient) logVerbose(`slack: skip main-session last route for ${senderRecipient} (pinned owner ${pinnedMainDmOwner})`);
|
|
1394
|
+
else await updateLastRoute({
|
|
1395
|
+
storePath,
|
|
1396
|
+
sessionKey: inboundLastRouteSessionKey,
|
|
1397
|
+
deliveryContext: {
|
|
1398
|
+
channel: "slack",
|
|
1399
|
+
to: `user:${message.user}`,
|
|
1400
|
+
accountId: route.accountId,
|
|
1401
|
+
threadId: prepared.ctxPayload.MessageThreadId ?? prepared.ctxPayload.TransportThreadId
|
|
1402
|
+
},
|
|
1403
|
+
ctx: prepared.ctxPayload
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
const threadTargets = resolveSlackThreadTargets({
|
|
1407
|
+
message,
|
|
1408
|
+
replyToMode: prepared.replyToMode
|
|
1409
|
+
});
|
|
1410
|
+
const forcedReplyThreadTs = prepared.forcedReplyThreadTs;
|
|
1411
|
+
const slackMessageMetadata = prepared.slackMessageMetadata;
|
|
1412
|
+
const statusThreadTs = forcedReplyThreadTs ?? threadTargets.statusThreadTs;
|
|
1413
|
+
const isThreadReply = threadTargets.isThreadReply;
|
|
1414
|
+
const replyDeliveryMode = forcedReplyThreadTs ? "off" : prepared.replyToMode;
|
|
1415
|
+
const sourceReplyDeliveryMode = resolveChannelMessageSourceReplyDeliveryMode({
|
|
1416
|
+
cfg,
|
|
1417
|
+
ctx: prepared.ctxPayload
|
|
1418
|
+
});
|
|
1419
|
+
const sourceRepliesAreToolOnly = sourceReplyDeliveryMode === "message_tool_only";
|
|
1420
|
+
const suppressRoomEventTyping = prepared.ctxPayload.InboundEventKind === "room_event";
|
|
1421
|
+
const messageSentHookTarget = prepared.ctxPayload.OriginatingTo ?? prepared.ctxPayload.To ?? prepared.replyTarget;
|
|
1422
|
+
const messageSentHookContext = {
|
|
1423
|
+
sessionKeyForInternalHooks: prepared.ctxPayload.SessionKey ?? route.sessionKey,
|
|
1424
|
+
isGroup: prepared.isRoomish,
|
|
1425
|
+
groupId: prepared.isRoomish ? message.channel : void 0
|
|
1426
|
+
};
|
|
1427
|
+
const messageSentDeliveryHookContext = {
|
|
1428
|
+
...messageSentHookContext,
|
|
1429
|
+
messageSentHookTarget
|
|
1430
|
+
};
|
|
1431
|
+
const reactionMessageTs = prepared.ackReactionMessageTs;
|
|
1432
|
+
const messageTs = message.ts ?? message.event_ts;
|
|
1433
|
+
const incomingThreadTs = message.thread_ts;
|
|
1434
|
+
let didSetStatus = false;
|
|
1435
|
+
const statusReactionsEnabled = prepared.ctxPayload.InboundEventKind !== "room_event" && Boolean(prepared.ackReactionPromise) && Boolean(reactionMessageTs) && cfg.messages?.statusReactions?.enabled === true;
|
|
1436
|
+
const statusReactions = createStatusReactionController({
|
|
1437
|
+
enabled: statusReactionsEnabled,
|
|
1438
|
+
adapter: {
|
|
1439
|
+
setReaction: async (emoji) => {
|
|
1440
|
+
await reactSlackMessage(message.channel, reactionMessageTs ?? "", emoji, {
|
|
1441
|
+
token: ctx.botToken,
|
|
1442
|
+
client: slackClient
|
|
1443
|
+
}).catch((err) => {
|
|
1444
|
+
if (formatErrorMessage(err).includes("already_reacted")) return;
|
|
1445
|
+
throw err;
|
|
1581
1446
|
});
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1447
|
+
},
|
|
1448
|
+
removeReaction: async (emoji) => {
|
|
1449
|
+
await removeSlackReaction(message.channel, reactionMessageTs ?? "", emoji, {
|
|
1450
|
+
token: ctx.botToken,
|
|
1451
|
+
client: slackClient
|
|
1452
|
+
}).catch((err) => {
|
|
1453
|
+
if (formatErrorMessage(err).includes("no_reaction")) return;
|
|
1454
|
+
throw err;
|
|
1586
1455
|
});
|
|
1587
|
-
return;
|
|
1588
1456
|
}
|
|
1457
|
+
},
|
|
1458
|
+
initialEmoji: prepared.ackReactionValue || "eyes",
|
|
1459
|
+
emojis: void 0,
|
|
1460
|
+
timing: DEFAULT_TIMING,
|
|
1461
|
+
onError: (err) => {
|
|
1462
|
+
logAckFailure({
|
|
1463
|
+
log: logVerbose,
|
|
1464
|
+
channel: "slack",
|
|
1465
|
+
target: `${message.channel}/${message.ts}`,
|
|
1466
|
+
error: err
|
|
1467
|
+
});
|
|
1589
1468
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
payload: supplementalPayload,
|
|
1660
|
-
kind: info.kind,
|
|
1661
|
-
forcedThreadTs: previewThreadTs
|
|
1662
|
-
});
|
|
1663
|
-
markPreviewPayloadDelivered({
|
|
1664
|
-
kind: info.kind,
|
|
1665
|
-
payload,
|
|
1666
|
-
threadTs: supplementalThreadTs
|
|
1667
|
-
});
|
|
1668
|
-
},
|
|
1669
|
-
logPreviewEditFailure: (err) => {
|
|
1670
|
-
logVerbose(`slack: preview final edit failed; falling back to standard send (${formatSlackError(err)})`);
|
|
1671
|
-
}
|
|
1672
|
-
}),
|
|
1673
|
-
deliverNormally: async () => {
|
|
1674
|
-
await deliverNormally({
|
|
1675
|
-
payload: shouldRestoreTtsSupplementTextForPreviewFallback ? {
|
|
1676
|
-
...payload,
|
|
1677
|
-
text: ttsSupplement?.spokenText
|
|
1678
|
-
} : payload,
|
|
1679
|
-
kind: info.kind
|
|
1469
|
+
});
|
|
1470
|
+
if (statusReactionsEnabled) statusReactions.setQueued();
|
|
1471
|
+
const hasRepliedRef = { value: false };
|
|
1472
|
+
const replyPlan = createSlackReplyDeliveryPlan({
|
|
1473
|
+
replyToMode: replyDeliveryMode,
|
|
1474
|
+
incomingThreadTs: forcedReplyThreadTs ?? incomingThreadTs,
|
|
1475
|
+
messageTs,
|
|
1476
|
+
hasRepliedRef,
|
|
1477
|
+
isThreadReply: Boolean(forcedReplyThreadTs) || isThreadReply
|
|
1478
|
+
});
|
|
1479
|
+
const typingTarget = statusThreadTs ? `${message.channel}/${statusThreadTs}` : message.channel;
|
|
1480
|
+
const typingReaction = ctx.typingReaction;
|
|
1481
|
+
const { onModelSelected, ...replyPipeline } = createChannelMessageReplyPipeline({
|
|
1482
|
+
cfg,
|
|
1483
|
+
agentId: route.agentId,
|
|
1484
|
+
channel: "slack",
|
|
1485
|
+
accountId: route.accountId,
|
|
1486
|
+
transformReplyPayload: (payload) => {
|
|
1487
|
+
if (payload.isReasoning === true) return null;
|
|
1488
|
+
return payload;
|
|
1489
|
+
},
|
|
1490
|
+
typing: {
|
|
1491
|
+
start: async () => {
|
|
1492
|
+
didSetStatus = true;
|
|
1493
|
+
await ctx.setSlackThreadStatus({
|
|
1494
|
+
channelId: message.channel,
|
|
1495
|
+
threadTs: statusThreadTs,
|
|
1496
|
+
status: "is typing...",
|
|
1497
|
+
eventScope: prepared.eventScope
|
|
1498
|
+
});
|
|
1499
|
+
if (typingReaction && message.ts) await reactSlackMessage(message.channel, message.ts, typingReaction, {
|
|
1500
|
+
token: ctx.botToken,
|
|
1501
|
+
client: slackClient
|
|
1502
|
+
}).catch((err) => {
|
|
1503
|
+
logVerbose(`slack send: typing reaction failed: ${formatSlackError(err)}`);
|
|
1504
|
+
});
|
|
1505
|
+
},
|
|
1506
|
+
stop: async () => {
|
|
1507
|
+
if (!didSetStatus) return;
|
|
1508
|
+
didSetStatus = false;
|
|
1509
|
+
await ctx.setSlackThreadStatus({
|
|
1510
|
+
channelId: message.channel,
|
|
1511
|
+
threadTs: statusThreadTs,
|
|
1512
|
+
status: "",
|
|
1513
|
+
eventScope: prepared.eventScope
|
|
1514
|
+
});
|
|
1515
|
+
if (typingReaction && message.ts) await removeSlackReaction(message.channel, message.ts, typingReaction, {
|
|
1516
|
+
token: ctx.botToken,
|
|
1517
|
+
client: slackClient
|
|
1518
|
+
}).catch((err) => {
|
|
1519
|
+
logVerbose(`slack send: typing reaction removal failed: ${formatSlackError(err)}`);
|
|
1520
|
+
});
|
|
1521
|
+
},
|
|
1522
|
+
onStartError: (err) => {
|
|
1523
|
+
logTypingFailure({
|
|
1524
|
+
log: (messageValue) => runtime.error?.(danger(messageValue)),
|
|
1525
|
+
channel: "slack",
|
|
1526
|
+
action: "start",
|
|
1527
|
+
target: typingTarget,
|
|
1528
|
+
error: err
|
|
1529
|
+
});
|
|
1530
|
+
},
|
|
1531
|
+
onStopError: (err) => {
|
|
1532
|
+
logTypingFailure({
|
|
1533
|
+
log: (messageLocal) => runtime.error?.(danger(messageLocal)),
|
|
1534
|
+
channel: "slack",
|
|
1535
|
+
action: "stop",
|
|
1536
|
+
target: typingTarget,
|
|
1537
|
+
error: err
|
|
1680
1538
|
});
|
|
1681
1539
|
}
|
|
1682
|
-
}
|
|
1540
|
+
}
|
|
1541
|
+
});
|
|
1542
|
+
const slackStreaming = resolveSlackStreamingConfig({
|
|
1543
|
+
streaming: account.config.streaming,
|
|
1544
|
+
nativeStreaming: resolveChannelStreamingNativeTransport(account.config)
|
|
1545
|
+
});
|
|
1546
|
+
const streamThreadHint = forcedReplyThreadTs ?? resolveSlackStreamingThreadHint({
|
|
1547
|
+
replyToMode: replyDeliveryMode,
|
|
1548
|
+
incomingThreadTs,
|
|
1549
|
+
messageTs,
|
|
1550
|
+
isThreadReply
|
|
1551
|
+
});
|
|
1552
|
+
const previewStreamingEnabled = !sourceRepliesAreToolOnly && shouldEnableSlackPreviewStreaming({ mode: slackStreaming.mode });
|
|
1553
|
+
const hasSlackCustomIdentity = Boolean(slackIdentity?.username || slackIdentity?.iconUrl || slackIdentity?.iconEmoji);
|
|
1554
|
+
const useStreaming = shouldUseStreaming({
|
|
1555
|
+
streamingEnabled: !sourceRepliesAreToolOnly && isSlackStreamingEnabled({
|
|
1556
|
+
mode: slackStreaming.mode,
|
|
1557
|
+
nativeStreaming: slackStreaming.nativeStreaming,
|
|
1558
|
+
nativeProgressTaskCards: resolveSlackNativeProgressTaskCards(account.config)
|
|
1559
|
+
}),
|
|
1560
|
+
threadTs: streamThreadHint
|
|
1561
|
+
});
|
|
1562
|
+
const shouldUseDraftStream = !hasSlackCustomIdentity && shouldInitializeSlackDraftStream({
|
|
1563
|
+
previewStreamingEnabled,
|
|
1564
|
+
useStreaming
|
|
1565
|
+
});
|
|
1566
|
+
const blockStreamingEnabled = resolveChannelStreamingBlockEnabled(account.config);
|
|
1567
|
+
const disableBlockStreaming = sourceRepliesAreToolOnly ? true : resolveSlackDisableBlockStreaming({
|
|
1568
|
+
useStreaming,
|
|
1569
|
+
shouldUseDraftStream,
|
|
1570
|
+
blockStreamingEnabled
|
|
1571
|
+
});
|
|
1572
|
+
const onSlackDeliveryError = (err, info) => {
|
|
1573
|
+
runtime.error?.(danger(`slack ${info.kind} reply failed: ${formatSlackError(err)}`));
|
|
1574
|
+
replyPipeline.typingCallbacks?.onIdle?.();
|
|
1575
|
+
};
|
|
1576
|
+
return {
|
|
1577
|
+
prepared,
|
|
1578
|
+
ctx,
|
|
1579
|
+
account,
|
|
1580
|
+
message,
|
|
1581
|
+
route,
|
|
1582
|
+
slackClient,
|
|
1583
|
+
slackStreamFallbackTeamId,
|
|
1584
|
+
cfg,
|
|
1585
|
+
runtime,
|
|
1586
|
+
slackIdentity,
|
|
1587
|
+
forcedReplyThreadTs,
|
|
1588
|
+
slackMessageMetadata,
|
|
1589
|
+
statusThreadTs,
|
|
1590
|
+
isThreadReply,
|
|
1591
|
+
replyDeliveryMode,
|
|
1592
|
+
sourceReplyDeliveryMode,
|
|
1593
|
+
sourceRepliesAreToolOnly,
|
|
1594
|
+
suppressRoomEventTyping,
|
|
1595
|
+
messageSentHookTarget,
|
|
1596
|
+
messageSentHookContext,
|
|
1597
|
+
messageSentDeliveryHookContext,
|
|
1598
|
+
incomingThreadTs,
|
|
1599
|
+
messageTs,
|
|
1600
|
+
statusReactionsEnabled,
|
|
1601
|
+
statusReactions,
|
|
1602
|
+
hasRepliedRef,
|
|
1603
|
+
replyPlan,
|
|
1604
|
+
onModelSelected,
|
|
1605
|
+
replyPipeline,
|
|
1606
|
+
slackStreaming,
|
|
1607
|
+
streamThreadHint,
|
|
1608
|
+
previewStreamingEnabled,
|
|
1609
|
+
shouldUseDraftStream,
|
|
1610
|
+
disableBlockStreaming,
|
|
1611
|
+
useStreaming,
|
|
1612
|
+
onSlackDeliveryError
|
|
1683
1613
|
};
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1614
|
+
}
|
|
1615
|
+
//#endregion
|
|
1616
|
+
//#region extensions/slack/src/monitor/message-handler/dispatch-streaming.ts
|
|
1617
|
+
function createSlackStreamingDeliveryRuntime(setup) {
|
|
1618
|
+
const { account, ctx, forcedReplyThreadTs, isThreadReply, message, messageSentDeliveryHookContext, messageSentHookContext, messageSentHookTarget, prepared, replyDeliveryMode, replyPlan, runtime, slackClient, slackIdentity, slackMessageMetadata, slackStreamFallbackTeamId } = setup;
|
|
1619
|
+
const state = {
|
|
1620
|
+
streamSession: null,
|
|
1621
|
+
nativeProgressStreamStartPromise: null,
|
|
1622
|
+
nativeProgressStreamThreadTs: void 0,
|
|
1623
|
+
streamFailed: false,
|
|
1624
|
+
usedReplyThreadTs: void 0,
|
|
1625
|
+
usedBlockReplyThreadTs: void 0,
|
|
1626
|
+
observedReplyDelivery: false,
|
|
1627
|
+
observedFinalReplyDelivery: false
|
|
1687
1628
|
};
|
|
1688
|
-
const
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
...prepared.eventScope ? { eventScope: prepared.eventScope } : {},
|
|
1694
|
-
identity: slackIdentity,
|
|
1695
|
-
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
|
|
1696
|
-
maxChars: Math.min(ctx.textLimit, SLACK_TEXT_LIMIT),
|
|
1697
|
-
resolveThreadTs: () => {
|
|
1698
|
-
const ts = replyPlan.peekThreadTs();
|
|
1699
|
-
if (ts) usedReplyThreadTs ??= ts;
|
|
1700
|
-
return ts;
|
|
1701
|
-
},
|
|
1702
|
-
log: logVerbose,
|
|
1703
|
-
warn: logVerbose
|
|
1704
|
-
}) : void 0;
|
|
1705
|
-
let hasStreamedMessage = false;
|
|
1706
|
-
const streamMode = slackStreaming.draftMode;
|
|
1707
|
-
const useNativeProgressStreaming = useStreaming && slackStreaming.mode === "progress";
|
|
1708
|
-
const progressDraftActive = Boolean(draftStream) || useNativeProgressStreaming;
|
|
1709
|
-
const previewToolProgressEnabled = progressDraftActive && resolveChannelStreamingPreviewToolProgress(account.config);
|
|
1710
|
-
let shouldYieldDraftProgress = () => false;
|
|
1711
|
-
const suppressDefaultToolProgressMessages = resolveChannelStreamingSuppressDefaultToolProgressMessages(account.config, {
|
|
1712
|
-
draftStreamActive: Boolean(draftStream) || useNativeProgressStreaming,
|
|
1713
|
-
previewToolProgressEnabled,
|
|
1714
|
-
previewStreamingEnabled
|
|
1715
|
-
});
|
|
1716
|
-
let previewToolProgressSuppressed = false;
|
|
1717
|
-
let legacyPreviewToolProgressLines = [];
|
|
1718
|
-
let nativeTaskState = /* @__PURE__ */ new Map();
|
|
1719
|
-
let appendRenderedText = "";
|
|
1720
|
-
let appendSourceText = "";
|
|
1721
|
-
let nativeProgressCompletionSent = false;
|
|
1722
|
-
let nativeProgressTerminalStatus = "complete";
|
|
1723
|
-
let nativeProgressChunkKey;
|
|
1724
|
-
const progressReceipt = createChannelProgressReceiptTracker();
|
|
1725
|
-
let progressReceiptCollapsed = false;
|
|
1726
|
-
let pendingNativeProgressReceipt;
|
|
1727
|
-
const progressSeed = `${account.accountId}:${message.channel}`;
|
|
1728
|
-
const useRichProgressDraft = streamMode === "status_final" && resolveChannelProgressDraftRender(account.config) === "rich";
|
|
1729
|
-
const explicitProgressTitle = resolveExplicitSlackProgressTitle(account.config);
|
|
1730
|
-
const progressDraftMaxLineChars = resolveChannelProgressDraftMaxLineChars(account.config);
|
|
1731
|
-
const waitForNativeProgressStreamStart = async () => {
|
|
1732
|
-
if (streamSession || !nativeProgressStreamStartPromise) return true;
|
|
1733
|
-
try {
|
|
1734
|
-
await nativeProgressStreamStartPromise;
|
|
1735
|
-
} catch {
|
|
1736
|
-
streamFailed = true;
|
|
1737
|
-
return false;
|
|
1738
|
-
}
|
|
1739
|
-
return !streamFailed;
|
|
1629
|
+
const streamedDeliveries = [];
|
|
1630
|
+
const streamedFailuresOwnedByDispatcher = {
|
|
1631
|
+
tool: 0,
|
|
1632
|
+
block: 0,
|
|
1633
|
+
final: 0
|
|
1740
1634
|
};
|
|
1741
|
-
const
|
|
1742
|
-
if (
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
label: reasoning ? "Reasoning" : "Update",
|
|
1750
|
-
prefix: false
|
|
1635
|
+
const refreshStreamedAcknowledgements = (session) => {
|
|
1636
|
+
if (session.pendingText.length === 0) for (const delivery of streamedDeliveries) delivery.acknowledged = true;
|
|
1637
|
+
};
|
|
1638
|
+
const recordStreamedDelivery = (kind, content) => {
|
|
1639
|
+
const delivery = {
|
|
1640
|
+
kind,
|
|
1641
|
+
content,
|
|
1642
|
+
acknowledged: false
|
|
1751
1643
|
};
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
const resolveNativeProgressLines = (snapshot) => {
|
|
1755
|
-
const lines = resolveStructuredProgressLines(snapshot.lines);
|
|
1756
|
-
if (snapshot.plan?.length || !snapshot.planExplanation) return lines;
|
|
1757
|
-
const explanationLine = buildChannelProgressDraftLine({
|
|
1758
|
-
event: "plan",
|
|
1759
|
-
phase: "update",
|
|
1760
|
-
explanation: snapshot.planExplanation
|
|
1761
|
-
});
|
|
1762
|
-
return explanationLine ? [...lines, explanationLine] : lines;
|
|
1644
|
+
streamedDeliveries.push(delivery);
|
|
1645
|
+
return delivery;
|
|
1763
1646
|
};
|
|
1764
|
-
const
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
usedReplyThreadTs ??= threadTs;
|
|
1781
|
-
rememberDeliveredThreadTs("block", threadTs);
|
|
1647
|
+
const rememberStreamedDelivery = (kind, content, session) => {
|
|
1648
|
+
recordStreamedDelivery(kind, content);
|
|
1649
|
+
refreshStreamedAcknowledgements(session);
|
|
1650
|
+
};
|
|
1651
|
+
const emitAcknowledgedStreamedDeliveries = (messageId) => {
|
|
1652
|
+
for (const delivery of streamedDeliveries) {
|
|
1653
|
+
if (!delivery.acknowledged || delivery.outcome) continue;
|
|
1654
|
+
emitSlackMessageSentHooks({
|
|
1655
|
+
...messageSentHookContext,
|
|
1656
|
+
to: messageSentHookTarget,
|
|
1657
|
+
accountId: account.accountId,
|
|
1658
|
+
content: delivery.content,
|
|
1659
|
+
success: true,
|
|
1660
|
+
...messageId ? { messageId } : {}
|
|
1661
|
+
});
|
|
1662
|
+
delivery.outcome = "success";
|
|
1782
1663
|
}
|
|
1783
1664
|
};
|
|
1784
|
-
const
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1665
|
+
const acknowledgeStoppedStreamedDeliveries = (session, messageId) => {
|
|
1666
|
+
refreshStreamedAcknowledgements(session);
|
|
1667
|
+
for (const delivery of streamedDeliveries) delivery.acknowledged = true;
|
|
1668
|
+
emitAcknowledgedStreamedDeliveries(messageId);
|
|
1669
|
+
};
|
|
1670
|
+
const emitFailedPendingStreamedDeliveries = (error) => {
|
|
1671
|
+
for (const delivery of streamedDeliveries) {
|
|
1672
|
+
if (delivery.acknowledged || delivery.outcome) continue;
|
|
1673
|
+
emitSlackMessageSentHooks({
|
|
1674
|
+
...messageSentHookContext,
|
|
1675
|
+
to: messageSentHookTarget,
|
|
1676
|
+
accountId: account.accountId,
|
|
1677
|
+
content: delivery.content,
|
|
1678
|
+
success: false,
|
|
1679
|
+
error
|
|
1680
|
+
});
|
|
1681
|
+
delivery.outcome = "failure";
|
|
1790
1682
|
}
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
token: ctx.botToken,
|
|
1803
|
-
userId: message.user,
|
|
1804
|
-
fallbackTeamId: slackStreamFallbackTeamId
|
|
1805
|
-
}),
|
|
1806
|
-
userId: message.user
|
|
1683
|
+
};
|
|
1684
|
+
const emitSuccessfulPendingStreamedDeliveries = (messageId) => {
|
|
1685
|
+
for (const delivery of streamedDeliveries) {
|
|
1686
|
+
if (delivery.acknowledged || delivery.outcome) continue;
|
|
1687
|
+
emitSlackMessageSentHooks({
|
|
1688
|
+
...messageSentHookContext,
|
|
1689
|
+
to: messageSentHookTarget,
|
|
1690
|
+
accountId: account.accountId,
|
|
1691
|
+
content: delivery.content,
|
|
1692
|
+
success: true,
|
|
1693
|
+
...messageId ? { messageId } : {}
|
|
1807
1694
|
});
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1695
|
+
delivery.outcome = "success";
|
|
1696
|
+
}
|
|
1697
|
+
};
|
|
1698
|
+
let deliveryTracker = createSlackEventDeliveryTracker();
|
|
1699
|
+
const markPreviewPayloadDelivered = (params) => {
|
|
1700
|
+
deliveryTracker.markDelivered(params);
|
|
1701
|
+
const nextThreadTs = replyPlan.peekThreadTs();
|
|
1702
|
+
if (nextThreadTs !== params.threadTs) deliveryTracker.markDelivered({
|
|
1703
|
+
...params,
|
|
1704
|
+
threadTs: nextThreadTs
|
|
1705
|
+
});
|
|
1706
|
+
};
|
|
1707
|
+
const resolveDeliveryThreadTs = (params) => {
|
|
1708
|
+
const plannedThreadTs = params.forcedThreadTs ? void 0 : replyPlan.nextThreadTs();
|
|
1709
|
+
return params.forcedThreadTs ?? plannedThreadTs ?? (params.kind === "block" ? state.usedBlockReplyThreadTs : void 0);
|
|
1710
|
+
};
|
|
1711
|
+
const rememberDeliveredThreadTs = (kind, deliveredThreadTs) => {
|
|
1712
|
+
if (!deliveredThreadTs) return;
|
|
1713
|
+
state.usedReplyThreadTs ??= deliveredThreadTs;
|
|
1714
|
+
if (kind === "block") state.usedBlockReplyThreadTs = deliveredThreadTs;
|
|
1715
|
+
};
|
|
1716
|
+
const deliverPendingStreamFallback = async (session, err) => {
|
|
1717
|
+
let fallbackError = err;
|
|
1718
|
+
if (!session.stopped) try {
|
|
1719
|
+
const stopResult = await stopSlackStream({
|
|
1720
|
+
session,
|
|
1721
|
+
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
|
|
1722
|
+
});
|
|
1723
|
+
acknowledgeStoppedStreamedDeliveries(session, stopResult.messageId);
|
|
1724
|
+
state.observedReplyDelivery = true;
|
|
1725
|
+
state.usedReplyThreadTs ??= session.threadTs;
|
|
1726
|
+
return true;
|
|
1727
|
+
} catch (stopErr) {
|
|
1728
|
+
if (stopErr instanceof SlackStreamNotDeliveredError) fallbackError = stopErr;
|
|
1729
|
+
else runtime.error?.(danger(`slack-stream: failed to finalize buffered text before fallback: ${formatSlackError(stopErr)}`));
|
|
1730
|
+
}
|
|
1731
|
+
emitAcknowledgedStreamedDeliveries();
|
|
1732
|
+
const fallbackText = fallbackError.pendingText.trim();
|
|
1733
|
+
if (!fallbackText) return false;
|
|
1813
1734
|
try {
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1735
|
+
await deliverReplies({
|
|
1736
|
+
cfg: ctx.cfg,
|
|
1737
|
+
replies: [{ text: fallbackText }],
|
|
1738
|
+
target: prepared.replyTarget,
|
|
1739
|
+
token: ctx.botToken,
|
|
1740
|
+
accountId: account.accountId,
|
|
1741
|
+
runtime,
|
|
1742
|
+
textLimit: ctx.textLimit,
|
|
1743
|
+
mediaMaxBytes: ctx.mediaMaxBytes,
|
|
1744
|
+
replyThreadTs: session.threadTs,
|
|
1745
|
+
replyToMode: replyDeliveryMode,
|
|
1746
|
+
...slackIdentity ? { identity: slackIdentity } : {},
|
|
1747
|
+
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
|
|
1748
|
+
...messageSentDeliveryHookContext,
|
|
1749
|
+
deferMessageSentHooks: true,
|
|
1750
|
+
...prepared.eventScope ? { eventScope: prepared.eventScope } : {}
|
|
1751
|
+
});
|
|
1752
|
+
markSlackStreamFallbackDelivered(session);
|
|
1753
|
+
if (!session.stopped) try {
|
|
1754
|
+
await stopSlackStream({
|
|
1755
|
+
session,
|
|
1756
|
+
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
|
|
1757
|
+
});
|
|
1758
|
+
} catch (finalizeErr) {
|
|
1759
|
+
runtime.error?.(danger(`slack-stream: failed to finalize native stream after fallback delivery: ${formatSlackError(finalizeErr)}`));
|
|
1760
|
+
}
|
|
1761
|
+
emitSuccessfulPendingStreamedDeliveries();
|
|
1762
|
+
state.observedReplyDelivery = true;
|
|
1763
|
+
state.usedReplyThreadTs ??= session.threadTs;
|
|
1764
|
+
logVerbose(`slack-stream: streamed delivery failed (${fallbackError.slackCode}); delivered ${fallbackText.length} chars via deliverReplies fallback`);
|
|
1765
|
+
return true;
|
|
1766
|
+
} catch (postErr) {
|
|
1767
|
+
emitFailedPendingStreamedDeliveries(formatErrorMessage(postErr));
|
|
1768
|
+
runtime.error?.(danger(`slack-stream: fallback deliverReplies failed after ${fallbackError.slackCode}: ${formatErrorMessage(postErr)}`));
|
|
1769
|
+
return false;
|
|
1817
1770
|
}
|
|
1818
|
-
|
|
1819
|
-
|
|
1771
|
+
};
|
|
1772
|
+
const deliverNormally = async (params) => {
|
|
1773
|
+
if (params.payload.isReasoning === true) return;
|
|
1774
|
+
const replyThreadTs = resolveDeliveryThreadTs(params);
|
|
1775
|
+
const deliveryReplyThreadTs = replyDeliveryMode === "off" && !forcedReplyThreadTs && !isThreadReply ? void 0 : replyThreadTs;
|
|
1776
|
+
if (deliveryTracker.hasDelivered({
|
|
1777
|
+
kind: params.kind,
|
|
1778
|
+
payload: params.payload,
|
|
1779
|
+
threadTs: deliveryReplyThreadTs
|
|
1780
|
+
})) {
|
|
1781
|
+
logVerbose("slack: suppressed duplicate normal delivery within the same turn");
|
|
1782
|
+
return deliveryReplyThreadTs;
|
|
1783
|
+
}
|
|
1784
|
+
await deliverReplies({
|
|
1785
|
+
cfg: ctx.cfg,
|
|
1786
|
+
replies: [params.payload],
|
|
1787
|
+
target: prepared.replyTarget,
|
|
1788
|
+
token: ctx.botToken,
|
|
1789
|
+
accountId: account.accountId,
|
|
1790
|
+
runtime,
|
|
1791
|
+
textLimit: ctx.textLimit,
|
|
1792
|
+
mediaMaxBytes: ctx.mediaMaxBytes,
|
|
1793
|
+
replyThreadTs: deliveryReplyThreadTs,
|
|
1794
|
+
replyToMode: replyDeliveryMode,
|
|
1795
|
+
...slackIdentity ? { identity: slackIdentity } : {},
|
|
1796
|
+
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {},
|
|
1797
|
+
...messageSentDeliveryHookContext,
|
|
1798
|
+
...prepared.eventScope ? { eventScope: prepared.eventScope } : {}
|
|
1799
|
+
});
|
|
1800
|
+
state.observedReplyDelivery = true;
|
|
1801
|
+
if (params.kind === "final") state.observedFinalReplyDelivery = true;
|
|
1802
|
+
const deliveredThreadTs = resolveDeliveredSlackReplyThreadTs({
|
|
1803
|
+
replyToMode: replyDeliveryMode,
|
|
1804
|
+
payloadReplyToId: params.payload.replyToId,
|
|
1805
|
+
replyThreadTs: deliveryReplyThreadTs
|
|
1806
|
+
});
|
|
1807
|
+
rememberDeliveredThreadTs(params.kind, deliveredThreadTs);
|
|
1820
1808
|
replyPlan.markSent();
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
session: streamSession,
|
|
1826
|
-
chunks
|
|
1809
|
+
deliveryTracker.markDelivered({
|
|
1810
|
+
kind: params.kind,
|
|
1811
|
+
payload: params.payload,
|
|
1812
|
+
threadTs: deliveryReplyThreadTs
|
|
1827
1813
|
});
|
|
1828
|
-
|
|
1829
|
-
nativeProgressChunkKey = chunkKey;
|
|
1814
|
+
return deliveryReplyThreadTs;
|
|
1830
1815
|
};
|
|
1831
|
-
const
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
if (!useNativeProgressStreaming || streamFailed || progressLines.length === 0 && !snapshot.plan?.length && !snapshot.statusHeadline && !explicitProgressTitle && !hasRetirableNativeTasks) return;
|
|
1836
|
-
if (!await waitForNativeProgressStreamStart()) return;
|
|
1837
|
-
const reconciled = reconcileSlackNativeTaskChunks({
|
|
1838
|
-
previousTasks: nativeTaskState,
|
|
1839
|
-
chunks: buildNativeProgressChunks(snapshot)
|
|
1840
|
-
});
|
|
1841
|
-
const chunks = reconciled.chunks;
|
|
1842
|
-
if (!chunks?.length) return;
|
|
1843
|
-
const chunkKey = JSON.stringify(chunks);
|
|
1844
|
-
if (chunkKey === nativeProgressChunkKey) return;
|
|
1845
|
-
try {
|
|
1846
|
-
if (!streamSession) await startNativeProgressStream(chunks, chunkKey);
|
|
1847
|
-
else await appendNativeProgressStream(chunks, chunkKey);
|
|
1848
|
-
nativeTaskState = reconciled.tasks;
|
|
1849
|
-
} catch (err) {
|
|
1850
|
-
runtime.error?.(danger(`slack-stream: native progress stream failed: ${formatSlackError(err)}, falling back`));
|
|
1851
|
-
streamFailed = true;
|
|
1816
|
+
const deliverBufferedStreamFallback = async (params) => {
|
|
1817
|
+
if (!await deliverPendingStreamFallback(params.session, params.err)) {
|
|
1818
|
+
streamedFailuresOwnedByDispatcher[params.kind] += 1;
|
|
1819
|
+
return false;
|
|
1852
1820
|
}
|
|
1821
|
+
replyPlan.markSent();
|
|
1822
|
+
if (params.kind === "final") state.observedFinalReplyDelivery = true;
|
|
1823
|
+
deliveryTracker.markDelivered({
|
|
1824
|
+
kind: params.kind,
|
|
1825
|
+
payload: params.payload,
|
|
1826
|
+
threadTs: params.session.threadTs,
|
|
1827
|
+
textOverride: params.textOverride
|
|
1828
|
+
});
|
|
1829
|
+
rememberDeliveredThreadTs(params.kind, params.session.threadTs);
|
|
1830
|
+
return true;
|
|
1853
1831
|
};
|
|
1854
|
-
const
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
token,
|
|
1865
|
-
accountId: account.accountId,
|
|
1866
|
-
...params
|
|
1832
|
+
const deliverWithStreaming = async (params) => {
|
|
1833
|
+
if (params.payload.isReasoning === true) return;
|
|
1834
|
+
const reply = resolveSendableOutboundReplyParts(params.payload);
|
|
1835
|
+
const renderPlan = resolveSlackReplyRenderPlan(params.payload);
|
|
1836
|
+
const plannedBlocks = renderPlan.mode === "single" ? renderPlan.blocks : renderPlan.blockPart?.blocks;
|
|
1837
|
+
if (state.streamFailed || reply.hasMedia || renderPlan.mode === "split" || Boolean(plannedBlocks?.length) || readSlackReplyBlocks(params.payload)?.length || !reply.hasText) {
|
|
1838
|
+
await deliverNormally({
|
|
1839
|
+
payload: params.payload,
|
|
1840
|
+
kind: params.kind,
|
|
1841
|
+
forcedThreadTs: state.streamSession?.threadTs ?? state.nativeProgressStreamThreadTs
|
|
1867
1842
|
});
|
|
1868
|
-
|
|
1869
|
-
} catch (err) {
|
|
1870
|
-
logVerbose(`slack: progress receipt edit failed (${formatSlackError(err)})`);
|
|
1843
|
+
return;
|
|
1871
1844
|
}
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
commentaryItalics: false,
|
|
1883
|
-
updateOnLineChange: useNativeProgressStreaming || useRichProgressDraft,
|
|
1884
|
-
update: async (previewText, options) => {
|
|
1885
|
-
if (useNativeProgressStreaming) {
|
|
1886
|
-
await updateNativeProgressStream();
|
|
1845
|
+
const text = reply.trimmedText;
|
|
1846
|
+
let plannedThreadTs;
|
|
1847
|
+
try {
|
|
1848
|
+
if (!state.streamSession && state.nativeProgressStreamStartPromise) await state.nativeProgressStreamStartPromise;
|
|
1849
|
+
if (state.streamFailed) {
|
|
1850
|
+
await deliverNormally({
|
|
1851
|
+
payload: params.payload,
|
|
1852
|
+
kind: params.kind,
|
|
1853
|
+
forcedThreadTs: state.streamSession?.threadTs ?? state.nativeProgressStreamThreadTs
|
|
1854
|
+
});
|
|
1887
1855
|
return;
|
|
1888
1856
|
}
|
|
1889
|
-
if (!
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1857
|
+
if (!state.streamSession) {
|
|
1858
|
+
const streamThreadTs = replyPlan.nextThreadTs();
|
|
1859
|
+
plannedThreadTs = streamThreadTs;
|
|
1860
|
+
if (!streamThreadTs) {
|
|
1861
|
+
logVerbose("slack-stream: no reply thread target for stream start, falling back to normal delivery");
|
|
1862
|
+
state.streamFailed = true;
|
|
1863
|
+
await deliverNormally({
|
|
1864
|
+
payload: params.payload,
|
|
1865
|
+
kind: params.kind
|
|
1866
|
+
});
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
if (deliveryTracker.hasDelivered({
|
|
1870
|
+
kind: params.kind,
|
|
1871
|
+
payload: params.payload,
|
|
1872
|
+
threadTs: streamThreadTs,
|
|
1873
|
+
textOverride: text
|
|
1874
|
+
})) {
|
|
1875
|
+
logVerbose("slack-stream: suppressed duplicate stream start payload");
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
state.streamSession = await startSlackStream({
|
|
1879
|
+
client: slackClient,
|
|
1880
|
+
channel: message.channel,
|
|
1881
|
+
threadTs: streamThreadTs,
|
|
1882
|
+
text,
|
|
1883
|
+
...slackIdentity ? { identity: slackIdentity } : {},
|
|
1884
|
+
teamId: await resolveSlackStreamRecipientTeamId({
|
|
1885
|
+
client: slackClient,
|
|
1886
|
+
token: ctx.botToken,
|
|
1887
|
+
userId: message.user,
|
|
1888
|
+
fallbackTeamId: slackStreamFallbackTeamId
|
|
1889
|
+
}),
|
|
1890
|
+
userId: message.user
|
|
1891
|
+
});
|
|
1892
|
+
refreshStreamedAcknowledgements(state.streamSession);
|
|
1893
|
+
if (state.streamSession.delivered) {
|
|
1894
|
+
state.observedReplyDelivery = true;
|
|
1895
|
+
if (params.kind === "final") state.observedFinalReplyDelivery = true;
|
|
1896
|
+
}
|
|
1897
|
+
if (text) rememberStreamedDelivery(params.kind, text, state.streamSession);
|
|
1898
|
+
rememberDeliveredThreadTs(params.kind, streamThreadTs);
|
|
1899
|
+
replyPlan.markSent();
|
|
1900
|
+
deliveryTracker.markDelivered({
|
|
1901
|
+
kind: params.kind,
|
|
1902
|
+
payload: params.payload,
|
|
1903
|
+
threadTs: streamThreadTs,
|
|
1904
|
+
textOverride: text
|
|
1905
|
+
});
|
|
1906
|
+
return;
|
|
1907
|
+
}
|
|
1908
|
+
if (deliveryTracker.hasDelivered({
|
|
1909
|
+
kind: params.kind,
|
|
1910
|
+
payload: params.payload,
|
|
1911
|
+
threadTs: state.streamSession.threadTs,
|
|
1912
|
+
textOverride: text
|
|
1913
|
+
})) {
|
|
1914
|
+
logVerbose("slack-stream: suppressed duplicate append payload");
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1917
|
+
if (text) recordStreamedDelivery(params.kind, text);
|
|
1918
|
+
await appendSlackStream({
|
|
1919
|
+
session: state.streamSession,
|
|
1920
|
+
text: "\n" + text
|
|
1934
1921
|
});
|
|
1935
|
-
|
|
1936
|
-
if (
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1922
|
+
refreshStreamedAcknowledgements(state.streamSession);
|
|
1923
|
+
if (state.streamSession.delivered) {
|
|
1924
|
+
state.observedReplyDelivery = true;
|
|
1925
|
+
if (params.kind === "final") state.observedFinalReplyDelivery = true;
|
|
1926
|
+
}
|
|
1927
|
+
deliveryTracker.markDelivered({
|
|
1928
|
+
kind: params.kind,
|
|
1929
|
+
payload: params.payload,
|
|
1930
|
+
threadTs: state.streamSession.threadTs,
|
|
1931
|
+
textOverride: text
|
|
1941
1932
|
});
|
|
1942
1933
|
} catch (err) {
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1934
|
+
if (err instanceof SlackStreamNotDeliveredError) {
|
|
1935
|
+
state.streamFailed = true;
|
|
1936
|
+
if (state.streamSession) {
|
|
1937
|
+
if (await deliverBufferedStreamFallback({
|
|
1938
|
+
session: state.streamSession,
|
|
1939
|
+
err,
|
|
1940
|
+
payload: params.payload,
|
|
1941
|
+
kind: params.kind,
|
|
1942
|
+
textOverride: text
|
|
1943
|
+
})) return;
|
|
1944
|
+
throw err;
|
|
1945
|
+
}
|
|
1946
|
+
await deliverNormally({
|
|
1947
|
+
payload: params.payload,
|
|
1948
|
+
kind: params.kind,
|
|
1949
|
+
forcedThreadTs: plannedThreadTs
|
|
1950
|
+
});
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
runtime.error?.(danger(`slack-stream: streaming API call failed: ${formatSlackError(err)}, falling back`));
|
|
1954
|
+
state.streamFailed = true;
|
|
1955
|
+
if (state.streamSession && state.streamSession.pendingText) {
|
|
1956
|
+
const bufferedFallbackErr = new SlackStreamNotDeliveredError(state.streamSession.pendingText, "unknown");
|
|
1957
|
+
if (await deliverBufferedStreamFallback({
|
|
1958
|
+
session: state.streamSession,
|
|
1959
|
+
err: bufferedFallbackErr,
|
|
1960
|
+
payload: params.payload,
|
|
1961
|
+
kind: params.kind,
|
|
1962
|
+
textOverride: text
|
|
1963
|
+
})) return;
|
|
1964
|
+
throw err;
|
|
1965
|
+
}
|
|
1966
|
+
await deliverNormally({
|
|
1967
|
+
payload: params.payload,
|
|
1968
|
+
kind: params.kind,
|
|
1969
|
+
forcedThreadTs: state.streamSession?.threadTs ?? state.nativeProgressStreamThreadTs ?? plannedThreadTs
|
|
1970
|
+
});
|
|
1947
1971
|
}
|
|
1948
|
-
streamSession = null;
|
|
1949
|
-
nativeProgressStreamStartPromise = null;
|
|
1950
|
-
nativeProgressStreamThreadTs = void 0;
|
|
1951
|
-
streamFailed = false;
|
|
1952
1972
|
};
|
|
1953
|
-
const
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
if (text) {
|
|
1968
|
-
draftStream.update(text);
|
|
1969
|
-
hasStreamedMessage = true;
|
|
1973
|
+
const reconcileCounts = (counts) => {
|
|
1974
|
+
const next = { ...counts };
|
|
1975
|
+
let changed = false;
|
|
1976
|
+
for (const kind of [
|
|
1977
|
+
"tool",
|
|
1978
|
+
"block",
|
|
1979
|
+
"final"
|
|
1980
|
+
]) {
|
|
1981
|
+
const failedStreamedCount = streamedDeliveries.filter((delivery) => delivery.kind === kind && delivery.outcome === "failure").length;
|
|
1982
|
+
const additionalFailedStreamed = Math.max(0, failedStreamedCount - streamedFailuresOwnedByDispatcher[kind]);
|
|
1983
|
+
if (additionalFailedStreamed > 0) {
|
|
1984
|
+
next[kind] = Math.max(0, (next[kind] ?? 0) - additionalFailedStreamed);
|
|
1985
|
+
changed = true;
|
|
1986
|
+
}
|
|
1970
1987
|
}
|
|
1988
|
+
return changed ? next : counts;
|
|
1971
1989
|
};
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1990
|
+
return Object.assign(state, {
|
|
1991
|
+
acknowledgeStoppedStreamedDeliveries,
|
|
1992
|
+
deliverNormally,
|
|
1993
|
+
deliverPendingStreamFallback,
|
|
1994
|
+
deliverWithStreaming,
|
|
1995
|
+
emitAcknowledgedStreamedDeliveries,
|
|
1996
|
+
emitFailedPendingStreamedDeliveries,
|
|
1997
|
+
hasDelivered: (params) => deliveryTracker.hasDelivered(params),
|
|
1998
|
+
markPreviewPayloadDelivered,
|
|
1999
|
+
rememberDeliveredThreadTs,
|
|
2000
|
+
reconcileCounts,
|
|
2001
|
+
resetDeliveryTracker: () => {
|
|
2002
|
+
deliveryTracker = createSlackEventDeliveryTracker();
|
|
2003
|
+
}
|
|
2004
|
+
});
|
|
2005
|
+
}
|
|
2006
|
+
//#endregion
|
|
2007
|
+
//#region extensions/slack/src/monitor/message-handler/dispatch.ts
|
|
2008
|
+
async function dispatchPreparedSlackMessage(prepared) {
|
|
2009
|
+
const setup = await createSlackDispatchSetup(prepared);
|
|
2010
|
+
const { account, cfg, ctx, disableBlockStreaming, hasRepliedRef, message, messageSentHookContext, messageSentHookTarget, onModelSelected, onSlackDeliveryError, previewStreamingEnabled, replyPipeline, replyPlan, route, runtime, slackClient, slackMessageMetadata, slackStreaming, sourceReplyDeliveryMode, statusReactions, statusReactionsEnabled, statusThreadTs, suppressRoomEventTyping, useStreaming } = setup;
|
|
2011
|
+
const delivery = createSlackStreamingDeliveryRuntime(setup);
|
|
2012
|
+
const draftPreviewCommitted = { value: false };
|
|
2013
|
+
const progress = createSlackProgressRuntime({
|
|
2014
|
+
setup,
|
|
2015
|
+
delivery,
|
|
2016
|
+
resetPreviewDeliveryState: () => {
|
|
2017
|
+
draftPreviewCommitted.value = false;
|
|
2018
|
+
delivery.observedFinalReplyDelivery = false;
|
|
2019
|
+
}
|
|
2020
|
+
});
|
|
2021
|
+
const draftStream = progress.draftStream;
|
|
2022
|
+
const deliverSlackPayload = async (payload, info) => {
|
|
2023
|
+
if (payload.isReasoning === true) return { visibleReplySent: false };
|
|
2024
|
+
if (info.kind === "final" && slackStreaming.mode === "progress" && progress.streamMode === "status_final") {
|
|
2025
|
+
const hadProgressDraft = progress.progressDraft.hasStarted;
|
|
2026
|
+
progress.progressDraft.markFinalReplyStarted();
|
|
2027
|
+
if (progress.useNativeProgressStreaming) {
|
|
2028
|
+
await progress.waitForNativeProgressStreamStart();
|
|
2029
|
+
const finalThreadTs = delivery.streamSession?.threadTs ?? delivery.nativeProgressStreamThreadTs;
|
|
2030
|
+
await delivery.deliverNormally({
|
|
2031
|
+
payload,
|
|
2032
|
+
kind: info.kind,
|
|
2033
|
+
forcedThreadTs: finalThreadTs
|
|
2034
|
+
});
|
|
2035
|
+
await progress.appendNativeProgressCompletion(payload.isError === true);
|
|
2036
|
+
progress.progressDraft.markFinalReplyDelivered();
|
|
2037
|
+
if (!payload.isError && hadProgressDraft && delivery.streamSession) progress.pendingNativeProgressReceipt = progress.progressReceipt.buildSummaryLine();
|
|
1979
2038
|
return;
|
|
1980
2039
|
}
|
|
1981
|
-
|
|
2040
|
+
if (hadProgressDraft) try {
|
|
2041
|
+
await draftStream?.flush();
|
|
2042
|
+
} catch (err) {
|
|
2043
|
+
logVerbose(`slack: progress draft flush before final failed (${formatSlackError(err)})`);
|
|
2044
|
+
}
|
|
2045
|
+
const receiptChannelId = hadProgressDraft ? draftStream?.channelId() : void 0;
|
|
2046
|
+
const receiptMessageId = hadProgressDraft ? draftStream?.messageId() : void 0;
|
|
2047
|
+
const draftThreadTs = hadProgressDraft ? delivery.usedReplyThreadTs ?? statusThreadTs : void 0;
|
|
2048
|
+
await delivery.deliverNormally({
|
|
2049
|
+
payload,
|
|
2050
|
+
kind: info.kind,
|
|
2051
|
+
...draftThreadTs ? { forcedThreadTs: draftThreadTs } : {}
|
|
2052
|
+
});
|
|
2053
|
+
progress.progressDraft.markFinalReplyDelivered();
|
|
2054
|
+
if (!payload.isError && receiptChannelId && receiptMessageId && !progress.progressReceiptCollapsed) await progress.collapseProgressReceipt({
|
|
2055
|
+
channelId: receiptChannelId,
|
|
2056
|
+
messageId: receiptMessageId,
|
|
2057
|
+
text: progress.progressReceipt.buildSummaryLine(),
|
|
2058
|
+
threadTs: delivery.usedReplyThreadTs ?? statusThreadTs
|
|
2059
|
+
});
|
|
1982
2060
|
return;
|
|
1983
2061
|
}
|
|
1984
|
-
if (
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
entry: account.config,
|
|
1990
|
-
lines: legacyPreviewToolProgressLines,
|
|
1991
|
-
seed: progressSeed,
|
|
1992
|
-
formatLine: escapeSlackMrkdwn
|
|
1993
|
-
}));
|
|
1994
|
-
hasStreamedMessage = true;
|
|
1995
|
-
};
|
|
1996
|
-
const updateDraftFromPartial = (text) => {
|
|
1997
|
-
const trimmed = text?.trimEnd();
|
|
1998
|
-
if (!trimmed) return;
|
|
1999
|
-
if (streamMode === "append") {
|
|
2000
|
-
previewToolProgressSuppressed = true;
|
|
2001
|
-
legacyPreviewToolProgressLines = [];
|
|
2002
|
-
const next = applyAppendOnlyStreamUpdate({
|
|
2003
|
-
incoming: trimmed,
|
|
2004
|
-
rendered: appendRenderedText,
|
|
2005
|
-
source: appendSourceText
|
|
2062
|
+
if (progress.useNativeProgressStreaming) {
|
|
2063
|
+
await delivery.deliverNormally({
|
|
2064
|
+
payload,
|
|
2065
|
+
kind: info.kind,
|
|
2066
|
+
forcedThreadTs: delivery.streamSession?.threadTs ?? delivery.nativeProgressStreamThreadTs
|
|
2006
2067
|
});
|
|
2007
|
-
appendRenderedText = next.rendered;
|
|
2008
|
-
appendSourceText = next.source;
|
|
2009
|
-
if (!next.changed) return;
|
|
2010
|
-
draftStream?.update(next.rendered);
|
|
2011
|
-
hasStreamedMessage = true;
|
|
2012
2068
|
return;
|
|
2013
2069
|
}
|
|
2014
|
-
if (
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
hasStreamedMessage = true;
|
|
2019
|
-
};
|
|
2020
|
-
const pushReasoningProgress = async (payload) => {
|
|
2021
|
-
if (!payload?.text) return;
|
|
2022
|
-
if (streamMode !== "status_final") {
|
|
2023
|
-
const normalized = progressDraft.mergeReasoningProgress(payload.text, { snapshot: payload.isReasoningSnapshot === true }).replace(/^_(.*)_$/su, "$1").trim();
|
|
2024
|
-
if (!normalized) return;
|
|
2025
|
-
await pushPreviewProgress({
|
|
2026
|
-
id: "reasoning",
|
|
2027
|
-
kind: "item",
|
|
2028
|
-
text: normalized,
|
|
2029
|
-
label: "Reasoning"
|
|
2070
|
+
if (useStreaming) {
|
|
2071
|
+
await delivery.deliverWithStreaming({
|
|
2072
|
+
payload,
|
|
2073
|
+
kind: info.kind
|
|
2030
2074
|
});
|
|
2031
2075
|
return;
|
|
2032
2076
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2077
|
+
const reply = resolveSendableOutboundReplyParts(payload);
|
|
2078
|
+
const ttsSupplement = getReplyPayloadTtsSupplement(payload);
|
|
2079
|
+
const replySourceText = payload.text ?? ttsSupplement?.spokenText;
|
|
2080
|
+
const replyRenderPlan = resolveSlackReplyRenderPlan(payload, replySourceText);
|
|
2081
|
+
const slackBlocks = replyRenderPlan.mode === "single" ? replyRenderPlan.blocks : replyRenderPlan.blockPart?.blocks;
|
|
2082
|
+
const requiresSeparateFallbackDelivery = replyRenderPlan.mode === "split";
|
|
2083
|
+
const trimmedFinalText = replyRenderPlan.mode === "single" ? replyRenderPlan.text.trim() : replyRenderPlan.fallbackText.trim();
|
|
2084
|
+
const previewFinalText = replyRenderPlan.mode === "single" && replyRenderPlan.textIsSlackMrkdwn ? trimmedFinalText : normalizeSlackOutboundText((replySourceText ?? "").trim());
|
|
2085
|
+
const shouldRestoreTtsSupplementTextForPreviewFallback = Boolean(ttsSupplement) && ttsSupplement?.visibleTextAlreadyDelivered !== true && Boolean(draftStream) && !draftPreviewCommitted.value && !delivery.observedFinalReplyDelivery && previewStreamingEnabled && !payload.text?.trim();
|
|
2086
|
+
if (info.kind === "final" && ttsSupplement && draftStream && !draftPreviewCommitted.value && !delivery.observedFinalReplyDelivery && previewStreamingEnabled && !payload.isError && !requiresSeparateFallbackDelivery && trimmedFinalText.length > 0) {
|
|
2087
|
+
const channelId = draftStream.channelId();
|
|
2088
|
+
const messageId = draftStream.messageId();
|
|
2089
|
+
if (channelId && messageId) {
|
|
2090
|
+
const finalThreadTs = delivery.usedReplyThreadTs ?? statusThreadTs;
|
|
2091
|
+
await draftStream.flush();
|
|
2092
|
+
await draftStream.seal();
|
|
2093
|
+
try {
|
|
2094
|
+
await finalizeSlackPreviewEdit({
|
|
2095
|
+
client: slackClient,
|
|
2096
|
+
token: ctx.botToken,
|
|
2097
|
+
accountId: account.accountId,
|
|
2098
|
+
channelId,
|
|
2099
|
+
messageId,
|
|
2100
|
+
text: previewFinalText,
|
|
2101
|
+
...slackBlocks?.length ? { blocks: slackBlocks } : {},
|
|
2102
|
+
threadTs: finalThreadTs
|
|
2103
|
+
});
|
|
2104
|
+
} catch (err) {
|
|
2105
|
+
logVerbose(`slack: preview final edit failed; falling back to standard send (${formatSlackError(err)})`);
|
|
2106
|
+
await draftStream.discardPending();
|
|
2107
|
+
let delivered = false;
|
|
2108
|
+
try {
|
|
2109
|
+
await delivery.deliverNormally({
|
|
2110
|
+
payload: payload.text?.trim() ? payload : {
|
|
2111
|
+
...payload,
|
|
2112
|
+
text: ttsSupplement.spokenText
|
|
2113
|
+
},
|
|
2114
|
+
kind: info.kind,
|
|
2115
|
+
forcedThreadTs: finalThreadTs
|
|
2116
|
+
});
|
|
2117
|
+
delivered = true;
|
|
2118
|
+
} finally {
|
|
2119
|
+
if (delivered) await draftStream.clear();
|
|
2120
|
+
}
|
|
2121
|
+
return;
|
|
2122
|
+
}
|
|
2123
|
+
draftPreviewCommitted.value = true;
|
|
2124
|
+
delivery.observedFinalReplyDelivery = true;
|
|
2125
|
+
delivery.observedReplyDelivery = true;
|
|
2126
|
+
replyPlan.markSent();
|
|
2127
|
+
await delivery.deliverNormally({
|
|
2128
|
+
payload: buildTtsSupplementMediaPayload(payload),
|
|
2129
|
+
kind: info.kind,
|
|
2130
|
+
forcedThreadTs: finalThreadTs
|
|
2131
|
+
});
|
|
2132
|
+
delivery.markPreviewPayloadDelivered({
|
|
2133
|
+
kind: info.kind,
|
|
2134
|
+
payload,
|
|
2135
|
+
threadTs: finalThreadTs
|
|
2136
|
+
});
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2066
2139
|
}
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2140
|
+
await deliverWithFinalizableLivePreviewAdapter({
|
|
2141
|
+
kind: info.kind,
|
|
2142
|
+
payload,
|
|
2143
|
+
adapter: defineFinalizableLivePreviewAdapter({
|
|
2144
|
+
draft: draftStream && !draftPreviewCommitted.value && !delivery.observedFinalReplyDelivery ? {
|
|
2145
|
+
flush: draftStream.flush,
|
|
2146
|
+
clear: draftStream.clear,
|
|
2147
|
+
discardPending: draftStream.discardPending,
|
|
2148
|
+
seal: draftStream.seal,
|
|
2149
|
+
id: () => {
|
|
2150
|
+
const channelId = draftStream.channelId();
|
|
2151
|
+
const messageId = draftStream.messageId();
|
|
2152
|
+
return channelId && messageId ? {
|
|
2153
|
+
channelId,
|
|
2154
|
+
messageId
|
|
2155
|
+
} : void 0;
|
|
2156
|
+
}
|
|
2157
|
+
} : void 0,
|
|
2158
|
+
buildFinalEdit: () => {
|
|
2159
|
+
if (!previewStreamingEnabled || reply.hasMedia && !ttsSupplement || payload.isError || requiresSeparateFallbackDelivery || trimmedFinalText.length === 0 && !slackBlocks?.length) return;
|
|
2160
|
+
return {
|
|
2161
|
+
text: previewFinalText,
|
|
2162
|
+
blocks: slackBlocks,
|
|
2163
|
+
threadTs: delivery.usedReplyThreadTs ?? statusThreadTs
|
|
2164
|
+
};
|
|
2165
|
+
},
|
|
2166
|
+
editFinal: async (preview, edit) => {
|
|
2167
|
+
if (delivery.hasDelivered({
|
|
2168
|
+
kind: info.kind,
|
|
2169
|
+
payload,
|
|
2170
|
+
threadTs: edit.threadTs
|
|
2171
|
+
})) return;
|
|
2172
|
+
await finalizeSlackPreviewEdit({
|
|
2173
|
+
client: slackClient,
|
|
2174
|
+
token: ctx.botToken,
|
|
2175
|
+
accountId: account.accountId,
|
|
2176
|
+
channelId: preview.channelId,
|
|
2177
|
+
messageId: preview.messageId,
|
|
2178
|
+
text: edit.text,
|
|
2179
|
+
...edit.blocks?.length ? { blocks: edit.blocks } : {},
|
|
2180
|
+
threadTs: edit.threadTs
|
|
2181
|
+
});
|
|
2182
|
+
if (!ttsSupplement) emitSlackMessageSentHooks({
|
|
2183
|
+
...messageSentHookContext,
|
|
2184
|
+
to: messageSentHookTarget,
|
|
2185
|
+
accountId: account.accountId,
|
|
2186
|
+
content: trimmedFinalText,
|
|
2187
|
+
success: true,
|
|
2188
|
+
messageId: preview.messageId
|
|
2189
|
+
});
|
|
2190
|
+
draftPreviewCommitted.value = true;
|
|
2191
|
+
delivery.observedFinalReplyDelivery = true;
|
|
2192
|
+
},
|
|
2193
|
+
onPreviewFinalized: (_preview) => {
|
|
2194
|
+
draftPreviewCommitted.value = true;
|
|
2195
|
+
delivery.observedFinalReplyDelivery = true;
|
|
2196
|
+
const finalThreadTs = delivery.usedReplyThreadTs ?? statusThreadTs;
|
|
2197
|
+
delivery.observedReplyDelivery = true;
|
|
2198
|
+
replyPlan.markSent();
|
|
2199
|
+
if (!ttsSupplement) delivery.markPreviewPayloadDelivered({
|
|
2200
|
+
kind: info.kind,
|
|
2201
|
+
payload,
|
|
2202
|
+
threadTs: finalThreadTs
|
|
2203
|
+
});
|
|
2204
|
+
},
|
|
2205
|
+
buildSupplementalPayload: () => ttsSupplement ? buildTtsSupplementMediaPayload(payload) : void 0,
|
|
2206
|
+
deliverSupplemental: async (supplementalPayload) => {
|
|
2207
|
+
const previewThreadTs = delivery.usedReplyThreadTs ?? statusThreadTs;
|
|
2208
|
+
const supplementalThreadTs = await delivery.deliverNormally({
|
|
2209
|
+
payload: supplementalPayload,
|
|
2210
|
+
kind: info.kind,
|
|
2211
|
+
forcedThreadTs: previewThreadTs
|
|
2212
|
+
});
|
|
2213
|
+
delivery.markPreviewPayloadDelivered({
|
|
2214
|
+
kind: info.kind,
|
|
2215
|
+
payload,
|
|
2216
|
+
threadTs: supplementalThreadTs
|
|
2217
|
+
});
|
|
2218
|
+
},
|
|
2219
|
+
logPreviewEditFailure: (err) => {
|
|
2220
|
+
logVerbose(`slack: preview final edit failed; falling back to standard send (${formatSlackError(err)})`);
|
|
2221
|
+
}
|
|
2222
|
+
}),
|
|
2223
|
+
deliverNormally: async () => {
|
|
2224
|
+
await delivery.deliverNormally({
|
|
2225
|
+
payload: shouldRestoreTtsSupplementTextForPreviewFallback ? {
|
|
2226
|
+
...payload,
|
|
2227
|
+
text: ttsSupplement?.spokenText
|
|
2228
|
+
} : payload,
|
|
2229
|
+
kind: info.kind
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
});
|
|
2080
2233
|
};
|
|
2081
2234
|
let dispatchError;
|
|
2082
2235
|
let queuedFinal = false;
|
|
@@ -2110,33 +2263,33 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
2110
2263
|
hasRepliedRef,
|
|
2111
2264
|
disableBlockStreaming,
|
|
2112
2265
|
onModelSelected,
|
|
2113
|
-
suppressDefaultToolProgressMessages: suppressDefaultToolProgressMessages ? true : void 0,
|
|
2114
|
-
commentaryProgressEnabled: commentaryProgressEnabled ? true : void 0,
|
|
2115
|
-
progressPreambleEnabled: progressDraftActive && slackStreaming.mode === "progress" ? true : void 0,
|
|
2116
|
-
commentaryPayloadsEnabled: commentaryProgressEnabled ? true : void 0,
|
|
2117
|
-
onVerboseProgressVisibility: commentaryProgressEnabled ? (isActive) => {
|
|
2118
|
-
|
|
2266
|
+
suppressDefaultToolProgressMessages: progress.suppressDefaultToolProgressMessages ? true : void 0,
|
|
2267
|
+
commentaryProgressEnabled: progress.commentaryProgressEnabled ? true : void 0,
|
|
2268
|
+
progressPreambleEnabled: progress.progressDraftActive && slackStreaming.mode === "progress" ? true : void 0,
|
|
2269
|
+
commentaryPayloadsEnabled: progress.commentaryProgressEnabled ? true : void 0,
|
|
2270
|
+
onVerboseProgressVisibility: progress.commentaryProgressEnabled ? (isActive) => {
|
|
2271
|
+
progress.setShouldYieldDraftProgress(isActive);
|
|
2119
2272
|
} : void 0,
|
|
2120
2273
|
allowProgressCallbacksWhenSourceDeliverySuppressed: sourceReplyDeliveryMode === "message_tool_only" && statusReactionsEnabled ? true : void 0,
|
|
2121
2274
|
allowToolLifecycleWhenProgressHidden: statusReactionsEnabled ? true : void 0,
|
|
2122
2275
|
onPartialReply: useStreaming ? void 0 : !previewStreamingEnabled ? void 0 : async (payload) => {
|
|
2123
|
-
updateDraftFromPartial(payload.text);
|
|
2276
|
+
progress.updateDraftFromPartial(payload.text);
|
|
2124
2277
|
},
|
|
2125
|
-
onAssistantMessageStart: onDraftBoundary,
|
|
2278
|
+
onAssistantMessageStart: progress.onDraftBoundary,
|
|
2126
2279
|
onReasoningEnd: async () => {
|
|
2127
|
-
progressReceipt.closeReasoning();
|
|
2128
|
-
await onDraftBoundary?.();
|
|
2280
|
+
progress.progressReceipt.closeReasoning();
|
|
2281
|
+
await progress.onDraftBoundary?.();
|
|
2129
2282
|
},
|
|
2130
|
-
onQueuedFollowupAdmitted,
|
|
2131
|
-
onReasoningStream: statusReactionsEnabled || previewToolProgressEnabled ? async (payload) => {
|
|
2132
|
-
await pushReasoningProgress(payload);
|
|
2283
|
+
onQueuedFollowupAdmitted: progress.onQueuedFollowupAdmitted,
|
|
2284
|
+
onReasoningStream: statusReactionsEnabled || progress.previewToolProgressEnabled ? async (payload) => {
|
|
2285
|
+
await progress.pushReasoningProgress(payload);
|
|
2133
2286
|
if (!statusReactionsEnabled) return;
|
|
2134
2287
|
await statusReactions.setThinking();
|
|
2135
2288
|
} : void 0,
|
|
2136
2289
|
onToolStart: async (payload) => {
|
|
2137
2290
|
if (statusReactionsEnabled) await statusReactions.setTool(payload.name);
|
|
2138
|
-
if (payload.phase === "start") progressReceipt.noteToolCall(payload.name);
|
|
2139
|
-
await pushPreviewProgress(buildChannelProgressDraftLineForEntry(account.config, {
|
|
2291
|
+
if (payload.phase === "start") progress.progressReceipt.noteToolCall(payload.name);
|
|
2292
|
+
await progress.pushPreviewProgress(buildChannelProgressDraftLineForEntry(account.config, {
|
|
2140
2293
|
event: "tool",
|
|
2141
2294
|
itemId: payload.itemId,
|
|
2142
2295
|
toolCallId: payload.toolCallId,
|
|
@@ -2146,15 +2299,15 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
2146
2299
|
}, payload.detailMode ? { detailMode: payload.detailMode } : void 0), { toolName: payload.name });
|
|
2147
2300
|
},
|
|
2148
2301
|
onItemEvent: async (payload) => {
|
|
2149
|
-
if (streamMode === "status_final" && payload.kind === "preamble") {
|
|
2150
|
-
if (shouldYieldDraftProgress()) return;
|
|
2151
|
-
await progressDraft.pushPreambleHeadline(payload.progressText, { itemId: payload.itemId });
|
|
2152
|
-
if (commentaryProgressEnabled) {
|
|
2153
|
-
if (await progressDraft.pushCommentaryProgress(payload.progressText, { itemId: payload.itemId })) progressReceipt.noteCommentary(payload.itemId, payload.progressText);
|
|
2302
|
+
if (progress.streamMode === "status_final" && payload.kind === "preamble") {
|
|
2303
|
+
if (progress.shouldYieldDraftProgress()) return;
|
|
2304
|
+
await progress.progressDraft.pushPreambleHeadline(payload.progressText, { itemId: payload.itemId });
|
|
2305
|
+
if (progress.commentaryProgressEnabled) {
|
|
2306
|
+
if (await progress.progressDraft.pushCommentaryProgress(payload.progressText, { itemId: payload.itemId })) progress.progressReceipt.noteCommentary(payload.itemId, payload.progressText);
|
|
2154
2307
|
}
|
|
2155
2308
|
return;
|
|
2156
2309
|
}
|
|
2157
|
-
await pushPreviewProgress(buildChannelProgressDraftLineForEntry(account.config, {
|
|
2310
|
+
await progress.pushPreviewProgress(buildChannelProgressDraftLineForEntry(account.config, {
|
|
2158
2311
|
event: "item",
|
|
2159
2312
|
itemId: payload.itemId,
|
|
2160
2313
|
toolCallId: payload.toolCallId,
|
|
@@ -2170,11 +2323,11 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
2170
2323
|
},
|
|
2171
2324
|
onPlanUpdate: async (payload) => {
|
|
2172
2325
|
if (payload.phase !== "update") return;
|
|
2173
|
-
await pushPlanProgress(payload.steps, payload.explanation);
|
|
2326
|
+
await progress.pushPlanProgress(payload.steps, payload.explanation);
|
|
2174
2327
|
},
|
|
2175
2328
|
onApprovalEvent: async (payload) => {
|
|
2176
2329
|
if (payload.phase !== "requested") return;
|
|
2177
|
-
await pushPreviewProgress(buildChannelProgressDraftLine({
|
|
2330
|
+
await progress.pushPreviewProgress(buildChannelProgressDraftLine({
|
|
2178
2331
|
event: "approval",
|
|
2179
2332
|
phase: payload.phase,
|
|
2180
2333
|
title: payload.title,
|
|
@@ -2185,7 +2338,7 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
2185
2338
|
},
|
|
2186
2339
|
onCommandOutput: async (payload) => {
|
|
2187
2340
|
if (payload.phase !== "end") return;
|
|
2188
|
-
await pushPreviewProgress(buildChannelProgressDraftLine({
|
|
2341
|
+
await progress.pushPreviewProgress(buildChannelProgressDraftLine({
|
|
2189
2342
|
event: "command-output",
|
|
2190
2343
|
itemId: payload.itemId,
|
|
2191
2344
|
toolCallId: payload.toolCallId,
|
|
@@ -2198,7 +2351,7 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
2198
2351
|
},
|
|
2199
2352
|
onPatchSummary: async (payload) => {
|
|
2200
2353
|
if (payload.phase !== "end") return;
|
|
2201
|
-
await pushPreviewProgress(buildChannelProgressDraftLine({
|
|
2354
|
+
await progress.pushPreviewProgress(buildChannelProgressDraftLine({
|
|
2202
2355
|
event: "patch",
|
|
2203
2356
|
itemId: payload.itemId,
|
|
2204
2357
|
toolCallId: payload.toolCallId,
|
|
@@ -2221,79 +2374,59 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
2221
2374
|
} catch (err) {
|
|
2222
2375
|
dispatchError = err;
|
|
2223
2376
|
} finally {
|
|
2224
|
-
progressDraft.cancel();
|
|
2377
|
+
progress.progressDraft.cancel();
|
|
2225
2378
|
await draftStream?.discardPending();
|
|
2226
2379
|
}
|
|
2227
2380
|
let streamFallbackDelivered = false;
|
|
2228
|
-
const finalStream = streamSession;
|
|
2381
|
+
const finalStream = delivery.streamSession;
|
|
2229
2382
|
if (finalStream && !finalStream.stopped) try {
|
|
2230
|
-
const completionChunks = useNativeProgressStreaming && !nativeProgressCompletionSent ? buildNativeProgressCompletionChunks(dispatchError ? "error" : nativeProgressTerminalStatus) : void 0;
|
|
2231
|
-
if (completionChunks?.length) nativeProgressCompletionSent = true;
|
|
2383
|
+
const completionChunks = progress.useNativeProgressStreaming && !progress.nativeProgressCompletionSent ? progress.buildNativeProgressCompletionChunks(dispatchError ? "error" : progress.nativeProgressTerminalStatus) : void 0;
|
|
2384
|
+
if (completionChunks?.length) progress.nativeProgressCompletionSent = true;
|
|
2232
2385
|
const stopResult = await stopSlackStream({
|
|
2233
2386
|
session: finalStream,
|
|
2234
2387
|
...completionChunks?.length ? { chunks: completionChunks } : {},
|
|
2235
2388
|
...slackMessageMetadata ? { metadata: slackMessageMetadata } : {}
|
|
2236
2389
|
});
|
|
2237
|
-
acknowledgeStoppedStreamedDeliveries(finalStream, stopResult?.messageId);
|
|
2238
|
-
if (pendingNativeProgressReceipt && stopResult?.messageId && !progressReceiptCollapsed) await collapseProgressReceipt({
|
|
2390
|
+
delivery.acknowledgeStoppedStreamedDeliveries(finalStream, stopResult?.messageId);
|
|
2391
|
+
if (progress.pendingNativeProgressReceipt && stopResult?.messageId && !progress.progressReceiptCollapsed) await progress.collapseProgressReceipt({
|
|
2239
2392
|
channelId: finalStream.channel,
|
|
2240
2393
|
messageId: stopResult.messageId,
|
|
2241
|
-
text: pendingNativeProgressReceipt,
|
|
2394
|
+
text: progress.pendingNativeProgressReceipt,
|
|
2242
2395
|
threadTs: finalStream.threadTs
|
|
2243
2396
|
});
|
|
2244
2397
|
} catch (err) {
|
|
2245
2398
|
if (err instanceof SlackStreamNotDeliveredError) {
|
|
2246
|
-
streamFallbackDelivered = await deliverPendingStreamFallback(finalStream, err);
|
|
2399
|
+
streamFallbackDelivered = await delivery.deliverPendingStreamFallback(finalStream, err);
|
|
2247
2400
|
if (!streamFallbackDelivered) dispatchError ??= err;
|
|
2248
2401
|
} else {
|
|
2249
2402
|
const error = formatSlackError(err);
|
|
2250
|
-
emitAcknowledgedStreamedDeliveries();
|
|
2251
|
-
emitFailedPendingStreamedDeliveries(error);
|
|
2403
|
+
delivery.emitAcknowledgedStreamedDeliveries();
|
|
2404
|
+
delivery.emitFailedPendingStreamedDeliveries(error);
|
|
2252
2405
|
runtime.error?.(danger(`slack-stream: failed to stop stream: ${error}`));
|
|
2253
2406
|
if (!finalStream.delivered) dispatchError ??= err;
|
|
2254
2407
|
}
|
|
2255
2408
|
}
|
|
2256
|
-
|
|
2257
|
-
"tool",
|
|
2258
|
-
"block",
|
|
2259
|
-
"final"
|
|
2260
|
-
]) {
|
|
2261
|
-
const failedStreamedCount = streamedDeliveries.filter((delivery) => delivery.kind === kind && delivery.outcome === "failure").length;
|
|
2262
|
-
const additionalFailedStreamed = Math.max(0, failedStreamedCount - streamedFailuresOwnedByDispatcher[kind]);
|
|
2263
|
-
if (additionalFailedStreamed > 0) counts = {
|
|
2264
|
-
...counts,
|
|
2265
|
-
[kind]: Math.max(0, (counts[kind] ?? 0) - additionalFailedStreamed)
|
|
2266
|
-
};
|
|
2267
|
-
}
|
|
2409
|
+
counts = delivery.reconcileCounts(counts);
|
|
2268
2410
|
queuedFinal = queuedFinal && (counts.final ?? 0) > 0;
|
|
2269
2411
|
const anyReplyDelivered = hasVisibleInboundReplyDispatch({
|
|
2270
2412
|
queuedFinal,
|
|
2271
2413
|
counts
|
|
2272
2414
|
}, {
|
|
2273
|
-
observedReplyDelivery,
|
|
2415
|
+
observedReplyDelivery: delivery.observedReplyDelivery,
|
|
2274
2416
|
fallbackDelivered: streamFallbackDelivered
|
|
2275
2417
|
});
|
|
2276
|
-
if (statusReactionsEnabled) if (dispatchError)
|
|
2277
|
-
|
|
2278
|
-
if (ctx.removeAckAfterReply) (async () => {
|
|
2279
|
-
await sleep(statusReactionTiming.errorHoldMs);
|
|
2280
|
-
if (anyReplyDelivered) await statusReactions.clear();
|
|
2281
|
-
})();
|
|
2282
|
-
} else if (anyReplyDelivered) {
|
|
2418
|
+
if (statusReactionsEnabled) if (dispatchError) await statusReactions.setError();
|
|
2419
|
+
else if (anyReplyDelivered) {
|
|
2283
2420
|
await statusReactions.setDone();
|
|
2284
|
-
|
|
2285
|
-
await sleep(statusReactionTiming.doneHoldMs);
|
|
2286
|
-
await statusReactions.clear();
|
|
2287
|
-
})();
|
|
2288
|
-
else statusReactions.restoreInitial();
|
|
2421
|
+
statusReactions.restoreInitial();
|
|
2289
2422
|
} else await statusReactions.restoreInitial();
|
|
2290
|
-
const participationThreadTs = usedReplyThreadTs ?? statusThreadTs;
|
|
2423
|
+
const participationThreadTs = delivery.usedReplyThreadTs ?? statusThreadTs;
|
|
2291
2424
|
if (anyReplyDelivered && participationThreadTs) recordSlackThreadParticipation(account.accountId, message.channel, participationThreadTs, {
|
|
2292
2425
|
agentId: route.agentId,
|
|
2293
2426
|
...prepared.eventScope ? { teamId: prepared.eventScope.teamId } : {}
|
|
2294
2427
|
});
|
|
2295
|
-
if (dispatchError) throw
|
|
2296
|
-
if (!anyReplyDelivered && !draftPreviewCommitted) {
|
|
2428
|
+
if (dispatchError) throw toErrorObject(dispatchError, "Slack dispatch failed");
|
|
2429
|
+
if (!anyReplyDelivered && !draftPreviewCommitted.value) {
|
|
2297
2430
|
await draftStream?.clear();
|
|
2298
2431
|
return;
|
|
2299
2432
|
}
|
|
@@ -2301,30 +2434,51 @@ async function dispatchPreparedSlackMessage(prepared) {
|
|
|
2301
2434
|
const finalCount = counts.final;
|
|
2302
2435
|
logVerbose(`slack: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${prepared.replyTarget}`);
|
|
2303
2436
|
}
|
|
2304
|
-
if (!statusReactionsEnabled) removeAckReactionAfterReply({
|
|
2305
|
-
removeAfterReply: ctx.removeAckAfterReply && anyReplyDelivered,
|
|
2306
|
-
ackReactionPromise: prepared.ackReactionPromise,
|
|
2307
|
-
ackReactionValue: prepared.ackReactionValue,
|
|
2308
|
-
remove: () => removeSlackReaction(message.channel, prepared.ackReactionMessageTs ?? "", prepared.ackReactionValue, {
|
|
2309
|
-
token: ctx.botToken,
|
|
2310
|
-
client: slackClient
|
|
2311
|
-
}),
|
|
2312
|
-
onError: (err) => {
|
|
2313
|
-
logAckFailure({
|
|
2314
|
-
log: logVerbose,
|
|
2315
|
-
channel: "slack",
|
|
2316
|
-
target: `${message.channel}/${message.ts}`,
|
|
2317
|
-
error: err
|
|
2318
|
-
});
|
|
2319
|
-
}
|
|
2320
|
-
});
|
|
2321
2437
|
}
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2438
|
+
//#endregion
|
|
2439
|
+
//#region extensions/slack/src/agent-context.ts
|
|
2440
|
+
function asRecord(value) {
|
|
2441
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2442
|
+
}
|
|
2443
|
+
function trimmedString(value) {
|
|
2444
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
2445
|
+
}
|
|
2446
|
+
function normalizeEntity(value) {
|
|
2447
|
+
const entity = asRecord(value);
|
|
2448
|
+
const type = trimmedString(entity?.type);
|
|
2449
|
+
if (!entity || !type) return;
|
|
2450
|
+
const teamId = trimmedString(entity.team_id);
|
|
2451
|
+
if (type === "slack#/types/channel_id" || type === "slack#/types/canvas_id" || type === "slack#/types/list_id") {
|
|
2452
|
+
const entityValue = trimmedString(entity.value);
|
|
2453
|
+
return entityValue ? {
|
|
2454
|
+
type,
|
|
2455
|
+
value: entityValue,
|
|
2456
|
+
...teamId ? { team_id: teamId } : {}
|
|
2457
|
+
} : void 0;
|
|
2458
|
+
}
|
|
2459
|
+
if (type !== "slack#/types/message_context") return;
|
|
2460
|
+
const message = asRecord(entity.value);
|
|
2461
|
+
const channelId = trimmedString(message?.channel_id);
|
|
2462
|
+
const messageTs = trimmedString(message?.message_ts);
|
|
2463
|
+
return channelId && messageTs ? {
|
|
2464
|
+
type,
|
|
2465
|
+
value: {
|
|
2466
|
+
channel_id: channelId,
|
|
2467
|
+
message_ts: messageTs
|
|
2468
|
+
},
|
|
2469
|
+
...teamId ? { team_id: teamId } : {}
|
|
2470
|
+
} : void 0;
|
|
2471
|
+
}
|
|
2472
|
+
function isSlackAppContext(value) {
|
|
2473
|
+
return Boolean(asRecord(value));
|
|
2474
|
+
}
|
|
2475
|
+
function normalizeSlackAppContextEntities(value) {
|
|
2476
|
+
const context = asRecord(value);
|
|
2477
|
+
if (!Array.isArray(context?.entities)) return [];
|
|
2478
|
+
return context.entities.flatMap((entity) => {
|
|
2479
|
+
const normalized = normalizeEntity(entity);
|
|
2480
|
+
return normalized ? [normalized] : [];
|
|
2481
|
+
});
|
|
2328
2482
|
}
|
|
2329
2483
|
//#endregion
|
|
2330
2484
|
//#region extensions/slack/src/monitor/message-handler/preflight-audio.ts
|
|
@@ -2366,8 +2520,7 @@ async function resolveSlackPreflightAudioTranscript(params) {
|
|
|
2366
2520
|
const { transcribeFirstAudio } = await loadSlackPreflightAudioRuntime();
|
|
2367
2521
|
const transcript = await transcribeFirstAudio({
|
|
2368
2522
|
ctx: {
|
|
2369
|
-
|
|
2370
|
-
MediaTypes: params.media.map((entry) => entry.contentType ?? ""),
|
|
2523
|
+
media: [...params.media],
|
|
2371
2524
|
Provider: "slack",
|
|
2372
2525
|
Surface: "slack",
|
|
2373
2526
|
OriginatingChannel: "slack",
|
|
@@ -2420,7 +2573,7 @@ async function discardSlackPreflightMedia(media) {
|
|
|
2420
2573
|
const SLACK_MENTION_RESOLUTION_CONCURRENCY = 4;
|
|
2421
2574
|
const SLACK_MENTION_RESOLUTION_MAX_LOOKUPS_PER_MESSAGE = 20;
|
|
2422
2575
|
const SLACK_USER_MENTION_RE$1 = /<@([A-Z0-9]+)(?:\|[^>]+)?>/gi;
|
|
2423
|
-
const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-
|
|
2576
|
+
const loadSlackMediaModule$1 = createLazyRuntimeModule(() => import("./actions-BkOaiDSX.js").then((n) => n.g));
|
|
2424
2577
|
function collectUniqueSlackMentionIds$1(texts) {
|
|
2425
2578
|
const seen = /* @__PURE__ */ new Set();
|
|
2426
2579
|
const mentionIds = [];
|
|
@@ -2699,7 +2852,7 @@ function resolveSlackInitialAgentRoute(params) {
|
|
|
2699
2852
|
};
|
|
2700
2853
|
}
|
|
2701
2854
|
function resolveSlackRoutingContext(params) {
|
|
2702
|
-
const { ctx, account, message, isDirectMessage, isGroupDm, isRoom, isRoomish, channelConfig, seedTopLevelRoomThread, assistantThreadTs, eventScope } = params;
|
|
2855
|
+
const { ctx, account, message, isDirectMessage, isGroupDm, isRoom, isRoomish, channelConfig, seedTopLevelRoomThread, assistantThreadTs, agentViewThreadTs, eventScope } = params;
|
|
2703
2856
|
let route = resolveSlackInitialAgentRoute({
|
|
2704
2857
|
ctx,
|
|
2705
2858
|
account,
|
|
@@ -2719,7 +2872,7 @@ function resolveSlackRoutingContext(params) {
|
|
|
2719
2872
|
const isThreadReply = threadContext.isThreadReply;
|
|
2720
2873
|
const autoThreadId = !isThreadReply && replyToMode === "all" && threadContext.messageTs ? threadContext.messageTs : void 0;
|
|
2721
2874
|
const seedCandidateThreadId = threadContext.incomingThreadTs ?? threadContext.messageTs;
|
|
2722
|
-
const routedThreadId = (isDirectMessage ? assistantThreadTs : isRoomish ? isThreadReply && threadTs ? threadTs : void 0 : isThreadReply ? threadTs : autoThreadId) ?? (isRoomish ? !isThreadReply && isRoom && seedTopLevelRoomThread && replyToMode !== "off" && seedCandidateThreadId ? seedCandidateThreadId : void 0 : void 0);
|
|
2875
|
+
const routedThreadId = (isDirectMessage ? assistantThreadTs ?? agentViewThreadTs : isRoomish ? isThreadReply && threadTs ? threadTs : void 0 : isThreadReply ? threadTs : autoThreadId) ?? (isRoomish ? !isThreadReply && isRoom && seedTopLevelRoomThread && replyToMode !== "off" && seedCandidateThreadId ? seedCandidateThreadId : void 0 : void 0);
|
|
2723
2876
|
const baseConversationId = resolveSlackBaseConversationId({
|
|
2724
2877
|
message,
|
|
2725
2878
|
isDirectMessage,
|
|
@@ -2842,10 +2995,11 @@ function formatSlackThreadLabelSnippet(text) {
|
|
|
2842
2995
|
}
|
|
2843
2996
|
//#endregion
|
|
2844
2997
|
//#region extensions/slack/src/monitor/message-handler/prepare-thread-context.ts
|
|
2845
|
-
const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-
|
|
2998
|
+
const loadSlackMediaModule = createLazyRuntimeModule(() => import("./actions-BkOaiDSX.js").then((n) => n.g));
|
|
2846
2999
|
const SLACK_THREAD_CONTEXT_USER_LOOKUP_CONCURRENCY = 4;
|
|
2847
3000
|
function resolveSlackThreadSessionFreshness(params) {
|
|
2848
3001
|
return params.ctx.channelRuntime?.session?.resolveEntryResetFreshness?.({
|
|
3002
|
+
defaultAgentId: resolveDefaultAgentId(params.ctx.cfg),
|
|
2849
3003
|
storePath: params.storePath,
|
|
2850
3004
|
sessionKey: params.sessionKey,
|
|
2851
3005
|
sessionCfg: params.ctx.cfg.session,
|
|
@@ -3495,6 +3649,14 @@ async function prepareSlackMessage(params) {
|
|
|
3495
3649
|
const assistantThreadContext = mergeSlackAssistantThreadContext(messageAssistantThreadContext, cachedAssistantThreadContext ?? restoredAssistantThreadContext);
|
|
3496
3650
|
const assistantThreadContextToCache = messageAssistantThreadContext || restoredAssistantThreadContext ? assistantThreadContext : void 0;
|
|
3497
3651
|
if (assistantThreadContextToCache) ctx.saveSlackAssistantThreadContext(assistantThreadContextToCache, opts.eventScope);
|
|
3652
|
+
const hasAgentViewMessageSignal = isDirectMessage && !opts.eventScope && !assistantThreadContext && isSlackAppContext(message.app_context);
|
|
3653
|
+
if (hasAgentViewMessageSignal) await ctx.recordSlackAgentView();
|
|
3654
|
+
const managedViewThreadTs = message.thread_ts;
|
|
3655
|
+
const isManagedViewRoot = isDirectMessage && !opts.eventScope && !assistantThreadContext && !message.parent_user_id && Boolean(managedViewThreadTs && message.ts && managedViewThreadTs === message.ts);
|
|
3656
|
+
if (isManagedViewRoot && managedViewThreadTs) await ctx.recordSlackManagedViewThread(message.channel, managedViewThreadTs);
|
|
3657
|
+
const hasManagedViewThread = isDirectMessage && !opts.eventScope && !assistantThreadContext && Boolean(managedViewThreadTs && await ctx.isSlackManagedViewThread(message.channel, managedViewThreadTs));
|
|
3658
|
+
const isAgentViewMessage = isDirectMessage && !opts.eventScope && !assistantThreadContext && (hasAgentViewMessageSignal || isManagedViewRoot || hasManagedViewThread || await ctx.isSlackAgentView());
|
|
3659
|
+
const agentViewThreadTs = isAgentViewMessage ? message.thread_ts ?? message.ts : void 0;
|
|
3498
3660
|
const channelReplyToMode = channelConfig?.replyToMode ?? resolveSlackReplyToMode(account, channelChatType);
|
|
3499
3661
|
const willImplicitlyThreadReply = isRoom && !channelRequireMention && channelReplyToMode !== "off";
|
|
3500
3662
|
const seedTopLevelRoomThreadBySource = opts.source === "app_mention" || opts.wasMentioned === true || explicitlyMentioned || willImplicitlyThreadReply;
|
|
@@ -3509,6 +3671,7 @@ async function prepareSlackMessage(params) {
|
|
|
3509
3671
|
channelConfig,
|
|
3510
3672
|
seedTopLevelRoomThread: seedTopLevelRoomThreadBySource,
|
|
3511
3673
|
assistantThreadTs: assistantThreadContext?.threadTs,
|
|
3674
|
+
agentViewThreadTs,
|
|
3512
3675
|
eventScope: opts.eventScope
|
|
3513
3676
|
});
|
|
3514
3677
|
let mentionCheckTranscript;
|
|
@@ -3534,6 +3697,7 @@ async function prepareSlackMessage(params) {
|
|
|
3534
3697
|
const { configuredBinding, configuredBindingSessionKey } = routing;
|
|
3535
3698
|
const isAssistantThreadMessage = Boolean(isDirectMessage && messageAssistantThreadContext);
|
|
3536
3699
|
const forcedAssistantReplyThreadTs = Boolean(assistantThreadContext?.threadTs && (isThreadReply || isAssistantThreadMessage || replyToMode !== "off")) ? assistantThreadContext?.threadTs : void 0;
|
|
3700
|
+
const forcedReplyThreadTs = agentViewThreadTs ?? forcedAssistantReplyThreadTs;
|
|
3537
3701
|
if (runtimeBinding && shouldLogVerbose()) logVerbose(`slack: routed via bound conversation ${runtimeBinding.conversation.conversationId} -> ${runtimeBinding.targetSessionKey}`);
|
|
3538
3702
|
if (configuredBinding) {
|
|
3539
3703
|
const ensured = await ensureConfiguredBindingRouteReady({
|
|
@@ -3744,6 +3908,7 @@ async function prepareSlackMessage(params) {
|
|
|
3744
3908
|
channelConfig,
|
|
3745
3909
|
seedTopLevelRoomThread: true,
|
|
3746
3910
|
assistantThreadTs: assistantThreadContext?.threadTs,
|
|
3911
|
+
agentViewThreadTs,
|
|
3747
3912
|
eventScope: opts.eventScope
|
|
3748
3913
|
});
|
|
3749
3914
|
return seededMentionRouting;
|
|
@@ -3795,7 +3960,7 @@ async function prepareSlackMessage(params) {
|
|
|
3795
3960
|
preflightAudioTranscript = void 0;
|
|
3796
3961
|
preflightAudioMedia = void 0;
|
|
3797
3962
|
}
|
|
3798
|
-
const directThreadRoutedToDmSession = !assistantThreadContext && isDirectMessage && isThreadReply && threadTs && runtimeBinding?.conversation.conversationId !== threadTs;
|
|
3963
|
+
const directThreadRoutedToDmSession = !assistantThreadContext && !agentViewThreadTs && isDirectMessage && isThreadReply && threadTs && runtimeBinding?.conversation.conversationId !== threadTs;
|
|
3799
3964
|
const mentionDecision = resolveInboundMentionDecision({
|
|
3800
3965
|
facts: {
|
|
3801
3966
|
canDetectMention,
|
|
@@ -3956,7 +4121,7 @@ async function prepareSlackMessage(params) {
|
|
|
3956
4121
|
previousTimestamp,
|
|
3957
4122
|
envelope: envelopeOptions
|
|
3958
4123
|
});
|
|
3959
|
-
const dmHistoryContext = isDirectMessage && !isThreadReply && dmHistoryLimit > 0 && !previousTimestamp ? await resolveSlackDmHistoryContext({
|
|
4124
|
+
const dmHistoryContext = isDirectMessage && !assistantThreadContext && !agentViewThreadTs && !isThreadReply && dmHistoryLimit > 0 && !previousTimestamp ? await resolveSlackDmHistoryContext({
|
|
3960
4125
|
ctx,
|
|
3961
4126
|
channelId: message.channel,
|
|
3962
4127
|
currentMessageTs: message.ts,
|
|
@@ -3983,7 +4148,7 @@ async function prepareSlackMessage(params) {
|
|
|
3983
4148
|
})
|
|
3984
4149
|
});
|
|
3985
4150
|
const slackTo = isDirectMessage ? `user:${message.user}` : `channel:${message.channel}`;
|
|
3986
|
-
const {
|
|
4151
|
+
const { channelMetadata, groupSystemPrompt } = resolveSlackRoomContextHints({
|
|
3987
4152
|
isRoomish,
|
|
3988
4153
|
channelInfo,
|
|
3989
4154
|
channelConfig
|
|
@@ -4014,7 +4179,8 @@ async function prepareSlackMessage(params) {
|
|
|
4014
4179
|
}) : dmHistoryContext.inboundHistory;
|
|
4015
4180
|
const commandBody = textForCommandDetection.trim();
|
|
4016
4181
|
const supplementalThreadHistoryBody = directThreadRoutedToDmSession && !threadHistoryBody ? threadStarterBody : threadHistoryBody;
|
|
4017
|
-
const effectiveMessageThreadId = assistantThreadContext?.threadTs ?? threadContext.messageThreadId;
|
|
4182
|
+
const effectiveMessageThreadId = assistantThreadContext?.threadTs ?? agentViewThreadTs ?? threadContext.messageThreadId;
|
|
4183
|
+
const agentContextEntities = isAgentViewMessage ? normalizeSlackAppContextEntities(message.app_context) : [];
|
|
4018
4184
|
const ctxPayload = buildChannelInboundEventContext({
|
|
4019
4185
|
channel: "slack",
|
|
4020
4186
|
accountId: route.accountId,
|
|
@@ -4056,6 +4222,7 @@ async function prepareSlackMessage(params) {
|
|
|
4056
4222
|
commandBody,
|
|
4057
4223
|
inboundHistory
|
|
4058
4224
|
},
|
|
4225
|
+
sessionTranscript: { historyLimit: isRoomish ? ctx.historyLimit : dmHistoryLimit },
|
|
4059
4226
|
access: {
|
|
4060
4227
|
mentions: {
|
|
4061
4228
|
canDetectMention: isRoomish,
|
|
@@ -4078,9 +4245,16 @@ async function prepareSlackMessage(params) {
|
|
|
4078
4245
|
},
|
|
4079
4246
|
extra: {
|
|
4080
4247
|
GroupSubject: isRoomish ? roomLabel : void 0,
|
|
4081
|
-
|
|
4248
|
+
ChannelPromptContext: channelMetadata ? [channelMetadata] : void 0,
|
|
4249
|
+
ChannelStructuredContext: agentContextEntities.length > 0 ? [{
|
|
4250
|
+
label: "Slack active context",
|
|
4251
|
+
source: "slack",
|
|
4252
|
+
type: "active_view",
|
|
4253
|
+
payload: { entities: agentContextEntities }
|
|
4254
|
+
}] : void 0,
|
|
4082
4255
|
TransportThreadId: directThreadRoutedToDmSession ? threadContext.messageThreadId : void 0,
|
|
4083
4256
|
SlackAssistantThread: assistantThreadContext ? true : void 0,
|
|
4257
|
+
SlackAgentThread: agentViewThreadTs ? true : void 0,
|
|
4084
4258
|
SlackAssistantThreadContextChannelId: assistantThreadContext?.channelId,
|
|
4085
4259
|
SlackAssistantThreadContextTeamId: assistantThreadContext?.teamId,
|
|
4086
4260
|
SlackAssistantThreadContextEnterpriseId: assistantThreadContext?.enterpriseId ?? void 0,
|
|
@@ -4170,7 +4344,7 @@ async function prepareSlackMessage(params) {
|
|
|
4170
4344
|
} : void 0
|
|
4171
4345
|
},
|
|
4172
4346
|
replyToMode,
|
|
4173
|
-
...
|
|
4347
|
+
...forcedReplyThreadTs ? { forcedReplyThreadTs } : {},
|
|
4174
4348
|
...assistantThreadContext ? { slackMessageMetadata: buildSlackAssistantThreadMetadata(assistantThreadContext) } : {},
|
|
4175
4349
|
requireMention: shouldRequireMention,
|
|
4176
4350
|
isDirectMessage,
|