agents 0.20.0 → 0.21.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.
- package/README.md +50 -12
- package/dist/{agent-tool-types-Btk9ETS-.d.ts → agent-tool-types-CzGGB-20.d.ts} +403 -115
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-UuScsJg3.d.ts → agent-tools-zR2d5uij.d.ts} +2 -2
- package/dist/agent-tools.d.ts +24 -8
- package/dist/agent-tools.js +14 -6
- package/dist/agent-tools.js.map +1 -1
- package/dist/browser/ai.d.ts +5 -3
- package/dist/browser/ai.js +86 -7
- package/dist/browser/ai.js.map +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/tanstack-ai.js +13 -1
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/chat/index.d.ts +31 -2
- package/dist/chat/index.js +57 -2
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +5 -179
- package/dist/chat/react.js +18 -555
- package/dist/chat/react.js.map +1 -1
- package/dist/chat/transport.d.ts +10 -0
- package/dist/chat/transport.js +2 -0
- package/dist/chat-sdk/index.d.ts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/cloudflare-BduZwmYK.js +204 -0
- package/dist/cloudflare-BduZwmYK.js.map +1 -0
- package/dist/{connector-v2M1zlZp.d.ts → connector-CkQD4MK3.d.ts} +20 -3
- package/dist/{connector-KEJnl6e5.js → connector-CptFKzRh.js} +158 -40
- package/dist/connector-CptFKzRh.js.map +1 -0
- package/dist/{handler-stateless-8hQN_kC3.js → handler-stateless-CIkKPETH.js} +20 -69
- package/dist/handler-stateless-CIkKPETH.js.map +1 -0
- package/dist/index.d.ts +21 -11
- package/dist/index.js +31 -21
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +20 -20
- package/dist/mcp/index.d.ts +35 -35
- package/dist/mcp/index.js +32 -98
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/observability/ai/index.d.ts +10 -131
- package/dist/observability/ai/index.js +209 -444
- package/dist/observability/ai/index.js.map +1 -1
- package/dist/{wire-types-CU9rLoeS.js → protocol-Dqc2MQxo.js} +2 -46
- package/dist/protocol-Dqc2MQxo.js.map +1 -0
- package/dist/react.d.ts +1 -1
- package/dist/react.js +5 -8
- package/dist/react.js.map +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/sub-routing.d.ts +18 -6
- package/dist/sub-routing.js +92 -2
- package/dist/sub-routing.js.map +1 -1
- package/dist/wire-types-CnMt6_HR.js +47 -0
- package/dist/wire-types-CnMt6_HR.js.map +1 -0
- package/dist/workflow-types.d.ts +25 -25
- package/dist/workflow-types.js.map +1 -1
- package/dist/workflows.d.ts +22 -22
- package/dist/ws-chat-transport-CIoOBbO7.js +561 -0
- package/dist/ws-chat-transport-CIoOBbO7.js.map +1 -0
- package/dist/ws-chat-transport-UNRIS2xl.d.ts +184 -0
- package/docs/adding-to-existing-project.md +4 -2
- package/docs/agent-class.md +1 -1
- package/docs/agent-tools.md +29 -0
- package/docs/browse-the-web.md +16 -1
- package/docs/chat-agents.md +3 -1
- package/docs/client-sdk.md +12 -8
- package/docs/configuration.md +7 -1
- package/docs/cross-domain-authentication.md +7 -35
- package/docs/email.md +2 -13
- package/docs/human-in-the-loop.md +15 -12
- package/docs/long-running-agents.md +11 -11
- package/docs/mcp-client.md +1 -1
- package/docs/mcp-servers.md +7 -1
- package/docs/mcp-transports.md +18 -12
- package/docs/migration-to-ai-sdk-v5.md +2 -2
- package/docs/migration-to-ai-sdk-v6.md +5 -1
- package/docs/observability.md +24 -67
- package/docs/routing.md +27 -0
- package/docs/sub-agents.md +47 -2
- package/docs/webhooks.md +109 -136
- package/docs/workflows.md +9 -1
- package/package.json +19 -10
- package/dist/cloudflare-BldFV0Pa.js +0 -117
- package/dist/cloudflare-BldFV0Pa.js.map +0 -1
- package/dist/connector-KEJnl6e5.js.map +0 -1
- package/dist/handler-stateless-8hQN_kC3.js.map +0 -1
- package/dist/wire-types-CU9rLoeS.js.map +0 -1
- package/dist/workflow-types-Baz_PO5v.d.ts +0 -280
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { ChatTransport, UIMessage, UIMessageChunk } from "ai";
|
|
2
|
+
|
|
3
|
+
//#region src/chat/ws-chat-transport.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Agent-like interface for sending/receiving WebSocket messages.
|
|
6
|
+
* Matches `AgentClient` from `agents/client` and the connection returned by
|
|
7
|
+
* `useAgent` from `agents/react`.
|
|
8
|
+
*/
|
|
9
|
+
interface AgentConnection {
|
|
10
|
+
send: (data: string) => void;
|
|
11
|
+
addEventListener: (
|
|
12
|
+
type: string,
|
|
13
|
+
listener: (event: MessageEvent) => void,
|
|
14
|
+
options?: {
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
}
|
|
17
|
+
) => void;
|
|
18
|
+
removeEventListener: (
|
|
19
|
+
type: string,
|
|
20
|
+
listener: (event: MessageEvent) => void
|
|
21
|
+
) => void;
|
|
22
|
+
}
|
|
23
|
+
type WebSocketChatTransportOptions<ChatMessage extends UIMessage = UIMessage> =
|
|
24
|
+
{
|
|
25
|
+
/** The framework-neutral AgentClient or useAgent connection. */ agent: AgentConnection;
|
|
26
|
+
/**
|
|
27
|
+
* Callback to add custom fields to the request body before sending.
|
|
28
|
+
*/
|
|
29
|
+
prepareBody?: (options: {
|
|
30
|
+
messages: ChatMessage[];
|
|
31
|
+
trigger: "submit-message" | "regenerate-message";
|
|
32
|
+
messageId?: string;
|
|
33
|
+
}) => Promise<Record<string, unknown>> | Record<string, unknown>;
|
|
34
|
+
/**
|
|
35
|
+
* Optional set to track active request IDs.
|
|
36
|
+
* IDs are added when a request starts and removed when it completes.
|
|
37
|
+
* Used by the onAgentMessage handler to skip messages already handled by the transport.
|
|
38
|
+
*/
|
|
39
|
+
activeRequestIds?: Set<string>;
|
|
40
|
+
/**
|
|
41
|
+
* Whether generic client-side abort/cancel lifecycle should cancel the
|
|
42
|
+
* server turn. Explicit cancellation via cancelActiveServerTurn() always
|
|
43
|
+
* sends CF_AGENT_CHAT_REQUEST_CANCEL.
|
|
44
|
+
* @default false
|
|
45
|
+
*/
|
|
46
|
+
cancelOnClientAbort?: boolean;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* ChatTransport that sends messages over WebSocket and returns a
|
|
50
|
+
* ReadableStream<UIMessageChunk> that the AI SDK's useChat consumes directly.
|
|
51
|
+
*
|
|
52
|
+
* This low-level transport handles new and regenerated request streams plus
|
|
53
|
+
* cancellation. Higher-level protocol coordination such as automatic reconnect
|
|
54
|
+
* resume, cross-tab transcript synchronization, and client-tool continuations
|
|
55
|
+
* is provided by integrations such as `useAgentChat`.
|
|
56
|
+
*/
|
|
57
|
+
declare class WebSocketChatTransport<
|
|
58
|
+
ChatMessage extends UIMessage = UIMessage
|
|
59
|
+
> implements ChatTransport<ChatMessage> {
|
|
60
|
+
agent: AgentConnection;
|
|
61
|
+
private prepareBody?;
|
|
62
|
+
private activeRequestIds?;
|
|
63
|
+
private cancelOnClientAbort;
|
|
64
|
+
private _resumeResolver;
|
|
65
|
+
private _resumeNoneResolver;
|
|
66
|
+
private _onStreamPending;
|
|
67
|
+
private _retryResumeProbe;
|
|
68
|
+
private _expectToolContinuation;
|
|
69
|
+
private _abortToolContinuation;
|
|
70
|
+
private _activeServerTurnId;
|
|
71
|
+
private _cancelAttachedStream;
|
|
72
|
+
private _detachResumeStream;
|
|
73
|
+
constructor(options: WebSocketChatTransportOptions<ChatMessage>);
|
|
74
|
+
/**
|
|
75
|
+
* Point the singleton transport at a new Agent connection. A pending resolver
|
|
76
|
+
* belongs to the old Chat/socket generation and must settle before messages
|
|
77
|
+
* from the replacement connection can be consumed (#1914 review).
|
|
78
|
+
*/
|
|
79
|
+
setAgent(agent: AgentConnection): void;
|
|
80
|
+
setCancelOnClientAbort(cancelOnClientAbort: boolean): void;
|
|
81
|
+
/**
|
|
82
|
+
* Explicitly cancel the active server turn, if any.
|
|
83
|
+
* This is separate from generic client-side abort/cancel lifecycle so
|
|
84
|
+
* clients can detach locally without stopping server work.
|
|
85
|
+
*/
|
|
86
|
+
cancelActiveServerTurn(): boolean;
|
|
87
|
+
private sendCancelFrame;
|
|
88
|
+
private setActiveServerTurn;
|
|
89
|
+
private clearActiveServerTurn;
|
|
90
|
+
/**
|
|
91
|
+
* Mark that the next reconnectToStream() call should attach to a
|
|
92
|
+
* server-initiated tool continuation rather than a page-load resume.
|
|
93
|
+
*/
|
|
94
|
+
expectToolContinuation(): void;
|
|
95
|
+
/**
|
|
96
|
+
* Abort the active client-side tool continuation stream, if one is attached
|
|
97
|
+
* to a server request id.
|
|
98
|
+
*/
|
|
99
|
+
abortActiveToolContinuation(): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* True when the transport is waiting for a resume handshake.
|
|
102
|
+
*/
|
|
103
|
+
isAwaitingResume(): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Settle and detach the current handshake without interpreting it as a
|
|
106
|
+
* server-idle response. Used when the owning hook/agent generation changes.
|
|
107
|
+
*/
|
|
108
|
+
cancelPendingResume(): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Invalidate all client-side resume state for an obsolete hook/agent
|
|
111
|
+
* generation without cancelling its durable server turn.
|
|
112
|
+
*/
|
|
113
|
+
resetResumeState(): void;
|
|
114
|
+
/**
|
|
115
|
+
* Re-send the active handshake request on the latest socket generation. This
|
|
116
|
+
* preserves one AI SDK resume operation while recovering a request/reply lost
|
|
117
|
+
* with the previous WebSocket.
|
|
118
|
+
*/
|
|
119
|
+
retryPendingResume(): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Called by onAgentMessage when it receives CF_AGENT_STREAM_RESUMING.
|
|
122
|
+
* If reconnectToStream is waiting, this handles the resume handshake
|
|
123
|
+
* (ACK + stream creation) and returns true. Otherwise returns false
|
|
124
|
+
* so the caller can use its own fallback path.
|
|
125
|
+
*/
|
|
126
|
+
handleStreamResuming(data: { id: string }): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Called by onAgentMessage when it receives CF_AGENT_STREAM_RESUME_NONE.
|
|
129
|
+
* If reconnectToStream is waiting, resolves the promise with null
|
|
130
|
+
* immediately (no 5-second timeout). Returns true if handled.
|
|
131
|
+
*/
|
|
132
|
+
handleStreamResumeNone(data?: { probeId?: string }): boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Called by onAgentMessage when it receives CF_AGENT_STREAM_PENDING (#1784):
|
|
135
|
+
* the server accepted a turn but its stream has not started yet. If a resume
|
|
136
|
+
* path is awaiting, extend its probe timeout (so it keeps waiting for the
|
|
137
|
+
* eventual STREAM_RESUMING / STREAM_RESUME_NONE instead of resolving null
|
|
138
|
+
* after the short window). Returns true if a waiting path consumed it.
|
|
139
|
+
*/
|
|
140
|
+
handleStreamPending(): boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Called by the hook's shared message handler when a server turn finishes
|
|
143
|
+
* outside the currently attached transport stream, such as after local-only
|
|
144
|
+
* client cleanup.
|
|
145
|
+
*/
|
|
146
|
+
handleServerTurnCompleted(requestId: string): void;
|
|
147
|
+
/**
|
|
148
|
+
* Register a server turn that is being rendered outside a transport-owned
|
|
149
|
+
* stream, such as the hook's fallback cross-tab/resume observer path.
|
|
150
|
+
*/
|
|
151
|
+
observeServerTurn(requestId: string): void;
|
|
152
|
+
sendMessages(options: {
|
|
153
|
+
chatId: string;
|
|
154
|
+
messages: ChatMessage[];
|
|
155
|
+
abortSignal: AbortSignal | undefined;
|
|
156
|
+
trigger: "submit-message" | "regenerate-message";
|
|
157
|
+
messageId?: string;
|
|
158
|
+
body?: object;
|
|
159
|
+
headers?: Record<string, string> | Headers;
|
|
160
|
+
metadata?: unknown;
|
|
161
|
+
}): Promise<ReadableStream<UIMessageChunk>>;
|
|
162
|
+
reconnectToStream(_options: {
|
|
163
|
+
chatId: string;
|
|
164
|
+
}): Promise<ReadableStream<UIMessageChunk> | null>;
|
|
165
|
+
/**
|
|
166
|
+
* Creates a deferred ReadableStream for client-side tool continuations.
|
|
167
|
+
* The stream is returned immediately so AI SDK status becomes "submitted"
|
|
168
|
+
* right after addToolOutput()/addToolApprovalResponse(), then it waits for
|
|
169
|
+
* the server to announce the continuation via STREAM_RESUMING.
|
|
170
|
+
*/
|
|
171
|
+
private _createToolContinuationStream;
|
|
172
|
+
/**
|
|
173
|
+
* Creates a ReadableStream that receives resumed stream chunks
|
|
174
|
+
* and forwards them to useChat as UIMessageChunk objects.
|
|
175
|
+
*/
|
|
176
|
+
private _createResumeStream;
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
export {
|
|
180
|
+
WebSocketChatTransport as n,
|
|
181
|
+
WebSocketChatTransportOptions as r,
|
|
182
|
+
AgentConnection as t
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=ws-chat-transport-UNRIS2xl.d.ts.map
|
|
@@ -32,7 +32,7 @@ npm install agents hono-agents
|
|
|
32
32
|
Create a new file for your agent (e.g., `src/agents/counter.ts`):
|
|
33
33
|
|
|
34
34
|
```typescript
|
|
35
|
-
import { Agent } from "agents";
|
|
35
|
+
import { Agent, callable } from "agents";
|
|
36
36
|
|
|
37
37
|
type CounterState = {
|
|
38
38
|
count: number;
|
|
@@ -41,11 +41,13 @@ type CounterState = {
|
|
|
41
41
|
export class Counter extends Agent<Env, CounterState> {
|
|
42
42
|
initialState: CounterState = { count: 0 };
|
|
43
43
|
|
|
44
|
+
@callable()
|
|
44
45
|
increment() {
|
|
45
46
|
this.setState({ count: this.state.count + 1 });
|
|
46
47
|
return this.state.count;
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
@callable()
|
|
49
51
|
decrement() {
|
|
50
52
|
this.setState({ count: this.state.count - 1 });
|
|
51
53
|
return this.state.count;
|
|
@@ -371,7 +373,7 @@ By default, agents are routed at `/agents/{agent-name}/{instance-name}`. You can
|
|
|
371
373
|
import { routeAgentRequest } from "agents";
|
|
372
374
|
|
|
373
375
|
const agentResponse = await routeAgentRequest(request, env, {
|
|
374
|
-
prefix: "
|
|
376
|
+
prefix: "api/agents" // Now routes at /api/agents/{agent-name}/{instance-name}
|
|
375
377
|
});
|
|
376
378
|
```
|
|
377
379
|
|
package/docs/agent-class.md
CHANGED
|
@@ -250,7 +250,7 @@ class MyAgent extends Agent {
|
|
|
250
250
|
|
|
251
251
|
### RPC and Callable Methods
|
|
252
252
|
|
|
253
|
-
`agents` take Durable Objects RPC one step forward by implementing RPC through WebSockets, so clients can also call methods on the Agent directly. To make a method callable through WS, developers can use the `@callable` decorator. Methods can return a serializable value or
|
|
253
|
+
`agents` take Durable Objects RPC one step forward by implementing RPC through WebSockets, so clients can also call methods on the Agent directly. To make a method callable through WS, developers can use the `@callable` decorator. Methods can return a serializable value or stream chunks (when using `@callable({ streaming: true })`).
|
|
254
254
|
|
|
255
255
|
```ts
|
|
256
256
|
class MyAgent extends Agent {
|
package/docs/agent-tools.md
CHANGED
|
@@ -48,6 +48,35 @@ export class Assistant extends Think<Env> {
|
|
|
48
48
|
}
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
### Use Valibot or another schema library
|
|
52
|
+
|
|
53
|
+
`agentTool()` accepts the AI SDK's flexible schema format. For Valibot, wrap the
|
|
54
|
+
schema with `valibotSchema()` so the AI SDK receives both runtime validation and
|
|
55
|
+
the JSON Schema required by the model. Use `@ai-sdk/valibot` v2 with AI SDK 6
|
|
56
|
+
and v3 with AI SDK 7:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { valibotSchema } from "@ai-sdk/valibot";
|
|
60
|
+
import { agentTool } from "agents/agent-tools";
|
|
61
|
+
import * as v from "valibot";
|
|
62
|
+
|
|
63
|
+
const researchInput = valibotSchema(
|
|
64
|
+
v.object({
|
|
65
|
+
query: v.pipe(v.string(), v.minLength(3))
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const research = agentTool(Researcher, {
|
|
70
|
+
description: "Research one topic in depth.",
|
|
71
|
+
inputSchema: researchInput
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
You can also provide a Zod schema, a Standard JSON Schema-compatible schema, or
|
|
76
|
+
a raw JSON Schema wrapped with `jsonSchema()` from `ai`. A validation-only
|
|
77
|
+
Standard Schema is not sufficient for a tool input because it does not provide
|
|
78
|
+
the JSON Schema sent to the model.
|
|
79
|
+
|
|
51
80
|
The child can also be an `AIChatAgent`:
|
|
52
81
|
|
|
53
82
|
```ts
|
package/docs/browse-the-web.md
CHANGED
|
@@ -244,7 +244,7 @@ const md = await browserMarkdown(this.env.BROWSER, { url });
|
|
|
244
244
|
const data = await browserExtract<{ price: number }>(this.env.BROWSER, {
|
|
245
245
|
url,
|
|
246
246
|
prompt: "the product price",
|
|
247
|
-
response_format: { type: "json_schema",
|
|
247
|
+
response_format: { type: "json_schema", json_schema: priceSchema }
|
|
248
248
|
});
|
|
249
249
|
```
|
|
250
250
|
|
|
@@ -279,6 +279,21 @@ class Researcher extends Think<Env> {
|
|
|
279
279
|
|
|
280
280
|
Quick Actions require a Worker `compatibility_date` of `2026-03-24` or later and `remote: true` on the browser binding for local `wrangler dev`.
|
|
281
281
|
|
|
282
|
+
## Using Kitesurf
|
|
283
|
+
|
|
284
|
+
To use Kitesurf with the CDP-based `browser_execute` tool, select it in the session options:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
const tools = createBrowserTools({
|
|
288
|
+
ctx: this.ctx,
|
|
289
|
+
browser: this.env.BROWSER,
|
|
290
|
+
loader: this.env.LOADER,
|
|
291
|
+
session: { browser: "kitesurf" }
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
A Kitesurf browser is scoped to its CDP WebSocket. It therefore supports only one-shot execution. Session reuse, pause and resume, Live View, recording, `keepAliveMs`, protocol discovery, and Kitesurf-backed Quick Actions are not available.
|
|
296
|
+
|
|
282
297
|
## Live View and human-in-the-loop
|
|
283
298
|
|
|
284
299
|
[Live View](https://developers.cloudflare.com/browser-run/features/live-view/) lets a human open a URL and watch — or take control of — a running browser session in real time. It is the building block for human-in-the-loop steps such as logging in, solving a CAPTCHA, completing MFA, or entering data you do not want to pass through an automation script.
|
package/docs/chat-agents.md
CHANGED
|
@@ -25,7 +25,7 @@ Built on the [AI SDK](https://ai-sdk.dev) and Cloudflare Durable Objects, you ge
|
|
|
25
25
|
### Install
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
npm install @cloudflare/ai-chat agents ai workers-ai-provider
|
|
28
|
+
npm install @cloudflare/ai-chat agents ai @ai-sdk/react workers-ai-provider
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
### Server
|
|
@@ -956,6 +956,8 @@ async onChatMessage() {
|
|
|
956
956
|
}
|
|
957
957
|
```
|
|
958
958
|
|
|
959
|
+
The `inputSchema` accepts the AI SDK's flexible schema format, so you are not limited to Zod. You can also use Valibot, a Standard JSON Schema-compatible schema, or a raw JSON Schema wrapped with `jsonSchema()` from `ai`. See [Use Valibot or another schema library](./agent-tools.md#use-valibot-or-another-schema-library) for details.
|
|
960
|
+
|
|
959
961
|
### Client-Side Tools
|
|
960
962
|
|
|
961
963
|
Define a tool on the server without `execute`, then handle it on the client with `onToolCall`. Use this for tools that need browser APIs:
|
package/docs/client-sdk.md
CHANGED
|
@@ -264,17 +264,21 @@ const result = await agent.stub.processData({ input: "test" });
|
|
|
264
264
|
|
|
265
265
|
### Streaming Responses
|
|
266
266
|
|
|
267
|
-
|
|
267
|
+
Mark a callable method as streaming. The framework passes a
|
|
268
|
+
`StreamingResponse` as its first argument:
|
|
268
269
|
|
|
269
270
|
```typescript
|
|
270
|
-
// Agent-side
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
271
|
+
// Agent-side
|
|
272
|
+
import { Agent, callable, type StreamingResponse } from "agents";
|
|
273
|
+
|
|
274
|
+
export class AIAgent extends Agent {
|
|
275
|
+
@callable({ streaming: true })
|
|
276
|
+
async generateText(stream: StreamingResponse, prompt: string) {
|
|
277
|
+
for (const chunk of ["Generated response for ", prompt]) {
|
|
278
|
+
stream.send(chunk);
|
|
276
279
|
}
|
|
277
|
-
|
|
280
|
+
stream.end();
|
|
281
|
+
}
|
|
278
282
|
}
|
|
279
283
|
|
|
280
284
|
// Client-side:
|
package/docs/configuration.md
CHANGED
|
@@ -617,12 +617,18 @@ Define environments in wrangler.jsonc:
|
|
|
617
617
|
"env": {
|
|
618
618
|
"staging": {
|
|
619
619
|
"name": "my-agent-staging",
|
|
620
|
+
"durable_objects": {
|
|
621
|
+
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
|
|
622
|
+
},
|
|
620
623
|
"vars": {
|
|
621
624
|
"ENVIRONMENT": "staging"
|
|
622
625
|
}
|
|
623
626
|
},
|
|
624
627
|
"production": {
|
|
625
628
|
"name": "my-agent-production",
|
|
629
|
+
"durable_objects": {
|
|
630
|
+
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
|
|
631
|
+
},
|
|
626
632
|
"vars": {
|
|
627
633
|
"ENVIRONMENT": "production"
|
|
628
634
|
}
|
|
@@ -647,7 +653,7 @@ npx wrangler secret put OPENAI_API_KEY --env production
|
|
|
647
653
|
|
|
648
654
|
### Separate Durable Objects
|
|
649
655
|
|
|
650
|
-
Each environment gets its own Durable Objects. Staging agents
|
|
656
|
+
Durable Object bindings are not inherited by named environments, so repeat them in every environment as shown above. Each environment gets its own Durable Objects. Staging agents do not share state with production agents.
|
|
651
657
|
|
|
652
658
|
To explicitly separate:
|
|
653
659
|
|
|
@@ -24,7 +24,7 @@ If the client and server share the origin, the browser will send cookies during
|
|
|
24
24
|
|
|
25
25
|
### Cross origin
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Cross-origin cookie behavior depends on the cookie's domain and `SameSite` attributes, whether the two origins are same-site, and browser third-party cookie policy. If you cannot rely on a cookie, pass a short-lived credential in the URL query and verify it on the server.
|
|
28
28
|
|
|
29
29
|
## Usage examples
|
|
30
30
|
|
|
@@ -79,50 +79,22 @@ function ChatComponent() {
|
|
|
79
79
|
|
|
80
80
|
### JWT refresh pattern
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
`useAgent` resolves an async query before connecting and reevaluates it when reconnecting. Return a fresh, short-lived application token each time:
|
|
83
83
|
|
|
84
84
|
```ts
|
|
85
85
|
import { useAgent } from "agents/react";
|
|
86
|
-
import { useCallback
|
|
87
|
-
|
|
88
|
-
const validateToken = async (token: string) => {
|
|
89
|
-
// An example of how you might implement this
|
|
90
|
-
const res = await fetch(`${API_HOST}/api/users/me`, {
|
|
91
|
-
headers: {
|
|
92
|
-
Authorization: `Bearer ${token}`
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
return res.ok;
|
|
97
|
-
};
|
|
86
|
+
import { useCallback } from "react";
|
|
98
87
|
|
|
99
|
-
|
|
100
|
-
// Depends on implementation:
|
|
101
|
-
// - You could use a longer-lived token to refresh the expired token
|
|
102
|
-
// - De-auth the app and prompt the user to log in manually
|
|
103
|
-
// - ...
|
|
104
|
-
};
|
|
88
|
+
declare function getShortLivedAccessToken(): Promise<string>;
|
|
105
89
|
|
|
106
90
|
function useJWTAgent(agentName: string) {
|
|
107
91
|
const asyncQuery = useCallback(async () => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// If no token OR the token is no longer valid
|
|
111
|
-
// request a fresh token
|
|
112
|
-
if (!token && !(await validateToken(token))) {
|
|
113
|
-
token = await refreshToken();
|
|
114
|
-
localStorage.setItem("jwt", token);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
token
|
|
119
|
-
};
|
|
92
|
+
return { token: await getShortLivedAccessToken() };
|
|
120
93
|
}, []);
|
|
121
94
|
|
|
122
|
-
|
|
95
|
+
return useAgent({
|
|
123
96
|
agent: agentName,
|
|
124
|
-
query: asyncQuery
|
|
125
|
-
queryDeps: [] // Run on mount
|
|
97
|
+
query: asyncQuery
|
|
126
98
|
});
|
|
127
99
|
}
|
|
128
100
|
```
|
package/docs/email.md
CHANGED
|
@@ -419,21 +419,10 @@ When your agent sends emails and expects replies, use secure reply routing to pr
|
|
|
419
419
|
|
|
420
420
|
### Setup
|
|
421
421
|
|
|
422
|
-
1.
|
|
423
|
-
|
|
424
|
-
```jsonc
|
|
425
|
-
// wrangler.jsonc
|
|
426
|
-
{
|
|
427
|
-
"vars": {
|
|
428
|
-
"EMAIL_SECRET": "change-me-in-production"
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
For production, use Wrangler secrets instead:
|
|
422
|
+
1. Store the signing key as a Wrangler secret. Do not put it in `vars` or commit it to source control:
|
|
434
423
|
|
|
435
424
|
```bash
|
|
436
|
-
wrangler secret put EMAIL_SECRET
|
|
425
|
+
pnpm exec wrangler secret put EMAIL_SECRET
|
|
437
426
|
```
|
|
438
427
|
|
|
439
428
|
2. Use the combined resolver pattern:
|
|
@@ -196,7 +196,7 @@ Define tools with `needsApproval` to require human confirmation:
|
|
|
196
196
|
```typescript
|
|
197
197
|
import { AIChatAgent } from "@cloudflare/ai-chat";
|
|
198
198
|
import { createWorkersAI } from "workers-ai-provider";
|
|
199
|
-
import { streamText, tool, convertToModelMessages } from "ai";
|
|
199
|
+
import { streamText, tool, convertToModelMessages, stepCountIs } from "ai";
|
|
200
200
|
import { z } from "zod";
|
|
201
201
|
|
|
202
202
|
export class MyAgent extends AIChatAgent {
|
|
@@ -238,7 +238,7 @@ export class MyAgent extends AIChatAgent {
|
|
|
238
238
|
execute: async ({ city }) => fetchWeather(city)
|
|
239
239
|
})
|
|
240
240
|
},
|
|
241
|
-
|
|
241
|
+
stopWhen: stepCountIs(5)
|
|
242
242
|
});
|
|
243
243
|
|
|
244
244
|
return result.toUIMessageStreamResponse();
|
|
@@ -246,6 +246,8 @@ export class MyAgent extends AIChatAgent {
|
|
|
246
246
|
}
|
|
247
247
|
```
|
|
248
248
|
|
|
249
|
+
The `inputSchema` accepts the AI SDK's flexible schema format, so you are not limited to Zod. You can also use Valibot, a Standard JSON Schema-compatible schema, or a raw JSON Schema wrapped with `jsonSchema()` from `ai`. See [Use Valibot or another schema library](./agent-tools.md#use-valibot-or-another-schema-library) for details.
|
|
250
|
+
|
|
249
251
|
### Client
|
|
250
252
|
|
|
251
253
|
Handle approval requests with `addToolApprovalResponse`:
|
|
@@ -380,7 +382,7 @@ export class MyAgent extends AIChatAgent {
|
|
|
380
382
|
inputSchema: z.object({})
|
|
381
383
|
})
|
|
382
384
|
},
|
|
383
|
-
|
|
385
|
+
stopWhen: stepCountIs(3)
|
|
384
386
|
});
|
|
385
387
|
|
|
386
388
|
return result.toUIMessageStreamResponse();
|
|
@@ -410,17 +412,18 @@ const { messages, sendMessage } = useAgentChat({
|
|
|
410
412
|
});
|
|
411
413
|
```
|
|
412
414
|
|
|
413
|
-
The server receives the tool output via `CF_AGENT_TOOL_RESULT` and can auto-continue the conversation
|
|
415
|
+
The server receives the tool output via `CF_AGENT_TOOL_RESULT` and can auto-continue the conversation when `stopWhen` allows another step, letting the LLM respond to the location data in the same turn.
|
|
414
416
|
|
|
415
417
|
### OpenAI Agents SDK Pattern
|
|
416
418
|
|
|
417
419
|
When using the [OpenAI Agents SDK](https://openai.github.io/openai-agents-js/), use the `needsApproval` function for conditional approval:
|
|
418
420
|
|
|
419
421
|
```typescript
|
|
420
|
-
import { Agent } from "agents";
|
|
421
|
-
import { tool, run } from "@openai/agents";
|
|
422
|
+
import { Agent as CloudflareAgent } from "agents";
|
|
423
|
+
import { Agent as OpenAIAgent, tool, run } from "@openai/agents";
|
|
424
|
+
import { z } from "zod";
|
|
422
425
|
|
|
423
|
-
export class WeatherAgent extends
|
|
426
|
+
export class WeatherAgent extends CloudflareAgent<Env> {
|
|
424
427
|
async processQuery(query: string) {
|
|
425
428
|
const weatherTool = tool({
|
|
426
429
|
name: "get_weather",
|
|
@@ -438,13 +441,13 @@ export class WeatherAgent extends Agent<Env, AgentState> {
|
|
|
438
441
|
}
|
|
439
442
|
});
|
|
440
443
|
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
444
|
+
const openaiAgent = new OpenAIAgent({
|
|
445
|
+
name: "Weather assistant",
|
|
446
|
+
instructions: "Help the user check the weather.",
|
|
447
|
+
tools: [weatherTool]
|
|
445
448
|
});
|
|
446
449
|
|
|
447
|
-
return
|
|
450
|
+
return run(openaiAgent, query);
|
|
448
451
|
}
|
|
449
452
|
}
|
|
450
453
|
```
|
|
@@ -86,7 +86,7 @@ type Task = {
|
|
|
86
86
|
externalJobId?: string;
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
-
export class ProjectManager extends Agent<ProjectState> {
|
|
89
|
+
export class ProjectManager extends Agent<Env, ProjectState> {
|
|
90
90
|
initialState: ProjectState = {
|
|
91
91
|
name: "",
|
|
92
92
|
status: "planning",
|
|
@@ -115,7 +115,7 @@ The pattern extends naturally to any event source that can reach a Worker — an
|
|
|
115
115
|
The agent does not need to be "started" or "deployed" separately for each wake source — they all route to the same Durable Object instance. The agent's identity (its name) is the routing key.
|
|
116
116
|
|
|
117
117
|
```typescript
|
|
118
|
-
export class ProjectManager extends Agent<ProjectState> {
|
|
118
|
+
export class ProjectManager extends Agent<Env, ProjectState> {
|
|
119
119
|
async onStart() {
|
|
120
120
|
// Daily deadline check at 9am UTC — idempotent, safe across restarts
|
|
121
121
|
await this.schedule(
|
|
@@ -163,7 +163,7 @@ Sometimes an agent needs to do work that takes longer than the idle eviction win
|
|
|
163
163
|
`keepAlive()` prevents this by creating a heartbeat that resets the inactivity timer:
|
|
164
164
|
|
|
165
165
|
```typescript
|
|
166
|
-
export class ProjectManager extends Agent<ProjectState> {
|
|
166
|
+
export class ProjectManager extends Agent<Env, ProjectState> {
|
|
167
167
|
async generateProjectPlan(goal: string) {
|
|
168
168
|
const result = await this.keepAliveWhile(async () => {
|
|
169
169
|
const plan = await this.callLLM(`Create a project plan for: ${goal}`);
|
|
@@ -223,7 +223,7 @@ side effects. If recovery succeeds, return a recovery result from
|
|
|
223
223
|
aborted, failed, or intentionally left interrupted.
|
|
224
224
|
|
|
225
225
|
```typescript
|
|
226
|
-
export class ProjectManager extends Agent<ProjectState> {
|
|
226
|
+
export class ProjectManager extends Agent<Env, ProjectState> {
|
|
227
227
|
async executeTask(task: Task) {
|
|
228
228
|
await this.runFiber(`task:${task.id}`, async (ctx) => {
|
|
229
229
|
const resources = await this.gatherResources(task);
|
|
@@ -268,7 +268,7 @@ The project manager frequently kicks off work that takes far longer than any sin
|
|
|
268
268
|
The project manager starts a CI pipeline for a task. The pipeline takes 20 minutes. Rather than holding a connection open, the agent registers its own URL as the callback and goes to sleep:
|
|
269
269
|
|
|
270
270
|
```typescript
|
|
271
|
-
export class ProjectManager extends Agent<ProjectState> {
|
|
271
|
+
export class ProjectManager extends Agent<Env, ProjectState> {
|
|
272
272
|
async startCIPipeline(task: Task) {
|
|
273
273
|
const response = await fetch("https://ci.example.com/api/pipelines", {
|
|
274
274
|
method: "POST",
|
|
@@ -307,7 +307,7 @@ export class ProjectManager extends Agent<ProjectState> {
|
|
|
307
307
|
Not every external service supports callbacks. When the project manager submits a video asset for generation, it needs to check back periodically until the job completes:
|
|
308
308
|
|
|
309
309
|
```typescript
|
|
310
|
-
export class ProjectManager extends Agent<ProjectState> {
|
|
310
|
+
export class ProjectManager extends Agent<Env, ProjectState> {
|
|
311
311
|
async startVideoGeneration(task: Task) {
|
|
312
312
|
const response = await fetch("https://video-api.example.com/generate", {
|
|
313
313
|
method: "POST",
|
|
@@ -354,7 +354,7 @@ export class ProjectManager extends Agent<ProjectState> {
|
|
|
354
354
|
A production deployment involves multiple steps that must each retry independently — build, test, stage, promote. The project manager should not manage these steps internally; it delegates to a [Workflow](./workflows.md) that handles retries and step sequencing:
|
|
355
355
|
|
|
356
356
|
```typescript
|
|
357
|
-
export class ProjectManager extends Agent<ProjectState> {
|
|
357
|
+
export class ProjectManager extends Agent<Env, ProjectState> {
|
|
358
358
|
async startDeployment(task: Task) {
|
|
359
359
|
const instanceId = await this.runWorkflow("DEPLOY_WORKFLOW", {
|
|
360
360
|
taskId: task.id,
|
|
@@ -422,7 +422,7 @@ type PlanStep = {
|
|
|
422
422
|
result?: unknown;
|
|
423
423
|
};
|
|
424
424
|
|
|
425
|
-
export class ProjectManager extends Agent<ProjectState> {
|
|
425
|
+
export class ProjectManager extends Agent<Env, ProjectState> {
|
|
426
426
|
async createPlan(goal: string) {
|
|
427
427
|
const steps = await this.keepAliveWhile(async () => {
|
|
428
428
|
return this.callLLM(`
|
|
@@ -508,7 +508,7 @@ This pattern has several advantages for long-running agents:
|
|
|
508
508
|
A project manager does not do everything itself. It delegates specialized work to sub-agents — child Durable Objects (facets) spawned under the parent. Each facet has its own isolated SQLite state and runs in parallel, but stays colocated on the same machine as the parent.
|
|
509
509
|
|
|
510
510
|
```typescript
|
|
511
|
-
export class ProjectManager extends Agent<ProjectState> {
|
|
511
|
+
export class ProjectManager extends Agent<Env, ProjectState> {
|
|
512
512
|
async delegateTask(task: Task) {
|
|
513
513
|
// Get a stub to a specialized agent (same DO namespace, unique name)
|
|
514
514
|
const researcher = await this.subAgent(
|
|
@@ -586,7 +586,7 @@ An agent that runs for months accumulates data: conversation history, timeline e
|
|
|
586
586
|
Schedule periodic cleanup to prune old data and archive completed work:
|
|
587
587
|
|
|
588
588
|
```typescript
|
|
589
|
-
export class ProjectManager extends Agent<ProjectState> {
|
|
589
|
+
export class ProjectManager extends Agent<Env, ProjectState> {
|
|
590
590
|
async onStart() {
|
|
591
591
|
await this.schedule("0 0 * * *", "housekeeping", {}, { idempotent: true });
|
|
592
592
|
}
|
|
@@ -634,7 +634,7 @@ For simpler cases: keep only the last N messages in the active context (sliding
|
|
|
634
634
|
A long-running agent eventually completes its purpose. The project ships, the investigation concludes, the monitoring window closes. Clean up explicitly:
|
|
635
635
|
|
|
636
636
|
```typescript
|
|
637
|
-
export class ProjectManager extends Agent<ProjectState> {
|
|
637
|
+
export class ProjectManager extends Agent<Env, ProjectState> {
|
|
638
638
|
async completeProject() {
|
|
639
639
|
// Cancel remaining schedules
|
|
640
640
|
const schedules = await this.listSchedules();
|
package/docs/mcp-client.md
CHANGED
|
@@ -18,7 +18,7 @@ The MCP client capability lets your agent:
|
|
|
18
18
|
Install the exact MCP client peer used by this Agents release:
|
|
19
19
|
|
|
20
20
|
```sh
|
|
21
|
-
pnpm add agents @modelcontextprotocol/client@2.0.0
|
|
21
|
+
pnpm add agents @modelcontextprotocol/client@2.0.0
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
```typescript
|
package/docs/mcp-servers.md
CHANGED
|
@@ -14,12 +14,18 @@ This guide covers the different ways to create MCP servers with the Agents SDK a
|
|
|
14
14
|
- **`McpAgent`** is a retained, feature-frozen SDK v1 path for existing stateful deployments. New servers should use `createMcpHandler()`.
|
|
15
15
|
- **Raw transport** gives you low-level control if the standard handler lifecycle is not suitable.
|
|
16
16
|
|
|
17
|
+
`McpAgent` and the other retained SDK v1 registration APIs use the upstream
|
|
18
|
+
Zod-based schema contract. Define their tool input and output schemas as Zod
|
|
19
|
+
shapes; AI SDK flexible-schema adapters are not accepted. For a new MCP server
|
|
20
|
+
that needs Standard Schema support, use `@modelcontextprotocol/server` v2 with
|
|
21
|
+
`createMcpHandler()`.
|
|
22
|
+
|
|
17
23
|
## Stateless MCP Server with `createMcpHandler()`
|
|
18
24
|
|
|
19
25
|
The simplest way to create an MCP server. Install the exact SDK v2 server peer, then use the isolated server entry point so legacy Agents transports and MCP clients stay out of your Worker bundle:
|
|
20
26
|
|
|
21
27
|
```sh
|
|
22
|
-
pnpm add agents @modelcontextprotocol/server@2.0.0
|
|
28
|
+
pnpm add agents @modelcontextprotocol/server@2.0.0 zod
|
|
23
29
|
```
|
|
24
30
|
|
|
25
31
|
```typescript
|