Skip to content

fix: emit server tool results when mixed with client tools in same turn#323

Merged
AlemTuzlak merged 2 commits intomainfrom
client-server-tool-fix
Feb 27, 2026
Merged

fix: emit server tool results when mixed with client tools in same turn#323
AlemTuzlak merged 2 commits intomainfrom
client-server-tool-fix

Conversation

@jherr
Copy link
Contributor

@jherr jherr commented Feb 27, 2026

Summary

  • Bug fix: When the LLM requests both a server tool and a client tool in the same turn, the server tool's result was silently dropped. processToolCalls and checkForPendingToolCalls both had an early return to wait for client tool execution, which skipped emitToolResults entirely — causing the session to stall indefinitely waiting for a result that would never arrive.
  • Fix: Emit any already-completed server tool results before the early return in both methods.
  • Smoke test fix: Update harness and test fixtures to use chunk.value instead of chunk.data for CUSTOM events, following the rename introduced in refactor: change CustomEvent property from 'data' to 'value' for AG-UI #307 that was missed in the smoke tests — this was causing the APR (approval flow) smoke test to always report 0 approvals.

Test plan

  • Run existing unit tests: cd packages/typescript/ai && pnpm test:lib
  • Run APR smoke test: tsx src/cli.ts run --adapters ollama --tests APR
  • Run full smoke test suite: tsx src/cli.ts run --adapters ollama
  • Verify mixed server+client tool scenario no longer stalls (new test added in chat.test.ts)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed chat session stalls when server and client tools are requested simultaneously in the same turn.
  • Tests

    • Added regression tests to ensure proper handling of mixed server and client tool calls.
  • Chores

    • Updated default AI model configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 27, 2026

📝 Walkthrough

Walkthrough

This PR fixes a chat stall that occurs when both server-side and client-side tools are requested in the same turn. It emits server tool results before waiting for client tool approval, preventing data loss. The changeset also aligns test fixtures and harness code with a property rename from data to value for CUSTOM events introduced in a related PR.

Changes

Cohort / File(s) Summary
Core Tool Handling Fix
packages/typescript/ai/src/activities/chat/index.ts, .changeset/fix-mixed-server-client-tool-stall.md
Adds emission of tool results before the early return that waits for client tool completion in both checkForPendingToolCalls and processToolCalls to prevent server tool results from being dropped when client tools are pending.
Regression Tests
packages/typescript/ai/tests/chat.test.ts
Adds comprehensive regression tests for mixed server and client tool scenarios, verifying that server tool results are emitted with TOOL_CALL_END before client tool approval is awaited.
Event Property Updates
packages/typescript/smoke-tests/adapters/src/harness.ts, packages/typescript/smoke-tests/adapters/src/tests/tools/approval.test.ts, packages/typescript/smoke-tests/adapters/src/tests/tools/client-tool.test.ts, packages/typescript/smoke-tests/adapters/src/tests/tools/sequences.test.ts
Aligns CUSTOM event handling to use chunk.value instead of chunk.data across test harness and assertions, consistent with the rename in a related PR.
Supporting Updates
packages/typescript/smoke-tests/adapters/src/adapters/index.ts
Updates Anthropic model constant from claude-3-5-haiku-20241022 to claude-haiku-4-5.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • AlemTuzlak
  • harry-whorlow

Poem

🐰 Server tools complete, yet patience held its breath,
Client tools awaited, while results faced quiet death.
Now emit before we pause—the stall is finally gone,
One hop, two hops, and the session carries on! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: fixing server tool result emission when both server and client tools are requested in the same turn.
Description check ✅ Passed The description comprehensively covers the bug, the fix, smoke test updates, and a clear test plan. It aligns well with the template structure despite minor formatting differences.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch client-server-tool-fix

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 Feb 27, 2026

View your CI Pipeline Execution ↗ for commit 5701235

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

☁️ Nx Cloud last updated this comment at 2026-02-27 18:58:48 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 27, 2026

Open in StackBlitz

@tanstack/ai

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

@tanstack/ai-anthropic

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

@tanstack/ai-client

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

@tanstack/ai-devtools-core

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

@tanstack/ai-fal

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

@tanstack/ai-gemini

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

@tanstack/ai-grok

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

@tanstack/ai-ollama

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

@tanstack/ai-openai

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

@tanstack/ai-openrouter

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

@tanstack/ai-preact

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

@tanstack/ai-react

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

@tanstack/ai-react-ui

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

@tanstack/ai-solid

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

@tanstack/ai-solid-ui

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

@tanstack/ai-svelte

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

@tanstack/ai-vue

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

@tanstack/ai-vue-ui

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

@tanstack/preact-ai-devtools

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

@tanstack/react-ai-devtools

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

@tanstack/solid-ai-devtools

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

commit: 9d720ec

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 (1)
packages/typescript/ai/tests/chat.test.ts (1)

520-537: Add explicit ordering assertions to lock in “server result before wait signal” behavior.

Right now these tests verify both events exist, but not their relative order.

Proposed test hardening
@@
       const toolEndWithResult = chunks.filter(
         (c) =>
           c.type === 'TOOL_CALL_END' &&
           (c as any).toolName === 'searchTools' &&
           'result' in c &&
           (c as any).result,
       )
       expect(toolEndWithResult).toHaveLength(1)
+      const toolEndIndex = chunks.findIndex(
+        (c) => c.type === 'TOOL_CALL_END' && (c as any).toolName === 'searchTools',
+      )
+      const inputAvailableIndex = chunks.findIndex(
+        (c) => c.type === 'CUSTOM' && (c as any).name === 'tool-input-available',
+      )
+      expect(toolEndIndex).toBeGreaterThanOrEqual(0)
+      expect(inputAvailableIndex).toBeGreaterThanOrEqual(0)
+      expect(toolEndIndex).toBeLessThan(inputAvailableIndex)
@@
       const toolEndWithResult = chunks.filter(
         (c) =>
           c.type === 'TOOL_CALL_END' &&
           (c as any).toolName === 'getWeather' &&
           'result' in c &&
           (c as any).result,
       )
       expect(toolEndWithResult).toHaveLength(1)
+      const toolEndIndex = chunks.findIndex(
+        (c) => c.type === 'TOOL_CALL_END' && (c as any).toolName === 'getWeather',
+      )
+      const inputAvailableIndex = chunks.findIndex(
+        (c) => c.type === 'CUSTOM' && (c as any).name === 'tool-input-available',
+      )
+      expect(toolEndIndex).toBeGreaterThanOrEqual(0)
+      expect(inputAvailableIndex).toBeGreaterThanOrEqual(0)
+      expect(toolEndIndex).toBeLessThan(inputAvailableIndex)

Also applies to: 588-604

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai/tests/chat.test.ts` around lines 520 - 537, The test
currently checks presence of a server TOOL_CALL_END with result (filtered into
toolEndWithResult) and a CLIENT CUSTOM 'tool-input-available' (customChunks) but
not their ordering; update the assertions to assert that the TOOL_CALL_END event
for toolName 'searchTools' with a result appears before the CUSTOM event named
'tool-input-available' for toolName 'showNotification' (e.g., compare their
indices in the chunks array or use findIndex on chunks for the specific event
predicates), and apply the same ordering assertion for the analogous assertions
around the block referenced at lines 588-604 to lock in "server result before
wait signal" behavior.
🤖 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/tests/chat.test.ts`:
- Around line 520-537: The test currently checks presence of a server
TOOL_CALL_END with result (filtered into toolEndWithResult) and a CLIENT CUSTOM
'tool-input-available' (customChunks) but not their ordering; update the
assertions to assert that the TOOL_CALL_END event for toolName 'searchTools'
with a result appears before the CUSTOM event named 'tool-input-available' for
toolName 'showNotification' (e.g., compare their indices in the chunks array or
use findIndex on chunks for the specific event predicates), and apply the same
ordering assertion for the analogous assertions around the block referenced at
lines 588-604 to lock in "server result before wait signal" behavior.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 808c32d and 5701235.

📒 Files selected for processing (8)
  • .changeset/fix-mixed-server-client-tool-stall.md
  • packages/typescript/ai/src/activities/chat/index.ts
  • packages/typescript/ai/tests/chat.test.ts
  • packages/typescript/smoke-tests/adapters/src/adapters/index.ts
  • packages/typescript/smoke-tests/adapters/src/harness.ts
  • packages/typescript/smoke-tests/adapters/src/tests/tools/approval.test.ts
  • packages/typescript/smoke-tests/adapters/src/tests/tools/client-tool.test.ts
  • packages/typescript/smoke-tests/adapters/src/tests/tools/sequences.test.ts

@AlemTuzlak AlemTuzlak merged commit d8678e2 into main Feb 27, 2026
5 checks passed
@AlemTuzlak AlemTuzlak deleted the client-server-tool-fix branch February 27, 2026 19:22
This was referenced Feb 27, 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.

2 participants