Skip to content

feat(ai): add type-safe tool call events to chat() stream#452

Open
AlemTuzlak wants to merge 8 commits intomainfrom
worktree-serialized-exploring-wall
Open

feat(ai): add type-safe tool call events to chat() stream#452
AlemTuzlak wants to merge 8 commits intomainfrom
worktree-serialized-exploring-wall

Conversation

@AlemTuzlak
Copy link
Copy Markdown
Contributor

@AlemTuzlak AlemTuzlak commented Apr 15, 2026

Summary

  • Made ToolCallStartEvent and ToolCallEndEvent generic with backward-compatible defaults (string / unknown)
  • Added TypedStreamChunk<TTools> exported type that replaces untyped tool call events with typed versions based on the tools array
  • Threaded TTools generic through TextActivityOptions, TextActivityResult, chat(), and createChatOptions() so the stream return type carries tool type information
  • Added IsAny guard in ToolInputsOf to prevent any leaking through InferSchemaType for tools without inputSchema
  • Added 29 type-level tests covering: typed names/inputs, server/client/definition tool variants, empty/default/readonly arrays, backward compatibility, and chat() inference without explicit type args
  • Updated streaming docs with "Type-Safe Tool Call Events" section, StreamChunk.md reference, and cross-links from tools docs
  • Added showcase snippet in ts-react-chat example

Test plan

  • All 660 unit tests pass (pnpm --filter @tanstack/ai test:lib)
  • Build succeeds (pnpm --filter @tanstack/ai build)
  • Example app builds (pnpm --filter ts-react-chat build)
  • 29 type-level tests verify: literal name narrowing, input type inference, fallback to string/unknown, empty arrays, as const, server/client tool variants, chat() inference, backward compat (assignability, StreamChunk unchanged)
  • toBeUnknown() assertions used instead of toEqualTypeOf<unknown>() to catch any leaks

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Type-safe tool-call events in chat streaming: when using typed tools, streaming chunks now include type information, with toolName narrowed to tool name literals and input typed to corresponding tool schemas.
  • Bug Fixes

    • Fixed callback propagation in the Svelte integration to ensure all callbacks stay synchronized with option updates.
  • Documentation

    • Added comprehensive guides on type-safe streaming with tools, including examples and type references.

When tools with Zod schemas are passed to chat(), the stream chunks now
carry type information on TOOL_CALL_START and TOOL_CALL_END events:
- toolName narrows to the union of tool name literals
- input on TOOL_CALL_END is typed as the union of tool input types

Made ToolCallStartEvent and ToolCallEndEvent generic with backward-
compatible defaults. Added TypedStreamChunk<TTools> type that threads
through TextActivityOptions, TextActivityResult, chat(), and
createChatOptions(). Includes IsAny guard in ToolInputsOf to prevent
`any` leaking through InferSchemaType for tools without inputSchema.

Fully backward compatible — StreamChunk and AGUIEvent are unchanged,
unparameterized event types use string/unknown defaults.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The changes introduce type-safe streaming for tool calls in the chat function. When chat() receives tools with Zod schemas via toolDefinition(), the streaming output now provides typed TOOL_CALL_START and TOOL_CALL_END events where toolName is narrowed to tool name literals and input is narrowed to corresponding tool input types, forming discriminated unions.

Changes

Cohort / File(s) Summary
Documentation & Type Reference
docs/chat/streaming.md, docs/reference/type-aliases/StreamChunk.md, docs/tools/tools.md, docs/tools/server-tools.md
Added documentation and type reference explaining type-safe tool-call streaming, TypedStreamChunk helper type, discriminated union behavior with multiple tools, and how Zod-based type inference is preserved in streaming output.
Core Type System
packages/typescript/ai/src/types.ts
Made ToolCallStartEvent and ToolCallEndEvent generic with tool name and input type parameters. Added TypedStreamChunk<TTools> export that conditionally creates discriminated union types for tool-call events when concrete tool types are provided; falls back to StreamChunk when tools lack type information.
Chat API Integration
packages/typescript/ai/src/activities/chat/index.ts
Added TTools generic parameter (defaulting to ReadonlyArray<Tool<any, any, any>>) to TextActivityOptions, createChatOptions, and chat function. Updated streaming return type from AsyncIterable<StreamChunk> to AsyncIterable<TypedStreamChunk<TTools>> to propagate tool type information through the API.
Type Safety Tests
packages/typescript/ai/tests/type-check.test.ts
Expanded type-check tests with tool definition fixtures (Zod-backed, schema-less, plain JSON Schema variants) and extensive compile-time assertions validating TypedStreamChunk tool-call typing: toolName literal narrowing, input union typing discriminated by tool name, and backward compatibility with unparameterized event types.
Changeset Metadata
.changeset/svelte-callback-propagation.md
Added patch-level changeset documenting a Svelte callback propagation fix ensuring all five createChat callbacks are routed through consistent call-time indirection.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 With whiskers twitching at the types so fine,
Tool calls now speak in literals divine,
Each input narrowed, each name so clear,
Type-safe streaming brings a cheer!
From Zod to streams, the magic's spun,
A rabbit's joy—the typing's done! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding type-safe tool call events to the chat() stream, which is the primary objective of this PR.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering all major changes, test results, and implementation details that align with the repository's template expectations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-serialized-exploring-wall

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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 15, 2026

🚀 Changeset Version Preview

11 package(s) bumped directly, 22 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-fal 0.6.17 → 1.0.0 Changeset
@tanstack/ai-gemini 0.9.1 → 1.0.0 Changeset
@tanstack/ai-grok 0.6.8 → 1.0.0 Changeset
@tanstack/ai-openai 0.8.1 → 1.0.0 Changeset
@tanstack/ai-openrouter 0.8.1 → 1.0.0 Changeset
@tanstack/ai-react 0.7.15 → 1.0.0 Changeset
@tanstack/ai-solid 0.6.19 → 1.0.0 Changeset
@tanstack/ai-svelte 0.6.19 → 1.0.0 Changeset
@tanstack/ai-vue 0.6.19 → 1.0.0 Changeset
@tanstack/ai-anthropic 0.8.1 → 1.0.0 Dependent
@tanstack/ai-code-mode 0.1.7 → 1.0.0 Dependent
@tanstack/ai-code-mode-skills 0.1.7 → 1.0.0 Dependent
@tanstack/ai-elevenlabs 0.1.7 → 1.0.0 Dependent
@tanstack/ai-event-client 0.2.7 → 1.0.0 Dependent
@tanstack/ai-groq 0.1.7 → 1.0.0 Dependent
@tanstack/ai-isolate-node 0.1.7 → 1.0.0 Dependent
@tanstack/ai-isolate-quickjs 0.1.7 → 1.0.0 Dependent
@tanstack/ai-ollama 0.6.9 → 1.0.0 Dependent
@tanstack/ai-preact 0.6.19 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.6.1 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.6.1 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai 0.13.0 → 0.14.0 Changeset
@tanstack/ai-client 0.7.14 → 0.8.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-code-mode-models-eval 0.0.10 → 0.0.11 Dependent
@tanstack/ai-devtools-core 0.3.24 → 0.3.25 Dependent
@tanstack/ai-isolate-cloudflare 0.1.7 → 0.1.8 Dependent
@tanstack/ai-vue-ui 0.1.30 → 0.1.31 Dependent
@tanstack/preact-ai-devtools 0.1.28 → 0.1.29 Dependent
@tanstack/react-ai-devtools 0.2.28 → 0.2.29 Dependent
@tanstack/solid-ai-devtools 0.2.28 → 0.2.29 Dependent
ts-svelte-chat 0.1.36 → 0.1.37 Dependent
ts-vue-chat 0.1.36 → 0.1.37 Dependent
vanilla-chat 0.0.34 → 0.0.35 Dependent

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Apr 15, 2026

View your CI Pipeline Execution ↗ for commit f2aaac0

Command Status Duration Result
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 1m 44s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-24 10:51:27 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 15, 2026

Open in StackBlitz

@tanstack/ai

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

@tanstack/ai-anthropic

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

@tanstack/ai-client

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

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@452

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/@tanstack/ai-code-mode-skills@452

@tanstack/ai-devtools-core

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

@tanstack/ai-elevenlabs

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

@tanstack/ai-event-client

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

@tanstack/ai-fal

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

@tanstack/ai-gemini

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

@tanstack/ai-grok

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

@tanstack/ai-groq

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

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@452

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@452

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@452

@tanstack/ai-ollama

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

@tanstack/ai-openai

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

@tanstack/ai-openrouter

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

@tanstack/ai-preact

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

@tanstack/ai-react

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

@tanstack/ai-react-ui

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

@tanstack/ai-solid

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

@tanstack/ai-solid-ui

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

@tanstack/ai-svelte

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

@tanstack/ai-vue

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

@tanstack/ai-vue-ui

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

@tanstack/preact-ai-devtools

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

@tanstack/react-ai-devtools

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

@tanstack/solid-ai-devtools

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

commit: f2aaac0

Copy link
Copy Markdown
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.

Actionable comments posted: 3

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

Inline comments:
In `@docs/chat/streaming.md`:
- Around line 115-117: Remove the blank line between the two blockquotes so the
"**Note:**" and "**Tip:**" paragraphs are adjacent (or merge them into one
blockquote); specifically edit the section containing the "**Note:** When
multiple tools..." and the "**Tip:** The typed stream chunk type..." strings so
there is no empty line between those blockquote lines to satisfy markdownlint
MD028.

In `@docs/reference/type-aliases/StreamChunk.md`:
- Line 23: The "Defined in" source anchor for the TypedStreamChunk entry is
stale; update the anchor in docs/reference/type-aliases/StreamChunk.md to point
to the current declaration location packages/typescript/ai/src/types.ts at line
1048 (or to the exact URL/permalink generated by TypeDoc for TypedStreamChunk),
ensuring the link text and URL reflect the new file and line; update the
markdown so it uses the correct Markdown link format and matches the repo's
TypeDoc-generated anchor for TypedStreamChunk.

In `@packages/typescript/ai/tests/type-check.test.ts`:
- Around line 6-17: The named imports in this test file are not alphabetized
causing ESLint sort-imports errors; reorder the members inside each import's
braces alphabetically (e.g., change "describe, it, expectTypeOf" to "describe,
expectTypeOf, it" for the vitest import, and similarly alphabetize "chat,
createChatOptions, toolDefinition" and the type import list "JSONSchema,
StreamChunk, Tool, ToolCallArgsEvent, ToolCallEndEvent, ToolCallStartEvent,
TypedStreamChunk") so each import's named members are in ascending alphabetical
order.
🪄 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

Run ID: 28ddc119-4e8d-45ba-8567-1aae00525797

📥 Commits

Reviewing files that changed from the base of the PR and between db5c3d3 and a2c2f9c.

📒 Files selected for processing (7)
  • docs/chat/streaming.md
  • docs/reference/type-aliases/StreamChunk.md
  • docs/tools/tools.md
  • examples/ts-react-chat/src/routes/api.tanchat.ts
  • packages/typescript/ai/src/activities/chat/index.ts
  • packages/typescript/ai/src/types.ts
  • packages/typescript/ai/tests/type-check.test.ts

Comment thread docs/chat/streaming.md Outdated
Comment thread docs/reference/type-aliases/StreamChunk.md Outdated
Comment on lines 6 to +17
import { describe, it, expectTypeOf } from 'vitest'
import { createChatOptions } from '../src'
import { z } from 'zod'
import { chat, createChatOptions, toolDefinition } from '../src'
import type {
JSONSchema,
StreamChunk,
Tool,
ToolCallArgsEvent,
ToolCallStartEvent,
ToolCallEndEvent,
TypedStreamChunk,
} from '../src'
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.

⚠️ Potential issue | 🟡 Minor

Fix the import member ordering before merge.

ESLint is already flagging this block (sort-imports on Line 6 and Line 15), so this file will stay red until the named imports are alphabetized.

🧰 Tools
🪛 ESLint

[error] 6-6: Member 'expectTypeOf' of the import declaration should be sorted alphabetically.

(sort-imports)


[error] 15-15: Member 'ToolCallEndEvent' of the import declaration should be sorted alphabetically.

(sort-imports)

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

In `@packages/typescript/ai/tests/type-check.test.ts` around lines 6 - 17, The
named imports in this test file are not alphabetized causing ESLint sort-imports
errors; reorder the members inside each import's braces alphabetically (e.g.,
change "describe, it, expectTypeOf" to "describe, expectTypeOf, it" for the
vitest import, and similarly alphabetize "chat, createChatOptions,
toolDefinition" and the type import list "JSONSchema, StreamChunk, Tool,
ToolCallArgsEvent, ToolCallEndEvent, ToolCallStartEvent, TypedStreamChunk") so
each import's named members are in ascending alphabetical order.

AlemTuzlak and others added 2 commits April 16, 2026 12:58
…put narrowing

Replace flat toolName/input unions with distributive conditional types
so checking toolName === 'x' narrows input to that specific tool's type.
Copy link
Copy Markdown
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.

♻️ Duplicate comments (2)
docs/reference/type-aliases/StreamChunk.md (1)

23-23: ⚠️ Potential issue | 🟡 Minor

Stale Defined in link for TypedStreamChunk.

The link points to types.ts:1033, but TypedStreamChunk is defined at line 1065 in the current code. This will create a broken anchor.

📝 Suggested fix
-Defined in: [types.ts:1033](https://github.com/TanStack/ai/blob/main/packages/typescript/ai/src/types.ts#L1033)
+Defined in: [types.ts:1065](https://github.com/TanStack/ai/blob/main/packages/typescript/ai/src/types.ts#L1065)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/reference/type-aliases/StreamChunk.md` at line 23, The documentation
entry for TypedStreamChunk has a stale "Defined in" anchor; update the link
target in docs/reference/type-aliases/StreamChunk.md so it points to the actual
definition of TypedStreamChunk in types.ts (current location around line 1065)
or replace the fragile line-number anchor with a file-level link to types.ts and
a search-friendly fragment for TypedStreamChunk; ensure the reference text
"Defined in" continues to reference types.ts and that the anchor resolves to the
TypedStreamChunk definition.
packages/typescript/ai/tests/type-check.test.ts (1)

6-17: ⚠️ Potential issue | 🟡 Minor

Import members need alphabetical sorting.

ESLint's sort-imports rule requires alphabetically ordered named imports within each statement.

🔧 Suggested fix
-import { describe, it, expectTypeOf } from 'vitest'
+import { describe, expectTypeOf, it } from 'vitest'
 import { z } from 'zod'
 import { chat, createChatOptions, toolDefinition } from '../src'
 import type {
   JSONSchema,
   StreamChunk,
   Tool,
   ToolCallArgsEvent,
-  ToolCallStartEvent,
   ToolCallEndEvent,
+  ToolCallStartEvent,
   TypedStreamChunk,
 } from '../src'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai/tests/type-check.test.ts` around lines 6 - 17, The
named imports in this file are not alphabetized; reorder the identifiers
alphabetically within each import statement—for example change "describe, it,
expectTypeOf" to "describe, expectTypeOf, it", "chat, createChatOptions,
toolDefinition" to "chat, createChatOptions, toolDefinition" (ensure correct
alphabetical order), and the type import list "JSONSchema, StreamChunk, Tool,
ToolCallArgsEvent, ToolCallStartEvent, ToolCallEndEvent, TypedStreamChunk"
should be reordered alphabetically (e.g., "JSONSchema, StreamChunk, Tool,
ToolCallArgsEvent, ToolCallEndEvent, ToolCallStartEvent, TypedStreamChunk") so
each import's named members follow ESLint's sort-imports rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@docs/reference/type-aliases/StreamChunk.md`:
- Line 23: The documentation entry for TypedStreamChunk has a stale "Defined in"
anchor; update the link target in docs/reference/type-aliases/StreamChunk.md so
it points to the actual definition of TypedStreamChunk in types.ts (current
location around line 1065) or replace the fragile line-number anchor with a
file-level link to types.ts and a search-friendly fragment for TypedStreamChunk;
ensure the reference text "Defined in" continues to reference types.ts and that
the anchor resolves to the TypedStreamChunk definition.

In `@packages/typescript/ai/tests/type-check.test.ts`:
- Around line 6-17: The named imports in this file are not alphabetized; reorder
the identifiers alphabetically within each import statement—for example change
"describe, it, expectTypeOf" to "describe, expectTypeOf, it", "chat,
createChatOptions, toolDefinition" to "chat, createChatOptions, toolDefinition"
(ensure correct alphabetical order), and the type import list "JSONSchema,
StreamChunk, Tool, ToolCallArgsEvent, ToolCallStartEvent, ToolCallEndEvent,
TypedStreamChunk" should be reordered alphabetically (e.g., "JSONSchema,
StreamChunk, Tool, ToolCallArgsEvent, ToolCallEndEvent, ToolCallStartEvent,
TypedStreamChunk") so each import's named members follow ESLint's sort-imports
rule.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 230ab7f9-f926-49b5-a973-0080ff8b4502

📥 Commits

Reviewing files that changed from the base of the PR and between a2c2f9c and 5e3ebd7.

📒 Files selected for processing (5)
  • docs/chat/streaming.md
  • docs/reference/type-aliases/StreamChunk.md
  • examples/ts-react-chat/src/routes/api.tanchat.ts
  • packages/typescript/ai/src/types.ts
  • packages/typescript/ai/tests/type-check.test.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/chat/streaming.md

- Show practical property access after discriminated narrowing
- Add description field to searchTool example for consistency
- Add cross-link from server-tools to streaming type safety
- Fix stale line number references in StreamChunk.md
- Remove T & Tool<any,any,any> intersection in DistributedToolCallEnd
  that caused tsc to resolve input as unknown for all tools
- Relax SafeToolInput constraint to structural match (no generic bound)
- Fix "Zod schema inference" → "Standard Schema inference" in JSDoc/docs
- Fix off-by-one line number in StreamChunk.md reference
- Fix misleading test comment about searchClientTool
- Add | undefined to input type annotation in streaming docs
- Broaden server-tools.md tip to cover all typed tool variants
- Add tests for mixed Zod+JSON Schema and chat() with server/client tools
…xploring-wall

# Conflicts:
#	docs/reference/type-aliases/StreamChunk.md
#	packages/typescript/ai/src/activities/chat/index.ts
#	packages/typescript/ai/src/types.ts
Main's upstream bump to `@ag-ui/core` switched the AG-UI event interfaces
to `z.infer<...>` of a `"passthrough"` `z.ZodObject`, which introduces an
`[k: string]: unknown` index signature. `ToolCallStartEvent<TToolName>` was
declared as `Omit<AGUIToolCallStartEvent, 'toolCallName'> & { toolCallName: TToolName; ... }`
— and `Omit` on an index-signature'd type collapses the re-declared field
in ways that destroy discriminated-union narrowing on `StreamChunk`.

Consequences before this change:
  - `Extract<StreamChunk, { type: 'TOOL_CALL_START' }>['toolName']` resolved
    to `never`, so every type-check test around `TypedStreamChunk` failed.
  - `switch (chunk.type) { case 'TOOL_CALL_START': ... }` in `chat/index.ts`
    and `handleToolCallStartEvent`'s `Extract<..., {type:'TOOL_CALL_START'}>`
    parameter couldn't narrow, cascading 60 tsc errors through
    `activities/chat/**`, `middlewares/content-guard.ts`, and tests.

Redesign (types.ts):
  - `ToolCallStartEvent<TToolName>` now `extends AGUIToolCallStartEvent`
    with **no `Omit`**. The AG-UI `toolCallName: string` is inherited
    verbatim on the base interface; narrowing to a literal happens purely
    through intersection in the per-tool variants.
  - `ToolCallEndEvent<TToolName, TInput>` extends cleanly the same way.
    `toolName` is kept required (matches pre-merge TanStack surface and
    every adapter emits it).
  - `HasTypedTools<TTools>` now partitions out `ProviderTool` before
    checking `string extends ... ['name']`. Provider tools carry opaque
    provider metadata with a generic `string` name — without filtering,
    a user passing `[webSearchTool, myTypedTool]` would silently fall
    through to the untyped branch.
  - `DistributedToolCallStart` / `DistributedToolCallEnd` now distribute
    over `NonProviderTools<TTools>` and match any tool-like shape via
    `T extends { name: infer TName extends string }` — picking up
    `Tool`, `ServerTool`, and `ClientTool` uniformly.
  - The `ProviderTool` partition uses a structural brand match
    (`{ readonly '~provider': string; readonly '~toolKind': string }`)
    to avoid a circular import between `types.ts` and `./tools/provider-tool.ts`.

Test fix (tests/tool-calls-null-input.test.ts):
  - Two fixture calls to `manager.completeToolCall(...)` now include
    `toolCallName` / `toolName`. The runtime type requires them; the
    pre-merge test relied on the optional-ness accidentally introduced
    by the Omit-based surface.

Minor bucket from the review:
  - docs/chat/streaming.md: replace the hallucinated `gpt-5.2` model id
    with `gpt-4o` in all four occurrences (lines 27, 49, 109, 134).
  - examples/ts-react-chat/src/routes/api.tanchat.ts: remove the dead
    `typedStreamShowcase` function (`void typedStreamShowcase`).
  - packages/typescript/ai-svelte/src/create-chat.svelte.ts: wrap
    `onResponse`, `onChunk`, and `onCustomEvent` the same way
    `onFinish`/`onError` already were, so callers can mutate the
    `options` object and propagate new callbacks (matches the
    React/Preact/Vue/Solid sibling wrappers). Comment explains why.
  - Add `.changeset/svelte-callback-propagation.md` (patch bump).

Verification:
  - `pnpm --filter @tanstack/ai test:types` emits zero errors.
  - `pnpm --filter @tanstack/ai test:lib`         735/735 passing.
  - `pnpm --filter @tanstack/ai-openai test:lib`  131/131 passing.
  - `pnpm --filter @tanstack/ai-anthropic test:lib` 62/62 passing.
  - `pnpm --filter @tanstack/ai-svelte test:lib`   53/53 passing.
  - `pnpm --filter ... test:types` green across the same four packages.
@AlemTuzlak AlemTuzlak force-pushed the worktree-serialized-exploring-wall branch from c798ae0 to f2aaac0 Compare April 24, 2026 10:43
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