agents 0.26.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 (233) 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/harness/pi/index.d.ts +2 -1
  85. package/dist/harness/store/index.d.ts +24 -0
  86. package/dist/harness/store/index.js +2 -0
  87. package/dist/harness/think/index.d.ts +821 -0
  88. package/dist/harness/think/index.js +2529 -0
  89. package/dist/harness/think/index.js.map +1 -0
  90. package/dist/harness-B6B9S3rb.d.ts +203 -0
  91. package/dist/{images-iiTDSyRu.js → images-Ce9SCu3X.js} +2 -248
  92. package/dist/images-Ce9SCu3X.js.map +1 -0
  93. package/dist/{index-BreL0YrQ.d.ts → index-D0A5tbWu.d.ts} +2 -2
  94. package/dist/index-Ib_4dwOo.d.ts +200 -0
  95. package/dist/index.d.ts +5 -5
  96. package/dist/index.js +2 -2
  97. package/dist/ingress-EFbrS547.js +9 -0
  98. package/dist/ingress-EFbrS547.js.map +1 -0
  99. package/dist/input-schema-DNrpTu49.js +23 -0
  100. package/dist/input-schema-DNrpTu49.js.map +1 -0
  101. package/dist/internal-kmw70xwR.js +46 -0
  102. package/dist/internal-kmw70xwR.js.map +1 -0
  103. package/dist/internal_context.d.ts +1 -1
  104. package/dist/lifecycle/index.d.ts +3 -3
  105. package/dist/mcp/client/do-oauth-client-provider.d.ts +1 -1
  106. package/dist/mcp/client/do-oauth-client-provider.js +9 -0
  107. package/dist/mcp/client/do-oauth-client-provider.js.map +1 -1
  108. package/dist/mcp/client/index.d.ts +1 -1
  109. package/dist/mcp/client/index.js +1 -1
  110. package/dist/mcp/client/x402.d.ts +35 -4
  111. package/dist/mcp/client/x402.js +24 -3
  112. package/dist/mcp/client/x402.js.map +1 -1
  113. package/dist/mcp/index.d.ts +1 -1
  114. package/dist/mcp/index.js +2 -2
  115. package/dist/models/ai-sdk/index.d.ts +16 -14
  116. package/dist/models/ai-sdk/index.js +1 -2442
  117. package/dist/models/opencode/index.d.ts +65 -0
  118. package/dist/models/opencode/index.js +285 -0
  119. package/dist/models/opencode/index.js.map +1 -0
  120. package/dist/models/pi-ai/index.d.ts +9 -7
  121. package/dist/models/pi-ai/index.js +2 -1
  122. package/dist/models/pi-ai/index.js.map +1 -1
  123. package/dist/options-pwTNq9z6.d.ts +134 -0
  124. package/dist/protocol-BEgrJYC5.d.ts +331 -0
  125. package/dist/protocol-CukuI73M.d.ts +386 -0
  126. package/dist/protocol-cOklVBBD.js +227 -0
  127. package/dist/protocol-cOklVBBD.js.map +1 -0
  128. package/dist/{protocol-kHsr-f0X.js → protocol-eXeJxhgu.js} +2 -67
  129. package/dist/protocol-eXeJxhgu.js.map +1 -0
  130. package/dist/protocol-y2sSY584.js +148 -0
  131. package/dist/protocol-y2sSY584.js.map +1 -0
  132. package/dist/queue/index.d.ts +1 -1
  133. package/dist/react.d.ts +2 -2
  134. package/dist/routing/index.d.ts +2 -2
  135. package/dist/{scheduler-RdACNXDP.d.ts → scheduler-CrFCnv_J.d.ts} +2 -2
  136. package/dist/schedules/index.d.ts +1 -1
  137. package/dist/serializable.d.ts +1 -1
  138. package/dist/sessions/index.d.ts +11 -434
  139. package/dist/sessions/index.js +1 -2271
  140. package/dist/sessions-B9pNuxdT.js +2272 -0
  141. package/dist/sessions-B9pNuxdT.js.map +1 -0
  142. package/dist/settings-C45hDliX.js +253 -0
  143. package/dist/settings-C45hDliX.js.map +1 -0
  144. package/dist/{transport-PfFnt-Ld.d.ts → settings-NgGwF7Qt.d.ts} +14 -208
  145. package/dist/{src-BZeu-SCD.js → src-DgCbZtIR.js} +4 -4
  146. package/dist/{src-BZeu-SCD.js.map → src-DgCbZtIR.js.map} +1 -1
  147. package/dist/stall-watchdog-BpRF2uwG.js +195 -0
  148. package/dist/stall-watchdog-BpRF2uwG.js.map +1 -0
  149. package/dist/state/index.d.ts +1 -1
  150. package/dist/store-DLeqKuj3.d.ts +310 -0
  151. package/dist/store-vgcPYo8K.js +432 -0
  152. package/dist/store-vgcPYo8K.js.map +1 -0
  153. package/dist/streams/index.d.ts +1 -1
  154. package/dist/streams/index.js +1 -1
  155. package/dist/{streams-DW-UCOUc.js → streams-CITClVer.js} +16 -3
  156. package/dist/streams-CITClVer.js.map +1 -0
  157. package/dist/{streams-Cmkj9QfW.d.ts → streams-fbG61qM8.d.ts} +2 -2
  158. package/dist/sub-routing.d.ts +1 -1
  159. package/dist/tasks/index.d.ts +1 -1
  160. package/dist/tasks/index.js +1 -1
  161. package/dist/{tasks-68DESxf0.js → tasks-CkTZNaV1.js} +2 -1
  162. package/dist/tasks-CkTZNaV1.js.map +1 -0
  163. package/dist/{tasks-9YxYtByh.d.ts → tasks-PtlB4Uyn.d.ts} +2 -2
  164. package/dist/tool-3PA8xjB_.js +421 -0
  165. package/dist/tool-3PA8xjB_.js.map +1 -0
  166. package/dist/tool-BPJEbwaX.d.ts +278 -0
  167. package/dist/transport-k2mFrtL8.d.ts +200 -0
  168. package/dist/turns-DCV3ycDl.js +317 -0
  169. package/dist/turns-DCV3ycDl.js.map +1 -0
  170. package/dist/voice/client.js +1 -1
  171. package/dist/voice/index.d.ts +2 -2
  172. package/dist/voice/index.js +1 -1
  173. package/dist/websearch/index.d.ts +58 -0
  174. package/dist/websearch/index.js +2 -0
  175. package/dist/websearch/tools/ai-sdk.d.ts +77 -0
  176. package/dist/websearch/tools/ai-sdk.js +46 -0
  177. package/dist/websearch/tools/ai-sdk.js.map +1 -0
  178. package/dist/websearch/tools/pi.d.ts +82 -0
  179. package/dist/websearch/tools/pi.js +89 -0
  180. package/dist/websearch/tools/pi.js.map +1 -0
  181. package/dist/websearch/tools/tanstack-ai.d.ts +71 -0
  182. package/dist/websearch/tools/tanstack-ai.js +46 -0
  183. package/dist/websearch/tools/tanstack-ai.js.map +1 -0
  184. package/dist/websockets/index.d.ts +12 -296
  185. package/dist/websockets/index.js +1 -1
  186. package/dist/{websockets-Ci-LZFHH.js → websockets-CmENGb-Z.js} +18 -4
  187. package/dist/websockets-CmENGb-Z.js.map +1 -0
  188. package/dist/{wire-types-CrjKvDyi.d.ts → wire-types-Cv0iwjk5.d.ts} +3 -51
  189. package/dist/workflow-types.d.ts +1 -1
  190. package/dist/workflows.d.ts +1 -1
  191. package/dist/workflows.js +1 -1
  192. package/docs/browse-the-web.md +3 -0
  193. package/docs/harnesses/think.md +215 -0
  194. package/docs/index.md +2 -1
  195. package/docs/lifecycle.md +40 -0
  196. package/docs/search-the-web.md +133 -0
  197. package/package.json +117 -38
  198. package/dist/browser-tool-CQ10e6Kj.js.map +0 -1
  199. package/dist/channel-Bnm4S7T2.d.ts +0 -491
  200. package/dist/channels/ai-sdk.d.ts +0 -46
  201. package/dist/channels/ai-sdk.js +0 -120
  202. package/dist/channels/ai-sdk.js.map +0 -1
  203. package/dist/channels/email.js.map +0 -1
  204. package/dist/channels/index.d.ts +0 -233
  205. package/dist/channels/index.js +0 -608
  206. package/dist/channels/index.js.map +0 -1
  207. package/dist/channels/slack.js +0 -614
  208. package/dist/channels/slack.js.map +0 -1
  209. package/dist/channels/tanstack-ai.d.ts +0 -39
  210. package/dist/channels/tanstack-ai.js +0 -17
  211. package/dist/channels/tanstack-ai.js.map +0 -1
  212. package/dist/channels/telegram.js +0 -427
  213. package/dist/channels/telegram.js.map +0 -1
  214. package/dist/channels/voice.d.ts +0 -45
  215. package/dist/channels/voice.js +0 -122
  216. package/dist/channels/voice.js.map +0 -1
  217. package/dist/images-iiTDSyRu.js.map +0 -1
  218. package/dist/index-BB0kqhIz.d.ts +0 -101
  219. package/dist/ingress-BfetZbMO.js +0 -83
  220. package/dist/ingress-BfetZbMO.js.map +0 -1
  221. package/dist/internal-CYlgHl1l.js +0 -59
  222. package/dist/internal-CYlgHl1l.js.map +0 -1
  223. package/dist/models/ai-sdk/index.js.map +0 -1
  224. package/dist/protocol-kHsr-f0X.js.map +0 -1
  225. package/dist/sessions/index.js.map +0 -1
  226. package/dist/streams-DW-UCOUc.js.map +0 -1
  227. package/dist/surface-bZZJqBka.js +0 -17
  228. package/dist/surface-bZZJqBka.js.map +0 -1
  229. package/dist/tasks-68DESxf0.js.map +0 -1
  230. package/dist/tool-schema-CBjGPrsQ.js +0 -31
  231. package/dist/tool-schema-CBjGPrsQ.js.map +0 -1
  232. package/dist/websockets-Ci-LZFHH.js.map +0 -1
  233. package/docs/channels.md +0 -323
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/experimental/channels/harness-conversations.ts","../../../src/experimental/channels/response.ts","../../../src/experimental/channels/conversations.ts","../../../src/experimental/channels/stream.ts","../../../src/experimental/channels/surface.ts","../../../src/experimental/channels/gateway.ts","../../../src/experimental/channels/identity.ts","../../../src/experimental/channels/routes.ts"],"sourcesContent":["import type {\n AgentHarness,\n HarnessSession,\n InputPart,\n OperationStatus,\n SessionEvent,\n SessionWatch\n} from \"./harness\";\nimport type {\n ConversationInfo,\n ConversationOperation,\n ConversationSnapshot,\n EventOrigin,\n InboundEvent,\n ResponseChunk,\n TranscriptMessage,\n TurnStatus\n} from \"./protocol\";\nimport type { ResponseWriter } from \"./response\";\n\n/** An inbound event acting in a conversation, rather than on it. */\nexport type TurnEvent = Exclude<InboundEvent, { type: ConversationOperation }>;\n\nexport function isTurnEvent(event: InboundEvent): event is TurnEvent {\n return (\n event.type !== \"conversation-create\" &&\n event.type !== \"conversation-fork\" &&\n event.type !== \"conversation-reset\"\n );\n}\n\n/** What harness conversations need from Channels. */\nexport type HarnessConversationsHost = {\n /** Read once installed; Channels has no storage when constructed. */\n kv(): SyncKvStorage;\n openResponse(conversationId: string, turnId: string): Promise<ResponseWriter>;\n publishTurn(conversationId: string, turn: TurnStatus): Promise<void>;\n publishMessages(\n conversationId: string,\n messages: TranscriptMessage[]\n ): Promise<void>;\n reset(conversationId: string): Promise<void>;\n};\n\n/**\n * A turn record: its status, the user message it answers, and the operation\n * driving it now. A turn starts with its own operation (same id); each tool\n * answer that continues it is a new operation, aliased to the turn.\n */\ntype Turn = TurnStatus & { messageId: string; operation?: string };\n\nconst PREFIX = \"channels:harness:\";\n\n/**\n * Serves harness sessions as Channels conversations: the conversation id is\n * the session id, a turn is one operation (its id is the inbound message's\n * event id), and a run is one response. Operations that join a running run\n * (steers) share its response.\n *\n * The harness owns the transcript. Turn records live in storage; each\n * watched session's transcript is held in memory, from the watch's state\n * and the messages that follow it.\n */\nexport class HarnessConversations {\n readonly #harness: AgentHarness;\n readonly #host: HarnessConversationsHost;\n readonly #links = new Map<string, Promise<SessionLink>>();\n\n constructor(harness: AgentHarness, host: HarnessConversationsHost) {\n this.#harness = harness;\n this.#host = host;\n }\n\n /** The conversation a surface joins when it names none. */\n defaultConversation(): string {\n return this.#harness.session().id;\n }\n\n /** Follow every session a surface has joined. Call from `onStart`. */\n async attachAll(): Promise<void> {\n const sessions = this.#host\n .kv()\n .list<true>({ prefix: `${PREFIX}session:` });\n await Promise.all(\n [...sessions].map(([key]) =>\n this.#link(key.slice(`${PREFIX}session:`.length)).catch((error) =>\n console.error(\"Failed to follow a harness session\", error)\n )\n )\n );\n }\n\n async snapshot(conversationId: string): Promise<ConversationSnapshot> {\n const link = await this.#link(conversationId);\n return {\n messages: [...link.messages],\n turns: this.#turns(conversationId).filter(isOpen)\n };\n }\n\n async onEvent(event: TurnEvent, origin: EventOrigin): Promise<void> {\n const conversationId = origin.conversationId;\n const link = await this.#link(conversationId);\n switch (event.type) {\n case \"message\": {\n if (!this.#turnRecord(conversationId, event.eventId)) {\n await this.#save(conversationId, {\n turnId: event.eventId,\n startedBy: event.eventId,\n messageId: event.message.id,\n status: \"queued\"\n });\n }\n // The harness deduplicates by operation id, so redelivery is harmless.\n await link.session.submit(\n {\n parts: inputParts(event.message),\n messageId: event.message.id,\n from: { participantId: origin.participant.id }\n },\n { operationId: event.eventId }\n );\n return;\n }\n case \"tool-result\":\n case \"approval-response\": {\n // The harness decides whether the answer is wanted and whose it is.\n const turn = this.#turnRecord(conversationId, event.turnId);\n if (!turn) return;\n const operationId = event.eventId;\n this.#host.kv().put(aliasKey(conversationId, operationId), turn.turnId);\n await link.session.submit(\n {\n ...(event.type === \"tool-result\"\n ? {\n type: \"tool-result\" as const,\n toolCallId: event.toolCallId,\n result: event.result\n }\n : {\n type: \"approval\" as const,\n approvalId: event.approvalId,\n approved: event.approved,\n ...(event.reason !== undefined && { reason: event.reason })\n }),\n from: { participantId: origin.participant.id }\n },\n { operationId }\n );\n return;\n }\n case \"cancel\": {\n const turn = this.#turnRecord(conversationId, event.turnId);\n await link.session.abort(turn?.operation ?? event.turnId);\n return;\n }\n }\n }\n\n /** Every session, as conversations. */\n async list(): Promise<ConversationInfo[]> {\n const sessions = await this.#harness.sessions.list();\n return sessions.map(({ id, parent, busy }) => ({\n id,\n busy,\n ...(parent !== undefined && { parent })\n }));\n }\n\n /** Create a session; a conversation operation. */\n async create(): Promise<string> {\n const session = await this.#harness.sessions.create();\n await this.#link(session.id);\n return session.id;\n }\n\n /** Fork a session; a conversation operation. */\n async fork(from: string): Promise<string> {\n const session = await this.#harness.sessions.fork(from);\n await this.#link(session.id);\n return session.id;\n }\n\n /** Reset a session's context; the watch reports it as a `reset` event. */\n async reset(conversationId: string, handoff?: string): Promise<void> {\n const link = await this.#link(conversationId);\n await link.session.reset(handoff);\n }\n\n // ── Following a session ────────────────────────────────────────────────\n\n #link(conversationId: string): Promise<SessionLink> {\n let link = this.#links.get(conversationId);\n if (!link) {\n const attached: Promise<SessionLink> = this.#attach(conversationId).then(\n (linked) => {\n // A closed watch is followed again on next use.\n void linked.watch.closed.then(() => {\n if (this.#links.get(conversationId) === attached) {\n this.#links.delete(conversationId);\n }\n });\n return linked;\n },\n (error: unknown) => {\n this.#links.delete(conversationId);\n throw error;\n }\n );\n link = attached;\n this.#links.set(conversationId, link);\n }\n return link;\n }\n\n async #attach(conversationId: string): Promise<SessionLink> {\n const session = this.#harness.session(conversationId);\n const watch = await session.watch();\n this.#host.kv().put(`${PREFIX}session:${conversationId}`, true);\n const link = new SessionLink(session, watch);\n await this.#resume(conversationId, link);\n watch.start((events) => this.#onEvents(conversationId, link, events));\n return link;\n }\n\n /** Catch up from the watch's state, after an attach or a restart. */\n async #resume(conversationId: string, link: SessionLink): Promise<void> {\n const { state } = link.watch;\n const live = new Set(state.pending.map((p) => p.operationId));\n for (const status of state.pending) {\n await this.#operation(conversationId, link, status);\n }\n if (state.run) {\n for (const id of state.run.operations) live.add(id);\n await this.#runStart(conversationId, link, state.run.operations);\n // The output so far; the watch's chunks continue its open parts.\n for (const chunk of state.run.partial ?? []) link.append(chunk);\n }\n // Turns that settled while nothing was watching.\n for (const turn of this.#turns(conversationId)) {\n const operationId = turn.operation ?? turn.turnId;\n if (turn.status === \"settled\" || live.has(operationId)) continue;\n void link.session.wait(operationId).then(\n (result) =>\n this.#operation(conversationId, link, result).catch((error) =>\n console.error(\"Failed to settle a turn\", error)\n ),\n (error) => console.error(\"Failed to settle a turn\", error)\n );\n }\n }\n\n async #onEvents(\n conversationId: string,\n link: SessionLink,\n events: readonly SessionEvent[]\n ): Promise<void> {\n for (const event of events) {\n try {\n await this.#onEvent(conversationId, link, event);\n } catch (error) {\n console.error(\"Harness event failed\", event.type, error);\n }\n }\n }\n\n async #onEvent(\n conversationId: string,\n link: SessionLink,\n event: SessionEvent\n ): Promise<void> {\n switch (event.type) {\n case \"operation\":\n return this.#operation(conversationId, link, event.status);\n case \"run-start\":\n return this.#runStart(conversationId, link, event.operations);\n case \"chunk\":\n link.append(event.chunk);\n return;\n case \"message\":\n link.upsert(event.message);\n await this.#host.publishMessages(conversationId, [event.message]);\n return;\n case \"run-end\":\n link.endResponse();\n return;\n case \"reset\":\n link.endResponse();\n link.messages = [];\n await this.#host.reset(conversationId);\n return;\n }\n }\n\n async #runStart(\n conversationId: string,\n link: SessionLink,\n operations: readonly string[]\n ): Promise<void> {\n link.endResponse();\n const queued = this.#turns(conversationId).find(\n (t) => t.status === \"queued\"\n );\n const first = operations[0] ?? queued?.operation ?? queued?.turnId;\n if (first === undefined) return;\n const turn = this.#turn(conversationId, first);\n if (!turn) return;\n link.response = await this.#host.openResponse(conversationId, turn.turnId);\n // A run that answers a tool call continues the message that made it.\n const last = link.messages.at(-1);\n link.extends =\n turn.turnId !== first && last?.role === \"assistant\" ? last.id : undefined;\n for (const id of operations.length ? operations : [first]) {\n await this.#join(conversationId, link, id);\n }\n }\n\n async #operation(\n conversationId: string,\n link: SessionLink,\n status: OperationStatus\n ): Promise<void> {\n const { operationId } = status;\n const turn = this.#turn(conversationId, operationId);\n switch (status.status) {\n case \"queued\":\n // An operation a client of the harness itself submitted.\n if (!turn) {\n await this.#save(conversationId, {\n turnId: operationId,\n startedBy: operationId,\n messageId: operationId,\n status: \"queued\"\n });\n } else if (turn.turnId !== operationId && turn.status === \"settled\") {\n // A tool answer reopens the turn it answers.\n await this.#save(conversationId, {\n turnId: turn.turnId,\n startedBy: turn.startedBy,\n messageId: turn.messageId,\n operation: operationId,\n status: \"queued\"\n });\n }\n return;\n case \"placed\":\n // A steer joins the running run.\n return this.#join(conversationId, link, operationId);\n case \"done\":\n return this.#settle(conversationId, link, operationId, {\n outcome: \"completed\"\n });\n case \"unanswered\": {\n const stopped =\n status.reason === \"aborted\" || status.reason === \"withdrawn\";\n // A refused tool answer (not the owner's, or not wanted) leaves the\n // turn as it was: still waiting, if its calls are.\n const refusedAnswer =\n turn !== undefined && turn.turnId !== operationId && !stopped;\n return this.#settle(\n conversationId,\n link,\n operationId,\n stopped\n ? { outcome: \"aborted\" }\n : refusedAnswer\n ? { outcome: \"completed\" }\n : {\n outcome: \"failed\",\n error: `Not answered${status.reason ? `: ${status.reason}` : \"\"}`\n }\n );\n }\n }\n }\n\n async #join(\n conversationId: string,\n link: SessionLink,\n operationId: string\n ): Promise<void> {\n const turn = this.#turn(conversationId, operationId);\n if (!turn || !link.response) return;\n const answer = turn.turnId !== operationId;\n // A settled turn runs again only for a tool answer.\n if (turn.status === \"settled\" && !answer) return;\n if (turn.status === \"running\" && turn.responseId === link.response.id) {\n return;\n }\n await this.#save(conversationId, {\n turnId: turn.turnId,\n startedBy: turn.startedBy,\n messageId: turn.messageId,\n ...(answer && { operation: operationId }),\n status: \"running\",\n responseId: link.response.id,\n ...(link.extends !== undefined && { extends: link.extends })\n });\n }\n\n async #settle(\n conversationId: string,\n link: SessionLink,\n operationId: string,\n end:\n | { outcome: \"completed\" | \"aborted\" }\n | { outcome: \"failed\"; error: string }\n ): Promise<void> {\n const turn = this.#turn(conversationId, operationId);\n if (!turn || turn.status === \"settled\") return;\n // Only the operation driving the turn settles it.\n if ((turn.operation ?? turn.turnId) !== operationId) return;\n // The turn's messages are what follows its user message.\n const start = link.messages.findIndex((m) => m.id === turn.messageId);\n const messageIds: string[] = [];\n for (const message of start === -1 ? [] : link.messages.slice(start + 1)) {\n if (message.role === \"user\") break;\n messageIds.push(message.id);\n }\n // Completed with a tool call still unanswered: the turn waits for it.\n const waiting =\n end.outcome === \"completed\" &&\n link.messages.some(\n (message) => messageIds.includes(message.id) && awaitsInput(message)\n );\n await this.#save(conversationId, {\n turnId: turn.turnId,\n startedBy: turn.startedBy,\n messageId: turn.messageId,\n ...(turn.operation !== undefined && { operation: turn.operation }),\n status: \"settled\",\n messageIds,\n ...(waiting ? { outcome: \"awaiting-input\" as const } : end)\n });\n }\n\n // ── Turn records ───────────────────────────────────────────────────────\n\n async #save(conversationId: string, turn: Turn): Promise<void> {\n this.#host.kv().put(turnKey(conversationId, turn.turnId), turn);\n await this.#host.publishTurn(conversationId, turn);\n }\n\n #turnRecord(conversationId: string, turnId: string): Turn | undefined {\n return this.#host.kv().get<Turn>(turnKey(conversationId, turnId));\n }\n\n /** The turn an operation belongs to: its own, or the one it answers. */\n #turn(conversationId: string, operationId: string): Turn | undefined {\n const turnId =\n this.#host.kv().get<string>(aliasKey(conversationId, operationId)) ??\n operationId;\n return this.#turnRecord(conversationId, turnId);\n }\n\n #turns(conversationId: string): Turn[] {\n const prefix = turnKey(conversationId, \"\");\n return [...this.#host.kv().list<Turn>({ prefix })].map(([, turn]) => turn);\n }\n}\n\n/** One watched session: its transcript and the response of its run. */\nclass SessionLink {\n messages: TranscriptMessage[];\n response: ResponseWriter | undefined;\n /** The saved message the run's response continues, if any. */\n extends: string | undefined;\n\n constructor(\n readonly session: HarnessSession,\n readonly watch: SessionWatch\n ) {\n this.messages = [...watch.state.messages];\n }\n\n upsert(message: TranscriptMessage): void {\n const index = this.messages.findIndex((m) => m.id === message.id);\n if (index === -1) this.messages.push(message);\n else this.messages[index] = message;\n }\n\n append(chunk: ResponseChunk): void {\n try {\n this.response?.append(chunk);\n } catch (error) {\n console.warn(\"Dropped a harness chunk\", chunk.type, error);\n }\n }\n\n endResponse(): void {\n this.response?.end();\n this.response = undefined;\n this.extends = undefined;\n }\n}\n\nfunction turnKey(conversationId: string, turnId: string): string {\n // The quoted id keeps one conversation's prefix from matching another's.\n return `${PREFIX}turn:${JSON.stringify(conversationId)}:${turnId}`;\n}\n\nfunction aliasKey(conversationId: string, operationId: string): string {\n return `${PREFIX}alias:${JSON.stringify(conversationId)}:${operationId}`;\n}\n\n/** Whether the message has a tool call waiting for a result or approval. */\nfunction awaitsInput(message: TranscriptMessage): boolean {\n return message.parts.some(\n (part) =>\n part.type === \"tool\" &&\n (part.state === \"input-available\" || part.state === \"approval-requested\")\n );\n}\n\nfunction isOpen(turn: TurnStatus): boolean {\n return turn.status !== \"settled\" || turn.outcome === \"awaiting-input\";\n}\n\nfunction inputParts(message: TranscriptMessage): InputPart[] {\n return message.parts.flatMap((part): InputPart[] => {\n if (part.type === \"text\") return [{ type: \"text\", text: part.text }];\n if (part.type === \"file\") {\n return [\n {\n type: \"file\",\n mediaType: part.mediaType,\n url: part.url,\n ...(part.filename !== undefined && { filename: part.filename })\n }\n ];\n }\n return [];\n });\n}\n","import type { StreamJson, StreamWriter } from \"../../streams\";\nimport type { ResponseChunk } from \"./protocol\";\n\n/** A chunk that breaks the response grammar. Nothing was recorded. */\nexport class ResponseGrammarError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ResponseGrammarError\";\n }\n}\n\n/** Producer handle for one response. */\nexport type ResponseWriter = {\n readonly id: string;\n /** Records the chunk and wakes readers. Throws if it cannot be recorded. */\n append(chunk: ResponseChunk): void;\n end(): void;\n /**\n * Append every chunk, then end the response. If the source or an append\n * fails, settles the response as interrupted and rejects.\n */\n pipe(chunks: AsyncIterable<ResponseChunk>): Promise<void>;\n};\n\nexport function responseWriter(writer: StreamWriter): ResponseWriter {\n // Open parts, keyed by kind and id. Text and reasoning ids are separate.\n const open = new Set<string>();\n\n // Checks the chunk and returns the change it makes to the open parts,\n // applied only once the chunk is recorded.\n function check(chunk: ResponseChunk): (() => void) | undefined {\n const [kind, id, step] = partOf(chunk);\n if (!kind) return undefined;\n const key = `${kind}:${id}`;\n if (step === \"start\") {\n if (open.has(key)) fail(`${kind} \"${id}\" is already open`);\n return () => open.add(key);\n }\n if (step !== \"close\" && !open.has(key)) fail(`${kind} \"${id}\" is not open`);\n return step === \"delta\" ? undefined : () => open.delete(key);\n }\n\n const response: ResponseWriter = {\n id: writer.streamId,\n append(chunk) {\n const apply = check(chunk);\n // SAFETY: ResponseChunk fields are JSON, and undefined optional fields\n // are dropped when the chunk is serialized.\n writer.append(chunk as unknown as StreamJson);\n apply?.();\n },\n end() {\n writer.close();\n },\n async pipe(chunks) {\n try {\n for await (const chunk of chunks) response.append(chunk);\n } catch (error) {\n writer.error(\"interrupted\");\n throw error;\n }\n response.end();\n }\n };\n return response;\n}\n\n// `close` ends a part that may never have been started.\ntype PartStep = \"start\" | \"delta\" | \"end\" | \"close\";\n\nfunction partOf(\n chunk: ResponseChunk\n): [kind: string, id: string, step: PartStep] | [kind: undefined] {\n switch (chunk.type) {\n case \"text-start\":\n return [\"text\", chunk.id, \"start\"];\n case \"text-delta\":\n return [\"text\", chunk.id, \"delta\"];\n case \"text-end\":\n return [\"text\", chunk.id, \"end\"];\n case \"reasoning-start\":\n return [\"reasoning\", chunk.id, \"start\"];\n case \"reasoning-delta\":\n return [\"reasoning\", chunk.id, \"delta\"];\n case \"reasoning-end\":\n return [\"reasoning\", chunk.id, \"end\"];\n case \"tool-input-start\":\n return [\"tool input\", chunk.toolCallId, \"start\"];\n case \"tool-input-delta\":\n return [\"tool input\", chunk.toolCallId, \"delta\"];\n case \"tool-input-available\":\n case \"tool-input-error\":\n return [\"tool input\", chunk.toolCallId, \"close\"];\n default:\n return [undefined];\n }\n}\n\nfunction fail(message: string): never {\n throw new ResponseGrammarError(message);\n}\n","// Imported from their modules, not \"../lifecycle\", which loads\n// cloudflare:workers and would stop agents/experimental/channels loading outside Workers.\nimport { LifecycleCapability } from \"../../lifecycle/capability\";\nimport { Streams } from \"../../streams/streams\";\nimport type { WebSockets } from \"../../websockets\";\nimport type {\n GatewayOrigin,\n ConversationInfo,\n ConversationOperation,\n ConversationSnapshot,\n DispatchResult,\n EventOrigin,\n InboundEvent,\n ResponseChunk,\n TranscriptMessage,\n TurnStatus\n} from \"./protocol\";\nimport type { ChannelMessageSurface } from \"./surface\";\nimport type { AgentHarness } from \"./harness\";\nimport { HarnessConversations, isTurnEvent } from \"./harness-conversations\";\nimport { responseWriter, type ResponseWriter } from \"./response\";\n\nconst TAG_PREFIX = \"channels:\";\nconst GENERIC_FAILURE = \"The turn failed.\";\n// How many recent event ids Channels remembers so a repeated event, such as\n// one a client resends after reconnecting, reaches the agent once. Past this\n// the oldest is forgotten, and a repeat that old is delivered again.\nconst SEEN_EVENTS_LIMIT = 1000;\n\n/**\n * An inbound event as the gateway sends it. An approval response from a\n * webhook names no turn; Channels finds it from the conversation snapshot.\n */\nexport type GatewayEvent =\n | InboundEvent\n | Omit<Extract<InboundEvent, { type: \"approval-response\" }>, \"turnId\">;\n\n/** How a response finished, as a reader sees it. */\nexport type ResponseEnding = \"ended\" | \"interrupted\" | \"not-found\";\n\n/** A change Channels pushes to every channel showing a conversation. */\nexport type ConversationUpdate =\n | { type: \"turn\"; turn: TurnStatus }\n | {\n type: \"messages\";\n messages: TranscriptMessage[];\n /** Set when the messages arrived as an inbound event. */\n origin?: EventOrigin;\n /** The inbound event's id, set with `origin`. */\n eventId?: string;\n }\n | { type: \"reset\" }\n | { type: \"response-end\"; responseId: string; ending: ResponseEnding };\n\nexport type ResponseReadOptions = {\n from?: number;\n signal?: AbortSignal;\n onChunks(from: number, chunks: ResponseChunk[]): void;\n /** Everything recorded so far has been read; what follows is live. */\n onCaughtUp(cursor: number): void;\n};\n\n/** Durable state a channel keeps in the agent, private to that channel. */\nexport type ChannelState = {\n get<T>(key: string): T | undefined;\n put(key: string, value: unknown): void;\n delete(key: string): void;\n};\n\n/** What Channels gives a mounted channel. */\nexport type ChannelsHost = {\n readonly channelKey: string;\n /**\n * The WebSockets this Channels' channels share: the instance Channels was\n * given, or else the one the first channel to ask creates. Install it with\n * `Lifecycle.use(channels.websockets)`.\n */\n websockets(create: () => WebSockets): WebSockets;\n /** This channel's surfaces that have joined the conversation. */\n surfaces(conversationId: string): Promise<ChannelMessageSurface[]>;\n readonly state: ChannelState;\n snapshot(conversationId: string): Promise<ConversationSnapshot>;\n /** The conversation a surface joins when it names none. */\n defaultConversation(): string;\n /** The conversation operations the agent supports. */\n readonly operations: readonly ConversationOperation[];\n /** The agent's conversations. */\n listConversations(): Promise<ConversationInfo[]>;\n /**\n * Deliver an event to the agent. Resolves once the agent accepted it and\n * rejects if the agent threw.\n */\n dispatch(event: InboundEvent, origin: EventOrigin): Promise<DispatchResult>;\n readResponse(\n conversationId: string,\n responseId: string,\n options: ResponseReadOptions\n ): Promise<ResponseEnding>;\n};\n\n/** An interface that shows conversations, such as the Web Channel. */\nexport interface ConversationChannel {\n /** Called once, when Channels is constructed. Take shared dependencies here. */\n mount(host: ChannelsHost): void;\n publish(\n conversationId: string,\n update: ConversationUpdate\n ): void | Promise<void>;\n}\n\nexport type ChannelsOptions = {\n /**\n * The agent: its sessions are served as conversations, a conversation's\n * id being its session's id.\n */\n harness: AgentHarness;\n /** Install it before Channels. */\n streams: Streams;\n /** Shared with channels that serve WebSockets, such as the Web Channel. */\n websockets?: WebSockets;\n channels: Record<string, ConversationChannel>;\n};\n\nexport type ForHarnessOptions = {\n channels: Record<string, ConversationChannel>;\n /** Default: a new Streams, exposed as `channels.streams`. */\n streams?: Streams;\n /** Default: one the Web Channel creates, exposed as `channels.websockets`. */\n websockets?: WebSockets;\n};\n\n/**\n * Serves an agent harness's sessions as conversations: carries inbound\n * events to the harness, and responses, turn statuses and transcript\n * updates back to every channel showing the conversation.\n */\nexport class Channels extends LifecycleCapability {\n /**\n * Channels for any shared-interface harness, with their own Streams and\n * (when a channel needs them) WebSockets. Lifecycle does not install one\n * capability from another yet, so install them alongside:\n *\n * ```ts\n * readonly channels = Channels.forHarness(harness, {\n * channels: { web: new WebChannel() }\n * });\n * readonly lifecycle = Lifecycle.install(this)\n * .use(this.harness)\n * .use(this.channels.streams)\n * .use(this.channels)\n * .use(this.channels.websockets);\n *\n * receive(event: GatewayEvent, origin: GatewayOrigin) {\n * return this.channels.receive(event, origin);\n * }\n * ```\n */\n static forHarness(\n harness: AgentHarness,\n options: ForHarnessOptions\n ): Channels {\n return new Channels({\n harness,\n channels: options.channels,\n streams: options.streams ?? new Streams(),\n ...(options.websockets && { websockets: options.websockets })\n });\n }\n\n /** The responses' Streams. Install it before Channels. */\n readonly streams: Streams;\n #websockets: WebSockets | undefined;\n readonly #harness: HarnessConversations;\n readonly #channels: [string, ConversationChannel][];\n readonly #queues = new Map<string, Promise<unknown>>();\n /** Recent events by key, each with the outcome repeats receive. */\n readonly #seen = new Map<string, Promise<DispatchResult>>();\n\n constructor(options: ChannelsOptions) {\n super(\"channels\");\n this.streams = options.streams;\n this.#websockets = options.websockets;\n this.#harness = new HarnessConversations(options.harness, {\n kv: () => this.lifecycle.storage.kv,\n openResponse: (conversationId, turnId) =>\n this.#openResponse(conversationId, turnId),\n publishTurn: (conversationId, turn) =>\n this.#publish(conversationId, { type: \"turn\", turn: statusOf(turn) }),\n publishMessages: (conversationId, messages) =>\n this.#publish(conversationId, { type: \"messages\", messages }),\n reset: (conversationId) => this.#reset(conversationId)\n });\n this.#channels = Object.entries(options.channels);\n for (const [channelKey, channel] of this.#channels) {\n channel.mount(this.#host(channelKey));\n }\n }\n\n /**\n * The WebSockets the channels share. Install it with Lifecycle. Throws\n * when no channel uses WebSockets.\n */\n get websockets(): WebSockets {\n if (!this.#websockets) {\n throw new Error(\"No channel uses WebSockets\");\n }\n return this.#websockets;\n }\n\n /** Mark responses left streaming by an earlier instance as interrupted. */\n async onStart(): Promise<void> {\n const streaming = await this.streams.list({\n state: \"streaming\",\n // Streams lists 100 by default. A loop until none are left would drop\n // the cap, but `list()` matches tags exactly, not by prefix, so streams\n // other capabilities left streaming would come back on every pass.\n limit: 1000\n });\n for (const status of streaming) {\n const conversationId = conversationOf(status.tag);\n if (conversationId === undefined) continue;\n const writer = await this.streams.open(status.streamId);\n writer.error(\"interrupted\");\n await this.#publish(conversationId, {\n type: \"response-end\",\n responseId: status.streamId,\n ending: \"interrupted\"\n });\n }\n // Follow harness sessions again; a run in progress opens a new response.\n await this.#harness.attachAll();\n }\n\n /**\n * Deliver an inbound event from the gateway, through the same path as\n * the agent's own channels. The agent calls this from its RPC method.\n */\n async receive(\n event: GatewayEvent,\n from: GatewayOrigin | EventOrigin\n ): Promise<void> {\n const origin: EventOrigin = {\n conversationId:\n from.conversationId ?? this.#harness.defaultConversation(),\n participant: from.participant,\n surface: from.surface\n };\n // Gateway surfaces (Slack, Telegram) never move to another\n // conversation, so they take no conversation operations.\n if (isConversationOperation(event.type)) {\n throw new Error(`${event.type} is not supported on this surface`);\n }\n // The surface joins the conversation, so every turn reaches it.\n const { surface } = origin;\n this.lifecycle.storage.kv.put(\n surfaceKey(origin.conversationId, surface.channelKey, surface.address),\n surface\n );\n await this.#dispatch(\n event.type !== \"approval-response\" || \"turnId\" in event\n ? event\n : await this.#withTurn(event, origin),\n origin\n );\n }\n\n /** Open a new response for a turn. Each run opens one. */\n async #openResponse(\n conversationId: string,\n turnId: string\n ): Promise<ResponseWriter> {\n const writer = await this.streams.open(crypto.randomUUID(), {\n tag: TAG_PREFIX + conversationId,\n metadata: { turnId }\n });\n return responseWriter(writer);\n }\n\n /**\n * After the harness reset a session: delete the conversation's responses,\n * stopping live producers, then push an empty snapshot.\n */\n async #reset(conversationId: string): Promise<void> {\n const streams = this.streams;\n const tag = TAG_PREFIX + conversationId;\n for (;;) {\n const batch = await streams.list({ tag });\n if (batch.length === 0) break;\n for (const { streamId, state } of batch) {\n if (state === \"streaming\") (await streams.open(streamId)).close();\n await streams.delete(streamId);\n }\n }\n // Events accepted before the reset may be sent again to rebuild it.\n const prefix = JSON.stringify([conversationId]).slice(0, -1) + \",\";\n for (const key of this.#seen.keys()) {\n if (key.startsWith(prefix)) this.#seen.delete(key);\n }\n await this.#publish(conversationId, { type: \"reset\" });\n }\n\n async #publish(\n conversationId: string,\n update: ConversationUpdate\n ): Promise<void> {\n await Promise.all(\n this.#channels.map(async ([channelKey, channel]) => {\n try {\n await channel.publish(conversationId, update);\n } catch (error) {\n console.error(`Channel \"${channelKey}\" failed to publish`, error);\n }\n })\n );\n }\n\n #host(channelKey: string): ChannelsHost {\n const kv = () => this.lifecycle.storage.kv;\n const stateKey = (key: string) =>\n `${TAG_PREFIX}state:${JSON.stringify([channelKey, key])}`;\n return {\n channelKey,\n websockets: (create) => (this.#websockets ??= create()),\n surfaces: async (conversationId) => {\n const prefix = surfaceKey(conversationId, channelKey);\n return [...kv().list<ChannelMessageSurface>({ prefix })].map(\n ([, surface]) => surface\n );\n },\n state: {\n get: (key) => kv().get(stateKey(key)),\n put: (key, value) => kv().put(stateKey(key), value),\n delete: (key) => void kv().delete(stateKey(key))\n },\n snapshot: async (conversationId) => {\n const snapshot = await this.#runInHost(() =>\n this.#harness.snapshot(conversationId)\n );\n return { ...snapshot, turns: snapshot.turns.map(statusOf) };\n },\n defaultConversation: () => this.#harness.defaultConversation(),\n operations: CONVERSATION_OPERATIONS,\n listConversations: () => this.#runInHost(() => this.#harness.list()),\n dispatch: (event, origin) => this.#dispatch(event, origin),\n readResponse: (conversationId, responseId, options) =>\n this.#readResponse(conversationId, responseId, options)\n };\n }\n\n /** Events take effect in arrival order, one at a time per conversation. */\n #dispatch(event: InboundEvent, origin: EventOrigin): Promise<DispatchResult> {\n const key = JSON.stringify([origin.conversationId, event.eventId]);\n const seen = this.#seen.get(key);\n if (seen) return seen;\n\n const previous = this.#queues.get(origin.conversationId);\n const run = async (): Promise<DispatchResult> => {\n let result: DispatchResult = {};\n try {\n result = await this.#runInHost(async () => {\n if (isTurnEvent(event)) {\n await this.#harness.onEvent(event, origin);\n return {};\n }\n return this.#operate(event, origin);\n });\n } catch (error) {\n // A rejected event may be sent again.\n this.#seen.delete(key);\n throw error;\n }\n if (event.type === \"message\") {\n await this.#publish(origin.conversationId, {\n type: \"messages\",\n messages: [event.message],\n origin,\n eventId: event.eventId\n });\n }\n return result;\n };\n const result = previous ? previous.then(run, run) : run();\n this.#seen.set(key, result);\n if (this.#seen.size > SEEN_EVENTS_LIMIT) {\n this.#seen.delete(this.#seen.keys().next().value as string);\n }\n const settled = result.catch(() => {});\n this.#queues.set(origin.conversationId, settled);\n void settled.then(() => {\n if (this.#queues.get(origin.conversationId) === settled) {\n this.#queues.delete(origin.conversationId);\n }\n });\n return result;\n }\n\n /**\n * Act on the conversation itself. Any participant may create, fork or\n * reset: the agent object is the authorization boundary, and the gateway\n * decides who reaches it.\n */\n async #operate(\n event: InboundEvent,\n origin: EventOrigin\n ): Promise<DispatchResult> {\n const harness = this.#harness;\n switch (event.type) {\n case \"conversation-create\":\n return { conversationId: await harness.create() };\n case \"conversation-fork\":\n return { conversationId: await harness.fork(origin.conversationId) };\n case \"conversation-reset\":\n await harness.reset(origin.conversationId, event.handoff);\n return {};\n default:\n throw new Error(`${event.type} is not a conversation operation`);\n }\n }\n\n /** Name the turn awaiting an approval that arrived without one. */\n async #withTurn(\n event: Exclude<GatewayEvent, InboundEvent>,\n origin: EventOrigin\n ): Promise<InboundEvent> {\n const { messages, turns } = await this.#runInHost(() =>\n this.#harness.snapshot(origin.conversationId)\n );\n const message = messages.find((m) =>\n m.parts.some(\n (part) =>\n part.type === \"tool\" &&\n part.state === \"approval-requested\" &&\n part.approval?.id === event.approvalId\n )\n );\n const turn = turns.find(\n (t) =>\n t.status === \"settled\" &&\n message !== undefined &&\n t.messageIds.includes(message.id)\n );\n if (!turn) throw new Error(\"No turn is waiting for this approval\");\n return { ...event, turnId: turn.turnId };\n }\n\n async #readResponse(\n conversationId: string,\n responseId: string,\n options: ResponseReadOptions\n ): Promise<ResponseEnding> {\n const streams = this.streams;\n const status = await streams.status(responseId);\n if (status?.tag !== TAG_PREFIX + conversationId) return \"not-found\";\n let cursor = options.from ?? 0;\n for await (const batch of streams.readBatches(responseId, {\n from: cursor,\n signal: options.signal,\n onUpToDate: () => options.onCaughtUp(cursor)\n })) {\n options.onChunks(\n batch[0].seq,\n // SAFETY: Channels records only chunks that passed the grammar.\n batch.map((item) => item.chunk as unknown as ResponseChunk)\n );\n cursor = batch[batch.length - 1].seq + 1;\n }\n const ending = await streams.status(responseId);\n return ending?.state === \"errored\" ? \"interrupted\" : \"ended\";\n }\n\n async #runInHost<T>(fn: () => Promise<T>): Promise<T> {\n // SAFETY: runInHostContext resolves with fn's own result.\n return (await this.lifecycle.runInHostContext(fn)) as T;\n }\n}\n\n/**\n * Where a surface is recorded. Without an address, the prefix for all of a\n * channel's surfaces in the conversation.\n */\nfunction surfaceKey(\n conversationId: string,\n channelKey: string,\n address?: unknown\n): string {\n const scope = `${TAG_PREFIX}surface:${JSON.stringify([conversationId, channelKey])}:`;\n return address === undefined ? scope : scope + JSON.stringify(address);\n}\n\nconst CONVERSATION_OPERATIONS: readonly ConversationOperation[] = [\n \"conversation-create\",\n \"conversation-fork\",\n \"conversation-reset\"\n];\n\nfunction isConversationOperation(type: string): type is ConversationOperation {\n return (CONVERSATION_OPERATIONS as readonly string[]).includes(type);\n}\n\n/** A turn status with only its protocol fields. */\nfunction statusOf(turn: TurnStatus): TurnStatus {\n const { turnId, startedBy } = turn;\n switch (turn.status) {\n case \"queued\":\n return { turnId, startedBy, status: \"queued\" };\n case \"running\": {\n const { responseId, extends: extended } = turn;\n return {\n turnId,\n startedBy,\n status: \"running\",\n responseId,\n ...(extended !== undefined && { extends: extended })\n };\n }\n case \"settled\": {\n const { messageIds } = turn;\n return turn.outcome === \"failed\"\n ? {\n turnId,\n startedBy,\n status: \"settled\",\n outcome: \"failed\",\n messageIds,\n error: turn.error ?? GENERIC_FAILURE\n }\n : {\n turnId,\n startedBy,\n status: \"settled\",\n outcome: turn.outcome,\n messageIds\n };\n }\n }\n}\n\nfunction conversationOf(tag: string | undefined): string | undefined {\n return tag?.startsWith(TAG_PREFIX) ? tag.slice(TAG_PREFIX.length) : undefined;\n}\n","import { TextSegmentJoiner } from \"../../chat/text-segment-joiner\";\nimport type { Awaitable } from \"./channel\";\nimport type { ResponseChunk } from \"./protocol\";\n\n/** Why a consumption loop stopped reading. */\nexport type StreamOutcome =\n | { interrupted: false }\n | { interrupted: true; error: unknown };\n\nexport type ChunkConsumer<TChunk, TResult> = {\n onChunk(chunk: TChunk): Awaitable<void>;\n /** Runs exactly once, whether the stream closed or ended abnormally. */\n onFinish(outcome: StreamOutcome): Awaitable<TResult>;\n};\n\n/** The complete text of a stream, and whether it ended before its answer did. */\nexport type CollectedText = {\n text: string;\n interrupted: boolean;\n};\n\n/**\n * Read a stream to completion, then finalize exactly once.\n *\n * `onFinish` runs whether the stream closed normally, errored because the\n * generation failed, or stopped because `onChunk` threw. A Channel that\n * finalizes here cannot lose a terminal provider call to an early ending.\n */\nexport async function consumeChunks<TChunk, TResult>(\n chunks: ReadableStream<TChunk>,\n consumer: ChunkConsumer<TChunk, TResult>\n): Promise<TResult> {\n const reader = chunks.getReader();\n let outcome: StreamOutcome = { interrupted: false };\n let cancellation: Promise<void> | undefined;\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n await consumer.onChunk(value);\n }\n } catch (error) {\n outcome = { interrupted: true, error };\n // Start cancellation before finalizing, but do not block the terminal\n // provider call on sibling tee branches. Await cleanup only afterward.\n cancellation = reader.cancel().catch(() => {});\n } finally {\n reader.releaseLock();\n }\n try {\n return await consumer.onFinish(outcome);\n } finally {\n await cancellation;\n }\n}\n\n/**\n * Turn a response's chunks into answer text, one chunk at a time. Deltas of\n * one part join directly; a new text part or tool activity in between adds a\n * space. Metadata and data never split text. Returns the text each chunk adds.\n */\nexport function createTextCollector(): (chunk: ResponseChunk) => string {\n const joiner = new TextSegmentJoiner();\n return (chunk) => {\n if (\n chunk.type === \"metadata\" ||\n chunk.type === \"data\" ||\n chunk.type === \"text-end\"\n ) {\n return \"\";\n }\n let added = \"\";\n for (const event of joiner.pushChunk(\n chunk.type === \"text-delta\"\n ? { type: \"text-delta\", text: chunk.delta }\n : { type: chunk.type }\n )) {\n if (event.type === \"text\") added += event.text;\n }\n return added;\n };\n}\n\n/**\n * Collect a response's text into one Markdown answer.\n *\n * The result reports interruption instead of throwing, because a Channel that\n * has partial text still has to decide what to deliver.\n */\nexport function collectText(\n chunks: ReadableStream<ResponseChunk>\n): Promise<CollectedText> {\n let text = \"\";\n const collect = createTextCollector();\n return consumeChunks(chunks, {\n onChunk(chunk) {\n text += collect(chunk);\n },\n onFinish: (outcome) => ({ text, interrupted: outcome.interrupted })\n });\n}\n\n/** A finished message as a response's chunks. */\nexport function messageChunks(markdown: string): ReadableStream<ResponseChunk> {\n return new ReadableStream({\n start(controller) {\n controller.enqueue({ type: \"text-start\", id: \"message\" });\n controller.enqueue({\n type: \"text-delta\",\n id: \"message\",\n delta: markdown\n });\n controller.enqueue({ type: \"text-end\", id: \"message\" });\n controller.close();\n }\n });\n}\n\n/**\n * Pace repeated provider calls without dropping anything.\n *\n * A Channel accumulates into its own buffer and asks whether enough time has\n * passed to flush. Keeping the buffer in the Channel means an interrupted\n * stream leaves its tail in hand, ready for the terminal provider call, rather\n * than stranded inside a transform.\n */\nexport function createPacer(intervalMs: number): () => boolean {\n let lastFlushAt = Number.NEGATIVE_INFINITY;\n return () => {\n const now = Date.now();\n if (now - lastFlushAt < intervalMs) return false;\n lastFlushAt = now;\n return true;\n };\n}\n","/**\n * A JSON value safe to persist across Worker and Durable Object boundaries.\n *\n * The recursive arms are interfaces rather than inline type literals so that\n * TypeScript defers resolving them. Workers RPC checks a return type with\n * `R extends Rpc.Serializable<R>`, which eagerly expands a self-referential\n * type alias and fails with \"Type instantiation is excessively deep\". Deferred\n * arms let a surface be returned from a Durable Object method.\n */\nexport type ChannelSurfaceValue =\n | null\n | boolean\n | number\n | string\n | ChannelSurfaceArray\n | ChannelSurfaceObject;\n\nexport interface ChannelSurfaceArray extends ReadonlyArray<ChannelSurfaceValue> {}\n\nexport interface ChannelSurfaceObject {\n readonly [key: string]: ChannelSurfaceValue;\n}\n\nfunction isSurfaceValue(value: unknown): value is ChannelSurfaceValue {\n if (\n value === null ||\n typeof value === \"boolean\" ||\n typeof value === \"string\"\n ) {\n return true;\n }\n if (typeof value === \"number\") return Number.isFinite(value);\n if (Array.isArray(value)) return value.every(isSurfaceValue);\n return (\n typeof value === \"object\" && Object.values(value).every(isSurfaceValue)\n );\n}\n\n/** A durable configured destination for one outbound message attempt. */\nexport type ChannelMessageSurface<\n TChannelKey extends string = string,\n TAddress extends ChannelSurfaceValue = ChannelSurfaceValue\n> = Readonly<{\n channelKey: TChannelKey;\n version: 1;\n address: TAddress;\n /** Human-readable destination text captured when the surface is created. */\n label: string;\n}>;\n\n/** A Channel-produced destination before its Host stamps the configured key. */\nexport type ChannelMessageSurfaceInput<\n TAddress extends ChannelSurfaceValue = ChannelSurfaceValue\n> = Readonly<{\n version: 1;\n address: TAddress;\n label: string;\n}>;\n\n/** Validate the common envelope before provider-specific address parsing. */\nexport function isChannelMessageSurface(\n value: unknown\n): value is ChannelMessageSurface {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n return false;\n }\n const surface = value as Record<string, unknown>;\n return (\n typeof surface.channelKey === \"string\" &&\n surface.channelKey.length > 0 &&\n surface.version === 1 &&\n isSurfaceValue(surface.address) &&\n typeof surface.label === \"string\" &&\n surface.label.trim().length > 0\n );\n}\n","import type {\n Channel,\n ChannelChunkSource,\n ChannelDeliveryOptions,\n ChannelMessage,\n ChannelStreamOptions,\n ChannelUpgrade,\n ChannelUpgradeMatch,\n DeliveryResult\n} from \"./channel\";\nimport type { ChannelIdentity, ChannelIdentityInput } from \"./identity\";\nimport { participantRoute, toParticipant, unsupported } from \"./internal\";\nimport { collectText, messageChunks } from \"./stream\";\nimport type {\n ChannelEmailInput,\n ChannelIngressEnvelope,\n ChannelIngressEvent,\n ChannelIngressEventInput\n} from \"./ingress\";\nimport type { GatewayOrigin, MessagePart, Participant } from \"./protocol\";\nimport {\n isChannelMessageSurface,\n type ChannelMessageSurface,\n type ChannelMessageSurfaceInput\n} from \"./surface\";\nimport type { GatewayEvent } from \"./conversations\";\nimport { WEB_IDENTITY_HEADER, type WebIdentity } from \"./web/protocol\";\n\nexport type ChannelRouteEvent = {\n channelKey: string;\n event: ChannelIngressEvent;\n /** Who the Channel resolved the sender as; null when it refused them. */\n participant: Participant | null;\n /** The agent object the event reaches; null when it is ignored or refused. */\n route: string | null;\n /** Stable identity derived only from the configured Channel and eventId. */\n dispatchId: string;\n};\n\n/** The agent object a route names, as the gateway reaches it. */\nexport type GatewayAgent = {\n /** Hand the agent an inbound event; Channels' `receive` serves it. */\n receive(event: GatewayEvent, origin: GatewayOrigin): Promise<unknown>;\n fetch(request: Request): Promise<Response>;\n};\n\nexport type ChannelGatewayOptions = {\n /**\n * The Channels this Worker serves. Each one that takes ingress says who\n * its senders are (`participant`) and which agent object they reach\n * (`route`).\n */\n channels: Record<string, Channel>;\n /** The agent object a route names. */\n agent(route: string): GatewayAgent;\n /** Observes every valid route outcome before it is sent to the agent. */\n onRoute?(event: ChannelRouteEvent): void | Promise<void>;\n};\n\ntype OutboundOperation = (\n channel: Channel,\n surface: ChannelMessageSurface\n) => Promise<DeliveryResult>;\n\n/**\n * The Worker's entry point for Channels. Authenticates and normalizes\n * webhooks, resolves WebSocket upgrades, routes each to the agent object\n * its route names, and sends to surfaces.\n *\n * The gateway is the trust boundary. It alone decides who a sender is and\n * tells the agent, so an agent serving Channels must be reachable only\n * through it: an app that forwards an upgrade to the agent some other way\n * (another fetch handler, `routeAgentRequest`, RPC) lets the caller pick\n * its own participant and conversation.\n *\n * The agent object is the authorization boundary. Whoever a Channel routes\n * to an object may list, join, create, fork and reset every conversation in\n * it. To keep people apart, route them to different objects; by default\n * each participant gets an object of their own.\n */\nexport class ChannelGateway {\n readonly #channels: Record<string, Channel>;\n readonly #agent: ChannelGatewayOptions[\"agent\"];\n readonly #onRoute: ChannelGatewayOptions[\"onRoute\"];\n\n constructor(options: ChannelGatewayOptions) {\n this.#channels = { ...options.channels };\n for (const [channelKey, channel] of Object.entries(this.#channels)) {\n if ((channel.ingress || channel.emailIngress) && !channel.participant) {\n throw new Error(\n `Channel \"${channelKey}\" takes ingress but has no participant; say who its senders are`\n );\n }\n }\n this.#agent = options.agent;\n this.#onRoute = options.onRoute;\n }\n\n /** Serve a request if it is for Channels, or return undefined. */\n async fetch(request: Request): Promise<Response | undefined> {\n if (request.headers.get(\"Upgrade\")?.toLowerCase() === \"websocket\") {\n const web = await this.#upgrade(request);\n if (web) return web;\n }\n return this.#webhook(request);\n }\n\n /**\n * Forward a WebSocket upgrade to its agent with the resolved identity.\n * Any identity header the client sent is replaced. An upgrade no Channel\n * takes is left to the Worker, header and all, so the Worker must not\n * forward it to a Channels agent.\n */\n async #upgrade(request: Request): Promise<Response | undefined> {\n for (const [channelKey, channel] of Object.entries(this.#channels)) {\n const upgrade = channel.upgrade;\n const match = upgrade?.match(request);\n if (!upgrade || !match) continue;\n return this.#forwardUpgrade(channelKey, upgrade, match, request);\n }\n return undefined;\n }\n\n async #forwardUpgrade(\n channelKey: string,\n upgrade: ChannelUpgrade,\n match: ChannelUpgradeMatch,\n request: Request\n ): Promise<Response> {\n const source = `Channel \"${channelKey}\"`;\n const participant = toParticipant(\n await upgrade.participant(request),\n source\n );\n if (!participant) return new Response(\"Unauthorized\", { status: 401 });\n const route = checkRoute(await upgrade.route(request, participant), source);\n if (route === null) return new Response(\"Forbidden\", { status: 403 });\n const identity: WebIdentity = {\n route,\n ...(match.conversationId !== undefined && {\n conversationId: match.conversationId\n }),\n participant\n };\n const headers = new Headers(request.headers);\n headers.set(WEB_IDENTITY_HEADER, JSON.stringify(identity));\n return this.#agent(route).fetch(new Request(request, { headers }));\n }\n\n async #webhook(request: Request): Promise<Response | undefined> {\n for (const [channelKey, channel] of Object.entries(this.#channels)) {\n const ingress = channel.ingress;\n if (!ingress) continue;\n\n try {\n const result = await ingress.receive(request);\n if (!result) continue;\n for (const envelope of result.events) {\n await this.#dispatch(channelKey, channel, envelope);\n }\n return result.response;\n } catch {\n return new Response(\"Failed to handle Channel event\", { status: 500 });\n }\n }\n return undefined;\n }\n\n async handleEmail(email: ChannelEmailInput): Promise<boolean> {\n for (const [channelKey, channel] of Object.entries(this.#channels)) {\n const ingress = channel.emailIngress;\n if (!ingress) continue;\n\n const result = await ingress.receive(email);\n if (!result) continue;\n for (const envelope of result.events) {\n await this.#dispatch(channelKey, channel, envelope);\n }\n return true;\n }\n return false;\n }\n\n /** Deliver through the configured Channel named by the surface. */\n deliver(\n surface: ChannelMessageSurface,\n message: ChannelMessage,\n options?: ChannelDeliveryOptions\n ): Promise<DeliveryResult> {\n return this.#outbound(surface, (channel, destination) => {\n if (channel.deliver) {\n return channel.deliver(destination, message, options);\n }\n // A Channel that streams renders a message the same way as an answer.\n if (channel.stream) {\n return channel.stream(destination, messageChunks(message.markdown), {\n ...(message.title !== undefined && { title: message.title }),\n ...options\n });\n }\n return Promise.resolve(\n unsupported(\n \"CHANNEL_DELIVERY_UNSUPPORTED\",\n `Channel \"${destination.channelKey}\" does not support delivery`\n )\n );\n });\n }\n\n /**\n * Deliver a progressively generated answer to the Channel\n * named by the surface.\n *\n * A Channel that can stream consumes the stream itself. A Channel that\n * cannot never learns it was a stream, because the Host collects the answer\n * and calls `deliver` once.\n */\n async stream(\n surface: ChannelMessageSurface,\n chunks: ChannelChunkSource,\n options: ChannelStreamOptions = {}\n ): Promise<DeliveryResult> {\n if (!isChannelMessageSurface(surface)) {\n await chunks.cancel().catch(() => {});\n return invalidSurface();\n }\n\n let channel: Channel;\n try {\n channel = this.#configuredChannel(surface.channelKey);\n } catch (error) {\n await chunks.cancel().catch(() => {});\n throw error;\n }\n if (!channel.stream && !channel.deliver) {\n await chunks.cancel().catch(() => {});\n return unsupported(\n \"CHANNEL_DELIVERY_UNSUPPORTED\",\n `Channel \"${surface.channelKey}\" does not support delivery`\n );\n }\n\n if (channel.stream) return channel.stream(surface, chunks, options);\n return collectAndDeliver(channel, surface, chunks, options);\n }\n\n /** Return the identity's configured Channel destination, when supported. */\n contactSurface(identity: ChannelIdentity): ChannelMessageSurface | null {\n const channel = Object.prototype.hasOwnProperty.call(\n this.#channels,\n identity.channelKey\n )\n ? this.#channels[identity.channelKey]\n : undefined;\n const surface = channel?.contactSurface?.(identity);\n return surface ? stampSurface(identity.channelKey, surface) : null;\n }\n\n async #outbound(\n surface: ChannelMessageSurface,\n operation: OutboundOperation\n ): Promise<DeliveryResult> {\n if (!isChannelMessageSurface(surface)) return invalidSurface();\n const channel = this.#configuredChannel(surface.channelKey);\n return operation(channel, surface);\n }\n\n #configuredChannel(channelKey: string): Channel {\n const channel = Object.prototype.hasOwnProperty.call(\n this.#channels,\n channelKey\n )\n ? this.#channels[channelKey]\n : undefined;\n if (!channel) {\n throw new Error(\n `Channel message surface names unknown configured Channel key \"${channelKey}\"`\n );\n }\n return channel;\n }\n\n async #dispatch(\n channelKey: string,\n channel: Channel,\n envelope: ChannelIngressEnvelope\n ): Promise<void> {\n const rawEvent = envelope.event;\n const event = stampEvent(channelKey, rawEvent);\n const source = `Channel \"${channelKey}\"`;\n // The constructor checked that every Channel with ingress has one.\n const participant = toParticipant(\n await channel.participant!(event, envelope.raw),\n source\n );\n const route = participant\n ? checkRoute(\n channel.route\n ? await channel.route(event, envelope.raw, participant)\n : participantRoute(participant),\n source\n )\n : null;\n const dispatchId = await createDispatchId(channelKey, event.eventId);\n await this.#onRoute?.({\n channelKey,\n event,\n participant,\n route,\n dispatchId\n });\n if (!participant || route === null) return;\n if (!event.replySurface) {\n throw new Error(\n `Channel \"${channelKey}\" produced an event without a reply surface`\n );\n }\n await this.#agent(route).receive(toInboundEvent(event, dispatchId), {\n route,\n participant,\n surface: event.replySurface\n });\n }\n}\n\n/** Check what an application's `route` callback returned. */\nfunction checkRoute(route: unknown, source: string): string | null {\n if (route === undefined) {\n throw new Error(\n `${source} route returned undefined; return null to ignore an event`\n );\n }\n if (route !== null && typeof route !== \"string\") {\n throw new Error(`${source} route must return a string or null`);\n }\n return route;\n}\n\n/** The provider's event as an inbound event, keyed by its dispatch id. */\nfunction toInboundEvent(\n event: ChannelIngressEvent,\n dispatchId: string\n): GatewayEvent {\n if (event.type === \"approval-response\") {\n return {\n type: \"approval-response\",\n eventId: dispatchId,\n approvalId: event.approvalId,\n approved: event.decision === \"approve\"\n };\n }\n const { message } = event;\n const parts: MessagePart[] = [\n { type: \"text\", text: message.markdown ?? message.text }\n ];\n for (const attachment of message.attachments ?? []) {\n if (attachment.url && attachment.mediaType) {\n parts.push({\n type: \"file\",\n url: attachment.url,\n mediaType: attachment.mediaType,\n ...(attachment.name !== undefined && { filename: attachment.name })\n });\n }\n }\n return {\n type: \"message\",\n eventId: dispatchId,\n message: { id: dispatchId, role: \"user\", parts }\n };\n}\n\nfunction invalidSurface(): DeliveryResult {\n return unsupported(\n \"CHANNEL_SURFACE_INVALID\",\n \"Cannot resolve an invalid Channel message surface\"\n );\n}\n\n/**\n * Serve a Channel that cannot stream by collecting the answer first.\n *\n * A generation that failed part-way still delivers what it produced, because\n * losing the partial answer helps nobody, but the result is downgraded to\n * `uncertain` since the reader received an incomplete answer.\n */\nasync function collectAndDeliver(\n channel: Channel,\n surface: ChannelMessageSurface,\n stream: ChannelChunkSource,\n options: ChannelStreamOptions\n): Promise<DeliveryResult> {\n const collected = await collectText(stream);\n if (collected.interrupted && collected.text.length === 0) {\n return {\n status: \"failed\",\n retryable: false,\n error: {\n code: \"CHANNEL_STREAM_INTERRUPTED\",\n message: \"The stream ended before producing any content to deliver\"\n }\n };\n }\n\n const result = await channel.deliver!(\n surface,\n {\n ...(options.title !== undefined && { title: options.title }),\n markdown: collected.text\n },\n options.delivery ? { delivery: options.delivery } : undefined\n );\n if (!collected.interrupted || result.status !== \"delivered\") return result;\n return {\n status: \"uncertain\",\n ...(result.reference !== undefined && { reference: result.reference }),\n error: {\n code: \"CHANNEL_STREAM_INTERRUPTED\",\n message:\n \"An incomplete answer was delivered because the stream ended early\"\n }\n };\n}\n\nfunction stampSurface<TAddress extends ChannelMessageSurfaceInput[\"address\"]>(\n channelKey: string,\n surface: ChannelMessageSurfaceInput<TAddress>\n): ChannelMessageSurface<string, TAddress> {\n return { ...surface, channelKey };\n}\n\nfunction stampIdentity(\n channelKey: string,\n identity: ChannelIdentityInput\n): ChannelIdentity {\n return { ...identity, channelKey };\n}\n\nfunction stampEvent(\n channelKey: string,\n event: ChannelIngressEventInput\n): ChannelIngressEvent {\n return {\n ...event,\n ...(event.replySurface && {\n replySurface: stampSurface(channelKey, event.replySurface)\n }),\n ...(event.actor && {\n actor: {\n ...event.actor,\n ...(event.actor.identity && {\n identity: stampIdentity(channelKey, event.actor.identity)\n })\n }\n })\n } as ChannelIngressEvent;\n}\n\n/** Hash an unambiguous tuple so dispatch identities remain safe to carry. */\nasync function createDispatchId(\n channelKey: string,\n eventId: string\n): Promise<string> {\n const identity = new TextEncoder().encode(\n JSON.stringify([channelKey, eventId])\n );\n const digest = await crypto.subtle.digest(\"SHA-256\", identity);\n return `sha256:${Array.from(new Uint8Array(digest), (byte) =>\n byte.toString(16).padStart(2, \"0\")\n ).join(\"\")}`;\n}\n","export type ChannelIdentity = Readonly<{\n /** Configured Channel that observed this identity. */\n channelKey: string;\n /** Stable Channel namespace in which `subject` is unique. @default \"default\" */\n scope?: string;\n /** Stable Channel subject within `scope`. */\n subject: string;\n}>;\n\n/** A Channel-produced identity before its Host stamps the configured key. */\nexport type ChannelIdentityInput = Readonly<{\n scope?: string;\n subject: string;\n}>;\n\nconst DEFAULT_IDENTITY_SCOPE = \"default\";\n\ntype NormalizedChannelIdentity = Readonly<{\n channelKey: string;\n scope: string;\n subject: string;\n}>;\n\nfunction normalizeIdentity(\n identity: ChannelIdentity\n): NormalizedChannelIdentity {\n return {\n channelKey: identity.channelKey,\n scope: identity.scope ?? DEFAULT_IDENTITY_SCOPE,\n subject: identity.subject\n };\n}\n\n/** Build a stable key for comparing or indexing a Channel identity. */\nexport function identityKey(identity: ChannelIdentity): string {\n const normalized = normalizeIdentity(identity);\n return JSON.stringify([\n normalized.channelKey,\n normalized.scope,\n normalized.subject\n ]);\n}\n","import type { ChannelIngressEvent } from \"./ingress\";\nimport { participantRoute } from \"./internal\";\nimport type { Participant } from \"./protocol\";\n\n/**\n * Common deterministic routing policies for normalized Channel events.\n *\n * Each one maps an event to a namespaced agent object. That object is the\n * authorization boundary: anyone routed to it may use every conversation in\n * it. Deciding whether an event is relevant at all is application policy:\n * write that in your own `route` function and return `null` to ignore it.\n */\nexport const routes = {\n /**\n * Give each participant an agent object of their own. The default for every\n * Channel, so that a group chat never shares an object by accident.\n *\n * A surface that names no conversation, such as a Slack thread or an\n * email, joins the object's default conversation, so a participant keeps\n * one history wherever they write from.\n */\n perParticipant(\n _event: ChannelIngressEvent,\n _raw: unknown,\n participant: Participant\n ): string {\n return participantRoute(participant);\n },\n\n /**\n * Give every event its own application route. Useful to kick off a\n * new conversation for each ingress, and where you don't want subsequent\n * messages routed back to that same conversation.\n **/\n perEvent(event: ChannelIngressEvent): string {\n return `event:${event.eventId}`;\n },\n\n /**\n * Send every event in the same thread to the same object, whoever sent\n * it. Everyone in the thread can then use that object's conversations.\n */\n perThread(event: ChannelIngressEvent): string {\n return `thread:${event.thread.id}`;\n }\n};\n"],"mappings":"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"}
@@ -1,12 +1,13 @@
1
1
  import {
2
- F as ChannelMessageSurface,
3
- d as ChannelRoute,
4
- k as ChannelIngress,
2
+ D as ChannelIngress,
3
+ c as ChannelRoute,
5
4
  n as Channel,
6
- u as ChannelMessage
7
- } from "../channel-Bnm4S7T2.js";
5
+ o as ChannelMessage,
6
+ s as ChannelParticipant
7
+ } from "../../../channel-Bak_6zDP.js";
8
+ import { b as ChannelMessageSurface } from "../../../protocol-CukuI73M.js";
8
9
 
9
- //#region src/channels/adapters/slack.d.ts
10
+ //#region src/experimental/channels/slack/index.d.ts
10
11
  /** The subset of a Slack event used by the adapter, with unknown fields retained. */
11
12
  type SlackEvent = {
12
13
  type?: string;
@@ -110,7 +111,16 @@ type SlackChannelOptions = {
110
111
  * one interval are appended together. @default 500
111
112
  */
112
113
  streamIntervalMs?: number /** Add signed Slack HTTP ingress to the returned Channel. */;
113
- webhook?: SlackWebhookOptions /** Select an application route from the event, exact payload, and Host context. */;
114
+ webhook?: SlackWebhookOptions;
115
+ /**
116
+ * Who sent a verified Slack event, such as your user linked to
117
+ * `event.actor`. Return null to drop the event. Required with `webhook`.
118
+ */
119
+ participant?: ChannelParticipant<SlackIngressPayload>;
120
+ /**
121
+ * Pick the agent object an event reaches. Default: a room of the
122
+ * participant's own, so a shared thread is not shared by accident.
123
+ */
114
124
  route?: ChannelRoute<SlackIngressPayload> /** Override fetch for testing or custom network routing. */;
115
125
  fetch?: typeof globalThis.fetch;
116
126
  };
@@ -118,7 +128,7 @@ type SlackChannelOptions = {
118
128
  declare function slackWebhook(
119
129
  options: SlackWebhookOptions
120
130
  ): ChannelIngress<SlackIngressPayload>;
121
- /** Create a configured Slack Web API Channel. */
131
+ /** Create a configured Slack ingress Channel. */
122
132
  declare function slack(
123
133
  options: SlackChannelOptions
124
134
  ): Channel<SlackIngressPayload>;
@@ -137,4 +147,4 @@ export {
137
147
  slack,
138
148
  slackWebhook
139
149
  };
140
- //# sourceMappingURL=slack.d.ts.map
150
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,267 @@
1
+ import { n as encodeUtf8, r as isRecord, t as emptyIngressResponse } from "../../../internal-kmw70xwR.js";
2
+ import { t as matchesPath } from "../../../ingress-EFbrS547.js";
3
+ //#region src/experimental/channels/slack/index.ts
4
+ const DEFAULT_SLACK_WEBHOOK_PATH = "/webhooks/slack";
5
+ const DEFAULT_MAX_SKEW_SECONDS = 300;
6
+ const APPROVE_ACTION_ID = "cloudflare_channels_approve_v1";
7
+ const REJECT_ACTION_ID = "cloudflare_channels_reject_v1";
8
+ function channelIdentity(teamId, channelId) {
9
+ return `slack:${teamId}:channel:${channelId}`;
10
+ }
11
+ function messageIdentity(teamId, channelId, timestamp) {
12
+ return `${channelIdentity(teamId, channelId)}:message:${timestamp}`;
13
+ }
14
+ function replySurfaceLabel(channelId, threadTimestamp) {
15
+ return threadTimestamp ? `Slack · ${channelId} · thread ${threadTimestamp}` : `Slack · ${channelId}`;
16
+ }
17
+ function threadIdentity(teamId, channelId, timestamp, threadTimestamp, isDirectMessage) {
18
+ const channel = channelIdentity(teamId, channelId);
19
+ if (isDirectMessage && threadTimestamp === void 0) return channel;
20
+ return `${channel}:thread:${threadTimestamp ?? timestamp}`;
21
+ }
22
+ function slackTimestamp(timestamp) {
23
+ const milliseconds = Number(timestamp) * 1e3;
24
+ if (!Number.isFinite(milliseconds)) return void 0;
25
+ try {
26
+ return new Date(milliseconds).toISOString();
27
+ } catch {
28
+ return;
29
+ }
30
+ }
31
+ function selfUserIds(payload, configuredBotUserId) {
32
+ const ids = /* @__PURE__ */ new Set();
33
+ if (configuredBotUserId) ids.add(configuredBotUserId);
34
+ if (Array.isArray(payload.authorizations)) {
35
+ for (const authorization of payload.authorizations) if (authorization?.is_bot === true && typeof authorization.user_id === "string") ids.add(authorization.user_id);
36
+ }
37
+ return ids;
38
+ }
39
+ function normalizedEvent(payload, configuredBotUserId) {
40
+ const event = payload.event;
41
+ if (!event || !isRecord(event)) return void 0;
42
+ if (event.type !== "app_mention" && event.type !== "message") return;
43
+ const isMention = event.type === "app_mention";
44
+ const isDirectMessage = event.type === "message" && event.channel_type === "im";
45
+ if (event.subtype !== void 0 || typeof event.bot_id === "string" || typeof event.app_id === "string") return;
46
+ const teamId = payload.team_id;
47
+ const eventId = payload.event_id;
48
+ const channelId = event.channel;
49
+ const actorId = event.user;
50
+ const text = event.text;
51
+ const timestamp = event.ts;
52
+ if (typeof teamId !== "string" || typeof eventId !== "string" || typeof channelId !== "string" || typeof actorId !== "string" || typeof text !== "string" || typeof timestamp !== "string") return;
53
+ if (selfUserIds(payload, configuredBotUserId).has(actorId)) return;
54
+ const threadTimestamp = typeof event.thread_ts === "string" ? event.thread_ts : void 0;
55
+ const sentAt = slackTimestamp(timestamp);
56
+ return {
57
+ type: "message",
58
+ eventId: `slack:${teamId}:event:${eventId}`,
59
+ thread: {
60
+ id: threadIdentity(teamId, channelId, timestamp, threadTimestamp, isDirectMessage),
61
+ isDirectMessage
62
+ },
63
+ replySurface: {
64
+ version: 1,
65
+ address: {
66
+ teamId,
67
+ channelId,
68
+ ...(threadTimestamp || !isDirectMessage) && { threadTs: threadTimestamp ?? timestamp },
69
+ recipientUserId: actorId,
70
+ recipientTeamId: teamId
71
+ },
72
+ label: replySurfaceLabel(channelId, threadTimestamp ?? (isDirectMessage ? void 0 : timestamp))
73
+ },
74
+ actor: {
75
+ id: `slack:${teamId}:user:${actorId}`,
76
+ identity: {
77
+ scope: teamId,
78
+ subject: actorId
79
+ },
80
+ isBot: false,
81
+ isSelf: false
82
+ },
83
+ message: {
84
+ id: messageIdentity(teamId, channelId, timestamp),
85
+ text,
86
+ markdown: text,
87
+ ...isMention && { isMention: true },
88
+ ...threadTimestamp && threadTimestamp !== timestamp && { reply: { id: messageIdentity(teamId, channelId, threadTimestamp) } },
89
+ ...sentAt && { metadata: { sentAt } }
90
+ }
91
+ };
92
+ }
93
+ function parseApprovalValue(value) {
94
+ let parsed;
95
+ try {
96
+ parsed = JSON.parse(value);
97
+ } catch {
98
+ return;
99
+ }
100
+ if (!isRecord(parsed) || parsed.v !== 1 || typeof parsed.approvalId !== "string" || !parsed.approvalId || parsed.decision !== "approve" && parsed.decision !== "reject") return;
101
+ return parsed;
102
+ }
103
+ async function sha256Hex(value) {
104
+ const digest = await crypto.subtle.digest("SHA-256", encodeUtf8(value));
105
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
106
+ }
107
+ async function normalizedInteractions(payload, rawBody) {
108
+ const teamId = payload.team?.id;
109
+ const actorId = payload.user?.id;
110
+ const channelId = typeof payload.channel?.id === "string" ? payload.channel.id : payload.container?.channel_id;
111
+ const timestamp = typeof payload.message?.ts === "string" ? payload.message.ts : payload.container?.message_ts;
112
+ if (typeof teamId !== "string" || typeof actorId !== "string" || typeof channelId !== "string" || typeof timestamp !== "string" || !Array.isArray(payload.actions)) return [];
113
+ const threadTimestamp = typeof payload.message?.thread_ts === "string" ? payload.message.thread_ts : typeof payload.container?.thread_ts === "string" ? payload.container.thread_ts : void 0;
114
+ const isDirectMessage = channelId.startsWith("D");
115
+ const bodyHash = await sha256Hex(rawBody);
116
+ const events = [];
117
+ for (const [index, action] of payload.actions.entries()) {
118
+ if (!action || typeof action.value !== "string") continue;
119
+ const expectedDecision = action.action_id === APPROVE_ACTION_ID ? "approve" : action.action_id === REJECT_ACTION_ID ? "reject" : void 0;
120
+ if (!expectedDecision) continue;
121
+ const approval = parseApprovalValue(action.value);
122
+ if (!approval || approval.decision !== expectedDecision) continue;
123
+ const stableEventId = `slack:${teamId}:interaction:sha256:${bodyHash}:action:${index}`;
124
+ const actionTimestamp = typeof action.action_ts === "string" ? action.action_ts : void 0;
125
+ events.push({
126
+ type: "approval-response",
127
+ eventId: stableEventId,
128
+ thread: {
129
+ id: threadIdentity(teamId, channelId, timestamp, threadTimestamp, isDirectMessage),
130
+ isDirectMessage
131
+ },
132
+ replySurface: {
133
+ version: 1,
134
+ address: {
135
+ teamId,
136
+ channelId,
137
+ threadTs: threadTimestamp ?? timestamp,
138
+ ...!isDirectMessage && {
139
+ recipientUserId: actorId,
140
+ recipientTeamId: teamId
141
+ }
142
+ },
143
+ label: replySurfaceLabel(channelId, threadTimestamp ?? timestamp)
144
+ },
145
+ actor: {
146
+ id: `slack:${teamId}:user:${actorId}`,
147
+ identity: {
148
+ scope: teamId,
149
+ subject: actorId
150
+ },
151
+ ...typeof payload.user?.username === "string" && { username: payload.user.username },
152
+ isBot: false
153
+ },
154
+ approvalId: approval.approvalId,
155
+ decision: approval.decision,
156
+ reference: actionTimestamp ? `${channelIdentity(teamId, channelId)}:action:${actionTimestamp}` : stableEventId
157
+ });
158
+ }
159
+ return events;
160
+ }
161
+ function bytesFromHex(hex) {
162
+ if (!/^[a-f0-9]{64}$/.test(hex)) return void 0;
163
+ const bytes = new Uint8Array(hex.length / 2);
164
+ for (let index = 0; index < bytes.length; index += 1) bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
165
+ return bytes;
166
+ }
167
+ async function hasValidSignature(request, rawBody, signingSecret, maxSkewSeconds) {
168
+ const timestamp = request.headers.get("x-slack-request-timestamp");
169
+ const signature = request.headers.get("x-slack-signature");
170
+ if (!timestamp || !signature || !/^\d+$/.test(timestamp)) return false;
171
+ const timestampSeconds = Number(timestamp);
172
+ if (!Number.isSafeInteger(timestampSeconds) || Math.abs(Math.floor(Date.now() / 1e3) - timestampSeconds) > maxSkewSeconds) return false;
173
+ const match = signature.match(/^v0=([a-f0-9]{64})$/);
174
+ const signatureBytes = match ? bytesFromHex(match[1]) : void 0;
175
+ if (!signatureBytes) return false;
176
+ const key = await crypto.subtle.importKey("raw", encodeUtf8(signingSecret), {
177
+ name: "HMAC",
178
+ hash: "SHA-256"
179
+ }, false, ["verify"]);
180
+ return crypto.subtle.verify("HMAC", key, signatureBytes, encodeUtf8(`v0:${timestamp}:${rawBody}`));
181
+ }
182
+ function parseJsonPayload(rawBody) {
183
+ let parsed;
184
+ try {
185
+ parsed = JSON.parse(rawBody);
186
+ } catch {
187
+ return;
188
+ }
189
+ if (!isRecord(parsed)) return void 0;
190
+ if (parsed.type !== "url_verification" && parsed.type !== "event_callback") return;
191
+ return parsed;
192
+ }
193
+ function parseInteractionPayload(rawBody) {
194
+ const encodedPayload = new URLSearchParams(rawBody).get("payload");
195
+ if (!encodedPayload) return void 0;
196
+ let parsed;
197
+ try {
198
+ parsed = JSON.parse(encodedPayload);
199
+ } catch {
200
+ return;
201
+ }
202
+ return isRecord(parsed) && parsed.type === "block_actions" ? parsed : void 0;
203
+ }
204
+ /** Create dependency-free, request-signed Slack HTTP ingress. */
205
+ function slackWebhook(options) {
206
+ if (!options.signingSecret.trim()) throw new Error("signingSecret is required to create Slack webhook ingress");
207
+ const maxSkewSeconds = options.maxSkewSeconds ?? DEFAULT_MAX_SKEW_SECONDS;
208
+ if (!Number.isInteger(maxSkewSeconds) || maxSkewSeconds < 0) throw new Error("maxSkewSeconds must be a non-negative integer");
209
+ const path = options.path ?? DEFAULT_SLACK_WEBHOOK_PATH;
210
+ return { async receive(request) {
211
+ if (!matchesPath(request, path)) return null;
212
+ if (request.method !== "POST") return emptyIngressResponse(405);
213
+ const rawBody = await request.text();
214
+ if (!await hasValidSignature(request, rawBody, options.signingSecret, maxSkewSeconds)) return emptyIngressResponse(401);
215
+ if ((request.headers.get("content-type")?.split(";", 1)[0]?.trim() ?? "") === "application/x-www-form-urlencoded") {
216
+ const payload = parseInteractionPayload(rawBody);
217
+ if (!payload) return emptyIngressResponse(400);
218
+ return {
219
+ events: (await normalizedInteractions(payload, rawBody)).map((event) => ({
220
+ event,
221
+ raw: payload
222
+ })),
223
+ response: new Response(null, { status: 200 })
224
+ };
225
+ }
226
+ const payload = parseJsonPayload(rawBody);
227
+ if (!payload) return emptyIngressResponse(400);
228
+ if (payload.type === "url_verification") return typeof payload.challenge === "string" ? {
229
+ events: [],
230
+ response: Response.json({ challenge: payload.challenge })
231
+ } : emptyIngressResponse(400);
232
+ const event = normalizedEvent(payload, options.botUserId);
233
+ return {
234
+ events: event ? [{
235
+ event,
236
+ raw: payload
237
+ }] : [],
238
+ response: new Response(null, { status: 200 })
239
+ };
240
+ } };
241
+ }
242
+ /** Create a configured Slack ingress Channel. */
243
+ function slack(options) {
244
+ if (!options.botToken.trim()) throw new Error("botToken is required to create a Slack channel");
245
+ if (options.webhook && !options.participant) throw new Error("participant is required with a Slack webhook");
246
+ const ingress = options.webhook ? slackWebhook(options.webhook) : void 0;
247
+ return {
248
+ ...options.participant && { participant: options.participant },
249
+ ...options.route && { route: options.route },
250
+ ...ingress && { ingress },
251
+ contactSurface(identity) {
252
+ if (identity.scope === void 0) return null;
253
+ return {
254
+ version: 1,
255
+ address: {
256
+ teamId: identity.scope,
257
+ userId: identity.subject
258
+ },
259
+ label: `Slack · user ${identity.subject}`
260
+ };
261
+ }
262
+ };
263
+ }
264
+ //#endregion
265
+ export { slack, slackWebhook };
266
+
267
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/experimental/channels/slack/index.ts"],"sourcesContent":["import type {\n Channel,\n ChannelMessage,\n ChannelParticipant,\n ChannelRoute\n} from \"../channel\";\nimport type { ChannelIdentity } from \"../identity\";\nimport type { ChannelMessageSurface } from \"../surface\";\nimport {\n matchesPath,\n type ChannelApprovalResponseInput,\n type ChannelInboundMessageInput,\n type ChannelIngress\n} from \"../ingress\";\nimport { emptyIngressResponse, encodeUtf8, isRecord } 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 /**\n * Who sent a verified Slack event, such as your user linked to\n * `event.actor`. Return null to drop the event. Required with `webhook`.\n */\n participant?: ChannelParticipant<SlackIngressPayload>;\n /**\n * Pick the agent object an event reaches. Default: a room of the\n * participant's own, so a shared thread is not shared by accident.\n */\n route?: ChannelRoute<SlackIngressPayload>;\n /** Override fetch for testing or custom network routing. */\n fetch?: typeof globalThis.fetch;\n};\n\ntype ApprovalValue = {\n v: 1;\n approvalId: 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\";\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 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.approvalId !== \"string\" ||\n !parsed.approvalId ||\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 approvalId: approval.approvalId,\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\n/** Create a configured Slack ingress 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 if (options.webhook && !options.participant) {\n throw new Error(\"participant is required with a Slack webhook\");\n }\n const ingress = options.webhook ? slackWebhook(options.webhook) : undefined;\n\n return {\n ...(options.participant && { participant: options.participant }),\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 };\n}\n"],"mappings":"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"}