@mcp-b/embedded-agent 1.2.7 → 1.2.9

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 CHANGED
@@ -13,6 +13,15 @@ This package provides the `<webmcp-agent>` custom element in two formats:
13
13
  | `@mcp-b/embedded-agent` / `@mcp-b/embedded-agent/web-component` | ESM web component | Bundlers, monorepo dev |
14
14
  | `@mcp-b/embedded-agent/standalone` | IIFE web component | `<script>` tag embeds |
15
15
 
16
+ ### Bundle Sizes
17
+
18
+ | Build | Size | Gzipped | Use Case |
19
+ |-------|------|---------|----------|
20
+ | ESM | ~560 KB | ~115 KB | Bundlers (React externalized) |
21
+ | Standalone IIFE | ~2.2 MB | ~400 KB | Script tag embeds |
22
+
23
+ The standalone IIFE includes React, so it's larger but works on any website without dependencies.
24
+
16
25
  ### Why Two Builds?
17
26
 
18
27
  **ESM Build** (root export or `/web-component`)
@@ -90,11 +99,26 @@ useEffect(() => {
90
99
 
91
100
  ### Script Tag (Any Website)
92
101
 
102
+ Use `defer` for best performance - it loads the script without blocking page rendering:
103
+
93
104
  ```html
94
- <script src="https://cdn.webmcp.ai/agent.js"></script>
105
+ <!-- Recommended: defer loads async, executes after DOM ready -->
106
+ <script src="https://unpkg.com/@mcp-b/embedded-agent/dist/web-component-standalone.iife.js" defer></script>
95
107
  <webmcp-agent auth-token="eyJhbGciOi..."></webmcp-agent>
96
108
  ```
97
109
 
110
+ Alternative CDN (jsdelivr):
111
+
112
+ ```html
113
+ <script src="https://cdn.jsdelivr.net/npm/@mcp-b/embedded-agent/dist/web-component-standalone.iife.js" defer></script>
114
+ ```
115
+
116
+ Pin to a specific version for production:
117
+
118
+ ```html
119
+ <script src="https://unpkg.com/@mcp-b/embedded-agent@1.2.7/dist/web-component-standalone.iife.js" defer></script>
120
+ ```
121
+
98
122
  ### Web Component Attributes
99
123
 
100
124
  ```html
@@ -190,6 +214,35 @@ Common combinations:
190
214
  - `{ anthropicApiKey: "sk-ant-..." }` - External dev with your own key
191
215
  - `{ anthropicApiKey: "...", openaiApiKey: "...", useLocalApi: true }` - Full stack local dev
192
216
 
217
+ ## Performance
218
+
219
+ ### Script Loading
220
+
221
+ Always use `defer` or `async` when embedding the standalone script to avoid blocking page rendering:
222
+
223
+ ```html
224
+ <!-- GOOD: defer - loads in parallel, executes after DOM ready -->
225
+ <script src="https://unpkg.com/@mcp-b/embedded-agent/dist/web-component-standalone.iife.js" defer></script>
226
+
227
+ <!-- GOOD: async - loads in parallel, executes ASAP -->
228
+ <script src="https://unpkg.com/@mcp-b/embedded-agent/dist/web-component-standalone.iife.js" async></script>
229
+
230
+ <!-- BAD: blocks page rendering until script loads -->
231
+ <script src="https://unpkg.com/@mcp-b/embedded-agent/dist/web-component-standalone.iife.js"></script>
232
+ ```
233
+
234
+ **When to use which:**
235
+ - `defer` (recommended) - Script executes after HTML is parsed, preserves execution order
236
+ - `async` - Script executes as soon as it loads, good for independent widgets
237
+
238
+ ### Preconnect Hint
239
+
240
+ Speed up loading by adding a preconnect hint in your `<head>`:
241
+
242
+ ```html
243
+ <link rel="preconnect" href="https://unpkg.com" crossorigin>
244
+ ```
245
+
193
246
  ## Features
194
247
 
195
248
  - **MCP Tool Support** - Connect to any MCP server
@@ -0,0 +1,625 @@
1
+ import { c } from "react-compiler-runtime";
2
+ import { createContext, useContext, useEffect, useRef, useState } from "react";
3
+ import { AlertCircle, Check, ChevronDownIcon, ChevronUpIcon, FileText } from "lucide-react";
4
+ import { makeAssistantToolUI } from "@assistant-ui/react";
5
+ import { clsx } from "clsx";
6
+ import { twMerge } from "tailwind-merge";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { Slot } from "@radix-ui/react-slot";
9
+ import { cva } from "class-variance-authority";
10
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
11
+ import Ajv from "ajv";
12
+ import { withTheme } from "@rjsf/core";
13
+ import { Theme } from "@rjsf/shadcn";
14
+ import validator from "@rjsf/validator-ajv8";
15
+
16
+ //#region src/utils/cn.ts
17
+ /**
18
+ * Merges Tailwind CSS classes with proper precedence handling.
19
+ * Combines clsx for conditional classes and tailwind-merge for deduplication.
20
+ */
21
+ function cn(...inputs) {
22
+ return twMerge(clsx(inputs));
23
+ }
24
+
25
+ //#endregion
26
+ //#region src/components/ui/button.tsx
27
+ const buttonVariants = cva("inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
28
+ variants: {
29
+ variant: {
30
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
31
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
32
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
33
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
34
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
35
+ link: "text-primary underline-offset-4 hover:underline"
36
+ },
37
+ size: {
38
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
39
+ sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
40
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
41
+ icon: "size-9",
42
+ "icon-sm": "size-8",
43
+ "icon-lg": "size-10"
44
+ }
45
+ },
46
+ defaultVariants: {
47
+ variant: "default",
48
+ size: "default"
49
+ }
50
+ });
51
+ function Button(t0) {
52
+ const $ = c(16);
53
+ let className;
54
+ let props;
55
+ let t1;
56
+ let t2;
57
+ let t3;
58
+ if ($[0] !== t0) {
59
+ ({className, variant: t1, size: t2, asChild: t3, ...props} = t0);
60
+ $[0] = t0;
61
+ $[1] = className;
62
+ $[2] = props;
63
+ $[3] = t1;
64
+ $[4] = t2;
65
+ $[5] = t3;
66
+ } else {
67
+ className = $[1];
68
+ props = $[2];
69
+ t1 = $[3];
70
+ t2 = $[4];
71
+ t3 = $[5];
72
+ }
73
+ const variant = t1 === void 0 ? "default" : t1;
74
+ const size = t2 === void 0 ? "default" : t2;
75
+ const Comp = (t3 === void 0 ? false : t3) ? Slot : "button";
76
+ let t4;
77
+ if ($[6] !== className || $[7] !== size || $[8] !== variant) {
78
+ t4 = cn(buttonVariants({
79
+ variant,
80
+ size,
81
+ className
82
+ }));
83
+ $[6] = className;
84
+ $[7] = size;
85
+ $[8] = variant;
86
+ $[9] = t4;
87
+ } else t4 = $[9];
88
+ let t5;
89
+ if ($[10] !== Comp || $[11] !== props || $[12] !== size || $[13] !== t4 || $[14] !== variant) {
90
+ t5 = /* @__PURE__ */ jsx(Comp, {
91
+ "data-slot": "button",
92
+ "data-variant": variant,
93
+ "data-size": size,
94
+ className: t4,
95
+ ...props
96
+ });
97
+ $[10] = Comp;
98
+ $[11] = props;
99
+ $[12] = size;
100
+ $[13] = t4;
101
+ $[14] = variant;
102
+ $[15] = t5;
103
+ } else t5 = $[15];
104
+ return t5;
105
+ }
106
+
107
+ //#endregion
108
+ //#region src/components/ui/collapsible.tsx
109
+ function Collapsible(t0) {
110
+ const $ = c(4);
111
+ let props;
112
+ if ($[0] !== t0) {
113
+ ({...props} = t0);
114
+ $[0] = t0;
115
+ $[1] = props;
116
+ } else props = $[1];
117
+ let t1;
118
+ if ($[2] !== props) {
119
+ t1 = /* @__PURE__ */ jsx(CollapsiblePrimitive.Root, {
120
+ "data-slot": "collapsible",
121
+ ...props
122
+ });
123
+ $[2] = props;
124
+ $[3] = t1;
125
+ } else t1 = $[3];
126
+ return t1;
127
+ }
128
+ function CollapsibleTrigger(t0) {
129
+ const $ = c(4);
130
+ let props;
131
+ if ($[0] !== t0) {
132
+ ({...props} = t0);
133
+ $[0] = t0;
134
+ $[1] = props;
135
+ } else props = $[1];
136
+ let t1;
137
+ if ($[2] !== props) {
138
+ t1 = /* @__PURE__ */ jsx(CollapsiblePrimitive.CollapsibleTrigger, {
139
+ "data-slot": "collapsible-trigger",
140
+ ...props
141
+ });
142
+ $[2] = props;
143
+ $[3] = t1;
144
+ } else t1 = $[3];
145
+ return t1;
146
+ }
147
+ function CollapsibleContent(t0) {
148
+ const $ = c(4);
149
+ let props;
150
+ if ($[0] !== t0) {
151
+ ({...props} = t0);
152
+ $[0] = t0;
153
+ $[1] = props;
154
+ } else props = $[1];
155
+ let t1;
156
+ if ($[2] !== props) {
157
+ t1 = /* @__PURE__ */ jsx(CollapsiblePrimitive.CollapsibleContent, {
158
+ "data-slot": "collapsible-content",
159
+ ...props
160
+ });
161
+ $[2] = props;
162
+ $[3] = t1;
163
+ } else t1 = $[3];
164
+ return t1;
165
+ }
166
+
167
+ //#endregion
168
+ //#region src/providers/ToolExecutionProvider.tsx
169
+ const ToolExecutionContext = createContext(null);
170
+ const ToolExecutionProvider = (t0) => {
171
+ const $ = c(3);
172
+ const { value, children } = t0;
173
+ let t1;
174
+ if ($[0] !== children || $[1] !== value) {
175
+ t1 = /* @__PURE__ */ jsx(ToolExecutionContext.Provider, {
176
+ value,
177
+ children
178
+ });
179
+ $[0] = children;
180
+ $[1] = value;
181
+ $[2] = t1;
182
+ } else t1 = $[2];
183
+ return t1;
184
+ };
185
+ function useOptionalToolExecution() {
186
+ return useContext(ToolExecutionContext);
187
+ }
188
+
189
+ //#endregion
190
+ //#region src/tools/FormToolUI.tsx
191
+ const Form = withTheme(Theme);
192
+ const ajv = new Ajv();
193
+ /**
194
+ * Normalize a JSON Schema to handle common model mistakes.
195
+ * - Converts uppercase types (Gemini format) to lowercase (JSON Schema format)
196
+ * - Recursively processes nested schemas
197
+ */
198
+ function normalizeSchema(schema) {
199
+ if (!schema || typeof schema !== "object") return schema;
200
+ const obj = { ...schema };
201
+ if (typeof obj.type === "string") obj.type = obj.type.toLowerCase();
202
+ if (obj.properties && typeof obj.properties === "object") obj.properties = Object.fromEntries(Object.entries(obj.properties).map(([key, value]) => [key, normalizeSchema(value)]));
203
+ if (obj.items) if (Array.isArray(obj.items)) obj.items = obj.items.map(normalizeSchema);
204
+ else obj.items = normalizeSchema(obj.items);
205
+ if (obj.additionalProperties && typeof obj.additionalProperties === "object") obj.additionalProperties = normalizeSchema(obj.additionalProperties);
206
+ for (const keyword of [
207
+ "allOf",
208
+ "anyOf",
209
+ "oneOf"
210
+ ]) if (Array.isArray(obj[keyword])) obj[keyword] = obj[keyword].map(normalizeSchema);
211
+ if (obj.not && typeof obj.not === "object") obj.not = normalizeSchema(obj.not);
212
+ return obj;
213
+ }
214
+ /**
215
+ * Validate a JSON Schema using AJV's meta-schema validation.
216
+ * Returns null if valid, or an error message string if invalid.
217
+ */
218
+ function validateSchema(schema) {
219
+ if (ajv.validateSchema(schema)) return null;
220
+ return [
221
+ "Invalid JSON Schema:",
222
+ ...(ajv.errors ?? []).map((err) => {
223
+ return `- ${err.instancePath || "root"}: ${err.message}`;
224
+ }),
225
+ "",
226
+ "Please fix the schema and try again."
227
+ ].join("\n");
228
+ }
229
+ /**
230
+ * Display component for schema validation errors
231
+ */
232
+ function SchemaErrorDisplay(t0) {
233
+ const $ = c(16);
234
+ const { title, error } = t0;
235
+ const [isOpen, setIsOpen] = useState(true);
236
+ let t1;
237
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
238
+ t1 = /* @__PURE__ */ jsxs("div", {
239
+ className: "relative",
240
+ children: [/* @__PURE__ */ jsx(FileText, { className: "h-3.5 w-3.5 text-destructive" }), /* @__PURE__ */ jsx(AlertCircle, { className: "absolute -bottom-0.5 -right-0.5 h-2 w-2 text-destructive" })]
241
+ });
242
+ $[0] = t1;
243
+ } else t1 = $[0];
244
+ let t2;
245
+ if ($[1] !== title) {
246
+ t2 = /* @__PURE__ */ jsxs("span", {
247
+ className: "flex-grow text-xs font-medium text-destructive",
248
+ children: [title, " - Invalid Schema"]
249
+ });
250
+ $[1] = title;
251
+ $[2] = t2;
252
+ } else t2 = $[2];
253
+ let t3;
254
+ if ($[3] !== isOpen) {
255
+ t3 = /* @__PURE__ */ jsx("span", {
256
+ className: "inline-flex h-5 w-5 items-center justify-center p-0",
257
+ children: isOpen ? /* @__PURE__ */ jsx(ChevronUpIcon, { className: "h-3 w-3 text-destructive/70" }) : /* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-3 w-3 text-destructive/70" })
258
+ });
259
+ $[3] = isOpen;
260
+ $[4] = t3;
261
+ } else t3 = $[4];
262
+ let t4;
263
+ if ($[5] !== t2 || $[6] !== t3) {
264
+ t4 = /* @__PURE__ */ jsx(CollapsibleTrigger, {
265
+ asChild: true,
266
+ children: /* @__PURE__ */ jsxs("button", {
267
+ type: "button",
268
+ className: "flex w-full items-center gap-1.5 px-2 py-1.5 text-left transition-colors hover:bg-destructive/15",
269
+ children: [
270
+ t1,
271
+ t2,
272
+ t3
273
+ ]
274
+ })
275
+ });
276
+ $[5] = t2;
277
+ $[6] = t3;
278
+ $[7] = t4;
279
+ } else t4 = $[7];
280
+ let t5;
281
+ if ($[8] !== error) {
282
+ t5 = /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", {
283
+ className: "border-t border-destructive/20 px-2 py-1.5",
284
+ children: /* @__PURE__ */ jsx("pre", {
285
+ className: "whitespace-pre-wrap text-[11px] text-foreground/70",
286
+ children: error
287
+ })
288
+ }) });
289
+ $[8] = error;
290
+ $[9] = t5;
291
+ } else t5 = $[9];
292
+ let t6;
293
+ if ($[10] !== t4 || $[11] !== t5) {
294
+ t6 = /* @__PURE__ */ jsxs("div", {
295
+ className: "my-1 overflow-hidden rounded-lg bg-destructive/10 border border-destructive/20",
296
+ children: [t4, t5]
297
+ });
298
+ $[10] = t4;
299
+ $[11] = t5;
300
+ $[12] = t6;
301
+ } else t6 = $[12];
302
+ let t7;
303
+ if ($[13] !== isOpen || $[14] !== t6) {
304
+ t7 = /* @__PURE__ */ jsx(Collapsible, {
305
+ open: isOpen,
306
+ onOpenChange: setIsOpen,
307
+ children: t6
308
+ });
309
+ $[13] = isOpen;
310
+ $[14] = t6;
311
+ $[15] = t7;
312
+ } else t7 = $[15];
313
+ return t7;
314
+ }
315
+ /**
316
+ * Parse form result data, handling cases where it may be stringified
317
+ */
318
+ function parseFormData(data) {
319
+ if (typeof data === "object" && data !== null) {
320
+ if (Object.keys(data).some((key) => isNaN(Number(key)))) return data;
321
+ }
322
+ if (typeof data === "string") try {
323
+ const parsed = JSON.parse(data);
324
+ if (typeof parsed === "object" && parsed !== null) return parsed;
325
+ } catch (error) {
326
+ console.warn("[FormToolUI] Failed to parse form data as JSON:", {
327
+ data: data.substring(0, 100),
328
+ error: error instanceof Error ? error.message : "Unknown parse error"
329
+ });
330
+ return { value: data };
331
+ }
332
+ return typeof data === "object" && data !== null ? data : { value: String(data) };
333
+ }
334
+ /**
335
+ * Format a value for display
336
+ */
337
+ function formatValue(value) {
338
+ if (value === null || value === void 0) return "—";
339
+ if (typeof value === "boolean") return value ? "Yes" : "No";
340
+ if (typeof value === "object") return JSON.stringify(value);
341
+ return String(value);
342
+ }
343
+ /**
344
+ * Display component for submitted form data - matches UnifiedToolFallback pattern
345
+ */
346
+ function FormResultDisplay(t0) {
347
+ const $ = c(41);
348
+ const { title, data } = t0;
349
+ const [isOpen, setIsOpen] = useState(false);
350
+ let T0;
351
+ let T1;
352
+ let t1;
353
+ let t2;
354
+ let t3;
355
+ let t4;
356
+ let t5;
357
+ let t6;
358
+ let t7;
359
+ let t8;
360
+ if ($[0] !== data || $[1] !== isOpen || $[2] !== title) {
361
+ const parsedData = parseFormData(data);
362
+ const entries = Object.entries(parsedData);
363
+ T1 = Collapsible;
364
+ t7 = isOpen;
365
+ t8 = setIsOpen;
366
+ t5 = "my-1 overflow-hidden rounded-lg bg-muted/50";
367
+ let t9$1;
368
+ if ($[13] === Symbol.for("react.memo_cache_sentinel")) {
369
+ t9$1 = /* @__PURE__ */ jsxs("div", {
370
+ className: "relative",
371
+ children: [/* @__PURE__ */ jsx(FileText, { className: "h-3.5 w-3.5 text-muted-foreground" }), /* @__PURE__ */ jsx(Check, { className: "absolute -bottom-0.5 -right-0.5 h-2 w-2 text-muted-foreground" })]
372
+ });
373
+ $[13] = t9$1;
374
+ } else t9$1 = $[13];
375
+ let t10$1;
376
+ if ($[14] !== title) {
377
+ t10$1 = /* @__PURE__ */ jsx("span", {
378
+ className: "flex-grow text-xs font-medium text-foreground/80",
379
+ children: title
380
+ });
381
+ $[14] = title;
382
+ $[15] = t10$1;
383
+ } else t10$1 = $[15];
384
+ let t11$1;
385
+ if ($[16] !== isOpen) {
386
+ t11$1 = /* @__PURE__ */ jsx("span", {
387
+ className: "inline-flex h-5 w-5 items-center justify-center p-0",
388
+ children: isOpen ? /* @__PURE__ */ jsx(ChevronUpIcon, { className: "h-3 w-3 text-muted-foreground/70" }) : /* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-3 w-3 text-muted-foreground/70" })
389
+ });
390
+ $[16] = isOpen;
391
+ $[17] = t11$1;
392
+ } else t11$1 = $[17];
393
+ if ($[18] !== t10$1 || $[19] !== t11$1) {
394
+ t6 = /* @__PURE__ */ jsx(CollapsibleTrigger, {
395
+ asChild: true,
396
+ children: /* @__PURE__ */ jsxs("button", {
397
+ type: "button",
398
+ className: "flex w-full items-center gap-1.5 px-2 py-1.5 text-left transition-colors hover:bg-muted/70",
399
+ children: [
400
+ t9$1,
401
+ t10$1,
402
+ t11$1
403
+ ]
404
+ })
405
+ });
406
+ $[18] = t10$1;
407
+ $[19] = t11$1;
408
+ $[20] = t6;
409
+ } else t6 = $[20];
410
+ T0 = CollapsibleContent;
411
+ t3 = "border-t border-border px-2 py-1.5";
412
+ if ($[21] === Symbol.for("react.memo_cache_sentinel")) {
413
+ t4 = /* @__PURE__ */ jsx("div", {
414
+ className: "mb-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground/70",
415
+ children: "Submitted data"
416
+ });
417
+ $[21] = t4;
418
+ } else t4 = $[21];
419
+ t1 = "rounded bg-muted/50 p-1.5 space-y-0.5";
420
+ t2 = entries.map(_temp);
421
+ $[0] = data;
422
+ $[1] = isOpen;
423
+ $[2] = title;
424
+ $[3] = T0;
425
+ $[4] = T1;
426
+ $[5] = t1;
427
+ $[6] = t2;
428
+ $[7] = t3;
429
+ $[8] = t4;
430
+ $[9] = t5;
431
+ $[10] = t6;
432
+ $[11] = t7;
433
+ $[12] = t8;
434
+ } else {
435
+ T0 = $[3];
436
+ T1 = $[4];
437
+ t1 = $[5];
438
+ t2 = $[6];
439
+ t3 = $[7];
440
+ t4 = $[8];
441
+ t5 = $[9];
442
+ t6 = $[10];
443
+ t7 = $[11];
444
+ t8 = $[12];
445
+ }
446
+ let t9;
447
+ if ($[22] !== t1 || $[23] !== t2) {
448
+ t9 = /* @__PURE__ */ jsx("div", {
449
+ className: t1,
450
+ children: t2
451
+ });
452
+ $[22] = t1;
453
+ $[23] = t2;
454
+ $[24] = t9;
455
+ } else t9 = $[24];
456
+ let t10;
457
+ if ($[25] !== t3 || $[26] !== t4 || $[27] !== t9) {
458
+ t10 = /* @__PURE__ */ jsxs("div", {
459
+ className: t3,
460
+ children: [t4, t9]
461
+ });
462
+ $[25] = t3;
463
+ $[26] = t4;
464
+ $[27] = t9;
465
+ $[28] = t10;
466
+ } else t10 = $[28];
467
+ let t11;
468
+ if ($[29] !== T0 || $[30] !== t10) {
469
+ t11 = /* @__PURE__ */ jsx(T0, { children: t10 });
470
+ $[29] = T0;
471
+ $[30] = t10;
472
+ $[31] = t11;
473
+ } else t11 = $[31];
474
+ let t12;
475
+ if ($[32] !== t11 || $[33] !== t5 || $[34] !== t6) {
476
+ t12 = /* @__PURE__ */ jsxs("div", {
477
+ className: t5,
478
+ children: [t6, t11]
479
+ });
480
+ $[32] = t11;
481
+ $[33] = t5;
482
+ $[34] = t6;
483
+ $[35] = t12;
484
+ } else t12 = $[35];
485
+ let t13;
486
+ if ($[36] !== T1 || $[37] !== t12 || $[38] !== t7 || $[39] !== t8) {
487
+ t13 = /* @__PURE__ */ jsx(T1, {
488
+ open: t7,
489
+ onOpenChange: t8,
490
+ children: t12
491
+ });
492
+ $[36] = T1;
493
+ $[37] = t12;
494
+ $[38] = t7;
495
+ $[39] = t8;
496
+ $[40] = t13;
497
+ } else t13 = $[40];
498
+ return t13;
499
+ }
500
+ /**
501
+ * FormToolUI - Renders a dynamic form from JSON Schema
502
+ *
503
+ * HITL Pattern (AI SDK v6):
504
+ * - Model calls `form` tool with JSON Schema
505
+ * - This UI renders the form using @rjsf/shadcn
506
+ * - User fills out and submits form
507
+ * - addToolOutput() sends data to backend via WebSocket + updates local UI state
508
+ *
509
+ * Schema Handling:
510
+ * - Normalizes uppercase types (Gemini format) to lowercase (JSON Schema)
511
+ * - Validates schema with AJV and sends errors back to model if invalid
512
+ */
513
+ function _temp(t0) {
514
+ const [key, value] = t0;
515
+ return /* @__PURE__ */ jsxs("div", {
516
+ className: "flex gap-2 text-[11px]",
517
+ children: [/* @__PURE__ */ jsxs("span", {
518
+ className: "font-medium text-muted-foreground",
519
+ children: [key, ":"]
520
+ }), /* @__PURE__ */ jsx("span", {
521
+ className: "text-foreground/70",
522
+ children: formatValue(value)
523
+ })]
524
+ }, key);
525
+ }
526
+ const FormToolUI = makeAssistantToolUI({
527
+ toolName: "form",
528
+ render: ({ args, result, status, addResult, toolCallId }) => {
529
+ const toolExecution = useOptionalToolExecution();
530
+ const errorSentRef = useRef(false);
531
+ const [schemaError, setSchemaError] = useState(null);
532
+ const isWaiting = status.type === "requires-action";
533
+ const normalizedSchema = args.schema && typeof args.schema === "object" ? normalizeSchema(args.schema) : null;
534
+ useEffect(() => {
535
+ if (!normalizedSchema || !isWaiting || errorSentRef.current || result) return;
536
+ const validationError = validateSchema(normalizedSchema);
537
+ if (validationError) {
538
+ errorSentRef.current = true;
539
+ setSchemaError(validationError);
540
+ addResult({
541
+ _schemaError: true,
542
+ message: validationError
543
+ });
544
+ if (toolExecution) toolExecution.addToolOutput({
545
+ toolCallId,
546
+ output: `Error: ${validationError}`
547
+ });
548
+ else console.error("[FormToolUI] Cannot send schema error: ToolExecution context not available");
549
+ }
550
+ }, [
551
+ normalizedSchema,
552
+ isWaiting,
553
+ result,
554
+ addResult,
555
+ toolExecution,
556
+ toolCallId
557
+ ]);
558
+ if (schemaError) return /* @__PURE__ */ jsx(SchemaErrorDisplay, {
559
+ title: args.title ?? "Form",
560
+ error: schemaError
561
+ });
562
+ if (result) return /* @__PURE__ */ jsx(FormResultDisplay, {
563
+ title: args.title ?? "Form",
564
+ data: result
565
+ });
566
+ const handleSubmit = ({ formData }) => {
567
+ if (!formData) return;
568
+ if (!toolExecution) {
569
+ console.error("[FormToolUI] Cannot submit form: ToolExecution context not available", { toolCallId });
570
+ return;
571
+ }
572
+ const outputValue = typeof formData === "object" ? JSON.stringify(formData) : String(formData);
573
+ toolExecution.addToolOutput({
574
+ toolCallId,
575
+ output: outputValue
576
+ });
577
+ addResult(formData);
578
+ };
579
+ if (!normalizedSchema) return /* @__PURE__ */ jsxs("div", {
580
+ className: "my-1 flex items-center gap-1.5 rounded-lg bg-muted/50 px-2 py-1.5 text-sm text-foreground/70",
581
+ children: [/* @__PURE__ */ jsx(FileText, { className: "h-3.5 w-3.5 animate-pulse text-muted-foreground" }), /* @__PURE__ */ jsx("span", {
582
+ className: "text-xs",
583
+ children: "Loading form..."
584
+ })]
585
+ });
586
+ return /* @__PURE__ */ jsxs("div", {
587
+ className: "my-1 overflow-hidden rounded-lg bg-muted/50",
588
+ children: [/* @__PURE__ */ jsxs("div", {
589
+ className: "flex items-center gap-1.5 px-2 py-1.5 border-b border-border",
590
+ children: [/* @__PURE__ */ jsx(FileText, { className: "h-3.5 w-3.5 text-foreground/70" }), /* @__PURE__ */ jsx("span", {
591
+ className: "text-xs font-medium text-foreground",
592
+ children: args.title ?? "Form"
593
+ })]
594
+ }), /* @__PURE__ */ jsxs("div", {
595
+ className: "px-2 py-2",
596
+ children: [args.description && /* @__PURE__ */ jsx("p", {
597
+ className: "text-xs text-muted-foreground mb-3",
598
+ children: args.description
599
+ }), /* @__PURE__ */ jsx(Form, {
600
+ schema: normalizedSchema,
601
+ validator,
602
+ onSubmit: handleSubmit,
603
+ disabled: !isWaiting,
604
+ className: "space-y-3",
605
+ uiSchema: { "ui:submitButtonOptions": { norender: true } },
606
+ children: /* @__PURE__ */ jsx("div", {
607
+ className: "flex justify-end pt-1",
608
+ children: /* @__PURE__ */ jsxs(Button, {
609
+ type: "submit",
610
+ disabled: !isWaiting,
611
+ size: "sm",
612
+ className: "h-7 px-3 text-xs",
613
+ children: [/* @__PURE__ */ jsx(Check, { className: "h-3 w-3 mr-1" }), args.submitLabel || "Submit"]
614
+ })
615
+ })
616
+ })]
617
+ })]
618
+ });
619
+ }
620
+ });
621
+ var FormToolUI_default = FormToolUI;
622
+
623
+ //#endregion
624
+ export { Collapsible as a, Button as c, useOptionalToolExecution as i, cn as l, FormToolUI_default as n, CollapsibleContent as o, ToolExecutionProvider as r, CollapsibleTrigger as s, FormToolUI as t };
625
+ //# sourceMappingURL=FormToolUI-Dhl3il9B.js.map