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
@@ -0,0 +1,181 @@
1
+ # Chat SDK state
2
+
3
+ Use `agents/chat-sdk` when you run the [Chat SDK](https://chat-sdk.dev/) inside an Agent and want the Chat SDK `StateAdapter` to use Agents sub-agents for durable state.
4
+
5
+ The adapter stores Chat SDK subscriptions, locks, queues, dedupe keys, thread state, channel state, callback metadata, transcript lists, and thread history in Durable Object SQLite. Each state shard is a `ChatSdkStateAgent` sub-agent under your ingress Agent.
6
+
7
+ ## Install
8
+
9
+ Install both packages in the Worker that hosts your messenger ingress:
10
+
11
+ ```bash
12
+ npm install agents chat
13
+ ```
14
+
15
+ `agents/chat-sdk` itself does not provide a messenger adapter. Use it with any Chat SDK adapter, such as Telegram, Slack, Discord, Teams, or Google Chat.
16
+
17
+ ## Basic setup
18
+
19
+ Create a parent Agent that owns your Chat SDK runtime. Pass `createChatSdkState()` as the Chat SDK `state` option.
20
+
21
+ ```typescript
22
+ import { Agent } from "agents";
23
+ import { Chat } from "chat";
24
+ import { createChatSdkState } from "agents/chat-sdk";
25
+ import { createTelegramAdapter } from "@chat-adapter/telegram";
26
+
27
+ export { ChatSdkStateAgent } from "agents/chat-sdk";
28
+
29
+ export class MessengerAgent extends Agent<Env> {
30
+ private chat!: Chat;
31
+
32
+ onStart() {
33
+ const telegram = createTelegramAdapter({
34
+ botToken: this.env.TELEGRAM_BOT_TOKEN,
35
+ mode: "webhook",
36
+ secretToken: this.env.TELEGRAM_WEBHOOK_SECRET_TOKEN,
37
+ userName: "my_bot"
38
+ });
39
+
40
+ this.chat = new Chat({
41
+ adapters: { telegram },
42
+ userName: "my_bot",
43
+ state: createChatSdkState(),
44
+ concurrency: { strategy: "burst", debounceMs: 600 }
45
+ });
46
+ }
47
+ }
48
+ ```
49
+
50
+ Add the parent Agent to your Durable Object migration:
51
+
52
+ ```jsonc
53
+ {
54
+ "durable_objects": {
55
+ "bindings": [{ "name": "MessengerAgent", "class_name": "MessengerAgent" }]
56
+ },
57
+ "migrations": [
58
+ {
59
+ "tag": "v1",
60
+ "new_sqlite_classes": ["MessengerAgent"]
61
+ }
62
+ ]
63
+ }
64
+ ```
65
+
66
+ Export `ChatSdkStateAgent` from your Worker entry point so sub-agent routing can resolve it. When `createChatSdkState()` is called inside an Agent lifecycle method, it uses the current Agent as the parent and creates state shards with `this.subAgent()`.
67
+
68
+ ## State sharding
69
+
70
+ By default, Chat SDK state is sharded by the first two colon-separated segments of a thread-like key.
71
+
72
+ For example, `telegram:-100123:456` and `telegram:-100123:789` share the same state shard, `telegram:-100123`.
73
+
74
+ The default key sharder recognizes these Chat SDK key prefixes:
75
+
76
+ - `thread-state:`
77
+ - `channel-state:`
78
+ - `msg-history:`
79
+ - `transcripts:user:`
80
+
81
+ Unknown keys use the adapter's default shard name, `default`.
82
+
83
+ ## Custom sharding
84
+
85
+ Use `shardKey` to control how thread IDs map to state sub-agent names:
86
+
87
+ ```typescript
88
+ const state = createChatSdkState({
89
+ shardKey(threadId) {
90
+ return threadId.split(":").slice(0, 2).join(":");
91
+ }
92
+ });
93
+ ```
94
+
95
+ Use `keyShard` when an adapter stores non-thread-shaped keys that should still route to a provider-specific shard:
96
+
97
+ ```typescript
98
+ const state = createChatSdkState({
99
+ keyShard(key) {
100
+ if (!key.startsWith("dedupe:telegram:")) {
101
+ return undefined;
102
+ }
103
+
104
+ const chatId = key.slice("dedupe:telegram:".length).split(":")[0];
105
+ return chatId ? `telegram:${chatId}` : undefined;
106
+ }
107
+ });
108
+ ```
109
+
110
+ Returning `undefined` falls back to the built-in key sharder and then to the default shard.
111
+
112
+ ## API
113
+
114
+ ### `createChatSdkState(options)`
115
+
116
+ Creates a Chat SDK `StateAdapter` backed by a `ChatSdkStateAgent` sub-agent.
117
+
118
+ ```typescript
119
+ import { createChatSdkState } from "agents/chat-sdk";
120
+
121
+ export { ChatSdkStateAgent } from "agents/chat-sdk";
122
+
123
+ const state = createChatSdkState({
124
+ // parent: this // optional, defaults to the current Agent from `getCurrentAgent()`
125
+ });
126
+ ```
127
+
128
+ Options:
129
+
130
+ - `agent`: Optional custom subclass of `ChatSdkStateAgent`. Defaults to `ChatSdkStateAgent`.
131
+ - `parent`: Optional parent Agent that will call `subAgent()` to create state shards. Defaults to the current Agent from `getCurrentAgent()` when called inside an Agent lifecycle method or request handler.
132
+ - `name`: Default shard name for keys that cannot be mapped. Defaults to `default`.
133
+ - `shardKey(threadId)`: Maps Chat SDK thread IDs and lock keys to a shard name.
134
+ - `keyShard(key)`: Maps generic Chat SDK cache/list keys to a shard name.
135
+
136
+ ### `ChatSdkStateAgent`
137
+
138
+ The sub-agent class that stores state in SQLite. Export it from your Worker entry point so the runtime can create it.
139
+
140
+ ```typescript
141
+ export { ChatSdkStateAgent } from "agents/chat-sdk";
142
+ ```
143
+
144
+ ### `ChatSdkStateAdapter`
145
+
146
+ The concrete `StateAdapter` implementation returned by `createChatSdkState()`. Most applications do not need to instantiate it directly.
147
+
148
+ ## What is stored
149
+
150
+ The adapter implements the full Chat SDK `StateAdapter` interface:
151
+
152
+ - Subscriptions for `thread.subscribe()` and `thread.unsubscribe()`.
153
+ - Locks for per-thread or per-channel concurrency.
154
+ - Pending message queues for `queue`, `debounce`, and `burst` concurrency strategies.
155
+ - Generic key-value cache entries with optional TTL.
156
+ - Append-only lists with max-length trimming and list-level TTL refresh.
157
+
158
+ Chat SDK features built on these primitives include:
159
+
160
+ - Message deduplication.
161
+ - Thread and channel state.
162
+ - Persistent thread history for adapters that opt in to `persistThreadHistory`.
163
+ - Callback URL token storage.
164
+ - Modal context storage.
165
+ - Cross-platform transcripts.
166
+
167
+ ## Cleanup behavior
168
+
169
+ TTL reads are strict: expired locks, cache values, queue entries, and list entries are ignored or deleted before they are returned.
170
+
171
+ Physical cleanup is lazy. `ChatSdkStateAgent` schedules one cleanup callback for the earliest known expiry and reschedules after cleanup runs. This keeps idle shards quiet while preventing expired rows from accumulating indefinitely.
172
+
173
+ ## Example
174
+
175
+ See `examples/chat-sdk-messenger` for a complete Telegram bot that uses:
176
+
177
+ - `createChatSdkState()` for Chat SDK state.
178
+ - `ThinkMessengerStateAgent`, a Think-specific wrapper around
179
+ `ChatSdkStateAgent`, as a sub-agent.
180
+ - Chat SDK burst/debounce concurrency.
181
+ - Think-backed AI replies running in managed fibers.
@@ -0,0 +1,520 @@
1
+ # Client SDK
2
+
3
+ Connect to agents from any JavaScript runtime — browsers, Node.js, Deno, Bun, or edge functions — using WebSockets or HTTP. The SDK provides real-time state synchronization, RPC method calls, and streaming responses.
4
+
5
+ ## Overview
6
+
7
+ The client SDK offers two ways to connect with a websocket connection, and one way to make HTTP requests.
8
+
9
+ | Client | Use Case |
10
+ | ------------- | ----------------------------------------------------------- |
11
+ | `useAgent` | React hook with automatic reconnection and state management |
12
+ | `AgentClient` | Vanilla JavaScript/TypeScript class for any environment |
13
+ | `agentFetch` | HTTP requests when WebSocket isn't needed |
14
+
15
+ All clients provide:
16
+
17
+ - **Bidirectional state sync** - Push and receive state updates in real-time
18
+ - **RPC calls** - Call agent methods with typed arguments and return values
19
+ - **Streaming** - Handle chunked responses for AI completions
20
+ - **Auto-reconnection** - Built on [PartySocket](https://docs.partykit.io/reference/partysocket-api/) for reliable connections
21
+
22
+ ## Quick Start
23
+
24
+ ### React
25
+
26
+ ```tsx
27
+ import { useAgent } from "agents/react";
28
+
29
+ function Chat() {
30
+ const agent = useAgent({
31
+ agent: "ChatAgent",
32
+ name: "room-123"
33
+ });
34
+
35
+ const sendMessage = async () => {
36
+ const response = await agent.call("sendMessage", ["Hello!"]);
37
+ console.log("Response:", response);
38
+ };
39
+
40
+ return (
41
+ <div>
42
+ <p>Messages: {agent.state?.messageCount ?? 0}</p>
43
+ <button onClick={sendMessage}>Send</button>
44
+ </div>
45
+ );
46
+ }
47
+ ```
48
+
49
+ ### Vanilla JavaScript
50
+
51
+ ```typescript
52
+ import { AgentClient } from "agents/client";
53
+
54
+ const client = new AgentClient({
55
+ agent: "ChatAgent",
56
+ name: "room-123",
57
+ host: "your-worker.your-subdomain.workers.dev"
58
+ });
59
+
60
+ await client.ready;
61
+
62
+ // Read state directly
63
+ console.log("Current state:", client.state);
64
+
65
+ // Call a method
66
+ const response = await client.call("sendMessage", ["Hello!"]);
67
+ ```
68
+
69
+ ## Connecting to Agents
70
+
71
+ ### Agent Naming
72
+
73
+ The `agent` parameter is your agent class name. It's automatically converted from camelCase to kebab-case for the URL:
74
+
75
+ ```typescript
76
+ // These are equivalent:
77
+ useAgent({ agent: "ChatAgent" }); // → /agents/chat-agent/...
78
+ useAgent({ agent: "MyCustomAgent" }); // → /agents/my-custom-agent/...
79
+ useAgent({ agent: "LOUD_AGENT" }); // → /agents/loud-agent/...
80
+ ```
81
+
82
+ ### Instance Names
83
+
84
+ The `name` parameter identifies a specific agent instance. If omitted, defaults to `"default"`:
85
+
86
+ ```typescript
87
+ // Connect to a specific chat room
88
+ useAgent({ agent: "ChatAgent", name: "room-123" });
89
+
90
+ // Connect to a user's personal agent
91
+ useAgent({ agent: "UserAgent", name: userId });
92
+
93
+ // Uses "default" instance
94
+ useAgent({ agent: "ChatAgent" });
95
+ ```
96
+
97
+ ### Connection Options
98
+
99
+ Both `useAgent` and `AgentClient` accept PartySocket options:
100
+
101
+ ```typescript
102
+ useAgent({
103
+ agent: "ChatAgent",
104
+ name: "room-123",
105
+
106
+ // Connection settings
107
+ host: "my-worker.workers.dev", // Custom host (defaults to current origin)
108
+ path: "/custom/path", // Custom path prefix
109
+
110
+ // Query parameters (sent on connection)
111
+ query: {
112
+ token: "abc123",
113
+ version: "2"
114
+ },
115
+
116
+ // Event handlers
117
+ onOpen: () => console.log("Connected"),
118
+ onClose: () => console.log("Disconnected"),
119
+ onError: (error) => console.error("Error:", error)
120
+ });
121
+ ```
122
+
123
+ ### Async Query Parameters
124
+
125
+ For authentication tokens or other async data, pass a function that returns a Promise:
126
+
127
+ ```typescript
128
+ useAgent({
129
+ agent: "ChatAgent",
130
+ name: "room-123",
131
+
132
+ // Async query - called before connecting
133
+ query: async () => {
134
+ const token = await getAuthToken();
135
+ return { token };
136
+ },
137
+
138
+ // Dependencies that trigger re-fetching the query
139
+ queryDeps: [userId],
140
+
141
+ // Cache TTL for the query result (default: 5 minutes)
142
+ cacheTtl: 60 * 1000 // 1 minute
143
+ });
144
+ ```
145
+
146
+ The query function is cached and only re-called when:
147
+
148
+ - `queryDeps` change
149
+ - `cacheTtl` expires
150
+ - The component remounts
151
+
152
+ ## State Synchronization
153
+
154
+ Agents can maintain state that syncs bidirectionally with all connected clients. Both `useAgent` and `AgentClient` expose a `state` property that tracks the current agent state.
155
+
156
+ ### Reading State
157
+
158
+ ```tsx
159
+ const agent = useAgent({
160
+ agent: "GameAgent",
161
+ name: "game-123"
162
+ });
163
+
164
+ // Read state directly — reactive in React (re-renders on change)
165
+ return <div>Score: {agent.state?.score}</div>;
166
+ ```
167
+
168
+ `agent.state` starts as `undefined` and is populated when the server sends state on connect (from the agent's `initialState`). Use optional chaining for safe access.
169
+
170
+ ### Pushing State Updates
171
+
172
+ ```typescript
173
+ // Update the agent's state from the client
174
+ agent.setState({ score: 100, level: 5 });
175
+
176
+ // Spread existing state for partial updates
177
+ agent.setState({ ...agent.state, score: agent.state.score + 10 });
178
+ ```
179
+
180
+ When you call `setState()`:
181
+
182
+ 1. The state is sent to the agent over WebSocket
183
+ 2. The agent's `onStateChanged()` method is called
184
+ 3. The agent broadcasts the new state to all connected clients
185
+ 4. `agent.state` updates on the next render (React) or immediately (`AgentClient`)
186
+
187
+ ### Listening for State Changes
188
+
189
+ For side effects when state changes, use the `onStateUpdate` callback:
190
+
191
+ ```typescript
192
+ const agent = useAgent({
193
+ agent: "GameAgent",
194
+ name: "game-123",
195
+ onStateUpdate: (state, source) => {
196
+ // source: "server" (agent pushed) or "client" (you pushed)
197
+ console.log(`State updated from ${source}:`, state);
198
+ }
199
+ });
200
+ ```
201
+
202
+ > For most use cases, reading `agent.state` directly is simpler than tracking state manually with `onStateUpdate`. Use `onStateUpdate` when you need to trigger side effects on state changes.
203
+
204
+ ### State Flow
205
+
206
+ ```
207
+ ┌─────────┐ ┌─────────┐
208
+ │ Client │ ── setState() ────▶ │ Agent │
209
+ │ │ │ │
210
+ │ .state │ ◀── state update ── │ │
211
+ └─────────┘ (broadcast) └─────────┘
212
+ ```
213
+
214
+ ## Calling Agent Methods (RPC)
215
+
216
+ Call methods on your agent that are decorated with `@callable()`.
217
+
218
+ > **Note:** The `@callable()` decorator is only required for methods called from external runtimes (browsers, other services). When calling from within the same Worker, you can use standard [Durable Object RPC](https://developers.cloudflare.com/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/#invoke-rpc-methods) directly on the stub without the decorator.
219
+
220
+ ### Using call()
221
+
222
+ ```typescript
223
+ // Basic call
224
+ const result = await agent.call("getUser", [userId]);
225
+
226
+ // Call with multiple arguments
227
+ const result = await agent.call("createPost", [title, content, tags]);
228
+
229
+ // Call with no arguments
230
+ const result = await agent.call("getStats");
231
+ ```
232
+
233
+ ### Using the Stub Proxy
234
+
235
+ The `stub` property provides a cleaner syntax for method calls:
236
+
237
+ ```typescript
238
+ // Instead of:
239
+ const user = await agent.call("getUser", ["user-123"]);
240
+
241
+ // You can write:
242
+ const user = await agent.stub.getUser("user-123");
243
+
244
+ // Multiple arguments work naturally:
245
+ const post = await agent.stub.createPost(title, content, tags);
246
+ ```
247
+
248
+ ### TypeScript Integration
249
+
250
+ For full type safety, pass your Agent class as a type parameter:
251
+
252
+ ```typescript
253
+ import type { MyAgent } from "./agents/my-agent";
254
+
255
+ const agent = useAgent<MyAgent, MyAgentState>({
256
+ agent: "MyAgent",
257
+ name: "instance-1"
258
+ });
259
+
260
+ // Now stub methods are fully typed!
261
+ const result = await agent.stub.processData({ input: "test" });
262
+ // ^? Awaited<ReturnType<MyAgent["processData"]>>
263
+ ```
264
+
265
+ ### Streaming Responses
266
+
267
+ For methods that return `StreamingResponse`, handle chunks as they arrive:
268
+
269
+ ```typescript
270
+ // Agent-side:
271
+ @callable()
272
+ async generateText(prompt: string) {
273
+ return new StreamingResponse(async (stream) => {
274
+ for await (const chunk of llm.stream(prompt)) {
275
+ await stream.write(chunk);
276
+ }
277
+ });
278
+ }
279
+
280
+ // Client-side:
281
+ await agent.call("generateText", [prompt], {
282
+ onChunk: (chunk) => {
283
+ // Called for each chunk
284
+ appendToOutput(chunk);
285
+ },
286
+ onDone: (finalResult) => {
287
+ // Called when stream completes
288
+ console.log("Complete:", finalResult);
289
+ },
290
+ onError: (error) => {
291
+ // Called if streaming fails
292
+ console.error("Stream error:", error);
293
+ }
294
+ });
295
+ ```
296
+
297
+ ## HTTP Requests with agentFetch
298
+
299
+ For one-off requests without maintaining a WebSocket connection:
300
+
301
+ ```typescript
302
+ import { agentFetch } from "agents/client";
303
+
304
+ // GET request
305
+ const response = await agentFetch({
306
+ agent: "DataAgent",
307
+ name: "instance-1",
308
+ host: "my-worker.workers.dev"
309
+ });
310
+
311
+ const data = await response.json();
312
+
313
+ // POST request with body
314
+ const response = await agentFetch(
315
+ {
316
+ agent: "DataAgent",
317
+ name: "instance-1",
318
+ host: "my-worker.workers.dev"
319
+ },
320
+ {
321
+ method: "POST",
322
+ headers: { "Content-Type": "application/json" },
323
+ body: JSON.stringify({ action: "process" })
324
+ }
325
+ );
326
+ ```
327
+
328
+ **When to use `agentFetch` vs WebSocket:**
329
+
330
+ | Use `agentFetch` | Use `useAgent`/`AgentClient` |
331
+ | ------------------------------- | ---------------------------- |
332
+ | One-time requests | Real-time updates needed |
333
+ | Server-to-server calls | Bidirectional communication |
334
+ | Simple REST-style API | State synchronization |
335
+ | No persistent connection needed | Multiple RPC calls |
336
+
337
+ ## React Hook Reference
338
+
339
+ ### UseAgentOptions
340
+
341
+ ```typescript
342
+ type UseAgentOptions<State> = {
343
+ // Required
344
+ agent: string; // Agent class name
345
+
346
+ // Optional
347
+ name?: string; // Instance name (default: "default")
348
+ host?: string; // Custom host
349
+ path?: string; // Custom path prefix
350
+
351
+ // Query parameters
352
+ query?:
353
+ | Record<string, string | null>
354
+ | (() => Promise<Record<string, string | null>>);
355
+ queryDeps?: unknown[]; // Dependencies for async query
356
+ cacheTtl?: number; // Query cache TTL in ms (default: 5 min)
357
+
358
+ // Callbacks
359
+ onStateUpdate?: (state: State, source: "server" | "client") => void;
360
+ onMcpUpdate?: (mcpServers: MCPServersState) => void;
361
+ onOpen?: () => void;
362
+ onClose?: () => void;
363
+ onError?: (error: Event) => void;
364
+ onMessage?: (message: MessageEvent) => void;
365
+ };
366
+ ```
367
+
368
+ ### Return Value
369
+
370
+ ```typescript
371
+ const agent = useAgent(options);
372
+
373
+ agent.state; // State | undefined - Current agent state (reactive)
374
+ agent.agent; // string - Kebab-case agent name
375
+ agent.name; // string - Instance name
376
+ agent.setState(state); // void - Push state to agent
377
+ agent.call(method, args?, streamOptions?); // Promise<T> - Call agent method
378
+ agent.stub; // Proxy - Typed method calls
379
+ agent.send(data); // void - Send raw WebSocket message
380
+ agent.close(); // void - Close connection
381
+ agent.reconnect(); // void - Force reconnection
382
+ ```
383
+
384
+ ## Vanilla JS Reference
385
+
386
+ ### AgentClientOptions
387
+
388
+ ```typescript
389
+ type AgentClientOptions<State> = {
390
+ // Required
391
+ agent: string; // Agent class name
392
+ host: string; // Worker host
393
+
394
+ // Optional
395
+ name?: string; // Instance name (default: "default")
396
+ path?: string; // Custom path prefix
397
+ query?: Record<string, string>;
398
+
399
+ // Callbacks
400
+ onStateUpdate?: (state: State, source: "server" | "client") => void;
401
+ };
402
+ ```
403
+
404
+ ### AgentClient Methods
405
+
406
+ ```typescript
407
+ const client = new AgentClient(options);
408
+
409
+ client.state; // State | undefined - Current agent state
410
+ client.agent; // string - Kebab-case agent name
411
+ client.name; // string - Instance name
412
+ client.setState(state); // void - Push state to agent
413
+ client.call(method, args?, streamOptions?); // Promise<T> - Call agent method
414
+ client.send(data); // void - Send raw WebSocket message
415
+ client.close(); // void - Close connection
416
+ client.reconnect(); // void - Force reconnection
417
+
418
+ // Event listeners (inherited from PartySocket)
419
+ client.addEventListener("open", () => {});
420
+ client.addEventListener("close", () => {});
421
+ client.addEventListener("error", () => {});
422
+ client.addEventListener("message", () => {});
423
+ ```
424
+
425
+ ## MCP Server Integration
426
+
427
+ If your agent uses MCP (Model Context Protocol) servers, you can receive updates about their state:
428
+
429
+ ```typescript
430
+ const agent = useAgent({
431
+ agent: "AssistantAgent",
432
+ name: "session-123",
433
+ onMcpUpdate: (mcpServers) => {
434
+ // mcpServers is a record of server states
435
+ for (const [serverId, server] of Object.entries(mcpServers)) {
436
+ console.log(`${serverId}: ${server.connectionState}`);
437
+ console.log(`Tools: ${server.tools?.map((t) => t.name).join(", ")}`);
438
+ }
439
+ }
440
+ });
441
+ ```
442
+
443
+ ## Error Handling
444
+
445
+ ### Connection Errors
446
+
447
+ ```typescript
448
+ const agent = useAgent({
449
+ agent: "MyAgent",
450
+ onError: (error) => {
451
+ console.error("WebSocket error:", error);
452
+ },
453
+ onClose: () => {
454
+ console.log("Connection closed, will auto-reconnect...");
455
+ }
456
+ });
457
+ ```
458
+
459
+ ### RPC Errors
460
+
461
+ ```typescript
462
+ try {
463
+ const result = await agent.call("riskyMethod", [data]);
464
+ } catch (error) {
465
+ // Error thrown by the agent method
466
+ console.error("RPC failed:", error.message);
467
+ }
468
+ ```
469
+
470
+ ### Streaming Errors
471
+
472
+ ```typescript
473
+ await agent.call("streamingMethod", [data], {
474
+ onChunk: (chunk) => handleChunk(chunk),
475
+ onError: (errorMessage) => {
476
+ // Stream-specific error handling
477
+ console.error("Stream error:", errorMessage);
478
+ }
479
+ });
480
+ ```
481
+
482
+ ## Best Practices
483
+
484
+ ### 1. Use Typed Stubs
485
+
486
+ ```typescript
487
+ // Prefer this:
488
+ const user = await agent.stub.getUser(id);
489
+
490
+ // Over this:
491
+ const user = await agent.call("getUser", [id]);
492
+ ```
493
+
494
+ ### 2. Reconnection is Automatic
495
+
496
+ The client auto-reconnects and the agent automatically sends the current state on each connection. `agent.state` is updated automatically — no manual re-sync needed.
497
+
498
+ ### 3. Optimize Query Caching
499
+
500
+ ```typescript
501
+ // For auth tokens that expire hourly:
502
+ useAgent({
503
+ query: async () => ({ token: await getToken() }),
504
+ cacheTtl: 55 * 60 * 1000, // Refresh 5 min before expiry
505
+ queryDeps: [userId] // Refresh if user changes
506
+ });
507
+ ```
508
+
509
+ ### 4. Clean Up Connections
510
+
511
+ In vanilla JS, close connections when done:
512
+
513
+ ```typescript
514
+ const client = new AgentClient({ agent: "MyAgent", host: "..." });
515
+
516
+ // When done:
517
+ client.close();
518
+ ```
519
+
520
+ React's `useAgent` handles cleanup automatically on unmount.