Skip to content

fix(ai): emit TOOL_CALL_START/ARGS during continuation re-executions#372

Open
DiegoGBrisa wants to merge 5 commits intoTanStack:mainfrom
DiegoGBrisa:fix/continuation-chunk-emission
Open

fix(ai): emit TOOL_CALL_START/ARGS during continuation re-executions#372
DiegoGBrisa wants to merge 5 commits intoTanStack:mainfrom
DiegoGBrisa:fix/continuation-chunk-emission

Conversation

@DiegoGBrisa
Copy link
Contributor

@DiegoGBrisa DiegoGBrisa commented Mar 13, 2026

Summary

Continuation re-executions (pending tool calls resumed from message history) only emit TOOL_CALL_END, skipping TOOL_CALL_START and TOOL_CALL_ARGS. This causes clients to store tool calls with empty
arguments, leading to infinite re-execution loops.

  • buildToolResultChunks now accepts an optional argsMap parameter
  • When provided (from checkForPendingToolCalls), emits the full START → ARGS → END sequence per tool call using the arguments from the original ToolCall objects
  • Normal flow (processToolCalls) is unaffected — the adapter stream already emits START/ARGS

Test plan

  • Commit 1 adds failing tests reproducing the issue (tests-first format)
  • Commit 2 applies the fix, all tests pass
  • Single pending tool call emits full START → ARGS → END sequence
  • Batch of pending tool calls each emit full START → ARGS → END sequence
  • Chunk ordering verified: START < ARGS < END per tool call
  • Existing 30 tests unaffected

Summary by CodeRabbit

  • Bug Fixes

    • Pending tool calls now emit the full event sequence (start, arguments, and end) during continuations, ensuring complete tool call information is available downstream.
  • Tests

    • Added tests covering single and batched pending tool call scenarios (including mixed server/client tools) to validate correct event ordering and completeness.

  Continuation re-executions (pending tool calls from message history) only
  emit TOOL_CALL_END, skipping TOOL_CALL_START and TOOL_CALL_ARGS. This
  causes clients to store tool calls with empty arguments, leading to
  infinite re-execution loops.

  Two tests added:
  - Single pending tool call emits full START → ARGS → END sequence
  - Batch of pending tool calls each emit full START → ARGS → END sequence
…re-executions

  Pending tool calls resumed from message history only emit TOOL_CALL_END,
  causing clients to store empty arguments and triggering infinite
  re-execution loops. Emit the full START → ARGS → END sequence using the
  arguments from the original ToolCall objects.
@changeset-bot
Copy link

changeset-bot bot commented Mar 13, 2026

🦋 Changeset detected

Latest commit: ec66e30

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

This PR includes changesets to release 29 packages
Name Type
@tanstack/ai Patch
@tanstack/ai-anthropic Patch
@tanstack/ai-client Patch
@tanstack/ai-devtools-core Patch
@tanstack/ai-elevenlabs Patch
@tanstack/ai-event-client Patch
@tanstack/ai-fal Patch
@tanstack/ai-gemini Patch
@tanstack/ai-grok Patch
@tanstack/ai-groq Patch
@tanstack/ai-ollama Patch
@tanstack/ai-openai Patch
@tanstack/ai-openrouter Patch
@tanstack/ai-preact Patch
@tanstack/ai-react Patch
@tanstack/ai-solid Patch
@tanstack/ai-svelte Patch
@tanstack/ai-vue Patch
@tanstack/tests-adapters Patch
@tanstack/smoke-tests-e2e Patch
ts-svelte-chat Patch
ts-vue-chat Patch
@tanstack/ai-react-ui Patch
@tanstack/ai-solid-ui Patch
vanilla-chat Patch
@tanstack/preact-ai-devtools Patch
@tanstack/react-ai-devtools Patch
@tanstack/solid-ai-devtools Patch
@tanstack/ai-vue-ui 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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: de018ab7-b568-4b5a-ac72-a3fb8dc2c37d

📥 Commits

Reviewing files that changed from the base of the PR and between c2522b6 and ec66e30.

📒 Files selected for processing (1)
  • packages/typescript/ai/tests/chat.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/typescript/ai/tests/chat.test.ts

📝 Walkthrough

Walkthrough

Adds emission of full tool-call chunks during continuation re-executions: an argsMap captures pending tool call arguments, and buildToolResultChunks now emits TOOL_CALL_START, TOOL_CALL_ARGS, then TOOL_CALL_END for pending/continuation tool executions so clients can reconstruct calls.

Changes

Cohort / File(s) Summary
Changelog
.changeset/fix-continuation-chunk-emission.md
New changelog entry documenting the patch release fix to emit TOOL_CALL_START and TOOL_CALL_ARGS during continuation tool executions.
Core Implementation
packages/typescript/ai/src/activities/chat/index.ts
Adds an argsMap to collect pending tool call arguments and propagates it into buildToolResultChunks; updates chunk emission to optionally emit TOOL_CALL_START and TOOL_CALL_ARGS before TOOL_CALL_END for continuation/pending tool calls.
Tests
packages/typescript/ai/tests/chat.test.ts
Adds tests verifying ordering and content of TOOL_CALL_STARTTOOL_CALL_ARGSTOOL_CALL_END for single and batched pending tool calls, including mixed server/client tool scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibble at chunks where continuations sleep,
START then ARGS now wake what was deep,
END waits its turn, the sequence restored,
Tools sing their lines, no argument ignored,
Hooray — the stream hops clean and neat!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: emitting TOOL_CALL_START and TOOL_CALL_ARGS during continuation re-executions, which directly addresses the core fix implemented in the changeset.
Description check ✅ Passed The description comprehensively covers the problem, solution, and test plan. It follows the template structure with clear sections explaining the changes and includes test verification details, though the formal template checklist items are not explicitly marked.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Mar 13, 2026

View your CI Pipeline Execution ↗ for commit ec66e30

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

☁️ Nx Cloud last updated this comment at 2026-03-13 09:11:37 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 13, 2026

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@372

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@372

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@372

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@372

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@372

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@372

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@372

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@372

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@372

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@372

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@372

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@372

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@372

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@372

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@372

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@372

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@372

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@372

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@372

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@372

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@372

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@372

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@372

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@372

commit: 561b566

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/typescript/ai/src/activities/chat/index.ts (1)

1027-1034: Populate args on the synthetic TOOL_CALL_ARGS chunk.

At continuation time you already have the full serialized arguments, so emitting only delta makes these synthetic chunks a little less expressive than the adapter-emitted ones. Setting args too keeps raw stream consumers from needing a continuation-only code path.

💡 Suggested change
         const args = argsMap.get(result.toolCallId) ?? '{}'
         chunks.push({
           type: 'TOOL_CALL_ARGS',
           timestamp: Date.now(),
           model: finishEvent.model,
           toolCallId: result.toolCallId,
           delta: args,
+          args,
         })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai/src/activities/chat/index.ts` around lines 1027 -
1034, The synthetic TOOL_CALL_ARGS chunk currently only sets delta and omits the
full args; update the chunk created in the continuation path (the block using
argsMap.get(result.toolCallId) and pushing into chunks) to also set an args
property with the serialized arguments (the same value you pull from argsMap,
e.g., args or parsed equivalent) so consumers see the full arguments on the
synthetic chunk alongside delta, model, toolCallId, and timestamp.
packages/typescript/ai/tests/chat.test.ts (1)

722-801: Add one regression for the mixed pending/wait branch too.

These cases cover the path where pending server tools finish and the engine proceeds to the next model call. The new argsMap plumbing is also used when continuation pauses again for needsClientExecution or needsApproval, so I’d add one assertion there that TOOL_CALL_START and TOOL_CALL_ARGS are still emitted for the completed server tool.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/typescript/ai/src/activities/chat/index.ts`:
- Around line 1027-1034: The synthetic TOOL_CALL_ARGS chunk currently only sets
delta and omits the full args; update the chunk created in the continuation path
(the block using argsMap.get(result.toolCallId) and pushing into chunks) to also
set an args property with the serialized arguments (the same value you pull from
argsMap, e.g., args or parsed equivalent) so consumers see the full arguments on
the synthetic chunk alongside delta, model, toolCallId, and timestamp.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 09507dd7-efca-4f4a-8822-f6c0d6ef6b33

📥 Commits

Reviewing files that changed from the base of the PR and between 570c9f6 and c2522b6.

📒 Files selected for processing (3)
  • .changeset/fix-continuation-chunk-emission.md
  • packages/typescript/ai/src/activities/chat/index.ts
  • packages/typescript/ai/tests/chat.test.ts

@DiegoGBrisa
Copy link
Contributor Author

🧹 Nitpick comments (2)

packages/typescript/ai/src/activities/chat/index.ts (1)> 1027-1034: Populate args on the synthetic TOOL_CALL_ARGS chunk.

At continuation time you already have the full serialized arguments, so emitting only delta makes these synthetic chunks a little less expressive than the adapter-emitted ones. Setting args too keeps raw stream consumers from needing a continuation-only code path.

💡 Suggested change

         const args = argsMap.get(result.toolCallId) ?? '{}'
         chunks.push({
           type: 'TOOL_CALL_ARGS',
           timestamp: Date.now(),
           model: finishEvent.model,
           toolCallId: result.toolCallId,
           delta: args,
+          args,
         })

🤖 Prompt for AI Agents

Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai/src/activities/chat/index.ts` around lines 1027 -
1034, The synthetic TOOL_CALL_ARGS chunk currently only sets delta and omits the
full args; update the chunk created in the continuation path (the block using
argsMap.get(result.toolCallId) and pushing into chunks) to also set an args
property with the serialized arguments (the same value you pull from argsMap,
e.g., args or parsed equivalent) so consumers see the full arguments on the
synthetic chunk alongside delta, model, toolCallId, and timestamp.

packages/typescript/ai/tests/chat.test.ts (1)> 722-801: Add one regression for the mixed pending/wait branch too.

These cases cover the path where pending server tools finish and the engine proceeds to the next model call. The new argsMap plumbing is also used when continuation pauses again for needsClientExecution or needsApproval, so I’d add one assertion there that TOOL_CALL_START and TOOL_CALL_ARGS are still emitted for the completed server tool.

🤖 Prompt for all review comments with AI agents

Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/typescript/ai/src/activities/chat/index.ts`:
- Around line 1027-1034: The synthetic TOOL_CALL_ARGS chunk currently only sets
delta and omits the full args; update the chunk created in the continuation path
(the block using argsMap.get(result.toolCallId) and pushing into chunks) to also
set an args property with the serialized arguments (the same value you pull from
argsMap, e.g., args or parsed equivalent) so consumers see the full arguments on
the synthetic chunk alongside delta, model, toolCallId, and timestamp.

ℹ️ Review info

Intentionally omitting args — adapter-emitted TOOL_CALL_ARGS chunks only use delta, so the synthetic chunks match the same shape. Adding a non-standard field would require consumers to handle continuation chunks differently from regular ones.

@AlemTuzlak AlemTuzlak requested a review from jherr March 13, 2026 09:34
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