agents 0.23.0 → 0.25.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 (187) hide show
  1. package/dist/{agent-routing-DE5zmCQ8.d.ts → agent-routing-0DR0Tk2v.d.ts} +270 -190
  2. package/dist/agent-routing.d.ts +1 -1
  3. package/dist/agent-routing.js.map +1 -1
  4. package/dist/agent-tool-types.d.ts +13 -7
  5. package/dist/agent-tool-types.js +23 -1
  6. package/dist/agent-tool-types.js.map +1 -1
  7. package/dist/{agent-tools-y7zLfw4Q.js → agent-tools-CdJC-8Dc.js} +72 -9
  8. package/dist/agent-tools-CdJC-8Dc.js.map +1 -0
  9. package/dist/{agent-tools-DtXMTDGM.d.ts → agent-tools-YrATZJu2.d.ts} +29 -15
  10. package/dist/agent-tools.d.ts +6 -4
  11. package/dist/agent-tools.js.map +1 -1
  12. package/dist/ai-fm7xx1BK.js +312 -0
  13. package/dist/ai-fm7xx1BK.js.map +1 -0
  14. package/dist/base64-redaction-C1Uu29t5.js +59 -0
  15. package/dist/base64-redaction-C1Uu29t5.js.map +1 -0
  16. package/dist/browser/ai-sdk.d.ts +90 -0
  17. package/dist/browser/ai-sdk.js +51 -0
  18. package/dist/browser/ai-sdk.js.map +1 -0
  19. package/dist/browser/ai.d.ts +4 -4
  20. package/dist/browser/ai.js +1 -347
  21. package/dist/browser/index.d.ts +81 -79
  22. package/dist/browser/index.js +361 -2
  23. package/dist/browser/index.js.map +1 -0
  24. package/dist/browser/tanstack-ai.d.ts +96 -1
  25. package/dist/browser/tanstack-ai.js +62 -2
  26. package/dist/browser/tanstack-ai.js.map +1 -1
  27. package/dist/browser-DE3d-RLk.d.ts +275 -0
  28. package/dist/browser-tool-CQ10e6Kj.js +480 -0
  29. package/dist/browser-tool-CQ10e6Kj.js.map +1 -0
  30. package/dist/browser-tool-DuNdlwZ9.d.ts +73 -0
  31. package/dist/{capability-BjSKYpzg.js → capability-B4WbF81e.js} +2 -1
  32. package/dist/capability-B4WbF81e.js.map +1 -0
  33. package/dist/{capability-runner-Be_-PLR1.d.ts → capability-runner-DBAnGr6L.d.ts} +45 -15
  34. package/dist/chat/index.d.ts +341 -232
  35. package/dist/chat/index.js +602 -72
  36. package/dist/chat/index.js.map +1 -1
  37. package/dist/chat/react.d.ts +42 -2
  38. package/dist/chat/react.js +191 -35
  39. package/dist/chat/react.js.map +1 -1
  40. package/dist/chat/transport.d.ts +1 -1
  41. package/dist/chat/transport.js +1 -1
  42. package/dist/chat-sdk/index.d.ts +17 -3
  43. package/dist/chat-sdk/index.js +28 -2
  44. package/dist/chat-sdk/index.js.map +1 -1
  45. package/dist/client-4OlMIJx-.js +591 -0
  46. package/dist/client-4OlMIJx-.js.map +1 -0
  47. package/dist/{client-jagG8a9_.js → client-fyQrLGkD.js} +2 -2
  48. package/dist/{client-jagG8a9_.js.map → client-fyQrLGkD.js.map} +1 -1
  49. package/dist/client-je3koD9Z.d.ts +446 -0
  50. package/dist/client.d.ts +29 -283
  51. package/dist/client.js +2 -274
  52. package/dist/{connector-CptFKzRh.js → connector-58XDGvc3.js} +251 -120
  53. package/dist/connector-58XDGvc3.js.map +1 -0
  54. package/dist/{connector-CkQD4MK3.d.ts → connector-Cl-VqPmA.d.ts} +244 -146
  55. package/dist/context/index.d.ts +29 -0
  56. package/dist/context/index.js +64 -17
  57. package/dist/context/index.js.map +1 -1
  58. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  59. package/dist/current-agent-vAk_5JMt.d.ts +93 -0
  60. package/dist/{current-agent-Da_C9a3b.d.ts → durable-object-lifecycle-HhgOMWq9.d.ts} +26 -127
  61. package/dist/harnesses/pi/index.d.ts +348 -0
  62. package/dist/harnesses/pi/index.js +754 -0
  63. package/dist/harnesses/pi/index.js.map +1 -0
  64. package/dist/images-iiTDSyRu.js +1631 -0
  65. package/dist/images-iiTDSyRu.js.map +1 -0
  66. package/dist/index-BreL0YrQ.d.ts +78 -0
  67. package/dist/index-kbSUIer3.d.ts +343 -0
  68. package/dist/index.d.ts +43 -41
  69. package/dist/index.js +2 -2
  70. package/dist/internal_context.d.ts +1 -1
  71. package/dist/lifecycle/index.d.ts +19 -17
  72. package/dist/lifecycle/index.js +2 -2
  73. package/dist/{lifecycle-CMRGjZdw.js → lifecycle-CFu6OV03.js} +73 -29
  74. package/dist/lifecycle-CFu6OV03.js.map +1 -0
  75. package/dist/mcp/client/index.d.ts +20 -20
  76. package/dist/mcp/client/index.js +1 -1
  77. package/dist/mcp/client/x402.js +21 -19
  78. package/dist/mcp/client/x402.js.map +1 -1
  79. package/dist/mcp/index.d.ts +35 -35
  80. package/dist/mcp/index.js +2 -2
  81. package/dist/{message-builder-BymO4N_D.js → message-builder-DAl9AhHH.js} +89 -4
  82. package/dist/message-builder-DAl9AhHH.js.map +1 -0
  83. package/dist/models/ai-sdk/index.d.ts +353 -0
  84. package/dist/models/ai-sdk/index.js +2443 -0
  85. package/dist/models/ai-sdk/index.js.map +1 -0
  86. package/dist/models/pi-ai/index.d.ts +313 -0
  87. package/dist/models/pi-ai/index.js +1795 -0
  88. package/dist/models/pi-ai/index.js.map +1 -0
  89. package/dist/observability/ai/index.js +20 -4
  90. package/dist/observability/ai/index.js.map +1 -1
  91. package/dist/{protocol-B0nh6KNf.js → protocol-kHsr-f0X.js} +26 -12
  92. package/dist/{protocol-B0nh6KNf.js.map → protocol-kHsr-f0X.js.map} +1 -1
  93. package/dist/queue/index.d.ts +123 -0
  94. package/dist/queue/index.js +2 -0
  95. package/dist/queue-BB-zfIYF.js +406 -0
  96. package/dist/queue-BB-zfIYF.js.map +1 -0
  97. package/dist/react.d.ts +31 -13
  98. package/dist/react.js +190 -71
  99. package/dist/react.js.map +1 -1
  100. package/dist/{retries-D9Ds-1lz.d.ts → retries-CV6AkXmQ.d.ts} +23 -8
  101. package/dist/retries.d.ts +12 -8
  102. package/dist/retries.js +19 -1
  103. package/dist/retries.js.map +1 -1
  104. package/dist/routing/index.d.ts +2 -2
  105. package/dist/routing/index.js +1 -1
  106. package/dist/{scheduler-DD9NdYbF.js → scheduler-DZ3xQfeK.js} +4 -11
  107. package/dist/scheduler-DZ3xQfeK.js.map +1 -0
  108. package/dist/{scheduler-Dwh85ZGl.d.ts → scheduler-RdACNXDP.d.ts} +6 -6
  109. package/dist/schedules/index.d.ts +1 -1
  110. package/dist/schedules/index.js +1 -1
  111. package/dist/serializable.d.ts +1 -1
  112. package/dist/sessions/index.d.ts +70 -3
  113. package/dist/sessions/index.js +276 -67
  114. package/dist/sessions/index.js.map +1 -1
  115. package/dist/skills/index.d.ts +29 -314
  116. package/dist/socket-address.d.ts +16 -0
  117. package/dist/socket-address.js +19 -0
  118. package/dist/socket-address.js.map +1 -0
  119. package/dist/{src-DlSHshb2.js → src-BZeu-SCD.js} +541 -500
  120. package/dist/src-BZeu-SCD.js.map +1 -0
  121. package/dist/state/index.d.ts +6 -0
  122. package/dist/state/index.js +137 -0
  123. package/dist/state/index.js.map +1 -0
  124. package/dist/streams/index.d.ts +1 -1
  125. package/dist/streams/index.js +1 -1
  126. package/dist/{streams-D6tJ0NN9.d.ts → streams-Cmkj9QfW.d.ts} +4 -2
  127. package/dist/{streams-DZKgAj9b.js → streams-DW-UCOUc.js} +9 -2
  128. package/dist/streams-DW-UCOUc.js.map +1 -0
  129. package/dist/sub-routing.d.ts +12 -12
  130. package/dist/tasks/index.d.ts +1 -1
  131. package/dist/tasks/index.js +1 -1
  132. package/dist/{tasks-ylZgBjhj.js → tasks-68DESxf0.js} +3 -3
  133. package/dist/{tasks-ylZgBjhj.js.map → tasks-68DESxf0.js.map} +1 -1
  134. package/dist/{tasks-BRJ5zgya.d.ts → tasks-9YxYtByh.d.ts} +5 -5
  135. package/dist/transport-PfFnt-Ld.d.ts +525 -0
  136. package/dist/transport-protocol-BxyngQ_R.js +25 -0
  137. package/dist/transport-protocol-BxyngQ_R.js.map +1 -0
  138. package/dist/transport-protocol-ZTew5Wso.d.ts +27 -0
  139. package/dist/types-BA4anrjI.d.ts +70 -0
  140. package/dist/voice/index.d.ts +2 -2
  141. package/dist/websockets/index.d.ts +195 -69
  142. package/dist/websockets/index.js +3 -2
  143. package/dist/websockets-Ci-LZFHH.js +1030 -0
  144. package/dist/websockets-Ci-LZFHH.js.map +1 -0
  145. package/dist/wire-types-CnMt6_HR.js.map +1 -1
  146. package/dist/wire-types-CrjKvDyi.d.ts +300 -0
  147. package/dist/workflow-types.d.ts +25 -25
  148. package/dist/workflows.d.ts +21 -21
  149. package/dist/workflows.js +1 -1
  150. package/dist/{ws-chat-transport-UNRIS2xl.d.ts → ws-chat-transport-BbT1A5Lc.d.ts} +51 -2
  151. package/dist/{ws-chat-transport-rWwta645.js → ws-chat-transport-cqMKq3IY.js} +143 -16
  152. package/dist/ws-chat-transport-cqMKq3IY.js.map +1 -0
  153. package/docs/agent-class.md +2 -2
  154. package/docs/agent-tools.md +29 -4
  155. package/docs/browse-the-web.md +87 -5
  156. package/docs/chat-agents.md +124 -16
  157. package/docs/client-sdk.md +30 -0
  158. package/docs/context.md +27 -0
  159. package/docs/durable-execution.md +2 -0
  160. package/docs/harnesses/pi.md +91 -0
  161. package/docs/human-in-the-loop.md +4 -0
  162. package/docs/index.md +4 -1
  163. package/docs/lifecycle.md +114 -23
  164. package/docs/models-pi-ai.md +325 -0
  165. package/docs/models.md +504 -0
  166. package/docs/queue.md +135 -241
  167. package/docs/resumable-streaming.md +2 -0
  168. package/docs/retries.md +1 -1
  169. package/docs/routing.md +13 -13
  170. package/docs/sessions.md +36 -1
  171. package/docs/state.md +1 -0
  172. package/docs/sub-agents.md +12 -3
  173. package/package.json +51 -3
  174. package/dist/agent-tools-y7zLfw4Q.js.map +0 -1
  175. package/dist/browser/ai.js.map +0 -1
  176. package/dist/capability-BjSKYpzg.js.map +0 -1
  177. package/dist/client-tools-aIBO0Fk7.d.ts +0 -53
  178. package/dist/client.js.map +0 -1
  179. package/dist/connector-CptFKzRh.js.map +0 -1
  180. package/dist/lifecycle-CMRGjZdw.js.map +0 -1
  181. package/dist/message-builder-BymO4N_D.js.map +0 -1
  182. package/dist/scheduler-DD9NdYbF.js.map +0 -1
  183. package/dist/src-DlSHshb2.js.map +0 -1
  184. package/dist/streams-DZKgAj9b.js.map +0 -1
  185. package/dist/websockets-DUfRHPRq.js +0 -502
  186. package/dist/websockets-DUfRHPRq.js.map +0 -1
  187. package/dist/ws-chat-transport-rWwta645.js.map +0 -1
package/docs/queue.md CHANGED
@@ -1,218 +1,157 @@
1
- # Queue System
1
+ # Queue
2
2
 
3
- The Agents SDK provides a built-in queue system that allows you to schedule tasks for asynchronous execution. This is particularly useful for background processing, delayed operations, and managing workloads that don't need immediate execution.
3
+ Durable background work for an Agent or any Lifecycle Object. A queued item
4
+ persists a callback name and a payload, runs from the Durable Object's alarm
5
+ loop one item at a time in push order, retries when the callback throws, and
6
+ survives the object leaving memory.
4
7
 
5
- ## Overview
8
+ ## Queue Lifecycle capability
6
9
 
7
- The queue system is built into the base `Agent` class. Tasks are stored in a SQLite table and processed automatically in FIFO (First In, First Out) order.
10
+ > **Experimental.** The `Queue` capability and the `agents/lifecycle` surface
11
+ > it builds on may change between releases. Agent's established queue methods
12
+ > (`this.queue()` and friends) are stable.
8
13
 
9
- ## QueueItem Type
14
+ `Queue` is a reusable Lifecycle capability. A plain Lifecycle Object can
15
+ install it without extending `Agent`:
10
16
 
11
17
  ```typescript
12
- export type QueueItem<T = string> = {
13
- id: string; // Unique identifier for the queued task
14
- payload: T; // Data to pass to the callback function
15
- callback: keyof Agent<unknown>; // Name of the method to call
16
- created_at: number; // Timestamp when the task was created
17
- retry?: RetryOptions; // Retry options (if configured)
18
- };
19
- ```
20
-
21
- ## Core Methods
22
-
23
- ### queue()
24
-
25
- Adds a task to the queue for future execution.
26
-
27
- ```typescript
28
- async queue<T = unknown>(
29
- callback: keyof this,
30
- payload: T,
31
- options?: { retry?: RetryOptions }
32
- ): Promise<string>
33
- ```
34
-
35
- **Parameters:**
36
-
37
- - `callback`: The name of the method to call when processing the task
38
- - `payload`: Data to pass to the callback method
39
- - `options.retry`: Optional retry configuration. See [Retries](./retries.md) for details.
40
-
41
- **Returns:** The unique ID of the queued task
42
-
43
- **Example:**
18
+ import { DurableObject } from "cloudflare:workers";
19
+ import { Lifecycle } from "agents/lifecycle";
20
+ import { Queue, type QueueItem } from "agents/queue";
44
21
 
45
- ```typescript
46
- class MyAgent extends Agent {
47
- async processEmail(data: { email: string; subject: string }) {
48
- // Process the email
49
- console.log(`Processing email: ${data.subject}`);
50
- }
22
+ export class UploadObject extends DurableObject<Env> {
23
+ readonly queue = new Queue({
24
+ callbacks: {
25
+ processUpload: (
26
+ payload: { fileName: string },
27
+ item: QueueItem<{ fileName: string }>
28
+ ) => {
29
+ console.log(item.id, payload.fileName);
30
+ }
31
+ }
32
+ });
51
33
 
52
- async onMessage(message: string) {
53
- // Queue an email processing task
54
- const taskId = await this.queue("processEmail", {
55
- email: "user@example.com",
56
- subject: "Welcome!"
57
- });
34
+ readonly lifecycle = Lifecycle.install(this).use(this.queue);
58
35
 
59
- console.log(`Queued task with ID: ${taskId}`);
36
+ async enqueueUpload(fileName: string): Promise<string> {
37
+ const item = await this.queue.push("processUpload", { fileName });
38
+ return item.id;
60
39
  }
61
40
  }
62
41
  ```
63
42
 
64
- ### dequeue()
65
-
66
- Removes a specific task from the queue by ID.
67
-
68
- ```typescript
69
- dequeue(id: string): void
70
- ```
71
-
72
- **Parameters:**
73
-
74
- - `id`: The ID of the task to remove
75
-
76
- **Example:**
77
-
78
- ```typescript
79
- // Remove a specific task
80
- agent.dequeue("abc123def");
81
- ```
82
-
83
- ### dequeueAll()
84
-
85
- Removes all tasks from the queue.
86
-
87
- ```typescript
88
- dequeueAll(): void
89
- ```
90
-
91
- **Example:**
92
-
93
- ```typescript
94
- // Clear the entire queue
95
- agent.dequeueAll();
96
- ```
97
-
98
- ### dequeueAllByCallback()
99
-
100
- Removes all tasks that match a specific callback method.
101
-
102
- ```typescript
103
- dequeueAllByCallback(callback: string): void
104
- ```
105
-
106
- **Parameters:**
107
-
108
- - `callback`: Name of the callback method
109
-
110
- **Example:**
111
-
112
- ```typescript
113
- // Remove all email processing tasks
114
- agent.dequeueAllByCallback("processEmail");
115
- ```
116
-
117
- ### getQueue()
118
-
119
- Retrieves a specific queued task by ID.
120
-
121
- ```typescript
122
- getQueue(id: string): QueueItem<string> | undefined
123
- ```
124
-
125
- **Parameters:**
43
+ Queue owns no storage of its own. Each item is one job in the Lifecycle job
44
+ queue, due immediately; Lifecycle owns the physical Durable Object alarm, the
45
+ alarm event loop, retry policy, the deadman pre-arm, and the memory-limit
46
+ circuit breaker. See [Durable Object lifecycle](./lifecycle.md#the-job-queue).
126
47
 
127
- - `id`: The ID of the task to retrieve
48
+ Queue's API is small: callbacks are registered by name in the constructor,
49
+ `push()` creates items typed against that registration, and `get()`,
50
+ `list()`, `cancel()`, and `cancelAll()` manage them. All of these are
51
+ asynchronous and work inside routed sub-agents.
128
52
 
129
- **Returns:** The QueueItem with parsed payload or undefined if not found
53
+ Queue Lifecycle hooks run without ambient host context. Registered callbacks
54
+ are user code, so they run inside the host invocation context with the
55
+ Lifecycle Object available through `getCurrentAgent()`.
130
56
 
131
- **Note:** The payload is automatically parsed from JSON before being returned
132
-
133
- **Example:**
134
-
135
- ```typescript
136
- const task = agent.getQueue("abc123def");
137
- if (task) {
138
- console.log(`Task callback: ${task.callback}`);
139
- console.log(`Task payload:`, task.payload);
140
- }
141
- ```
57
+ ### `new Queue(options?)`
142
58
 
143
- ### getQueues()
59
+ | Option | Description |
60
+ | ----------- | ------------------------------------------------------------------------------------------------------------- |
61
+ | `callbacks` | Named callbacks `(payload, item) => unknown`. Register in a field initializer so names re-bind on every wake. |
62
+ | `retry` | Default retry policy: `{ maxAttempts: 3, baseDelayMs: 100, maxDelayMs: 3000 }`. See [Retries](./retries.md). |
63
+ | `onError` | Observes an item's terminal failure after its attempts are exhausted. Runs without host context. |
144
64
 
145
- Retrieves all queued tasks that match a specific key-value pair in their payload.
65
+ ### `push(callback, payload?, options?)`
146
66
 
147
- ```typescript
148
- getQueues(key: string, value: string): QueueItem<string>[]
149
- ```
67
+ Queues one item and returns the `QueueItem`. The item is durable before the
68
+ returned promise resolves, and it runs from the next alarm, so the caller
69
+ never waits for the callback.
150
70
 
151
- **Parameters:**
71
+ - `options.retry` overrides the Queue's retry policy for this item.
72
+ - `options.id` sets a stable id. A push with an existing id replaces that
73
+ item in place, keeping its position in the queue (and superseding a
74
+ dispatch of it still in flight); use it for idempotent enqueues keyed by
75
+ your own identifier.
152
76
 
153
- - `key`: The key to filter by in the payload
154
- - `value`: The value to match
77
+ Once the Lifecycle has started, the item row is written synchronously before
78
+ `push()` returns its promise, so a push made in the same synchronous block as
79
+ your own SQL writes commits atomically with them.
155
80
 
156
- **Returns:** Array of matching QueueItem objects
81
+ ### `get(id)`, `list(criteria?)`, `cancel(id)`, `cancelAll(callback?)`
157
82
 
158
- **Note:** This method fetches all queue items and filters them in memory by parsing each payload and checking if the specified key matches the value
83
+ `list()` returns pending items in push order; `{ callback }` filters by
84
+ callback name. `cancel()` returns whether an item was removed; `cancelAll()`
85
+ returns how many were.
159
86
 
160
- **Example:**
87
+ ### QueueItem
161
88
 
162
89
  ```typescript
163
- // Find all tasks for a specific user
164
- const userTasks = await agent.getQueues("userId", "12345");
90
+ type QueueItem<T = unknown> = {
91
+ id: string; // Unique identifier
92
+ callback: string; // Callback name
93
+ payload: T; // Data passed to the callback
94
+ createdAt: number; // Unix timestamp in seconds
95
+ retry?: RetryOptions; // Resolved retry policy
96
+ };
165
97
  ```
166
98
 
167
- ## How Queue Processing Works
168
-
169
- 1. **Validation**: When calling `queue()`, the method validates that the callback exists as a function on the agent
170
- 2. **Automatic Processing**: After queuing, the system automatically attempts to flush the queue
171
- 3. **FIFO Order**: Tasks are processed in the order they were created (`created_at` timestamp)
172
- 4. **Context Preservation**: Each queued task runs with the same agent context (connection, request, email)
173
- 5. **Automatic Retries**: If a callback fails, it is retried with exponential backoff (configurable per task)
174
- 6. **Automatic Dequeue**: Tasks are removed from the queue after successful execution or after all retry attempts are exhausted
175
- 7. **Error Handling**: If a callback method does not exist at execution time, an error is logged and the task is skipped
176
- 8. **Persistence**: Tasks are stored in the `cf_agents_queues` table and survive agent restarts
99
+ ## Using the queue through Agent
177
100
 
178
- ## Queue Callback Methods
179
-
180
- When defining callback methods for queued tasks, they must follow this signature:
181
-
182
- ```typescript
183
- async callbackMethod(payload: unknown, queueItem: QueueItem<string>): Promise<void>
184
- ```
185
-
186
- **Example:**
101
+ `Agent` installs a `Queue` and resolves callback names to its own methods, so
102
+ `this.queue("methodName", payload)` keeps working with no registration:
187
103
 
188
104
  ```typescript
189
105
  class MyAgent extends Agent {
190
- async sendNotification(
191
- payload: { userId: string; message: string },
192
- queueItem: QueueItem<string>
106
+ async processEmail(
107
+ payload: { email: string; subject: string },
108
+ item: QueueItem<{ email: string; subject: string }>
193
109
  ) {
194
- console.log(`Processing task ${queueItem.id}`);
195
- console.log(
196
- `Sending notification to user ${payload.userId}: ${payload.message}`
197
- );
198
-
199
- // Your notification logic here
200
- await this.notificationService.send(payload.userId, payload.message);
110
+ console.log(`Processing ${item.id}: ${payload.subject}`);
201
111
  }
202
112
 
203
- async onUserSignup(userData: any) {
204
- // Queue a welcome notification
205
- await this.queue("sendNotification", {
206
- userId: userData.id,
207
- message: "Welcome to our platform!"
113
+ async onMessage(message: string) {
114
+ const id = await this.queue("processEmail", {
115
+ email: "user@example.com",
116
+ subject: "Welcome!"
208
117
  });
118
+ console.log(`Queued task ${id}`);
209
119
  }
210
120
  }
211
121
  ```
212
122
 
213
- ## Use Cases
214
-
215
- ### Background Processing
123
+ | Method | Description |
124
+ | ------------------------------------ | ------------------------------------------------------------------- |
125
+ | `queue(callback, payload, options?)` | Queue a method by name. Returns the item id. Accepts `retry`, `id`. |
126
+ | `dequeue(id)` | Remove a pending item. Resolves to whether one was removed. |
127
+ | `dequeueAll()` | Remove every pending item. Resolves to the count. |
128
+ | `dequeueAllByCallback(callback)` | Remove every pending item for one callback. Resolves to the count. |
129
+ | `getQueue(id)` | Read a pending item. |
130
+ | `getQueues(key, value)` | Pending items whose payload has `key` equal to `value`. |
131
+
132
+ ## How queue processing works
133
+
134
+ 1. **Validation**: `queue()` checks the callback exists and validates any
135
+ retry options. Invalid input throws immediately.
136
+ 2. **Durable first**: the item is a row in the `cf_agents_jobs` job queue
137
+ before `queue()` resolves. Items created before this capability existed
138
+ are migrated out of the old `cf_agents_queues` table on the next start.
139
+ 3. **Alarm-driven**: every push re-arms the Durable Object alarm. The
140
+ Lifecycle event loop runs due items in push order, awaiting each callback
141
+ before starting the next, in a fresh invocation rather than the request
142
+ that queued them.
143
+ 4. **Retries**: a throwing callback is retried with exponential backoff per
144
+ its retry policy, emitting `queue:retry` on each extra attempt.
145
+ 5. **Terminal failure**: after the last attempt the item is dropped, a
146
+ `queue:error` event is emitted, and Agent's `onError` (or the Queue's
147
+ `onError` option) observes the error.
148
+ 6. **Recovery**: an isolate that dies mid-callback wakes again on the
149
+ Lifecycle deadman alarm and resumes the queue, so callbacks should be
150
+ idempotent.
151
+
152
+ ## Use cases
153
+
154
+ Queue work that must not block the current request or WebSocket message:
216
155
 
217
156
  ```typescript
218
157
  class DataProcessor extends Agent {
@@ -221,52 +160,31 @@ class DataProcessor extends Agent {
221
160
  await this.notifyUser(data.userId, results);
222
161
  }
223
162
 
224
- async onDataUpload(uploadData: any) {
225
- // Queue the processing instead of doing it synchronously
163
+ async onDataUpload(uploadData: { id: string; userId: string }) {
226
164
  await this.queue("processLargeDataset", {
227
165
  datasetId: uploadData.id,
228
166
  userId: uploadData.userId
229
167
  });
230
-
231
168
  return { message: "Data upload received, processing started" };
232
169
  }
233
170
  }
234
171
  ```
235
172
 
236
- ### Delayed Operations
237
-
238
- ```typescript
239
- class ReminderAgent extends Agent {
240
- async sendReminder(data: { userId: string; message: string }) {
241
- await this.emailService.send(data.userId, data.message);
242
- }
243
-
244
- async scheduleReminder(userId: string, message: string, delayMs: number) {
245
- // Note: For true delayed execution, combine with the scheduling system
246
- // This example shows queueing for later processing
247
- await this.queue("sendReminder", { userId, message });
248
- }
249
- }
250
- ```
251
-
252
- ### Batch Operations
173
+ Split large requests into batches that run one after another:
253
174
 
254
175
  ```typescript
255
176
  class BatchProcessor extends Agent {
256
- async processBatch(data: { items: any[]; batchId: string }) {
177
+ async processBatch(data: { items: unknown[]; batchId: string }) {
257
178
  for (const item of data.items) {
258
179
  await this.processItem(item);
259
180
  }
260
- console.log(`Completed batch ${data.batchId}`);
261
181
  }
262
182
 
263
- async onLargeRequest(items: any[]) {
264
- // Split large requests into smaller batches
183
+ async onLargeRequest(items: unknown[]) {
265
184
  const batchSize = 10;
266
185
  for (let i = 0; i < items.length; i += batchSize) {
267
- const batch = items.slice(i, i + batchSize);
268
186
  await this.queue("processBatch", {
269
- items: batch,
187
+ items: items.slice(i, i + batchSize),
270
188
  batchId: `batch-${i / batchSize + 1}`
271
189
  });
272
190
  }
@@ -274,56 +192,32 @@ class BatchProcessor extends Agent {
274
192
  }
275
193
  ```
276
194
 
277
- ## Best Practices
195
+ For work that should run at a specific time or on a schedule, use
196
+ [Scheduling](./scheduling.md) instead. Both ride the same Lifecycle job queue.
278
197
 
279
- 1. **Keep Payloads Small**: Payloads are JSON-serialized and stored in the database
280
- 2. **Idempotent Operations**: Design callback methods to be safe to retry
281
- 3. **Error Handling**: Include proper error handling in callback methods
282
- 4. **Monitoring**: Use logging to track queue processing
283
- 5. **Cleanup**: Regularly clean up completed or failed tasks if needed
198
+ ## Best practices
284
199
 
285
- ## Error Handling and Retries
200
+ 1. **Keep payloads small**: payloads are JSON-serialized into the job row.
201
+ 2. **Idempotent callbacks**: an item can run again after an isolate reset.
202
+ 3. **Bounded callbacks**: the event loop awaits each callback, so a long one
203
+ delays every other job on the object. Detach unbounded work and return.
204
+ 4. **Stable ids for dedupe**: pass `id` when the same logical task may be
205
+ queued more than once.
286
206
 
287
- Queued tasks are automatically retried on failure with exponential backoff. The default is 3 attempts. You can customize this per task:
207
+ ## Error handling and retries
288
208
 
289
209
  ```typescript
290
- // Retry up to 5 times with custom backoff
291
210
  await this.queue("reliableTask", payload, {
292
211
  retry: { maxAttempts: 5, baseDelayMs: 500 }
293
212
  });
294
213
  ```
295
214
 
296
- If you need custom error handling in the callback:
297
-
298
- ```typescript
299
- class RobustAgent extends Agent {
300
- async reliableTask(payload: { data: string }, queueItem: QueueItem<string>) {
301
- try {
302
- await this.doSomethingRisky(payload);
303
- } catch (error) {
304
- console.error(`Task ${queueItem.id} failed:`, error);
305
- // The retry system will catch this error and retry automatically
306
- throw error;
307
- }
308
- }
309
- }
310
- ```
311
-
312
- See [Retries](./retries.md) for full documentation on retry options and patterns.
313
-
314
- ## Integration with Other Features
315
-
316
- The queue system works seamlessly with other Agent SDK features:
317
-
318
- - **State Management**: Access agent state within queued callbacks
319
- - **Scheduling**: Combine with `schedule()` for time-based queue processing
320
- - **Retries**: Built-in retry with exponential backoff. See [Retries](./retries.md).
321
- - **Context**: Queued tasks maintain the original request context
322
- - **Database**: Uses the same database as other agent data
215
+ See [Retries](./retries.md) for full documentation on retry options, and
216
+ [Observability](./observability.md) for the `queue:create`, `queue:retry`,
217
+ and `queue:error` events.
323
218
 
324
219
  ## Limitations
325
220
 
326
- - Tasks are processed sequentially, not in parallel
327
- - No priority system (FIFO only)
328
- - Queue processing happens during agent execution, not as separate background jobs
329
- - Failed tasks are dequeued after all retry attempts are exhausted (no dead-letter queue)
221
+ - Items run sequentially, not in parallel.
222
+ - No priority system (FIFO only).
223
+ - Failed items are dropped after all retry attempts (no dead-letter queue).
@@ -84,6 +84,8 @@ function Chat() {
84
84
 
85
85
  Replayed chunks include `replay: true` to distinguish them from live chunks. The client uses this to batch-apply all replayed chunks before rendering, which prevents intermediate states (like reasoning "Thinking..." indicators) from flashing briefly during replay. During a live stream, chunks arrive gradually and React renders each intermediate state naturally.
86
86
 
87
+ Live and replayed chunks also carry a `seq` field: the chunk's position in the stream, starting at `0`. A continuation stream (for example, a tool continuation) appends to an assistant message that the client already holds, so replaying it from the start would apply the same text twice. The client remembers the highest `seq` it has applied for each request and skips replayed continuation chunks at or below it. Replays of non-continuation streams rebuild the message from the first chunk and are not filtered.
88
+
87
89
  ## Durable client cleanup
88
90
 
89
91
  `resume: true` controls whether the client tries to reconnect to an active stream. `cancelOnClientAbort: false` is the default cancellation behavior: generic client stream abort/cleanup is local-only, while explicit `stop()` still cancels the server turn.
package/docs/retries.md CHANGED
@@ -188,7 +188,7 @@ await this.queue("processWebhook", webhookData, {
188
188
  });
189
189
  ```
190
190
 
191
- If the callback throws, it is retried before the task is dequeued. After all attempts are exhausted, the task is dequeued and the error is logged.
191
+ If the callback throws, it is retried before the item is removed. After all attempts are exhausted, the item is dropped, a `queue:error` event is emitted, and the error is logged.
192
192
 
193
193
  ## Validation
194
194
 
package/docs/routing.md CHANGED
@@ -777,9 +777,9 @@ The error message lists available agents. Check:
777
777
  2. Check that `routeAgentRequest()` is called before your 404 handler
778
778
  3. Ensure the response from `routeAgentRequest()` is returned (not just called)
779
779
 
780
- ### WebSocket won't connect
780
+ ### WebSocket will not connect
781
781
 
782
- 1. Don't modify the response from `routeAgentRequest()` for WebSocket upgrades
782
+ 1. Do not modify the response from `routeAgentRequest()` for WebSocket upgrades
783
783
  2. Ensure CORS is enabled if connecting from a different origin
784
784
  3. Check browser dev tools for the actual error
785
785
 
@@ -798,16 +798,16 @@ The error message lists available agents. Check:
798
798
 
799
799
  Routes a request to the appropriate agent.
800
800
 
801
- | Parameter | Type | Description |
802
- | ------------------------- | ------------------------- | --------------------------------------------------- |
803
- | `request` | `Request` | The incoming request |
804
- | `env` | `Env` | Environment with agent bindings |
805
- | `options.cors` | `boolean \| HeadersInit` | Enable CORS headers |
806
- | `options.props` | `Record<string, unknown>` | Props passed to whichever agent handles the request |
807
- | `options.locationHint` | `string` | Preferred location for agent instances |
808
- | `options.jurisdiction` | `string` | Data jurisdiction for agent instances |
809
- | `options.onBeforeConnect` | `Function` | Callback before WebSocket connections |
810
- | `options.onBeforeRequest` | `Function` | Callback before HTTP requests |
801
+ | Parameter | Type | Description |
802
+ | ------------------------- | ------------------------ | ------------------------------------------------------------------------------------ |
803
+ | `request` | `Request` | The incoming request |
804
+ | `env` | `Env` | Environment with agent bindings |
805
+ | `options.cors` | `boolean \| HeadersInit` | Enable CORS headers |
806
+ | `options.props` | `object` | Props passed to whichever agent handles the request. Sent as JSON, so use plain data |
807
+ | `options.locationHint` | `string` | Preferred location for agent instances |
808
+ | `options.jurisdiction` | `string` | Data jurisdiction for agent instances |
809
+ | `options.onBeforeConnect` | `Function` | Callback before WebSocket connections |
810
+ | `options.onBeforeRequest` | `Function` | Callback before HTTP requests |
811
811
 
812
812
  **Returns:** `Promise<Response \| undefined>` - Response if matched, undefined if no agent route
813
813
 
@@ -821,7 +821,7 @@ Get an agent instance by name for server-side RPC or request forwarding.
821
821
  | `name` | `string` | Instance name |
822
822
  | `options.locationHint` | `string` | Preferred location |
823
823
  | `options.jurisdiction` | `string` | Data jurisdiction |
824
- | `options.props` | `Record<string, unknown>` | Initialization properties for `onStart` |
824
+ | `options.props` | `object` | Initialization properties for `onStart` |
825
825
 
826
826
  **Returns:** `Promise<DurableObjectStub<T>>` - Typed stub for calling agent methods or forwarding requests
827
827
 
package/docs/sessions.md CHANGED
@@ -242,7 +242,23 @@ await session.addCompaction(summary, fromMessageId, toMessageId);
242
242
 
243
243
  Sessions stamps each message with a token estimate when the row is written. `compactAfter()` gates on that O(1) aggregate and never reads the transcript to decide whether to compact. Auto-compaction failures are non-fatal: they log, emit `session:error`, and leave the transcript alone.
244
244
 
245
- To trim a transcript before handing it to a model, `truncateOlderMessages` is exported from [`agents/chat`](./chat-agents.md), not from `agents/sessions`.
245
+ To trim a transcript before handing it to a model, `truncateOlderMessages` is exported from [`agents/chat`](./chat-agents.md), not from `agents/sessions`. If your tools define `toModelOutput`, a truncated tool output may no longer match the tool's output schema. In that case, pass `toolOutputs: false` to `truncateOlderMessages` and call `truncateOlderToolResults` on the result of `convertToModelMessages`, which truncates what the model reads after `toModelOutput` has run. Provider-executed tool outputs are never truncated.
246
+
247
+ With the default `keepRecent`, the truncation cutoff moves forward by one message whenever a message is added, so every turn rewrites a message near the end of the prompt and invalidates the provider's prompt cache from that point. To keep the prefix stable, move the cutoff in steps and pass the same `keepRecent` to both functions:
248
+
249
+ ```ts
250
+ const step = 8;
251
+ const keepRecent = history.length <= 4 ? 4 : 4 + ((history.length - 4) % step);
252
+ const truncated = truncateOlderMessages(history, {
253
+ keepRecent,
254
+ toolOutputs: false
255
+ });
256
+ const modelMessages = truncateOlderToolResults(
257
+ await convertToModelMessages(truncated, { tools }),
258
+ truncated,
259
+ { keepRecent }
260
+ );
261
+ ```
246
262
 
247
263
  ## Large messages
248
264
 
@@ -345,6 +361,25 @@ const unsubscribe = sessions.subscribe(async (event) => {
345
361
 
346
362
  `import` fires once per `importMessage()` and carries the row; it is deliberately not an `append`, so a projection does not patch itself per imported row during a migration — it marks itself stale and re-derives once. `compaction` fires when an overlay is stored directly through `addCompaction()`; `compact()` reports its own overlay as `compact`.
347
363
 
364
+ A host that keeps an in-memory transcript does not need to write that reduction itself. `session.mirror()` subscribes for one session and patches an array in place: an append or update replaces the entry with the same id (or pushes a new row), a delete filters, and a clear empties it:
365
+
366
+ ```ts
367
+ const stop = sessions.session("").mirror<UIMessage>({
368
+ get: () => this.messages,
369
+ set: (messages) => {
370
+ this.messages = messages;
371
+ },
372
+ transform: (message) => toCachedMessage(message),
373
+ intercept: async (event) => {
374
+ if (event.type !== "compact") return false;
375
+ this.messages = await reloadFromStorage();
376
+ return true;
377
+ }
378
+ });
379
+ ```
380
+
381
+ `get()` is read on every event, so reassigning the array is safe. `intercept` handles an event instead of the default reduction. Use it for changes an in-place patch cannot express, such as a branch append or a compaction. `onApplied` runs after an append or update was written, with the entry it replaced.
382
+
348
383
  This is a local cache-coherence feed, not a cross-object event log. Capability diagnostics are also emitted through Lifecycle observability.
349
384
 
350
385
  ## Errors
package/docs/state.md CHANGED
@@ -301,6 +301,7 @@ const client = new AgentClient({
301
301
  host: "your-worker.workers.dev"
302
302
  });
303
303
 
304
+ // Resolves once the agent's stored state has arrived, if it has any
304
305
  await client.ready;
305
306
 
306
307
  // Read state directly
@@ -4,7 +4,7 @@ Dynamic agents are child Durable Objects **colocated under and supervised by** a
4
4
 
5
5
  Use dynamic agents for code whose **class or lifecycle the parent owns**: dynamically-loaded or AI-generated code that has no wrangler binding, per-run tool agents, sandboxed components that need isolated storage plus supervised abort/restart. That is what the runtime built facets for.
6
6
 
7
- Do **not** use dynamic agents to model an open-ended set of independent peers — many chats, documents, or sessions per user. Those want one top-level Durable Object each plus a per-user index; see [When to use dynamic agents](#when-to-use-dynamic-agents) and [`examples/next/chats`](https://github.com/cloudflare/agents/tree/main/examples/next/chats).
7
+ Do **not** use dynamic agents to model an open-ended set of independent peers — many chats, documents, or sessions per user. Those want one top-level Durable Object each plus a per-user index; see [When to use dynamic agents](#when-to-use-dynamic-agents) and [`examples/next/routing`](https://github.com/cloudflare/agents/tree/main/examples/next/routing).
8
8
 
9
9
  If you want a parent chat agent to dispatch another chat-capable agent during a
10
10
  single turn and render that child's progress inline, use [Agent Tools](./agent-tools.md).
@@ -246,6 +246,15 @@ The hook receives the **original** request with its URL intact — including the
246
246
 
247
247
  WebSocket upgrade requests flow through this hook the same way as plain HTTP. If you return a mutated `Request`, keep the original `Upgrade: websocket` and `Sec-WebSocket-*` headers — cloning via `new Headers(req.headers)` and only adding or replacing entries is the safest recipe.
248
248
 
249
+ When the hook returns a `Response` for a WebSocket upgrade, the framework accepts the socket and immediately closes it, because browsers hide the status of a failed handshake and reconnecting clients would retry it forever. The close code carries the status:
250
+
251
+ | Returned status | Close code | Client behavior |
252
+ | --------------- | --------------- | --------------------------------------------------------------------- |
253
+ | `4xx` | `4000 + status` | Terminal: `useAgent` and `AgentClient` stop and set `connectionError` |
254
+ | anything else | `1011` | Retried with backoff |
255
+
256
+ The close reason is `Sub-agent connection rejected (<status>)`, so a `404` arrives as code `4404`. At the first `/sub/` hop, a returned `Response` that already carries a `webSocket` is passed through unchanged; deeper hops always close with the mapped code.
257
+
249
258
  ### `this.parentPath` and `this.selfPath`
250
259
 
251
260
  Root-first ancestor chains. `parentPath` covers strict ancestors; `selfPath` includes the current agent.
@@ -471,13 +480,13 @@ await user.recordChatActivity(chatId, { title, lastMessage, seq });
471
480
 
472
481
  `RoutedAgents` packages the hub side of this pattern: a durable ID-to-Agent catalog plus request and WebSocket forwarding under one route segment. See [Routing to independent Agents](./routing.md#routing-to-independent-agents).
473
482
 
474
- Each chat gets its own alarms, placement, and storage budget; deletion is one `chats.delete(id)` call; and "search across all my chats" reads only the index. The example treats that index as a best-effort derived projection: a failed push leaves it stale until the next message, and a push for a deleted chat is refused. See [`examples/next/chats`](https://github.com/cloudflare/agents/tree/main/examples/next/chats) for the pattern built on `RoutedAgents`, with tests. Idempotency and repair belong to the production design in [`design/rfc-user-chat-durable-objects.md`](https://github.com/cloudflare/agents/blob/main/design/rfc-user-chat-durable-objects.md).
483
+ Each chat gets its own alarms, placement, and storage budget; deletion is one `chats.delete(id)` call; and "search across all my chats" reads only the index. The example treats that index as a best-effort derived projection: a failed push leaves it stale until the next message, and a push for a deleted chat is refused. See [`examples/next/routing`](https://github.com/cloudflare/agents/tree/main/examples/next/routing) for the pattern built on `RoutedAgents`, with tests. Idempotency and repair belong to the production design in [`design/rfc-user-chat-durable-objects.md`](https://github.com/cloudflare/agents/blob/main/design/rfc-user-chat-durable-objects.md).
475
484
 
476
485
  ## Examples
477
486
 
478
487
  - [`examples/next/dynamic-agents`](https://github.com/cloudflare/agents/tree/main/examples/next/dynamic-agents) — the headline use case: a supervisor stores user-submitted Durable Object code, loads it via Worker Loader, and runs it as facets with isolated storage, supervised abort, and code upgrades over stable state.
479
488
  - [`examples/agents-as-tools`](https://github.com/cloudflare/agents/tree/main/examples/agents-as-tools) — per-run child agents as tools with inline streaming.
480
- - [`examples/multi-ai-chat`](https://github.com/cloudflare/agents/tree/main/examples/multi-ai-chat) — a multi-session chat app built on facet children under one `Inbox`. It works and demonstrates the routing surface, but for many long-lived chats per user prefer the top-level-DO-per-chat pattern in [`examples/next/chats`](https://github.com/cloudflare/agents/tree/main/examples/next/chats) — see [When to use dynamic agents](#when-to-use-dynamic-agents).
489
+ - [`examples/multi-ai-chat`](https://github.com/cloudflare/agents/tree/main/examples/multi-ai-chat) — a multi-session chat app built on facet children under one `Inbox`. It works and demonstrates the routing surface, but for many long-lived chats per user prefer the top-level-DO-per-chat pattern in [`examples/next/routing`](https://github.com/cloudflare/agents/tree/main/examples/next/routing) — see [When to use dynamic agents](#when-to-use-dynamic-agents).
481
490
 
482
491
  ## Related
483
492