@tanstack/ai-client 0.22.1 → 0.23.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.
- package/README.md +15 -1
- package/dist/esm/audio-recorder.js +190 -213
- package/dist/esm/audio-recorder.js.map +1 -1
- package/dist/esm/chat-client.d.ts +172 -3
- package/dist/esm/chat-client.js +1656 -1386
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/cleared-stream-tracker.d.ts +23 -0
- package/dist/esm/cleared-stream-tracker.js +97 -0
- package/dist/esm/cleared-stream-tracker.js.map +1 -0
- package/dist/esm/client-persistor.d.ts +25 -12
- package/dist/esm/client-persistor.js +260 -235
- package/dist/esm/client-persistor.js.map +1 -1
- package/dist/esm/connection-adapters.d.ts +231 -10
- package/dist/esm/connection-adapters.js +989 -574
- package/dist/esm/connection-adapters.js.map +1 -1
- package/dist/esm/devtools-noop.d.ts +1 -0
- package/dist/esm/devtools-noop.js +79 -139
- package/dist/esm/devtools-noop.js.map +1 -1
- package/dist/esm/devtools.d.ts +31 -1
- package/dist/esm/devtools.js +977 -1127
- package/dist/esm/devtools.js.map +1 -1
- package/dist/esm/events.js +224 -226
- package/dist/esm/events.js.map +1 -1
- package/dist/esm/generation-client.d.ts +145 -2
- package/dist/esm/generation-client.js +659 -321
- package/dist/esm/generation-client.js.map +1 -1
- package/dist/esm/generation-reconstruct.d.ts +21 -0
- package/dist/esm/generation-reconstruct.js +85 -0
- package/dist/esm/generation-reconstruct.js.map +1 -0
- package/dist/esm/generation-types.d.ts +289 -3
- package/dist/esm/generation-types.js +356 -13
- package/dist/esm/generation-types.js.map +1 -1
- package/dist/esm/index.d.ts +9 -4
- package/dist/esm/index.js +7 -39
- package/dist/esm/interrupt-manager.d.ts +77 -0
- package/dist/esm/interrupt-manager.js +787 -0
- package/dist/esm/interrupt-manager.js.map +1 -0
- package/dist/esm/mcp-app-bridge.js +56 -64
- package/dist/esm/mcp-app-bridge.js.map +1 -1
- package/dist/esm/realtime-client.js +366 -440
- package/dist/esm/realtime-client.js.map +1 -1
- package/dist/esm/response-stream.js +19 -26
- package/dist/esm/response-stream.js.map +1 -1
- package/dist/esm/sse-parser.js +44 -47
- package/dist/esm/sse-parser.js.map +1 -1
- package/dist/esm/sse-utils.js +8 -9
- package/dist/esm/sse-utils.js.map +1 -1
- package/dist/esm/storage-adapters.d.ts +62 -0
- package/dist/esm/storage-adapters.js +174 -0
- package/dist/esm/storage-adapters.js.map +1 -0
- package/dist/esm/types.d.ts +212 -10
- package/dist/esm/types.js +38 -7
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/video-generation-client.d.ts +113 -2
- package/dist/esm/video-generation-client.js +665 -379
- package/dist/esm/video-generation-client.js.map +1 -1
- package/package.json +7 -7
- package/src/chat-client.ts +1079 -61
- package/src/cleared-stream-tracker.ts +151 -0
- package/src/client-persistor.ts +102 -33
- package/src/connection-adapters.ts +1185 -142
- package/src/devtools-noop.ts +4 -3
- package/src/devtools.ts +121 -3
- package/src/generation-client.ts +563 -13
- package/src/generation-reconstruct.ts +121 -0
- package/src/generation-types.ts +727 -3
- package/src/index.ts +56 -1
- package/src/interrupt-manager.ts +1440 -0
- package/src/storage-adapters.ts +242 -0
- package/src/types.ts +301 -9
- package/src/video-generation-client.ts +479 -13
- package/dist/esm/index.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AnyClientTool, ModelMessage, StreamChunk } from '@tanstack/ai/client';
|
|
1
|
+
import { AnyClientTool, ModelMessage, RunAgentResumeItem, StreamChunk } from '@tanstack/ai/client';
|
|
2
2
|
import { ConnectionAdapter } from './connection-adapters.js';
|
|
3
|
-
import { ChatClientOptions, ChatClientState, ChatFetcher, ConnectionStatus, MultimodalContent, QueueOption, QueueStrategy, QueuedMessage, SendMessageOptions, UIMessage, WhenBusy } from './types.js';
|
|
3
|
+
import { BoundInterrupts, ChatClientOptions, ChatClientState, ChatFetcher, ChatInterrupt, ChatInterruptState, ChatResumeState, ConnectionStatus, MultimodalContent, QueueOption, QueueStrategy, QueuedMessage, SendMessageOptions, UIMessage, WhenBusy } from './types.js';
|
|
4
4
|
type ChatClientUpdateOptionsWithoutContext<TTools extends ReadonlyArray<AnyClientTool>> = {
|
|
5
5
|
connection?: ConnectionAdapter;
|
|
6
6
|
fetcher?: ChatFetcher;
|
|
@@ -17,6 +17,13 @@ type ChatClientUpdateOptionsWithoutContext<TTools extends ReadonlyArray<AnyClien
|
|
|
17
17
|
onConnectionStatusChange?: (status: ConnectionStatus) => void;
|
|
18
18
|
onSessionGeneratingChange?: (isGenerating: boolean) => void;
|
|
19
19
|
onQueueChange?: (queue: Array<QueuedMessage>) => void;
|
|
20
|
+
onResumeStateChange?: (resumeState: ChatResumeState | null, pendingInterrupts: BoundInterrupts<TTools>) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Fires whenever the id of the run in flight changes: the new id when a run
|
|
23
|
+
* starts (including a rejoin), `null` when it settles.
|
|
24
|
+
*/
|
|
25
|
+
onRunIdChange?: (runId: string | null) => void;
|
|
26
|
+
onInterruptStateChange?: (state: ChatInterruptState<TTools>) => void;
|
|
20
27
|
onCustomEvent?: (eventType: string, data: unknown, context: {
|
|
21
28
|
toolCallId?: string;
|
|
22
29
|
}) => void;
|
|
@@ -42,7 +49,19 @@ export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = an
|
|
|
42
49
|
private readonly uniqueId;
|
|
43
50
|
private readonly threadId;
|
|
44
51
|
private readonly persistor?;
|
|
52
|
+
private readonly clearedStreamTracker;
|
|
45
53
|
private currentRunId;
|
|
54
|
+
private lastResume;
|
|
55
|
+
private rejoinedRunId;
|
|
56
|
+
private readonly interruptManager;
|
|
57
|
+
private activeInterruptSubmission;
|
|
58
|
+
private interruptSubmissionFailure;
|
|
59
|
+
private readonly joinedRunWaiters;
|
|
60
|
+
private pendingResumeParentRunId;
|
|
61
|
+
private pendingResumeThreadId;
|
|
62
|
+
private pendingResumeItems;
|
|
63
|
+
private activeResumeThreadId;
|
|
64
|
+
private activeResumeRunId;
|
|
46
65
|
private bodyOption;
|
|
47
66
|
private forwardedPropsOption;
|
|
48
67
|
private context;
|
|
@@ -102,9 +121,74 @@ export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = an
|
|
|
102
121
|
private draining;
|
|
103
122
|
private sessionGenerating;
|
|
104
123
|
private readonly activeRunIds;
|
|
124
|
+
/** Latched by `dispose()`; stops any late async callback starting new work. */
|
|
125
|
+
private disposed;
|
|
126
|
+
/** Whether a view is currently watching. See `attach` / `detach`. */
|
|
127
|
+
private tailing;
|
|
128
|
+
/** Constructor inputs `attach()` needs on every re-attach, not just the first. */
|
|
129
|
+
private readonly rejoinRunId;
|
|
130
|
+
private readonly cachesMessages;
|
|
105
131
|
private devtoolsMounted;
|
|
106
132
|
private readonly callbacksRef;
|
|
107
133
|
constructor(options: ChatClientOptions<TTools, TContext>);
|
|
134
|
+
/**
|
|
135
|
+
* START TAILING: re-attach to an in-flight run so its chunks arrive here.
|
|
136
|
+
*
|
|
137
|
+
* Called by the constructor, and again by a UI wrapper every time its view
|
|
138
|
+
* mounts. Idempotent — attaching while already attached does nothing — so the
|
|
139
|
+
* constructor call and a wrapper's first mount cost one attach between them.
|
|
140
|
+
*
|
|
141
|
+
* Pairs with {@link detach}. The pair exists because tailing used to begin ONLY
|
|
142
|
+
* in the constructor, which meant a view could never stop tailing and then
|
|
143
|
+
* resume: unmount had to either keep the connection open or lose it for good.
|
|
144
|
+
* Keeping it open is what starved the page — a browser allows ~6 connections per
|
|
145
|
+
* origin, and one long-lived stream per view reaches that after a handful of
|
|
146
|
+
* views, after which every other request queues (measured: an in-page fetch took
|
|
147
|
+
* over two minutes while the same request from outside the browser took 17ms).
|
|
148
|
+
*/
|
|
149
|
+
attach(): void;
|
|
150
|
+
/**
|
|
151
|
+
* STOP TAILING: drop the connection, keep everything else.
|
|
152
|
+
*
|
|
153
|
+
* Called by a UI wrapper when its view unmounts. The transcript, the resume
|
|
154
|
+
* pointer and the run id all stay, so a later {@link attach} repaints instantly
|
|
155
|
+
* and re-tails from the durable log — nothing is lost, because the run keeps
|
|
156
|
+
* going server-side and its log holds every chunk.
|
|
157
|
+
*
|
|
158
|
+
* Deliberately NOT `dispose()`: this client is expected back. And deliberately
|
|
159
|
+
* not `stop()`, which means "the user ended this run" — detaching says only that
|
|
160
|
+
* nobody is watching right now.
|
|
161
|
+
*
|
|
162
|
+
* `rejoinedRunId` is cleared so the next `attach` can re-join the same run;
|
|
163
|
+
* without that reset the guard in {@link maybeRejoinInFlight} would treat the
|
|
164
|
+
* run as already joined and the view would come back silent.
|
|
165
|
+
*/
|
|
166
|
+
detach(): void;
|
|
167
|
+
private applyResumeSnapshot;
|
|
168
|
+
/**
|
|
169
|
+
* Apply a resume snapshot read from durable storage. Restores interrupt state,
|
|
170
|
+
* and for a bare in-flight run (no pending interrupts) also rejoins it. This is
|
|
171
|
+
* the async-store counterpart to the synchronous rejoin in the constructor:
|
|
172
|
+
* `applyResumeSnapshot` alone only handles interrupts, so an async store
|
|
173
|
+
* (`indexedDBPersistence`) would otherwise never rejoin a mid-stream run.
|
|
174
|
+
*/
|
|
175
|
+
private applyPersistedResume;
|
|
176
|
+
/**
|
|
177
|
+
* Rejoin a persisted in-flight run, guarded so it fires at most once and never
|
|
178
|
+
* while another run is already active. Skipped when the connection is not
|
|
179
|
+
* resumable (`joinRun` absent), so a non-durable transport is a no-op.
|
|
180
|
+
*/
|
|
181
|
+
private maybeRejoinInFlight;
|
|
182
|
+
/**
|
|
183
|
+
* Server-authoritative mount hydration (`persistence: true`). The client holds
|
|
184
|
+
* no transcript and no run pointer; on mount it asks the server — keyed by the
|
|
185
|
+
* stable threadId — for the stored transcript and whether a run is still
|
|
186
|
+
* generating. The transcript repaints immediately; an in-flight run is tailed
|
|
187
|
+
* through the same durability rejoin as a reload. Best-effort and
|
|
188
|
+
* non-blocking: a failure leaves the client empty rather than throwing, and a
|
|
189
|
+
* send that starts first owns the client (hydration then backs off).
|
|
190
|
+
*/
|
|
191
|
+
private hydrateFromServer;
|
|
108
192
|
mountDevtools(): void;
|
|
109
193
|
/**
|
|
110
194
|
* Drain a runId-less RUN_ERROR that belongs to a cleared run the client is
|
|
@@ -112,13 +196,57 @@ export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = an
|
|
|
112
196
|
* owns the active-run / session / processing state.
|
|
113
197
|
*/
|
|
114
198
|
private drainIgnoredRunlessChunk;
|
|
199
|
+
private retireIgnoredClearedTerminalChunk;
|
|
115
200
|
private updateRunLifecycle;
|
|
201
|
+
/**
|
|
202
|
+
* Track interrupt state off the stream's terminal events. A RUN_FINISHED with
|
|
203
|
+
* an interrupt outcome records the pending interrupts + the run/thread to
|
|
204
|
+
* resume; any other terminal event for the tracked/current run clears that
|
|
205
|
+
* state. This is interrupt (state) resume — there is no delivery cursor.
|
|
206
|
+
*/
|
|
207
|
+
private observeInterruptState;
|
|
208
|
+
/**
|
|
209
|
+
* The interrupt-resume state for the active/interrupted run (its run/thread
|
|
210
|
+
* ids), or null when there is nothing to resume. Apps can persist this to
|
|
211
|
+
* resume interrupts across a full reload.
|
|
212
|
+
*/
|
|
213
|
+
getResumeState(): ChatResumeState | null;
|
|
214
|
+
/**
|
|
215
|
+
* The id of the run this client has in flight — one it started via a send or
|
|
216
|
+
* rejoined via `joinRun` — or null when there is none. Unlike
|
|
217
|
+
* {@link getResumeState}, this tracks ordinary runs too, not only one that is
|
|
218
|
+
* interrupted or being resumed. A run another client started and that arrives
|
|
219
|
+
* over a live subscription is not this client's run and is not reported here.
|
|
220
|
+
*/
|
|
221
|
+
getCurrentRunId(): string | null;
|
|
222
|
+
private setCurrentRunId;
|
|
223
|
+
getInterruptState(): ChatInterruptState<TTools>;
|
|
224
|
+
getInterrupts(): BoundInterrupts<TTools>;
|
|
225
|
+
/** @deprecated Use getInterrupts(). */
|
|
226
|
+
getPendingInterrupts(): BoundInterrupts<TTools>;
|
|
227
|
+
resolveInterrupts(approved: boolean): void;
|
|
228
|
+
resolveInterrupts(resolver: (interrupt: ChatInterrupt<TTools>) => undefined): void;
|
|
229
|
+
cancelInterrupts(): void;
|
|
230
|
+
retryInterrupts(): void;
|
|
231
|
+
/** Unsafe low-level resume escape hatch. Prefer bound interrupt methods. */
|
|
232
|
+
resumeInterruptsUnsafe(resume: Array<RunAgentResumeItem>, state?: ChatResumeState): Promise<boolean>;
|
|
233
|
+
/** @deprecated Use bound interrupt methods or resumeInterruptsUnsafe(). */
|
|
234
|
+
resumeInterrupts(resume: Array<RunAgentResumeItem>, state?: ChatResumeState): Promise<boolean>;
|
|
235
|
+
private submitInterruptBatch;
|
|
236
|
+
private takeInterruptSubmissionFailure;
|
|
237
|
+
private interruptGeneration;
|
|
116
238
|
private generateUniqueId;
|
|
117
239
|
private setIsLoading;
|
|
118
240
|
private setStatus;
|
|
119
241
|
private setIsSubscribed;
|
|
120
242
|
private setConnectionStatus;
|
|
121
243
|
private setSessionGenerating;
|
|
244
|
+
private notifyResumeStateChange;
|
|
245
|
+
/**
|
|
246
|
+
* Build the durable resume snapshot from the current resume state + pending
|
|
247
|
+
* interrupt descriptors and hand it to the persistor (null clears it).
|
|
248
|
+
*/
|
|
249
|
+
private persistResumeSnapshot;
|
|
122
250
|
private resetSessionGenerating;
|
|
123
251
|
private setError;
|
|
124
252
|
private buildDevtoolsBridgeOptions;
|
|
@@ -136,6 +264,40 @@ export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = an
|
|
|
136
264
|
* Consume chunks from the connection subscription.
|
|
137
265
|
*/
|
|
138
266
|
private consumeSubscription;
|
|
267
|
+
/**
|
|
268
|
+
* Re-attach to an in-flight run after a full page reload, replaying its stream
|
|
269
|
+
* from the server's delivery-durability log via `joinRun` (which returns the
|
|
270
|
+
* whole run so far, then tails live to completion).
|
|
271
|
+
*
|
|
272
|
+
* The log is the single source of truth for the run, so we rebuild the
|
|
273
|
+
* in-flight assistant bubble from it rather than trying to reconcile the
|
|
274
|
+
* server-hydrated partial with the replay: on the first chunk that actually
|
|
275
|
+
* (re)builds a message we drop the hydrated in-flight assistant, and the
|
|
276
|
+
* replay reconstructs one clean bubble. Dropping only on real content (not on
|
|
277
|
+
* `RUN_STARTED`) means a rejoin that connects but delivers nothing can never
|
|
278
|
+
* leave an empty bubble behind.
|
|
279
|
+
*
|
|
280
|
+
* Bounded connect: a durable backend keeps a from-start join open waiting for
|
|
281
|
+
* a producer, so a stale pointer to an unknown/evicted run would otherwise pin
|
|
282
|
+
* the UI in a loading state for the backend's full first-chunk deadline. We
|
|
283
|
+
* give up after {@link REJOIN_CONNECT_DEADLINE_MS} if no chunk arrives and
|
|
284
|
+
* clear the dead pointer so it does not retry on the next load.
|
|
285
|
+
*
|
|
286
|
+
* Replay chunks are processed WITHOUT the per-chunk yield the live path uses,
|
|
287
|
+
* so the buffered prefix snaps in and only the genuinely-live tail streams at
|
|
288
|
+
* network speed — a reload looks like the run continued, not like it re-typed.
|
|
289
|
+
*/
|
|
290
|
+
private resumeInFlightRun;
|
|
291
|
+
/**
|
|
292
|
+
* Drop a hydrated, still-in-flight assistant turn so a resume replay can
|
|
293
|
+
* rebuild it cleanly. Only touches a trailing assistant message (the shape a
|
|
294
|
+
* reload-mid-stream leaves); a thread whose last turn is a user message (run
|
|
295
|
+
* never produced, or already settled) is left untouched.
|
|
296
|
+
*/
|
|
297
|
+
private dropTrailingInFlightAssistant;
|
|
298
|
+
private processIncomingChunk;
|
|
299
|
+
private isActiveInterruptSubmissionFailure;
|
|
300
|
+
private resolveJoinedRun;
|
|
139
301
|
/**
|
|
140
302
|
* Ensure subscription loop is running, starting it if needed.
|
|
141
303
|
*/
|
|
@@ -185,11 +347,18 @@ export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = an
|
|
|
185
347
|
* ],
|
|
186
348
|
* id: 'custom-message-id'
|
|
187
349
|
* },
|
|
188
|
-
* { model: 'gpt-
|
|
350
|
+
* { model: 'gpt-5.5' }
|
|
189
351
|
* )
|
|
190
352
|
* ```
|
|
191
353
|
*/
|
|
192
354
|
sendMessage(content: string | MultimodalContent, body?: Record<string, any>, sendOptions?: SendMessageOptions): Promise<void>;
|
|
355
|
+
/**
|
|
356
|
+
* True when the client still has user-actionable interrupts (or is mid
|
|
357
|
+
* resume submission). Staged/submitting items that are already being
|
|
358
|
+
* continued do not block a later turn once the resume stream has cleared
|
|
359
|
+
* resume state.
|
|
360
|
+
*/
|
|
361
|
+
private hasBlockingInterrupts;
|
|
193
362
|
/** True while a stream is active, a send is claiming the client, or the queue is draining. */
|
|
194
363
|
private isSendBusy;
|
|
195
364
|
private resolveBusyReason;
|