feat: multi-capability assistants β defineAssistant (server) + useAssistant (client)#930
feat: multi-capability assistants β defineAssistant (server) + useAssistant (client)#930AlemTuzlak wants to merge 4 commits into
Conversation
π Changeset Version Preview19 package(s) bumped directly, 26 bumped as dependents. π₯ Major bumps
π¨ Minor bumps
π© Patch bumps
|
|
View your CI Pipeline Execution β for commit 71f4748
βοΈ Nx Cloud last updated this comment at |
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
π WalkthroughWalkthroughThis PR introduces multi-capability assistants via ChangesMulti-capability assistants
Estimated code review effort: 4 (Complex) | ~75 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant useAssistant
participant AssistantClient
participant AssistantHandler
participant CapabilityCallback
Client->>useAssistant: call system.chat.sendMessage / system.image.generate
useAssistant->>AssistantClient: dispatch via chat or one-shot sub-client
AssistantClient->>AssistantHandler: POST request with forwardedProps.capability
AssistantHandler->>AssistantHandler: validate capability against declared keys
AssistantHandler->>CapabilityCallback: invoke matching callback
CapabilityCallback-->>AssistantHandler: stream or promise result
AssistantHandler-->>AssistantClient: SSE response
AssistantClient-->>useAssistant: update chat/one-shot reactive state
useAssistant-->>Client: render updated system surface
Suggested reviewers: π₯ Pre-merge checks | β 4 | β 1β Failed checks (1 warning)
β Passed checks (4 passed)
β¨ Finishing Touchesπ§ͺ Generate unit tests (beta)
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. Comment |
@tanstack/ai
@tanstack/ai-acp
@tanstack/ai-angular
@tanstack/ai-anthropic
@tanstack/ai-bedrock
@tanstack/ai-claude-code
@tanstack/ai-client
@tanstack/ai-code-mode
@tanstack/ai-code-mode-skills
@tanstack/ai-codex
@tanstack/ai-devtools-core
@tanstack/ai-elevenlabs
@tanstack/ai-event-client
@tanstack/ai-fal
@tanstack/ai-gemini
@tanstack/ai-grok
@tanstack/ai-grok-build
@tanstack/ai-groq
@tanstack/ai-isolate-cloudflare
@tanstack/ai-isolate-node
@tanstack/ai-isolate-quickjs
@tanstack/ai-mcp
@tanstack/ai-mistral
@tanstack/ai-ollama
@tanstack/ai-openai
@tanstack/ai-opencode
@tanstack/ai-openrouter
@tanstack/ai-preact
@tanstack/ai-react
@tanstack/ai-react-ui
@tanstack/ai-sandbox
@tanstack/ai-sandbox-cloudflare
@tanstack/ai-sandbox-daytona
@tanstack/ai-sandbox-docker
@tanstack/ai-sandbox-local-process
@tanstack/ai-sandbox-sprites
@tanstack/ai-sandbox-vercel
@tanstack/ai-solid
@tanstack/ai-solid-ui
@tanstack/ai-svelte
@tanstack/ai-utils
@tanstack/ai-vue
@tanstack/ai-vue-ui
@tanstack/openai-base
@tanstack/preact-ai-devtools
@tanstack/react-ai-devtools
@tanstack/solid-ai-devtools
commit: |
There was a problem hiding this comment.
Actionable comments posted: 8
π€ 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-react/src/use-assistant.ts`:
- Line 70: Exclude the framework-internal callbacks option from both hooksβ
public options types. Update the options parameter Omit in Reactβs useAssistant
and Vueβs useAssistant to omit both assistant and callbacks, preserving all
other AssistantClientOptions fields.
In `@packages/ai-solid/src/use-assistant.ts`:
- Around line 302-303: The AssistantSystem casts unnecessarily pass through
unknown, triggering no-unnecessary-type-assertion. In
packages/ai-solid/src/use-assistant.ts lines 302-303, update the cast in the
assembled system return to AssistantSystem<TDef, TChatTools> directly; in
packages/ai-svelte/src/create-assistant.svelte.ts lines 271-274, cast directly
to AssistantSystem<TDef, TChatTools> & { dispose: () => void } while preserving
the existing behavior.
- Around line 102-160: Remove the second argument from the createMemo call that
constructs the AssistantClient in the client memo, leaving the existing callback
and AssistantClient configuration unchanged.
In `@packages/ai-vue/src/use-assistant.ts`:
- Around line 62-68: Align the callbacks type contract in useAssistant with the
corresponding React hook and shared callback fix. Update the
AssistantClientOptions usage and any related generic or omitted option types so
callbacks are correctly typed while keeping assistant excluded from
caller-provided options.
In `@packages/ai/skills/ai-core/assistant/SKILL.md`:
- Around line 88-98: Update all three snippets in SKILL.md that import from
../lib/assistant to import the exported blogAssistant symbol, including the
route and client examples; ensure client locals do not conflict with that import
while continuing to pass blogAssistant to useAssistant.
In `@packages/ai/src/activities/assistant/index.ts`:
- Around line 29-40: Validate the parsed body in the assistant request handler
before accessing properties through bodyRecord. Reject null and all non-object
JSON values with the existing 400 invalid-body response, while preserving the
forwardedProps/data fallback for valid object bodies.
In `@packages/ai/src/activities/assistant/types.ts`:
- Around line 83-89: Update the AssistantConfig.chat type and its surrounding
definitions in types.ts to match the dispatcherβs actual async-iterable-only
contract. Remove the unsupported Promise<string> and Promise<object> return
variants while preserving ChatStream and StructuredOutputStream<any>, so
non-streaming chat calls are rejected at compile time.
In `@testing/e2e/src/routes/assistant.tsx`:
- Around line 9-12: Reorder the imports in the assistant route so the type-only
Provider import from '`@/lib/types`' appears before the internal value imports
ChatUI, createTextAdapter, and createImageAdapter, satisfying the configured
ESLint import/order rule.
πͺ 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: 7c3307a4-0ffb-47bd-95a3-4b720f7fcfa8
π Files selected for processing (36)
.changeset/assistant-multi-capability.mddocs/assistant/index.mddocs/config.jsonpackages/ai-client/src/assistant-client.tspackages/ai-client/src/assistant-types.tspackages/ai-client/src/index.tspackages/ai-client/tests/assistant-client.test.tspackages/ai-react/src/index.tspackages/ai-react/src/use-assistant.tspackages/ai-react/tests/use-assistant.test.tsxpackages/ai-solid/src/index.tspackages/ai-solid/src/use-assistant.tspackages/ai-solid/tests/use-assistant.test.tsxpackages/ai-svelte/src/create-assistant.svelte.tspackages/ai-svelte/src/index.tspackages/ai-svelte/tests/create-assistant.svelte.test.tspackages/ai-vue/src/index.tspackages/ai-vue/src/use-assistant.tspackages/ai-vue/tests/use-assistant.test.tspackages/ai/skills/ai-core/assistant/SKILL.mdpackages/ai/skills/ai-core/chat-experience/SKILL.mdpackages/ai/skills/ai-core/custom-backend-integration/SKILL.mdpackages/ai/skills/ai-core/media-generation/SKILL.mdpackages/ai/src/activities/assistant/index.tspackages/ai/src/activities/assistant/types.tspackages/ai/src/activities/index.tspackages/ai/src/index.tspackages/ai/tests/activities/assistant/index.test.tstesting/e2e/fixtures/assistant/basic.jsontesting/e2e/src/lib/feature-support.tstesting/e2e/src/lib/features.tstesting/e2e/src/lib/types.tstesting/e2e/src/routeTree.gen.tstesting/e2e/src/routes/api.assistant.tstesting/e2e/src/routes/assistant.tsxtesting/e2e/tests/assistant.spec.ts
| let body: unknown | ||
| try { | ||
| body = await request.json() | ||
| } catch { | ||
| return new Response('Invalid JSON body', { status: 400 }) | ||
| } | ||
|
|
||
| const bodyRecord = body as Record<string, unknown> | ||
| const forwardedProps: Record<string, unknown> = | ||
| (bodyRecord.forwardedProps as Record<string, unknown> | undefined) ?? | ||
| (bodyRecord.data as Record<string, unknown> | undefined) ?? | ||
| {} |
There was a problem hiding this comment.
π©Ί Stability & Availability | π‘ Minor | β‘ Quick win
Guard against non-object JSON bodies (e.g. literal null).
request.json() can resolve to null (or any primitive) as valid JSON. The try/catch only covers .json() itself; bodyRecord.forwardedProps at line 38 will throw on a null body since that access is unguarded, turning a bad request into an unhandled rejection instead of a 400.
π‘οΈ Validate body shape before destructuring
const bodyRecord = body as Record<string, unknown>
+ if (typeof body !== 'object' || body === null) {
+ return new Response('Invalid JSON body', { status: 400 })
+ }
+ const bodyRecord = body as Record<string, unknown>π€ 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/src/activities/assistant/index.ts` around lines 29 - 40, Validate
the parsed body in the assistant request handler before accessing properties
through bodyRecord. Reject null and all non-object JSON values with the existing
400 invalid-body response, while preserving the forwardedProps/data fallback for
valid object bodies.
| import { ChatUI } from '@/components/ChatUI' | ||
| import { createTextAdapter } from '@/lib/providers' | ||
| import { createImageAdapter } from '@/lib/media-providers' | ||
| import type { Provider } from '@/lib/types' |
There was a problem hiding this comment.
π Maintainability & Code Quality | π‘ Minor | β‘ Quick win
Fix ESLint import/order error: move type import before internal value imports.
ESLint reports an error on line 12 β @/lib/types type import should occur before @/components/ChatUI. This is configured as an error and may fail the lint step in CI.
π§ Proposed fix
import { fetchServerSentEvents, useAssistant } from '`@tanstack/ai-react`'
+import type { Provider } from '`@/lib/types`'
import { ChatUI } from '`@/components/ChatUI`'
import { createTextAdapter } from '`@/lib/providers`'
import { createImageAdapter } from '`@/lib/media-providers`'
-import type { Provider } from '`@/lib/types`'π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { ChatUI } from '@/components/ChatUI' | |
| import { createTextAdapter } from '@/lib/providers' | |
| import { createImageAdapter } from '@/lib/media-providers' | |
| import type { Provider } from '@/lib/types' | |
| import type { Provider } from '`@/lib/types`' | |
| import { ChatUI } from '`@/components/ChatUI`' | |
| import { createTextAdapter } from '`@/lib/providers`' | |
| import { createImageAdapter } from '`@/lib/media-providers`' |
π§° Tools
πͺ ESLint
[error] 12-12: @/lib/types type import should occur before import of @/components/ChatUI
(import/order)
π€ 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 `@testing/e2e/src/routes/assistant.tsx` around lines 9 - 12, Reorder the
imports in the assistant route so the type-only Provider import from
'`@/lib/types`' appears before the internal value imports ChatUI,
createTextAdapter, and createImageAdapter, satisfying the configured ESLint
import/order rule.
Source: Linters/SAST tools
b50eeae to
4b65ae5
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
β»οΈ Duplicate comments (3)
packages/ai/src/activities/assistant/index.ts (1)
36-40: π©Ί Stability & Availability | π‘ Minor | β‘ Quick winStill missing the null-body guard flagged previously.
request.json()can resolve tonull(or another primitive) as valid JSON; the try/catch only covers parsing itself.bodyRecord.forwardedPropshere dereferences a property on a non-object, throwing before either the chat or one-shot branch is reached β for every capability, not just chat.π‘οΈ Validate body shape before destructuring
- const bodyRecord = body as Record<string, unknown> + if (typeof body !== 'object' || body === null) { + return new Response('Invalid JSON body', { status: 400 }) + } + const bodyRecord = body as Record<string, unknown>π€ 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/src/activities/assistant/index.ts` around lines 36 - 40, Add a body-shape guard before the cast and property access in the request handling flow: only treat non-null object bodies as records, and use an empty record for null or primitive JSON values. Ensure this validation applies before both the chat and one-shot capability branches so forwardedProps extraction never dereferences an invalid body.testing/e2e/src/routes/assistant.tsx (1)
7-8: π Maintainability & Code Quality | π‘ Minor | β‘ Quick winFix ESLint import/order error: move type import before internal value imports.
ESLint reports an error because the type import from
@/lib/typesappears after the value import from@/components/ChatUI. Reorder them to satisfy the rule and prevent CI lint failures.π§ Proposed fix
-import { ChatUI } from '`@/components/ChatUI`' -import type { Provider } from '`@/lib/types`' +import type { Provider } from '`@/lib/types`' +import { ChatUI } from '`@/components/ChatUI`'π€ 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 `@testing/e2e/src/routes/assistant.tsx` around lines 7 - 8, Reorder the imports in the assistant route so the type-only Provider import from `@/lib/types` appears before the ChatUI value import from `@/components/ChatUI`, satisfying the ESLint import/order rule without changing usage.Source: Linters/SAST tools
packages/ai-solid/src/use-assistant.ts (1)
320-321: π Maintainability & Code Quality | π‘ Minor | β‘ Quick winUnnecessary
unknowncast triggers ESLint error.In both files, the dynamically assembled
systemobject can be directly cast toAssistantSystem<TDef, TChatTools>without the intermediateunknownhop. Removing it resolves the@typescript-eslint/no-unnecessary-type-assertionerror while preserving the same type behavior.
packages/ai-solid/src/use-assistant.ts#L320-L321: change the cast toreturn system as AssistantSystem<TDef, TChatTools>packages/ai-vue/src/use-assistant.ts#L246-L247: change the cast toreturn system as AssistantSystem<TDef, TChatTools>π€ 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-solid/src/use-assistant.ts` around lines 320 - 321, Remove the unnecessary unknown intermediary from the system casts in packages/ai-solid/src/use-assistant.ts#L320-L321 and packages/ai-vue/src/use-assistant.ts#L246-L247, changing both returns to cast system directly as AssistantSystem<TDef, TChatTools>. Preserve the existing dynamically assembled system behavior and surrounding lint suppression.Source: Linters/SAST tools
π§Ή Nitpick comments (3)
packages/ai-solid/tests/use-assistant.test.tsx (2)
1-2: π Maintainability & Code Quality | π΅ Trivial | π€ Low valuePlace unit test files alongside the source they cover.
As per coding guidelines, unit tests should be placed in
*.test.ts(or.tsx) files alongside the source modules they cover, rather than in a separatetests/directory.
packages/ai-solid/tests/use-assistant.test.tsx#L1-L2: Move this file topackages/ai-solid/src/use-assistant.test.tsxand update relative imports (e.g.,useAssistantandtest-utils).packages/ai-vue/tests/use-assistant.test.ts#L1-L2: Move this file topackages/ai-vue/src/use-assistant.test.tsand update relative imports.π€ 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-solid/tests/use-assistant.test.tsx` around lines 1 - 2, Move packages/ai-solid/tests/use-assistant.test.tsx to packages/ai-solid/src/use-assistant.test.tsx and update its relative imports, including useAssistant and test-utils; move packages/ai-vue/tests/use-assistant.test.ts to packages/ai-vue/src/use-assistant.test.ts and update its relative imports accordingly.Source: Coding guidelines
5-7: π Maintainability & Code Quality | π΅ Trivial | π€ Low valueFix import order.
The static analysis tool indicates that the type import for
@tanstack/aishould occur after the import of./test-utils.β»οΈ Proposed fix
-import type { StreamChunk } from '`@tanstack/ai`' import { useAssistant } from '../src/use-assistant.js' import { createTextChunks } from './test-utils' +import type { StreamChunk } from '`@tanstack/ai`'π€ 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-solid/tests/use-assistant.test.tsx` around lines 5 - 7, Reorder the imports in the use-assistant test so the local ./test-utils import appears before the type-only `@tanstack/ai` import, while preserving the existing imported symbols.Source: Linters/SAST tools
packages/ai-vue/tests/use-assistant.test.ts (1)
23-29: π Maintainability & Code Quality | π΅ Trivial | π€ Low valueRemove unnecessary type assertion.
The static analysis tool points out that the
as unknown as Array<StreamChunk>assertion is unnecessary, as the returned array already satisfies the function's return type.β»οΈ Proposed fix
{ type: 'RUN_FINISHED', runId: 'run-1', finishReason: 'stop', timestamp: Date.now(), }, - ] as unknown as Array<StreamChunk> + ] }π€ 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-vue/tests/use-assistant.test.ts` around lines 23 - 29, Remove the unnecessary βas unknown as Array<StreamChunk>β assertion from the stream chunk fixture in the use-assistant test, leaving the array directly typed through its existing context and preserving the current entries unchanged.Source: Linters/SAST tools
π€ 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-vue/src/use-assistant.ts`:
- Line 140: Update the Vue composable initialization flow in useAssistant to
synchronize the initial state from the newly instantiated AssistantClient,
matching the Solid implementation. Populate chatMessages and the other exposed
state refs from the clientβs initial values immediately after client creation,
including configured initialMessages.
---
Duplicate comments:
In `@packages/ai-solid/src/use-assistant.ts`:
- Around line 320-321: Remove the unnecessary unknown intermediary from the
system casts in packages/ai-solid/src/use-assistant.ts#L320-L321 and
packages/ai-vue/src/use-assistant.ts#L246-L247, changing both returns to cast
system directly as AssistantSystem<TDef, TChatTools>. Preserve the existing
dynamically assembled system behavior and surrounding lint suppression.
In `@packages/ai/src/activities/assistant/index.ts`:
- Around line 36-40: Add a body-shape guard before the cast and property access
in the request handling flow: only treat non-null object bodies as records, and
use an empty record for null or primitive JSON values. Ensure this validation
applies before both the chat and one-shot capability branches so forwardedProps
extraction never dereferences an invalid body.
In `@testing/e2e/src/routes/assistant.tsx`:
- Around line 7-8: Reorder the imports in the assistant route so the type-only
Provider import from `@/lib/types` appears before the ChatUI value import from
`@/components/ChatUI`, satisfying the ESLint import/order rule without changing
usage.
---
Nitpick comments:
In `@packages/ai-solid/tests/use-assistant.test.tsx`:
- Around line 1-2: Move packages/ai-solid/tests/use-assistant.test.tsx to
packages/ai-solid/src/use-assistant.test.tsx and update its relative imports,
including useAssistant and test-utils; move
packages/ai-vue/tests/use-assistant.test.ts to
packages/ai-vue/src/use-assistant.test.ts and update its relative imports
accordingly.
- Around line 5-7: Reorder the imports in the use-assistant test so the local
./test-utils import appears before the type-only `@tanstack/ai` import, while
preserving the existing imported symbols.
In `@packages/ai-vue/tests/use-assistant.test.ts`:
- Around line 23-29: Remove the unnecessary βas unknown as Array<StreamChunk>β
assertion from the stream chunk fixture in the use-assistant test, leaving the
array directly typed through its existing context and preserving the current
entries unchanged.
πͺ 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: ca6b9103-9cce-4c5c-8d5d-b3519b3fdff0
π Files selected for processing (52)
.changeset/assistant-multi-capability.mddocs/assistant/index.mddocs/config.jsonpackages/ai-client/package.jsonpackages/ai-client/src/assistant-client.tspackages/ai-client/src/assistant-structured.tspackages/ai-client/src/assistant-types.tspackages/ai-client/src/assistant.tspackages/ai-client/tests/assistant-client.test.tspackages/ai-client/tests/assistant-structured.test.tspackages/ai-client/vite.config.tspackages/ai-react/package.jsonpackages/ai-react/src/assistant.tspackages/ai-react/src/use-assistant.tspackages/ai-react/tests/use-assistant.test.tsxpackages/ai-react/vite.config.tspackages/ai-solid/package.jsonpackages/ai-solid/src/assistant.tspackages/ai-solid/src/use-assistant.tspackages/ai-solid/tests/use-assistant.test.tsxpackages/ai-solid/tsdown.config.tspackages/ai-svelte/package.jsonpackages/ai-svelte/src/assistant.tspackages/ai-svelte/src/create-assistant.svelte.tspackages/ai-svelte/tests/create-assistant.svelte.test.tspackages/ai-vue/package.jsonpackages/ai-vue/src/assistant.tspackages/ai-vue/src/use-assistant.tspackages/ai-vue/tests/use-assistant.test.tspackages/ai-vue/tsdown.config.tspackages/ai/package.jsonpackages/ai/skills/ai-core/assistant/SKILL.mdpackages/ai/skills/ai-core/chat-experience/SKILL.mdpackages/ai/skills/ai-core/custom-backend-integration/SKILL.mdpackages/ai/skills/ai-core/media-generation/SKILL.mdpackages/ai/src/activities/assistant/index.tspackages/ai/src/activities/assistant/types.tspackages/ai/src/activities/chat/index.tspackages/ai/src/assistant.tspackages/ai/src/index.tspackages/ai/src/types.tspackages/ai/tests/activities/assistant/index.test.tspackages/ai/tests/activities/chat/chat-result-meta.test.tspackages/ai/vite.config.tstesting/e2e/fixtures/assistant/basic.jsontesting/e2e/src/lib/feature-support.tstesting/e2e/src/lib/features.tstesting/e2e/src/lib/types.tstesting/e2e/src/routeTree.gen.tstesting/e2e/src/routes/api.assistant.tstesting/e2e/src/routes/assistant.tsxtesting/e2e/tests/assistant.spec.ts
π§ Files skipped from review as they are similar to previous changes (19)
- packages/ai/tests/activities/chat/chat-result-meta.test.ts
- docs/config.json
- testing/e2e/src/lib/types.ts
- testing/e2e/src/lib/features.ts
- packages/ai/src/types.ts
- testing/e2e/src/routes/api.assistant.ts
- packages/ai-svelte/tests/create-assistant.svelte.test.ts
- packages/ai/src/activities/assistant/types.ts
- .changeset/assistant-multi-capability.md
- packages/ai-react/tests/use-assistant.test.tsx
- packages/ai-client/src/assistant-structured.ts
- testing/e2e/src/lib/feature-support.ts
- packages/ai-react/src/use-assistant.ts
- packages/ai/src/activities/chat/index.ts
- packages/ai-client/src/assistant-client.ts
- packages/ai-client/tests/assistant-structured.test.ts
- testing/e2e/tests/assistant.spec.ts
- packages/ai-client/src/assistant-types.ts
- testing/e2e/src/routeTree.gen.ts
| }), | ||
| }, | ||
| }) | ||
|
|
There was a problem hiding this comment.
π― Functional Correctness | π Major | β‘ Quick win
Sync initial chat state.
Unlike the Solid implementation which manually synchronizes the initial chat state after client instantiation, the Vue composable omits this step. Consequently, chatMessages and other state refs will incorrectly remain empty/default initially, even if the AssistantClient was initialized with properties like initialMessages.
π Proposed fix
},
})
+ if (client.chat) {
+ chatMessages.value = client.chat.getMessages()
+ chatIsLoading.value = client.chat.getIsLoading()
+ chatError.value = client.chat.getError()
+ chatStatus.value = client.chat.getStatus()
+ chatIsSubscribed.value = client.chat.getIsSubscribed()
+ chatConnectionStatus.value = client.chat.getConnectionStatus()
+ chatSessionGenerating.value = client.chat.getSessionGenerating()
+ }
+
onMounted(() => {
client.chat?.mountDevtools()π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (client.chat) { | |
| chatMessages.value = client.chat.getMessages() | |
| chatIsLoading.value = client.chat.getIsLoading() | |
| chatError.value = client.chat.getError() | |
| chatStatus.value = client.chat.getStatus() | |
| chatIsSubscribed.value = client.chat.getIsSubscribed() | |
| chatConnectionStatus.value = client.chat.getConnectionStatus() | |
| chatSessionGenerating.value = client.chat.getSessionGenerating() | |
| } |
π€ 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-vue/src/use-assistant.ts` at line 140, Update the Vue composable
initialization flow in useAssistant to synchronize the initial state from the
newly instantiated AssistantClient, matching the Solid implementation. Populate
chatMessages and the other exposed state refs from the clientβs initial values
immediately after client creation, including configured initialMessages.
4b65ae5 to
0acb9a9
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
π§Ή Nitpick comments (4)
docs/config.json (1)
314-319: π Maintainability & Code Quality | π΅ Trivial | π€ Low valueRemove
updatedAtfor new documentation pages.As per coding guidelines, new pages should only set
addedAt. TheupdatedAtfield is reserved for substantive content changes to existing pages. Since this page is newly added in this PR, please removeupdatedAt.β»οΈ Proposed fix
{ "label": "Overview", "to": "assistant/overview", - "addedAt": "2026-07-13", - "updatedAt": "2026-07-14" + "addedAt": "2026-07-14" },π€ 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 `@docs/config.json` around lines 314 - 319, Remove the updatedAt property from the new βOverviewβ documentation page entry, keeping its label, destination, and addedAt metadata unchanged.Source: Coding guidelines
packages/ai-solid/src/use-assistant.ts (1)
251-256: π Performance & Scalability | π΅ Trivial | β‘ Quick winMemoize
computeStructuredPartsinstead of recomputing on every access.Unlike the Vue (
computed) and Svelte ($derived) implementations, these getters callcomputeStructuredParts(chatState().messages)directly and unmemoized β twice per read when bothpartialandfinalare accessed. For large message histories this recomputes the full derivation on every render/read.β»οΈ Proposed fix
+ const structuredParts = createMemo(() => + computeStructuredParts(chatState().messages), + ) + for (const capability of assistant.capabilities) { if (capability === 'chat') { system.chat = { ... get partial() { - return computeStructuredParts(chatState().messages).partial + return structuredParts().partial }, get final() { - return computeStructuredParts(chatState().messages).final + return structuredParts().final },π€ 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-solid/src/use-assistant.ts` around lines 251 - 256, Memoize the result of computeStructuredParts for the assistant state instead of invoking it directly in the partial and final getters. Reuse the memoized result from both getters so each messages state is derived once per update, matching the computed/derived behavior of the other implementations.packages/ai-solid/tests/use-assistant.test.tsx (1)
5-7: π Maintainability & Code Quality | π΅ Trivial | π€ Low valueReorder imports to satisfy linter.
Static analysis tools indicate that the type import for
@tanstack/aishould be placed after the relative imports to adhere to the project's import order conventions.β»οΈ Proposed fix
-import type { StreamChunk } from '`@tanstack/ai`' import { useAssistant } from '../src/use-assistant.js' import { createTextChunks } from './test-utils' +import type { StreamChunk } from '`@tanstack/ai`'π€ 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-solid/tests/use-assistant.test.tsx` around lines 5 - 7, Reorder the imports in the use-assistant test so the relative imports, including useAssistant and createTextChunks, appear before the type import from `@tanstack/ai`, preserving all existing imports and behavior.Source: Linters/SAST tools
packages/ai-vue/tests/use-assistant.test.ts (1)
23-29: π Maintainability & Code Quality | π΅ Trivial | π€ Low valueRemove unnecessary type assertion.
Static analysis tools flag that the double assertion
as unknown as Array<StreamChunk>is unnecessary because the returned array literal already correctly satisfies the function's explicitArray<StreamChunk>return type.β»οΈ Proposed fix
{ type: 'RUN_FINISHED', runId: 'run-1', finishReason: 'stop', timestamp: Date.now(), }, - ] as unknown as Array<StreamChunk> + ]π€ 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-vue/tests/use-assistant.test.ts` around lines 23 - 29, Remove the unnecessary `as unknown as Array<StreamChunk>` assertion from the stream chunk array in the test fixture, relying on the functionβs explicit `Array<StreamChunk>` return type and preserving the existing array contents.Source: Linters/SAST tools
π€ 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-solid/src/use-assistant.ts`:
- Around line 320-321: Remove the unnecessary unknown cast from all three
AssistantSystem assertions. In packages/ai-solid/src/use-assistant.ts lines
320-321, use a direct AssistantSystem<TDef, TChatTools> cast; in
packages/ai-svelte/src/create-assistant.svelte.ts lines 293-296, preserve the
dispose intersection while casting directly; and in
packages/ai-vue/src/use-assistant.ts lines 246-248, use the direct
AssistantSystem<TDef, TChatTools> cast. Update each eslint-disable comment to
target the applicable rule or remove it if no longer needed.
In `@packages/ai-svelte/src/create-assistant.svelte.ts`:
- Line 164: Update the post-construction synchronization in the assistant setup
around client.chat?.getMessages() so it initializes all seven chat state fields
from client.chat, including chatIsLoading, chatError, chatStatus,
chatIsSubscribed, chatConnectionStatus, and chatSessionGenerating, while
preserving the existing messages fallback.
In `@packages/ai-vue/src/use-assistant.ts`:
- Around line 246-248: Update the return in the Vue assistant composable to
remove the unnecessary unknown-based type assertion and its misplaced
eslint-disable comment. Return system directly as AssistantSystem<TDef,
TChatTools>, matching the shared fix used by the Solid and Svelte hooks.
---
Nitpick comments:
In `@docs/config.json`:
- Around line 314-319: Remove the updatedAt property from the new βOverviewβ
documentation page entry, keeping its label, destination, and addedAt metadata
unchanged.
In `@packages/ai-solid/src/use-assistant.ts`:
- Around line 251-256: Memoize the result of computeStructuredParts for the
assistant state instead of invoking it directly in the partial and final
getters. Reuse the memoized result from both getters so each messages state is
derived once per update, matching the computed/derived behavior of the other
implementations.
In `@packages/ai-solid/tests/use-assistant.test.tsx`:
- Around line 5-7: Reorder the imports in the use-assistant test so the relative
imports, including useAssistant and createTextChunks, appear before the type
import from `@tanstack/ai`, preserving all existing imports and behavior.
In `@packages/ai-vue/tests/use-assistant.test.ts`:
- Around line 23-29: Remove the unnecessary `as unknown as Array<StreamChunk>`
assertion from the stream chunk array in the test fixture, relying on the
functionβs explicit `Array<StreamChunk>` return type and preserving the existing
array contents.
πͺ 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: a8c2c137-6831-442b-90e4-edef2675d6d3
π Files selected for processing (54)
.changeset/assistant-multi-capability.mddocs/assistant/multiple-assistants.mddocs/assistant/overview.mddocs/assistant/scenarios.mddocs/config.jsonpackages/ai-client/package.jsonpackages/ai-client/src/assistant-client.tspackages/ai-client/src/assistant-structured.tspackages/ai-client/src/assistant-types.tspackages/ai-client/src/assistant.tspackages/ai-client/tests/assistant-client.test.tspackages/ai-client/tests/assistant-structured.test.tspackages/ai-client/vite.config.tspackages/ai-react/package.jsonpackages/ai-react/src/assistant.tspackages/ai-react/src/use-assistant.tspackages/ai-react/tests/use-assistant.test.tsxpackages/ai-react/vite.config.tspackages/ai-solid/package.jsonpackages/ai-solid/src/assistant.tspackages/ai-solid/src/use-assistant.tspackages/ai-solid/tests/use-assistant.test.tsxpackages/ai-solid/tsdown.config.tspackages/ai-svelte/package.jsonpackages/ai-svelte/src/assistant.tspackages/ai-svelte/src/create-assistant.svelte.tspackages/ai-svelte/tests/create-assistant.svelte.test.tspackages/ai-vue/package.jsonpackages/ai-vue/src/assistant.tspackages/ai-vue/src/use-assistant.tspackages/ai-vue/tests/use-assistant.test.tspackages/ai-vue/tsdown.config.tspackages/ai/package.jsonpackages/ai/skills/ai-core/assistant/SKILL.mdpackages/ai/skills/ai-core/chat-experience/SKILL.mdpackages/ai/skills/ai-core/custom-backend-integration/SKILL.mdpackages/ai/skills/ai-core/media-generation/SKILL.mdpackages/ai/src/activities/assistant/index.tspackages/ai/src/activities/assistant/types.tspackages/ai/src/activities/chat/index.tspackages/ai/src/assistant.tspackages/ai/src/index.tspackages/ai/src/types.tspackages/ai/tests/activities/assistant/index.test.tspackages/ai/tests/activities/chat/chat-result-meta.test.tspackages/ai/vite.config.tstesting/e2e/fixtures/assistant/basic.jsontesting/e2e/src/lib/feature-support.tstesting/e2e/src/lib/features.tstesting/e2e/src/lib/types.tstesting/e2e/src/routeTree.gen.tstesting/e2e/src/routes/api.assistant.tstesting/e2e/src/routes/assistant.tsxtesting/e2e/tests/assistant.spec.ts
π§ Files skipped from review as they are similar to previous changes (39)
- packages/ai-vue/src/assistant.ts
- packages/ai-svelte/src/assistant.ts
- packages/ai-solid/src/assistant.ts
- testing/e2e/fixtures/assistant/basic.json
- testing/e2e/tests/assistant.spec.ts
- packages/ai-solid/package.json
- testing/e2e/src/lib/types.ts
- packages/ai-client/package.json
- packages/ai-client/src/assistant-structured.ts
- packages/ai-solid/tsdown.config.ts
- packages/ai/src/index.ts
- packages/ai-vue/tsdown.config.ts
- packages/ai-react/vite.config.ts
- packages/ai-svelte/package.json
- packages/ai-react/src/assistant.ts
- packages/ai/tests/activities/chat/chat-result-meta.test.ts
- packages/ai-client/src/assistant.ts
- packages/ai-client/tests/assistant-structured.test.ts
- packages/ai/vite.config.ts
- packages/ai-client/tests/assistant-client.test.ts
- packages/ai-svelte/tests/create-assistant.svelte.test.ts
- packages/ai/src/types.ts
- testing/e2e/src/lib/feature-support.ts
- packages/ai-react/package.json
- packages/ai/src/assistant.ts
- testing/e2e/src/routes/api.assistant.ts
- testing/e2e/src/lib/features.ts
- packages/ai-client/vite.config.ts
- packages/ai/src/activities/assistant/types.ts
- packages/ai-vue/package.json
- packages/ai/package.json
- packages/ai-react/tests/use-assistant.test.tsx
- packages/ai/tests/activities/assistant/index.test.ts
- packages/ai-react/src/use-assistant.ts
- packages/ai-client/src/assistant-client.ts
- packages/ai-client/src/assistant-types.ts
- packages/ai/src/activities/assistant/index.ts
- packages/ai/src/activities/chat/index.ts
- testing/e2e/src/routeTree.gen.ts
| // eslint-disable-next-line no-restricted-syntax -- built dynamically per declared capability; TS can't structurally verify the assembled object against the mapped AssistantSystem type | ||
| return system as unknown as AssistantSystem<TDef, TChatTools> |
There was a problem hiding this comment.
π Maintainability & Code Quality | π‘ Minor | β‘ Quick win
Drop the unnecessary unknown hop in all three AssistantSystem casts β the eslint-disable-next-line targets the wrong rule. Each site casts the assembled system object through unknown before AssistantSystem<...>, which the object is already structurally assignable to; the disable comment silences no-restricted-syntax while ESLint actually flags no-unnecessary-type-assertion at each of these lines, so the error persists in CI.
packages/ai-solid/src/use-assistant.ts#L320-L321: changesystem as unknown as AssistantSystem<TDef, TChatTools>tosystem as AssistantSystem<TDef, TChatTools>.packages/ai-svelte/src/create-assistant.svelte.ts#L293-L296: changesystem as unknown as AssistantSystem<TDef, TChatTools> & { dispose: () => void }tosystem as AssistantSystem<TDef, TChatTools> & { dispose: () => void }.packages/ai-vue/src/use-assistant.ts#L246-L248: changesystem as unknown as AssistantSystem<TDef, TChatTools>tosystem as AssistantSystem<TDef, TChatTools>.
π§° Tools
πͺ ESLint
[error] 321-321: This assertion is unnecessary since the receiver accepts the original type of the expression.
(@typescript-eslint/no-unnecessary-type-assertion)
π Affects 3 files
packages/ai-solid/src/use-assistant.ts#L320-L321(this comment)packages/ai-svelte/src/create-assistant.svelte.ts#L293-L296packages/ai-vue/src/use-assistant.ts#L246-L248
π€ 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-solid/src/use-assistant.ts` around lines 320 - 321, Remove the
unnecessary unknown cast from all three AssistantSystem assertions. In
packages/ai-solid/src/use-assistant.ts lines 320-321, use a direct
AssistantSystem<TDef, TChatTools> cast; in
packages/ai-svelte/src/create-assistant.svelte.ts lines 293-296, preserve the
dispose intersection while casting directly; and in
packages/ai-vue/src/use-assistant.ts lines 246-248, use the direct
AssistantSystem<TDef, TChatTools> cast. Update each eslint-disable comment to
target the applicable rule or remove it if no longer needed.
Source: Linters/SAST tools
| }, | ||
| }) | ||
|
|
||
| chatMessages = client.chat?.getMessages() ?? [] |
There was a problem hiding this comment.
π― Functional Correctness | π Major | β‘ Quick win
Initial chat state sync is incomplete β only messages is pulled from the constructed client.
chatIsLoading, chatError, chatStatus, chatIsSubscribed, chatConnectionStatus, and chatSessionGenerating stay at their $state defaults instead of being read from the just-constructed client.chat. If the sub-client's actual initial state diverges from these defaults (e.g. resuming a thread), the returned system will report stale values until the next callback fires. The Solid implementation of this same hook syncs all seven fields after construction.
π Proposed fix
- chatMessages = client.chat?.getMessages() ?? []
+ if (client.chat) {
+ chatMessages = client.chat.getMessages()
+ chatIsLoading = client.chat.getIsLoading()
+ chatError = client.chat.getError()
+ chatStatus = client.chat.getStatus()
+ chatIsSubscribed = client.chat.getIsSubscribed()
+ chatConnectionStatus = client.chat.getConnectionStatus()
+ chatSessionGenerating = client.chat.getSessionGenerating()
+ }π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| chatMessages = client.chat?.getMessages() ?? [] | |
| if (client.chat) { | |
| chatMessages = client.chat.getMessages() | |
| chatIsLoading = client.chat.getIsLoading() | |
| chatError = client.chat.getError() | |
| chatStatus = client.chat.getStatus() | |
| chatIsSubscribed = client.chat.getIsSubscribed() | |
| chatConnectionStatus = client.chat.getConnectionStatus() | |
| chatSessionGenerating = client.chat.getSessionGenerating() | |
| } |
π€ 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-svelte/src/create-assistant.svelte.ts` at line 164, Update the
post-construction synchronization in the assistant setup around
client.chat?.getMessages() so it initializes all seven chat state fields from
client.chat, including chatIsLoading, chatError, chatStatus, chatIsSubscribed,
chatConnectionStatus, and chatSessionGenerating, while preserving the existing
messages fallback.
| // eslint-disable-next-line no-restricted-syntax -- composable return shape (nested refs per capability) diverges from the framework-agnostic AssistantSystem type (plain values); TS can't structurally relate the two | ||
| return system as unknown as AssistantSystem<TDef, TChatTools> | ||
| } |
There was a problem hiding this comment.
π Maintainability & Code Quality | π Major | β‘ Quick win
eslint-disable-next-line targets the wrong rule; type-assertion error remains.
Same mismatch as the Solid/Svelte hooks: the disable comment silences no-restricted-syntax, but ESLint flags no-unnecessary-type-assertion here (system is already structurally assignable to AssistantSystem<TDef, TChatTools> without the unknown hop). See the consolidated comment for the shared fix.
π§° Tools
πͺ ESLint
[error] 247-247: This assertion is unnecessary since the receiver accepts the original type of the expression.
(@typescript-eslint/no-unnecessary-type-assertion)
π€ 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-vue/src/use-assistant.ts` around lines 246 - 248, Update the
return in the Vue assistant composable to remove the unnecessary unknown-based
type assertion and its misplaced eslint-disable comment. Return system directly
as AssistantSystem<TDef, TChatTools>, matching the shared fix used by the Solid
and Svelte hooks.
Source: Linters/SAST tools
Declare an assistant's capabilities once on the server and consume them
from one typed client hook, over a single endpoint.
Server (`@tanstack/ai/assistant`)
- `defineAssistant(config)`: an inert registry of per-capability callbacks
(chat, image, audio, speech, video, transcription, summarize), each
invoking an existing activity. Exposes one `handler(request)` that parses
the AG-UI request, routes by a `capability` discriminator, and streams the
result back over the existing SSE wire. Nothing is constructed until a
request arrives.
- Every callback β chat and one-shots alike β receives the same validated
AG-UI envelope: `chat` gets `AssistantChatRequest`; one-shots get their
input plus `threadId`/`runId`/`parentRunId`/`state`/`aguiContext`/
`forwardedProps` and the raw `request`, all parsed through the same
request validator (`chatParamsFromRequestBody`). Untrusted input fields
(e.g. `req.prompt`) stay `unknown` so callbacks narrow before use.
Client core (`@tanstack/ai-client/assistant`)
- `AssistantClient` composes one `ChatClient` + one `GenerationClient` per
declared capability behind a single connection. `AssistantSystem<TDef>`
maps declared capabilities to fully-typed surfaces, inferred from the
definition β no call-site generics.
- The imperative methods resolve to their results, so capabilities chain
with a plain `await` instead of reading reactive state (which is stale in
an async closure under React): `assistant.<oneshot>.generate(input)`
resolves to the generation result (`TResult | null`), and
`assistant.chat.sendMessage(...)` resolves to the validated structured
`final` when the chat callback declared an `outputSchema`, otherwise to
the messages array. Reactive `.result`/`.messages`/`.partial`/`.final`
remain for rendering.
Framework hooks
- `useAssistant` for React / Solid / Vue and `createAssistant` for Svelte
(`@tanstack/ai-<fw>/assistant`). Each capability entry is the corresponding
primitive hook's own return (`useChat` / `useGeneration`).
Chat type inference
- `chat()`'s return carries an optional type-only phantom
(`ChatResultMeta<TTools, TSchema, TStream>`), so `assistant.chat` infers
typed `partial`/`final` from an `outputSchema` and types message tool-call
parts from the callback's tools β no client re-declaration. Runtime
unchanged. Client-executed tools still pass through `chat: { tools }` for
their browser implementations (their code can't cross the wire).
Docs (`docs/assistant`): a 3-page section β Overview (what an assistant is,
when to reach for one vs. single hooks, and typed chat/tools/structured
output), Scenarios (sequential pipeline + CMS content workflow, chained via
the resolved `await` returns), and Multiple Assistants (specialized
per-surface assistants across routes, plus composing two assistants together
on one page). Plus an `ai-core/assistant` agent skill, E2E coverage (chat
verified end-to-end; image one-shot skipped pending an aimock media fixture),
and a changeset.
0acb9a9 to
114416c
Compare
A topic β finished blog post demo of the multi-capability assistant. One `defineAssistant` route hosts structured-output chat, image, and speech; the page drafts the post, then illustrates and narrates it in parallel via the resolved `await` returns, and renders the result as an editorial article (hero image, Markdown body, voice-over player) in a two-column studio layout. Failed image/speech steps degrade gracefully with an honest per-step status; narration text is Markdown-stripped and length-capped for the TTS limit.
`useAssistant` now accepts a per-one-shot-capability `{ onResult, forwardedProps }`
option (keyed by capability name), mirroring the standalone generation hooks.
`onResult` runs on the raw backend result and its return type becomes
`assistant.<capability>.result` (via `InferGenerationOutput`); the parameter is
typed as the raw result. Full inference both directions β locked by an
`expectTypeOf` test. Threaded through `AssistantClient` into each
`GenerationClient`, across all four framework hooks.
Also refactors the Blog Studio example to derive 100% of its UI state from the
assistant surface β zero `useState`/`useEffect`/`useRef`. Chat
`isLoading`/`partial`/`final` and each one-shot's `result`/`status`/`error`
drive the stepper, hero, and article; the voice-over uses `speech.onResult` to
expose a ready `{ src }` data URL (no object-URL, no cleanup).
Docs (assistant overview) and the ai-core/assistant skill updated for the new
option.
What
Adds a multi-capability assistant layer: declare capabilities once on the server, consume them all from one typed client hook.
@tanstack/ai) βdefineAssistant(config): an inert registry of per-capability callbacks (chat,image,audio,speech,video,transcription,summarize). Each callback just invokes the existing activity (chat(),generateImage(), β¦). It exposes a singlehandler(request)that parses the AG-UI request, routes by acapabilitydiscriminator, invokes the callback, and streams the result back over the existing SSE wire. Nothing is constructed until a request arrives.@tanstack/ai-client) βAssistantClientcomposes oneChatClient+ oneGenerationClientper declared capability behind a single connection, each tagging its capability.AssistantSystem<TDef>maps the declared capabilities to fully-typed surfaces.useAssistantin@tanstack/ai-react,@tanstack/ai-solid,@tanstack/ai-vue, andcreateAssistantin@tanstack/ai-svelte. No generics at the call site β types are inferred from the passed definition. Each capability entry is the corresponding primitive hook's own return (useChat/useGeneration).Design & plan
Brainstormed and specced before implementation; the design doc and implementation plan drove a wave-by-wave build with per-wave review.
Testing
generation:result, 400 on unknown/inherited-prototype capability),AssistantClientcomposition +dispose,AssistantSystemtype-level assertions, and each framework hook (declared-capability surface,generateβresult,sendMessageβmessages).testing/e2e): a/assistantpage drivinguseAssistantagainst a real/api/assistanthandler. The chat capability is verified end-to-end through the assistant handler (streams a mocked response). The image one-shot leg istest.skipped with a documented reason β image generation is mocked programmatically viaregisterMediaFixtures(needsmatch.endpoint), which isn't wired for the assistant prompt; the one-shot dispatch itself is covered by the server + hook unit tests.docs/assistant/guide +ai-core/assistantagent skill (+ sibling cross-links); changeset (minor) for the six packages.Type-inference upgrade (follow-on commits)
assistant.chatis now fully inferred from the chat callback's return:chatcallback withoutputSchemagives typedassistant.chat.final(Schema | null) andassistant.chat.partial(DeepPartial<Schema>), across React/Solid/Vue/Svelte.chatcallback typeassistant.chat.messagestool-call parts with no client re-declaration.chat: { tools }remains, optional, for client-executed tools' runtime implementations (their code can't cross the wire).Enabled by an optional type-only phantom on
chat()'s return (ChatResultMeta<TTools, TSchema, TStream>) β runtime unchanged, still assignable everywhere.Note on local
test:prTwo failures show up only in a local Windows run, in packages this PR does not modify (empty diff for
ai-sandbox/ai-acp), pulled in via Nxaffectedβ they pass on CI (Linux):@tanstack/ai-sandbox:test:libβ one Windows path-separator assertion (/tmp/β¦vs\tmp\β¦); 190/191 pass.@tanstack/ai-acp:test:libβ sandbox/ACP integration tests spawning real harnesses time out under a contended local Windows worktree.(
ai-e2e:buildbriefly failed on CI because the assistant test page imported the multi-provider adapter factory, pullingollama'snode:fsinto the browser bundle β fixed by using the openai adapters directly.)π€ Generated with Claude Code
Summary by CodeRabbit
New Features
defineAssistantfor server configuration anduseAssistant/createAssistantintegrations for React, Solid, Vue, and Svelte.Documentation
Tests