Skip to content

feat: hoist useAgentChat into agents/chat/react with product wrappers#1801

Merged
threepointone merged 2 commits into
mainfrom
shared-chat-react-core
Jun 23, 2026
Merged

feat: hoist useAgentChat into agents/chat/react with product wrappers#1801
threepointone merged 2 commits into
mainfrom
shared-chat-react-core

Conversation

@threepointone

@threepointone threepointone commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves the canonical useAgentChat implementation, the WebSocket chat transport, and the chat wire types out of @cloudflare/ai-chat and into a new shared agents/chat/react entry. @cloudflare/ai-chat/react and a new @cloudflare/think/react become thin wrappers over this shared core.

Net effect: Think apps stop depending on @cloudflare/ai-chat just to get the React hook, and the long-standing setMessages divergence between AIChatAgent and Think is now explicit and safe by default.

Motivation

Using useAgentChat from @cloudflare/ai-chat against a Think server was awkward and behaviorally divergent. The sharpest edge was setMessages:

  • AIChatAgent persists a client-pushed flat transcript.
  • Think is 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)

  • New agents/chat/react entry exporting useAgentChat, WebSocketChatTransport, and shared chat wire types (MessageType, OutgoingMessage, IncomingMessage).
  • New syncMessagesToServer option (default true) so server-authoritative hosts can keep setMessages local-only.
  • @ai-sdk/react added as an optional peer dependency (only the chat/react subpath needs it).

@cloudflare/ai-chat (patch)

  • 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.

@cloudflare/think (minor)

  • New @cloudflare/think/react wrapper that hardcodes syncMessagesToServer: false (and omits it from its option type), so setMessages stays a local view update. Dev-only warn-once covers plain-JS callers that pass the option anyway.
  • Server: warn once (dev only) when Think receives a client-pushed CF_AGENT_CHAT_MESSAGES frame, pointing to @cloudflare/think/react and clearHistory(). 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

  • All Think-backed clients now import the hook from @cloudflare/think/react and drop the unnecessary @cloudflare/ai-chat dependency.
  • AIChatAgent-backed examples are intentionally left on @cloudflare/ai-chat/react.
  • Documented that setMessages is display-only on Think (it won't survive a refresh; use clearHistory() to persist a clear).

Design notes

  • No dependency cycle: agents/chat/react imports nothing from @cloudflare/ai-chat; the dependency arrow points one way (ai-chat -> agents).
  • WebSocketChatTransport is exported only from the react subpath (not the server-safe agents/chat index) to keep it out of server bundles.
  • The compile-time Omit of syncMessagesToServer in the Think wrapper closes the footgun for TS users; the server-side warning backstops everyone else.

Test plan

  • pnpm run typecheck — all 113 projects pass
  • Added test: ai-chat core — setMessages with syncMessagesToServer: false updates local state but sends no CF_AGENT_CHAT_MESSAGES frame
  • Added test: @cloudflare/think/reactsetMessages is local-only by default; clearHistory() sends CF_AGENT_CHAT_CLEAR
  • CI: pnpm run check + affected tests
  • Manual smoke: a Think example chat (e.g. examples/assistant) and an AIChatAgent example (e.g. examples/ai-chat)

Changesets

  • agents: minor
  • @cloudflare/think: minor
  • @cloudflare/ai-chat: patch

Made with Cursor


Open in Devin Review

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-bot

changeset-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f1af2d0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@cloudflare/ai-chat Patch
agents Minor
@cloudflare/think Minor

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

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

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>
@pkg-pr-new

pkg-pr-new Bot commented Jun 23, 2026

Copy link
Copy Markdown

Open in StackBlitz

agents

npm i https://pkg.pr.new/agents@1801

@cloudflare/ai-chat

npm i https://pkg.pr.new/@cloudflare/ai-chat@1801

@cloudflare/codemode

npm i https://pkg.pr.new/@cloudflare/codemode@1801

create-think

npm i https://pkg.pr.new/create-think@1801

hono-agents

npm i https://pkg.pr.new/hono-agents@1801

@cloudflare/shell

npm i https://pkg.pr.new/@cloudflare/shell@1801

@cloudflare/think

npm i https://pkg.pr.new/@cloudflare/think@1801

@cloudflare/voice

npm i https://pkg.pr.new/@cloudflare/voice@1801

@cloudflare/worker-bundler

npm i https://pkg.pr.new/@cloudflare/worker-bundler@1801

commit: f1af2d0

@threepointone threepointone merged commit c58b401 into main Jun 23, 2026
7 checks passed
@threepointone threepointone deleted the shared-chat-react-core branch June 23, 2026 16:52
@github-actions github-actions Bot mentioned this pull request Jun 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant