feat: hoist useAgentChat into agents/chat/react with product wrappers#1801
Merged
Conversation
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>
🦋 Changeset detectedLatest commit: f1af2d0 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
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>
agents
@cloudflare/ai-chat
@cloudflare/codemode
create-think
hono-agents
@cloudflare/shell
@cloudflare/think
@cloudflare/voice
@cloudflare/worker-bundler
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Moves the canonical
useAgentChatimplementation, the WebSocket chat transport, and the chat wire types out of@cloudflare/ai-chatand into a new sharedagents/chat/reactentry.@cloudflare/ai-chat/reactand a new@cloudflare/think/reactbecome thin wrappers over this shared core.Net effect: Think apps stop depending on
@cloudflare/ai-chatjust to get the React hook, and the long-standingsetMessagesdivergence betweenAIChatAgentand Think is now explicit and safe by default.Motivation
Using
useAgentChatfrom@cloudflare/ai-chatagainst a Think server was awkward and behaviorally divergent. The sharpest edge wassetMessages:AIChatAgentpersists a client-pushed flat transcript.Thinkis server-authoritative and silently ignores it.So apps pulled in an unrelated package and still got surprising behavior. This PR hoists the shared implementation and gives each product a wrapper with the right defaults.
What changed
agents(minor)agents/chat/reactentry exportinguseAgentChat,WebSocketChatTransport, and shared chat wire types (MessageType,OutgoingMessage,IncomingMessage).syncMessagesToServeroption (defaulttrue) so server-authoritative hosts can keepsetMessageslocal-only.@ai-sdk/reactadded as an optional peer dependency (only thechat/reactsubpath needs it).@cloudflare/ai-chat(patch)react.tsx,types.ts, andws-chat-transport.tsare now thin re-export shims overagents/chat/react. Public API and behavior are unchanged for existing consumers.@cloudflare/think(minor)@cloudflare/think/reactwrapper that hardcodessyncMessagesToServer: false(and omits it from its option type), sosetMessagesstays a local view update. Dev-only warn-once covers plain-JS callers that pass the option anyway.CF_AGENT_CHAT_MESSAGESframe, pointing to@cloudflare/think/reactandclearHistory(). It is behavior-triggered, so it only fires when an app actually hits the divergence (e.g. the ai-chat hook against a Think server) — no noise otherwise.Docs / examples / starters
@cloudflare/think/reactand drop the unnecessary@cloudflare/ai-chatdependency.AIChatAgent-backed examples are intentionally left on@cloudflare/ai-chat/react.setMessagesis display-only on Think (it won't survive a refresh; useclearHistory()to persist a clear).Design notes
agents/chat/reactimports nothing from@cloudflare/ai-chat; the dependency arrow points one way (ai-chat->agents).WebSocketChatTransportis exported only from thereactsubpath (not the server-safeagents/chatindex) to keep it out of server bundles.OmitofsyncMessagesToServerin the Think wrapper closes the footgun for TS users; the server-side warning backstops everyone else.Test plan
pnpm run typecheck— all 113 projects passsetMessageswithsyncMessagesToServer: falseupdates local state but sends noCF_AGENT_CHAT_MESSAGESframe@cloudflare/think/react—setMessagesis local-only by default;clearHistory()sendsCF_AGENT_CHAT_CLEARpnpm run check+ affected testsexamples/assistant) and anAIChatAgentexample (e.g.examples/ai-chat)Changesets
agents: minor@cloudflare/think: minor@cloudflare/ai-chat: patchMade with Cursor