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
package/docs/channels.md DELETED
@@ -1,323 +0,0 @@
1
- # `agents/channels`
2
-
3
- `agents/channels` gives an agent one interface for sending and receiving
4
- messages across different platforms. Use a Channel directly, expose it as an AI
5
- tool, or register it with a durable `ChannelHost` that owns routing and delivery
6
- recovery.
7
-
8
- > [!NOTE]
9
- > Channels is experimental. Its interface _will_ change before the package
10
- > reaches a stable release.
11
-
12
- ## Install
13
-
14
- ```bash
15
- npm install agents
16
- ```
17
-
18
- ## Create channels
19
-
20
- Each adapter turns provider configuration into the same `Channel` interface. A
21
- `ChannelHost` holds them, keyed by names you choose:
22
-
23
- ```typescript
24
- import { ChannelHost, routes } from "agents/channels";
25
- import { email } from "agents/channels/email";
26
- import { slack } from "agents/channels/slack";
27
- import { telegram } from "agents/channels/telegram";
28
-
29
- const host = new ChannelHost({
30
- channels: {
31
- slack: slack({
32
- botToken: env.SLACK_BOT_TOKEN,
33
- webhook: {
34
- signingSecret: env.SLACK_SIGNING_SECRET,
35
- botUserId: env.SLACK_BOT_USER_ID
36
- },
37
- route: routes.perThread
38
- }),
39
- telegram: telegram({
40
- botToken: env.TELEGRAM_BOT_TOKEN,
41
- webhook: { secretToken: env.TELEGRAM_WEBHOOK_SECRET },
42
- route: routes.perThread
43
- }),
44
- email: email({
45
- binding: env.EMAIL,
46
- from: "agent@example.com",
47
- route: routes.perThread
48
- })
49
- },
50
-
51
- async onMessage({ route, dispatchId, message }) {
52
- await conversationFor(route).receive(dispatchId, message);
53
- }
54
- });
55
- ```
56
-
57
- ## 1. Send a message
58
-
59
- A **surface** is a destination for an outbound message.
60
- You get one from an inbound message's reply field, or by constructing one from a raw channel identifier through the host.
61
-
62
- ```typescript
63
- const surface = host.contactSurface({
64
- channelKey: "slack",
65
- scope: "T123",
66
- subject: "U456"
67
- });
68
-
69
- await host.deliver(surface, {
70
- title: "Import needs attention",
71
- markdown: "The customer import stopped after **1,240 records**."
72
- });
73
- ```
74
-
75
- An identity's `channelKey` names the configured Channel that observed it. Its
76
- optional `scope` names a tenant within that Channel and defaults to `"default"`;
77
- for example, one configured Slack app can observe the same user ID in several
78
- workspaces. The Host stamps `channelKey` on inbound identities because an adapter
79
- does not know the key it was configured under.
80
-
81
- The same human observed through two configured Channels on one platform is two
82
- Channel identities, just as the same human on Slack and email is. Applications
83
- that know they are the same person link those identities explicitly.
84
-
85
- Compose destinations with `fallback()` and `fanout()`:
86
-
87
- ```typescript
88
- import { fallback, fanout } from "agents/channels";
89
-
90
- await host.deliver(fallback([slackSurface, emailSurface]), message);
91
- await host.deliver(fanout([slackSurface, emailSurface]), message);
92
- ```
93
-
94
- `fallback()` tries destinations in order, advancing only after a _confirmed_
95
- failure, so it can never duplicate a delivery. For a stream, it advances only
96
- when that failure happens before the destination starts reading; replaying an
97
- arbitrarily large consumed prefix would require an unbounded buffer. `fanout()`
98
- sends to all destinations; a partial or uncertain result is reported as
99
- `uncertain` for the same reason.
100
- The Host installs both policies as ordinary Channels under reserved keys. You
101
- can register another composite policy as an ordinary Channel under your own key
102
- and pair it with a surface constructor that writes that key; inject only the
103
- outbound resolution capability the policy needs, as the exported built-in
104
- policy Channels do.
105
-
106
- ## 2. Give a destination to a model
107
-
108
- ```typescript
109
- import { generateText, stepCountIs } from "ai";
110
- import { createWorkersAI } from "workers-ai-provider";
111
- import { createSendMessageTool } from "agents/channels/ai-sdk";
112
-
113
- const workersai = createWorkersAI({ binding: env.AI });
114
-
115
- await generateText({
116
- model: workersai("@cf/moonshotai/kimi-k2.7-code"),
117
- prompt: "An import stopped after 1,240 records. Notify support.",
118
- tools: {
119
- contactSupport: createSendMessageTool(
120
- host,
121
- fallback([slackSurface, emailSurface]),
122
- { description: "Contact support when a person needs to intervene" }
123
- )
124
- },
125
- stopWhen: stepCountIs(2)
126
- });
127
- ```
128
-
129
- The model writes the message; you chose the destination. TanStack AI exports the
130
- same `createSendMessageTool(host, surface, options)` from
131
- `agents/channels/tanstack-ai`.
132
-
133
- ## 3. Receive messages
134
-
135
- One entry point covers every configured Channel's webhook, and Workers Email
136
- arrives the same way:
137
-
138
- ```typescript
139
- export default {
140
- async fetch(request: Request): Promise<Response> {
141
- const response = await host.handleRequest(request);
142
- if (response) return response;
143
- return new Response("Not found", { status: 404 });
144
- },
145
-
146
- async email(message: ForwardableEmailMessage): Promise<void> {
147
- await host.handleEmail(message);
148
- }
149
- } satisfies ExportedHandler<Env>;
150
- ```
151
-
152
- Each Channel authenticates its own input and declines what isn't its business,
153
- so the Host asks them in configuration order and the first to claim it wins.
154
-
155
- ### Routing
156
-
157
- A Channel's `route` turns one normalized event into an opaque application
158
- string — a Durable Object name, a queue key, a database id — or `null` to ignore
159
- the event entirely. The key is used to identify a common destination for
160
- messages -- i.e. typically a single conversation:
161
-
162
- ```typescript
163
- telegram({
164
- // …credentials…
165
- route(event) {
166
- return event.thread.isDirectMessage === true ? event.thread.id : null;
167
- }
168
- });
169
- ```
170
-
171
- Channels exposes builtin helpers for the common mappings — `routes.perThread`
172
- and `routes.perEvent`, which namespace their routes as `thread:…` and
173
- `event:…`.
174
-
175
- Deciding whether an event is relevant at all is a different question, and one
176
- only your application can answer, because the answer usually depends on state
177
- you hold. Write that in your own `route` and return `null` to ignore the event:
178
-
179
- ```typescript
180
- route(event) {
181
- // Slack shows a bot every message in every channel it belongs to. Answer
182
- // when addressed, and otherwise only join threads we are already in.
183
- const thread = routes.perThread(event);
184
- const addressed =
185
- event.thread.isDirectMessage === true || event.message.isMention === true;
186
- if (addressed) return thread;
187
- return (await myConversations.knows(thread)) ? thread : null;
188
- }
189
- ```
190
-
191
- A lookup like that runs for every event reaching it, so put it behind a cheaper
192
- check, keep it read-only, and do not let it create the state it is testing for —
193
- otherwise the first stray message conjures the thing the check is looking for.
194
-
195
- ### Link identities
196
-
197
- Personal agents often wwant to resolve users regardless of the channel they messaged on. Your application can explicitly record connections between channel identities and expose them to the Host for messages to be routed on:
198
-
199
- ```typescript
200
- const host = new ChannelHost({
201
- channels,
202
- findUser: (identity) => users.findUser(identity),
203
- onMessage,
204
- onApprovalResponse
205
- });
206
-
207
- // ...
208
- // route to a user's central conversaion if one exists, else start a new
209
- // conversation for each thread:
210
- route: routes.byUser(routes.perThread);
211
-
212
- // or prefer the linked person, then the sender we recognise, then a new
213
- // conversation per event:
214
- route: routes.byUser(routes.byIdentity(routes.perEvent));
215
- ```
216
-
217
- `byIdentity` groups events carrying the _same_ identity. It never infers that
218
- two different identities belong to one person — that stays an explicit
219
- application decision, which `byUser` then exposes to routing. Omit its fallback
220
- to ignore events that carry no identity at all.
221
-
222
- If your application does not already store user identities, `createUserIdentityStore(storage)` creates a Durable Object SQL store of the right shape. Your application can call `store.link` to connect multiple identities together.
223
-
224
- ### Ask for approval
225
-
226
- ### Request approval
227
-
228
- The Host exposes a utility for durably correlating inbound approvals to outbound requests:
229
-
230
- ```typescript
231
- await host.requestApproval({
232
- interactionId: "deploy-42",
233
- request: {
234
- title: "Production deployment",
235
- summary: "Deploy version 2026.08.17 to production?",
236
- input: {
237
- version: "2026.08.17",
238
- environment: "production"
239
- }
240
- }
241
- });
242
- await host.requestApproval(surface, {
243
- interactionId: crypto.randomUUID(),
244
- request: {
245
- title: "Production deployment",
246
- summary: "Deploy version 2026.08.17 to production?",
247
- input: { version: "2026.08.17" }
248
- }
249
- });
250
- ```
251
-
252
- Users can respond to approval requests through native surfaces (e.g. Telegram
253
- buttons) or HTTP inbound URLs, resolved by the Channel Host itself for your application to settle.
254
-
255
- ## Custom channels
256
-
257
- Any transport can become a Channel:
258
-
259
- ```typescript
260
- import { matchesPath, routes, type Channel } from "agents/channels";
261
-
262
- const supportForm: Channel = {
263
- route: routes.perEvent,
264
- ingress: {
265
- async receive(request) {
266
- if (!matchesPath(request, "/support")) return null;
267
-
268
- const raw = await request.json<{ message: string; email: string }>();
269
- const eventId = crypto.randomUUID();
270
- return {
271
- events: [
272
- {
273
- raw,
274
- event: {
275
- type: "message",
276
- eventId,
277
- thread: { id: eventId, isDirectMessage: true },
278
- actor: {
279
- id: raw.email,
280
- // The Host stamps the configured `channelKey`.
281
- identity: { subject: raw.email }
282
- },
283
- message: { id: eventId, text: raw.message }
284
- }
285
- }
286
- ],
287
- response: Response.json({ accepted: true }, { status: 202 })
288
- };
289
- }
290
- }
291
- };
292
- ```
293
-
294
- Returning `null` declines the request so another Channel can claim it.
295
-
296
- ## Durability contract
297
-
298
- Channels holds no state: no outbox, no retries, no deduplication, no scheduler.
299
- Durability is a property of how your application uses it. A caller-supplied
300
- `deliveryId` is correlation metadata, not an idempotency guarantee; an adapter
301
- may map it to a provider primitive when one exists.
302
-
303
- | Channels guarantees | Your application must |
304
- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------- |
305
- | A `dispatchId` stable across redelivery and unaffected by routing | Deduplicate on it before starting any side effect |
306
- | The Host awaits your callback before the provider is acknowledged | Hand off durably before returning — a DO RPC, queue send, or workflow start |
307
- | One outbound attempt per `deliver()` or `stream()`, reported honestly | Decide whether to retry; `uncertain` may duplicate a real delivery |
308
- | Surfaces are plain JSON you can persist | Keep configured channel keys stable |
309
- | Decisions arrive as normalized events carrying your own `interactionId` | Own settlement; an interaction id is not an authorization credential |
310
-
311
- ## Future work
312
-
313
- - [ ] Approval-link ingress: signing, verification, and a confirmation page, so
314
- link approvals return through the same normalized path as Slack buttons
315
- - [ ] Reader-initiated stream cancellation: Slack's `message_stream_stopped`
316
- and Telegram's `stopped_message_generation` should reach the running
317
- generation as ordinary ingress, so aborting it errors the stream and
318
- each Channel finalizes on the path it already has
319
- - [ ] More built-in channels
320
- - [ ] Rendering templates (pretty emails)
321
- - [ ] Automatic webhook registration
322
- - [ ] Security review of approval flows
323
- - [ ] Conformance tests of adapters