@octanejs/tanstack-ai 0.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dominic Gannaway
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # @octanejs/tanstack-ai
2
+
3
+ [TanStack AI](https://tanstack.com/ai) bindings for the
4
+ [Octane](https://github.com/octanejs/octane) UI framework.
5
+
6
+ This package ports `@tanstack/ai-react@0.17.0` onto Octane while reusing
7
+ `@tanstack/ai` and `@tanstack/ai-client` unchanged. The runtime export surface
8
+ matches the React adapter, so migration starts by changing the package import:
9
+
10
+ ```ts
11
+ // before
12
+ import { useChat } from '@tanstack/ai-react'
13
+
14
+ // after
15
+ import { useChat } from '@octanejs/tanstack-ai'
16
+ ```
17
+
18
+ The renderer-bearing hook modules are authored as `.tsrx` and compiled by
19
+ Octane. Matching `.tsrx.d.ts` companions are checked declaration emits of
20
+ those implementations, preserving the complete generic surface for
21
+ TypeScript consumers.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pnpm add @octanejs/tanstack-ai @tanstack/ai @tanstack/ai-client octane
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ```tsx
32
+ import { useState } from 'octane'
33
+ import { useChat } from '@octanejs/tanstack-ai'
34
+
35
+ export function Chat() @{
36
+ const [input, setInput] = useState('')
37
+ const chat = useChat({
38
+ fetcher: myFetcher,
39
+ })
40
+
41
+ <div>
42
+ @for (const message of chat.messages; key message.id) {
43
+ <p>
44
+ {(message.role +
45
+ ': ' +
46
+ message.parts
47
+ .map((part) => (part.type === 'text' ? part.content : ''))
48
+ .join('')) as string}
49
+ </p>
50
+ }
51
+ <input
52
+ value={input}
53
+ onInput={(event) => setInput(event.currentTarget.value)}
54
+ />
55
+ <button
56
+ onClick={() => {
57
+ void chat.sendMessage(input)
58
+ setInput('')
59
+ }}
60
+ >
61
+ Send
62
+ </button>
63
+ </div>
64
+ }
65
+ ```
66
+
67
+ `useChat` has no input state of its own — hold the text box value in a local
68
+ `useState` and pass it to `sendMessage`. Note the `onInput` handler: Octane
69
+ drives text controls per keystroke through the native `input` event, not a
70
+ synthetic `onChange`.
71
+
72
+ ## API
73
+
74
+ The adapter includes `useChat`, `useRealtimeChat`, `useMcpAppBridge`,
75
+ `useGeneration`, `useGenerateImage`, `useGenerateAudio`, `useGenerateSpeech`,
76
+ `useGenerateVideo`, `useTranscription`, `useSummarize`, and
77
+ `useAudioRecorder`. It also re-exports all 30 `@tanstack/ai-client`
78
+ convenience helpers and types (`fetchServerSentEvents`, `fetchHttpStream`,
79
+ `xhrServerSentEvents`, `xhrHttpStream`, `stream`, `rpcStream`,
80
+ `createChatClientOptions`, `createMcpAppBridge`, and their associated types)
81
+ unchanged, mirroring the upstream `@tanstack/ai-react` index.
82
+
83
+ Server rendering through `octane/server` is supported. `useChat` renders its
84
+ initial message snapshot without browser-only setup.
85
+
86
+ ## Divergences from `@tanstack/ai-react`
87
+
88
+ - The `./mcp-apps` subpath and its `MCPAppResource` component are not ported:
89
+ they render `AppRenderer` from the React-only `@mcp-ui/client`, which has no
90
+ Octane equivalent. The framework-agnostic `useMcpAppBridge` hook is ported
91
+ and available on the main entry.
92
+ - Octane uses native events: text/file/recorder inputs drive updates via
93
+ `onInput`; there is no synthetic `onChange` layer.
94
+ - Octane has no StrictMode double-invoke and always provides `useId`, so no
95
+ random-id fallback is needed.
96
+ - Realtime reconnects and token refreshes use the latest `getToken` and adapter
97
+ supplied to the hook; upstream captures the first render's callbacks.
98
+ - The declared realtime `onStatusChange` callback is invoked alongside the
99
+ hook's state update; upstream 0.17.0 currently drops the external callback.
100
+ - One upstream `useChat` test case ("auto-resume on mount / when the browser
101
+ comes back online") is omitted: it targets
102
+ `ChatClient.prototype.maybeAutoResume`, an API absent from the pinned (and
103
+ latest published) `@tanstack/ai-client@0.21.0` and never invoked by
104
+ `useChat`. It is untestable in this binding until that dependency ships the
105
+ method.
106
+
107
+ ## Verification
108
+
109
+ The port runs TanStack AI's React adapter tests against Octane across all
110
+ eleven hooks, with no skipped, todo, or expected-failure cases (except the
111
+ untestable auto-resume case above). A differential test compiles a shared
112
+ chat fixture for Octane and React and compares streamed output after each
113
+ step; output is byte-equal against real `@tanstack/ai-react@0.17.0`. An SSR
114
+ fixture and the upstream compile-time type tests are also included.
115
+
116
+ Current scope and verification status are tracked in the generated
117
+ [bindings status table](../../docs/bindings-status.md), sourced from this
118
+ package's [`status.json`](./status.json).
119
+
120
+ ## License
121
+
122
+ MIT — contains source derived from
123
+ [TanStack AI](https://github.com/TanStack/ai) (MIT), adapted for Octane.
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@octanejs/tanstack-ai",
3
+ "version": "0.0.1",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=22"
8
+ },
9
+ "description": "TanStack AI bindings for Octane — reuses @tanstack/ai and @tanstack/ai-client and ports the React adapter's hooks (chat, realtime, generation, transcription, media).",
10
+ "author": {
11
+ "name": "Dominic Gannaway",
12
+ "email": "dg@domgan.com"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/octanejs/octane.git",
20
+ "directory": "packages/tanstack-ai"
21
+ },
22
+ "main": "src/index.ts",
23
+ "module": "src/index.ts",
24
+ "types": "src/index.ts",
25
+ "files": [
26
+ "src",
27
+ "README.md"
28
+ ],
29
+ "exports": {
30
+ ".": "./src/index.ts"
31
+ },
32
+ "dependencies": {
33
+ "@tanstack/ai": "0.41.0",
34
+ "@tanstack/ai-client": "0.21.0"
35
+ },
36
+ "peerDependencies": {
37
+ "octane": "0.1.7"
38
+ },
39
+ "devDependencies": {
40
+ "@tanstack/ai-react": "0.17.0",
41
+ "@testing-library/jest-dom": "^6.9.1",
42
+ "@testing-library/user-event": "^14.6.1",
43
+ "@tsrx/react": "^0.2.37",
44
+ "esbuild": "^0.28.1",
45
+ "react": "^19.2.0",
46
+ "react-dom": "^19.2.0",
47
+ "vitest": "^4.1.9",
48
+ "@octanejs/testing-library": "0.1.4",
49
+ "octane": "0.1.7"
50
+ },
51
+ "scripts": {
52
+ "test": "vitest run"
53
+ }
54
+ }
package/src/index.ts ADDED
@@ -0,0 +1,62 @@
1
+ export { useChat } from './use-chat.tsrx';
2
+ export { useRealtimeChat } from './use-realtime-chat.tsrx';
3
+ export { useMcpAppBridge } from './use-mcp-app-bridge.tsrx';
4
+ export type { UseMcpAppBridgeOptions } from './use-mcp-app-bridge.tsrx';
5
+ export type {
6
+ DeepPartial,
7
+ UseChatOptions,
8
+ UseChatReturn,
9
+ UIMessage,
10
+ ChatRequestBody,
11
+ } from './types';
12
+ export type { UseRealtimeChatOptions, UseRealtimeChatReturn } from './realtime-types';
13
+
14
+ export { useGeneration } from './use-generation.tsrx';
15
+ export type { UseGenerationOptions, UseGenerationReturn } from './use-generation.tsrx';
16
+ export { useGenerateImage } from './use-generate-image.tsrx';
17
+ export type { UseGenerateImageOptions, UseGenerateImageReturn } from './use-generate-image.tsrx';
18
+ export { useGenerateAudio } from './use-generate-audio.tsrx';
19
+ export type { UseGenerateAudioOptions, UseGenerateAudioReturn } from './use-generate-audio.tsrx';
20
+ export { useGenerateSpeech } from './use-generate-speech.tsrx';
21
+ export type { UseGenerateSpeechOptions, UseGenerateSpeechReturn } from './use-generate-speech.tsrx';
22
+ export { useTranscription } from './use-transcription.tsrx';
23
+ export type { UseTranscriptionOptions, UseTranscriptionReturn } from './use-transcription.tsrx';
24
+ export { useSummarize } from './use-summarize.tsrx';
25
+ export type { UseSummarizeOptions, UseSummarizeReturn } from './use-summarize.tsrx';
26
+ export { useGenerateVideo } from './use-generate-video.tsrx';
27
+ export type { UseGenerateVideoOptions, UseGenerateVideoReturn } from './use-generate-video.tsrx';
28
+ export { useAudioRecorder } from './use-audio-recorder.tsrx';
29
+ export type { UseAudioRecorderOptions, UseAudioRecorderReturn } from './use-audio-recorder.tsrx';
30
+
31
+ // Re-export from ai-client for convenience (mirror upstream index.ts)
32
+ export {
33
+ fetchServerSentEvents,
34
+ fetchHttpStream,
35
+ xhrServerSentEvents,
36
+ xhrHttpStream,
37
+ stream,
38
+ rpcStream,
39
+ createChatClientOptions,
40
+ createMcpAppBridge,
41
+ type McpAppBridge,
42
+ type CreateMcpAppBridgeOptions,
43
+ type ChatFetcher,
44
+ type ChatFetcherInput,
45
+ type ChatFetcherOptions,
46
+ type ConnectionAdapter,
47
+ type ConnectConnectionAdapter,
48
+ type SubscribeConnectionAdapter,
49
+ type RunAgentInputContext,
50
+ type FetchConnectionOptions,
51
+ type XhrConnectionOptions,
52
+ type InferChatMessages,
53
+ type GenerationClientState,
54
+ type ImageGenerateInput,
55
+ type AudioGenerateInput,
56
+ type SpeechGenerateInput,
57
+ type TranscriptionGenerateInput,
58
+ type SummarizeGenerateInput,
59
+ type VideoGenerateInput,
60
+ type VideoGenerateResult,
61
+ type VideoStatusInfo,
62
+ } from '@tanstack/ai-client';
@@ -0,0 +1,146 @@
1
+ import type {
2
+ AnyClientTool,
3
+ RealtimeMessage,
4
+ RealtimeMode,
5
+ RealtimeSessionConfig,
6
+ RealtimeStatus,
7
+ RealtimeToken,
8
+ UsageInfo,
9
+ } from '@tanstack/ai';
10
+ import type { RealtimeAdapter } from '@tanstack/ai-client';
11
+
12
+ /**
13
+ * Options for the useRealtimeChat hook.
14
+ */
15
+ export interface UseRealtimeChatOptions {
16
+ /**
17
+ * Function to fetch a realtime token from the server.
18
+ * Called on connect and when token needs refresh.
19
+ */
20
+ getToken: () => Promise<RealtimeToken>;
21
+
22
+ /**
23
+ * The realtime adapter to use (e.g., openaiRealtime())
24
+ */
25
+ adapter: RealtimeAdapter;
26
+
27
+ /**
28
+ * Client-side tools with execution logic
29
+ */
30
+ tools?: ReadonlyArray<AnyClientTool>;
31
+
32
+ /**
33
+ * Auto-play assistant audio (default: true)
34
+ */
35
+ autoPlayback?: boolean;
36
+
37
+ /**
38
+ * Request microphone access on connect (default: true)
39
+ */
40
+ autoCapture?: boolean;
41
+
42
+ /**
43
+ * System instructions for the assistant
44
+ */
45
+ instructions?: string;
46
+
47
+ /**
48
+ * Voice to use for audio output
49
+ */
50
+ voice?: string;
51
+
52
+ /**
53
+ * Voice activity detection mode (default: 'server')
54
+ */
55
+ vadMode?: 'server' | 'semantic' | 'manual';
56
+
57
+ /**
58
+ * Output modalities for responses (e.g., ['audio', 'text'])
59
+ */
60
+ outputModalities?: Array<'audio' | 'text'>;
61
+
62
+ /**
63
+ * Temperature for generation (provider-specific range)
64
+ */
65
+ temperature?: number;
66
+
67
+ /**
68
+ * Maximum number of tokens in a response
69
+ */
70
+ maxOutputTokens?: number | 'inf';
71
+
72
+ /**
73
+ * Eagerness level for semantic VAD ('low', 'medium', 'high')
74
+ */
75
+ semanticEagerness?: 'low' | 'medium' | 'high';
76
+
77
+ // Callbacks
78
+ onConnect?: () => void;
79
+ onDisconnect?: () => void;
80
+ onError?: (error: Error) => void;
81
+ onMessage?: (message: RealtimeMessage) => void;
82
+ onModeChange?: (mode: RealtimeMode) => void;
83
+ onInterrupted?: () => void;
84
+ onUsage?: (usage: UsageInfo) => void;
85
+ onGoAway?: (timeLeft?: string) => void;
86
+ onStatusChange?: (status: RealtimeStatus) => void;
87
+ }
88
+
89
+ /**
90
+ * Return type for the useRealtimeChat hook.
91
+ */
92
+ export interface UseRealtimeChatReturn {
93
+ // Connection state
94
+ /** Current connection status */
95
+ status: RealtimeStatus;
96
+ /** Current error, if any */
97
+ error: Error | null;
98
+ /** Connect to the realtime session */
99
+ connect: () => Promise<void>;
100
+ /** Disconnect from the realtime session */
101
+ disconnect: () => Promise<void>;
102
+
103
+ // Conversation state
104
+ /** Current mode (idle, listening, thinking, speaking) */
105
+ mode: RealtimeMode;
106
+ /** Conversation messages */
107
+ messages: Array<RealtimeMessage>;
108
+ /** User transcript while speaking (before finalized) */
109
+ pendingUserTranscript: string | null;
110
+ /** Assistant transcript while speaking (before finalized) */
111
+ pendingAssistantTranscript: string | null;
112
+
113
+ // Voice control
114
+ /** Start listening for voice input (manual VAD mode) */
115
+ startListening: () => void;
116
+ /** Stop listening for voice input (manual VAD mode) */
117
+ stopListening: () => void;
118
+ /** Interrupt the current assistant response */
119
+ interrupt: () => void;
120
+
121
+ // Text input
122
+ /** Send a text message instead of voice */
123
+ sendText: (text: string) => void;
124
+
125
+ // Image input
126
+ /** Send an image to the conversation */
127
+ sendImage: (imageData: string, mimeType: string) => void;
128
+
129
+ // Audio visualization (0-1 normalized)
130
+ /** Current input (microphone) volume level */
131
+ inputLevel: number;
132
+ /** Current output (speaker) volume level */
133
+ outputLevel: number;
134
+ /** Get frequency data for input audio visualization */
135
+ getInputFrequencyData: () => Uint8Array;
136
+ /** Get frequency data for output audio visualization */
137
+ getOutputFrequencyData: () => Uint8Array;
138
+ /** Get time domain data for input waveform */
139
+ getInputTimeDomainData: () => Uint8Array;
140
+ /** Get time domain data for output waveform */
141
+ getOutputTimeDomainData: () => Uint8Array;
142
+
143
+ // Session control
144
+ /** Update the active session and persist the configuration for reconnects. */
145
+ updateSession: (config: RealtimeSessionConfig) => void;
146
+ }
package/src/types.ts ADDED
@@ -0,0 +1,213 @@
1
+ import type {
2
+ AnyClientTool,
3
+ InferSchemaType,
4
+ ModelMessage,
5
+ SchemaInput,
6
+ } from '@tanstack/ai/client';
7
+ import type {
8
+ AIDevtoolsDisplayOptions,
9
+ ChatClientOptions,
10
+ ChatClientState,
11
+ ChatRequestBody,
12
+ ClientContextOptionFromTools,
13
+ ConnectionStatus,
14
+ DistributedOmit,
15
+ InferredClientContext,
16
+ MultimodalContent,
17
+ UIMessage,
18
+ } from '@tanstack/ai-client';
19
+
20
+ // Re-export types from ai-client
21
+ export type { ChatRequestBody, MultimodalContent, UIMessage };
22
+
23
+ /**
24
+ * Recursive partial — every property and every nested array element is optional.
25
+ * Used to type the in-flight `partial` value the hook exposes while a structured
26
+ * output stream is still arriving (the JSON has shape but is incomplete).
27
+ */
28
+ export type DeepPartial<T> =
29
+ T extends ReadonlyArray<infer U>
30
+ ? Array<DeepPartial<U>>
31
+ : T extends object
32
+ ? { [K in keyof T]?: DeepPartial<T[K]> }
33
+ : T;
34
+
35
+ /**
36
+ * Options for the useChat hook.
37
+ *
38
+ * Pass either `connection` or `fetcher` — the XOR is enforced at the type
39
+ * level via `ChatTransport`.
40
+ *
41
+ * This extends ChatClientOptions but omits the state change callbacks that are
42
+ * managed internally by hook state:
43
+ * - `onMessagesChange` - Managed by hook state (exposed as `messages`)
44
+ * - `onLoadingChange` - Managed by hook state (exposed as `isLoading`)
45
+ * - `onErrorChange` - Managed by hook state (exposed as `error`)
46
+ * - `onStatusChange` - Managed by hook state (exposed as `status`)
47
+ *
48
+ * All other callbacks (onResponse, onChunk, onFinish, onError) are
49
+ * passed through to the underlying ChatClient and can be used for side effects.
50
+ *
51
+ * When `outputSchema` is supplied, the hook returns a typed `partial` (live
52
+ * progressive object, updated from `TEXT_MESSAGE_CONTENT` deltas via
53
+ * `parsePartialJSON`) and `final` (validated terminal payload from the
54
+ * `structured-output.complete` event). The schema is used purely for type
55
+ * inference on the client — server-side validation still runs against the
56
+ * schema you pass to `chat({ outputSchema })` on the server route.
57
+ *
58
+ * Changing `connection` or `fetcher` updates the active ChatClient in place,
59
+ * preserving its state. Changing `id` creates a fresh client.
60
+ */
61
+ export type UseChatOptions<
62
+ TTools extends ReadonlyArray<AnyClientTool> = any,
63
+ TSchema extends SchemaInput | undefined = undefined,
64
+ TContext = InferredClientContext<TTools>,
65
+ > = DistributedOmit<
66
+ ChatClientOptions<TTools, TContext>,
67
+ | 'onMessagesChange'
68
+ | 'onLoadingChange'
69
+ | 'onErrorChange'
70
+ | 'onStatusChange'
71
+ | 'onSubscriptionChange'
72
+ | 'onConnectionStatusChange'
73
+ | 'onSessionGeneratingChange'
74
+ | 'context'
75
+ | 'devtools'
76
+ > & {
77
+ /** Display options for TanStack AI Devtools. */
78
+ devtools?: AIDevtoolsDisplayOptions;
79
+ /**
80
+ * Opt into mount-time live subscription behavior.
81
+ * When enabled, the hook subscribes on mount and unsubscribes on unmount.
82
+ */
83
+ live?: boolean;
84
+ /**
85
+ * Standard-schema-compatible schema (Zod, Valibot, ArkType, or a plain JSON
86
+ * Schema). Used to infer the shape of `partial` and `final` in the return.
87
+ * The schema is **not** sent to the server — server-side validation runs
88
+ * against the schema passed to `chat({ outputSchema })` on the server route.
89
+ */
90
+ outputSchema?: TSchema;
91
+ } & ClientContextOptionFromTools<TTools, TContext>;
92
+
93
+ /**
94
+ * Discriminated return shape: when `outputSchema` is supplied, the hook adds
95
+ * typed `partial` / `final` fields; when it is omitted (default), the return
96
+ * is unchanged.
97
+ */
98
+ export type UseChatReturn<
99
+ TTools extends ReadonlyArray<AnyClientTool> = any,
100
+ TSchema extends SchemaInput | undefined = undefined,
101
+ > = BaseUseChatReturn<TTools, TSchema extends SchemaInput ? InferSchemaType<TSchema> : unknown> &
102
+ (TSchema extends SchemaInput
103
+ ? {
104
+ /**
105
+ * Live, progressively-parsed structured output. Updated from
106
+ * `TEXT_MESSAGE_CONTENT` deltas via `parsePartialJSON` while the stream
107
+ * is still arriving, and snapped to the validated payload when
108
+ * `structured-output.complete` fires. Resets on every new run
109
+ * (`sendMessage` / `reload`).
110
+ */
111
+ partial: DeepPartial<InferSchemaType<TSchema>>;
112
+ /**
113
+ * Final, schema-validated structured output. `null` until the terminal
114
+ * `structured-output.complete` event arrives. Resets on every new run.
115
+ */
116
+ final: InferSchemaType<TSchema> | null;
117
+ }
118
+ : Record<never, never>);
119
+
120
+ interface BaseUseChatReturn<TTools extends ReadonlyArray<AnyClientTool> = any, TData = unknown> {
121
+ /**
122
+ * Current messages in the conversation. When `outputSchema` is supplied,
123
+ * `messages[i].parts.find(p => p.type === 'structured-output')` is typed
124
+ * with the schema's inferred shape — `data: T`, `partial: DeepPartial<T>`.
125
+ */
126
+ messages: Array<UIMessage<TTools, TData>>;
127
+
128
+ /**
129
+ * Send a message and get a response.
130
+ * Can be a simple string or multimodal content with images, audio, etc.
131
+ */
132
+ sendMessage: (content: string | MultimodalContent) => Promise<void>;
133
+
134
+ /**
135
+ * Append a message to the conversation
136
+ */
137
+ append: (message: ModelMessage | UIMessage<TTools, TData>) => Promise<void>;
138
+
139
+ /**
140
+ * Add the result of a client-side tool execution
141
+ */
142
+ addToolResult: (result: {
143
+ toolCallId: string;
144
+ tool: string;
145
+ output: any;
146
+ state?: 'output-available' | 'output-error';
147
+ errorText?: string;
148
+ }) => Promise<void>;
149
+
150
+ /**
151
+ * Respond to a tool approval request
152
+ */
153
+ addToolApprovalResponse: (response: {
154
+ id: string; // approval.id, not toolCallId
155
+ approved: boolean;
156
+ }) => Promise<void>;
157
+
158
+ /**
159
+ * Reload the last assistant message
160
+ */
161
+ reload: () => Promise<void>;
162
+
163
+ /**
164
+ * Stop the current response generation
165
+ */
166
+ stop: () => void;
167
+
168
+ /**
169
+ * Whether a response is currently being generated
170
+ */
171
+ isLoading: boolean;
172
+
173
+ /**
174
+ * Current error, if any
175
+ */
176
+ error: Error | undefined;
177
+
178
+ /**
179
+ * Current status of the chat client
180
+ */
181
+ status: ChatClientState;
182
+
183
+ /**
184
+ * Whether the subscription loop is currently active
185
+ */
186
+ isSubscribed: boolean;
187
+
188
+ /**
189
+ * Current connection lifecycle status
190
+ */
191
+ connectionStatus: ConnectionStatus;
192
+
193
+ /**
194
+ * Whether the shared session is actively generating.
195
+ * Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).
196
+ * Unlike `isLoading` (request-local), this reflects shared generation
197
+ * activity visible to all subscribers (e.g. across tabs/devices).
198
+ */
199
+ sessionGenerating: boolean;
200
+
201
+ /**
202
+ * Set messages manually
203
+ */
204
+ setMessages: (messages: Array<UIMessage<TTools, TData>>) => void;
205
+
206
+ /**
207
+ * Clear all messages
208
+ */
209
+ clear: () => void;
210
+ }
211
+
212
+ // Note: createChatClientOptions and InferChatMessages are now in @tanstack/ai-client
213
+ // and re-exported from there for convenience