agents 0.22.0 → 0.23.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 (186) hide show
  1. package/README.md +20 -1
  2. package/dist/{agent-routing-CnkaHb-v.d.ts → agent-routing-DE5zmCQ8.d.ts} +1038 -394
  3. package/dist/agent-routing.d.ts +1 -1
  4. package/dist/agent-tool-types.d.ts +26 -26
  5. package/dist/{agent-tools-C0XJqWzB.d.ts → agent-tools-DtXMTDGM.d.ts} +7 -7
  6. package/dist/agent-tools.d.ts +21 -21
  7. package/dist/browser/ai.js +4 -1
  8. package/dist/browser/ai.js.map +1 -1
  9. package/dist/browser/tanstack-ai.js.map +1 -1
  10. package/dist/callable-decorator-DP__HhBA.d.ts +72 -0
  11. package/dist/callable-decorator.d.ts +18 -0
  12. package/dist/callable-decorator.js +71 -0
  13. package/dist/callable-decorator.js.map +1 -0
  14. package/dist/capability-BjSKYpzg.js.map +1 -1
  15. package/dist/capability-runner-Be_-PLR1.d.ts +459 -0
  16. package/dist/channel-Bnm4S7T2.d.ts +491 -0
  17. package/dist/channels/ai-sdk.d.ts +46 -0
  18. package/dist/channels/ai-sdk.js +120 -0
  19. package/dist/channels/ai-sdk.js.map +1 -0
  20. package/dist/channels/email.d.ts +95 -0
  21. package/dist/channels/email.js +323 -0
  22. package/dist/channels/email.js.map +1 -0
  23. package/dist/channels/index.d.ts +233 -0
  24. package/dist/channels/index.js +608 -0
  25. package/dist/channels/index.js.map +1 -0
  26. package/dist/channels/slack.d.ts +140 -0
  27. package/dist/channels/slack.js +614 -0
  28. package/dist/channels/slack.js.map +1 -0
  29. package/dist/channels/tanstack-ai.d.ts +39 -0
  30. package/dist/channels/tanstack-ai.js +17 -0
  31. package/dist/channels/tanstack-ai.js.map +1 -0
  32. package/dist/channels/telegram.d.ts +106 -0
  33. package/dist/channels/telegram.js +427 -0
  34. package/dist/channels/telegram.js.map +1 -0
  35. package/dist/channels/voice.d.ts +45 -0
  36. package/dist/channels/voice.js +122 -0
  37. package/dist/channels/voice.js.map +1 -0
  38. package/dist/chat/index.d.ts +2325 -2019
  39. package/dist/chat/index.js +888 -518
  40. package/dist/chat/index.js.map +1 -1
  41. package/dist/chat-sdk/index.d.ts +7 -7
  42. package/dist/chat-sdk/index.js +1 -1
  43. package/dist/client.d.ts +1 -1
  44. package/dist/context/index.d.ts +216 -0
  45. package/dist/context/index.js +454 -0
  46. package/dist/context/index.js.map +1 -0
  47. package/dist/{current-agent-CuMErtly.d.ts → current-agent-Da_C9a3b.d.ts} +90 -107
  48. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  49. package/dist/{diagnostics-CaBjfz4J.js → diagnostics-BzvaX2UT.js} +5 -1
  50. package/dist/diagnostics-BzvaX2UT.js.map +1 -0
  51. package/dist/diagnostics-C4jcz3VK.js +360 -0
  52. package/dist/diagnostics-C4jcz3VK.js.map +1 -0
  53. package/dist/index-BB0kqhIz.d.ts +101 -0
  54. package/dist/index-BVVgDSdq.d.ts +1 -0
  55. package/dist/index-XDkuQ7zm.d.ts +89 -0
  56. package/dist/{index-DcSAZKsB.d.ts → index-YSKgfgg9.d.ts} +3 -1
  57. package/dist/index.d.ts +91 -82
  58. package/dist/index.js +3 -2
  59. package/dist/ingress-BfetZbMO.js +83 -0
  60. package/dist/ingress-BfetZbMO.js.map +1 -0
  61. package/dist/internal-CYlgHl1l.js +59 -0
  62. package/dist/internal-CYlgHl1l.js.map +1 -0
  63. package/dist/internal_context.d.ts +1 -1
  64. package/dist/lifecycle/index.d.ts +35 -17
  65. package/dist/lifecycle/index.js +1 -1
  66. package/dist/lifecycle-CMRGjZdw.js +1299 -0
  67. package/dist/lifecycle-CMRGjZdw.js.map +1 -0
  68. package/dist/mcp/client/index.d.ts +20 -20
  69. package/dist/mcp/index.d.ts +35 -35
  70. package/dist/mcp/index.js +1 -1
  71. package/dist/observability/index.d.ts +1 -1
  72. package/dist/observability/index.js +1 -1
  73. package/dist/react.d.ts +4 -4
  74. package/dist/{retries-CAvxtG9d.d.ts → retries-D9Ds-1lz.d.ts} +17 -6
  75. package/dist/retries.d.ts +8 -6
  76. package/dist/retries.js +13 -1
  77. package/dist/retries.js.map +1 -1
  78. package/dist/routing/index.d.ts +137 -0
  79. package/dist/routing/index.js +244 -0
  80. package/dist/routing/index.js.map +1 -0
  81. package/dist/sanitize-D9TujEK8.js +79 -0
  82. package/dist/sanitize-D9TujEK8.js.map +1 -0
  83. package/dist/scheduler-DD9NdYbF.js +665 -0
  84. package/dist/scheduler-DD9NdYbF.js.map +1 -0
  85. package/dist/{scheduler-DQoTGoAW.d.ts → scheduler-Dwh85ZGl.d.ts} +21 -22
  86. package/dist/schedules/index.d.ts +1 -1
  87. package/dist/schedules/index.js +1 -1
  88. package/dist/sentence-chunker-BAidJ4DA.d.ts +68 -0
  89. package/dist/serializable.d.ts +1 -1
  90. package/dist/sessions/index.d.ts +441 -0
  91. package/dist/sessions/index.js +2063 -0
  92. package/dist/sessions/index.js.map +1 -0
  93. package/dist/skills/index.d.ts +99 -0
  94. package/dist/skills/index.js +254 -5
  95. package/dist/skills/index.js.map +1 -1
  96. package/dist/{src-5W6JNKVb.js → src-DlSHshb2.js} +1460 -1110
  97. package/dist/src-DlSHshb2.js.map +1 -0
  98. package/dist/streams/index.d.ts +120 -0
  99. package/dist/streams/index.js +107 -0
  100. package/dist/streams/index.js.map +1 -0
  101. package/dist/streams-D6tJ0NN9.d.ts +370 -0
  102. package/dist/streams-DZKgAj9b.js +709 -0
  103. package/dist/streams-DZKgAj9b.js.map +1 -0
  104. package/dist/sub-routing.d.ts +12 -12
  105. package/dist/surface-bZZJqBka.js +17 -0
  106. package/dist/surface-bZZJqBka.js.map +1 -0
  107. package/dist/tasks/index.d.ts +64 -0
  108. package/dist/tasks/index.js +2 -0
  109. package/dist/tasks-BRJ5zgya.d.ts +517 -0
  110. package/dist/tasks-ylZgBjhj.js +1656 -0
  111. package/dist/tasks-ylZgBjhj.js.map +1 -0
  112. package/dist/text-segment-joiner-BtAFQSA_.js +57 -0
  113. package/dist/text-segment-joiner-BtAFQSA_.js.map +1 -0
  114. package/dist/text-stream-CpdiKrJB.js +272 -0
  115. package/dist/text-stream-CpdiKrJB.js.map +1 -0
  116. package/dist/tokens-nHAKcN6M.js +52 -0
  117. package/dist/tokens-nHAKcN6M.js.map +1 -0
  118. package/dist/tool-schema-CBjGPrsQ.js +31 -0
  119. package/dist/tool-schema-CBjGPrsQ.js.map +1 -0
  120. package/dist/types-B7LojTe4.d.ts +202 -0
  121. package/dist/types-_Faxb570.d.ts +439 -0
  122. package/dist/voice/client.d.ts +226 -0
  123. package/dist/voice/client.js +932 -0
  124. package/dist/voice/client.js.map +1 -0
  125. package/dist/voice/errors.d.ts +43 -0
  126. package/dist/voice/errors.js +41 -0
  127. package/dist/voice/errors.js.map +1 -0
  128. package/dist/voice/index.d.ts +271 -0
  129. package/dist/voice/index.js +1812 -0
  130. package/dist/voice/index.js.map +1 -0
  131. package/dist/voice/react.d.ts +167 -0
  132. package/dist/voice/react.js +234 -0
  133. package/dist/voice/react.js.map +1 -0
  134. package/dist/voice/sfu.d.ts +71 -0
  135. package/dist/voice/sfu.js +157 -0
  136. package/dist/voice/sfu.js.map +1 -0
  137. package/dist/voice/text.d.ts +6 -0
  138. package/dist/voice/text.js +2 -0
  139. package/dist/voice/types.d.ts +58 -0
  140. package/dist/voice/types.js +18 -0
  141. package/dist/voice/types.js.map +1 -0
  142. package/dist/voice/workers-ai.d.ts +136 -0
  143. package/dist/voice/workers-ai.js +568 -0
  144. package/dist/voice/workers-ai.js.map +1 -0
  145. package/dist/websockets/index.d.ts +192 -0
  146. package/dist/websockets/index.js +2 -0
  147. package/dist/websockets-DUfRHPRq.js +502 -0
  148. package/dist/websockets-DUfRHPRq.js.map +1 -0
  149. package/dist/workflow-types.d.ts +25 -25
  150. package/dist/workflows.d.ts +21 -21
  151. package/dist/workflows.js +1 -1
  152. package/docs/agent-class.md +2 -2
  153. package/docs/agent-tools.md +2 -1
  154. package/docs/channels.md +323 -0
  155. package/docs/chat-agents.md +6 -13
  156. package/docs/context.md +131 -0
  157. package/docs/index.md +15 -12
  158. package/docs/lifecycle.md +102 -55
  159. package/docs/long-running-agents.md +2 -2
  160. package/docs/mcp-servers.md +5 -1
  161. package/docs/resumable-streaming.md +1 -1
  162. package/docs/routing.md +105 -0
  163. package/docs/sessions.md +237 -871
  164. package/docs/streams.md +213 -0
  165. package/docs/sub-agents.md +184 -124
  166. package/docs/tasks.md +246 -0
  167. package/docs/voice.md +745 -0
  168. package/package.json +115 -13
  169. package/dist/capability-runner-CvHGZqUu.d.ts +0 -150
  170. package/dist/compaction-helpers-iiKMr2TQ.js +0 -340
  171. package/dist/compaction-helpers-iiKMr2TQ.js.map +0 -1
  172. package/dist/compaction-helpers-wUz6M3us.d.ts +0 -621
  173. package/dist/diagnostics-CaBjfz4J.js.map +0 -1
  174. package/dist/durable-object-lifecycle-D6nNQJJd.js +0 -862
  175. package/dist/durable-object-lifecycle-D6nNQJJd.js.map +0 -1
  176. package/dist/experimental/memory/session/index.d.ts +0 -671
  177. package/dist/experimental/memory/session/index.js +0 -2379
  178. package/dist/experimental/memory/session/index.js.map +0 -1
  179. package/dist/experimental/memory/utils/index.d.ts +0 -96
  180. package/dist/experimental/memory/utils/index.js +0 -79
  181. package/dist/experimental/memory/utils/index.js.map +0 -1
  182. package/dist/scheduler-CR9RHGos.js +0 -857
  183. package/dist/scheduler-CR9RHGos.js.map +0 -1
  184. package/dist/src-5W6JNKVb.js.map +0 -1
  185. package/dist/tool-output-truncation-CNnnGZQ3.js +0 -98
  186. package/dist/tool-output-truncation-CNnnGZQ3.js.map +0 -1
@@ -0,0 +1,614 @@
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, n as collectText, r as consumeChunks, t as matchesPath } from "../ingress-BfetZbMO.js";
4
+ //#region src/channels/adapters/slack.ts
5
+ const DEFAULT_SLACK_WEBHOOK_PATH = "/webhooks/slack";
6
+ const DEFAULT_MAX_SKEW_SECONDS = 300;
7
+ const APPROVE_ACTION_ID = "cloudflare_channels_approve_v1";
8
+ const REJECT_ACTION_ID = "cloudflare_channels_reject_v1";
9
+ const DEFAULT_STREAM_INTERVAL_MS = 500;
10
+ const SLACK_APPEND_LIMIT = 12e3;
11
+ const SLACK_TASK_FIELD_LIMIT = 256;
12
+ const SLACK_CONTEXT_LIMIT = 3e3;
13
+ const AMBIGUOUS_SLACK_ERRORS = /* @__PURE__ */ new Set([
14
+ "fatal_error",
15
+ "internal_error",
16
+ "request_timeout",
17
+ "service_unavailable"
18
+ ]);
19
+ function approvalText(request) {
20
+ return [
21
+ ...request.title ? [request.title] : [],
22
+ request.summary,
23
+ `Input:\n\`\`\`\n${renderInput(request.input)}\n\`\`\``
24
+ ].join("\n\n");
25
+ }
26
+ function channelIdentity(teamId, channelId) {
27
+ return `slack:${teamId}:channel:${channelId}`;
28
+ }
29
+ function messageIdentity(teamId, channelId, timestamp) {
30
+ return `${channelIdentity(teamId, channelId)}:message:${timestamp}`;
31
+ }
32
+ function outboundReference(channelId, timestamp) {
33
+ return `slack:channel:${channelId}:message:${timestamp}`;
34
+ }
35
+ function replySurfaceLabel(channelId, threadTimestamp) {
36
+ return threadTimestamp ? `Slack · ${channelId} · thread ${threadTimestamp}` : `Slack · ${channelId}`;
37
+ }
38
+ function threadIdentity(teamId, channelId, timestamp, threadTimestamp, isDirectMessage) {
39
+ const channel = channelIdentity(teamId, channelId);
40
+ if (isDirectMessage && threadTimestamp === void 0) return channel;
41
+ return `${channel}:thread:${threadTimestamp ?? timestamp}`;
42
+ }
43
+ function slackTimestamp(timestamp) {
44
+ const milliseconds = Number(timestamp) * 1e3;
45
+ if (!Number.isFinite(milliseconds)) return void 0;
46
+ try {
47
+ return new Date(milliseconds).toISOString();
48
+ } catch {
49
+ return;
50
+ }
51
+ }
52
+ function selfUserIds(payload, configuredBotUserId) {
53
+ const ids = /* @__PURE__ */ new Set();
54
+ if (configuredBotUserId) ids.add(configuredBotUserId);
55
+ if (Array.isArray(payload.authorizations)) {
56
+ for (const authorization of payload.authorizations) if (authorization?.is_bot === true && typeof authorization.user_id === "string") ids.add(authorization.user_id);
57
+ }
58
+ return ids;
59
+ }
60
+ function normalizedEvent(payload, configuredBotUserId) {
61
+ const event = payload.event;
62
+ if (!event || !isRecord(event)) return void 0;
63
+ if (event.type !== "app_mention" && event.type !== "message") return;
64
+ const isMention = event.type === "app_mention";
65
+ const isDirectMessage = event.type === "message" && event.channel_type === "im";
66
+ if (event.subtype !== void 0 || typeof event.bot_id === "string" || typeof event.app_id === "string") return;
67
+ const teamId = payload.team_id;
68
+ const eventId = payload.event_id;
69
+ const channelId = event.channel;
70
+ const actorId = event.user;
71
+ const text = event.text;
72
+ const timestamp = event.ts;
73
+ if (typeof teamId !== "string" || typeof eventId !== "string" || typeof channelId !== "string" || typeof actorId !== "string" || typeof text !== "string" || typeof timestamp !== "string") return;
74
+ if (selfUserIds(payload, configuredBotUserId).has(actorId)) return;
75
+ const threadTimestamp = typeof event.thread_ts === "string" ? event.thread_ts : void 0;
76
+ const sentAt = slackTimestamp(timestamp);
77
+ return {
78
+ type: "message",
79
+ eventId: `slack:${teamId}:event:${eventId}`,
80
+ thread: {
81
+ id: threadIdentity(teamId, channelId, timestamp, threadTimestamp, isDirectMessage),
82
+ isDirectMessage
83
+ },
84
+ replySurface: {
85
+ version: 1,
86
+ address: {
87
+ teamId,
88
+ channelId,
89
+ ...(threadTimestamp || !isDirectMessage) && { threadTs: threadTimestamp ?? timestamp },
90
+ recipientUserId: actorId,
91
+ recipientTeamId: teamId
92
+ },
93
+ label: replySurfaceLabel(channelId, threadTimestamp ?? (isDirectMessage ? void 0 : timestamp))
94
+ },
95
+ actor: {
96
+ id: `slack:${teamId}:user:${actorId}`,
97
+ identity: {
98
+ scope: teamId,
99
+ subject: actorId
100
+ },
101
+ isBot: false,
102
+ isSelf: false
103
+ },
104
+ message: {
105
+ id: messageIdentity(teamId, channelId, timestamp),
106
+ text,
107
+ markdown: text,
108
+ ...isMention && { isMention: true },
109
+ ...threadTimestamp && threadTimestamp !== timestamp && { reply: { id: messageIdentity(teamId, channelId, threadTimestamp) } },
110
+ ...sentAt && { metadata: { sentAt } }
111
+ }
112
+ };
113
+ }
114
+ function parseApprovalValue(value) {
115
+ let parsed;
116
+ try {
117
+ parsed = JSON.parse(value);
118
+ } catch {
119
+ return;
120
+ }
121
+ if (!isRecord(parsed) || parsed.v !== 1 || typeof parsed.interactionId !== "string" || !parsed.interactionId || parsed.decision !== "approve" && parsed.decision !== "reject") return;
122
+ return parsed;
123
+ }
124
+ async function sha256Hex(value) {
125
+ const digest = await crypto.subtle.digest("SHA-256", encodeUtf8(value));
126
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
127
+ }
128
+ async function normalizedInteractions(payload, rawBody) {
129
+ const teamId = payload.team?.id;
130
+ const actorId = payload.user?.id;
131
+ const channelId = typeof payload.channel?.id === "string" ? payload.channel.id : payload.container?.channel_id;
132
+ const timestamp = typeof payload.message?.ts === "string" ? payload.message.ts : payload.container?.message_ts;
133
+ if (typeof teamId !== "string" || typeof actorId !== "string" || typeof channelId !== "string" || typeof timestamp !== "string" || !Array.isArray(payload.actions)) return [];
134
+ const threadTimestamp = typeof payload.message?.thread_ts === "string" ? payload.message.thread_ts : typeof payload.container?.thread_ts === "string" ? payload.container.thread_ts : void 0;
135
+ const isDirectMessage = channelId.startsWith("D");
136
+ const bodyHash = await sha256Hex(rawBody);
137
+ const events = [];
138
+ for (const [index, action] of payload.actions.entries()) {
139
+ if (!action || typeof action.value !== "string") continue;
140
+ const expectedDecision = action.action_id === APPROVE_ACTION_ID ? "approve" : action.action_id === REJECT_ACTION_ID ? "reject" : void 0;
141
+ if (!expectedDecision) continue;
142
+ const approval = parseApprovalValue(action.value);
143
+ if (!approval || approval.decision !== expectedDecision) continue;
144
+ const stableEventId = `slack:${teamId}:interaction:sha256:${bodyHash}:action:${index}`;
145
+ const actionTimestamp = typeof action.action_ts === "string" ? action.action_ts : void 0;
146
+ events.push({
147
+ type: "approval-response",
148
+ eventId: stableEventId,
149
+ thread: {
150
+ id: threadIdentity(teamId, channelId, timestamp, threadTimestamp, isDirectMessage),
151
+ isDirectMessage
152
+ },
153
+ replySurface: {
154
+ version: 1,
155
+ address: {
156
+ teamId,
157
+ channelId,
158
+ threadTs: threadTimestamp ?? timestamp,
159
+ ...!isDirectMessage && {
160
+ recipientUserId: actorId,
161
+ recipientTeamId: teamId
162
+ }
163
+ },
164
+ label: replySurfaceLabel(channelId, threadTimestamp ?? timestamp)
165
+ },
166
+ actor: {
167
+ id: `slack:${teamId}:user:${actorId}`,
168
+ identity: {
169
+ scope: teamId,
170
+ subject: actorId
171
+ },
172
+ ...typeof payload.user?.username === "string" && { username: payload.user.username },
173
+ isBot: false
174
+ },
175
+ interactionId: approval.interactionId,
176
+ decision: approval.decision,
177
+ reference: actionTimestamp ? `${channelIdentity(teamId, channelId)}:action:${actionTimestamp}` : stableEventId
178
+ });
179
+ }
180
+ return events;
181
+ }
182
+ function bytesFromHex(hex) {
183
+ if (!/^[a-f0-9]{64}$/.test(hex)) return void 0;
184
+ const bytes = new Uint8Array(hex.length / 2);
185
+ for (let index = 0; index < bytes.length; index += 1) bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
186
+ return bytes;
187
+ }
188
+ async function hasValidSignature(request, rawBody, signingSecret, maxSkewSeconds) {
189
+ const timestamp = request.headers.get("x-slack-request-timestamp");
190
+ const signature = request.headers.get("x-slack-signature");
191
+ if (!timestamp || !signature || !/^\d+$/.test(timestamp)) return false;
192
+ const timestampSeconds = Number(timestamp);
193
+ if (!Number.isSafeInteger(timestampSeconds) || Math.abs(Math.floor(Date.now() / 1e3) - timestampSeconds) > maxSkewSeconds) return false;
194
+ const match = signature.match(/^v0=([a-f0-9]{64})$/);
195
+ const signatureBytes = match ? bytesFromHex(match[1]) : void 0;
196
+ if (!signatureBytes) return false;
197
+ const key = await crypto.subtle.importKey("raw", encodeUtf8(signingSecret), {
198
+ name: "HMAC",
199
+ hash: "SHA-256"
200
+ }, false, ["verify"]);
201
+ return crypto.subtle.verify("HMAC", key, signatureBytes, encodeUtf8(`v0:${timestamp}:${rawBody}`));
202
+ }
203
+ function parseJsonPayload(rawBody) {
204
+ let parsed;
205
+ try {
206
+ parsed = JSON.parse(rawBody);
207
+ } catch {
208
+ return;
209
+ }
210
+ if (!isRecord(parsed)) return void 0;
211
+ if (parsed.type !== "url_verification" && parsed.type !== "event_callback") return;
212
+ return parsed;
213
+ }
214
+ function parseInteractionPayload(rawBody) {
215
+ const encodedPayload = new URLSearchParams(rawBody).get("payload");
216
+ if (!encodedPayload) return void 0;
217
+ let parsed;
218
+ try {
219
+ parsed = JSON.parse(encodedPayload);
220
+ } catch {
221
+ return;
222
+ }
223
+ return isRecord(parsed) && parsed.type === "block_actions" ? parsed : void 0;
224
+ }
225
+ /** Create dependency-free, request-signed Slack HTTP ingress. */
226
+ function slackWebhook(options) {
227
+ if (!options.signingSecret.trim()) throw new Error("signingSecret is required to create Slack webhook ingress");
228
+ const maxSkewSeconds = options.maxSkewSeconds ?? DEFAULT_MAX_SKEW_SECONDS;
229
+ if (!Number.isInteger(maxSkewSeconds) || maxSkewSeconds < 0) throw new Error("maxSkewSeconds must be a non-negative integer");
230
+ const path = options.path ?? DEFAULT_SLACK_WEBHOOK_PATH;
231
+ return { async receive(request) {
232
+ if (!matchesPath(request, path)) return null;
233
+ if (request.method !== "POST") return emptyIngressResponse(405);
234
+ const rawBody = await request.text();
235
+ if (!await hasValidSignature(request, rawBody, options.signingSecret, maxSkewSeconds)) return emptyIngressResponse(401);
236
+ if ((request.headers.get("content-type")?.split(";", 1)[0]?.trim() ?? "") === "application/x-www-form-urlencoded") {
237
+ const payload = parseInteractionPayload(rawBody);
238
+ if (!payload) return emptyIngressResponse(400);
239
+ return {
240
+ events: (await normalizedInteractions(payload, rawBody)).map((event) => ({
241
+ event,
242
+ raw: payload
243
+ })),
244
+ response: new Response(null, { status: 200 })
245
+ };
246
+ }
247
+ const payload = parseJsonPayload(rawBody);
248
+ if (!payload) return emptyIngressResponse(400);
249
+ if (payload.type === "url_verification") return typeof payload.challenge === "string" ? {
250
+ events: [],
251
+ response: Response.json({ challenge: payload.challenge })
252
+ } : emptyIngressResponse(400);
253
+ const event = normalizedEvent(payload, options.botUserId);
254
+ return {
255
+ events: event ? [{
256
+ event,
257
+ raw: payload
258
+ }] : [],
259
+ response: new Response(null, { status: 200 })
260
+ };
261
+ } };
262
+ }
263
+ function failed(code, message, retryable) {
264
+ return {
265
+ status: "failed",
266
+ retryable,
267
+ error: {
268
+ code,
269
+ message
270
+ }
271
+ };
272
+ }
273
+ function slackErrorCode(error) {
274
+ return `SLACK_API_ERROR_${error.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}`;
275
+ }
276
+ function classifyApiFailure(response, payload) {
277
+ if (payload.ok === false) {
278
+ const error = typeof payload.error === "string" ? payload.error : "unknown_error";
279
+ const code = slackErrorCode(error);
280
+ const message = `Slack rejected the message: ${error}`;
281
+ if (response.status >= 500 || AMBIGUOUS_SLACK_ERRORS.has(error)) return uncertain(code, message);
282
+ return failed(code, message, response.status === 429 || error === "ratelimited");
283
+ }
284
+ if (response.status === 429) return failed("SLACK_HTTP_ERROR_429", "Slack rate limited the message", true);
285
+ if (response.status >= 400 && response.status < 500) return failed(`SLACK_HTTP_ERROR_${response.status}`, `Slack rejected the message with HTTP ${response.status}`, false);
286
+ return uncertain("SLACK_DELIVERY_ERROR", "Slack returned an invalid delivery response");
287
+ }
288
+ function asApiResponse(value) {
289
+ return isRecord(value) ? value : void 0;
290
+ }
291
+ function approvalValue(interactionId, decision) {
292
+ return JSON.stringify({
293
+ v: 1,
294
+ interactionId,
295
+ decision
296
+ });
297
+ }
298
+ function optionalId(value) {
299
+ return typeof value === "string" && value.length > 0 ? value : void 0;
300
+ }
301
+ function slackTarget(surface) {
302
+ if (!isChannelMessageSurface(surface) || surface.version !== 1 || !isRecord(surface.address)) return;
303
+ const address = surface.address;
304
+ if ("userId" in address) return typeof address.teamId === "string" && address.teamId.length > 0 && typeof address.userId === "string" && address.userId.length > 0 ? {
305
+ teamId: address.teamId,
306
+ userId: address.userId
307
+ } : void 0;
308
+ if (typeof address.channelId !== "string" || address.channelId.length === 0) return;
309
+ if ("threadTs" in address && address.threadTs !== void 0 && (typeof address.threadTs !== "string" || address.threadTs.length === 0)) return;
310
+ const recipientUserId = optionalId(address.recipientUserId);
311
+ const recipientTeamId = optionalId(address.recipientTeamId);
312
+ return {
313
+ channelId: address.channelId,
314
+ ...typeof address.threadTs === "string" && { threadTs: address.threadTs },
315
+ ...recipientUserId && recipientTeamId && {
316
+ recipientUserId,
317
+ recipientTeamId
318
+ }
319
+ };
320
+ }
321
+ const SLACK_TASK_STATUS = {
322
+ started: "in_progress",
323
+ completed: "complete",
324
+ failed: "error"
325
+ };
326
+ function splitText(text, limit) {
327
+ const pieces = [];
328
+ let piece = "";
329
+ let length = 0;
330
+ for (const character of text) {
331
+ if (length === limit) {
332
+ pieces.push(piece);
333
+ piece = "";
334
+ length = 0;
335
+ }
336
+ piece += character;
337
+ length += 1;
338
+ }
339
+ if (piece.length > 0) pieces.push(piece);
340
+ return pieces;
341
+ }
342
+ function clamp(value, limit) {
343
+ const characters = [...value];
344
+ return characters.length <= limit ? value : `${characters.slice(0, limit - 1).join("")}\u2026`;
345
+ }
346
+ function escapeMrkdwn(value) {
347
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
348
+ }
349
+ function escapeLinkUrl(value) {
350
+ return escapeMrkdwn(value.replaceAll("|", "%7C"));
351
+ }
352
+ function escapeLinkLabel(value) {
353
+ return escapeMrkdwn(value).replaceAll("|", "&#124;");
354
+ }
355
+ /**
356
+ * Render a neutral chunk as Slack stream content.
357
+ *
358
+ * `reasoning` has no Slack rendering and is dropped. A `source` is collected
359
+ * rather than appended, so it can be rendered once beneath the finished
360
+ * message instead of interrupting the text.
361
+ */
362
+ function toStreamChunks(chunk, sources) {
363
+ switch (chunk.type) {
364
+ case "text": return splitText(chunk.text, SLACK_APPEND_LIMIT).map((text) => ({
365
+ type: "markdown_text",
366
+ text
367
+ }));
368
+ case "tool": return [{
369
+ type: "task_update",
370
+ id: clamp(chunk.id ?? chunk.name, SLACK_TASK_FIELD_LIMIT),
371
+ title: clamp(chunk.title ?? chunk.name, SLACK_TASK_FIELD_LIMIT),
372
+ status: SLACK_TASK_STATUS[chunk.status],
373
+ ...chunk.detail !== void 0 && { details: clamp(chunk.detail, SLACK_TASK_FIELD_LIMIT) }
374
+ }];
375
+ case "source":
376
+ sources.push(chunk);
377
+ return [];
378
+ case "reasoning": return [];
379
+ }
380
+ }
381
+ function sourcesBlock(sources) {
382
+ return {
383
+ type: "context",
384
+ elements: [{
385
+ type: "mrkdwn",
386
+ text: clamp(sources.map((source) => `<${escapeLinkUrl(source.url)}|${escapeLinkLabel(source.title ?? source.url)}>`).join(" · "), SLACK_CONTEXT_LIMIT)
387
+ }]
388
+ };
389
+ }
390
+ /** Create a configured Slack Web API Channel. */
391
+ function slack(options) {
392
+ if (!options.botToken.trim()) throw new Error("botToken is required to create a Slack channel");
393
+ const fetch = options.fetch ?? globalThis.fetch;
394
+ const apiBaseUrl = (options.apiBaseUrl ?? "https://slack.com/api").replace(/\/$/, "");
395
+ const toText = options.toText ?? defaultText;
396
+ const streamIntervalMs = options.streamIntervalMs ?? DEFAULT_STREAM_INTERVAL_MS;
397
+ if (!Number.isInteger(streamIntervalMs) || streamIntervalMs < 0) throw new Error("streamIntervalMs must be a non-negative integer");
398
+ const ingress = options.webhook ? slackWebhook(options.webhook) : void 0;
399
+ async function resolveTarget(target) {
400
+ if (!("userId" in target)) return target;
401
+ let response;
402
+ try {
403
+ response = await fetch(`${apiBaseUrl}/conversations.open`, {
404
+ method: "POST",
405
+ headers: {
406
+ authorization: `Bearer ${options.botToken}`,
407
+ "content-type": "application/json; charset=utf-8"
408
+ },
409
+ body: JSON.stringify({ users: target.userId })
410
+ });
411
+ } catch {
412
+ return failed("SLACK_CONTACT_RESOLUTION_FAILED", "Slack could not resolve a direct-message destination", true);
413
+ }
414
+ let payload;
415
+ try {
416
+ payload = await response.json();
417
+ } catch {
418
+ return failed("SLACK_CONTACT_RESOLUTION_FAILED", "Slack returned an invalid direct-message destination", response.status === 429 || response.status >= 500);
419
+ }
420
+ const result = isRecord(payload) ? payload : void 0;
421
+ if (result?.ok === true && typeof result.channel?.id === "string") return {
422
+ channelId: result.channel.id,
423
+ recipientUserId: target.userId,
424
+ recipientTeamId: target.teamId
425
+ };
426
+ const error = typeof result?.error === "string" ? result.error : "unknown";
427
+ return failed(slackErrorCode(error), `Slack could not open a direct message: ${error}`, response.status === 429 || response.status >= 500 || error === "ratelimited");
428
+ }
429
+ async function postMessage(destination, text, blocks) {
430
+ const unresolvedTarget = slackTarget(destination);
431
+ if (!unresolvedTarget) return failed("SLACK_SURFACE_INVALID", `Slack cannot parse the address for Channel "${destination.channelKey}"`, false);
432
+ const target = await resolveTarget(unresolvedTarget);
433
+ if ("status" in target) return target;
434
+ const sent = await callSlack("chat.postMessage", {
435
+ channel: target.channelId,
436
+ text,
437
+ mrkdwn: true,
438
+ ...target.threadTs && { thread_ts: target.threadTs },
439
+ ...blocks && { blocks }
440
+ });
441
+ return "status" in sent ? sent : {
442
+ status: "delivered",
443
+ reference: outboundReference(sent.channelId, sent.ts)
444
+ };
445
+ }
446
+ /**
447
+ * One transport path for every Slack method this Channel calls.
448
+ *
449
+ * Each of them answers with the conversation and timestamp on success, and
450
+ * every failure mode is classified the same way, so posting and streaming
451
+ * cannot drift apart.
452
+ */
453
+ async function callSlack(method, body) {
454
+ let response;
455
+ try {
456
+ response = await fetch(`${apiBaseUrl}/${method}`, {
457
+ method: "POST",
458
+ headers: {
459
+ authorization: `Bearer ${options.botToken}`,
460
+ "content-type": "application/json; charset=utf-8"
461
+ },
462
+ body: JSON.stringify(body)
463
+ });
464
+ } catch {
465
+ return uncertain("SLACK_DELIVERY_ERROR", "Slack delivery failed with an unknown outcome");
466
+ }
467
+ let payload;
468
+ try {
469
+ payload = await response.json();
470
+ } catch {
471
+ payload = void 0;
472
+ }
473
+ const apiResponse = asApiResponse(payload);
474
+ if (apiResponse?.ok === true && typeof apiResponse.channel === "string" && typeof apiResponse.ts === "string") return {
475
+ channelId: apiResponse.channel,
476
+ ts: apiResponse.ts
477
+ };
478
+ return apiResponse?.ok === true ? uncertain("SLACK_DELIVERY_ERROR", "Slack returned an invalid delivery response") : classifyApiFailure(response, apiResponse ?? {});
479
+ }
480
+ async function streamMessage(destination, chunks, streamOptions) {
481
+ const unresolvedTarget = slackTarget(destination);
482
+ if (!unresolvedTarget) {
483
+ await chunks.cancel().catch(() => {});
484
+ return failed("SLACK_SURFACE_INVALID", `Slack cannot parse the address for Channel "${destination.channelKey}"`, false);
485
+ }
486
+ if (!("userId" in unresolvedTarget) && !unresolvedTarget.threadTs && !unresolvedTarget.recipientUserId) {
487
+ const collected = await collectText(chunks);
488
+ if (collected.interrupted && collected.text.length === 0) return failed("SLACK_STREAM_INTERRUPTED", "The stream ended before producing any content to deliver", false);
489
+ const delivered = await postMessage(destination, toText({
490
+ ...streamOptions.title && { title: streamOptions.title },
491
+ markdown: collected.text
492
+ }));
493
+ if (!collected.interrupted || delivered.status !== "delivered") return delivered;
494
+ return uncertain("SLACK_STREAM_INTERRUPTED", "An incomplete answer was delivered because the stream ended early", delivered.reference);
495
+ }
496
+ const target = await resolveTarget(unresolvedTarget);
497
+ if ("status" in target) {
498
+ await chunks.cancel().catch(() => {});
499
+ return target;
500
+ }
501
+ const started = await callSlack("chat.startStream", {
502
+ channel: target.channelId,
503
+ ...target.threadTs && { thread_ts: target.threadTs },
504
+ ...target.recipientUserId && {
505
+ recipient_user_id: target.recipientUserId,
506
+ recipient_team_id: target.recipientTeamId
507
+ },
508
+ ...streamOptions.title && { chunks: splitText(`${streamOptions.title}\n\n`, SLACK_APPEND_LIMIT).map((text) => ({
509
+ type: "markdown_text",
510
+ text
511
+ })) }
512
+ });
513
+ if ("status" in started) {
514
+ await chunks.cancel().catch(() => {});
515
+ return started;
516
+ }
517
+ const reference = outboundReference(started.channelId, started.ts);
518
+ const sources = [];
519
+ const shouldFlush = createPacer(streamIntervalMs);
520
+ let pending = [];
521
+ let appendFailure;
522
+ return consumeChunks(chunks, {
523
+ async onChunk(chunk) {
524
+ pending.push(...toStreamChunks(chunk, sources));
525
+ if (pending.length === 0 || !shouldFlush()) return;
526
+ const appended = await callSlack("chat.appendStream", {
527
+ channel: started.channelId,
528
+ ts: started.ts,
529
+ chunks: pending
530
+ });
531
+ pending = [];
532
+ if ("status" in appended) {
533
+ appendFailure = appended;
534
+ throw new Error(appended.error.message);
535
+ }
536
+ },
537
+ async onFinish(outcome) {
538
+ const stopped = await callSlack("chat.stopStream", {
539
+ channel: started.channelId,
540
+ ts: started.ts,
541
+ ...pending.length > 0 && !appendFailure && { chunks: pending },
542
+ ...sources.length > 0 && { blocks: [sourcesBlock(sources)] }
543
+ });
544
+ if ("status" in stopped) return uncertain(stopped.error.code, `Slack could not stop the stream: ${stopped.error.message}`, reference);
545
+ if (appendFailure) return uncertain(appendFailure.error.code, appendFailure.error.message, reference);
546
+ if (outcome.interrupted) return uncertain("SLACK_STREAM_INTERRUPTED", "The answer ended early, so the Slack message is incomplete", reference);
547
+ return {
548
+ status: "delivered",
549
+ reference
550
+ };
551
+ }
552
+ });
553
+ }
554
+ return {
555
+ ...options.route && { route: options.route },
556
+ ...ingress && { ingress },
557
+ contactSurface(identity) {
558
+ if (identity.scope === void 0) return null;
559
+ return {
560
+ version: 1,
561
+ address: {
562
+ teamId: identity.scope,
563
+ userId: identity.subject
564
+ },
565
+ label: `Slack · user ${identity.subject}`
566
+ };
567
+ },
568
+ deliver(destination, message) {
569
+ return postMessage(destination, toText(message));
570
+ },
571
+ stream(destination, chunks, streamOptions) {
572
+ return streamMessage(destination, chunks, streamOptions);
573
+ },
574
+ requestApproval(destination, { interactionId, request }) {
575
+ if (interactionId.length === 0) return Promise.resolve(failed("SLACK_INTERACTION_ID_REQUIRED", "Slack approval requests require a non-empty interaction id", false));
576
+ const text = approvalText(request);
577
+ const approveValue = approvalValue(interactionId, "approve");
578
+ const rejectValue = approvalValue(interactionId, "reject");
579
+ if (text.length > 3e3 || approveValue.length > 2e3 || rejectValue.length > 2e3) return Promise.resolve(failed("SLACK_APPROVAL_TOO_LONG", "Slack approval content exceeds Block Kit limits", false));
580
+ return postMessage(destination, text, [{
581
+ type: "section",
582
+ text: {
583
+ type: "mrkdwn",
584
+ text
585
+ }
586
+ }, {
587
+ type: "actions",
588
+ elements: [{
589
+ type: "button",
590
+ action_id: APPROVE_ACTION_ID,
591
+ text: {
592
+ type: "plain_text",
593
+ text: "Approve"
594
+ },
595
+ style: "primary",
596
+ value: approveValue
597
+ }, {
598
+ type: "button",
599
+ action_id: REJECT_ACTION_ID,
600
+ text: {
601
+ type: "plain_text",
602
+ text: "Reject"
603
+ },
604
+ style: "danger",
605
+ value: rejectValue
606
+ }]
607
+ }]);
608
+ }
609
+ };
610
+ }
611
+ //#endregion
612
+ export { slack, slackWebhook };
613
+
614
+ //# sourceMappingURL=slack.js.map