agents 0.16.2 → 0.17.1

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 (126) hide show
  1. package/README.md +11 -8
  2. package/dist/{agent-tool-types-CTw3UJUP.d.ts → agent-tool-types-CNyE1iz_.d.ts} +671 -138
  3. package/dist/agent-tool-types.d.ts +34 -18
  4. package/dist/agent-tool-types.js +20 -1
  5. package/dist/agent-tool-types.js.map +1 -0
  6. package/dist/agent-tools-BXlsuX0d.js +304 -0
  7. package/dist/agent-tools-BXlsuX0d.js.map +1 -0
  8. package/dist/agent-tools-CSnyGvJ2.d.ts +119 -0
  9. package/dist/agent-tools.d.ts +24 -18
  10. package/dist/agent-tools.js.map +1 -1
  11. package/dist/ai-chat-agent.d.ts +1 -1
  12. package/dist/ai-chat-agent.js +1 -2
  13. package/dist/ai-chat-agent.js.map +1 -1
  14. package/dist/ai-chat-v5-migration.d.ts +1 -1
  15. package/dist/ai-chat-v5-migration.js +1 -2
  16. package/dist/ai-chat-v5-migration.js.map +1 -1
  17. package/dist/ai-react.d.ts +1 -1
  18. package/dist/ai-react.js +1 -2
  19. package/dist/ai-react.js.map +1 -1
  20. package/dist/ai-types.d.ts +1 -7
  21. package/dist/ai-types.js +2 -8
  22. package/dist/ai-types.js.map +1 -1
  23. package/dist/browser/ai.js +1 -1
  24. package/dist/browser/index.js +1 -1
  25. package/dist/chat/index.d.ts +2033 -77
  26. package/dist/chat/index.js +1828 -245
  27. package/dist/chat/index.js.map +1 -1
  28. package/dist/chat/react.d.ts +602 -0
  29. package/dist/chat/react.js +1506 -0
  30. package/dist/chat/react.js.map +1 -0
  31. package/dist/chat-sdk/index.d.ts +4 -4
  32. package/dist/{classPrivateFieldGet2-CZ7QjTXN.js → classPrivateFieldGet2-DZBYAB34.js} +5 -5
  33. package/dist/{classPrivateMethodInitSpec-D-0__zd9.js → classPrivateMethodInitSpec-qMjJ6sHQ.js} +2 -2
  34. package/dist/{client-BXJ9n2f7.js → client-BZ-B3NhC.js} +2 -2
  35. package/dist/client-BZ-B3NhC.js.map +1 -0
  36. package/dist/client-tools-aIBO0Fk7.d.ts +53 -0
  37. package/dist/client.d.ts +76 -57
  38. package/dist/client.js +33 -5
  39. package/dist/client.js.map +1 -1
  40. package/dist/{connector-CrKhowfD.js → connector-CdldGF3h.js} +5 -5
  41. package/dist/{connector-CrKhowfD.js.map → connector-CdldGF3h.js.map} +1 -1
  42. package/dist/email.js +1 -1
  43. package/dist/email.js.map +1 -1
  44. package/dist/{index-B7IbEeze.d.ts → index-BRnybD6X.d.ts} +197 -14
  45. package/dist/index.d.ts +95 -73
  46. package/dist/index.js +694 -25
  47. package/dist/index.js.map +1 -1
  48. package/dist/mcp/client.d.ts +18 -14
  49. package/dist/mcp/client.js +1 -1
  50. package/dist/mcp/index.d.ts +30 -30
  51. package/dist/mcp/index.js +7 -7
  52. package/dist/mcp/index.js.map +1 -1
  53. package/dist/{agent-tools-3zLG7MgA.js → message-builder-BymO4N_D.js} +45 -126
  54. package/dist/message-builder-BymO4N_D.js.map +1 -0
  55. package/dist/observability/index.d.ts +1 -1
  56. package/dist/observability/index.js +4 -2
  57. package/dist/observability/index.js.map +1 -1
  58. package/dist/react.d.ts +123 -110
  59. package/dist/react.js +44 -11
  60. package/dist/react.js.map +1 -1
  61. package/dist/{retries-CwlpAGet.d.ts → retries-CvHJwSuh.d.ts} +15 -6
  62. package/dist/retries.d.ts +8 -6
  63. package/dist/retries.js +44 -1
  64. package/dist/retries.js.map +1 -1
  65. package/dist/serializable.d.ts +1 -1
  66. package/dist/skills/compile.js +1 -1
  67. package/dist/skills/compile.js.map +1 -1
  68. package/dist/skills/index.js +5 -5
  69. package/dist/skills/index.js.map +1 -1
  70. package/dist/sub-routing.d.ts +6 -6
  71. package/dist/utils.js +1 -1
  72. package/dist/utils.js.map +1 -1
  73. package/dist/vite.js +5 -5
  74. package/dist/vite.js.map +1 -1
  75. package/dist/wire-types-nflOzNuU.js +240 -0
  76. package/dist/wire-types-nflOzNuU.js.map +1 -0
  77. package/dist/{workflow-types-SrZK_o9p.d.ts → workflow-types-Baz_PO5v.d.ts} +42 -22
  78. package/dist/workflow-types.d.ts +25 -21
  79. package/dist/workflow-types.js.map +1 -1
  80. package/dist/workflows.d.ts +22 -21
  81. package/dist/workflows.js +31 -7
  82. package/dist/workflows.js.map +1 -1
  83. package/docs/adding-to-existing-project.md +450 -0
  84. package/docs/agent-class.md +503 -0
  85. package/docs/agent-tools.md +552 -0
  86. package/docs/browse-the-web.md +430 -0
  87. package/docs/callable-methods.md +627 -0
  88. package/docs/chat-agents.md +1696 -0
  89. package/docs/chat-sdk.md +181 -0
  90. package/docs/client-sdk.md +520 -0
  91. package/docs/client-tools-continuation.md +177 -0
  92. package/docs/codemode.md +440 -0
  93. package/docs/configuration.md +775 -0
  94. package/docs/cross-domain-authentication.md +171 -0
  95. package/docs/durable-execution.md +537 -0
  96. package/docs/email.md +663 -0
  97. package/docs/get-current-agent.md +204 -0
  98. package/docs/getting-started.md +305 -0
  99. package/docs/http-websockets.md +668 -0
  100. package/docs/human-in-the-loop.md +661 -0
  101. package/docs/index.md +151 -0
  102. package/docs/long-running-agents.md +730 -0
  103. package/docs/mcp-client.md +620 -0
  104. package/docs/mcp-servers.md +526 -0
  105. package/docs/mcp-transports.md +308 -0
  106. package/docs/migration-to-ai-sdk-v5.md +96 -0
  107. package/docs/migration-to-ai-sdk-v6.md +163 -0
  108. package/docs/observability.md +261 -0
  109. package/docs/push-notifications.md +367 -0
  110. package/docs/queue.md +329 -0
  111. package/docs/readonly-connections.md +278 -0
  112. package/docs/resumable-streaming.md +127 -0
  113. package/docs/retries.md +444 -0
  114. package/docs/routing.md +749 -0
  115. package/docs/scheduling.md +898 -0
  116. package/docs/securing-mcp-servers.md +359 -0
  117. package/docs/server-driven-messages.md +477 -0
  118. package/docs/sessions.md +1024 -0
  119. package/docs/state.md +512 -0
  120. package/docs/sub-agents.md +389 -0
  121. package/docs/webhooks.md +604 -0
  122. package/docs/workflows.md +877 -0
  123. package/package.json +41 -14
  124. package/dist/agent-tools-3zLG7MgA.js.map +0 -1
  125. package/dist/agent-tools-DZhI5F6Q.d.ts +0 -14
  126. package/dist/client-BXJ9n2f7.js.map +0 -1
package/docs/state.md ADDED
@@ -0,0 +1,512 @@
1
+ # State Management
2
+
3
+ Agents provide built-in state management with automatic persistence and real-time synchronization across all connected clients.
4
+
5
+ ## Overview
6
+
7
+ Agent state is:
8
+
9
+ - **Persistent** - Automatically saved to SQLite, survives restarts and hibernation
10
+ - **Synchronized** - Changes broadcast to all connected WebSocket clients instantly
11
+ - **Bidirectional** - Both server and clients can update state
12
+ - **Type-safe** - Full TypeScript support with generics
13
+
14
+ ```typescript
15
+ import { Agent } from "agents";
16
+
17
+ type GameState = {
18
+ players: string[];
19
+ score: number;
20
+ status: "waiting" | "playing" | "finished";
21
+ };
22
+
23
+ export class GameAgent extends Agent<Env, GameState> {
24
+ // Default state for new agents
25
+ initialState: GameState = {
26
+ players: [],
27
+ score: 0,
28
+ status: "waiting"
29
+ };
30
+
31
+ // React to state changes
32
+ onStateChanged(state: GameState, source: Connection | "server") {
33
+ if (source !== "server" && state.players.length >= 2) {
34
+ // Client added a player, start the game
35
+ this.setState({ ...state, status: "playing" });
36
+ }
37
+ }
38
+
39
+ addPlayer(name: string) {
40
+ this.setState({
41
+ ...this.state,
42
+ players: [...this.state.players, name]
43
+ });
44
+ }
45
+ }
46
+ ```
47
+
48
+ ## Defining Initial State
49
+
50
+ Use the `initialState` property to define default values for new agent instances:
51
+
52
+ ```typescript
53
+ type State = {
54
+ messages: Message[];
55
+ settings: UserSettings;
56
+ lastActive: string | null;
57
+ };
58
+
59
+ export class ChatAgent extends Agent<Env, State> {
60
+ initialState: State = {
61
+ messages: [],
62
+ settings: { theme: "dark", notifications: true },
63
+ lastActive: null
64
+ };
65
+ }
66
+ ```
67
+
68
+ ### Type Safety
69
+
70
+ The second generic parameter to `Agent<Env, State>` defines your state type:
71
+
72
+ ```typescript
73
+ // State is fully typed
74
+ export class MyAgent extends Agent<Env, MyState> {
75
+ initialState: MyState = { count: 0 };
76
+
77
+ increment() {
78
+ // TypeScript knows this.state is MyState
79
+ this.setState({ count: this.state.count + 1 });
80
+ }
81
+ }
82
+ ```
83
+
84
+ ### When Initial State Applies
85
+
86
+ Initial state is applied lazily on first access, not on every wake:
87
+
88
+ 1. **New agent** - `initialState` is used and persisted
89
+ 2. **Existing agent** - Persisted state is loaded from SQLite
90
+ 3. **No `initialState` defined** - `this.state` is `undefined`
91
+
92
+ ```typescript
93
+ async onStart() {
94
+ // Safe to access - returns initialState if new, or persisted state
95
+ console.log("Current count:", this.state.count);
96
+ }
97
+ ```
98
+
99
+ ## Reading State
100
+
101
+ Access the current state via the `this.state` getter:
102
+
103
+ ```typescript
104
+ async onRequest(request: Request) {
105
+ // Read current state
106
+ const { players, status } = this.state;
107
+
108
+ if (status === "waiting" && players.length < 2) {
109
+ return new Response("Waiting for players...");
110
+ }
111
+
112
+ return new Response(JSON.stringify(this.state));
113
+ }
114
+ ```
115
+
116
+ ### Undefined State
117
+
118
+ If you don't define `initialState`, `this.state` returns `undefined`:
119
+
120
+ ```typescript
121
+ export class MinimalAgent extends Agent<Env> {
122
+ // No initialState defined
123
+
124
+ async onConnect(connection: Connection) {
125
+ if (!this.state) {
126
+ // First time - initialize state
127
+ this.setState({ initialized: true });
128
+ }
129
+ }
130
+ }
131
+ ```
132
+
133
+ ## Updating State
134
+
135
+ Use `setState()` to update state. This:
136
+
137
+ 1. Saves to SQLite (persistent)
138
+ 2. Broadcasts to all connected clients
139
+ 3. Triggers `onStateChanged()` (after broadcast; best-effort)
140
+
141
+ ```typescript
142
+ // Replace entire state
143
+ this.setState({
144
+ players: ["Alice", "Bob"],
145
+ score: 0,
146
+ status: "playing"
147
+ });
148
+
149
+ // Update specific fields (spread existing state)
150
+ this.setState({
151
+ ...this.state,
152
+ score: this.state.score + 10
153
+ });
154
+ ```
155
+
156
+ ### State Must Be Serializable
157
+
158
+ State is stored as JSON, so it must be serializable:
159
+
160
+ ```typescript
161
+ // Good - plain objects, arrays, primitives
162
+ this.setState({
163
+ items: ["a", "b", "c"],
164
+ count: 42,
165
+ active: true,
166
+ metadata: { key: "value" }
167
+ });
168
+
169
+ // Bad - functions, classes, circular references
170
+ this.setState({
171
+ callback: () => {}, // Functions don't serialize
172
+ date: new Date(), // Becomes string, loses methods
173
+ self: this // Circular reference
174
+ });
175
+
176
+ // For dates, use ISO strings
177
+ this.setState({
178
+ createdAt: new Date().toISOString()
179
+ });
180
+ ```
181
+
182
+ ## Responding to State Changes
183
+
184
+ Override `onStateChanged()` to react when state changes (notifications/side-effects):
185
+
186
+ ```typescript
187
+ onStateChanged(state: GameState, source: Connection | "server") {
188
+ console.log("State updated:", state);
189
+ console.log("Updated by:", source === "server" ? "server" : source.id);
190
+ }
191
+ ```
192
+
193
+ ## Validating State Updates
194
+
195
+ If you want to validate or reject state updates, override `validateStateChange()`:
196
+
197
+ - **Runs before persistence and broadcast**
198
+ - **Must be synchronous**
199
+ - **Throwing aborts the update**
200
+
201
+ ```typescript
202
+ validateStateChange(nextState: GameState, source: Connection | "server") {
203
+ // Example: reject negative scores
204
+ if (nextState.score < 0) {
205
+ throw new Error("score cannot be negative");
206
+ }
207
+ }
208
+ ```
209
+
210
+ > `onStateChanged()` is not intended for validation; it is a notification hook and should not block broadcasts.
211
+ >
212
+ > **Migration note:** `onStateChanged` replaces the deprecated `onStateUpdate` (server-side hook). If you're using `onStateUpdate` on your agent class, rename it to `onStateChanged` — the signature and behavior are identical. A console warning will fire once per class until you rename it.
213
+
214
+ ### The `source` Parameter
215
+
216
+ The `source` tells you who triggered the update:
217
+
218
+ | Value | Meaning |
219
+ | ------------ | ----------------------------------- |
220
+ | `"server"` | Agent called `setState()` |
221
+ | `Connection` | A client pushed state via WebSocket |
222
+
223
+ This is useful for:
224
+
225
+ - Avoiding infinite loops (don't react to your own updates)
226
+ - Validating client input
227
+ - Triggering side effects only on client actions
228
+
229
+ ```typescript
230
+ onStateChanged(state: State, source: Connection | "server") {
231
+ // Ignore server-initiated updates
232
+ if (source === "server") return;
233
+
234
+ // A client updated state - validate and process
235
+ const connection = source;
236
+ console.log(`Client ${connection.id} updated state`);
237
+
238
+ // Maybe trigger something based on the change
239
+ if (state.status === "submitted") {
240
+ this.processSubmission(state);
241
+ }
242
+ }
243
+ ```
244
+
245
+ ### Common Pattern: Client-Driven Actions
246
+
247
+ ```typescript
248
+ onStateChanged(state: State, source: Connection | "server") {
249
+ if (source === "server") return;
250
+
251
+ // Client added a message
252
+ const lastMessage = state.messages[state.messages.length - 1];
253
+ if (lastMessage && !lastMessage.processed) {
254
+ // Process and update
255
+ this.setState({
256
+ ...state,
257
+ messages: state.messages.map(m =>
258
+ m.id === lastMessage.id ? { ...m, processed: true } : m
259
+ )
260
+ });
261
+ }
262
+ }
263
+ ```
264
+
265
+ ## Client-Side State Sync
266
+
267
+ State synchronizes automatically with connected clients. Both `useAgent` and `AgentClient` expose a `state` property that tracks the current agent state. See [Client SDK](./client-sdk.md) for full details.
268
+
269
+ ### React (useAgent)
270
+
271
+ ```tsx
272
+ import { useAgent } from "agents/react";
273
+
274
+ function GameUI() {
275
+ const agent = useAgent({
276
+ agent: "game-agent",
277
+ name: "room-123"
278
+ });
279
+
280
+ // Read state directly — reactive, triggers re-render on change
281
+ // Push state to agent with spread for partial updates
282
+ const addPlayer = (name: string) => {
283
+ agent.setState({
284
+ ...agent.state,
285
+ players: [...(agent.state?.players ?? []), name]
286
+ });
287
+ };
288
+
289
+ return <div>Players: {agent.state?.players.join(", ")}</div>;
290
+ }
291
+ ```
292
+
293
+ ### Vanilla JS (AgentClient)
294
+
295
+ ```typescript
296
+ import { AgentClient } from "agents/client";
297
+
298
+ const client = new AgentClient({
299
+ agent: "game-agent",
300
+ name: "room-123",
301
+ host: "your-worker.workers.dev"
302
+ });
303
+
304
+ await client.ready;
305
+
306
+ // Read state directly
307
+ console.log("Score:", client.state?.score);
308
+
309
+ // Push state update with spread for partial updates
310
+ client.setState({ ...client.state, score: 100 });
311
+ ```
312
+
313
+ ### State Flow
314
+
315
+ ```
316
+ ┌─────────────────────────────────────────────────────────────┐
317
+ │ Agent │
318
+ │ ┌─────────────────────────────────────────────────────┐ │
319
+ │ │ this.state │ │
320
+ │ │ (persisted in SQLite) │ │
321
+ │ └─────────────────────────────────────────────────────┘ │
322
+ │ ▲ │ │
323
+ │ │ setState() │ broadcast │
324
+ │ │ ▼ │
325
+ └───────────┼──────────────────────────────┼──────────────────┘
326
+ │ │
327
+ │ │ WebSocket
328
+ │ │
329
+ ┌───────────┴──────────────────────────────┴───────────────────┐
330
+ │ Clients │
331
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
332
+ │ │ Client 1 │ │ Client 2 │ │ Client 3 │ │
333
+ │ │ state │ │ state │ │ state │ │
334
+ │ └──────────┘ └──────────┘ └──────────┘ │
335
+ │ │
336
+ │ Any client can call setState() to push updates │
337
+ └──────────────────────────────────────────────────────────────┘
338
+ ```
339
+
340
+ ## State from Workflows
341
+
342
+ When using [Workflows](./workflows.md), you can update agent state from workflow steps:
343
+
344
+ ```typescript
345
+ // In your workflow
346
+ async run(event: AgentWorkflowEvent<Params>, step: AgentWorkflowStep) {
347
+ // Replace entire state
348
+ await step.updateAgentState({ status: "processing", progress: 0 });
349
+
350
+ // Merge partial updates (preserves other fields)
351
+ await step.mergeAgentState({ progress: 50 });
352
+
353
+ // Reset to initialState
354
+ await step.resetAgentState();
355
+
356
+ return result;
357
+ }
358
+ ```
359
+
360
+ These are durable operations - they persist even if the workflow retries.
361
+
362
+ ## Patterns & Best Practices
363
+
364
+ ### Keep State Small
365
+
366
+ State is broadcast to all clients on every change. For large data:
367
+
368
+ ```typescript
369
+ // Bad - storing large arrays in state
370
+ initialState = {
371
+ allMessages: [] // Could grow to thousands of items
372
+ };
373
+
374
+ // Good - store in SQL, keep state light
375
+ initialState = {
376
+ messageCount: 0,
377
+ lastMessageId: null
378
+ };
379
+
380
+ // Query SQL for full data
381
+ async getMessages(limit = 50) {
382
+ return this.sql`SELECT * FROM messages ORDER BY created_at DESC LIMIT ${limit}`;
383
+ }
384
+ ```
385
+
386
+ ### Optimistic Updates
387
+
388
+ For responsive UIs, update client state immediately:
389
+
390
+ ```typescript
391
+ // Client-side
392
+ function sendMessage(text: string) {
393
+ const optimisticMessage = {
394
+ id: crypto.randomUUID(),
395
+ text,
396
+ pending: true
397
+ };
398
+
399
+ // Update immediately — agent.state updates optimistically
400
+ agent.setState({
401
+ ...agent.state,
402
+ messages: [...(agent.state?.messages ?? []), optimisticMessage]
403
+ });
404
+
405
+ // Server will confirm/update
406
+ }
407
+
408
+ // Server-side
409
+ onStateChanged(state: State, source: Connection | "server") {
410
+ if (source === "server") return;
411
+
412
+ const pendingMessages = state.messages.filter(m => m.pending);
413
+ for (const msg of pendingMessages) {
414
+ // Validate and confirm
415
+ this.setState({
416
+ ...state,
417
+ messages: state.messages.map(m =>
418
+ m.id === msg.id ? { ...m, pending: false, timestamp: Date.now() } : m
419
+ )
420
+ });
421
+ }
422
+ }
423
+ ```
424
+
425
+ ### State vs SQL
426
+
427
+ | Use State For | Use SQL For |
428
+ | ---------------------------------- | ----------------- |
429
+ | UI state (loading, selected items) | Historical data |
430
+ | Real-time counters | Large collections |
431
+ | Active session data | Relationships |
432
+ | Configuration | Queryable data |
433
+
434
+ ```typescript
435
+ export class ChatAgent extends Agent<Env, State> {
436
+ // State: current UI state
437
+ initialState = {
438
+ typing: [],
439
+ unreadCount: 0,
440
+ activeUsers: []
441
+ };
442
+
443
+ // SQL: message history
444
+ async getMessages(limit = 100) {
445
+ return this.sql`
446
+ SELECT * FROM messages
447
+ ORDER BY created_at DESC
448
+ LIMIT ${limit}
449
+ `;
450
+ }
451
+
452
+ async saveMessage(message: Message) {
453
+ this.sql`
454
+ INSERT INTO messages (id, text, user_id, created_at)
455
+ VALUES (${message.id}, ${message.text}, ${message.userId}, ${Date.now()})
456
+ `;
457
+ // Update state for real-time UI
458
+ this.setState({
459
+ ...this.state,
460
+ unreadCount: this.state.unreadCount + 1
461
+ });
462
+ }
463
+ }
464
+ ```
465
+
466
+ ### Avoid Infinite Loops
467
+
468
+ Be careful not to trigger state updates in response to your own updates:
469
+
470
+ ```typescript
471
+ // Bad - infinite loop
472
+ onStateChanged(state: State) {
473
+ this.setState({ ...state, lastUpdated: Date.now() });
474
+ }
475
+
476
+ // Good - check source
477
+ onStateChanged(state: State, source: Connection | "server") {
478
+ if (source === "server") return; // Don't react to own updates
479
+ this.setState({ ...state, lastUpdated: Date.now() });
480
+ }
481
+ ```
482
+
483
+ ## API Reference
484
+
485
+ ### Properties
486
+
487
+ | Property | Type | Description |
488
+ | -------------- | ------- | ---------------------------- |
489
+ | `state` | `State` | Current state (getter) |
490
+ | `initialState` | `State` | Default state for new agents |
491
+
492
+ ### Methods
493
+
494
+ | Method | Signature | Description |
495
+ | ---------------- | -------------------------------------------------------- | --------------------------------------------- |
496
+ | `setState` | `(state: State) => void` | Update state, persist, and broadcast |
497
+ | `onStateChanged` | `(state: State, source: Connection \| "server") => void` | Called after state is persisted and broadcast |
498
+
499
+ ### Workflow Step Methods
500
+
501
+ | Method | Description |
502
+ | ------------------------------- | ------------------------------------- |
503
+ | `step.updateAgentState(state)` | Replace agent state from workflow |
504
+ | `step.mergeAgentState(partial)` | Merge partial state from workflow |
505
+ | `step.resetAgentState()` | Reset to `initialState` from workflow |
506
+
507
+ ## Next Steps
508
+
509
+ - [Readonly Connections](./readonly-connections.md) - Restrict which connections can update state
510
+ - [Client SDK](./client-sdk.md) - Full client-side state sync documentation
511
+ - [Workflows](./workflows.md) - Durable state updates from workflows
512
+ - TODO: SQL API - When to use SQL instead of state