@qualified-salesforce/web 0.0.1-oidc-bootstrap
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/41842993ec71d6628e4949e9a22b146b.svg +3 -0
- package/7271f3433ed82fc7f56adcd9e30275f4.svg +17 -0
- package/LICENSE.txt +82 -0
- package/components/helpers/entriesStringValidations.js +69 -0
- package/components/helpers/languages.js +32 -0
- package/components/shared/assertUnreachable.js +5 -0
- package/components/shared/controls/imageCropperHelpers.js +43 -0
- package/components/shared/meetingLoadingProgressSpec.js +116 -0
- package/constants.generated.js +9883 -0
- package/css/widget/multimodal_v2/host.split-css.css +162 -0
- package/css/widget/multimodal_v2/index.split-css.panda.css +5240 -0
- package/css/widget/widget.split-css.css +100 -0
- package/fonts/inter/Inter-Black.woff +0 -0
- package/fonts/inter/Inter-Black.woff2 +0 -0
- package/fonts/inter/Inter-BlackItalic.woff +0 -0
- package/fonts/inter/Inter-BlackItalic.woff2 +0 -0
- package/fonts/inter/Inter-Bold.woff +0 -0
- package/fonts/inter/Inter-Bold.woff2 +0 -0
- package/fonts/inter/Inter-BoldItalic.woff +0 -0
- package/fonts/inter/Inter-BoldItalic.woff2 +0 -0
- package/fonts/inter/Inter-ExtraBold.woff +0 -0
- package/fonts/inter/Inter-ExtraBold.woff2 +0 -0
- package/fonts/inter/Inter-ExtraBoldItalic.woff +0 -0
- package/fonts/inter/Inter-ExtraBoldItalic.woff2 +0 -0
- package/fonts/inter/Inter-ExtraLight.woff +0 -0
- package/fonts/inter/Inter-ExtraLight.woff2 +0 -0
- package/fonts/inter/Inter-ExtraLightItalic.woff +0 -0
- package/fonts/inter/Inter-ExtraLightItalic.woff2 +0 -0
- package/fonts/inter/Inter-Italic.woff +0 -0
- package/fonts/inter/Inter-Italic.woff2 +0 -0
- package/fonts/inter/Inter-Light.woff +0 -0
- package/fonts/inter/Inter-Light.woff2 +0 -0
- package/fonts/inter/Inter-LightItalic.woff +0 -0
- package/fonts/inter/Inter-LightItalic.woff2 +0 -0
- package/fonts/inter/Inter-Medium.woff +0 -0
- package/fonts/inter/Inter-Medium.woff2 +0 -0
- package/fonts/inter/Inter-MediumItalic.woff +0 -0
- package/fonts/inter/Inter-MediumItalic.woff2 +0 -0
- package/fonts/inter/Inter-Regular.woff +0 -0
- package/fonts/inter/Inter-Regular.woff2 +0 -0
- package/fonts/inter/Inter-SemiBold.woff +0 -0
- package/fonts/inter/Inter-SemiBold.woff2 +0 -0
- package/fonts/inter/Inter-SemiBoldItalic.woff +0 -0
- package/fonts/inter/Inter-SemiBoldItalic.woff2 +0 -0
- package/fonts/inter/Inter-Thin.woff +0 -0
- package/fonts/inter/Inter-Thin.woff2 +0 -0
- package/fonts/inter/Inter-ThinItalic.woff +0 -0
- package/fonts/inter/Inter-ThinItalic.woff2 +0 -0
- package/fonts/inter/Inter-italic.var.woff2 +0 -0
- package/fonts/inter/Inter-roman.var.woff2 +0 -0
- package/images/close.js +63 -0
- package/images/logo.js +22 -0
- package/js/4101.js +19721 -0
- package/js/daily-esm.js +9948 -0
- package/js/date-fns.js +427 -0
- package/js/multimodal_v2.js +1653 -0
- package/js/runtime.js +109 -0
- package/media/Inter-Black.796be8d8bf.woff2 +0 -0
- package/media/Inter-Black.b254104c56.woff +0 -0
- package/media/Inter-BlackItalic.70306b0eae.woff +0 -0
- package/media/Inter-BlackItalic.752f1e0aaa.woff2 +0 -0
- package/media/Inter-Bold.579e0f95ca.woff2 +0 -0
- package/media/Inter-Bold.b1234477a5.woff +0 -0
- package/media/Inter-BoldItalic.3d0d804323.woff +0 -0
- package/media/Inter-BoldItalic.828a494e04.woff2 +0 -0
- package/media/Inter-ExtraBold.208a9e6c99.woff +0 -0
- package/media/Inter-ExtraBold.d65f028165.woff2 +0 -0
- package/media/Inter-ExtraBoldItalic.30429962ec.woff +0 -0
- package/media/Inter-ExtraBoldItalic.8c7eac5ce3.woff2 +0 -0
- package/media/Inter-ExtraLight.15c082a57d.woff +0 -0
- package/media/Inter-ExtraLight.6824177d1e.woff2 +0 -0
- package/media/Inter-ExtraLightItalic.05138dddf3.woff2 +0 -0
- package/media/Inter-ExtraLightItalic.3beee54f59.woff +0 -0
- package/media/Inter-Italic.9beab78452.woff +0 -0
- package/media/Inter-Italic.d593a8df79.woff2 +0 -0
- package/media/Inter-Light.27083fa637.woff2 +0 -0
- package/media/Inter-Light.91dfddd842.woff +0 -0
- package/media/Inter-LightItalic.0a783b89b3.woff2 +0 -0
- package/media/Inter-LightItalic.f8ccf2813f.woff +0 -0
- package/media/Inter-Medium.dc792b508e.woff2 +0 -0
- package/media/Inter-Medium.ec7dd2d9d8.woff +0 -0
- package/media/Inter-MediumItalic.17301e5b40.woff +0 -0
- package/media/Inter-MediumItalic.84d3493a9f.woff2 +0 -0
- package/media/Inter-Regular.f1f0c35b32.woff2 +0 -0
- package/media/Inter-Regular.f356e84aa0.woff +0 -0
- package/media/Inter-SemiBold.55027e4724.woff +0 -0
- package/media/Inter-SemiBold.fcb100c760.woff2 +0 -0
- package/media/Inter-SemiBoldItalic.ab9105ed3b.woff +0 -0
- package/media/Inter-SemiBoldItalic.d90f295d0c.woff2 +0 -0
- package/media/Inter-Thin.16325d9b0f.woff +0 -0
- package/media/Inter-Thin.dafaa44d99.woff2 +0 -0
- package/media/Inter-ThinItalic.4fc662b3cf.woff2 +0 -0
- package/media/Inter-ThinItalic.d3d8994788.woff +0 -0
- package/media/Inter-italic.var.958a0b9742.woff2 +0 -0
- package/media/Inter-roman.var.b2129c009c.woff2 +0 -0
- package/package.json +20 -0
- package/packs/widget/api/docking/docking_host.js +97 -0
- package/packs/widget/api/docking/index.js +294 -0
- package/packs/widget/api/docking/shared.js +164 -0
- package/packs/widget/api/docking/viewport_lock.js +488 -0
- package/packs/widget/api/docking/viewport_resize.js +1203 -0
- package/packs/widget/esm/fonts/Inter-Black.796be8d8bf.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-Black.b254104c56.woff +0 -0
- package/packs/widget/esm/fonts/Inter-BlackItalic.70306b0eae.woff +0 -0
- package/packs/widget/esm/fonts/Inter-BlackItalic.752f1e0aaa.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-Bold.579e0f95ca.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-Bold.b1234477a5.woff +0 -0
- package/packs/widget/esm/fonts/Inter-BoldItalic.3d0d804323.woff +0 -0
- package/packs/widget/esm/fonts/Inter-BoldItalic.828a494e04.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-ExtraBold.208a9e6c99.woff +0 -0
- package/packs/widget/esm/fonts/Inter-ExtraBold.d65f028165.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-ExtraBoldItalic.30429962ec.woff +0 -0
- package/packs/widget/esm/fonts/Inter-ExtraBoldItalic.8c7eac5ce3.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-ExtraLight.15c082a57d.woff +0 -0
- package/packs/widget/esm/fonts/Inter-ExtraLight.6824177d1e.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-ExtraLightItalic.05138dddf3.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-ExtraLightItalic.3beee54f59.woff +0 -0
- package/packs/widget/esm/fonts/Inter-Italic.9beab78452.woff +0 -0
- package/packs/widget/esm/fonts/Inter-Italic.d593a8df79.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-Light.27083fa637.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-Light.91dfddd842.woff +0 -0
- package/packs/widget/esm/fonts/Inter-LightItalic.0a783b89b3.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-LightItalic.f8ccf2813f.woff +0 -0
- package/packs/widget/esm/fonts/Inter-Medium.dc792b508e.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-Medium.ec7dd2d9d8.woff +0 -0
- package/packs/widget/esm/fonts/Inter-MediumItalic.17301e5b40.woff +0 -0
- package/packs/widget/esm/fonts/Inter-MediumItalic.84d3493a9f.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-Regular.f1f0c35b32.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-Regular.f356e84aa0.woff +0 -0
- package/packs/widget/esm/fonts/Inter-SemiBold.55027e4724.woff +0 -0
- package/packs/widget/esm/fonts/Inter-SemiBold.fcb100c760.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-SemiBoldItalic.ab9105ed3b.woff +0 -0
- package/packs/widget/esm/fonts/Inter-SemiBoldItalic.d90f295d0c.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-Thin.16325d9b0f.woff +0 -0
- package/packs/widget/esm/fonts/Inter-Thin.dafaa44d99.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-ThinItalic.4fc662b3cf.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-ThinItalic.d3d8994788.woff +0 -0
- package/packs/widget/esm/fonts/Inter-italic.var.958a0b9742.woff2 +0 -0
- package/packs/widget/esm/fonts/Inter-roman.var.b2129c009c.woff2 +0 -0
- package/packs/widget/esm/index.js +183 -0
- package/packs/widget/esm/qualified.css +5502 -0
- package/routes/app/offers/scripts/OfferMainScript.js +154 -0
- package/routes/app/offers/scripts/constants.js +71 -0
- package/routes/app/offers/scripts/main/ButtonManager.js +65 -0
- package/routes/app/offers/scripts/main/FormManager.js +325 -0
- package/routes/app/offers/scripts/main/MainContext.js +19 -0
- package/routes/app/offers/scripts/main/OfferManager.js +91 -0
- package/routes/app/offers/scripts/main/TriggerManager.js +125 -0
- package/routes/app/offers/scripts/rendering/ContentManager.js +53 -0
- package/routes/app/offers/scripts/rendering/LocationManager.js +83 -0
- package/routes/app/offers/scripts/rendering/RenderedOffer.js +188 -0
- package/routes/app/offers/scripts/rendering/Renderer.js +69 -0
- package/routes/app/offers/scripts/rendering/RenderingInitializer.js +62 -0
- package/routes/app/offers/scripts/rendering/animations/AnimationManager.js +31 -0
- package/routes/app/offers/scripts/rendering/animations/AnimationManagerFooter.js +81 -0
- package/routes/app/offers/scripts/rendering/animations/AnimationManagerHeader.js +81 -0
- package/routes/app/offers/scripts/rendering/animations/AnimationManagerModal.js +117 -0
- package/routes/app/offers/scripts/rendering/animations/AnimationManagerSidebar.js +75 -0
- package/routes/app/offers/scripts/rendering/animations/AnimationResult.js +43 -0
- package/routes/app/offers/scripts/rendering/animations/Animator.js +78 -0
- package/routes/app/offers/scripts/rendering/animations/BaseAnimationManager.js +25 -0
- package/routes/app/offers/scripts/rendering/lib/BaseComponent.js +106 -0
- package/routes/app/offers/scripts/rendering/lib/FormComponent.js +134 -0
- package/routes/app/offers/scripts/rendering/lib/OfferComponent.js +144 -0
- package/routes/app/offers/scripts/rendering/lib/ScrollbarWidth.js +27 -0
- package/routes/app/offers/scripts/rendering/lib/decorators.js +20 -0
- package/routes/app/offers/scripts/rendering/locations/CSSVarsManager.js +66 -0
- package/routes/app/offers/scripts/rendering/locations/ElementResizeObserver.js +49 -0
- package/routes/app/offers/scripts/rendering/locations/footer.js +53 -0
- package/routes/app/offers/scripts/rendering/locations/header/AsyncBatchProcessor.js +70 -0
- package/routes/app/offers/scripts/rendering/locations/header/CSSTransitionObserver.js +182 -0
- package/routes/app/offers/scripts/rendering/locations/header/ElementStyleChangeObserver.js +138 -0
- package/routes/app/offers/scripts/rendering/locations/header/ExternalStyleManager.js +110 -0
- package/routes/app/offers/scripts/rendering/locations/header/Header.js +353 -0
- package/routes/app/offers/scripts/rendering/locations/header/InlineStyleManager.js +105 -0
- package/routes/app/offers/scripts/rendering/locations/header/MatchingElementsObserver.js +107 -0
- package/routes/app/offers/scripts/rendering/locations/header/NewElementsObserver.js +129 -0
- package/routes/app/offers/scripts/rendering/locations/header/RuleWrapper.js +46 -0
- package/routes/app/offers/scripts/rendering/locations/header/SelectorObservable.js +23 -0
- package/routes/app/offers/scripts/rendering/locations/header/SelectorObserver.js +61 -0
- package/routes/app/offers/scripts/rendering/locations/header/ShiftedElementsManager.js +45 -0
- package/routes/app/offers/scripts/rendering/locations/header/StyleElementManager.js +37 -0
- package/routes/app/offers/scripts/rendering/locations/header/StyleElementWrapper.js +62 -0
- package/routes/app/offers/scripts/rendering/locations/header/StyleSelectorObserver/ObservedSelectorValue.js +45 -0
- package/routes/app/offers/scripts/rendering/locations/header/StyleSelectorObserver/ObservedSelectors.js +62 -0
- package/routes/app/offers/scripts/rendering/locations/header/StyleSelectorObserver/StyleSelectorObserver.js +294 -0
- package/routes/app/offers/scripts/rendering/locations/header/StyleSelectorObserver/constants.js +21 -0
- package/routes/app/offers/scripts/rendering/locations/header/WindowResizeObserver.js +43 -0
- package/routes/app/offers/scripts/rendering/locations/header/utils.js +25 -0
- package/routes/app/offers/scripts/rendering/locations/popup.js +66 -0
- package/routes/app/offers/scripts/rendering/locations/sidebar.js +21 -0
- package/routes/app/offers/scripts/rendering/variations/ButtonBanner.js +105 -0
- package/routes/app/offers/scripts/rendering/variations/ButtonModal.js +114 -0
- package/routes/app/offers/scripts/rendering/variations/FormBanner.js +214 -0
- package/routes/app/offers/scripts/rendering/variations/FormModal.js +325 -0
- package/routes/app/offers/scripts/rendering/variations/LinkBanner.js +105 -0
- package/routes/app/offers/scripts/rendering/variations/components/Body.js +29 -0
- package/routes/app/offers/scripts/rendering/variations/components/Button.js +128 -0
- package/routes/app/offers/scripts/rendering/variations/components/DismissButton.js +39 -0
- package/routes/app/offers/scripts/rendering/variations/components/ForegroundImageBanner.js +110 -0
- package/routes/app/offers/scripts/rendering/variations/components/ForegroundImageModal.js +128 -0
- package/routes/app/offers/scripts/rendering/variations/components/Headline.js +94 -0
- package/routes/app/offers/scripts/rendering/variations/components/MultiFieldForm.js +302 -0
- package/routes/app/offers/scripts/rendering/variations/components/Tooltip.js +74 -0
- package/routes/app/offers/scripts/rendering/variations/icons.js +15 -0
- package/routes/app/offers/scripts/utils.js +115 -0
- package/routes/app/offers/utils/HTMLUtils.js +10 -0
- package/routes/app/offers/utils/createTypeGuard.js +32 -0
- package/routes/app/offers/utils/promise-utils.js +22 -0
- package/routes/app/websiteBuilder/stores/storeHelpers.js +18 -0
- package/sounds/message-initial.mp3 +0 -0
- package/sounds/message-received.mp3 +0 -0
- package/sounds/message-sent.mp3 +0 -0
- package/src/arrays.js +15 -0
- package/src/audio.js +106 -0
- package/src/bounded-set.js +16 -0
- package/src/bounding_box_observer.js +129 -0
- package/src/decorators.js +29 -0
- package/src/dom.js +128 -0
- package/src/dom_roots.js +26 -0
- package/src/embedded_forms/EmbeddedForm.js +638 -0
- package/src/embedded_forms/QualifiedEmbeddedForm.js +51 -0
- package/src/embedded_forms/QualifiedFormField.js +137 -0
- package/src/embedded_forms/utils.js +17 -0
- package/src/graphql-types.js +1686 -0
- package/src/iframes.js +86 -0
- package/src/localstorage.js +43 -0
- package/src/mirror/Shipper.js +133 -0
- package/src/mirror/constants.generated.js +13 -0
- package/src/mirror/constants.js +238 -0
- package/src/mirror/shipper/ChangeQueue.js +155 -0
- package/src/mirror/shipper/DOMSerializer.js +392 -0
- package/src/mirror/shipper/observers.js +441 -0
- package/src/mirror/shipper/utils.js +98 -0
- package/src/mixins/disposable.js +125 -0
- package/src/mixins/injectable.js +37 -0
- package/src/personalizations/personalization-manager.js +110 -0
- package/src/personalizations/strategies/node-freezing-strategy.js +174 -0
- package/src/personalizations/utils.js +187 -0
- package/src/promises.js +107 -0
- package/src/proxy.js +144 -0
- package/src/qualified_root.js +35 -0
- package/src/query_params.js +55 -0
- package/src/shadow-dom-helper.js +63 -0
- package/src/singleton_host.js +39 -0
- package/src/smart_website/trigger.js +49 -0
- package/src/smart_website/utils.js +149 -0
- package/src/string.js +53 -0
- package/src/timezone.js +57 -0
- package/src/timing.js +49 -0
- package/src/type-utils.js +76 -0
- package/src/url-utils.js +102 -0
- package/src/url.js +50 -0
- package/src/utils.js +72 -0
- package/src/validation.js.js +29 -0
- package/src/validation_ts.js +6 -0
- package/vendor/actioncable/adapters.js +6 -0
- package/vendor/actioncable/connection.js +204 -0
- package/vendor/actioncable/connection_monitor.js +129 -0
- package/vendor/actioncable/consumer.js +91 -0
- package/vendor/actioncable/index.js +34 -0
- package/vendor/actioncable/internal.js +22 -0
- package/vendor/actioncable/logger.js +28 -0
- package/vendor/actioncable/subscription.js +93 -0
- package/vendor/actioncable/subscriptions.js +89 -0
- package/vendor/open-telemetry-idgenerator.js +43 -0
- package/widget/analytics/facebook_tracker.js +8 -0
- package/widget/analytics/google_analytics_tracker.js +83 -0
- package/widget/analytics/google_tag_manager_tracker.js +22 -0
- package/widget/analytics.js +58 -0
- package/widget/api_handler/api_handler.js +730 -0
- package/widget/api_handler/base_api_handler.js +249 -0
- package/widget/booker_intent_tracker.js +391 -0
- package/widget/booker_outcome_tracker.js +329 -0
- package/widget/capture_widget_exceptions.js +150 -0
- package/widget/channel.js +282 -0
- package/widget/composite_experience_adapter.js +101 -0
- package/widget/constants.generated.js +350 -0
- package/widget/constants.js +21 -0
- package/widget/container.js +193 -0
- package/widget/content_activity_emitter.js +36 -0
- package/widget/data360_visitor.js +67 -0
- package/widget/date_provider.js +11 -0
- package/widget/debug_mode_state.js +175 -0
- package/widget/dummy_module.js +9 -0
- package/widget/eloqua_visitor.js +17 -0
- package/widget/form_cta_booker_intent.js +246 -0
- package/widget/hubspot_visitor.js +39 -0
- package/widget/inline_meeting_booker_page.js +90 -0
- package/widget/local_state/abstract_local_state.js +38 -0
- package/widget/local_state/debug_state/abstract_debug_flow.js +29 -0
- package/widget/local_state/debug_state/custom_form_flow.js +14 -0
- package/widget/local_state/debug_state/hubspot_form_flow.js +19 -0
- package/widget/local_state/debug_state/hubspot_v4_form_flow.js +19 -0
- package/widget/local_state/debug_state/marketo_form_flow.js +20 -0
- package/widget/local_state/debug_state/pardot_form_flow.js +18 -0
- package/widget/local_state/debug_state.js +362 -0
- package/widget/local_state/trace_state.js +20 -0
- package/widget/location_provider.js +9 -0
- package/widget/marketo_visitor.js +39 -0
- package/widget/meetings/tracing/buttons_forms_triggering_tracer.js +124 -0
- package/widget/meetings/tracing/spans/button_spans.js +18 -0
- package/widget/meetings/tracing/spans/form_spans.js +11 -0
- package/widget/multimodal_v2/assets_preloader.js +97 -0
- package/widget/multimodal_v2/components/AppLayout.js +93 -0
- package/widget/multimodal_v2/components/CursorOverlay.js +93 -0
- package/widget/multimodal_v2/components/PointerRectOverlay.js +74 -0
- package/widget/multimodal_v2/components/app/Artifact.js +109 -0
- package/widget/multimodal_v2/components/app/Background.js +27 -0
- package/widget/multimodal_v2/components/app/CalendarLoadingContent.js +255 -0
- package/widget/multimodal_v2/components/app/Chat.js +39 -0
- package/widget/multimodal_v2/components/app/CookieConsentModal.js +149 -0
- package/widget/multimodal_v2/components/app/Foreground.js +48 -0
- package/widget/multimodal_v2/components/app/HiddenInput.js +41 -0
- package/widget/multimodal_v2/components/app/Input.js +72 -0
- package/widget/multimodal_v2/components/app/LightboxExperience.js +769 -0
- package/widget/multimodal_v2/components/app/LoadingOverlay.js +97 -0
- package/widget/multimodal_v2/components/app/Main.js +985 -0
- package/widget/multimodal_v2/components/app/MessengerButton.js +160 -0
- package/widget/multimodal_v2/components/app/Multimodal.js +17 -0
- package/widget/multimodal_v2/components/app/OverlayControls.js +79 -0
- package/widget/multimodal_v2/components/app/Preview.js +32 -0
- package/widget/multimodal_v2/components/app/PrivacyDisclosure.js +57 -0
- package/widget/multimodal_v2/components/app/RotateDeviceOverlay.js +70 -0
- package/widget/multimodal_v2/components/app/Toolbar.js +156 -0
- package/widget/multimodal_v2/components/chat/ChatComposer.js +129 -0
- package/widget/multimodal_v2/components/chat/ChatInputForm.js +398 -0
- package/widget/multimodal_v2/components/chat/ColdStartQuestions.js +354 -0
- package/widget/multimodal_v2/components/chat/ComposerErrorBanner.js +62 -0
- package/widget/multimodal_v2/components/chat/PostCallSurvey.js +359 -0
- package/widget/multimodal_v2/components/chat/preview/FieldRequestRenderer.js +41 -0
- package/widget/multimodal_v2/components/chat/preview/PreviewMessageRenderer.js +60 -0
- package/widget/multimodal_v2/components/chat/preview/SharedOfferRenderer.js +143 -0
- package/widget/multimodal_v2/components/chat/preview/TextRenderer.js +27 -0
- package/widget/multimodal_v2/components/chat/preview/TimelinePreview.js +254 -0
- package/widget/multimodal_v2/components/chat/timeline/CalendarEventOfferRenderer.js +103 -0
- package/widget/multimodal_v2/components/chat/timeline/CalendarEventPickRenderer.js +148 -0
- package/widget/multimodal_v2/components/chat/timeline/FieldEntryRenderer.js +84 -0
- package/widget/multimodal_v2/components/chat/timeline/FieldRequestRenderer.js +56 -0
- package/widget/multimodal_v2/components/chat/timeline/InlinePrivacyDisclosure.js +66 -0
- package/widget/multimodal_v2/components/chat/timeline/LinkPreviewCard.js +70 -0
- package/widget/multimodal_v2/components/chat/timeline/MessageEntry.js +88 -0
- package/widget/multimodal_v2/components/chat/timeline/PresentContentRenderer.js +30 -0
- package/widget/multimodal_v2/components/chat/timeline/PresentDeckRenderer.js +279 -0
- package/widget/multimodal_v2/components/chat/timeline/PresentVideoRenderer.js +131 -0
- package/widget/multimodal_v2/components/chat/timeline/ShareContentRenderer.js +59 -0
- package/widget/multimodal_v2/components/chat/timeline/SharedOfferRenderer.js +131 -0
- package/widget/multimodal_v2/components/chat/timeline/SystemRenderer.js +82 -0
- package/widget/multimodal_v2/components/chat/timeline/TextRenderer.js +66 -0
- package/widget/multimodal_v2/components/chat/timeline/Timeline.js +158 -0
- package/widget/multimodal_v2/components/debug/DebugMultimodalPanel.js +327 -0
- package/widget/multimodal_v2/components/decks/index.js +160 -0
- package/widget/multimodal_v2/components/field_request/BooleanField.js +77 -0
- package/widget/multimodal_v2/components/field_request/FieldComponents.js +69 -0
- package/widget/multimodal_v2/components/field_request/Form.js +87 -0
- package/widget/multimodal_v2/components/field_request/MultiPicklistField.js +240 -0
- package/widget/multimodal_v2/components/field_request/PicklistField.js +166 -0
- package/widget/multimodal_v2/components/field_request/SingleLineInputField.js +69 -0
- package/widget/multimodal_v2/components/field_request/TextField.js +37 -0
- package/widget/multimodal_v2/components/field_request/index.js +170 -0
- package/widget/multimodal_v2/components/helpers/AutoScroll.js +37 -0
- package/widget/multimodal_v2/components/helpers/ErrorBoundary.js +53 -0
- package/widget/multimodal_v2/components/helpers/FocusTrapDialog.js +26 -0
- package/widget/multimodal_v2/components/helpers/ImpressionTracker.js +94 -0
- package/widget/multimodal_v2/components/helpers/ListRenderer.js +37 -0
- package/widget/multimodal_v2/components/helpers/Localization.js +76 -0
- package/widget/multimodal_v2/components/helpers/Persisted.js +166 -0
- package/widget/multimodal_v2/components/helpers/RichText.js +358 -0
- package/widget/multimodal_v2/components/helpers/ScreenReaderHeading.js +17 -0
- package/widget/multimodal_v2/components/helpers/Thumbnail.js +190 -0
- package/widget/multimodal_v2/components/helpers/VisuallyHidden.js +41 -0
- package/widget/multimodal_v2/components/helpers/content_click_payloads.js +58 -0
- package/widget/multimodal_v2/components/helpers/useDraggable.js +103 -0
- package/widget/multimodal_v2/components/helpers/useTextOverflow.js +44 -0
- package/widget/multimodal_v2/components/lightbox_gather/index.js +741 -0
- package/widget/multimodal_v2/components/meetings/ArtifactEntryPoint.js +252 -0
- package/widget/multimodal_v2/components/meetings/MeetingAdditionalAttendees.js +325 -0
- package/widget/multimodal_v2/components/meetings/MeetingBookerEntry.js +100 -0
- package/widget/multimodal_v2/components/meetings/MeetingDetailsCard.js +99 -0
- package/widget/multimodal_v2/components/meetings/MeetingErrorBanner.js +62 -0
- package/widget/multimodal_v2/components/meetings/MeetingOfferEmailStep.js +239 -0
- package/widget/multimodal_v2/components/meetings/MeetingOfferFooter.js +104 -0
- package/widget/multimodal_v2/components/meetings/MeetingOfferSelectionStep.js +60 -0
- package/widget/multimodal_v2/components/meetings/MeetingRepHeader.js +222 -0
- package/widget/multimodal_v2/components/meetings/MeetingStepShell.js +92 -0
- package/widget/multimodal_v2/components/meetings/MeetingSummaryRail.js +515 -0
- package/widget/multimodal_v2/components/meetings/MeetingTimeSelector.js +603 -0
- package/widget/multimodal_v2/components/meetings/MonthDatePicker.js +422 -0
- package/widget/multimodal_v2/components/meetings/NavigationButtons.js +76 -0
- package/widget/multimodal_v2/components/meetings/TimeSlotHeader.js +357 -0
- package/widget/multimodal_v2/components/meetings/WeekViewHeader.js +20 -0
- package/widget/multimodal_v2/components/meetings/constants.js +7 -0
- package/widget/multimodal_v2/components/meetings/index.js +47 -0
- package/widget/multimodal_v2/components/meetings/utils.js +328 -0
- package/widget/multimodal_v2/components/multimodal/Audio.js +28 -0
- package/widget/multimodal_v2/components/multimodal/Controls.js +188 -0
- package/widget/multimodal_v2/components/multimodal/LiveVideo.js +63 -0
- package/widget/multimodal_v2/components/multimodal/MultiModalComponent.js +424 -0
- package/widget/multimodal_v2/components/multimodal/MultimodalAvatar.js +36 -0
- package/widget/multimodal_v2/components/multimodal/OrbAvatar.js +326 -0
- package/widget/multimodal_v2/components/multimodal/Status.js +220 -0
- package/widget/multimodal_v2/components/multimodal/VideoAvatar.js +24 -0
- package/widget/multimodal_v2/components/multimodal/VideoPreview.js +114 -0
- package/widget/multimodal_v2/components/multimodal/useAudioLevelMonitoring.js +96 -0
- package/widget/multimodal_v2/components/multimodal/useMultimodal.js +434 -0
- package/widget/multimodal_v2/components/presentations/index.js +372 -0
- package/widget/multimodal_v2/components/shared/Avatar.js +92 -0
- package/widget/multimodal_v2/components/shared/AvatarGroup.js +135 -0
- package/widget/multimodal_v2/components/shared/Button.js +245 -0
- package/widget/multimodal_v2/components/shared/ButtonGroup.js +133 -0
- package/widget/multimodal_v2/components/shared/ComposerForm.js +98 -0
- package/widget/multimodal_v2/components/shared/SpeakingIndicator.js +239 -0
- package/widget/multimodal_v2/components/shared/ThinkingAnimation.js +165 -0
- package/widget/multimodal_v2/components/shared/Tooltip.js +266 -0
- package/widget/multimodal_v2/components/shared/controls/Dropdown.js +79 -0
- package/widget/multimodal_v2/components/shared/controls/PillToggle.js +142 -0
- package/widget/multimodal_v2/components/shared/controls/TextArea.js +114 -0
- package/widget/multimodal_v2/components/shared/toolCallLabels.js +12 -0
- package/widget/multimodal_v2/components/slides/SlidesThumbnail.js +47 -0
- package/widget/multimodal_v2/components/slides/index.js +61 -0
- package/widget/multimodal_v2/constants.js +88 -0
- package/widget/multimodal_v2/host.split-css.js +2 -0
- package/widget/multimodal_v2/host.string.js +96 -0
- package/widget/multimodal_v2/images/icons.js +582 -0
- package/widget/multimodal_v2/images/posters/piper_low_res.jpg +0 -0
- package/widget/multimodal_v2/index.js +23 -0
- package/widget/multimodal_v2/index.panda.js +28 -0
- package/widget/multimodal_v2/index.split-css.panda.js +2 -0
- package/widget/multimodal_v2/inline_meeting_booker.js +182 -0
- package/widget/multimodal_v2/locales/cs.js +196 -0
- package/widget/multimodal_v2/locales/da.js +196 -0
- package/widget/multimodal_v2/locales/de.js +196 -0
- package/widget/multimodal_v2/locales/en.js +196 -0
- package/widget/multimodal_v2/locales/en_AU.js +197 -0
- package/widget/multimodal_v2/locales/en_GB.js +197 -0
- package/widget/multimodal_v2/locales/es.js +197 -0
- package/widget/multimodal_v2/locales/es_LA.js +196 -0
- package/widget/multimodal_v2/locales/fi.js +196 -0
- package/widget/multimodal_v2/locales/fr.js +197 -0
- package/widget/multimodal_v2/locales/fr_CA.js +196 -0
- package/widget/multimodal_v2/locales/id.js +196 -0
- package/widget/multimodal_v2/locales/index.js +48 -0
- package/widget/multimodal_v2/locales/it.js +196 -0
- package/widget/multimodal_v2/locales/ja.js +196 -0
- package/widget/multimodal_v2/locales/ko.js +196 -0
- package/widget/multimodal_v2/locales/nb.js +196 -0
- package/widget/multimodal_v2/locales/nl.js +196 -0
- package/widget/multimodal_v2/locales/pl.js +196 -0
- package/widget/multimodal_v2/locales/pt_BR.js +196 -0
- package/widget/multimodal_v2/locales/ru.js +196 -0
- package/widget/multimodal_v2/locales/sv.js +196 -0
- package/widget/multimodal_v2/locales/th.js +196 -0
- package/widget/multimodal_v2/locales/tr.js +196 -0
- package/widget/multimodal_v2/locales/vi.js +196 -0
- package/widget/multimodal_v2/locales/zh_CN.js +196 -0
- package/widget/multimodal_v2/locales/zh_TW.js +196 -0
- package/widget/multimodal_v2/multimodal.js +633 -0
- package/widget/multimodal_v2/multimodal_channel.js +195 -0
- package/widget/multimodal_v2/signals/active_conversation_signal.js +33 -0
- package/widget/multimodal_v2/signals/allowed_conversation_mode_signal.js +38 -0
- package/widget/multimodal_v2/signals/artifact_signal.js +22 -0
- package/widget/multimodal_v2/signals/layout_signal.js +98 -0
- package/widget/multimodal_v2/signals/messages_signal.js +207 -0
- package/widget/multimodal_v2/signals/ui_state_signal.js +128 -0
- package/widget/multimodal_v2/stable_bundle_src.js +44 -0
- package/widget/multimodal_v2/stores/ai_conversation_store.js +221 -0
- package/widget/multimodal_v2/stores/artifact_store.js +205 -0
- package/widget/multimodal_v2/stores/base_store.js +16 -0
- package/widget/multimodal_v2/stores/call_store.js +770 -0
- package/widget/multimodal_v2/stores/call_ui_adapter.js +225 -0
- package/widget/multimodal_v2/stores/debug_multimodal_scenario_store.js +471 -0
- package/widget/multimodal_v2/stores/decks_store.js +285 -0
- package/widget/multimodal_v2/stores/device_store.js +63 -0
- package/widget/multimodal_v2/stores/docking_store.js +62 -0
- package/widget/multimodal_v2/stores/field_request_store.js +326 -0
- package/widget/multimodal_v2/stores/meeting_store.js +477 -0
- package/widget/multimodal_v2/stores/participant_store.js +73 -0
- package/widget/multimodal_v2/stores/post_call_survey_store.js +160 -0
- package/widget/multimodal_v2/stores/pounce_tracking_store.js +163 -0
- package/widget/multimodal_v2/stores/pounce_tracking_store_v2.js +511 -0
- package/widget/multimodal_v2/stores/presentations_store.js +111 -0
- package/widget/multimodal_v2/stores/root_store.js +1919 -0
- package/widget/multimodal_v2/stores/screen_share_store.js +217 -0
- package/widget/multimodal_v2/stores/slides_store.js +21 -0
- package/widget/multimodal_v2/stores/translation_store.js +185 -0
- package/widget/multimodal_v2/stores/ui_store.js +496 -0
- package/widget/multimodal_v2/stores/waiting_store.js +159 -0
- package/widget/multimodal_v2/stores/website_navigation_store.js +767 -0
- package/widget/multimodal_v2/styled-system/css/conditions.js +34 -0
- package/widget/multimodal_v2/styled-system/css/css.js +48 -0
- package/widget/multimodal_v2/styled-system/css/cva.js +96 -0
- package/widget/multimodal_v2/styled-system/css/cx.js +13 -0
- package/widget/multimodal_v2/styled-system/css/index.js +15 -0
- package/widget/multimodal_v2/styled-system/css/sva.js +69 -0
- package/widget/multimodal_v2/styled-system/helpers.js +384 -0
- package/widget/multimodal_v2/styled-system/jsx/create-style-context.js +96 -0
- package/widget/multimodal_v2/styled-system/jsx/factory-helper.js +19 -0
- package/widget/multimodal_v2/styled-system/jsx/factory.js +111 -0
- package/widget/multimodal_v2/styled-system/jsx/index.js +12 -0
- package/widget/multimodal_v2/styled-system/jsx/is-valid-prop.js +22 -0
- package/widget/multimodal_v2/types.js +105 -0
- package/widget/multimodal_v2/utils/auto_scroller.js +157 -0
- package/widget/multimodal_v2/utils/flip_animation.js +124 -0
- package/widget/multimodal_v2/utils/focus_gate.js +237 -0
- package/widget/multimodal_v2/utils/locale.js +16 -0
- package/widget/multimodal_v2/utils/media_queries.js +30 -0
- package/widget/multimodal_v2/utils/multimodal_cache.js +19 -0
- package/widget/multimodal_v2/utils/mutation_effect.js +14 -0
- package/widget/multimodal_v2/utils/navigation_interceptor.js +205 -0
- package/widget/multimodal_v2/utils/navigation_strategies/dom_swap.js +393 -0
- package/widget/multimodal_v2/utils/navigation_strategies/index.js +12 -0
- package/widget/multimodal_v2/utils/navigation_strategies/lwr.js +61 -0
- package/widget/multimodal_v2/utils/navigation_strategies/navigation_utils.js +46 -0
- package/widget/multimodal_v2/utils/navigation_strategies/next_js.js +24 -0
- package/widget/multimodal_v2/utils/navigation_strategies/wouter_v3.js +15 -0
- package/widget/multimodal_v2/utils/objects_map.js +38 -0
- package/widget/multimodal_v2/utils/perceived_voice_turn_tracker.js +345 -0
- package/widget/multimodal_v2/utils/sanitize_html.js +132 -0
- package/widget/multimodal_v2/utils/signals.js +97 -0
- package/widget/multimodal_v2/utils/slide_zoom.js +114 -0
- package/widget/multimodal_v2/utils/theme_styles.js +137 -0
- package/widget/multimodal_v2/utils/url_observer.js +52 -0
- package/widget/multimodal_v2/utils/voice_onset_detector.js +127 -0
- package/widget/page_mirror.js +89 -0
- package/widget/page_view_activity_emitter.js +71 -0
- package/widget/pardot_visitor.js +137 -0
- package/widget/personalization_observer.js +96 -0
- package/widget/personalizations_config.js +160 -0
- package/widget/qualified_form/constants.js +23 -0
- package/widget/qualified_form/phone_utils.js +38 -0
- package/widget/qualified_form/qualified_form_action.js +91 -0
- package/widget/qualified_form/qualified_form_action_orchestrator.js +21 -0
- package/widget/qualified_form/qualified_form_api.js +471 -0
- package/widget/scroll_disabler.js +109 -0
- package/widget/server-logger.js +156 -0
- package/widget/smart_website_config.js +112 -0
- package/widget/telemetry/context.js +75 -0
- package/widget/telemetry/index.js +16 -0
- package/widget/telemetry/messenger_uptime.js +80 -0
- package/widget/telemetry/metrics.js +120 -0
- package/widget/telemetry/span.js +82 -0
- package/widget/telemetry/telemetry.js +180 -0
- package/widget/telemetry/tracer.js +41 -0
- package/widget/telemetry/tracing_store.js +310 -0
- package/widget/tracker.js +916 -0
- package/widget/utils/native_globals.js +129 -0
- package/widget/v1_adapter.js +50 -0
- package/widget/v2_adapter.js +368 -0
- package/widget/visitor/base_visitor_state.js +11 -0
- package/widget/visitor/cookie_visitor_state.js +128 -0
- package/widget/visitor/in_memory_visitor_state.js +29 -0
- package/widget/visitor/visitor_state.js +384 -0
- package/widget/visitor.js +356 -0
- package/widget/widget/linked_widget_v2.js +227 -0
- package/widget/widget/page_mirror_factory.js +22 -0
- package/widget/widget/sentry.js +130 -0
- package/widget/widget/widget.js +1698 -0
- package/widget/widget.split-css.js +2 -0
- package/widget/widget_bootstrap.js +97 -0
- package/widget/widget_object.js +73 -0
|
@@ -0,0 +1,1919 @@
|
|
|
1
|
+
import { __webpack_require__ } from "../../../js/runtime.js";
|
|
2
|
+
import { hooks_module_x, message_initial_namespaceObject, Q, message_sent_namespaceObject, signals_core_module_d, signals_core_module_w, message_received_namespaceObject, signals_core_module_r } from "../../../js/multimodal_v2.js";
|
|
3
|
+
import { playAudio } from "../../../src/audio.js";
|
|
4
|
+
import { BoundedSet } from "../../../src/bounded-set.js";
|
|
5
|
+
import { isDebugMode } from "../../../src/smart_website/utils.js";
|
|
6
|
+
import { snakify } from "../../../src/utils.js";
|
|
7
|
+
import { BookerIntentTracker } from "../../booker_intent_tracker.js";
|
|
8
|
+
import { BOOKER_PATHWAY_ORIGIN, BOOKER_SURFACE } from "../../booker_outcome_tracker.js";
|
|
9
|
+
import { serverLogger } from "../../server-logger.js";
|
|
10
|
+
import "../../telemetry/index.js";
|
|
11
|
+
import { getSentryClient } from "../../widget/sentry.js";
|
|
12
|
+
import { CAN_OFFER_MEETING_BOOKER_EVENT_TYPE } from "../constants.js";
|
|
13
|
+
import { createActiveConversationSignal } from "../signals/active_conversation_signal.js";
|
|
14
|
+
import { createMessagesSignal, isStreamableMessage, isAssistantMessage, isEventMessage } from "../signals/messages_signal.js";
|
|
15
|
+
import { $fromImport } from "../utils/signals.js";
|
|
16
|
+
import { ArtifactStore } from "./artifact_store.js";
|
|
17
|
+
import { getCallUiAdapter } from "./call_ui_adapter.js";
|
|
18
|
+
import { DockingStore } from "./docking_store.js";
|
|
19
|
+
import { TranslationStore } from "./translation_store.js";
|
|
20
|
+
import { UIStore } from "./ui_store.js";
|
|
21
|
+
import { BaseStore } from "./base_store.js";
|
|
22
|
+
import "../../utils/native_globals.js";
|
|
23
|
+
import { validateEmail } from "../../../src/validation.js.js";
|
|
24
|
+
import { ConversationMode as types_ConversationMode, LoadingState as types_LoadingState, DisplayMode as types_DisplayMode, MaximizedState as types_MaximizedState, InputState as types_InputState } from "../types.js";
|
|
25
|
+
import { buildCustEvChatViewPayload, buildCustEvContentImpressionPayload } from "../components/helpers/content_click_payloads.js";
|
|
26
|
+
import { esm_browser_v4 } from "../../../js/4101.js";
|
|
27
|
+
import { metrics } from "../../telemetry/metrics.js";
|
|
28
|
+
|
|
29
|
+
/* provided dependency */ var __nativeDocumentRemoveEventListener = __webpack_require__(68034).LA;
|
|
30
|
+
/* provided dependency */ var root_store_setTimeout = __webpack_require__(68034).pS;
|
|
31
|
+
/* provided dependency */ var root_store_clearTimeout = __webpack_require__(68034).Kj;
|
|
32
|
+
/* provided dependency */ var __nativeDocumentAddEventListener = __webpack_require__(68034).Qj;
|
|
33
|
+
function _extends() {
|
|
34
|
+
_extends = Object.assign || function(target) {
|
|
35
|
+
for(var i = 1; i < arguments.length; i++){
|
|
36
|
+
var source = arguments[i];
|
|
37
|
+
for(var key in source){
|
|
38
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
39
|
+
target[key] = source[key];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return target;
|
|
44
|
+
};
|
|
45
|
+
return _extends.apply(this, arguments);
|
|
46
|
+
}
|
|
47
|
+
function _object_without_properties_loose(source, excluded) {
|
|
48
|
+
if (source == null) return {};
|
|
49
|
+
var target = {}, sourceKeys = Object.getOwnPropertyNames(source), key, i;
|
|
50
|
+
for(i = 0; i < sourceKeys.length; i++){
|
|
51
|
+
key = sourceKeys[i];
|
|
52
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
53
|
+
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
|
|
54
|
+
target[key] = source[key];
|
|
55
|
+
}
|
|
56
|
+
return target;
|
|
57
|
+
}
|
|
58
|
+
/* eslint-disable no-continue */
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
// A reply slower than RESPONSE_BUDGET_MS (or none at all) counts as unavailable. Both live in the
|
|
88
|
+
// leaf constants module (avoids a store import cycle), re-exported so `from "./root_store"`
|
|
89
|
+
// importers still work.
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Whether an offer was authored by the AI rather than a rep, a form, or a bot
|
|
93
|
+
* script. `auto_respond` is the same marker `bookerPathwayOrigin` reads to
|
|
94
|
+
* classify an offer as `ai_agent`; both AI tool paths stamp it.
|
|
95
|
+
*/ function isAiAuthoredOffer(source) {
|
|
96
|
+
return (source != null ? source : "").toLowerCase() === "auto_respond";
|
|
97
|
+
}
|
|
98
|
+
class RootStore extends BaseStore {
|
|
99
|
+
get allowVoiceVideoToggle() {
|
|
100
|
+
return this.uiStore.allowVoiceVideoToggle;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get the appropriate CallUiAdapter for the current context.
|
|
104
|
+
* In debug mode, returns a debug adapter backed by DebugMultimodalScenarioStore.
|
|
105
|
+
* Otherwise, returns the real adapter backed by CallStore/DeviceStore/TavusStore.
|
|
106
|
+
*/ getCallUiAdapter() {
|
|
107
|
+
return getCallUiAdapter(this);
|
|
108
|
+
}
|
|
109
|
+
// --- Backward-compat getters delegating to UIStore ---
|
|
110
|
+
get uiState() {
|
|
111
|
+
return this.uiStore.uiState;
|
|
112
|
+
}
|
|
113
|
+
get maximizedState() {
|
|
114
|
+
return this.uiStore.maximizedState;
|
|
115
|
+
}
|
|
116
|
+
get initialDisplayMode() {
|
|
117
|
+
return this.uiStore.initialDisplayMode;
|
|
118
|
+
}
|
|
119
|
+
get openedDisplayMode() {
|
|
120
|
+
return this.uiStore.openedDisplayMode;
|
|
121
|
+
}
|
|
122
|
+
get launcherImageUrl() {
|
|
123
|
+
return this.uiStore.launcherImageUrl;
|
|
124
|
+
}
|
|
125
|
+
get orbConfig() {
|
|
126
|
+
return this.uiStore.orbConfig;
|
|
127
|
+
}
|
|
128
|
+
get previewVideoConfig() {
|
|
129
|
+
return this.uiStore.previewVideoConfig;
|
|
130
|
+
}
|
|
131
|
+
get demoFeatures() {
|
|
132
|
+
return this.uiStore.demoFeatures;
|
|
133
|
+
}
|
|
134
|
+
get layout() {
|
|
135
|
+
return this.uiStore.layout;
|
|
136
|
+
}
|
|
137
|
+
get piperConversationId() {
|
|
138
|
+
var _ref;
|
|
139
|
+
const conversation = this.activeConversation.value;
|
|
140
|
+
return (_ref = conversation == null ? void 0 : conversation.encodedId) != null ? _ref : "";
|
|
141
|
+
}
|
|
142
|
+
get featureFlags() {
|
|
143
|
+
var _ref;
|
|
144
|
+
var _this_config;
|
|
145
|
+
return (_ref = (_this_config = this.config) == null ? void 0 : _this_config.featureFlags) != null ? _ref : {};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Server-side email validation shared by chat and meeting flows.
|
|
149
|
+
* Fail-open: network / API errors treat the email as valid.
|
|
150
|
+
*/ async validateEmail(email, source) {
|
|
151
|
+
if (source === void 0) source = "Chat";
|
|
152
|
+
try {
|
|
153
|
+
const url = this.visitor.resourceUrl("validate_email", {});
|
|
154
|
+
const response = await fetch(url, {
|
|
155
|
+
method: "POST",
|
|
156
|
+
headers: {
|
|
157
|
+
"Content-Type": "application/json; charset=utf-8"
|
|
158
|
+
},
|
|
159
|
+
body: JSON.stringify({
|
|
160
|
+
email,
|
|
161
|
+
source
|
|
162
|
+
})
|
|
163
|
+
});
|
|
164
|
+
if (response.ok) {
|
|
165
|
+
const json = await response.json();
|
|
166
|
+
return json.valid !== false;
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
} catch (error) {
|
|
170
|
+
var _getSentryClient;
|
|
171
|
+
(_getSentryClient = getSentryClient({
|
|
172
|
+
project: "multimodal"
|
|
173
|
+
})) == null ? void 0 : _getSentryClient.captureException(error);
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
get displayMode() {
|
|
178
|
+
return this.uiStore.displayMode;
|
|
179
|
+
}
|
|
180
|
+
get conversationMode() {
|
|
181
|
+
return this.uiStore.conversationMode;
|
|
182
|
+
}
|
|
183
|
+
get input() {
|
|
184
|
+
return this.uiStore.input;
|
|
185
|
+
}
|
|
186
|
+
get artifact() {
|
|
187
|
+
return this.artifactStore.current;
|
|
188
|
+
}
|
|
189
|
+
/** Messages whose artifact is on its way into the overlay. See ArtifactStore#pending. */ get pendingArtifacts() {
|
|
190
|
+
return this.artifactStore.pending;
|
|
191
|
+
}
|
|
192
|
+
get userHasIgnoredMultimodal() {
|
|
193
|
+
return this.uiStore.userHasIgnoredMultimodal;
|
|
194
|
+
}
|
|
195
|
+
get isPiP() {
|
|
196
|
+
return this.uiStore.isPiP;
|
|
197
|
+
}
|
|
198
|
+
dispose() {
|
|
199
|
+
var _this_pounceTrackingStore_value, _this_callStore_value, _this_waitingStore_value, _this_websiteNavigationStore_value, _this_presentationsStore_value, _this_decksStore_value, _this_screenShareStore_value;
|
|
200
|
+
// Unsubscribe WS message handlers from the (persistent) channel so a rebuilt
|
|
201
|
+
// store doesn't run alongside this disposed one when the multimodal UI is
|
|
202
|
+
// torn down and later recreated on the same channel.
|
|
203
|
+
for (const [event, handler] of Object.entries(this.messageHandlers)){
|
|
204
|
+
this.channel.off(event, handler);
|
|
205
|
+
}
|
|
206
|
+
this.dockingStore.disable();
|
|
207
|
+
// Armed trackers hold window/document listeners; the multimodal rebuild path
|
|
208
|
+
// replaces this store in-page, so an undisposed tracker would outlive it and
|
|
209
|
+
// emit a false abandonment alongside its replacement.
|
|
210
|
+
this.bookerIntentTracker.dispose();
|
|
211
|
+
(_this_pounceTrackingStore_value = this.pounceTrackingStore.value) == null ? void 0 : _this_pounceTrackingStore_value.dispose();
|
|
212
|
+
(_this_callStore_value = this.callStore.value) == null ? void 0 : _this_callStore_value.dispose();
|
|
213
|
+
(_this_waitingStore_value = this.waitingStore.value) == null ? void 0 : _this_waitingStore_value.dispose();
|
|
214
|
+
(_this_websiteNavigationStore_value = this.websiteNavigationStore.value) == null ? void 0 : _this_websiteNavigationStore_value.dispose();
|
|
215
|
+
(_this_presentationsStore_value = this.presentationsStore.value) == null ? void 0 : _this_presentationsStore_value.dispose();
|
|
216
|
+
(_this_decksStore_value = this.decksStore.value) == null ? void 0 : _this_decksStore_value.dispose();
|
|
217
|
+
(_this_screenShareStore_value = this.screenShareStore.value) == null ? void 0 : _this_screenShareStore_value.dispose();
|
|
218
|
+
this.clearResponseTimeout();
|
|
219
|
+
this.flushHeldTimeout();
|
|
220
|
+
this.turnToken++;
|
|
221
|
+
if (typeof document !== "undefined") {
|
|
222
|
+
__nativeDocumentRemoveEventListener("visibilitychange", this.handleTurnVisibilityChange);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
startTextTurn(messageClientId) {
|
|
226
|
+
// Before any per-turn state resets: the held metric belongs to the turn that
|
|
227
|
+
// just timed out and reads `turnCause` when it fires.
|
|
228
|
+
this.flushHeldTimeout();
|
|
229
|
+
this.lastVisitorMessageSentAt = performance.now();
|
|
230
|
+
this.turnMessageClientId = messageClientId;
|
|
231
|
+
this.turnCounterEmitted = false;
|
|
232
|
+
this.turnReplyHandled = false;
|
|
233
|
+
this.turnObscured = this.isDocumentHidden();
|
|
234
|
+
this.turnCallActive = this.callInProgress.peek();
|
|
235
|
+
this.turnCause = null;
|
|
236
|
+
this.turnHasToolCall = false;
|
|
237
|
+
const token = ++this.turnToken;
|
|
238
|
+
this.clearResponseTimeout();
|
|
239
|
+
this.responseTimeoutId = root_store_setTimeout(()=>{
|
|
240
|
+
this.responseTimeoutId = null;
|
|
241
|
+
if (token !== this.turnToken || this.turnCounterEmitted) return;
|
|
242
|
+
// Treat the turn as obscured if the tab was hidden at any point, not just
|
|
243
|
+
// now: backgrounded tabs throttle setTimeout, so the timer often fires only
|
|
244
|
+
// after the visitor returns (document.hidden already false). Checking the
|
|
245
|
+
// whole wait keeps that throttling delay out of the timeout (unavailable) count.
|
|
246
|
+
const obscured = this.turnObscured || this.isDocumentHidden();
|
|
247
|
+
// The turn is counted from this moment, whenever the metric actually
|
|
248
|
+
// leaves: a reply or a `textResponseEnded` arriving later must not talk us
|
|
249
|
+
// out of a timeout the visitor has already experienced.
|
|
250
|
+
this.turnCounterEmitted = true;
|
|
251
|
+
if (obscured) {
|
|
252
|
+
metrics.emitIncrement("multimodal_text_response", {
|
|
253
|
+
status: "backgrounded",
|
|
254
|
+
call_active: this.turnCallActiveTag
|
|
255
|
+
});
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
// Before the breadcrumb: nothing between latching the outcome and arming the
|
|
259
|
+
// emission, so a counted timeout cannot be lost to a failure in between.
|
|
260
|
+
this.holdTimeoutForCause();
|
|
261
|
+
serverLogger.serverLog({
|
|
262
|
+
type: "log",
|
|
263
|
+
message: "Multimodal text reply did not arrive within response budget"
|
|
264
|
+
});
|
|
265
|
+
}, (/* inlined export .RESPONSE_BUDGET_MS */25000));
|
|
266
|
+
return token;
|
|
267
|
+
}
|
|
268
|
+
get turnCallActiveTag() {
|
|
269
|
+
return this.turnCallActive ? "true" : "false";
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Holds a decided timeout's metric back briefly so the server's cause report
|
|
273
|
+
* can land on it (see CAUSE_REPORT_GRACE_MS). Only the `cause` tag is
|
|
274
|
+
* outstanding — the turn is already counted — so this delays an emission, it
|
|
275
|
+
* never changes an outcome.
|
|
276
|
+
*
|
|
277
|
+
* Ends early when the report arrives, and is flushed rather than dropped if the
|
|
278
|
+
* turn is superseded or the store torn down first, so a held timeout can't go
|
|
279
|
+
* missing from the SLI.
|
|
280
|
+
*/ holdTimeoutForCause() {
|
|
281
|
+
// Flush rather than clear: a hold here would already have been counted, and
|
|
282
|
+
// dropping it would delete a failure from the SLI. Unreachable today (one
|
|
283
|
+
// budget timer per turn, and starting a turn flushes), enforced here so it
|
|
284
|
+
// stays that way.
|
|
285
|
+
this.flushHeldTimeout();
|
|
286
|
+
// Captured now: a turn starting during the window resets the live value.
|
|
287
|
+
const callActive = this.turnCallActiveTag;
|
|
288
|
+
this.heldTimeoutFlush = ()=>{
|
|
289
|
+
var _this_turnCause;
|
|
290
|
+
this.clearTimeoutHold();
|
|
291
|
+
metrics.emitIncrement("multimodal_text_response", {
|
|
292
|
+
status: "timeout",
|
|
293
|
+
call_active: callActive,
|
|
294
|
+
// `unknown` rather than omitted: every timeout carries a cause, so a
|
|
295
|
+
// dashboard split by cause always adds up to the timeout total. A growing
|
|
296
|
+
// `unknown` slice means the report is not reaching us.
|
|
297
|
+
cause: (_this_turnCause = this.turnCause) != null ? _this_turnCause : "unknown"
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
this.timeoutHoldId = root_store_setTimeout(this.heldTimeoutFlush, (/* inlined export .CAUSE_REPORT_GRACE_MS */1500));
|
|
301
|
+
}
|
|
302
|
+
// No-op unless a timeout is being held. Self-clearing, so it is safe to call
|
|
303
|
+
// from every path that might race the window.
|
|
304
|
+
flushHeldTimeout() {
|
|
305
|
+
this.heldTimeoutFlush == null ? void 0 : this.heldTimeoutFlush.call(this);
|
|
306
|
+
}
|
|
307
|
+
clearTimeoutHold() {
|
|
308
|
+
if (this.timeoutHoldId !== null) {
|
|
309
|
+
root_store_clearTimeout(this.timeoutHoldId);
|
|
310
|
+
this.timeoutHoldId = null;
|
|
311
|
+
}
|
|
312
|
+
this.heldTimeoutFlush = null;
|
|
313
|
+
}
|
|
314
|
+
clearResponseTimeout() {
|
|
315
|
+
if (this.responseTimeoutId !== null) {
|
|
316
|
+
root_store_clearTimeout(this.responseTimeoutId);
|
|
317
|
+
this.responseTimeoutId = null;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
isDocumentHidden() {
|
|
321
|
+
return typeof document !== "undefined" && document.hidden;
|
|
322
|
+
}
|
|
323
|
+
handleTextTurnSendResult(result, turnToken) {
|
|
324
|
+
if (!(result == null ? void 0 : result.error)) return;
|
|
325
|
+
if (turnToken !== this.turnToken || this.turnCounterEmitted) return;
|
|
326
|
+
this.clearResponseTimeout();
|
|
327
|
+
metrics.emitIncrement("multimodal_text_response", {
|
|
328
|
+
status: "send_failed",
|
|
329
|
+
call_active: this.turnCallActiveTag
|
|
330
|
+
});
|
|
331
|
+
// A failed send has no reply coming, so close the turn entirely (no latency).
|
|
332
|
+
this.turnCounterEmitted = true;
|
|
333
|
+
this.turnReplyHandled = true;
|
|
334
|
+
this.turnToken++;
|
|
335
|
+
}
|
|
336
|
+
static isTextResponseEndedReason(reason) {
|
|
337
|
+
return RootStore.TEXT_RESPONSE_ENDED_REASONS.includes(reason);
|
|
338
|
+
}
|
|
339
|
+
static isTurnCause(cause) {
|
|
340
|
+
return RootStore.TURN_CAUSES.includes(cause);
|
|
341
|
+
}
|
|
342
|
+
unresolvedAiSdrEmailRequest() {
|
|
343
|
+
var _this_activeConversation_value;
|
|
344
|
+
const activeConversationId = (_this_activeConversation_value = this.activeConversation.value) == null ? void 0 : _this_activeConversation_value.id;
|
|
345
|
+
if (!activeConversationId) return null;
|
|
346
|
+
const resolvedFieldIds = new Set();
|
|
347
|
+
for (const message of [
|
|
348
|
+
...this.messages.value
|
|
349
|
+
].reverse()){
|
|
350
|
+
if (message.conversationId !== activeConversationId) continue;
|
|
351
|
+
if (message.type === "field_entry" && message.fieldId) {
|
|
352
|
+
resolvedFieldIds.add(message.fieldId);
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (message.type === "field_request" && message.createdByMultimodalAgent === true && message.fieldType === "email" && (!message.fieldId || !resolvedFieldIds.has(message.fieldId))) {
|
|
356
|
+
return message;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
async aisdrEmailReplyPassesPreflight(text) {
|
|
362
|
+
if (!this.featureFlags.aisdrChatEmailValidation) return true;
|
|
363
|
+
if (!this.unresolvedAiSdrEmailRequest()) return true;
|
|
364
|
+
const email = text.trim();
|
|
365
|
+
if (!email.includes("@") || /\s/.test(email)) return true;
|
|
366
|
+
const [formatValid] = validateEmail(email, false);
|
|
367
|
+
if (!formatValid) return false;
|
|
368
|
+
return this.validateEmail(email);
|
|
369
|
+
}
|
|
370
|
+
async sendVisitorTextWithoutCookieConsent(text, extra) {
|
|
371
|
+
if (!await this.aisdrEmailReplyPassesPreflight(text)) {
|
|
372
|
+
this.sendTextError.value = "Invalid email address";
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const result = await this.sendMessage(_extends({
|
|
376
|
+
type: "text",
|
|
377
|
+
text,
|
|
378
|
+
isOwn: true
|
|
379
|
+
}, extra));
|
|
380
|
+
if (result.error) {
|
|
381
|
+
this.sendTextError.value = result.error.message || "Your message wasn't sent. Please try again.";
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// Initialize voice and video stores when needed
|
|
385
|
+
async initializeVoiceAndVideo() {
|
|
386
|
+
this.isVideoLoading.value = true;
|
|
387
|
+
await this.callStore.load();
|
|
388
|
+
}
|
|
389
|
+
async checkPermissions() {
|
|
390
|
+
// Check current permission status before
|
|
391
|
+
let res = await navigator.permissions.query({
|
|
392
|
+
name: "microphone"
|
|
393
|
+
});
|
|
394
|
+
if (res.state !== "prompt") {
|
|
395
|
+
this.sendMessage({
|
|
396
|
+
type: "event",
|
|
397
|
+
eventType: "mediaPermissionStatus",
|
|
398
|
+
mediaStatus: "audio_already_" + res.state
|
|
399
|
+
});
|
|
400
|
+
} else {
|
|
401
|
+
// Request permission
|
|
402
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
403
|
+
video: false,
|
|
404
|
+
audio: true
|
|
405
|
+
}).catch(()=>null);
|
|
406
|
+
stream == null ? void 0 : stream.getTracks().forEach((track)=>track.stop());
|
|
407
|
+
// Check state again
|
|
408
|
+
res = await navigator.permissions.query({
|
|
409
|
+
name: "microphone"
|
|
410
|
+
});
|
|
411
|
+
this.sendMessage({
|
|
412
|
+
type: "event",
|
|
413
|
+
eventType: "mediaPermissionStatus",
|
|
414
|
+
mediaStatus: "audio_prompted_" + res.state
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
constructor(context, config = null){
|
|
419
|
+
super(context), this.config = config, // Initialized is true once we receive the first init state.
|
|
420
|
+
this.initialized = signals_core_module_d(false), // Set while a socket reconnect is in flight. The server answers a reconnect
|
|
421
|
+
// with a full initState re-broadcast, which must not move the widget away
|
|
422
|
+
// from where the visitor left it. Cleared once that initState is applied.
|
|
423
|
+
this.isReconnecting = signals_core_module_d(false), /** Admin-customizable chat input placeholder (English only) */ this.inputPlaceholder = signals_core_module_d(null), /** Whether to hide the visible 'Ask {name}' label on the multimodal messenger launcher */ this.multimodalHideLauncherLabel = signals_core_module_d(false), /**
|
|
424
|
+
* When the user expresses chat-view intent before initState provides the `formJoinIdUnique`
|
|
425
|
+
* payload field (or before conversations exist), we defer `custEv_chatView` until the store
|
|
426
|
+
* has `formJoinIDUnique` and an active conversation.
|
|
427
|
+
*/ this.chatViewDeferredUserIntent = false, // Sub Stores (lazy-loaded via $fromImport)
|
|
428
|
+
this.meetingStore = $fromImport(()=>import("./meeting_store.js"), this), this.fieldRequestStore = $fromImport(()=>import("./field_request_store.js"), this), this.callStore = $fromImport(()=>import("./call_store.js"), this), this.pounceTrackingStore = $fromImport(()=>this.featureFlags.piperXAgent ? import("./pounce_tracking_store_v2.js") : import("./pounce_tracking_store.js"), this), this.slidesStore = $fromImport(()=>import("./slides_store.js"), this), this.presentationsStore = $fromImport(()=>import("./presentations_store.js"), this), this.decksStore = $fromImport(()=>import("./decks_store.js"), this), this.screenShareStore = $fromImport(()=>import("./screen_share_store.js"), this), this.waitingStore = $fromImport(()=>import("./waiting_store.js"), this), this.websiteNavigationStore = $fromImport(()=>import("./website_navigation_store.js"), this), this.debugScenarioStore = $fromImport(()=>import("./debug_multimodal_scenario_store.js"), this), this.postCallSurveyStore = $fromImport(()=>import("./post_call_survey_store.js"), this), /**
|
|
429
|
+
* Pre-offer booker SLI (intent -> offer -> shown). Owned here, not by
|
|
430
|
+
* `meetingStore`, for two reasons: the intent signal arrives on this store's
|
|
431
|
+
* message stream, and `meetingStore` is lazily imported — routing the arm
|
|
432
|
+
* through it would make the intent timestamp wait on a dynamic import,
|
|
433
|
+
* inflating the very latency we are measuring. `meetingStore` resolves it
|
|
434
|
+
* (`markOffered` / `markShown`) once the offer lands.
|
|
435
|
+
*/ this.bookerIntentTracker = new BookerIntentTracker({
|
|
436
|
+
surface: BOOKER_SURFACE.MULTIMODAL,
|
|
437
|
+
log: (params)=>serverLogger.serverLog(params)
|
|
438
|
+
}), // Translation store is directly instantiated (not lazy-loaded) because
|
|
439
|
+
// translations are needed immediately when the app renders
|
|
440
|
+
this.translationStore = new TranslationStore(this), this.dockingStore = new DockingStore(this), this.artifactStore = new ArtifactStore(this), // Mutable State
|
|
441
|
+
this.isVisitorBlocked = signals_core_module_d(false), /** Piper form join ID for this page view; same for all custEv_* events on the page. */ this.formJoinIDUnique = signals_core_module_d(null), /** Tracks fired custEv_contentImpression IDs; bounded to prevent unbounded growth. */ this.impressedMessageIds = new BoundedSet(RootStore.IMPRESSED_MESSAGE_IDS_MAX_SIZE), this.agentsAvailable = signals_core_module_d(false), this.companyName = signals_core_module_d(), this.disableTitleFlash = signals_core_module_d(false), this.messengerEventListener = null, this.callInProgress = signals_core_module_w(()=>{
|
|
442
|
+
var _this_callStore_value;
|
|
443
|
+
return ((_this_callStore_value = this.callStore.value) == null ? void 0 : _this_callStore_value.dailyCall.value) != null;
|
|
444
|
+
}), this.typingIndicatorState = signals_core_module_d(false), this.activeToolCall = signals_core_module_d(null), this.pounceResponseStartTimestamp = null, this.callInitiatedAt = null, // Whether the team has configured a custom voice greeting (set by the admin
|
|
445
|
+
// via the multimodal voice greeting setting). When true, the initial greeting
|
|
446
|
+
// can be multiple sentences and varies per team, which affects greeting-sensitive
|
|
447
|
+
// timing metrics. Used to tag those metrics so dashboards can filter them out.
|
|
448
|
+
this.hasCustomVoiceGreeting = false, this.pounceTrackingLoadStarted = false, this.lastVisitorMessageSentAt = null, this.turnCounterEmitted = false, // Separate from the counter: lets a late reply still record latency after the
|
|
449
|
+
// timer resolved availability.
|
|
450
|
+
this.turnReplyHandled = false, this.responseTimeoutId = null, // A timeout that has been counted but whose metric is still waiting on the
|
|
451
|
+
// server's cause report (see holdTimeoutForCause).
|
|
452
|
+
this.timeoutHoldId = null, this.heldTimeoutFlush = null, // Dedups a late reply against a turn the timer already resolved.
|
|
453
|
+
this.turnToken = 0, // clientId of the message that opened the current turn; used to ignore a
|
|
454
|
+
// stale `textResponseEnded` event from a turn we've already superseded.
|
|
455
|
+
this.turnMessageClientId = null, // Latest cause the server reported for the outstanding turn, if any. Read only
|
|
456
|
+
// when the turn times out.
|
|
457
|
+
this.turnCause = null, // Whether the agent reached for a tool on this turn, which costs the call plus
|
|
458
|
+
// another model round trip to use what it returned. Reported as it happens, so a
|
|
459
|
+
// turn that never finishes is still on the right side of the split.
|
|
460
|
+
this.turnHasToolCall = false, // Tab hidden mid-turn → bucket as `backgrounded` (not a false `timeout`) and skip its latency.
|
|
461
|
+
this.turnObscured = false, this.turnCallActive = false, this.conversations = signals_core_module_d([]), this.messages = createMessagesSignal(), this.pounceCandidates = signals_core_module_d(null), this.isVideoLoading = signals_core_module_d(false), this.isBlurredVideoActive = signals_core_module_d(false), /**
|
|
462
|
+
* Which provider's track Coffee has committed to as the avatar video for this call.
|
|
463
|
+
* Null until Coffee reports one (an older Coffee never will), which callers treat as
|
|
464
|
+
* "fall back to the per-team flag".
|
|
465
|
+
*/ this.activeAvatarProvider = signals_core_module_d(null), this.conversationModeFailover = signals_core_module_d({
|
|
466
|
+
voiceDisabled: false,
|
|
467
|
+
videoDisabled: false
|
|
468
|
+
}), this.modalityPreference = signals_core_module_d(null), this.dailyCallObjectBundleUrl = signals_core_module_d(null), this.dailyWsProxyUrl = signals_core_module_d(null), this.privacyDisclosures = signals_core_module_d(null), this.privacyDisclosureShown = signals_core_module_d(false), this.privacyDisclosureConsented = signals_core_module_d(false), this.cookieConsent = signals_core_module_d(null), this.cookieConsentGiven = signals_core_module_d(false), this.showCookieConsentModal = signals_core_module_d(false), this.pendingMessage = signals_core_module_d(null), // A call start awaiting cookie consent. When set, it also carries the optional
|
|
469
|
+
// trigger context (e.g. from a "present_video" card) so a single signal survives
|
|
470
|
+
// the consent re-entry path; the consent handlers read it back and re-invoke
|
|
471
|
+
// startCall with the stored context. `null` means no pending call.
|
|
472
|
+
this.pendingCallStart = signals_core_module_d(null), // Set when a visitor-typed text send is rejected server-side (e.g. the 2000-character
|
|
473
|
+
// limit in Message::Text) so the composer can tell the visitor their message never went
|
|
474
|
+
// through, rather than leaving them to wait on a reply that isn't coming. Cleared at the
|
|
475
|
+
// start of the next send attempt.
|
|
476
|
+
this.sendTextError = signals_core_module_d(null), // Loading overlay state (for lightbox / messenger loading screens)
|
|
477
|
+
this.loadingState = signals_core_module_d(types_LoadingState.NONE), this.loadingUuid = signals_core_module_d(null), // Element conversation state (for lightbox_gather from smart buttons)
|
|
478
|
+
this.elementConversation = signals_core_module_d(null), // Element conversation meeting offer (for calendar_event_offer from smart forms)
|
|
479
|
+
this.elementConversationOffer = signals_core_module_d(null), // Element conversation text message (for LightboxMessage step from offer/form bots)
|
|
480
|
+
this.elementConversationTextMessage = signals_core_module_d(null), // True while waiting for the backend to respond after submitLightboxGather
|
|
481
|
+
this.elementConversationLoading = signals_core_module_d(false), // Bumped whenever the element-conversation loading phase (re)starts. The inline
|
|
482
|
+
// meeting booker uses this to distinguish "the experience is still producing a
|
|
483
|
+
// conversation" (a fresh loading tick arrived after an endLoadingState) from
|
|
484
|
+
// "it ended without one", so it doesn't clear the spinner mid-flow (a white
|
|
485
|
+
// flash) while a slow booker is still computing its offer.
|
|
486
|
+
this.elementConversationLoadingSeq = signals_core_module_d(0), // Set by restoreGatherFromOffer ("Not you?") while a change_identity event is
|
|
487
|
+
// in flight and we are waiting for the backend to re-run the gather step. For a
|
|
488
|
+
// manual booker the backend echoes a stale calendar_event_offer (a known
|
|
489
|
+
// change_identity limitation) *before* the fresh lightbox_gather, both as
|
|
490
|
+
// separate elementMessage broadcasts in that order. Without this flag the offer
|
|
491
|
+
// flips elementConversationLoading to false and the gather that follows is
|
|
492
|
+
// dropped by the loading guard, stranding the visitor on an email-less offer
|
|
493
|
+
// instead of the email step. While set, an incoming offer is ignored; the flag
|
|
494
|
+
// clears when the gather (or a text/end) arrives, or the element state resets.
|
|
495
|
+
this.awaitingIdentityChangeRegather = signals_core_module_d(false), // True when a manual-experience meeting booker is being rendered inline into a
|
|
496
|
+
// page container div (inline_meeting_booker flag + a tagged container present).
|
|
497
|
+
// While set, the full-screen LightboxExperience suppresses itself so the booker
|
|
498
|
+
// only appears in the container, not as the default full-screen overlay.
|
|
499
|
+
this.inlineMeetingBookerActive = signals_core_module_d(false), // Computed: select the right disclosure based on effective locale
|
|
500
|
+
this.activePrivacyDisclosure = signals_core_module_w(()=>{
|
|
501
|
+
var _disclosures_;
|
|
502
|
+
var _disclosuresData_disclosures;
|
|
503
|
+
const disclosuresData = this.privacyDisclosures.value;
|
|
504
|
+
if (!(disclosuresData == null ? void 0 : (_disclosuresData_disclosures = disclosuresData.disclosures) == null ? void 0 : _disclosuresData_disclosures.length)) return null;
|
|
505
|
+
const locale = this.translationStore.effectiveLocale.value;
|
|
506
|
+
const { disclosures } = disclosuresData;
|
|
507
|
+
// First try exact locale match
|
|
508
|
+
const exactMatch = disclosures.find((d)=>d.locale === locale);
|
|
509
|
+
if (exactMatch) return exactMatch;
|
|
510
|
+
// Fallback to English
|
|
511
|
+
const englishMatch = disclosures.find((d)=>d.locale === "en");
|
|
512
|
+
if (englishMatch) return englishMatch;
|
|
513
|
+
// Return first available if no English
|
|
514
|
+
return (_disclosures_ = disclosures[0]) != null ? _disclosures_ : null;
|
|
515
|
+
}), // Computed: select the right cookie consent disclosure based on visitor location and locale.
|
|
516
|
+
// Resolution: state-specific > country-specific > ALL, with locale match then English fallback.
|
|
517
|
+
this.activeCookieDisclosure = signals_core_module_w(()=>{
|
|
518
|
+
var _cookieConsentData_disclosures;
|
|
519
|
+
const cookieConsentData = this.cookieConsent.value;
|
|
520
|
+
if (!(cookieConsentData == null ? void 0 : (_cookieConsentData_disclosures = cookieConsentData.disclosures) == null ? void 0 : _cookieConsentData_disclosures.length)) return null;
|
|
521
|
+
const locale = this.translationStore.effectiveLocale.value;
|
|
522
|
+
const { disclosures, visitorCountryCode, visitorStateCode } = cookieConsentData;
|
|
523
|
+
const findByLocation = (countryCode, stateCode)=>{
|
|
524
|
+
const localeMatch = disclosures.find((d)=>{
|
|
525
|
+
var _d_stateCode;
|
|
526
|
+
return d.countryCode === countryCode && ((_d_stateCode = d.stateCode) != null ? _d_stateCode : null) === (stateCode != null ? stateCode : null) && d.locale === locale;
|
|
527
|
+
});
|
|
528
|
+
if (localeMatch) return localeMatch;
|
|
529
|
+
const englishMatch = disclosures.find((d)=>{
|
|
530
|
+
var _d_stateCode;
|
|
531
|
+
return d.countryCode === countryCode && ((_d_stateCode = d.stateCode) != null ? _d_stateCode : null) === (stateCode != null ? stateCode : null) && d.locale === "en";
|
|
532
|
+
});
|
|
533
|
+
if (englishMatch) return englishMatch;
|
|
534
|
+
return null;
|
|
535
|
+
};
|
|
536
|
+
if (visitorStateCode && visitorCountryCode) {
|
|
537
|
+
const stateMatch = findByLocation(visitorCountryCode, visitorStateCode);
|
|
538
|
+
if (stateMatch) return stateMatch;
|
|
539
|
+
}
|
|
540
|
+
if (visitorCountryCode) {
|
|
541
|
+
const countryMatch = findByLocation(visitorCountryCode, null);
|
|
542
|
+
if (countryMatch) return countryMatch;
|
|
543
|
+
}
|
|
544
|
+
return findByLocation("ALL", null);
|
|
545
|
+
}), this.needsCookieConsent = signals_core_module_w(()=>{
|
|
546
|
+
var _cookieConsentData_consentedDisclosureIds;
|
|
547
|
+
if (!this.featureFlags.piperXCookieConsent) return false;
|
|
548
|
+
const cookieConsentData = this.cookieConsent.value;
|
|
549
|
+
if (!cookieConsentData) return !this.initialized.value;
|
|
550
|
+
const activeDisclosure = this.activeCookieDisclosure.value;
|
|
551
|
+
if (activeDisclosure && ((_cookieConsentData_consentedDisclosureIds = cookieConsentData.consentedDisclosureIds) == null ? void 0 : _cookieConsentData_consentedDisclosureIds.includes(activeDisclosure.id))) return false;
|
|
552
|
+
if (this.cookieConsentGiven.value) return false;
|
|
553
|
+
return !!this.activeCookieDisclosure.value;
|
|
554
|
+
}), // Cold start questions
|
|
555
|
+
this.coldStartQuestions = signals_core_module_d([]), this.coldStartVisibleLimit = signals_core_module_d(3), // Team-configured inactivity threshold (ms) before a follow-up pounce fires.
|
|
556
|
+
// Falls back to the hardcoded ACTIVE_THRESHOLD_MS default if not set.
|
|
557
|
+
this.pounceInactivityThresholdMs = signals_core_module_d(null), /**
|
|
558
|
+
* Debug mode for multimodal development.
|
|
559
|
+
* Enabled by adding `?q_debug_multimodal=1` to the URL.
|
|
560
|
+
* When enabled, allows previewing call UI states without starting a real call.
|
|
561
|
+
*/ this.isDebugMultimodal = signals_core_module_w(()=>{
|
|
562
|
+
try {
|
|
563
|
+
var _window_location;
|
|
564
|
+
if (typeof window === "undefined") return false;
|
|
565
|
+
if (!((_window_location = window.location) == null ? void 0 : _window_location.search)) return false;
|
|
566
|
+
return new URLSearchParams(window.location.search).get("q_debug_multimodal") === "1";
|
|
567
|
+
} catch (unused) {
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
}), this.joinCallForPresentation = ()=>{
|
|
571
|
+
this.getCallUiAdapter().actions.startCall();
|
|
572
|
+
}, this.activeConversation = createActiveConversationSignal(this.conversations), this.userEngaged = signals_core_module_w(()=>!!this.activeConversation.user.value), this.isMessageStreamingEnabled = signals_core_module_w(()=>{
|
|
573
|
+
return !this.getCallUiAdapter().state.isInCall.value;
|
|
574
|
+
}), // UIStore must be initialized after messages, activeConversation, callStore
|
|
575
|
+
// since createLayoutSignal uses those during construction.
|
|
576
|
+
this.uiStore = new UIStore(this), /**
|
|
577
|
+
* Emits custEv_chatView piper web tracking event when eligible.
|
|
578
|
+
* Fires once per conversation after active user intent (expand launcher, focus composer,
|
|
579
|
+
* CTA/canned prompt, voice CTA, etc.) — not on page load when the widget restores opened.
|
|
580
|
+
* Pass `{ fromUserIntent: true }` from UI entry points so we can defer until initState fills
|
|
581
|
+
* `formJoinIdUnique` (stored as `formJoinIDUnique`) and conversations when intent is early.
|
|
582
|
+
* Only emits when `formJoinIDUnique` is set so the event matches other custEv_* events.
|
|
583
|
+
*/ this.emitChatViewIfEligible = (options)=>{
|
|
584
|
+
var _ref, _this_formJoinIDUnique_value;
|
|
585
|
+
var _this_config_featureFlags, _this_config, _this_activeConversation_value;
|
|
586
|
+
const fromUserIntent = (_ref = options == null ? void 0 : options.fromUserIntent) != null ? _ref : false;
|
|
587
|
+
if (!((_this_config = this.config) == null ? void 0 : (_this_config_featureFlags = _this_config.featureFlags) == null ? void 0 : _this_config_featureFlags.piperWebTrackingEvents)) return;
|
|
588
|
+
if (this.userEngaged.value) {
|
|
589
|
+
this.chatViewDeferredUserIntent = false;
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
const activeConvId = (_this_activeConversation_value = this.activeConversation.value) == null ? void 0 : _this_activeConversation_value.id;
|
|
593
|
+
if (!activeConvId) {
|
|
594
|
+
// Mirror !formJoinIDUnique: only set defer on user intent; do not clear defer here —
|
|
595
|
+
// system calls before conversations exist must not wipe a pending user intent.
|
|
596
|
+
if (fromUserIntent) {
|
|
597
|
+
this.chatViewDeferredUserIntent = true;
|
|
598
|
+
}
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
const hasFired = this.messages.peek().some((m)=>m.type === "event" && m.eventType === "chatViewFired" && m.conversationId === activeConvId);
|
|
602
|
+
if (hasFired) {
|
|
603
|
+
this.chatViewDeferredUserIntent = false;
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
const formJoinIDUnique = (_this_formJoinIDUnique_value = this.formJoinIDUnique.value) != null ? _this_formJoinIDUnique_value : "";
|
|
607
|
+
if (!formJoinIDUnique) {
|
|
608
|
+
if (fromUserIntent) {
|
|
609
|
+
this.chatViewDeferredUserIntent = true;
|
|
610
|
+
}
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
this.chatViewDeferredUserIntent = false;
|
|
614
|
+
this.sendMessage({
|
|
615
|
+
type: "event",
|
|
616
|
+
eventType: "chatViewFired"
|
|
617
|
+
});
|
|
618
|
+
const data = buildCustEvChatViewPayload(this.piperConversationId, formJoinIDUnique);
|
|
619
|
+
this.channel.emitPiperWebTrackingEvent({
|
|
620
|
+
name: "custEv_chatView",
|
|
621
|
+
data
|
|
622
|
+
});
|
|
623
|
+
}, /**
|
|
624
|
+
* Emits custEv_contentImpression piper web tracking event.
|
|
625
|
+
* Used when links or rich content become visible in the viewport.
|
|
626
|
+
* Payload matches cold-start content impressions (no formJoinIDUnique).
|
|
627
|
+
*/ this.emitContentImpression = (modules)=>{
|
|
628
|
+
var _this_config_featureFlags, _this_config;
|
|
629
|
+
if (!((_this_config = this.config) == null ? void 0 : (_this_config_featureFlags = _this_config.featureFlags) == null ? void 0 : _this_config_featureFlags.piperWebTrackingEvents)) return;
|
|
630
|
+
if (modules.length === 0) return;
|
|
631
|
+
const data = buildCustEvContentImpressionPayload(modules);
|
|
632
|
+
this.channel.emitPiperWebTrackingEvent({
|
|
633
|
+
name: "custEv_contentImpression",
|
|
634
|
+
data
|
|
635
|
+
});
|
|
636
|
+
}, this.handleTurnVisibilityChange = ()=>{
|
|
637
|
+
if (this.isDocumentHidden() && this.lastVisitorMessageSentAt !== null && !this.turnReplyHandled) {
|
|
638
|
+
this.turnObscured = true;
|
|
639
|
+
}
|
|
640
|
+
}, // Actions — delegated to UIStore
|
|
641
|
+
this.setUIState = (uiState)=>{
|
|
642
|
+
this.uiStore.setUIState(uiState);
|
|
643
|
+
}, this.setDisplayMode = (displayMode)=>{
|
|
644
|
+
this.uiStore.setDisplayMode(displayMode);
|
|
645
|
+
}, this.setOpenedDisplayMode = ()=>{
|
|
646
|
+
this.uiStore.setOpenedDisplayMode();
|
|
647
|
+
}, this.updateOpenedDisplayMode = (openedDisplayMode)=>{
|
|
648
|
+
this.uiStore.updateOpenedDisplayMode(openedDisplayMode);
|
|
649
|
+
}, this.setConversationMode = (conversationMode)=>{
|
|
650
|
+
this.uiStore.setConversationMode(conversationMode);
|
|
651
|
+
}, this.toggleVideoMode = (mode)=>{
|
|
652
|
+
this.uiStore.toggleVideoMode(mode);
|
|
653
|
+
}, this.setMaximizedState = (state)=>{
|
|
654
|
+
this.uiStore.setMaximizedState(state);
|
|
655
|
+
}, this.clearMaximizedState = (param)=>{
|
|
656
|
+
let { when } = param === void 0 ? {} : param;
|
|
657
|
+
this.uiStore.clearMaximizedState({
|
|
658
|
+
when
|
|
659
|
+
});
|
|
660
|
+
}, this.sendTypingEvent = (content)=>{
|
|
661
|
+
this.channel.perform("update_visitor_typing", {
|
|
662
|
+
content
|
|
663
|
+
});
|
|
664
|
+
}, this.updateConversationModeFailover = (failover)=>{
|
|
665
|
+
signals_core_module_r(()=>{
|
|
666
|
+
this.conversationModeFailover.value = _extends({}, this.conversationModeFailover.value, failover);
|
|
667
|
+
if (this.conversationModeFailover.value.voiceDisabled) {
|
|
668
|
+
this.cancelCallOrConnection({
|
|
669
|
+
closeWidget: false
|
|
670
|
+
});
|
|
671
|
+
} else if (this.conversationModeFailover.value.videoDisabled) {
|
|
672
|
+
this.isVideoLoading.value = false;
|
|
673
|
+
}
|
|
674
|
+
this.uiStore.ensureValidUIState();
|
|
675
|
+
});
|
|
676
|
+
}, this.sendMessage = async (payload, param)=>{
|
|
677
|
+
let { optimistic = true, conversationId } = param === void 0 ? {} : param;
|
|
678
|
+
var _ref, _ref1, _ref2;
|
|
679
|
+
var _this_elementConversationOffer_value, _this_activeConversation_value, _this_elementConversation_value;
|
|
680
|
+
const id = esm_browser_v4();
|
|
681
|
+
const resolvedConversationId = (_ref = (_ref1 = (_ref2 = conversationId != null ? conversationId : (_this_elementConversationOffer_value = this.elementConversationOffer.value) == null ? void 0 : _this_elementConversationOffer_value.conversationId) != null ? _ref2 : (_this_activeConversation_value = this.activeConversation.value) == null ? void 0 : _this_activeConversation_value.id) != null ? _ref1 : (_this_elementConversation_value = this.elementConversation.value) == null ? void 0 : _this_elementConversation_value.id) != null ? _ref : "";
|
|
682
|
+
const message = _extends({}, payload, {
|
|
683
|
+
id,
|
|
684
|
+
clientId: id,
|
|
685
|
+
optimistic,
|
|
686
|
+
conversationId: resolvedConversationId,
|
|
687
|
+
createdAt: new Date().toISOString(),
|
|
688
|
+
source: "standard",
|
|
689
|
+
multimodalDisplayMode: this.displayMode.value
|
|
690
|
+
});
|
|
691
|
+
if (optimistic && payload.type !== "field_entries") {
|
|
692
|
+
this.addMessage(message);
|
|
693
|
+
}
|
|
694
|
+
const { isInCall, isVideoLoading } = this.getCallUiAdapter().state;
|
|
695
|
+
if (!isInCall.peek() && !isVideoLoading.peek() && payload.type === "text" && payload.isOwn && this.uiStore.allowedConversationModes.video.peek()) {
|
|
696
|
+
this.setUIState({
|
|
697
|
+
userHasIgnoredMultimodal: true
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
const isVisitorAction = payload.type === "text" && "isOwn" in payload && payload.isOwn || payload.type === "field_entry" && "isOwn" in payload && payload.isOwn || payload.type === "calendar_event_pick";
|
|
701
|
+
if (message.type !== "event") {
|
|
702
|
+
// Visitor actions (text, field entry, calendar pick) imply chat-view intent even when
|
|
703
|
+
// the widget is already open — e.g. first interaction is booking while in SIDEBAR.
|
|
704
|
+
if (isVisitorAction) {
|
|
705
|
+
this.emitChatViewIfEligible({
|
|
706
|
+
fromUserIntent: true
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
// Don't pop the full messenger open beside the inline meeting booker: its
|
|
710
|
+
// container has no overlay, so opening the messenger would be an unwanted
|
|
711
|
+
// visible side-effect (same reasoning as openMessengerForElementConversation).
|
|
712
|
+
// The inline gather submit (field_entries) and time confirm
|
|
713
|
+
// (calendar_event_pick) route through here, so guard them too.
|
|
714
|
+
if (!this.inlineMeetingBookerActive.value) {
|
|
715
|
+
this.setOpenedDisplayMode();
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (payload.type === "text") {
|
|
719
|
+
this.recordPrivacyDisclosureConsent(id);
|
|
720
|
+
}
|
|
721
|
+
// Known limitation: we track a single outstanding turn (first-message-wins).
|
|
722
|
+
// If a turn times out and the visitor sends again before its late reply lands,
|
|
723
|
+
// that reply is attributed to the newer turn (replies carry no link to the
|
|
724
|
+
// visitor message they answer, so we can't match them by anything but timing).
|
|
725
|
+
// Rare (needs a >budget reply plus a resend in the gap) and self-correcting in
|
|
726
|
+
// aggregate, so we accept it rather than track per-message turns.
|
|
727
|
+
const startedTurnToken = isVisitorAction && (this.lastVisitorMessageSentAt === null || this.turnCounterEmitted) ? this.startTextTurn(id) : null;
|
|
728
|
+
const result = await this.channel.perform("send_message", snakify(message));
|
|
729
|
+
if (startedTurnToken !== null) {
|
|
730
|
+
this.handleTextTurnSendResult(result, startedTurnToken);
|
|
731
|
+
}
|
|
732
|
+
return result;
|
|
733
|
+
}, /**
|
|
734
|
+
* Backend closed a turn with no reply on purpose (guardrail block, rep
|
|
735
|
+
* takeover, blank output, ...). Close the turn under a distinct status so the
|
|
736
|
+
* budget timer doesn't fire a false `timeout` and dashboards can exclude it
|
|
737
|
+
* from the success rate. Guarded on `turnCounterEmitted` so a turn already
|
|
738
|
+
* resolved by a real reply (or the timer) is left untouched.
|
|
739
|
+
*/ this.handleTextResponseEnded = (payload)=>{
|
|
740
|
+
var _ref;
|
|
741
|
+
if (this.lastVisitorMessageSentAt === null || this.turnCounterEmitted) return;
|
|
742
|
+
// Ignore a late event from a turn we've already superseded: it must belong
|
|
743
|
+
// to the message that opened the current outstanding turn.
|
|
744
|
+
if ((payload == null ? void 0 : payload.clientId) !== this.turnMessageClientId) return;
|
|
745
|
+
const reason = (_ref = payload == null ? void 0 : payload.reason) != null ? _ref : "";
|
|
746
|
+
const status = RootStore.isTextResponseEndedReason(reason) ? reason : "suppressed";
|
|
747
|
+
this.clearResponseTimeout();
|
|
748
|
+
metrics.emitIncrement("multimodal_text_response", _extends({
|
|
749
|
+
status,
|
|
750
|
+
call_active: this.turnCallActiveTag
|
|
751
|
+
}, this.turnCause ? {
|
|
752
|
+
cause: this.turnCause
|
|
753
|
+
} : {}));
|
|
754
|
+
// No reply is coming, so close the turn entirely (no latency recorded).
|
|
755
|
+
this.turnCounterEmitted = true;
|
|
756
|
+
this.turnReplyHandled = true;
|
|
757
|
+
this.turnToken++;
|
|
758
|
+
}, /**
|
|
759
|
+
* The server telling us what this turn is spending its time on, sent when the
|
|
760
|
+
* response budget expires (and again afterwards for turns that finished slow).
|
|
761
|
+
* Purely an annotation: it never resolves or ends a turn, it only supplies the
|
|
762
|
+
* `cause` tag the timeout would otherwise lack.
|
|
763
|
+
*/ this.handleTextTurnDiagnostics = (payload)=>{
|
|
764
|
+
var _ref;
|
|
765
|
+
if ((payload == null ? void 0 : payload.clientId) !== this.turnMessageClientId) return;
|
|
766
|
+
if ((payload == null ? void 0 : payload.hasToolCall) === true) this.turnHasToolCall = true;
|
|
767
|
+
// A tool report carries no cause and arrives mid-turn, so it must not release a
|
|
768
|
+
// held timeout: that would emit the metric before the cause it is waiting for.
|
|
769
|
+
const cause = (_ref = payload == null ? void 0 : payload.cause) != null ? _ref : "";
|
|
770
|
+
if (!RootStore.isTurnCause(cause)) return;
|
|
771
|
+
// Preserve the first concrete explanation for this client turn; `unknown`
|
|
772
|
+
// is a placeholder and may be replaced when a real cause arrives.
|
|
773
|
+
this.turnCause = this.turnCause && this.turnCause !== "unknown" ? this.turnCause : cause;
|
|
774
|
+
// This report is what a held timeout was waiting for; no reason to sit out
|
|
775
|
+
// the rest of the window.
|
|
776
|
+
this.flushHeldTimeout();
|
|
777
|
+
}, this.sendVisitorText = async (text, extra)=>{
|
|
778
|
+
this.sendTextError.value = null;
|
|
779
|
+
if (this.featureFlags.piperXCookieConsent && this.needsCookieConsent.value) {
|
|
780
|
+
this.pendingMessage.value = _extends({
|
|
781
|
+
text
|
|
782
|
+
}, extra);
|
|
783
|
+
this.setOpenedDisplayMode();
|
|
784
|
+
this.showCookieConsentModal.value = true;
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
await this.sendVisitorTextWithoutCookieConsent(text, extra);
|
|
788
|
+
}, this.markMessagesAsRead = (messages)=>{
|
|
789
|
+
const messagesToMarkAsRead = messages != null ? messages : this.messages.unread.value;
|
|
790
|
+
const messageIds = messagesToMarkAsRead.filter((m)=>!m.isRead).map((m)=>m.id);
|
|
791
|
+
if (messageIds.length === 0) return;
|
|
792
|
+
this.channel.perform("mark_messages_as_read", snakify({
|
|
793
|
+
messageIds
|
|
794
|
+
}));
|
|
795
|
+
signals_core_module_r(()=>{
|
|
796
|
+
for (const message of messagesToMarkAsRead){
|
|
797
|
+
this.updateMessage(message, {
|
|
798
|
+
isRead: true
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
}, this.dismissPounce = ()=>{
|
|
803
|
+
if (this.messages.pounceActive.peek()) {
|
|
804
|
+
this.sendMessage({
|
|
805
|
+
type: "event",
|
|
806
|
+
eventType: "pounce_dismissed"
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
this.markMessagesAsRead();
|
|
810
|
+
}, this.fireMessagesDismissed = ()=>{
|
|
811
|
+
this.messengerEventListener == null ? void 0 : this.messengerEventListener.call(this, "messagesDismissed");
|
|
812
|
+
}, this.recordPrivacyDisclosure = ()=>{
|
|
813
|
+
var _this_activeConversation_value, _this_activeConversation_value1;
|
|
814
|
+
const disclosure = this.activePrivacyDisclosure.value;
|
|
815
|
+
if (!disclosure || !((_this_activeConversation_value = this.activeConversation.value) == null ? void 0 : _this_activeConversation_value.id)) return;
|
|
816
|
+
// Send the exact disclosure ID that was shown to the user
|
|
817
|
+
this.channel.perform("record_privacy_disclosure_shown", {
|
|
818
|
+
disclosure_id: disclosure.id,
|
|
819
|
+
conversation_id: (_this_activeConversation_value1 = this.activeConversation.value) == null ? void 0 : _this_activeConversation_value1.id
|
|
820
|
+
});
|
|
821
|
+
this.privacyDisclosureShown.value = true;
|
|
822
|
+
}, this.recordPrivacyDisclosureConsent = (clientId)=>{
|
|
823
|
+
if (this.privacyDisclosureConsented.value) return;
|
|
824
|
+
if (!this.privacyDisclosures.value) return;
|
|
825
|
+
this.channel.perform("record_privacy_disclosure_consent_given", {
|
|
826
|
+
client_id: clientId
|
|
827
|
+
});
|
|
828
|
+
this.privacyDisclosureConsented.value = true;
|
|
829
|
+
}, // Cookie consent actions
|
|
830
|
+
this.acceptCookieConsent = ()=>{
|
|
831
|
+
if (!this.featureFlags.piperXCookieConsent) return;
|
|
832
|
+
const pending = this.pendingMessage.value;
|
|
833
|
+
const disclosure = this.activeCookieDisclosure.value;
|
|
834
|
+
const cookieConsentData = this.cookieConsent.value;
|
|
835
|
+
if (!disclosure || !cookieConsentData) return;
|
|
836
|
+
const pendingCall = this.pendingCallStart.value;
|
|
837
|
+
signals_core_module_r(()=>{
|
|
838
|
+
this.channel.perform("record_cookie_consent", {
|
|
839
|
+
messenger_theme_id: cookieConsentData.messengerThemeId,
|
|
840
|
+
cookie_disclosure_id: disclosure.id
|
|
841
|
+
});
|
|
842
|
+
this.cookieConsentGiven.value = true;
|
|
843
|
+
this.showCookieConsentModal.value = false;
|
|
844
|
+
if (pending) {
|
|
845
|
+
const { text } = pending, extra = _object_without_properties_loose(pending, [
|
|
846
|
+
"text"
|
|
847
|
+
]);
|
|
848
|
+
this.pendingMessage.value = null;
|
|
849
|
+
void this.sendVisitorTextWithoutCookieConsent(text, extra);
|
|
850
|
+
}
|
|
851
|
+
this.pendingCallStart.value = null;
|
|
852
|
+
});
|
|
853
|
+
if (pendingCall) {
|
|
854
|
+
var _pendingCall_context;
|
|
855
|
+
this.startCall((_pendingCall_context = pendingCall.context) != null ? _pendingCall_context : undefined);
|
|
856
|
+
}
|
|
857
|
+
}, this.denyCookieConsent = ()=>{
|
|
858
|
+
if (!this.featureFlags.piperXCookieConsent) return;
|
|
859
|
+
signals_core_module_r(()=>{
|
|
860
|
+
this.showCookieConsentModal.value = false;
|
|
861
|
+
this.pendingMessage.value = null;
|
|
862
|
+
// Clearing the pending call also discards any stashed trigger context, so a
|
|
863
|
+
// later regular call is never mistaken for a presentation start.
|
|
864
|
+
this.pendingCallStart.value = null;
|
|
865
|
+
this.setDisplayMode(types_DisplayMode.CLOSED);
|
|
866
|
+
});
|
|
867
|
+
}, this.flushPendingCookieConsentActions = ()=>{
|
|
868
|
+
if (!this.featureFlags.piperXCookieConsent) return;
|
|
869
|
+
const pending = this.pendingMessage.value;
|
|
870
|
+
const pendingCall = this.pendingCallStart.value;
|
|
871
|
+
if (!pending && !pendingCall) return;
|
|
872
|
+
if (this.needsCookieConsent.value) {
|
|
873
|
+
this.setOpenedDisplayMode();
|
|
874
|
+
this.showCookieConsentModal.value = true;
|
|
875
|
+
} else {
|
|
876
|
+
if (pending) {
|
|
877
|
+
const { text } = pending, extra = _object_without_properties_loose(pending, [
|
|
878
|
+
"text"
|
|
879
|
+
]);
|
|
880
|
+
this.pendingMessage.value = null;
|
|
881
|
+
void this.sendVisitorTextWithoutCookieConsent(text, extra);
|
|
882
|
+
}
|
|
883
|
+
if (pendingCall) {
|
|
884
|
+
var _pendingCall_context;
|
|
885
|
+
this.pendingCallStart.value = null;
|
|
886
|
+
this.startCall((_pendingCall_context = pendingCall.context) != null ? _pendingCall_context : undefined);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}, this.updateMessage = (message, payload)=>{
|
|
890
|
+
this.messages.value = this.messages.value.map((m)=>m.id === message.id ? this.mergeMessage(m, payload) : m);
|
|
891
|
+
}, this.mergeMessage = (existing, incoming)=>_extends({}, existing, incoming, {
|
|
892
|
+
isRead: existing.isRead || incoming.isRead
|
|
893
|
+
}), this.updateInputMessage = async (message)=>{
|
|
894
|
+
await this.fieldRequestStore.load();
|
|
895
|
+
signals_core_module_r(()=>{
|
|
896
|
+
this.setOpenedDisplayMode();
|
|
897
|
+
this.setUIState({
|
|
898
|
+
inputMessageId: message.id
|
|
899
|
+
});
|
|
900
|
+
});
|
|
901
|
+
}, /** Opens the booker in the artifact overlay, or closes it if it is already showing this offer. */ this.toggleMeetingBookerArtifact = async (message)=>{
|
|
902
|
+
if (this.artifact.messageId.peek() === message.id) {
|
|
903
|
+
this.clearArtifact();
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
await this.artifactStore.presentArtifactMessage(message);
|
|
907
|
+
}, /** Dismisses the booker on whichever surface is showing it. */ this.closeMeetingBooker = ()=>{
|
|
908
|
+
signals_core_module_r(()=>{
|
|
909
|
+
this.clearMeetingArtifact();
|
|
910
|
+
this.elementConversationOffer.value = null;
|
|
911
|
+
this.elementConversationLoading.value = false;
|
|
912
|
+
// NOTE: elementConversationTextMessage is intentionally NOT cleared here —
|
|
913
|
+
// see the note in clearInputMessage.
|
|
914
|
+
});
|
|
915
|
+
}, /** Tears down the artifact overlay only when it is the meeting booker. */ this.clearMeetingArtifact = ()=>{
|
|
916
|
+
var _this_artifact_message_peek;
|
|
917
|
+
if (((_this_artifact_message_peek = this.artifact.message.peek()) == null ? void 0 : _this_artifact_message_peek.type) !== "calendar_event_offer") return;
|
|
918
|
+
this.clearArtifact();
|
|
919
|
+
}, /** Hands an incoming message to the overlay. See ArtifactStore#queueArtifactMessage. */ this.queueArtifactMessage = (message, options)=>{
|
|
920
|
+
if (options === void 0) options = {};
|
|
921
|
+
return this.artifactStore.queueArtifactMessage(message, options);
|
|
922
|
+
}, /** Re-opens an artifact the visitor asked for from the timeline. */ this.presentArtifactMessage = (message)=>this.artifactStore.presentArtifactMessage(message), this.revisitDeckSlide = (message, slideIndex, pageTitle)=>this.artifactStore.revisitDeckSlide(message, slideIndex, pageTitle), this.clearArtifact = ()=>{
|
|
923
|
+
this.artifactStore.clearArtifact();
|
|
924
|
+
}, this.clearInputMessage = (param)=>{
|
|
925
|
+
let { when } = param === void 0 ? {} : param;
|
|
926
|
+
if (!when || this.input.state.value === when) {
|
|
927
|
+
this.setUIState({
|
|
928
|
+
inputMessageId: null
|
|
929
|
+
});
|
|
930
|
+
signals_core_module_r(()=>{
|
|
931
|
+
this.elementConversationOffer.value = null;
|
|
932
|
+
this.elementConversationLoading.value = false;
|
|
933
|
+
});
|
|
934
|
+
// NOTE: elementConversationTextMessage is intentionally NOT cleared here.
|
|
935
|
+
// After a successful booking, the server broadcasts calendar_event_confirmation
|
|
936
|
+
// (which sets elementConversationTextMessage) BEFORE send_message:response is
|
|
937
|
+
// processed. Clearing it here would race with that broadcast and erase the
|
|
938
|
+
// confirmation overlay. Instead, clear it when a new element conversation
|
|
939
|
+
// begins (handleInitElementConversation / handleInitQualifiedFormElementConversation)
|
|
940
|
+
// or when the user dismisses it via the "Done" button in LightboxExperience.
|
|
941
|
+
}
|
|
942
|
+
}, this.setTypingIndicator = (typing)=>{
|
|
943
|
+
this.typingIndicatorState.value = typing;
|
|
944
|
+
if (!typing) {
|
|
945
|
+
this.activeToolCall.value = null;
|
|
946
|
+
}
|
|
947
|
+
}, this.startCall = async (startCallContext, displayState)=>{
|
|
948
|
+
if (displayState === void 0) displayState = "open";
|
|
949
|
+
var _this_pounceTrackingStore_value, _this_callStore_value;
|
|
950
|
+
if (this.conversationMode.text.peek()) {
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
if (this.featureFlags.piperXCookieConsent && this.needsCookieConsent.value) {
|
|
954
|
+
// Stash the (optional) trigger context so it survives the cookie-consent
|
|
955
|
+
// re-entry path: acceptCookieConsent/flushPendingCookieConsentActions read
|
|
956
|
+
// it back and re-call startCall with the stored context.
|
|
957
|
+
//
|
|
958
|
+
// The requested display state is deliberately dropped. Asking for consent
|
|
959
|
+
// opens the widget and puts the visitor in front of it, so re-applying the
|
|
960
|
+
// original request afterwards would move (or close) a widget they are
|
|
961
|
+
// already looking at.
|
|
962
|
+
this.pendingCallStart.value = {
|
|
963
|
+
context: startCallContext != null ? startCallContext : null
|
|
964
|
+
};
|
|
965
|
+
this.setOpenedDisplayMode();
|
|
966
|
+
this.showCookieConsentModal.value = true;
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
this.callInitiatedAt = performance.now();
|
|
970
|
+
this.messengerEventListener == null ? void 0 : this.messengerEventListener.call(this, "callRequested");
|
|
971
|
+
(_this_pounceTrackingStore_value = this.pounceTrackingStore.value) == null ? void 0 : _this_pounceTrackingStore_value.notifyCallRequested();
|
|
972
|
+
this.sendMessage({
|
|
973
|
+
type: "event",
|
|
974
|
+
eventType: "ai_sdr_call_requested"
|
|
975
|
+
});
|
|
976
|
+
signals_core_module_r(()=>{
|
|
977
|
+
this.setUIState({
|
|
978
|
+
userHasIgnoredMultimodal: false
|
|
979
|
+
});
|
|
980
|
+
this.applyStartCallDisplayState(displayState);
|
|
981
|
+
});
|
|
982
|
+
const callStore = this.callStore.value;
|
|
983
|
+
if (!callStore) {
|
|
984
|
+
await this.initializeVoiceAndVideo();
|
|
985
|
+
}
|
|
986
|
+
const dailyCall = (_this_callStore_value = this.callStore.value) == null ? void 0 : _this_callStore_value.dailyCall.value;
|
|
987
|
+
if (!dailyCall) {
|
|
988
|
+
var _this_callStore_value1;
|
|
989
|
+
(_this_callStore_value1 = this.callStore.value) == null ? void 0 : _this_callStore_value1.aiConversationStore.startConversation(false, startCallContext != null ? startCallContext : null);
|
|
990
|
+
}
|
|
991
|
+
}, /**
|
|
992
|
+
* Position the widget for a call that is about to start.
|
|
993
|
+
*
|
|
994
|
+
* `minimized` runs the call in picture-in-picture over the host page, which
|
|
995
|
+
* only reads as picture-in-picture with nothing layered on top: a maximized
|
|
996
|
+
* artifact keeps its backdrop and z-index even once the display mode is
|
|
997
|
+
* closed, so it has to be cleared as well.
|
|
998
|
+
*/ this.applyStartCallDisplayState = (displayState)=>{
|
|
999
|
+
if (displayState === "minimized") {
|
|
1000
|
+
this.clearMaximizedState();
|
|
1001
|
+
this.setDisplayMode(types_DisplayMode.CLOSED);
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
// Expand to opened display mode if minimized
|
|
1005
|
+
if (this.displayMode.minimized.value) {
|
|
1006
|
+
this.setOpenedDisplayMode();
|
|
1007
|
+
}
|
|
1008
|
+
// Maximizing centers the multimodal UI, which collides with the custom entry
|
|
1009
|
+
// points voice-only setups render, so it is video-only.
|
|
1010
|
+
if (displayState === "maximized" && this.conversationMode.video.peek()) {
|
|
1011
|
+
this.setMaximizedState(types_MaximizedState.MULTIMODAL);
|
|
1012
|
+
}
|
|
1013
|
+
}, this.resetToStartingPosition = ()=>{
|
|
1014
|
+
this.uiStore.resetToStartingPosition();
|
|
1015
|
+
}, this.dismissWidget = ()=>{
|
|
1016
|
+
this.uiStore.dismissWidget();
|
|
1017
|
+
}, this.closeWidget = ()=>{
|
|
1018
|
+
this.uiStore.closeWidget();
|
|
1019
|
+
}, /**
|
|
1020
|
+
* Show a full-screen lightbox loading overlay and expand the widget.
|
|
1021
|
+
* If the widget is minimized, opens it first.
|
|
1022
|
+
*/ this.showLightboxLoading = ()=>{
|
|
1023
|
+
signals_core_module_r(()=>{
|
|
1024
|
+
this.loadingState.value = types_LoadingState.LIGHTBOX;
|
|
1025
|
+
if (this.displayMode.minimized.value && !this.uiStore.hasPendingRedirectDismissedUIState()) {
|
|
1026
|
+
this.setOpenedDisplayMode();
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
}, /**
|
|
1030
|
+
* Opens the messenger for an element-conversation step when the widget is
|
|
1031
|
+
* minimized — unless the meeting booker is being rendered inline into a page
|
|
1032
|
+
* container ({@link inlineMeetingBookerActive}). The full-screen lightbox flow
|
|
1033
|
+
* hides the opened messenger behind its overlay, but inline mode has no
|
|
1034
|
+
* overlay, so popping the messenger open would be an unwanted visible
|
|
1035
|
+
* side-effect next to the container booker.
|
|
1036
|
+
*/ this.openMessengerForElementConversation = ()=>{
|
|
1037
|
+
if (this.inlineMeetingBookerActive.value) return;
|
|
1038
|
+
if (this.displayMode.minimized.value) {
|
|
1039
|
+
this.setOpenedDisplayMode();
|
|
1040
|
+
}
|
|
1041
|
+
}, /**
|
|
1042
|
+
* Enters the element-conversation loading phase used by lightbox smart-site
|
|
1043
|
+
* flows before the gather/offer payload is available.
|
|
1044
|
+
*/ this.showElementConversationLoadingState = ()=>{
|
|
1045
|
+
signals_core_module_r(()=>{
|
|
1046
|
+
this.openMessengerForElementConversation();
|
|
1047
|
+
this.elementConversation.value = null;
|
|
1048
|
+
this.elementConversationOffer.value = null;
|
|
1049
|
+
this.elementConversationTextMessage.value = null;
|
|
1050
|
+
this.elementConversationLoading.value = true;
|
|
1051
|
+
this.elementConversationLoadingSeq.value += 1;
|
|
1052
|
+
this.closeLightbox();
|
|
1053
|
+
});
|
|
1054
|
+
}, /**
|
|
1055
|
+
* Show a messenger-panel loading overlay and expand the widget.
|
|
1056
|
+
*/ this.showMessengerLoading = ()=>{
|
|
1057
|
+
signals_core_module_r(()=>{
|
|
1058
|
+
this.loadingState.value = types_LoadingState.MESSENGER;
|
|
1059
|
+
this.setOpenedDisplayMode();
|
|
1060
|
+
});
|
|
1061
|
+
}, /**
|
|
1062
|
+
* Show an inline spinner for a specific personalization element.
|
|
1063
|
+
* The DOM spinner is managed by PersonalizationManager; this just
|
|
1064
|
+
* tracks the uuid for cleanup purposes.
|
|
1065
|
+
*/ this.showInlineLoading = (uuid)=>{
|
|
1066
|
+
signals_core_module_r(()=>{
|
|
1067
|
+
this.loadingState.value = types_LoadingState.INLINE;
|
|
1068
|
+
this.loadingUuid.value = uuid;
|
|
1069
|
+
});
|
|
1070
|
+
}, /**
|
|
1071
|
+
* Clear any active loading state (lightbox, messenger, or inline).
|
|
1072
|
+
* No-op when loading state is already NONE.
|
|
1073
|
+
*/ this.closeLightbox = ()=>{
|
|
1074
|
+
if (this.loadingState.peek() !== types_LoadingState.NONE) {
|
|
1075
|
+
signals_core_module_r(()=>{
|
|
1076
|
+
this.loadingState.value = types_LoadingState.NONE;
|
|
1077
|
+
this.loadingUuid.value = null;
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
}, /**
|
|
1081
|
+
* Cancels any active call or connecting state and returns to starting position.
|
|
1082
|
+
* Used when the Toolbar close button is clicked while in a call or connecting.
|
|
1083
|
+
*
|
|
1084
|
+
* Behavior:
|
|
1085
|
+
* - FULLSCREEN → COLLAPSED
|
|
1086
|
+
* - SIDEBAR → CLOSED
|
|
1087
|
+
*/ this.cancelCallOrConnection = async (param)=>{
|
|
1088
|
+
let { closeWidget = true } = param === void 0 ? {} : param;
|
|
1089
|
+
// Reset maximized state when ending call
|
|
1090
|
+
this.clearMaximizedState();
|
|
1091
|
+
// Handle debug mode - reset the scenario store
|
|
1092
|
+
const debugStore = this.debugScenarioStore.value;
|
|
1093
|
+
if (this.isDebugMultimodal.value && debugStore) {
|
|
1094
|
+
debugStore.reset();
|
|
1095
|
+
if (closeWidget) {
|
|
1096
|
+
this.closeWidget();
|
|
1097
|
+
}
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
// Handle real call mode
|
|
1101
|
+
const callStore = this.callStore.value;
|
|
1102
|
+
const aiConversationStore = callStore == null ? void 0 : callStore.aiConversationStore;
|
|
1103
|
+
const dailyCall = callStore == null ? void 0 : callStore.dailyCall.value;
|
|
1104
|
+
// Always reset the loading state first to prevent any pending async operations
|
|
1105
|
+
// from continuing (e.g., tavusStore.handleStartResponse checks this flag)
|
|
1106
|
+
this.isVideoLoading.value = false;
|
|
1107
|
+
this.isBlurredVideoActive.value = false;
|
|
1108
|
+
this.activeAvatarProvider.value = null;
|
|
1109
|
+
this.setTypingIndicator(false);
|
|
1110
|
+
// End the tavus conversation if one exists
|
|
1111
|
+
if (aiConversationStore == null ? void 0 : aiConversationStore.conversation.value) {
|
|
1112
|
+
await aiConversationStore.endConversation();
|
|
1113
|
+
}
|
|
1114
|
+
// Leave any active Daily call
|
|
1115
|
+
if (dailyCall) {
|
|
1116
|
+
await (callStore == null ? void 0 : callStore.leaveCall());
|
|
1117
|
+
}
|
|
1118
|
+
if (closeWidget) {
|
|
1119
|
+
this.closeWidget();
|
|
1120
|
+
}
|
|
1121
|
+
}, // Everything below is private
|
|
1122
|
+
// Signal subscriptions
|
|
1123
|
+
this.signalSubscriptions = [
|
|
1124
|
+
()=>this.activePrivacyDisclosure.subscribe(this.handlePrivacyDisclosureChange),
|
|
1125
|
+
()=>this.messages.pounceActive.subscribe((active)=>{
|
|
1126
|
+
var _this_pounceTrackingStore_value;
|
|
1127
|
+
if (active) {
|
|
1128
|
+
this.startPounceTracking();
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
if (this.pounceTrackingStore.value) {
|
|
1132
|
+
this.pounceTrackingLoadStarted = false;
|
|
1133
|
+
}
|
|
1134
|
+
(_this_pounceTrackingStore_value = this.pounceTrackingStore.value) == null ? void 0 : _this_pounceTrackingStore_value.dispose();
|
|
1135
|
+
this.setTypingIndicator(false);
|
|
1136
|
+
})
|
|
1137
|
+
], this.handlePrivacyDisclosureChange = ()=>{
|
|
1138
|
+
var _this_privacyDisclosures_value, _this_activeConversation_value;
|
|
1139
|
+
const isInline = ((_this_privacyDisclosures_value = this.privacyDisclosures.value) == null ? void 0 : _this_privacyDisclosures_value.displayMode) === "inline";
|
|
1140
|
+
const isDisclosureVisible = isInline ? this.displayMode.opened.value : !this.displayMode.closed.value;
|
|
1141
|
+
if (isDisclosureVisible && this.activePrivacyDisclosure.value && ((_this_activeConversation_value = this.activeConversation.value) == null ? void 0 : _this_activeConversation_value.id)) {
|
|
1142
|
+
this.recordPrivacyDisclosure();
|
|
1143
|
+
}
|
|
1144
|
+
}, // Message handlers
|
|
1145
|
+
this.messageHandlers = {
|
|
1146
|
+
initState: (payload)=>this.handleInitState(payload),
|
|
1147
|
+
stateUpdate: (payload)=>this.handleStateUpdate(payload),
|
|
1148
|
+
message: (payload)=>this.handleMessage(payload),
|
|
1149
|
+
messageRead: (payload)=>this.handleMessageRead(payload),
|
|
1150
|
+
typingStarted: ()=>this.setTypingIndicator(true),
|
|
1151
|
+
typingEnded: ()=>this.setTypingIndicator(false),
|
|
1152
|
+
visitorBlocked: ()=>signals_core_module_r(()=>{
|
|
1153
|
+
this.isVisitorBlocked.value = true;
|
|
1154
|
+
this.setTypingIndicator(false);
|
|
1155
|
+
this.setConversationMode(types_ConversationMode.TEXT);
|
|
1156
|
+
}),
|
|
1157
|
+
visitorUnblocked: ()=>this.isVisitorBlocked.value = false,
|
|
1158
|
+
botProcessEnded: ()=>this.handleBotProcessEnded(),
|
|
1159
|
+
textResponseEnded: (payload)=>this.handleTextResponseEnded(payload),
|
|
1160
|
+
textTurnDiagnostics: (payload)=>this.handleTextTurnDiagnostics(payload),
|
|
1161
|
+
toolCallStarted: (payload)=>{
|
|
1162
|
+
var _ref;
|
|
1163
|
+
this.activeToolCall.value = (_ref = payload == null ? void 0 : payload.toolName) != null ? _ref : null;
|
|
1164
|
+
},
|
|
1165
|
+
toolCallCompleted: ()=>{
|
|
1166
|
+
this.activeToolCall.value = null;
|
|
1167
|
+
},
|
|
1168
|
+
open: ()=>{
|
|
1169
|
+
// Close via the adapter so the active experience's lightbox closes, whether it's the legacy iframe (V1Adapter) or multimodal (V2Adapter).
|
|
1170
|
+
this.experienceAdapter.closeLightbox();
|
|
1171
|
+
this.setOpenedDisplayMode();
|
|
1172
|
+
}
|
|
1173
|
+
}, this.handleInitElementConversation = (payload)=>{
|
|
1174
|
+
var _payload_messages_filter, _payload_messages;
|
|
1175
|
+
// Use the LAST lightbox_gather message — conversations with show_follow_up_questions
|
|
1176
|
+
// accumulate multiple lightbox_gather messages (initial + follow-up). .find() would
|
|
1177
|
+
// return the stale initial message; .at(-1) returns the most recent one.
|
|
1178
|
+
const lightboxGatherMsg = (_payload_messages = payload.messages) == null ? void 0 : (_payload_messages_filter = _payload_messages.filter((m)=>m.type === "lightbox_gather")) == null ? void 0 : _payload_messages_filter.at(-1);
|
|
1179
|
+
if (!lightboxGatherMsg) {
|
|
1180
|
+
var _payload_messages1;
|
|
1181
|
+
// No gather step — check for a direct meeting booker offer (bot goes straight
|
|
1182
|
+
// to RouteForMeeting without a LightboxGather step). The server still broadcasts
|
|
1183
|
+
// initElementConversation for manual (non-form) bots; the payload contains the
|
|
1184
|
+
// calendar_event_offer message we need to show the meeting calendar.
|
|
1185
|
+
const offerMsg = (_payload_messages1 = payload.messages) == null ? void 0 : _payload_messages1.find((m)=>m.type === "calendar_event_offer");
|
|
1186
|
+
if (offerMsg) {
|
|
1187
|
+
// "Not you?" in flight: ignore the stale offer and keep waiting for the
|
|
1188
|
+
// re-gather (see restoreGatherFromOffer / handleInitQualifiedFormElementConversation).
|
|
1189
|
+
if (this.awaitingIdentityChangeRegather.value) return;
|
|
1190
|
+
signals_core_module_r(()=>{
|
|
1191
|
+
this.openMessengerForElementConversation();
|
|
1192
|
+
this.elementConversationLoading.value = false;
|
|
1193
|
+
this.elementConversationOffer.value = offerMsg;
|
|
1194
|
+
this.elementConversationTextMessage.value = null;
|
|
1195
|
+
this.closeLightbox();
|
|
1196
|
+
});
|
|
1197
|
+
this.meetingStore.load();
|
|
1198
|
+
} else {
|
|
1199
|
+
signals_core_module_r(()=>{
|
|
1200
|
+
this.openMessengerForElementConversation();
|
|
1201
|
+
// StartElementConversation can arrive before the backend has produced the
|
|
1202
|
+
// follow-up gather or offer message. Keep the lightbox loading overlay up
|
|
1203
|
+
// so the visitor sees progress instead of falling back to no overlay.
|
|
1204
|
+
this.elementConversationLoading.value = true;
|
|
1205
|
+
// Bump the loading seq so the inline booker's end-of-experience grace
|
|
1206
|
+
// check (InlineMeetingBooker.handleExperienceEnd) sees this restart and
|
|
1207
|
+
// does not blank the container while the offer is still in flight.
|
|
1208
|
+
this.elementConversationLoadingSeq.value += 1;
|
|
1209
|
+
// Clear any stale confirmation overlay from a previous booking.
|
|
1210
|
+
this.elementConversationTextMessage.value = null;
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
signals_core_module_r(()=>{
|
|
1216
|
+
this.openMessengerForElementConversation();
|
|
1217
|
+
this.elementConversation.value = {
|
|
1218
|
+
id: payload.id,
|
|
1219
|
+
message: lightboxGatherMsg
|
|
1220
|
+
};
|
|
1221
|
+
this.elementConversationTextMessage.value = null;
|
|
1222
|
+
// Clear loading state — this handles both the initial gather and any
|
|
1223
|
+
// follow-up gather steps (show_follow_up_questions) where the server
|
|
1224
|
+
// re-broadcasts initElementConversation after processing the response.
|
|
1225
|
+
this.elementConversationLoading.value = false;
|
|
1226
|
+
// The re-gather we were waiting on ("Not you?") has arrived.
|
|
1227
|
+
this.awaitingIdentityChangeRegather.value = false;
|
|
1228
|
+
this.closeLightbox();
|
|
1229
|
+
});
|
|
1230
|
+
}, this.submitLightboxGather = (entries)=>{
|
|
1231
|
+
const elementConv = this.elementConversation.value;
|
|
1232
|
+
if (!elementConv) return;
|
|
1233
|
+
// Batched so subscribers (e.g. LightboxExperience's completion-hold effects)
|
|
1234
|
+
// never observe elementConversationLoading=true while elementConversation is
|
|
1235
|
+
// still its stale pre-submit value — that intermediate state spuriously
|
|
1236
|
+
// satisfied the gather-hold's isReady() and snapped the bar to 100% for the
|
|
1237
|
+
// wrong reason, long before the real offer ever arrived.
|
|
1238
|
+
signals_core_module_r(()=>{
|
|
1239
|
+
this.elementConversationLoading.value = true;
|
|
1240
|
+
// Restart the loading seq so the inline booker's grace check keeps the
|
|
1241
|
+
// spinner up while the offer computes (see handleInitElementConversation).
|
|
1242
|
+
this.elementConversationLoadingSeq.value += 1;
|
|
1243
|
+
this.elementConversation.value = null;
|
|
1244
|
+
});
|
|
1245
|
+
this.sendMessage({
|
|
1246
|
+
type: "field_entries",
|
|
1247
|
+
entries,
|
|
1248
|
+
id: elementConv.message.id,
|
|
1249
|
+
botStep: elementConv.message.botStep,
|
|
1250
|
+
executionStep: elementConv.message.executionStep
|
|
1251
|
+
}, {
|
|
1252
|
+
optimistic: false,
|
|
1253
|
+
conversationId: elementConv.id
|
|
1254
|
+
});
|
|
1255
|
+
}, /**
|
|
1256
|
+
* Clears the meeting offer and asks the backend to drop the visitor's
|
|
1257
|
+
* identity so the LightboxGather step can re-run ("Not you?" flow). The
|
|
1258
|
+
* server destroys identities in MeetingBookerOffer#call and broadcasts a
|
|
1259
|
+
* fresh lightbox_gather message, which the standard init path hydrates
|
|
1260
|
+
* back into `elementConversation`.
|
|
1261
|
+
*/ this.restoreGatherFromOffer = ()=>{
|
|
1262
|
+
const offer = this.elementConversationOffer.value;
|
|
1263
|
+
if (!offer) return;
|
|
1264
|
+
this.sendMessage({
|
|
1265
|
+
type: "event",
|
|
1266
|
+
eventType: "change_identity",
|
|
1267
|
+
botStep: offer.botStep,
|
|
1268
|
+
executionStep: offer.executionStep
|
|
1269
|
+
}, {
|
|
1270
|
+
conversationId: offer.conversationId
|
|
1271
|
+
}).then((result)=>{
|
|
1272
|
+
// MultimodalChannel.perform reports timeouts and server errors by
|
|
1273
|
+
// resolving with an { error } result rather than rejecting, so a channel
|
|
1274
|
+
// failure lands here (not in catch). Without this, loading and the
|
|
1275
|
+
// re-gather guard would stay set forever and every later offer would be
|
|
1276
|
+
// dropped by the guard in handleInitElementConversation.
|
|
1277
|
+
if (result == null ? void 0 : result.error) this.clearRegatherOnChannelFailure();
|
|
1278
|
+
}).catch(()=>this.clearRegatherOnChannelFailure());
|
|
1279
|
+
signals_core_module_r(()=>{
|
|
1280
|
+
this.elementConversationOffer.value = null;
|
|
1281
|
+
this.elementConversationLoading.value = true;
|
|
1282
|
+
// Restart the loading seq so the inline booker's grace check keeps the
|
|
1283
|
+
// spinner up until the re-gathered lightbox_gather arrives.
|
|
1284
|
+
this.elementConversationLoadingSeq.value += 1;
|
|
1285
|
+
// Ignore the stale offer the backend echoes before the re-gather; hold the
|
|
1286
|
+
// spinner until the lightbox_gather arrives so we land on the email step.
|
|
1287
|
+
this.awaitingIdentityChangeRegather.value = true;
|
|
1288
|
+
});
|
|
1289
|
+
}, /**
|
|
1290
|
+
* Mirror LightboxEntry#handleRefreshMeetingOffer: when the "Not you?" re-gather
|
|
1291
|
+
* channel call drops or resolves with an error, clear the spinner so the
|
|
1292
|
+
* visitor isn't stuck on a perpetual overlay, and clear the re-gather guard so
|
|
1293
|
+
* a later offer isn't silently dropped.
|
|
1294
|
+
*/ this.clearRegatherOnChannelFailure = ()=>{
|
|
1295
|
+
this.elementConversationLoading.value = false;
|
|
1296
|
+
this.awaitingIdentityChangeRegather.value = false;
|
|
1297
|
+
}, this.handleInitQualifiedFormElementConversation = (conversation)=>{
|
|
1298
|
+
var _conversation_messages;
|
|
1299
|
+
const offerMessage = conversation == null ? void 0 : (_conversation_messages = conversation.messages) == null ? void 0 : _conversation_messages.find((m)=>m.type === "calendar_event_offer");
|
|
1300
|
+
// "Not you?" in flight: the backend echoes a stale calendar_event_offer just
|
|
1301
|
+
// before the fresh gather. Ignore it so the spinner holds until the
|
|
1302
|
+
// lightbox_gather arrives and shows the email step (see restoreGatherFromOffer).
|
|
1303
|
+
if (offerMessage && this.awaitingIdentityChangeRegather.value) return;
|
|
1304
|
+
if (!offerMessage) {
|
|
1305
|
+
var _conversation_messages_filter, _conversation_messages1;
|
|
1306
|
+
const textMessage = conversation == null ? void 0 : (_conversation_messages1 = conversation.messages) == null ? void 0 : (_conversation_messages_filter = _conversation_messages1.filter((m)=>m.type === "text")) == null ? void 0 : _conversation_messages_filter.at(-1);
|
|
1307
|
+
if (!textMessage) return;
|
|
1308
|
+
this.handleElementConversationTextMessage(textMessage);
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
signals_core_module_r(()=>{
|
|
1312
|
+
this.openMessengerForElementConversation();
|
|
1313
|
+
this.elementConversationLoading.value = false;
|
|
1314
|
+
this.elementConversationOffer.value = offerMessage;
|
|
1315
|
+
this.elementConversationTextMessage.value = null;
|
|
1316
|
+
this.closeLightbox();
|
|
1317
|
+
});
|
|
1318
|
+
this.meetingStore.load();
|
|
1319
|
+
}, this.handleElementConversationTextMessage = (payload)=>{
|
|
1320
|
+
var _ref, _ref1, _ref2;
|
|
1321
|
+
const title = (_ref = payload == null ? void 0 : payload.title) != null ? _ref : "";
|
|
1322
|
+
const text = (_ref1 = payload == null ? void 0 : payload.text) != null ? _ref1 : "";
|
|
1323
|
+
const buttonText = (_ref2 = payload == null ? void 0 : payload.button_text) != null ? _ref2 : "Done";
|
|
1324
|
+
if (!title && !text) return;
|
|
1325
|
+
signals_core_module_r(()=>{
|
|
1326
|
+
this.openMessengerForElementConversation();
|
|
1327
|
+
this.elementConversationLoading.value = false;
|
|
1328
|
+
this.elementConversationTextMessage.value = {
|
|
1329
|
+
title,
|
|
1330
|
+
text,
|
|
1331
|
+
buttonText
|
|
1332
|
+
};
|
|
1333
|
+
// If the experience ended with text instead of a re-gather, stop waiting.
|
|
1334
|
+
this.awaitingIdentityChangeRegather.value = false;
|
|
1335
|
+
});
|
|
1336
|
+
}, this.handleInitState = (initialState)=>{
|
|
1337
|
+
// Ignore any state changes just set the reconnecting flag this ensures the next initState will be handled correctly.
|
|
1338
|
+
if (initialState.isReconnecting) {
|
|
1339
|
+
this.isReconnecting.value = true;
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
this.dockingStore.cancelAbort();
|
|
1343
|
+
this.initialized.value = true;
|
|
1344
|
+
metrics.multimodal.timeToInit.log();
|
|
1345
|
+
signals_core_module_r(()=>{
|
|
1346
|
+
var _ref, _initialState_formJoinIdUnique, _initialState_multimodalModalityPreference, _initialState_conversationModeFailover, _initialState_dailyCallObjectBundleUrl, _initialState_dailyWsProxyUrl;
|
|
1347
|
+
var _initialState_themeConfig, _initialState_themeConfig1, _initialState_themeConfig2;
|
|
1348
|
+
// Clear any loading overlay now that the widget has fully initialised.
|
|
1349
|
+
this.closeLightbox();
|
|
1350
|
+
if (initialState.companyName) {
|
|
1351
|
+
this.companyName.value = initialState.companyName;
|
|
1352
|
+
}
|
|
1353
|
+
if (initialState.disableTitleFlash != null) {
|
|
1354
|
+
this.disableTitleFlash.value = initialState.disableTitleFlash;
|
|
1355
|
+
}
|
|
1356
|
+
if (initialState.hasCustomVoiceGreeting !== undefined) {
|
|
1357
|
+
this.hasCustomVoiceGreeting = initialState.hasCustomVoiceGreeting;
|
|
1358
|
+
}
|
|
1359
|
+
if (initialState.conversations) {
|
|
1360
|
+
this.conversations.value = initialState.conversations;
|
|
1361
|
+
}
|
|
1362
|
+
if (initialState.agentsAvailable) {
|
|
1363
|
+
this.agentsAvailable.value = initialState.agentsAvailable;
|
|
1364
|
+
}
|
|
1365
|
+
if (initialState.messages) {
|
|
1366
|
+
const mergedServerMessages = initialState.messages.map((incoming)=>{
|
|
1367
|
+
const existing = this.messages.value.find((m)=>m.id === incoming.id);
|
|
1368
|
+
return existing ? this.mergeMessage(existing, incoming) : incoming;
|
|
1369
|
+
});
|
|
1370
|
+
this.messages.value = mergedServerMessages;
|
|
1371
|
+
this.handleLastInitStateMessage(initialState.messages);
|
|
1372
|
+
// Track pounce response time: start timer when the experience has started
|
|
1373
|
+
// (startChatBot present) but the pounce hasn't arrived yet. Skipped on
|
|
1374
|
+
// page reloads where the pounce is already in the initial messages.
|
|
1375
|
+
//
|
|
1376
|
+
// Not every startChatBot leads to a pounce. Generation is blocked when
|
|
1377
|
+
// proactive pounce is not configured, the visitor replied before the
|
|
1378
|
+
// pounce was created, or the pounce was dismissed. In those cases the
|
|
1379
|
+
// timer is set but never consumed — no metric is emitted.
|
|
1380
|
+
//
|
|
1381
|
+
// NOTE: If a visitor has multiple tabs open and the second tab connects
|
|
1382
|
+
// before the pounce arrives, both tabs will emit the metric. This is
|
|
1383
|
+
// acceptable for a P95 distribution — the duplication window is narrow
|
|
1384
|
+
// and multi-tab is rare.
|
|
1385
|
+
if (this.activeConversationAwaitsPounce() && this.featureFlags.piperXAgent) {
|
|
1386
|
+
this.pounceResponseStartTimestamp = performance.now();
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
if (initialState.pounceCandidates !== undefined) {
|
|
1390
|
+
var _initialState_pounceCandidates;
|
|
1391
|
+
this.pounceCandidates.value = (_initialState_pounceCandidates = initialState.pounceCandidates) != null ? _initialState_pounceCandidates : null;
|
|
1392
|
+
}
|
|
1393
|
+
if (this.messages.pounceActive.value) {
|
|
1394
|
+
this.startPounceTracking();
|
|
1395
|
+
}
|
|
1396
|
+
const formJoinIDUnique = (_ref = (_initialState_formJoinIdUnique = initialState.formJoinIdUnique) != null ? _initialState_formJoinIdUnique : (_initialState_themeConfig = initialState.themeConfig) == null ? void 0 : _initialState_themeConfig.formJoinIDUnique) != null ? _ref : null;
|
|
1397
|
+
if (formJoinIDUnique) {
|
|
1398
|
+
this.formJoinIDUnique.value = formJoinIDUnique;
|
|
1399
|
+
}
|
|
1400
|
+
if (initialState.themeConfig) {
|
|
1401
|
+
var _themeConfig_inputPlaceholder, _themeConfig_multimodalHideLauncherLabel;
|
|
1402
|
+
const { themeConfig } = initialState;
|
|
1403
|
+
this.inputPlaceholder.value = (_themeConfig_inputPlaceholder = themeConfig.inputPlaceholder) != null ? _themeConfig_inputPlaceholder : null;
|
|
1404
|
+
this.multimodalHideLauncherLabel.value = (_themeConfig_multimodalHideLauncherLabel = themeConfig.multimodalHideLauncherLabel) != null ? _themeConfig_multimodalHideLauncherLabel : false;
|
|
1405
|
+
if (themeConfig.formJoinIDUnique != null && this.formJoinIDUnique.value == null) {
|
|
1406
|
+
this.formJoinIDUnique.value = themeConfig.formJoinIDUnique;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
this.modalityPreference.value = (_initialState_multimodalModalityPreference = initialState.multimodalModalityPreference) != null ? _initialState_multimodalModalityPreference : null;
|
|
1410
|
+
// Delegate UI-related init to UIStore after server-side modality is available.
|
|
1411
|
+
this.uiStore.handleInitState(initialState);
|
|
1412
|
+
const inputMessage = this.input.message.peek();
|
|
1413
|
+
if (inputMessage) {
|
|
1414
|
+
this.updateInputMessage(inputMessage);
|
|
1415
|
+
}
|
|
1416
|
+
if ((_initialState_themeConfig1 = initialState.themeConfig) == null ? void 0 : _initialState_themeConfig1.privacyDisclosures) {
|
|
1417
|
+
this.privacyDisclosures.value = initialState.themeConfig.privacyDisclosures;
|
|
1418
|
+
}
|
|
1419
|
+
if ((_initialState_themeConfig2 = initialState.themeConfig) == null ? void 0 : _initialState_themeConfig2.cookieConsent) {
|
|
1420
|
+
this.cookieConsent.value = initialState.themeConfig.cookieConsent;
|
|
1421
|
+
}
|
|
1422
|
+
this.flushPendingCookieConsentActions();
|
|
1423
|
+
// Initialize cold start questions
|
|
1424
|
+
if (initialState.coldStartQuestions) {
|
|
1425
|
+
this.coldStartQuestions.value = initialState.coldStartQuestions;
|
|
1426
|
+
}
|
|
1427
|
+
if (typeof initialState.coldStartVisibleLimit === "number") {
|
|
1428
|
+
this.coldStartVisibleLimit.value = initialState.coldStartVisibleLimit;
|
|
1429
|
+
}
|
|
1430
|
+
if (typeof initialState.pounceInactivityThresholdMs === "number") {
|
|
1431
|
+
this.pounceInactivityThresholdMs.value = initialState.pounceInactivityThresholdMs;
|
|
1432
|
+
} else if (initialState.pounceInactivityThresholdMs === null) {
|
|
1433
|
+
this.pounceInactivityThresholdMs.value = null;
|
|
1434
|
+
}
|
|
1435
|
+
if (initialState.scriptLocale) {
|
|
1436
|
+
this.translationStore.setScriptLocale(initialState.scriptLocale);
|
|
1437
|
+
}
|
|
1438
|
+
this.updateConversationModeFailover((_initialState_conversationModeFailover = initialState.conversationModeFailover) != null ? _initialState_conversationModeFailover : {
|
|
1439
|
+
voiceDisabled: false,
|
|
1440
|
+
videoDisabled: false
|
|
1441
|
+
});
|
|
1442
|
+
this.dailyCallObjectBundleUrl.value = (_initialState_dailyCallObjectBundleUrl = initialState.dailyCallObjectBundleUrl) != null ? _initialState_dailyCallObjectBundleUrl : null;
|
|
1443
|
+
this.dailyWsProxyUrl.value = (_initialState_dailyWsProxyUrl = initialState.dailyWsProxyUrl) != null ? _initialState_dailyWsProxyUrl : null;
|
|
1444
|
+
if (this.chatViewDeferredUserIntent) {
|
|
1445
|
+
this.emitChatViewIfEligible({
|
|
1446
|
+
fromUserIntent: true
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
if (!this.displayMode.minimized.peek()) {
|
|
1450
|
+
this.markMessagesAsRead();
|
|
1451
|
+
}
|
|
1452
|
+
});
|
|
1453
|
+
this.submitInitialDisplayMode();
|
|
1454
|
+
this.submitInitialModality();
|
|
1455
|
+
this.submitInitialMessage();
|
|
1456
|
+
this.armBookerIntentFromHistory();
|
|
1457
|
+
this.isReconnecting.value = false;
|
|
1458
|
+
}, this.clearElementConversationState = ()=>{
|
|
1459
|
+
signals_core_module_r(()=>{
|
|
1460
|
+
this.elementConversation.value = null;
|
|
1461
|
+
this.elementConversationOffer.value = null;
|
|
1462
|
+
this.elementConversationTextMessage.value = null;
|
|
1463
|
+
this.elementConversationLoading.value = false;
|
|
1464
|
+
this.awaitingIdentityChangeRegather.value = false;
|
|
1465
|
+
});
|
|
1466
|
+
}, this.handleStateUpdate = (stateUpdate)=>{
|
|
1467
|
+
signals_core_module_r(()=>{
|
|
1468
|
+
if (typeof stateUpdate.agentsAvailable === "boolean") {
|
|
1469
|
+
this.agentsAvailable.value = stateUpdate.agentsAvailable;
|
|
1470
|
+
}
|
|
1471
|
+
if (stateUpdate.pounceCandidates !== undefined) {
|
|
1472
|
+
var _stateUpdate_pounceCandidates;
|
|
1473
|
+
this.pounceCandidates.value = (_stateUpdate_pounceCandidates = stateUpdate.pounceCandidates) != null ? _stateUpdate_pounceCandidates : null;
|
|
1474
|
+
}
|
|
1475
|
+
if (stateUpdate.hasCustomVoiceGreeting !== undefined) {
|
|
1476
|
+
this.hasCustomVoiceGreeting = stateUpdate.hasCustomVoiceGreeting;
|
|
1477
|
+
}
|
|
1478
|
+
if (stateUpdate.conversations) {
|
|
1479
|
+
stateUpdate.conversations.forEach((conversation)=>{
|
|
1480
|
+
this.updateConversation(conversation);
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
if (stateUpdate.coldStartQuestions) {
|
|
1484
|
+
this.coldStartQuestions.value = stateUpdate.coldStartQuestions;
|
|
1485
|
+
}
|
|
1486
|
+
// A multimodal experience that starts after a delay broadcasts a stateUpdate with
|
|
1487
|
+
// its resolved starting state. Re-apply it so the widget opens in the correct mode
|
|
1488
|
+
// and with the correct profile assets rather than the fallbacks baked in at init.
|
|
1489
|
+
if (stateUpdate.multimodalModalityPreference !== undefined) {
|
|
1490
|
+
var _stateUpdate_multimodalModalityPreference;
|
|
1491
|
+
this.modalityPreference.value = (_stateUpdate_multimodalModalityPreference = stateUpdate.multimodalModalityPreference) != null ? _stateUpdate_multimodalModalityPreference : null;
|
|
1492
|
+
}
|
|
1493
|
+
this.uiStore.applyMultimodalStartState(stateUpdate);
|
|
1494
|
+
});
|
|
1495
|
+
}, this.handleMessage = (incomingMessage)=>{
|
|
1496
|
+
const existingMessage = this.messages.value.find((message)=>message.id === incomingMessage.id);
|
|
1497
|
+
if (existingMessage) {
|
|
1498
|
+
this.handleExistingMessage(existingMessage, incomingMessage);
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
const clientMessageIndex = this.messages.value.findIndex((message)=>message.id === incomingMessage.clientId);
|
|
1502
|
+
// Play audio for text messages
|
|
1503
|
+
if (incomingMessage.type === "text") {
|
|
1504
|
+
this.playMessageAudio(incomingMessage);
|
|
1505
|
+
}
|
|
1506
|
+
signals_core_module_r(()=>{
|
|
1507
|
+
if (incomingMessage.conversation) {
|
|
1508
|
+
this.updateConversation(incomingMessage.conversation);
|
|
1509
|
+
}
|
|
1510
|
+
if (isEventMessage(incomingMessage, "startChatBot") && this.pounceResponseStartTimestamp === null && this.featureFlags.piperXAgent) {
|
|
1511
|
+
this.pounceResponseStartTimestamp = performance.now();
|
|
1512
|
+
} else if (incomingMessage.isPounce && this.pounceResponseStartTimestamp !== null) {
|
|
1513
|
+
const deltaMs = performance.now() - this.pounceResponseStartTimestamp;
|
|
1514
|
+
metrics.emitTiming("multimodal_first_pounce_ms", deltaMs);
|
|
1515
|
+
this.pounceResponseStartTimestamp = null;
|
|
1516
|
+
}
|
|
1517
|
+
if (isAssistantMessage(incomingMessage) && !incomingMessage.isPounce && this.lastVisitorMessageSentAt !== null && !this.turnReplyHandled) {
|
|
1518
|
+
this.clearResponseTimeout();
|
|
1519
|
+
const deltaMs = performance.now() - this.lastVisitorMessageSentAt;
|
|
1520
|
+
const withinBudget = deltaMs <= (/* inlined export .RESPONSE_BUDGET_MS */25000);
|
|
1521
|
+
const obscured = this.turnObscured || this.isDocumentHidden();
|
|
1522
|
+
// Within budget the wait is short enough to trust regardless of visibility;
|
|
1523
|
+
// over budget we only trust the timing if the tab stayed foreground,
|
|
1524
|
+
// since a hidden tab can throttle delivery.
|
|
1525
|
+
if (withinBudget || !obscured) {
|
|
1526
|
+
metrics.emitTiming("multimodal_text_response_ms", deltaMs, {
|
|
1527
|
+
call_active: this.turnCallActiveTag,
|
|
1528
|
+
has_tool_call: this.turnHasToolCall ? "true" : "false"
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
if (!this.turnCounterEmitted) {
|
|
1532
|
+
var _this_turnCause;
|
|
1533
|
+
const status = withinBudget ? "success" : obscured ? "backgrounded" : "timeout";
|
|
1534
|
+
metrics.emitIncrement("multimodal_text_response", _extends({
|
|
1535
|
+
status,
|
|
1536
|
+
call_active: this.turnCallActiveTag
|
|
1537
|
+
}, status === "timeout" ? {
|
|
1538
|
+
cause: (_this_turnCause = this.turnCause) != null ? _this_turnCause : "unknown"
|
|
1539
|
+
} : {}));
|
|
1540
|
+
this.turnCounterEmitted = true;
|
|
1541
|
+
} else {
|
|
1542
|
+
// The 25s timer already fired and counted this turn as a timeout.
|
|
1543
|
+
// Log the late reply so we can distinguish "AI responded slowly" from
|
|
1544
|
+
// "reply never arrived" when investigating timeout events.
|
|
1545
|
+
serverLogger.serverLog({
|
|
1546
|
+
type: "log",
|
|
1547
|
+
message: "Multimodal text reply arrived after response budget (late)",
|
|
1548
|
+
extra: {
|
|
1549
|
+
context: "elapsed_ms:" + Math.round(deltaMs)
|
|
1550
|
+
}
|
|
1551
|
+
});
|
|
1552
|
+
}
|
|
1553
|
+
this.turnReplyHandled = true;
|
|
1554
|
+
this.turnToken++;
|
|
1555
|
+
}
|
|
1556
|
+
this.addMessage(incomingMessage, clientMessageIndex);
|
|
1557
|
+
if (isAssistantMessage(incomingMessage)) {
|
|
1558
|
+
this.setTypingIndicator(false);
|
|
1559
|
+
this.markIncomingMessageAsRead(incomingMessage);
|
|
1560
|
+
}
|
|
1561
|
+
this.handleIncomingMessage(incomingMessage);
|
|
1562
|
+
});
|
|
1563
|
+
}, this.handleExistingMessage = (existingMessage, incomingMessage)=>{
|
|
1564
|
+
// Capture before updateMessage mutates existingMessage in place.
|
|
1565
|
+
// In voice mode the CalendarEventOffer starts hidden (showCalendarEventOfferUi: false)
|
|
1566
|
+
// until the voice placeholder is filled. When it transitions to visible we delay
|
|
1567
|
+
// showing the meeting booker so the visitor can read the transcript first.
|
|
1568
|
+
const wasHiddenByPlaceholder = existingMessage.type === "calendar_event_offer" && existingMessage.showCalendarEventOfferUi === false;
|
|
1569
|
+
signals_core_module_r(()=>{
|
|
1570
|
+
this.updateMessage(existingMessage, incomingMessage);
|
|
1571
|
+
this.markIncomingMessageAsRead(incomingMessage);
|
|
1572
|
+
this.handleIncomingMessage(incomingMessage, {
|
|
1573
|
+
wasHiddenByPlaceholder
|
|
1574
|
+
});
|
|
1575
|
+
});
|
|
1576
|
+
}, this.markIncomingMessageAsRead = (incomingMessage)=>{
|
|
1577
|
+
if (isAssistantMessage(incomingMessage) && !this.displayMode.minimized.value && (!isStreamableMessage(incomingMessage) || !this.isMessageStreamingEnabled.value)) {
|
|
1578
|
+
this.markMessagesAsRead([
|
|
1579
|
+
incomingMessage
|
|
1580
|
+
]);
|
|
1581
|
+
}
|
|
1582
|
+
}, this.handleIncomingMessage = (incomingMessage, param)=>{
|
|
1583
|
+
let { wasHiddenByPlaceholder = false } = param === void 0 ? {} : param;
|
|
1584
|
+
switch(incomingMessage.type){
|
|
1585
|
+
case "calendar_event_offer":
|
|
1586
|
+
// Closes the pre-offer SLI here, at message *receipt*, which is where the
|
|
1587
|
+
// metric is defined to end. Resolving it off `activeOfferMessage` instead
|
|
1588
|
+
// would fold in client-side delay: that subscription waits on the lazy
|
|
1589
|
+
// `meetingStore` import, and the voice-placeholder path below defers it a
|
|
1590
|
+
// further second — neither of which is time the visitor spent waiting for
|
|
1591
|
+
// an offer.
|
|
1592
|
+
//
|
|
1593
|
+
// Only an AI-authored offer closes an AI intent. The client is never told
|
|
1594
|
+
// when the server decided *not* to offer (`no_offer` lives only in the
|
|
1595
|
+
// tool's `decision_outcome`), so a rejected intent stays armed — and a
|
|
1596
|
+
// later offer from a rep or a form would otherwise report `offered`
|
|
1597
|
+
// against it, giving one journey two terminal outcomes and inflating the
|
|
1598
|
+
// offer-made rate. `auto_respond` is the same discriminator
|
|
1599
|
+
// `bookerPathwayOrigin` uses to tell `ai_agent` from `chat`, and both AI
|
|
1600
|
+
// paths stamp it (Goals inline, Foundry via the deferred offer). Checked
|
|
1601
|
+
// directly rather than through that helper, which resolves `element`
|
|
1602
|
+
// conversations to `form` first and so would miss an AI offer made inside
|
|
1603
|
+
// one.
|
|
1604
|
+
if (isAiAuthoredOffer(incomingMessage.source)) {
|
|
1605
|
+
var _incomingMessage_calendarEventTypeId;
|
|
1606
|
+
this.bookerIntentTracker.markOffered(incomingMessage.id, {
|
|
1607
|
+
calendarEventTypeId: (_incomingMessage_calendarEventTypeId = incomingMessage.calendarEventTypeId) == null ? void 0 : _incomingMessage_calendarEventTypeId.toString()
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
if (incomingMessage.showCalendarEventOfferUi !== false) {
|
|
1611
|
+
// When transitioning from hidden to visible (voice placeholder just
|
|
1612
|
+
// filled), delay showing the meeting booker so the visitor has time
|
|
1613
|
+
// to read the transcript text that appeared in the placeholder.
|
|
1614
|
+
void this.queueArtifactMessage(incomingMessage, {
|
|
1615
|
+
delayMs: wasHiddenByPlaceholder ? RootStore.MEETING_BOOKER_PLACEHOLDER_DELAY_MS : 0
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
break;
|
|
1619
|
+
case "field_request":
|
|
1620
|
+
if (incomingMessage.showFieldUi) {
|
|
1621
|
+
this.updateInputMessage(incomingMessage);
|
|
1622
|
+
}
|
|
1623
|
+
break;
|
|
1624
|
+
case "field_entry":
|
|
1625
|
+
if (!this.conversationMode.text.value) {
|
|
1626
|
+
this.clearInputMessage({
|
|
1627
|
+
when: types_InputState.FIELD_REQUEST
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
break;
|
|
1631
|
+
case "present_content":
|
|
1632
|
+
case "present_video":
|
|
1633
|
+
case "present_deck":
|
|
1634
|
+
case "navigate_content":
|
|
1635
|
+
void this.queueArtifactMessage(incomingMessage);
|
|
1636
|
+
break;
|
|
1637
|
+
case "point_cursor":
|
|
1638
|
+
{
|
|
1639
|
+
const { x, y, rect, nodeId, textSnippet } = incomingMessage;
|
|
1640
|
+
this.websiteNavigationStore.load().then((store)=>{
|
|
1641
|
+
// x/y => point at a spot on the slide/video image; otherwise anchor to a page element by node-id.
|
|
1642
|
+
if (x != null && y != null) {
|
|
1643
|
+
store.pointAtSlide(x, y, rect != null ? rect : null);
|
|
1644
|
+
} else {
|
|
1645
|
+
store.pointAtNode(nodeId != null ? nodeId : null, textSnippet);
|
|
1646
|
+
}
|
|
1647
|
+
});
|
|
1648
|
+
break;
|
|
1649
|
+
}
|
|
1650
|
+
case "event":
|
|
1651
|
+
if (incomingMessage.eventType === "hide_artifact") {
|
|
1652
|
+
this.artifactStore.hideArtifact(incomingMessage.artifactMessageId);
|
|
1653
|
+
}
|
|
1654
|
+
if (incomingMessage.eventType === "waiting") {
|
|
1655
|
+
this.waitingStore.load().then((store)=>store.handleWaitingMessage(incomingMessage));
|
|
1656
|
+
}
|
|
1657
|
+
if (incomingMessage.eventType === CAN_OFFER_MEETING_BOOKER_EVENT_TYPE) {
|
|
1658
|
+
this.armBookerIntent(incomingMessage.id, {
|
|
1659
|
+
scriptId: incomingMessage.scriptId
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
break;
|
|
1663
|
+
case "system":
|
|
1664
|
+
{
|
|
1665
|
+
const { eventType } = incomingMessage;
|
|
1666
|
+
// if we are in an active call and a rep joins, coffee will signal to us to switch over to text
|
|
1667
|
+
if (eventType === "participant_joined" && !this.getCallUiAdapter().state.isInCall.peek()) {
|
|
1668
|
+
this.setConversationMode(types_ConversationMode.TEXT);
|
|
1669
|
+
}
|
|
1670
|
+
break;
|
|
1671
|
+
}
|
|
1672
|
+
default:
|
|
1673
|
+
break;
|
|
1674
|
+
}
|
|
1675
|
+
}, this.handleMessageRead = (messageId)=>{
|
|
1676
|
+
const message = this.messages.value.find((m)=>m.id === messageId);
|
|
1677
|
+
if (message && !message.isRead) {
|
|
1678
|
+
this.updateMessage(message, {
|
|
1679
|
+
isRead: true
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
}, this.handleBotProcessEnded = ()=>{
|
|
1683
|
+
var _this_waitingStore_value;
|
|
1684
|
+
if (this.messages.hasStartChatBotEvent.peek() || ((_this_waitingStore_value = this.waitingStore.value) == null ? void 0 : _this_waitingStore_value.isWaiting.value)) {
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
this.setTypingIndicator(false);
|
|
1688
|
+
}, // Helpers
|
|
1689
|
+
/**
|
|
1690
|
+
* Scans init state messages from the end to find the last message that needs
|
|
1691
|
+
* to be handled (e.g. an unreplied field_request or an unresolved waiting event).
|
|
1692
|
+
* Only the first match is handled, ensuring a single call to handleIncomingMessage.
|
|
1693
|
+
*/ this.handleLastInitStateMessage = (messages)=>{
|
|
1694
|
+
const matchers = [
|
|
1695
|
+
// Unreplied field_request from an assistant
|
|
1696
|
+
(m)=>m.type === "field_request" && m.showFieldUi === true && !this.messages.repliedToIds.value.has(m.id),
|
|
1697
|
+
// Active (unresolved) waiting event
|
|
1698
|
+
(m)=>isEventMessage(m, "waiting") && !this.messages.repliedToIds.value.has(m.id)
|
|
1699
|
+
];
|
|
1700
|
+
for(let i = messages.length - 1; i >= 0; i--){
|
|
1701
|
+
const message = messages[i];
|
|
1702
|
+
if (matchers.some((matcher)=>matcher(message))) {
|
|
1703
|
+
this.handleIncomingMessage(message);
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
}, /**
|
|
1708
|
+
* Inject a client-only text message into the active conversation. Renders as a
|
|
1709
|
+
* normal (non-own) text bubble, not a SystemMessage. Used for transient UI notices
|
|
1710
|
+
* (e.g. graceful degradation when voice/video is unavailable). Not persisted server-side.
|
|
1711
|
+
*/ this.displayLocalNotice = (text)=>{
|
|
1712
|
+
var _this_activeConversation_value;
|
|
1713
|
+
const conversationId = (_this_activeConversation_value = this.activeConversation.value) == null ? void 0 : _this_activeConversation_value.id;
|
|
1714
|
+
if (!conversationId) return;
|
|
1715
|
+
this.addMessage({
|
|
1716
|
+
id: "local-" + Date.now() + "-" + Math.random().toString(36).slice(2, 8),
|
|
1717
|
+
type: "text",
|
|
1718
|
+
text,
|
|
1719
|
+
isOwn: false,
|
|
1720
|
+
conversationId,
|
|
1721
|
+
createdAt: new Date().toISOString()
|
|
1722
|
+
});
|
|
1723
|
+
}, this.addMessage = (message, index)=>{
|
|
1724
|
+
if (index !== undefined && index >= 0) {
|
|
1725
|
+
this.messages.value[index] = message;
|
|
1726
|
+
this.messages.value = [
|
|
1727
|
+
...this.messages.value
|
|
1728
|
+
];
|
|
1729
|
+
} else {
|
|
1730
|
+
this.messages.value = [
|
|
1731
|
+
...this.messages.value,
|
|
1732
|
+
message
|
|
1733
|
+
];
|
|
1734
|
+
}
|
|
1735
|
+
}, this.firstSystemMessageReceived = false, this.playMessageAudio = (message)=>{
|
|
1736
|
+
if (this.featureFlags.disableMessengerSounds) return;
|
|
1737
|
+
if (this.getCallUiAdapter().state.isInCall.peek()) {
|
|
1738
|
+
// Don't play audio if we're in a call
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1741
|
+
if (message.isOwn) {
|
|
1742
|
+
playAudio(message_sent_namespaceObject);
|
|
1743
|
+
} else if (!this.firstSystemMessageReceived) {
|
|
1744
|
+
playAudio(message_initial_namespaceObject);
|
|
1745
|
+
this.firstSystemMessageReceived = true;
|
|
1746
|
+
} else {
|
|
1747
|
+
playAudio(message_received_namespaceObject);
|
|
1748
|
+
}
|
|
1749
|
+
}, this.updateConversation = (conversation)=>{
|
|
1750
|
+
this.conversations.value = this.conversations.value.map((c)=>c.id === conversation.id ? conversation : c);
|
|
1751
|
+
}, /**
|
|
1752
|
+
* Arms the pre-offer booker SLI for the AI pathway.
|
|
1753
|
+
*
|
|
1754
|
+
* `pathway_origin` is the literal `ai_agent` rather than the derivation the
|
|
1755
|
+
* post-offer tracker uses: that derivation reads the offer's `source`, and at
|
|
1756
|
+
* intent there is no offer yet — it would silently classify every AI intent as
|
|
1757
|
+
* `chat`. This signal *is* the AI deciding to offer.
|
|
1758
|
+
*/ this.armBookerIntent = (intentId, param)=>{
|
|
1759
|
+
let { scriptId, fromHistory = false } = param === void 0 ? {} : param;
|
|
1760
|
+
var _ref;
|
|
1761
|
+
var _this_activeConversation_peek;
|
|
1762
|
+
// Gated at arm rather than at emit: with nothing armed the tracker holds no
|
|
1763
|
+
// state and its listeners are never attached, so the feature costs nothing
|
|
1764
|
+
// when off. Does not affect the already-live `booker_outcome` stream, which
|
|
1765
|
+
// `meeting_store` emits independently.
|
|
1766
|
+
if (!this.featureFlags.bookerJourneySLI) return;
|
|
1767
|
+
this.bookerIntentTracker.arm({
|
|
1768
|
+
intentId,
|
|
1769
|
+
pathwayOrigin: BOOKER_PATHWAY_ORIGIN.AI_AGENT,
|
|
1770
|
+
conversationType: (_ref = (_this_activeConversation_peek = this.activeConversation.peek()) == null ? void 0 : _this_activeConversation_peek.type) != null ? _ref : "chat",
|
|
1771
|
+
scriptId,
|
|
1772
|
+
fromHistory
|
|
1773
|
+
});
|
|
1774
|
+
}, /**
|
|
1775
|
+
* Re-arms the pre-offer SLI after a reload that lands mid-wait: the intent
|
|
1776
|
+
* event is already in the hydrated history, so the live `case "event"` path
|
|
1777
|
+
* never sees it and the wait would go unmeasured.
|
|
1778
|
+
*
|
|
1779
|
+
* Armed as `fromHistory`, which reports it far more conservatively: no timings
|
|
1780
|
+
* (the intent moment belongs to a previous JS context) and never
|
|
1781
|
+
* `abandoned_pre_offer`. Absence of a later offer does not prove the intent is
|
|
1782
|
+
* still pending — a rejected server-side decision leaves exactly this shape,
|
|
1783
|
+
* since `no_offer` never reaches the client — so abandoning here would
|
|
1784
|
+
* double-count that journey against the server's own `no_offer`. `offered` is
|
|
1785
|
+
* the one transition this path can claim unambiguously.
|
|
1786
|
+
*/ this.armBookerIntentFromHistory = ()=>{
|
|
1787
|
+
const messages = this.messages.peek();
|
|
1788
|
+
const intentIndex = messages.findLastIndex((message)=>message.type === "event" && message.eventType === CAN_OFFER_MEETING_BOOKER_EVENT_TYPE);
|
|
1789
|
+
if (intentIndex === -1) return;
|
|
1790
|
+
const intentMessage = messages[intentIndex];
|
|
1791
|
+
// If the offer this intent asked for already arrived, the wait is over.
|
|
1792
|
+
// Arming anyway would emit a second `offered` for an offer no one waited on.
|
|
1793
|
+
//
|
|
1794
|
+
// Matched on AI-authored offers only, the same test the live path applies. A
|
|
1795
|
+
// rep can offer a meeting while the AI's own offer is still being prepared, and
|
|
1796
|
+
// treating that as the intent's answer would abandon a wait that is still
|
|
1797
|
+
// running and drop the journey from the SLI entirely. Arming when the AI never
|
|
1798
|
+
// does answer is harmless: a hydrated intent cannot report abandonment.
|
|
1799
|
+
const offerArrived = messages.slice(intentIndex + 1).some((message)=>message.type === "calendar_event_offer" && isAiAuthoredOffer("source" in message ? message.source : undefined));
|
|
1800
|
+
if (offerArrived) return;
|
|
1801
|
+
this.armBookerIntent(intentMessage.id, {
|
|
1802
|
+
// An index cannot carry the narrowing out of the predicate above, and the
|
|
1803
|
+
// optimistic (not-yet-persisted) message variants carry no server fields at
|
|
1804
|
+
// all, so this probes for the field rather than asserting it away.
|
|
1805
|
+
scriptId: "scriptId" in intentMessage ? intentMessage.scriptId : undefined,
|
|
1806
|
+
fromHistory: true
|
|
1807
|
+
});
|
|
1808
|
+
}, this.submitInitialDisplayMode = ()=>{
|
|
1809
|
+
const hasStartingDisplayModeMessage = !!this.messages.peek().find((message)=>message.type === "event" && message.eventType === "startingDisplayMode");
|
|
1810
|
+
if (!hasStartingDisplayModeMessage) {
|
|
1811
|
+
this.sendMessage({
|
|
1812
|
+
type: "event",
|
|
1813
|
+
eventType: "startingDisplayMode"
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
}, this.submitInitialModality = ()=>{
|
|
1817
|
+
const hasStartingModalityMessage = !!this.messages.peek().find((message)=>message.type === "event" && message.eventType === "startingModality");
|
|
1818
|
+
if (hasStartingModalityMessage) return;
|
|
1819
|
+
const modality = this.modalityPreference.peek();
|
|
1820
|
+
if (!modality) return;
|
|
1821
|
+
this.sendMessage({
|
|
1822
|
+
type: "event",
|
|
1823
|
+
eventType: "startingModality",
|
|
1824
|
+
multimodalStartingModality: modality
|
|
1825
|
+
});
|
|
1826
|
+
}, this.submitInitialMessage = ()=>{
|
|
1827
|
+
var _this_activeConversation_peek;
|
|
1828
|
+
const initialMessage = (_this_activeConversation_peek = this.activeConversation.peek()) == null ? void 0 : _this_activeConversation_peek.initialMessage;
|
|
1829
|
+
if (!initialMessage) return;
|
|
1830
|
+
const hasVisitorText = this.getActiveConversationMessages().some((m)=>m.type === "text" && "isOwn" in m && m.isOwn);
|
|
1831
|
+
if (!hasVisitorText) {
|
|
1832
|
+
this.sendVisitorText(initialMessage);
|
|
1833
|
+
}
|
|
1834
|
+
}, this.getActiveConversationMessages = ()=>{
|
|
1835
|
+
var _this_activeConversation_peek;
|
|
1836
|
+
const conversationId = (_this_activeConversation_peek = this.activeConversation.peek()) == null ? void 0 : _this_activeConversation_peek.id;
|
|
1837
|
+
if (!conversationId) return [];
|
|
1838
|
+
return this.messages.value.filter((m)=>m.conversationId === conversationId);
|
|
1839
|
+
}, this.activeConversationAwaitsPounce = ()=>{
|
|
1840
|
+
let pastStartChatBot = false;
|
|
1841
|
+
for (const m of this.getActiveConversationMessages()){
|
|
1842
|
+
if (isEventMessage(m, "startChatBot")) {
|
|
1843
|
+
pastStartChatBot = true;
|
|
1844
|
+
} else if (pastStartChatBot && m.type !== "event") {
|
|
1845
|
+
return false;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
return pastStartChatBot;
|
|
1849
|
+
}, this.debugLog = (msg, context)=>{
|
|
1850
|
+
if (isDebugMode()) {
|
|
1851
|
+
// eslint-disable-next-line no-console
|
|
1852
|
+
console.log("[Multimodal - RootStore] " + msg, context);
|
|
1853
|
+
}
|
|
1854
|
+
}, this.startPounceTracking = ()=>{
|
|
1855
|
+
if (this.pounceTrackingLoadStarted) {
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
this.pounceTrackingLoadStarted = true;
|
|
1859
|
+
this.pounceTrackingStore.load().then((pounceTrackingStore)=>{
|
|
1860
|
+
if (this.messages.pounceActive.peek()) {
|
|
1861
|
+
pounceTrackingStore.startTracking();
|
|
1862
|
+
} else {
|
|
1863
|
+
pounceTrackingStore.dispose();
|
|
1864
|
+
this.pounceTrackingLoadStarted = false;
|
|
1865
|
+
}
|
|
1866
|
+
}).catch(()=>{
|
|
1867
|
+
this.pounceTrackingLoadStarted = false;
|
|
1868
|
+
// Pounce tracking is non-critical - fail silently
|
|
1869
|
+
});
|
|
1870
|
+
};
|
|
1871
|
+
// WS message handlers
|
|
1872
|
+
for (const [event, handler] of Object.entries(this.messageHandlers)){
|
|
1873
|
+
this.channel.on(event, handler);
|
|
1874
|
+
}
|
|
1875
|
+
// Effect handlers
|
|
1876
|
+
for (const handler of this.signalSubscriptions){
|
|
1877
|
+
handler();
|
|
1878
|
+
}
|
|
1879
|
+
// Initialize translation store (sets up locale effects)
|
|
1880
|
+
this.translationStore.initialize();
|
|
1881
|
+
if (typeof document !== "undefined") {
|
|
1882
|
+
__nativeDocumentAddEventListener("visibilitychange", this.handleTurnVisibilityChange);
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
RootStore.IMPRESSED_MESSAGE_IDS_MAX_SIZE = 100;
|
|
1887
|
+
/**
|
|
1888
|
+
* How long the booker waits when the offer arrives by way of a voice
|
|
1889
|
+
* placeholder, so the visitor can read the transcript text that just filled it
|
|
1890
|
+
* before the overlay covers the page.
|
|
1891
|
+
*/ RootStore.MEETING_BOOKER_PLACEHOLDER_DELAY_MS = 1000;
|
|
1892
|
+
// Kept in sync with MultimodalAgentWorker#broadcast_text_response_ended.
|
|
1893
|
+
RootStore.TEXT_RESPONSE_ENDED_REASONS = [
|
|
1894
|
+
"suppressed",
|
|
1895
|
+
"blank_output"
|
|
1896
|
+
];
|
|
1897
|
+
// Kept in sync with AiConversations::TurnCause::CAUSES.
|
|
1898
|
+
RootStore.TURN_CAUSES = [
|
|
1899
|
+
"queue_wait",
|
|
1900
|
+
"setup",
|
|
1901
|
+
"llm_slow",
|
|
1902
|
+
"agent_loop",
|
|
1903
|
+
"tool_slow",
|
|
1904
|
+
"side_llm_wait",
|
|
1905
|
+
"generation_failed",
|
|
1906
|
+
"output_moderation_retry",
|
|
1907
|
+
"unknown"
|
|
1908
|
+
];
|
|
1909
|
+
const RootStoreContext = Q(null);
|
|
1910
|
+
/**
|
|
1911
|
+
* Please avoid using this hook as much as possible!!!
|
|
1912
|
+
* It makes components harder to test and work with.
|
|
1913
|
+
*/ function useRootStore() {
|
|
1914
|
+
return hooks_module_x(RootStoreContext);
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
var root_store_CAUSE_REPORT_GRACE_MS = (/* inlined export .CAUSE_REPORT_GRACE_MS */1500);
|
|
1918
|
+
var root_store_RESPONSE_BUDGET_MS = (/* inlined export .RESPONSE_BUDGET_MS */25000);
|
|
1919
|
+
export { RootStore, RootStoreContext, root_store_CAUSE_REPORT_GRACE_MS as CAUSE_REPORT_GRACE_MS, root_store_RESPONSE_BUDGET_MS as RESPONSE_BUDGET_MS, useRootStore };
|