@tanstack/ai-svelte 0.17.0 → 0.18.1

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.
@@ -1,5 +1,5 @@
1
1
  import type { InferredClientContext } from '@tanstack/ai-client';
2
- import type { AnyClientTool, SchemaInput } from '@tanstack/ai';
2
+ import type { AnyClientTool, InterruptDefinition, SchemaInput } from '@tanstack/ai';
3
3
  import type { CreateChatOptions, CreateChatReturn } from './types';
4
4
  /**
5
5
  * Creates a reactive chat instance for Svelte 5.
@@ -31,4 +31,4 @@ import type { CreateChatOptions, CreateChatReturn } from './types';
31
31
  * </div>
32
32
  * ```
33
33
  */
34
- export declare function createChat<const TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined, TContext = InferredClientContext<TTools>>(options: CreateChatOptions<TTools, TSchema, TContext>): CreateChatReturn<TTools, TSchema, TContext>;
34
+ export declare function createChat<const TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined, TContext = InferredClientContext<TTools>, const TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> = readonly []>(options: CreateChatOptions<TTools, TSchema, TContext, TInterrupts>): CreateChatReturn<TTools, TSchema, TContext, TInterrupts>;
@@ -63,9 +63,9 @@ export function createChat(options) {
63
63
  const transport = options.connection
64
64
  ? { connection: options.connection }
65
65
  : { fetcher: options.fetcher };
66
- // The hook's identity is its `threadId`, which ChatClient also uses as the
67
- // persistence key — no separate `id`. When no `threadId` is given the client
68
- // generates one, so an ephemeral chat still works but is not restored on reload.
66
+ // The hook's identity is its `threadId`. When no `threadId` is given the
67
+ // client mints one after mount, so an ephemeral chat still works but is not
68
+ // restored on reload.
69
69
  const client = new ChatClient({
70
70
  devtoolsBridgeFactory: createChatDevtoolsBridge,
71
71
  ...transport,
@@ -105,6 +105,9 @@ export function createChat(options) {
105
105
  options.onError?.(err);
106
106
  },
107
107
  tools: options.tools,
108
+ ...(options.interrupts !== undefined && {
109
+ interrupts: options.interrupts,
110
+ }),
108
111
  ...(options.onCustomEvent !== undefined && {
109
112
  onCustomEvent: options.onCustomEvent,
110
113
  }),
@@ -140,9 +143,9 @@ export function createChat(options) {
140
143
  onRunIdChange: (nextRunId) => {
141
144
  runId = nextRunId;
142
145
  },
143
- onInterruptStateChange: (nextInterruptState) => {
146
+ onInterruptStateChange: (nextInterruptState, context) => {
144
147
  interruptState = nextInterruptState;
145
- options.onInterruptStateChange?.(nextInterruptState);
148
+ options.onInterruptStateChange?.(nextInterruptState, context);
146
149
  },
147
150
  });
148
151
  function syncResumeState() {
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { AnyClientTool, InferSchemaType, ModelMessage, RunAgentResumeItem, SchemaInput } from '@tanstack/ai';
2
- import type { AIDevtoolsDisplayOptions, BoundInterrupts, ChatClientOptions, ChatClientState, ChatInterrupt, ChatInterruptState, ChatRequestBody, ChatResumeState, ClientContextOptionFromTools, ConnectionStatus, DistributedOmit, InferredClientContext, MultimodalContent, QueueConfig, QueueOption, QueueStrategy, QueuedMessage, SendMessageOptions, UIMessage, WhenBusy } from '@tanstack/ai-client';
1
+ import type { AnyClientTool, InterruptDefinition, InferSchemaType, ModelMessage, RunAgentResumeItem, SchemaInput } from '@tanstack/ai';
2
+ import type { AIDevtoolsDisplayOptions, BoundInterrupts, ChatClientOptions, ChatClientState, ResolvableChatInterrupt, ChatInterruptState, ChatRequestBody, ChatResumeState, ClientContextOptionFromTools, ConnectionStatus, DistributedOmit, InferredClientContext, MultimodalContent, QueueConfig, QueueOption, QueueStrategy, QueuedMessage, SendMessageOptions, UIMessage, WhenBusy } from '@tanstack/ai-client';
3
3
  export type { ChatRequestBody, MultimodalContent, QueueConfig, QueuedMessage, QueueOption, QueueStrategy, SendMessageOptions, UIMessage, WhenBusy, };
4
4
  /**
5
5
  * Recursive partial — every property and every nested array element is
@@ -30,7 +30,7 @@ export type DeepPartial<T> = T extends ReadonlyArray<infer U> ? Array<DeepPartia
30
30
  * Note: Connection and body changes will recreate the ChatClient instance.
31
31
  * To update these options, remount the component or use a key prop.
32
32
  */
33
- export type CreateChatOptions<TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined, TContext = InferredClientContext<TTools>> = DistributedOmit<ChatClientOptions<TTools, TContext>, 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange' | 'onStatusChange' | 'onSubscriptionChange' | 'onConnectionStatusChange' | 'onSessionGeneratingChange' | 'onQueueChange' | 'onResumeStateChange' | 'onRunIdChange' | 'context' | 'devtools'> & {
33
+ export type CreateChatOptions<TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined, TContext = InferredClientContext<TTools>, TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> = readonly []> = DistributedOmit<ChatClientOptions<TTools, TContext, TInterrupts>, 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange' | 'onStatusChange' | 'onSubscriptionChange' | 'onConnectionStatusChange' | 'onSessionGeneratingChange' | 'onQueueChange' | 'onResumeStateChange' | 'onRunIdChange' | 'context' | 'devtools'> & {
34
34
  live?: boolean;
35
35
  /** Display options for TanStack AI Devtools. */
36
36
  devtools?: AIDevtoolsDisplayOptions;
@@ -45,7 +45,7 @@ export type CreateChatOptions<TTools extends ReadonlyArray<AnyClientTool> = any,
45
45
  * typed `partial` / `final` reactive getters; otherwise the return is
46
46
  * unchanged.
47
47
  */
48
- export type CreateChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined, TContext = unknown> = BaseCreateChatReturn<TTools, TSchema extends SchemaInput ? InferSchemaType<TSchema> : unknown, TContext> & (TSchema extends SchemaInput ? {
48
+ export type CreateChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined, TContext = unknown, TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> = readonly []> = BaseCreateChatReturn<TTools, TSchema extends SchemaInput ? InferSchemaType<TSchema> : unknown, TContext, TInterrupts> & (TSchema extends SchemaInput ? {
49
49
  /**
50
50
  * Live progressively-parsed structured output (reactive getter).
51
51
  * Derived from the latest assistant message's structured-output part.
@@ -58,7 +58,7 @@ export type CreateChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any,
58
58
  */
59
59
  readonly final: InferSchemaType<TSchema> | null;
60
60
  } : Record<never, never>);
61
- interface BaseCreateChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any, TData = unknown, TContext = unknown> {
61
+ interface BaseCreateChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any, TData = unknown, TContext = unknown, TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> = readonly []> {
62
62
  /**
63
63
  * Current messages in the conversation (reactive getter). When
64
64
  * `outputSchema` is supplied, `messages[i].parts.find(p => p.type ===
@@ -112,14 +112,14 @@ interface BaseCreateChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any
112
112
  * it, correlate a log line).
113
113
  */
114
114
  readonly runId: string | null;
115
- readonly interrupts: BoundInterrupts<TTools>;
115
+ readonly interrupts: BoundInterrupts<TTools, TInterrupts>;
116
116
  /** @deprecated Use `interrupts`. */
117
- readonly pendingInterrupts: BoundInterrupts<TTools>;
118
- readonly interruptErrors: ChatInterruptState<TTools>['interruptErrors'];
117
+ readonly pendingInterrupts: BoundInterrupts<TTools, TInterrupts>;
118
+ readonly interruptErrors: ChatInterruptState<TTools, TInterrupts>['interruptErrors'];
119
119
  readonly resuming: boolean;
120
120
  resolveInterrupts: {
121
121
  (approved: boolean): void;
122
- (resolver: (interrupt: ChatInterrupt<TTools>) => undefined): void;
122
+ (resolver: (interrupt: ResolvableChatInterrupt<TTools, TInterrupts>) => undefined): void;
123
123
  };
124
124
  cancelInterrupts: () => void;
125
125
  retryInterrupts: () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-svelte",
3
- "version": "0.17.0",
3
+ "version": "0.18.1",
4
4
  "description": "Svelte 5 bindings for TanStack AI streaming chat, structured outputs, and media generation.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -46,24 +46,24 @@
46
46
  "media-generation"
47
47
  ],
48
48
  "dependencies": {
49
- "@tanstack/ai-client": "^0.24.0"
49
+ "@tanstack/ai-client": "^0.25.1"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "svelte": "^5.0.0",
53
- "@tanstack/ai": "^0.46.0"
53
+ "@tanstack/ai": "^0.47.1"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@standard-schema/spec": "^1.1.0",
57
57
  "@sveltejs/package": "^2.3.10",
58
- "@sveltejs/vite-plugin-svelte": "^5.1.1",
58
+ "@sveltejs/vite-plugin-svelte": "^7.3.0",
59
59
  "@types/node": "^24.10.1",
60
60
  "@vitest/coverage-v8": "4.1.10",
61
61
  "jsdom": "^27.4.0",
62
- "svelte": "^5.20.0",
62
+ "svelte": "^5.56.9",
63
63
  "svelte-check": "^4.2.0",
64
64
  "typescript": "5.9.3",
65
65
  "vite": "^8.2.1",
66
- "@tanstack/ai": "0.46.0"
66
+ "@tanstack/ai": "0.47.1"
67
67
  },
68
68
  "scripts": {
69
69
  "clean": "rm -rf ./dist",
@@ -3,7 +3,7 @@ import { createChatDevtoolsBridge } from '@tanstack/ai-client/devtools'
3
3
  import { onMount } from 'svelte'
4
4
  import type {
5
5
  ChatClientState,
6
- ChatInterrupt,
6
+ ResolvableChatInterrupt,
7
7
  ChatInterruptState,
8
8
  ChatResumeState,
9
9
  ConnectionStatus,
@@ -14,6 +14,7 @@ import type {
14
14
  } from '@tanstack/ai-client'
15
15
  import type {
16
16
  AnyClientTool,
17
+ InterruptDefinition,
17
18
  InferSchemaType,
18
19
  ModelMessage,
19
20
  RunAgentResumeItem,
@@ -65,9 +66,12 @@ export function createChat<
65
66
  const TTools extends ReadonlyArray<AnyClientTool> = any,
66
67
  TSchema extends SchemaInput | undefined = undefined,
67
68
  TContext = InferredClientContext<TTools>,
69
+ const TInterrupts extends ReadonlyArray<
70
+ InterruptDefinition<any, any, any, any>
71
+ > = readonly [],
68
72
  >(
69
- options: CreateChatOptions<TTools, TSchema, TContext>,
70
- ): CreateChatReturn<TTools, TSchema, TContext> {
73
+ options: CreateChatOptions<TTools, TSchema, TContext, TInterrupts>,
74
+ ): CreateChatReturn<TTools, TSchema, TContext, TInterrupts> {
71
75
  // Create reactive state using Svelte 5 runes
72
76
  let messages = $state<Array<UIMessage<TTools>>>(options.initialMessages || [])
73
77
  let isLoading = $state(false)
@@ -78,7 +82,7 @@ export function createChat<
78
82
  let sessionGenerating = $state(false)
79
83
  let queue = $state<Array<QueuedMessage>>([])
80
84
  let runId = $state<string | null>(null)
81
- let interruptState = $state.raw<ChatInterruptState<TTools>>({
85
+ let interruptState = $state.raw<ChatInterruptState<TTools, TInterrupts>>({
82
86
  interrupts: EMPTY_INTERRUPTS,
83
87
  pendingInterrupts: EMPTY_INTERRUPTS,
84
88
  interruptErrors: EMPTY_INTERRUPT_ERRORS,
@@ -106,10 +110,10 @@ export function createChat<
106
110
  ? { connection: options.connection }
107
111
  : { fetcher: options.fetcher }
108
112
 
109
- // The hook's identity is its `threadId`, which ChatClient also uses as the
110
- // persistence key — no separate `id`. When no `threadId` is given the client
111
- // generates one, so an ephemeral chat still works but is not restored on reload.
112
- const client = new ChatClient<TTools, TContext>({
113
+ // The hook's identity is its `threadId`. When no `threadId` is given the
114
+ // client mints one after mount, so an ephemeral chat still works but is not
115
+ // restored on reload.
116
+ const client = new ChatClient<TTools, TContext, TInterrupts>({
113
117
  devtoolsBridgeFactory: createChatDevtoolsBridge,
114
118
  ...transport,
115
119
  ...(options.initialMessages !== undefined && {
@@ -148,6 +152,9 @@ export function createChat<
148
152
  options.onError?.(err)
149
153
  },
150
154
  tools: options.tools,
155
+ ...(options.interrupts !== undefined && {
156
+ interrupts: options.interrupts,
157
+ }),
151
158
  ...(options.onCustomEvent !== undefined && {
152
159
  onCustomEvent: options.onCustomEvent,
153
160
  }),
@@ -183,9 +190,9 @@ export function createChat<
183
190
  onRunIdChange: (nextRunId) => {
184
191
  runId = nextRunId
185
192
  },
186
- onInterruptStateChange: (nextInterruptState) => {
193
+ onInterruptStateChange: (nextInterruptState, context) => {
187
194
  interruptState = nextInterruptState
188
- options.onInterruptStateChange?.(nextInterruptState)
195
+ options.onInterruptStateChange?.(nextInterruptState, context)
189
196
  },
190
197
  })
191
198
 
@@ -308,7 +315,11 @@ export function createChat<
308
315
  }
309
316
 
310
317
  const resolveInterrupts = (
311
- resolution: boolean | ((interrupt: ChatInterrupt<TTools>) => undefined),
318
+ resolution:
319
+ | boolean
320
+ | ((
321
+ interrupt: ResolvableChatInterrupt<TTools, TInterrupts>,
322
+ ) => undefined),
312
323
  ) => {
313
324
  if (typeof resolution === 'boolean') {
314
325
  client.resolveInterrupts(resolution)
@@ -451,5 +462,5 @@ export function createChat<
451
462
  updateBody,
452
463
  updateForwardedProps,
453
464
  updateContext,
454
- } as unknown as CreateChatReturn<TTools, TSchema, TContext>
465
+ } as unknown as CreateChatReturn<TTools, TSchema, TContext, TInterrupts>
455
466
  }
package/src/types.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type {
2
2
  AnyClientTool,
3
+ InterruptDefinition,
3
4
  InferSchemaType,
4
5
  ModelMessage,
5
6
  RunAgentResumeItem,
@@ -10,7 +11,7 @@ import type {
10
11
  BoundInterrupts,
11
12
  ChatClientOptions,
12
13
  ChatClientState,
13
- ChatInterrupt,
14
+ ResolvableChatInterrupt,
14
15
  ChatInterruptState,
15
16
  ChatRequestBody,
16
17
  ChatResumeState,
@@ -78,8 +79,10 @@ export type CreateChatOptions<
78
79
  TTools extends ReadonlyArray<AnyClientTool> = any,
79
80
  TSchema extends SchemaInput | undefined = undefined,
80
81
  TContext = InferredClientContext<TTools>,
82
+ TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
83
+ readonly [],
81
84
  > = DistributedOmit<
82
- ChatClientOptions<TTools, TContext>,
85
+ ChatClientOptions<TTools, TContext, TInterrupts>,
83
86
  | 'onMessagesChange'
84
87
  | 'onLoadingChange'
85
88
  | 'onErrorChange'
@@ -112,10 +115,13 @@ export type CreateChatReturn<
112
115
  TTools extends ReadonlyArray<AnyClientTool> = any,
113
116
  TSchema extends SchemaInput | undefined = undefined,
114
117
  TContext = unknown,
118
+ TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
119
+ readonly [],
115
120
  > = BaseCreateChatReturn<
116
121
  TTools,
117
122
  TSchema extends SchemaInput ? InferSchemaType<TSchema> : unknown,
118
- TContext
123
+ TContext,
124
+ TInterrupts
119
125
  > &
120
126
  (TSchema extends SchemaInput
121
127
  ? {
@@ -137,6 +143,8 @@ interface BaseCreateChatReturn<
137
143
  TTools extends ReadonlyArray<AnyClientTool> = any,
138
144
  TData = unknown,
139
145
  TContext = unknown,
146
+ TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =
147
+ readonly [],
140
148
  > {
141
149
  /**
142
150
  * Current messages in the conversation (reactive getter). When
@@ -201,14 +209,21 @@ interface BaseCreateChatReturn<
201
209
  * it, correlate a log line).
202
210
  */
203
211
  readonly runId: string | null
204
- readonly interrupts: BoundInterrupts<TTools>
212
+ readonly interrupts: BoundInterrupts<TTools, TInterrupts>
205
213
  /** @deprecated Use `interrupts`. */
206
- readonly pendingInterrupts: BoundInterrupts<TTools>
207
- readonly interruptErrors: ChatInterruptState<TTools>['interruptErrors']
214
+ readonly pendingInterrupts: BoundInterrupts<TTools, TInterrupts>
215
+ readonly interruptErrors: ChatInterruptState<
216
+ TTools,
217
+ TInterrupts
218
+ >['interruptErrors']
208
219
  readonly resuming: boolean
209
220
  resolveInterrupts: {
210
221
  (approved: boolean): void
211
- (resolver: (interrupt: ChatInterrupt<TTools>) => undefined): void
222
+ (
223
+ resolver: (
224
+ interrupt: ResolvableChatInterrupt<TTools, TInterrupts>,
225
+ ) => undefined,
226
+ ): void
212
227
  }
213
228
  cancelInterrupts: () => void
214
229
  retryInterrupts: () => void