@tanstack/ai-client 0.29.0 → 0.29.2

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.
@@ -123,6 +123,12 @@ export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = an
123
123
  private continuationPending;
124
124
  private subscriptionAbortController;
125
125
  private processingResolve;
126
+ /**
127
+ * `connect()` adapters push the full HTTP body into the subscribe queue, then
128
+ * wait until that queue is idle. After `send()` returns, every chunk from this
129
+ * request has been processed. Subscribe/send sockets do not drain that way.
130
+ */
131
+ private connectionDrainsOnSend;
126
132
  private errorReportedGeneration;
127
133
  private streamGeneration;
128
134
  private continuationGeneration;
@@ -22,6 +22,16 @@ function resolveTransport(transport) {
22
22
  if (fetcher) return fetcherToConnectionAdapter(fetcher);
23
23
  throw new Error("ChatClient: either `connection` or `fetcher` is required.");
24
24
  }
25
+ function connectionDrainsOnSend(connection) {
26
+ return "connect" in connection;
27
+ }
28
+ function isIntermediateToolTurn(chunk) {
29
+ if (chunk.type !== "RUN_FINISHED") return false;
30
+ if (chunk.outcome?.type === "interrupt") return false;
31
+ const extra = chunk;
32
+ if (extra.finishReason !== void 0) return extra.finishReason === "tool_calls";
33
+ return tanstackMetadata(chunk)?.finishReason === "tool_calls";
34
+ }
25
35
  function normalizeQueueOption(option) {
26
36
  const base = {
27
37
  whenBusy: "queue",
@@ -204,6 +214,12 @@ var ChatClient = class {
204
214
  continuationPending = false;
205
215
  subscriptionAbortController = null;
206
216
  processingResolve = null;
217
+ /**
218
+ * `connect()` adapters push the full HTTP body into the subscribe queue, then
219
+ * wait until that queue is idle. After `send()` returns, every chunk from this
220
+ * request has been processed. Subscribe/send sockets do not drain that way.
221
+ */
222
+ connectionDrainsOnSend = false;
207
223
  errorReportedGeneration = null;
208
224
  streamGeneration = 0;
209
225
  continuationGeneration = 0;
@@ -239,7 +255,9 @@ var ChatClient = class {
239
255
  this.byokProvider = options.byokProvider;
240
256
  this.context = options.context;
241
257
  this.queueConfig = normalizeQueueOption(options.queue);
242
- this.connection = normalizeConnectionAdapter(resolveTransport(options));
258
+ const transport = resolveTransport(options);
259
+ this.connectionDrainsOnSend = connectionDrainsOnSend(transport);
260
+ this.connection = normalizeConnectionAdapter(transport);
243
261
  this.clientToolsRef = { current: /* @__PURE__ */ new Map() };
244
262
  if (options.tools) for (const tool of options.tools) this.clientToolsRef.current.set(tool.name, tool);
245
263
  this.devtoolsBridge = (options.devtoolsBridgeFactory ?? createNoOpChatDevtoolsBridge)(this.buildDevtoolsBridgeOptions(options.devtools));
@@ -374,6 +392,8 @@ var ChatClient = class {
374
392
  },
375
393
  onCustomEvent: (eventType, data, context) => {
376
394
  if (eventType === "memory:state") this.devtoolsBridge.recordMemoryState(data);
395
+ if (eventType === "compaction:started" || eventType === "compaction:state" || eventType === "compaction:ended") this.devtoolsBridge.recordCompactionEvent(eventType, data);
396
+ if (eventType === "skills:state") this.devtoolsBridge.recordSkillsState(data);
377
397
  this.callbacksRef.current.onCustomEvent(eventType, data, context);
378
398
  }
379
399
  }
@@ -562,7 +582,8 @@ var ChatClient = class {
562
582
  this.clearedStreamTracker.onSessionRunError();
563
583
  }
564
584
  this.setSessionGenerating(this.activeRunIds.size > 0);
565
- if (options?.resolveProcessing !== false) this.resolveProcessing();
585
+ const skipProcessingResolve = chunk.type === "RUN_FINISHED" && isIntermediateToolTurn(chunk);
586
+ if (options?.resolveProcessing !== false && !skipProcessingResolve) this.resolveProcessing();
566
587
  }
567
588
  /**
568
589
  * Track interrupt state off the stream's terminal events. A RUN_FINISHED with
@@ -1302,6 +1323,7 @@ var ChatClient = class {
1302
1323
  this.devtoolsBridge.emitSnapshot();
1303
1324
  await this.connection.send(messages, mergedBody, signal, runContext);
1304
1325
  if (generation !== this.streamGeneration || signal.aborted) return false;
1326
+ if (this.connectionDrainsOnSend) this.resolveProcessing();
1305
1327
  await processingComplete;
1306
1328
  if (generation !== this.streamGeneration) return false;
1307
1329
  if (this.status === "error") {
@@ -1710,10 +1732,12 @@ var ChatClient = class {
1710
1732
  this.resetSessionGenerating();
1711
1733
  this.setIsSubscribed(false);
1712
1734
  this.setConnectionStatus("disconnected");
1713
- this.connection = normalizeConnectionAdapter(resolveTransport({
1735
+ const transport = resolveTransport({
1714
1736
  connection: options.connection,
1715
1737
  fetcher: options.fetcher
1716
- }));
1738
+ });
1739
+ this.connectionDrainsOnSend = connectionDrainsOnSend(transport);
1740
+ this.connection = normalizeConnectionAdapter(transport);
1717
1741
  if (wasSubscribed) this.subscribe();
1718
1742
  }
1719
1743
  if (options.body !== void 0) this.bodyOption = options.body;