Skip to content

fix(agents): stop useAgentChat's tool-result cleanup dispatching per chunk (#2217) - #2403

Merged
threepointone merged 2 commits into
fix/2268-ready-after-statefrom
fix/2217-tool-result-cleanup
Sep 28, 2026
Merged

threepointone merged 2 commits into
fix/2268-ready-after-statefrom
fix/2217-tool-result-cleanup

Conversation

@threepointone

@threepointone threepointone commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2217. This takes over the fix from the reporter's fork, with co-author credit to @Konan69.

Problem

useAgentChat's cleanup effect for stale clientToolResults called setState on every message change, and relied on the updater returning prev to make that a no-op. During a stream, React re-renders anyway and counts every update made in a passive effect toward its nested update limit. Long answers threw "Maximum update depth exceeded". I reproduced this with the reporter's test.

Fix

A ref mirrors clientToolResults. The effect checks the ref for stale keys before it dispatches anything. The updater still recomputes from prev and returns prev when nothing changed, so a race cannot drop a fresh result.

Tests

  • tool-result-prune-updates.test.tsx, from the fork, fails with "Maximum update depth exceeded" without the fix.
  • resume-replay-burst.test.tsx now waits for the throttled render instead of sleeping 20 ms. The spurious re-render used to flush it early.

@changeset-bot

changeset-bot Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5fba64a

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

This PR includes changesets to release 2 packages
Name Type
agents Patch
@cloudflare/agent-think Patch

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

@threepointone
threepointone added this pull request to stack #2407 September 28, 2026 14:47
@threepointone
threepointone marked this pull request as ready for review September 28, 2026 14:50
devin-ai-integration[bot]

This comment was marked as resolved.

@threepointone
threepointone force-pushed the fix/2217-tool-result-cleanup branch from a999c3f to 8114a60 Compare September 28, 2026 15:46
@agent-think

agent-think Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

✅ agents import sizes: no significant changes (4e4c13c0 → 5fba64a1, workflow run)

@pkg-pr-new

pkg-pr-new Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

agents

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

@cloudflare/ai-chat

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

@cloudflare/codemode

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

hono-agents

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

@cloudflare/shell

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

@cloudflare/think

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

@cloudflare/voice

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

@cloudflare/worker-bundler

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

commit: 5fba64a

threepointone and others added 2 commits September 28, 2026 17:50
…chunk (#2217)

The stale clientToolResults cleanup effect called setState on every message
change and relied on the updater returning prev. During a stream React
re-renders anyway and counts each passive-effect update toward its nested
update limit, so long answers threw "Maximum update depth exceeded". Decide
staleness from a ref mirror before dispatching.

The resume-replay-burst ordering check now waits for the throttled render
instead of sleeping 20ms; the spurious re-render used to flush it early.

Co-authored-by: Konan <kixeyems0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@threepointone
threepointone force-pushed the fix/2217-tool-result-cleanup branch from 8114a60 to 5fba64a Compare September 28, 2026 16:54
@threepointone
threepointone merged commit 6b5b4a0 into main Sep 28, 2026
17 of 30 checks passed
@threepointone
threepointone deleted the fix/2217-tool-result-cleanup branch September 28, 2026 18:04
@github-actions github-actions Bot mentioned this pull request Oct 2, 2026
tombeckenham added a commit to tombeckenham/agents-tom that referenced this pull request Oct 7, 2026
Brings react-agui.tsx level with the legacy hook in agents/chat/react,
which received these through the upstream merge:

- cloudflare#2344 the server snapshot heals a diverged observed assistant
- cloudflare#2348 an observer skips replayed continuation frames it already applied
- cloudflare#2361 onToolCall fires only after the stream ends
- cloudflare#2378 onTurnEnd fires once per ended request
- cloudflare#2383 turns settle across replay, resume and observers (held and
  recovering requests, the resume:false idle probe)
- cloudflare#2496 streaming protection is released when the socket closes
- cloudflare#2394 follow the socket when the agent name changes
- cloudflare#2403 the tool-result cleanup no longer dispatches per chunk
- cloudflare#2039 keep an optimistic send the reconnect transcript omits

The last three had no failing test: their upstream tests are ported into
react-tests (the address-change one against a fake of useAgent's pending
socket, since this package has no test worker) and failed before the fix.

restoreBufferedSends, MAX_REMEMBERED_ENDED_TURNS and
isSocketAddressPending are exported from agents/chat/react so the two
hooks share them.
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.

useAgentChat: the clientToolResults cleanup effect dispatches setState once per streamed chunk, accumulating nestedUpdateCount to the React #185 throw

1 participant