@runtypelabs/persona 4.23.2 → 4.25.0

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 (166) hide show
  1. package/dist/animations/glyph-cycle.d.cts +1 -1
  2. package/dist/animations/glyph-cycle.d.ts +1 -1
  3. package/dist/animations/{types-CI7OckWI.d.cts → types-Db61aI9J.d.cts} +2 -0
  4. package/dist/animations/{types-CI7OckWI.d.ts → types-Db61aI9J.d.ts} +2 -0
  5. package/dist/animations/wipe.d.cts +1 -1
  6. package/dist/animations/wipe.d.ts +1 -1
  7. package/dist/animations-extra.d.cts +2 -0
  8. package/dist/animations-extra.d.ts +2 -0
  9. package/dist/approval-ui-3NMYNXBD.js +1 -0
  10. package/dist/approval-ui.cjs +2 -2
  11. package/dist/approval-ui.d.cts +278 -37
  12. package/dist/approval-ui.d.ts +278 -37
  13. package/dist/approval-ui.js +2 -2
  14. package/dist/{artifacts-ui-F4N6VMKF.js → artifacts-ui-IGB5YKLO.js} +4 -4
  15. package/dist/artifacts-ui.d.cts +273 -25
  16. package/dist/artifacts-ui.d.ts +273 -25
  17. package/dist/{chunk-NIKYKSNZ.js → chunk-7CO4T5KB.js} +1 -1
  18. package/dist/codegen.cjs +11 -11
  19. package/dist/codegen.js +13 -13
  20. package/dist/context-mentions.d.cts +273 -25
  21. package/dist/context-mentions.d.ts +273 -25
  22. package/dist/event-stream-view.d.cts +273 -25
  23. package/dist/event-stream-view.d.ts +273 -25
  24. package/dist/forms-ui.d.cts +319 -27
  25. package/dist/forms-ui.d.ts +319 -27
  26. package/dist/history-view.d.cts +2 -0
  27. package/dist/history-view.d.ts +2 -0
  28. package/dist/index.cjs +70 -69
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +344 -39
  31. package/dist/index.d.ts +344 -39
  32. package/dist/index.global.js +57 -54
  33. package/dist/index.global.js.map +1 -1
  34. package/dist/index.js +70 -69
  35. package/dist/index.js.map +1 -1
  36. package/dist/install.global.js +1 -1
  37. package/dist/install.global.js.map +1 -1
  38. package/dist/launcher.global.js +2 -2
  39. package/dist/launcher.global.js.map +1 -1
  40. package/dist/session-reconnect.d.cts +273 -25
  41. package/dist/session-reconnect.d.ts +273 -25
  42. package/dist/smart-dom-reader.d.cts +273 -25
  43. package/dist/smart-dom-reader.d.ts +273 -25
  44. package/dist/theme-editor-preview.cjs +74 -73
  45. package/dist/theme-editor-preview.d.cts +280 -25
  46. package/dist/theme-editor-preview.d.ts +280 -25
  47. package/dist/theme-editor-preview.js +51 -50
  48. package/dist/theme-editor.cjs +10 -10
  49. package/dist/theme-editor.d.cts +293 -27
  50. package/dist/theme-editor.d.ts +293 -27
  51. package/dist/theme-editor.js +10 -10
  52. package/dist/theme-reference.cjs +1 -1
  53. package/dist/theme-reference.d.cts +1522 -11
  54. package/dist/theme-reference.d.ts +1522 -11
  55. package/dist/theme-reference.js +1 -1
  56. package/dist/voice-runtime-7DA7MJH4.js +1 -0
  57. package/dist/voice-runtime.cjs +1 -1
  58. package/dist/voice-runtime.d.cts +33 -0
  59. package/dist/voice-runtime.d.ts +33 -0
  60. package/dist/voice-runtime.js +1 -1
  61. package/dist/widget.css +1 -1
  62. package/package.json +3 -2
  63. package/src/__fixtures__/defaults-parity.v4.json +776 -0
  64. package/src/approval-ui-entry.ts +0 -1
  65. package/src/approval-ui-loader.ts +5 -4
  66. package/src/approval-ui.ts +0 -1
  67. package/src/client-identity-proof.test.ts +193 -0
  68. package/src/client-session-warm.test.ts +216 -0
  69. package/src/client-visitor-history.test.ts +6 -0
  70. package/src/client.ts +122 -30
  71. package/src/codegen.test.ts +24 -0
  72. package/src/components/activity-row.test.ts +100 -0
  73. package/src/components/activity-row.ts +136 -0
  74. package/src/components/approval-actions.test.ts +8 -5
  75. package/src/components/approval-actions.ts +19 -6
  76. package/src/components/approval-bubble.test.ts +56 -9
  77. package/src/components/approval-bubble.ts +36 -41
  78. package/src/components/composer-builder.ts +5 -2
  79. package/src/components/composer-parts.ts +10 -2
  80. package/src/components/header-builder.test.ts +32 -0
  81. package/src/components/header-builder.ts +7 -4
  82. package/src/components/header-layouts.test.ts +12 -0
  83. package/src/components/header-layouts.ts +3 -2
  84. package/src/components/launcher.ts +11 -2
  85. package/src/components/message-bubble.test.ts +22 -0
  86. package/src/components/message-bubble.ts +7 -3
  87. package/src/components/panel.test.ts +52 -1
  88. package/src/components/panel.ts +47 -8
  89. package/src/components/pill-composer-builder.test.ts +2 -0
  90. package/src/components/pill-composer-builder.ts +0 -1
  91. package/src/components/reasoning-bubble.ts +29 -17
  92. package/src/components/suggestions.test.ts +1 -0
  93. package/src/components/suggestions.ts +0 -16
  94. package/src/components/tool-bubble.ts +131 -114
  95. package/src/components/tool-details.test.ts +87 -0
  96. package/src/components/tool-details.ts +102 -0
  97. package/src/components/welcome.test.ts +8 -0
  98. package/src/components/welcome.ts +4 -2
  99. package/src/defaults-parity.test.ts +158 -0
  100. package/src/defaults.test.ts +69 -0
  101. package/src/defaults.ts +128 -49
  102. package/src/generated/runtype-openapi-contract.ts +2 -2
  103. package/src/index-core.ts +4 -0
  104. package/src/install.test.ts +24 -0
  105. package/src/install.ts +3 -1
  106. package/src/launcher-global.ts +2 -1
  107. package/src/launcher-variants.test.ts +52 -0
  108. package/src/runtime/init-update-reset.test.ts +21 -0
  109. package/src/session.ts +59 -7
  110. package/src/session.voice.test.ts +72 -1
  111. package/src/styles/widget-styles.test.ts +19 -0
  112. package/src/styles/widget.css +295 -23
  113. package/src/theme-editor/index.ts +2 -0
  114. package/src/theme-editor/preview-utils.test.ts +8 -0
  115. package/src/theme-editor/preview-utils.ts +7 -3
  116. package/src/theme-editor/sections.test.ts +31 -0
  117. package/src/theme-editor/sections.ts +55 -1
  118. package/src/theme-editor/state-preferences.test.ts +49 -0
  119. package/src/theme-editor/state.ts +73 -15
  120. package/src/theme-reference.test.ts +20 -0
  121. package/src/theme-reference.ts +11 -5
  122. package/src/tool-call-display-defaults.test.ts +33 -1
  123. package/src/types/theme.ts +80 -3
  124. package/src/types.ts +207 -22
  125. package/src/ui.activity-row.test.ts +91 -0
  126. package/src/ui.approval-plugin.test.ts +63 -0
  127. package/src/ui.composer-compact.test.ts +24 -2
  128. package/src/ui.composer-placement.test.ts +23 -0
  129. package/src/ui.detached-panel.test.ts +33 -6
  130. package/src/ui.docked.test.ts +19 -0
  131. package/src/ui.header-update-stability.test.ts +25 -0
  132. package/src/ui.identity-proof.test.ts +58 -0
  133. package/src/ui.launcher-teaser.test.ts +25 -0
  134. package/src/ui.message-width.test.ts +17 -2
  135. package/src/ui.reasoning-display.test.ts +92 -0
  136. package/src/ui.rebuild-composer.test.ts +20 -3
  137. package/src/ui.scroll.test.ts +32 -0
  138. package/src/ui.session-init.test.ts +312 -0
  139. package/src/ui.status-indicator.test.ts +82 -0
  140. package/src/ui.suggest-replies.test.ts +11 -0
  141. package/src/ui.ts +422 -74
  142. package/src/ui.v5-defaults.test.ts +144 -0
  143. package/src/ui.voice-prewarm.test.ts +222 -0
  144. package/src/ui.welcome.test.ts +92 -3
  145. package/src/utils/__snapshots__/tokens-parity.test.ts.snap +811 -140
  146. package/src/utils/code-generators.ts +14 -0
  147. package/src/utils/composer-compact.test.ts +0 -2
  148. package/src/utils/composer-compact.ts +2 -4
  149. package/src/utils/composer-placement.ts +2 -3
  150. package/src/utils/config-merge.test.ts +81 -5
  151. package/src/utils/config-merge.ts +13 -3
  152. package/src/utils/defaults-provenance.ts +67 -0
  153. package/src/utils/dock.ts +7 -1
  154. package/src/utils/launcher-variant.ts +16 -0
  155. package/src/utils/panel-config.ts +97 -0
  156. package/src/utils/status-indicator.ts +39 -0
  157. package/src/utils/theme.test.ts +182 -0
  158. package/src/utils/theme.ts +131 -18
  159. package/src/utils/tokens.test.ts +122 -1
  160. package/src/utils/tokens.ts +177 -17
  161. package/src/voice/runtype-voice-provider.ts +238 -27
  162. package/src/voice/voice.test.ts +272 -2
  163. package/src/welcome.test.ts +23 -0
  164. package/src/welcome.ts +16 -9
  165. package/dist/approval-ui-K7R5Q5OC.js +0 -1
  166. package/dist/voice-runtime-EFR3R5ES.js +0 -1
@@ -1 +1 @@
1
- var t=(()=>{let r={};for(let e of[0,1,2,3,4,5,6,8,10,12,16,20,24,32,40,48,56,64])r[e]=e===0?"0px":`${e/4}rem`;return r})(),o={colors:{primary:{50:"#ffffff",100:"#f5f5f5",200:"#d4d4d4",300:"#a3a3a3",400:"#737373",500:"#171717",600:"#0f0f0f",700:"#0a0a0a",800:"#050505",900:"#030303",950:"#000000"},secondary:{50:"#f5f3ff",100:"#ede9fe",200:"#ddd6fe",300:"#c4b5fd",400:"#a78bfa",500:"#8b5cf6",600:"#7c3aed",700:"#6d28d9",800:"#5b21b6",900:"#4c1d95",950:"#2e1065"},accent:{50:"#ecfeff",100:"#cffafe",200:"#a5f3fc",300:"#67e8f9",400:"#22d3ee",500:"#06b6d4",600:"#0891b2",700:"#0e7490",800:"#155e75",900:"#164e63",950:"#083344"},gray:{50:"#f9fafb",100:"#f3f4f6",200:"#e5e7eb",300:"#d1d5db",400:"#9ca3af",500:"#6b7280",600:"#4b5563",700:"#374151",800:"#1f2937",900:"#111827",950:"#030712"},success:{50:"#f0fdf4",100:"#dcfce7",200:"#bbf7d0",300:"#86efac",400:"#4ade80",500:"#22c55e",600:"#16a34a",700:"#15803d",800:"#166534",900:"#14532d"},warning:{50:"#fefce8",100:"#fef9c3",200:"#fef08a",300:"#fde047",400:"#facc15",500:"#eab308",600:"#ca8a04",700:"#a16207",800:"#854d0e",900:"#713f12"},error:{50:"#fef2f2",100:"#fee2e2",200:"#fecaca",300:"#fca5a5",400:"#f87171",500:"#ef4444",600:"#dc2626",700:"#b91c1c",800:"#991b1b",900:"#7f1d1d"},info:{50:"#eff6ff",100:"#dbeafe",200:"#bfdbfe",300:"#93c5fd",400:"#60a5fa",500:"#3b82f6",600:"#2563eb",700:"#1d4ed8",800:"#1e40af",900:"#1e3a8a",950:"#172554"},purple:{50:"#faf5ff",100:"#f3e8ff",200:"#e9d5ff",300:"#d8b4fe",400:"#c084fc",500:"#a855f7",600:"#9333ea",700:"#7e22ce",800:"#6b21a8",900:"#581c87",950:"#3b0764"},teal:{50:"#f0fdfa",100:"#ccfbf1",200:"#99f6e4",300:"#5eead4",400:"#2dd4bf",500:"#14b8a6",600:"#0d9488",700:"#0f766e",800:"#115e59",900:"#134e4a",950:"#042f2e"}},spacing:t,typography:{fontFamily:{sans:'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',serif:'Georgia, Cambria, "Times New Roman", Times, serif',mono:"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace"},fontSize:{xs:"0.75rem",sm:"0.875rem",base:"1rem",lg:"1.125rem",xl:"1.25rem","2xl":"1.5rem","3xl":"1.875rem","4xl":"2.25rem"},fontWeight:{normal:"400",medium:"500",semibold:"600",bold:"700"},lineHeight:{tight:"1.25",normal:"1.5",relaxed:"1.625"}},shadows:{none:"none",sm:"0 1px 2px 0 rgb(0 0 0 / 0.05)",md:"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",lg:"0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",xl:"0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)","2xl":"0 25px 50px -12px rgb(0 0 0 / 0.25)"},borders:{none:"none",sm:"1px solid",md:"2px solid",lg:"4px solid"},radius:{none:"0px",sm:"0.125rem",md:"0.375rem",lg:"0.5rem",xl:"0.75rem","2xl":"1rem",full:"9999px"}};var a={overview:"Persona uses a three-layer design token system: palette \u2192 semantic \u2192 components. Most themes only need palette.colors overrides: semantic and component layers auto-derive from palette values. Config also accepts non-theme appearance options (launcher, sendButton, toolCall, etc.).",layers:{palette:{description:"Primitive design tokens. Override specific shades to change the entire widget feel.",colors:{description:"7 color scales, each with shades 50 (lightest) to 950 (darkest). Override only the shades you need.",scales:{gray:"Neutrals: backgrounds, text, borders. Key shades: 50 (lightest bg), 100 (secondary bg), 200 (borders), 500 (muted text), 900 (primary text).",primary:"Brand color: buttons, links, interactive elements. Key shades: 500 (default), 600 (hover).",accent:"Secondary highlight. Key shades: 500 (default), 600 (hover).",secondary:"Tertiary color scale.",success:"Positive feedback (default: green).",warning:"Caution feedback (default: yellow).",error:"Error/danger feedback (default: red)."}},radius:{description:"Border radius scale. Add custom keys like launcher, button.",defaults:{none:"0px",sm:"0.125rem",md:"0.375rem",lg:"0.5rem",xl:"0.75rem","2xl":"1rem",full:"9999px"}},typography:{fontFamily:"Three stacks: sans (system-ui), serif (Georgia), mono (ui-monospace). Override individual stack values.",fontSize:"Scale: xs (0.75rem), sm (0.875rem), base (1rem), lg (1.125rem), xl (1.25rem), 2xl (1.5rem), 3xl (1.875rem), 4xl (2.25rem).",fontWeight:"normal (400), medium (500), semibold (600), bold (700).",lineHeight:"tight (1.25), normal (1.5), relaxed (1.625)."},shadows:"Scale: none, sm, md, lg, xl, 2xl. Values are CSS box-shadow strings.",borders:"Scale: none, sm (1px solid), md (2px solid), lg (4px solid).",spacing:"Scale: 0 (0px), 1 (0.25rem), 2 (0.5rem), 3 (0.75rem), 4 (1rem), 5 (1.25rem), 6 (1.5rem), 8 (2rem), 10 (2.5rem), 12 (3rem), 16-64."},semantic:{description:'Design intent tokens. Auto-derived from palette by default. Override to redirect token resolution. Values are token reference strings like "palette.colors.primary.500".',colors:{primary:"palette.colors.primary.500: Primary brand color.",secondary:"palette.colors.gray.500: Secondary color.",accent:"palette.colors.primary.600: Accent/interactive color.",surface:"palette.colors.gray.50: Panel/card backgrounds.",background:"palette.colors.gray.50: Page background.",container:"palette.colors.gray.100: Container backgrounds.",text:"palette.colors.gray.900: Primary text.",textMuted:"palette.colors.gray.500: Muted/secondary text.",textInverse:"palette.colors.gray.50: Text on dark backgrounds.",border:"palette.colors.gray.200: Default border color.",divider:"palette.colors.gray.200: Divider lines.",interactive:{default:"palette.colors.primary.500",hover:"palette.colors.primary.600",focus:"palette.colors.primary.700",active:"palette.colors.primary.800",disabled:"palette.colors.gray.300"},feedback:{success:"palette.colors.success.500",warning:"palette.colors.warning.500",error:"palette.colors.error.500",info:"palette.colors.primary.500"}},spacing:"xs (0.25rem), sm (0.5rem), md (1rem), lg (1.5rem), xl (2rem), 2xl (2.5rem).",typography:"fontFamily, fontSize, fontWeight, lineHeight: reference palette typography tokens."},components:{description:"UI element tokens. Rarely needed for basic theming. Override for fine-grained control. All values are token references or raw CSS strings.",button:{description:"Three variants: primary, secondary, ghost.",properties:"background, foreground, borderRadius, padding."},input:{description:"Message input field.",properties:"background, placeholder, borderRadius, padding, focus.border, focus.ring."},launcher:{description:"Floating launcher button.",properties:"size (60px), iconSize (28px), iconStrokeWidth (2, unitless glyph stroke weight; applies to registry-icon launchers, inert for emoji and image icons), borderRadius, shadow."},panel:{description:"Chat panel container.",properties:'width, maxWidth (440px), height (600px), maxHeight, borderRadius, shadow, inset (16px, gap around a detached panel), canvasBackground (transparent, fills the region behind a detached panel in docked and inline embed modes; a no-op in sidebar mode, where the gap stays click-through and the host page shows through like floating). inset and canvasBackground apply only when launcher.detachedPanel is true or artifacts.layout.paneAppearance is "detached".'},header:{description:"Chat panel header. background and foreground are the pair that colors the band: set those two and the rest of the header text follows.",properties:"background, foreground (anchors the header text; the title takes it directly, the subtitle and action icons take color-mix(in srgb, foreground 72%, background), and the border takes color-mix(in srgb, foreground 14%, background)), border, borderRadius, padding, iconBackground, iconForeground (the avatar tile, which keeps its own defaults and never derives), actionIconForeground, controlSize (32px, the box of every header icon button), controlIconSize (20px, the glyph inside it), controlStrokeWidth (1.5, unitless glyph stroke weight; sparse-viewBox glyphs such as the close X scale it by 0.7), shadow, borderBottom, minHeight (height floor for the header strip), title and subtitle (each fontFamily, fontSize, fontWeight, lineHeight, letterSpacing, color). Derivation only fills keys the theme leaves unset, so any explicit border, titleForeground, subtitleForeground, actionIconForeground, title.color, or subtitle.color wins. With no foreground either, each key falls back as before: the primary color for the title, the muted text color for the subtitle and action icons, the divider color for the border.",legacy:"titleForeground and subtitleForeground are legacy aliases of header.title.color and header.subtitle.color. Both still work; the title/subtitle color wins when both are set, and either alias still outranks the foreground derivation."},history:{description:"Messages (conversation history) chrome and motion. railHeader styles the rail sidebar top strip, which runs beside the widget header in rail presentation; overlay styles the floating rail a collapsed overlay rail opens on hover; motion times the surface entering and leaving.",properties:'railHeader.background (defaults to the rail surface), railHeader.border (full border-bottom shorthand, default 0), railHeader.minHeight (defaults to components.header.minHeight, then 56px). Pin railHeader.minHeight and header.minHeight to the same value so both top strips read as one band. railHeader.title takes TextStyleTokens for the strip view title ("Messages"): color (defaults to the text color; the sidebar identity reads at full strength), fontFamily, fontSize (14px), fontWeight (600), lineHeight, letterSpacing. listHeading takes the same TextStyleTokens for the rail-only "Conversations" heading over the list block (14px/600 in the text color); groupHeading styles the muted sub-headers, both the date groups ("Today", "Yesterday") and rail nav section titles (rail 13px/500, muted text color). Every key of all three is pinned in the widget CSS, so host-page h2/h3 rules cannot restyle them. overlay.margin (8px, the gap from the trigger, the docked edge and the bottom), overlay.borderRadius (16px), overlay.shadow (0 12px 40px rgba(0, 0, 0, 0.25)), overlay.background (defaults to the rail surface). The floating rail keeps features.history.rail width and side, which are config, not tokens. motion.enterDurationMs (180) and motion.exitDurationMs (160) are bare millisecond numbers, 0 disables that leg; motion.enterEasing (cubic-bezier(0, 0, 0.2, 1)) and motion.exitEasing (cubic-bezier(0.4, 0, 1, 1)) take any CSS easing. Only the body animates, and prefers-reduced-motion always wins.'},tooltip:{description:"The portaled tooltip on icon controls, plus the muted shortcut hint chip after its label.",properties:"background (#111827), foreground (#ffffff), hintForeground (rgba(255, 255, 255, 0.55)), borderRadius (palette.radius.sm), fontSize (12px), padding (6px 12px), shadow, maxWidth (min(320px, calc(100vw - 16px))), arrow (boolean, default true; false drops the caret). Unset keys keep the built-in dark look."},message:{description:"Chat message bubbles.",user:"background, text, borderRadius, shadow.",assistant:"background, text, borderRadius, border (optional), shadow (optional)."},markdown:{description:"Markdown rendering in messages and artifact pane.",properties:"inlineCode (background, foreground), link.foreground, prose.fontFamily, heading.h1/h2 (fontSize, fontWeight), codeBlock (background, borderColor, textColor, borderRadius: follows palette.radius.md), table (headerBackground, borderColor), hr.color, blockquote (borderColor, background, textColor)."},introCard:{description:"Welcome surface above the message list. Flat by default (transparent, no shadow).",properties:"background, borderRadius, padding, shadow, border (full CSS shorthand, default none), title and subtitle (each fontFamily, fontSize, fontWeight, lineHeight, letterSpacing, color)."},suggestion:{description:"Starter prompt and follow-up suggestion chrome. Three variants: chip, card, list.",properties:"background, foreground, border, borderRadius, padding, shadow, gap (icon to copy inside one item), itemGap (space between items, default 8px), minHeight, fontSize, lineHeight, iconSize, hoverBackground, hoverForeground, hoverBorder, pressedBackground, focusRing, disabledOpacity."},voice:"recording (indicator, background, border), processing (icon, background), speaking (icon).",approval:"requested (background, border, text, shadow), approve (background, foreground), deny (background, foreground).",attachment:"image (background, border).",scrollToBottom:"Floating scroll-to-bottom affordance shared by transcript and event stream: background, foreground, border, size, borderRadius, shadow, padding, gap, fontSize, iconSize.",toolBubble:"shadow: tool call row box-shadow.",scrollbar:"Shared scrollbar appearance for every scroller: thumb (default semantic.colors.border), track (default transparent).",reasoningBubble:"shadow: reasoning/thinking row box-shadow.",composer:'Message input form: shadow (box-shadow), borderColor, padding (form inset), gap (textarea row to actions row), fontSize and lineHeight (textarea type), controlSize (edge of every icon control in the action row: attachment, mention, mic, action buttons, overflow trigger, icon-mode send; default 40px) and controlIconSize (glyph inside them, default 24px). Per-control config keys (sendButton.size, voiceRecognition.iconSize) override controlSize; coarse-pointer devices floor the hit area at 40px. overlayBand paints the band behind an overlaid footer (composer.placement: "overlay"). segmented styles a segmented mode-group track (trackBackground, trackBorderRadius, padding, activeBackground, activeForeground, activeShadow, inactiveForeground, itemPadding); every key is optional and the stylesheet owns the defaults. modelPicker styles the model picker (background, hoverBackground, borderColor, borderRadius and labelColor on the closed control, which the native select and the popover trigger share; menuBackground, menuBorderRadius, rowHoverBackground, descriptionColor and suffixColor on the popover only), also all optional. overflowMenu styles the composer overflow "+" menu panel (background, borderColor, borderRadius, foreground, shadow), also all optional.',artifact:"toolbar (icon styling, copy menu), tab (background, active states), pane (background, toolbarBackground)."}},colorScheme:'"dark" merges darkTheme overrides on top of theme. "auto" detects system preference or <html class="dark">. "light" is default. colorScheme does NOT auto-invert colors: provide dark palette and semantic overrides yourself.',plugins:{description:"Plugins transform theme tokens before resolution. Use with createTheme().",available:{brandPlugin:'Auto-generates full color scales from a single brand hex: brandPlugin({ colors: { primary: "#7c3aed" } }).',accessibilityPlugin:"Enhanced focus indicators and disabled states.",highContrastPlugin:"Increased contrast for visual accessibility.",reducedMotionPlugin:"Disables all animations (sets transitions to 0ms).",animationsPlugin:"Adds transition and easing tokens."},usage:'createTheme(themeOverrides, { plugins: [brandPlugin({ colors: { primary: "#7c3aed" } })] })'},widgetConfig:{description:'Non-theme config options on the widget config object that affect appearance. These are siblings of "theme" in the config, not nested inside it.',launcher:{description:"Floating launcher button and panel positioning.",properties:{enabled:"Show/hide the launcher button.",title:"Header title text.",subtitle:"Header subtitle text.",position:'"bottom-right" | "bottom-left" | "top-right" | "top-left".',width:"Chat panel width (CSS value).",fullHeight:"Fill full height of container.",mountMode:'"floating" | "docked".',agentIconText:"Emoji/text for agent icon.",border:"Border style for launcher button.",shadow:"Box shadow for launcher button.",collapsedMaxWidth:"Max-width for launcher pill when panel closed."}},sendButton:{description:"Send button appearance.",properties:"backgroundColor, textColor, borderWidth, borderColor, paddingX, paddingY, iconText, iconName, size."},closeButton:{description:"Close button (on launcher config). Unset closeButtonSize means theme.components.header.controlSize owns the box.",properties:"closeButtonSize, closeButtonColor, closeButtonBackgroundColor, closeButtonBorderWidth, closeButtonBorderColor, closeButtonBorderRadius."},clearChat:{description:"Clear chat button (on launcher.clearChat config). Unset size means theme.components.header.controlSize owns the box.",properties:"enabled, iconColor, backgroundColor, borderWidth, borderColor, borderRadius, size."},scrollToBottom:{description:"Shared transcript + event-stream jump-to-latest affordance.",properties:'features.scrollToBottom.enabled, features.scrollToBottom.iconName, features.scrollToBottom.label (empty string renders icon-only). Defaults: enabled=true, iconName="arrow-down", label="".'},toolCall:{description:"Tool call display styling, text templates, loading animations, and rendering hooks. Text templates support placeholders ({toolName}, {duration}) and inline formatting (~dim~, *italic*, **bold**). renderCollapsedSummary receives elapsed (static string) and createElapsedElement() (live-updating span) in its context.",properties:"shadow, backgroundColor, borderColor, borderWidth, borderRadius, headerBackgroundColor, headerTextColor, headerPaddingX, headerPaddingY, contentBackgroundColor, contentTextColor, contentPaddingX, contentPaddingY, codeBlockBackgroundColor, codeBlockBorderColor, codeBlockTextColor, toggleTextColor, labelTextColor, activeTextTemplate, completeTextTemplate, loadingAnimationColor, loadingAnimationSecondaryColor, loadingAnimationDuration, renderCollapsedSummary, renderCollapsedPreview, renderGroupedSummary."},reasoning:{description:"Reasoning/thinking row rendering hooks, text templates, and loading animations. Text templates support {duration} placeholder and inline formatting (~dim~, *italic*, **bold**). renderCollapsedSummary receives elapsed (static string) and createElapsedElement() (live-updating span) in its context.",properties:"renderCollapsedSummary, renderCollapsedPreview, activeTextTemplate, completeTextTemplate, loadingAnimationColor, loadingAnimationSecondaryColor, loadingAnimationDuration."},approval:{description:`Tool approval card styling and behavior. Neutral surface card; the primary action anchors to the brand --persona-primary token. Set to false to disable. Set enableAlwaysAllow: true to add the split "Always allow / Allow once" control (needs a backend to persist the policy via onDecision's remember).`,properties:'enableAlwaysAllow, detailsDisplay ("collapsed" | "expanded" | "hidden"), showDetailsLabel, hideDetailsLabel, backgroundColor, borderColor, titleColor, descriptionColor, reasonColor, reasonLabel, approveButtonColor, approveButtonTextColor, denyButtonColor, denyButtonTextColor, parameterBackgroundColor, parameterTextColor, approveLabel, denyLabel, formatDescription. (title no longer renders in the default card; use formatDescription to customize the summary.)'},copy:{description:"Widget text content.",properties:"inputPlaceholder, sendButtonLabel, stopButtonLabel. (showWelcomeCard, welcomeTitle, welcomeSubtitle are deprecated aliases of the welcome namespace.)"},voiceRecognition:{description:"Voice input configuration.",properties:"enabled, pauseDuration, iconColor, backgroundColor."},textToSpeech:{description:"Text-to-speech for assistant messages.",properties:'enabled, provider ("browser" | "runtype"), browserFallback, voice, rate, pitch.'},suggestions:{description:"Rich starter and follow-up suggestion surfaces. Strings remain valid item shorthand.",properties:'starters.items, starters.variant ("card" | "chip" | "list"), starters.placement ("auto" | "welcome" | "composer"), starters.behavior ("send" | "fill"), starters.overflow ("scroll" | "wrap"), starters.maxItems, followUps.enabled, followUps.expose, followUps.variant, followUps.placement ("auto" | "after-message" | "composer"), followUps.behavior, followUps.overflow ("scroll" | "wrap"), followUps.maxItems.'},welcome:{description:"First-open welcome surface. Supersedes copy.welcomeTitle / welcomeSubtitle / showWelcomeCard.",properties:'title, subtitle, icon ({ type: "lucide", name } | { type: "image", url, alt } | { type: "text", text } | function), variant ("card" | "hero" | "none"), dismiss ("never" | "on-first-message"), message (display-only greeting bubble, ignored under "hero").'},suggestionChips:"Deprecated string[] shorthand for starter prompts. Prefer suggestions.starters.items.",messageActions:{description:"Message action buttons (copy, upvote, downvote, read aloud, regenerate, edit, quote).",properties:'enabled, showCopy, showUpvote, showDownvote, showReadAloud, showRegenerate (final retryable assistant turn only), showEdit (text-only user messages only), showQuote, visibility ("hover" | "always"), align ("left" | "center" | "right"), layout ("pill-inside" | "row-inside").'},attachments:{description:"File attachment configuration.",properties:"enabled, allowedTypes (string[]), maxFileSize (bytes), maxFiles, buttonIconName, buttonTooltipText."},markdown:{description:"Markdown rendering configuration.",properties:"options (gfm, breaks, headerIds, headerPrefix, pedantic, mangle, silent), disableDefaultStyles."},layout:{description:"Layout configuration.",showHeader:"boolean: show/hide the header section entirely.",showFooter:"boolean: show/hide the footer/composer section entirely.",contentMaxWidth:'CSS width value for centering content (e.g. "720px", "90ch").',header:'"default" | "minimal". Options: showIcon, showTitle, showSubtitle, showCloseButton, showClearChat.',messages:'"bubble" | "flat" | "minimal". Options: groupConsecutive, avatar (show, position, userAvatar, assistantAvatar), timestamp (show, position).'},statusIndicator:{description:"Status text shown below the composer.",properties:{visible:"Show/hide the status indicator.",align:'"left" | "center" | "right": text alignment (default: "right").',idleText:'Text shown when idle (default: "Online").',idleLink:"URL to open when idle text is clicked (wraps text in a link).",connectingText:'Text shown while connecting (default: "Connecting\u2026").',connectedText:'Text shown while streaming (default: "Streaming\u2026").',errorText:'Text shown on error (default: "Offline").'}},features:{description:"Feature flags.",properties:"showReasoning (AI thinking steps), showToolCalls (tool invocations), toolCallDisplay (collapsedMode, activePreview, activeMinHeight, previewMaxLines, grouped, groupedMode, expandable, loadingAnimation), reasoningDisplay (activePreview, activeMinHeight, previewMaxLines, expandable, loadingAnimation), artifacts (sidebar config)."}}},n={darkIndigo:{description:"Dark mode with indigo accent: override grays for dark backgrounds and semantic tokens for inverted text/surface",theme:{palette:{colors:{primary:{500:"#6366f1",600:"#4f46e5"},gray:{50:"#f1f5f9",100:"#1e293b",200:"#334155",500:"#94a3b8",900:"#0f172a",950:"#020617"}}},semantic:{colors:{surface:"palette.colors.gray.900",background:"palette.colors.gray.900",container:"palette.colors.gray.100",text:"palette.colors.gray.50",textMuted:"palette.colors.gray.500",textInverse:"palette.colors.gray.900",border:"palette.colors.gray.200"}}}},warmVintage:{description:"Warm sepia tones with serif font and subtle radius",theme:{palette:{colors:{primary:{500:"#b45309",600:"#92400e"},gray:{50:"#fef3c7",100:"#fef9c3",200:"#d6d3d1",500:"#78716c",900:"#44403c"}},radius:{sm:"0.125rem",md:"0.25rem",lg:"0.375rem"},typography:{fontFamily:{sans:'Georgia, Cambria, "Times New Roman", Times, serif'}}}}},neonCyberpunk:{description:"Neon on dark with monospace font: full semantic override for dark background",theme:{palette:{colors:{primary:{500:"#f0abfc",600:"#e879f9"},accent:{500:"#22d3ee",600:"#06b6d4"},gray:{50:"#f0abfc",100:"#1e0a3c",200:"#3b0764",500:"#c084fc",900:"#0c0a1a",950:"#050412"}},radius:{sm:"0",md:"0.25rem",lg:"0.375rem"},typography:{fontFamily:{sans:"ui-monospace, SFMono-Regular, Menlo, monospace"}}},semantic:{colors:{surface:"palette.colors.gray.900",background:"palette.colors.gray.950",container:"palette.colors.gray.100",text:"palette.colors.gray.50",textMuted:"palette.colors.gray.500",border:"palette.colors.gray.200"}}}},cleanRounded:{description:"Clean light theme with large radius and panel styling",theme:{palette:{radius:{sm:"6px",md:"8px",lg:"12px",launcher:"50px",button:"8px"}},components:{panel:{borderRadius:"16px",shadow:"palette.shadows.2xl"}}}}};function k(){return{tokenDocs:a,defaultColorPalette:o.colors,defaultRadius:o.radius,examples:n,sdkPresets:["shop","minimal","fullscreen"]}}export{n as THEME_EXAMPLES,a as THEME_TOKEN_DOCS,k as getThemeReference};
1
+ var m={background:"semantic.colors.primary",foreground:"semantic.colors.textInverse",border:"transparent",size:"48px",iconSize:"24px",borderRadius:"palette.radius.full",shadow:"0 8px 24px rgba(0,0,0,.16)",offset:"20px"};var f=e=>typeof e=="object"&&e!==null&&!Array.isArray(e);function c(e,o){if(!e)return o;if(!o)return e;let t={...e};for(let[i,d]of Object.entries(o)){let g=t[i];f(g)&&f(d)?t[i]=c(g,d):t[i]=d}return t}var l="min(440px, calc(100vw - 24px))";var x="40px",k="How can I help...",v={collapsedMode:"tool-call",activePreview:!1,grouped:!1,groupedMode:"stack",previewMaxLines:3,expandable:!0,loadingAnimation:"none"},w={activePreview:!1,previewMaxLines:3,expandable:!0,loadingAnimation:"none"},T={enabled:!0,mountMode:"floating",dock:{side:"right",width:"420px"},title:"Chat Assistant",subtitle:"Here to help you get answers fast",agentIconText:"\u{1F4AC}",agentIconName:"bot",headerIconName:"bot",position:"bottom-right",width:l,heightOffset:0,autoExpand:!1,callToActionIconHidden:!1,agentIconSize:"40px",headerIconSize:x,closeButtonPaddingX:"0px",closeButtonPaddingY:"0px",callToActionIconName:"arrow-up-right",callToActionIconText:"",callToActionIconSize:"32px",callToActionIconPadding:"5px",callToActionIconColor:void 0,callToActionIconBackgroundColor:void 0,closeButtonBackgroundColor:"transparent",clearChat:{backgroundColor:"transparent",borderColor:"transparent",enabled:!0,placement:"inline",iconName:"refresh-cw",showTooltip:!0,tooltipText:"Clear chat",paddingX:"0px",paddingY:"0px"},headerIconHidden:!1,border:void 0,shadow:"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)"},C={apiUrl:"https://api.runtype.com/api/chat/dispatch",clientToken:void 0,agentId:void 0,target:void 0,theme:void 0,darkTheme:void 0,colorScheme:"light",tooltip:{delayMs:200,skipDelayMs:300},launcher:T,copy:{inputPlaceholder:k,sendButtonLabel:"Send"},sendButton:{borderWidth:"0px",paddingX:"12px",paddingY:"10px",borderColor:void 0,useIcon:!0,iconText:"\u2191",showTooltip:!0,tooltipText:"Send message",iconName:"send"},statusIndicator:{visible:!0,idleText:"Online",connectingText:"Connecting\u2026",connectedText:"Streaming\u2026",errorText:"Offline"},voiceRecognition:{enabled:!0,pauseDuration:2e3,iconName:"mic",borderWidth:"0px",iconColor:void 0,backgroundColor:"transparent",borderColor:"transparent",recordingIconColor:void 0,recordingBackgroundColor:void 0,recordingBorderColor:"transparent",showTooltip:!0,tooltipText:"Start voice recognition"},features:{showReasoning:!0,showToolCalls:!0,scrollToBottom:{enabled:!0,iconName:"arrow-down",label:""},scrollBehavior:{mode:"anchor-top",anchorTopOffset:16,showActivityWhilePinned:!0},toolCallDisplay:v,reasoningDisplay:w,streamAnimation:{type:"none",placeholder:"none",speed:120,duration:1800},askUserQuestion:{enabled:!0,slideInMs:180,freeTextLabel:"Other\u2026",freeTextPlaceholder:"Type your answer\u2026",submitLabel:"Send"},history:{enabled:!1,presentation:"panel",showScopeStatus:!0,showDelete:!0,showDeleteAll:!0}},suggestionChips:["Show me what you can help with","Walk me through getting started","Answer a question about this page"],suggestionChipsConfig:{fontFamily:"sans-serif",fontWeight:"500",paddingX:"12px",paddingY:"6px"},layout:{header:{layout:"default",showIcon:!0,showTitle:!0,showSubtitle:!0,showCloseButton:!0,showClearChat:!0},messages:{layout:"bubble",avatar:{show:!1,position:"left"},timestamp:{show:!1,position:"below"},groupConsecutive:!1},slots:{}},markdown:{options:{gfm:!0,breaks:!0},disableDefaultStyles:!1},messageActions:{enabled:!0,showCopy:!0,showUpvote:!1,showDownvote:!1,visibility:"hover",align:"right",layout:"pill-inside"},debug:!1},B={},S={variant:"circle",width:"min(400px, calc(100vw - 24px))",headerIconSize:"20px"},A={launcher:S,features:{toolCallDisplay:{variant:"row",iconVisibility:"active",autoExpand:!1,grouped:!0,groupedMode:"collapsible",loadingAnimation:"shimmer"},reasoningDisplay:{variant:"row",iconVisibility:"active",autoExpand:!1,loadingAnimation:"shimmer"}},composer:{layout:"single-row",placement:"overlay"},sendButton:{iconName:"arrow-up"},statusIndicator:{mode:"transient"},layout:{contentMaxWidth:"768px",topFade:!0,header:{showSubtitle:!1},messages:{assistant:{width:"full"}}}};function n(e){return e?.future?.v5Defaults===!0?"v5":"v4"}function p(e){let o=n(e)==="v5"?A:B;return c(C,o)}var Z=p();var P="16px",R="transparent",D=(()=>{let e={};for(let o of[0,1,2,3,4,5,6,8,10,12,16,20,24,32,40,48,56,64])e[o]=o===0?"0px":`${o/4}rem`;return e})(),a={colors:{primary:{50:"#ffffff",100:"#f5f5f5",200:"#d4d4d4",300:"#a3a3a3",400:"#737373",500:"#171717",600:"#0f0f0f",700:"#0a0a0a",800:"#050505",900:"#030303",950:"#000000"},secondary:{50:"#f5f3ff",100:"#ede9fe",200:"#ddd6fe",300:"#c4b5fd",400:"#a78bfa",500:"#8b5cf6",600:"#7c3aed",700:"#6d28d9",800:"#5b21b6",900:"#4c1d95",950:"#2e1065"},accent:{50:"#ecfeff",100:"#cffafe",200:"#a5f3fc",300:"#67e8f9",400:"#22d3ee",500:"#06b6d4",600:"#0891b2",700:"#0e7490",800:"#155e75",900:"#164e63",950:"#083344"},gray:{50:"#f9fafb",100:"#f3f4f6",200:"#e5e7eb",300:"#d1d5db",400:"#9ca3af",500:"#6b7280",600:"#4b5563",700:"#374151",800:"#1f2937",900:"#111827",950:"#030712"},success:{50:"#f0fdf4",100:"#dcfce7",200:"#bbf7d0",300:"#86efac",400:"#4ade80",500:"#22c55e",600:"#16a34a",700:"#15803d",800:"#166534",900:"#14532d"},warning:{50:"#fefce8",100:"#fef9c3",200:"#fef08a",300:"#fde047",400:"#facc15",500:"#eab308",600:"#ca8a04",700:"#a16207",800:"#854d0e",900:"#713f12"},error:{50:"#fef2f2",100:"#fee2e2",200:"#fecaca",300:"#fca5a5",400:"#f87171",500:"#ef4444",600:"#dc2626",700:"#b91c1c",800:"#991b1b",900:"#7f1d1d"},info:{50:"#eff6ff",100:"#dbeafe",200:"#bfdbfe",300:"#93c5fd",400:"#60a5fa",500:"#3b82f6",600:"#2563eb",700:"#1d4ed8",800:"#1e40af",900:"#1e3a8a",950:"#172554"},purple:{50:"#faf5ff",100:"#f3e8ff",200:"#e9d5ff",300:"#d8b4fe",400:"#c084fc",500:"#a855f7",600:"#9333ea",700:"#7e22ce",800:"#6b21a8",900:"#581c87",950:"#3b0764"},teal:{50:"#f0fdfa",100:"#ccfbf1",200:"#99f6e4",300:"#5eead4",400:"#2dd4bf",500:"#14b8a6",600:"#0d9488",700:"#0f766e",800:"#115e59",900:"#134e4a",950:"#042f2e"}},spacing:D,typography:{fontFamily:{sans:'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',serif:'Georgia, Cambria, "Times New Roman", Times, serif',mono:"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace"},fontSize:{xs:"0.75rem",sm:"0.875rem",base:"1rem",lg:"1.125rem",xl:"1.25rem","2xl":"1.5rem","3xl":"1.875rem","4xl":"2.25rem"},fontWeight:{normal:"400",medium:"500",semibold:"600",bold:"700"},lineHeight:{tight:"1.25",normal:"1.5",relaxed:"1.625"}},shadows:{none:"none",sm:"0 1px 2px 0 rgb(0 0 0 / 0.05)",md:"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",lg:"0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",xl:"0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)","2xl":"0 25px 50px -12px rgb(0 0 0 / 0.25)"},borders:{none:"none",sm:"1px solid",md:"2px solid",lg:"4px solid"},radius:{none:"0px",sm:"0.125rem",md:"0.375rem",lg:"0.5rem",xl:"0.75rem","2xl":"1rem",full:"9999px"}},s={colors:{primary:"palette.colors.primary.500",secondary:"palette.colors.secondary.500",accent:"palette.colors.primary.600",surface:"palette.colors.gray.50",background:"palette.colors.gray.50",container:"palette.colors.gray.50",text:"palette.colors.gray.900",textMuted:"palette.colors.gray.500",textInverse:"palette.colors.gray.50",border:"palette.colors.gray.200",divider:"palette.colors.gray.200",interactive:{default:"palette.colors.primary.600",hover:"palette.colors.primary.700",focus:"palette.colors.primary.600",active:"palette.colors.primary.600",disabled:"palette.colors.gray.300"},feedback:{success:"palette.colors.success.500",warning:"palette.colors.warning.500",error:"palette.colors.error.500",info:"palette.colors.info.500"}},spacing:{xs:"palette.spacing.1",sm:"palette.spacing.2",md:"palette.spacing.4",lg:"palette.spacing.6",xl:"palette.spacing.8","2xl":"palette.spacing.10"},typography:{fontFamily:"palette.typography.fontFamily.sans",fontSize:"palette.typography.fontSize.base",fontWeight:"palette.typography.fontWeight.normal",lineHeight:"palette.typography.lineHeight.normal"}},r={button:{primary:{background:"palette.colors.primary.500",foreground:"palette.colors.primary.50",borderRadius:"palette.radius.lg",padding:"semantic.spacing.md"},secondary:{background:"semantic.colors.surface",foreground:"semantic.colors.secondary",borderRadius:"palette.radius.lg",padding:"semantic.spacing.md"},ghost:{background:"transparent",foreground:"semantic.colors.text",borderRadius:"palette.radius.md",padding:"semantic.spacing.sm",hoverBackground:"rgba(0, 0, 0, 0.05)"}},input:{background:"semantic.colors.surface",placeholder:"semantic.colors.textMuted",borderRadius:"palette.radius.lg",padding:"semantic.spacing.md",focus:{border:"palette.colors.gray.400",ring:"palette.colors.gray.400"}},launcher:{background:"semantic.colors.surface",foreground:"semantic.colors.primary",border:"semantic.colors.border",size:"60px",iconSize:"28px",borderRadius:"palette.radius.full",shadow:"palette.shadows.lg"},panel:{width:l,maxWidth:"none",height:"min(640px, max(200px, calc(100vh - 64px)))",maxHeight:"none",borderRadius:"palette.radius.xl",shadow:"palette.shadows.xl",inset:P,canvasBackground:R,modes:{floating:{border:"1px solid var(--persona-border)",shadow:"palette.shadows.xl",borderRadius:"palette.radius.xl"},inline:{border:"1px solid var(--persona-border)",shadow:"none",borderRadius:"palette.radius.xl"},docked:{border:"none",shadow:"none",borderRadius:"palette.radius.xl"},sidebar:{border:"none",borderRadius:"0"},mobile:{border:"none",shadow:"none",borderRadius:"0"}}},header:{background:"palette.colors.primary.500",foreground:"palette.colors.primary.50",borderRadius:"palette.radius.xl palette.radius.xl 0 0",padding:"20px 24px",minimalPadding:"16px 24px",iconBackground:"palette.colors.primary.600",iconForeground:"palette.colors.primary.50"},message:{gap:"12px",user:{background:"palette.colors.primary.500",text:"palette.colors.primary.50",borderRadius:"palette.radius.lg",shadow:"palette.shadows.sm"},assistant:{background:"semantic.colors.container",text:"semantic.colors.text",borderRadius:"palette.radius.lg",border:"semantic.colors.border",shadow:"palette.shadows.sm"},border:"semantic.colors.border"},introCard:{background:"transparent",borderRadius:"palette.radius.2xl",padding:"semantic.spacing.lg",shadow:"palette.shadows.none"},suggestion:{chip:{background:"semantic.colors.surface",foreground:"semantic.colors.text",border:"semantic.colors.border",borderRadius:"palette.radius.full",padding:"0.5rem 0.875rem",gap:"0.5rem",itemGap:"8px",minHeight:"36px",fontSize:"0.8125rem",lineHeight:"1.25",iconSize:"16px",hoverBackground:"palette.colors.gray.100",hoverBorder:"palette.colors.gray.300",pressedBackground:"palette.colors.gray.200",focusRing:"semantic.colors.interactive.focus",disabledOpacity:"0.5"},card:{background:"semantic.colors.surface",foreground:"semantic.colors.text",border:"semantic.colors.border",borderRadius:"palette.radius.xl",padding:"1rem",shadow:"palette.shadows.sm",gap:"0.625rem",itemGap:"8px",minHeight:"72px",fontSize:"0.875rem",lineHeight:"1.35",iconSize:"18px",hoverBackground:"palette.colors.gray.100",hoverBorder:"palette.colors.gray.300",pressedBackground:"palette.colors.gray.200",focusRing:"semantic.colors.interactive.focus",disabledOpacity:"0.5"},list:{background:"transparent",foreground:"semantic.colors.text",border:"semantic.colors.border",borderRadius:"palette.radius.md",padding:"0.75rem",gap:"0.625rem",itemGap:"8px",minHeight:"44px",fontSize:"0.875rem",lineHeight:"1.35",iconSize:"17px",hoverBackground:"palette.colors.gray.100",hoverBorder:"palette.colors.gray.300",pressedBackground:"palette.colors.gray.200",focusRing:"semantic.colors.interactive.focus",disabledOpacity:"0.5"}},toolBubble:{shadow:"palette.shadows.sm"},reasoningBubble:{shadow:"palette.shadows.sm"},composer:{shadow:"palette.shadows.none"},scrollbar:{thumb:"semantic.colors.border",track:"transparent"},markdown:{inlineCode:{background:"palette.colors.gray.50",foreground:"palette.colors.gray.900"},link:{foreground:"palette.colors.primary.600"},prose:{fontFamily:"inherit"},codeBlock:{background:"semantic.colors.container",borderColor:"semantic.colors.border",textColor:"inherit",borderRadius:"palette.radius.md"},table:{headerBackground:"semantic.colors.container",borderColor:"semantic.colors.border"},hr:{color:"semantic.colors.divider"},blockquote:{borderColor:"palette.colors.gray.900",background:"transparent",textColor:"palette.colors.gray.500"}},collapsibleWidget:{container:"palette.colors.gray.50",surface:"semantic.colors.surface",border:"semantic.colors.border"},voice:{recording:{indicator:"palette.colors.error.500",background:"palette.colors.error.50",border:"palette.colors.error.200"},processing:{icon:"palette.colors.primary.500",background:"palette.colors.primary.50"},speaking:{icon:"palette.colors.success.500"}},approval:{requested:{background:"semantic.colors.surface",border:"semantic.colors.border",text:"palette.colors.gray.900",shadow:"0 1px 2px 0 rgba(11, 11, 11, 0.06), 0 2px 8px 0 rgba(11, 11, 11, 0.04)"},approve:{background:"semantic.colors.primary",foreground:"semantic.colors.textInverse",borderRadius:"palette.radius.md",padding:"semantic.spacing.sm"},deny:{background:"semantic.colors.container",foreground:"semantic.colors.text",borderRadius:"palette.radius.md",padding:"semantic.spacing.sm"}},attachment:{image:{background:"palette.colors.gray.100",border:"palette.colors.gray.200"}},eventStream:{badge:{flow:{background:"palette.colors.success.100",foreground:"palette.colors.success.700"},step:{background:"palette.colors.primary.100",foreground:"palette.colors.primary.700"},reasoning:{background:"palette.colors.warning.100",foreground:"palette.colors.warning.700"},tool:{background:"palette.colors.purple.100",foreground:"palette.colors.purple.800"},agent:{background:"palette.colors.teal.100",foreground:"palette.colors.teal.800"},error:{background:"palette.colors.error.100",foreground:"palette.colors.error.700"},default:{background:"palette.colors.gray.100",foreground:"palette.colors.gray.600"}}},scrollToBottom:{background:"components.button.primary.background",foreground:"components.button.primary.foreground",border:"semantic.colors.primary",size:"40px",borderRadius:"palette.radius.full",shadow:"palette.shadows.sm",padding:"0.5rem 0.875rem",gap:"0.5rem",fontSize:"0.875rem",iconSize:"14px"},artifact:{pane:{background:"semantic.colors.container",toolbarBackground:"semantic.colors.container"}}},E=a.colors.gray,W={50:"#fafafa",100:"#f5f5f5",200:"#e5e5e5",300:"#d4d4d4",400:"#a3a3a3",500:"#737373",600:"#525252",700:"#404040",800:"#262626",900:"#171717",950:"#0a0a0a"},F={...a,colors:{...a.colors,gray:E}},L={...a,colors:{...a.colors,gray:W},radius:{none:"0px",sm:"4px",md:"8px",lg:"12px",xl:"16px","2xl":"24px",full:"9999px"}};var z=s,H={...s,colors:{...s.colors,surface:"#ffffff",background:"#ffffff",container:"semantic.colors.surface",textInverse:"#ffffff"},typography:{...s.typography,fontSize:"14px",lineHeight:"1.5"}};var M=r,I={...r,launcher:m,activity:{rowHeight:"32px",labelSize:"13px",iconSize:"16px",indent:"0px",groupIndent:"0px",groupGap:"0px",groupPadding:"0px",transcriptGap:"0px",responseGap:"12px",bodyPadding:"4px 0",bodySize:"13px",bodyLineHeight:"1.5",bodyFontWeight:"400",sectionGap:"8px",bodySurface:"transparent"},toolBubble:{shadow:"none"},reasoningBubble:{shadow:"none"},input:{...r.input,borderRadius:"palette.radius.full"},introCard:{...r.introCard,title:{fontSize:"22px",fontWeight:"500"}},suggestion:{chip:{...r.suggestion.chip,fontWeight:"400"},card:{...r.suggestion.card,fontWeight:"400"},list:{...r.suggestion.list,fontWeight:"400"}},panel:{...r.panel,width:"min(400px, calc(100vw - 24px))",height:"min(704px, calc(100dvh - 104px))",borderRadius:"palette.radius.xl"},header:{...r.header,background:"semantic.colors.container",foreground:"semantic.colors.text",padding:"8px 8px 8px 16px",minimalPadding:"8px 8px 8px 16px",minHeight:"48px",borderBottom:"none",iconBackground:"transparent",iconForeground:"semantic.colors.text",iconScale:"1",controlSize:"28px",controlIconSize:"18px",controlStrokeWidth:"1.5",actionIconForeground:"semantic.colors.textMuted",controlBorderRadius:"8px",controlHoverBackground:"color-mix(in srgb, var(--persona-text) 8%, transparent)",controlHoverForeground:"semantic.colors.text",controlFocusOutline:"2px solid color-mix(in srgb, var(--persona-text) 50%, transparent)",title:{fontSize:"14px",fontWeight:"500",lineHeight:"1.5"}},message:{...r.message,gap:"20px",fullscreenGap:"28px",topFadeHeight:"18px",user:{...r.message.user,background:"palette.colors.gray.100",text:"semantic.colors.text",borderRadius:"palette.radius.xl",shadow:"none",padding:"8px 14px",fontSize:"14px",lineHeight:"1.5"},assistant:{...r.message.assistant,background:"transparent",border:"transparent",borderWidth:"0px",borderRadius:"0px",shadow:"none",padding:"0px",fontSize:"14px",lineHeight:"1.55"}},composer:{...r.composer,padding:"8px",fontSize:"15px",lineHeight:"1.5",controlSize:"32px",controlIconSize:"20px",sendIconSize:"18px",sendButtonRadius:"9999px",footerBorder:"none",overlayBand:"linear-gradient(to bottom, transparent, var(--persona-container) 24px)",focusBorderColor:"color-mix(in srgb, var(--persona-text) 28%, var(--persona-border))",focusRing:"1px solid color-mix(in srgb, var(--persona-text) 12%, transparent)"}};var u=e=>n({future:e}),b=e=>u(e)==="v5"?{palette:L,semantic:H,components:I}:{palette:F,semantic:z,components:M};var _={overview:"Persona uses a three-layer design token system: palette \u2192 semantic \u2192 components. Most themes only need palette.colors overrides: semantic and component layers auto-derive from palette values. Config also accepts non-theme appearance options (launcher, sendButton, toolCall, etc.).",layers:{palette:{description:"Primitive design tokens. Override specific shades to change the entire widget feel.",colors:{description:"7 color scales, each with shades 50 (lightest) to 950 (darkest). Override only the shades you need.",scales:{gray:"Neutrals: backgrounds, text, borders. Key shades: 50 (lightest bg), 100 (secondary bg), 200 (borders), 500 (muted text), 900 (primary text).",primary:"Brand color: buttons, links, interactive elements. Key shades: 500 (default), 600 (hover).",accent:"Secondary highlight. Key shades: 500 (default), 600 (hover).",secondary:"Tertiary color scale.",success:"Positive feedback (default: green).",warning:"Caution feedback (default: yellow).",error:"Error/danger feedback (default: red)."}},radius:{description:"Border radius scale. Add custom keys like launcher, button.",defaults:{none:"0px",sm:"0.125rem",md:"0.375rem",lg:"0.5rem",xl:"0.75rem","2xl":"1rem",full:"9999px"}},typography:{fontFamily:"Three stacks: sans (system-ui), serif (Georgia), mono (ui-monospace). Override individual stack values.",fontSize:"Scale: xs (0.75rem), sm (0.875rem), base (1rem), lg (1.125rem), xl (1.25rem), 2xl (1.5rem), 3xl (1.875rem), 4xl (2.25rem).",fontWeight:"normal (400), medium (500), semibold (600), bold (700).",lineHeight:"tight (1.25), normal (1.5), relaxed (1.625)."},shadows:"Scale: none, sm, md, lg, xl, 2xl. Values are CSS box-shadow strings.",borders:"Scale: none, sm (1px solid), md (2px solid), lg (4px solid).",spacing:"Scale: 0 (0px), 1 (0.25rem), 2 (0.5rem), 3 (0.75rem), 4 (1rem), 5 (1.25rem), 6 (1.5rem), 8 (2rem), 10 (2.5rem), 12 (3rem), 16-64."},semantic:{description:'Design intent tokens. Auto-derived from palette by default. Override to redirect token resolution. Values are token reference strings like "palette.colors.primary.500".',colors:{primary:"palette.colors.primary.500: Primary brand color.",secondary:"palette.colors.gray.500: Secondary color.",accent:"palette.colors.primary.600: Accent/interactive color.",surface:"palette.colors.gray.50: Panel/card backgrounds.",background:"palette.colors.gray.50: Page background.",container:"palette.colors.gray.100: Container backgrounds.",text:"palette.colors.gray.900: Primary text.",textMuted:"palette.colors.gray.500: Muted/secondary text.",textInverse:"palette.colors.gray.50: Text on dark backgrounds.",border:"palette.colors.gray.200: Default border color.",divider:"palette.colors.gray.200: Divider lines.",interactive:{default:"palette.colors.primary.500",hover:"palette.colors.primary.600",focus:"palette.colors.primary.700",active:"palette.colors.primary.800",disabled:"palette.colors.gray.300"},feedback:{success:"palette.colors.success.500",warning:"palette.colors.warning.500",error:"palette.colors.error.500",info:"palette.colors.primary.500"}},spacing:"xs (0.25rem), sm (0.5rem), md (1rem), lg (1.5rem), xl (2rem), 2xl (2.5rem).",typography:"fontFamily, fontSize, fontWeight, lineHeight: reference palette typography tokens."},components:{description:"UI element tokens. Rarely needed for basic theming. Override for fine-grained control. All values are token references or raw CSS strings.",button:{description:"Three variants: primary, secondary, ghost.",properties:"background, foreground, borderRadius, padding."},input:{description:"Message input field.",properties:"background, placeholder, borderRadius, padding, focus.border, focus.ring."},launcher:{description:"Floating launcher button.",properties:"size (60px), iconSize (28px), iconStrokeWidth (2, unitless glyph stroke weight; applies to registry-icon launchers, inert for emoji and image icons), borderRadius, shadow."},panel:{description:"Chat panel container.",properties:'width, maxWidth (440px), height (600px), maxHeight, borderRadius, shadow, inset (16px, gap around a detached panel), canvasBackground (transparent, fills the region behind a detached panel in docked and inline embed modes; a no-op in sidebar mode, where the gap stays click-through and the host page shows through like floating). inset and canvasBackground apply only when launcher.detachedPanel is true or artifacts.layout.paneAppearance is "detached".'},header:{description:"Chat panel header. background and foreground are the pair that colors the band: set those two and the rest of the header text follows.",properties:"background, foreground (anchors the header text; the title takes it directly, the subtitle and action icons take color-mix(in srgb, foreground 72%, background), and the border takes color-mix(in srgb, foreground 14%, background)), border, borderRadius, padding, iconBackground, iconForeground (the avatar tile, which keeps its own defaults and never derives), actionIconForeground, controlSize (32px, the box of every header icon button), controlIconSize (20px, the glyph inside it), controlStrokeWidth (1.5, unitless glyph stroke weight; sparse-viewBox glyphs such as the close X scale it by 0.7), shadow, borderBottom, minHeight (height floor for the header strip), title and subtitle (each fontFamily, fontSize, fontWeight, lineHeight, letterSpacing, color). Derivation only fills keys the theme leaves unset, so any explicit border, titleForeground, subtitleForeground, actionIconForeground, title.color, or subtitle.color wins. With no foreground either, each key falls back as before: the primary color for the title, the muted text color for the subtitle and action icons, the divider color for the border.",legacy:"titleForeground and subtitleForeground are legacy aliases of header.title.color and header.subtitle.color. Both still work; the title/subtitle color wins when both are set, and either alias still outranks the foreground derivation."},history:{description:"Messages (conversation history) chrome and motion. railHeader styles the rail sidebar top strip, which runs beside the widget header in rail presentation; overlay styles the floating rail a collapsed overlay rail opens on hover; motion times the surface entering and leaving.",properties:'railHeader.background (defaults to the rail surface), railHeader.border (full border-bottom shorthand, default 0), railHeader.minHeight (defaults to components.header.minHeight, then 56px). Pin railHeader.minHeight and header.minHeight to the same value so both top strips read as one band. railHeader.title takes TextStyleTokens for the strip view title ("Messages"): color (defaults to the text color; the sidebar identity reads at full strength), fontFamily, fontSize (14px), fontWeight (600), lineHeight, letterSpacing. listHeading takes the same TextStyleTokens for the rail-only "Conversations" heading over the list block (14px/600 in the text color); groupHeading styles the muted sub-headers, both the date groups ("Today", "Yesterday") and rail nav section titles (rail 13px/500, muted text color). Every key of all three is pinned in the widget CSS, so host-page h2/h3 rules cannot restyle them. overlay.margin (8px, the gap from the trigger, the docked edge and the bottom), overlay.borderRadius (16px), overlay.shadow (0 12px 40px rgba(0, 0, 0, 0.25)), overlay.background (defaults to the rail surface). The floating rail keeps features.history.rail width and side, which are config, not tokens. motion.enterDurationMs (180) and motion.exitDurationMs (160) are bare millisecond numbers, 0 disables that leg; motion.enterEasing (cubic-bezier(0, 0, 0.2, 1)) and motion.exitEasing (cubic-bezier(0.4, 0, 1, 1)) take any CSS easing. Only the body animates, and prefers-reduced-motion always wins.'},tooltip:{description:"The portaled tooltip on icon controls, plus the muted shortcut hint chip after its label.",properties:"background (#111827), foreground (#ffffff), hintForeground (rgba(255, 255, 255, 0.55)), borderRadius (palette.radius.sm), fontSize (12px), padding (6px 12px), shadow, maxWidth (min(320px, calc(100vw - 16px))), arrow (boolean, default true; false drops the caret). Unset keys keep the built-in dark look."},message:{description:"Chat message bubbles.",user:"background, text, borderRadius, shadow.",assistant:"background, text, borderRadius, border (optional), shadow (optional)."},markdown:{description:"Markdown rendering in messages and artifact pane.",properties:"inlineCode (background, foreground), link.foreground, prose.fontFamily, heading.h1/h2 (fontSize, fontWeight), codeBlock (background, borderColor, textColor, borderRadius: follows palette.radius.md), table (headerBackground, borderColor), hr.color, blockquote (borderColor, background, textColor)."},introCard:{description:"Welcome surface above the message list. Flat by default (transparent, no shadow).",properties:"background, borderRadius, padding, shadow, border (full CSS shorthand, default none), title and subtitle (each fontFamily, fontSize, fontWeight, lineHeight, letterSpacing, color)."},suggestion:{description:"Starter prompt and follow-up suggestion chrome. Three variants: chip, card, list.",properties:"background, foreground, border, borderRadius, padding, shadow, gap (icon to copy inside one item), itemGap (space between items, default 8px), minHeight, fontSize, lineHeight, iconSize, hoverBackground, hoverForeground, hoverBorder, pressedBackground, focusRing, disabledOpacity."},voice:"recording (indicator, background, border), processing (icon, background), speaking (icon).",approval:"requested (background, border, text, shadow), approve (background, foreground), deny (background, foreground).",attachment:"image (background, border).",scrollToBottom:"Floating scroll-to-bottom affordance shared by transcript and event stream: background, foreground, border, size, borderRadius, shadow, padding, gap, fontSize, iconSize.",toolBubble:"shadow: tool call row box-shadow.",scrollbar:"Shared scrollbar appearance for every scroller: thumb (default semantic.colors.border), track (default transparent).",reasoningBubble:"shadow: reasoning/thinking row box-shadow.",composer:'Message input form: sendIconSize (send/stop glyph; explicit sendButton.iconSize wins), sendButtonRadius (icon-mode submit radius), footerBorder (footer top border shorthand; none hides it), shadow (box-shadow), borderColor, focusBorderColor (focused form border; V4 keeps the resting border, V5 uses a stronger neutral mix), focusRing (CSS outline shorthand, 1px offset; V4 none, V5 a thin neutral ring; preserves shadow and causes no layout shift), padding (form inset), gap (textarea row to actions row), fontSize and lineHeight (textarea type), controlSize (edge of every icon control in the action row: attachment, mention, mic, action buttons, overflow trigger, icon-mode send; default 40px) and controlIconSize (glyph inside them, default 24px). Per-control config keys (sendButton.size, voiceRecognition.iconSize) override controlSize; coarse-pointer devices floor the hit area at 40px. overlayBand paints the band behind an overlaid footer (composer.placement: "overlay"). segmented styles a segmented mode-group track (trackBackground, trackBorderRadius, padding, activeBackground, activeForeground, activeShadow, inactiveForeground, itemPadding); every key is optional and the stylesheet owns the defaults. modelPicker styles the model picker (background, hoverBackground, borderColor, borderRadius and labelColor on the closed control, which the native select and the popover trigger share; menuBackground, menuBorderRadius, rowHoverBackground, descriptionColor and suffixColor on the popover only), also all optional. overflowMenu styles the composer overflow "+" menu panel (background, borderColor, borderRadius, foreground, shadow), also all optional.',artifact:"toolbar (icon styling, copy menu), tab (background, active states), pane (background, toolbarBackground)."}},colorScheme:'"dark" merges darkTheme overrides on top of theme. "auto" detects system preference or <html class="dark">. "light" is default. colorScheme does NOT auto-invert colors: provide dark palette and semantic overrides yourself.',plugins:{description:"Plugins transform theme tokens before resolution. Use with createTheme().",available:{brandPlugin:'Auto-generates full color scales from a single brand hex: brandPlugin({ colors: { primary: "#7c3aed" } }).',accessibilityPlugin:"Enhanced focus indicators and disabled states.",highContrastPlugin:"Increased contrast for visual accessibility.",reducedMotionPlugin:"Disables all animations (sets transitions to 0ms).",animationsPlugin:"Adds transition and easing tokens."},usage:'createTheme(themeOverrides, { plugins: [brandPlugin({ colors: { primary: "#7c3aed" } })] })'},widgetConfig:{description:'Non-theme config options on the widget config object that affect appearance. These are siblings of "theme" in the config, not nested inside it.',launcher:{description:"Floating launcher button and panel positioning.",properties:{enabled:"Show/hide the launcher button.",title:"Header title text.",subtitle:"Header subtitle text.",position:'"bottom-right" | "bottom-left" | "top-right" | "top-left".',width:"Chat panel width (CSS value).",fullHeight:"Fill full height of container.",mountMode:'"floating" | "docked".',agentIconText:"Emoji/text for agent icon.",border:"Border style for launcher button.",shadow:"Box shadow for launcher button.",collapsedMaxWidth:"Max-width for launcher pill when panel closed."}},sendButton:{description:"Send button appearance.",properties:"backgroundColor, textColor, borderWidth, borderColor, paddingX, paddingY, iconText, iconName, size."},closeButton:{description:"Close button (on launcher config). Unset closeButtonSize means theme.components.header.controlSize owns the box.",properties:"closeButtonSize, closeButtonColor, closeButtonBackgroundColor, closeButtonBorderWidth, closeButtonBorderColor, closeButtonBorderRadius."},clearChat:{description:"Clear chat button (on launcher.clearChat config). Unset size means theme.components.header.controlSize owns the box.",properties:"enabled, iconColor, backgroundColor, borderWidth, borderColor, borderRadius, size."},scrollToBottom:{description:"Shared transcript + event-stream jump-to-latest affordance.",properties:'features.scrollToBottom.enabled, features.scrollToBottom.iconName, features.scrollToBottom.label (empty string renders icon-only). Defaults: enabled=true, iconName="arrow-down", label="".'},toolCall:{description:"Tool call display styling, text templates, loading animations, and rendering hooks. Text templates support placeholders ({toolName}, {duration}) and inline formatting (~dim~, *italic*, **bold**). renderCollapsedSummary receives elapsed (static string) and createElapsedElement() (live-updating span) in its context.",properties:"shadow, backgroundColor, borderColor, borderWidth, borderRadius, headerBackgroundColor, headerTextColor, headerPaddingX, headerPaddingY, contentBackgroundColor, contentTextColor, contentPaddingX, contentPaddingY, codeBlockBackgroundColor, codeBlockBorderColor, codeBlockTextColor, toggleTextColor, labelTextColor, activeTextTemplate, completeTextTemplate, loadingAnimationColor, loadingAnimationSecondaryColor, loadingAnimationDuration, renderCollapsedSummary, renderCollapsedPreview, renderGroupedSummary."},reasoning:{description:"Reasoning/thinking row rendering hooks, text templates, and loading animations. Text templates support {duration} placeholder and inline formatting (~dim~, *italic*, **bold**). renderCollapsedSummary receives elapsed (static string) and createElapsedElement() (live-updating span) in its context.",properties:"renderCollapsedSummary, renderCollapsedPreview, activeTextTemplate, completeTextTemplate, loadingAnimationColor, loadingAnimationSecondaryColor, loadingAnimationDuration."},approval:{description:`Tool approval card styling and behavior. Neutral surface card; the primary action anchors to the brand --persona-primary token. Set to false to disable. Set enableAlwaysAllow: true to add the split "Always allow / Allow once" control (needs a backend to persist the policy via onDecision's remember).`,properties:'enableAlwaysAllow, detailsDisplay ("collapsed" | "expanded" | "hidden"), showDetailsLabel, hideDetailsLabel, backgroundColor, borderColor, titleColor, descriptionColor, reasonColor, reasonLabel, approveButtonColor, approveButtonTextColor, denyButtonColor, denyButtonTextColor, parameterBackgroundColor, parameterTextColor, approveLabel, denyLabel, formatDescription. (title no longer renders in the default card; use formatDescription to customize the summary.)'},copy:{description:"Widget text content.",properties:"inputPlaceholder, sendButtonLabel, stopButtonLabel. (showWelcomeCard, welcomeTitle, welcomeSubtitle are deprecated aliases of the welcome namespace.)"},voiceRecognition:{description:"Voice input configuration.",properties:"enabled, pauseDuration, iconColor, backgroundColor."},textToSpeech:{description:"Text-to-speech for assistant messages.",properties:'enabled, provider ("browser" | "runtype"), browserFallback, voice, rate, pitch.'},suggestions:{description:"Rich starter and follow-up suggestion surfaces. Strings remain valid item shorthand.",properties:'starters.items, starters.variant ("card" | "chip" | "list"), starters.placement ("auto" | "welcome" | "composer"), starters.behavior ("send" | "fill"), starters.overflow ("scroll" | "wrap"), starters.maxItems, followUps.enabled, followUps.expose, followUps.variant, followUps.placement ("auto" | "after-message" | "composer"), followUps.behavior, followUps.overflow ("scroll" | "wrap"), followUps.maxItems.'},welcome:{description:"First-open welcome surface. Supersedes copy.welcomeTitle / welcomeSubtitle / showWelcomeCard.",properties:'title, subtitle, icon ({ type: "lucide", name } | { type: "image", url, alt } | { type: "text", text } | function), variant ("card" | "hero" | "none"), dismiss ("never" | "on-first-message"), message (display-only greeting bubble, ignored under "hero").'},suggestionChips:"Deprecated string[] shorthand for starter prompts. Prefer suggestions.starters.items.",messageActions:{description:"Message action buttons (copy, upvote, downvote, read aloud, regenerate, edit, quote).",properties:'enabled, showCopy, showUpvote, showDownvote, showReadAloud, showRegenerate (final retryable assistant turn only), showEdit (text-only user messages only), showQuote, visibility ("hover" | "always"), align ("left" | "center" | "right"), layout ("pill-inside" | "row-inside").'},attachments:{description:"File attachment configuration.",properties:"enabled, allowedTypes (string[]), maxFileSize (bytes), maxFiles, buttonIconName, buttonTooltipText."},markdown:{description:"Markdown rendering configuration.",properties:"options (gfm, breaks, headerIds, headerPrefix, pedantic, mangle, silent), disableDefaultStyles."},layout:{description:"Layout configuration.",showHeader:"boolean: show/hide the header section entirely.",showFooter:"boolean: show/hide the footer/composer section entirely.",contentMaxWidth:'CSS width value for centering content (e.g. "720px", "90ch").',header:'"default" | "minimal". Options: showIcon, showTitle, showSubtitle, showCloseButton, showClearChat.',messages:'"bubble" | "flat" | "minimal". Options: groupConsecutive, avatar (show, position, userAvatar, assistantAvatar), timestamp (show, position).'},statusIndicator:{description:"Status text shown below the composer.",properties:{visible:"Show/hide the status indicator.",align:'"left" | "center" | "right": text alignment (default: "right").',idleText:'Text shown when idle (default: "Online").',idleLink:"URL to open when idle text is clicked (wraps text in a link).",connectingText:'Text shown while connecting (default: "Connecting\u2026").',connectedText:'Text shown while streaming (default: "Streaming\u2026").',errorText:'Text shown on error (default: "Offline").'}},features:{description:"Feature flags.",properties:"showReasoning (AI thinking steps), showToolCalls (tool invocations), toolCallDisplay (collapsedMode, activePreview, activeMinHeight, previewMaxLines, grouped, groupedMode, expandable, loadingAnimation), reasoningDisplay (activePreview, activeMinHeight, previewMaxLines, expandable, loadingAnimation), artifacts (sidebar config)."}}},N={darkIndigo:{description:"Dark mode with indigo accent: override grays for dark backgrounds and semantic tokens for inverted text/surface",theme:{palette:{colors:{primary:{500:"#6366f1",600:"#4f46e5"},gray:{50:"#f1f5f9",100:"#1e293b",200:"#334155",500:"#94a3b8",900:"#0f172a",950:"#020617"}}},semantic:{colors:{surface:"palette.colors.gray.900",background:"palette.colors.gray.900",container:"palette.colors.gray.100",text:"palette.colors.gray.50",textMuted:"palette.colors.gray.500",textInverse:"palette.colors.gray.900",border:"palette.colors.gray.200"}}}},warmVintage:{description:"Warm sepia tones with serif font and subtle radius",theme:{palette:{colors:{primary:{500:"#b45309",600:"#92400e"},gray:{50:"#fef3c7",100:"#fef9c3",200:"#d6d3d1",500:"#78716c",900:"#44403c"}},radius:{sm:"0.125rem",md:"0.25rem",lg:"0.375rem"},typography:{fontFamily:{sans:'Georgia, Cambria, "Times New Roman", Times, serif'}}}}},neonCyberpunk:{description:"Neon on dark with monospace font: full semantic override for dark background",theme:{palette:{colors:{primary:{500:"#f0abfc",600:"#e879f9"},accent:{500:"#22d3ee",600:"#06b6d4"},gray:{50:"#f0abfc",100:"#1e0a3c",200:"#3b0764",500:"#c084fc",900:"#0c0a1a",950:"#050412"}},radius:{sm:"0",md:"0.25rem",lg:"0.375rem"},typography:{fontFamily:{sans:"ui-monospace, SFMono-Regular, Menlo, monospace"}}},semantic:{colors:{surface:"palette.colors.gray.900",background:"palette.colors.gray.950",container:"palette.colors.gray.100",text:"palette.colors.gray.50",textMuted:"palette.colors.gray.500",border:"palette.colors.gray.200"}}}},cleanRounded:{description:"Clean light theme with large radius and panel styling",theme:{palette:{radius:{sm:"6px",md:"8px",lg:"12px",launcher:"50px",button:"8px"}},components:{panel:{borderRadius:"16px",shadow:"palette.shadows.2xl"}}}}};function ae(e){let o=b(e),t=p({future:e});return{tokenDocs:_,defaultsVersion:n({future:e}),themeDefaultsVersion:u(e),defaultColorPalette:o.palette.colors,defaultRadius:o.palette.radius,defaultLauncher:t.launcher,examples:N,sdkPresets:["shop","minimal","fullscreen"]}}export{N as THEME_EXAMPLES,_ as THEME_TOKEN_DOCS,ae as getThemeReference};
@@ -0,0 +1 @@
1
+ import{a as v}from"./chunk-5EIIHQLQ.js";var g=16e3,k=24e3,S=4096,C=4,E=1380533830,A=3e4,M=5e3,P=3e4,T=3e4,V=6e5;function _(r){return r.byteLength>=44&&new DataView(r).getUint32(0,!1)===E?new Uint8Array(r,44):new Uint8Array(r)}function w(r){let t=r.replace(/\/+$/,"");return/^wss?:\/\//i.test(t)?t:/^https?:\/\//i.test(t)?t.replace(/^http/i,"ws"):`${typeof window<"u"&&window.location?.protocol==="https:"?"wss:":"ws:"}//${t}`}function R(r){return w(r).replace(/^ws/i,"http")}function L(r){return typeof r!="number"||!Number.isFinite(r)?P:Math.min(V,Math.max(T,Math.round(r)))}var f=class{constructor(t){this.config=t;this.type="runtype";this.ws=null;this.captureContext=null;this.levelCallbacks=[];this.mediaStream=null;this.sourceNode=null;this.processor=null;this.playback=null;this.callLive=!1;this.isSpeaking=!1;this.callGeneration=0;this.intentionalClose=!1;this.lastPrewarmAt=Number.NEGATIVE_INFINITY;this.attached=null;this.resultCallbacks=[];this.errorCallbacks=[];this.statusCallbacks=[];this.transcriptCallbacks=[];this.metricsCallbacks=[]}async connect(){}prewarm(){if(this.callLive||this.attached)return;let t=this.config?.agentId,e=this.config?.clientToken,i=this.config?.host;if(!t||!e||!i)return;let s=Date.now();if(!(s-this.lastPrewarmAt<A)){this.lastPrewarmAt=s;try{this.config?.prewarmMode==="attach"?this.attach(i,t,e):this.requestPrewarm(i,t,e)}catch{}}}requestPrewarm(t,e,i){if(typeof fetch!="function")return;let s=`${R(t)}/v1/client/agents/${encodeURIComponent(e)}/voice/prewarm`;fetch(s,{method:"POST",headers:{Authorization:`Bearer ${i}`},keepalive:!0}).catch(()=>{})}attach(t,e,i){let s=new URLSearchParams({clientCapabilities:"attach"}),o=this.config?.attachIdleMs,h=L(o);o!==void 0&&s.set("attachIdleMs",String(h));let c=new WebSocket(`${this.voiceSocketUrl(t,e)}?${s}`,["runtype.bearer","runtype.attach",i]);c.binaryType="arraybuffer";let u=()=>{},d=new Promise(n=>{u=n}),a={ws:c,state:"connecting",ready:d,settle:u,openedAt:0,idleTimer:void 0};this.attached=a,c.onopen=()=>{this.attached===a&&(a.state=c.protocol==="runtype.attach"?"attached":"live",a.openedAt=Date.now(),this.armAttachIdle(a,h),a.state==="attached"&&c.send('{"type":"ping"}'),a.settle())},c.onmessage=n=>{if(this.attached!==a||typeof n.data!="string")return;let l;try{l=JSON.parse(n.data)}catch{return}l.type==="attached"&&typeof l.idleMs=="number"&&this.armAttachIdle(a,l.idleMs-(Date.now()-a.openedAt))},c.onerror=()=>{},c.onclose=()=>{this.attached===a&&this.releaseAttached()}}armAttachIdle(t,e){clearTimeout(t.idleTimer),t.idleTimer=setTimeout(()=>{this.attached===t&&this.releaseAttached()},Math.max(0,e))}releaseAttached(){let t=this.attached;if(!t)return;this.attached=null,clearTimeout(t.idleTimer);let{ws:e}=t;if(e.onopen=null,e.onmessage=null,e.onerror=null,e.onclose=null,t.settle(),e.readyState===WebSocket.CONNECTING||e.readyState===WebSocket.OPEN)try{e.close(1e3,"prewarm released")}catch{}}async takeAttachedSocket(){let t=this.attached;if(!t)return null;if(t.state==="connecting"){let e;await Promise.race([t.ready,new Promise(i=>{e=setTimeout(i,M)})]),clearTimeout(e)}return this.attached!==t?null:t.state==="connecting"||t.ws.readyState!==WebSocket.OPEN?(this.releaseAttached(),null):(this.attached=null,clearTimeout(t.idleTimer),{ws:t.ws,live:t.state==="live"})}voiceSocketUrl(t,e){return`${w(t)}/ws/agents/${encodeURIComponent(e)}/voice`}async startListening(){if(this.callLive)return;let t=this.config?.agentId,e=this.config?.clientToken,i=this.config?.host;if(!t)throw new Error("Runtype voice requires an agentId");if(!e)throw new Error("Runtype voice requires a clientToken");if(!i)throw new Error("Runtype voice requires a host (or widget apiUrl)");let s=++this.callGeneration;this.intentionalClose=!1,this.callLive=!0;try{let o=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:g,channelCount:1,echoCancellation:!0}});if(s!==this.callGeneration){o.getTracks().forEach(n=>n.stop());return}this.mediaStream=o;let h=window.AudioContext||window.webkitAudioContext,c=new h({sampleRate:g});c.state==="suspended"&&await c.resume().catch(()=>{}),this.captureContext=c;let u=this.config?.createPlaybackEngine?await this.config.createPlaybackEngine():new v(k);if(s!==this.callGeneration){u.destroy(),o.getTracks().forEach(n=>n.stop()),c.close().catch(()=>{});return}this.playback=u,u.onFinished(()=>{s===this.callGeneration&&(this.isSpeaking=!1,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.emitStatus("listening"))});let d=await this.takeAttachedSocket();if(s!==this.callGeneration){d?.ws.close(1e3,"client ended call");return}if(d){let{ws:n}=d;this.ws=n,this.bindCallSocket(n,s),d.live||n.send('{"type":"start"}'),this.emitStatus("listening"),this.startCapture(c,o,n,s);return}let a=new WebSocket(this.voiceSocketUrl(i,t),["runtype.bearer",e]);a.binaryType="arraybuffer",this.ws=a,a.onopen=()=>{s===this.callGeneration&&(this.emitStatus("listening"),this.startCapture(c,o,a,s))},this.bindCallSocket(a,s)}catch(o){throw this.cleanup(),this.emitError(o),this.emitStatus("error"),o}}bindCallSocket(t,e){t.onmessage=i=>this.handleMessage(i,e),t.onerror=()=>{e===this.callGeneration&&(this.emitError(new Error("Voice connection failed")),this.emitStatus("error"),this.cleanup())},t.onclose=i=>{if(this.intentionalClose){this.intentionalClose=!1;return}if(e===this.callGeneration){if(i.code!==1e3){let s=i.code?` (code ${i.code})`:"";this.emitError(new Error(`Voice connection closed${s}`)),this.emitStatus("error")}else this.emitStatus("idle");this.cleanup()}}}async stopListening(){this.cleanup(),this.emitStatus("idle")}async disconnect(){this.cleanup(),this.emitStatus("disconnected"),this.resultCallbacks=[],this.errorCallbacks=[],this.statusCallbacks=[],this.transcriptCallbacks=[],this.metricsCallbacks=[]}stopPlayback(){this.playback&&this.playback.flush(),this.isSpeaking=!1,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.emitStatus("listening")}getInterruptionMode(){return"barge-in"}isBargeInActive(){return this.callLive}onLevel(t){this.levelCallbacks.push(t)}async deactivateBargeIn(){this.cleanup(),this.emitStatus("idle")}startCapture(t,e,i,s){let o=t.createMediaStreamSource(e);this.sourceNode=o;let h=t.createScriptProcessor(S,1,1);this.processor=h,h.onaudioprocess=c=>{if(s!==this.callGeneration||i.readyState!==WebSocket.OPEN)return;let u=c.inputBuffer.getChannelData(0),d=new Int16Array(u.length),a=0;for(let n=0;n<u.length;n++){let l=Math.max(-1,Math.min(1,u[n]));a+=l*l,d[n]=l<0?l*32768:l*32767}if(i.send(d.buffer),this.levelCallbacks.length>0){let n=Math.sqrt(a/u.length),l=Math.max(0,Math.min(1,n*C));for(let b of this.levelCallbacks)b(l)}},o.connect(h),h.connect(t.destination)}handleMessage(t,e){if(e!==this.callGeneration)return;if(t.data instanceof ArrayBuffer){this.handleAudioFrame(t.data,e);return}let i;try{i=JSON.parse(t.data)}catch{return}switch(i.type){case"transcript_interim":this.emitStatus("listening"),this.emitTranscript("user",i.text??"",!1);break;case"transcript_final":{let s=i.role==="assistant"?"assistant":"user";this.emitStatus(s==="user"?"processing":"speaking"),this.emitTranscript(s,i.text??"",!0);break}case"audio_end":this.playback?this.playback.markStreamEnd():(this.isSpeaking=!1,this.emitStatus("listening"));break;case"metrics":this.emitMetrics({llmMs:i.llm_ms,ttsMs:i.tts_ms,firstAudioMs:i.first_audio_ms,totalMs:i.total_ms});break;case"error":this.emitError(new Error(i.error||"Voice error")),this.emitStatus("error");break}}handleAudioFrame(t,e){if(e!==this.callGeneration||!this.playback)return;let i=_(t);i.length!==0&&(this.isSpeaking||(this.isSpeaking=!0,this.emitStatus("speaking")),this.playback.enqueue(i))}cleanup(){if(this.callGeneration+=1,this.callLive=!1,this.isSpeaking=!1,this.releaseAttached(),this.processor&&(this.processor.onaudioprocess=null,this.processor.disconnect(),this.processor=null),this.sourceNode&&(this.sourceNode.disconnect(),this.sourceNode=null),this.mediaStream&&(this.mediaStream.getTracks().forEach(t=>t.stop()),this.mediaStream=null),this.captureContext&&(this.captureContext.close().catch(()=>{}),this.captureContext=null),this.playback&&(this.playback.destroy(),this.playback=null),this.ws){this.intentionalClose=!0;try{this.ws.close(1e3,"client ended call")}catch{}this.ws=null}}onResult(t){this.resultCallbacks.push(t)}onError(t){this.errorCallbacks.push(t)}onStatusChange(t){this.statusCallbacks.push(t)}onTranscript(t){this.transcriptCallbacks.push(t)}onMetrics(t){this.metricsCallbacks.push(t)}emitStatus(t){this.statusCallbacks.forEach(e=>e(t))}emitError(t){this.errorCallbacks.forEach(e=>e(t))}emitTranscript(t,e,i){this.transcriptCallbacks.forEach(s=>s(t,e,i))}emitMetrics(t){this.metricsCallbacks.forEach(e=>e(t))}};var p=class{constructor(t={}){this.config=t;this.type="browser";this.recognition=null;this.resultCallbacks=[];this.errorCallbacks=[];this.statusCallbacks=[];this.isListening=!1;this.w=typeof window<"u"?window:void 0}async connect(){this.statusCallbacks.forEach(t=>t("connected"))}async startListening(){try{if(this.isListening)throw new Error("Already listening");if(!this.w)throw new Error("Window object not available");let t=this.w.SpeechRecognition||this.w.webkitSpeechRecognition;if(!t)throw new Error("Browser speech recognition not supported");this.recognition=new t,this.recognition.lang=this.config?.language||"en-US",this.recognition.continuous=this.config?.continuous||!1,this.recognition.interimResults=!0,this.recognition.onresult=e=>{let i=Array.from(e.results).map(o=>o[0]).map(o=>o.transcript).join(""),s=e.results[e.results.length-1].isFinal;this.resultCallbacks.forEach(o=>o({text:i,confidence:s?.8:.5,provider:"browser"})),s&&!this.config?.continuous&&this.stopListening()},this.recognition.onerror=e=>{this.errorCallbacks.forEach(i=>i(new Error(e.error))),this.statusCallbacks.forEach(i=>i("error"))},this.recognition.onstart=()=>{this.isListening=!0,this.statusCallbacks.forEach(e=>e("listening"))},this.recognition.onend=()=>{this.isListening=!1,this.statusCallbacks.forEach(e=>e("idle"))},this.recognition.start()}catch(t){throw this.errorCallbacks.forEach(e=>e(t)),this.statusCallbacks.forEach(e=>e("error")),t}}async stopListening(){this.recognition&&(this.recognition.stop(),this.recognition=null),this.isListening=!1,this.statusCallbacks.forEach(t=>t("idle"))}onResult(t){this.resultCallbacks.push(t)}onError(t){this.errorCallbacks.push(t)}onStatusChange(t){this.statusCallbacks.push(t)}async disconnect(){await this.stopListening(),this.statusCallbacks.forEach(t=>t("disconnected"))}static isSupported(){return"SpeechRecognition"in window||"webkitSpeechRecognition"in window}};function m(r){switch(r.type){case"runtype":if(!r.runtype)throw new Error("Runtype voice provider requires configuration");return new f(r.runtype);case"browser":if(!p.isSupported())throw new Error("Browser speech recognition not supported");return new p(r.browser||{});case"custom":{let t=r.custom;if(!t)throw new Error("Custom voice provider requires a `custom` provider instance or factory");let e=typeof t=="function"?t():t;if(!e||typeof e.startListening!="function")throw new Error("Custom voice provider `custom` must be a VoiceProvider (or a factory returning one)");return e}default:throw new Error(`Unknown voice provider type: ${r.type}`)}}function y(r){if(r?.type==="custom"&&r.custom)return m({type:"custom",custom:r.custom});if(r?.type==="runtype"&&r.runtype)return m({type:"runtype",runtype:r.runtype});if(p.isSupported())return m({type:"browser",browser:r?.browser||{language:"en-US"}});throw new Error("No supported voice providers available")}function I(r){try{return y(r),!0}catch{return!1}}export{p as BrowserVoiceProvider,f as RuntypeVoiceProvider,y as createBestAvailableVoiceProvider,m as createVoiceProvider,I as isVoiceSupported};
@@ -1 +1 @@
1
- "use strict";var g=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var x=(r,e)=>{for(var t in e)g(r,t,{get:e[t],enumerable:!0})},A=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of k(e))!E.call(r,s)&&s!==t&&g(r,s,{get:()=>e[s],enumerable:!(i=C(e,s))||i.enumerable});return r};var V=r=>A(g({},"__esModule",{value:!0}),r);var F={};x(F,{BrowserVoiceProvider:()=>p,RuntypeVoiceProvider:()=>d,createBestAvailableVoiceProvider:()=>b,createVoiceProvider:()=>f,isVoiceSupported:()=>w});module.exports=V(F);var v=class{constructor(e=24e3,t={}){this.ctx=null;this.nextStartTime=0;this.activeSources=[];this.finishedCallbacks=[];this.startedCallbacks=[];this.playing=!1;this.streamEnded=!1;this.pendingCount=0;this.started=!1;this.userPaused=!1;this.pendingBuffers=[];this.pendingSamples=0;this.remainder=null;this.sampleRate=e;let i=Math.max(0,t.prebufferMs??0);this.waterlineSamples=Math.round(e*i/1e3),this.buffering=this.waterlineSamples>0}ensureContext(){if(!this.ctx){let t=typeof window<"u"?window:void 0;if(!t)throw new Error("AudioPlaybackManager requires a browser environment");let i=t.AudioContext||t.webkitAudioContext;this.ctx=new i({sampleRate:this.sampleRate})}let e=this.ctx;return e.state==="suspended"&&!this.userPaused&&e.resume(),e}enqueue(e){if(e.length===0)return;let t=e;if(this.remainder){let s=new Uint8Array(this.remainder.length+e.length);s.set(this.remainder),s.set(e,this.remainder.length),t=s,this.remainder=null}if(t.length%2!==0&&(this.remainder=new Uint8Array([t[t.length-1]]),t=t.subarray(0,t.length-1)),t.length===0)return;let i=this.pcmToFloat32(t);i.length!==0&&(this.buffering?(this.pendingBuffers.push(i),this.pendingSamples+=i.length,this.pendingSamples>=this.waterlineSamples&&this.releaseBuffer()):this.scheduleSamples(i))}markStreamEnd(){this.pendingBuffers.length>0&&this.releaseBuffer(),this.streamEnded=!0,this.checkFinished()}flush(){for(let e of this.activeSources)try{e.stop(),e.disconnect()}catch{}this.activeSources=[],this.pendingCount=0,this.nextStartTime=0,this.playing=!1,this.streamEnded=!1,this.finishedCallbacks=[],this.startedCallbacks=[],this.remainder=null,this.pendingBuffers=[],this.pendingSamples=0,this.buffering=this.waterlineSamples>0,this.started=!1}isPlaying(){return this.playing}onFinished(e){this.finishedCallbacks.push(e)}onStarted(e){this.startedCallbacks.push(e)}pause(){this.userPaused=!0,this.ctx&&this.ctx.state==="running"&&this.ctx.suspend()}resume(){this.userPaused=!1,this.ctx&&this.ctx.state==="suspended"&&this.ctx.resume()}async destroy(){this.flush(),this.ctx&&(await this.ctx.close(),this.ctx=null)}releaseBuffer(){this.buffering=!1;let e=this.pendingBuffers;this.pendingBuffers=[],this.pendingSamples=0;for(let t of e)this.scheduleSamples(t)}scheduleSamples(e){if(e.length===0)return;let t=this.ensureContext(),i=t.createBuffer(1,e.length,this.sampleRate);i.getChannelData(0).set(e);let s=t.createBufferSource();s.buffer=i,s.connect(t.destination);let n=t.currentTime;if(this.nextStartTime===0?this.nextStartTime=n:this.nextStartTime<n&&(this.nextStartTime=n,this.waterlineSamples>0&&(this.buffering=!0)),s.start(this.nextStartTime),this.nextStartTime+=i.duration,this.activeSources.push(s),this.pendingCount++,this.playing=!0,!this.started){this.started=!0;let a=this.startedCallbacks.slice();this.startedCallbacks=[];for(let l of a)l()}s.onended=()=>{let a=this.activeSources.indexOf(s);a!==-1&&this.activeSources.splice(a,1),this.pendingCount--,this.checkFinished()}}checkFinished(){if(this.streamEnded&&this.pendingCount<=0&&this.pendingBuffers.length===0){this.playing=!1,this.streamEnded=!1;let e=this.finishedCallbacks.slice();this.finishedCallbacks=[];for(let t of e)t()}}pcmToFloat32(e){let t=Math.floor(e.length/2),i=new Float32Array(t),s=new DataView(e.buffer,e.byteOffset,e.byteLength);for(let n=0;n<t;n++){let a=s.getInt16(n*2,!0);i[n]=a/32768}return i}};var y=16e3,P=24e3,M=4096,R=4,B=1380533830;function L(r){return r.byteLength>=44&&new DataView(r).getUint32(0,!1)===B?new Uint8Array(r,44):new Uint8Array(r)}function T(r){let e=r.replace(/\/+$/,"");return/^wss?:\/\//i.test(e)?e:/^https?:\/\//i.test(e)?e.replace(/^http/i,"ws"):`${typeof window<"u"&&window.location?.protocol==="https:"?"wss:":"ws:"}//${e}`}var d=class{constructor(e){this.config=e;this.type="runtype";this.ws=null;this.captureContext=null;this.levelCallbacks=[];this.mediaStream=null;this.sourceNode=null;this.processor=null;this.playback=null;this.callLive=!1;this.isSpeaking=!1;this.callGeneration=0;this.intentionalClose=!1;this.resultCallbacks=[];this.errorCallbacks=[];this.statusCallbacks=[];this.transcriptCallbacks=[];this.metricsCallbacks=[]}async connect(){}async startListening(){if(this.callLive)return;let e=this.config?.agentId,t=this.config?.clientToken,i=this.config?.host;if(!e)throw new Error("Runtype voice requires an agentId");if(!t)throw new Error("Runtype voice requires a clientToken");if(!i)throw new Error("Runtype voice requires a host (or widget apiUrl)");let s=++this.callGeneration;this.intentionalClose=!1,this.callLive=!0;try{let n=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:y,channelCount:1,echoCancellation:!0}});if(s!==this.callGeneration){n.getTracks().forEach(o=>o.stop());return}this.mediaStream=n;let a=window.AudioContext||window.webkitAudioContext,l=new a({sampleRate:y});l.state==="suspended"&&await l.resume().catch(()=>{}),this.captureContext=l;let h=this.config?.createPlaybackEngine?await this.config.createPlaybackEngine():new v(P);if(s!==this.callGeneration){h.destroy(),n.getTracks().forEach(o=>o.stop()),l.close().catch(()=>{});return}this.playback=h,h.onFinished(()=>{s===this.callGeneration&&(this.isSpeaking=!1,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.emitStatus("listening"))});let m=`${T(i)}/ws/agents/${encodeURIComponent(e)}/voice`,c=new WebSocket(m,["runtype.bearer",t]);c.binaryType="arraybuffer",this.ws=c,c.onopen=()=>{s===this.callGeneration&&(this.emitStatus("listening"),this.startCapture(l,n,c,s))},c.onmessage=o=>this.handleMessage(o,s),c.onerror=()=>{s===this.callGeneration&&(this.emitError(new Error("Voice connection failed")),this.emitStatus("error"),this.cleanup())},c.onclose=o=>{if(this.intentionalClose){this.intentionalClose=!1;return}if(s===this.callGeneration){if(o.code!==1e3){let u=o.code?` (code ${o.code})`:"";this.emitError(new Error(`Voice connection closed${u}`)),this.emitStatus("error")}else this.emitStatus("idle");this.cleanup()}}}catch(n){throw this.cleanup(),this.emitError(n),this.emitStatus("error"),n}}async stopListening(){this.cleanup(),this.emitStatus("idle")}async disconnect(){this.cleanup(),this.emitStatus("disconnected"),this.resultCallbacks=[],this.errorCallbacks=[],this.statusCallbacks=[],this.transcriptCallbacks=[],this.metricsCallbacks=[]}stopPlayback(){this.playback&&this.playback.flush(),this.isSpeaking=!1,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.emitStatus("listening")}getInterruptionMode(){return"barge-in"}isBargeInActive(){return this.callLive}onLevel(e){this.levelCallbacks.push(e)}async deactivateBargeIn(){this.cleanup(),this.emitStatus("idle")}startCapture(e,t,i,s){let n=e.createMediaStreamSource(t);this.sourceNode=n;let a=e.createScriptProcessor(M,1,1);this.processor=a,a.onaudioprocess=l=>{if(s!==this.callGeneration||i.readyState!==WebSocket.OPEN)return;let h=l.inputBuffer.getChannelData(0),m=new Int16Array(h.length),c=0;for(let o=0;o<h.length;o++){let u=Math.max(-1,Math.min(1,h[o]));c+=u*u,m[o]=u<0?u*32768:u*32767}if(i.send(m.buffer),this.levelCallbacks.length>0){let o=Math.sqrt(c/h.length),u=Math.max(0,Math.min(1,o*R));for(let S of this.levelCallbacks)S(u)}},n.connect(a),a.connect(e.destination)}handleMessage(e,t){if(t!==this.callGeneration)return;if(e.data instanceof ArrayBuffer){this.handleAudioFrame(e.data,t);return}let i;try{i=JSON.parse(e.data)}catch{return}switch(i.type){case"transcript_interim":this.emitStatus("listening"),this.emitTranscript("user",i.text??"",!1);break;case"transcript_final":{let s=i.role==="assistant"?"assistant":"user";this.emitStatus(s==="user"?"processing":"speaking"),this.emitTranscript(s,i.text??"",!0);break}case"audio_end":this.playback?this.playback.markStreamEnd():(this.isSpeaking=!1,this.emitStatus("listening"));break;case"metrics":this.emitMetrics({llmMs:i.llm_ms,ttsMs:i.tts_ms,firstAudioMs:i.first_audio_ms,totalMs:i.total_ms});break;case"error":this.emitError(new Error(i.error||"Voice error")),this.emitStatus("error");break}}handleAudioFrame(e,t){if(t!==this.callGeneration||!this.playback)return;let i=L(e);i.length!==0&&(this.isSpeaking||(this.isSpeaking=!0,this.emitStatus("speaking")),this.playback.enqueue(i))}cleanup(){if(this.callGeneration+=1,this.callLive=!1,this.isSpeaking=!1,this.processor&&(this.processor.onaudioprocess=null,this.processor.disconnect(),this.processor=null),this.sourceNode&&(this.sourceNode.disconnect(),this.sourceNode=null),this.mediaStream&&(this.mediaStream.getTracks().forEach(e=>e.stop()),this.mediaStream=null),this.captureContext&&(this.captureContext.close().catch(()=>{}),this.captureContext=null),this.playback&&(this.playback.destroy(),this.playback=null),this.ws){this.intentionalClose=!0;try{this.ws.close(1e3,"client ended call")}catch{}this.ws=null}}onResult(e){this.resultCallbacks.push(e)}onError(e){this.errorCallbacks.push(e)}onStatusChange(e){this.statusCallbacks.push(e)}onTranscript(e){this.transcriptCallbacks.push(e)}onMetrics(e){this.metricsCallbacks.push(e)}emitStatus(e){this.statusCallbacks.forEach(t=>t(e))}emitError(e){this.errorCallbacks.forEach(t=>t(e))}emitTranscript(e,t,i){this.transcriptCallbacks.forEach(s=>s(e,t,i))}emitMetrics(e){this.metricsCallbacks.forEach(t=>t(e))}};var p=class{constructor(e={}){this.config=e;this.type="browser";this.recognition=null;this.resultCallbacks=[];this.errorCallbacks=[];this.statusCallbacks=[];this.isListening=!1;this.w=typeof window<"u"?window:void 0}async connect(){this.statusCallbacks.forEach(e=>e("connected"))}async startListening(){try{if(this.isListening)throw new Error("Already listening");if(!this.w)throw new Error("Window object not available");let e=this.w.SpeechRecognition||this.w.webkitSpeechRecognition;if(!e)throw new Error("Browser speech recognition not supported");this.recognition=new e,this.recognition.lang=this.config?.language||"en-US",this.recognition.continuous=this.config?.continuous||!1,this.recognition.interimResults=!0,this.recognition.onresult=t=>{let i=Array.from(t.results).map(n=>n[0]).map(n=>n.transcript).join(""),s=t.results[t.results.length-1].isFinal;this.resultCallbacks.forEach(n=>n({text:i,confidence:s?.8:.5,provider:"browser"})),s&&!this.config?.continuous&&this.stopListening()},this.recognition.onerror=t=>{this.errorCallbacks.forEach(i=>i(new Error(t.error))),this.statusCallbacks.forEach(i=>i("error"))},this.recognition.onstart=()=>{this.isListening=!0,this.statusCallbacks.forEach(t=>t("listening"))},this.recognition.onend=()=>{this.isListening=!1,this.statusCallbacks.forEach(t=>t("idle"))},this.recognition.start()}catch(e){throw this.errorCallbacks.forEach(t=>t(e)),this.statusCallbacks.forEach(t=>t("error")),e}}async stopListening(){this.recognition&&(this.recognition.stop(),this.recognition=null),this.isListening=!1,this.statusCallbacks.forEach(e=>e("idle"))}onResult(e){this.resultCallbacks.push(e)}onError(e){this.errorCallbacks.push(e)}onStatusChange(e){this.statusCallbacks.push(e)}async disconnect(){await this.stopListening(),this.statusCallbacks.forEach(e=>e("disconnected"))}static isSupported(){return"SpeechRecognition"in window||"webkitSpeechRecognition"in window}};function f(r){switch(r.type){case"runtype":if(!r.runtype)throw new Error("Runtype voice provider requires configuration");return new d(r.runtype);case"browser":if(!p.isSupported())throw new Error("Browser speech recognition not supported");return new p(r.browser||{});case"custom":{let e=r.custom;if(!e)throw new Error("Custom voice provider requires a `custom` provider instance or factory");let t=typeof e=="function"?e():e;if(!t||typeof t.startListening!="function")throw new Error("Custom voice provider `custom` must be a VoiceProvider (or a factory returning one)");return t}default:throw new Error(`Unknown voice provider type: ${r.type}`)}}function b(r){if(r?.type==="custom"&&r.custom)return f({type:"custom",custom:r.custom});if(r?.type==="runtype"&&r.runtype)return f({type:"runtype",runtype:r.runtype});if(p.isSupported())return f({type:"browser",browser:r?.browser||{language:"en-US"}});throw new Error("No supported voice providers available")}function w(r){try{return b(r),!0}catch{return!1}}0&&(module.exports={BrowserVoiceProvider,RuntypeVoiceProvider,createBestAvailableVoiceProvider,createVoiceProvider,isVoiceSupported});
1
+ "use strict";var g=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var x=(r,t)=>{for(var e in t)g(r,e,{get:t[e],enumerable:!0})},P=(r,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of E(t))!A.call(r,s)&&s!==e&&g(r,s,{get:()=>t[s],enumerable:!(i=C(t,s))||i.enumerable});return r};var T=r=>P(g({},"__esModule",{value:!0}),r);var O={};x(O,{BrowserVoiceProvider:()=>p,RuntypeVoiceProvider:()=>f,createBestAvailableVoiceProvider:()=>b,createVoiceProvider:()=>m,isVoiceSupported:()=>S});module.exports=T(O);var v=class{constructor(t=24e3,e={}){this.ctx=null;this.nextStartTime=0;this.activeSources=[];this.finishedCallbacks=[];this.startedCallbacks=[];this.playing=!1;this.streamEnded=!1;this.pendingCount=0;this.started=!1;this.userPaused=!1;this.pendingBuffers=[];this.pendingSamples=0;this.remainder=null;this.sampleRate=t;let i=Math.max(0,e.prebufferMs??0);this.waterlineSamples=Math.round(t*i/1e3),this.buffering=this.waterlineSamples>0}ensureContext(){if(!this.ctx){let e=typeof window<"u"?window:void 0;if(!e)throw new Error("AudioPlaybackManager requires a browser environment");let i=e.AudioContext||e.webkitAudioContext;this.ctx=new i({sampleRate:this.sampleRate})}let t=this.ctx;return t.state==="suspended"&&!this.userPaused&&t.resume(),t}enqueue(t){if(t.length===0)return;let e=t;if(this.remainder){let s=new Uint8Array(this.remainder.length+t.length);s.set(this.remainder),s.set(t,this.remainder.length),e=s,this.remainder=null}if(e.length%2!==0&&(this.remainder=new Uint8Array([e[e.length-1]]),e=e.subarray(0,e.length-1)),e.length===0)return;let i=this.pcmToFloat32(e);i.length!==0&&(this.buffering?(this.pendingBuffers.push(i),this.pendingSamples+=i.length,this.pendingSamples>=this.waterlineSamples&&this.releaseBuffer()):this.scheduleSamples(i))}markStreamEnd(){this.pendingBuffers.length>0&&this.releaseBuffer(),this.streamEnded=!0,this.checkFinished()}flush(){for(let t of this.activeSources)try{t.stop(),t.disconnect()}catch{}this.activeSources=[],this.pendingCount=0,this.nextStartTime=0,this.playing=!1,this.streamEnded=!1,this.finishedCallbacks=[],this.startedCallbacks=[],this.remainder=null,this.pendingBuffers=[],this.pendingSamples=0,this.buffering=this.waterlineSamples>0,this.started=!1}isPlaying(){return this.playing}onFinished(t){this.finishedCallbacks.push(t)}onStarted(t){this.startedCallbacks.push(t)}pause(){this.userPaused=!0,this.ctx&&this.ctx.state==="running"&&this.ctx.suspend()}resume(){this.userPaused=!1,this.ctx&&this.ctx.state==="suspended"&&this.ctx.resume()}async destroy(){this.flush(),this.ctx&&(await this.ctx.close(),this.ctx=null)}releaseBuffer(){this.buffering=!1;let t=this.pendingBuffers;this.pendingBuffers=[],this.pendingSamples=0;for(let e of t)this.scheduleSamples(e)}scheduleSamples(t){if(t.length===0)return;let e=this.ensureContext(),i=e.createBuffer(1,t.length,this.sampleRate);i.getChannelData(0).set(t);let s=e.createBufferSource();s.buffer=i,s.connect(e.destination);let n=e.currentTime;if(this.nextStartTime===0?this.nextStartTime=n:this.nextStartTime<n&&(this.nextStartTime=n,this.waterlineSamples>0&&(this.buffering=!0)),s.start(this.nextStartTime),this.nextStartTime+=i.duration,this.activeSources.push(s),this.pendingCount++,this.playing=!0,!this.started){this.started=!0;let l=this.startedCallbacks.slice();this.startedCallbacks=[];for(let c of l)c()}s.onended=()=>{let l=this.activeSources.indexOf(s);l!==-1&&this.activeSources.splice(l,1),this.pendingCount--,this.checkFinished()}}checkFinished(){if(this.streamEnded&&this.pendingCount<=0&&this.pendingBuffers.length===0){this.playing=!1,this.streamEnded=!1;let t=this.finishedCallbacks.slice();this.finishedCallbacks=[];for(let e of t)e()}}pcmToFloat32(t){let e=Math.floor(t.length/2),i=new Float32Array(e),s=new DataView(t.buffer,t.byteOffset,t.byteLength);for(let n=0;n<e;n++){let l=s.getInt16(n*2,!0);i[n]=l/32768}return i}};var y=16e3,M=24e3,V=4096,R=4,_=1380533830,L=3e4,I=5e3,B=3e4,N=3e4,U=6e5;function F(r){return r.byteLength>=44&&new DataView(r).getUint32(0,!1)===_?new Uint8Array(r,44):new Uint8Array(r)}function w(r){let t=r.replace(/\/+$/,"");return/^wss?:\/\//i.test(t)?t:/^https?:\/\//i.test(t)?t.replace(/^http/i,"ws"):`${typeof window<"u"&&window.location?.protocol==="https:"?"wss:":"ws:"}//${t}`}function W(r){return w(r).replace(/^ws/i,"http")}function G(r){return typeof r!="number"||!Number.isFinite(r)?B:Math.min(U,Math.max(N,Math.round(r)))}var f=class{constructor(t){this.config=t;this.type="runtype";this.ws=null;this.captureContext=null;this.levelCallbacks=[];this.mediaStream=null;this.sourceNode=null;this.processor=null;this.playback=null;this.callLive=!1;this.isSpeaking=!1;this.callGeneration=0;this.intentionalClose=!1;this.lastPrewarmAt=Number.NEGATIVE_INFINITY;this.attached=null;this.resultCallbacks=[];this.errorCallbacks=[];this.statusCallbacks=[];this.transcriptCallbacks=[];this.metricsCallbacks=[]}async connect(){}prewarm(){if(this.callLive||this.attached)return;let t=this.config?.agentId,e=this.config?.clientToken,i=this.config?.host;if(!t||!e||!i)return;let s=Date.now();if(!(s-this.lastPrewarmAt<L)){this.lastPrewarmAt=s;try{this.config?.prewarmMode==="attach"?this.attach(i,t,e):this.requestPrewarm(i,t,e)}catch{}}}requestPrewarm(t,e,i){if(typeof fetch!="function")return;let s=`${W(t)}/v1/client/agents/${encodeURIComponent(e)}/voice/prewarm`;fetch(s,{method:"POST",headers:{Authorization:`Bearer ${i}`},keepalive:!0}).catch(()=>{})}attach(t,e,i){let s=new URLSearchParams({clientCapabilities:"attach"}),n=this.config?.attachIdleMs,l=G(n);n!==void 0&&s.set("attachIdleMs",String(l));let c=new WebSocket(`${this.voiceSocketUrl(t,e)}?${s}`,["runtype.bearer","runtype.attach",i]);c.binaryType="arraybuffer";let u=()=>{},d=new Promise(o=>{u=o}),a={ws:c,state:"connecting",ready:d,settle:u,openedAt:0,idleTimer:void 0};this.attached=a,c.onopen=()=>{this.attached===a&&(a.state=c.protocol==="runtype.attach"?"attached":"live",a.openedAt=Date.now(),this.armAttachIdle(a,l),a.state==="attached"&&c.send('{"type":"ping"}'),a.settle())},c.onmessage=o=>{if(this.attached!==a||typeof o.data!="string")return;let h;try{h=JSON.parse(o.data)}catch{return}h.type==="attached"&&typeof h.idleMs=="number"&&this.armAttachIdle(a,h.idleMs-(Date.now()-a.openedAt))},c.onerror=()=>{},c.onclose=()=>{this.attached===a&&this.releaseAttached()}}armAttachIdle(t,e){clearTimeout(t.idleTimer),t.idleTimer=setTimeout(()=>{this.attached===t&&this.releaseAttached()},Math.max(0,e))}releaseAttached(){let t=this.attached;if(!t)return;this.attached=null,clearTimeout(t.idleTimer);let{ws:e}=t;if(e.onopen=null,e.onmessage=null,e.onerror=null,e.onclose=null,t.settle(),e.readyState===WebSocket.CONNECTING||e.readyState===WebSocket.OPEN)try{e.close(1e3,"prewarm released")}catch{}}async takeAttachedSocket(){let t=this.attached;if(!t)return null;if(t.state==="connecting"){let e;await Promise.race([t.ready,new Promise(i=>{e=setTimeout(i,I)})]),clearTimeout(e)}return this.attached!==t?null:t.state==="connecting"||t.ws.readyState!==WebSocket.OPEN?(this.releaseAttached(),null):(this.attached=null,clearTimeout(t.idleTimer),{ws:t.ws,live:t.state==="live"})}voiceSocketUrl(t,e){return`${w(t)}/ws/agents/${encodeURIComponent(e)}/voice`}async startListening(){if(this.callLive)return;let t=this.config?.agentId,e=this.config?.clientToken,i=this.config?.host;if(!t)throw new Error("Runtype voice requires an agentId");if(!e)throw new Error("Runtype voice requires a clientToken");if(!i)throw new Error("Runtype voice requires a host (or widget apiUrl)");let s=++this.callGeneration;this.intentionalClose=!1,this.callLive=!0;try{let n=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:y,channelCount:1,echoCancellation:!0}});if(s!==this.callGeneration){n.getTracks().forEach(o=>o.stop());return}this.mediaStream=n;let l=window.AudioContext||window.webkitAudioContext,c=new l({sampleRate:y});c.state==="suspended"&&await c.resume().catch(()=>{}),this.captureContext=c;let u=this.config?.createPlaybackEngine?await this.config.createPlaybackEngine():new v(M);if(s!==this.callGeneration){u.destroy(),n.getTracks().forEach(o=>o.stop()),c.close().catch(()=>{});return}this.playback=u,u.onFinished(()=>{s===this.callGeneration&&(this.isSpeaking=!1,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.emitStatus("listening"))});let d=await this.takeAttachedSocket();if(s!==this.callGeneration){d?.ws.close(1e3,"client ended call");return}if(d){let{ws:o}=d;this.ws=o,this.bindCallSocket(o,s),d.live||o.send('{"type":"start"}'),this.emitStatus("listening"),this.startCapture(c,n,o,s);return}let a=new WebSocket(this.voiceSocketUrl(i,t),["runtype.bearer",e]);a.binaryType="arraybuffer",this.ws=a,a.onopen=()=>{s===this.callGeneration&&(this.emitStatus("listening"),this.startCapture(c,n,a,s))},this.bindCallSocket(a,s)}catch(n){throw this.cleanup(),this.emitError(n),this.emitStatus("error"),n}}bindCallSocket(t,e){t.onmessage=i=>this.handleMessage(i,e),t.onerror=()=>{e===this.callGeneration&&(this.emitError(new Error("Voice connection failed")),this.emitStatus("error"),this.cleanup())},t.onclose=i=>{if(this.intentionalClose){this.intentionalClose=!1;return}if(e===this.callGeneration){if(i.code!==1e3){let s=i.code?` (code ${i.code})`:"";this.emitError(new Error(`Voice connection closed${s}`)),this.emitStatus("error")}else this.emitStatus("idle");this.cleanup()}}}async stopListening(){this.cleanup(),this.emitStatus("idle")}async disconnect(){this.cleanup(),this.emitStatus("disconnected"),this.resultCallbacks=[],this.errorCallbacks=[],this.statusCallbacks=[],this.transcriptCallbacks=[],this.metricsCallbacks=[]}stopPlayback(){this.playback&&this.playback.flush(),this.isSpeaking=!1,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.emitStatus("listening")}getInterruptionMode(){return"barge-in"}isBargeInActive(){return this.callLive}onLevel(t){this.levelCallbacks.push(t)}async deactivateBargeIn(){this.cleanup(),this.emitStatus("idle")}startCapture(t,e,i,s){let n=t.createMediaStreamSource(e);this.sourceNode=n;let l=t.createScriptProcessor(V,1,1);this.processor=l,l.onaudioprocess=c=>{if(s!==this.callGeneration||i.readyState!==WebSocket.OPEN)return;let u=c.inputBuffer.getChannelData(0),d=new Int16Array(u.length),a=0;for(let o=0;o<u.length;o++){let h=Math.max(-1,Math.min(1,u[o]));a+=h*h,d[o]=h<0?h*32768:h*32767}if(i.send(d.buffer),this.levelCallbacks.length>0){let o=Math.sqrt(a/u.length),h=Math.max(0,Math.min(1,o*R));for(let k of this.levelCallbacks)k(h)}},n.connect(l),l.connect(t.destination)}handleMessage(t,e){if(e!==this.callGeneration)return;if(t.data instanceof ArrayBuffer){this.handleAudioFrame(t.data,e);return}let i;try{i=JSON.parse(t.data)}catch{return}switch(i.type){case"transcript_interim":this.emitStatus("listening"),this.emitTranscript("user",i.text??"",!1);break;case"transcript_final":{let s=i.role==="assistant"?"assistant":"user";this.emitStatus(s==="user"?"processing":"speaking"),this.emitTranscript(s,i.text??"",!0);break}case"audio_end":this.playback?this.playback.markStreamEnd():(this.isSpeaking=!1,this.emitStatus("listening"));break;case"metrics":this.emitMetrics({llmMs:i.llm_ms,ttsMs:i.tts_ms,firstAudioMs:i.first_audio_ms,totalMs:i.total_ms});break;case"error":this.emitError(new Error(i.error||"Voice error")),this.emitStatus("error");break}}handleAudioFrame(t,e){if(e!==this.callGeneration||!this.playback)return;let i=F(t);i.length!==0&&(this.isSpeaking||(this.isSpeaking=!0,this.emitStatus("speaking")),this.playback.enqueue(i))}cleanup(){if(this.callGeneration+=1,this.callLive=!1,this.isSpeaking=!1,this.releaseAttached(),this.processor&&(this.processor.onaudioprocess=null,this.processor.disconnect(),this.processor=null),this.sourceNode&&(this.sourceNode.disconnect(),this.sourceNode=null),this.mediaStream&&(this.mediaStream.getTracks().forEach(t=>t.stop()),this.mediaStream=null),this.captureContext&&(this.captureContext.close().catch(()=>{}),this.captureContext=null),this.playback&&(this.playback.destroy(),this.playback=null),this.ws){this.intentionalClose=!0;try{this.ws.close(1e3,"client ended call")}catch{}this.ws=null}}onResult(t){this.resultCallbacks.push(t)}onError(t){this.errorCallbacks.push(t)}onStatusChange(t){this.statusCallbacks.push(t)}onTranscript(t){this.transcriptCallbacks.push(t)}onMetrics(t){this.metricsCallbacks.push(t)}emitStatus(t){this.statusCallbacks.forEach(e=>e(t))}emitError(t){this.errorCallbacks.forEach(e=>e(t))}emitTranscript(t,e,i){this.transcriptCallbacks.forEach(s=>s(t,e,i))}emitMetrics(t){this.metricsCallbacks.forEach(e=>e(t))}};var p=class{constructor(t={}){this.config=t;this.type="browser";this.recognition=null;this.resultCallbacks=[];this.errorCallbacks=[];this.statusCallbacks=[];this.isListening=!1;this.w=typeof window<"u"?window:void 0}async connect(){this.statusCallbacks.forEach(t=>t("connected"))}async startListening(){try{if(this.isListening)throw new Error("Already listening");if(!this.w)throw new Error("Window object not available");let t=this.w.SpeechRecognition||this.w.webkitSpeechRecognition;if(!t)throw new Error("Browser speech recognition not supported");this.recognition=new t,this.recognition.lang=this.config?.language||"en-US",this.recognition.continuous=this.config?.continuous||!1,this.recognition.interimResults=!0,this.recognition.onresult=e=>{let i=Array.from(e.results).map(n=>n[0]).map(n=>n.transcript).join(""),s=e.results[e.results.length-1].isFinal;this.resultCallbacks.forEach(n=>n({text:i,confidence:s?.8:.5,provider:"browser"})),s&&!this.config?.continuous&&this.stopListening()},this.recognition.onerror=e=>{this.errorCallbacks.forEach(i=>i(new Error(e.error))),this.statusCallbacks.forEach(i=>i("error"))},this.recognition.onstart=()=>{this.isListening=!0,this.statusCallbacks.forEach(e=>e("listening"))},this.recognition.onend=()=>{this.isListening=!1,this.statusCallbacks.forEach(e=>e("idle"))},this.recognition.start()}catch(t){throw this.errorCallbacks.forEach(e=>e(t)),this.statusCallbacks.forEach(e=>e("error")),t}}async stopListening(){this.recognition&&(this.recognition.stop(),this.recognition=null),this.isListening=!1,this.statusCallbacks.forEach(t=>t("idle"))}onResult(t){this.resultCallbacks.push(t)}onError(t){this.errorCallbacks.push(t)}onStatusChange(t){this.statusCallbacks.push(t)}async disconnect(){await this.stopListening(),this.statusCallbacks.forEach(t=>t("disconnected"))}static isSupported(){return"SpeechRecognition"in window||"webkitSpeechRecognition"in window}};function m(r){switch(r.type){case"runtype":if(!r.runtype)throw new Error("Runtype voice provider requires configuration");return new f(r.runtype);case"browser":if(!p.isSupported())throw new Error("Browser speech recognition not supported");return new p(r.browser||{});case"custom":{let t=r.custom;if(!t)throw new Error("Custom voice provider requires a `custom` provider instance or factory");let e=typeof t=="function"?t():t;if(!e||typeof e.startListening!="function")throw new Error("Custom voice provider `custom` must be a VoiceProvider (or a factory returning one)");return e}default:throw new Error(`Unknown voice provider type: ${r.type}`)}}function b(r){if(r?.type==="custom"&&r.custom)return m({type:"custom",custom:r.custom});if(r?.type==="runtype"&&r.runtype)return m({type:"runtype",runtype:r.runtype});if(p.isSupported())return m({type:"browser",browser:r?.browser||{language:"en-US"}});throw new Error("No supported voice providers available")}function S(r){try{return b(r),!0}catch{return!1}}0&&(module.exports={BrowserVoiceProvider,RuntypeVoiceProvider,createBestAvailableVoiceProvider,createVoiceProvider,isVoiceSupported});
@@ -77,6 +77,10 @@ type VoiceConfig = {
77
77
  * `@runtypelabs/persona/voice-worklet-player` for a jitter-buffered engine.
78
78
  */
79
79
  createPlaybackEngine?: () => VoicePlaybackEngine | Promise<VoicePlaybackEngine>;
80
+ /** See `voiceRecognition.provider.runtype.prewarmMode`. @default 'request' */
81
+ prewarmMode?: 'request' | 'attach';
82
+ /** See `voiceRecognition.provider.runtype.attachIdleMs`. @default 30000 */
83
+ attachIdleMs?: number;
80
84
  /** @deprecated No-op on the realtime path: the server's STT owns turn-taking. */
81
85
  pauseDuration?: number;
82
86
  /** @deprecated No-op on the realtime path: the server's STT owns turn-taking. */
@@ -137,6 +141,13 @@ interface VoiceProvider {
137
141
  deactivateBargeIn?(): Promise<void>;
138
142
  /** Stop playback / cancel in-flight request without starting recording */
139
143
  stopPlayback?(): void;
144
+ /**
145
+ * Warm the provider ahead of `startListening()` (the widget calls it on mic
146
+ * intent, see `voiceRecognition.prewarm`). Must be fire-and-forget: never
147
+ * throw, emit errors or status, or acquire the microphone. May be called
148
+ * repeatedly; throttle as needed.
149
+ */
150
+ prewarm?(): void;
140
151
  }
141
152
 
142
153
  declare function createVoiceProvider(config: VoiceConfig): VoiceProvider;
@@ -157,6 +168,8 @@ declare class RuntypeVoiceProvider implements VoiceProvider {
157
168
  private isSpeaking;
158
169
  private callGeneration;
159
170
  private intentionalClose;
171
+ private lastPrewarmAt;
172
+ private attached;
160
173
  private resultCallbacks;
161
174
  private errorCallbacks;
162
175
  private statusCallbacks;
@@ -165,8 +178,28 @@ declare class RuntypeVoiceProvider implements VoiceProvider {
165
178
  constructor(config: VoiceConfig["runtype"]);
166
179
  /** No-op: the WS session opens lazily in `startListening` (the "call"). */
167
180
  connect(): Promise<void>;
181
+ /**
182
+ * Warm the voice path ahead of the click. Fire-and-forget and throttled to
183
+ * once per {@link PREWARM_THROTTLE_MS}; never emits an error or a status.
184
+ */
185
+ prewarm(): void;
186
+ private requestPrewarm;
187
+ private attach;
188
+ private armAttachIdle;
189
+ /** Close and forget the prewarmed socket, if any. Silent by design. */
190
+ private releaseAttached;
191
+ /**
192
+ * Hand the prewarmed socket to the call, waiting up to
193
+ * {@link ATTACH_HANDSHAKE_TIMEOUT_MS} for a handshake still in flight.
194
+ * Resolves `null` when there is none, it stalled, or it dropped, so the
195
+ * caller opens a fresh socket.
196
+ */
197
+ private takeAttachedSocket;
198
+ private voiceSocketUrl;
168
199
  /** Start the call: acquire mic, open the WS, stream PCM until hang-up. */
169
200
  startListening(): Promise<void>;
201
+ /** Route a call socket's frames, errors, and close into this call. */
202
+ private bindCallSocket;
170
203
  /** End the call (hang up). */
171
204
  stopListening(): Promise<void>;
172
205
  /** Tear down the call and drop all callbacks (used by `cleanupVoice`). */
@@ -77,6 +77,10 @@ type VoiceConfig = {
77
77
  * `@runtypelabs/persona/voice-worklet-player` for a jitter-buffered engine.
78
78
  */
79
79
  createPlaybackEngine?: () => VoicePlaybackEngine | Promise<VoicePlaybackEngine>;
80
+ /** See `voiceRecognition.provider.runtype.prewarmMode`. @default 'request' */
81
+ prewarmMode?: 'request' | 'attach';
82
+ /** See `voiceRecognition.provider.runtype.attachIdleMs`. @default 30000 */
83
+ attachIdleMs?: number;
80
84
  /** @deprecated No-op on the realtime path: the server's STT owns turn-taking. */
81
85
  pauseDuration?: number;
82
86
  /** @deprecated No-op on the realtime path: the server's STT owns turn-taking. */
@@ -137,6 +141,13 @@ interface VoiceProvider {
137
141
  deactivateBargeIn?(): Promise<void>;
138
142
  /** Stop playback / cancel in-flight request without starting recording */
139
143
  stopPlayback?(): void;
144
+ /**
145
+ * Warm the provider ahead of `startListening()` (the widget calls it on mic
146
+ * intent, see `voiceRecognition.prewarm`). Must be fire-and-forget: never
147
+ * throw, emit errors or status, or acquire the microphone. May be called
148
+ * repeatedly; throttle as needed.
149
+ */
150
+ prewarm?(): void;
140
151
  }
141
152
 
142
153
  declare function createVoiceProvider(config: VoiceConfig): VoiceProvider;
@@ -157,6 +168,8 @@ declare class RuntypeVoiceProvider implements VoiceProvider {
157
168
  private isSpeaking;
158
169
  private callGeneration;
159
170
  private intentionalClose;
171
+ private lastPrewarmAt;
172
+ private attached;
160
173
  private resultCallbacks;
161
174
  private errorCallbacks;
162
175
  private statusCallbacks;
@@ -165,8 +178,28 @@ declare class RuntypeVoiceProvider implements VoiceProvider {
165
178
  constructor(config: VoiceConfig["runtype"]);
166
179
  /** No-op: the WS session opens lazily in `startListening` (the "call"). */
167
180
  connect(): Promise<void>;
181
+ /**
182
+ * Warm the voice path ahead of the click. Fire-and-forget and throttled to
183
+ * once per {@link PREWARM_THROTTLE_MS}; never emits an error or a status.
184
+ */
185
+ prewarm(): void;
186
+ private requestPrewarm;
187
+ private attach;
188
+ private armAttachIdle;
189
+ /** Close and forget the prewarmed socket, if any. Silent by design. */
190
+ private releaseAttached;
191
+ /**
192
+ * Hand the prewarmed socket to the call, waiting up to
193
+ * {@link ATTACH_HANDSHAKE_TIMEOUT_MS} for a handshake still in flight.
194
+ * Resolves `null` when there is none, it stalled, or it dropped, so the
195
+ * caller opens a fresh socket.
196
+ */
197
+ private takeAttachedSocket;
198
+ private voiceSocketUrl;
168
199
  /** Start the call: acquire mic, open the WS, stream PCM until hang-up. */
169
200
  startListening(): Promise<void>;
201
+ /** Route a call socket's frames, errors, and close into this call. */
202
+ private bindCallSocket;
170
203
  /** End the call (hang up). */
171
204
  stopListening(): Promise<void>;
172
205
  /** Tear down the call and drop all callbacks (used by `cleanupVoice`). */
@@ -1 +1 @@
1
- var m=class{constructor(e=24e3,t={}){this.ctx=null;this.nextStartTime=0;this.activeSources=[];this.finishedCallbacks=[];this.startedCallbacks=[];this.playing=!1;this.streamEnded=!1;this.pendingCount=0;this.started=!1;this.userPaused=!1;this.pendingBuffers=[];this.pendingSamples=0;this.remainder=null;this.sampleRate=e;let i=Math.max(0,t.prebufferMs??0);this.waterlineSamples=Math.round(e*i/1e3),this.buffering=this.waterlineSamples>0}ensureContext(){if(!this.ctx){let t=typeof window<"u"?window:void 0;if(!t)throw new Error("AudioPlaybackManager requires a browser environment");let i=t.AudioContext||t.webkitAudioContext;this.ctx=new i({sampleRate:this.sampleRate})}let e=this.ctx;return e.state==="suspended"&&!this.userPaused&&e.resume(),e}enqueue(e){if(e.length===0)return;let t=e;if(this.remainder){let s=new Uint8Array(this.remainder.length+e.length);s.set(this.remainder),s.set(e,this.remainder.length),t=s,this.remainder=null}if(t.length%2!==0&&(this.remainder=new Uint8Array([t[t.length-1]]),t=t.subarray(0,t.length-1)),t.length===0)return;let i=this.pcmToFloat32(t);i.length!==0&&(this.buffering?(this.pendingBuffers.push(i),this.pendingSamples+=i.length,this.pendingSamples>=this.waterlineSamples&&this.releaseBuffer()):this.scheduleSamples(i))}markStreamEnd(){this.pendingBuffers.length>0&&this.releaseBuffer(),this.streamEnded=!0,this.checkFinished()}flush(){for(let e of this.activeSources)try{e.stop(),e.disconnect()}catch{}this.activeSources=[],this.pendingCount=0,this.nextStartTime=0,this.playing=!1,this.streamEnded=!1,this.finishedCallbacks=[],this.startedCallbacks=[],this.remainder=null,this.pendingBuffers=[],this.pendingSamples=0,this.buffering=this.waterlineSamples>0,this.started=!1}isPlaying(){return this.playing}onFinished(e){this.finishedCallbacks.push(e)}onStarted(e){this.startedCallbacks.push(e)}pause(){this.userPaused=!0,this.ctx&&this.ctx.state==="running"&&this.ctx.suspend()}resume(){this.userPaused=!1,this.ctx&&this.ctx.state==="suspended"&&this.ctx.resume()}async destroy(){this.flush(),this.ctx&&(await this.ctx.close(),this.ctx=null)}releaseBuffer(){this.buffering=!1;let e=this.pendingBuffers;this.pendingBuffers=[],this.pendingSamples=0;for(let t of e)this.scheduleSamples(t)}scheduleSamples(e){if(e.length===0)return;let t=this.ensureContext(),i=t.createBuffer(1,e.length,this.sampleRate);i.getChannelData(0).set(e);let s=t.createBufferSource();s.buffer=i,s.connect(t.destination);let n=t.currentTime;if(this.nextStartTime===0?this.nextStartTime=n:this.nextStartTime<n&&(this.nextStartTime=n,this.waterlineSamples>0&&(this.buffering=!0)),s.start(this.nextStartTime),this.nextStartTime+=i.duration,this.activeSources.push(s),this.pendingCount++,this.playing=!0,!this.started){this.started=!0;let a=this.startedCallbacks.slice();this.startedCallbacks=[];for(let l of a)l()}s.onended=()=>{let a=this.activeSources.indexOf(s);a!==-1&&this.activeSources.splice(a,1),this.pendingCount--,this.checkFinished()}}checkFinished(){if(this.streamEnded&&this.pendingCount<=0&&this.pendingBuffers.length===0){this.playing=!1,this.streamEnded=!1;let e=this.finishedCallbacks.slice();this.finishedCallbacks=[];for(let t of e)t()}}pcmToFloat32(e){let t=Math.floor(e.length/2),i=new Float32Array(t),s=new DataView(e.buffer,e.byteOffset,e.byteLength);for(let n=0;n<t;n++){let a=s.getInt16(n*2,!0);i[n]=a/32768}return i}};var g=16e3,w=24e3,S=4096,C=4,k=1380533830;function E(r){return r.byteLength>=44&&new DataView(r).getUint32(0,!1)===k?new Uint8Array(r,44):new Uint8Array(r)}function x(r){let e=r.replace(/\/+$/,"");return/^wss?:\/\//i.test(e)?e:/^https?:\/\//i.test(e)?e.replace(/^http/i,"ws"):`${typeof window<"u"&&window.location?.protocol==="https:"?"wss:":"ws:"}//${e}`}var d=class{constructor(e){this.config=e;this.type="runtype";this.ws=null;this.captureContext=null;this.levelCallbacks=[];this.mediaStream=null;this.sourceNode=null;this.processor=null;this.playback=null;this.callLive=!1;this.isSpeaking=!1;this.callGeneration=0;this.intentionalClose=!1;this.resultCallbacks=[];this.errorCallbacks=[];this.statusCallbacks=[];this.transcriptCallbacks=[];this.metricsCallbacks=[]}async connect(){}async startListening(){if(this.callLive)return;let e=this.config?.agentId,t=this.config?.clientToken,i=this.config?.host;if(!e)throw new Error("Runtype voice requires an agentId");if(!t)throw new Error("Runtype voice requires a clientToken");if(!i)throw new Error("Runtype voice requires a host (or widget apiUrl)");let s=++this.callGeneration;this.intentionalClose=!1,this.callLive=!0;try{let n=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:g,channelCount:1,echoCancellation:!0}});if(s!==this.callGeneration){n.getTracks().forEach(o=>o.stop());return}this.mediaStream=n;let a=window.AudioContext||window.webkitAudioContext,l=new a({sampleRate:g});l.state==="suspended"&&await l.resume().catch(()=>{}),this.captureContext=l;let h=this.config?.createPlaybackEngine?await this.config.createPlaybackEngine():new m(w);if(s!==this.callGeneration){h.destroy(),n.getTracks().forEach(o=>o.stop()),l.close().catch(()=>{});return}this.playback=h,h.onFinished(()=>{s===this.callGeneration&&(this.isSpeaking=!1,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.emitStatus("listening"))});let f=`${x(i)}/ws/agents/${encodeURIComponent(e)}/voice`,c=new WebSocket(f,["runtype.bearer",t]);c.binaryType="arraybuffer",this.ws=c,c.onopen=()=>{s===this.callGeneration&&(this.emitStatus("listening"),this.startCapture(l,n,c,s))},c.onmessage=o=>this.handleMessage(o,s),c.onerror=()=>{s===this.callGeneration&&(this.emitError(new Error("Voice connection failed")),this.emitStatus("error"),this.cleanup())},c.onclose=o=>{if(this.intentionalClose){this.intentionalClose=!1;return}if(s===this.callGeneration){if(o.code!==1e3){let u=o.code?` (code ${o.code})`:"";this.emitError(new Error(`Voice connection closed${u}`)),this.emitStatus("error")}else this.emitStatus("idle");this.cleanup()}}}catch(n){throw this.cleanup(),this.emitError(n),this.emitStatus("error"),n}}async stopListening(){this.cleanup(),this.emitStatus("idle")}async disconnect(){this.cleanup(),this.emitStatus("disconnected"),this.resultCallbacks=[],this.errorCallbacks=[],this.statusCallbacks=[],this.transcriptCallbacks=[],this.metricsCallbacks=[]}stopPlayback(){this.playback&&this.playback.flush(),this.isSpeaking=!1,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.emitStatus("listening")}getInterruptionMode(){return"barge-in"}isBargeInActive(){return this.callLive}onLevel(e){this.levelCallbacks.push(e)}async deactivateBargeIn(){this.cleanup(),this.emitStatus("idle")}startCapture(e,t,i,s){let n=e.createMediaStreamSource(t);this.sourceNode=n;let a=e.createScriptProcessor(S,1,1);this.processor=a,a.onaudioprocess=l=>{if(s!==this.callGeneration||i.readyState!==WebSocket.OPEN)return;let h=l.inputBuffer.getChannelData(0),f=new Int16Array(h.length),c=0;for(let o=0;o<h.length;o++){let u=Math.max(-1,Math.min(1,h[o]));c+=u*u,f[o]=u<0?u*32768:u*32767}if(i.send(f.buffer),this.levelCallbacks.length>0){let o=Math.sqrt(c/h.length),u=Math.max(0,Math.min(1,o*C));for(let y of this.levelCallbacks)y(u)}},n.connect(a),a.connect(e.destination)}handleMessage(e,t){if(t!==this.callGeneration)return;if(e.data instanceof ArrayBuffer){this.handleAudioFrame(e.data,t);return}let i;try{i=JSON.parse(e.data)}catch{return}switch(i.type){case"transcript_interim":this.emitStatus("listening"),this.emitTranscript("user",i.text??"",!1);break;case"transcript_final":{let s=i.role==="assistant"?"assistant":"user";this.emitStatus(s==="user"?"processing":"speaking"),this.emitTranscript(s,i.text??"",!0);break}case"audio_end":this.playback?this.playback.markStreamEnd():(this.isSpeaking=!1,this.emitStatus("listening"));break;case"metrics":this.emitMetrics({llmMs:i.llm_ms,ttsMs:i.tts_ms,firstAudioMs:i.first_audio_ms,totalMs:i.total_ms});break;case"error":this.emitError(new Error(i.error||"Voice error")),this.emitStatus("error");break}}handleAudioFrame(e,t){if(t!==this.callGeneration||!this.playback)return;let i=E(e);i.length!==0&&(this.isSpeaking||(this.isSpeaking=!0,this.emitStatus("speaking")),this.playback.enqueue(i))}cleanup(){if(this.callGeneration+=1,this.callLive=!1,this.isSpeaking=!1,this.processor&&(this.processor.onaudioprocess=null,this.processor.disconnect(),this.processor=null),this.sourceNode&&(this.sourceNode.disconnect(),this.sourceNode=null),this.mediaStream&&(this.mediaStream.getTracks().forEach(e=>e.stop()),this.mediaStream=null),this.captureContext&&(this.captureContext.close().catch(()=>{}),this.captureContext=null),this.playback&&(this.playback.destroy(),this.playback=null),this.ws){this.intentionalClose=!0;try{this.ws.close(1e3,"client ended call")}catch{}this.ws=null}}onResult(e){this.resultCallbacks.push(e)}onError(e){this.errorCallbacks.push(e)}onStatusChange(e){this.statusCallbacks.push(e)}onTranscript(e){this.transcriptCallbacks.push(e)}onMetrics(e){this.metricsCallbacks.push(e)}emitStatus(e){this.statusCallbacks.forEach(t=>t(e))}emitError(e){this.errorCallbacks.forEach(t=>t(e))}emitTranscript(e,t,i){this.transcriptCallbacks.forEach(s=>s(e,t,i))}emitMetrics(e){this.metricsCallbacks.forEach(t=>t(e))}};var p=class{constructor(e={}){this.config=e;this.type="browser";this.recognition=null;this.resultCallbacks=[];this.errorCallbacks=[];this.statusCallbacks=[];this.isListening=!1;this.w=typeof window<"u"?window:void 0}async connect(){this.statusCallbacks.forEach(e=>e("connected"))}async startListening(){try{if(this.isListening)throw new Error("Already listening");if(!this.w)throw new Error("Window object not available");let e=this.w.SpeechRecognition||this.w.webkitSpeechRecognition;if(!e)throw new Error("Browser speech recognition not supported");this.recognition=new e,this.recognition.lang=this.config?.language||"en-US",this.recognition.continuous=this.config?.continuous||!1,this.recognition.interimResults=!0,this.recognition.onresult=t=>{let i=Array.from(t.results).map(n=>n[0]).map(n=>n.transcript).join(""),s=t.results[t.results.length-1].isFinal;this.resultCallbacks.forEach(n=>n({text:i,confidence:s?.8:.5,provider:"browser"})),s&&!this.config?.continuous&&this.stopListening()},this.recognition.onerror=t=>{this.errorCallbacks.forEach(i=>i(new Error(t.error))),this.statusCallbacks.forEach(i=>i("error"))},this.recognition.onstart=()=>{this.isListening=!0,this.statusCallbacks.forEach(t=>t("listening"))},this.recognition.onend=()=>{this.isListening=!1,this.statusCallbacks.forEach(t=>t("idle"))},this.recognition.start()}catch(e){throw this.errorCallbacks.forEach(t=>t(e)),this.statusCallbacks.forEach(t=>t("error")),e}}async stopListening(){this.recognition&&(this.recognition.stop(),this.recognition=null),this.isListening=!1,this.statusCallbacks.forEach(e=>e("idle"))}onResult(e){this.resultCallbacks.push(e)}onError(e){this.errorCallbacks.push(e)}onStatusChange(e){this.statusCallbacks.push(e)}async disconnect(){await this.stopListening(),this.statusCallbacks.forEach(e=>e("disconnected"))}static isSupported(){return"SpeechRecognition"in window||"webkitSpeechRecognition"in window}};function v(r){switch(r.type){case"runtype":if(!r.runtype)throw new Error("Runtype voice provider requires configuration");return new d(r.runtype);case"browser":if(!p.isSupported())throw new Error("Browser speech recognition not supported");return new p(r.browser||{});case"custom":{let e=r.custom;if(!e)throw new Error("Custom voice provider requires a `custom` provider instance or factory");let t=typeof e=="function"?e():e;if(!t||typeof t.startListening!="function")throw new Error("Custom voice provider `custom` must be a VoiceProvider (or a factory returning one)");return t}default:throw new Error(`Unknown voice provider type: ${r.type}`)}}function b(r){if(r?.type==="custom"&&r.custom)return v({type:"custom",custom:r.custom});if(r?.type==="runtype"&&r.runtype)return v({type:"runtype",runtype:r.runtype});if(p.isSupported())return v({type:"browser",browser:r?.browser||{language:"en-US"}});throw new Error("No supported voice providers available")}function A(r){try{return b(r),!0}catch{return!1}}export{p as BrowserVoiceProvider,d as RuntypeVoiceProvider,b as createBestAvailableVoiceProvider,v as createVoiceProvider,A as isVoiceSupported};
1
+ var m=class{constructor(t=24e3,e={}){this.ctx=null;this.nextStartTime=0;this.activeSources=[];this.finishedCallbacks=[];this.startedCallbacks=[];this.playing=!1;this.streamEnded=!1;this.pendingCount=0;this.started=!1;this.userPaused=!1;this.pendingBuffers=[];this.pendingSamples=0;this.remainder=null;this.sampleRate=t;let i=Math.max(0,e.prebufferMs??0);this.waterlineSamples=Math.round(t*i/1e3),this.buffering=this.waterlineSamples>0}ensureContext(){if(!this.ctx){let e=typeof window<"u"?window:void 0;if(!e)throw new Error("AudioPlaybackManager requires a browser environment");let i=e.AudioContext||e.webkitAudioContext;this.ctx=new i({sampleRate:this.sampleRate})}let t=this.ctx;return t.state==="suspended"&&!this.userPaused&&t.resume(),t}enqueue(t){if(t.length===0)return;let e=t;if(this.remainder){let s=new Uint8Array(this.remainder.length+t.length);s.set(this.remainder),s.set(t,this.remainder.length),e=s,this.remainder=null}if(e.length%2!==0&&(this.remainder=new Uint8Array([e[e.length-1]]),e=e.subarray(0,e.length-1)),e.length===0)return;let i=this.pcmToFloat32(e);i.length!==0&&(this.buffering?(this.pendingBuffers.push(i),this.pendingSamples+=i.length,this.pendingSamples>=this.waterlineSamples&&this.releaseBuffer()):this.scheduleSamples(i))}markStreamEnd(){this.pendingBuffers.length>0&&this.releaseBuffer(),this.streamEnded=!0,this.checkFinished()}flush(){for(let t of this.activeSources)try{t.stop(),t.disconnect()}catch{}this.activeSources=[],this.pendingCount=0,this.nextStartTime=0,this.playing=!1,this.streamEnded=!1,this.finishedCallbacks=[],this.startedCallbacks=[],this.remainder=null,this.pendingBuffers=[],this.pendingSamples=0,this.buffering=this.waterlineSamples>0,this.started=!1}isPlaying(){return this.playing}onFinished(t){this.finishedCallbacks.push(t)}onStarted(t){this.startedCallbacks.push(t)}pause(){this.userPaused=!0,this.ctx&&this.ctx.state==="running"&&this.ctx.suspend()}resume(){this.userPaused=!1,this.ctx&&this.ctx.state==="suspended"&&this.ctx.resume()}async destroy(){this.flush(),this.ctx&&(await this.ctx.close(),this.ctx=null)}releaseBuffer(){this.buffering=!1;let t=this.pendingBuffers;this.pendingBuffers=[],this.pendingSamples=0;for(let e of t)this.scheduleSamples(e)}scheduleSamples(t){if(t.length===0)return;let e=this.ensureContext(),i=e.createBuffer(1,t.length,this.sampleRate);i.getChannelData(0).set(t);let s=e.createBufferSource();s.buffer=i,s.connect(e.destination);let n=e.currentTime;if(this.nextStartTime===0?this.nextStartTime=n:this.nextStartTime<n&&(this.nextStartTime=n,this.waterlineSamples>0&&(this.buffering=!0)),s.start(this.nextStartTime),this.nextStartTime+=i.duration,this.activeSources.push(s),this.pendingCount++,this.playing=!0,!this.started){this.started=!0;let l=this.startedCallbacks.slice();this.startedCallbacks=[];for(let c of l)c()}s.onended=()=>{let l=this.activeSources.indexOf(s);l!==-1&&this.activeSources.splice(l,1),this.pendingCount--,this.checkFinished()}}checkFinished(){if(this.streamEnded&&this.pendingCount<=0&&this.pendingBuffers.length===0){this.playing=!1,this.streamEnded=!1;let t=this.finishedCallbacks.slice();this.finishedCallbacks=[];for(let e of t)e()}}pcmToFloat32(t){let e=Math.floor(t.length/2),i=new Float32Array(e),s=new DataView(t.buffer,t.byteOffset,t.byteLength);for(let n=0;n<e;n++){let l=s.getInt16(n*2,!0);i[n]=l/32768}return i}};var g=16e3,S=24e3,k=4096,C=4,E=1380533830,A=3e4,x=5e3,P=3e4,T=3e4,M=6e5;function V(r){return r.byteLength>=44&&new DataView(r).getUint32(0,!1)===E?new Uint8Array(r,44):new Uint8Array(r)}function b(r){let t=r.replace(/\/+$/,"");return/^wss?:\/\//i.test(t)?t:/^https?:\/\//i.test(t)?t.replace(/^http/i,"ws"):`${typeof window<"u"&&window.location?.protocol==="https:"?"wss:":"ws:"}//${t}`}function R(r){return b(r).replace(/^ws/i,"http")}function _(r){return typeof r!="number"||!Number.isFinite(r)?P:Math.min(M,Math.max(T,Math.round(r)))}var f=class{constructor(t){this.config=t;this.type="runtype";this.ws=null;this.captureContext=null;this.levelCallbacks=[];this.mediaStream=null;this.sourceNode=null;this.processor=null;this.playback=null;this.callLive=!1;this.isSpeaking=!1;this.callGeneration=0;this.intentionalClose=!1;this.lastPrewarmAt=Number.NEGATIVE_INFINITY;this.attached=null;this.resultCallbacks=[];this.errorCallbacks=[];this.statusCallbacks=[];this.transcriptCallbacks=[];this.metricsCallbacks=[]}async connect(){}prewarm(){if(this.callLive||this.attached)return;let t=this.config?.agentId,e=this.config?.clientToken,i=this.config?.host;if(!t||!e||!i)return;let s=Date.now();if(!(s-this.lastPrewarmAt<A)){this.lastPrewarmAt=s;try{this.config?.prewarmMode==="attach"?this.attach(i,t,e):this.requestPrewarm(i,t,e)}catch{}}}requestPrewarm(t,e,i){if(typeof fetch!="function")return;let s=`${R(t)}/v1/client/agents/${encodeURIComponent(e)}/voice/prewarm`;fetch(s,{method:"POST",headers:{Authorization:`Bearer ${i}`},keepalive:!0}).catch(()=>{})}attach(t,e,i){let s=new URLSearchParams({clientCapabilities:"attach"}),n=this.config?.attachIdleMs,l=_(n);n!==void 0&&s.set("attachIdleMs",String(l));let c=new WebSocket(`${this.voiceSocketUrl(t,e)}?${s}`,["runtype.bearer","runtype.attach",i]);c.binaryType="arraybuffer";let u=()=>{},d=new Promise(o=>{u=o}),a={ws:c,state:"connecting",ready:d,settle:u,openedAt:0,idleTimer:void 0};this.attached=a,c.onopen=()=>{this.attached===a&&(a.state=c.protocol==="runtype.attach"?"attached":"live",a.openedAt=Date.now(),this.armAttachIdle(a,l),a.state==="attached"&&c.send('{"type":"ping"}'),a.settle())},c.onmessage=o=>{if(this.attached!==a||typeof o.data!="string")return;let h;try{h=JSON.parse(o.data)}catch{return}h.type==="attached"&&typeof h.idleMs=="number"&&this.armAttachIdle(a,h.idleMs-(Date.now()-a.openedAt))},c.onerror=()=>{},c.onclose=()=>{this.attached===a&&this.releaseAttached()}}armAttachIdle(t,e){clearTimeout(t.idleTimer),t.idleTimer=setTimeout(()=>{this.attached===t&&this.releaseAttached()},Math.max(0,e))}releaseAttached(){let t=this.attached;if(!t)return;this.attached=null,clearTimeout(t.idleTimer);let{ws:e}=t;if(e.onopen=null,e.onmessage=null,e.onerror=null,e.onclose=null,t.settle(),e.readyState===WebSocket.CONNECTING||e.readyState===WebSocket.OPEN)try{e.close(1e3,"prewarm released")}catch{}}async takeAttachedSocket(){let t=this.attached;if(!t)return null;if(t.state==="connecting"){let e;await Promise.race([t.ready,new Promise(i=>{e=setTimeout(i,x)})]),clearTimeout(e)}return this.attached!==t?null:t.state==="connecting"||t.ws.readyState!==WebSocket.OPEN?(this.releaseAttached(),null):(this.attached=null,clearTimeout(t.idleTimer),{ws:t.ws,live:t.state==="live"})}voiceSocketUrl(t,e){return`${b(t)}/ws/agents/${encodeURIComponent(e)}/voice`}async startListening(){if(this.callLive)return;let t=this.config?.agentId,e=this.config?.clientToken,i=this.config?.host;if(!t)throw new Error("Runtype voice requires an agentId");if(!e)throw new Error("Runtype voice requires a clientToken");if(!i)throw new Error("Runtype voice requires a host (or widget apiUrl)");let s=++this.callGeneration;this.intentionalClose=!1,this.callLive=!0;try{let n=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:g,channelCount:1,echoCancellation:!0}});if(s!==this.callGeneration){n.getTracks().forEach(o=>o.stop());return}this.mediaStream=n;let l=window.AudioContext||window.webkitAudioContext,c=new l({sampleRate:g});c.state==="suspended"&&await c.resume().catch(()=>{}),this.captureContext=c;let u=this.config?.createPlaybackEngine?await this.config.createPlaybackEngine():new m(S);if(s!==this.callGeneration){u.destroy(),n.getTracks().forEach(o=>o.stop()),c.close().catch(()=>{});return}this.playback=u,u.onFinished(()=>{s===this.callGeneration&&(this.isSpeaking=!1,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.emitStatus("listening"))});let d=await this.takeAttachedSocket();if(s!==this.callGeneration){d?.ws.close(1e3,"client ended call");return}if(d){let{ws:o}=d;this.ws=o,this.bindCallSocket(o,s),d.live||o.send('{"type":"start"}'),this.emitStatus("listening"),this.startCapture(c,n,o,s);return}let a=new WebSocket(this.voiceSocketUrl(i,t),["runtype.bearer",e]);a.binaryType="arraybuffer",this.ws=a,a.onopen=()=>{s===this.callGeneration&&(this.emitStatus("listening"),this.startCapture(c,n,a,s))},this.bindCallSocket(a,s)}catch(n){throw this.cleanup(),this.emitError(n),this.emitStatus("error"),n}}bindCallSocket(t,e){t.onmessage=i=>this.handleMessage(i,e),t.onerror=()=>{e===this.callGeneration&&(this.emitError(new Error("Voice connection failed")),this.emitStatus("error"),this.cleanup())},t.onclose=i=>{if(this.intentionalClose){this.intentionalClose=!1;return}if(e===this.callGeneration){if(i.code!==1e3){let s=i.code?` (code ${i.code})`:"";this.emitError(new Error(`Voice connection closed${s}`)),this.emitStatus("error")}else this.emitStatus("idle");this.cleanup()}}}async stopListening(){this.cleanup(),this.emitStatus("idle")}async disconnect(){this.cleanup(),this.emitStatus("disconnected"),this.resultCallbacks=[],this.errorCallbacks=[],this.statusCallbacks=[],this.transcriptCallbacks=[],this.metricsCallbacks=[]}stopPlayback(){this.playback&&this.playback.flush(),this.isSpeaking=!1,this.ws&&this.ws.readyState===WebSocket.OPEN&&this.emitStatus("listening")}getInterruptionMode(){return"barge-in"}isBargeInActive(){return this.callLive}onLevel(t){this.levelCallbacks.push(t)}async deactivateBargeIn(){this.cleanup(),this.emitStatus("idle")}startCapture(t,e,i,s){let n=t.createMediaStreamSource(e);this.sourceNode=n;let l=t.createScriptProcessor(k,1,1);this.processor=l,l.onaudioprocess=c=>{if(s!==this.callGeneration||i.readyState!==WebSocket.OPEN)return;let u=c.inputBuffer.getChannelData(0),d=new Int16Array(u.length),a=0;for(let o=0;o<u.length;o++){let h=Math.max(-1,Math.min(1,u[o]));a+=h*h,d[o]=h<0?h*32768:h*32767}if(i.send(d.buffer),this.levelCallbacks.length>0){let o=Math.sqrt(a/u.length),h=Math.max(0,Math.min(1,o*C));for(let w of this.levelCallbacks)w(h)}},n.connect(l),l.connect(t.destination)}handleMessage(t,e){if(e!==this.callGeneration)return;if(t.data instanceof ArrayBuffer){this.handleAudioFrame(t.data,e);return}let i;try{i=JSON.parse(t.data)}catch{return}switch(i.type){case"transcript_interim":this.emitStatus("listening"),this.emitTranscript("user",i.text??"",!1);break;case"transcript_final":{let s=i.role==="assistant"?"assistant":"user";this.emitStatus(s==="user"?"processing":"speaking"),this.emitTranscript(s,i.text??"",!0);break}case"audio_end":this.playback?this.playback.markStreamEnd():(this.isSpeaking=!1,this.emitStatus("listening"));break;case"metrics":this.emitMetrics({llmMs:i.llm_ms,ttsMs:i.tts_ms,firstAudioMs:i.first_audio_ms,totalMs:i.total_ms});break;case"error":this.emitError(new Error(i.error||"Voice error")),this.emitStatus("error");break}}handleAudioFrame(t,e){if(e!==this.callGeneration||!this.playback)return;let i=V(t);i.length!==0&&(this.isSpeaking||(this.isSpeaking=!0,this.emitStatus("speaking")),this.playback.enqueue(i))}cleanup(){if(this.callGeneration+=1,this.callLive=!1,this.isSpeaking=!1,this.releaseAttached(),this.processor&&(this.processor.onaudioprocess=null,this.processor.disconnect(),this.processor=null),this.sourceNode&&(this.sourceNode.disconnect(),this.sourceNode=null),this.mediaStream&&(this.mediaStream.getTracks().forEach(t=>t.stop()),this.mediaStream=null),this.captureContext&&(this.captureContext.close().catch(()=>{}),this.captureContext=null),this.playback&&(this.playback.destroy(),this.playback=null),this.ws){this.intentionalClose=!0;try{this.ws.close(1e3,"client ended call")}catch{}this.ws=null}}onResult(t){this.resultCallbacks.push(t)}onError(t){this.errorCallbacks.push(t)}onStatusChange(t){this.statusCallbacks.push(t)}onTranscript(t){this.transcriptCallbacks.push(t)}onMetrics(t){this.metricsCallbacks.push(t)}emitStatus(t){this.statusCallbacks.forEach(e=>e(t))}emitError(t){this.errorCallbacks.forEach(e=>e(t))}emitTranscript(t,e,i){this.transcriptCallbacks.forEach(s=>s(t,e,i))}emitMetrics(t){this.metricsCallbacks.forEach(e=>e(t))}};var p=class{constructor(t={}){this.config=t;this.type="browser";this.recognition=null;this.resultCallbacks=[];this.errorCallbacks=[];this.statusCallbacks=[];this.isListening=!1;this.w=typeof window<"u"?window:void 0}async connect(){this.statusCallbacks.forEach(t=>t("connected"))}async startListening(){try{if(this.isListening)throw new Error("Already listening");if(!this.w)throw new Error("Window object not available");let t=this.w.SpeechRecognition||this.w.webkitSpeechRecognition;if(!t)throw new Error("Browser speech recognition not supported");this.recognition=new t,this.recognition.lang=this.config?.language||"en-US",this.recognition.continuous=this.config?.continuous||!1,this.recognition.interimResults=!0,this.recognition.onresult=e=>{let i=Array.from(e.results).map(n=>n[0]).map(n=>n.transcript).join(""),s=e.results[e.results.length-1].isFinal;this.resultCallbacks.forEach(n=>n({text:i,confidence:s?.8:.5,provider:"browser"})),s&&!this.config?.continuous&&this.stopListening()},this.recognition.onerror=e=>{this.errorCallbacks.forEach(i=>i(new Error(e.error))),this.statusCallbacks.forEach(i=>i("error"))},this.recognition.onstart=()=>{this.isListening=!0,this.statusCallbacks.forEach(e=>e("listening"))},this.recognition.onend=()=>{this.isListening=!1,this.statusCallbacks.forEach(e=>e("idle"))},this.recognition.start()}catch(t){throw this.errorCallbacks.forEach(e=>e(t)),this.statusCallbacks.forEach(e=>e("error")),t}}async stopListening(){this.recognition&&(this.recognition.stop(),this.recognition=null),this.isListening=!1,this.statusCallbacks.forEach(t=>t("idle"))}onResult(t){this.resultCallbacks.push(t)}onError(t){this.errorCallbacks.push(t)}onStatusChange(t){this.statusCallbacks.push(t)}async disconnect(){await this.stopListening(),this.statusCallbacks.forEach(t=>t("disconnected"))}static isSupported(){return"SpeechRecognition"in window||"webkitSpeechRecognition"in window}};function v(r){switch(r.type){case"runtype":if(!r.runtype)throw new Error("Runtype voice provider requires configuration");return new f(r.runtype);case"browser":if(!p.isSupported())throw new Error("Browser speech recognition not supported");return new p(r.browser||{});case"custom":{let t=r.custom;if(!t)throw new Error("Custom voice provider requires a `custom` provider instance or factory");let e=typeof t=="function"?t():t;if(!e||typeof e.startListening!="function")throw new Error("Custom voice provider `custom` must be a VoiceProvider (or a factory returning one)");return e}default:throw new Error(`Unknown voice provider type: ${r.type}`)}}function y(r){if(r?.type==="custom"&&r.custom)return v({type:"custom",custom:r.custom});if(r?.type==="runtype"&&r.runtype)return v({type:"runtype",runtype:r.runtype});if(p.isSupported())return v({type:"browser",browser:r?.browser||{language:"en-US"}});throw new Error("No supported voice providers available")}function L(r){try{return y(r),!0}catch{return!1}}export{p as BrowserVoiceProvider,f as RuntypeVoiceProvider,y as createBestAvailableVoiceProvider,v as createVoiceProvider,L as isVoiceSupported};