@mcp-b/embedded-agent 1.2.6 → 1.2.8
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 +54 -1
- package/dist/FormToolUI-Dhl3il9B.js +625 -0
- package/dist/FormToolUI-Dhl3il9B.js.map +1 -0
- package/dist/FormToolUI-Zjy9RdnT.js +3 -0
- package/dist/jsx.d.ts +123 -7
- package/dist/styles/globals.css +1 -1
- package/dist/web-component.d.ts +7 -7
- package/dist/web-component.d.ts.map +1 -1
- package/dist/web-component.js +3868 -4625
- package/dist/web-component.js.map +1 -1
- package/package.json +32 -25
- package/dist/web-component-standalone.css.map +0 -1
- package/dist/web-component-standalone.iife.js +0 -118591
- package/dist/web-component-standalone.iife.js.map +0 -1
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
|
-
|
|
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
|