@heroui/agent 1.0.0-beta.1 → 1.0.0-beta.10

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +195 -72
  3. package/dist/chunk-23EBJXRA.js +1 -0
  4. package/dist/chunk-5CSSZ76R.js +1 -0
  5. package/dist/chunk-AR424OVX.js +1 -0
  6. package/dist/chunk-FZXNXBTZ.js +1 -0
  7. package/dist/chunk-KFWXAD55.js +1 -0
  8. package/dist/chunk-MB3T5S5N.js +1 -0
  9. package/dist/client-tools-manifest-PCUOVUXR.js +1 -0
  10. package/dist/css/index.css +1 -2
  11. package/dist/index.d.ts +9 -669
  12. package/dist/index.js +1 -1
  13. package/dist/internal/host.d.ts +188 -0
  14. package/dist/internal/host.js +1 -0
  15. package/dist/internal/shared.d.ts +67 -0
  16. package/dist/internal/shared.js +1 -0
  17. package/dist/next.d.ts +3 -4
  18. package/dist/next.js +1 -1
  19. package/dist/persistence-YI6JSYCT.js +1 -0
  20. package/dist/server.d.ts +23 -2
  21. package/dist/server.js +1 -1
  22. package/dist/types-C3g7R0hr.d.ts +592 -0
  23. package/dist/version-LiwSi-9e.d.ts +5 -0
  24. package/dist/zod.d.ts +1 -0
  25. package/dist/zod.js +5 -0
  26. package/package.json +25 -37
  27. package/dist/chart-content-BBHQVMP4.js +0 -1
  28. package/dist/chunk-4JEZZR5J.js +0 -1
  29. package/dist/chunk-5RHO3S5E.js +0 -2
  30. package/dist/chunk-64RI74L5.js +0 -1
  31. package/dist/chunk-6ZJJN322.js +0 -1
  32. package/dist/chunk-7KCGPNEN.js +0 -1
  33. package/dist/chunk-BBB5EHDN.js +0 -5
  34. package/dist/chunk-CLHBM3HQ.js +0 -1
  35. package/dist/chunk-CR3H7AVI.js +0 -1
  36. package/dist/chunk-HCLXS4HQ.js +0 -1
  37. package/dist/chunk-PZJ4NC3J.js +0 -1
  38. package/dist/chunk-RNNGE7KM.js +0 -1
  39. package/dist/chunk-S6OIAJSC.js +0 -1
  40. package/dist/chunk-UHHKMESI.js +0 -1
  41. package/dist/chunk-USFF5UMB.js +0 -2
  42. package/dist/component-renderer-XOCP6UFF.js +0 -1
  43. package/dist/composer-draft-NSKK65F3.js +0 -1
  44. package/dist/composer-image-draft-KAGTNQR7.js +0 -1
  45. package/dist/contracts.d.ts +0 -1651
  46. package/dist/contracts.js +0 -2
  47. package/dist/embed-runtime-FP77ORXA.js +0 -5
  48. package/dist/identity-3P8-17km.d.ts +0 -95
  49. package/dist/interactive-map-surface-WC2LPCR7.js +0 -1
  50. package/dist/internal/client-tools.js +0 -1
  51. package/dist/internal/models.js +0 -1
  52. package/dist/internal/runtime.js +0 -1
  53. package/dist/internal/theme.js +0 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,66 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Preserve thrown client-tool messages and distinguish oversized results from invalid JSON. Logs, Runs API responses, exports, and webhook run payloads include bounded tool-error details.
6
+ - Reject reserved client-tool names at mount with an actionable error; stop repeating configuration failures and bound iframe handshake retries with backoff.
7
+ - Stop the exact in-flight turn without requeueing its prompt, including when admission is still pending. Queued follow-ups remain paused.
8
+ - Support pasting images into the composer through the existing attachment validation and upload flow.
9
+ - Keep provider reasoning headings out of progress labels and discard unexpected Chinese text in generated titles while preserving Chinese and Japanese conversations.
10
+ - Return actionable OpenUI validation errors with component signatures so the agent can repair a view using the same data. The partner's original intermittent chart failure has not been reproduced.
11
+ - Overlap independent API admission checks and expose their individual timing spans. Controlled API timing improved; the reported production admission delay still needs its original trace.
12
+ - Fix shared context typing for interfaces while retaining validation of the reserved `page` callback.
13
+ - Hide dictation when iframe microphone policy explicitly blocks it; preserve explicit opt-out.
14
+ - Add launcher `iconSize` and opt-in mobile sheets with configurable height. Existing sizing and full-screen defaults remain unchanged.
15
+ - Export pinned `z` from `@heroui/agent/zod` and align anonymous/sign-in token examples.
16
+ - Add descriptive server-assigned token metadata, snapshotted per run.
17
+ - Hosted dashboard: preserve edited color alpha, explain prop precedence, and configure signed webhooks for selected run events (`run.started`, `run.completed`, `run.failed`, and `run.cancelled`) with delivery status.
18
+ - Automatically pause webhooks after five consecutive exhausted delivery cycles, resume from the dashboard, and retry individual failed events with their original payload and event ID. Preserve attempt history and bound internal queue recovery.
19
+ - Preserve elapsed "Worked for" duration across transcript reloads.
20
+ - Expose available run timing data for diagnosis. Fast tool execution alone does not establish total-response latency; no measured latency improvement is claimed in this release.
21
+
22
+ - Add `locale` to React and vanilla integrations, including `auto` for browser language detection, regional formatting, and optional `translations` for custom copy by language.
23
+ - Select localized agent copy using the SDK override or saved agent default. AI responses follow the user's language.
24
+
25
+ ## Unreleased
26
+
27
+ - Move the model catalog, attachment limits and MIME allowlist, and design theme
28
+ list out of the published package and into the hosted runtime. Adding a model,
29
+ attachment type, or theme variant no longer requires a customer npm release.
30
+ Removed exports: `AGENT_MODEL_IDS`, `AGENT_MODEL_OPTIONS`,
31
+ `DEFAULT_AGENT_PICKER_MODEL_ID`, `LEGACY_AGENT_MODEL_IDS`,
32
+ `resolveAgentModelId`, `isAgentModelId`, `getAgentModelTier`,
33
+ `AgentModelOption`, `AgentModelTier`, `AGENT_DESIGN_THEMES`, every
34
+ `HEROUI_AGENT_*ATTACHMENT*` constant, `isHeroUIAgentAttachmentContentType`,
35
+ `resolveHeroUIAgentAttachmentContentType`, and
36
+ `HeroUIAgentAttachmentContentType`.
37
+ - Type `composer.defaultModel` (`AgentModelId`), `composer.attachments`
38
+ (`AgentAttachmentContentType`), and `theme.designTheme` (`AgentDesignTheme`)
39
+ as plain strings. The hosted runtime validates them: retired model ids resolve
40
+ to their replacement, unknown model ids fall back to the hosted default,
41
+ unsupported attachment types are dropped, and unknown design themes render
42
+ `base`.
43
+ - Accept client tool results and page context whose optional fields are
44
+ `undefined`. The bridge previously rejected them as not JSON-serializable
45
+ even though `JSON.stringify` drops them; it now forwards the canonical JSON.
46
+ - Move the visible Agent UI into a cross-origin iframe hosted by HeroUI. This is
47
+ the one-time host-bridge upgrade; UI, renderer, chart, PDF/XLSX, and backend
48
+ changes no longer require another customer npm release.
49
+ - Keep client tools, product context, navigation, mutations, identity, drafts,
50
+ attachments, and the durable message outbox in the customer page behind the
51
+ origin- and session-bound iframe RPC boundary.
52
+ - Replace custom React tool/Markdown renderers and React icon components with
53
+ hosted declarative UI and HeroUI-owned icon identifiers.
54
+ - Keep `@heroui/agent/css` as a compatibility no-op; the iframe ships its own
55
+ hashed, code-split styles and assets.
56
+ - Move the full conversation implementation into the private
57
+ `@heroui/agent-hosted-runtime` workspace package. The published package keeps
58
+ only the host bridge, public types, client tools, and server helper, and
59
+ no longer installs Streamdown as a customer dependency.
60
+ - Move runtime, telemetry, remote configuration, token-claim, and generated UI
61
+ schemas into the private `@heroui/agent-runtime-contracts` workspace
62
+ package. Remove the beta-only public `@heroui/agent/contracts` entry point.
63
+ - Retire `@heroui/agent-client`; its session manager now belongs to the public
64
+ bridge, while conversation targeting and history belong to the hosted runtime.
5
65
  - Keep the built-in Agent palette isolated from generic host variables such as
6
66
  `--background` and `--foreground`, which may contain framework-specific color
7
67
  channels rather than valid CSS colors and previously made panels transparent.
package/README.md CHANGED
@@ -2,23 +2,54 @@
2
2
 
3
3
  # HeroUI Agents SDK
4
4
 
5
- Embed a hosted generative-UI agent in your React app with [`@heroui/agent`](https://heroui.pro/agents). Declare typed client tools that read your data — the hosted runtime plans the answer, runs calculations in a sandbox, and streams back charts, tables, and metrics.
5
+ Embed a hosted generative-UI agent in any modern website. Use
6
+ [`@heroui/agent`](https://heroui.pro/agents) for typed React integrations, or the CDN loader with
7
+ vanilla JavaScript. The hosted runtime plans answers, runs calculations in a sandbox, and streams
8
+ back charts, tables, and metrics.
6
9
 
7
- ## Installation
10
+ The npm package contains only the iframe bridge, public types, client-tool executor, and server
11
+ token helper. The full conversation UI lives in the private `@heroui/agent-hosted-runtime` workspace
12
+ package and is deployed through `agent.heroui.pro`.
13
+
14
+ ## React installation
8
15
 
9
16
  ```bash
10
- npm install @heroui/agent@beta
17
+ npm install @heroui/agent@latest
11
18
  ```
12
19
 
13
- Import the stylesheet once:
20
+ ## React (Vite) usage
21
+
22
+ ```tsx
23
+ import {HeroUIAgent} from "@heroui/agent";
24
+
25
+ const agentId = process.env.HEROUI_AGENT_ID;
14
26
 
15
- ```css
16
- @import "@heroui/agent/css";
27
+ export function AppAgent() {
28
+ return (
29
+ <HeroUIAgent
30
+ agentId={agentId!}
31
+ getAuthToken={async (context) => {
32
+ const response = await fetch("/api/heroui-agent/auth-token", {
33
+ method: "POST",
34
+ signal: context.signal,
35
+ headers: {"Content-Type": "application/json"},
36
+ body: JSON.stringify(context),
37
+ });
38
+
39
+ if (!response.ok) throw new Error("Agent authentication failed");
40
+
41
+ return response.json();
42
+ }}
43
+ />
44
+ );
45
+ }
17
46
  ```
18
47
 
19
- ## Usage
48
+ ## Next.js usage
20
49
 
21
50
  ```tsx
51
+ "use client";
52
+
22
53
  import {HeroUIAgent} from "@heroui/agent/next";
23
54
 
24
55
  export function AppAgent() {
@@ -28,6 +59,7 @@ export function AppAgent() {
28
59
  getAuthToken={async (context) => {
29
60
  const response = await fetch("/api/heroui-agent/auth-token", {
30
61
  method: "POST",
62
+ signal: context.signal,
31
63
  headers: {"Content-Type": "application/json"},
32
64
  body: JSON.stringify(context),
33
65
  });
@@ -48,49 +80,83 @@ import {createAuthToken} from "@heroui/agent/server";
48
80
 
49
81
  export async function POST(request: Request) {
50
82
  const {anonymousId, agentId} = await request.json();
83
+ const configuredAgentId = process.env.HEROUI_AGENT_ID;
84
+
85
+ if (!configuredAgentId || agentId !== configuredAgentId) {
86
+ return Response.json({error: "Invalid agent"}, {status: 400});
87
+ }
51
88
 
52
89
  return Response.json(
53
90
  await createAuthToken({
91
+ anonymousId,
54
92
  apiKey: process.env.HEROUI_AGENT_API_KEY!,
55
93
  identity: {id: anonymousId, type: "anonymous"},
56
- agentId,
94
+ agentId: configuredAgentId,
57
95
  }),
58
96
  );
59
97
  }
60
98
  ```
61
99
 
100
+ ## Vanilla JavaScript
101
+
102
+ No npm package or React dependency is required:
103
+
104
+ ```html
105
+ <script src="https://agent.heroui.pro/loader.js" defer></script>
106
+ <script>
107
+ window.addEventListener("heroui-agent:loaded", () => {
108
+ window.agent = window.HeroUIAgent.mount({
109
+ agentId: "your_agent_id",
110
+ getAuthToken: async (context) => {
111
+ const response = await fetch("/api/heroui-agent/auth-token", {
112
+ body: JSON.stringify(context),
113
+ headers: {"Content-Type": "application/json"},
114
+ method: "POST",
115
+ });
116
+
117
+ if (!response.ok) throw new Error("Agent authentication failed");
118
+
119
+ return response.json();
120
+ },
121
+ });
122
+ });
123
+ </script>
124
+ ```
125
+
126
+ Vanilla client tools use raw JSON Schema for `parameters`. The returned controller exposes `show`,
127
+ `hide`, `toggle`, `newConversation`, `refreshAuth`, `shutdown`, and `destroy`.
128
+
62
129
  ## Overview
63
130
 
64
131
  `@heroui/agent` provides a unified SDK for embedding HeroUI Agents into web applications.
65
132
 
66
133
  ### Entrypoints
67
134
 
68
- | Entrypoint | Description | Links |
69
- | -------------------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
70
- | [`@heroui/agent`](#herouiagent) | React embed, hooks, and client tools | [npm](https://www.npmjs.com/package/@heroui/agent) • [Docs](https://heroui.pro/docs/agent/getting-started/installation) |
71
- | [`@heroui/agent/next`](#herouiagentnext) | Next.js App Router entry | [Docs](https://heroui.pro/docs/agent/getting-started/installation) |
72
- | [`@heroui/agent/server`](#herouiagentserver) | Server helpers for minting auth tokens | [Docs](https://heroui.pro/docs/agent/getting-started/identifying-users) |
73
- | [`@heroui/agent/css`](#styles) | Agent stylesheet | |
74
- | [`@heroui/agent/contracts`](#contracts) | Shared protocol types and schemas | — |
135
+ | Entrypoint | Description | Links |
136
+ | -------------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
137
+ | [`@heroui/agent`](#herouiagent) | React bridge, hooks, and typed client tools | [npm](https://www.npmjs.com/package/@heroui/agent) • [Docs](https://heroui.pro/docs/agents/installation) |
138
+ | [`@heroui/agent/next`](#herouiagentnext) | Next.js App Router entry | [Docs](https://heroui.pro/docs/agents/installation) |
139
+ | `agent.heroui.pro/loader.js` | Framework-independent browser bridge | [Docs](https://heroui.pro/docs/agents/installation) |
140
+ | [`@heroui/agent/server`](#herouiagentserver) | Framework-independent auth-token server helper | [Docs](https://heroui.pro/docs/agents/identifying-users) |
75
141
 
76
142
  ## Package Details
77
143
 
78
144
  ### @heroui/agent
79
145
 
80
- The core React SDK for embedding a hosted agent.
146
+ The typed React bridge for embedding the hosted Agent iframe.
81
147
 
82
148
  #### Features
83
149
 
84
150
  - **Generative UI** — Charts, tables, metrics, and forms streamed from a validated component union
85
151
  - **Client tools** — Typed browser functions that run with the signed-in user's session
86
- - **Dashboard-driven appearance** — Theme, launcher, greeting, and composer without a redeploy
152
+ - **Dashboard-driven appearance** — Theme, launcher, greeting, subtitle, and composer without a redeploy
87
153
  - **Anonymous → identified** — Merge guest history when the user signs in
88
- - **Edge-safe contracts** — Protocol types with no Node built-ins
154
+ - **Shared host core** — React, Next.js, and the CDN loader use the same iframe bridge
89
155
 
90
156
  #### Installation
91
157
 
92
158
  ```bash
93
- npm install @heroui/agent@beta
159
+ npm install @heroui/agent@latest
94
160
  ```
95
161
 
96
162
  ### @heroui/agent/next
@@ -109,34 +175,13 @@ Server-only helpers for exchanging a project API key for a short-lived browser c
109
175
  import {createAuthToken} from "@heroui/agent/server";
110
176
  ```
111
177
 
112
- ### Styles
113
-
114
- ```css
115
- @import "@heroui/agent/css";
116
- ```
117
-
118
- Optional Pro theme variants (`brutalism`, `glass`, `mouve`) ship from [`@heroui-pro/react`](https://heroui.pro/docs):
119
-
120
- ```tsx
121
- import "@heroui/agent/css";
122
- import "@heroui-pro/react/agent-themes/glass";
123
- ```
124
-
125
- ### Contracts
126
-
127
- Shared protocol version, message types, and schemas used by the SDK and hosted API.
128
-
129
- ```ts
130
- import {HEROUI_AGENT_PROTOCOL_VERSION} from "@heroui/agent/contracts";
131
- ```
132
-
133
178
  ## Client Tools
134
179
 
135
180
  Client tools let the agent call into your application — fetch data, change filters, create records:
136
181
 
137
182
  ```tsx
138
183
  import {HeroUIAgent, createToolHelper} from "@heroui/agent";
139
- import {z} from "zod";
184
+ import {z} from "@heroui/agent/zod";
140
185
 
141
186
  const tool = createToolHelper<{apiClient: ApiClient}>();
142
187
 
@@ -150,60 +195,95 @@ const tools = [
150
195
  ];
151
196
 
152
197
  <HeroUIAgent
153
- agentId={process.env.NEXT_PUBLIC_HEROUI_AGENT_ID!}
198
+ agentId={process.env.HEROUI_AGENT_ID!}
154
199
  getAuthToken={getAuthToken}
155
200
  context={{apiClient}}
156
201
  tools={tools}
157
202
  />;
158
203
  ```
159
204
 
160
- Only tool names, descriptions, and JSON schemas reach the hosted agent. Implementations and `context` never leave the browser.
205
+ Tool manifests, JSON-safe page context, and validated tool results cross the iframe boundary. Tool
206
+ implementations, API clients, routers, state setters, and the rest of `context` stay in the browser.
207
+
208
+ ### Image results
209
+
210
+ Any client tool can return a `ClientToolVisualResult` to send an image to the
211
+ model alongside JSON details. This works through the same result contract for
212
+ all agents; no particular tool name or agent ID is required.
213
+
214
+ ```ts
215
+ import type {ClientToolVisualResult} from "@heroui/agent";
216
+
217
+ // Return this from your client tool's execute function.
218
+ const result: ClientToolVisualResult = {
219
+ kind: "heroui-agent-visual-result",
220
+ image: {data: imageBase64, mediaType: "image/png"},
221
+ description: "The chart currently visible in the application",
222
+ };
223
+ ```
224
+
225
+ Use JPEG or PNG base64 without a data-URL prefix, with at most 40,000 base64
226
+ characters. Additional JSON fields accompany the image as text. The complete
227
+ tool result must still fit the existing 64 KiB receipt limit. Use a model that
228
+ supports image input. Ordinary JSON results remain unchanged.
229
+
230
+ Validation failures return `CLIENT_TOOL_INVALID_INPUT` before execution. When
231
+ the schema provides issues, the result includes bounded field paths, error
232
+ codes, expected types, and numeric limits so the model can correct its input.
233
+ Submitted values and arbitrary exception messages are not included.
161
234
 
162
- [Learn more](https://heroui.pro/docs/agent/api-reference/client-tools)
235
+ [Learn more](https://heroui.pro/docs/agents/api-reference/client-tools)
163
236
 
164
237
  ## Documentation
165
238
 
166
- - [Installation](https://heroui.pro/docs/agent/getting-started/installation)
167
- - [Identifying users](https://heroui.pro/docs/agent/getting-started/identifying-users)
168
- - [Configuration](https://heroui.pro/docs/agent/api-reference/configuration)
169
- - [Client tools](https://heroui.pro/docs/agent/api-reference/client-tools)
170
- - [Appearance](https://heroui.pro/docs/agent/configure/appearance)
171
- - [Configure](https://heroui.pro/docs/agent/configure)
239
+ - [Installation](https://heroui.pro/docs/agents/installation)
240
+ - [Identifying users](https://heroui.pro/docs/agents/identifying-users)
241
+ - [Configuration](https://heroui.pro/docs/agents/api-reference/configuration)
242
+ - [Client tools](https://heroui.pro/docs/agents/api-reference/client-tools)
243
+ - [Appearance](https://heroui.pro/docs/agents/configure/appearance)
244
+ - [Configure](https://heroui.pro/docs/agents/configure)
172
245
 
173
246
  Projects and API keys are managed in the [Agents dashboard](https://heroui.pro/dashboard/agents).
174
247
 
175
248
  ## What runs where
176
249
 
177
- The HeroUI API authenticates the visitor once, verifies conversation ownership, and returns a
178
- one-use connection ticket. The browser then keeps a resumable WebSocket directly to the
179
- conversation's Cloudflare Durable Object. That object owns the transcript, streams from the model
180
- provider, and starts MCP or an offline, per-call-scoped sandbox only when a tool actually needs
181
- one. Before the composer clears, the SDK durably stores the submitted turn and attachment blobs in
182
- an identity-and-conversation-scoped IndexedDB outbox. It removes that entry only after the Durable
183
- Object acknowledges committed admission, and reconciles the same `turnId` after a reload. InstantDB
184
- is updated asynchronously for dashboard monitoring and never sits in front of a response stream.
185
-
186
- | In the browser | At the HeroUI edge runtime |
187
- | ------------------------------------------- | -------------------------------------------- |
188
- | Panel, theme, composer, generated UI | Conversation Durable Object and model stream |
189
- | Client tools, running as the signed-in user | Server tools, lazy MCP and offline Python |
190
- | Pre-admission IndexedDB turn outbox | SDK transcript and resumable stream storage |
191
- | A short-lived HeroUI browser credential | Hashed tickets, credit lease, Queue outbox |
250
+ The npm package creates a cross-origin iframe at `agent.heroui.pro` and keeps the customer-product
251
+ trust boundary in the parent page. A versioned `heroui-agent-bridge/v1` handshake exchanges the
252
+ browser credential for a short-lived iframe session bound to both origins. Credentials never appear
253
+ in the iframe URL.
254
+
255
+ | In the customer page | In the hosted iframe and edge runtime |
256
+ | --------------------------------------------- | --------------------------------------------- |
257
+ | Client tool functions and product context | Panel, composer, Markdown, and generated UI |
258
+ | Navigation, mutations, and approval effects | Approval, progress, result, and error cards |
259
+ | Identity, drafts, attachments, durable outbox | Conversation Durable Object and model stream |
260
+ | Outside clicks, page margin, focus, z-index | Server tools, lazy MCP, PDF/XLSX, and sandbox |
261
+
262
+ Add the iframe origin to your Content Security Policy:
263
+
264
+ ```http
265
+ Content-Security-Policy: frame-src https://agent.heroui.pro
266
+ ```
267
+
268
+ When using the CDN loader, allow the same origin in `script-src`:
269
+
270
+ ```http
271
+ Content-Security-Policy: frame-src https://agent.heroui.pro; script-src 'self' https://agent.heroui.pro
272
+ ```
192
273
 
193
274
  ## Compatibility
194
275
 
195
- - React 19+
196
- - Next.js 15+ for `@heroui/agent/next`
276
+ - Any modern website through `agent.heroui.pro/loader.js`
277
+ - React 19+ only when using `@heroui/agent`
278
+ - Next.js 15+ only when using `@heroui/agent/next`
197
279
  - Chrome/Edge 120+, Firefox 121+, Safari 17.2+
198
- - ESM only
199
-
200
- This SDK speaks protocol version `6`. The hosted API returns `426 Upgrade Required` when a beta SDK is no longer compatible.
280
+ - ESM-only npm entry points; the CDN loader is a classic browser script
201
281
 
202
282
  ## Support
203
283
 
204
- - [Documentation](https://heroui.pro/docs/agent)
284
+ - [Documentation](https://heroui.pro/docs/agents)
205
285
  - [Agents product](https://heroui.pro/agents)
206
- - [Issues](https://github.com/heroui-inc/heroui-platform/issues)
286
+ - [Issues](https://github.com/heroui-inc/heroui-pro/issues)
207
287
  - [Discord](https://discord.gg/9b6yyZKmH4)
208
288
 
209
289
  ## License
@@ -213,3 +293,46 @@ MIT — see [LICENSE](LICENSE).
213
293
  ---
214
294
 
215
295
  Built by [HeroUI](https://www.heroui.com)
296
+
297
+ ## Status, recovery, and cancellation
298
+
299
+ Use `onOpenChange(open)` to observe visible panel opens and closes. Preloading
300
+ and `onReady` do not count as opening the panel.
301
+
302
+ Use `onStatusChange(status)` to observe startup, connection, generation, finishing, and recovery.
303
+ `onError(error)` receives a safe `message`, `category`, `phase`, `retryable`, and `diagnosticId`.
304
+ Use the diagnostic ID to correlate failures; keep prompts, form values, files, credentials, and tool
305
+ outputs out of telemetry. The hosted panel supplies recovery actions and preserves conversation drafts.
306
+
307
+ Pass `context.signal` from `getAuthToken` into your authentication fetch. Authentication expires
308
+ after 15 seconds and superseded attempts are cancelled. Client tools receive
309
+ `execution.signal` as their third argument; pass it into cancellable requests and use
310
+ `execution.idempotencyKey` for server mutations. Cancellation after dispatch can leave an unknown
311
+ outcome, so the runtime preserves a receipt and does not automatically repeat the effect.
312
+
313
+ Generated forms submit their validated values as a queued user message without changing the composer
314
+ draft. “Submitted to agent” confirms queue acceptance; action completion is reported separately.
315
+ The selected conversation stays connected while visible. Settled inactive conversations disconnect
316
+ after a grace period; closing the panel leaves active work running.
317
+
318
+ ## Language
319
+
320
+ Set the default language in the HeroUI Agents editor. Pass `locale` to override it for a user:
321
+
322
+ ```tsx
323
+ <HeroUIAgent agentId={agentId} getAuthToken={getAuthToken} locale="es" />
324
+ ```
325
+
326
+ The vanilla integration accepts the same `locale` option. English (`en`) and neutral Latin American Spanish (`es`) are supported. Regional tags such as `es-AR` share Spanish text and use regional number and date formatting. Unsupported locales fall back to English.
327
+
328
+ Set `locale="auto"` (or choose **Auto** in the editor) to use the first supported language in the visitor’s browser preferences, with English as the fallback. Regional formatting is preserved. Auto updates when browser language preferences change.
329
+
330
+ The SDK locale takes precedence over the saved agent locale, then English. With `remoteConfig={false}`, only the SDK locale and English fallback apply. Update the prop when your application's language changes; existing messages stay intact and subsequent submissions capture the new locale.
331
+
332
+ Interface language and response language are separate: the AI follows the user's message, keeps the previous conversational language for ambiguous follow-ups, and otherwise uses the configured locale. Explicit translation requests are honored.
333
+
334
+ Edit English and Spanish greetings, subtitles, placeholders, disclaimers, and suggested prompts in the editor's **Edit and preview** tabs. Explicit SDK copy overrides the selected translation. With `remoteConfig={false}`, pass a `translations` object keyed by base language (`{en: {greeting: "Hello"}, es: {greeting: "Hola"}}`) to provide custom copy for Auto. Missing Spanish copy uses built-in Spanish defaults; untranslated suggested prompts are hidden. Empty values and disabled disclaimers are preserved. Legacy custom copy remains English.
335
+
336
+ ### Pasting images
337
+
338
+ Paste an image into the composer with Cmd+V or Ctrl+V. Pasted images use the same accepted file types, size limits, previews, and upload flow as the attachment button. Disabling attachments also disables image paste. Ordinary pasted text remains editable.
@@ -0,0 +1 @@
1
+ var t="1.0.0-beta.10",o="https://api.heroui.pro";export{t as a,o as b};
@@ -0,0 +1 @@
1
+ import{b as te}from"./chunk-23EBJXRA.js";import{c as ee,h as G,i as ne,j as oe,m as re,n as ie,o as B}from"./chunk-FZXNXBTZ.js";function ye(e,t){return e.throwIfAborted(),new Promise((n,o)=>{let s=()=>o(e.reason);e.addEventListener("abort",s,{once:!0});try{t().then(n,o).finally(()=>e.removeEventListener("abort",s))}catch(u){e.removeEventListener("abort",s),o(u)}})}async function V(e,t,n){let o=new AbortController,s=setTimeout(()=>o.abort(new DOMException("Request timed out","TimeoutError")),e),u=n?AbortSignal.any([n,o.signal]):o.signal;try{return await ye(u,()=>t(u))}finally{clearTimeout(s)}}var be={approval:"This approval has expired. Ask the agent to request it again.",authentication:"Could not authenticate. Sign in if needed, then retry.",chunk:"Could not load the conversation. Retry, or reload the panel.",configuration:"Could not load the agent configuration. Try again.",connection:"Connection interrupted. Completed page changes remain applied. Reconnect to continue this chat.",credits:"This agent has reached its usage limit.",deleted:"This conversation was deleted. Start a new conversation.",rate_limit:"Too many requests. Wait a moment before retrying.",unknown:"The request could not be completed. Try again."},ve="Too many conversations are active. Wait a few minutes, then retry this chat.";function X(e,t="connecting"){let n=e&&typeof e=="object"?e:{},o=[e instanceof Error?e.message:"",n.code,n.status].filter(u=>typeof u=="string"||typeof u=="number").join(" ").toLowerCase(),s=t==="authenticating"||/auth|401|sign.in/.test(o)?"authentication":/429|rate.limit|concurrency_limit|too many conversations are active/.test(o)?"rate_limit":/credit|usage.limit|usage_limit|402/.test(o)?"credits":/deleted|conversation_not_found/.test(o)?"deleted":/approval.*expir/.test(o)?"approval":/dynamic.*import|chunk|module.*fetch/.test(o)?"chunk":/config/.test(o)?"configuration":/network|fetch|connect|timeout|timed out/.test(o)?"connection":"unknown";return{category:s,diagnosticId:crypto.randomUUID(),message:/concurrency_limit|too many conversations are active/.test(o)?ve:be[s],phase:t,retryable:!["credits","deleted","approval"].includes(s)}}var Ae=5e3;function ae(e){return`heroui-agent:anonymous:${e}`}function Te(e){return e instanceof Error?e:new Error("Auth token request failed")}var M=class{constructor(t,n){this.agentId=t;this.getAuthToken=n}cached=null;failure=null;generation=0;pending=null;controller=new AbortController;clear(){this.controller.abort(),this.controller=new AbortController,this.generation+=1,this.cached=null,this.failure=null,this.pending=null}reset(){this.clear();try{localStorage.removeItem(ae(this.agentId))}catch{}}async get(){if(this.cached&&this.expiresAt(this.cached)>Date.now()+6e4)return this.cached.token;if(!this.pending&&this.failure&&Date.now()<this.failure.retryAt)throw this.failure.error;let t=this.generation,n=this.pending??this.refresh();this.pending||(this.pending=n);try{let o=await n;return t!==this.generation?this.get():(this.cached=o,this.failure=null,o.token)}catch(o){throw t===this.generation&&(this.failure={error:Te(o),retryAt:Date.now()+Ae}),o}finally{this.pending===n&&(this.pending=null)}}async subjectHash(){let t=await this.get(),n=xe(t),o=typeof n.sub=="string"?n.sub:"anonymous",s=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(`${this.agentId}:${o}`));return Array.from(new Uint8Array(s),u=>u.toString(16).padStart(2,"0")).join("").slice(0,24)}anonymousId(){let t=ae(this.agentId);try{let n=localStorage.getItem(t);if(n)return n;let o=crypto.randomUUID();return localStorage.setItem(t,o),o}catch{return crypto.randomUUID()}}expiresAt(t){return t.expiresAt}async refresh(){let t=await V(15e3,n=>this.getAuthToken({agentId:this.agentId,anonymousId:this.anonymousId(),signal:n}),this.controller.signal);if(!t||typeof t.token!="string"||!t.token.trim()||typeof t.expiresAt!="number"||!Number.isFinite(t.expiresAt)||t.expiresAt<=0)throw new Error("Auth token callback returned invalid data");return{expiresAt:t.expiresAt,token:t.token}}};function xe(e){try{let t=e.split(".")[1];if(!t)return{};let n=t.replace(/-/g,"+").replace(/_/g,"/"),o=n.padEnd(Math.ceil(n.length/4)*4,"=");return JSON.parse(atob(o))}catch{return{}}}var Ee=64*1024,H=class extends Error{constructor(n,o){super(o);this.code=n}};function se(e){let t;try{let n=e??null;if(!B(n))throw new Error("Not a JSON value");t=JSON.stringify(n)}catch{throw new H("CLIENT_TOOL_RESULT_NOT_SERIALIZABLE","The client tool returned a result that is not JSON-serializable. Return plain JSON values; convert Map and Date values explicitly and remove circular references. The tool already ran; do not repeat an action without checking its outcome.")}if(new TextEncoder().encode(t).byteLength>Ee)throw new H("CLIENT_TOOL_RESULT_TOO_LARGE","The client tool result exceeds the 64 KiB limit. Request fewer rows or fields, paginate the data, or use uploadDataSource for a larger dataset. The tool already ran; do not repeat an action without checking its outcome.");return JSON.parse(t)}function le(e){return{bounds:null,closing:!1,dragging:!1,expanded:e.appearance?.panel?.expanded??!1,mounted:!1,open:!1,overlayOpen:!1,ready:!1,resizing:!1,sidebarWidth:null,viewMode:e.appearance?.viewMode??"floating"}}function A(e,t,n,o){return{background:"transparent",border:"0",clipPath:Ie(e,t,n),colorScheme:"normal",height:n&&e.open&&o?`${o.height}px`:"100dvh",inset:n&&e.open&&o?`${o.offsetTop}px 0 auto`:"0",position:"fixed",width:"100vw",zIndex:t.appearance?.zIndex??40}}function de(e){if(!e||typeof e!="object")return null;let t=e;return typeof t.open!="boolean"||typeof t.ready!="boolean"||typeof t.expanded!="boolean"||t.viewMode!=="chat-bar"&&t.viewMode!=="floating"&&t.viewMode!=="sidebar"?null:{bounds:ke(t.bounds),closing:t.closing===!0,dragging:t.dragging===!0,expanded:t.expanded,mounted:t.mounted!==!1,open:t.open,overlayOpen:t.overlayOpen===!0,ready:t.ready,resizing:t.resizing===!0,sidebarWidth:U(t.sidebarWidth)?t.sidebarWidth:null,viewMode:t.viewMode}}function ce(){let e=document.documentElement,t=document.body;return{active:!1,body:t,original:{bodyLeft:t.style.left,bodyOverflow:t.style.overflow,bodyPosition:t.style.position,bodyRight:t.style.right,bodyTop:t.style.top,bodyWidth:t.style.width,rootMarginRight:e.style.marginRight,rootOverflow:e.style.overflow,rootTransition:e.style.transition},root:e,scrollLock:null}}function P(e,t,n,o){let s=t.open||t.closing;if(!s&&!e.active)return;s&&(e.active=!0);let u=s&&(t.viewMode==="chat-bar"?t.expanded:o||t.viewMode==="sidebar"&&t.expanded);pe(e,u);let l=s&&t.viewMode==="sidebar"&&!t.expanded&&!o?Se(t,n):e.original.rootMarginRight;e.root.style.transition=e.original.rootTransition,e.root.style.marginRight=l,s||(e.active=!1)}function ue(e){pe(e,!1),e.root.style.marginRight=e.original.rootMarginRight,e.root.style.transition=e.original.rootTransition,e.active=!1}function Ie(e,t,n){let o=e.open||e.closing;if(o&&(e.overlayOpen||n&&e.viewMode!=="chat-bar"))return"inset(0px)";if(e.bounds)return`inset(${e.bounds.top}px calc(100% - ${e.bounds.right}px) calc(100% - ${e.bounds.bottom}px) ${e.bounds.left}px)`;if(e.viewMode==="chat-bar"){if(e.expanded&&o)return"inset(0px)";let w=o?"calc(100% - min(610px, calc(100dvh - 20px)))":"calc(100% - 114px)",v=o?"calc(50% - min(288px, 50%))":"calc(50% - min(228px, 50%))";return`inset(${w} ${v} 0px ${v})`}let s=t.appearance?.launcher,u=s?.position==="bottom-left",f=(s?.offset?.x??24)+60,l=(s?.offset?.y??24)+60;if(!o)return u?`inset(calc(100% - ${l}px) calc(100% - ${f}px) 0px 0px)`:`inset(calc(100% - ${l}px) 0px 0px calc(100% - ${f}px))`;if(e.viewMode==="sidebar")return`inset(0px 0px 0px calc(100% - (${D(t.appearance?.panel?.initialWidth??420)} + 16px)))`;let g=D(t.appearance?.panel?.initialWidth??440),E=`calc(100% - min(calc(${D(t.appearance?.panel?.initialHeight??"max(420px, 56dvh)")} + 32px), 100dvh))`;return u?`inset(${E} calc(100% - min(calc(${g} + 32px), 100vw)) 0px 0px)`:`inset(${E} 0px 0px calc(100% - min(calc(${g} + 32px), 100vw)))`}function ke(e){if(!e||typeof e!="object")return null;let t=e;return!U(t.top)||!U(t.right)||!U(t.bottom)||!U(t.left)||t.right<t.left||t.bottom<t.top?null:{bottom:t.bottom,left:t.left,right:t.right,top:t.top}}function U(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0&&e<=1e5}function Se(e,t){return e.sidebarWidth===null?D(t.appearance?.panel?.initialWidth??420):`${Math.round(e.sidebarWidth)}px`}function D(e){return typeof e=="number"?`${Math.round(e)}px`:e}function pe(e,t){if(t&&!e.scrollLock){e.scrollLock={x:window.scrollX,y:window.scrollY},e.root.style.overflow="hidden",e.body.style.overflow="hidden",e.body.style.position="fixed",e.body.style.top=`${-e.scrollLock.y}px`,e.body.style.left=`${-e.scrollLock.x}px`,e.body.style.right="0",e.body.style.width="100%";return}if(!t&&e.scrollLock){let{x:n,y:o}=e.scrollLock;e.root.style.overflow=e.original.rootOverflow,e.body.style.overflow=e.original.bodyOverflow,e.body.style.position=e.original.bodyPosition,e.body.style.top=e.original.bodyTop,e.body.style.left=e.original.bodyLeft,e.body.style.right=e.original.bodyRight,e.body.style.width=e.original.bodyWidth,e.scrollLock=null;try{window.scrollTo(n,o)}catch{}}}var Le=16*1024;function et(e,t){let n=e;if(!n?.agentId||typeof n.getAuthToken!="function")throw new Error("HeroUIAgent.mount requires agentId and getAuthToken");G(n.tools??[]);let o=crypto.randomUUID(),s=crypto.randomUUID(),u=(n._api?.baseUrl??te).replace(/\/$/,""),f=Ue(u,n._api?.embedOrigin),l=document.createElement("iframe"),g=new Map,b=[],E=new Set,w=new Map,v=new Set,I=ce(),T=new M(n.agentId,n.getAuthToken),a=le(n),F=null,y=!1,x=!1,k=null,q=!1,S=!1,j=n.preload!==!1,C=()=>{S||T.get().catch(()=>{})},z=new URL("/embed",f);z.searchParams.set("agentId",n.agentId),z.hash=new URLSearchParams({nonce:s,parentOrigin:window.location.origin,sessionId:o}).toString(),l.allow="microphone; clipboard-write",l.referrerPolicy="origin",l.setAttribute("sandbox","allow-downloads allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"),l.src=z.toString(),l.title="HeroUI Agent",Object.assign(l.style,A(a,n,window.innerWidth<640,window.visualViewport?.scale===1?window.visualViewport:void 0)),document.body.append(l);let m=(r,i,d)=>{S||l.contentWindow?.postMessage(re(o,s,r,i,d),f)},fe=(r,i)=>{let d=crypto.randomUUID();return new Promise((c,p)=>{let h=window.setTimeout(()=>{g.delete(d),p(new Error(`HeroUI Agent iframe request timed out: ${r}`))},3e4);g.set(d,{reject:p,resolve:c,timer:h}),m(r,i,d)})},_=new Map,J=0,Y="",N=!1,K=async(r,i=!1)=>{let d=++J;if(N)return;let c;try{c=await He(n,t)}catch(h){if(S||d!==J)return;N=!0;let O={...X(h,"failed"),category:"configuration",message:h instanceof Error?h.message.slice(0,1e3):"Invalid agent configuration",retryable:!1};n.onError?.(O),m("host.init-error",O);return}if(S||d!==J)return;let p=JSON.stringify({props:c.props,tools:c.tools});!i&&p===Y||(Y=p,m(r,{...c,restoreSession:r==="host.init"&&x}))},R=(r,i)=>{if(!a.mounted){b.push({name:r,payload:i}),b.length>32&&b.shift();return}m("host.command",{name:r,payload:i})},W=r=>{r&&C(),r&&!y&&document.activeElement!==l&&(F=document.activeElement),y=r,r||(x=!1),R(r?"show":"hide")},me=()=>{if(a.mounted)for(let r of b.splice(0))m("host.command",r)},he=()=>{x=a.mounted&&y&&!a.closing,a={...a,bounds:null,closing:!1,mounted:!1,open:x,ready:!1},q=!1;for(let r of v)r();Object.assign(l.style,A(a,n,window.innerWidth<640,window.visualViewport?.scale===1?window.visualViewport:void 0)),P(I,a,n,window.innerWidth<640)},Z=r=>{if(r.origin!==f||r.source!==l.contentWindow)return;let i=ie(r.data);if(!(!i||i.sessionId!==o||i.nonce!==s)){if(i.type==="iframe.rpc-result"||i.type==="iframe.rpc-error"){Pe(i,g);return}Me(E,i.requestId)&&we(i)}},we=async r=>{try{if(r.type==="iframe.ready"){(a.mounted||a.ready)&&he(),await K("host.init",!0);return}if(r.type==="iframe.reload"){let i=l.cloneNode(!1),d=new URL(l.src);d.pathname=`/reload/${crypto.randomUUID()}/embed`,i.src=d.href,l.replaceWith(i),l=i;return}if(r.type==="iframe.state"){let i=de(r.payload);if(i){let d=a.mounted,c=a.open||a.closing,p=i;x?i.mounted?(x=!1,i.open||(m("host.command",{name:"show"}),p={...i,bounds:null,closing:!1,open:!0,sidebarWidth:i.sidebarWidth??a.sidebarWidth})):p={...i,bounds:null,closing:!1,open:!0,sidebarWidth:i.sidebarWidth??a.sidebarWidth}:i.open?y=!0:(i.closing||a.open||a.closing)&&(y=!1);let h=p.open||p.closing;if(h&&!c&&document.activeElement!==l&&(F=document.activeElement),!h&&c&&F?.isConnected){let O=F;window.requestAnimationFrame(()=>window.requestAnimationFrame(()=>O.focus({preventScroll:!0})))}h!==c&&n.onOpenChange?.(h),a=p,a.mounted&&!d&&K("host.update"),h&&a.expanded?l.dataset.expanded="true":l.removeAttribute("data-expanded");for(let O of v)O();Object.assign(l.style,A(a,n,window.innerWidth<640,window.visualViewport?.scale===1?window.visualViewport:void 0)),P(I,a,n,window.innerWidth<640),me(),a.ready&&!q&&(q=!0,n.onReady?.())}return}if(r.type==="iframe.error"){n.onError?.(r.payload);return}if(r.type==="iframe.status"){n.onStatusChange?.(r.payload);return}if(r.type==="iframe.tool-cancel"){let i=r.payload;i.idempotencyKey&&_.get(i.idempotencyKey)?.abort();return}if(r.type==="iframe.auth-request"){let i=await T.get(),d=await V(15e3,c=>fetch(`${u}/v1/agents/${encodeURIComponent(n.agentId)}/embed-sessions`,{body:JSON.stringify({iframeOrigin:f,sessionId:o}),cache:"no-store",headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json"},method:"POST",signal:c}));if(!d.ok)throw new Error("Could not authenticate HeroUI Agent");m("host.rpc-result",{value:await d.json()},r.requestId);return}if(r.type==="iframe.context-request"){let i=_e(await n.context?.page?.()??{},Le,"Agent page context");m("host.rpc-result",{value:i},r.requestId);return}if(r.type==="iframe.tool-execute"){let i=Ce(r.payload),d=n.tools?.find(p=>p.name===i.name);if(!d)throw new Error(`Unknown client tool: ${i.name}`);let c=w.get(i.idempotencyKey);if(!c){let p=new AbortController;_.set(i.idempotencyKey,p),c=Oe(d,i,n,fe,p.signal).finally(()=>_.delete(i.idempotencyKey)),w.set(i.idempotencyKey,c),w.size>2048&&w.delete(w.keys().next().value)}m("host.rpc-result",{value:await c},r.requestId);return}if(r.type==="iframe.feedback"){await n.onFeedback?.(r.payload);return}if(r.type==="iframe.storage"){Re(r.payload);return}if(r.type==="iframe.persistence"){let i=await import("./persistence-YI6JSYCT.js").then(({executeAgentHostPersistence:d})=>d(n.agentId,r.payload));m("host.rpc-result",{value:i},r.requestId)}}catch(i){if(S)return;let d=X(i,r.type==="iframe.auth-request"?"authenticating":"failed"),c=r.type==="iframe.tool-execute",p=c&&i instanceof Error?i.message.slice(0,1e3):d.message;n.onError?.({...d,message:p}),m("host.rpc-error",{error:p,...c?{code:i instanceof H?i.code:"CLIENT_TOOL_EXECUTION_ERROR"}:{}},r.requestId)}},Q=r=>{r.composedPath().includes(l)||a.open&&(a.viewMode==="floating"||a.viewMode==="chat-bar")&&(n.appearance?.shouldCloseOnInteractOutside??!0)&&W(!1)},L=()=>{a={...a,bounds:null},Object.assign(l.style,A(a,n,window.innerWidth<640,window.visualViewport?.scale===1?window.visualViewport:void 0)),P(I,a,n,window.innerWidth<640)},$=()=>{document.visibilityState==="visible"&&(a={...a,bounds:null},Object.assign(l.style,A(a,n,window.innerWidth<640,window.visualViewport?.scale===1?window.visualViewport:void 0)),P(I,a,n,window.innerWidth<640),y&&a.mounted&&m("host.command",{name:"show"}),k!==null&&window.cancelAnimationFrame(k),k=window.requestAnimationFrame(()=>{k=null,Object.assign(l.style,A(a,n,window.innerWidth<640,window.visualViewport?.scale===1?window.visualViewport:void 0))}))};return window.addEventListener("message",Z),window.addEventListener("pointerdown",Q,!0),window.addEventListener("resize",L),window.addEventListener("pageshow",$),window.visualViewport?.addEventListener("resize",L),window.visualViewport?.addEventListener("scroll",L),document.addEventListener("visibilitychange",$),j&&C(),{destroy:()=>{S=!0,T.clear();for(let r of _.values())r.abort();_.clear(),window.removeEventListener("message",Z),window.removeEventListener("pointerdown",Q,!0),window.removeEventListener("resize",L),window.removeEventListener("pageshow",$),window.visualViewport?.removeEventListener("resize",L),window.visualViewport?.removeEventListener("scroll",L),document.removeEventListener("visibilitychange",$),k!==null&&window.cancelAnimationFrame(k);for(let r of g.values())window.clearTimeout(r.timer),r.reject(new Error("HeroUI Agent iframe was removed"));g.clear(),b.length=0,v.clear(),ue(I),l.remove()},hide:()=>W(!1),newConversation:r=>{C(),y=!0,R("newConversation",{prompt:r})},preload:()=>{C(),R("preload")},get ready(){return a.ready},refreshAuth:()=>{T.clear(),R("refreshAuth")},show:()=>W(!0),shutdown:()=>{y=!1,x=!1,T.reset(),R("shutdown")},subscribe:r=>(v.add(r),()=>v.delete(r)),toggle:()=>W(!y),update:r=>{if(r.agentId!==n.agentId)throw new Error("HeroUI Agent agentId cannot change without remounting");G(r.tools??[]);let i=N;N=!1;let d=r.getAuthToken!==n.getAuthToken,c=r.preload!==!1&&(d||!j);j=r.preload!==!1,d&&(T.clear(),T=new M(r.agentId,r.getAuthToken)),n=r,c&&C(),Object.assign(l.style,A(a,n,window.innerWidth<640,window.visualViewport?.scale===1?window.visualViewport:void 0)),P(I,a,n,window.innerWidth<640),(a.mounted||i)&&K(a.mounted?"host.update":"host.init")}}}async function Oe(e,t,n,o,s){let u={conversationId:t.conversationId,idempotencyKey:t.idempotencyKey,signal:s,toolCallId:t.toolCallId,uploadDataSource:async g=>await o("host.upload-data-source",{toolCallId:t.toolCallId,upload:g})},f;try{f=ee(e,t.args)}catch(g){return{code:"CLIENT_TOOL_INVALID_INPUT",...Fe(g),error:`The arguments for ${e.name} do not match its declared schema. Correct the input and try again. No action was performed.`,success:!1}}let l=await e.execute(f,n.context??{},u)??null;return se(l)}async function He(e,t){let{context:n,getAuthToken:o,onError:s,onFeedback:u,onOpenChange:f,onReady:l,onStatusChange:g,tools:b,...E}=e,w=await t(b??[]);return{localStorage:ge(window.localStorage),props:JSON.parse(JSON.stringify(E)),sessionStorage:ge(window.sessionStorage),tools:w}}function Pe(e,t){let n=t.get(e.requestId);if(n)if(t.delete(e.requestId),window.clearTimeout(n.timer),e.type==="iframe.rpc-error"){let o=e.payload;n.reject(new Error(typeof o?.error=="string"?o.error:"Iframe request failed"))}else{let o=e.payload;n.resolve(o?.value)}}function Ce(e){if(!e||typeof e!="object")throw new Error("Invalid client tool request");let t=e;if(typeof t.conversationId!="string"||typeof t.idempotencyKey!="string"||typeof t.name!="string"||typeof t.toolCallId!="string")throw new Error("Invalid client tool request");return t}function _e(e,t,n){if(!B(e))throw new Error(`${n} must be JSON-serializable`);let o=JSON.stringify(e);if(new TextEncoder().encode(o).byteLength>t)throw new Error(`${n} exceeds ${Math.round(t/1024)} KiB`);return JSON.parse(o)}function ge(e){let t={};try{for(let n=0;n<e.length;n+=1){let o=e.key(n);o?.startsWith("heroui-agent:")&&(t[o]=e.getItem(o)??"")}}catch{}return t}function Re(e){if(!e||typeof e!="object")return;let t=e;if(typeof t.key=="string"&&!t.key.startsWith("heroui-agent:"))return;let n=t.storage==="session"?sessionStorage:localStorage;try{if(t.operation==="set"&&typeof t.key=="string"&&typeof t.value=="string")n.setItem(t.key,t.value);else if(t.operation==="remove"&&typeof t.key=="string")n.removeItem(t.key);else if(t.operation==="clear")for(let o=n.length-1;o>=0;o-=1){let s=n.key(o);s?.startsWith("heroui-agent:")&&n.removeItem(s)}}catch{}}function Me(e,t){return e.has(t)?!1:(e.add(t),e.size>2048&&e.delete(e.values().next().value),!0)}function Ue(e,t){if(t){let n=new URL(t);if(n.protocol!=="http:"&&n.protocol!=="https:")throw new Error("HeroUI Agent embed origin must use HTTP or HTTPS");return n.origin}return/^https?:\/\/(?:localhost|127\.0\.0\.1)(?::\d+)?$/i.test(e)?"http://localhost:8793":e.includes("staging")?oe:ne}function Fe(e){if(!e||typeof e!="object"||!("issues"in e)||!Array.isArray(e.issues))return{};let t=e.issues.slice(0,8).flatMap(n=>{if(!n||typeof n!="object")return[];let o=n;return!Array.isArray(o.path)||typeof o.code!="string"?[]:[{code:o.code.slice(0,60),path:o.path.filter(s=>typeof s=="string"||typeof s=="number").join(".").slice(0,200),...typeof o.minimum=="number"&&Number.isFinite(o.minimum)?{minimum:o.minimum}:{},...typeof o.maximum=="number"&&Number.isFinite(o.maximum)?{maximum:o.maximum}:{},...typeof o.expected=="string"?{expected:o.expected.slice(0,100)}:{}}]});return t.length?{issues:t}:{}}export{ye as a,V as b,X as c,M as d,H as e,se as f,et as g};
@@ -0,0 +1 @@
1
+ var e={animation:"fadeIn",duration:160,easing:"ease-out",sep:"word",stagger:18};export{e as a};
@@ -0,0 +1 @@
1
+ function T(){return function(e){return e}}function u(t){return!!(t&&typeof t=="object"&&typeof t.safeParse=="function")}function h(t,e){return u(t.parameters)?t.parameters.parse(e):e}function x(t,e){let n=t.get(e);return!n||n.needsApproval?null:n}var d=["readConversationHistory","callMcpTool","composeUI","executeSandbox","generateFile","getUIComponents","readSkill","renderComponent","renderDocument","searchKnowledge","searchMcpTools","searchWeb"],l="mcp_";function p(t){return/^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/.test(t)?d.includes(t)||t.toLowerCase().startsWith(l)?`Client tool name '${t}' is reserved by HeroUI Agent. Rename this tool.`:null:`Client tool name '${t}' must start with a letter and contain only letters, digits, underscores, or hyphens (maximum 64 characters).`}function S(t){let e=new Set;for(let{name:n}of t){let o=p(n);if(o)throw new Error(o);if(e.has(n))throw new Error(`Duplicate client tool name: ${n}`);e.add(n)}}var b="https://agent.heroui.pro",w="https://staging-agent.heroui.pro",k=262144,B=57671680;function C(t,e,n,o,i=crypto.randomUUID()){return{nonce:e,...o===void 0?{}:{payload:o},requestId:i,sessionId:t,type:n}}function _(t){if(!t||typeof t!="object")return null;let e=t;if(typeof e.sessionId!="string"||!a(e.sessionId)||typeof e.nonce!="string"||!a(e.nonce)||typeof e.requestId!="string"||!a(e.requestId)||typeof e.type!="string"||e.type.length===0||!A.has(e.type))return null;let n=e.type==="iframe.persistence"||e.type==="host.upload-data-source"||e.type==="host.rpc-result"?57671680:262144;return m(t)>n||!y(e.type,e.payload)?null:{nonce:e.nonce,...Object.hasOwn(e,"payload")?{payload:e.payload}:{},requestId:e.requestId,sessionId:e.sessionId,type:e.type}}function f(t,e=new Set){if(t===null||typeof t=="string"||typeof t=="boolean")return!0;if(typeof t=="number")return Number.isFinite(t);if(typeof t!="object"||e.has(t))return!1;e.add(t);let n=i=>i===void 0||f(i,e),o=Array.isArray(t)?t.every(n):Object.getPrototypeOf(t)===Object.prototype&&Object.values(t).every(n);return e.delete(t),o}function m(t){return g(t,new Set)}function g(t,e){if(t==null)return 4;if(typeof t=="string")return new TextEncoder().encode(t).byteLength+2;if(typeof t=="boolean"||typeof t=="number")return 8;if(typeof t!="object"||e.has(t))return Number.POSITIVE_INFINITY;if(t instanceof ArrayBuffer||ArrayBuffer.isView(t))return t.byteLength;if(typeof Blob<"u"&&t instanceof Blob)return t.size;e.add(t);let n=2;for(let[o,i]of Object.entries(t))if(n+=g(o,e)+g(i,e),!Number.isFinite(n))break;return e.delete(t),n}function a(t){return t.length>0&&t.length<=128&&/^[A-Za-z0-9._:-]+$/.test(t)}var A=new Set(["host.command","host.init","host.init-error","host.update","host.rpc-result","host.rpc-error","host.upload-data-source","iframe.ready","iframe.reload","iframe.auth-request","iframe.context-request","iframe.feedback","iframe.persistence","iframe.state","iframe.storage","iframe.rpc-result","iframe.rpc-error","iframe.error","iframe.status","iframe.tool-cancel","iframe.tool-execute"]),c=["loading","authenticating","connecting","ready","generating","finishing","reconnecting","failed"],s=t=>!!(t&&typeof t=="object"&&!Array.isArray(t)),r=t=>typeof t=="string";function y(t,e){if(t==="iframe.ready"||t==="iframe.reload"||t==="iframe.auth-request"||t==="iframe.context-request")return e===void 0;if(t==="iframe.status")return c.includes(e);if(!s(e))return!1;switch(t){case"host.init":case"host.update":return s(e.props)&&s(e.localStorage)&&s(e.sessionStorage)&&Object.values(e.localStorage).every(r)&&Object.values(e.sessionStorage).every(r)&&(e.restoreSession===void 0||typeof e.restoreSession=="boolean")&&Array.isArray(e.tools)&&e.tools.every(n=>s(n)&&r(n.name)&&r(n.description)&&s(n.inputSchema));case"host.command":return["show","hide","toggle","preload","refreshAuth","shutdown","newConversation"].includes(e.name);case"host.rpc-error":case"iframe.rpc-error":return r(e.error)&&(e.code===void 0||r(e.code));case"host.rpc-result":case"iframe.rpc-result":return!0;case"host.upload-data-source":return r(e.toolCallId)&&s(e.upload);case"iframe.tool-cancel":return r(e.idempotencyKey);case"iframe.tool-execute":return["name","conversationId","idempotencyKey","toolCallId"].every(n=>r(e[n]));case"host.init-error":case"iframe.error":return["authentication","connection","configuration","chunk","rate_limit","credits","deleted","approval","unknown"].includes(e.category)&&c.includes(e.phase)&&r(e.message)&&r(e.diagnosticId)&&typeof e.retryable=="boolean";case"iframe.state":return["open","ready","expanded","closing","dragging","mounted","overlayOpen","resizing"].every(n=>typeof e[n]=="boolean")&&["chat-bar","floating","sidebar"].includes(e.viewMode);case"iframe.storage":return["local","session"].includes(e.storage)&&(e.operation==="clear"||r(e.key)&&(e.operation==="remove"||e.operation==="set"&&r(e.value)));case"iframe.persistence":return["composer-image-drafts","message-outboxes"].includes(e.store)&&(e.operation==="deletePrefix"?r(e.prefix):["get","put","delete"].includes(e.operation)&&r(e.key));case"iframe.feedback":return r(e.conversationId)&&r(e.messageId)&&["positive","negative"].includes(e.rating)&&Array.isArray(e.reasons)}}export{T as a,u as b,h as c,x as d,d as e,l as f,p as g,S as h,b as i,w as j,k,B as l,C as m,_ as n,f as o,m as p};
@@ -0,0 +1 @@
1
+ import{b as i,g as a,o as m}from"./chunk-FZXNXBTZ.js";import{z as t}from"zod";var c=20,p=16*1024,d=t.object({description:t.string().trim().min(1).max(1e3),inputSchema:t.record(t.string(),t.unknown()),name:t.string().superRefine((e,n)=>{let o=a(e);o&&n.addIssue({code:"custom",message:o})}),needsApproval:t.boolean().optional()}),l=t.array(d).max(c).superRefine((e,n)=>{let o=new Set;for(let r of e)o.has(r.name)&&n.addIssue({code:"custom",message:`Duplicate client tool name: ${r.name}`}),o.add(r.name);new TextEncoder().encode(JSON.stringify(e)).byteLength>p&&n.addIssue({code:"custom",message:"Client tool manifest exceeds 16KB"})});function f(e){let n=i(e.parameters)?t.toJSONSchema(e.parameters):e.parameters;if(!m(n))throw new Error(`Client tool ${e.name} parameters must be a JSON-serializable schema`);return s(n)}function s(e){return Array.isArray(e)?e.map(s):e&&typeof e=="object"?Object.fromEntries(Object.entries(e).map(([n,o])=>[n,s(o)])):e}function h(e){return l.parse(e.map(n=>({description:n.description,inputSchema:f(n),name:n.name,...n.needsApproval?{needsApproval:!0}:{}})))}export{c as a,p as b,d as c,l as d,h as e};
@@ -0,0 +1 @@
1
+ import{g as S}from"./chunk-5CSSZ76R.js";import{b as p}from"./chunk-FZXNXBTZ.js";import{useEffect as O,useLayoutEffect as m,useMemo as b,useRef as f,useState as k,useSyncExternalStore as H}from"react";var i=new Map,h=new Set;function C(){for(let t of h)t()}function R(t){return h.add(t),()=>h.delete(t)}function a(t,n,r){let o=t?i.get(t):[...i.values()][i.size-1];if(!o){console.warn("HeroUI Agent is not mounted yet. Add <HeroUIAgent /> to your layout.");return}n==="newConversation"?o.newConversation(r):o[n]()}function w(t){let n=H(R,()=>(t?i.get(t):[...i.values()].at(-1))?.ready??!1,()=>!1);return b(()=>({hide:()=>a(t,"hide"),newConversation:r=>a(t,"newConversation",r),preload:()=>a(t,"preload"),ready:n,refreshAuth:()=>a(t,"refreshAuth"),show:()=>a(t,"show"),shutdown:()=>a(t,"shutdown"),toggle:()=>a(t,"toggle")}),[t,n])}async function I(t){let{serializeClientTools:n}=await import("./client-tools-manifest-PCUOVUXR.js");return n(t).map(o=>{let s=t.find(l=>l.name===o.name);return{...o,...s?.displayName?{displayName:s.displayName}:{},...s?.icon?{icon:s.icon}:{},...s?.iconColor?{iconColor:s.iconColor}:{}}})}function P(t){let n=f(t),r=f(null),o=f(null);m(()=>{n.current=t}),o.current||(o.current={getAuthToken:e=>n.current.getAuthToken(e),onError:e=>n.current.onError?.(e),onFeedback:e=>n.current.onFeedback?.(e),onOpenChange:e=>n.current.onOpenChange?.(e),onReady:()=>n.current.onReady?.(),onStatusChange:e=>n.current.onStatusChange?.(e)},Object.defineProperties(o.current,{context:{enumerable:!0,get(){return n.current.context}},tools:{enumerable:!0,get(){return n.current.tools}}}));let s=x(t),l=o.current;for(let e of Object.keys(l))e!=="context"&&e!=="getAuthToken"&&e!=="onError"&&e!=="onStatusChange"&&e!=="onFeedback"&&e!=="onReady"&&e!=="onOpenChange"&&e!=="tools"&&!(e in s)&&delete l[e];Object.assign(l,s,{agentId:t.agentId});let u=o.current;m(()=>{let e=S(u,I),c=e.subscribe(C);return r.current=e,i.set(t.agentId,e),C(),()=>{c(),e.destroy(),i.get(t.agentId)===e&&i.delete(t.agentId),r.current===e&&(r.current=null),C()}},[t.agentId]);let[y,g]=k("");O(()=>{let e=t.tools??[];if(!e.some(d=>p(d.parameters))){g("");return}let c=!1;return import("./client-tools-manifest-PCUOVUXR.js").then(({serializeClientTools:d})=>{c||g(JSON.stringify(d(e).map(T=>({inputSchema:T.inputSchema,name:T.name}))))}),()=>{c=!0}},[t.tools]);let A=JSON.stringify({props:x(t),tools:(t.tools??[]).map(e=>({description:e.description,displayName:e.displayName,icon:e.icon,iconColor:e.iconColor,name:e.name,needsApproval:e.needsApproval===!0,parameters:p(e.parameters)?null:e.parameters})),zodToolsSchemasKey:y});return m(()=>{r.current?.update(u)},[A]),null}function x(t){let{context:n,getAuthToken:r,onError:o,onFeedback:s,onOpenChange:l,onReady:u,onStatusChange:y,tools:g,...A}=t;return JSON.parse(JSON.stringify(A))}export{w as a,P as b};
@@ -0,0 +1 @@
1
+ import{a as c,b as d,c as e,d as f,e as g}from"./chunk-KFWXAD55.js";import{e as a,f as b}from"./chunk-FZXNXBTZ.js";export{c as MAX_CLIENT_TOOLS,d as MAX_CLIENT_TOOLS_BYTES,a as RESERVED_AGENT_TOOL_NAMES,b as RESERVED_AGENT_TOOL_PREFIX,e as clientToolManifestEntrySchema,f as clientToolsSchema,g as serializeClientTools};