@tanstack/ai-client 0.31.1 → 0.32.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.
@@ -130,7 +130,7 @@ export interface ConnectConnectionAdapter {
130
130
  * `fetchServerSentEvents` / `fetchHttpStream`, and on `stream()` /
131
131
  * `rpcStream()` when supplied via {@link StreamConnectionHandlers}.
132
132
  */
133
- hydrate?: (threadId: string) => Promise<ChatHydrationResult>;
133
+ hydrate?: (threadId: string, options?: ChatHydrateOptions) => Promise<ChatHydrationResult>;
134
134
  }
135
135
  /**
136
136
  * Server-resolved hydration for a generation thread. `resumeSnapshot` is the
@@ -166,11 +166,22 @@ export interface GenerationHydrationResult {
166
166
  runId: string;
167
167
  } | null;
168
168
  }
169
+ /**
170
+ * Optional paging for a chat hydrate GET.
171
+ * Omit both fields to load the full thread.
172
+ */
173
+ export interface ChatHydrateOptions {
174
+ limit?: number;
175
+ before?: string;
176
+ }
169
177
  /**
170
178
  * Server-resolved hydration for a thread. `messages` is the stored transcript;
171
179
  * `activeRun` is a cursor to a run still generating for the thread (or `null`).
172
180
  * Keyed on the STABLE thread id — the client never handles a run id, so a turn
173
181
  * that spans several runs (interrupt/tool continuations) reconnects correctly.
182
+ *
183
+ * `page` is additive. Old servers omit it; treat that as a full list
184
+ * (`truncated: false`).
174
185
  */
175
186
  export interface ChatHydrationResult {
176
187
  messages: Array<UIMessage>;
@@ -186,6 +197,16 @@ export interface ChatHydrationResult {
186
197
  runId: string;
187
198
  pending: Array<ChatPendingInterrupt>;
188
199
  } | null;
200
+ /**
201
+ * Whether this window is a slice of a longer thread.
202
+ * `cursor` is set when `truncated` is true and is sent back as `before`.
203
+ */
204
+ page?: {
205
+ truncated: false;
206
+ } | {
207
+ truncated: true;
208
+ cursor: string;
209
+ };
189
210
  }
190
211
  /**
191
212
  * A {@link ConnectConnectionAdapter} that also supports joining an existing run
@@ -205,7 +226,7 @@ export interface ResumableConnectConnectionAdapter extends ConnectConnectionAdap
205
226
  * JSON GET (`?threadId`), so it is transport-agnostic regardless of how the
206
227
  * delivery stream is served.
207
228
  */
208
- hydrate?: (threadId: string) => Promise<ChatHydrationResult>;
229
+ hydrate?: (threadId: string, options?: ChatHydrateOptions) => Promise<ChatHydrationResult>;
209
230
  }
210
231
  export interface SubscribeConnectionAdapter {
211
232
  /**
@@ -228,7 +249,7 @@ export interface SubscribeConnectionAdapter {
228
249
  * cursor). Present only when the underlying connection supports it. The client
229
250
  * calls it on mount to re-hydrate without any app-side loader or prop.
230
251
  */
231
- hydrate?: (threadId: string) => Promise<ChatHydrationResult>;
252
+ hydrate?: (threadId: string, options?: ChatHydrateOptions) => Promise<ChatHydrationResult>;
232
253
  }
233
254
  /**
234
255
  * Connection adapter union.
@@ -394,7 +415,7 @@ export interface StreamConnectionHandlers {
394
415
  * Server-driven chat hydration for `persistence: true`: the stored
395
416
  * transcript for `threadId` plus a cursor to an in-flight run.
396
417
  */
397
- hydrate?: (threadId: string) => Promise<ChatHydrationResult>;
418
+ hydrate?: (threadId: string, options?: ChatHydrateOptions) => Promise<ChatHydrationResult>;
398
419
  /**
399
420
  * Server-driven generation hydration for `persistence: true`: the last
400
421
  * generation's resume snapshot for `threadId` plus a cursor to a run still
@@ -309,15 +309,33 @@ function errorFromXhrStatus(xhr) {
309
309
  }
310
310
  return /* @__PURE__ */ new Error(`XHR error! status: ${xhr.status} ${xhr.statusText}`);
311
311
  }
312
+ function chatHydrationSearchParams(threadId, options) {
313
+ const values = { threadId };
314
+ if (options?.limit !== void 0) values.limit = String(options.limit);
315
+ if (options?.before !== void 0) values.before = options.before;
316
+ return values;
317
+ }
318
+ function readChatHydrationPage(value) {
319
+ if (value === null || typeof value !== "object") return { truncated: false };
320
+ if (!("truncated" in value) || value.truncated !== true) return { truncated: false };
321
+ if (!("cursor" in value) || typeof value.cursor !== "string" || value.cursor === "") return { truncated: false };
322
+ return {
323
+ truncated: true,
324
+ cursor: value.cursor
325
+ };
326
+ }
312
327
  /**
313
328
  * GET the hydration endpoint for a thread and parse its JSON `{ messages,
314
329
  * activeRun }` body. This is the transport-agnostic reconnect probe: keyed on
315
330
  * the STABLE thread id, it returns the stored transcript and — if a run is still
316
331
  * generating — a cursor the caller tails via `joinRun`. Shared by every fetch/
317
332
  * XHR adapter so the client never has to know which transport is in use.
333
+ *
334
+ * Pass `limit` / `before` to page. Old servers that omit `page` are treated as
335
+ * a full list (`truncated: false`).
318
336
  */
319
- async function fetchThreadHydration(fetchClient, url, headers, credentials, threadId) {
320
- const response = await fetchClient(withSearchParams(url, { threadId }), {
337
+ async function fetchThreadHydration(fetchClient, url, headers, credentials, threadId, options) {
338
+ const response = await fetchClient(withSearchParams(url, chatHydrationSearchParams(threadId, options)), {
321
339
  method: "GET",
322
340
  headers: {
323
341
  Accept: "application/json",
@@ -335,7 +353,8 @@ async function fetchThreadHydration(fetchClient, url, headers, credentials, thre
335
353
  return {
336
354
  messages: Array.isArray(data.messages) ? normalizeMessagesDates(data.messages) : [],
337
355
  activeRun,
338
- interrupts
356
+ interrupts,
357
+ page: readChatHydrationPage(data.page)
339
358
  };
340
359
  }
341
360
  /**
@@ -559,7 +578,7 @@ function normalizeConnectionAdapter(connection) {
559
578
  ...typeof connection.joinRun === "function" ? { joinRun: (runId, abortSignal) => connection.joinRun(runId, abortSignal) } : {},
560
579
  ...(() => {
561
580
  const hydrate = connection.hydrate;
562
- return typeof hydrate === "function" ? { hydrate: (threadId) => hydrate(threadId) } : {};
581
+ return typeof hydrate === "function" ? { hydrate: (threadId, options) => hydrate(threadId, options) } : {};
563
582
  })()
564
583
  };
565
584
  }
@@ -657,10 +676,10 @@ function fetchServerSentEvents(url, options = {}) {
657
676
  credentials: resolvedOptions.credentials || "same-origin"
658
677
  }, (response, sseSignal) => responseToSSEEvents(response, sseSignal, { runId })), signal, resolvedOptions.reconnect);
659
678
  },
660
- async hydrate(threadId) {
679
+ async hydrate(threadId, hydrateOptions) {
661
680
  const resolvedUrl = typeof url === "function" ? url() : url;
662
681
  const resolvedOptions = typeof options === "function" ? await options() : options;
663
- return fetchThreadHydration(resolvedOptions.fetchClient ?? fetch, resolvedUrl, mergeHeaders(resolvedOptions.headers), resolvedOptions.credentials || "same-origin", threadId);
682
+ return fetchThreadHydration(resolvedOptions.fetchClient ?? fetch, resolvedUrl, mergeHeaders(resolvedOptions.headers), resolvedOptions.credentials || "same-origin", threadId, hydrateOptions);
664
683
  },
665
684
  async hydrateGeneration(threadId) {
666
685
  const resolvedUrl = typeof url === "function" ? url() : url;
@@ -740,10 +759,10 @@ function fetchHttpStream(url, options = {}) {
740
759
  credentials: resolvedOptions.credentials || "same-origin"
741
760
  }, responseToNdjsonEvents), signal, resolvedOptions.reconnect);
742
761
  },
743
- async hydrate(threadId) {
762
+ async hydrate(threadId, hydrateOptions) {
744
763
  const resolvedUrl = typeof url === "function" ? url() : url;
745
764
  const resolvedOptions = typeof options === "function" ? await options() : options;
746
- return fetchThreadHydration(resolvedOptions.fetchClient ?? fetch, resolvedUrl, mergeHeaders(resolvedOptions.headers), resolvedOptions.credentials || "same-origin", threadId);
765
+ return fetchThreadHydration(resolvedOptions.fetchClient ?? fetch, resolvedUrl, mergeHeaders(resolvedOptions.headers), resolvedOptions.credentials || "same-origin", threadId, hydrateOptions);
747
766
  },
748
767
  async hydrateGeneration(threadId) {
749
768
  const resolvedUrl = typeof url === "function" ? url() : url;
@@ -921,10 +940,10 @@ function xhrServerSentEvents(url, options = {}) {
921
940
  runId
922
941
  }), resolvedOptions, "GET", [], void 0, void 0, (lines) => linesToSSEEvents(lines, { runId })), signal, resolvedOptions.reconnect);
923
942
  },
924
- async hydrate(threadId) {
943
+ async hydrate(threadId, hydrateOptions) {
925
944
  const resolvedUrl = typeof url === "function" ? url() : url;
926
945
  const resolvedOptions = await resolveXhrConnectionOptions(options);
927
- return fetchThreadHydration(fetch, resolvedUrl, mergeHeaders(resolvedOptions.headers), resolvedOptions.withCredentials ? "include" : "same-origin", threadId);
946
+ return fetchThreadHydration(fetch, resolvedUrl, mergeHeaders(resolvedOptions.headers), resolvedOptions.withCredentials ? "include" : "same-origin", threadId, hydrateOptions);
928
947
  },
929
948
  async hydrateGeneration(threadId) {
930
949
  const resolvedUrl = typeof url === "function" ? url() : url;
@@ -958,10 +977,10 @@ function xhrHttpStream(url, options = {}) {
958
977
  runId
959
978
  }), resolvedOptions, "GET", [], void 0, void 0, linesToNdjsonEvents), signal, resolvedOptions.reconnect);
960
979
  },
961
- async hydrate(threadId) {
980
+ async hydrate(threadId, hydrateOptions) {
962
981
  const resolvedUrl = typeof url === "function" ? url() : url;
963
982
  const resolvedOptions = await resolveXhrConnectionOptions(options);
964
- return fetchThreadHydration(fetch, resolvedUrl, mergeHeaders(resolvedOptions.headers), resolvedOptions.withCredentials ? "include" : "same-origin", threadId);
983
+ return fetchThreadHydration(fetch, resolvedUrl, mergeHeaders(resolvedOptions.headers), resolvedOptions.withCredentials ? "include" : "same-origin", threadId, hydrateOptions);
965
984
  },
966
985
  async hydrateGeneration(threadId) {
967
986
  const resolvedUrl = typeof url === "function" ? url() : url;