Skip to content

Commit c58b401

Browse files
feat: hoist useAgentChat into agents/chat/react with product wrappers (#1801)
* feat: hoist useAgentChat into agents/chat/react with product wrappers Move the canonical `useAgentChat` implementation, WebSocket chat transport, and chat wire types out of `@cloudflare/ai-chat` and into a new shared `agents/chat/react` entry. `@cloudflare/ai-chat/react` and the new `@cloudflare/think/react` become thin wrappers over this shared core, so Think apps no longer need to depend on `@cloudflare/ai-chat` just to get the React hook. Why --- Using `useAgentChat` from `@cloudflare/ai-chat` against a Think server was awkward and behaviorally divergent. The biggest footgun was `setMessages`: `AIChatAgent` persists a client-pushed flat transcript, while Think is server-authoritative and silently ignores it. Apps had to pull in an unrelated package and still got surprising behavior. What changed ------------ - agents: add `agents/chat/react` exposing `useAgentChat`, `WebSocketChatTransport`, and shared chat wire types (`MessageType`, `OutgoingMessage`, `IncomingMessage`). Add a new `syncMessagesToServer` option (default `true`) so server-authoritative hosts can keep `setMessages` local-only. `@ai-sdk/react` is added as an optional peer dependency (only the chat/react subpath needs it). - ai-chat: `react.tsx`, `types.ts`, and `ws-chat-transport.ts` are now thin re-export shims over `agents/chat/react`. Public API and behavior are unchanged for existing consumers. - think: add `@cloudflare/think/react`, a Think-tuned wrapper that hardcodes `syncMessagesToServer: false` (and omits it from its option type), so `setMessages` stays a local view update. A dev-only warn-once covers plain JS callers that pass the option anyway. - think (server): warn once (dev only) when Think receives a client-pushed `CF_AGENT_CHAT_MESSAGES` frame, naming `@cloudflare/think/react` and `clearHistory()` as the fix. This is behavior-triggered, so it only fires when an app actually hits the divergence (e.g. the ai-chat hook against a Think server). - docs/examples/starters: migrate all Think-backed clients to import the hook from `@cloudflare/think/react` and drop the now-unnecessary `@cloudflare/ai-chat` dependency. AIChatAgent-backed examples are unchanged. Document that `setMessages` is display-only on Think. Changesets: agents (minor), @cloudflare/think (minor), @cloudflare/ai-chat (patch). Co-authored-by: Cursor <cursoragent@cursor.com> * test(think-cli): assert basic template marker after ai-chat removal The starter migration dropped `@cloudflare/ai-chat` from the basic Think template, so the CLI scaffold test's dependency assertion was stale. Assert on `@cloudflare/kumo` instead — still a full-starter-only marker that distinguishes a template scaffold from an augment. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Sunil Pai <18808+threepointone@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 3c2afc9 commit c58b401

60 files changed

Lines changed: 3762 additions & 3531 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cloudflare/ai-chat": patch
3+
---
4+
5+
Refactor `@cloudflare/ai-chat/react` to re-export the shared implementation from `agents/chat/react` while preserving existing behavior and exports.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"agents": minor
3+
---
4+
5+
Add the shared `agents/chat/react` entry with `useAgentChat`, chat transport helpers, and shared chat wire types. The hook also adds `syncMessagesToServer` so hosts with server-authoritative transcript storage can keep `setMessages` local-only.

.changeset/think-react-wrapper.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cloudflare/think": minor
3+
---
4+
5+
Add `@cloudflare/think/react`, a Think-tuned `useAgentChat` wrapper that keeps `setMessages` local-only by default while reusing the shared chat React implementation.

docs/think/client-tools.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ On the client, pass `clientTools` to `useAgentChat`:
1818

1919
```tsx
2020
import { useAgent } from "agents/react";
21-
import { useAgentChat } from "@cloudflare/ai-chat/react";
21+
import { useAgentChat } from "@cloudflare/think/react";
2222

2323
function Chat() {
2424
const agent = useAgent({ agent: "MyAgent" });

docs/think/getting-started.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ npm init -y
2525
Install dependencies:
2626

2727
```sh
28-
npm install @cloudflare/think @cloudflare/ai-chat agents ai @cloudflare/shell zod workers-ai-provider react react-dom
28+
npm install @cloudflare/think agents ai @cloudflare/shell zod workers-ai-provider react react-dom
2929
npm install -D wrangler @cloudflare/vite-plugin @cloudflare/workers-types @vitejs/plugin-react @tailwindcss/vite tailwindcss typescript vite
3030
```
3131

@@ -119,7 +119,7 @@ Create `src/client.tsx`:
119119
```tsx
120120
import { createRoot } from "react-dom/client";
121121
import { useAgent } from "agents/react";
122-
import { useAgentChat } from "@cloudflare/ai-chat/react";
122+
import { useAgentChat } from "@cloudflare/think/react";
123123

124124
function Chat() {
125125
const agent = useAgent({ agent: "MyAgent" });
@@ -194,6 +194,8 @@ npx vite dev
194194

195195
Open the browser and send a message. The agent responds with streaming text, and workspace file tools are available to the model automatically.
196196

197+
> **`setMessages` is display-only on Think.** Think is server-authoritative — its transcript is a projection of the Session tree, not a flat array the client owns. `useAgentChat` from `@cloudflare/think/react` therefore keeps `setMessages` local to the React view: edits update what's on screen but are **not** persisted and won't survive a refresh or reconnect (the server re-projects the authoritative history). To persist a clear, call `clearHistory()`. If you push a full transcript to Think anyway (for example, by using `@cloudflare/ai-chat/react` against a Think server), the server ignores it and logs a one-time dev warning.
198+
197199
## 6. Add persistent memory
198200

199201
Override `configureSession` to give the model writable memory that survives restarts:

docs/think/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ chats.
584584

585585
```tsx
586586
import { useAgent } from "agents/react";
587-
import { useAgentChat } from "@cloudflare/ai-chat/react";
587+
import { useAgentChat } from "@cloudflare/think/react";
588588

589589
function Chat() {
590590
const agent = useAgent({ agent: "MyAgent" });

examples/AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ Every example should include an info card at the top of the page explaining what
209209

210210
### Chat message rendering
211211

212-
Any example with a chat UI (`useAgentChat` from `@cloudflare/ai-chat/react`, or `useChat`) must render the full shape of an assistant turn — not just text. Reference implementations: **`examples/ai-chat`** (streamdown + complete tool states) and **`examples/assistant`** (adds reasoning + approvals + branches).
212+
Any example with a chat UI (`useAgentChat` from `@cloudflare/ai-chat/react` or `@cloudflare/think/react`, or `useChat`) must render the full shape of an assistant turn — not just text. Reference implementations: **`examples/ai-chat`** (streamdown + complete tool states) and **`examples/assistant`** (adds reasoning + approvals + branches).
213213

214214
Rules:
215215

examples/agent-skills/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
"types": "wrangler types env.d.ts --include-runtime false"
1111
},
1212
"dependencies": {
13-
"@cloudflare/ai-chat": "*",
1413
"@cloudflare/kumo": "^2.5.2",
1514
"@cloudflare/think": "*",
1615
"@phosphor-icons/react": "^2.1.10",

examples/agent-skills/src/client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useAgent } from "agents/react";
2-
import { useAgentChat } from "@cloudflare/ai-chat/react";
2+
import { useAgentChat } from "@cloudflare/think/react";
33
import { getToolName, isToolUIPart } from "ai";
44
import {
55
ChatCircleTextIcon,

examples/agents-as-tools/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
"types": "wrangler types env.d.ts --include-runtime false"
1414
},
1515
"dependencies": {
16-
"@cloudflare/ai-chat": "*",
1716
"@cloudflare/kumo": "^2.5.2",
1817
"@cloudflare/think": "*",
1918
"@phosphor-icons/react": "^2.1.10",

0 commit comments

Comments
 (0)