agents 0.25.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/README.md +8 -9
  2. package/dist/{agent-routing-0DR0Tk2v.d.ts → agent-routing-Bhdbvf7s.d.ts} +13 -8
  3. package/dist/agent-routing.d.ts +1 -1
  4. package/dist/agent-tool-types.d.ts +1 -1
  5. package/dist/{agent-tools-YrATZJu2.d.ts → agent-tools-bJZwBMfq.d.ts} +2 -2
  6. package/dist/agent-tools.d.ts +1 -1
  7. package/dist/ai-sdk-OoGkFy3s.js +2444 -0
  8. package/dist/ai-sdk-OoGkFy3s.js.map +1 -0
  9. package/dist/apply-chunks-1FheRb9W.js +99 -0
  10. package/dist/apply-chunks-1FheRb9W.js.map +1 -0
  11. package/dist/broadcast-state-CEyI4uAK.js +69 -0
  12. package/dist/broadcast-state-CEyI4uAK.js.map +1 -0
  13. package/dist/browser/ai-sdk.d.ts +1 -1
  14. package/dist/browser/ai-sdk.js +2 -2
  15. package/dist/browser/ai-sdk.js.map +1 -1
  16. package/dist/browser/index.d.ts +1 -1
  17. package/dist/browser/tanstack-ai.d.ts +1 -1
  18. package/dist/browser/tanstack-ai.js +1 -1
  19. package/dist/{browser-DE3d-RLk.d.ts → browser-tmkcAAI3.d.ts} +2 -2
  20. package/dist/{browser-tool-DuNdlwZ9.d.ts → browser-tool-Bn-nzJQI.d.ts} +3 -3
  21. package/dist/{browser-tool-CQ10e6Kj.js → browser-tool-CBqs6hqa.js} +58 -18
  22. package/dist/browser-tool-CBqs6hqa.js.map +1 -0
  23. package/dist/capability-B4WbF81e.js.map +1 -1
  24. package/dist/{capability-runner-DBAnGr6L.d.ts → capability-runner-O5VbQzqg.d.ts} +9 -1
  25. package/dist/channel-Bak_6zDP.d.ts +333 -0
  26. package/dist/chat/index.d.ts +11 -9
  27. package/dist/chat/index.js +5 -194
  28. package/dist/chat/index.js.map +1 -1
  29. package/dist/chat/react.d.ts +2 -4
  30. package/dist/chat/react.js +3 -1
  31. package/dist/chat/react.js.map +1 -1
  32. package/dist/chat-sdk/index.d.ts +1 -1
  33. package/dist/chat-sdk/index.js +1 -1
  34. package/dist/cli.js +144 -0
  35. package/dist/{client-fyQrLGkD.js → client-1tEQ1Oqz.js} +27 -2
  36. package/dist/{client-fyQrLGkD.js.map → client-1tEQ1Oqz.js.map} +1 -1
  37. package/dist/{client-je3koD9Z.d.ts → client-DS3Dz9l0.d.ts} +2 -2
  38. package/dist/client-tools-aIBO0Fk7.d.ts +53 -0
  39. package/dist/client.d.ts +1 -1
  40. package/dist/conversations-DH0ut_8n.d.ts +192 -0
  41. package/dist/{current-agent-vAk_5JMt.d.ts → current-agent-C07C5o5Y.d.ts} +3 -3
  42. package/dist/daemon-core-Buubj-y1.js +481 -0
  43. package/dist/daemon-core-Buubj-y1.js.map +1 -0
  44. package/dist/daemon-core-CbohVPAu.d.ts +132 -0
  45. package/dist/{do-oauth-client-provider-Tmf1vgKz.d.ts → do-oauth-client-provider-DfsUcpc3.d.ts} +7 -1
  46. package/dist/{durable-object-lifecycle-HhgOMWq9.d.ts → durable-object-lifecycle-DRqp475_.d.ts} +2 -2
  47. package/dist/{channels/email.d.ts → experimental/channels/email/index.d.ts} +25 -12
  48. package/dist/{channels/email.js → experimental/channels/email/index.js} +13 -35
  49. package/dist/experimental/channels/email/index.js.map +1 -0
  50. package/dist/experimental/channels/index.d.ts +270 -0
  51. package/dist/experimental/channels/index.js +1186 -0
  52. package/dist/experimental/channels/index.js.map +1 -0
  53. package/dist/{channels/slack.d.ts → experimental/channels/slack/index.d.ts} +19 -9
  54. package/dist/experimental/channels/slack/index.js +267 -0
  55. package/dist/experimental/channels/slack/index.js.map +1 -0
  56. package/dist/{channels/telegram.d.ts → experimental/channels/telegram/index.d.ts} +19 -9
  57. package/dist/experimental/channels/telegram/index.js +198 -0
  58. package/dist/experimental/channels/telegram/index.js.map +1 -0
  59. package/dist/experimental/channels/web/ai-sdk.d.ts +55 -0
  60. package/dist/experimental/channels/web/ai-sdk.js +266 -0
  61. package/dist/experimental/channels/web/ai-sdk.js.map +1 -0
  62. package/dist/experimental/channels/web/client.d.ts +97 -0
  63. package/dist/experimental/channels/web/client.js +349 -0
  64. package/dist/experimental/channels/web/client.js.map +1 -0
  65. package/dist/experimental/channels/web/index.d.ts +189 -0
  66. package/dist/experimental/channels/web/index.js +332 -0
  67. package/dist/experimental/channels/web/index.js.map +1 -0
  68. package/dist/gateway-BDC07gYO.d.ts +100 -0
  69. package/dist/handle-zAzh0UW2.d.ts +460 -0
  70. package/dist/harness/ai-sdk/index.d.ts +175 -0
  71. package/dist/harness/ai-sdk/index.js +605 -0
  72. package/dist/harness/ai-sdk/index.js.map +1 -0
  73. package/dist/harness/container/daemon/index.d.ts +76 -0
  74. package/dist/harness/container/daemon/index.js +3 -0
  75. package/dist/harness/container/index.d.ts +740 -0
  76. package/dist/harness/container/index.js +2152 -0
  77. package/dist/harness/container/index.js.map +1 -0
  78. package/dist/harness/container/runtime/index.d.ts +144 -0
  79. package/dist/harness/container/runtime/index.js +1082 -0
  80. package/dist/harness/container/runtime/index.js.map +1 -0
  81. package/dist/harness/opencode/index.d.ts +369 -0
  82. package/dist/harness/opencode/index.js +1072 -0
  83. package/dist/harness/opencode/index.js.map +1 -0
  84. package/dist/{harnesses → harness}/pi/index.d.ts +85 -88
  85. package/dist/{harnesses → harness}/pi/index.js +196 -77
  86. package/dist/harness/pi/index.js.map +1 -0
  87. package/dist/harness/store/index.d.ts +24 -0
  88. package/dist/harness/store/index.js +2 -0
  89. package/dist/harness/think/index.d.ts +821 -0
  90. package/dist/harness/think/index.js +2529 -0
  91. package/dist/harness/think/index.js.map +1 -0
  92. package/dist/harness-B6B9S3rb.d.ts +203 -0
  93. package/dist/{images-iiTDSyRu.js → images-Ce9SCu3X.js} +2 -248
  94. package/dist/images-Ce9SCu3X.js.map +1 -0
  95. package/dist/{index-BreL0YrQ.d.ts → index-D0A5tbWu.d.ts} +2 -2
  96. package/dist/index-Ib_4dwOo.d.ts +200 -0
  97. package/dist/index.d.ts +5 -5
  98. package/dist/index.js +2 -2
  99. package/dist/ingress-EFbrS547.js +9 -0
  100. package/dist/ingress-EFbrS547.js.map +1 -0
  101. package/dist/input-schema-DNrpTu49.js +23 -0
  102. package/dist/input-schema-DNrpTu49.js.map +1 -0
  103. package/dist/internal-kmw70xwR.js +46 -0
  104. package/dist/internal-kmw70xwR.js.map +1 -0
  105. package/dist/internal_context.d.ts +1 -1
  106. package/dist/lifecycle/index.d.ts +3 -3
  107. package/dist/mcp/client/do-oauth-client-provider.d.ts +1 -1
  108. package/dist/mcp/client/do-oauth-client-provider.js +9 -0
  109. package/dist/mcp/client/do-oauth-client-provider.js.map +1 -1
  110. package/dist/mcp/client/index.d.ts +1 -1
  111. package/dist/mcp/client/index.js +1 -1
  112. package/dist/mcp/client/x402.d.ts +35 -4
  113. package/dist/mcp/client/x402.js +24 -3
  114. package/dist/mcp/client/x402.js.map +1 -1
  115. package/dist/mcp/index.d.ts +1 -1
  116. package/dist/mcp/index.js +2 -2
  117. package/dist/models/ai-sdk/index.d.ts +16 -14
  118. package/dist/models/ai-sdk/index.js +1 -2442
  119. package/dist/models/opencode/index.d.ts +65 -0
  120. package/dist/models/opencode/index.js +285 -0
  121. package/dist/models/opencode/index.js.map +1 -0
  122. package/dist/models/pi-ai/index.d.ts +23 -21
  123. package/dist/models/pi-ai/index.js +7 -6
  124. package/dist/models/pi-ai/index.js.map +1 -1
  125. package/dist/options-pwTNq9z6.d.ts +134 -0
  126. package/dist/protocol-BEgrJYC5.d.ts +331 -0
  127. package/dist/protocol-CukuI73M.d.ts +386 -0
  128. package/dist/protocol-cOklVBBD.js +227 -0
  129. package/dist/protocol-cOklVBBD.js.map +1 -0
  130. package/dist/{protocol-kHsr-f0X.js → protocol-eXeJxhgu.js} +2 -67
  131. package/dist/protocol-eXeJxhgu.js.map +1 -0
  132. package/dist/protocol-y2sSY584.js +148 -0
  133. package/dist/protocol-y2sSY584.js.map +1 -0
  134. package/dist/queue/index.d.ts +1 -1
  135. package/dist/react.d.ts +2 -2
  136. package/dist/routing/index.d.ts +2 -2
  137. package/dist/{scheduler-RdACNXDP.d.ts → scheduler-CrFCnv_J.d.ts} +2 -2
  138. package/dist/schedules/index.d.ts +1 -1
  139. package/dist/serializable.d.ts +1 -1
  140. package/dist/sessions/index.d.ts +11 -434
  141. package/dist/sessions/index.js +1 -2271
  142. package/dist/sessions-B9pNuxdT.js +2272 -0
  143. package/dist/sessions-B9pNuxdT.js.map +1 -0
  144. package/dist/settings-C45hDliX.js +253 -0
  145. package/dist/settings-C45hDliX.js.map +1 -0
  146. package/dist/{transport-PfFnt-Ld.d.ts → settings-NgGwF7Qt.d.ts} +14 -208
  147. package/dist/{src-BZeu-SCD.js → src-DgCbZtIR.js} +4 -4
  148. package/dist/{src-BZeu-SCD.js.map → src-DgCbZtIR.js.map} +1 -1
  149. package/dist/stall-watchdog-BpRF2uwG.js +195 -0
  150. package/dist/stall-watchdog-BpRF2uwG.js.map +1 -0
  151. package/dist/state/index.d.ts +1 -1
  152. package/dist/store-DLeqKuj3.d.ts +310 -0
  153. package/dist/store-vgcPYo8K.js +432 -0
  154. package/dist/store-vgcPYo8K.js.map +1 -0
  155. package/dist/streams/index.d.ts +1 -1
  156. package/dist/streams/index.js +1 -1
  157. package/dist/{streams-DW-UCOUc.js → streams-CITClVer.js} +16 -3
  158. package/dist/streams-CITClVer.js.map +1 -0
  159. package/dist/{streams-Cmkj9QfW.d.ts → streams-fbG61qM8.d.ts} +2 -2
  160. package/dist/sub-routing.d.ts +1 -1
  161. package/dist/tasks/index.d.ts +1 -1
  162. package/dist/tasks/index.js +1 -1
  163. package/dist/{tasks-68DESxf0.js → tasks-CkTZNaV1.js} +2 -1
  164. package/dist/tasks-CkTZNaV1.js.map +1 -0
  165. package/dist/{tasks-9YxYtByh.d.ts → tasks-PtlB4Uyn.d.ts} +2 -2
  166. package/dist/tool-3PA8xjB_.js +421 -0
  167. package/dist/tool-3PA8xjB_.js.map +1 -0
  168. package/dist/tool-BPJEbwaX.d.ts +278 -0
  169. package/dist/transport-k2mFrtL8.d.ts +200 -0
  170. package/dist/turns-DCV3ycDl.js +317 -0
  171. package/dist/turns-DCV3ycDl.js.map +1 -0
  172. package/dist/voice/client.js +1 -1
  173. package/dist/voice/index.d.ts +2 -2
  174. package/dist/voice/index.js +1 -1
  175. package/dist/websearch/index.d.ts +58 -0
  176. package/dist/websearch/index.js +2 -0
  177. package/dist/websearch/tools/ai-sdk.d.ts +77 -0
  178. package/dist/websearch/tools/ai-sdk.js +46 -0
  179. package/dist/websearch/tools/ai-sdk.js.map +1 -0
  180. package/dist/websearch/tools/pi.d.ts +82 -0
  181. package/dist/websearch/tools/pi.js +89 -0
  182. package/dist/websearch/tools/pi.js.map +1 -0
  183. package/dist/websearch/tools/tanstack-ai.d.ts +71 -0
  184. package/dist/websearch/tools/tanstack-ai.js +46 -0
  185. package/dist/websearch/tools/tanstack-ai.js.map +1 -0
  186. package/dist/websockets/index.d.ts +12 -296
  187. package/dist/websockets/index.js +1 -1
  188. package/dist/{websockets-Ci-LZFHH.js → websockets-CmENGb-Z.js} +18 -4
  189. package/dist/websockets-CmENGb-Z.js.map +1 -0
  190. package/dist/{wire-types-CrjKvDyi.d.ts → wire-types-Cv0iwjk5.d.ts} +3 -51
  191. package/dist/workflow-types.d.ts +1 -1
  192. package/dist/workflows.d.ts +1 -1
  193. package/dist/workflows.js +1 -1
  194. package/docs/browse-the-web.md +3 -0
  195. package/docs/harnesses/pi.md +114 -32
  196. package/docs/harnesses/think.md +215 -0
  197. package/docs/index.md +4 -3
  198. package/docs/lifecycle.md +40 -0
  199. package/docs/models-pi-ai.md +5 -5
  200. package/docs/search-the-web.md +133 -0
  201. package/package.json +126 -47
  202. package/dist/browser-tool-CQ10e6Kj.js.map +0 -1
  203. package/dist/channel-Bnm4S7T2.d.ts +0 -491
  204. package/dist/channels/ai-sdk.d.ts +0 -46
  205. package/dist/channels/ai-sdk.js +0 -120
  206. package/dist/channels/ai-sdk.js.map +0 -1
  207. package/dist/channels/email.js.map +0 -1
  208. package/dist/channels/index.d.ts +0 -233
  209. package/dist/channels/index.js +0 -608
  210. package/dist/channels/index.js.map +0 -1
  211. package/dist/channels/slack.js +0 -614
  212. package/dist/channels/slack.js.map +0 -1
  213. package/dist/channels/tanstack-ai.d.ts +0 -39
  214. package/dist/channels/tanstack-ai.js +0 -17
  215. package/dist/channels/tanstack-ai.js.map +0 -1
  216. package/dist/channels/telegram.js +0 -427
  217. package/dist/channels/telegram.js.map +0 -1
  218. package/dist/channels/voice.d.ts +0 -45
  219. package/dist/channels/voice.js +0 -122
  220. package/dist/channels/voice.js.map +0 -1
  221. package/dist/harnesses/pi/index.js.map +0 -1
  222. package/dist/images-iiTDSyRu.js.map +0 -1
  223. package/dist/index-BB0kqhIz.d.ts +0 -101
  224. package/dist/ingress-BfetZbMO.js +0 -83
  225. package/dist/ingress-BfetZbMO.js.map +0 -1
  226. package/dist/internal-CYlgHl1l.js +0 -59
  227. package/dist/internal-CYlgHl1l.js.map +0 -1
  228. package/dist/models/ai-sdk/index.js.map +0 -1
  229. package/dist/protocol-kHsr-f0X.js.map +0 -1
  230. package/dist/sessions/index.js.map +0 -1
  231. package/dist/streams-DW-UCOUc.js.map +0 -1
  232. package/dist/surface-bZZJqBka.js +0 -17
  233. package/dist/surface-bZZJqBka.js.map +0 -1
  234. package/dist/tasks-68DESxf0.js.map +0 -1
  235. package/dist/tool-schema-CBjGPrsQ.js +0 -31
  236. package/dist/tool-schema-CBjGPrsQ.js.map +0 -1
  237. package/dist/websockets-Ci-LZFHH.js.map +0 -1
  238. package/docs/channels.md +0 -323
@@ -1 +0,0 @@
1
- {"version":3,"file":"slack.js","names":[],"sources":["../../src/channels/adapters/slack.ts"],"sourcesContent":["import type {\n Channel,\n ChannelApprovalRequest,\n ChannelChunk,\n ChannelMessage,\n ChannelRoute,\n ChannelStreamOptions,\n DeliveryResult\n} from \"../channel\";\nimport { collectText, consumeChunks, createPacer } from \"../stream\";\nimport type { ChannelIdentity } from \"../identity\";\nimport {\n isChannelMessageSurface,\n type ChannelMessageSurface\n} from \"../surface\";\nimport {\n matchesPath,\n type ChannelApprovalResponseInput,\n type ChannelInboundMessageInput,\n type ChannelIngress\n} from \"../ingress\";\nimport {\n defaultText,\n emptyIngressResponse,\n encodeUtf8,\n isRecord,\n renderInput,\n uncertain\n} from \"../internal\";\n\n/** The subset of a Slack event used by the adapter, with unknown fields retained. */\nexport type SlackEvent = {\n type?: string;\n user?: string;\n text?: string;\n ts?: string;\n thread_ts?: string;\n channel?: string;\n channel_type?: string;\n subtype?: string;\n bot_id?: string;\n app_id?: string;\n [key: string]: unknown;\n};\n\nexport type SlackAuthorization = {\n team_id?: string;\n user_id?: string;\n is_bot?: boolean;\n [key: string]: unknown;\n};\n\n/** A typed Slack Events API callback retained in an ingress envelope. */\nexport type SlackEventCallback = {\n type: \"event_callback\";\n event_id: string;\n team_id: string;\n event: SlackEvent;\n authorizations?: readonly SlackAuthorization[];\n [key: string]: unknown;\n};\n\n/** A typed Slack URL verification request. */\nexport type SlackUrlVerification = {\n type: \"url_verification\";\n challenge: string;\n [key: string]: unknown;\n};\n\nexport type SlackBlockAction = {\n action_id?: string;\n action_ts?: string;\n value?: string;\n [key: string]: unknown;\n};\n\n/** A typed Slack Block Kit interaction retained in an ingress envelope. */\nexport type SlackBlockActions = {\n type: \"block_actions\";\n team?: { id?: string; [key: string]: unknown };\n user?: {\n id?: string;\n username?: string;\n name?: string;\n [key: string]: unknown;\n };\n channel?: { id?: string; [key: string]: unknown };\n message?: {\n ts?: string;\n thread_ts?: string;\n [key: string]: unknown;\n };\n container?: {\n channel_id?: string;\n message_ts?: string;\n thread_ts?: string;\n [key: string]: unknown;\n };\n actions?: readonly SlackBlockAction[];\n [key: string]: unknown;\n};\n\n/** Authenticated Slack payloads which may be passed to a Channel route. */\nexport type SlackIngressPayload = SlackEventCallback | SlackBlockActions;\n\n/** Authenticated JSON payloads handled before routable events are emitted. */\ntype SlackJsonPayload = SlackEventCallback | SlackUrlVerification;\n\nexport type SlackWebhookOptions = {\n /** Slack app signing secret. */\n signingSecret: string;\n /** Exact webhook pathname accepted by this ingress. @default \"/webhooks/slack\" */\n path?: string;\n /** Maximum accepted request timestamp skew in seconds. @default 300 */\n maxSkewSeconds?: number;\n /** Bot user id used to discard events sent by this app. */\n botUserId?: string;\n};\n\nexport type SlackMessageSurface = ChannelMessageSurface<\n string,\n | {\n channelId: string;\n threadTs?: string;\n /** The reader a channel stream is rendered for. Slack requires it. */\n recipientUserId?: string;\n recipientTeamId?: string;\n }\n | { teamId: string; userId: string }\n>;\n\n/** Configuration for a Slack Channel. */\nexport type SlackChannelOptions = {\n /** Bot token used with chat.postMessage. */\n botToken: string;\n /** Slack Web API origin. @default \"https://slack.com/api\" */\n apiBaseUrl?: string;\n /** Project canonical Channel Markdown into Slack mrkdwn text. */\n toText?: (message: ChannelMessage) => string;\n /**\n * Smallest gap between `chat.appendStream` calls. Chunks produced inside\n * one interval are appended together. @default 500\n */\n streamIntervalMs?: number;\n /** Add signed Slack HTTP ingress to the returned Channel. */\n webhook?: SlackWebhookOptions;\n /** Select an application route from the event, exact payload, and Host context. */\n route?: ChannelRoute<SlackIngressPayload>;\n /** Override fetch for testing or custom network routing. */\n fetch?: typeof globalThis.fetch;\n};\n\ntype SlackApiResponse = {\n ok?: unknown;\n error?: unknown;\n channel?: unknown;\n ts?: unknown;\n};\n\ntype SlackOpenConversationResponse = {\n ok?: unknown;\n error?: unknown;\n channel?: { id?: unknown };\n};\n\ntype ApprovalValue = {\n v: 1;\n interactionId: string;\n decision: \"approve\" | \"reject\";\n};\n\nconst DEFAULT_SLACK_WEBHOOK_PATH = \"/webhooks/slack\";\nconst DEFAULT_MAX_SKEW_SECONDS = 5 * 60;\nconst APPROVE_ACTION_ID = \"cloudflare_channels_approve_v1\";\nconst REJECT_ACTION_ID = \"cloudflare_channels_reject_v1\";\nconst DEFAULT_STREAM_INTERVAL_MS = 500;\nconst SLACK_APPEND_LIMIT = 12_000;\nconst SLACK_TASK_FIELD_LIMIT = 256;\nconst SLACK_CONTEXT_LIMIT = 3000;\nconst AMBIGUOUS_SLACK_ERRORS = new Set([\n \"fatal_error\",\n \"internal_error\",\n \"request_timeout\",\n \"service_unavailable\"\n]);\n\nfunction approvalText(request: ChannelApprovalRequest): string {\n return [\n ...(request.title ? [request.title] : []),\n request.summary,\n `Input:\\n\\`\\`\\`\\n${renderInput(request.input)}\\n\\`\\`\\``\n ].join(\"\\n\\n\");\n}\n\nfunction channelIdentity(teamId: string, channelId: string): string {\n return `slack:${teamId}:channel:${channelId}`;\n}\n\nfunction messageIdentity(\n teamId: string,\n channelId: string,\n timestamp: string\n): string {\n return `${channelIdentity(teamId, channelId)}:message:${timestamp}`;\n}\n\nfunction outboundReference(channelId: string, timestamp: string): string {\n return `slack:channel:${channelId}:message:${timestamp}`;\n}\n\nfunction replySurfaceLabel(\n channelId: string,\n threadTimestamp: string | undefined\n): string {\n return threadTimestamp\n ? `Slack · ${channelId} · thread ${threadTimestamp}`\n : `Slack · ${channelId}`;\n}\n\nfunction threadIdentity(\n teamId: string,\n channelId: string,\n timestamp: string,\n threadTimestamp: string | undefined,\n isDirectMessage: boolean\n): string {\n const channel = channelIdentity(teamId, channelId);\n if (isDirectMessage && threadTimestamp === undefined) return channel;\n return `${channel}:thread:${threadTimestamp ?? timestamp}`;\n}\n\nfunction slackTimestamp(timestamp: string): string | undefined {\n const milliseconds = Number(timestamp) * 1000;\n if (!Number.isFinite(milliseconds)) return undefined;\n try {\n return new Date(milliseconds).toISOString();\n } catch {\n return undefined;\n }\n}\n\nfunction selfUserIds(\n payload: SlackEventCallback,\n configuredBotUserId: string | undefined\n): Set<string> {\n const ids = new Set<string>();\n if (configuredBotUserId) ids.add(configuredBotUserId);\n if (Array.isArray(payload.authorizations)) {\n for (const authorization of payload.authorizations) {\n if (\n authorization?.is_bot === true &&\n typeof authorization.user_id === \"string\"\n ) {\n ids.add(authorization.user_id);\n }\n }\n }\n return ids;\n}\n\nfunction normalizedEvent(\n payload: SlackEventCallback,\n configuredBotUserId: string | undefined\n): ChannelInboundMessageInput | undefined {\n const event = payload.event;\n if (!event || !isRecord(event)) return undefined;\n\n if (event.type !== \"app_mention\" && event.type !== \"message\") {\n return undefined;\n }\n const isMention = event.type === \"app_mention\";\n const isDirectMessage =\n event.type === \"message\" && event.channel_type === \"im\";\n\n if (\n event.subtype !== undefined ||\n typeof event.bot_id === \"string\" ||\n typeof event.app_id === \"string\"\n ) {\n return undefined;\n }\n\n const teamId = payload.team_id;\n const eventId = payload.event_id;\n const channelId = event.channel;\n const actorId = event.user;\n const text = event.text;\n const timestamp = event.ts;\n if (\n typeof teamId !== \"string\" ||\n typeof eventId !== \"string\" ||\n typeof channelId !== \"string\" ||\n typeof actorId !== \"string\" ||\n typeof text !== \"string\" ||\n typeof timestamp !== \"string\"\n ) {\n return undefined;\n }\n\n if (selfUserIds(payload, configuredBotUserId).has(actorId)) {\n return undefined;\n }\n\n const threadTimestamp =\n typeof event.thread_ts === \"string\" ? event.thread_ts : undefined;\n const sentAt = slackTimestamp(timestamp);\n return {\n type: \"message\",\n eventId: `slack:${teamId}:event:${eventId}`,\n thread: {\n id: threadIdentity(\n teamId,\n channelId,\n timestamp,\n threadTimestamp,\n isDirectMessage\n ),\n isDirectMessage\n },\n replySurface: {\n version: 1,\n address: {\n teamId,\n channelId,\n ...((threadTimestamp || !isDirectMessage) && {\n threadTs: threadTimestamp ?? timestamp\n }),\n // Preserve the reader even for a known DM conversation so outbound\n // routing can distinguish it from a top-level public channel.\n recipientUserId: actorId,\n recipientTeamId: teamId\n },\n label: replySurfaceLabel(\n channelId,\n threadTimestamp ?? (isDirectMessage ? undefined : timestamp)\n )\n },\n actor: {\n id: `slack:${teamId}:user:${actorId}`,\n identity: {\n scope: teamId,\n subject: actorId\n },\n isBot: false,\n isSelf: false\n },\n message: {\n id: messageIdentity(teamId, channelId, timestamp),\n text,\n markdown: text,\n ...(isMention && { isMention: true }),\n ...(threadTimestamp &&\n threadTimestamp !== timestamp && {\n reply: {\n id: messageIdentity(teamId, channelId, threadTimestamp)\n }\n }),\n ...(sentAt && { metadata: { sentAt } })\n }\n };\n}\n\nfunction parseApprovalValue(value: string): ApprovalValue | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(value);\n } catch {\n return undefined;\n }\n if (\n !isRecord(parsed) ||\n parsed.v !== 1 ||\n typeof parsed.interactionId !== \"string\" ||\n !parsed.interactionId ||\n (parsed.decision !== \"approve\" && parsed.decision !== \"reject\")\n ) {\n return undefined;\n }\n return parsed as ApprovalValue;\n}\n\nasync function sha256Hex(value: string): Promise<string> {\n const digest = await crypto.subtle.digest(\"SHA-256\", encodeUtf8(value));\n return Array.from(new Uint8Array(digest), (byte) =>\n byte.toString(16).padStart(2, \"0\")\n ).join(\"\");\n}\n\nasync function normalizedInteractions(\n payload: SlackBlockActions,\n rawBody: string\n): Promise<readonly ChannelApprovalResponseInput[]> {\n const teamId = payload.team?.id;\n const actorId = payload.user?.id;\n const channelId =\n typeof payload.channel?.id === \"string\"\n ? payload.channel.id\n : payload.container?.channel_id;\n const timestamp =\n typeof payload.message?.ts === \"string\"\n ? payload.message.ts\n : payload.container?.message_ts;\n if (\n typeof teamId !== \"string\" ||\n typeof actorId !== \"string\" ||\n typeof channelId !== \"string\" ||\n typeof timestamp !== \"string\" ||\n !Array.isArray(payload.actions)\n ) {\n return [];\n }\n\n const threadTimestamp =\n typeof payload.message?.thread_ts === \"string\"\n ? payload.message.thread_ts\n : typeof payload.container?.thread_ts === \"string\"\n ? payload.container.thread_ts\n : undefined;\n const isDirectMessage = channelId.startsWith(\"D\");\n const bodyHash = await sha256Hex(rawBody);\n const events: ChannelApprovalResponseInput[] = [];\n\n for (const [index, action] of payload.actions.entries()) {\n if (!action || typeof action.value !== \"string\") continue;\n const expectedDecision =\n action.action_id === APPROVE_ACTION_ID\n ? \"approve\"\n : action.action_id === REJECT_ACTION_ID\n ? \"reject\"\n : undefined;\n if (!expectedDecision) continue;\n\n const approval = parseApprovalValue(action.value);\n if (!approval || approval.decision !== expectedDecision) continue;\n\n const stableEventId = `slack:${teamId}:interaction:sha256:${bodyHash}:action:${index}`;\n const actionTimestamp =\n typeof action.action_ts === \"string\" ? action.action_ts : undefined;\n events.push({\n type: \"approval-response\",\n eventId: stableEventId,\n thread: {\n id: threadIdentity(\n teamId,\n channelId,\n timestamp,\n threadTimestamp,\n isDirectMessage\n ),\n isDirectMessage\n },\n replySurface: {\n version: 1,\n address: {\n teamId,\n channelId,\n threadTs: threadTimestamp ?? timestamp,\n ...(!isDirectMessage && {\n recipientUserId: actorId,\n recipientTeamId: teamId\n })\n },\n label: replySurfaceLabel(channelId, threadTimestamp ?? timestamp)\n },\n actor: {\n id: `slack:${teamId}:user:${actorId}`,\n identity: {\n scope: teamId,\n subject: actorId\n },\n ...(typeof payload.user?.username === \"string\" && {\n username: payload.user.username\n }),\n isBot: false\n },\n interactionId: approval.interactionId,\n decision: approval.decision,\n reference: actionTimestamp\n ? `${channelIdentity(teamId, channelId)}:action:${actionTimestamp}`\n : stableEventId\n });\n }\n\n return events;\n}\n\nfunction bytesFromHex(hex: string): Uint8Array<ArrayBuffer> | undefined {\n if (!/^[a-f0-9]{64}$/.test(hex)) return undefined;\n const bytes = new Uint8Array(hex.length / 2);\n for (let index = 0; index < bytes.length; index += 1) {\n bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);\n }\n return bytes;\n}\n\nasync function hasValidSignature(\n request: Request,\n rawBody: string,\n signingSecret: string,\n maxSkewSeconds: number\n): Promise<boolean> {\n const timestamp = request.headers.get(\"x-slack-request-timestamp\");\n const signature = request.headers.get(\"x-slack-signature\");\n if (!timestamp || !signature || !/^\\d+$/.test(timestamp)) return false;\n\n const timestampSeconds = Number(timestamp);\n if (\n !Number.isSafeInteger(timestampSeconds) ||\n Math.abs(Math.floor(Date.now() / 1000) - timestampSeconds) > maxSkewSeconds\n ) {\n return false;\n }\n\n const match = signature.match(/^v0=([a-f0-9]{64})$/);\n const signatureBytes = match ? bytesFromHex(match[1]) : undefined;\n if (!signatureBytes) return false;\n\n const key = await crypto.subtle.importKey(\n \"raw\",\n encodeUtf8(signingSecret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"verify\"]\n );\n return crypto.subtle.verify(\n \"HMAC\",\n key,\n signatureBytes,\n encodeUtf8(`v0:${timestamp}:${rawBody}`)\n );\n}\n\nfunction parseJsonPayload(rawBody: string): SlackJsonPayload | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawBody);\n } catch {\n return undefined;\n }\n if (!isRecord(parsed)) return undefined;\n if (parsed.type !== \"url_verification\" && parsed.type !== \"event_callback\") {\n return undefined;\n }\n return parsed as SlackUrlVerification | SlackEventCallback;\n}\n\nfunction parseInteractionPayload(\n rawBody: string\n): SlackBlockActions | undefined {\n const encodedPayload = new URLSearchParams(rawBody).get(\"payload\");\n if (!encodedPayload) return undefined;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(encodedPayload);\n } catch {\n return undefined;\n }\n return isRecord(parsed) && parsed.type === \"block_actions\"\n ? (parsed as SlackBlockActions)\n : undefined;\n}\n\n/** Create dependency-free, request-signed Slack HTTP ingress. */\nexport function slackWebhook(\n options: SlackWebhookOptions\n): ChannelIngress<SlackIngressPayload> {\n if (!options.signingSecret.trim()) {\n throw new Error(\n \"signingSecret is required to create Slack webhook ingress\"\n );\n }\n const maxSkewSeconds = options.maxSkewSeconds ?? DEFAULT_MAX_SKEW_SECONDS;\n if (!Number.isInteger(maxSkewSeconds) || maxSkewSeconds < 0) {\n throw new Error(\"maxSkewSeconds must be a non-negative integer\");\n }\n\n const path = options.path ?? DEFAULT_SLACK_WEBHOOK_PATH;\n return {\n async receive(request) {\n if (!matchesPath(request, path)) return null;\n if (request.method !== \"POST\") return emptyIngressResponse(405);\n\n const rawBody = await request.text();\n if (\n !(await hasValidSignature(\n request,\n rawBody,\n options.signingSecret,\n maxSkewSeconds\n ))\n ) {\n return emptyIngressResponse(401);\n }\n\n const contentType =\n request.headers.get(\"content-type\")?.split(\";\", 1)[0]?.trim() ?? \"\";\n if (contentType === \"application/x-www-form-urlencoded\") {\n const payload = parseInteractionPayload(rawBody);\n if (!payload) return emptyIngressResponse(400);\n const events = await normalizedInteractions(payload, rawBody);\n return {\n events: events.map((event) => ({ event, raw: payload })),\n response: new Response(null, { status: 200 })\n };\n }\n\n const payload = parseJsonPayload(rawBody);\n if (!payload) return emptyIngressResponse(400);\n if (payload.type === \"url_verification\") {\n return typeof payload.challenge === \"string\"\n ? {\n events: [],\n response: Response.json({ challenge: payload.challenge })\n }\n : emptyIngressResponse(400);\n }\n\n const event = normalizedEvent(payload, options.botUserId);\n return {\n events: event ? [{ event, raw: payload }] : [],\n response: new Response(null, { status: 200 })\n };\n }\n };\n}\n\nfunction failed(\n code: string,\n message: string,\n retryable: boolean\n): Extract<DeliveryResult, { status: \"failed\" }> {\n return {\n status: \"failed\",\n retryable,\n error: { code, message }\n };\n}\n\nfunction slackErrorCode(error: string): string {\n return `SLACK_API_ERROR_${error.toUpperCase().replace(/[^A-Z0-9]+/g, \"_\")}`;\n}\n\nfunction classifyApiFailure(\n response: Response,\n payload: SlackApiResponse\n): Exclude<DeliveryResult, { status: \"delivered\" }> {\n if (payload.ok === false) {\n const error =\n typeof payload.error === \"string\" ? payload.error : \"unknown_error\";\n const code = slackErrorCode(error);\n const message = `Slack rejected the message: ${error}`;\n if (response.status >= 500 || AMBIGUOUS_SLACK_ERRORS.has(error)) {\n return uncertain(code, message);\n }\n return failed(\n code,\n message,\n response.status === 429 || error === \"ratelimited\"\n );\n }\n\n if (response.status === 429) {\n return failed(\n \"SLACK_HTTP_ERROR_429\",\n \"Slack rate limited the message\",\n true\n );\n }\n if (response.status >= 400 && response.status < 500) {\n return failed(\n `SLACK_HTTP_ERROR_${response.status}`,\n `Slack rejected the message with HTTP ${response.status}`,\n false\n );\n }\n return uncertain(\n \"SLACK_DELIVERY_ERROR\",\n \"Slack returned an invalid delivery response\"\n );\n}\n\nfunction asApiResponse(value: unknown): SlackApiResponse | undefined {\n return isRecord(value) ? value : undefined;\n}\n\nfunction approvalValue(\n interactionId: string,\n decision: \"approve\" | \"reject\"\n): string {\n return JSON.stringify({\n v: 1,\n interactionId,\n decision\n } satisfies ApprovalValue);\n}\n\n/** A resolved conversation, with the reader a stream should be rendered for. */\ntype SlackDestination = {\n channelId: string;\n threadTs?: string;\n recipientUserId?: string;\n recipientTeamId?: string;\n};\n\ntype SlackTarget = SlackDestination | { teamId: string; userId: string };\n\nfunction optionalId(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction slackTarget(surface: ChannelMessageSurface): SlackTarget | undefined {\n if (\n !isChannelMessageSurface(surface) ||\n surface.version !== 1 ||\n !isRecord(surface.address)\n ) {\n return undefined;\n }\n const address = surface.address;\n if (\"userId\" in address) {\n return typeof address.teamId === \"string\" &&\n address.teamId.length > 0 &&\n typeof address.userId === \"string\" &&\n address.userId.length > 0\n ? { teamId: address.teamId, userId: address.userId }\n : undefined;\n }\n if (typeof address.channelId !== \"string\" || address.channelId.length === 0) {\n return undefined;\n }\n if (\n \"threadTs\" in address &&\n address.threadTs !== undefined &&\n (typeof address.threadTs !== \"string\" || address.threadTs.length === 0)\n ) {\n return undefined;\n }\n const recipientUserId = optionalId(address.recipientUserId);\n const recipientTeamId = optionalId(address.recipientTeamId);\n return {\n channelId: address.channelId,\n ...(typeof address.threadTs === \"string\" && {\n threadTs: address.threadTs\n }),\n // Slack rejects one without the other, so only carry a complete pair.\n ...(recipientUserId &&\n recipientTeamId && { recipientUserId, recipientTeamId })\n };\n}\n\ntype SlackStreamChunk =\n | { type: \"markdown_text\"; text: string }\n | {\n type: \"task_update\";\n id: string;\n title: string;\n status: \"in_progress\" | \"complete\" | \"error\";\n details?: string;\n };\n\nconst SLACK_TASK_STATUS = {\n started: \"in_progress\",\n completed: \"complete\",\n failed: \"error\"\n} as const;\n\nfunction splitText(text: string, limit: number): string[] {\n const pieces: string[] = [];\n let piece = \"\";\n let length = 0;\n for (const character of text) {\n if (length === limit) {\n pieces.push(piece);\n piece = \"\";\n length = 0;\n }\n piece += character;\n length += 1;\n }\n if (piece.length > 0) pieces.push(piece);\n return pieces;\n}\n\nfunction clamp(value: string, limit: number): string {\n const characters = [...value];\n return characters.length <= limit\n ? value\n : `${characters.slice(0, limit - 1).join(\"\")}\\u2026`;\n}\n\nfunction escapeMrkdwn(value: string): string {\n return value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\");\n}\n\nfunction escapeLinkUrl(value: string): string {\n return escapeMrkdwn(value.replaceAll(\"|\", \"%7C\"));\n}\n\nfunction escapeLinkLabel(value: string): string {\n return escapeMrkdwn(value).replaceAll(\"|\", \"&#124;\");\n}\n\n/**\n * Render a neutral chunk as Slack stream content.\n *\n * `reasoning` has no Slack rendering and is dropped. A `source` is collected\n * rather than appended, so it can be rendered once beneath the finished\n * message instead of interrupting the text.\n */\nfunction toStreamChunks(\n chunk: ChannelChunk,\n sources: { url: string; title?: string }[]\n): SlackStreamChunk[] {\n switch (chunk.type) {\n case \"text\":\n return splitText(chunk.text, SLACK_APPEND_LIMIT).map((text) => ({\n type: \"markdown_text\",\n text\n }));\n case \"tool\":\n return [\n {\n type: \"task_update\",\n id: clamp(chunk.id ?? chunk.name, SLACK_TASK_FIELD_LIMIT),\n title: clamp(chunk.title ?? chunk.name, SLACK_TASK_FIELD_LIMIT),\n status: SLACK_TASK_STATUS[chunk.status],\n ...(chunk.detail !== undefined && {\n details: clamp(chunk.detail, SLACK_TASK_FIELD_LIMIT)\n })\n }\n ];\n case \"source\":\n sources.push(chunk);\n return [];\n case \"reasoning\":\n return [];\n }\n}\n\nfunction sourcesBlock(\n sources: readonly { url: string; title?: string }[]\n): Record<string, unknown> {\n const text = sources\n .map(\n (source) =>\n `<${escapeLinkUrl(source.url)}|${escapeLinkLabel(source.title ?? source.url)}>`\n )\n .join(\" \\u00b7 \");\n return {\n type: \"context\",\n elements: [{ type: \"mrkdwn\", text: clamp(text, SLACK_CONTEXT_LIMIT) }]\n };\n}\n\n/** Create a configured Slack Web API Channel. */\nexport function slack(\n options: SlackChannelOptions\n): Channel<SlackIngressPayload> {\n if (!options.botToken.trim()) {\n throw new Error(\"botToken is required to create a Slack channel\");\n }\n const fetch = options.fetch ?? globalThis.fetch;\n const apiBaseUrl = (options.apiBaseUrl ?? \"https://slack.com/api\").replace(\n /\\/$/,\n \"\"\n );\n const toText = options.toText ?? defaultText;\n const streamIntervalMs =\n options.streamIntervalMs ?? DEFAULT_STREAM_INTERVAL_MS;\n if (!Number.isInteger(streamIntervalMs) || streamIntervalMs < 0) {\n throw new Error(\"streamIntervalMs must be a non-negative integer\");\n }\n const ingress = options.webhook ? slackWebhook(options.webhook) : undefined;\n\n async function resolveTarget(\n target: SlackTarget\n ): Promise<SlackDestination | DeliveryResult> {\n if (!(\"userId\" in target)) return target;\n\n let response: Response;\n try {\n response = await fetch(`${apiBaseUrl}/conversations.open`, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${options.botToken}`,\n \"content-type\": \"application/json; charset=utf-8\"\n },\n body: JSON.stringify({ users: target.userId })\n });\n } catch {\n return failed(\n \"SLACK_CONTACT_RESOLUTION_FAILED\",\n \"Slack could not resolve a direct-message destination\",\n true\n );\n }\n\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n return failed(\n \"SLACK_CONTACT_RESOLUTION_FAILED\",\n \"Slack returned an invalid direct-message destination\",\n response.status === 429 || response.status >= 500\n );\n }\n const result = isRecord(payload)\n ? (payload as SlackOpenConversationResponse)\n : undefined;\n if (result?.ok === true && typeof result.channel?.id === \"string\") {\n return {\n channelId: result.channel.id,\n recipientUserId: target.userId,\n recipientTeamId: target.teamId\n };\n }\n const error = typeof result?.error === \"string\" ? result.error : \"unknown\";\n return failed(\n slackErrorCode(error),\n `Slack could not open a direct message: ${error}`,\n response.status === 429 ||\n response.status >= 500 ||\n error === \"ratelimited\"\n );\n }\n\n async function postMessage(\n destination: ChannelMessageSurface,\n text: string,\n blocks?: readonly Record<string, unknown>[]\n ): Promise<DeliveryResult> {\n const unresolvedTarget = slackTarget(destination);\n if (!unresolvedTarget) {\n return failed(\n \"SLACK_SURFACE_INVALID\",\n `Slack cannot parse the address for Channel \"${destination.channelKey}\"`,\n false\n );\n }\n\n const target = await resolveTarget(unresolvedTarget);\n if (\"status\" in target) return target;\n\n const sent = await callSlack(\"chat.postMessage\", {\n channel: target.channelId,\n text,\n mrkdwn: true,\n ...(target.threadTs && { thread_ts: target.threadTs }),\n ...(blocks && { blocks })\n });\n return \"status\" in sent\n ? sent\n : {\n status: \"delivered\",\n reference: outboundReference(sent.channelId, sent.ts)\n };\n }\n\n /**\n * One transport path for every Slack method this Channel calls.\n *\n * Each of them answers with the conversation and timestamp on success, and\n * every failure mode is classified the same way, so posting and streaming\n * cannot drift apart.\n */\n async function callSlack(\n method: string,\n body: Record<string, unknown>\n ): Promise<\n | { channelId: string; ts: string }\n | Exclude<DeliveryResult, { status: \"delivered\" }>\n > {\n let response: Response;\n try {\n response = await fetch(`${apiBaseUrl}/${method}`, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${options.botToken}`,\n \"content-type\": \"application/json; charset=utf-8\"\n },\n body: JSON.stringify(body)\n });\n } catch {\n return uncertain(\n \"SLACK_DELIVERY_ERROR\",\n \"Slack delivery failed with an unknown outcome\"\n );\n }\n\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n payload = undefined;\n }\n const apiResponse = asApiResponse(payload);\n if (\n apiResponse?.ok === true &&\n typeof apiResponse.channel === \"string\" &&\n typeof apiResponse.ts === \"string\"\n ) {\n return { channelId: apiResponse.channel, ts: apiResponse.ts };\n }\n return apiResponse?.ok === true\n ? uncertain(\n \"SLACK_DELIVERY_ERROR\",\n \"Slack returned an invalid delivery response\"\n )\n : classifyApiFailure(response, apiResponse ?? {});\n }\n\n async function streamMessage(\n destination: ChannelMessageSurface,\n chunks: ReadableStream<ChannelChunk>,\n streamOptions: ChannelStreamOptions\n ): Promise<DeliveryResult> {\n const unresolvedTarget = slackTarget(destination);\n if (!unresolvedTarget) {\n await chunks.cancel().catch(() => {});\n return failed(\n \"SLACK_SURFACE_INVALID\",\n `Slack cannot parse the address for Channel \"${destination.channelKey}\"`,\n false\n );\n }\n\n // Slack only permits native channel streaming in a thread. Preserve the\n // simpler top-level application API by collecting and posting one ordinary\n // message; direct-message targets still use native streaming.\n if (\n !(\"userId\" in unresolvedTarget) &&\n !unresolvedTarget.threadTs &&\n !unresolvedTarget.recipientUserId\n ) {\n const collected = await collectText(chunks);\n if (collected.interrupted && collected.text.length === 0) {\n return failed(\n \"SLACK_STREAM_INTERRUPTED\",\n \"The stream ended before producing any content to deliver\",\n false\n );\n }\n const delivered = await postMessage(\n destination,\n toText({\n ...(streamOptions.title && { title: streamOptions.title }),\n markdown: collected.text\n })\n );\n if (!collected.interrupted || delivered.status !== \"delivered\") {\n return delivered;\n }\n return uncertain(\n \"SLACK_STREAM_INTERRUPTED\",\n \"An incomplete answer was delivered because the stream ended early\",\n delivered.reference\n );\n }\n\n const target = await resolveTarget(unresolvedTarget);\n if (\"status\" in target) {\n await chunks.cancel().catch(() => {});\n return target;\n }\n\n const started = await callSlack(\"chat.startStream\", {\n channel: target.channelId,\n ...(target.threadTs && { thread_ts: target.threadTs }),\n ...(target.recipientUserId && {\n recipient_user_id: target.recipientUserId,\n recipient_team_id: target.recipientTeamId\n }),\n // Every call in a stream must use the mode the stream was opened in,\n // so the title goes in a chunk rather than `markdown_text`. Opening\n // with `markdown_text` makes Slack reject each later append with\n // `streaming_mode_mismatch`, losing the whole answer.\n ...(streamOptions.title && {\n chunks: splitText(`${streamOptions.title}\\n\\n`, SLACK_APPEND_LIMIT).map(\n (text) => ({ type: \"markdown_text\", text })\n )\n })\n });\n if (\"status\" in started) {\n await chunks.cancel().catch(() => {});\n return started;\n }\n\n const reference = outboundReference(started.channelId, started.ts);\n const sources: { url: string; title?: string }[] = [];\n const shouldFlush = createPacer(streamIntervalMs);\n let pending: SlackStreamChunk[] = [];\n let appendFailure:\n | Exclude<DeliveryResult, { status: \"delivered\" }>\n | undefined;\n\n return consumeChunks(chunks, {\n async onChunk(chunk) {\n pending.push(...toStreamChunks(chunk, sources));\n if (pending.length === 0 || !shouldFlush()) return;\n const appended = await callSlack(\"chat.appendStream\", {\n channel: started.channelId,\n ts: started.ts,\n chunks: pending\n });\n pending = [];\n if (\"status\" in appended) {\n appendFailure = appended;\n // Stop reading, but still stop the stream: Slack leaves a message\n // stuck in its streaming state otherwise.\n throw new Error(appended.error.message);\n }\n },\n async onFinish(outcome) {\n // Whatever the pacer withheld rides along on the terminal call, so an\n // interrupted answer keeps its tail without an extra round trip.\n const stopped = await callSlack(\"chat.stopStream\", {\n channel: started.channelId,\n ts: started.ts,\n ...(pending.length > 0 && !appendFailure && { chunks: pending }),\n ...(sources.length > 0 && { blocks: [sourcesBlock(sources)] })\n });\n if (\"status\" in stopped) {\n return uncertain(\n stopped.error.code,\n `Slack could not stop the stream: ${stopped.error.message}`,\n reference\n );\n }\n if (appendFailure) {\n return uncertain(\n appendFailure.error.code,\n appendFailure.error.message,\n reference\n );\n }\n if (outcome.interrupted) {\n return uncertain(\n \"SLACK_STREAM_INTERRUPTED\",\n \"The answer ended early, so the Slack message is incomplete\",\n reference\n );\n }\n return { status: \"delivered\", reference };\n }\n });\n }\n\n return {\n ...(options.route && { route: options.route }),\n ...(ingress && { ingress }),\n contactSurface(identity: ChannelIdentity) {\n if (identity.scope === undefined) return null;\n return {\n version: 1,\n address: { teamId: identity.scope, userId: identity.subject },\n label: `Slack · user ${identity.subject}`\n };\n },\n deliver(destination, message) {\n return postMessage(destination, toText(message));\n },\n stream(destination, chunks, streamOptions) {\n return streamMessage(destination, chunks, streamOptions);\n },\n requestApproval(destination, { interactionId, request }) {\n if (interactionId.length === 0) {\n return Promise.resolve(\n failed(\n \"SLACK_INTERACTION_ID_REQUIRED\",\n \"Slack approval requests require a non-empty interaction id\",\n false\n )\n );\n }\n const text = approvalText(request);\n const approveValue = approvalValue(interactionId, \"approve\");\n const rejectValue = approvalValue(interactionId, \"reject\");\n if (\n text.length > 3000 ||\n approveValue.length > 2000 ||\n rejectValue.length > 2000\n ) {\n return Promise.resolve(\n failed(\n \"SLACK_APPROVAL_TOO_LONG\",\n \"Slack approval content exceeds Block Kit limits\",\n false\n )\n );\n }\n return postMessage(destination, text, [\n {\n type: \"section\",\n text: { type: \"mrkdwn\", text }\n },\n {\n type: \"actions\",\n elements: [\n {\n type: \"button\",\n action_id: APPROVE_ACTION_ID,\n text: { type: \"plain_text\", text: \"Approve\" },\n style: \"primary\",\n value: approveValue\n },\n {\n type: \"button\",\n action_id: REJECT_ACTION_ID,\n text: { type: \"plain_text\", text: \"Reject\" },\n style: \"danger\",\n value: rejectValue\n }\n ]\n }\n ]);\n }\n };\n}\n"],"mappings":"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"}
@@ -1,39 +0,0 @@
1
- import { F as ChannelMessageSurface } from "../channel-Bnm4S7T2.js";
2
- import { n as ChannelHost } from "../index-BB0kqhIz.js";
3
-
4
- //#region src/channels/tanstack-ai.d.ts
5
- type CreateSendMessageToolOptions = {
6
- /** TanStack AI tools carry their name in the definition. */ name: string;
7
- description?: string;
8
- needsApproval?: boolean;
9
- metadata?: Record<string, unknown>;
10
- };
11
- /** Adapt one Host-resolved surface to a TanStack AI server tool. */
12
- declare function createSendMessageTool(
13
- host: ChannelHost,
14
- surface: ChannelMessageSurface,
15
- options: CreateSendMessageToolOptions
16
- ): import("@tanstack/ai").ServerTool<
17
- {
18
- type: "object";
19
- properties: {
20
- title: {
21
- type: "string";
22
- description: string;
23
- };
24
- markdown: {
25
- type: "string";
26
- minLength: number;
27
- description: string;
28
- };
29
- };
30
- required: string[];
31
- additionalProperties: false;
32
- },
33
- import("@tanstack/ai").SchemaInput,
34
- string,
35
- unknown
36
- >;
37
- //#endregion
38
- export { CreateSendMessageToolOptions, createSendMessageTool };
39
- //# sourceMappingURL=tanstack-ai.d.ts.map
@@ -1,17 +0,0 @@
1
- import { n as parseChannelMessage, t as channelMessageJsonSchema } from "../tool-schema-CBjGPrsQ.js";
2
- import { toolDefinition } from "@tanstack/ai";
3
- //#region src/channels/tanstack-ai.ts
4
- /** Adapt one Host-resolved surface to a TanStack AI server tool. */
5
- function createSendMessageTool(host, surface, options) {
6
- return toolDefinition({
7
- name: options.name,
8
- description: options.description ?? "Deliver a message through this Channel",
9
- inputSchema: channelMessageJsonSchema,
10
- ...options.needsApproval && { needsApproval: true },
11
- metadata: options.metadata
12
- }).server(async (value) => host.deliver(surface, parseChannelMessage(value)));
13
- }
14
- //#endregion
15
- export { createSendMessageTool };
16
-
17
- //# sourceMappingURL=tanstack-ai.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tanstack-ai.js","names":[],"sources":["../../src/channels/tanstack-ai.ts"],"sourcesContent":["import { toolDefinition } from \"@tanstack/ai\";\nimport type { ChannelHost } from \"./host\";\nimport type { ChannelMessageSurface } from \"./surface\";\nimport { channelMessageJsonSchema, parseChannelMessage } from \"./tool-schema\";\n\nexport type CreateSendMessageToolOptions = {\n /** TanStack AI tools carry their name in the definition. */\n name: string;\n description?: string;\n needsApproval?: boolean;\n metadata?: Record<string, unknown>;\n};\n\n/** Adapt one Host-resolved surface to a TanStack AI server tool. */\nexport function createSendMessageTool(\n host: ChannelHost,\n surface: ChannelMessageSurface,\n options: CreateSendMessageToolOptions\n) {\n const definition = toolDefinition({\n name: options.name,\n description:\n options.description ?? \"Deliver a message through this Channel\",\n inputSchema: channelMessageJsonSchema,\n ...(options.needsApproval && { needsApproval: true }),\n metadata: options.metadata\n });\n\n return definition.server(async (value) =>\n host.deliver(surface, parseChannelMessage(value))\n );\n}\n"],"mappings":";;;;AAcA,SAAgB,sBACd,MACA,SACA,SACA;CAUA,OATmB,eAAe;EAChC,MAAM,QAAQ;EACd,aACE,QAAQ,eAAe;EACzB,aAAa;EACb,GAAI,QAAQ,iBAAiB,EAAE,eAAe,KAAK;EACnD,UAAU,QAAQ;CACpB,CAEgB,CAAC,CAAC,OAAO,OAAO,UAC9B,KAAK,QAAQ,SAAS,oBAAoB,KAAK,CAAC,CAClD;AACF"}
@@ -1,427 +0,0 @@
1
- import { t as isChannelMessageSurface } from "../surface-bZZJqBka.js";
2
- import { a as isRecord, i as encodeUtf8, n as defaultText, o as renderInput, r as emptyIngressResponse, s as uncertain } from "../internal-CYlgHl1l.js";
3
- import { i as createPacer, r as consumeChunks, t as matchesPath } from "../ingress-BfetZbMO.js";
4
- //#region src/channels/adapters/telegram.ts
5
- const TELEGRAM_MESSAGE_LIMIT = 4096;
6
- const DEFAULT_STREAM_INTERVAL_MS = 500;
7
- const DEFAULT_TELEGRAM_WEBHOOK_PATH = "/webhooks/telegram";
8
- const APPROVAL_INSTRUCTIONS = "Reply YES to approve or NO to reject.";
9
- const INTERACTION_FOOTER = /\n\nReply YES to approve or NO to reject\.\n\n\[channel-interaction:v1:([A-Za-z0-9_-]+)\]$/;
10
- function channelIdentity(chatId) {
11
- return `telegram:chat:${chatId}`;
12
- }
13
- function userIdentity(userId) {
14
- return `telegram:user:${userId}`;
15
- }
16
- function messageIdentity(chatId, messageId) {
17
- return `${channelIdentity(chatId)}:message:${messageId}`;
18
- }
19
- function updateIdentity(chatId, updateId) {
20
- return `${channelIdentity(chatId)}:update:${updateId}`;
21
- }
22
- function encodeInteractionId(interactionId) {
23
- const bytes = encodeUtf8(interactionId);
24
- let binary = "";
25
- for (const byte of bytes) binary += String.fromCharCode(byte);
26
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
27
- }
28
- function decodeInteractionId(value) {
29
- try {
30
- const base64 = value.replace(/-/g, "+").replace(/_/g, "/");
31
- const padding = "=".repeat((4 - base64.length % 4) % 4);
32
- const binary = atob(base64 + padding);
33
- const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
34
- return new TextDecoder("utf-8", { fatal: true }).decode(bytes) || void 0;
35
- } catch {
36
- return;
37
- }
38
- }
39
- function approvalText(interactionId, request) {
40
- return [
41
- ...request.title ? [request.title] : [],
42
- request.summary,
43
- `Input:\n${renderInput(request.input)}`,
44
- APPROVAL_INSTRUCTIONS,
45
- `[channel-interaction:v1:${encodeInteractionId(interactionId)}]`
46
- ].join("\n\n");
47
- }
48
- function asApiResponse(value) {
49
- return value !== null && typeof value === "object" ? value : void 0;
50
- }
51
- function classifyResponse(response, payload) {
52
- if (payload.ok === true) {
53
- const result = asApiResponse(payload.result);
54
- if (typeof result?.message_id === "number") return {
55
- status: "delivered",
56
- reference: String(result.message_id)
57
- };
58
- return uncertain("TELEGRAM_DELIVERY_ERROR", "Telegram returned an invalid delivery response");
59
- }
60
- if (payload.ok === false) {
61
- const errorCode = typeof payload.error_code === "number" ? payload.error_code : response.status;
62
- const message = typeof payload.description === "string" ? payload.description : "Telegram rejected the message";
63
- const error = {
64
- code: `TELEGRAM_API_ERROR_${errorCode}`,
65
- message
66
- };
67
- if (errorCode >= 500) return {
68
- status: "uncertain",
69
- error
70
- };
71
- return {
72
- status: "failed",
73
- retryable: errorCode === 429,
74
- error
75
- };
76
- }
77
- return uncertain("TELEGRAM_DELIVERY_ERROR", "Telegram returned an invalid delivery response");
78
- }
79
- function asTelegramMessage(value) {
80
- return value !== null && typeof value === "object" ? value : void 0;
81
- }
82
- function telegramActor(message) {
83
- const sender = message.from;
84
- if (sender) {
85
- const fullName = [sender.first_name, sender.last_name].filter(Boolean).join(" ");
86
- return {
87
- id: userIdentity(sender.id),
88
- identity: { subject: `user:${sender.id}` },
89
- ...sender.username && { username: sender.username },
90
- ...fullName && { fullName },
91
- isBot: sender.is_bot
92
- };
93
- }
94
- const senderChat = message.sender_chat;
95
- if (!senderChat) return void 0;
96
- const fullName = senderChat.title ?? [senderChat.first_name, senderChat.last_name].filter(Boolean).join(" ");
97
- return {
98
- id: channelIdentity(senderChat.id),
99
- identity: { subject: `chat:${senderChat.id}` },
100
- ...senderChat.username && { username: senderChat.username },
101
- ...fullName && { fullName },
102
- isBot: "unknown"
103
- };
104
- }
105
- function chatLabel(chat) {
106
- return `Telegram · ${(chat.title ?? (chat.username ? `@${chat.username}` : void 0) ?? [chat.first_name, chat.last_name].filter(Boolean).join(" ") ?? String(chat.id)) || chat.id}`;
107
- }
108
- function eventContext(message, eventId, botUserId) {
109
- const actor = telegramActor(message);
110
- const channelId = channelIdentity(message.chat.id);
111
- return {
112
- eventId,
113
- thread: {
114
- id: typeof message.message_thread_id === "number" ? `${channelId}:topic:${message.message_thread_id}` : channelId,
115
- isDirectMessage: message.chat.type === "private" ? true : message.chat.type === "group" || message.chat.type === "supergroup" || message.chat.type === "channel" ? false : "unknown"
116
- },
117
- replySurface: {
118
- version: 1,
119
- address: {
120
- chatId: String(message.chat.id),
121
- ...botUserId && { botUserId },
122
- ...typeof message.message_thread_id === "number" && { messageThreadId: message.message_thread_id },
123
- replyToMessageId: message.message_id
124
- },
125
- label: chatLabel(message.chat)
126
- },
127
- ...actor && { actor }
128
- };
129
- }
130
- function telegramTimestamp(value) {
131
- if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
132
- const date = /* @__PURE__ */ new Date(value * 1e3);
133
- return Number.isFinite(date.getTime()) ? date.toISOString() : void 0;
134
- }
135
- function normalizedMessage(message, eventId, edited, botUserId) {
136
- const reply = asTelegramMessage(message.reply_to_message);
137
- const sentAt = telegramTimestamp(message.date);
138
- const editedAt = telegramTimestamp(message.edit_date);
139
- return {
140
- type: "message",
141
- ...eventContext(message, eventId, botUserId),
142
- message: {
143
- id: messageIdentity(message.chat.id, message.message_id),
144
- text: message.text,
145
- ...typeof reply?.message_id === "number" && { reply: {
146
- id: messageIdentity(message.chat.id, reply.message_id),
147
- ...typeof reply.text === "string" && { text: reply.text }
148
- } },
149
- ...(sentAt || edited || editedAt) && { metadata: {
150
- ...sentAt && { sentAt },
151
- ...edited && { edited: true },
152
- ...editedAt && { editedAt }
153
- } }
154
- }
155
- };
156
- }
157
- function approvalResponse(message, eventId, botUserId) {
158
- if (botUserId === void 0) return void 0;
159
- if (message.text !== "YES" && message.text !== "NO") return void 0;
160
- const reply = asTelegramMessage(message.reply_to_message);
161
- if (typeof reply?.message_id !== "number") return void 0;
162
- const author = reply.from;
163
- if (author?.is_bot !== true || author.id !== botUserId) return void 0;
164
- const encodedInteractionId = typeof reply.text === "string" ? reply.text.match(INTERACTION_FOOTER)?.[1] : void 0;
165
- const interactionId = encodedInteractionId ? decodeInteractionId(encodedInteractionId) : void 0;
166
- if (!interactionId) return void 0;
167
- return {
168
- type: "approval-response",
169
- ...eventContext(message, eventId, botUserId),
170
- interactionId,
171
- decision: message.text === "YES" ? "approve" : "reject",
172
- reference: messageIdentity(message.chat.id, message.message_id)
173
- };
174
- }
175
- function inboundEvent(update, botUserId) {
176
- const edited = update.edited_message !== void 0;
177
- const message = asTelegramMessage(edited ? update.edited_message : update.message);
178
- if (typeof update.update_id !== "number" || typeof message?.message_id !== "number" || typeof message.text !== "string" || typeof message.chat?.id !== "number") return;
179
- const eventId = updateIdentity(message.chat.id, update.update_id);
180
- return approvalResponse(message, eventId, botUserId) ?? normalizedMessage(message, eventId, edited, botUserId);
181
- }
182
- /** Recover this bot's numeric user id from its BotFather token prefix. */
183
- function telegramBotUserId(botToken) {
184
- const prefix = botToken.match(/^(\d+):/)?.[1];
185
- if (!prefix) return void 0;
186
- const botUserId = Number(prefix);
187
- return Number.isSafeInteger(botUserId) && botUserId > 0 ? botUserId : void 0;
188
- }
189
- /** Create dependency-free Telegram webhook ingress for a destination chat. */
190
- function telegramWebhook(options) {
191
- if (!options.secretToken.trim()) throw new Error("secretToken is required to create Telegram webhook ingress");
192
- if (options.botUserId !== void 0 && (!Number.isSafeInteger(options.botUserId) || options.botUserId <= 0)) throw new Error("botUserId must be a positive Telegram user id");
193
- const botUserId = options.botUserId;
194
- const path = options.path ?? DEFAULT_TELEGRAM_WEBHOOK_PATH;
195
- return { async receive(request) {
196
- if (!matchesPath(request, path)) return null;
197
- if (request.method !== "POST") return emptyIngressResponse(405);
198
- if (request.headers.get("x-telegram-bot-api-secret-token") !== options.secretToken) return emptyIngressResponse(401);
199
- let update;
200
- try {
201
- update = await request.json();
202
- } catch {
203
- return emptyIngressResponse(400);
204
- }
205
- if (update === null || typeof update !== "object") return emptyIngressResponse(400);
206
- const raw = update;
207
- const event = inboundEvent(raw, botUserId);
208
- return {
209
- events: event ? [{
210
- event,
211
- raw
212
- }] : [],
213
- response: new Response(null, { status: 200 })
214
- };
215
- } };
216
- }
217
- function telegramSurface(surface) {
218
- if (!isChannelMessageSurface(surface) || surface.version !== 1 || !isRecord(surface.address) || typeof surface.address.chatId !== "string" || surface.address.chatId.length === 0 || surface.address.botUserId !== void 0 && (!Number.isSafeInteger(surface.address.botUserId) || Number(surface.address.botUserId) <= 0)) return;
219
- if (surface.address.messageThreadId !== void 0 && !Number.isSafeInteger(surface.address.messageThreadId) || surface.address.replyToMessageId !== void 0 && !Number.isSafeInteger(surface.address.replyToMessageId)) return;
220
- return surface;
221
- }
222
- /**
223
- * Whether a chat can show an animated draft.
224
- *
225
- * `sendMessageDraft` is private chats only, and Telegram gives private chats
226
- * positive ids. Anywhere else the answer is simply sent once at the end.
227
- */
228
- function supportsDraft(chatId) {
229
- const numeric = Number(chatId);
230
- return Number.isSafeInteger(numeric) && numeric > 0;
231
- }
232
- /** A non-zero draft identifier; reusing one animates between snapshots. */
233
- function newDraftId() {
234
- const [random] = crypto.getRandomValues(/* @__PURE__ */ new Uint32Array(1));
235
- return (random ?? 1) % 2147483647 + 1;
236
- }
237
- function splitText(text, limit) {
238
- const pieces = [];
239
- let piece = "";
240
- let length = 0;
241
- for (const character of text) {
242
- if (length === limit) {
243
- pieces.push(piece);
244
- piece = "";
245
- length = 0;
246
- }
247
- piece += character;
248
- length += 1;
249
- }
250
- if (piece.length > 0) pieces.push(piece);
251
- return pieces;
252
- }
253
- function textLength(text) {
254
- return [...text].length;
255
- }
256
- /** Create a configured Telegram Bot API Channel. */
257
- function telegram(options) {
258
- if (!options.botToken.trim()) throw new Error("botToken is required to create a Telegram channel");
259
- const fetch = options.fetch ?? globalThis.fetch;
260
- const apiBaseUrl = (options.apiBaseUrl ?? "https://api.telegram.org").replace(/\/$/, "");
261
- const maxLength = options.maxLength ?? TELEGRAM_MESSAGE_LIMIT;
262
- if (!Number.isInteger(maxLength) || maxLength < 1 || maxLength > TELEGRAM_MESSAGE_LIMIT) throw new Error(`maxLength must be an integer between 1 and ${TELEGRAM_MESSAGE_LIMIT}`);
263
- const toText = options.toText ?? defaultText;
264
- const streamIntervalMs = options.streamIntervalMs ?? DEFAULT_STREAM_INTERVAL_MS;
265
- if (!Number.isInteger(streamIntervalMs) || streamIntervalMs < 0) throw new Error("streamIntervalMs must be a non-negative integer");
266
- const botUserId = telegramBotUserId(options.botToken);
267
- const ingress = options.webhook ? telegramWebhook({
268
- ...options.webhook,
269
- botUserId
270
- }) : void 0;
271
- async function send(destinationValue, text, parseMode) {
272
- const destination = telegramSurface(destinationValue);
273
- if (!destination || botUserId !== void 0 && destination.address.botUserId !== void 0 && destination.address.botUserId !== botUserId) return {
274
- status: "failed",
275
- retryable: false,
276
- error: {
277
- code: "TELEGRAM_SURFACE_INVALID",
278
- message: `Telegram cannot parse the address for Channel "${destinationValue.channelKey}"`
279
- }
280
- };
281
- if (textLength(text) > maxLength) return {
282
- status: "failed",
283
- retryable: false,
284
- error: {
285
- code: "TELEGRAM_MESSAGE_TOO_LONG",
286
- message: `Telegram message exceeds the configured ${maxLength}-character limit`
287
- }
288
- };
289
- let response;
290
- try {
291
- response = await fetch(`${apiBaseUrl}/bot${options.botToken}/sendMessage`, {
292
- method: "POST",
293
- headers: { "content-type": "application/json" },
294
- body: JSON.stringify({
295
- chat_id: destination.address.chatId,
296
- text,
297
- ...destination.address.messageThreadId !== void 0 && { message_thread_id: destination.address.messageThreadId },
298
- ...destination.address.replyToMessageId !== void 0 && { reply_parameters: { message_id: destination.address.replyToMessageId } },
299
- ...parseMode && { parse_mode: parseMode }
300
- })
301
- });
302
- } catch {
303
- return uncertain("TELEGRAM_DELIVERY_ERROR", "Telegram delivery failed with an unknown outcome");
304
- }
305
- let payload;
306
- try {
307
- payload = await response.json();
308
- } catch {
309
- return uncertain("TELEGRAM_DELIVERY_ERROR", "Telegram returned an invalid delivery response");
310
- }
311
- const apiResponse = asApiResponse(payload);
312
- return apiResponse ? classifyResponse(response, apiResponse) : uncertain("TELEGRAM_DELIVERY_ERROR", "Telegram returned an invalid delivery response");
313
- }
314
- /**
315
- * Show one ephemeral preview. Failures are swallowed on purpose: a draft is
316
- * a 30-second animation, and losing one must never cost the real message.
317
- *
318
- * The configured parse mode is deliberately not applied. A partial answer
319
- * routinely holds unbalanced markup, which Telegram rejects outright.
320
- */
321
- async function sendDraft(destination, draftId, text) {
322
- try {
323
- return asApiResponse(await (await fetch(`${apiBaseUrl}/bot${options.botToken}/sendMessageDraft`, {
324
- method: "POST",
325
- headers: { "content-type": "application/json" },
326
- body: JSON.stringify({
327
- chat_id: Number(destination.address.chatId),
328
- draft_id: draftId,
329
- text,
330
- ...destination.address.messageThreadId !== void 0 && { message_thread_id: destination.address.messageThreadId }
331
- })
332
- })).json())?.ok === true;
333
- } catch {
334
- return false;
335
- }
336
- }
337
- /** Persist the answer, splitting it when it outgrows one Telegram message. */
338
- async function sendComplete(destination, text) {
339
- const [head, ...tail] = splitText(text, maxLength);
340
- const parseMode = tail.length === 0 ? options.parseMode : void 0;
341
- const first = await send(destination, head, parseMode);
342
- if (first.status !== "delivered") return first;
343
- for (const piece of tail) if ((await send(destination, piece, parseMode)).status !== "delivered") return uncertain("TELEGRAM_STREAM_PARTIAL", "Telegram accepted only part of a split answer", first.reference);
344
- return first;
345
- }
346
- async function streamMessage(destinationValue, chunks, streamOptions) {
347
- const destination = telegramSurface(destinationValue);
348
- if (!destination || botUserId !== void 0 && destination.address.botUserId !== void 0 && destination.address.botUserId !== botUserId) {
349
- await chunks.cancel().catch(() => {});
350
- return {
351
- status: "failed",
352
- retryable: false,
353
- error: {
354
- code: "TELEGRAM_SURFACE_INVALID",
355
- message: `Telegram cannot parse the address for Channel "${destinationValue.channelKey}"`
356
- }
357
- };
358
- }
359
- const draftId = supportsDraft(destination.address.chatId) ? newDraftId() : void 0;
360
- const prefix = streamOptions.title ? `${streamOptions.title}\n\n` : "";
361
- const shouldPreview = createPacer(streamIntervalMs);
362
- let answer = "";
363
- let draftsStopped = draftId === void 0;
364
- return consumeChunks(chunks, {
365
- async onChunk(chunk) {
366
- if (chunk.type !== "text" || chunk.text.length === 0) return;
367
- answer += chunk.text;
368
- if (draftsStopped || !shouldPreview()) return;
369
- if (!await sendDraft(destination, draftId, splitText(`${prefix}${answer}`, maxLength)[0] ?? "")) draftsStopped = true;
370
- },
371
- async onFinish(outcome) {
372
- if (answer.length === 0) return {
373
- status: "failed",
374
- retryable: false,
375
- error: outcome.interrupted ? {
376
- code: "TELEGRAM_STREAM_INTERRUPTED",
377
- message: "The answer ended before producing any text to send"
378
- } : {
379
- code: "TELEGRAM_STREAM_EMPTY",
380
- message: "The stream carried no text to send"
381
- }
382
- };
383
- const result = await sendComplete(destination, `${prefix}${answer}`);
384
- if (!outcome.interrupted || result.status !== "delivered") return result;
385
- return uncertain("TELEGRAM_STREAM_INTERRUPTED", "An incomplete answer was sent because the stream ended early", result.reference);
386
- }
387
- });
388
- }
389
- return {
390
- ...options.route && { route: options.route },
391
- ...ingress && { ingress },
392
- contactSurface(identity) {
393
- if ((identity.scope ?? "default") !== "default") return null;
394
- const match = identity.subject.match(/^(user|chat):(-?\d+)$/);
395
- if (!match) return null;
396
- return {
397
- version: 1,
398
- address: {
399
- chatId: match[2],
400
- ...botUserId && { botUserId }
401
- },
402
- label: `Telegram · ${match[1]} ${match[2]}`
403
- };
404
- },
405
- deliver(destination, message) {
406
- return send(destination, toText(message), options.parseMode);
407
- },
408
- stream(destination, chunks, streamOptions) {
409
- return streamMessage(destination, chunks, streamOptions);
410
- },
411
- requestApproval(destination, { interactionId, request }) {
412
- if (interactionId.length === 0) return Promise.resolve({
413
- status: "failed",
414
- retryable: false,
415
- error: {
416
- code: "TELEGRAM_INTERACTION_ID_REQUIRED",
417
- message: "Telegram approval requests require a non-empty interaction id"
418
- }
419
- });
420
- return send(destination, approvalText(interactionId, request));
421
- }
422
- };
423
- }
424
- //#endregion
425
- export { telegram, telegramWebhook };
426
-
427
- //# sourceMappingURL=telegram.js.map