@tanstack/ai-client 0.23.3 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,6 +36,14 @@
36
36
  </a>
37
37
  </div>
38
38
 
39
+ <br />
40
+
41
+ <div align="center">
42
+ <a href="https://tanstack.com/blog/tanstack-open-source-awards-2026">
43
+ <img src="https://raw.githubusercontent.com/TanStack/ai/16826d81cade868956df240d6239a671e689193c/media/js-open-source-award-2026-ai-project-of-the-year.svg" alt="Winner of the 2026 JavaScript Open Source Award for AI Project of the Year" width="250" />
44
+ </a>
45
+ </div>
46
+
39
47
  # TanStack AI
40
48
 
41
49
  Type-safe, provider-agnostic TypeScript SDK for building streaming chat,
@@ -191,7 +199,7 @@ Learn more in the
191
199
  build low-latency realtime voice experiences.
192
200
  - [Code Mode](https://tanstack.com/ai/latest/docs/code-mode/code-mode) - let
193
201
  models write and execute TypeScript inside a secure isolate.
194
- - [Code Mode with Skills](https://tanstack.com/ai/latest/docs/code-mode/code-mode-with-skills) -
202
+ - [Code Mode with Snippets](https://tanstack.com/ai/latest/docs/code-mode/code-mode-with-snippets) -
195
203
  give Code Mode reusable runtime capabilities.
196
204
 
197
205
  ## Providers
@@ -208,6 +216,7 @@ Official adapters include:
208
216
  | [`@tanstack/ai-grok`](https://tanstack.com/ai/latest/docs/adapters/grok) | xAI Grok chat, images, and realtime |
209
217
  | [`@tanstack/ai-groq`](https://tanstack.com/ai/latest/docs/adapters/groq) | Groq low-latency inference |
210
218
  | [`@tanstack/ai-elevenlabs`](https://tanstack.com/ai/latest/docs/adapters/elevenlabs) | ElevenLabs realtime voice, speech, transcription, music, and sound effects |
219
+ | [`@tanstack/ai-byteplus`](https://tanstack.com/ai/latest/docs/adapters/byteplus) | BytePlus Seed chat, Seedance video, Seedream image, and Seed Speech TTS/ASR |
211
220
  | [`@tanstack/ai-fal`](https://tanstack.com/ai/latest/docs/adapters/fal) | fal.ai image, video, audio, speech, and transcription models |
212
221
 
213
222
  The adapter system is tree-shakeable by activity. Import `openaiText` for chat,
@@ -46,8 +46,8 @@ export declare function normalizeQueueOption(option: QueueOption | undefined): N
46
46
  export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = any, TContext = unknown> {
47
47
  private readonly processor;
48
48
  private connection;
49
- private readonly uniqueId;
50
- private readonly threadId;
49
+ private uniqueId;
50
+ private threadId;
51
51
  private readonly persistor?;
52
52
  private readonly clearedStreamTracker;
53
53
  private currentRunId;
@@ -190,6 +190,7 @@ export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = an
190
190
  */
191
191
  private hydrateFromServer;
192
192
  mountDevtools(): void;
193
+ private ensureThreadId;
193
194
  /**
194
195
  * Drain a runId-less RUN_ERROR that belongs to a cleared run the client is
195
196
  * still tracking. The persistor owns the cleared-run bookkeeping; the client
@@ -197,13 +197,15 @@ var ChatClient = class {
197
197
  devtoolsMounted = false;
198
198
  callbacksRef;
199
199
  constructor(options) {
200
- this.threadId = options.threadId || this.generateUniqueId("thread");
201
- this.uniqueId = options.id || this.threadId;
200
+ this.threadId = options.threadId || "";
201
+ this.uniqueId = this.threadId;
202
202
  let cachesMessages = true;
203
- if (options.persistence === true) cachesMessages = false;
204
- else if (options.persistence) {
205
- const persistenceKey = options.id ?? this.threadId;
206
- this.persistor = new ChatPersistor(options.persistence, persistenceKey, (messages) => this.processor.setMessages(messages), (snapshot) => this.applyPersistedResume(snapshot));
203
+ if (options.persistence === true) {
204
+ if (!options.threadId) throw new Error("[TanStack AI] persistence needs a stable `threadId` to key on. Pass a threadId from your app (for example support-42).");
205
+ cachesMessages = false;
206
+ } else if (options.persistence) {
207
+ if (!options.threadId) throw new Error("[TanStack AI] persistence needs a stable `threadId` to key on. Pass a threadId from your app (for example support-42).");
208
+ this.persistor = new ChatPersistor(options.persistence, options.threadId, (messages) => this.processor.setMessages(messages), (snapshot) => this.applyPersistedResume(snapshot));
207
209
  }
208
210
  this.bodyOption = options.body || {};
209
211
  this.forwardedPropsOption = options.forwardedProps || {};
@@ -367,6 +369,7 @@ var ChatClient = class {
367
369
  */
368
370
  attach() {
369
371
  if (this.disposed || this.tailing) return;
372
+ this.ensureThreadId();
370
373
  this.tailing = true;
371
374
  if (this.rejoinRunId) this.maybeRejoinInFlight(this.rejoinRunId);
372
375
  if (!this.cachesMessages && this.connection.hydrate) this.hydrateFromServer();
@@ -474,10 +477,16 @@ var ChatClient = class {
474
477
  })();
475
478
  }
476
479
  mountDevtools() {
480
+ this.ensureThreadId();
477
481
  if (this.devtoolsMounted) return;
478
482
  this.devtoolsMounted = true;
479
483
  this.devtoolsBridge.mountWithTools(this.processor.getMessages().length);
480
484
  }
485
+ ensureThreadId() {
486
+ if (!this.threadId) this.threadId = this.generateUniqueId("thread");
487
+ this.uniqueId = this.threadId;
488
+ return this.threadId;
489
+ }
481
490
  /**
482
491
  * Drain a runId-less RUN_ERROR that belongs to a cleared run the client is
483
492
  * still tracking. The persistor owns the cleared-run bookkeeping; the client
@@ -732,10 +741,17 @@ var ChatClient = class {
732
741
  this.events.errorChanged(error?.message || null);
733
742
  }
734
743
  buildDevtoolsBridgeOptions(devtools) {
744
+ const client = this;
735
745
  return {
736
- hookId: this.uniqueId,
737
- clientId: this.uniqueId,
738
- threadId: this.threadId,
746
+ get hookId() {
747
+ return client.uniqueId;
748
+ },
749
+ get clientId() {
750
+ return client.uniqueId;
751
+ },
752
+ get threadId() {
753
+ return client.threadId;
754
+ },
739
755
  metadata: {
740
756
  hookName: devtools?.hookName ?? "useChat",
741
757
  outputKind: devtools?.outputKind ?? "chat",