Skip to content

fix(ai): re-open force-completed tool calls when more args arrive - #1019

Open
Mohith26 wants to merge 2 commits into
TanStack:mainfrom
Mohith26:fix/interleaved-text-tool-input-corruption
Open

fix(ai): re-open force-completed tool calls when more args arrive#1019
Mohith26 wants to merge 2 commits into
TanStack:mainfrom
Mohith26:fix/interleaved-text-tool-input-corruption

Conversation

@Mohith26

@Mohith26 Mohith26 commented Jul 30, 2026

Copy link
Copy Markdown

Fixes #1017, reported by @andrewmcgivery, matching the diagnosis in the issue exactly.

handleTextMessageContentEvent calls completeAllToolCallsForMessage() on every text delta, which finalizes in-flight tool calls with a lenient partial-JSON parse into input. handleToolCallEndEvent then early-skips calls already in input-complete, so when a TEXT_MESSAGE_CONTENT delta interleaves between two TOOL_CALL_ARGS deltas, the truncated parse becomes permanent even though arguments holds the full correct JSON.

The fix is 8 lines in handleToolCallArgsEvent: a further args delta on an input-complete call re-opens it to input-streaming, letting the authoritative TOOL_CALL_END (or the RUN_FINISHED safety net) re-finalize input from the complete arguments string. Self-correcting across interleaving orders; the existing inferred-completion heuristic is preserved.

Patch changeset included. Five regression tests (END-finalized ordering, END-input-canonical, RUN_FINISHED safety net, multiple parallel tool calls, the internal re-open transition) were written fail-first and all fail without the fix, reproducing the issue's exact truncated output. Package suite: 1291 passed, zero new failures; oxlint and tsc clean.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed tool-call inputs being truncated when text content appears between streamed argument updates.
    • Ensured completed tool calls use the full, authoritative arguments and input.
    • Improved recovery when tool-call completion events are missing.
    • Corrected handling for multiple tool calls processed in parallel.
  • Tests

    • Added regression coverage for interleaved text and tool-call streaming scenarios.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@tombeckenham, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 18 minutes

Limit details: You’ve used all 8 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c0b84615-66d7-4620-b764-ef9d8a05c387

📥 Commits

Reviewing files that changed from the base of the PR and between c092231 and 2713e79.

📒 Files selected for processing (3)
  • .changeset/interleaved-text-tool-input.md
  • packages/ai/src/activities/chat/stream/processor.ts
  • packages/ai/tests/stream-processor.test.ts
📝 Walkthrough

Walkthrough

The stream processor reopens tool calls marked complete when later argument deltas arrive. Authoritative or safety-net completion can then rebuild input from the complete arguments. Regression tests cover interleaved text, end input, missing end events, and parallel calls.

Changes

Tool-call input repair

Layer / File(s) Summary
Reopen force-completed tool calls
packages/ai/src/activities/chat/stream/processor.ts, .changeset/interleaved-text-tool-input.md
Additional tool-call argument deltas transition input-complete calls back to input-streaming. The changeset documents the patch release and correction.
Validate interleaved argument finalization
packages/ai/tests/stream-processor.test.ts
Tests verify completion after interleaved text, canonical TOOL_CALL_END.input, RUN_FINISHED fallback completion, and independent repair of parallel tool calls.

Estimated code review effort: 2 (Simple) | ~15 minutes

Possibly related PRs

  • TanStack/ai#1020: Both PRs modify StreamProcessor and regression tests for interleaved text and tool-argument completion.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary fix for reopening force-completed tool calls.
Description check ✅ Passed The description explains the bug, fix, tests, and release impact, although it does not reproduce the template headings or checklist format.
Linked Issues check ✅ Passed The implementation and regression tests address issue #1017 by restoring complete tool-call input after interleaved text deltas.
Out of Scope Changes check ✅ Passed The changeset, processor fix, and focused regression tests are directly related to issue #1017 and the stated pull request objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/ai/tests/stream-processor.test.ts (1)

829-855: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the canonical input intentionally differ from streamed JSON.

The supplied TOOL_CALL_END.input equals JSON.parse(FULL_ARGS), so this test cannot prove the canonical override path ran. Use a distinct canonical value and assert both part.input and parsedArguments use it while arguments remains FULL_ARGS.

Proposed regression strengthening
           ev.toolEnd('tc-1', 'offerTemplates', {
-            input: { templateIds: ['mock-gsk-efimosfermin'] },
+            input: { templateIds: ['canonical-template-id'] },
           }),
@@
+        expect(part?.arguments).toBe(FULL_ARGS)
-        expect(part?.input).toEqual({ templateIds: ['mock-gsk-efimosfermin'] })
+        expect(part?.input).toEqual({
+          templateIds: ['canonical-template-id'],
+        })
@@
-        ).toEqual({ templateIds: ['mock-gsk-efimosfermin'] })
+        ).toEqual({ templateIds: ['canonical-template-id'] })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai/tests/stream-processor.test.ts` around lines 829 - 855,
Strengthen the test around StreamProcessor by making TOOL_CALL_END.input differ
from the streamed FULL_ARGS value. Update the canonical input fixture in the
toolEnd call, then assert part.input and the tc-1 parsedArguments both equal
that distinct canonical value while part.arguments remains FULL_ARGS.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ai/tests/stream-processor.test.ts`:
- Around line 776-915: Move the regression test block “text delta interleaved
between TOOL_CALL_ARGS deltas (`#1017`)” from the package-level tests into a
*.test.ts file alongside the source containing StreamProcessor. Preserve all
five test cases and their existing assertions unchanged.

---

Nitpick comments:
In `@packages/ai/tests/stream-processor.test.ts`:
- Around line 829-855: Strengthen the test around StreamProcessor by making
TOOL_CALL_END.input differ from the streamed FULL_ARGS value. Update the
canonical input fixture in the toolEnd call, then assert part.input and the tc-1
parsedArguments both equal that distinct canonical value while part.arguments
remains FULL_ARGS.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 49951d96-af20-4710-9e97-9068fdfd5912

📥 Commits

Reviewing files that changed from the base of the PR and between 05280a5 and 6a4b458.

📒 Files selected for processing (3)
  • .changeset/interleaved-text-tool-input.md
  • packages/ai/src/activities/chat/stream/processor.ts
  • packages/ai/tests/stream-processor.test.ts

Comment thread packages/ai/tests/stream-processor.test.ts
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@tombeckenham

Copy link
Copy Markdown
Contributor

Maintainer sweep: rebased onto main (force-with-lease). CI approval handled if it was waiting.

@nx-cloud

nx-cloud Bot commented Aug 10, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 2713e79

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 7m 18s View ↗
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 1m 46s View ↗

☁️ Nx Cloud last updated this comment at 2026-08-20 11:03:18 UTC

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the PR, @Mohith26! 🙌 @AlemTuzlak will take a look.

Automated pre-review checks

  • ⚠️ CI failing — worth a look before review
  • ✅ No merge conflicts
  • ✅ Changeset present
  • ⚠️ No E2E test changes detected — behavior changes need coverage under testing/e2e/ (see CONTRIBUTING)

Automated triage — a human review follows.

@github-actions github-actions Bot added the waiting-on: author Waiting for the author to respond or update label Aug 13, 2026
@tombeckenham
tombeckenham force-pushed the fix/interleaved-text-tool-input-corruption branch from d4e99f9 to 5440c52 Compare August 20, 2026 10:20
@tombeckenham

Copy link
Copy Markdown
Contributor

Maintainer sweep: rebased onto main (force-with-lease). CI approval handled if it was waiting.

A TEXT_MESSAGE_CONTENT event between two TOOL_CALL_ARGS events force-completes
the in-flight tool call, populating part.input from a lenient partial-JSON
parse of the arguments received so far. The subsequent authoritative
TOOL_CALL_END was then skipped because the call was already input-complete,
so the truncated input was never corrected even though the arguments string
held the complete JSON.

Re-open such an inferred-complete call back to input-streaming when a further
TOOL_CALL_ARGS delta arrives, so TOOL_CALL_END (or the RUN_FINISHED safety
net) re-finalizes input from the complete arguments string.

Fixes TanStack#1017
Regression tests for TanStack#1017: interleaving orders with TOOL_CALL_END,
TOOL_CALL_END.input as canonical, the RUN_FINISHED safety net with no
TOOL_CALL_END, multiple parallel tool calls, and the internal
input-complete -> input-streaming re-open transition.
@tombeckenham
tombeckenham force-pushed the fix/interleaved-text-tool-input-corruption branch from 5440c52 to 2713e79 Compare August 20, 2026 10:41
@tombeckenham

Copy link
Copy Markdown
Contributor

Maintainer sweep: rebased onto main (force-with-lease). CI approval handled if it was waiting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-on: author Waiting for the author to respond or update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Interleaved TEXT_MESSAGE_CONTENT permanently corrupts tool-call input with a partial-JSON parse

3 participants