@lantos1618/better-ui 0.2.2 → 0.3.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/README.md +231 -148
- package/dist/index.d.mts +314 -0
- package/dist/index.d.ts +314 -0
- package/dist/index.js +522 -0
- package/dist/index.mjs +491 -0
- package/package.json +59 -20
- package/lib/aui/README.md +0 -136
- package/lib/aui/__tests__/aui-complete.test.ts +0 -251
- package/lib/aui/__tests__/aui-comprehensive.test.ts +0 -376
- package/lib/aui/__tests__/aui-concise.test.ts +0 -278
- package/lib/aui/__tests__/aui-integration.test.ts +0 -309
- package/lib/aui/__tests__/aui-simple.test.ts +0 -116
- package/lib/aui/__tests__/aui.test.ts +0 -269
- package/lib/aui/__tests__/concise-api.test.ts +0 -165
- package/lib/aui/__tests__/core.test.ts +0 -265
- package/lib/aui/__tests__/simple-api.test.ts +0 -200
- package/lib/aui/ai-assistant.ts +0 -408
- package/lib/aui/ai-control.ts +0 -353
- package/lib/aui/client/use-aui.ts +0 -55
- package/lib/aui/client-control.ts +0 -551
- package/lib/aui/client-executor.ts +0 -417
- package/lib/aui/components/ToolRenderer.tsx +0 -22
- package/lib/aui/core.ts +0 -137
- package/lib/aui/demo.tsx +0 -89
- package/lib/aui/examples/ai-complete-demo.tsx +0 -359
- package/lib/aui/examples/ai-control-demo.tsx +0 -356
- package/lib/aui/examples/ai-control-tools.ts +0 -308
- package/lib/aui/examples/concise-api.tsx +0 -153
- package/lib/aui/examples/index.tsx +0 -163
- package/lib/aui/examples/quick-demo.tsx +0 -91
- package/lib/aui/examples/simple-demo.tsx +0 -71
- package/lib/aui/examples/simple-tools.tsx +0 -160
- package/lib/aui/examples/user-api.tsx +0 -208
- package/lib/aui/examples/user-requested.tsx +0 -174
- package/lib/aui/examples/weather-search-tools.tsx +0 -119
- package/lib/aui/examples.tsx +0 -367
- package/lib/aui/hooks/useAUITool.ts +0 -142
- package/lib/aui/hooks/useAUIToolEnhanced.ts +0 -343
- package/lib/aui/hooks/useAUITools.ts +0 -195
- package/lib/aui/index.ts +0 -156
- package/lib/aui/provider.tsx +0 -45
- package/lib/aui/server-control.ts +0 -386
- package/lib/aui/server-executor.ts +0 -165
- package/lib/aui/server.ts +0 -167
- package/lib/aui/tool-registry.ts +0 -380
- package/lib/aui/tools/advanced-examples.tsx +0 -86
- package/lib/aui/tools/ai-complete.ts +0 -375
- package/lib/aui/tools/api-tools.tsx +0 -230
- package/lib/aui/tools/data-tools.tsx +0 -232
- package/lib/aui/tools/dom-tools.tsx +0 -202
- package/lib/aui/tools/examples.ts +0 -43
- package/lib/aui/tools/file-tools.tsx +0 -202
- package/lib/aui/tools/form-tools.tsx +0 -233
- package/lib/aui/tools/index.ts +0 -8
- package/lib/aui/tools/navigation-tools.tsx +0 -172
- package/lib/aui/tools/notification-tools.ts +0 -213
- package/lib/aui/tools/state-tools.tsx +0 -209
- package/lib/aui/types.ts +0 -47
- package/lib/aui/vercel-ai.ts +0 -100
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import React, { ReactElement } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Better UI v2 - Tool Definition
|
|
6
|
+
*
|
|
7
|
+
* Clean, type-safe tool definition inspired by TanStack AI,
|
|
8
|
+
* with Better UI's unique view integration.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Context passed to tool handlers
|
|
13
|
+
*
|
|
14
|
+
* SECURITY NOTE:
|
|
15
|
+
* - Server-only fields (env, headers, cookies, user, session) are automatically
|
|
16
|
+
* stripped when running on the client to prevent accidental leakage.
|
|
17
|
+
* - Server handlers NEVER run on the client - they auto-fetch via API instead.
|
|
18
|
+
* - Never store secrets in tool output - it gets sent to the client.
|
|
19
|
+
*/
|
|
20
|
+
interface ToolContext {
|
|
21
|
+
/** Shared cache for deduplication */
|
|
22
|
+
cache: Map<string, any>;
|
|
23
|
+
/** Fetch function (can be customized for auth headers, etc.) */
|
|
24
|
+
fetch: typeof fetch;
|
|
25
|
+
/** Whether running on server */
|
|
26
|
+
isServer: boolean;
|
|
27
|
+
/** Environment variables - NEVER available on client */
|
|
28
|
+
env?: Record<string, string>;
|
|
29
|
+
/** Request headers - NEVER available on client */
|
|
30
|
+
headers?: Headers;
|
|
31
|
+
/** Cookies - NEVER available on client */
|
|
32
|
+
cookies?: Record<string, string>;
|
|
33
|
+
/** Authenticated user - NEVER available on client */
|
|
34
|
+
user?: any;
|
|
35
|
+
/** Session data - NEVER available on client */
|
|
36
|
+
session?: any;
|
|
37
|
+
/** Optimistic update function */
|
|
38
|
+
optimistic?: <T>(data: Partial<T>) => void;
|
|
39
|
+
}
|
|
40
|
+
interface CacheConfig<TInput> {
|
|
41
|
+
ttl: number;
|
|
42
|
+
key?: (input: TInput) => string;
|
|
43
|
+
}
|
|
44
|
+
/** Configuration for auto-fetch behavior when no client handler is defined */
|
|
45
|
+
interface ClientFetchConfig {
|
|
46
|
+
/** API endpoint for tool execution (default: '/api/tools/execute') */
|
|
47
|
+
endpoint?: string;
|
|
48
|
+
}
|
|
49
|
+
interface ToolConfig<TInput, TOutput> {
|
|
50
|
+
name: string;
|
|
51
|
+
description?: string;
|
|
52
|
+
input: z.ZodType<TInput>;
|
|
53
|
+
output?: z.ZodType<TOutput>;
|
|
54
|
+
tags?: string[];
|
|
55
|
+
cache?: CacheConfig<TInput>;
|
|
56
|
+
/** Configure auto-fetch behavior for client-side execution */
|
|
57
|
+
clientFetch?: ClientFetchConfig;
|
|
58
|
+
}
|
|
59
|
+
type ServerHandler<TInput, TOutput> = (input: TInput, ctx: ToolContext) => Promise<TOutput> | TOutput;
|
|
60
|
+
type ClientHandler<TInput, TOutput> = (input: TInput, ctx: ToolContext) => Promise<TOutput> | TOutput;
|
|
61
|
+
type ViewState<TInput = any> = {
|
|
62
|
+
loading?: boolean;
|
|
63
|
+
error?: Error | null;
|
|
64
|
+
onAction?: (input: TInput) => void | Promise<void>;
|
|
65
|
+
};
|
|
66
|
+
type ViewComponent<TOutput, TInput = any> = (data: TOutput, state?: ViewState<TInput>) => ReactElement | null;
|
|
67
|
+
declare class Tool<TInput = any, TOutput = any> {
|
|
68
|
+
readonly name: string;
|
|
69
|
+
readonly description?: string;
|
|
70
|
+
readonly inputSchema: z.ZodType<TInput>;
|
|
71
|
+
readonly outputSchema?: z.ZodType<TOutput>;
|
|
72
|
+
readonly tags: string[];
|
|
73
|
+
readonly cacheConfig?: CacheConfig<TInput>;
|
|
74
|
+
readonly clientFetchConfig?: ClientFetchConfig;
|
|
75
|
+
private _server?;
|
|
76
|
+
private _client?;
|
|
77
|
+
private _view?;
|
|
78
|
+
constructor(config: ToolConfig<TInput, TOutput>);
|
|
79
|
+
/**
|
|
80
|
+
* Define server-side implementation
|
|
81
|
+
* Runs on server (API routes, server components, etc.)
|
|
82
|
+
*/
|
|
83
|
+
server(handler: ServerHandler<TInput, TOutput>): this;
|
|
84
|
+
/**
|
|
85
|
+
* Define client-side implementation
|
|
86
|
+
* Runs in browser. If not specified, auto-fetches to /api/tools/{name}
|
|
87
|
+
*/
|
|
88
|
+
client(handler: ClientHandler<TInput, TOutput>): this;
|
|
89
|
+
/**
|
|
90
|
+
* Define view component for rendering results
|
|
91
|
+
* Our differentiator from TanStack AI
|
|
92
|
+
*/
|
|
93
|
+
view(component: ViewComponent<TOutput>): this;
|
|
94
|
+
/**
|
|
95
|
+
* Execute the tool
|
|
96
|
+
* Automatically uses server or client handler based on environment
|
|
97
|
+
*
|
|
98
|
+
* SECURITY: Server handlers only run on server. Client automatically
|
|
99
|
+
* fetches from /api/tools/execute if no client handler is defined.
|
|
100
|
+
*/
|
|
101
|
+
run(input: TInput, ctx?: Partial<ToolContext>): Promise<TOutput>;
|
|
102
|
+
/**
|
|
103
|
+
* Make the tool callable directly: await weather({ city: 'London' })
|
|
104
|
+
*/
|
|
105
|
+
call(input: TInput, ctx?: Partial<ToolContext>): Promise<TOutput>;
|
|
106
|
+
/**
|
|
107
|
+
* Default client fetch when no .client() is defined
|
|
108
|
+
*
|
|
109
|
+
* SECURITY: This ensures server handlers never run on the client.
|
|
110
|
+
* The server-side /api/tools/execute endpoint handles execution safely.
|
|
111
|
+
*/
|
|
112
|
+
private _defaultClientFetch;
|
|
113
|
+
/**
|
|
114
|
+
* Render the tool's view component
|
|
115
|
+
* Memoized to prevent unnecessary re-renders when parent state changes
|
|
116
|
+
*/
|
|
117
|
+
View: React.NamedExoticComponent<{
|
|
118
|
+
data: TOutput | null;
|
|
119
|
+
loading?: boolean;
|
|
120
|
+
error?: Error | null;
|
|
121
|
+
onAction?: (input: TInput) => void | Promise<void>;
|
|
122
|
+
}>;
|
|
123
|
+
private _initView;
|
|
124
|
+
/**
|
|
125
|
+
* Check if tool has a view
|
|
126
|
+
*/
|
|
127
|
+
get hasView(): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Check if tool has server implementation
|
|
130
|
+
*/
|
|
131
|
+
get hasServer(): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Check if tool has custom client implementation
|
|
134
|
+
*/
|
|
135
|
+
get hasClient(): boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Convert to plain object (for serialization)
|
|
138
|
+
*
|
|
139
|
+
* SECURITY: This intentionally excludes handlers and schemas to prevent
|
|
140
|
+
* accidental exposure of server logic or validation details.
|
|
141
|
+
*/
|
|
142
|
+
toJSON(): {
|
|
143
|
+
name: string;
|
|
144
|
+
description: string | undefined;
|
|
145
|
+
tags: string[];
|
|
146
|
+
hasServer: boolean;
|
|
147
|
+
hasClient: boolean;
|
|
148
|
+
hasView: boolean;
|
|
149
|
+
hasCache: boolean;
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Convert to AI SDK format (Vercel AI SDK v5 compatible)
|
|
153
|
+
*/
|
|
154
|
+
toAITool(): {
|
|
155
|
+
description: string;
|
|
156
|
+
inputSchema: z.ZodType<TInput, z.ZodTypeDef, TInput>;
|
|
157
|
+
execute: (input: TInput) => Promise<TOutput>;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Create a new tool with object config
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* const weather = tool({
|
|
165
|
+
* name: 'weather',
|
|
166
|
+
* description: 'Get weather for a city',
|
|
167
|
+
* input: z.object({ city: z.string() }),
|
|
168
|
+
* output: z.object({ temp: z.number() }),
|
|
169
|
+
* });
|
|
170
|
+
*
|
|
171
|
+
* weather.server(async ({ city }) => {
|
|
172
|
+
* return { temp: await getTemp(city) };
|
|
173
|
+
* });
|
|
174
|
+
*/
|
|
175
|
+
declare function tool<TInput, TOutput = any>(config: ToolConfig<TInput, TOutput>): Tool<TInput, TOutput>;
|
|
176
|
+
/**
|
|
177
|
+
* Create a new tool with fluent builder
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* const weather = tool('weather')
|
|
181
|
+
* .description('Get weather for a city')
|
|
182
|
+
* .input(z.object({ city: z.string() }))
|
|
183
|
+
* .output(z.object({ temp: z.number() }))
|
|
184
|
+
* .server(async ({ city }) => ({ temp: 72 }));
|
|
185
|
+
*/
|
|
186
|
+
declare function tool(name: string): ToolBuilder;
|
|
187
|
+
declare class ToolBuilder<TInput = any, TOutput = any> {
|
|
188
|
+
private _name;
|
|
189
|
+
private _description?;
|
|
190
|
+
private _input?;
|
|
191
|
+
private _output?;
|
|
192
|
+
private _tags;
|
|
193
|
+
private _cache?;
|
|
194
|
+
private _clientFetch?;
|
|
195
|
+
private _serverHandler?;
|
|
196
|
+
private _clientHandler?;
|
|
197
|
+
private _viewComponent?;
|
|
198
|
+
constructor(name: string);
|
|
199
|
+
description(desc: string): this;
|
|
200
|
+
/**
|
|
201
|
+
* Define input schema - enables type inference for handlers
|
|
202
|
+
*
|
|
203
|
+
* NOTE: Uses type assertion internally. This is safe because:
|
|
204
|
+
* 1. The schema is stored and used correctly at runtime
|
|
205
|
+
* 2. The return type correctly reflects the new generic parameter
|
|
206
|
+
* 3. TypeScript doesn't support "this type mutation" in fluent builders
|
|
207
|
+
*/
|
|
208
|
+
input<T>(schema: z.ZodType<T>): ToolBuilder<T, TOutput>;
|
|
209
|
+
/**
|
|
210
|
+
* Define output schema - enables type inference for results
|
|
211
|
+
*/
|
|
212
|
+
output<O>(schema: z.ZodType<O>): ToolBuilder<TInput, O>;
|
|
213
|
+
tags(...tags: string[]): this;
|
|
214
|
+
cache(config: CacheConfig<TInput>): this;
|
|
215
|
+
/** Configure auto-fetch endpoint for client-side execution */
|
|
216
|
+
clientFetch(config: ClientFetchConfig): this;
|
|
217
|
+
server(handler: ServerHandler<TInput, TOutput>): this;
|
|
218
|
+
client(handler: ClientHandler<TInput, TOutput>): this;
|
|
219
|
+
view(component: ViewComponent<TOutput>): this;
|
|
220
|
+
/**
|
|
221
|
+
* Build the final Tool instance
|
|
222
|
+
*/
|
|
223
|
+
build(): Tool<TInput, TOutput>;
|
|
224
|
+
/**
|
|
225
|
+
* Auto-build when accessing Tool methods
|
|
226
|
+
*/
|
|
227
|
+
run(input: TInput, ctx?: Partial<ToolContext>): Promise<TOutput>;
|
|
228
|
+
get View(): React.NamedExoticComponent<{
|
|
229
|
+
data: TOutput | null;
|
|
230
|
+
loading?: boolean;
|
|
231
|
+
error?: Error | null;
|
|
232
|
+
onAction?: ((input: TInput) => void | Promise<void>) | undefined;
|
|
233
|
+
}>;
|
|
234
|
+
toJSON(): {
|
|
235
|
+
name: string;
|
|
236
|
+
description: string | undefined;
|
|
237
|
+
tags: string[];
|
|
238
|
+
hasServer: boolean;
|
|
239
|
+
hasClient: boolean;
|
|
240
|
+
hasView: boolean;
|
|
241
|
+
hasCache: boolean;
|
|
242
|
+
};
|
|
243
|
+
toAITool(): {
|
|
244
|
+
description: string;
|
|
245
|
+
inputSchema: z.ZodType<TInput, z.ZodTypeDef, TInput>;
|
|
246
|
+
execute: (input: TInput) => Promise<TOutput>;
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
interface UseToolOptions {
|
|
251
|
+
/** Auto-execute on mount or when input changes */
|
|
252
|
+
auto?: boolean;
|
|
253
|
+
/** Custom context overrides */
|
|
254
|
+
context?: Partial<ToolContext>;
|
|
255
|
+
/** Callback on success */
|
|
256
|
+
onSuccess?: (data: any) => void;
|
|
257
|
+
/** Callback on error */
|
|
258
|
+
onError?: (error: Error) => void;
|
|
259
|
+
}
|
|
260
|
+
interface UseToolResult<TInput, TOutput> {
|
|
261
|
+
/** The result data */
|
|
262
|
+
data: TOutput | null;
|
|
263
|
+
/** Loading state */
|
|
264
|
+
loading: boolean;
|
|
265
|
+
/** Error if any */
|
|
266
|
+
error: Error | null;
|
|
267
|
+
/** Execute the tool manually */
|
|
268
|
+
execute: (input?: TInput) => Promise<TOutput | null>;
|
|
269
|
+
/** Reset state */
|
|
270
|
+
reset: () => void;
|
|
271
|
+
/** Whether the tool has been executed at least once */
|
|
272
|
+
executed: boolean;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* React hook for executing tools
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* // Manual execution
|
|
279
|
+
* const { data, loading, execute } = useTool(weather);
|
|
280
|
+
* <button onClick={() => execute({ city: 'London' })}>Get Weather</button>
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* // Auto execution when input changes
|
|
284
|
+
* const { data, loading } = useTool(weather, { city }, { auto: true });
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* // With callbacks
|
|
288
|
+
* const { execute } = useTool(weather, undefined, {
|
|
289
|
+
* onSuccess: (data) => console.log('Got weather:', data),
|
|
290
|
+
* onError: (error) => console.error('Failed:', error),
|
|
291
|
+
* });
|
|
292
|
+
*/
|
|
293
|
+
declare function useTool<TInput, TOutput>(tool: Tool<TInput, TOutput>, initialInput?: TInput, options?: UseToolOptions): UseToolResult<TInput, TOutput>;
|
|
294
|
+
/**
|
|
295
|
+
* React hook for executing multiple tools
|
|
296
|
+
*
|
|
297
|
+
* This hook properly manages state for multiple tools using a single useState,
|
|
298
|
+
* avoiding the hooks-in-loop anti-pattern.
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* // Define tools outside component or memoize with useMemo
|
|
302
|
+
* const myTools = { weather, search };
|
|
303
|
+
*
|
|
304
|
+
* function MyComponent() {
|
|
305
|
+
* const tools = useTools(myTools);
|
|
306
|
+
* await tools.weather.execute({ city: 'London' });
|
|
307
|
+
* await tools.search.execute({ query: 'restaurants' });
|
|
308
|
+
* }
|
|
309
|
+
*/
|
|
310
|
+
declare function useTools<T extends Record<string, Tool>>(tools: T, options?: UseToolOptions): {
|
|
311
|
+
[K in keyof T]: UseToolResult<T[K] extends Tool<infer I, any> ? I : never, T[K] extends Tool<any, infer O> ? O : never>;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
export { type CacheConfig, type ClientFetchConfig, type ClientHandler, type ServerHandler, Tool, ToolBuilder, type ToolConfig, type ToolContext, type UseToolOptions, type UseToolResult, type ViewComponent, tool, useTool, useTools };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import React, { ReactElement } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Better UI v2 - Tool Definition
|
|
6
|
+
*
|
|
7
|
+
* Clean, type-safe tool definition inspired by TanStack AI,
|
|
8
|
+
* with Better UI's unique view integration.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Context passed to tool handlers
|
|
13
|
+
*
|
|
14
|
+
* SECURITY NOTE:
|
|
15
|
+
* - Server-only fields (env, headers, cookies, user, session) are automatically
|
|
16
|
+
* stripped when running on the client to prevent accidental leakage.
|
|
17
|
+
* - Server handlers NEVER run on the client - they auto-fetch via API instead.
|
|
18
|
+
* - Never store secrets in tool output - it gets sent to the client.
|
|
19
|
+
*/
|
|
20
|
+
interface ToolContext {
|
|
21
|
+
/** Shared cache for deduplication */
|
|
22
|
+
cache: Map<string, any>;
|
|
23
|
+
/** Fetch function (can be customized for auth headers, etc.) */
|
|
24
|
+
fetch: typeof fetch;
|
|
25
|
+
/** Whether running on server */
|
|
26
|
+
isServer: boolean;
|
|
27
|
+
/** Environment variables - NEVER available on client */
|
|
28
|
+
env?: Record<string, string>;
|
|
29
|
+
/** Request headers - NEVER available on client */
|
|
30
|
+
headers?: Headers;
|
|
31
|
+
/** Cookies - NEVER available on client */
|
|
32
|
+
cookies?: Record<string, string>;
|
|
33
|
+
/** Authenticated user - NEVER available on client */
|
|
34
|
+
user?: any;
|
|
35
|
+
/** Session data - NEVER available on client */
|
|
36
|
+
session?: any;
|
|
37
|
+
/** Optimistic update function */
|
|
38
|
+
optimistic?: <T>(data: Partial<T>) => void;
|
|
39
|
+
}
|
|
40
|
+
interface CacheConfig<TInput> {
|
|
41
|
+
ttl: number;
|
|
42
|
+
key?: (input: TInput) => string;
|
|
43
|
+
}
|
|
44
|
+
/** Configuration for auto-fetch behavior when no client handler is defined */
|
|
45
|
+
interface ClientFetchConfig {
|
|
46
|
+
/** API endpoint for tool execution (default: '/api/tools/execute') */
|
|
47
|
+
endpoint?: string;
|
|
48
|
+
}
|
|
49
|
+
interface ToolConfig<TInput, TOutput> {
|
|
50
|
+
name: string;
|
|
51
|
+
description?: string;
|
|
52
|
+
input: z.ZodType<TInput>;
|
|
53
|
+
output?: z.ZodType<TOutput>;
|
|
54
|
+
tags?: string[];
|
|
55
|
+
cache?: CacheConfig<TInput>;
|
|
56
|
+
/** Configure auto-fetch behavior for client-side execution */
|
|
57
|
+
clientFetch?: ClientFetchConfig;
|
|
58
|
+
}
|
|
59
|
+
type ServerHandler<TInput, TOutput> = (input: TInput, ctx: ToolContext) => Promise<TOutput> | TOutput;
|
|
60
|
+
type ClientHandler<TInput, TOutput> = (input: TInput, ctx: ToolContext) => Promise<TOutput> | TOutput;
|
|
61
|
+
type ViewState<TInput = any> = {
|
|
62
|
+
loading?: boolean;
|
|
63
|
+
error?: Error | null;
|
|
64
|
+
onAction?: (input: TInput) => void | Promise<void>;
|
|
65
|
+
};
|
|
66
|
+
type ViewComponent<TOutput, TInput = any> = (data: TOutput, state?: ViewState<TInput>) => ReactElement | null;
|
|
67
|
+
declare class Tool<TInput = any, TOutput = any> {
|
|
68
|
+
readonly name: string;
|
|
69
|
+
readonly description?: string;
|
|
70
|
+
readonly inputSchema: z.ZodType<TInput>;
|
|
71
|
+
readonly outputSchema?: z.ZodType<TOutput>;
|
|
72
|
+
readonly tags: string[];
|
|
73
|
+
readonly cacheConfig?: CacheConfig<TInput>;
|
|
74
|
+
readonly clientFetchConfig?: ClientFetchConfig;
|
|
75
|
+
private _server?;
|
|
76
|
+
private _client?;
|
|
77
|
+
private _view?;
|
|
78
|
+
constructor(config: ToolConfig<TInput, TOutput>);
|
|
79
|
+
/**
|
|
80
|
+
* Define server-side implementation
|
|
81
|
+
* Runs on server (API routes, server components, etc.)
|
|
82
|
+
*/
|
|
83
|
+
server(handler: ServerHandler<TInput, TOutput>): this;
|
|
84
|
+
/**
|
|
85
|
+
* Define client-side implementation
|
|
86
|
+
* Runs in browser. If not specified, auto-fetches to /api/tools/{name}
|
|
87
|
+
*/
|
|
88
|
+
client(handler: ClientHandler<TInput, TOutput>): this;
|
|
89
|
+
/**
|
|
90
|
+
* Define view component for rendering results
|
|
91
|
+
* Our differentiator from TanStack AI
|
|
92
|
+
*/
|
|
93
|
+
view(component: ViewComponent<TOutput>): this;
|
|
94
|
+
/**
|
|
95
|
+
* Execute the tool
|
|
96
|
+
* Automatically uses server or client handler based on environment
|
|
97
|
+
*
|
|
98
|
+
* SECURITY: Server handlers only run on server. Client automatically
|
|
99
|
+
* fetches from /api/tools/execute if no client handler is defined.
|
|
100
|
+
*/
|
|
101
|
+
run(input: TInput, ctx?: Partial<ToolContext>): Promise<TOutput>;
|
|
102
|
+
/**
|
|
103
|
+
* Make the tool callable directly: await weather({ city: 'London' })
|
|
104
|
+
*/
|
|
105
|
+
call(input: TInput, ctx?: Partial<ToolContext>): Promise<TOutput>;
|
|
106
|
+
/**
|
|
107
|
+
* Default client fetch when no .client() is defined
|
|
108
|
+
*
|
|
109
|
+
* SECURITY: This ensures server handlers never run on the client.
|
|
110
|
+
* The server-side /api/tools/execute endpoint handles execution safely.
|
|
111
|
+
*/
|
|
112
|
+
private _defaultClientFetch;
|
|
113
|
+
/**
|
|
114
|
+
* Render the tool's view component
|
|
115
|
+
* Memoized to prevent unnecessary re-renders when parent state changes
|
|
116
|
+
*/
|
|
117
|
+
View: React.NamedExoticComponent<{
|
|
118
|
+
data: TOutput | null;
|
|
119
|
+
loading?: boolean;
|
|
120
|
+
error?: Error | null;
|
|
121
|
+
onAction?: (input: TInput) => void | Promise<void>;
|
|
122
|
+
}>;
|
|
123
|
+
private _initView;
|
|
124
|
+
/**
|
|
125
|
+
* Check if tool has a view
|
|
126
|
+
*/
|
|
127
|
+
get hasView(): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Check if tool has server implementation
|
|
130
|
+
*/
|
|
131
|
+
get hasServer(): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Check if tool has custom client implementation
|
|
134
|
+
*/
|
|
135
|
+
get hasClient(): boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Convert to plain object (for serialization)
|
|
138
|
+
*
|
|
139
|
+
* SECURITY: This intentionally excludes handlers and schemas to prevent
|
|
140
|
+
* accidental exposure of server logic or validation details.
|
|
141
|
+
*/
|
|
142
|
+
toJSON(): {
|
|
143
|
+
name: string;
|
|
144
|
+
description: string | undefined;
|
|
145
|
+
tags: string[];
|
|
146
|
+
hasServer: boolean;
|
|
147
|
+
hasClient: boolean;
|
|
148
|
+
hasView: boolean;
|
|
149
|
+
hasCache: boolean;
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Convert to AI SDK format (Vercel AI SDK v5 compatible)
|
|
153
|
+
*/
|
|
154
|
+
toAITool(): {
|
|
155
|
+
description: string;
|
|
156
|
+
inputSchema: z.ZodType<TInput, z.ZodTypeDef, TInput>;
|
|
157
|
+
execute: (input: TInput) => Promise<TOutput>;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Create a new tool with object config
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* const weather = tool({
|
|
165
|
+
* name: 'weather',
|
|
166
|
+
* description: 'Get weather for a city',
|
|
167
|
+
* input: z.object({ city: z.string() }),
|
|
168
|
+
* output: z.object({ temp: z.number() }),
|
|
169
|
+
* });
|
|
170
|
+
*
|
|
171
|
+
* weather.server(async ({ city }) => {
|
|
172
|
+
* return { temp: await getTemp(city) };
|
|
173
|
+
* });
|
|
174
|
+
*/
|
|
175
|
+
declare function tool<TInput, TOutput = any>(config: ToolConfig<TInput, TOutput>): Tool<TInput, TOutput>;
|
|
176
|
+
/**
|
|
177
|
+
* Create a new tool with fluent builder
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* const weather = tool('weather')
|
|
181
|
+
* .description('Get weather for a city')
|
|
182
|
+
* .input(z.object({ city: z.string() }))
|
|
183
|
+
* .output(z.object({ temp: z.number() }))
|
|
184
|
+
* .server(async ({ city }) => ({ temp: 72 }));
|
|
185
|
+
*/
|
|
186
|
+
declare function tool(name: string): ToolBuilder;
|
|
187
|
+
declare class ToolBuilder<TInput = any, TOutput = any> {
|
|
188
|
+
private _name;
|
|
189
|
+
private _description?;
|
|
190
|
+
private _input?;
|
|
191
|
+
private _output?;
|
|
192
|
+
private _tags;
|
|
193
|
+
private _cache?;
|
|
194
|
+
private _clientFetch?;
|
|
195
|
+
private _serverHandler?;
|
|
196
|
+
private _clientHandler?;
|
|
197
|
+
private _viewComponent?;
|
|
198
|
+
constructor(name: string);
|
|
199
|
+
description(desc: string): this;
|
|
200
|
+
/**
|
|
201
|
+
* Define input schema - enables type inference for handlers
|
|
202
|
+
*
|
|
203
|
+
* NOTE: Uses type assertion internally. This is safe because:
|
|
204
|
+
* 1. The schema is stored and used correctly at runtime
|
|
205
|
+
* 2. The return type correctly reflects the new generic parameter
|
|
206
|
+
* 3. TypeScript doesn't support "this type mutation" in fluent builders
|
|
207
|
+
*/
|
|
208
|
+
input<T>(schema: z.ZodType<T>): ToolBuilder<T, TOutput>;
|
|
209
|
+
/**
|
|
210
|
+
* Define output schema - enables type inference for results
|
|
211
|
+
*/
|
|
212
|
+
output<O>(schema: z.ZodType<O>): ToolBuilder<TInput, O>;
|
|
213
|
+
tags(...tags: string[]): this;
|
|
214
|
+
cache(config: CacheConfig<TInput>): this;
|
|
215
|
+
/** Configure auto-fetch endpoint for client-side execution */
|
|
216
|
+
clientFetch(config: ClientFetchConfig): this;
|
|
217
|
+
server(handler: ServerHandler<TInput, TOutput>): this;
|
|
218
|
+
client(handler: ClientHandler<TInput, TOutput>): this;
|
|
219
|
+
view(component: ViewComponent<TOutput>): this;
|
|
220
|
+
/**
|
|
221
|
+
* Build the final Tool instance
|
|
222
|
+
*/
|
|
223
|
+
build(): Tool<TInput, TOutput>;
|
|
224
|
+
/**
|
|
225
|
+
* Auto-build when accessing Tool methods
|
|
226
|
+
*/
|
|
227
|
+
run(input: TInput, ctx?: Partial<ToolContext>): Promise<TOutput>;
|
|
228
|
+
get View(): React.NamedExoticComponent<{
|
|
229
|
+
data: TOutput | null;
|
|
230
|
+
loading?: boolean;
|
|
231
|
+
error?: Error | null;
|
|
232
|
+
onAction?: ((input: TInput) => void | Promise<void>) | undefined;
|
|
233
|
+
}>;
|
|
234
|
+
toJSON(): {
|
|
235
|
+
name: string;
|
|
236
|
+
description: string | undefined;
|
|
237
|
+
tags: string[];
|
|
238
|
+
hasServer: boolean;
|
|
239
|
+
hasClient: boolean;
|
|
240
|
+
hasView: boolean;
|
|
241
|
+
hasCache: boolean;
|
|
242
|
+
};
|
|
243
|
+
toAITool(): {
|
|
244
|
+
description: string;
|
|
245
|
+
inputSchema: z.ZodType<TInput, z.ZodTypeDef, TInput>;
|
|
246
|
+
execute: (input: TInput) => Promise<TOutput>;
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
interface UseToolOptions {
|
|
251
|
+
/** Auto-execute on mount or when input changes */
|
|
252
|
+
auto?: boolean;
|
|
253
|
+
/** Custom context overrides */
|
|
254
|
+
context?: Partial<ToolContext>;
|
|
255
|
+
/** Callback on success */
|
|
256
|
+
onSuccess?: (data: any) => void;
|
|
257
|
+
/** Callback on error */
|
|
258
|
+
onError?: (error: Error) => void;
|
|
259
|
+
}
|
|
260
|
+
interface UseToolResult<TInput, TOutput> {
|
|
261
|
+
/** The result data */
|
|
262
|
+
data: TOutput | null;
|
|
263
|
+
/** Loading state */
|
|
264
|
+
loading: boolean;
|
|
265
|
+
/** Error if any */
|
|
266
|
+
error: Error | null;
|
|
267
|
+
/** Execute the tool manually */
|
|
268
|
+
execute: (input?: TInput) => Promise<TOutput | null>;
|
|
269
|
+
/** Reset state */
|
|
270
|
+
reset: () => void;
|
|
271
|
+
/** Whether the tool has been executed at least once */
|
|
272
|
+
executed: boolean;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* React hook for executing tools
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* // Manual execution
|
|
279
|
+
* const { data, loading, execute } = useTool(weather);
|
|
280
|
+
* <button onClick={() => execute({ city: 'London' })}>Get Weather</button>
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* // Auto execution when input changes
|
|
284
|
+
* const { data, loading } = useTool(weather, { city }, { auto: true });
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* // With callbacks
|
|
288
|
+
* const { execute } = useTool(weather, undefined, {
|
|
289
|
+
* onSuccess: (data) => console.log('Got weather:', data),
|
|
290
|
+
* onError: (error) => console.error('Failed:', error),
|
|
291
|
+
* });
|
|
292
|
+
*/
|
|
293
|
+
declare function useTool<TInput, TOutput>(tool: Tool<TInput, TOutput>, initialInput?: TInput, options?: UseToolOptions): UseToolResult<TInput, TOutput>;
|
|
294
|
+
/**
|
|
295
|
+
* React hook for executing multiple tools
|
|
296
|
+
*
|
|
297
|
+
* This hook properly manages state for multiple tools using a single useState,
|
|
298
|
+
* avoiding the hooks-in-loop anti-pattern.
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* // Define tools outside component or memoize with useMemo
|
|
302
|
+
* const myTools = { weather, search };
|
|
303
|
+
*
|
|
304
|
+
* function MyComponent() {
|
|
305
|
+
* const tools = useTools(myTools);
|
|
306
|
+
* await tools.weather.execute({ city: 'London' });
|
|
307
|
+
* await tools.search.execute({ query: 'restaurants' });
|
|
308
|
+
* }
|
|
309
|
+
*/
|
|
310
|
+
declare function useTools<T extends Record<string, Tool>>(tools: T, options?: UseToolOptions): {
|
|
311
|
+
[K in keyof T]: UseToolResult<T[K] extends Tool<infer I, any> ? I : never, T[K] extends Tool<any, infer O> ? O : never>;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
export { type CacheConfig, type ClientFetchConfig, type ClientHandler, type ServerHandler, Tool, ToolBuilder, type ToolConfig, type ToolContext, type UseToolOptions, type UseToolResult, type ViewComponent, tool, useTool, useTools };
|