@tanstack/ai-client 0.33.2 → 0.34.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/dist/esm/chat-client.d.ts +9 -1
- package/dist/esm/chat-client.js +92 -3
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/types.d.ts +84 -7
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/ui/selectors.js +2 -1
- package/dist/esm/ui/selectors.js.map +1 -1
- package/dist/esm/ui/types.d.ts +53 -10
- package/dist/esm/ui.d.ts +1 -1
- package/package.json +2 -2
- package/src/chat-client.ts +139 -2
- package/src/index.ts +7 -0
- package/src/types.ts +122 -6
- package/src/ui/selectors.ts +1 -0
- package/src/ui/types.ts +119 -11
- package/src/ui.ts +7 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AnyClientTool, InterruptDefinition, ModelMessage, RunAgentResumeItem, StreamChunk } from '@tanstack/ai/client';
|
|
2
2
|
import { ByokClient } from './byok.js';
|
|
3
3
|
import { ConnectionAdapter } from './connection-adapters.js';
|
|
4
|
-
import { BoundInterrupts, ChatClientOptions, ChatClientState, ChatFetcher, ChatInterruptState, ResolvableChatInterrupt, ChatResumeState, ConnectionStatus, MultimodalContent, QueueOption, QueueStrategy, QueuedMessage, SendMessageOptions, UIMessage, WhenBusy } from './types.js';
|
|
4
|
+
import { BoundInterrupts, ChatClientOptions, ChatClientState, ChatFetcher, ChatInterruptState, ResolvableChatInterrupt, ChatResumeState, ConnectionStatus, MultimodalContent, QueueOption, QueueStrategy, QueuedMessage, SendMessageOptions, SubagentHandle, UIMessage, WhenBusy } from './types.js';
|
|
5
5
|
type ChatClientUpdateOptionsWithoutContext<TTools extends ReadonlyArray<AnyClientTool>, TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> = readonly []> = {
|
|
6
6
|
connection?: ConnectionAdapter;
|
|
7
7
|
fetcher?: ChatFetcher;
|
|
@@ -51,6 +51,8 @@ export interface NormalizedQueueConfig {
|
|
|
51
51
|
export declare function normalizeQueueOption(option: QueueOption | undefined): NormalizedQueueConfig;
|
|
52
52
|
export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = any, TContext = unknown, TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> = any> {
|
|
53
53
|
private readonly processor;
|
|
54
|
+
private readonly subagentHandles;
|
|
55
|
+
private readonly stoppedSubagentIds;
|
|
54
56
|
private connection;
|
|
55
57
|
private uniqueId;
|
|
56
58
|
private threadId;
|
|
@@ -484,6 +486,12 @@ export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = an
|
|
|
484
486
|
* Get current messages
|
|
485
487
|
*/
|
|
486
488
|
getMessages(): Array<UIMessage<TTools>>;
|
|
489
|
+
getSubagents(): SubagentHandle[];
|
|
490
|
+
private devtoolsSnapshotQueued;
|
|
491
|
+
/** One devtools snapshot per microtask, however many chunks arrive. */
|
|
492
|
+
private queueDevtoolsSnapshot;
|
|
493
|
+
private syncSubagentHandles;
|
|
494
|
+
private stopSubagent;
|
|
487
495
|
/**
|
|
488
496
|
* True when the last hydrate or older-page response said more messages exist.
|
|
489
497
|
*/
|
package/dist/esm/chat-client.js
CHANGED
|
@@ -6,7 +6,7 @@ import { fetcherToConnectionAdapter, getChunkRunId as getChunkRunId$1, normalize
|
|
|
6
6
|
import { ChatPersistor } from "./client-persistor.js";
|
|
7
7
|
import { ClearedStreamTracker } from "./cleared-stream-tracker.js";
|
|
8
8
|
import { InterruptManager } from "./interrupt-manager.js";
|
|
9
|
-
import { StreamProcessor, convertSchemaToJsonSchema, generateMessageId, isStandardSchema, mergeMetadata, normalizeToUIMessage, parseWithStandardSchema, restoreInboundChunk, tanstackMetadata } from "@tanstack/ai/client";
|
|
9
|
+
import { EventType, StreamProcessor, convertSchemaToJsonSchema, generateMessageId, isStandardSchema, mergeMetadata, normalizeToUIMessage, parseWithStandardSchema, restoreInboundChunk, tanstackMetadata } from "@tanstack/ai/client";
|
|
10
10
|
import { ByokBlockedError, ByokMissingError, ByokUnresolvedProviderError } from "@tanstack/ai/byok";
|
|
11
11
|
//#region src/chat-client.ts
|
|
12
12
|
function assertUniqueInterruptDefinitions(interrupts) {
|
|
@@ -139,11 +139,35 @@ var REJOIN_REBUILD_TRIGGERS = /* @__PURE__ */ new Set([
|
|
|
139
139
|
"TEXT_MESSAGE_START",
|
|
140
140
|
"TEXT_MESSAGE_CONTENT",
|
|
141
141
|
"TOOL_CALL_START",
|
|
142
|
-
"MESSAGES_SNAPSHOT"
|
|
142
|
+
"MESSAGES_SNAPSHOT",
|
|
143
|
+
"SUBAGENT_STARTED"
|
|
143
144
|
]);
|
|
145
|
+
/** Every subagent card in the messages, nested cards included. */
|
|
146
|
+
function collectSubagentParts(messages) {
|
|
147
|
+
const cards = [];
|
|
148
|
+
for (const message of messages) for (const part of message.parts) {
|
|
149
|
+
if (part.type !== "subagent") continue;
|
|
150
|
+
cards.push(part);
|
|
151
|
+
cards.push(...collectSubagentParts(part.subagent.messages));
|
|
152
|
+
}
|
|
153
|
+
return cards;
|
|
154
|
+
}
|
|
155
|
+
function readSubagentRunId(chunk) {
|
|
156
|
+
if ("subagentRunId" in chunk && typeof chunk.subagentRunId === "string") return chunk.subagentRunId;
|
|
157
|
+
}
|
|
144
158
|
var MAX_RUN_LINEAGE_DEPTH = 64;
|
|
159
|
+
function copyForDevtools(value) {
|
|
160
|
+
if (Array.isArray(value)) return value.map(copyForDevtools);
|
|
161
|
+
if (value instanceof Date) return new Date(value.getTime());
|
|
162
|
+
if (value === null || typeof value !== "object") return value;
|
|
163
|
+
const out = {};
|
|
164
|
+
for (const [key, item] of Object.entries(value)) if (typeof item !== "function") out[key] = copyForDevtools(item);
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
145
167
|
var ChatClient = class {
|
|
146
168
|
processor;
|
|
169
|
+
subagentHandles = /* @__PURE__ */ new Map();
|
|
170
|
+
stoppedSubagentIds = /* @__PURE__ */ new Set();
|
|
147
171
|
connection;
|
|
148
172
|
uniqueId;
|
|
149
173
|
threadId;
|
|
@@ -321,8 +345,10 @@ var ChatClient = class {
|
|
|
321
345
|
...initialMessages ? { initialMessages } : {},
|
|
322
346
|
events: {
|
|
323
347
|
onMessagesChange: (messages) => {
|
|
348
|
+
this.syncSubagentHandles();
|
|
324
349
|
this.persistor?.notifyMessagesChanged(messages);
|
|
325
350
|
this.callbacksRef.current.onMessagesChange(messages);
|
|
351
|
+
this.queueDevtoolsSnapshot();
|
|
326
352
|
},
|
|
327
353
|
onStreamStart: () => {
|
|
328
354
|
this.setStatus("streaming");
|
|
@@ -414,6 +440,7 @@ var ChatClient = class {
|
|
|
414
440
|
}
|
|
415
441
|
}
|
|
416
442
|
});
|
|
443
|
+
this.syncSubagentHandles();
|
|
417
444
|
this.persistor?.hydrateAsync(persistedState);
|
|
418
445
|
this.rejoinRunId = rejoinRunId;
|
|
419
446
|
this.cachesMessages = cachesMessages;
|
|
@@ -882,7 +909,7 @@ var ChatClient = class {
|
|
|
882
909
|
}
|
|
883
910
|
getDevtoolsSnapshot() {
|
|
884
911
|
return {
|
|
885
|
-
messages: this.processor.getMessages(),
|
|
912
|
+
messages: copyForDevtools(this.processor.getMessages()),
|
|
886
913
|
status: this.status,
|
|
887
914
|
isLoading: this.isLoading,
|
|
888
915
|
isSubscribed: this.isSubscribed,
|
|
@@ -1061,7 +1088,15 @@ var ChatClient = class {
|
|
|
1061
1088
|
}
|
|
1062
1089
|
this.callbacksRef.current.onChunk(chunk);
|
|
1063
1090
|
this.devtoolsBridge.observeChunk(chunk);
|
|
1091
|
+
const attributedId = readSubagentRunId(chunk);
|
|
1092
|
+
if (attributedId && chunk.type === EventType.SUBAGENT_STARTED) this.stoppedSubagentIds.delete(attributedId);
|
|
1093
|
+
if (attributedId && this.stoppedSubagentIds.has(attributedId)) {
|
|
1094
|
+
this.updateRunLifecycle(chunk);
|
|
1095
|
+
this.resolveJoinedRun(chunk);
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1064
1098
|
this.processor.processChunk(chunk);
|
|
1099
|
+
this.syncSubagentHandles();
|
|
1065
1100
|
this.updateRunLifecycle(chunk);
|
|
1066
1101
|
this.observeInterruptState(chunk);
|
|
1067
1102
|
if (options?.defer !== false && (typeof document === "undefined" || !document.hidden)) await new Promise((resolve) => setTimeout(resolve, 0));
|
|
@@ -1502,6 +1537,7 @@ var ChatClient = class {
|
|
|
1502
1537
|
this.resetHistoryPaging();
|
|
1503
1538
|
this.discardPendingSends();
|
|
1504
1539
|
this.persistor?.remove();
|
|
1540
|
+
this.stoppedSubagentIds.clear();
|
|
1505
1541
|
this.lastResume = null;
|
|
1506
1542
|
this.interruptManager.reset();
|
|
1507
1543
|
this.pendingResumeThreadId = null;
|
|
@@ -1639,6 +1675,59 @@ var ChatClient = class {
|
|
|
1639
1675
|
getMessages() {
|
|
1640
1676
|
return this.processor.getMessages();
|
|
1641
1677
|
}
|
|
1678
|
+
getSubagents() {
|
|
1679
|
+
return [...this.subagentHandles.values()];
|
|
1680
|
+
}
|
|
1681
|
+
devtoolsSnapshotQueued = false;
|
|
1682
|
+
/** One devtools snapshot per microtask, however many chunks arrive. */
|
|
1683
|
+
queueDevtoolsSnapshot() {
|
|
1684
|
+
if (this.devtoolsSnapshotQueued) return;
|
|
1685
|
+
this.devtoolsSnapshotQueued = true;
|
|
1686
|
+
queueMicrotask(() => {
|
|
1687
|
+
this.devtoolsSnapshotQueued = false;
|
|
1688
|
+
this.devtoolsBridge.emitSnapshot();
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
syncSubagentHandles() {
|
|
1692
|
+
const cards = collectSubagentParts(this.processor.getMessages());
|
|
1693
|
+
const present = new Set(cards.map((part) => part.subagent.id));
|
|
1694
|
+
for (const id of this.subagentHandles.keys()) if (!present.has(id)) this.subagentHandles.delete(id);
|
|
1695
|
+
for (const part of cards) {
|
|
1696
|
+
const id = part.subagent.id;
|
|
1697
|
+
const existing = this.subagentHandles.get(id);
|
|
1698
|
+
if (existing) {
|
|
1699
|
+
if (part.subagent === existing) continue;
|
|
1700
|
+
const { stop } = existing;
|
|
1701
|
+
for (const key of Object.keys(existing)) Reflect.deleteProperty(existing, key);
|
|
1702
|
+
Object.assign(existing, part.subagent, { stop });
|
|
1703
|
+
part.subagent = existing;
|
|
1704
|
+
continue;
|
|
1705
|
+
}
|
|
1706
|
+
const handle = {
|
|
1707
|
+
...part.subagent,
|
|
1708
|
+
stop: () => {
|
|
1709
|
+
this.stopSubagent(id);
|
|
1710
|
+
}
|
|
1711
|
+
};
|
|
1712
|
+
this.subagentHandles.set(id, handle);
|
|
1713
|
+
part.subagent = handle;
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
stopSubagent(id) {
|
|
1717
|
+
const handle = this.subagentHandles.get(id);
|
|
1718
|
+
if (!handle || handle.status !== "running") return;
|
|
1719
|
+
this.stoppedSubagentIds.add(id);
|
|
1720
|
+
this.processor.processChunk({
|
|
1721
|
+
type: EventType.SUBAGENT_ERROR,
|
|
1722
|
+
subagentRunId: id,
|
|
1723
|
+
message: "Stopped",
|
|
1724
|
+
timestamp: Date.now()
|
|
1725
|
+
});
|
|
1726
|
+
this.syncSubagentHandles();
|
|
1727
|
+
handle.status = "error";
|
|
1728
|
+
handle.error = { message: "Stopped" };
|
|
1729
|
+
this.abortController?.abort();
|
|
1730
|
+
}
|
|
1642
1731
|
/**
|
|
1643
1732
|
* True when the last hydrate or older-page response said more messages exist.
|
|
1644
1733
|
*/
|