@heroui/agent 0.2.0-beta.6 → 0.2.0-beta.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.
@@ -18,218 +18,27 @@ function resolveDirectToolAction(tools, toolName) {
18
18
  return tool;
19
19
  }
20
20
 
21
- // src/embed/composer-draft.ts
22
- var AGENT_COMPOSER_DRAFT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
23
- var AGENT_COMPOSER_DRAFT_DB_NAME = "heroui-agent-embed";
24
- var AGENT_COMPOSER_DRAFT_DB_VERSION = 1;
25
- var AGENT_COMPOSER_IMAGE_DRAFT_STORE = "composer-image-drafts";
26
- var AGENT_COMPOSER_DRAFT_MARKER = ":composer-draft:";
27
- var pendingImageDraftMutations = /* @__PURE__ */ new Map();
28
- function agentComposerDraftStorageKey(conversationStorageKey, conversationId) {
29
- return `${conversationStorageKey}${AGENT_COMPOSER_DRAFT_MARKER}${conversationId}:v1`;
30
- }
31
- function readAgentComposerPromptDraft(key, now = Date.now()) {
32
- if (typeof window === "undefined") return "";
33
- try {
34
- const raw = window.localStorage.getItem(key);
35
- if (!raw) return "";
36
- const parsed = JSON.parse(raw);
37
- const isFresh = typeof parsed.savedAt === "number" && now - parsed.savedAt <= AGENT_COMPOSER_DRAFT_MAX_AGE_MS;
38
- if (!isFresh || typeof parsed.prompt !== "string" || !parsed.prompt.trim()) {
39
- window.localStorage.removeItem(key);
40
- return "";
41
- }
42
- return parsed.prompt;
43
- } catch {
44
- try {
45
- window.localStorage.removeItem(key);
46
- } catch {
47
- }
48
- return "";
49
- }
50
- }
51
- function saveAgentComposerPromptDraft(key, prompt, now = Date.now()) {
52
- if (typeof window === "undefined") return;
53
- try {
54
- if (!prompt.trim()) {
55
- window.localStorage.removeItem(key);
56
- return;
57
- }
58
- window.localStorage.setItem(
59
- key,
60
- JSON.stringify({ prompt, savedAt: now })
61
- );
62
- } catch {
63
- }
64
- }
65
- async function readAgentComposerImageDraft(key, now = Date.now()) {
66
- await pendingImageDraftMutations.get(key);
67
- const stored = await readImageDraftRecord(key);
68
- if (!stored) return [];
69
- const isFresh = now - stored.savedAt <= AGENT_COMPOSER_DRAFT_MAX_AGE_MS;
70
- const files = stored.files.filter(isPersistableImageFile);
71
- if (!isFresh || files.length === 0) {
72
- await queueImageDraftMutation(key, () => deleteImageDraftRecord(key));
73
- return [];
74
- }
75
- return files;
76
- }
77
- function saveAgentComposerImageDraft(key, files, now = Date.now()) {
78
- const images = files.filter(isPersistableImageFile);
79
- return queueImageDraftMutation(
80
- key,
81
- () => images.length > 0 ? writeImageDraftRecord({ files: images, key, savedAt: now }) : deleteImageDraftRecord(key)
82
- );
83
- }
84
- function clearAgentComposerDraft(key) {
85
- if (typeof window !== "undefined") {
86
- try {
87
- window.localStorage.removeItem(key);
88
- } catch {
89
- }
90
- }
91
- return queueImageDraftMutation(key, () => deleteImageDraftRecord(key));
92
- }
93
- async function clearAgentComposerDraftsForProject(agentId) {
94
- const prefix = `heroui-agent:conversation:${agentId}:`;
95
- if (typeof window !== "undefined") {
96
- try {
97
- for (let index = window.localStorage.length - 1; index >= 0; index -= 1) {
98
- const key = window.localStorage.key(index);
99
- if (key?.startsWith(prefix) && key.includes(AGENT_COMPOSER_DRAFT_MARKER)) {
100
- window.localStorage.removeItem(key);
101
- }
102
- }
103
- } catch {
104
- }
105
- }
106
- const pending = [...pendingImageDraftMutations.entries()].filter(([key]) => key.startsWith(prefix)).map(([, mutation]) => mutation);
107
- await Promise.all(pending);
108
- await deleteImageDraftRecordsByPrefix(prefix);
109
- }
110
- function isPersistableImageFile(value) {
111
- return typeof File !== "undefined" && value instanceof File && typeof value.type === "string" && value.type.startsWith("image/");
112
- }
113
- function queueImageDraftMutation(key, mutation) {
114
- const previous = pendingImageDraftMutations.get(key) ?? Promise.resolve();
115
- const next = previous.catch(() => void 0).then(mutation).catch(() => void 0);
116
- pendingImageDraftMutations.set(key, next);
117
- void next.finally(() => {
118
- if (pendingImageDraftMutations.get(key) === next) pendingImageDraftMutations.delete(key);
119
- });
120
- return next;
121
- }
122
- function openImageDraftDatabase() {
123
- if (typeof indexedDB === "undefined") return Promise.resolve(null);
124
- return new Promise((resolve) => {
125
- const request = indexedDB.open(AGENT_COMPOSER_DRAFT_DB_NAME, AGENT_COMPOSER_DRAFT_DB_VERSION);
126
- request.onerror = () => resolve(null);
127
- request.onupgradeneeded = () => {
128
- const database = request.result;
129
- if (!database.objectStoreNames.contains(AGENT_COMPOSER_IMAGE_DRAFT_STORE)) {
130
- database.createObjectStore(AGENT_COMPOSER_IMAGE_DRAFT_STORE, { keyPath: "key" });
131
- }
132
- };
133
- request.onsuccess = () => resolve(request.result);
134
- });
135
- }
136
- async function withImageDraftStore(mode, run) {
137
- let database;
138
- try {
139
- database = await openImageDraftDatabase();
140
- } catch {
141
- return null;
142
- }
143
- if (!database) return null;
144
- try {
145
- const transaction = database.transaction(AGENT_COMPOSER_IMAGE_DRAFT_STORE, mode);
146
- const completion = waitForTransaction(transaction);
147
- const [result] = await Promise.all([
148
- run(transaction.objectStore(AGENT_COMPOSER_IMAGE_DRAFT_STORE)),
149
- completion
150
- ]);
151
- return result;
152
- } catch {
153
- return null;
154
- } finally {
155
- database.close();
156
- }
157
- }
158
- async function readImageDraftRecord(key) {
159
- const result = await withImageDraftStore(
160
- "readonly",
161
- (store) => waitForRequest(store.get(key))
162
- );
163
- if (typeof result !== "object" || result === null) return null;
164
- const draft = result;
165
- if (draft.key !== key || typeof draft.savedAt !== "number" || !Array.isArray(draft.files)) {
166
- return null;
167
- }
168
- return { files: draft.files.filter(isPersistableImageFile), key, savedAt: draft.savedAt };
169
- }
170
- async function writeImageDraftRecord(draft) {
171
- await withImageDraftStore("readwrite", async (store) => {
172
- await waitForRequest(store.put(draft));
173
- });
174
- }
175
- async function deleteImageDraftRecord(key) {
176
- await withImageDraftStore("readwrite", async (store) => {
177
- await waitForRequest(store.delete(key));
178
- });
179
- }
180
- async function deleteImageDraftRecordsByPrefix(prefix) {
181
- await withImageDraftStore(
182
- "readwrite",
183
- (store) => new Promise((resolve, reject) => {
184
- const request = store.openCursor();
185
- request.onerror = () => reject(request.error ?? new Error("Unable to clear image drafts"));
186
- request.onsuccess = () => {
187
- const cursor = request.result;
188
- if (!cursor) {
189
- resolve();
190
- return;
191
- }
192
- if (typeof cursor.key === "string" && cursor.key.startsWith(prefix)) cursor.delete();
193
- cursor.continue();
194
- };
195
- })
196
- );
197
- }
198
- function waitForRequest(request) {
199
- return new Promise((resolve, reject) => {
200
- request.onerror = () => reject(request.error ?? new Error("IndexedDB request failed"));
201
- request.onsuccess = () => resolve(request.result);
202
- });
203
- }
204
- function waitForTransaction(transaction) {
205
- return new Promise((resolve, reject) => {
206
- transaction.onabort = () => reject(transaction.error ?? new Error("IndexedDB aborted"));
207
- transaction.onerror = () => reject(transaction.error ?? new Error("IndexedDB failed"));
208
- transaction.oncomplete = () => resolve();
209
- });
210
- }
211
-
212
- // src/embed/idle.ts
213
- function scheduleIdleTask(task, timeoutMs = 3e3) {
214
- if (typeof window === "undefined") return () => void 0;
215
- const idle = window.requestIdleCallback;
216
- if (!idle) {
217
- const timeout = window.setTimeout(task, Math.min(timeoutMs, 1500));
218
- return () => window.clearTimeout(timeout);
219
- }
220
- const handle = idle(() => task(), { timeout: timeoutMs });
221
- return () => window.cancelIdleCallback?.(handle);
21
+ // src/embed/agent-loading-state.tsx
22
+ import { jsx, jsxs } from "react/jsx-runtime";
23
+ function AgentLoadingState({
24
+ indicator,
25
+ label = "Loading agent\u2026"
26
+ }) {
27
+ return /* @__PURE__ */ jsxs("div", { "aria-live": "polite", className: "ha-agent-loading", role: "status", children: [
28
+ indicator,
29
+ /* @__PURE__ */ jsx("span", { children: label })
30
+ ] });
222
31
  }
223
32
 
224
33
  // src/embed/composer-control-tooltip.tsx
225
- import { jsx, jsxs } from "react/jsx-runtime";
34
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
226
35
  function ComposerControlTooltip({
227
36
  children,
228
37
  shortcut
229
38
  }) {
230
- return /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", className: "ha-control-tooltip", children: [
231
- /* @__PURE__ */ jsx("span", { children }),
232
- shortcut ? /* @__PURE__ */ jsx("kbd", { className: "ha-control-tooltip__shortcut", children: shortcut }) : null
39
+ return /* @__PURE__ */ jsxs2("span", { "aria-hidden": "true", className: "ha-control-tooltip", children: [
40
+ /* @__PURE__ */ jsx2("span", { children }),
41
+ shortcut ? /* @__PURE__ */ jsx2("kbd", { className: "ha-control-tooltip__shortcut", children: shortcut }) : null
233
42
  ] });
234
43
  }
235
44
 
@@ -265,7 +74,7 @@ function persistSidebarWidth(agentId, width) {
265
74
  }
266
75
 
267
76
  // src/embed/panel-shell.tsx
268
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
77
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
269
78
  function PanelShell({
270
79
  actionsSlot,
271
80
  agentId,
@@ -275,6 +84,7 @@ function PanelShell({
275
84
  expandedSidebarSlot,
276
85
  expandedTitle,
277
86
  historySlot,
87
+ keepMounted = false,
278
88
  modal,
279
89
  name,
280
90
  onClose,
@@ -351,7 +161,7 @@ function PanelShell({
351
161
  const first = panel.querySelector(
352
162
  "button,textarea,input,[tabindex]:not([tabindex='-1'])"
353
163
  );
354
- first?.focus();
164
+ if (!panel.contains(document.activeElement)) first?.focus();
355
165
  const handleKeyDown = (event) => {
356
166
  if (!effectiveModal && event.key === "Escape") {
357
167
  if (dismissOpenPanelOverlay(panel)) {
@@ -396,11 +206,11 @@ function PanelShell({
396
206
  document.body.style.right = previousBodyRight;
397
207
  document.body.style.width = previousBodyWidth;
398
208
  if (effectiveModal) window.scrollTo(scrollX, scrollY);
399
- previous?.focus();
209
+ if (document.activeElement === document.body) previous?.focus();
400
210
  };
401
211
  }, [effectiveModal, open]);
402
- if (!open) return null;
403
- return /* @__PURE__ */ jsxs2(
212
+ if (!open && !keepMounted) return null;
213
+ return /* @__PURE__ */ jsxs3(
404
214
  "dialog",
405
215
  {
406
216
  ref: panelRef,
@@ -411,6 +221,7 @@ function PanelShell({
411
221
  "data-expanded": !isMobileViewport && isExpanded ? "true" : void 0,
412
222
  "data-expanded-sidebar": showExpandedSidebar ? "true" : void 0,
413
223
  "data-resizing": isResizing ? "true" : void 0,
224
+ hidden: !open,
414
225
  onCancel: (event) => {
415
226
  event.preventDefault();
416
227
  if (Date.now() - overlayEscapeAt.current < 350 || dismissOpenPanelOverlay(panelRef.current)) {
@@ -420,23 +231,23 @@ function PanelShell({
420
231
  onClose();
421
232
  },
422
233
  children: [
423
- isDesktopSidebar && !isExpanded ? /* @__PURE__ */ jsx2(SidebarResizeHandle, { agentId, panelRef }) : null,
424
- showExpandedSidebar ? /* @__PURE__ */ jsx2("aside", { "aria-label": "Chat history", className: "ha-expanded-sidebar", children: expandedSidebarSlot }) : null,
425
- /* @__PURE__ */ jsxs2("div", { className: "ha-panel-main", children: [
426
- /* @__PURE__ */ jsxs2("header", { className: "ha-header", children: [
427
- showExpandedSidebar ? /* @__PURE__ */ jsx2("span", { className: "ha-session-title ha-expanded-chat-title", children: expandedTitle ?? name }) : historySlot ?? /* @__PURE__ */ jsx2("strong", { children: name }),
428
- /* @__PURE__ */ jsxs2("div", { className: "ha-header-actions", children: [
429
- showExpandedSidebar ? null : actionsSlot ?? (onNewConversation ? /* @__PURE__ */ jsx2(
234
+ isDesktopSidebar && !isExpanded ? /* @__PURE__ */ jsx3(SidebarResizeHandle, { agentId, panelRef }) : null,
235
+ showExpandedSidebar ? /* @__PURE__ */ jsx3("aside", { "aria-label": "Chat history", className: "ha-expanded-sidebar", children: expandedSidebarSlot }) : null,
236
+ /* @__PURE__ */ jsxs3("div", { className: "ha-panel-main", children: [
237
+ /* @__PURE__ */ jsxs3("header", { className: "ha-header", children: [
238
+ showExpandedSidebar ? /* @__PURE__ */ jsx3("span", { className: "ha-session-title ha-expanded-chat-title", children: expandedTitle ?? name }) : historySlot ?? /* @__PURE__ */ jsx3("strong", { children: name }),
239
+ /* @__PURE__ */ jsxs3("div", { className: "ha-header-actions", children: [
240
+ showExpandedSidebar ? null : actionsSlot ?? (onNewConversation ? /* @__PURE__ */ jsx3(
430
241
  "button",
431
242
  {
432
243
  "aria-label": "New chat",
433
244
  className: "ha-icon-button ha-new-chat-button",
434
245
  type: "button",
435
246
  onClick: onNewConversation,
436
- children: /* @__PURE__ */ jsx2(PlusGlyph, {})
247
+ children: /* @__PURE__ */ jsx3(PlusGlyph, {})
437
248
  }
438
249
  ) : null),
439
- canExpand ? /* @__PURE__ */ jsx2(
250
+ canExpand ? /* @__PURE__ */ jsx3(
440
251
  "button",
441
252
  {
442
253
  "aria-expanded": isExpanded,
@@ -447,10 +258,10 @@ function PanelShell({
447
258
  setIsResizing(true);
448
259
  setIsExpanded((expanded) => !expanded);
449
260
  },
450
- children: isExpanded ? /* @__PURE__ */ jsx2(ChevronsCollapseUpRight, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx2(ChevronsExpandUpRight, { "aria-hidden": "true" })
261
+ children: isExpanded ? /* @__PURE__ */ jsx3(ChevronsCollapseUpRight, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx3(ChevronsExpandUpRight, { "aria-hidden": "true" })
451
262
  }
452
263
  ) : null,
453
- /* @__PURE__ */ jsxs2(
264
+ /* @__PURE__ */ jsxs3(
454
265
  "button",
455
266
  {
456
267
  "aria-keyshortcuts": "Escape",
@@ -461,15 +272,15 @@ function PanelShell({
461
272
  type: "button",
462
273
  onClick: onClose,
463
274
  children: [
464
- /* @__PURE__ */ jsx2(CloseGlyph, {}),
465
- /* @__PURE__ */ jsx2(ComposerControlTooltip, { shortcut: "ESC", children: "Close assistant" })
275
+ /* @__PURE__ */ jsx3(CloseGlyph, {}),
276
+ /* @__PURE__ */ jsx3(ComposerControlTooltip, { shortcut: "ESC", children: "Close assistant" })
466
277
  ]
467
278
  }
468
279
  )
469
280
  ] })
470
281
  ] }),
471
282
  children,
472
- /* @__PURE__ */ jsx2("div", { className: "ha-overlay-portal", "data-agent-overlay-portal": "" })
283
+ /* @__PURE__ */ jsx3("div", { className: "ha-overlay-portal", "data-agent-overlay-portal": "" })
473
284
  ] })
474
285
  ]
475
286
  }
@@ -496,8 +307,9 @@ function SidebarResizeHandle({
496
307
  const dragRef = useRef(null);
497
308
  const applyWidth = useCallback(
498
309
  (nextWidth) => {
499
- const root = panelRef.current?.getRootNode();
500
- const host = root instanceof ShadowRoot ? root.host : null;
310
+ const panel = panelRef.current;
311
+ const root = panel?.getRootNode();
312
+ const host = root instanceof ShadowRoot ? root.host : panel?.closest("[data-heroui-agent]");
501
313
  host?.style.setProperty("--ha-sidebar-width", `${nextWidth}px`);
502
314
  if (window.innerWidth >= 640) {
503
315
  document.documentElement.style.marginRight = `${Math.min(nextWidth, window.innerWidth)}px`;
@@ -517,7 +329,7 @@ function SidebarResizeHandle({
517
329
  );
518
330
  return (
519
331
  // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- A focusable separator is the WAI-ARIA "window splitter" pattern: it is interactive by specification even though jsx-a11y does not model it.
520
- /* @__PURE__ */ jsx2(
332
+ /* @__PURE__ */ jsx3(
521
333
  "div",
522
334
  {
523
335
  "aria-label": "Resize assistant panel",
@@ -539,6 +351,7 @@ function SidebarResizeHandle({
539
351
  if (!drag) return;
540
352
  dragRef.current = null;
541
353
  delete event.currentTarget.dataset["resizing"];
354
+ if (panelRef.current) delete panelRef.current.dataset["dragResizing"];
542
355
  document.documentElement.style.transition = drag.pageTransition;
543
356
  resizeTo(drag.startWidth, false);
544
357
  },
@@ -548,32 +361,35 @@ function SidebarResizeHandle({
548
361
  event.currentTarget.setPointerCapture(event.pointerId);
549
362
  event.currentTarget.dataset["resizing"] = "true";
550
363
  dragRef.current = {
364
+ currentWidth: width,
551
365
  pageTransition: document.documentElement.style.transition,
552
366
  startWidth: width,
553
367
  startX: event.clientX
554
368
  };
555
369
  document.documentElement.style.transition = "none";
370
+ if (panelRef.current) panelRef.current.dataset["dragResizing"] = "true";
556
371
  },
557
372
  onPointerMove: (event) => {
558
373
  const drag = dragRef.current;
559
374
  if (!drag) return;
560
- resizeTo(drag.startWidth + (drag.startX - event.clientX), false);
375
+ drag.currentWidth = resizeTo(drag.startWidth + (drag.startX - event.clientX), false);
561
376
  },
562
377
  onPointerUp: (event) => {
563
378
  const drag = dragRef.current;
564
379
  if (!drag) return;
565
380
  dragRef.current = null;
566
381
  delete event.currentTarget.dataset["resizing"];
382
+ if (panelRef.current) delete panelRef.current.dataset["dragResizing"];
567
383
  event.currentTarget.releasePointerCapture(event.pointerId);
568
384
  document.documentElement.style.transition = drag.pageTransition;
569
- persistSidebarWidth(agentId, width);
385
+ persistSidebarWidth(agentId, drag.currentWidth);
570
386
  }
571
387
  }
572
388
  )
573
389
  );
574
390
  }
575
391
  function CloseGlyph() {
576
- return /* @__PURE__ */ jsx2("svg", { "aria-hidden": "true", fill: "none", height: "18", viewBox: "0 0 18 18", width: "18", children: /* @__PURE__ */ jsx2(
392
+ return /* @__PURE__ */ jsx3("svg", { "aria-hidden": "true", fill: "none", height: "18", viewBox: "0 0 18 18", width: "18", children: /* @__PURE__ */ jsx3(
577
393
  "path",
578
394
  {
579
395
  d: "m4.25 4.25 9.5 9.5m0-9.5-9.5 9.5",
@@ -584,7 +400,192 @@ function CloseGlyph() {
584
400
  ) });
585
401
  }
586
402
  function PlusGlyph() {
587
- return /* @__PURE__ */ jsx2("svg", { "aria-hidden": "true", fill: "none", height: "18", viewBox: "0 0 18 18", width: "18", children: /* @__PURE__ */ jsx2("path", { d: "M9 4v10M4 9h10", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.6" }) });
403
+ return /* @__PURE__ */ jsx3("svg", { "aria-hidden": "true", fill: "none", height: "18", viewBox: "0 0 18 18", width: "18", children: /* @__PURE__ */ jsx3("path", { d: "M9 4v10M4 9h10", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.6" }) });
404
+ }
405
+
406
+ // src/contracts/models.ts
407
+ var AGENT_MODEL_IDS = [
408
+ "moonshotai/Kimi-K3",
409
+ "openai/gpt-5.6-luna",
410
+ "openai/gpt-5.6-terra",
411
+ "openai/gpt-5.6-sol",
412
+ "google/gemini-3.6-flash",
413
+ "anthropic/claude-sonnet-5",
414
+ "anthropic/claude-opus-4.8"
415
+ ];
416
+ var LEGACY_AGENT_MODEL_IDS = {
417
+ "google/gemini-3.5-flash": "google/gemini-3.6-flash"
418
+ };
419
+ function resolveAgentModelId(value) {
420
+ return LEGACY_AGENT_MODEL_IDS[value] ?? value;
421
+ }
422
+ var DEFAULT_AGENT_PICKER_MODEL_ID = "openai/gpt-5.6-luna";
423
+ var AGENT_MODEL_OPTIONS = [
424
+ {
425
+ description: "Flagship model for coding, reasoning, and knowledge work",
426
+ id: "moonshotai/Kimi-K3",
427
+ label: "Kimi K3",
428
+ provider: "Moonshot AI",
429
+ tier: "light"
430
+ },
431
+ {
432
+ description: "Fast answers and lightweight agent workflows",
433
+ id: "openai/gpt-5.6-luna",
434
+ label: "GPT-5.6 Luna",
435
+ provider: "OpenAI",
436
+ tier: "light"
437
+ },
438
+ {
439
+ description: "Balanced reasoning for everyday agent tasks",
440
+ id: "openai/gpt-5.6-terra",
441
+ label: "GPT-5.6 Terra",
442
+ provider: "OpenAI",
443
+ tier: "codegen"
444
+ },
445
+ {
446
+ description: "Deep reasoning for complex, multi-step analysis",
447
+ id: "openai/gpt-5.6-sol",
448
+ label: "GPT-5.6 Sol",
449
+ provider: "OpenAI",
450
+ tier: "complex"
451
+ },
452
+ {
453
+ description: "Fast multimodal analysis with a large context window",
454
+ id: "google/gemini-3.6-flash",
455
+ label: "Gemini 3.6 Flash",
456
+ provider: "Google",
457
+ tier: "light"
458
+ },
459
+ {
460
+ description: "Strong agentic reasoning and polished UI decisions",
461
+ id: "anthropic/claude-sonnet-5",
462
+ label: "Claude Sonnet 5",
463
+ provider: "Anthropic",
464
+ tier: "codegen"
465
+ },
466
+ {
467
+ description: "Highest-capability Claude for difficult research and analysis",
468
+ id: "anthropic/claude-opus-4.8",
469
+ label: "Claude Opus 4.8",
470
+ provider: "Anthropic",
471
+ tier: "complex"
472
+ }
473
+ ];
474
+ var AGENT_MODEL_ID_SET = new Set(AGENT_MODEL_IDS);
475
+ function isAgentModelId(value) {
476
+ return typeof value === "string" && AGENT_MODEL_ID_SET.has(value);
477
+ }
478
+ function getAgentModelTier(modelId) {
479
+ const resolved = modelId ? resolveAgentModelId(modelId) : void 0;
480
+ return AGENT_MODEL_OPTIONS.find((option) => option.id === resolved)?.tier ?? "light";
481
+ }
482
+
483
+ // src/embed/shell-handoff.ts
484
+ function createSubmittedAgentShellHandoff(prompt) {
485
+ const startedAt = performance.now();
486
+ const startedAtWallClock = Date.now();
487
+ const trimmedPrompt = prompt.trim();
488
+ if (!trimmedPrompt) return null;
489
+ return {
490
+ messageId: crypto.randomUUID(),
491
+ prompt: trimmedPrompt,
492
+ startedAt,
493
+ startedAtWallClock,
494
+ submitted: true
495
+ };
496
+ }
497
+ var handoffs = /* @__PURE__ */ new Map();
498
+ var attachmentHandoffs = /* @__PURE__ */ new Map();
499
+ var SHELL_DRAFT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
500
+ var focus = /* @__PURE__ */ new Map();
501
+ function shellDraftStorageKey(agentId) {
502
+ return `heroui-agent:shell-draft:${agentId}:composer-draft:v1`;
503
+ }
504
+ function saveShellDraft(agentId, prompt) {
505
+ try {
506
+ const key = shellDraftStorageKey(agentId);
507
+ if (prompt.trim()) localStorage.setItem(key, JSON.stringify({ prompt, savedAt: Date.now() }));
508
+ else localStorage.removeItem(key);
509
+ } catch {
510
+ }
511
+ }
512
+ function readShellDraft(agentId) {
513
+ const key = shellDraftStorageKey(agentId);
514
+ try {
515
+ const draft = JSON.parse(localStorage.getItem(key) ?? "null");
516
+ if (draft && typeof draft.prompt === "string" && draft.prompt.trim() && typeof draft.savedAt === "number" && Date.now() - draft.savedAt <= SHELL_DRAFT_MAX_AGE_MS) {
517
+ return draft.prompt;
518
+ }
519
+ localStorage.removeItem(key);
520
+ } catch {
521
+ }
522
+ return "";
523
+ }
524
+ function writeAgentShellHandoff(agentId, handoff) {
525
+ if (!handoff.prompt.trim() && !handoff.submitted) {
526
+ handoffs.delete(agentId);
527
+ saveShellDraft(agentId, "");
528
+ return;
529
+ }
530
+ handoffs.set(agentId, handoff);
531
+ if (!handoff.submitted) {
532
+ saveShellDraft(agentId, handoff.prompt);
533
+ }
534
+ }
535
+ function readAgentShellHandoff(agentId) {
536
+ const buffered = handoffs.get(agentId);
537
+ if (buffered) return buffered;
538
+ const persisted = readShellDraft(agentId);
539
+ return persisted ? { prompt: persisted, submitted: false } : void 0;
540
+ }
541
+ function markAgentShellFeedbackPainted(agentId, messageId, paintedAt) {
542
+ const handoff = handoffs.get(agentId);
543
+ if (!handoff?.submitted || handoff.messageId !== messageId || handoff.firstFeedbackPaintedAt !== void 0) {
544
+ return;
545
+ }
546
+ handoffs.set(agentId, { ...handoff, firstFeedbackPaintedAt: paintedAt });
547
+ }
548
+ function takeAgentShellHandoff(agentId) {
549
+ const handoff = readAgentShellHandoff(agentId);
550
+ handoffs.delete(agentId);
551
+ saveShellDraft(agentId, "");
552
+ return handoff;
553
+ }
554
+ function setFocus(agentId, focused, textarea) {
555
+ if (focused) {
556
+ focus.set(agentId, [
557
+ textarea?.selectionStart ?? 0,
558
+ textarea?.selectionEnd ?? 0,
559
+ textarea?.selectionDirection ?? "none"
560
+ ]);
561
+ } else {
562
+ focus.delete(agentId);
563
+ }
564
+ }
565
+ function restoreFocus(agentId, textarea) {
566
+ const selection = focus.get(agentId);
567
+ if (!selection || !textarea) return;
568
+ textarea.focus({ preventScroll: true });
569
+ const start = Math.min(selection[0], textarea.value.length);
570
+ const end = Math.min(selection[1], textarea.value.length);
571
+ textarea.setSelectionRange(start, end, selection[2]);
572
+ focus.set(agentId, [start, end, selection[2]]);
573
+ }
574
+ function containsComposerFocus(composer, target) {
575
+ if (!(target instanceof Node)) return false;
576
+ return composer.contains(target) || composer.closest(".ha-composer-wrap")?.querySelector(".ha-footnote")?.contains(target) === true || composer.closest(".ha-root")?.querySelector(".ha-overlay-portal")?.contains(target) === true;
577
+ }
578
+ function writeAgentShellAttachments(agentId, files) {
579
+ if (files.length === 0) attachmentHandoffs.delete(agentId);
580
+ else attachmentHandoffs.set(agentId, files);
581
+ }
582
+ function readAgentShellAttachments(agentId) {
583
+ return attachmentHandoffs.get(agentId) ?? [];
584
+ }
585
+ function takeAgentShellAttachments(agentId) {
586
+ const files = readAgentShellAttachments(agentId);
587
+ attachmentHandoffs.delete(agentId);
588
+ return files;
588
589
  }
589
590
 
590
591
  // src/embed/composer-textarea.ts
@@ -643,10 +644,10 @@ function resizeComposerTextarea(textarea, {
643
644
  }
644
645
 
645
646
  // src/embed/powered-by-hero.tsx
646
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
647
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
647
648
  var POWERED_BY_HERO_URL = "https://heroui.pro/?utm_source=heroui-agent&utm_medium=referral&utm_campaign=powered-by-hero";
648
649
  function PoweredByHero() {
649
- return /* @__PURE__ */ jsxs3(
650
+ return /* @__PURE__ */ jsxs4(
650
651
  "a",
651
652
  {
652
653
  "aria-label": "Powered by HeroUI Pro (opens in a new tab)",
@@ -655,16 +656,16 @@ function PoweredByHero() {
655
656
  rel: "noopener noreferrer",
656
657
  target: "_blank",
657
658
  children: [
658
- /* @__PURE__ */ jsx3("span", { children: "Powered by" }),
659
- /* @__PURE__ */ jsxs3("svg", { "aria-hidden": "true", className: "ha-powered-by-logo", fill: "none", viewBox: "0 0 140 44", children: [
660
- /* @__PURE__ */ jsx3(
659
+ /* @__PURE__ */ jsx4("span", { children: "Powered by" }),
660
+ /* @__PURE__ */ jsxs4("svg", { "aria-hidden": "true", className: "ha-powered-by-logo", fill: "none", viewBox: "0 0 140 44", children: [
661
+ /* @__PURE__ */ jsx4(
661
662
  "path",
662
663
  {
663
664
  d: "M.678 11.385V24.04c0 .599.307 1.155.813 1.471l8.628 5.396c1.15.719 2.639-.11 2.639-1.47V18.798c0-.612.322-1.18.847-1.491l5.263-3.13v27.267c0 1.355 1.48 2.186 2.63 1.476l8.905-5.497a1.72 1.72 0 0 0 .822-1.476V9.765c0-1.349-1.467-2.18-2.618-1.484l-9.74 5.897V2.556c0-1.345-1.46-2.177-2.61-1.488L1.52 9.897a1.72 1.72 0 0 0-.842 1.488Z",
664
665
  fill: "currentColor"
665
666
  }
666
667
  ),
667
- /* @__PURE__ */ jsx3(
668
+ /* @__PURE__ */ jsx4(
668
669
  "path",
669
670
  {
670
671
  d: "M63.876 24.071c0-3.59-1.468-5.246-4.405-5.246-3.363 0-5.732 2.255-5.732 7.316v11.595h-6.063V5.528h6.063v11.779c1.468-2.393 3.884-3.59 7.2-3.59 5.637 0 8.953 3.451 8.953 9.25v14.769h-6.016V24.071ZM84.9 38.473c-7.533 0-12.317-4.877-12.317-12.378 0-7.408 4.737-12.378 12.317-12.378 8.195 0 12.648 5.798 11.416 13.942H78.647c.331 3.865 2.605 6.075 6.253 6.075 2.984 0 4.784-1.52 5.258-3.037h6.016c-.947 4.509-5.115 7.776-11.274 7.776ZM78.789 23.657h11.653c-.047-3.175-2.132-5.338-5.685-5.338-3.174 0-5.4 1.84-5.968 5.338ZM99.623 20.344c0-3.82 2.131-5.89 6.205-5.89h7.77v4.97h-7.912v18.312h-6.063V20.344ZM126.863 38.473c-7.674 0-12.553-4.924-12.553-12.378 0-7.454 4.879-12.378 12.553-12.378 7.58 0 12.459 4.924 12.459 12.378 0 7.454-4.88 12.378-12.46 12.378Zm0-5.016c3.79 0 6.3-2.898 6.3-7.362 0-4.463-2.51-7.408-6.3-7.408-3.838 0-6.348 2.945-6.348 7.408 0 4.464 2.51 7.362 6.348 7.362Z",
@@ -677,43 +678,11 @@ function PoweredByHero() {
677
678
  );
678
679
  }
679
680
 
680
- // src/embed/shell-handoff.ts
681
- var handoffs = /* @__PURE__ */ new Map();
682
- function shellDraftStorageKey(agentId) {
683
- return `heroui-agent:shell-draft:${agentId}:composer-draft:v1`;
684
- }
685
- function writeAgentShellHandoff(agentId, handoff) {
686
- if (!handoff.prompt.trim() && !handoff.submitted) {
687
- handoffs.delete(agentId);
688
- saveAgentComposerPromptDraft(shellDraftStorageKey(agentId), "");
689
- return;
690
- }
691
- handoffs.set(agentId, handoff);
692
- if (!handoff.submitted) {
693
- saveAgentComposerPromptDraft(shellDraftStorageKey(agentId), handoff.prompt);
694
- }
695
- }
696
- function readAgentShellHandoff(agentId) {
697
- const buffered = handoffs.get(agentId);
698
- if (buffered) return buffered;
699
- const persisted = readAgentComposerPromptDraft(shellDraftStorageKey(agentId));
700
- return persisted ? { prompt: persisted, submitted: false } : void 0;
701
- }
702
- function takeAgentShellHandoff(agentId) {
703
- const handoff = readAgentShellHandoff(agentId);
704
- handoffs.delete(agentId);
705
- saveAgentComposerPromptDraft(shellDraftStorageKey(agentId), "");
706
- return handoff;
707
- }
708
-
709
681
  // src/embed/permissions.ts
710
682
  var AGENT_PERMISSION_MODES = /* @__PURE__ */ new Set(["ask", "auto", "full"]);
711
683
  function resolveAgentPermissionMode(value) {
712
684
  return AGENT_PERMISSION_MODES.has(value) ? value : "auto";
713
685
  }
714
- function shouldRestoreActiveAgentPermissionMode(bootstrapStatus, hasUnresolvedClientTool) {
715
- return hasUnresolvedClientTool || bootstrapStatus === "pending" || bootstrapStatus === "streaming";
716
- }
717
686
  function agentPermissionModeStorageKey(conversationStorageKey, conversationId) {
718
687
  return `${conversationStorageKey}:permission-mode:${conversationId}:v1`;
719
688
  }
@@ -838,81 +807,103 @@ function buildThemeStyle(theme) {
838
807
  return style;
839
808
  }
840
809
 
841
- // src/contracts/models.ts
842
- var AGENT_MODEL_IDS = [
843
- "moonshotai/Kimi-K3",
844
- "openai/gpt-5.6-luna",
845
- "openai/gpt-5.6-terra",
846
- "openai/gpt-5.6-sol",
847
- "google/gemini-3.6-flash",
848
- "anthropic/claude-sonnet-5",
849
- "anthropic/claude-opus-4.8"
850
- ];
851
- var LEGACY_AGENT_MODEL_IDS = {
852
- "google/gemini-3.5-flash": "google/gemini-3.6-flash"
810
+ // src/contracts/runtime.ts
811
+ var HEROUI_AGENT_RUNTIME_TIMING_EVENT = "heroui-agent:runtime-timing";
812
+ var HEROUI_AGENT_TURN_ADMITTED_FRAME_TYPE = "heroui_agent_turn_admitted";
813
+ var HEROUI_AGENT_TURN_ADMISSION_STATUS_METHOD = "getTurnAdmissionStatuses";
814
+ var HEROUI_AGENT_TURN_ADMISSION_STATUS_MAX_IDS = 50;
815
+
816
+ // ../agent-client/src/token-manager.ts
817
+ function anonymousStorageKey(agentId) {
818
+ return `heroui-agent:anonymous:${agentId}`;
819
+ }
820
+ var EmbedSessionManager = class {
821
+ constructor(agentId, getAuthToken) {
822
+ this.agentId = agentId;
823
+ this.getAuthToken = getAuthToken;
824
+ }
825
+ cached = null;
826
+ generation = 0;
827
+ pending = null;
828
+ clear() {
829
+ this.generation += 1;
830
+ this.cached = null;
831
+ this.pending = null;
832
+ }
833
+ /**
834
+ * Forgets both the cached credential and the browser's anonymous id, so the
835
+ * next request represents a brand-new person. Used on logout to stop a
836
+ * shared device from attributing the next visitor to the previous user.
837
+ */
838
+ reset() {
839
+ this.clear();
840
+ try {
841
+ localStorage.removeItem(anonymousStorageKey(this.agentId));
842
+ } catch {
843
+ }
844
+ }
845
+ async get() {
846
+ if (this.cached && this.expiresAt(this.cached) > Date.now() + 6e4) {
847
+ return this.cached.token;
848
+ }
849
+ const generation = this.generation;
850
+ const pending = this.pending ?? this.refresh();
851
+ if (!this.pending) this.pending = pending;
852
+ try {
853
+ const result = await pending;
854
+ if (generation !== this.generation) return this.get();
855
+ this.cached = result;
856
+ return result.token;
857
+ } finally {
858
+ if (this.pending === pending) this.pending = null;
859
+ }
860
+ }
861
+ async subjectHash() {
862
+ const token = await this.get();
863
+ const payload = decodeTokenPayload(token);
864
+ const subject = typeof payload["sub"] === "string" ? payload["sub"] : "anonymous";
865
+ const digest = await crypto.subtle.digest(
866
+ "SHA-256",
867
+ new TextEncoder().encode(`${this.agentId}:${subject}`)
868
+ );
869
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 24);
870
+ }
871
+ anonymousId() {
872
+ const key = anonymousStorageKey(this.agentId);
873
+ try {
874
+ const existing = localStorage.getItem(key);
875
+ if (existing) return existing;
876
+ const created = crypto.randomUUID();
877
+ localStorage.setItem(key, created);
878
+ return created;
879
+ } catch {
880
+ return crypto.randomUUID();
881
+ }
882
+ }
883
+ expiresAt(response) {
884
+ return response.expiresAt;
885
+ }
886
+ async refresh() {
887
+ const result = await this.getAuthToken({
888
+ agentId: this.agentId,
889
+ anonymousId: this.anonymousId()
890
+ });
891
+ if (!result || typeof result.token !== "string" || !result.token.trim() || typeof result.expiresAt !== "number" || !Number.isFinite(result.expiresAt) || result.expiresAt <= 0) {
892
+ throw new Error("Auth token callback returned invalid data");
893
+ }
894
+ return { expiresAt: result.expiresAt, token: result.token };
895
+ }
853
896
  };
854
- function resolveAgentModelId(value) {
855
- return LEGACY_AGENT_MODEL_IDS[value] ?? value;
856
- }
857
- var DEFAULT_AGENT_PICKER_MODEL_ID = "openai/gpt-5.6-luna";
858
- var AGENT_MODEL_OPTIONS = [
859
- {
860
- description: "Flagship model for coding, reasoning, and knowledge work",
861
- id: "moonshotai/Kimi-K3",
862
- label: "Kimi K3",
863
- provider: "Moonshot AI",
864
- tier: "light"
865
- },
866
- {
867
- description: "Fast answers and lightweight agent workflows",
868
- id: "openai/gpt-5.6-luna",
869
- label: "GPT-5.6 Luna",
870
- provider: "OpenAI",
871
- tier: "light"
872
- },
873
- {
874
- description: "Balanced reasoning for everyday agent tasks",
875
- id: "openai/gpt-5.6-terra",
876
- label: "GPT-5.6 Terra",
877
- provider: "OpenAI",
878
- tier: "codegen"
879
- },
880
- {
881
- description: "Deep reasoning for complex, multi-step analysis",
882
- id: "openai/gpt-5.6-sol",
883
- label: "GPT-5.6 Sol",
884
- provider: "OpenAI",
885
- tier: "complex"
886
- },
887
- {
888
- description: "Fast multimodal analysis with a large context window",
889
- id: "google/gemini-3.6-flash",
890
- label: "Gemini 3.6 Flash",
891
- provider: "Google",
892
- tier: "light"
893
- },
894
- {
895
- description: "Strong agentic reasoning and polished UI decisions",
896
- id: "anthropic/claude-sonnet-5",
897
- label: "Claude Sonnet 5",
898
- provider: "Anthropic",
899
- tier: "codegen"
900
- },
901
- {
902
- description: "Highest-capability Claude for difficult research and analysis",
903
- id: "anthropic/claude-opus-4.8",
904
- label: "Claude Opus 4.8",
905
- provider: "Anthropic",
906
- tier: "complex"
897
+ function decodeTokenPayload(token) {
898
+ try {
899
+ const payload = token.split(".")[1];
900
+ if (!payload) return {};
901
+ const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
902
+ const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
903
+ return JSON.parse(atob(padded));
904
+ } catch {
905
+ return {};
907
906
  }
908
- ];
909
- var AGENT_MODEL_ID_SET = new Set(AGENT_MODEL_IDS);
910
- function isAgentModelId(value) {
911
- return typeof value === "string" && AGENT_MODEL_ID_SET.has(value);
912
- }
913
- function getAgentModelTier(modelId) {
914
- const resolved = modelId ? resolveAgentModelId(modelId) : void 0;
915
- return AGENT_MODEL_OPTIONS.find((option) => option.id === resolved)?.tier ?? "light";
916
907
  }
917
908
 
918
909
  // src/contracts/attachments.ts
@@ -943,9 +934,210 @@ function isHeroUIAgentAttachmentContentType(value) {
943
934
  return HEROUI_AGENT_ATTACHMENT_CONTENT_TYPE_SET.has(value.trim().toLowerCase());
944
935
  }
945
936
 
937
+ // src/embed/shell-conversation.tsx
938
+ import { useEffect as useEffect2, useLayoutEffect, useRef as useRef2, useState as useState2 } from "react";
939
+ import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
940
+ function ShellConversation({
941
+ agentId,
942
+ composerConnecting = false,
943
+ composerControlsAvailable,
944
+ composerControlsRevealed,
945
+ composerEndSlot,
946
+ composerFooterSlot,
947
+ composerStartSlot,
948
+ greeting,
949
+ onComposerControlsReveal,
950
+ onIntent,
951
+ placeholder,
952
+ suggestedPrompts
953
+ }) {
954
+ const [initialHandoff] = useState2(() => readAgentShellHandoff(agentId));
955
+ const [input, setInput] = useState2(() => initialHandoff?.prompt ?? "");
956
+ const [submission, setSubmission] = useState2(
957
+ () => initialHandoff?.submitted ? initialHandoff : null
958
+ );
959
+ const [locallyRevealedControls, setLocallyRevealedControls] = useState2(false);
960
+ const textareaRef = useRef2(null);
961
+ const controlsAvailable = composerControlsAvailable ?? Boolean(composerStartSlot || composerEndSlot);
962
+ const controlsRevealed = composerControlsRevealed ?? (controlsAvailable && locallyRevealedControls);
963
+ const revealComposerControls = (event) => {
964
+ setFocus(agentId, true, event.currentTarget);
965
+ setLocallyRevealedControls(controlsAvailable);
966
+ if (controlsAvailable) onComposerControlsReveal?.();
967
+ onIntent();
968
+ };
969
+ const handleComposerBlur = (event) => {
970
+ const composer = event.currentTarget.querySelector(".ha-composer");
971
+ if (composer && containsComposerFocus(composer, event.relatedTarget)) return;
972
+ setLocallyRevealedControls(false);
973
+ setFocus(agentId, false);
974
+ };
975
+ useLayoutEffect(() => {
976
+ restoreFocus(agentId, textareaRef.current);
977
+ resizeComposerTextarea(textareaRef.current);
978
+ }, [agentId]);
979
+ useEffect2(() => {
980
+ if (submission) return;
981
+ writeAgentShellHandoff(agentId, { prompt: input, submitted: false });
982
+ }, [input, agentId, submission]);
983
+ useLayoutEffect(() => {
984
+ if (!submission) return;
985
+ const frame = requestAnimationFrame((paintedAt) => {
986
+ markAgentShellFeedbackPainted(agentId, submission.messageId, paintedAt);
987
+ });
988
+ return () => cancelAnimationFrame(frame);
989
+ }, [agentId, submission]);
990
+ const submit = (prompt) => {
991
+ if (submission) return;
992
+ const nextSubmission = createSubmittedAgentShellHandoff(prompt);
993
+ if (!nextSubmission) return;
994
+ setSubmission(nextSubmission);
995
+ setInput("");
996
+ textareaRef.current.style.height = "auto";
997
+ writeAgentShellHandoff(agentId, nextSubmission);
998
+ onIntent();
999
+ };
1000
+ return /* @__PURE__ */ jsxs5(Fragment, { children: [
1001
+ /* @__PURE__ */ jsx5("div", { className: "ha-conversation", children: /* @__PURE__ */ jsx5("div", { "aria-live": "polite", className: "ha-messages", role: "log", children: submission ? /* @__PURE__ */ jsxs5(Fragment, { children: [
1002
+ /* @__PURE__ */ jsx5("div", { className: "ha-message", "data-role": "user", children: /* @__PURE__ */ jsx5("div", { className: "ha-message-body", children: submission.prompt }) }),
1003
+ /* @__PURE__ */ jsx5(
1004
+ "div",
1005
+ {
1006
+ className: "ha-message",
1007
+ "data-role": "assistant",
1008
+ "data-stream-anchor": "true",
1009
+ "data-turn-feedback-id": submission.messageId,
1010
+ children: /* @__PURE__ */ jsx5("div", { className: "ha-message-body", children: /* @__PURE__ */ jsx5(
1011
+ "div",
1012
+ {
1013
+ "aria-label": "Working on your request\u2026",
1014
+ className: "ha-progress ha-activity-status ha-progress-shimmer",
1015
+ role: "status",
1016
+ children: /* @__PURE__ */ jsx5("span", { "aria-hidden": "true", className: "ha-shimmer", children: "Thinking\u2026" })
1017
+ }
1018
+ ) })
1019
+ }
1020
+ )
1021
+ ] }) : /* @__PURE__ */ jsxs5("div", { className: "ha-empty", children: [
1022
+ /* @__PURE__ */ jsx5("h2", { children: greeting }),
1023
+ /* @__PURE__ */ jsx5("p", { children: "Live answers with charts, metrics, and tables." })
1024
+ ] }) }) }),
1025
+ /* @__PURE__ */ jsxs5(
1026
+ "form",
1027
+ {
1028
+ className: "ha-composer-wrap",
1029
+ onBlur: handleComposerBlur,
1030
+ onSubmit: (event) => {
1031
+ event.preventDefault();
1032
+ submit(input);
1033
+ },
1034
+ children: [
1035
+ !submission && suggestedPrompts.length > 0 ? /* @__PURE__ */ jsx5("div", { "aria-label": "Suggested prompts", className: "ha-suggestions", role: "group", children: suggestedPrompts.map((prompt) => /* @__PURE__ */ jsxs5(
1036
+ "button",
1037
+ {
1038
+ className: "ha-suggestion",
1039
+ type: "button",
1040
+ onClick: () => submit(prompt),
1041
+ children: [
1042
+ /* @__PURE__ */ jsx5("span", { children: prompt }),
1043
+ /* @__PURE__ */ jsx5(ArrowUpRightGlyph, {})
1044
+ ]
1045
+ },
1046
+ prompt
1047
+ )) }) : null,
1048
+ /* @__PURE__ */ jsxs5(
1049
+ "div",
1050
+ {
1051
+ className: "@container ha-composer",
1052
+ "data-connecting": composerConnecting || void 0,
1053
+ "data-controls-revealed": controlsRevealed || void 0,
1054
+ children: [
1055
+ composerStartSlot,
1056
+ /* @__PURE__ */ jsx5(
1057
+ "textarea",
1058
+ {
1059
+ ref: textareaRef,
1060
+ "aria-label": "Message the assistant",
1061
+ placeholder,
1062
+ readOnly: Boolean(submission),
1063
+ rows: 1,
1064
+ value: input,
1065
+ onFocus: revealComposerControls,
1066
+ onSelect: (event) => setFocus(agentId, true, event.currentTarget),
1067
+ onChange: (event) => {
1068
+ const nextInput = event.currentTarget.value;
1069
+ writeAgentShellHandoff(agentId, { prompt: nextInput, submitted: false });
1070
+ setFocus(agentId, true, event.currentTarget);
1071
+ setInput(nextInput);
1072
+ resizeComposerTextarea(event.currentTarget);
1073
+ onIntent();
1074
+ },
1075
+ onKeyDown: (event) => {
1076
+ if (event.key !== "Enter" || event.shiftKey) return;
1077
+ event.preventDefault();
1078
+ submit(input);
1079
+ }
1080
+ }
1081
+ ),
1082
+ composerEndSlot,
1083
+ /* @__PURE__ */ jsxs5(
1084
+ "button",
1085
+ {
1086
+ "aria-label": "Send message",
1087
+ className: "ha-send",
1088
+ disabled: !input.trim() || Boolean(submission),
1089
+ type: "submit",
1090
+ children: [
1091
+ /* @__PURE__ */ jsx5(ArrowUpGlyph, {}),
1092
+ /* @__PURE__ */ jsx5(ComposerControlTooltip, { shortcut: "\u21B5", children: "Send message" })
1093
+ ]
1094
+ }
1095
+ )
1096
+ ]
1097
+ }
1098
+ ),
1099
+ /* @__PURE__ */ jsx5(
1100
+ "div",
1101
+ {
1102
+ className: "ha-composer-footer",
1103
+ "data-revealed": Boolean(composerFooterSlot) || void 0,
1104
+ children: /* @__PURE__ */ jsx5("div", { className: "ha-composer-footer-content", children: composerFooterSlot })
1105
+ }
1106
+ ),
1107
+ /* @__PURE__ */ jsx5(PoweredByHero, {})
1108
+ ]
1109
+ }
1110
+ )
1111
+ ] });
1112
+ }
1113
+ function ArrowUpGlyph() {
1114
+ return /* @__PURE__ */ jsx5("svg", { "aria-hidden": "true", fill: "none", height: "18", viewBox: "0 0 18 18", width: "18", children: /* @__PURE__ */ jsx5(
1115
+ "path",
1116
+ {
1117
+ d: "M9 14.5V3.5m0 0L4.5 8M9 3.5 13.5 8",
1118
+ stroke: "currentColor",
1119
+ strokeLinecap: "round",
1120
+ strokeLinejoin: "round",
1121
+ strokeWidth: "1.6"
1122
+ }
1123
+ ) });
1124
+ }
1125
+ function ArrowUpRightGlyph() {
1126
+ return /* @__PURE__ */ jsx5("svg", { "aria-hidden": "true", fill: "none", height: "16", viewBox: "0 0 16 16", width: "16", children: /* @__PURE__ */ jsx5(
1127
+ "path",
1128
+ {
1129
+ d: "M5 11 11 5m0 0H6m5 0v5",
1130
+ stroke: "currentColor",
1131
+ strokeLinecap: "round",
1132
+ strokeLinejoin: "round",
1133
+ strokeWidth: "1.5"
1134
+ }
1135
+ ) });
1136
+ }
1137
+
946
1138
  // src/embed/types.ts
947
1139
  var DEFAULT_MARKDOWN_ANIMATION = {
948
- animation: "blurIn",
1140
+ animation: "fadeIn",
949
1141
  duration: 160,
950
1142
  easing: "ease-out",
951
1143
  sep: "word",
@@ -987,16 +1179,18 @@ function resolveOptions(props) {
987
1179
  const composerAttachmentContentTypes = resolveComposerAttachmentContentTypes(
988
1180
  props.composer?.attachments
989
1181
  );
1182
+ const composerDisclaimer = typeof props.composer?.disclaimer === "string" ? props.composer.disclaimer.trim().slice(0, 240) || void 0 : void 0;
990
1183
  return {
991
1184
  apiBaseUrl: (props._api?.baseUrl ?? "https://api.heroui.com").replace(/\/$/, ""),
992
1185
  colorScheme: props.appearance?.theme?.colorScheme ?? "system",
993
1186
  componentExportFormats: props.componentExports === false ? [] : props.componentExports ?? ["csv", "svg", "png"],
994
1187
  composerAttachmentAccept: composerAttachmentContentTypes.join(","),
995
1188
  composerAttachmentContentTypes,
1189
+ composerControlsAvailable: composerAttachmentContentTypes.length > 0 || props.permissions?.showPicker === true && !!props.tools?.length || props.composer?.modelPicker === true || (props.composer?.dictation ?? true),
996
1190
  composerDefaultModel: props.composer?.defaultModel,
997
1191
  composerDictation: props.composer?.dictation ?? true,
998
1192
  designTheme: resolveAgentDesignTheme(props.appearance?.theme?.designTheme),
999
- ...typeof props.composer?.disclaimer === "string" && props.composer.disclaimer.trim() ? { composerDisclaimer: props.composer.disclaimer.trim().slice(0, 240) } : {},
1193
+ ...composerDisclaimer ? { composerDisclaimer } : {},
1000
1194
  agentId: props.agentId,
1001
1195
  composerModelPicker: props.composer?.modelPicker ?? false,
1002
1196
  composerPlaceholder: props.composer?.placeholder?.trim().slice(0, 120) || "Ask anything\u2026",
@@ -1045,14 +1239,8 @@ export {
1045
1239
  isZodSchema,
1046
1240
  parseClientToolArgs,
1047
1241
  resolveDirectToolAction,
1048
- agentComposerDraftStorageKey,
1049
- readAgentComposerPromptDraft,
1050
- saveAgentComposerPromptDraft,
1051
- readAgentComposerImageDraft,
1052
- saveAgentComposerImageDraft,
1053
- clearAgentComposerDraft,
1054
- clearAgentComposerDraftsForProject,
1055
- scheduleIdleTask,
1242
+ EmbedSessionManager,
1243
+ AgentLoadingState,
1056
1244
  ComposerControlTooltip,
1057
1245
  readSidebarWidth,
1058
1246
  PanelShell,
@@ -1061,12 +1249,26 @@ export {
1061
1249
  HEROUI_AGENT_ATTACHMENT_EXTENSION_BY_CONTENT_TYPE,
1062
1250
  HEROUI_AGENT_ATTACHMENT_CONTENT_TYPES,
1063
1251
  isHeroUIAgentAttachmentContentType,
1064
- resizeComposerTextarea,
1065
- PoweredByHero,
1252
+ AGENT_MODEL_IDS,
1253
+ LEGACY_AGENT_MODEL_IDS,
1254
+ resolveAgentModelId,
1255
+ DEFAULT_AGENT_PICKER_MODEL_ID,
1256
+ AGENT_MODEL_OPTIONS,
1257
+ isAgentModelId,
1258
+ getAgentModelTier,
1259
+ createSubmittedAgentShellHandoff,
1066
1260
  writeAgentShellHandoff,
1067
1261
  readAgentShellHandoff,
1068
1262
  takeAgentShellHandoff,
1069
- shouldRestoreActiveAgentPermissionMode,
1263
+ setFocus,
1264
+ restoreFocus,
1265
+ containsComposerFocus,
1266
+ writeAgentShellAttachments,
1267
+ readAgentShellAttachments,
1268
+ takeAgentShellAttachments,
1269
+ resizeComposerTextarea,
1270
+ PoweredByHero,
1271
+ ShellConversation,
1070
1272
  agentPermissionModeStorageKey,
1071
1273
  agentActivePermissionModeStorageKey,
1072
1274
  readStoredAgentPermissionMode,
@@ -1076,11 +1278,8 @@ export {
1076
1278
  AGENT_DESIGN_THEMES,
1077
1279
  DEFAULT_MARKDOWN_ANIMATION,
1078
1280
  resolveOptions,
1079
- AGENT_MODEL_IDS,
1080
- LEGACY_AGENT_MODEL_IDS,
1081
- resolveAgentModelId,
1082
- DEFAULT_AGENT_PICKER_MODEL_ID,
1083
- AGENT_MODEL_OPTIONS,
1084
- isAgentModelId,
1085
- getAgentModelTier
1281
+ HEROUI_AGENT_RUNTIME_TIMING_EVENT,
1282
+ HEROUI_AGENT_TURN_ADMITTED_FRAME_TYPE,
1283
+ HEROUI_AGENT_TURN_ADMISSION_STATUS_METHOD,
1284
+ HEROUI_AGENT_TURN_ADMISSION_STATUS_MAX_IDS
1086
1285
  };