@tanstack/ai-solid 0.24.4 → 0.25.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/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/use-chat.js +6 -2
- package/dist/use-chat.js.map +1 -1
- package/dist/use-web-mcp-tools.d.ts +40 -7
- package/dist/use-web-mcp-tools.js +43 -5
- package/dist/use-web-mcp-tools.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +15 -2
- package/src/use-chat.ts +11 -1
- package/src/use-web-mcp-tools.ts +72 -11
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ChatRequestBody, DeepPartial, QueueConfig, QueueOption, QueueStrategy,
|
|
|
2
2
|
import { useChat } from "./use-chat.js";
|
|
3
3
|
import { createChatHook } from "./create-chat-hook.js";
|
|
4
4
|
import { useByok } from "./use-byok.js";
|
|
5
|
-
import { UseWebMCPToolsOptions, useWebMCPTools } from "./use-web-mcp-tools.js";
|
|
5
|
+
import { UsePageWebMCPToolsOptions, UseRegisterWebMCPToolsOptions, UseWebMCPToolsOptions, usePageWebMCPTools, useRegisterWebMCPTools, useWebMCPTools } from "./use-web-mcp-tools.js";
|
|
6
6
|
import { UseGenerationOptions, UseGenerationReturn, useGeneration } from "./use-generation.js";
|
|
7
7
|
import { UseGenerateImageOptions, UseGenerateImageReturn, useGenerateImage } from "./use-generate-image.js";
|
|
8
8
|
import { UseGenerateAudioOptions, UseGenerateAudioReturn, useGenerateAudio } from "./use-generate-audio.js";
|
|
@@ -11,5 +11,5 @@ import { UseTranscriptionOptions, UseTranscriptionReturn, useTranscription } fro
|
|
|
11
11
|
import { UseSummarizeOptions, UseSummarizeReturn, useSummarize } from "./use-summarize.js";
|
|
12
12
|
import { UseGenerateVideoOptions, UseGenerateVideoReturn, useGenerateVideo } from "./use-generate-video.js";
|
|
13
13
|
import { UseAudioRecorderOptions, UseAudioRecorderReturn, useAudioRecorder } from "./use-audio-recorder.js";
|
|
14
|
-
import { AudioGenerateInput, AudioRecorder, BatchStrategy, ChatClient, ChatClientPersistence, ChatPersistedState, ChatPersistenceOption, ChatStorageAdapter, CompositeStrategy, ConnectConnectionAdapter, ConnectionAdapter, CreateMcpAppBridgeOptions, DurableStreamIncompleteError, FetchConnectionOptions, GENERATION_EVENTS, GenerationClient, GenerationClientState, ImageGenerateInput, ImmediateStrategy, IndexedDBPersistenceOptions, InferChatMessages, InterruptManager, McpAppBridge, PartialJSONParser, PunctuationStrategy, RealtimeClient, RunAgentInputContext, SpeechGenerateInput, StorageUnavailableError, StreamProcessor, StreamReconnectLimitError, StreamTruncatedError, SubscribeConnectionAdapter, SummarizeGenerateInput, TranscriptionGenerateInput, UnsupportedResponseStreamError, VideoGenerateInput, VideoGenerateResult, VideoGenerationClient, VideoStatusInfo, WebSocketConnectionOptions, WebStoragePersistenceOptions, WordBoundaryStrategy, XhrConnectionOptions, clientTools, convertMessagesToModelMessages, createAIDevtoolsGenerationPreview, createChatClientOptions, createMcpAppBridge, defaultJSONParser, fetchHttpStream, fetchServerSentEvents, generateMessageId, indexedDBPersistence, localStoragePersistence, modelMessageToUIMessage, modelMessagesToUIMessages, normalizeToUIMessage, parsePartialJSON, reconstructAudioResult, reconstructImageResult, reconstructSpeechResult, reconstructSummarizeResult, reconstructTranscriptionResult, registerWebMCPTools, rpcStream, sessionStoragePersistence, stream, uiMessageToModelMessages, webSocket, xhrHttpStream, xhrServerSentEvents } from "@tanstack/ai-client";
|
|
15
|
-
export { type AudioGenerateInput, AudioRecorder, BatchStrategy, ChatClient, type ChatClientPersistence, type ChatPersistedState, type ChatPersistenceOption, type ChatRequestBody, type ChatStorageAdapter, CompositeStrategy, type ConnectConnectionAdapter, type ConnectionAdapter, type CreateMcpAppBridgeOptions, type DeepPartial, DurableStreamIncompleteError, type FetchConnectionOptions, GENERATION_EVENTS, GenerationClient, type GenerationClientState, type ImageGenerateInput, ImmediateStrategy, type IndexedDBPersistenceOptions, type InferChatMessages, InterruptManager, type McpAppBridge, PartialJSONParser, PunctuationStrategy, type QueueConfig, type QueueOption, type QueueStrategy, type QueuedMessage, RealtimeClient, type RunAgentInputContext, type SendMessageOptions, type SpeechGenerateInput, StorageUnavailableError, StreamProcessor, StreamReconnectLimitError, StreamTruncatedError, type SubscribeConnectionAdapter, type SummarizeGenerateInput, type TranscriptionGenerateInput, type UIMessage, UnsupportedResponseStreamError, type UseAudioRecorderOptions, type UseAudioRecorderReturn, type UseChatOptions, type UseChatReturn, type UseGenerateAudioOptions, type UseGenerateAudioReturn, type UseGenerateImageOptions, type UseGenerateImageReturn, type UseGenerateSpeechOptions, type UseGenerateSpeechReturn, type UseGenerateVideoOptions, type UseGenerateVideoReturn, type UseGenerationOptions, type UseGenerationReturn, type UseSummarizeOptions, type UseSummarizeReturn, type UseTranscriptionOptions, type UseTranscriptionReturn, type UseWebMCPToolsOptions, type VideoGenerateInput, type VideoGenerateResult, VideoGenerationClient, type VideoStatusInfo, type WebSocketConnectionOptions, type WebStoragePersistenceOptions, type WhenBusy, WordBoundaryStrategy, type XhrConnectionOptions, clientTools, convertMessagesToModelMessages, createAIDevtoolsGenerationPreview, createChatClientOptions, createChatHook, createMcpAppBridge, defaultJSONParser, fetchHttpStream, fetchServerSentEvents, generateMessageId, indexedDBPersistence, localStoragePersistence, modelMessageToUIMessage, modelMessagesToUIMessages, normalizeToUIMessage, parsePartialJSON, reconstructAudioResult, reconstructImageResult, reconstructSpeechResult, reconstructSummarizeResult, reconstructTranscriptionResult, registerWebMCPTools, rpcStream, sessionStoragePersistence, stream, uiMessageToModelMessages, useAudioRecorder, useByok, useChat, useGenerateAudio, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, useSummarize, useTranscription, useWebMCPTools, webSocket, xhrHttpStream, xhrServerSentEvents };
|
|
14
|
+
import { AudioGenerateInput, AudioRecorder, BatchStrategy, ChatClient, ChatClientPersistence, ChatPersistedState, ChatPersistenceOption, ChatStorageAdapter, CompositeStrategy, ConnectConnectionAdapter, ConnectionAdapter, CreateMcpAppBridgeOptions, DurableStreamIncompleteError, FetchConnectionOptions, GENERATION_EVENTS, GenerationClient, GenerationClientState, GetWebMCPToolsOptions, ImageGenerateInput, ImmediateStrategy, IndexedDBPersistenceOptions, InferChatMessages, InterruptManager, McpAppBridge, PartialJSONParser, PunctuationStrategy, RealtimeClient, RunAgentInputContext, SpeechGenerateInput, StorageUnavailableError, StreamProcessor, StreamReconnectLimitError, StreamTruncatedError, SubscribeConnectionAdapter, SubscribeWebMCPToolsOptions, SummarizeGenerateInput, TranscriptionGenerateInput, UnsupportedResponseStreamError, VideoGenerateInput, VideoGenerateResult, VideoGenerationClient, VideoStatusInfo, WebMCPPageTool, WebSocketConnectionOptions, WebStoragePersistenceOptions, WordBoundaryStrategy, XhrConnectionOptions, clientTools, convertMessagesToModelMessages, createAIDevtoolsGenerationPreview, createChatClientOptions, createMcpAppBridge, defaultJSONParser, fetchHttpStream, fetchServerSentEvents, generateMessageId, getWebMCPTools, indexedDBPersistence, localStoragePersistence, modelMessageToUIMessage, modelMessagesToUIMessages, normalizeToUIMessage, parsePartialJSON, reconstructAudioResult, reconstructImageResult, reconstructSpeechResult, reconstructSummarizeResult, reconstructTranscriptionResult, registerWebMCPTools, rpcStream, sessionStoragePersistence, stream, subscribeWebMCPTools, uiMessageToModelMessages, webSocket, xhrHttpStream, xhrServerSentEvents } from "@tanstack/ai-client";
|
|
15
|
+
export { type AudioGenerateInput, AudioRecorder, BatchStrategy, ChatClient, type ChatClientPersistence, type ChatPersistedState, type ChatPersistenceOption, type ChatRequestBody, type ChatStorageAdapter, CompositeStrategy, type ConnectConnectionAdapter, type ConnectionAdapter, type CreateMcpAppBridgeOptions, type DeepPartial, DurableStreamIncompleteError, type FetchConnectionOptions, GENERATION_EVENTS, GenerationClient, type GenerationClientState, type GetWebMCPToolsOptions, type ImageGenerateInput, ImmediateStrategy, type IndexedDBPersistenceOptions, type InferChatMessages, InterruptManager, type McpAppBridge, PartialJSONParser, PunctuationStrategy, type QueueConfig, type QueueOption, type QueueStrategy, type QueuedMessage, RealtimeClient, type RunAgentInputContext, type SendMessageOptions, type SpeechGenerateInput, StorageUnavailableError, StreamProcessor, StreamReconnectLimitError, StreamTruncatedError, type SubscribeConnectionAdapter, type SubscribeWebMCPToolsOptions, type SummarizeGenerateInput, type TranscriptionGenerateInput, type UIMessage, UnsupportedResponseStreamError, type UseAudioRecorderOptions, type UseAudioRecorderReturn, type UseChatOptions, type UseChatReturn, type UseGenerateAudioOptions, type UseGenerateAudioReturn, type UseGenerateImageOptions, type UseGenerateImageReturn, type UseGenerateSpeechOptions, type UseGenerateSpeechReturn, type UseGenerateVideoOptions, type UseGenerateVideoReturn, type UseGenerationOptions, type UseGenerationReturn, type UsePageWebMCPToolsOptions, type UseRegisterWebMCPToolsOptions, type UseSummarizeOptions, type UseSummarizeReturn, type UseTranscriptionOptions, type UseTranscriptionReturn, type UseWebMCPToolsOptions, type VideoGenerateInput, type VideoGenerateResult, VideoGenerationClient, type VideoStatusInfo, type WebMCPPageTool, type WebSocketConnectionOptions, type WebStoragePersistenceOptions, type WhenBusy, WordBoundaryStrategy, type XhrConnectionOptions, clientTools, convertMessagesToModelMessages, createAIDevtoolsGenerationPreview, createChatClientOptions, createChatHook, createMcpAppBridge, defaultJSONParser, fetchHttpStream, fetchServerSentEvents, generateMessageId, getWebMCPTools, indexedDBPersistence, localStoragePersistence, modelMessageToUIMessage, modelMessagesToUIMessages, normalizeToUIMessage, parsePartialJSON, reconstructAudioResult, reconstructImageResult, reconstructSpeechResult, reconstructSummarizeResult, reconstructTranscriptionResult, registerWebMCPTools, rpcStream, sessionStoragePersistence, stream, subscribeWebMCPTools, uiMessageToModelMessages, useAudioRecorder, useByok, useChat, useGenerateAudio, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, usePageWebMCPTools, useRegisterWebMCPTools, useSummarize, useTranscription, useWebMCPTools, webSocket, xhrHttpStream, xhrServerSentEvents };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useChat } from "./use-chat.js";
|
|
2
2
|
import { createChatHook } from "./create-chat-hook.js";
|
|
3
3
|
import { useByok } from "./use-byok.js";
|
|
4
|
-
import { useWebMCPTools } from "./use-web-mcp-tools.js";
|
|
4
|
+
import { usePageWebMCPTools, useRegisterWebMCPTools, useWebMCPTools } from "./use-web-mcp-tools.js";
|
|
5
5
|
import { useGeneration } from "./use-generation.js";
|
|
6
6
|
import { useGenerateImage } from "./use-generate-image.js";
|
|
7
7
|
import { useGenerateAudio } from "./use-generate-audio.js";
|
|
@@ -10,6 +10,6 @@ import { useTranscription } from "./use-transcription.js";
|
|
|
10
10
|
import { useSummarize } from "./use-summarize.js";
|
|
11
11
|
import { useGenerateVideo } from "./use-generate-video.js";
|
|
12
12
|
import { useAudioRecorder } from "./use-audio-recorder.js";
|
|
13
|
-
import { AudioRecorder, BatchStrategy, ChatClient, CompositeStrategy, DurableStreamIncompleteError, GENERATION_EVENTS, GenerationClient, ImmediateStrategy, InterruptManager, PartialJSONParser, PunctuationStrategy, RealtimeClient, StorageUnavailableError, StreamProcessor, StreamReconnectLimitError, StreamTruncatedError, UnsupportedResponseStreamError, VideoGenerationClient, WordBoundaryStrategy, clientTools, convertMessagesToModelMessages, createAIDevtoolsGenerationPreview, createChatClientOptions, createMcpAppBridge, defaultJSONParser, fetchHttpStream, fetchServerSentEvents, generateMessageId, indexedDBPersistence, localStoragePersistence, modelMessageToUIMessage, modelMessagesToUIMessages, normalizeToUIMessage, parsePartialJSON, reconstructAudioResult, reconstructImageResult, reconstructSpeechResult, reconstructSummarizeResult, reconstructTranscriptionResult, registerWebMCPTools, rpcStream, sessionStoragePersistence, stream, uiMessageToModelMessages, webSocket, xhrHttpStream, xhrServerSentEvents } from "@tanstack/ai-client";
|
|
13
|
+
import { AudioRecorder, BatchStrategy, ChatClient, CompositeStrategy, DurableStreamIncompleteError, GENERATION_EVENTS, GenerationClient, ImmediateStrategy, InterruptManager, PartialJSONParser, PunctuationStrategy, RealtimeClient, StorageUnavailableError, StreamProcessor, StreamReconnectLimitError, StreamTruncatedError, UnsupportedResponseStreamError, VideoGenerationClient, WordBoundaryStrategy, clientTools, convertMessagesToModelMessages, createAIDevtoolsGenerationPreview, createChatClientOptions, createMcpAppBridge, defaultJSONParser, fetchHttpStream, fetchServerSentEvents, generateMessageId, getWebMCPTools, indexedDBPersistence, localStoragePersistence, modelMessageToUIMessage, modelMessagesToUIMessages, normalizeToUIMessage, parsePartialJSON, reconstructAudioResult, reconstructImageResult, reconstructSpeechResult, reconstructSummarizeResult, reconstructTranscriptionResult, registerWebMCPTools, rpcStream, sessionStoragePersistence, stream, subscribeWebMCPTools, uiMessageToModelMessages, webSocket, xhrHttpStream, xhrServerSentEvents } from "@tanstack/ai-client";
|
|
14
14
|
|
|
15
|
-
export { AudioRecorder, BatchStrategy, ChatClient, CompositeStrategy, DurableStreamIncompleteError, GENERATION_EVENTS, GenerationClient, ImmediateStrategy, InterruptManager, PartialJSONParser, PunctuationStrategy, RealtimeClient, StorageUnavailableError, StreamProcessor, StreamReconnectLimitError, StreamTruncatedError, UnsupportedResponseStreamError, VideoGenerationClient, WordBoundaryStrategy, clientTools, convertMessagesToModelMessages, createAIDevtoolsGenerationPreview, createChatClientOptions, createChatHook, createMcpAppBridge, defaultJSONParser, fetchHttpStream, fetchServerSentEvents, generateMessageId, indexedDBPersistence, localStoragePersistence, modelMessageToUIMessage, modelMessagesToUIMessages, normalizeToUIMessage, parsePartialJSON, reconstructAudioResult, reconstructImageResult, reconstructSpeechResult, reconstructSummarizeResult, reconstructTranscriptionResult, registerWebMCPTools, rpcStream, sessionStoragePersistence, stream, uiMessageToModelMessages, useAudioRecorder, useByok, useChat, useGenerateAudio, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, useSummarize, useTranscription, useWebMCPTools, webSocket, xhrHttpStream, xhrServerSentEvents };
|
|
15
|
+
export { AudioRecorder, BatchStrategy, ChatClient, CompositeStrategy, DurableStreamIncompleteError, GENERATION_EVENTS, GenerationClient, ImmediateStrategy, InterruptManager, PartialJSONParser, PunctuationStrategy, RealtimeClient, StorageUnavailableError, StreamProcessor, StreamReconnectLimitError, StreamTruncatedError, UnsupportedResponseStreamError, VideoGenerationClient, WordBoundaryStrategy, clientTools, convertMessagesToModelMessages, createAIDevtoolsGenerationPreview, createChatClientOptions, createChatHook, createMcpAppBridge, defaultJSONParser, fetchHttpStream, fetchServerSentEvents, generateMessageId, getWebMCPTools, indexedDBPersistence, localStoragePersistence, modelMessageToUIMessage, modelMessagesToUIMessages, normalizeToUIMessage, parsePartialJSON, reconstructAudioResult, reconstructImageResult, reconstructSpeechResult, reconstructSummarizeResult, reconstructTranscriptionResult, registerWebMCPTools, rpcStream, sessionStoragePersistence, stream, subscribeWebMCPTools, uiMessageToModelMessages, useAudioRecorder, useByok, useChat, useGenerateAudio, useGenerateImage, useGenerateSpeech, useGenerateVideo, useGeneration, usePageWebMCPTools, useRegisterWebMCPTools, useSummarize, useTranscription, useWebMCPTools, webSocket, xhrHttpStream, xhrServerSentEvents };
|
package/dist/use-chat.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createEffect, createMemo, createSignal, createUniqueId, onCleanup, onMount } from "solid-js";
|
|
1
|
+
import { createEffect, createMemo, createSignal, createUniqueId, onCleanup, onMount, untrack } from "solid-js";
|
|
2
2
|
import { ChatClient } from "@tanstack/ai-client";
|
|
3
3
|
import { createChatDevtoolsBridge } from "@tanstack/ai-client/devtools";
|
|
4
4
|
|
|
@@ -63,7 +63,7 @@ function useChat(options = {}) {
|
|
|
63
63
|
onError: (err) => {
|
|
64
64
|
options.onError?.(err);
|
|
65
65
|
},
|
|
66
|
-
tools: options.tools,
|
|
66
|
+
tools: untrack(() => options.tools),
|
|
67
67
|
...options.interrupts !== void 0 && { interrupts: options.interrupts },
|
|
68
68
|
onCustomEvent: (eventType, data, context) => options.onCustomEvent?.(eventType, data, context),
|
|
69
69
|
...options.streamProcessor !== void 0 && { streamProcessor: options.streamProcessor },
|
|
@@ -122,6 +122,10 @@ function useChat(options = {}) {
|
|
|
122
122
|
...options.queue !== void 0 && { queue: options.queue }
|
|
123
123
|
});
|
|
124
124
|
});
|
|
125
|
+
createEffect(() => {
|
|
126
|
+
const tools = options.tools;
|
|
127
|
+
if (tools !== void 0) client().updateOptions({ tools });
|
|
128
|
+
});
|
|
125
129
|
if (options.live) client().subscribe();
|
|
126
130
|
else client().unsubscribe();
|
|
127
131
|
createEffect(() => {
|
package/dist/use-chat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-chat.js","names":[],"sources":["../src/use-chat.ts"],"sourcesContent":["import {\n createEffect,\n createMemo,\n createSignal,\n createUniqueId,\n onCleanup,\n onMount,\n} from 'solid-js'\n\nimport { ChatClient } from '@tanstack/ai-client'\nimport { createChatDevtoolsBridge } from '@tanstack/ai-client/devtools'\nimport type {\n ChatClientState,\n ResolvableChatInterrupt,\n ChatInterruptState,\n ChatResumeState,\n ConnectionStatus,\n InferredClientContext,\n QueuedMessage,\n SendMessageOptions,\n StructuredOutputPart,\n} from '@tanstack/ai-client'\nimport type {\n AnyClientTool,\n InterruptDefinition,\n InferSchemaType,\n ModelMessage,\n RunAgentResumeItem,\n SchemaInput,\n StreamChunk,\n} from '@tanstack/ai'\nimport type {\n DeepPartial,\n MultimodalContent,\n UIMessage,\n UseChatOptions,\n UseChatReturn,\n} from './types'\n\nconst EMPTY_INTERRUPTS = Object.freeze([])\nconst EMPTY_INTERRUPT_ERRORS = Object.freeze([])\n\nexport function useChat<\n const TTools extends ReadonlyArray<AnyClientTool> = any,\n TSchema extends SchemaInput | undefined = undefined,\n TContext = InferredClientContext<TTools>,\n const TInterrupts extends ReadonlyArray<\n InterruptDefinition<any, any, any, any>\n > = readonly [],\n>(\n options: UseChatOptions<\n TTools,\n TSchema,\n TContext,\n TInterrupts\n > = {} as UseChatOptions<TTools, TSchema, TContext, TInterrupts>,\n): UseChatReturn<TTools, TSchema, TInterrupts> {\n // The hook's identity is its `threadId`. Reload with the same `threadId`\n // restores the same conversation. `hookId` is only a recreation key when no\n // `threadId` is given. It is never sent on the wire.\n const hookId = createUniqueId()\n const clientId = options.threadId ?? hookId\n\n const [messages, setMessages] = createSignal<Array<UIMessage<TTools>>>(\n options.initialMessages || [],\n )\n const [isLoading, setIsLoading] = createSignal(false)\n const [hasOlderMessages, setHasOlderMessages] = createSignal(false)\n const [error, setError] = createSignal<Error | undefined>(undefined)\n const [status, setStatus] = createSignal<ChatClientState>('ready')\n const [isSubscribed, setIsSubscribed] = createSignal(false)\n const [connectionStatus, setConnectionStatus] =\n createSignal<ConnectionStatus>('disconnected')\n const [sessionGenerating, setSessionGenerating] = createSignal(false)\n const [queue, setQueue] = createSignal<Array<QueuedMessage>>([])\n const [runId, setRunId] = createSignal<string | null>(null)\n const [interruptState, setInterruptState] = createSignal<\n ChatInterruptState<TTools, TInterrupts>\n >({\n interrupts: EMPTY_INTERRUPTS,\n pendingInterrupts: EMPTY_INTERRUPTS,\n interruptErrors: EMPTY_INTERRUPT_ERRORS,\n resuming: false,\n })\n\n const syncResumeState = () => {\n setRunId(client().getCurrentRunId())\n setInterruptState(client().getInterruptState())\n }\n\n // Structured-output `partial` / `final` are derived from `messages` —\n // specifically from the structured-output part on the latest assistant\n // message (the one after the most recent user message). Per-turn parts\n // keep history coherent without a separate reset signal.\n type Partial = DeepPartial<InferSchemaType<NonNullable<TSchema>>>\n type Final = InferSchemaType<NonNullable<TSchema>>\n\n // Create ChatClient instance with callbacks to sync state.\n // Every user-provided callback is wrapped so the LATEST `options.xxx` value\n // is read at call time. Direct assignment would freeze the callback to the\n // reference we saw at creation; the wrapper lets reactive `options` or\n // in-place mutations propagate. When the user clears a callback (sets it to\n // undefined), `?.` no-ops.\n const client = createMemo(() => {\n // Build options with conditional spreads for fields whose source\n // type is `T | undefined` but the ChatClient target uses a strict\n // optional (`field?: T`) — `exactOptionalPropertyTypes` rejects\n // assigning `undefined` to those, so we omit the key when absent.\n const transport = options.connection\n ? { connection: options.connection }\n : { fetcher: options.fetcher }\n const instance = new ChatClient<TTools, TContext, TInterrupts>({\n devtoolsBridgeFactory: createChatDevtoolsBridge,\n ...transport,\n ...(options.initialMessages !== undefined && {\n initialMessages: options.initialMessages,\n }),\n ...(typeof options.threadId === 'string' && options.persistence === true\n ? {\n persistence: true,\n threadId: options.threadId,\n ...(options.history !== undefined && {\n history: options.history,\n }),\n }\n : typeof options.threadId === 'string' && options.persistence\n ? {\n persistence: options.persistence,\n threadId: options.threadId,\n }\n : {\n ...(options.threadId !== undefined && {\n threadId: options.threadId,\n }),\n }),\n ...(options.initialResumeSnapshot !== undefined && {\n initialResumeSnapshot: options.initialResumeSnapshot,\n }),\n body: options.body,\n ...(options.forwardedProps !== undefined && {\n forwardedProps: options.forwardedProps,\n }),\n ...(options.byok !== undefined && { byok: options.byok }),\n byokProvider: () => options.byokProvider?.(),\n ...(options.context !== undefined && { context: options.context }),\n devtools: {\n ...options.devtools,\n framework: 'solid',\n hookName: 'useChat',\n outputKind: options.outputSchema ? 'structured' : 'chat',\n },\n onResponse: (response) => options.onResponse?.(response),\n onChunk: (chunk: StreamChunk) => {\n options.onChunk?.(chunk)\n },\n onFinish: (message) => {\n options.onFinish?.(message)\n },\n onError: (err) => {\n options.onError?.(err)\n },\n tools: options.tools,\n ...(options.interrupts !== undefined && {\n interrupts: options.interrupts,\n }),\n onCustomEvent: (eventType, data, context) =>\n options.onCustomEvent?.(eventType, data, context),\n ...(options.streamProcessor !== undefined && {\n streamProcessor: options.streamProcessor,\n }),\n onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {\n setMessages(newMessages)\n setHasOlderMessages(instance.getHasOlderMessages())\n },\n onLoadingChange: (newIsLoading: boolean) => {\n setIsLoading(newIsLoading)\n syncResumeState()\n },\n onStatusChange: (newStatus: ChatClientState) => {\n setStatus(newStatus)\n },\n onErrorChange: (newError: Error | undefined) => {\n setError(newError)\n },\n onSubscriptionChange: (nextIsSubscribed: boolean) => {\n setIsSubscribed(nextIsSubscribed)\n },\n onConnectionStatusChange: (nextStatus: ConnectionStatus) => {\n setConnectionStatus(nextStatus)\n },\n onSessionGeneratingChange: (isGenerating: boolean) => {\n setSessionGenerating(isGenerating)\n },\n ...(options.queue !== undefined && { queue: options.queue }),\n onQueueChange: (nextQueue: Array<QueuedMessage>) => {\n setQueue(nextQueue)\n },\n onRunIdChange: (nextRunId) => {\n setRunId(nextRunId)\n },\n onResumeStateChange: (_nextResumeState, nextPendingInterrupts) => {\n setInterruptState((current) => ({\n ...current,\n interrupts: nextPendingInterrupts,\n pendingInterrupts: nextPendingInterrupts,\n }))\n },\n onInterruptStateChange: (nextInterruptState, context) => {\n setInterruptState(nextInterruptState)\n options.onInterruptStateChange?.(nextInterruptState, context)\n },\n })\n // Only recreate when clientId changes\n // Connection and other options are captured at creation time\n return instance\n }, [clientId])\n\n setMessages(client().getMessages())\n setHasOlderMessages(client().getHasOlderMessages())\n syncResumeState()\n\n // Sync body / forwardedProps changes to the client.\n // Both populate the same wire payload; `forwardedProps` is preferred\n // and `body` is deprecated but still supported.\n createEffect(() => {\n // Conditional spread: `updateOptions` declares strict-optional\n // fields and rejects explicit `undefined` under EOPT.\n client().updateOptions({\n ...(options.body !== undefined && { body: options.body }),\n ...(options.forwardedProps !== undefined && {\n forwardedProps: options.forwardedProps,\n }),\n context: options.context,\n ...(options.queue !== undefined && { queue: options.queue }),\n })\n })\n\n // Apply initial live mode immediately on hook creation.\n if (options.live) {\n client().subscribe()\n } else {\n client().unsubscribe()\n }\n\n createEffect(() => {\n if (options.live) {\n client().subscribe()\n } else {\n client().unsubscribe()\n }\n })\n\n onMount(() => {\n // START TAILING HERE, not in the constructor. A client is idle until a view\n // attaches it, so a client that gets built and thrown away never opens a\n // connection — an unreachable stream would hold one of the browser's ~6\n // connections per origin until the page reloaded.\n client().attach()\n client().mountDevtools()\n // Delivery-durability resume is transparent: the resumable SSE connection\n // adapter reattaches via the browser's native Last-Event-ID on reconnect.\n // We only seed interrupt (state) resume from the client here.\n syncResumeState()\n })\n\n // Cleanup on unmount: stop any in-flight requests.\n onCleanup(() => {\n // Release the connection first: `detach` is the counterpart of the `attach`\n // above, and it keeps the transcript and resume pointer so a later mount can\n // pick the run back up from the durable log.\n client().detach()\n if (options.live) {\n client().unsubscribe()\n } else {\n client().stop()\n }\n client().dispose()\n })\n\n // Callback options are read through `options.xxx` at call time, so reactive\n // or mutated options propagate without recreating the client.\n\n const sendMessage = async (\n content: string | MultimodalContent,\n sendOptions?: SendMessageOptions,\n ) => {\n try {\n await client().sendMessage(content, undefined, sendOptions)\n } finally {\n syncResumeState()\n }\n }\n\n const append = async (message: ModelMessage | UIMessage<TTools>) => {\n try {\n await client().append(message)\n } finally {\n syncResumeState()\n }\n }\n\n const reload = async () => {\n try {\n await client().reload()\n } finally {\n syncResumeState()\n }\n }\n\n const loadOlderMessages = async () => {\n await client().loadOlderMessages()\n setHasOlderMessages(client().getHasOlderMessages())\n }\n\n const stop = () => {\n client().stop()\n }\n\n const clear = () => {\n client().clear()\n syncResumeState()\n }\n\n const setMessagesManually = (newMessages: Array<UIMessage<TTools>>) => {\n client().setMessagesManually(newMessages)\n }\n\n const addToolResult = async (result: {\n toolCallId: string\n tool: string\n output: any\n state?: 'output-available' | 'output-error'\n errorText?: string\n }) => {\n await client().addToolResult(result)\n }\n\n const addToolApprovalResponse = async (response: {\n id: string\n approved: boolean\n }) => {\n await client().addToolApprovalResponse(response)\n syncResumeState()\n }\n\n const cancelQueued = (id: string) => client().cancelQueued(id)\n\n const resumeInterrupts = async (\n resumeItems: Array<RunAgentResumeItem>,\n state?: ChatResumeState,\n ) => {\n const result = await client().resumeInterrupts(resumeItems, state)\n syncResumeState()\n return result\n }\n\n const resolveInterrupts = (\n resolution:\n | boolean\n | ((\n interrupt: ResolvableChatInterrupt<TTools, TInterrupts>,\n ) => undefined),\n ) => {\n if (typeof resolution === 'boolean') {\n client().resolveInterrupts(resolution)\n } else {\n client().resolveInterrupts(resolution)\n }\n }\n\n const cancelInterrupts = () => {\n client().cancelInterrupts()\n }\n\n const retryInterrupts = () => {\n client().retryInterrupts()\n }\n\n const resumeInterruptsUnsafe = (\n resumeItems: Array<RunAgentResumeItem>,\n state?: ChatResumeState,\n ) => client().resumeInterruptsUnsafe(resumeItems, state)\n\n const interrupts = () => interruptState().interrupts\n const pendingInterrupts = () => interruptState().pendingInterrupts\n const interruptErrors = () => interruptState().interruptErrors\n const resuming = () => interruptState().resuming\n\n // The \"active\" structured-output part is on the assistant message after\n // the latest user message. When no user message exists yet, return null\n // rather than scanning history — otherwise a stale `final` from\n // `initialMessages` would leak into the value on first render.\n const activeStructuredPart = createMemo<StructuredOutputPart | null>(() => {\n const list = messages()\n let lastUserIndex = -1\n for (let i = list.length - 1; i >= 0; i--) {\n if (list[i]?.role === 'user') {\n lastUserIndex = i\n break\n }\n }\n if (lastUserIndex === -1) return null\n for (let i = list.length - 1; i > lastUserIndex; i--) {\n const m = list[i]\n if (m?.role !== 'assistant') continue\n const part = m.parts.find(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n if (part) return part\n }\n return null\n })\n\n const partial = createMemo<Partial>(() => {\n const part = activeStructuredPart()\n if (!part) return {} as Partial\n const v = part.partial ?? part.data\n return (v ?? {}) as Partial\n })\n\n const final = createMemo<Final | null>(() => {\n const part = activeStructuredPart()\n if (!part || part.status !== 'complete') return null\n return part.data as Final\n })\n\n // partial / final are runtime-tracked unconditionally; the conditional\n // return type hides them when no `outputSchema` is supplied.\n // oxlint-disable-next-line eslint-js/no-restricted-syntax -- primitive return shape diverges from generic UseChatReturn<TTools, TSchema>; TS can't structurally narrow the conditional partial/final fields\n return {\n messages,\n queue,\n sendMessage,\n append,\n reload,\n stop,\n isLoading,\n hasOlderMessages,\n loadOlderMessages,\n error,\n status,\n isSubscribed,\n connectionStatus,\n sessionGenerating,\n setMessages: setMessagesManually,\n clear,\n addToolResult,\n addToolApprovalResponse,\n cancelQueued,\n runId,\n interrupts,\n pendingInterrupts,\n interruptErrors,\n resuming,\n resolveInterrupts,\n cancelInterrupts,\n retryInterrupts,\n resumeInterruptsUnsafe,\n resumeInterrupts,\n partial,\n final,\n } as unknown as UseChatReturn<TTools, TSchema, TInterrupts>\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-chat.js","names":[],"sources":["../src/use-chat.ts"],"sourcesContent":["import {\n createEffect,\n createMemo,\n createSignal,\n createUniqueId,\n onCleanup,\n onMount,\n untrack,\n} from 'solid-js'\n\nimport { ChatClient } from '@tanstack/ai-client'\nimport { createChatDevtoolsBridge } from '@tanstack/ai-client/devtools'\nimport type {\n ChatClientState,\n ResolvableChatInterrupt,\n ChatInterruptState,\n ChatResumeState,\n ConnectionStatus,\n InferredClientContext,\n QueuedMessage,\n SendMessageOptions,\n StructuredOutputPart,\n} from '@tanstack/ai-client'\nimport type {\n AnyClientTool,\n InterruptDefinition,\n InferSchemaType,\n ModelMessage,\n RunAgentResumeItem,\n SchemaInput,\n StreamChunk,\n} from '@tanstack/ai'\nimport type {\n DeepPartial,\n MultimodalContent,\n UIMessage,\n UseChatOptions,\n UseChatReturn,\n} from './types'\n\nconst EMPTY_INTERRUPTS = Object.freeze([])\nconst EMPTY_INTERRUPT_ERRORS = Object.freeze([])\n\nexport function useChat<\n const TTools extends ReadonlyArray<AnyClientTool> = any,\n TSchema extends SchemaInput | undefined = undefined,\n TContext = InferredClientContext<TTools>,\n const TInterrupts extends ReadonlyArray<\n InterruptDefinition<any, any, any, any>\n > = readonly [],\n>(\n options: UseChatOptions<\n TTools,\n TSchema,\n TContext,\n TInterrupts\n > = {} as UseChatOptions<TTools, TSchema, TContext, TInterrupts>,\n): UseChatReturn<TTools, TSchema, TInterrupts> {\n // The hook's identity is its `threadId`. Reload with the same `threadId`\n // restores the same conversation. `hookId` is only a recreation key when no\n // `threadId` is given. It is never sent on the wire.\n const hookId = createUniqueId()\n const clientId = options.threadId ?? hookId\n\n const [messages, setMessages] = createSignal<Array<UIMessage<TTools>>>(\n options.initialMessages || [],\n )\n const [isLoading, setIsLoading] = createSignal(false)\n const [hasOlderMessages, setHasOlderMessages] = createSignal(false)\n const [error, setError] = createSignal<Error | undefined>(undefined)\n const [status, setStatus] = createSignal<ChatClientState>('ready')\n const [isSubscribed, setIsSubscribed] = createSignal(false)\n const [connectionStatus, setConnectionStatus] =\n createSignal<ConnectionStatus>('disconnected')\n const [sessionGenerating, setSessionGenerating] = createSignal(false)\n const [queue, setQueue] = createSignal<Array<QueuedMessage>>([])\n const [runId, setRunId] = createSignal<string | null>(null)\n const [interruptState, setInterruptState] = createSignal<\n ChatInterruptState<TTools, TInterrupts>\n >({\n interrupts: EMPTY_INTERRUPTS,\n pendingInterrupts: EMPTY_INTERRUPTS,\n interruptErrors: EMPTY_INTERRUPT_ERRORS,\n resuming: false,\n })\n\n const syncResumeState = () => {\n setRunId(client().getCurrentRunId())\n setInterruptState(client().getInterruptState())\n }\n\n // Structured-output `partial` / `final` are derived from `messages` —\n // specifically from the structured-output part on the latest assistant\n // message (the one after the most recent user message). Per-turn parts\n // keep history coherent without a separate reset signal.\n type Partial = DeepPartial<InferSchemaType<NonNullable<TSchema>>>\n type Final = InferSchemaType<NonNullable<TSchema>>\n\n // Create ChatClient instance with callbacks to sync state.\n // Every user-provided callback is wrapped so the LATEST `options.xxx` value\n // is read at call time. Direct assignment would freeze the callback to the\n // reference we saw at creation; the wrapper lets reactive `options` or\n // in-place mutations propagate. When the user clears a callback (sets it to\n // undefined), `?.` no-ops.\n const client = createMemo(() => {\n // Build options with conditional spreads for fields whose source\n // type is `T | undefined` but the ChatClient target uses a strict\n // optional (`field?: T`) — `exactOptionalPropertyTypes` rejects\n // assigning `undefined` to those, so we omit the key when absent.\n const transport = options.connection\n ? { connection: options.connection }\n : { fetcher: options.fetcher }\n const instance = new ChatClient<TTools, TContext, TInterrupts>({\n devtoolsBridgeFactory: createChatDevtoolsBridge,\n ...transport,\n ...(options.initialMessages !== undefined && {\n initialMessages: options.initialMessages,\n }),\n ...(typeof options.threadId === 'string' && options.persistence === true\n ? {\n persistence: true,\n threadId: options.threadId,\n ...(options.history !== undefined && {\n history: options.history,\n }),\n }\n : typeof options.threadId === 'string' && options.persistence\n ? {\n persistence: options.persistence,\n threadId: options.threadId,\n }\n : {\n ...(options.threadId !== undefined && {\n threadId: options.threadId,\n }),\n }),\n ...(options.initialResumeSnapshot !== undefined && {\n initialResumeSnapshot: options.initialResumeSnapshot,\n }),\n body: options.body,\n ...(options.forwardedProps !== undefined && {\n forwardedProps: options.forwardedProps,\n }),\n ...(options.byok !== undefined && { byok: options.byok }),\n byokProvider: () => options.byokProvider?.(),\n ...(options.context !== undefined && { context: options.context }),\n devtools: {\n ...options.devtools,\n framework: 'solid',\n hookName: 'useChat',\n outputKind: options.outputSchema ? 'structured' : 'chat',\n },\n onResponse: (response) => options.onResponse?.(response),\n onChunk: (chunk: StreamChunk) => {\n options.onChunk?.(chunk)\n },\n onFinish: (message) => {\n options.onFinish?.(message)\n },\n onError: (err) => {\n options.onError?.(err)\n },\n // Untracked: a tools change must not rebuild the client. The effect\n // below syncs it instead.\n tools: untrack(() => options.tools),\n ...(options.interrupts !== undefined && {\n interrupts: options.interrupts,\n }),\n onCustomEvent: (eventType, data, context) =>\n options.onCustomEvent?.(eventType, data, context),\n ...(options.streamProcessor !== undefined && {\n streamProcessor: options.streamProcessor,\n }),\n onMessagesChange: (newMessages: Array<UIMessage<TTools>>) => {\n setMessages(newMessages)\n setHasOlderMessages(instance.getHasOlderMessages())\n },\n onLoadingChange: (newIsLoading: boolean) => {\n setIsLoading(newIsLoading)\n syncResumeState()\n },\n onStatusChange: (newStatus: ChatClientState) => {\n setStatus(newStatus)\n },\n onErrorChange: (newError: Error | undefined) => {\n setError(newError)\n },\n onSubscriptionChange: (nextIsSubscribed: boolean) => {\n setIsSubscribed(nextIsSubscribed)\n },\n onConnectionStatusChange: (nextStatus: ConnectionStatus) => {\n setConnectionStatus(nextStatus)\n },\n onSessionGeneratingChange: (isGenerating: boolean) => {\n setSessionGenerating(isGenerating)\n },\n ...(options.queue !== undefined && { queue: options.queue }),\n onQueueChange: (nextQueue: Array<QueuedMessage>) => {\n setQueue(nextQueue)\n },\n onRunIdChange: (nextRunId) => {\n setRunId(nextRunId)\n },\n onResumeStateChange: (_nextResumeState, nextPendingInterrupts) => {\n setInterruptState((current) => ({\n ...current,\n interrupts: nextPendingInterrupts,\n pendingInterrupts: nextPendingInterrupts,\n }))\n },\n onInterruptStateChange: (nextInterruptState, context) => {\n setInterruptState(nextInterruptState)\n options.onInterruptStateChange?.(nextInterruptState, context)\n },\n })\n // Only recreate when clientId changes\n // Connection and other options are captured at creation time\n return instance\n }, [clientId])\n\n setMessages(client().getMessages())\n setHasOlderMessages(client().getHasOlderMessages())\n syncResumeState()\n\n // Sync body / forwardedProps changes to the client.\n // Both populate the same wire payload; `forwardedProps` is preferred\n // and `body` is deprecated but still supported.\n createEffect(() => {\n // Conditional spread: `updateOptions` declares strict-optional\n // fields and rejects explicit `undefined` under EOPT.\n client().updateOptions({\n ...(options.body !== undefined && { body: options.body }),\n ...(options.forwardedProps !== undefined && {\n forwardedProps: options.forwardedProps,\n }),\n context: options.context,\n ...(options.queue !== undefined && { queue: options.queue }),\n })\n })\n\n // Sync tools, so a getter such as `get tools() { return pageTools() }`\n // updates the client.\n createEffect(() => {\n const tools = options.tools\n if (tools !== undefined) client().updateOptions({ tools })\n })\n\n // Apply initial live mode immediately on hook creation.\n if (options.live) {\n client().subscribe()\n } else {\n client().unsubscribe()\n }\n\n createEffect(() => {\n if (options.live) {\n client().subscribe()\n } else {\n client().unsubscribe()\n }\n })\n\n onMount(() => {\n // START TAILING HERE, not in the constructor. A client is idle until a view\n // attaches it, so a client that gets built and thrown away never opens a\n // connection — an unreachable stream would hold one of the browser's ~6\n // connections per origin until the page reloaded.\n client().attach()\n client().mountDevtools()\n // Delivery-durability resume is transparent: the resumable SSE connection\n // adapter reattaches via the browser's native Last-Event-ID on reconnect.\n // We only seed interrupt (state) resume from the client here.\n syncResumeState()\n })\n\n // Cleanup on unmount: stop any in-flight requests.\n onCleanup(() => {\n // Release the connection first: `detach` is the counterpart of the `attach`\n // above, and it keeps the transcript and resume pointer so a later mount can\n // pick the run back up from the durable log.\n client().detach()\n if (options.live) {\n client().unsubscribe()\n } else {\n client().stop()\n }\n client().dispose()\n })\n\n // Callback options are read through `options.xxx` at call time, so reactive\n // or mutated options propagate without recreating the client.\n\n const sendMessage = async (\n content: string | MultimodalContent,\n sendOptions?: SendMessageOptions,\n ) => {\n try {\n await client().sendMessage(content, undefined, sendOptions)\n } finally {\n syncResumeState()\n }\n }\n\n const append = async (message: ModelMessage | UIMessage<TTools>) => {\n try {\n await client().append(message)\n } finally {\n syncResumeState()\n }\n }\n\n const reload = async () => {\n try {\n await client().reload()\n } finally {\n syncResumeState()\n }\n }\n\n const loadOlderMessages = async () => {\n await client().loadOlderMessages()\n setHasOlderMessages(client().getHasOlderMessages())\n }\n\n const stop = () => {\n client().stop()\n }\n\n const clear = () => {\n client().clear()\n syncResumeState()\n }\n\n const setMessagesManually = (newMessages: Array<UIMessage<TTools>>) => {\n client().setMessagesManually(newMessages)\n }\n\n const addToolResult = async (result: {\n toolCallId: string\n tool: string\n output: any\n state?: 'output-available' | 'output-error'\n errorText?: string\n }) => {\n await client().addToolResult(result)\n }\n\n const addToolApprovalResponse = async (response: {\n id: string\n approved: boolean\n }) => {\n await client().addToolApprovalResponse(response)\n syncResumeState()\n }\n\n const cancelQueued = (id: string) => client().cancelQueued(id)\n\n const resumeInterrupts = async (\n resumeItems: Array<RunAgentResumeItem>,\n state?: ChatResumeState,\n ) => {\n const result = await client().resumeInterrupts(resumeItems, state)\n syncResumeState()\n return result\n }\n\n const resolveInterrupts = (\n resolution:\n | boolean\n | ((\n interrupt: ResolvableChatInterrupt<TTools, TInterrupts>,\n ) => undefined),\n ) => {\n if (typeof resolution === 'boolean') {\n client().resolveInterrupts(resolution)\n } else {\n client().resolveInterrupts(resolution)\n }\n }\n\n const cancelInterrupts = () => {\n client().cancelInterrupts()\n }\n\n const retryInterrupts = () => {\n client().retryInterrupts()\n }\n\n const resumeInterruptsUnsafe = (\n resumeItems: Array<RunAgentResumeItem>,\n state?: ChatResumeState,\n ) => client().resumeInterruptsUnsafe(resumeItems, state)\n\n const interrupts = () => interruptState().interrupts\n const pendingInterrupts = () => interruptState().pendingInterrupts\n const interruptErrors = () => interruptState().interruptErrors\n const resuming = () => interruptState().resuming\n\n // The \"active\" structured-output part is on the assistant message after\n // the latest user message. When no user message exists yet, return null\n // rather than scanning history — otherwise a stale `final` from\n // `initialMessages` would leak into the value on first render.\n const activeStructuredPart = createMemo<StructuredOutputPart | null>(() => {\n const list = messages()\n let lastUserIndex = -1\n for (let i = list.length - 1; i >= 0; i--) {\n if (list[i]?.role === 'user') {\n lastUserIndex = i\n break\n }\n }\n if (lastUserIndex === -1) return null\n for (let i = list.length - 1; i > lastUserIndex; i--) {\n const m = list[i]\n if (m?.role !== 'assistant') continue\n const part = m.parts.find(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n if (part) return part\n }\n return null\n })\n\n const partial = createMemo<Partial>(() => {\n const part = activeStructuredPart()\n if (!part) return {} as Partial\n const v = part.partial ?? part.data\n return (v ?? {}) as Partial\n })\n\n const final = createMemo<Final | null>(() => {\n const part = activeStructuredPart()\n if (!part || part.status !== 'complete') return null\n return part.data as Final\n })\n\n // partial / final are runtime-tracked unconditionally; the conditional\n // return type hides them when no `outputSchema` is supplied.\n // oxlint-disable-next-line eslint-js/no-restricted-syntax -- primitive return shape diverges from generic UseChatReturn<TTools, TSchema>; TS can't structurally narrow the conditional partial/final fields\n return {\n messages,\n queue,\n sendMessage,\n append,\n reload,\n stop,\n isLoading,\n hasOlderMessages,\n loadOlderMessages,\n error,\n status,\n isSubscribed,\n connectionStatus,\n sessionGenerating,\n setMessages: setMessagesManually,\n clear,\n addToolResult,\n addToolApprovalResponse,\n cancelQueued,\n runId,\n interrupts,\n pendingInterrupts,\n interruptErrors,\n resuming,\n resolveInterrupts,\n cancelInterrupts,\n retryInterrupts,\n resumeInterruptsUnsafe,\n resumeInterrupts,\n partial,\n final,\n } as unknown as UseChatReturn<TTools, TSchema, TInterrupts>\n}\n"],"mappings":"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"}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Accessor } from "solid-js";
|
|
2
|
+
import { AnyClientTool, InferredClientContext, RegisterWebMCPToolsOptions, SubscribeWebMCPToolsOptions } from "@tanstack/ai-client";
|
|
2
3
|
|
|
3
4
|
//#region src/use-web-mcp-tools.d.ts
|
|
4
|
-
/** Options for {@link
|
|
5
|
-
type
|
|
5
|
+
/** Options for {@link useRegisterWebMCPTools}. */
|
|
6
|
+
type UseRegisterWebMCPToolsOptions<TTools extends ReadonlyArray<AnyClientTool>, TContext = InferredClientContext<TTools>> = Omit<RegisterWebMCPToolsOptions<TTools, TContext>, 'signal'> & {
|
|
6
7
|
/** Receives an asynchronous registration error. */
|
|
7
8
|
onError?: (error: unknown) => void;
|
|
8
9
|
};
|
|
9
|
-
|
|
10
|
+
/** @deprecated Use `UseRegisterWebMCPToolsOptions`. Removed in 1.0.0. */
|
|
11
|
+
type UseWebMCPToolsOptions<TTools extends ReadonlyArray<AnyClientTool>, TContext = InferredClientContext<TTools>> = UseRegisterWebMCPToolsOptions<TTools, TContext>;
|
|
12
|
+
type UseRegisterWebMCPToolsArguments<TTools extends ReadonlyArray<AnyClientTool>, TContext> = {} extends UseRegisterWebMCPToolsOptions<TTools, TContext> ? [options?: UseRegisterWebMCPToolsOptions<TTools, TContext>] : [options: UseRegisterWebMCPToolsOptions<TTools, TContext>];
|
|
10
13
|
/**
|
|
11
14
|
* Registers executable client tools with WebMCP for the current Solid owner.
|
|
12
15
|
*
|
|
@@ -17,10 +20,40 @@ type UseWebMCPToolsArguments<TTools extends ReadonlyArray<AnyClientTool>, TConte
|
|
|
17
20
|
*
|
|
18
21
|
* @example
|
|
19
22
|
* ```ts
|
|
20
|
-
*
|
|
23
|
+
* useRegisterWebMCPTools([searchProducts])
|
|
21
24
|
* ```
|
|
22
25
|
*/
|
|
23
|
-
declare function
|
|
26
|
+
declare function useRegisterWebMCPTools<const TTools extends ReadonlyArray<AnyClientTool>, TContext = InferredClientContext<TTools>>(tools: TTools, ...[options]: UseRegisterWebMCPToolsArguments<TTools, TContext>): void;
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated Use `useRegisterWebMCPTools`. Removed in 1.0.0.
|
|
29
|
+
* @alias
|
|
30
|
+
*/
|
|
31
|
+
declare const useWebMCPTools: typeof useRegisterWebMCPTools;
|
|
32
|
+
/** Options for {@link usePageWebMCPTools}. */
|
|
33
|
+
type UsePageWebMCPToolsOptions = Omit<SubscribeWebMCPToolsOptions, 'signal'>;
|
|
34
|
+
/**
|
|
35
|
+
* Returns the WebMCP tools on the page as client tools for `useChat`.
|
|
36
|
+
*
|
|
37
|
+
* The accessor starts empty and updates when the page registers or removes a
|
|
38
|
+
* tool. The owner cleanup stops the updates. Unsupported browsers and server
|
|
39
|
+
* rendering keep an empty array.
|
|
40
|
+
*
|
|
41
|
+
* @param options - A filter that skips tools, and an error callback.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```tsx
|
|
45
|
+
* const pageTools = usePageWebMCPTools({
|
|
46
|
+
* filter: (tool) => tool.origin === location.origin,
|
|
47
|
+
* })
|
|
48
|
+
* const chat = useChat({
|
|
49
|
+
* connection,
|
|
50
|
+
* get tools() {
|
|
51
|
+
* return pageTools()
|
|
52
|
+
* },
|
|
53
|
+
* })
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
declare function usePageWebMCPTools(options?: UsePageWebMCPToolsOptions): Accessor<Array<AnyClientTool>>;
|
|
24
57
|
//#endregion
|
|
25
|
-
export { UseWebMCPToolsOptions, useWebMCPTools };
|
|
58
|
+
export { UsePageWebMCPToolsOptions, UseRegisterWebMCPToolsOptions, UseWebMCPToolsOptions, usePageWebMCPTools, useRegisterWebMCPTools, useWebMCPTools };
|
|
26
59
|
//# sourceMappingURL=use-web-mcp-tools.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { onCleanup } from "solid-js";
|
|
2
|
-
import { registerWebMCPTools } from "@tanstack/ai-client";
|
|
1
|
+
import { createSignal, onCleanup } from "solid-js";
|
|
2
|
+
import { registerWebMCPTools, subscribeWebMCPTools } from "@tanstack/ai-client";
|
|
3
3
|
|
|
4
4
|
//#region src/use-web-mcp-tools.ts
|
|
5
5
|
/**
|
|
@@ -12,10 +12,10 @@ import { registerWebMCPTools } from "@tanstack/ai-client";
|
|
|
12
12
|
*
|
|
13
13
|
* @example
|
|
14
14
|
* ```ts
|
|
15
|
-
*
|
|
15
|
+
* useRegisterWebMCPTools([searchProducts])
|
|
16
16
|
* ```
|
|
17
17
|
*/
|
|
18
|
-
function
|
|
18
|
+
function useRegisterWebMCPTools(tools, ...[options]) {
|
|
19
19
|
const controller = new AbortController();
|
|
20
20
|
registerWebMCPTools(tools, {
|
|
21
21
|
...options,
|
|
@@ -25,7 +25,45 @@ function useWebMCPTools(tools, ...[options]) {
|
|
|
25
25
|
});
|
|
26
26
|
onCleanup(() => controller.abort());
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* @deprecated Use `useRegisterWebMCPTools`. Removed in 1.0.0.
|
|
30
|
+
* @alias
|
|
31
|
+
*/
|
|
32
|
+
const useWebMCPTools = useRegisterWebMCPTools;
|
|
33
|
+
/**
|
|
34
|
+
* Returns the WebMCP tools on the page as client tools for `useChat`.
|
|
35
|
+
*
|
|
36
|
+
* The accessor starts empty and updates when the page registers or removes a
|
|
37
|
+
* tool. The owner cleanup stops the updates. Unsupported browsers and server
|
|
38
|
+
* rendering keep an empty array.
|
|
39
|
+
*
|
|
40
|
+
* @param options - A filter that skips tools, and an error callback.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```tsx
|
|
44
|
+
* const pageTools = usePageWebMCPTools({
|
|
45
|
+
* filter: (tool) => tool.origin === location.origin,
|
|
46
|
+
* })
|
|
47
|
+
* const chat = useChat({
|
|
48
|
+
* connection,
|
|
49
|
+
* get tools() {
|
|
50
|
+
* return pageTools()
|
|
51
|
+
* },
|
|
52
|
+
* })
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
function usePageWebMCPTools(options) {
|
|
56
|
+
const [tools, setTools] = createSignal([]);
|
|
57
|
+
const controller = new AbortController();
|
|
58
|
+
subscribeWebMCPTools((nextTools) => setTools(nextTools), {
|
|
59
|
+
signal: controller.signal,
|
|
60
|
+
filter: (tool) => options?.filter?.(tool) ?? true,
|
|
61
|
+
onError: (error) => options?.onError?.(error)
|
|
62
|
+
});
|
|
63
|
+
onCleanup(() => controller.abort());
|
|
64
|
+
return tools;
|
|
65
|
+
}
|
|
28
66
|
|
|
29
67
|
//#endregion
|
|
30
|
-
export { useWebMCPTools };
|
|
68
|
+
export { usePageWebMCPTools, useRegisterWebMCPTools, useWebMCPTools };
|
|
31
69
|
//# sourceMappingURL=use-web-mcp-tools.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-web-mcp-tools.js","names":[],"sources":["../src/use-web-mcp-tools.ts"],"sourcesContent":["import { onCleanup } from 'solid-js'\nimport { registerWebMCPTools } from '@tanstack/ai-client'\nimport type {\n AnyClientTool,\n InferredClientContext,\n RegisterWebMCPToolsOptions,\n} from '@tanstack/ai-client'\n\n/** Options for {@link
|
|
1
|
+
{"version":3,"file":"use-web-mcp-tools.js","names":[],"sources":["../src/use-web-mcp-tools.ts"],"sourcesContent":["import { createSignal, onCleanup } from 'solid-js'\nimport { registerWebMCPTools, subscribeWebMCPTools } from '@tanstack/ai-client'\nimport type { Accessor } from 'solid-js'\nimport type {\n AnyClientTool,\n InferredClientContext,\n RegisterWebMCPToolsOptions,\n SubscribeWebMCPToolsOptions,\n} from '@tanstack/ai-client'\n\n/** Options for {@link useRegisterWebMCPTools}. */\nexport type UseRegisterWebMCPToolsOptions<\n TTools extends ReadonlyArray<AnyClientTool>,\n TContext = InferredClientContext<TTools>,\n> = Omit<RegisterWebMCPToolsOptions<TTools, TContext>, 'signal'> & {\n /** Receives an asynchronous registration error. */\n onError?: (error: unknown) => void\n}\n\n/** @deprecated Use `UseRegisterWebMCPToolsOptions`. Removed in 1.0.0. */\nexport type UseWebMCPToolsOptions<\n TTools extends ReadonlyArray<AnyClientTool>,\n TContext = InferredClientContext<TTools>,\n> = UseRegisterWebMCPToolsOptions<TTools, TContext>\n\ntype UseRegisterWebMCPToolsArguments<\n TTools extends ReadonlyArray<AnyClientTool>,\n TContext,\n> =\n {} extends UseRegisterWebMCPToolsOptions<TTools, TContext>\n ? [options?: UseRegisterWebMCPToolsOptions<TTools, TContext>]\n : [options: UseRegisterWebMCPToolsOptions<TTools, TContext>]\n\n/**\n * Registers executable client tools with WebMCP for the current Solid owner.\n *\n * The owner cleanup removes every tool registered by this call.\n *\n * @param tools - The executable client tools to expose through WebMCP.\n * @param options - Runtime context, per-tool options, and error handling.\n *\n * @example\n * ```ts\n * useRegisterWebMCPTools([searchProducts])\n * ```\n */\nexport function useRegisterWebMCPTools<\n const TTools extends ReadonlyArray<AnyClientTool>,\n TContext = InferredClientContext<TTools>,\n>(\n tools: TTools,\n ...[options]: UseRegisterWebMCPToolsArguments<TTools, TContext>\n) {\n const controller = new AbortController()\n\n void registerWebMCPTools(tools, {\n ...options,\n signal: controller.signal,\n }).catch((error) => {\n if (!controller.signal.aborted) {\n options?.onError?.(error)\n }\n })\n\n onCleanup(() => controller.abort())\n}\n\n/**\n * @deprecated Use `useRegisterWebMCPTools`. Removed in 1.0.0.\n * @alias\n */\nexport const useWebMCPTools = useRegisterWebMCPTools\n\n/** Options for {@link usePageWebMCPTools}. */\nexport type UsePageWebMCPToolsOptions = Omit<\n SubscribeWebMCPToolsOptions,\n 'signal'\n>\n\n/**\n * Returns the WebMCP tools on the page as client tools for `useChat`.\n *\n * The accessor starts empty and updates when the page registers or removes a\n * tool. The owner cleanup stops the updates. Unsupported browsers and server\n * rendering keep an empty array.\n *\n * @param options - A filter that skips tools, and an error callback.\n *\n * @example\n * ```tsx\n * const pageTools = usePageWebMCPTools({\n * filter: (tool) => tool.origin === location.origin,\n * })\n * const chat = useChat({\n * connection,\n * get tools() {\n * return pageTools()\n * },\n * })\n * ```\n */\nexport function usePageWebMCPTools(\n options?: UsePageWebMCPToolsOptions,\n): Accessor<Array<AnyClientTool>> {\n const [tools, setTools] = createSignal<Array<AnyClientTool>>([])\n const controller = new AbortController()\n\n subscribeWebMCPTools((nextTools) => setTools(nextTools), {\n signal: controller.signal,\n filter: (tool) => options?.filter?.(tool) ?? true,\n onError: (error) => options?.onError?.(error),\n })\n\n onCleanup(() => controller.abort())\n return tools\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA8CA,SAAgB,uBAId,OACA,GAAG,CAAC,UACJ;CACA,MAAM,aAAa,IAAI,iBAAiB;AAExC,CAAK,oBAAoB,OAAO;EAC9B,GAAG;EACH,QAAQ,WAAW;EACpB,CAAC,CAAC,OAAO,UAAU;AAClB,MAAI,CAAC,WAAW,OAAO,QACrB,UAAS,UAAU,MAAM;GAE3B;AAEF,iBAAgB,WAAW,OAAO,CAAC;;;;;;AAOrC,MAAa,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;AA8B9B,SAAgB,mBACd,SACgC;CAChC,MAAM,CAAC,OAAO,YAAY,aAAmC,EAAE,CAAC;CAChE,MAAM,aAAa,IAAI,iBAAiB;AAExC,uBAAsB,cAAc,SAAS,UAAU,EAAE;EACvD,QAAQ,WAAW;EACnB,SAAS,SAAS,SAAS,SAAS,KAAK,IAAI;EAC7C,UAAU,UAAU,SAAS,UAAU,MAAM;EAC9C,CAAC;AAEF,iBAAgB,WAAW,OAAO,CAAC;AACnC,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-solid",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.1",
|
|
4
4
|
"description": "Solid hooks for TanStack AI streaming chat, structured outputs, and media generation.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -55,11 +55,11 @@
|
|
|
55
55
|
],
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@tanstack/markdown": "^0.0.13",
|
|
58
|
-
"@tanstack/ai-client": "^0.
|
|
58
|
+
"@tanstack/ai-client": "^0.35.1"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"solid-js": ">=1.9.10",
|
|
62
|
-
"@tanstack/ai": "^0.
|
|
62
|
+
"@tanstack/ai": "^0.61.0"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@solidjs/testing-library": "^0.8.10",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"vite": "^8.2.1",
|
|
74
74
|
"vite-plugin-solid": "^2.11.14",
|
|
75
75
|
"vitest": "^4.1.10",
|
|
76
|
-
"@tanstack/ai": "0.
|
|
76
|
+
"@tanstack/ai": "0.61.0"
|
|
77
77
|
},
|
|
78
78
|
"scripts": {
|
|
79
79
|
"clean": "premove ./build ./dist",
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
export { useChat } from './use-chat'
|
|
2
2
|
export { createChatHook } from './create-chat-hook'
|
|
3
3
|
export { useByok } from './use-byok'
|
|
4
|
-
export {
|
|
5
|
-
|
|
4
|
+
export {
|
|
5
|
+
usePageWebMCPTools,
|
|
6
|
+
useRegisterWebMCPTools,
|
|
7
|
+
useWebMCPTools,
|
|
8
|
+
} from './use-web-mcp-tools'
|
|
9
|
+
export type {
|
|
10
|
+
UsePageWebMCPToolsOptions,
|
|
11
|
+
UseRegisterWebMCPToolsOptions,
|
|
12
|
+
UseWebMCPToolsOptions,
|
|
13
|
+
} from './use-web-mcp-tools'
|
|
6
14
|
export type {
|
|
7
15
|
DeepPartial,
|
|
8
16
|
UseChatOptions,
|
|
@@ -98,6 +106,11 @@ export {
|
|
|
98
106
|
PartialJSONParser,
|
|
99
107
|
defaultJSONParser,
|
|
100
108
|
registerWebMCPTools,
|
|
109
|
+
getWebMCPTools,
|
|
110
|
+
subscribeWebMCPTools,
|
|
111
|
+
type GetWebMCPToolsOptions,
|
|
112
|
+
type SubscribeWebMCPToolsOptions,
|
|
113
|
+
type WebMCPPageTool,
|
|
101
114
|
clientTools,
|
|
102
115
|
createMcpAppBridge,
|
|
103
116
|
type McpAppBridge,
|
package/src/use-chat.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
createUniqueId,
|
|
6
6
|
onCleanup,
|
|
7
7
|
onMount,
|
|
8
|
+
untrack,
|
|
8
9
|
} from 'solid-js'
|
|
9
10
|
|
|
10
11
|
import { ChatClient } from '@tanstack/ai-client'
|
|
@@ -159,7 +160,9 @@ export function useChat<
|
|
|
159
160
|
onError: (err) => {
|
|
160
161
|
options.onError?.(err)
|
|
161
162
|
},
|
|
162
|
-
|
|
163
|
+
// Untracked: a tools change must not rebuild the client. The effect
|
|
164
|
+
// below syncs it instead.
|
|
165
|
+
tools: untrack(() => options.tools),
|
|
163
166
|
...(options.interrupts !== undefined && {
|
|
164
167
|
interrupts: options.interrupts,
|
|
165
168
|
}),
|
|
@@ -235,6 +238,13 @@ export function useChat<
|
|
|
235
238
|
})
|
|
236
239
|
})
|
|
237
240
|
|
|
241
|
+
// Sync tools, so a getter such as `get tools() { return pageTools() }`
|
|
242
|
+
// updates the client.
|
|
243
|
+
createEffect(() => {
|
|
244
|
+
const tools = options.tools
|
|
245
|
+
if (tools !== undefined) client().updateOptions({ tools })
|
|
246
|
+
})
|
|
247
|
+
|
|
238
248
|
// Apply initial live mode immediately on hook creation.
|
|
239
249
|
if (options.live) {
|
|
240
250
|
client().subscribe()
|
package/src/use-web-mcp-tools.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { onCleanup } from 'solid-js'
|
|
2
|
-
import { registerWebMCPTools } from '@tanstack/ai-client'
|
|
1
|
+
import { createSignal, onCleanup } from 'solid-js'
|
|
2
|
+
import { registerWebMCPTools, subscribeWebMCPTools } from '@tanstack/ai-client'
|
|
3
|
+
import type { Accessor } from 'solid-js'
|
|
3
4
|
import type {
|
|
4
5
|
AnyClientTool,
|
|
5
6
|
InferredClientContext,
|
|
6
7
|
RegisterWebMCPToolsOptions,
|
|
8
|
+
SubscribeWebMCPToolsOptions,
|
|
7
9
|
} from '@tanstack/ai-client'
|
|
8
10
|
|
|
9
|
-
/** Options for {@link
|
|
10
|
-
export type
|
|
11
|
+
/** Options for {@link useRegisterWebMCPTools}. */
|
|
12
|
+
export type UseRegisterWebMCPToolsOptions<
|
|
11
13
|
TTools extends ReadonlyArray<AnyClientTool>,
|
|
12
14
|
TContext = InferredClientContext<TTools>,
|
|
13
15
|
> = Omit<RegisterWebMCPToolsOptions<TTools, TContext>, 'signal'> & {
|
|
@@ -15,13 +17,19 @@ export type UseWebMCPToolsOptions<
|
|
|
15
17
|
onError?: (error: unknown) => void
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
/** @deprecated Use `UseRegisterWebMCPToolsOptions`. Removed in 1.0.0. */
|
|
21
|
+
export type UseWebMCPToolsOptions<
|
|
22
|
+
TTools extends ReadonlyArray<AnyClientTool>,
|
|
23
|
+
TContext = InferredClientContext<TTools>,
|
|
24
|
+
> = UseRegisterWebMCPToolsOptions<TTools, TContext>
|
|
25
|
+
|
|
26
|
+
type UseRegisterWebMCPToolsArguments<
|
|
19
27
|
TTools extends ReadonlyArray<AnyClientTool>,
|
|
20
28
|
TContext,
|
|
21
29
|
> =
|
|
22
|
-
{} extends
|
|
23
|
-
? [options?:
|
|
24
|
-
: [options:
|
|
30
|
+
{} extends UseRegisterWebMCPToolsOptions<TTools, TContext>
|
|
31
|
+
? [options?: UseRegisterWebMCPToolsOptions<TTools, TContext>]
|
|
32
|
+
: [options: UseRegisterWebMCPToolsOptions<TTools, TContext>]
|
|
25
33
|
|
|
26
34
|
/**
|
|
27
35
|
* Registers executable client tools with WebMCP for the current Solid owner.
|
|
@@ -33,13 +41,16 @@ type UseWebMCPToolsArguments<
|
|
|
33
41
|
*
|
|
34
42
|
* @example
|
|
35
43
|
* ```ts
|
|
36
|
-
*
|
|
44
|
+
* useRegisterWebMCPTools([searchProducts])
|
|
37
45
|
* ```
|
|
38
46
|
*/
|
|
39
|
-
export function
|
|
47
|
+
export function useRegisterWebMCPTools<
|
|
40
48
|
const TTools extends ReadonlyArray<AnyClientTool>,
|
|
41
49
|
TContext = InferredClientContext<TTools>,
|
|
42
|
-
>(
|
|
50
|
+
>(
|
|
51
|
+
tools: TTools,
|
|
52
|
+
...[options]: UseRegisterWebMCPToolsArguments<TTools, TContext>
|
|
53
|
+
) {
|
|
43
54
|
const controller = new AbortController()
|
|
44
55
|
|
|
45
56
|
void registerWebMCPTools(tools, {
|
|
@@ -53,3 +64,53 @@ export function useWebMCPTools<
|
|
|
53
64
|
|
|
54
65
|
onCleanup(() => controller.abort())
|
|
55
66
|
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @deprecated Use `useRegisterWebMCPTools`. Removed in 1.0.0.
|
|
70
|
+
* @alias
|
|
71
|
+
*/
|
|
72
|
+
export const useWebMCPTools = useRegisterWebMCPTools
|
|
73
|
+
|
|
74
|
+
/** Options for {@link usePageWebMCPTools}. */
|
|
75
|
+
export type UsePageWebMCPToolsOptions = Omit<
|
|
76
|
+
SubscribeWebMCPToolsOptions,
|
|
77
|
+
'signal'
|
|
78
|
+
>
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Returns the WebMCP tools on the page as client tools for `useChat`.
|
|
82
|
+
*
|
|
83
|
+
* The accessor starts empty and updates when the page registers or removes a
|
|
84
|
+
* tool. The owner cleanup stops the updates. Unsupported browsers and server
|
|
85
|
+
* rendering keep an empty array.
|
|
86
|
+
*
|
|
87
|
+
* @param options - A filter that skips tools, and an error callback.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```tsx
|
|
91
|
+
* const pageTools = usePageWebMCPTools({
|
|
92
|
+
* filter: (tool) => tool.origin === location.origin,
|
|
93
|
+
* })
|
|
94
|
+
* const chat = useChat({
|
|
95
|
+
* connection,
|
|
96
|
+
* get tools() {
|
|
97
|
+
* return pageTools()
|
|
98
|
+
* },
|
|
99
|
+
* })
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export function usePageWebMCPTools(
|
|
103
|
+
options?: UsePageWebMCPToolsOptions,
|
|
104
|
+
): Accessor<Array<AnyClientTool>> {
|
|
105
|
+
const [tools, setTools] = createSignal<Array<AnyClientTool>>([])
|
|
106
|
+
const controller = new AbortController()
|
|
107
|
+
|
|
108
|
+
subscribeWebMCPTools((nextTools) => setTools(nextTools), {
|
|
109
|
+
signal: controller.signal,
|
|
110
|
+
filter: (tool) => options?.filter?.(tool) ?? true,
|
|
111
|
+
onError: (error) => options?.onError?.(error),
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
onCleanup(() => controller.abort())
|
|
115
|
+
return tools
|
|
116
|
+
}
|