@mgamadeus/ddd-agents 0.0.1-stage → 0.1.0-alpha.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/dist/chat.js +5263 -0
- package/dist/chat.js.map +1 -0
- package/dist/chunks/AgentsConversationClientContext-C4T7g_Qk.js +22 -0
- package/dist/chunks/AgentsConversationClientContext-C4T7g_Qk.js.map +1 -0
- package/dist/chunks/ConversationMarkdown-W7NHglmL.js +399 -0
- package/dist/chunks/ConversationMarkdown-W7NHglmL.js.map +1 -0
- package/dist/chunks/WebSearchResultsConversationWidget-BvnLWHyi.js +5059 -0
- package/dist/chunks/WebSearchResultsConversationWidget-BvnLWHyi.js.map +1 -0
- package/dist/chunks/canvas-behaviours-BRFR1ALE.js +45 -0
- package/dist/chunks/canvas-behaviours-BRFR1ALE.js.map +1 -0
- package/dist/chunks/canvas-feature-flags-BmmxC3bz.js +11 -0
- package/dist/chunks/canvas-feature-flags-BmmxC3bz.js.map +1 -0
- package/dist/chunks/canvasBodies-9SuF6NAB.js +289 -0
- package/dist/chunks/canvasBodies-9SuF6NAB.js.map +1 -0
- package/dist/chunks/d3-7.6.1.min.js-DTmPafrl.js +6 -0
- package/dist/chunks/d3-7.6.1.min.js-DTmPafrl.js.map +1 -0
- package/dist/chunks/decide-canvas-open-Ct-wnNEJ.js +20 -0
- package/dist/chunks/decide-canvas-open-Ct-wnNEJ.js.map +1 -0
- package/dist/chunks/widget-image-readiness-signal-DeOuL89j.js +25 -0
- package/dist/chunks/widget-image-readiness-signal-DeOuL89j.js.map +1 -0
- package/dist/chunks/widget-name-resolver-DyIY6Zu4.js +9736 -0
- package/dist/chunks/widget-name-resolver-DyIY6Zu4.js.map +1 -0
- package/dist/chunks/withPollingChangeSubscription-BLDAWF-o.js +251 -0
- package/dist/chunks/withPollingChangeSubscription-BLDAWF-o.js.map +1 -0
- package/dist/client-testing.js +346 -0
- package/dist/client-testing.js.map +1 -0
- package/dist/client.js +16 -0
- package/dist/client.js.map +1 -0
- package/dist/contract.js +31 -0
- package/dist/contract.js.map +1 -0
- package/dist/image-render.js +334 -0
- package/dist/image-render.js.map +1 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/kit.js +13 -0
- package/dist/kit.js.map +1 -0
- package/dist/preview.js +733 -0
- package/dist/preview.js.map +1 -0
- package/dist/renderer.js +115 -0
- package/dist/renderer.js.map +1 -0
- package/dist/styles.css +6768 -0
- package/dist/types/chat/AgentChat/AgentChat.d.ts +37 -0
- package/dist/types/chat/AgentChatActivityHistory/AgentChatActivityHistory.d.ts +6 -0
- package/dist/types/chat/AgentChatActivityIndicator/AgentChatActivityIndicator.d.ts +8 -0
- package/dist/types/chat/CanvasPane/CanvasPane.d.ts +15 -0
- package/dist/types/chat/CanvasResizeHandle/CanvasResizeHandle.d.ts +10 -0
- package/dist/types/chat/CanvasResizeHandle/use-canvas-resize.d.ts +43 -0
- package/dist/types/chat/ConversationClosedBar/ConversationClosedBar.d.ts +10 -0
- package/dist/types/chat/ConversationErrorCard/ConversationErrorCard.d.ts +13 -0
- package/dist/types/chat/ConversationIntroCard/ConversationIntroCard.d.ts +10 -0
- package/dist/types/chat/ConversationLoadingStates.d.ts +2 -0
- package/dist/types/chat/ConversationMessage/ConversationCallMarkerChip/ConversationCallMarkerChip.d.ts +14 -0
- package/dist/types/chat/ConversationMessage/ConversationCallMarkerChip/VoiceBarsIcon/VoiceBarsIcon.d.ts +8 -0
- package/dist/types/chat/ConversationMessage/ConversationMessageSystem/ConversationMessageSystem.d.ts +8 -0
- package/dist/types/chat/ConversationMessage/ConversationMessageSystem/ConversationMessageSystemHeader/ConversationMessageSystemHeader.d.ts +9 -0
- package/dist/types/chat/ConversationMessage/ConversationMessageUser/ConversationMessageUser.d.ts +7 -0
- package/dist/types/chat/ConversationMessage/ConversationMessageUser/ConversationMessageUserWidgets.d.ts +14 -0
- package/dist/types/chat/ConversationMessage/ConversationMessageWidgets/ConversationMessageHeader/ConversationMessageHeader.d.ts +7 -0
- package/dist/types/chat/ConversationMessage/ConversationMessageWidgets/ConversationMessageThinking/ConversationMessageThinking.d.ts +7 -0
- package/dist/types/chat/ConversationMessage/ConversationMessageWidgets/ConversationMessageWidgets.d.ts +29 -0
- package/dist/types/chat/ConversationMessage/ConversationMessageWidgets/copyRichToClipboard.d.ts +1 -0
- package/dist/types/chat/ConversationMessage/ConversationMessageWidgets/hooks/useFreshMessageBlockReveal.d.ts +14 -0
- package/dist/types/chat/ConversationMessage/ConversationMessageWidgets/hooks/useStreamingText.d.ts +8 -0
- package/dist/types/chat/ConversationMessage/ConversationMessageWidgets/hooks/useTypewriter.d.ts +12 -0
- package/dist/types/chat/ConversationMessage/ConversationTaskInstructionDisclosure/ConversationTaskInstructionDisclosure.d.ts +12 -0
- package/dist/types/chat/ConversationMessageInput/ConversationMessageInput.d.ts +111 -0
- package/dist/types/chat/ConversationMessageInput/ConversationMessageInputAttachments/ConversationMessageInputAttachmentPreviewCard.d.ts +34 -0
- package/dist/types/chat/ConversationMessageInput/ConversationMessageInputAttachments/ConversationMessageInputAttachmentsErrorBox.d.ts +22 -0
- package/dist/types/chat/ConversationMessageInput/ConversationMessageInputAttachments/ConversationMessageInputAttachmentsPreviewStrip.d.ts +12 -0
- package/dist/types/chat/ConversationMessageInput/ConversationMessageInputAttachments/convert-bytes-to-size-string.d.ts +2 -0
- package/dist/types/chat/ConversationMessageInput/ConversationMessageInputComposerWidgets/ConversationMessageInputComposerWidgets.d.ts +12 -0
- package/dist/types/chat/ConversationMessageList/ConversationMessageList.d.ts +75 -0
- package/dist/types/chat/ConversationScrollToLatestMessageButton/ConversationScrollToLatestMessageButton.d.ts +6 -0
- package/dist/types/chat/ConversationSuggestionCard/ConversationSuggestionCard.d.ts +7 -0
- package/dist/types/chat/ConversationView.d.ts +216 -0
- package/dist/types/chat/ConversationViewBody/ConversationViewBody.d.ts +214 -0
- package/dist/types/chat/ConversationViewBody/hooks/useIsMessagesBottomOutOfView.d.ts +2 -0
- package/dist/types/chat/ConversationViewBody/hooks/usePinQuestionOnSend.d.ts +24 -0
- package/dist/types/chat/assistant-message-has-renderable-markdown.d.ts +13 -0
- package/dist/types/chat/chat-assistant-identity.d.ts +24 -0
- package/dist/types/chat/chat-message.d.ts +11 -0
- package/dist/types/chat/controller/AgentChatController.d.ts +97 -0
- package/dist/types/chat/controller/activity-ticker.d.ts +120 -0
- package/dist/types/chat/controller/changed-messages-request.d.ts +12 -0
- package/dist/types/chat/controller/chat-scroll-target.d.ts +10 -0
- package/dist/types/chat/controller/conversation-change-signal-rules.d.ts +64 -0
- package/dist/types/chat/controller/derive-agent-activity.d.ts +31 -0
- package/dist/types/chat/controller/merge-activity-elements.d.ts +25 -0
- package/dist/types/chat/controller/pending-start-request.d.ts +38 -0
- package/dist/types/chat/controller/use-activity-ticker.d.ts +60 -0
- package/dist/types/chat/controller/use-conversation-activity.d.ts +38 -0
- package/dist/types/chat/controller/use-conversation-live-updates.d.ts +30 -0
- package/dist/types/chat/controller/use-conversation-message-pages.d.ts +24 -0
- package/dist/types/chat/controller/use-conversation-scroll-jump.d.ts +47 -0
- package/dist/types/chat/controller/use-conversation-search.d.ts +31 -0
- package/dist/types/chat/controller/use-conversation-signals.d.ts +44 -0
- package/dist/types/chat/controller/use-debounced-semantic-search.d.ts +15 -0
- package/dist/types/chat/conversation-send-message.d.ts +43 -0
- package/dist/types/chat/last-wire-message-is-unanswered-user-message.d.ts +22 -0
- package/dist/types/chat/normalize-server-message-to-markdown.d.ts +23 -0
- package/dist/types/chat/store/agents-chat-state.d.ts +132 -0
- package/dist/types/chat/store/conversation-error-type.d.ts +32 -0
- package/dist/types/chat/store/create-agents-chat-store.d.ts +23 -0
- package/dist/types/chat/store/index.d.ts +5 -0
- package/dist/types/chat/store/merge-chat-messages.d.ts +20 -0
- package/dist/types/chat/store/reduce-agents-chat-state.d.ts +7 -0
- package/dist/types/chat/store/use-agents-chat-store.d.ts +22 -0
- package/dist/types/chat/strip-pre-call-message-marker.d.ts +12 -0
- package/dist/types/chat/use-canvas-shell-transitions.d.ts +25 -0
- package/dist/types/client/AgentsConversationClient.d.ts +93 -0
- package/dist/types/client/AgentsConversationClientContext.d.ts +17 -0
- package/dist/types/client/polling/createPollingConversationChangeSubscription.d.ts +62 -0
- package/dist/types/client/polling/withPollingChangeSubscription.d.ts +11 -0
- package/dist/types/client/testing/createInMemoryAgentsConversationClient.d.ts +139 -0
- package/dist/types/contract/conversation.d.ts +370 -0
- package/dist/types/entries/chat.d.ts +21 -0
- package/dist/types/entries/client-testing.d.ts +1 -0
- package/dist/types/entries/client.d.ts +4 -0
- package/dist/types/entries/contract.d.ts +1 -0
- package/dist/types/entries/image-render.d.ts +9 -0
- package/dist/types/entries/index.d.ts +9 -0
- package/dist/types/entries/kit.d.ts +4 -0
- package/dist/types/entries/preview.d.ts +8 -0
- package/dist/types/entries/renderer.d.ts +15 -0
- package/dist/types/entries/widgets.d.ts +16 -0
- package/dist/types/image-render/ComposedViewEmailDataTimes/ComposedViewEmailDataTimes.d.ts +15 -0
- package/dist/types/image-render/WidgetImageRender.d.ts +55 -0
- package/dist/types/image-render/WidgetImageRenderContext.d.ts +14 -0
- package/dist/types/image-render/composed-view-data-times.d.ts +20 -0
- package/dist/types/image-render/frozen-widget-requests.d.ts +2 -0
- package/dist/types/image-render/live-widget-requests.d.ts +4 -0
- package/dist/types/image-render/widget-image-google-map.d.ts +7 -0
- package/dist/types/image-render/widget-image-readiness-signal.d.ts +16 -0
- package/dist/types/image-render/widget-image-readiness.d.ts +9 -0
- package/dist/types/image-render/widget-image-render.types.d.ts +47 -0
- package/dist/types/kit/ComposedViewConversationWidget.d.ts +14 -0
- package/dist/types/kit/ComposedViewDataSourceLoader.d.ts +20 -0
- package/dist/types/kit/ComposedViewRenderer.d.ts +30 -0
- package/dist/types/kit/ComposedViewSlotErrorBoundary.d.ts +23 -0
- package/dist/types/kit/ModelText/ModelText.d.ts +12 -0
- package/dist/types/kit/components/embedding/EmbeddedConversationWidget.d.ts +26 -0
- package/dist/types/kit/components/map/map-full-screen-instance.d.ts +26 -0
- package/dist/types/kit/components/map/map-presentation-context.d.ts +8 -0
- package/dist/types/kit/components/text/shared/text-node-values.d.ts +39 -0
- package/dist/types/kit/composed-view-component-types.d.ts +61 -0
- package/dist/types/kit/composed-view-components.d.ts +7 -0
- package/dist/types/kit/composed-view-tones.d.ts +20 -0
- package/dist/types/kit/entity-detail/EntityDetailCanvasBody.d.ts +9 -0
- package/dist/types/kit/entity-detail/entity-detail-bodies.d.ts +32 -0
- package/dist/types/kit/entity-detail/entity-detail-name.d.ts +9 -0
- package/dist/types/kit/hooks/use-debounced-value.d.ts +2 -0
- package/dist/types/kit/map-full-screen/MapFullScreenCanvasBody.d.ts +11 -0
- package/dist/types/preview/WidgetPreviewHost.d.ts +20 -0
- package/dist/types/preview/preview-stub-components.d.ts +3 -0
- package/dist/types/preview/widget-preview-fixtures.d.ts +17 -0
- package/dist/types/preview/widget-preview-hydration.d.ts +10 -0
- package/dist/types/preview/widget-preview-readiness.d.ts +27 -0
- package/dist/types/preview/widget-preview-requests.d.ts +16 -0
- package/dist/types/preview/widget-preview-signal.d.ts +10 -0
- package/dist/types/preview/widget-preview.types.d.ts +134 -0
- package/dist/types/provider/AgentsProvider.d.ts +33 -0
- package/dist/types/provider/class-names.d.ts +7 -0
- package/dist/types/provider/components/default-components.d.ts +3 -0
- package/dist/types/provider/components/icon-names.d.ts +9 -0
- package/dist/types/provider/components/index.d.ts +39 -0
- package/dist/types/provider/components/types.d.ts +463 -0
- package/dist/types/provider/environment.d.ts +262 -0
- package/dist/types/provider/index.d.ts +4 -0
- package/dist/types/renderer/ConversationDeepLinkedWidget.d.ts +18 -0
- package/dist/types/renderer/ConversationMarkdown.d.ts +21 -0
- package/dist/types/renderer/ConversationWidgetActions/ConversationWidgetActions.d.ts +7 -0
- package/dist/types/renderer/ConversationWidgetCallbacksContext.d.ts +234 -0
- package/dist/types/renderer/ConversationWidgetContext.d.ts +32 -0
- package/dist/types/renderer/ConversationWidgetErrorBoundary.d.ts +25 -0
- package/dist/types/renderer/ConversationWidgetHost.d.ts +36 -0
- package/dist/types/renderer/ConversationWidgetRenderContext.d.ts +3 -0
- package/dist/types/renderer/MarkdownLink.d.ts +6 -0
- package/dist/types/renderer/WindowedWidget/WindowedWidget.d.ts +16 -0
- package/dist/types/renderer/_access/ConversationWidgetFeatureGate.d.ts +21 -0
- package/dist/types/renderer/_agent-space/ConversationWidgetAgentSpaceFrame.d.ts +12 -0
- package/dist/types/renderer/_agent-space/ConversationWidgetAgentSpaceHeader.d.ts +15 -0
- package/dist/types/renderer/_agent-space/agent-space-name.hydrator.d.ts +7 -0
- package/dist/types/renderer/_agent-space/declared-agent-space-key-field-name.d.ts +8 -0
- package/dist/types/renderer/_agent-space/use-resolved-conversation-widget-agent-space-key.d.ts +16 -0
- package/dist/types/renderer/_agent-space/widget-agent-space-key-attribute.d.ts +13 -0
- package/dist/types/renderer/balanced-json-object.d.ts +12 -0
- package/dist/types/renderer/canvas/CanvasRestoreOnMount.d.ts +19 -0
- package/dist/types/renderer/canvas/canvas-behaviours.d.ts +84 -0
- package/dist/types/renderer/canvas/canvas-body-props.d.ts +18 -0
- package/dist/types/renderer/canvas/canvas-feature-flags.d.ts +11 -0
- package/dist/types/renderer/canvas/canvas-reducers.d.ts +46 -0
- package/dist/types/renderer/canvas/canvas-state.d.ts +107 -0
- package/dist/types/renderer/canvas/canvasBodies.d.ts +19 -0
- package/dist/types/renderer/canvas/canvasStorage.d.ts +25 -0
- package/dist/types/renderer/canvas/decide-canvas-open.d.ts +32 -0
- package/dist/types/renderer/canvas/entity-detail-instance.d.ts +27 -0
- package/dist/types/renderer/canvas/find-widget-fences-in-markdown.d.ts +32 -0
- package/dist/types/renderer/canvas/use-anchor-widget-payload.d.ts +8 -0
- package/dist/types/renderer/composer-widget-fence.d.ts +30 -0
- package/dist/types/renderer/conversation-composer-widget.d.ts +13 -0
- package/dist/types/renderer/conversation-widget-props.d.ts +54 -0
- package/dist/types/renderer/conversation-widget-state-partition.d.ts +31 -0
- package/dist/types/renderer/conversationWidgetAstPlugin.d.ts +11 -0
- package/dist/types/renderer/conversationWidgetRegistry.d.ts +74 -0
- package/dist/types/renderer/detect-unrendered-widgets.d.ts +42 -0
- package/dist/types/renderer/markdown-contains-widget.d.ts +13 -0
- package/dist/types/renderer/reference-naked-widget-json.d.ts +1 -0
- package/dist/types/renderer/remarkWidgetFencePlugin.d.ts +22 -0
- package/dist/types/renderer/render-report/widget-render-report-collector.d.ts +57 -0
- package/dist/types/renderer/render-report/widget-render-report.d.ts +29 -0
- package/dist/types/renderer/repair-malformed-widget-fences.d.ts +11 -0
- package/dist/types/renderer/resolve-deep-linkable-widget.d.ts +25 -0
- package/dist/types/renderer/restoreTextDirectivesPlugin.d.ts +12 -0
- package/dist/types/renderer/widget-click-signal-sources.d.ts +15 -0
- package/dist/types/renderer/widget-fence-json.d.ts +16 -0
- package/dist/types/renderer/widget-fence-syntax.d.ts +25 -0
- package/dist/types/renderer/widget-hydration/index.d.ts +8 -0
- package/dist/types/renderer/widget-hydration/section-hydration.d.ts +41 -0
- package/dist/types/renderer/widget-hydration/use-widget-hydration.d.ts +27 -0
- package/dist/types/renderer/widget-hydration/widget-hydration-state-store.d.ts +27 -0
- package/dist/types/renderer/widget-hydration/widget-hydration.reducer.d.ts +39 -0
- package/dist/types/renderer/widget-hydration/widget-hydration.registry.d.ts +7 -0
- package/dist/types/renderer/widget-hydration/widget-hydration.slice.d.ts +8 -0
- package/dist/types/renderer/widget-hydration/widget-hydration.types.d.ts +27 -0
- package/dist/types/renderer/widget-name-resolver.d.ts +22 -0
- package/dist/types/renderer/widget-tree/bounded-json.d.ts +23 -0
- package/dist/types/renderer/widget-tree/composed-node-schema.d.ts +143 -0
- package/dist/types/renderer/widget-tree/composed-view-data-sources.d.ts +41 -0
- package/dist/types/renderer/widget-tree/composed-view-tree.d.ts +111 -0
- package/dist/types/renderer/widget-tree/expression/evaluate-expression.d.ts +65 -0
- package/dist/types/renderer/widget-tree/expression/parse-expression.d.ts +71 -0
- package/dist/types/renderer/widget-tree/resolve-composed-view-values.d.ts +49 -0
- package/dist/types/renderer/widget-tree/use-composed-view-state.d.ts +16 -0
- package/dist/types/renderer/widget-windowing.d.ts +25 -0
- package/dist/types/widgets/CanvasWidgetCard/CanvasWidgetCard.d.ts +79 -0
- package/dist/types/widgets/CanvasWidgetCard/CanvasWidgetSwitch.d.ts +61 -0
- package/dist/types/widgets/CanvasWidgetCard/canvas-for-message-widget.d.ts +8 -0
- package/dist/types/widgets/CanvasWidgetCard/use-canvas-viable.d.ts +15 -0
- package/dist/types/widgets/ClockConversationWidget/ClockConversationWidget.d.ts +11 -0
- package/dist/types/widgets/ClockConversationWidget/ClockWidget/AnalogClockFace.d.ts +12 -0
- package/dist/types/widgets/ClockConversationWidget/ClockWidget/ClockRow.d.ts +23 -0
- package/dist/types/widgets/ClockConversationWidget/ClockWidget/ClockWidget.d.ts +21 -0
- package/dist/types/widgets/ClockConversationWidget/ClockWidget/DayNightGlyph.d.ts +8 -0
- package/dist/types/widgets/ClockConversationWidget/shared/clock-time.d.ts +52 -0
- package/dist/types/widgets/ClockConversationWidget/shared/clock-widget-payload.d.ts +23 -0
- package/dist/types/widgets/ClockConversationWidget/shared/use-clock-settings.d.ts +18 -0
- package/dist/types/widgets/ConceptExplainerConversationWidget/ConceptExplainerConversationWidget.d.ts +14 -0
- package/dist/types/widgets/ConceptExplainerConversationWidget/ConceptExplainerWidget/ConceptExplainerWidget.d.ts +27 -0
- package/dist/types/widgets/ConceptExplainerConversationWidget/shared/ConceptExplainerTextView.d.ts +11 -0
- package/dist/types/widgets/ConceptExplainerConversationWidget/shared/concept-explainer-type.d.ts +89 -0
- package/dist/types/widgets/ConceptExplainerConversationWidget/shared/concept-explainer-types.d.ts +17 -0
- package/dist/types/widgets/ConceptExplainerConversationWidget/shared/concept-explainer-widget-payload.d.ts +25 -0
- package/dist/types/widgets/ConceptExplainerConversationWidget/shared/use-concept-explainer-language.d.ts +8 -0
- package/dist/types/widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsCanvas.d.ts +9 -0
- package/dist/types/widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsCanvasBody.d.ts +7 -0
- package/dist/types/widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsConversationWidget.d.ts +15 -0
- package/dist/types/widgets/ConversationSearchResultsConversationWidget/shared/conversation-search-results-widget-payload.d.ts +42 -0
- package/dist/types/widgets/ConversationSearchResultsConversationWidget/shared/search-hit-excerpt.d.ts +1 -0
- package/dist/types/widgets/CurrencyConverterConversationWidget/CurrencyConverterConversationWidget.d.ts +8 -0
- package/dist/types/widgets/CurrencyConverterConversationWidget/CurrencyConverterWidget/CurrencyConverterWidget.d.ts +17 -0
- package/dist/types/widgets/CurrencyConverterConversationWidget/CurrencyConverterWidget/CurrencyLabel/CurrencyLabel.d.ts +9 -0
- package/dist/types/widgets/CurrencyConverterConversationWidget/shared/currency-converter-widget-payload.d.ts +22 -0
- package/dist/types/widgets/CurrencyConverterConversationWidget/shared/currency-display.d.ts +30 -0
- package/dist/types/widgets/CustomVisualizationConversationWidget/CustomVisualizationConversationWidget.d.ts +11 -0
- package/dist/types/widgets/CustomVisualizationConversationWidget/CustomVisualizationWidget/CustomVisualizationFrame.d.ts +14 -0
- package/dist/types/widgets/CustomVisualizationConversationWidget/CustomVisualizationWidget/CustomVisualizationWidget.d.ts +15 -0
- package/dist/types/widgets/CustomVisualizationConversationWidget/shared/custom-visualization-frame-protocol.d.ts +77 -0
- package/dist/types/widgets/CustomVisualizationConversationWidget/shared/custom-visualization-libraries.d.ts +25 -0
- package/dist/types/widgets/CustomVisualizationConversationWidget/shared/custom-visualization-sandbox-config.d.ts +21 -0
- package/dist/types/widgets/CustomVisualizationConversationWidget/shared/custom-visualization-widget-payload.d.ts +55 -0
- package/dist/types/widgets/CustomVisualizationConversationWidget/shared/read-custom-visualization-theme.d.ts +5 -0
- package/dist/types/widgets/EntityMentionConversationWidget/EntityMentionAskButton/EntityMentionAskButton.d.ts +13 -0
- package/dist/types/widgets/EntityMentionConversationWidget/EntityMentionCard/EntityMentionCard.d.ts +11 -0
- package/dist/types/widgets/EntityMentionConversationWidget/EntityMentionConversationWidget.d.ts +9 -0
- package/dist/types/widgets/EntityMentionConversationWidget/shared/entity-mention-presentation.d.ts +4 -0
- package/dist/types/widgets/EntityMentionConversationWidget/shared/entity-mention.d.ts +33 -0
- package/dist/types/widgets/EntityMentionConversationWidget/shared/use-entity-mention-composer.d.ts +17 -0
- package/dist/types/widgets/MultiChoiceConversationWidget/MultiChoiceConversationWidget.d.ts +2 -0
- package/dist/types/widgets/MultiChoiceConversationWidget/shared/multi-choice-widget-payload.d.ts +16 -0
- package/dist/types/widgets/QuickRepliesConversationWidget/QuickRepliesConversationWidget.d.ts +38 -0
- package/dist/types/widgets/QuickRepliesConversationWidget/shared/quick-replies-widget-payload.d.ts +20 -0
- package/dist/types/widgets/SuggestedActions/SuggestedActions.d.ts +10 -0
- package/dist/types/widgets/ToolTraceConversationWidget/ToolTraceConversationWidget.d.ts +8 -0
- package/dist/types/widgets/WebSearchResultsConversationWidget/SourceFavicon.d.ts +10 -0
- package/dist/types/widgets/WebSearchResultsConversationWidget/WebSearchAnswer.d.ts +6 -0
- package/dist/types/widgets/WebSearchResultsConversationWidget/WebSearchReferences.d.ts +12 -0
- package/dist/types/widgets/WebSearchResultsConversationWidget/WebSearchResultsCanvas.d.ts +10 -0
- package/dist/types/widgets/WebSearchResultsConversationWidget/WebSearchResultsCanvasBody.d.ts +7 -0
- package/dist/types/widgets/WebSearchResultsConversationWidget/WebSearchResultsConversationWidget.d.ts +14 -0
- package/dist/types/widgets/WebSearchResultsConversationWidget/WebSearchSourcesList.d.ts +10 -0
- package/dist/types/widgets/WebSearchResultsConversationWidget/shared/display-hostname.d.ts +9 -0
- package/dist/types/widgets/WebSearchResultsConversationWidget/shared/web-search-results-widget-payload.d.ts +30 -0
- package/dist/types/widgets/WritingDraftConversationWidget/WritingDraftConversationWidget.d.ts +14 -0
- package/dist/types/widgets/WritingDraftConversationWidget/WritingDraftWidget/WritingDraftBody.d.ts +9 -0
- package/dist/types/widgets/WritingDraftConversationWidget/WritingDraftWidget/WritingDraftWidget.d.ts +44 -0
- package/dist/types/widgets/WritingDraftConversationWidget/shared/draft-mail-link.d.ts +17 -0
- package/dist/types/widgets/WritingDraftConversationWidget/shared/draft-plain-text.d.ts +7 -0
- package/dist/types/widgets/WritingDraftConversationWidget/shared/remark-draft-line-breaks.d.ts +13 -0
- package/dist/types/widgets/WritingDraftConversationWidget/shared/writing-draft-widget-payload.d.ts +23 -0
- package/dist/types/widgets/_shared/ConversationWidgetButton.d.ts +4 -0
- package/dist/types/widgets/_shared/DonePill.d.ts +14 -0
- package/dist/types/widgets/_shared/modern/WidgetKpiValue.d.ts +24 -0
- package/dist/types/widgets/_shared/modern/WidgetListRow.d.ts +14 -0
- package/dist/types/widgets/_shared/modern/WidgetStatusChip.d.ts +8 -0
- package/dist/types/widgets/_shared/modern/WidgetSurface.d.ts +13 -0
- package/dist/types/widgets/_shared/modern/WidgetSurfaceSkeleton.d.ts +11 -0
- package/dist/types/widgets/_shared/modern/index.d.ts +16 -0
- package/dist/types/widgets/_shared/skeleton-primitives.d.ts +20 -0
- package/dist/types/widgets/_shared/use-stack-when-wrapped.d.ts +6 -0
- package/dist/types/widgets/_shared/widget-payload-validation.d.ts +22 -0
- package/dist/widgets.js +84 -0
- package/dist/widgets.js.map +1 -0
- package/package.json +120 -3
package/dist/chat.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.js","sources":["../src/chat/AgentChatActivityHistory/AgentChatActivityHistory.tsx","../src/chat/AgentChatActivityIndicator/AgentChatActivityIndicator.tsx","../src/chat/ConversationClosedBar/ConversationClosedBar.tsx","../src/renderer/balanced-json-object.ts","../src/renderer/repair-malformed-widget-fences.ts","../src/renderer/reference-naked-widget-json.ts","../src/chat/strip-pre-call-message-marker.ts","../src/chat/normalize-server-message-to-markdown.ts","../src/renderer/canvas/canvasStorage.ts","../src/renderer/canvas/CanvasRestoreOnMount.tsx","../src/renderer/resolve-deep-linkable-widget.ts","../src/renderer/ConversationDeepLinkedWidget.tsx","../src/chat/store/agents-chat-state.ts","../src/renderer/canvas/canvas-reducers.ts","../src/chat/store/conversation-error-type.ts","../src/chat/store/merge-chat-messages.ts","../src/chat/store/reduce-agents-chat-state.ts","../src/chat/store/create-agents-chat-store.ts","../src/chat/store/use-agents-chat-store.tsx","../src/renderer/ConversationWidgetContext.tsx","../src/renderer/composer-widget-fence.ts","../src/chat/ConversationMessageInput/ConversationMessageInput.tsx","../src/chat/ConversationMessageInput/ConversationMessageInputComposerWidgets/ConversationMessageInputComposerWidgets.tsx","../src/chat/ConversationLoadingStates.tsx","../src/chat/ConversationMessage/ConversationMessageUser/ConversationMessageUserWidgets.tsx","../src/chat/ConversationMessage/ConversationMessageUser/ConversationMessageUser.tsx","../src/chat/ConversationMessage/ConversationMessageWidgets/hooks/useTypewriter.ts","../src/chat/ConversationMessage/ConversationMessageWidgets/hooks/useStreamingText.ts","../src/chat/ConversationMessage/ConversationMessageWidgets/hooks/useFreshMessageBlockReveal.ts","../src/chat/ConversationMessage/ConversationMessageWidgets/ConversationMessageHeader/ConversationMessageHeader.tsx","../src/chat/ConversationMessage/ConversationMessageWidgets/copyRichToClipboard.ts","../src/chat/ConversationMessage/ConversationMessageWidgets/ConversationMessageWidgets.tsx","../src/renderer/markdown-contains-widget.ts","../src/renderer/widget-click-signal-sources.ts","../src/chat/assistant-message-has-renderable-markdown.ts","../src/chat/last-wire-message-is-unanswered-user-message.ts","../src/chat/ConversationMessage/ConversationMessageSystem/ConversationMessageSystemHeader/ConversationMessageSystemHeader.tsx","../src/chat/ConversationMessage/ConversationMessageSystem/ConversationMessageSystem.tsx","../src/chat/ConversationMessage/ConversationCallMarkerChip/VoiceBarsIcon/VoiceBarsIcon.tsx","../src/chat/ConversationMessage/ConversationCallMarkerChip/ConversationCallMarkerChip.tsx","../src/chat/ConversationMessage/ConversationTaskInstructionDisclosure/ConversationTaskInstructionDisclosure.tsx","../src/chat/ConversationMessage/ConversationMessageWidgets/ConversationMessageThinking/ConversationMessageThinking.tsx","../src/chat/ConversationErrorCard/ConversationErrorCard.tsx","../src/chat/ConversationMessageList/ConversationMessageList.tsx","../src/chat/ConversationSuggestionCard/ConversationSuggestionCard.tsx","../src/chat/ConversationIntroCard/ConversationIntroCard.tsx","../src/chat/ConversationScrollToLatestMessageButton/ConversationScrollToLatestMessageButton.tsx","../src/chat/ConversationViewBody/hooks/useIsMessagesBottomOutOfView.ts","../src/chat/ConversationViewBody/hooks/usePinQuestionOnSend.ts","../src/chat/ConversationViewBody/ConversationViewBody.tsx","../src/chat/CanvasPane/CanvasPane.tsx","../src/chat/CanvasResizeHandle/use-canvas-resize.ts","../src/chat/CanvasResizeHandle/CanvasResizeHandle.tsx","../src/chat/use-canvas-shell-transitions.ts","../src/chat/ConversationView.tsx","../src/chat/controller/derive-agent-activity.ts","../src/chat/controller/pending-start-request.ts","../src/chat/controller/merge-activity-elements.ts","../src/chat/controller/activity-ticker.ts","../src/chat/controller/use-activity-ticker.ts","../src/chat/controller/use-conversation-activity.ts","../src/chat/controller/conversation-change-signal-rules.ts","../src/chat/controller/use-conversation-live-updates.ts","../src/chat/controller/changed-messages-request.ts","../src/chat/controller/use-conversation-message-pages.ts","../src/chat/controller/use-conversation-scroll-jump.ts","../src/renderer/detect-unrendered-widgets.ts","../src/chat/controller/use-conversation-signals.ts","../src/chat/controller/AgentChatController.tsx","../src/chat/AgentChat/AgentChat.tsx","../src/chat/controller/use-debounced-semantic-search.ts","../src/chat/controller/use-conversation-search.ts","../src/chat/ConversationMessageInput/ConversationMessageInputAttachments/ConversationMessageInputAttachmentsErrorBox.tsx","../src/chat/ConversationMessageInput/ConversationMessageInputAttachments/convert-bytes-to-size-string.ts","../src/chat/ConversationMessageInput/ConversationMessageInputAttachments/ConversationMessageInputAttachmentPreviewCard.tsx","../src/chat/ConversationMessageInput/ConversationMessageInputAttachments/ConversationMessageInputAttachmentsPreviewStrip.tsx"],"sourcesContent":["import { Text } from \"../../provider/components\";\nimport type { AgentChatActivityHistoryProps } from \"../controller/AgentChatController\";\nimport styles from \"./AgentChatActivityHistory.module.scss\";\n\n/**\n * The package default of a finished turn's kept activity lines under the message that started the turn (`AgentChat`\n * with `keepActivityHistoryAfterAnswer`): one muted line per step, oldest first.\n */\nexport const AgentChatActivityHistory = (props: AgentChatActivityHistoryProps) => {\n const { turn } = props;\n if (turn === null || turn.lines.length === 0) return null;\n return (\n <div className={styles.history} data-testid=\"agent-chat-activity-history\">\n {turn.lines.map((line) => (\n <Text key={`${line.toolCallMessageId}-${line.state}`} role=\"help\" color=\"--agents-fg-light\" translate={false}>\n {line.line}\n </Text>\n ))}\n </div>\n );\n};\n","import { Text } from \"../../provider/components\";\nimport type { AgentChatActivityProps } from \"../controller/AgentChatController\";\nimport styles from \"./AgentChatActivityIndicator.module.scss\";\n\n/**\n * The package default of the agent's activity at the tail of the thread (`AgentChat`'s `ThinkingIndicator`): three\n * dots and the line the activity ticker shows right now — the step the agent works on — while a turn runs; nothing\n * between turns or after a failed one (the error card speaks then). An app with an agent avatar replaces it\n * (host application: the agent's face beside the same ticker lines).\n */\nexport const AgentChatActivityIndicator = (props: AgentChatActivityProps) => {\n const { active, failed, activityTicker } = props;\n const isWorking = !failed && (active || activityTicker.phase === \"live\");\n if (!isWorking) return null;\n const currentLines = activityTicker.currentTurn?.lines ?? [];\n const currentLine = currentLines.length > 0 ? currentLines[currentLines.length - 1].line : null;\n return (\n <div className={styles.activity} role=\"status\" aria-live=\"polite\" data-testid=\"agent-chat-activity\">\n <div className={styles.dots} aria-hidden=\"true\">\n <span />\n <span />\n <span />\n </div>\n {currentLine !== null ? (\n <Text role=\"help\" color=\"--agents-fg-light\" translate={false}>\n {currentLine}\n </Text>\n ) : (\n <Text role=\"help\" color=\"--agents-fg-light\">\n Working on it…\n </Text>\n )}\n </div>\n );\n};\n","import { Icon, Text } from \"../../provider/components\";\nimport styles from \"./ConversationClosedBar.module.scss\";\n\nexport interface ConversationClosedBarProps {\n /** The status line; a text key. Default \"This conversation is closed.\". */\n text?: string;\n}\n\n/**\n * The package default in place of the composer on a closed conversation (its run state ended it: COMPLETED or\n * CANCELLED; the server refuses further input). A status line, no action — an app that offers one replaces the bar\n * through `AgentChat`'s `inputReplacementSlot`.\n */\nexport const ConversationClosedBar = (props: ConversationClosedBarProps) => {\n const { text = \"This conversation is closed.\" } = props;\n return (\n <div className={styles.bar} data-testid=\"agent-chat-closed-bar\">\n <Icon name=\"check\" color=\"--agents-success-500\" />\n <Text>{text}</Text>\n </div>\n );\n};\n","/**\n * The one string-aware brace walk every \"how far does this widget JSON payload extend\" question in the pipeline\n * needs — the malformed-fence repairer, the naked-JSON referencer and the unrendered-widget detector all call this\n * single copy of the scan.\n */\n\n/**\n * The index of the `}` closing the first `{ … }` JSON object that begins (after optional whitespace) at or after\n * `fromIndex` in `text`, or null when no object starts there or it never balances within `maxScanLength` characters\n * of its start. Quotes and escapes are tracked so a `{`/`}` inside a JSON string value never affects the brace\n * depth (e.g. a widget's `title` field containing literal `{braces}` text).\n */\nexport const findBalancedJsonObjectEnd = (text: string, fromIndex: number, maxScanLength = 5000): number | null => {\n let objectStart = fromIndex;\n while (objectStart < text.length && /\\s/.test(text[objectStart])) objectStart++;\n if (objectStart >= text.length || text[objectStart] !== \"{\") return null;\n\n let depth = 0;\n let inString = false;\n let escaped = false;\n const scanEnd = Math.min(text.length, objectStart + maxScanLength);\n for (let index = objectStart; index < scanEnd; index++) {\n const char = text[index];\n if (escaped) {\n escaped = false;\n continue;\n }\n if (char === \"\\\\\") {\n escaped = true;\n continue;\n }\n if (char === '\"') inString = !inString;\n if (inString) continue;\n if (char === \"{\") depth++;\n if (char === \"}\") {\n depth--;\n if (depth === 0) return index;\n }\n }\n return null;\n};\n","import { findBalancedJsonObjectEnd } from \"./balanced-json-object\";\nimport { WIDGET_FENCE_LANG } from \"./widget-fence-syntax\";\n\n/**\n * Repair the ```widget fence defects a model produces when it switches into a function call mid-text, or drops a\n * closing fence, or opens a second widget before closing the first (text + fence + tool call\n * in ONE response). A LINE-AWARE pass: CommonMark opens a fence on a line that (after up to 3 spaces of indent)\n * starts with ``` , and closes it only on a line whose sole content — same indent rule — is ``` . Content inside\n * ANY open fence (widget, json, or anything else) is data and is never scanned or touched, so a ``` mentioned\n * inside a JSON string value never gets mistaken for a delimiter.\n *\n * Two repairs, both scoped to the ```widget lang only:\n * 1. A glued opener (```widget stuck to the end of a prose line, so Markdown never recognized it as starting a\n * line) is relocated onto its own line ONLY when a JSON object begins right after it — otherwise the text is\n * left completely untouched (a prose sentence that merely mentions ```widget must not be mangled).\n * 2. An open widget fence with no proper closing-fence LINE before either end-of-text or the next fence-opener\n * line gets a ``` closer spliced in right after its balanced JSON object (string-aware, so a ``` inside a JSON\n * string is never treated as the closer). A widget fence whose body isn't a JSON object at all is left\n * untouched — nothing could safely render there either way, and deleting text is not this function's job.\n *\n * The backend stores the model text as emitted, so this is the ONE place the shape is corrected — for stored\n * history and for every new message alike. Runs before `referenceNakedWidgetJson` so the re-fenced JSON is not\n * wrapped a second time. Pure; well-formed text is returned unchanged.\n */\n\n/** A fence delimiter LINE per CommonMark, either character: up to 3 spaces indent, then a run of 3+ of the same\n * fence character (`` ` `` or `~`), then the rest of the line. Used by {@link findFencedBlockRanges} — the GENERAL\n * \"is this real fence syntax\" question, as opposed to the WIDGET-fence-only scanning above. */\nconst ANY_FENCE_DELIM_LINE = /^ {0,3}(`{3,}|~{3,})(.*)$/;\n\n/** One real fence delimiter: its character, marker length and (trimmed) info string. */\ninterface FenceDelimiter {\n char: \"`\" | \"~\";\n length: number;\n info: string;\n}\n\n/**\n * Parse one line as a CommonMark fence delimiter, or null when it isn't one. A backtick run's info string may not\n * itself contain a backtick — CommonMark's own rule disambiguating a fence opener from an inline code span — so a\n * line such as \"```js `x`\" is not a fence delimiter at all (it never opens or closes anything).\n */\nconst parseFenceDelimiterLine = (line: string): FenceDelimiter | null => {\n const match = ANY_FENCE_DELIM_LINE.exec(line);\n if (!match) return null;\n const marker = match[1];\n const char = marker[0] as \"`\" | \"~\";\n const info = match[2].trim();\n if (char === \"`\" && info.includes(\"`\")) return null;\n return { char, length: marker.length, info };\n};\n\nconst isWidgetFenceInfo = (info: string): boolean => new RegExp(`^${WIDGET_FENCE_LANG}\\\\b`).test(info.trim());\n\n/** A ```widget opener anywhere in the text, glued or not — built from the shared lang constant. */\nconst widgetOpenerPattern = (): RegExp => new RegExp(\"```[ \\\\t]*\" + WIDGET_FENCE_LANG + \"\\\\b\");\n\n/** One line of `text` starting at `lineStart`: its content (terminator excluded — a trailing `\\r` of a CRLF\n * line ending is stripped too, so a delimiter regex's `.` never has to account for it) and the index right after\n * the line INCLUDING its terminator (or `text.length` at end-of-text). */\nconst readLine = (text: string, lineStart: number): { content: string; afterLine: number } => {\n const newlineIndex = text.indexOf(\"\\n\", lineStart);\n const lineEnd = newlineIndex === -1 ? text.length : newlineIndex;\n const afterLine = newlineIndex === -1 ? text.length : newlineIndex + 1;\n const rawContent = text.slice(lineStart, lineEnd);\n return { content: rawContent.endsWith(\"\\r\") ? rawContent.slice(0, -1) : rawContent, afterLine };\n};\n\n/**\n * Scan forward from `bodyStart` for a line that CLOSES the given opener: same fence character, a marker run at\n * least as long as the opener's, and nothing but whitespace after it (CommonMark's closing-fence rule). Unclosed →\n * end-of-text.\n */\nconst findClosingFenceEnd = (text: string, bodyStart: number, opener: FenceDelimiter): number => {\n let cursor = bodyStart;\n while (cursor < text.length) {\n const { content, afterLine } = readLine(text, cursor);\n const delimiter = parseFenceDelimiterLine(content);\n if (delimiter && delimiter.char === opener.char && delimiter.length >= opener.length && delimiter.info === \"\") {\n return afterLine;\n }\n cursor = afterLine;\n }\n return text.length;\n};\n\n/**\n * The `[start, end)` ranges of every REAL fenced code block in `text` — ``` or ~~~, CommonMark's own rules (up to\n * 3 spaces of indent, a closer using the same character with a run at least as long as the opener's, a backtick\n * opener's info string never containing a backtick). An unclosed opener runs to end-of-text. The GENERAL-PURPOSE\n * counterpart of this file's widget-fence-only scanning, built on the same {@link readLine} line walk — used by\n * `referenceNakedWidgetJson` to skip content genuinely inside a fence: a `~~~widget` fence (no tilde support\n * existed before), and content that follows a line which LOOKS like a backtick fence opener but isn't one per the\n * info-string rule above (that line never opens anything, so it can no longer swallow the real fence after it).\n */\nexport const findFencedBlockRanges = (text: string): Array<[number, number]> => {\n const ranges: Array<[number, number]> = [];\n let cursor = 0;\n while (cursor < text.length) {\n const { content, afterLine } = readLine(text, cursor);\n const opener = parseFenceDelimiterLine(content);\n if (opener) {\n const end = findClosingFenceEnd(text, afterLine, opener);\n ranges.push([cursor, end]);\n cursor = end;\n continue;\n }\n cursor = afterLine;\n }\n return ranges;\n};\n\n/**\n * Scan forward from `bodyStart` (right after a fence opener line) for the next LINE-anchored BACKTICK fence\n * delimiter (via {@link parseFenceDelimiterLine} — the same CommonMark rules `findFencedBlockRanges` uses, so a\n * line like \"```js `x`\" — an invalid info string, not a fence delimiter at all — is correctly skipped as plain\n * content rather than mistaken for one): a proper closing line (empty info) closes the fence normally; any other\n * delimiter line (another opener, any lang) is the boundary where this fence gave up without closing. Neither\n * found before end-of-text → unclosed at EOF.\n */\nconst findFenceEnd = (text: string, bodyStart: number): { closesAt: number } | { unclosedBoundary: number } => {\n let cursor = bodyStart;\n while (cursor < text.length) {\n const { content, afterLine } = readLine(text, cursor);\n const delimiter = parseFenceDelimiterLine(content);\n if (delimiter && delimiter.char === \"`\") {\n return delimiter.info === \"\" ? { closesAt: afterLine } : { unclosedBoundary: cursor };\n }\n cursor = afterLine;\n }\n return { unclosedBoundary: text.length };\n};\n\n/**\n * Resolve an OPEN widget fence's body starting at `bodyStart`: verbatim through a real closing-fence line when one\n * exists before end-of-text or the next fence-opener line; otherwise a ``` closer spliced in right after the\n * balanced JSON object; or, when the body isn't a JSON object at all, everything to end-of-text left untouched.\n */\nconst closeWidgetFenceBody = (text: string, bodyStart: number): { appended: string; resumeAt: number } => {\n const end = findFenceEnd(text, bodyStart);\n if (\"closesAt\" in end) {\n return { appended: text.slice(bodyStart, end.closesAt), resumeAt: end.closesAt };\n }\n const jsonObjectEnd = findBalancedJsonObjectEnd(text, bodyStart);\n if (jsonObjectEnd === null) {\n return { appended: text.slice(bodyStart), resumeAt: text.length };\n }\n return { appended: text.slice(bodyStart, jsonObjectEnd + 1) + \"\\n```\", resumeAt: jsonObjectEnd + 1 };\n};\n\nexport const repairMalformedWidgetFences = (text: string): string => {\n if (!widgetOpenerPattern().test(text)) return text;\n\n let result = \"\";\n let cursor = 0;\n while (cursor < text.length) {\n const { content, afterLine } = readLine(text, cursor);\n\n // Backtick fences only here (tilde ```widget``` has no repair path, same as before): a line whose backtick\n // run's info string itself contains a backtick (e.g. \"```js `x`\") is not a fence delimiter at all per\n // CommonMark, so it falls through to the glued-opener check / plain-text copy below, never swallowing a real\n // ```widget fence that follows it as a bogus \"other fence\".\n const delimiter = parseFenceDelimiterLine(content);\n if (delimiter && delimiter.char === \"`\") {\n const info = delimiter.info;\n if (info !== \"\" && isWidgetFenceInfo(info)) {\n result += content + text.slice(cursor + content.length, afterLine); // the opener line, verbatim\n const { appended, resumeAt } = closeWidgetFenceBody(text, afterLine);\n result += appended;\n cursor = resumeAt;\n continue;\n }\n if (info !== \"\") {\n // Some other fenced language: copy its whole body through untouched — never scanned for a widget opener,\n // so a widget-fence mention inside a JSON string value in THIS fence's content is never touched (a widget\n // fence itself can't legally start inside an already-open fence either way).\n const otherFenceEnd = findFenceEnd(text, afterLine);\n const bodyEnd = \"closesAt\" in otherFenceEnd ? otherFenceEnd.closesAt : text.length;\n result += text.slice(cursor, bodyEnd);\n cursor = bodyEnd;\n continue;\n }\n }\n\n // Not a fence-delimiter line: a glued opener (the fence's backticks stuck to the end of prose). Relocate onto\n // its own line ONLY when a JSON object begins right after it; a prose line that merely mentions ```widget with\n // nothing to render is left completely untouched.\n const gluedMatch = widgetOpenerPattern().exec(content);\n if (gluedMatch && gluedMatch.index > 0 && content.slice(0, gluedMatch.index).trim() !== \"\") {\n const bodyStart = afterLine;\n if (findBalancedJsonObjectEnd(text, bodyStart) !== null) {\n const openerLine = content.slice(gluedMatch.index) + text.slice(cursor + content.length, afterLine);\n result += content.slice(0, gluedMatch.index) + \"\\n\" + openerLine;\n const { appended, resumeAt } = closeWidgetFenceBody(text, bodyStart);\n result += appended;\n cursor = resumeAt;\n continue;\n }\n }\n\n result += text.slice(cursor, afterLine);\n cursor = afterLine;\n }\n return result;\n};\n","import { findBalancedJsonObjectEnd } from \"./balanced-json-object\";\nimport { findFencedBlockRanges } from \"./repair-malformed-widget-fences\";\nimport { resolveWidgetName } from \"./widget-name-resolver\";\n\n/**\n * Weak models (Gemini-3.5-Flash) intermittently emit a widget as a NAKED JSON line —\n * `{ \"type\": \"ExampleWidget\" }` — without the ```widget fence the skills instruct. Such a line is not rendered\n * by remarkWidgetFencePlugin (no fence) and not by the bare-line path (no `widget` prefix), so the customer sees raw\n * JSON. `detectUnrenderedWidgets` only DETECTS this (to corrective-signal the model); here we RENDER it: wrap a naked\n * line-starting JSON object whose `type` resolves to a REGISTERED widget in a ```widget fence, so the plugin renders\n * it — no model round-trip, the customer never sees JSON. UNKNOWN types are left untouched (the corrective-signal\n * path still handles `type: \"example\"` → suggest `ExampleWidget`).\n *\n * Fence-aware: JSON already inside any REAL fenced block (``` or ~~~, CommonMark rules — see\n * `findFencedBlockRanges`) is skipped (never double-fenced). Uses the SAME resolveWidgetName as the render plugin +\n * the detector, so \"would render\" is decided in one place.\n */\n\n/** A line-starting balanced { … } JSON object and its [start, end) span in the source text. */\ninterface JsonSpan {\n json: string;\n start: number;\n end: number;\n}\n\n/**\n * Scan line-starting balanced `{ … }` JSON objects (string-aware brace walk), returning each with its span. Mirrors\n * detect-unrendered-widgets' `extractNakedJsonCandidates`, but keeps offsets so the object can be replaced in place.\n */\nconst scanLineStartingJsonSpans = (text: string): JsonSpan[] => {\n const spans: JsonSpan[] = [];\n const lines = text.split(\"\\n\");\n let offset = 0;\n for (const line of lines) {\n const lineStart = offset;\n offset += line.length + 1;\n if (!line.trimStart().startsWith(\"{\")) continue;\n const openIndex = lineStart + line.indexOf(\"{\");\n const jsonObjectEnd = findBalancedJsonObjectEnd(text, openIndex);\n if (jsonObjectEnd !== null) {\n spans.push({ json: text.slice(openIndex, jsonObjectEnd + 1), start: openIndex, end: jsonObjectEnd + 1 });\n }\n }\n return spans;\n};\n\n/** The string `type` of a JSON object, or null when it is not an object with a non-empty string `type`. */\nconst widgetTypeOf = (jsonCandidate: string): string | null => {\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonCandidate);\n } catch {\n return null;\n }\n if (typeof parsed !== \"object\" || parsed === null) return null;\n const type = (parsed as Record<string, unknown>).type;\n return typeof type === \"string\" && type !== \"\" ? type : null;\n};\n\nexport const referenceNakedWidgetJson = (text: string): string => {\n if (!text.includes('\"type\"')) return text;\n\n const fencedRanges = findFencedBlockRanges(text);\n const isInsideFence = (position: number) => fencedRanges.some(([start, end]) => position >= start && position < end);\n\n // Right-to-left so each replacement leaves the earlier spans' offsets valid.\n const spans = scanLineStartingJsonSpans(text)\n .filter((span) => !isInsideFence(span.start))\n .reverse();\n\n let result = text;\n for (const span of spans) {\n const type = widgetTypeOf(span.json);\n if (type === null || resolveWidgetName(type) === undefined) continue; // unknown → leave for the corrective signal\n const fencedBlock = \"```widget\\n\" + span.json + \"\\n```\";\n result = result.slice(0, span.start) + fencedBlock + result.slice(span.end);\n }\n return result;\n};\n","/**\n * Mirrors `AIConversationMessage::PRE_CALL_MESSAGE_MARKER` (PHP). The model prefixes a short \"I'm about to do X\"\n * status line with this sentinel; the BACKEND only detects/signals on it and stores it RAW (it never strips). Every\n * render layer — this is the customer chat one — strips it before showing the text to the user.\n */\nexport const PRE_CALL_MESSAGE_MARKER = \"{preCallMessage}\";\n\n/**\n * The MANGLED JSON form some models emit instead of the sentinel (Gemini 3.5 Flash): the status line\n * arrives as `{ \"preCallMessage\": \"…\" }<` — optionally with extra fields and/or a trailing `<|im_call:…` pseudo call\n * marker — fused into the assistant text. Normalized to the inner status text so the user never sees raw JSON.\n * Mirrors the PHP normalization in `AIConversationMessage::stripPreCallMessageMarker`.\n */\nconst MANGLED_PRE_CALL_RE = /\\{\\s*\"preCallMessage\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"\\s*,?\\s*[^}]*\\}\\s*(?:<\\S*)?/g;\n\n/**\n * Remove the {preCallMessage} sentinel from assistant text before display, and normalize the mangled JSON form to\n * its inner status line. Also holds back a leading PARTIAL of either shape while it is still streaming in (so the\n * user never sees a \"{preCa…\" or `{ \"preCallMessage\": \"Einen Mo` flash before the full form arrives).\n */\nexport const stripPreCallMessageMarker = (text: string): string => {\n if (!text) return text;\n const leading = text.replace(/^\\s+/, \"\");\n // The sentinel itself is still streaming (nothing after it yet) → render nothing until real content arrives.\n if (leading.length < PRE_CALL_MESSAGE_MARKER.length && PRE_CALL_MESSAGE_MARKER.startsWith(leading)) return \"\";\n // The mangled JSON form is still streaming (opened but not yet closed) → hold it back the same way.\n const mangledPrefix = '{ \"preCallMessage\"';\n const normalizedLeading = leading.replace(/\\s+/g, \" \");\n if (\n (normalizedLeading.length < mangledPrefix.length && mangledPrefix.startsWith(normalizedLeading)) ||\n (normalizedLeading.startsWith(mangledPrefix) && !normalizedLeading.includes(\"}\"))\n ) {\n return \"\";\n }\n const withMangledNormalized = text.replace(MANGLED_PRE_CALL_RE, (_match, escapedStatusLine: string) => {\n try {\n return JSON.parse(`\"${escapedStatusLine}\"`) as string;\n } catch {\n return escapedStatusLine;\n }\n });\n const withMarkerRemoved = withMangledNormalized.split(PRE_CALL_MESSAGE_MARKER).join(\"\");\n // The marker (or its mangled form) sits at the very start, after only whitespace → that whitespace was there\n // only to separate it from the rest of the text and is removed along with it. Anything else — a message that\n // never carried the marker there at all — keeps its OWN leading whitespace untouched, so a four-space-indented\n // code block at the start of a message stays indented (plain CommonMark code) instead of sliding to column 0\n // and becoming a widget fence.\n const markerWasLeading = leading.startsWith(PRE_CALL_MESSAGE_MARKER) || normalizedLeading.startsWith(mangledPrefix);\n return markerWasLeading ? withMarkerRemoved.replace(/^\\s+/, \"\") : withMarkerRemoved;\n};\n","import { referenceNakedWidgetJson } from \"../renderer/reference-naked-widget-json\";\nimport { repairMalformedWidgetFences } from \"../renderer/repair-malformed-widget-fences\";\n\nimport { stripPreCallMessageMarker } from \"./strip-pre-call-message-marker\";\n\ntype ServerTextPart = { type: \"text\"; text?: string };\n\ntype ServerComponentPart = {\n type: \"component\";\n name?: string;\n props?: Record<string, unknown>;\n};\n\ntype ServerToolUsePart = {\n type: \"tool_use\";\n name?: string;\n input?: unknown;\n};\n\ntype ServerMessagePart = ServerTextPart | ServerComponentPart | ServerToolUsePart;\n\nexport type AssistantServerMessagePayload = {\n role?: string;\n content?: ServerMessagePart[];\n parts?: ServerMessagePart[];\n messageId?: string;\n};\n\nconst WIDGET_ATTR_KEY = /^[a-zA-Z_:][a-zA-Z0-9_.:-]*$/;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === \"object\" && value !== null;\n\nconst isTextPart = (part: unknown): part is ServerTextPart => isRecord(part) && part.type === \"text\";\n\nconst isComponentPart = (part: unknown): part is ServerComponentPart =>\n isRecord(part) && part.type === \"component\" && typeof part.name === \"string\" && part.name.length > 0;\n\nconst isToolUsePart = (part: unknown): part is ServerToolUsePart => isRecord(part) && part.type === \"tool_use\";\n\nconst isMessagePart = (part: unknown): part is ServerMessagePart =>\n isTextPart(part) || isComponentPart(part) || isToolUsePart(part);\n\nconst isPayload = (value: unknown): value is AssistantServerMessagePayload => {\n if (!isRecord(value)) return false;\n const parts = value.content ?? value.parts;\n return Array.isArray(parts) && parts.every(isMessagePart);\n};\n\n/**\n * The model occasionally emits a single part object instead of an array\n * ({\"content\":{\"type\":\"text\",...}}). Coerce it so the message still renders.\n */\nconst coerceSinglePartPayload = (value: unknown): AssistantServerMessagePayload | null => {\n if (!isRecord(value)) return null;\n const part = value.content ?? value.parts;\n if (!isMessagePart(part)) return null;\n const rest = { ...value };\n delete rest.parts;\n return { ...rest, content: [part] };\n};\n\nconst encodeAttrValue = (value: unknown) => JSON.stringify(value).replace(/'/g, \"\\\\u0027\");\n\nconst propsToAttrs = (props: Record<string, unknown>) =>\n Object.entries(props)\n .filter(([key, value]) => value !== undefined && WIDGET_ATTR_KEY.test(key))\n .map(([key, value]) => `${key}='${encodeAttrValue(value)}'`)\n .join(\" \");\n\nconst componentPartToWidget = (part: ServerComponentPart) => {\n const attrs = propsToAttrs(part.props ?? {});\n return attrs ? `::${part.name}{${attrs}}` : `::${part.name}`;\n};\n\nconst toolUsePartToWidget = (part: ServerToolUsePart) => {\n const payload = encodeAttrValue({ name: part.name, input: part.input });\n return `::ToolTrace{payload='${payload}'}`;\n};\n\nconst partToMarkdown = (part: ServerMessagePart) => {\n if (isTextPart(part)) return part.text?.trim() ?? \"\";\n if (isToolUsePart(part)) return toolUsePartToWidget(part);\n return componentPartToWidget(part);\n};\n\nconst payloadToMarkdown = (payload: AssistantServerMessagePayload) => {\n return (payload.content ?? payload.parts ?? []).map(partToMarkdown).filter(Boolean).join(\"\\n\\n\");\n};\n\nconst tryParsePayload = (value: string): AssistantServerMessagePayload | null => {\n try {\n const parsed = JSON.parse(value) as unknown;\n return isPayload(parsed) ? parsed : coerceSinglePartPayload(parsed);\n } catch {\n return null;\n }\n};\n\nexport const normalizeServerMessageToMarkdown = (message: unknown): string => {\n let result: string;\n if (typeof message === \"string\") {\n const parsed = tryParsePayload(message);\n result = parsed ? payloadToMarkdown(parsed) : message;\n } else if (isPayload(message)) {\n result = payloadToMarkdown(message);\n } else {\n const coerced = coerceSinglePartPayload(message);\n if (coerced) {\n result = payloadToMarkdown(coerced);\n } else {\n console.warn(\"[AIExtensions] normalizeServerMessageToMarkdown: unrecognized message shape\", message);\n result = \"\";\n }\n }\n // The backend stores the {preCallMessage} status-line sentinel raw (it only detects/signals on it) — strip it here.\n // Then repair a ```widget fence the model broke mid-text (opener glued to the prose line / never closed — the model\n // switched into a tool call), then re-fence any naked widget JSON (a weak model emitted\n // `{ \"type\": \"ExampleWidget\" }` without a ```widget fence): wrapping a known widget type lets\n // remarkWidgetFencePlugin render it, so the customer never sees raw JSON.\n return referenceNakedWidgetJson(repairMalformedWidgetFences(stripPreCallMessageMarker(result)));\n};\n","import { isCanvasId } from \"./canvas-behaviours\";\nimport type { CanvasAnchor, CanvasState } from \"./canvas-state\";\n\n/**\n * Where the open canvas of a conversation is remembered across a reload: `sessionStorage`, per conversation, holding\n * the POINTER only — which canvas, which anchor, which instance, and the entity a body created. Never\n * content: the anchor message's fence supplies the body's data on restore. Tab-local by design (the canvas is viewing\n * state of this tab); a new tab starts with no canvas.\n *\n * Written on every change of the open canvas for ANY lifetime, cleared when the canvas closes; on read the pointer is\n * validated shape by shape — a stale id or a mangled entry reads as \"nothing stored\". Every accessor is try/catch-\n * guarded: a private window, blocked site data or a preview context can throw on the accessor itself.\n */\nexport interface StoredCanvasPointer {\n readonly canvasId: CanvasState[\"canvasId\"];\n readonly anchor: CanvasAnchor;\n readonly instanceId: string;\n readonly createdEntityId?: number;\n}\n\nconst CANVAS_STORAGE_KEY_PREFIX = \"agent.canvas.\";\n\nconst canvasStorageKey = (conversationId: number): string => `${CANVAS_STORAGE_KEY_PREFIX}${conversationId}`;\n\nconst isPositiveInteger = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isInteger(value) && value > 0;\n\nconst parseAnchor = (value: unknown): CanvasAnchor | null => {\n if (typeof value !== \"object\" || value === null) return null;\n const candidate = value as { kind?: unknown; messageId?: unknown; agentSpaceKey?: unknown };\n if (candidate.kind === \"message\" && isPositiveInteger(candidate.messageId)) {\n return { kind: \"message\", messageId: candidate.messageId };\n }\n if (candidate.kind === \"page\" && isPositiveInteger(candidate.agentSpaceKey)) {\n return { kind: \"page\", agentSpaceKey: candidate.agentSpaceKey };\n }\n return null;\n};\n\n/** Validate an untrusted value into a pointer, or null. Exported for the spec and for the shell's restore path. */\nexport const parseStoredCanvasPointer = (value: unknown): StoredCanvasPointer | null => {\n if (typeof value !== \"object\" || value === null) return null;\n const candidate = value as { canvasId?: unknown; anchor?: unknown; instanceId?: unknown; createdEntityId?: unknown };\n const anchor = parseAnchor(candidate.anchor);\n if (!isCanvasId(candidate.canvasId) || anchor === null) return null;\n if (typeof candidate.instanceId !== \"string\" || candidate.instanceId.length === 0) return null;\n return {\n canvasId: candidate.canvasId,\n anchor,\n instanceId: candidate.instanceId,\n ...(isPositiveInteger(candidate.createdEntityId) ? { createdEntityId: candidate.createdEntityId } : {}),\n };\n};\n\n/** Remember the canvas open in this conversation. No-op without a conversation id (a cold chat has none yet). */\nexport const storeOpenCanvas = (conversationId: number | undefined, canvas: CanvasState): void => {\n if (conversationId === undefined) return;\n const pointer: StoredCanvasPointer = {\n canvasId: canvas.canvasId,\n anchor: canvas.anchor,\n instanceId: canvas.instanceId,\n ...(canvas.createdEntityId !== undefined ? { createdEntityId: canvas.createdEntityId } : {}),\n };\n try {\n window.sessionStorage.setItem(canvasStorageKey(conversationId), JSON.stringify(pointer));\n } catch {\n // Storage unavailable — the canvas then simply does not survive the reload.\n }\n};\n\n/** The canvas that was open in this conversation, validated, or null (nothing stored, stale, or storage unusable). */\nexport const readStoredCanvas = (conversationId: number | undefined): StoredCanvasPointer | null => {\n if (conversationId === undefined) return null;\n try {\n const raw = window.sessionStorage.getItem(canvasStorageKey(conversationId));\n if (raw === null) return null;\n return parseStoredCanvasPointer(JSON.parse(raw));\n } catch {\n return null;\n }\n};\n\n/** Forget the canvas of this conversation — it closed, or its anchor is no longer among the loaded messages. */\nexport const clearStoredCanvas = (conversationId: number | undefined): void => {\n if (conversationId === undefined) return;\n try {\n window.sessionStorage.removeItem(canvasStorageKey(conversationId));\n } catch {\n // Storage unavailable — nothing was stored either.\n }\n};\n","import { useEffect, useRef } from \"react\";\nimport type { ChatMessage } from \"../../chat/chat-message\";\nimport { normalizeServerMessageToMarkdown } from \"../../chat/normalize-server-message-to-markdown\";\nimport { useConversationWidgetContext } from \"../ConversationWidgetCallbacksContext\";\nimport { conversationWidgetRegistry } from \"../conversationWidgetRegistry\";\nimport { canvasBehaviourOf } from \"./canvas-behaviours\";\nimport { payloadHashOfInstanceId } from \"./canvas-state\";\nimport { StoredCanvasPointer, clearStoredCanvas, readStoredCanvas } from \"./canvasStorage\";\nimport { findWidgetFencesInMarkdown } from \"./find-widget-fences-in-markdown\";\n\nexport interface CanvasRestoreOnMountProps {\n /** The conversation the host shows. Undefined for a cold chat — nothing to restore. */\n conversationId?: number;\n /** The host's loaded messages. A message-anchored restore waits for THIS conversation's page to be in. */\n messages: ChatMessage[] | null;\n}\n\n/** Does the anchor message still carry a widget that opens this canvas — the same instance, when the id names one? */\nconst anchorMessageCarriesCanvasWidget = (\n pointer: StoredCanvasPointer,\n anchorMessage: ChatMessage,\n): boolean => {\n const openers = new Set(\n conversationWidgetRegistry.widgets\n .filter((entry) => entry.opensCanvases?.includes(pointer.canvasId))\n .map((entry) => entry.name),\n );\n if (openers.size === 0) return false;\n const payloadHash = payloadHashOfInstanceId(pointer.instanceId);\n return findWidgetFencesInMarkdown(normalizeServerMessageToMarkdown(anchorMessage.message)).some(\n (fence) => openers.has(fence.name) && (payloadHash === undefined || fence.payloadHash === payloadHash),\n );\n};\n\n/**\n * Restores the canvas that was open in this conversation — on a reload, in a new tab, and when the customer comes\n * back to the conversation (A → B → A alike). Must render INSIDE the provider (it dispatches\n * through the context) and runs once per conversation, after the mount reset. A PAGE-anchored canvas (an app's\n * editor opened from a page) needs no message and restores as soon as the conversation id is known. A MESSAGE-anchored\n * canvas waits for the conversation's own messages and has three limits:\n * - the anchor message must be in the loaded page — otherwise the canvas is that far back and irrelevant: the pointer\n * is dropped, nothing is paged in;\n * - a `turn` canvas whose anchor message is no longer the newest assistant message is not restored — its turn is over;\n * - the anchor message must still carry a widget that opens this canvas (a stale or mangled pointer opens nothing).\n */\nexport const CanvasRestoreOnMount = ({ conversationId, messages }: CanvasRestoreOnMountProps) => {\n const { openCanvas } = useConversationWidgetContext();\n const restoredForConversationIdRef = useRef<number | undefined>(undefined);\n\n useEffect(() => {\n if (conversationId === undefined || restoredForConversationIdRef.current === conversationId) return;\n const pointer = readStoredCanvas(conversationId);\n if (pointer === null) {\n restoredForConversationIdRef.current = conversationId;\n return;\n }\n\n const restore = () =>\n openCanvas({\n canvasId: pointer.canvasId,\n instanceId: pointer.instanceId,\n anchor: pointer.anchor,\n origin: \"restoredAfterReload\",\n justDelivered: false,\n ...(pointer.createdEntityId !== undefined ? { createdEntityId: pointer.createdEntityId } : {}),\n });\n\n if (pointer.anchor.kind === \"page\") {\n restoredForConversationIdRef.current = conversationId;\n restore();\n return;\n }\n\n // Right after a switch the slice still holds the PREVIOUS conversation's messages for a beat — wait until the\n // list belongs to this conversation, or the anchor checks would read the wrong transcript.\n if (messages === null || !messages.some((message) => message.conversationId === conversationId)) return;\n restoredForConversationIdRef.current = conversationId;\n\n const anchorMessageId = pointer.anchor.messageId;\n const anchorMessage = messages.find((message) => message.id === anchorMessageId);\n const newestAssistantMessage = [...messages].reverse().find((message) => message.role === \"ASSISTANT\");\n const turnIsOver =\n canvasBehaviourOf(pointer.canvasId)?.lifetime === \"turn\" && newestAssistantMessage?.id !== anchorMessageId;\n if (anchorMessage === undefined || turnIsOver || !anchorMessageCarriesCanvasWidget(pointer, anchorMessage)) {\n clearStoredCanvas(conversationId);\n return;\n }\n restore();\n }, [conversationId, messages, openCanvas]);\n\n return null;\n};\n","import type { CanvasId } from \"./canvas/canvas-behaviours\";\nimport { ConversationWidgetEntry, conversationWidgetRegistry } from \"./conversationWidgetRegistry\";\n\n/**\n * Resolves the widget a `?agentWidget=<name>` deep-link (or a restored pinned canvas) may mount, by its CANONICAL\n * registry wire name.\n *\n * A URL is untrusted input, so this is an allow-list rather than a registry lookup: only an entry that declares\n * `deepLinkable: true` can be opened this way. Every other widget is emitted by the agent inside a message, where the\n * conversation itself is the authorisation — a link must not be able to mount an arbitrary widget (some carry\n * mount-time side effects) just because its name was typed into the address bar.\n *\n * Returns the entry (the caller renders its `component`, applying the feature gate the entry declares) or null when the\n * name is empty, unknown, or not deep-linkable.\n */\nexport const resolveDeepLinkableWidget = (widgetName?: string | null): ConversationWidgetEntry | null => {\n if (!widgetName) return null;\n return (\n conversationWidgetRegistry.widgets.find((entry) => entry.name === widgetName && entry.deepLinkable === true) ?? null\n );\n};\n\n/**\n * The reverse direction: which deep-linkable widget re-opens THIS canvas. The open canvas of the agent chat is\n * mirrored into `?agentWidget=` (AgentTrigger) so a reload brings the customer back to the surface they were working\n * on — an editor a cold chat opened has no conversation id yet, so the canvas model's own pointer storage\n * has nothing to restore from.\n *\n * Null for every canvas no deep-linkable widget declares: it is the agent's to open inside a message, and a URL must\n * not be able to mount it (the allow-list above).\n */\nexport const deepLinkableWidgetNameForCanvas = (canvasId: CanvasId | null | undefined): string | null => {\n if (!canvasId) return null;\n return (\n conversationWidgetRegistry.widgets.find(\n (entry) => entry.deepLinkable === true && entry.opensCanvases?.includes(canvasId),\n )?.name ?? null\n );\n};\n","import React, { useMemo } from \"react\";\nimport { resolveDeepLinkableWidget } from \"./resolve-deep-linkable-widget\";\nimport { ConversationWidgetFeatureGate } from \"./_access/ConversationWidgetFeatureGate\";\nimport { ConversationWidgetErrorBoundary } from \"./ConversationWidgetErrorBoundary\";\nimport { buildWidgetAgentSpaceKeyProps } from \"./_agent-space/widget-agent-space-key-attribute\";\n\nexport interface ConversationDeepLinkedWidgetProps {\n /** Canonical registry wire name, from `?agentWidget=` or a restored pinned canvas. Unknown / not deep-linkable → nothing renders. */\n widgetName?: string;\n /** The key of the chat's agent space, forwarded to the widget exactly as a markdown attribute would carry it. */\n agentSpaceKey?: number;\n}\n\n/**\n * Mounts ONE registered widget outside the message list — the customer opened it from a page (`?agentWidget=`). The\n * widget receives `openedByPage` (plus `interactive` / `justDelivered`), so a canvas-opening widget (an app's editor\n * widget, `ExampleWidget`) opens its canvas with origin `openedByPage` and a page anchor; nothing is written to the\n * conversation, no message and no signal. (A canvas that was open before a reload comes back through\n * `CanvasRestoreOnMount`, not through this mount.)\n *\n * Rendered with the entry's feature gate and the shared error boundary, but WITHOUT the viewport windowing of\n * {@link ConversationWidgetHost}: there is no message to scroll past here, and a windowed wrapper would unmount a\n * zero-height widget (these widgets render `null` and live entirely in the canvas), taking its canvas with it.\n */\nexport const ConversationDeepLinkedWidget = ({ widgetName, agentSpaceKey }: ConversationDeepLinkedWidgetProps) => {\n const entry = useMemo(() => resolveDeepLinkableWidget(widgetName), [widgetName]);\n if (!entry) return null;\n\n const { component: Component, featureFlag, upsellTrigger } = entry;\n const widget = <Component fresh interactive justDelivered openedByPage {...buildWidgetAgentSpaceKeyProps(agentSpaceKey)} />;\n\n return (\n <ConversationWidgetErrorBoundary widgetName={entry.name}>\n {featureFlag ? (\n <ConversationWidgetFeatureGate\n featureFlag={featureFlag}\n UpsellTrigger={upsellTrigger}\n agentSpaceKeyAttribute={agentSpaceKey}\n >\n {widget}\n </ConversationWidgetFeatureGate>\n ) : (\n widget\n )}\n </ConversationWidgetErrorBoundary>\n );\n};\n","import type {\n AgentsClientError,\n AgentsConversation,\n AgentsConversationMessage,\n PostedMessage,\n StartedConversation,\n} from \"../../contract/conversation\";\nimport type { ChatMessage } from \"../chat-message\";\nimport type { CanvasCreatedEntity, CanvasSliceState } from \"../../renderer/canvas/canvas-reducers\";\nimport type { CanvasOpenRequest } from \"../../renderer/canvas/canvas-state\";\nimport type { ConversationErrorType } from \"./conversation-error-type\";\n\n/**\n * How an entry point outside the chat hands a job over to the chat that opens next (one generic opening\n * request): the first visible message, or an invisible start signal so the agent speaks first, the entry point the\n * start comes from, and a widget to mount without a message (a page handing over a tool). Set before the chat mounts,\n * so a reset keeps it; the chat clears each part once it consumed it.\n */\nexport interface AgentsChatOpeningRequest {\n /** The first visible owner message of the next started conversation. */\n userMessage: string | null;\n /** The invisible start signal of the next started conversation (an empty visible message carries it). */\n startSignalText: string | null;\n /** The entry point the start comes from; the server reopens that entry point's recent conversation instead. */\n entryPointKey: string | null;\n /** The canonical wire name of a deep-linkable widget the chat mounts once, without a message. */\n deepLinkedWidgetName: string | null;\n}\n\n/** The state of one chat instance. */\nexport interface AgentsChatState extends CanvasSliceState {\n /** The conversation shown; null for a cold chat until a start or the metadata read brings it. */\n conversation: AgentsConversation | null;\n /** Null until the first page or a send; ordered by `indexInConversation`, the optimistic message last. */\n messages: ChatMessage[] | null;\n /** A send is on its way. */\n conversationLoading: boolean;\n /** The last read or send for this conversation came back. */\n conversationLoaded: boolean;\n /** An older page exists: the scroll-up load stays armed. */\n hasMoreOlder: boolean;\n /** The agent works on a turn (the controller mirrors the derived activity here; read by the launcher). */\n aiThinking: boolean;\n error: ConversationErrorType | null;\n /** Seconds until the quota frees up again; set only while `error` is RATE_LIMIT. */\n rateLimitRetryAfterSeconds: number | null;\n /**\n * The conversation the newest metadata or page read asked for; null after a reset. A late answer for another\n * conversation — or one that lands after a reset — is dropped.\n */\n requestedConversationId: number | null;\n /** A start is on its way; no second start is sent meanwhile. */\n startingNewConversation: boolean;\n /** The token of the start on its way; its answer applies only while it is still the pending one. */\n pendingStartToken: number | null;\n /**\n * How many starts this chat instance sent — the source of `pendingStartToken`. Survives a reset (like\n * `canvasMountCount`): the store outlives a remount of the chat, so a token is never reused for another start.\n */\n startTokenCount: number;\n openingRequest: AgentsChatOpeningRequest;\n}\n\nexport const EMPTY_AGENTS_CHAT_OPENING_REQUEST: AgentsChatOpeningRequest = {\n userMessage: null,\n startSignalText: null,\n entryPointKey: null,\n deepLinkedWidgetName: null,\n};\n\nexport const INITIAL_AGENTS_CHAT_STATE: AgentsChatState = {\n conversation: null,\n messages: null,\n conversationLoading: false,\n conversationLoaded: false,\n hasMoreOlder: false,\n aiThinking: false,\n error: null,\n rateLimitRetryAfterSeconds: null,\n requestedConversationId: null,\n startingNewConversation: false,\n pendingStartToken: null,\n startTokenCount: 0,\n openingRequest: EMPTY_AGENTS_CHAT_OPENING_REQUEST,\n canvas: null,\n lastClosedCanvas: null,\n canvasMountCount: 0,\n};\n\n/**\n * What happens to a chat instance. The read and send events carry the port's answers (`AgentsConversationClient`);\n * the controller dispatches the `…Requested` event before it calls the port and the `…Received` / `…Failed` event\n * with the answer.\n */\nexport type AgentsChatEvent =\n /** A conversation switch, an empty chat, the chat's unmount: everything goes except the opening request and the two counters. */\n | { type: \"reset\" }\n | { type: \"conversationMetadataRequested\"; conversationId: number }\n | { type: \"conversationMetadataReceived\"; conversationId: number; conversation: AgentsConversation }\n | { type: \"conversationMetadataFailed\"; conversationId: number }\n | { type: \"messagesPageRequested\"; conversationId: number }\n /** `hasMoreOlder` null: a changed-messages read, which says nothing about older pages. */\n | {\n type: \"messagesPageReceived\";\n conversationId: number;\n messages: AgentsConversationMessage[];\n hasMoreOlder: boolean | null;\n }\n | { type: \"messagePostRequested\"; message: ChatMessage }\n | { type: \"messagePostReceived\"; conversationId: number; posted: PostedMessage }\n | { type: \"messagePostFailed\"; error: AgentsClientError }\n /** Takes the next start token (`pendingStartToken`); the controller reads it after the dispatch. */\n | { type: \"startRequested\"; message: ChatMessage }\n | { type: \"startReceived\"; startToken: number; started: StartedConversation }\n | { type: \"startFailed\"; error: AgentsClientError }\n | { type: \"errorSet\"; error: ConversationErrorType | null }\n | { type: \"aiThinkingSet\"; aiThinking: boolean }\n /** Sets the named parts of the opening request; the others stay. */\n | { type: \"openingRequestSet\"; openingRequest: Partial<AgentsChatOpeningRequest> }\n | { type: \"canvasOpened\"; request: CanvasOpenRequest }\n | { type: \"canvasClosed\"; byCustomer: boolean }\n | { type: \"canvasResetForNewTurn\" }\n | { type: \"canvasCleared\" }\n | { type: \"canvasCreatedEntityIdSet\"; created: CanvasCreatedEntity };\n","import { canvasBehaviourOf } from \"./canvas-behaviours\";\nimport { CanvasOpenRequest, CanvasState, LastClosedCanvas, sameCanvasIdentity } from \"./canvas-state\";\nimport { CanvasOpenDecision, decideCanvasOpen } from \"./decide-canvas-open\";\n\n/**\n * The canvas part of a chat slice partition — the two fields every transition reads and writes. Both chat doors carry\n * them (`ConversationState`); the shell is shared, so the state must have the same shape in both partitions.\n */\nexport interface CanvasSliceState {\n canvas: CanvasState | null;\n lastClosedCanvas: LastClosedCanvas | null;\n /**\n * How many canvases this partition has mounted so far — the source of `mountGeneration`. Never reset by a close,\n * a send or a clear (and exempt from `resetState`), so a late asynchronous write from a body that was replaced,\n * cleared or belonged to the previous conversation can never match a new canvas's generation.\n */\n canvasMountCount: number;\n}\n\n/**\n * The lifetime state machine as REDUCERS. Hand-written, not generated setters:\n * a generated setter is a blind overwrite of a snapshot the caller read a render earlier, and two opens in one commit\n * would both see the same stale state. Each function mutates the Immer draft it receives and returns the decision it\n * applied, so the slice can expose them as case reducers and a spec can assert every transition on a plain object.\n */\n\nconst toLastClosed = (canvas: CanvasState, closedByCustomer: boolean): LastClosedCanvas => ({\n canvasId: canvas.canvasId,\n instanceId: canvas.instanceId,\n anchor: canvas.anchor,\n mountGeneration: canvas.mountGeneration,\n closedByCustomer,\n ...(canvas.createdEntityId !== undefined ? { createdEntityId: canvas.createdEntityId } : {}),\n});\n\n/** Next render key: strictly increasing for the partition's lifetime, so no two mounts ever share a generation. */\nconst nextMountGeneration = (state: CanvasSliceState): number => {\n state.canvasMountCount += 1;\n return state.canvasMountCount;\n};\n\n/**\n * Open a canvas — the ONE way to open, for all four origins. On Accept: the replaced canvas (a different identity)\n * becomes `lastClosedCanvas`; a `lastClosedCanvas` of the accepted identity is consumed (its veto is cleared — the\n * customer, the page or the restore decided to have it back); the created entity survives a re-open of the same\n * identity so a restored editor keeps its draft.\n */\nexport const applyOpenCanvas = (state: CanvasSliceState, request: CanvasOpenRequest): CanvasOpenDecision => {\n const decision = decideCanvasOpen(canvasBehaviourOf(request.canvasId), state.canvas, state.lastClosedCanvas, request);\n if (decision.kind === \"ignore\") return decision;\n\n const previous = state.canvas;\n const mountGeneration = nextMountGeneration(state);\n const carriedCreatedEntityId =\n request.createdEntityId ??\n (previous !== null && sameCanvasIdentity(previous, request) ? previous.createdEntityId : undefined) ??\n (state.lastClosedCanvas !== null && sameCanvasIdentity(state.lastClosedCanvas, request)\n ? state.lastClosedCanvas.createdEntityId\n : undefined);\n\n if (previous !== null && !sameCanvasIdentity(previous, request)) {\n state.lastClosedCanvas = toLastClosed(previous, false);\n } else if (state.lastClosedCanvas !== null && sameCanvasIdentity(state.lastClosedCanvas, request)) {\n state.lastClosedCanvas = null;\n }\n state.canvas = {\n canvasId: request.canvasId,\n instanceId: request.instanceId,\n anchor: request.anchor,\n origin: request.origin,\n mountGeneration,\n ...(carriedCreatedEntityId !== undefined ? { createdEntityId: carriedCreatedEntityId } : {}),\n };\n return decision;\n};\n\n/**\n * Close the open canvas but keep it re-openable: it becomes `lastClosedCanvas`. `byCustomer` marks the customer's own\n * close (the canvas close control) — a live delivery of the same identity is then ignored until a send, a card click\n * or a page hand-over. A shell-driven close (compact mode, the side panel opening, a narrow crossing) passes false.\n */\nexport const applyCloseCanvas = (state: CanvasSliceState, byCustomer: boolean): void => {\n if (state.canvas === null) return;\n state.lastClosedCanvas = toLastClosed(state.canvas, byCustomer);\n state.canvas = null;\n};\n\n/**\n * The SEND boundary: a new user message starts a new turn. A `turn` canvas is over for good — it leaves the open slot\n * AND `lastClosedCanvas` (the card in the transcript is the way back). A `pinned` canvas stays open,\n * and a closed pinned one stays re-openable with its customer veto cleared (the send is a clearing edge).\n */\nexport const applyResetCanvasForNewTurn = (state: CanvasSliceState): void => {\n if (state.canvas !== null && canvasBehaviourOf(state.canvas.canvasId)?.lifetime === \"turn\") {\n state.canvas = null;\n }\n if (state.lastClosedCanvas !== null) {\n if (canvasBehaviourOf(state.lastClosedCanvas.canvasId)?.lifetime === \"turn\") {\n state.lastClosedCanvas = null;\n } else if (state.lastClosedCanvas.closedByCustomer) {\n state.lastClosedCanvas = { ...state.lastClosedCanvas, closedByCustomer: false };\n }\n }\n};\n\n/** Forget the canvas entirely — a conversation switch, `closeAgent`, the modal unmount. Nothing is re-openable. */\nexport const applyClearCanvas = (state: CanvasSliceState): void => {\n state.canvas = null;\n state.lastClosedCanvas = null;\n};\n\nexport interface CanvasCreatedEntity {\n readonly canvasId: CanvasState[\"canvasId\"];\n readonly instanceId: string;\n /** The generation the body was mounted with — a late write from a body that was replaced meanwhile is dropped. */\n readonly mountGeneration: number;\n readonly createdEntityId: number;\n}\n\n/** A body created its entity (the post editor's draft): remember the id on the canvas it was created for. */\nexport const applySetCanvasCreatedEntityId = (state: CanvasSliceState, created: CanvasCreatedEntity): boolean => {\n const canvas = state.canvas;\n if (canvas === null || !sameCanvasIdentity(canvas, created) || canvas.mountGeneration !== created.mountGeneration) {\n return false;\n }\n state.canvas = { ...canvas, createdEntityId: created.createdEntityId };\n return true;\n};\n","import type { AgentsClientError } from \"../../contract/conversation\";\n\n/**\n * The error card the chat shows. A plain object with string values (not a TypeScript `enum`), so the chat store, the\n * view and an app's older chat state share one vocabulary without a nominal type in between.\n */\nexport const ConversationErrorType = {\n AI_BUDGET: \"AI_BUDGET\",\n /**\n * HTTP 429 from an AI endpoint — a per-minute/hour/day usage quota was hit. Distinct from AI_BUDGET (monthly budget):\n * this one clears on its own after the Retry-After window.\n */\n RATE_LIMIT: \"RATE_LIMIT\",\n NETWORK_ERROR: \"NETWORK_ERROR\",\n CONVERSATION_NOT_FOUND: \"CONVERSATION_NOT_FOUND\",\n /**\n * The backend run FAILED (e.g. the model provider returned an error). The raw provider error is admin-only (never\n * shown here); the owner gets a generic \"something went wrong\" card.\n */\n AGENT_ERROR: \"AGENT_ERROR\",\n /**\n * The conversation is COMPLETED/CANCELLED and refuses further owner input. Set from the server's stable error code\n * `CONVERSATION_CLOSED` or by the chat's own pre-send guard; the error card owns the copy.\n */\n CONVERSATION_CLOSED: \"CONVERSATION_CLOSED\",\n} as const;\n\nexport type ConversationErrorType = (typeof ConversationErrorType)[keyof typeof ConversationErrorType];\n\n/**\n * The error card of a refused send (post or start), read from the port's error. Only the stable outcomes select a\n * dedicated card; every other refusal is a generic network error — the same classification the older chat applies to\n * the server's error codes (`AI_BUDGET_REACHED`, `RATE_LIMIT_EXCEEDED`, `CONVERSATION_CLOSED`).\n */\nexport const readConversationErrorTypeOfRefusedSend = (error: AgentsClientError): ConversationErrorType => {\n if (error.kind === \"budget\") return ConversationErrorType.AI_BUDGET;\n if (error.kind === \"rateLimit\") return ConversationErrorType.RATE_LIMIT;\n if (error.kind === \"closed\") return ConversationErrorType.CONVERSATION_CLOSED;\n return ConversationErrorType.NETWORK_ERROR;\n};\n","import type { ChatMessage } from \"../chat-message\";\n\n/**\n * Merges two message lists by `indexInConversation`, preferring `incoming` on a collision, sorted ascending by index.\n * Messages without an index (the optimistic owner message) cannot be matched, so they follow the indexed ones in order.\n * A start or post answer carries the whole list and a live read brings it again: concatenating would show every\n * message twice.\n */\nexport const mergeChatMessagesByIndex = (existing: ChatMessage[], incoming: ChatMessage[]): ChatMessage[] => {\n const messagesByIndex = new Map<number, ChatMessage>();\n const messagesWithoutIndex: ChatMessage[] = [];\n const collectMessage = (message: ChatMessage) => {\n if (message.indexInConversation != null) {\n messagesByIndex.set(message.indexInConversation, message);\n } else {\n messagesWithoutIndex.push(message);\n }\n };\n existing.forEach(collectMessage);\n incoming.forEach(collectMessage);\n const indexedMessages = Array.from(messagesByIndex.values()).sort(\n (first, second) => (first.indexInConversation ?? 0) - (second.indexInConversation ?? 0),\n );\n return [...indexedMessages, ...messagesWithoutIndex];\n};\n\n/**\n * Tags the messages of a send's answer: `fresh` when the id was not in the list before — the server just delivered it,\n * so its one-shot widgets may fire; a replayed message never fires them again.\n */\nexport const tagFreshChatMessages = (incoming: ChatMessage[], previousMessages: ChatMessage[]): ChatMessage[] => {\n const previousIds = new Set(previousMessages.map((message) => message.id).filter((id): id is number => id != null));\n return incoming.map((message) => ({ ...message, fresh: message.id != null && !previousIds.has(message.id) }));\n};\n\n/**\n * Merges a page of a keyset read (the newest page, an older page, the changed tail) into the loaded messages by index.\n * A message is `fresh` only when it is a live arrival: never shown before and newer than everything loaded — so the\n * streamed reply opens its canvas while a scroll-up page and the first page on open stay history. A message already\n * shown keeps its tag. A page of another conversation than the one shown replaces the list.\n */\nexport const mergeMessagesPage = (\n loadedMessages: ChatMessage[] | null,\n page: ChatMessage[],\n requestedConversationId: number,\n): ChatMessage[] => {\n const shownConversationId = loadedMessages?.find((message) => message.conversationId != null)?.conversationId;\n const replacesShownConversation = shownConversationId != null && shownConversationId !== requestedConversationId;\n const existingMessages = replacesShownConversation ? [] : (loadedMessages ?? []);\n const messagesByIndex = new Map<number, ChatMessage>();\n for (const existingMessage of existingMessages) {\n if (existingMessage.indexInConversation != null) {\n messagesByIndex.set(existingMessage.indexInConversation, existingMessage);\n }\n }\n const hadMessagesBeforeMerge = existingMessages.length > 0;\n const highestShownIndex = Math.max(-1, ...existingMessages.map((message) => message.indexInConversation ?? -1));\n for (const incomingMessage of page) {\n if (incomingMessage.indexInConversation == null) continue;\n const previousMessage = messagesByIndex.get(incomingMessage.indexInConversation);\n const isLiveArrival =\n hadMessagesBeforeMerge && previousMessage == null && incomingMessage.indexInConversation > highestShownIndex;\n messagesByIndex.set(incomingMessage.indexInConversation, {\n ...incomingMessage,\n fresh: previousMessage?.fresh ?? isLiveArrival,\n });\n }\n return Array.from(messagesByIndex.values()).sort(\n (first, second) => (first.indexInConversation ?? 0) - (second.indexInConversation ?? 0),\n );\n};\n","import {\n applyClearCanvas,\n applyCloseCanvas,\n applyOpenCanvas,\n applyResetCanvasForNewTurn,\n applySetCanvasCreatedEntityId,\n} from \"../../renderer/canvas/canvas-reducers\";\nimport type { AgentsClientError } from \"../../contract/conversation\";\nimport type { ChatMessage } from \"../chat-message\";\nimport { type AgentsChatEvent, type AgentsChatState, INITIAL_AGENTS_CHAT_STATE } from \"./agents-chat-state\";\nimport { ConversationErrorType, readConversationErrorTypeOfRefusedSend } from \"./conversation-error-type\";\nimport { mergeChatMessagesByIndex, mergeMessagesPage, tagFreshChatMessages } from \"./merge-chat-messages\";\n\n/** The error card and the quota wait of a refused send. */\nconst withRefusedSendError = (state: AgentsChatState, error: AgentsClientError): AgentsChatState => {\n const conversationErrorType = readConversationErrorTypeOfRefusedSend(error);\n return {\n ...state,\n error: conversationErrorType,\n rateLimitRetryAfterSeconds:\n conversationErrorType === ConversationErrorType.RATE_LIMIT ? (error.retryAfterSeconds ?? null) : null,\n };\n};\n\n/** The messages a send's answer brings, merged over the loaded ones without the optimistic message (the last one). */\nconst mergeSendAnswer = (loadedMessages: ChatMessage[] | null, answerMessages: ChatMessage[]): ChatMessage[] => {\n const messagesWithoutOptimisticMessage = loadedMessages ? loadedMessages.slice(0, loadedMessages.length - 1) : [];\n return mergeChatMessagesByIndex(\n messagesWithoutOptimisticMessage,\n tagFreshChatMessages(answerMessages, messagesWithoutOptimisticMessage),\n );\n};\n\n/** The canvas transitions mutate the fields of the object they get (`canvas-reducers.ts`); this keeps the state immutable. */\nconst withCanvasTransition = (\n state: AgentsChatState,\n applyCanvasTransition: (nextState: AgentsChatState) => void,\n): AgentsChatState => {\n const nextState = { ...state };\n applyCanvasTransition(nextState);\n const changed =\n nextState.canvas !== state.canvas ||\n nextState.lastClosedCanvas !== state.lastClosedCanvas ||\n nextState.canvasMountCount !== state.canvasMountCount;\n return changed ? nextState : state;\n};\n\n/**\n * The chat store's reducer: plain functions over the contract types. Each case is the one of the older\n * chat state (a Redux slice and its cases) it replaces, reading the port's answers instead of the\n * generated client's actions; the replay spec feeds both the same recorded sequence.\n */\nexport const reduceAgentsChatState = (state: AgentsChatState, event: AgentsChatEvent): AgentsChatState => {\n switch (event.type) {\n case \"reset\":\n // The opening request is set from outside before the chat mounts (a cold chat resets as it comes up); the canvas\n // mount count and the start token count are monotonic sources of identities: all three survive.\n return {\n ...INITIAL_AGENTS_CHAT_STATE,\n openingRequest: state.openingRequest,\n canvasMountCount: state.canvasMountCount,\n startTokenCount: state.startTokenCount,\n };\n\n case \"conversationMetadataRequested\":\n case \"messagesPageRequested\":\n return state.requestedConversationId === event.conversationId\n ? state\n : { ...state, requestedConversationId: event.conversationId };\n\n case \"conversationMetadataReceived\":\n // Dropped after a reset or when another conversation was asked for since: a late answer would put the old\n // conversation back, and the next send would post into it instead of starting a new one.\n if (state.requestedConversationId === null || state.requestedConversationId !== event.conversationId) {\n return state;\n }\n return { ...state, conversation: event.conversation, conversationLoaded: true };\n\n case \"conversationMetadataFailed\":\n // A metadata read is best-effort: the chat stays as it is.\n return { ...state, conversationLoaded: true };\n\n case \"messagesPageReceived\": {\n if (state.requestedConversationId === null || state.requestedConversationId !== event.conversationId) {\n return state;\n }\n if (state.conversation !== null && state.conversation.id !== event.conversationId) {\n return state;\n }\n return {\n ...state,\n messages: mergeMessagesPage(state.messages, event.messages, event.conversationId),\n hasMoreOlder: event.hasMoreOlder ?? state.hasMoreOlder,\n conversationLoaded: true,\n };\n }\n\n case \"messagePostRequested\":\n return {\n ...state,\n conversationLoading: true,\n conversationLoaded: false,\n aiThinking: true,\n error: null,\n rateLimitRetryAfterSeconds: null,\n messages: [...(state.messages ?? []), event.message],\n };\n\n case \"messagePostReceived\":\n if (event.conversationId !== state.conversation?.id) {\n return state;\n }\n // The reply arrives through live updates, not in this answer: `aiThinking` stays as it is.\n return {\n ...state,\n messages: mergeSendAnswer(state.messages, event.posted.messages),\n conversationLoading: false,\n conversationLoaded: true,\n error: null,\n };\n\n case \"messagePostFailed\":\n return withRefusedSendError(\n { ...state, conversationLoading: false, conversationLoaded: true, aiThinking: false },\n event.error,\n );\n\n case \"startRequested\":\n return {\n ...state,\n conversation: null,\n conversationLoading: true,\n conversationLoaded: false,\n aiThinking: true,\n error: null,\n rateLimitRetryAfterSeconds: null,\n startingNewConversation: true,\n startTokenCount: state.startTokenCount + 1,\n pendingStartToken: state.startTokenCount + 1,\n // A signal-only start (an empty visible message carries the start signal) seeds no empty bubble.\n messages: event.message.message ? [event.message] : [],\n };\n\n case \"startReceived\": {\n if (event.startToken !== state.pendingStartToken) {\n return state;\n }\n const { started } = event;\n if (started.reopenedExisting) {\n // The recent conversation of the start's entry point: nothing was appended, no turn started. Its messages are\n // not in the answer — the controller reads its newest page — so the optimistic bubble goes.\n return {\n ...state,\n conversation: started.conversation,\n messages: [],\n conversationLoading: false,\n conversationLoaded: true,\n aiThinking: false,\n startingNewConversation: false,\n pendingStartToken: null,\n };\n }\n // The reply arrives through live updates: `aiThinking` stays on.\n return {\n ...state,\n conversation: started.conversation,\n messages: mergeSendAnswer(state.messages, started.messages),\n conversationLoading: false,\n conversationLoaded: true,\n startingNewConversation: false,\n pendingStartToken: null,\n };\n }\n\n case \"startFailed\":\n return withRefusedSendError(\n {\n ...state,\n conversation: null,\n conversationLoading: false,\n conversationLoaded: true,\n aiThinking: false,\n startingNewConversation: false,\n pendingStartToken: null,\n },\n event.error,\n );\n\n case \"errorSet\":\n return state.error === event.error ? state : { ...state, error: event.error };\n\n case \"aiThinkingSet\":\n return state.aiThinking === event.aiThinking ? state : { ...state, aiThinking: event.aiThinking };\n\n case \"openingRequestSet\":\n return { ...state, openingRequest: { ...state.openingRequest, ...event.openingRequest } };\n\n case \"canvasOpened\":\n return withCanvasTransition(state, (nextState) => {\n applyOpenCanvas(nextState, event.request);\n });\n\n case \"canvasClosed\":\n return withCanvasTransition(state, (nextState) => applyCloseCanvas(nextState, event.byCustomer));\n\n case \"canvasResetForNewTurn\":\n return withCanvasTransition(state, applyResetCanvasForNewTurn);\n\n case \"canvasCleared\":\n return withCanvasTransition(state, applyClearCanvas);\n\n case \"canvasCreatedEntityIdSet\":\n return withCanvasTransition(state, (nextState) => {\n applySetCanvasCreatedEntityId(nextState, event.created);\n });\n }\n};\n","import { type AgentsChatEvent, type AgentsChatState, INITIAL_AGENTS_CHAT_STATE } from \"./agents-chat-state\";\nimport { reduceAgentsChatState } from \"./reduce-agents-chat-state\";\n\n/**\n * The state handle of one chat instance. The app creates one per chat (host application: one per agent\n * window, created before the window's chat mounts) and releases it with the chat; the chat and the app's code outside\n * the chat read and dispatch through the same handle. React reads it through `useAgentsChatSelector`.\n */\nexport interface AgentsChatStore {\n getState: () => AgentsChatState;\n /** Calls the listener after every state change; returns the unsubscribe. */\n subscribe: (listener: () => void) => () => void;\n dispatch: (event: AgentsChatEvent) => void;\n}\n\n/** The plain implementation of `AgentsChatStore`: one state object, replaced by the reducer on every event. */\nexport class AgentsChatStateContainer implements AgentsChatStore {\n protected state: AgentsChatState;\n protected readonly listeners = new Set<() => void>();\n\n constructor(initialState: AgentsChatState = INITIAL_AGENTS_CHAT_STATE) {\n this.state = initialState;\n }\n\n getState = (): AgentsChatState => this.state;\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n dispatch = (event: AgentsChatEvent): void => {\n const nextState = reduceAgentsChatState(this.state, event);\n if (nextState === this.state) return;\n this.state = nextState;\n for (const notifyListener of [...this.listeners]) notifyListener();\n };\n}\n\n/** Creates the state handle of one chat instance. */\nexport const createAgentsChatStore = (initialState?: AgentsChatState): AgentsChatStore =>\n new AgentsChatStateContainer(initialState);\n","import { createContext, type ReactNode, useCallback, useContext, useSyncExternalStore } from \"react\";\nimport type { AgentsChatState } from \"./agents-chat-state\";\nimport type { AgentsChatStore } from \"./create-agents-chat-store\";\n\nconst AgentsChatStoreContext = createContext<AgentsChatStore | null>(null);\n\nexport interface AgentsChatStoreProviderProps {\n /** The state handle of the chat below (the app creates and releases it). */\n chatStore: AgentsChatStore;\n children?: ReactNode;\n}\n\n/** Puts one chat instance's state handle in reach of the chat and of the app's components inside it. */\nexport const AgentsChatStoreProvider = (props: AgentsChatStoreProviderProps) => {\n const { chatStore, children } = props;\n return <AgentsChatStoreContext.Provider value={chatStore}>{children}</AgentsChatStoreContext.Provider>;\n};\n\n/** The state handle of the chat this component renders in; throws outside `AgentsChatStoreProvider`. */\nexport const useAgentsChatStore = (): AgentsChatStore => {\n const chatStore = useContext(AgentsChatStoreContext);\n if (chatStore === null) {\n throw new Error(\"A chat component rendered outside AgentsChatStoreProvider: the app has to mount its chat store.\");\n }\n return chatStore;\n};\n\n/** The state handle of the chat this component renders in, or null outside a chat. */\nexport const useOptionalAgentsChatStore = (): AgentsChatStore | null => useContext(AgentsChatStoreContext);\n\nconst subscribeToNothing = () => () => undefined;\n\n/**\n * Reads one value of a chat instance's state and re-renders when it changes. The selector must return a value that\n * keeps its identity while the state does not change (a state field, not a new object). Without a store it returns\n * `valueWithoutStore`.\n */\nexport const useAgentsChatSelector = <Selected,>(\n chatStore: AgentsChatStore | null | undefined,\n selector: (state: AgentsChatState) => Selected,\n valueWithoutStore?: Selected,\n): Selected => {\n const subscribe = chatStore ? chatStore.subscribe : subscribeToNothing;\n const readSelectedValue = useCallback(\n () => (chatStore ? selector(chatStore.getState()) : (valueWithoutStore as Selected)),\n [chatStore, selector, valueWithoutStore],\n );\n return useSyncExternalStore(subscribe, readSelectedValue, readSelectedValue);\n};\n\n/** Reads one value of the state of the chat this component renders in. */\nexport const useAgentsChatState = <Selected,>(selector: (state: AgentsChatState) => Selected): Selected =>\n useAgentsChatSelector(useAgentsChatStore(), selector);\n","import React, { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAgentsEnvironment, useAgentsStateDispatch, useAgentsStateSelector } from \"../provider/AgentsProvider\";\nimport type { ConversationWidgetStatePartition } from \"./conversation-widget-state-partition\";\nimport { createAgentsChatStore } from \"../chat/store/create-agents-chat-store\";\nimport type { AgentsChatEvent } from \"../chat/store/agents-chat-state\";\nimport type { AgentsChatStore } from \"../chat/store/create-agents-chat-store\";\nimport { useAgentsChatSelector } from \"../chat/store/use-agents-chat-store\";\nimport type { CanvasState, LastClosedCanvas } from \"./canvas/canvas-state\";\nimport { CANVAS_VIABLE_MIN_WIDTH } from \"../widgets/CanvasWidgetCard/use-canvas-viable\";\nimport { ConversationComposerWidget } from \"./conversation-composer-widget\";\nimport type { CanvasCreatedEntity } from \"./canvas/canvas-reducers\";\nimport { clearStoredCanvas, storeOpenCanvas } from \"./canvas/canvasStorage\";\nimport {\n type CanvasOpenRequestFromOpener,\n type ConversationWidgetCallbacks,\n ConversationWidgetContext,\n defaultConversationWidgetCallbacks,\n type InjectSignalOptions,\n} from \"./ConversationWidgetCallbacksContext\";\n\n// The callbacks, their context and the chat-free hosts live in `ConversationWidgetCallbacksContext.tsx`; this module\n// adds the provider that reads the chat's state (the package chat store or an app's partition) and re-exports the rest.\nexport * from \"./ConversationWidgetCallbacksContext\";\n\n// Number of mounted agentic chat surfaces (widget, full-page chat, dashboard). Drives the app's \"a chat surface is\n// open\" action (`buildChatSurfaceOpenChangedAction`, e.g. a flag of the app's own flows): true while ≥1 is open.\n// Module-level so multiple providers share one count.\nlet agenticChatMountCount = 0;\n\nexport interface ConversationWidgetProviderProps {\n children: ReactNode;\n callbacks: Partial<ConversationWidgetCallbacks>;\n /**\n * The chat slice partition this provider's canvas lives in. The shell is shared by the two chat doors, so the door\n * says which partition: `AgentChat` passes `AgentChatInstance`; the AI-Extensions chat, every story and every spec\n * keep the default. A prop, not a callback — `callbacks` is `Partial`, and the partition is not optional.\n */\n sliceInstance?: ConversationWidgetStatePartition;\n /**\n * The package chat store of the chat: the agent chat passes its window's store and the provider reads\n * and writes the canvas there. Without it the provider reads a partition of the app's own chat state\n * (`sliceInstance`, else the environment's `defaultChatStatePartition`).\n */\n chatStore?: AgentsChatStore;\n /**\n * False for a host that renders the chat without the split (`ConversationView.disableSplitView`): there is nowhere\n * to open a canvas, so every open request is ignored with reason `noSurface`. Default true.\n */\n hostHasCanvasSurface?: boolean;\n}\n\n/** The canvas transitions the provider dispatches — the canvas events of the package chat store. */\ntype ConversationWidgetCanvasEvent = Extract<\n AgentsChatEvent,\n { type: \"canvasOpened\" | \"canvasClosed\" | \"canvasResetForNewTurn\" | \"canvasCleared\" | \"canvasCreatedEntityIdSet\" }\n>;\n\n/** What the provider reads from the chat's state and how it writes a canvas transition back. */\ninterface ConversationWidgetChatState {\n canvas: CanvasState | null;\n lastClosedCanvas: LastClosedCanvas | null;\n isConversationRunning: boolean;\n runState: string | null | undefined;\n messages: ConversationWidgetCallbacks[\"messages\"];\n dispatchCanvasEvent: (event: ConversationWidgetCanvasEvent) => void;\n}\n\ntype ConversationWidgetProviderBodyProps = Omit<ConversationWidgetProviderProps, \"sliceInstance\" | \"chatStore\"> & {\n chatState: ConversationWidgetChatState;\n};\n\nconst selectCanvas = (state: { canvas: CanvasState | null }) => state.canvas;\nconst selectLastClosedCanvas = (state: { lastClosedCanvas: LastClosedCanvas | null }) => state.lastClosedCanvas;\nconst selectAiThinking = (state: { aiThinking: boolean }) => state.aiThinking;\nconst selectRunState = (state: { conversation: { runState?: string | null } | null }) => state.conversation?.runState;\nconst selectMessages = (state: { messages: ConversationWidgetCallbacks[\"messages\"] }) => state.messages;\n\n/** The provider over the package chat store of the chat. */\nconst ChatStoreConversationWidgetProvider = (\n props: Omit<ConversationWidgetProviderProps, \"sliceInstance\" | \"chatStore\"> & { chatStore: AgentsChatStore },\n) => {\n const { chatStore, ...bodyProps } = props;\n const canvas = useAgentsChatSelector(chatStore, selectCanvas);\n const lastClosedCanvas = useAgentsChatSelector(chatStore, selectLastClosedCanvas);\n const isConversationRunning = useAgentsChatSelector(chatStore, selectAiThinking);\n const runState = useAgentsChatSelector(chatStore, selectRunState);\n const messages = useAgentsChatSelector(chatStore, selectMessages);\n const chatState = useMemo<ConversationWidgetChatState>(\n () => ({\n canvas,\n lastClosedCanvas,\n isConversationRunning,\n runState,\n messages,\n dispatchCanvasEvent: chatStore.dispatch,\n }),\n [canvas, lastClosedCanvas, isConversationRunning, runState, messages, chatStore],\n );\n return <ConversationWidgetProviderBody {...bodyProps} chatState={chatState} />;\n};\n\n/** The provider over a partition of the older chat state (the AI-Extensions chat, stories, specs). */\nconst SliceConversationWidgetProvider = (\n props: Omit<ConversationWidgetProviderProps, \"sliceInstance\" | \"chatStore\"> & {\n sliceInstance: ConversationWidgetStatePartition;\n },\n) => {\n const { sliceInstance, ...bodyProps } = props;\n const dispatch = useAgentsStateDispatch();\n // One selector per field, not one returning an object, so an unrelated store change does not re-render every widget\n // through a fresh object identity.\n const canvas = useAgentsStateSelector((state) => sliceInstance.selectState(state).canvas);\n const lastClosedCanvas = useAgentsStateSelector((state) => sliceInstance.selectState(state).lastClosedCanvas);\n const isConversationRunning = useAgentsStateSelector((state) => sliceInstance.selectState(state).aiThinking);\n const runState = useAgentsStateSelector((state) => sliceInstance.selectState(state).conversation?.runState);\n const messages = useAgentsStateSelector((state) => sliceInstance.selectState(state).messages);\n const dispatchCanvasEvent = useCallback(\n (event: ConversationWidgetCanvasEvent) => {\n const { actions } = sliceInstance;\n if (event.type === \"canvasOpened\") dispatch(actions.openCanvas(event.request));\n else if (event.type === \"canvasClosed\") dispatch(actions.closeCanvas({ byCustomer: event.byCustomer }));\n else if (event.type === \"canvasResetForNewTurn\") dispatch(actions.resetCanvasForNewTurn());\n else if (event.type === \"canvasCleared\") dispatch(actions.clearCanvas());\n else dispatch(actions.setCanvasCreatedEntityId(event.created));\n },\n [dispatch, sliceInstance],\n );\n const chatState = useMemo<ConversationWidgetChatState>(\n () => ({ canvas, lastClosedCanvas, isConversationRunning, runState, messages, dispatchCanvasEvent }),\n [canvas, lastClosedCanvas, isConversationRunning, runState, messages, dispatchCanvasEvent],\n );\n return <ConversationWidgetProviderBody {...bodyProps} chatState={chatState} />;\n};\n\n/** The provider over a chat store of its own — a host that brings neither a chat store nor a partition (a story). */\nconst OwnChatStoreConversationWidgetProvider = (\n props: Omit<ConversationWidgetProviderProps, \"sliceInstance\" | \"chatStore\">,\n) => {\n const [ownChatStore] = useState(createAgentsChatStore);\n return <ChatStoreConversationWidgetProvider {...props} chatStore={ownChatStore} />;\n};\n\n/**\n * The widget callbacks of one chat, the canvas model included. Reads the chat's state from the package chat store when\n * the host passes one (`chatStore`), else from a partition of the app's own chat state (`sliceInstance`, or the\n * environment's `defaultChatStatePartition` — host application: the AI-Extensions chat), else from a store of its own.\n */\nexport const ConversationWidgetProvider = (props: ConversationWidgetProviderProps) => {\n const { sliceInstance, chatStore, ...bodyProps } = props;\n const { defaultChatStatePartition } = useAgentsEnvironment();\n const statePartition = sliceInstance ?? defaultChatStatePartition;\n if (chatStore) return <ChatStoreConversationWidgetProvider {...bodyProps} chatStore={chatStore} />;\n if (statePartition) return <SliceConversationWidgetProvider {...bodyProps} sliceInstance={statePartition} />;\n return <OwnChatStoreConversationWidgetProvider {...bodyProps} />;\n};\n\nconst ConversationWidgetProviderBody = ({\n children,\n callbacks,\n hostHasCanvasSurface = true,\n chatState,\n}: ConversationWidgetProviderBodyProps) => {\n const dispatch = useAgentsStateDispatch();\n const { buildChatSurfaceOpenChangedAction } = useAgentsEnvironment();\n const { canvas, lastClosedCanvas, isConversationRunning, runState, messages } = chatState;\n const isConversationClosed = runState === \"COMPLETED\" || runState === \"CANCELLED\";\n const dispatchCanvasEventRef = useRef(chatState.dispatchCanvasEvent);\n dispatchCanvasEventRef.current = chatState.dispatchCanvasEvent;\n const hostHasCanvasSurfaceRef = useRef(hostHasCanvasSurface);\n hostHasCanvasSurfaceRef.current = hostHasCanvasSurface;\n\n // An agentic chat surface is open: the app hears it through its action (e.g. a flag every connect/create/verify flow\n // launched from inside the chat reads to stay in-widget instead of navigating away).\n // Set here (not at the connect button) so it also covers the verify path, where there is no connect click.\n // Ref-counted so concurrently-mounted chat surfaces don't clear each other's flag.\n const buildChatSurfaceOpenChangedActionRef = useRef(buildChatSurfaceOpenChangedAction);\n buildChatSurfaceOpenChangedActionRef.current = buildChatSurfaceOpenChangedAction;\n useEffect(() => {\n agenticChatMountCount += 1;\n if (agenticChatMountCount === 1 && buildChatSurfaceOpenChangedActionRef.current) {\n dispatch(buildChatSurfaceOpenChangedActionRef.current(true));\n }\n return () => {\n agenticChatMountCount -= 1;\n if (agenticChatMountCount === 0 && buildChatSurfaceOpenChangedActionRef.current) {\n dispatch(buildChatSurfaceOpenChangedActionRef.current(false));\n }\n };\n }, [dispatch]);\n\n // Mirror the latest callbacks in a ref so the exposed functions stay stable across\n // renders even if the parent passes a fresh `callbacks` object every render.\n const callbacksRef = useRef(callbacks);\n callbacksRef.current = callbacks;\n\n // Composer-widget ownership — controlled when the host supplies `composerWidgets` (the builder page, click-bridge\n // F), otherwise uncontrolled: the provider owns the state so a descendant (e.g. an app's editor drawer) can\n // mutate it directly. See ConversationWidgetCallbacks.\n const isComposerWidgetsControlled = callbacks.composerWidgets !== undefined;\n const [internalComposerWidgets, setInternalComposerWidgets] = useState<ConversationComposerWidget[]>([]);\n const composerWidgets = isComposerWidgetsControlled\n ? (callbacks.composerWidgets as ConversationComposerWidget[])\n : internalComposerWidgets;\n\n // Mirrors the latest resolved composerWidgets + controlled flag so the mutator callbacks below can compute the\n // next array off REAL current state while keeping stable identities across renders ([] deps) — same trick as\n // `callbacksRef` above.\n const composerWidgetsStateRef = useRef({ composerWidgets, isComposerWidgetsControlled });\n composerWidgetsStateRef.current = { composerWidgets, isComposerWidgetsControlled };\n\n const publishComposerWidgets = useCallback((next: ConversationComposerWidget[]) => {\n if (composerWidgetsStateRef.current.isComposerWidgetsControlled) {\n callbacksRef.current.onComposerWidgetsChange?.(next);\n } else {\n setInternalComposerWidgets(next);\n }\n }, []);\n\n const addComposerWidget = useCallback(\n (widgetName: string, fields: Record<string, unknown>): string => {\n const composerWidgetId = crypto.randomUUID();\n publishComposerWidgets([\n ...composerWidgetsStateRef.current.composerWidgets,\n { composerWidgetId, widgetName, fields },\n ]);\n return composerWidgetId;\n },\n [publishComposerWidgets],\n );\n\n const replaceComposerWidgetFields = useCallback(\n (composerWidgetId: string, fields: Record<string, unknown>) => {\n publishComposerWidgets(\n composerWidgetsStateRef.current.composerWidgets.map((widget) =>\n widget.composerWidgetId === composerWidgetId ? { ...widget, fields } : widget,\n ),\n );\n },\n [publishComposerWidgets],\n );\n\n const removeComposerWidget = useCallback(\n (composerWidgetId: string) => {\n publishComposerWidgets(\n composerWidgetsStateRef.current.composerWidgets.filter(\n (widget) => widget.composerWidgetId !== composerWidgetId,\n ),\n );\n },\n [publishComposerWidgets],\n );\n\n const clearComposerWidgets = useCallback(() => {\n publishComposerWidgets([]);\n }, [publishComposerWidgets]);\n\n // The one way to open a canvas (all four origins). The provider supplies what only it knows — the split's viability\n // (measured on the window, like `useCanvasViable`) and whether this host has a canvas surface — and the reducer\n // applies the opening rule. Ignored requests change nothing.\n const openCanvas = useCallback(\n (request: CanvasOpenRequestFromOpener) => {\n dispatchCanvasEventRef.current({\n type: \"canvasOpened\",\n request: {\n ...request,\n viewportViable: window.innerWidth >= CANVAS_VIABLE_MIN_WIDTH,\n hostHasCanvasSurface: hostHasCanvasSurfaceRef.current,\n },\n });\n },\n [],\n );\n\n // Close the open canvas; it stays re-openable as `lastClosedCanvas`. `byCustomer` = the canvas's own close control,\n // which vetoes a live re-delivery of the same identity until the next send / click / page hand-over.\n const closeCanvas = useCallback(\n (byCustomer: boolean) => {\n dispatchCanvasEventRef.current({ type: \"canvasClosed\", byCustomer });\n },\n [],\n );\n\n const lastClosedCanvasRef = useRef(lastClosedCanvas);\n lastClosedCanvasRef.current = lastClosedCanvas;\n const reopenLastClosedCanvas = useCallback(() => {\n const last = lastClosedCanvasRef.current;\n if (last === null) return;\n dispatchCanvasEventRef.current({\n type: \"canvasOpened\",\n request: {\n canvasId: last.canvasId,\n instanceId: last.instanceId,\n anchor: last.anchor,\n origin: \"cardClick\",\n justDelivered: false,\n viewportViable: window.innerWidth >= CANVAS_VIABLE_MIN_WIDTH,\n hostHasCanvasSurface: hostHasCanvasSurfaceRef.current,\n ...(last.createdEntityId !== undefined ? { createdEntityId: last.createdEntityId } : {}),\n },\n });\n }, []);\n\n const setCanvasCreatedEntityId = useCallback(\n (created: CanvasCreatedEntity) => {\n dispatchCanvasEventRef.current({ type: \"canvasCreatedEntityIdSet\", created });\n },\n [],\n );\n\n // The pointer of the OPEN canvas is remembered per conversation for a reload (`canvasStorage.ts`), for any\n // lifetime. Cleared when the canvas closes INSIDE the same conversation; a conversation switch clears the slice too,\n // but the LEFT conversation keeps its entry so coming back restores it.\n const storedCanvasConversationIdRef = useRef<number | undefined>(undefined);\n const conversationIdForStorage = callbacks.conversationId;\n useEffect(() => {\n if (canvas !== null) {\n storeOpenCanvas(conversationIdForStorage, canvas);\n storedCanvasConversationIdRef.current = conversationIdForStorage;\n return;\n }\n if (\n storedCanvasConversationIdRef.current !== undefined &&\n storedCanvasConversationIdRef.current === conversationIdForStorage\n ) {\n clearStoredCanvas(conversationIdForStorage);\n }\n storedCanvasConversationIdRef.current = undefined;\n }, [canvas, conversationIdForStorage]);\n\n // A conversation switch forgets the canvas and the last closed one (the LEFT conversation keeps its stored pointer).\n const resetCanvas = useCallback(() => {\n dispatchCanvasEventRef.current({ type: \"canvasCleared\" });\n }, []);\n\n // The SEND boundary: a turn canvas is over for good, a pinned one stays (`canvas-reducers.ts`).\n const resetCanvasForNewTurn = useCallback(() => {\n dispatchCanvasEventRef.current({ type: \"canvasResetForNewTurn\" });\n }, []);\n\n const sendMessageToChat = useCallback((message: string) => {\n return callbacksRef.current.sendMessageToChat?.(message);\n }, []);\n\n const prefillComposer = useCallback((text: string) => {\n callbacksRef.current.prefillComposer?.(text);\n }, []);\n\n const injectSignal = useCallback((signalText: string, options?: InjectSignalOptions) => {\n return callbacksRef.current.injectSignal?.(signalText, options);\n }, []);\n\n const minimizeChatHost = useCallback(() => {\n callbacksRef.current.minimizeChatHost?.();\n }, []);\n\n const contextValue = useMemo<ConversationWidgetCallbacks>(\n () => ({\n canvas,\n hostHasCanvasSurface,\n lastClosedCanvas,\n openCanvas,\n closeCanvas,\n reopenLastClosedCanvas,\n setCanvasCreatedEntityId,\n resetCanvas,\n resetCanvasForNewTurn,\n sendMessageToChat,\n prefillComposer,\n injectSignal,\n minimizeChatHost,\n chatAgentSpaceKey: callbacks.chatAgentSpaceKey,\n isConversationRunning,\n isConversationClosed,\n conversationId: callbacks.conversationId,\n composerWidgets,\n addComposerWidget,\n replaceComposerWidgetFields,\n removeComposerWidget,\n clearComposerWidgets,\n onOpenConversation: callbacks.onOpenConversation,\n onSwitchConversation: callbacks.onSwitchConversation,\n isOnboardingSetupConversation: callbacks.isOnboardingSetupConversation ?? false,\n messages,\n }),\n [\n canvas,\n hostHasCanvasSurface,\n lastClosedCanvas,\n openCanvas,\n closeCanvas,\n reopenLastClosedCanvas,\n setCanvasCreatedEntityId,\n resetCanvas,\n resetCanvasForNewTurn,\n sendMessageToChat,\n prefillComposer,\n injectSignal,\n minimizeChatHost,\n callbacks.chatAgentSpaceKey,\n isConversationRunning,\n isConversationClosed,\n callbacks.conversationId,\n composerWidgets,\n addComposerWidget,\n replaceComposerWidgetFields,\n removeComposerWidget,\n clearComposerWidgets,\n callbacks.onOpenConversation,\n callbacks.onSwitchConversation,\n callbacks.isOnboardingSetupConversation,\n messages,\n ],\n );\n\n return <ConversationWidgetContext.Provider value={contextValue}>{children}</ConversationWidgetContext.Provider>;\n};\n\n","import { ConversationComposerWidget } from \"./conversation-composer-widget\";\nimport { WIDGET_FENCE_LANG } from \"./widget-fence-syntax\";\nimport { resolveWidgetName } from \"./widget-name-resolver\";\nimport { findBalancedJsonObjectEnd } from \"./balanced-json-object\";\nimport { conversationWidgetRegistry } from \"./conversationWidgetRegistry\";\n\n/**\n * The FE twin of the backend's `AIConversationMessage::buildWidgetFenceBlock` — assembles / disassembles the\n * ```widget fence(s) a composer widget becomes inside the message TEXT (the record; no DB column, no DTO change). `buildComposerWidgetFenceBlock` / `buildMessageTextWithComposerWidgets` run on SEND;\n * `splitComposerWidgetFencesFromMessageText` runs on the USER BUBBLE to pull the fence back out as a\n * read-only widget above the prose.\n */\n\n/** One ```widget fence block for a composer widget — `type` first, then its own fields, one line of JSON. */\nexport const buildComposerWidgetFenceBlock = (widget: ConversationComposerWidget): string =>\n \"```\" + WIDGET_FENCE_LANG + \"\\n\" + JSON.stringify({ type: widget.widgetName, ...widget.fields }) + \"\\n```\";\n\n/**\n * The message text a send assembles: the typed text first, then one fence block per composer widget in composer\n * order, a blank line between blocks. Empty text + at least one widget is allowed (a fence-only send) — the result\n * is non-empty as long as EITHER side has content; both empty yields `\"\"`, the send guard's cue to no-op.\n */\nexport const buildMessageTextWithComposerWidgets = (text: string, widgets: ConversationComposerWidget[]): string =>\n [text.trim(), ...widgets.map(buildComposerWidgetFenceBlock)].filter(Boolean).join(\"\\n\\n\");\n\n/** The canonical names of every registered widget that declares a `composerComponent`. */\nconst composableWidgetNames = (): ReadonlySet<string> =>\n new Set(\n conversationWidgetRegistry.widgets\n .filter((entry) => entry.composerComponent !== undefined)\n .map((entry) => entry.name),\n );\n\nexport interface SplitComposerWidgetFencesResult {\n /** The message text with every extracted fence removed, trimmed. */\n text: string;\n /** The extracted composer widgets, in the order they appeared. */\n widgets: ConversationComposerWidget[];\n}\n\n/**\n * The inverse of {@link buildMessageTextWithComposerWidgets}. Extracts every ```widget fence in `text` whose `type`\n * resolves (canonical or legacy `Directive` form, see `widget-name-resolver.ts`) to a registered widget that\n * declares a `composerComponent` — the only fences a user bubble renders as a read-only widget instead of leaving as\n * text. Every other fence — prose, and fences for widgets WITHOUT a `composerComponent` — is left in `text`\n * verbatim. Never throws: an unterminated fence, malformed JSON, or an unresolved/non-composable `type` just leaves\n * that fence untouched.\n */\nexport const splitComposerWidgetFencesFromMessageText = (text: string): SplitComposerWidgetFencesResult => {\n const composableNames = composableWidgetNames();\n const widgets: ConversationComposerWidget[] = [];\n const fenceOpen = \"```\" + WIDGET_FENCE_LANG;\n\n let remaining = \"\";\n let cursor = 0;\n while (cursor < text.length) {\n const openIndex = text.indexOf(fenceOpen, cursor);\n if (openIndex === -1) {\n remaining += text.slice(cursor);\n break;\n }\n\n // The fence's info-string line must end right after the lang word (a newline follows) — otherwise this is some\n // other fence whose lang merely starts with \"widget\" (e.g. \"widgetx\"); leave it untouched and keep scanning.\n const afterOpen = openIndex + fenceOpen.length;\n const jsonStart = afterOpen + 1;\n const objectEnd = text[afterOpen] === \"\\n\" ? findBalancedJsonObjectEnd(text, jsonStart) : null;\n if (objectEnd === null) {\n remaining += text.slice(cursor, afterOpen);\n cursor = afterOpen;\n continue;\n }\n\n // The fence must close with ``` right after the JSON object (optional whitespace/newline in between).\n const afterJson = objectEnd + 1;\n const closeMatch = /^\\s*```/.exec(text.slice(afterJson));\n if (!closeMatch) {\n remaining += text.slice(cursor, afterOpen);\n cursor = afterOpen;\n continue;\n }\n const fenceEnd = afterJson + closeMatch[0].length;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(text.slice(jsonStart, objectEnd + 1));\n } catch {\n parsed = undefined;\n }\n const { type, ...fields } = (typeof parsed === \"object\" && parsed !== null ? parsed : {}) as Record<\n string,\n unknown\n >;\n const widgetName = typeof type === \"string\" ? resolveWidgetName(type) : undefined;\n\n if (!widgetName || !composableNames.has(widgetName)) {\n // Unknown / non-composable widget fence — leave it in the text verbatim (it still renders normally if this\n // text ever runs through the assistant-side remark pipeline; here it just stays as text).\n remaining += text.slice(cursor, fenceEnd);\n cursor = fenceEnd;\n continue;\n }\n\n remaining += text.slice(cursor, openIndex);\n widgets.push({ composerWidgetId: `${widgetName}:${widgets.length}`, widgetName, fields });\n cursor = fenceEnd;\n }\n\n return { text: remaining.trim(), widgets };\n};\n","import { Button, GlowFrame, Text, Tooltip } from \"../../provider/components\";\nimport React, {\n useRef,\n useState,\n KeyboardEvent,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n} from \"react\";\nimport styles from \"./ConversationMessageInput.module.scss\";\nimport type { ConversationAttachmentReference } from \"../../contract/conversation\";\nimport { SendMessageHandler } from \"../conversation-send-message\";\nimport { translateText } from \"../../provider/environment\";\nimport { classNames } from \"../../provider/class-names\";\nimport { ConversationComposerWidget } from \"../../renderer/conversation-composer-widget\";\nimport { buildMessageTextWithComposerWidgets } from \"../../renderer/composer-widget-fence\";\nimport { conversationWidgetRegistry } from \"../../renderer/conversationWidgetRegistry\";\nimport { useConversationWidgetContext } from \"../../renderer/ConversationWidgetContext\";\n\n// Half the one-line compact input (8px padding + the 40px send row + 8px): fully round at rest, and the same corners\n// as it grows with the text. The glow and the input share it through --ai-border-radius.\nconst COMPACT_INPUT_RADIUS_PX = 28;\n\n/**\n * Cap on the message a user can send. Covers the longest legitimate business\n * inputs (pasted homepage copy, article drafts, menus ≈ 8k chars) while blocking\n * accidental huge dumps. Typing and pasting past it stay possible — the text is\n * never trimmed — but sending is blocked until the user edits back under the cap.\n */\nexport const MAX_MESSAGE_LENGTH = 10000;\n\n/** The character counter stays hidden until the message approaches the cap. */\nexport const CHARACTER_COUNTER_SHOWN_FROM_LENGTH = Math.floor(MAX_MESSAGE_LENGTH * 0.9);\n\n/**\n * Imperative handle so a host-owned control (e.g. the agent \"+\" menu's \"Generate image\" entry)\n * can put text into the input without owning the contentEditable itself.\n */\nexport interface ConversationMessageInputHandle {\n /** Replaces the input's text and focuses the caret at the end. */\n insertTextIntoInput: (text: string) => void;\n}\n\nexport interface ConversationMessageInputProps {\n /**\n * Callback when a message is submitted. `options.attachments` echoes `outgoingAttachmentImages`\n * when the host provided any — ConversationMessageInput itself never builds attachments, it only rides them along.\n */\n onSendMessage?: SendMessageHandler;\n\n /**\n * Placeholder text for the input\n */\n placeholder?: string;\n\n /**\n * Whether the input is disabled\n */\n disabled?: boolean;\n\n /**\n * Theme color for the input\n */\n themeColor?: string;\n\n /**\n * One-line form: text and send button on the same row inside a pill, growing with the text. The agent dashboard's\n * input; the chat keeps the full form.\n */\n compact?: boolean;\n\n /**\n * Compact only: shrunk to a small glowing pill showing one line, the placeholder or the start of a draft, its buttons\n * hidden but kept (the agent dashboard while the page is scrolled). The host expands it again on hover, focus or at the\n * top of the page.\n */\n collapsed?: boolean;\n\n /**\n * Compact only: called with the width in px the collapsed pill needs to show the whole translated placeholder on one\n * line, its side padding included, and again whenever that width changes (a font loading, another language). The\n * host gives the pill this width, so the placeholder fits in every language.\n */\n onCollapsedPillWidthChange?: (collapsedPillWidthPx: number) => void;\n\n /**\n * Whether the AI is currently processing a response\n */\n isProcessing?: boolean;\n\n /**\n * Test ID for the component\n */\n testId?: string;\n\n /**\n * Additional CSS classes to apply to the component\n */\n className?: string;\n\n /**\n * Extra action controls rendered inside the input's bottom action row, left of the send button —\n * e.g. the agent model picker pill.\n */\n actionsSlot?: React.ReactNode;\n\n /**\n * Action controls pinned to the LEFT edge of the bottom action row (the `actionsSlot` cluster\n * stays right, next to the send button) — e.g. the agent \"+\" attachments menu.\n */\n actionsSlotStart?: React.ReactNode;\n\n /**\n * Host-owned content rendered above the editable text area, inside the input container —\n * attachment preview cards, validation error boxes, selection chips. Each injected block owns\n * its reveal/collapse animation.\n */\n topSlot?: React.ReactNode;\n\n /** Shown in place of the send button while there is nothing to send — e.g. the button that starts a voice chat. */\n sendReplacementWhenEmpty?: React.ReactNode;\n\n /** Painted behind the input, over its whole box — e.g. the voice glow of a running voice chat. */\n glowUnderlay?: React.ReactNode;\n\n /**\n * Hard send gate: blocks the send button AND Enter while true, but keeps the text editable\n * (unlike `disabled`/`isProcessing`). The host sets it e.g. while attachment uploads run.\n */\n sendDisabled?: boolean;\n\n /**\n * Uploaded media-library images that ride along in `onSendMessage`'s options object (`attachments`)\n * on the next send. Owned and cleared by the host; ConversationMessageInput only forwards them.\n */\n outgoingAttachmentImages?: ConversationAttachmentReference[];\n\n /**\n * Composer widgets that ride along on the next send — mirrors `outgoingAttachmentImages`. On send their fences\n * are appended to the typed text (`buildMessageTextWithComposerWidgets`) and they ride along in `onSendMessage`'s\n * options object (`composerWidgets`); a widget-only send (empty typed text) is allowed. Backspace on an empty\n * input removes the last part / widget via the widget context directly (`useConversationWidgetContext`).\n */\n outgoingComposerWidgets?: ConversationComposerWidget[];\n\n /** Called right after a successful send — the host clears the composer widgets it owns. */\n onComposerWidgetsSent?: () => void;\n\n /** Imperative handle for host-owned controls to put text into the input. */\n apiRef?: React.Ref<ConversationMessageInputHandle>;\n}\n\nexport const ConversationMessageInput = (props: ConversationMessageInputProps) => {\n const {\n onSendMessage,\n placeholder = \"Ask anything\",\n disabled = false,\n isProcessing = false,\n testId = \"chat-input\",\n className,\n actionsSlot,\n actionsSlotStart,\n topSlot,\n sendReplacementWhenEmpty,\n glowUnderlay,\n sendDisabled = false,\n outgoingAttachmentImages,\n outgoingComposerWidgets,\n onComposerWidgetsSent,\n apiRef,\n compact = false,\n collapsed = false,\n onCollapsedPillWidthChange,\n } = props;\n\n const { removeComposerWidget, replaceComposerWidgetFields } = useConversationWidgetContext();\n\n const [inputValue, setInputValue] = useState(\"\");\n const [isFocused, setIsFocused] = useState(false);\n const inputRef = useRef<HTMLSpanElement>(null);\n const isComposingRef = useRef(false);\n const translatedPlaceholder = useMemo(() => translateText(placeholder), [placeholder]);\n const collapsedPillSizerRef = useRef<HTMLSpanElement>(null);\n const measuresCollapsedPillWidth = compact && !!onCollapsedPillWidthChange;\n\n const isMessageTooLong = inputValue.length > MAX_MESSAGE_LENGTH;\n // Text OR at least one composer widget is enough to send (a fence-only message is allowed).\n const hasSendableContent = inputValue.trim() !== \"\" || (outgoingComposerWidgets?.length ?? 0) > 0;\n\n const focusInputAtEnd = () => {\n if (!inputRef.current) return;\n inputRef.current.focus();\n const sel = window.getSelection();\n const range = document.createRange();\n range.selectNodeContents(inputRef.current);\n range.collapse(false);\n sel?.removeAllRanges();\n sel?.addRange(range);\n };\n\n const setInputText = (text: string) => {\n setInputValue(text);\n if (inputRef.current) {\n inputRef.current.textContent = text;\n focusInputAtEnd();\n }\n };\n\n useImperativeHandle(apiRef, () => ({ insertTextIntoInput: setInputText }));\n\n const handleSendMessage = () => {\n const messageText = buildMessageTextWithComposerWidgets(inputValue, outgoingComposerWidgets ?? []);\n if (!messageText || disabled || isProcessing || isMessageTooLong || sendDisabled) return;\n\n onSendMessage?.(messageText, {\n attachments: outgoingAttachmentImages?.length ? outgoingAttachmentImages : undefined,\n composerWidgets: outgoingComposerWidgets?.length ? outgoingComposerWidgets : undefined,\n });\n onComposerWidgetsSent?.();\n setInputValue(\"\");\n if (inputRef.current) {\n inputRef.current.textContent = \"\";\n }\n };\n\n // Backspace on an EMPTY input removes the last removable part of the last composer widget (declared via the\n // registry's `removeLastPartFromComposerWidgetFields`), or the whole widget when the entry omits it / nothing is\n // left. Mutates through the widget context directly — `outgoingComposerWidgets` only supplies the \"which\n // widget is last\" snapshot, mirroring the host-owned value ridden into the send.\n const handleBackspaceOnEmptyInput = () => {\n const widgets = outgoingComposerWidgets ?? [];\n const lastWidget = widgets[widgets.length - 1];\n if (!lastWidget) return;\n\n const entry = conversationWidgetRegistry.widgets.find((candidate) => candidate.name === lastWidget.widgetName);\n const removeLastPart = entry?.removeLastPartFromComposerWidgetFields;\n if (!removeLastPart) {\n removeComposerWidget(lastWidget.composerWidgetId);\n return;\n }\n const nextFields = removeLastPart(lastWidget.fields);\n if (nextFields === null) {\n removeComposerWidget(lastWidget.composerWidgetId);\n } else {\n replaceComposerWidgetFields(lastWidget.composerWidgetId, nextFields);\n }\n };\n\n const handleKeyPress = (e: KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n if (isComposingRef.current) return;\n e.preventDefault();\n handleSendMessage();\n return;\n }\n if (e.key === \"Backspace\" && inputValue === \"\" && (outgoingComposerWidgets?.length ?? 0) > 0) {\n e.preventDefault();\n handleBackspaceOnEmptyInput();\n }\n };\n\n const handleInput = () => {\n if (isComposingRef.current) return;\n if (!inputRef.current) return;\n setInputValue(inputRef.current.textContent || \"\");\n };\n\n const handleCompositionStart = () => {\n isComposingRef.current = true;\n };\n\n const handleCompositionEnd = () => {\n isComposingRef.current = false;\n handleInput();\n };\n\n const handleFocus = () => {\n setIsFocused(true);\n };\n\n const handleBlur = () => {\n setIsFocused(false);\n };\n\n const handleContainerMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {\n const target = e.target as HTMLElement;\n // Let clicks on the editable span itself fall through — browser positions the caret at the click point.\n if (target === inputRef.current || inputRef.current?.contains(target)) return;\n // Let clicks on buttons (send, any future action buttons) fall through untouched.\n if (target.closest(\"button\")) return;\n // Dead space click: redirect focus to end of input, but only when editing is allowed.\n if (disabled || isProcessing) return;\n e.preventDefault();\n focusInputAtEnd();\n };\n\n // Before the first paint, so the pill never shows at a width that cuts the placeholder off; again when the sizer's\n // text changes width.\n useLayoutEffect(() => {\n const collapsedPillSizer = collapsedPillSizerRef.current;\n if (!collapsedPillSizer || !onCollapsedPillWidthChange) return;\n const reportCollapsedPillWidth = () =>\n onCollapsedPillWidthChange(Math.ceil(collapsedPillSizer.getBoundingClientRect().width));\n reportCollapsedPillWidth();\n const resizeObserver = new ResizeObserver(reportCollapsedPillWidth);\n resizeObserver.observe(collapsedPillSizer);\n return () => resizeObserver.disconnect();\n }, [measuresCollapsedPillWidth, onCollapsedPillWidthChange]);\n\n const wasProcessingRef = useRef(isProcessing);\n useEffect(() => {\n if (wasProcessingRef.current && !isProcessing && !disabled) {\n inputRef.current?.focus();\n }\n wasProcessingRef.current = isProcessing;\n }, [isProcessing, disabled]);\n\n useEffect(() => {\n const handlePasteEvent = (e: ClipboardEvent) => {\n e.preventDefault();\n\n // Plain text (works across modern browsers)\n const text = e.clipboardData?.getData(\"text/plain\");\n\n if (!text) {\n return;\n }\n\n // Insert text at caret position\n if (document.queryCommandSupported?.(\"insertText\")) {\n document.execCommand(\"insertText\", false, text);\n } else {\n // Fallback for newer/stricter environments\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n sel.deleteFromDocument();\n sel.getRangeAt(0).insertNode(document.createTextNode(text));\n sel.collapseToEnd();\n }\n // The fallback path mutates the DOM directly and fires no input event,\n // so sync state explicitly (harmless duplicate after execCommand).\n setInputValue(inputRef.current?.textContent || \"\");\n };\n const currentInput = inputRef.current;\n currentInput?.addEventListener(\"paste\", handlePasteEvent);\n\n return () => {\n currentInput?.removeEventListener(\"paste\", handlePasteEvent);\n };\n }, []);\n\n return (\n <div className={className}>\n {glowUnderlay}\n {/* Without its own base the glow takes the inherited --ai-base-color: the agent's own base colour on an agent surface, the\n category colour in an AI-extension category conversation (ConversationViewBody), the theme's AI colour\n elsewhere. */}\n <GlowFrame\n className={classNames(styles.aiGlow)}\n color={isMessageTooLong ? \"var(--agents-danger-500)\" : undefined}\n borderRadius={compact ? COMPACT_INPUT_RADIUS_PX : undefined}\n borderWidth={2}\n blurWidth={6}\n >\n <div\n className={classNames(styles.inputContainer, {\n [styles.compact]: compact,\n [styles.collapsed]: compact && collapsed,\n })}\n onMouseDown={handleContainerMouseDown}\n >\n {topSlot}\n <span\n ref={inputRef}\n contentEditable={!(disabled || isProcessing)}\n onInput={handleInput}\n onCompositionStart={handleCompositionStart}\n onCompositionEnd={handleCompositionEnd}\n onKeyDown={handleKeyPress}\n onFocus={handleFocus}\n onBlur={handleBlur}\n className={`${styles.editableInput} ${!inputValue && !isFocused ? styles.showPlaceholder : \"\"}`}\n data-placeholder={translatedPlaceholder}\n data-testid={`${testId}-textarea`}\n ></span>\n\n {/* Meta row above the actions: the character counter lives on its own line\n so it never crowds the send/attachment buttons on small screens. */}\n {inputValue.length >= CHARACTER_COUNTER_SHOWN_FROM_LENGTH && (\n <div className={styles.statusRow}>\n <Text\n role=\"caption\"\n translate={false}\n color={isMessageTooLong ? \"var(--agents-danger-500)\" : \"var(--agents-fg-lightest)\"}\n className={styles.characterCounter}\n testId={`${testId}-character-counter`}\n >\n {`${inputValue.length.toLocaleString()} / ${MAX_MESSAGE_LENGTH.toLocaleString()}`}\n </Text>\n </div>\n )}\n\n <div className={styles.actions}>\n {actionsSlotStart && <div className={styles.actionsStart}>{actionsSlotStart}</div>}\n {/* <div className={styles.buttons}> */}\n {/* TODO: Implement emoji functionality */}\n {/* <Button\n type={ButtonTypes.muted}\n onClick={() => {\n }}\n testId={`${testId}-voice-button`}\n disabled={disabled || isProcessing}\n icon={IconNames.audioMicrophone}\n rounded\n /> */}\n {/* Over the cap: send is disabled and hovering it shows the popover with the\n reason. The wrapper carries the not-allowed cursor and routes hover past\n the disabled button to the popover (see .sendBlocked in the module scss). */}\n {actionsSlot}\n {sendReplacementWhenEmpty && !hasSendableContent ? (\n sendReplacementWhenEmpty\n ) : (\n <div className={classNames({ [styles.sendBlocked]: isMessageTooLong })}>\n <Tooltip\n text={isMessageTooLong ? \"Your message is over the %maxLength% character limit\" : undefined}\n replacements={{ maxLength: MAX_MESSAGE_LENGTH.toLocaleString() }}\n placement=\"top\"\n >\n <Button\n variant={\n hasSendableContent && !isMessageTooLong && !sendDisabled ? \"shimmer\" : \"muted\"\n }\n onClick={handleSendMessage}\n icon=\"arrowUp\"\n testId={`${testId}-send-button`}\n disabled={!hasSendableContent || disabled || isProcessing || isMessageTooLong || sendDisabled}\n size={hasSendableContent && !isMessageTooLong && !sendDisabled ? \"medium\" : undefined}\n rounded\n />\n </Tooltip>\n </div>\n )}\n {/* </div> */}\n </div>\n </div>\n </GlowFrame>\n {measuresCollapsedPillWidth && (\n <span ref={collapsedPillSizerRef} className={styles.collapsedPillSizer} aria-hidden=\"true\">\n {translatedPlaceholder}\n </span>\n )}\n </div>\n );\n};\n","import React from \"react\";\nimport { classNames } from \"../../../provider/class-names\";\nimport styles from \"./ConversationMessageInputComposerWidgets.module.scss\";\nimport { useConversationWidgetContext } from \"../../../renderer/ConversationWidgetContext\";\nimport { conversationWidgetRegistry } from \"../../../renderer/conversationWidgetRegistry\";\n\nexport interface ConversationMessageInputComposerWidgetsProps {\n testId?: string;\n}\n\n/**\n * Generic strip of composer widgets above the chat input's text area — the composer-side twin of\n * `ConversationMessageInputAttachmentsPreviewStrip`, same grid-rows 0fr→1fr reveal. Reads `composerWidgets` from the\n * widget context itself (so every ConversationView host gets it for free, mirroring the deleted selection chip),\n * and for each widget resolves the registry entry by `widgetName` and renders its `composerComponent` with\n * `{ composerWidgetId, fields, replaceFields, remove }`. An unknown name, or an entry without a `composerComponent`,\n * renders nothing for that widget — the same kill switch the fence plugin applies.\n */\nexport const ConversationMessageInputComposerWidgets = (props: ConversationMessageInputComposerWidgetsProps) => {\n const { testId = \"chat-input\" } = props;\n const { composerWidgets, replaceComposerWidgetFields, removeComposerWidget } = useConversationWidgetContext();\n\n const renderableWidgets = composerWidgets.flatMap((widget) => {\n const entry = conversationWidgetRegistry.widgets.find((candidate) => candidate.name === widget.widgetName);\n return entry?.composerComponent ? [{ widget, Component: entry.composerComponent }] : [];\n });\n\n return (\n <div\n className={classNames(styles.stripReveal, { [styles.stripRevealOpen]: renderableWidgets.length > 0 })}\n data-testid={`${testId}-composer-widgets-strip`}\n >\n <div className={styles.stripRevealInner}>\n <div className={styles.strip}>\n {renderableWidgets.map(({ widget, Component }) => (\n <Component\n key={widget.composerWidgetId}\n composerWidgetId={widget.composerWidgetId}\n fields={widget.fields}\n replaceFields={(nextFields) => replaceComposerWidgetFields(widget.composerWidgetId, nextFields)}\n remove={() => removeComposerWidget(widget.composerWidgetId)}\n testId={`${testId}-composer-widget-${widget.composerWidgetId}`}\n />\n ))}\n </div>\n </div>\n </div>\n );\n};\n","import { Skeleton } from \"../provider/components\";\nimport React from \"react\";\nimport styles from \"./Conversation.module.scss\";\nimport loadingStyles from \"./ConversationLoadingStates.module.scss\";\n\nconst SKEL_RADIUS = \"var(--agents-skeleton-radius)\";\n\nexport const NoConversationLoading = () => {\n return (\n <div className={styles.extensionIntro}>\n <Skeleton className={loadingStyles.avatar} borderRadius={SKEL_RADIUS} />\n <Skeleton className={loadingStyles.title} />\n <Skeleton className={loadingStyles.subtitle} />\n <div className={styles.suggestions}>\n {Array.from({ length: 3 }, (_, index) => (\n <Skeleton key={index} className={loadingStyles.suggestionCard} borderRadius={SKEL_RADIUS} />\n ))}\n </div>\n </div>\n );\n};\n\nexport const ConversationLoading = () => {\n return (\n <div className={styles.chatMessages}>\n <Skeleton className={loadingStyles.userBubble} borderRadius={SKEL_RADIUS} />\n <div className={loadingStyles.messageHeader}>\n <Skeleton className={loadingStyles.messageAvatar} borderRadius={SKEL_RADIUS} />\n <Skeleton className={loadingStyles.messageAuthor} />\n </div>\n <Skeleton className={loadingStyles.messageBody} borderRadius={SKEL_RADIUS} />\n </div>\n );\n};\n","import React from \"react\";\nimport { ConversationComposerWidget } from \"../../../renderer/conversation-composer-widget\";\nimport { conversationWidgetRegistry } from \"../../../renderer/conversationWidgetRegistry\";\nimport { ConversationWidgetErrorBoundary } from \"../../../renderer/ConversationWidgetErrorBoundary\";\n\nexport interface ConversationMessageUserWidgetsProps {\n /** Composer-widget fences extracted from the customer's own message text (see `splitComposerWidgetFencesFromMessageText`). */\n widgets: ConversationComposerWidget[];\n}\n\n/**\n * Read-only echo of the composer widgets the customer sent inside their OWN message — rendered above the prose. Reuses each widget's registered message `component` (the same one an\n * assistant reply would mount via the remark pipeline) directly with the fence's fields as `payload` — NOT the\n * remark/markdown pipeline itself, which never runs for user text (interpreting the customer's own prose as\n * Markdown would change every user bubble). `fresh={false}`: this is always a replay of content already sent, never\n * a live delivery. Each widget renders inside its own `ConversationWidgetErrorBoundary` so a malformed fence never\n * blanks the whole bubble.\n */\nexport const ConversationMessageUserWidgets = (props: ConversationMessageUserWidgetsProps) => {\n const { widgets } = props;\n\n if (widgets.length === 0) return null;\n\n return (\n <>\n {widgets.map((widget) => {\n const entry = conversationWidgetRegistry.widgets.find((candidate) => candidate.name === widget.widgetName);\n if (!entry) return null;\n const Component = entry.component;\n return (\n <ConversationWidgetErrorBoundary key={widget.composerWidgetId} widgetName={entry.name}>\n <Component payload={JSON.stringify(widget.fields)} fresh={false} />\n </ConversationWidgetErrorBoundary>\n );\n })}\n </>\n );\n};\n","import { Text } from \"../../../provider/components\";\nimport React, { useMemo } from \"react\";\nimport { classNames } from \"../../../provider/class-names\";\nimport styles from \"./ConversationMessageUser.module.scss\";\nimport { splitComposerWidgetFencesFromMessageText } from \"../../../renderer/composer-widget-fence\";\nimport { ConversationMessageUserWidgets } from \"./ConversationMessageUserWidgets\";\n\nexport type ConversationMessageUserTheme = \"default\" | \"light\" | \"dark\" | \"primary\";\n\nexport interface ConversationMessageUserProps {\n text: string;\n theme?: ConversationMessageUserTheme;\n isGenericChat?: boolean;\n}\n\nexport const ConversationMessageUser = (props: ConversationMessageUserProps) => {\n const { text, isGenericChat, theme = \"default\" } = props;\n\n // Pull any registered composer-widget fences out of the customer's own message text so they render as\n // read-only widgets above the prose, exactly like the assistant-side remark pipeline renders them in a reply —\n // but WITHOUT running that pipeline over user text (never treat the customer's own prose as Markdown).\n const { text: proseText, widgets } = useMemo(() => splitComposerWidgetFencesFromMessageText(text), [text]);\n\n const bubbleClass =\n theme === \"light\"\n ? styles.whiteBubble\n : theme === \"dark\"\n ? styles.darkBubble\n : theme === \"primary\"\n ? styles.primaryBubble\n : undefined;\n\n const textColor =\n theme === \"light\"\n ? \"var(--agents-fg)\"\n : theme === \"dark\"\n ? \"var(--agents-chat-user-bubble-text)\"\n : \"var(--agents-fg-on-dark)\";\n\n return (\n <div className={classNames(styles.message, bubbleClass)} data-testid=\"chat-message-user\">\n <ConversationMessageUserWidgets widgets={widgets} />\n <Text allowNewLines translate={false} color={textColor}>\n {proseText}\n </Text>\n </div>\n );\n};\n","import { useRef, useState, useCallback } from \"react\";\n\ninterface UseTypewriterOptions {\n // Characters per second to reveal\n speed?: number;\n // Minimum interval between UI commits (ms) to reduce re-renders\n commitIntervalMs?: number;\n}\n\nexport const useTypewriter = (options: UseTypewriterOptions = {}) => {\n const { speed = 200, commitIntervalMs = 50 } = options;\n\n const [displayedText, setDisplayedText] = useState<string>(\"\");\n const [isFinished, setIsFinished] = useState<boolean>(false);\n\n const fullTextRef = useRef<string>(\"\");\n const typeRAFRef = useRef<number | null>(null);\n const lastTimestampRef = useRef<number | null>(null);\n const renderedLengthRef = useRef<number>(0);\n const speedRef = useRef<number>(speed);\n const commitIntervalRef = useRef<number>(commitIntervalMs);\n const lastCommitTsRef = useRef<number>(0);\n\n const startTypewriter = useCallback(() => {\n if (typeRAFRef.current !== null) return; // already running\n if (renderedLengthRef.current >= fullTextRef.current.length) return; // nothing to render\n\n const animate = (timestamp: number) => {\n if (lastTimestampRef.current === null) {\n lastTimestampRef.current = timestamp;\n }\n\n const elapsedSeconds = (timestamp - lastTimestampRef.current) / 1000;\n lastTimestampRef.current = timestamp;\n\n const charactersToAdd = Math.max(1, Math.floor(elapsedSeconds * speedRef.current));\n const nextLength = Math.min(fullTextRef.current.length, renderedLengthRef.current + charactersToAdd);\n\n renderedLengthRef.current = nextLength;\n\n // Only commit to React state at most every commitIntervalMs or when finished\n if (\n timestamp - lastCommitTsRef.current >= commitIntervalRef.current ||\n nextLength === fullTextRef.current.length\n ) {\n const nextText = fullTextRef.current.slice(0, nextLength);\n setDisplayedText(nextText);\n lastCommitTsRef.current = timestamp;\n }\n\n if (nextLength < fullTextRef.current.length) {\n typeRAFRef.current = requestAnimationFrame(animate);\n } else {\n typeRAFRef.current = null;\n lastTimestampRef.current = null;\n setIsFinished(true);\n }\n };\n\n typeRAFRef.current = requestAnimationFrame(animate);\n }, []);\n\n const appendText = useCallback(\n (text: string) => {\n if (!text) return;\n fullTextRef.current += text;\n setIsFinished(false);\n startTypewriter();\n },\n [startTypewriter],\n );\n\n const stopTypewriter = useCallback(() => {\n if (typeRAFRef.current !== null) {\n cancelAnimationFrame(typeRAFRef.current);\n typeRAFRef.current = null;\n lastTimestampRef.current = null;\n }\n }, []);\n\n const finishTypewriter = useCallback(() => {\n stopTypewriter();\n setDisplayedText(fullTextRef.current);\n renderedLengthRef.current = fullTextRef.current.length;\n setIsFinished(true);\n }, [stopTypewriter]);\n\n return {\n displayedText,\n appendText,\n stopTypewriter,\n finishTypewriter,\n isFinished,\n };\n};\n","import { useEffect, useRef, useCallback } from \"react\";\n\ninterface UseStreamingTextOptions {\n eventSource?: EventSource;\n onTextChunk?: (chunk: string) => void;\n onError?: () => void;\n onDone?: () => void;\n}\n\nexport const useStreamingText = ({ eventSource, onTextChunk, onError, onDone }: UseStreamingTextOptions) => {\n const bufferRef = useRef<string>(\"\");\n const flushRAFRef = useRef<number | null>(null);\n\n const onTextChunkRef = useRef<typeof onTextChunk>(null);\n const onErrorRef = useRef<typeof onError>(null);\n const onDoneRef = useRef<typeof onDone>(null);\n onTextChunkRef.current = onTextChunk;\n onErrorRef.current = onError;\n onDoneRef.current = onDone;\n\n const flushBuffer = useCallback(() => {\n flushRAFRef.current = null;\n if (!bufferRef.current) return;\n\n const chunk = bufferRef.current;\n bufferRef.current = \"\";\n onTextChunkRef.current?.(chunk);\n }, []);\n\n const finish = useCallback(() => {\n if (flushRAFRef.current !== null) {\n cancelAnimationFrame(flushRAFRef.current);\n flushRAFRef.current = null;\n }\n flushBuffer();\n onDoneRef.current?.();\n }, [flushBuffer]);\n\n const handleMessage = useCallback(\n (event: MessageEvent) => {\n // OpenAI Chat Completions terminator\n if (event.data === \"[DONE]\") {\n finish();\n return;\n }\n\n try {\n const parsed = JSON.parse(event.data);\n\n // Chat Completions streaming delta\n const piece =\n parsed?.choices?.[0]?.delta?.content ??\n // fallback: support simple formats too\n (typeof parsed === \"string\" ? parsed : (parsed?.text ?? \"\"));\n\n // Some servers send a final frame with finish_reason\n const finishReason = parsed?.choices?.[0]?.finish_reason;\n\n if (piece) {\n bufferRef.current += piece;\n if (flushRAFRef.current === null) {\n flushRAFRef.current = requestAnimationFrame(flushBuffer);\n }\n }\n\n if (finishReason === \"stop\") {\n finish();\n }\n } catch (error) {\n console.error(\"Error parsing SSE message:\", error, event.data);\n onErrorRef.current?.();\n }\n },\n [flushBuffer, finish],\n );\n\n const handleError = useCallback(() => {\n onErrorRef.current?.();\n\n if (flushRAFRef.current !== null) {\n cancelAnimationFrame(flushRAFRef.current);\n flushRAFRef.current = null;\n }\n\n flushBuffer();\n }, [flushBuffer]);\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !eventSource) return;\n\n eventSource.addEventListener(\"message\", handleMessage);\n eventSource.addEventListener(\"error\", handleError);\n\n return () => {\n eventSource.removeEventListener(\"message\", handleMessage);\n eventSource.removeEventListener(\"error\", handleError);\n\n if (flushRAFRef.current !== null) {\n cancelAnimationFrame(flushRAFRef.current);\n flushRAFRef.current = null;\n }\n\n flushBuffer();\n };\n }, [eventSource, handleMessage, handleError, flushBuffer]);\n};\n","import { RefObject, useContext, useLayoutEffect } from \"react\";\nimport { WidgetWindowRootContext } from \"../../../../renderer/widget-windowing\";\n\n/**\n * Fade a freshly-delivered assistant reply's rendered block elements in, one after another, instead of\n * popping the whole wall of text in at once. The reveal operates on the DOM blocks the markdown renderer\n * already produces (the direct children of the `[data-markdown-blocks-root]` div that is containerRef's\n * DIRECT child — nested Markdown instances rendered by widgets stamp the same attribute deeper\n * down and must not be picked up) — the markdown string is never split, so code fences, tables, nested\n * lists, and widgets are handled uniformly.\n *\n * - Blocks in view on arrival (tested against the FULL viewport, edge to edge): a quick staggered fade-in\n * (STAGGER_STEP_MS apart, capped at STAGGER_CAP_MS; each block fades over the faster INITIAL_FADE_MS set\n * inline) — the visible screen fills with text down to the bottom edge fast, no hidden band at the bottom.\n * - Blocks below the fold: a single, calmer fade at the CSS default duration the FIRST time any pixel of\n * them enters the scroll root — never replayed, never reversed on scroll-up.\n * - VISIBILITY DEADLINE: whatever the observers did or did not see, every block still hidden\n * VISIBILITY_DEADLINE_MS after the reply arrived becomes plainly visible (no fade) and the observers are\n * dropped. Delivered text must never depend on a scroll event to become readable.\n *\n * Invariants:\n * - Opacity + transform only, never display/visibility/height — hidden blocks keep their final layout space\n * from the first frame, so the pin/reserve machinery in ConversationViewBody sees stable heights and Cmd+F /\n * selection / a11y still find the pending text.\n * - Only fresh static replies animate (the caller gates via `enabled`); streaming is excluded upstream.\n * - Once per message id: a re-render / windowing pass / navigate-away-and-back never replays it.\n * - A block can never be stuck hidden: hiding happens in the same synchronous useLayoutEffect that creates\n * the observer; the deadline and the cleanup both finalize every still-hidden block to visible. (A\n * \"-15% into the viewport\" trigger left the reply's closing question invisible while the pinned-question\n * policy never scrolled — the owner saw the question only after their next send. Hence zero margin AND the\n * deadline.)\n */\n\nconst STAGGER_STEP_MS = 90;\nconst STAGGER_CAP_MS = 720;\n// The initial screen-fill batch fades each block faster than the CSS default (900ms) so the first screen\n// populates snappily; blocks scrolled into view later keep the calmer default fade untouched.\nconst INITIAL_FADE_MS = 550;\n// Scroll-reveal trigger — below-the-fold blocks ONLY (the initial batch tests against the full viewport).\n// Zero margin: a block reveals the moment any pixel of it is inside the scroll root. (A negative bottom\n// margin — \"reveal once the top is 15% up into the viewport\" — left a block whose top sat in that bottom band\n// hidden for good when nothing scrolled; a percentage margin is even measured against the root's WIDTH.)\nconst SCROLL_REVEAL_ROOT_MARGIN = \"0px\";\n// Hard upper bound on hiding: after this, every still-hidden block is made visible without animation.\nexport const VISIBILITY_DEADLINE_MS = 2500;\n\n// Module-scoped so the guard survives unmount/remount of ConversationMessageWidgets (Redux re-render, widget\n// windowing, navigate-away-and-back within the session) — the same one-shot pattern as\n// reloadReopenMessageIdRef in ConversationMessageList.\nconst alreadyRevealedMessageIds = new Set<string | number>();\n\nexport const useFreshMessageBlockReveal = (options: {\n /**\n * The rendered-message container — ConversationMessageWidgets's existing renderedRef. Its DIRECT child must be\n * the reply's own `[data-markdown-blocks-root]` div (ConversationMarkdown renders exactly one Markdown,\n * whose context provider adds no DOM node); the hook queries only that direct child.\n */\n containerRef: RefObject<HTMLDivElement | null>;\n /** fresh && message != null — static fresh assistant replies only. */\n enabled: boolean;\n /** Once-only guard key; when null/undefined the reply animates but is not recorded. */\n messageId?: string | number | null;\n}): void => {\n const { containerRef, enabled, messageId } = options;\n const scrollRoot = useContext(WidgetWindowRootContext);\n\n useLayoutEffect(() => {\n if (!enabled) return;\n if (messageId != null && alreadyRevealedMessageIds.has(messageId)) return;\n if (typeof IntersectionObserver === \"undefined\") return;\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n // Markdown rendered outside the chat scroll container (drawer/canvas hosts render their own Markdown\n // instances) has no scroll root and must never inherit the reveal.\n if (scrollRoot == null) return;\n\n // Direct child ONLY (`:scope >`): the shared Markdown component stamps `data-markdown-blocks-root` on\n // every instance, and a widget inside the reply can render its own nested Markdown (e.g.\n // WebSearchAnswer). The reply's own blocks root is always renderedRef's direct child — ConversationMarkdown\n // renders a context provider (no DOM node) around exactly one Markdown — so a descendant query could\n // conflate roots, while the direct-child query can only match the outer one.\n const blocksRoot = containerRef.current?.querySelector(\":scope > [data-markdown-blocks-root]\");\n if (blocksRoot == null) return;\n\n const blocks = Array.from(blocksRoot.children) as HTMLElement[];\n if (blocks.length === 0) return;\n\n for (const block of blocks) block.dataset.blockReveal = \"hidden\";\n if (messageId != null) alreadyRevealedMessageIds.add(messageId);\n\n // The one idempotent end state: observers gone, every still-hidden block plainly visible (the hook's\n // attribute and inline overrides removed — assigning \"revealed\" would start another 900ms fade), blocks\n // already revealed keep their finished state. Reached by the deadline, by cleanup, or by an observer\n // setup failure; queued observer callbacks after it are no-ops.\n let finalized = false;\n let scrollRevealObserver: IntersectionObserver | null = null;\n let initialViewportObserver: IntersectionObserver | null = null;\n const finalize = () => {\n if (finalized) return;\n finalized = true;\n initialViewportObserver?.disconnect();\n scrollRevealObserver?.disconnect();\n for (const block of blocks) {\n if (block.dataset.blockReveal === \"hidden\") {\n delete block.dataset.blockReveal;\n block.style.animationDelay = \"\";\n block.style.animationDuration = \"\";\n }\n }\n };\n const deadline = window.setTimeout(finalize, VISIBILITY_DEADLINE_MS);\n\n // Below-the-fold blocks reveal the first time any pixel of them enters the scroll root — single fade, no delay.\n scrollRevealObserver = new IntersectionObserver(\n (entries, observer) => {\n if (finalized) return;\n for (const entry of entries) {\n if (!entry.isIntersecting) continue;\n (entry.target as HTMLElement).dataset.blockReveal = \"revealed\";\n observer.unobserve(entry.target);\n }\n },\n { root: scrollRoot, rootMargin: SCROLL_REVEAL_ROOT_MARGIN, threshold: 0 },\n );\n\n // The initial pass fires exactly once, right after observe(), with every block's intersection state\n // against the FULL viewport (no trigger margin): everything visible — under the pinned question, down\n // to the bottom edge — reveals staggered, so the first screen is completely filled with text. The\n // blocks below the fold are handed to scrollRevealObserver.\n initialViewportObserver = new IntersectionObserver(\n (entries, observer) => {\n if (finalized) return;\n let staggerIndex = 0;\n for (const entry of entries) {\n const block = entry.target as HTMLElement;\n if (entry.isIntersecting) {\n block.style.animationDelay = `${Math.min(staggerIndex * STAGGER_STEP_MS, STAGGER_CAP_MS)}ms`;\n block.style.animationDuration = `${INITIAL_FADE_MS}ms`;\n staggerIndex++;\n block.dataset.blockReveal = \"revealed\";\n } else {\n scrollRevealObserver?.observe(block);\n }\n }\n observer.disconnect();\n },\n { root: scrollRoot, threshold: 0 },\n );\n\n for (const block of blocks) initialViewportObserver.observe(block);\n\n return () => {\n window.clearTimeout(deadline);\n finalize();\n };\n }, [enabled, messageId, scrollRoot, containerRef]);\n};\n","import { Icon, Text } from \"../../../../provider/components\";\nimport type { ChatAssistantCategory, ChatAssistantIdentity } from \"../../../chat-assistant-identity\";\nimport React, { memo } from \"react\";\nimport styles from \"./ConversationMessageHeader.module.scss\";\n\nexport interface ConversationMessageHeaderProps {\n aiPromptExtensionCategory?: ChatAssistantCategory;\n aiPromptExtension?: ChatAssistantIdentity;\n}\n\nexport const ConversationMessageHeader = memo((props: ConversationMessageHeaderProps) => {\n const { aiPromptExtensionCategory, aiPromptExtension } = props;\n\n // The extension's icon class is an icon of the app's icon set.\n const iconName = aiPromptExtension?.iconClass ?? \"ai\";\n\n return (\n <div className={styles.responseHeader}>\n <span className={styles.iconContainer}>\n <Icon color=\"white\" hostIconName={iconName} />\n </span>\n <div>\n <Text role=\"help\" weight=\"bold\">\n {aiPromptExtension?.name ?? \"AI Extensions\"}\n </Text>\n {aiPromptExtensionCategory !== undefined ? (\n <Text role=\"caption\" replacements={{ productName: aiPromptExtensionCategory?.name }}>\n %productName% Assistant\n </Text>\n ) : null}\n {aiPromptExtensionCategory === undefined ? (\n <Text role=\"caption\">Assistant</Text>\n ) : null}\n </div>\n </div>\n );\n});\n","export async function copyRichToClipboard(plainText: string, html: string) {\n if (!plainText) {\n console.warn(\"No text to copy\");\n return;\n }\n\n if (!navigator.clipboard) {\n console.warn(\"Clipboard API not available\");\n return;\n }\n\n if (!(html && typeof ClipboardItem !== \"undefined\" && navigator.clipboard.write)) {\n console.warn(\"Clipboard does not support rich text, copying plain text only\");\n return navigator.clipboard.writeText(plainText);\n }\n try {\n const item = new ClipboardItem({\n \"text/plain\": new Blob([plainText], { type: \"text/plain\" }),\n \"text/html\": new Blob([html], { type: \"text/html\" }),\n });\n return navigator.clipboard.write([item]);\n } catch (error) {\n console.error(\"Failed to copy rich text to clipboard:\", error);\n return navigator.clipboard.writeText(plainText);\n }\n}\n","import { Button } from \"../../../provider/components\";\nimport type { ChatAssistantCategory, ChatAssistantIdentity } from \"../../chat-assistant-identity\";\nimport React, { memo, useCallback, useEffect, useRef, useState } from \"react\";\nimport styles from \"./ConversationMessageWidgets.module.scss\";\nimport { useTypewriter } from \"./hooks/useTypewriter\";\nimport { useStreamingText } from \"./hooks/useStreamingText\";\nimport { useFreshMessageBlockReveal } from \"./hooks/useFreshMessageBlockReveal\";\nimport { ConversationMessageHeader } from \"./ConversationMessageHeader/ConversationMessageHeader\";\nimport { ConversationMarkdown } from \"../../../renderer/ConversationMarkdown\";\nimport { copyRichToClipboard } from \"./copyRichToClipboard\";\nimport { normalizeServerMessageToMarkdown } from \"../../normalize-server-message-to-markdown\";\nimport { stripPreCallMessageMarker } from \"../../strip-pre-call-message-marker\";\nimport { useAgentsEnvironment } from \"../../../provider/AgentsProvider\";\n\nexport interface ConversationMessageWidgetsProps {\n aiPromptExtensionCategory?: ChatAssistantCategory;\n aiPromptExtension?: ChatAssistantIdentity;\n eventSource?: EventSource;\n message?: unknown;\n /**\n * The reply's widgets may be used (buttons, forms, hydration refresh): a live-delivered reply, and the newest reply\n * after a reload. Forwarded to every widget as `interactive` (+ the deprecated alias `fresh`).\n */\n interactive?: boolean;\n /**\n * The reply was delivered live right now — never after a reload. Forwarded to every widget as `justDelivered`, the\n * only gate for one-shot side effects (canvas open, draft creation), and the gate of the block reveal.\n */\n justDelivered?: boolean;\n hideWidgets?: boolean;\n /** Id of the host message: once-only guard of the block reveal, and the identity of a canvas opened from it. */\n messageId?: string | number | null;\n /**\n * Hide the assistant header (icon + name + \"Assistant\") and the copy button.\n * Used by a host, which renders bare assistant messages.\n */\n hideAssistantMeta?: boolean;\n /** Opt-in staggered fade-in of this fresh reply's markdown blocks (see useFreshMessageBlockReveal). Default off. */\n enableFreshMessageBlockReveal?: boolean;\n}\n\nexport const ConversationMessageWidgets = memo((props: ConversationMessageWidgetsProps) => {\n const {\n aiPromptExtensionCategory,\n aiPromptExtension,\n eventSource,\n message,\n interactive = false,\n justDelivered = false,\n hideWidgets = false,\n hideAssistantMeta = false,\n enableFreshMessageBlockReveal = false,\n messageId,\n } = props;\n const { MessageFooter } = useAgentsEnvironment();\n\n const [copied, setCopied] = useState(false);\n const timeoutRef = useRef<number | null>(null);\n const renderedRef = useRef<HTMLDivElement | null>(null);\n\n const { displayedText, appendText, finishTypewriter } = useTypewriter({\n speed: 200,\n commitIntervalMs: 100,\n });\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current != null) window.clearTimeout(timeoutRef.current);\n };\n }, []);\n\n // Only stream when no static message is provided\n useStreamingText({\n eventSource: message != null ? undefined : eventSource,\n onTextChunk: appendText,\n onError: finishTypewriter,\n onDone: finishTypewriter,\n });\n\n // If a message is provided, render it directly; otherwise render streamed text\n // Static message → normalize (already strips the {preCallMessage} sentinel); live stream → strip it here too.\n const textToRender =\n message != null ? normalizeServerMessageToMarkdown(message) : stripPreCallMessageMarker(displayedText);\n\n // (The former \"a fresh widget message closes the open canvas\" layout effect is gone: a canvas is superseded only\n // by another canvas being opened — and a pinned canvas survives a widget reply.)\n\n // Fade the just-delivered reply's rendered blocks in (staggered in view, once on scroll below the fold). Static\n // live-delivered assistant replies only — never after a reload, and the streaming path (message == null) renders\n // progressively already.\n useFreshMessageBlockReveal({\n containerRef: renderedRef,\n enabled: enableFreshMessageBlockReveal && justDelivered && message != null,\n messageId,\n });\n\n const onCopy = useCallback(async () => {\n const fallbackText = textToRender || \"\";\n const plain = renderedRef.current?.innerText?.trim() || fallbackText;\n const html = renderedRef.current?.innerHTML || \"\";\n\n await copyRichToClipboard(plain, html);\n\n setCopied(true);\n if (timeoutRef.current != null) window.clearTimeout(timeoutRef.current);\n timeoutRef.current = window.setTimeout(() => setCopied(false), 2000);\n }, [textToRender]);\n\n return (\n <div className={styles.responseContainer}>\n {!hideAssistantMeta ? (\n <ConversationMessageHeader\n aiPromptExtensionCategory={aiPromptExtensionCategory}\n aiPromptExtension={aiPromptExtension}\n />\n ) : null}\n <div className={styles.responseMessage}>\n <div ref={renderedRef}>\n {/* TODO: move the markdown component into the component library */}\n <ConversationMarkdown\n markdown={textToRender}\n interactive={interactive}\n justDelivered={justDelivered}\n messageId={messageId ?? undefined}\n hideWidgets={hideWidgets}\n />\n </div>\n\n {!hideAssistantMeta ? (\n <Button\n variant=\"muted\"\n icon={copied ? \"check\" : \"duplicate\"}\n onClick={onCopy}\n size=\"small\"\n />\n ) : null}\n </div>\n\n {/* The app's footer under the message (for example an admin-only trace footer that gates itself). */}\n {MessageFooter ? <MessageFooter message={message} /> : null}\n </div>\n );\n});\n","import { conversationWidgetRegistry, getConversationWidgetRegistryRevision } from \"./conversationWidgetRegistry\";\nimport { parseWidgetJsonObject } from \"./widget-fence-json\";\nimport { WIDGET_FENCE_LANG } from \"./widget-fence-syntax\";\nimport { resolveCanonicalWidgetName } from \"./widget-name-resolver\";\n\n/**\n * Does a message's markdown carry a widget? The conversation asks this to reset the drawer when a fresh widget message\n * arrives, and to decide whether a reloaded last message should re-open its canvas (see ConversationMessageWidgets).\n *\n * Lives in its own file rather than in `conversationWidgetRegistry.ts` so the registry stays import-free: a registry →\n * resolver import back would be a cycle whose evaluation order decides whether the app boots. The registry is read\n * lazily for the same reason (a widget can import AgentChat, which imports this file).\n */\n\nlet widgetNames: ReadonlySet<string> | undefined;\nlet widgetNamesRevision = -1;\n\n// Every registered widget name, canonical. Read lazily — see the note above and in widget-name-resolver.ts.\nconst getWidgetNames = (): ReadonlySet<string> => {\n if (widgetNames === undefined || widgetNamesRevision !== getConversationWidgetRegistryRevision()) {\n widgetNames = new Set(conversationWidgetRegistry.widgets.map((widget) => widget.name));\n widgetNamesRevision = getConversationWidgetRegistryRevision();\n }\n return widgetNames;\n};\n\n// Matches the widget name at the start of a line in the stored `::Name` form (and the `:::Name`\n// form old messages may carry); a single-colon inline node doesn't match (min two colons).\nconst WIDGET_NODE_RE = /^[ \\t]*:{2,3}([A-Za-z][\\w]*)/gm;\n\n// Fenced code blocks must not count as widgets — remark-directive never parses\n// its nodes inside them. The open-fence variant drops a still-unterminated\n// fence so streamed partial markdown doesn't match either.\nconst CLOSED_CODE_FENCE_RE = /```[\\s\\S]*?```/g;\nconst OPEN_CODE_FENCE_RE = /```[\\s\\S]*$/;\n\n// A closed ```widget fence — the agent's widget syntax (see remarkWidgetFencePlugin). Only a CLOSED fence counts:\n// a still-streaming open fence has no terminating ``` yet and must not match prematurely. Built from the shared\n// lang constant so \"``` widget\" (a space before the lang — remark accepts it) resolves the same as everywhere else.\nconst CLOSED_WIDGET_FENCE_RE = new RegExp(\"```[ \\\\t]*\" + WIDGET_FENCE_LANG + \"\\\\s*([\\\\s\\\\S]*?)```\", \"g\");\n\n/**\n * True when the markdown contains at least one widget — either a `::Name` / `:::Name` node OR a closed ```widget\n * fence whose `type` resolves to a registered widget. BOTH forms go through `resolveCanonicalWidgetName`, so a stored\n * message written as `::ExampleWidgetDirective{…}` or `{\"type\":\"ExampleWidgetDirective\"}` counts exactly like the\n * canonical `::ExampleWidget` — the drawer resets for old conversations too.\n */\nexport const markdownContainsWidget = (markdown: string): boolean => widgetNamesInMarkdown(markdown).length > 0;\n\n/**\n * The CANONICAL names of every registered widget the markdown carries, in order of appearance, one entry per\n * occurrence (both syntaxes, legacy `…Directive` spellings resolved). The message list reads it to tell a superseded\n * widget from the live one: a later message carrying a widget of the SAME name retires the earlier one's controls.\n */\nexport const widgetNamesInMarkdown = (markdown: string): string[] => {\n if (!markdown) {\n return [];\n }\n const names: string[] = [];\n for (const match of markdown.matchAll(CLOSED_WIDGET_FENCE_RE)) {\n // malformed fence body → not a renderable widget; a surplus trailing `}` / `]` is read as the backend reads it\n const parsed = parseWidgetJsonObject(match[1]);\n const type = parsed?.type;\n const canonicalName = typeof type === \"string\" ? resolveCanonicalWidgetName(type, getWidgetNames()) : null;\n if (canonicalName) {\n names.push(canonicalName);\n }\n }\n const withoutCode = markdown.replace(CLOSED_CODE_FENCE_RE, \"\").replace(OPEN_CODE_FENCE_RE, \"\");\n for (const match of withoutCode.matchAll(WIDGET_NODE_RE)) {\n const canonicalName = resolveCanonicalWidgetName(match[1], getWidgetNames());\n if (canonicalName) {\n names.push(canonicalName);\n }\n }\n return names;\n};\n","import { getFurtherWidgetClickSignalSources } from \"../provider/environment\";\n\n/**\n * The `signalSource`s a widget CLICK posts into the conversation (`injectSignal`): the generic widget click and the\n * dedicated source the former owner-action consent widget sent (no component posts it any more; stored rows keep it),\n * plus the further sources an app's own widgets post (`AgentsEnvironment.furtherWidgetClickSignalSources`, e.g. a form\n * submit that starts its own owner turn). The message list reads them to tell an answered widget from an open one after\n * a reload — a widget the owner already clicked must never come back interactive (a consent widget re-armed is a second\n * confirmation for a destructive tool). Add a source here when a widget of the package starts posting a new one.\n */\nexport const FRONTEND_WIDGET_CLICK_SIGNAL_SOURCE = \"FRONTEND_WIDGET_CLICK\";\nexport const OWNER_ACTION_CONFIRM_SIGNAL_SOURCE = \"OWNER_ACTION_CONFIRM\";\n\n/** The package's own widget click sources (the backend's sources of the package that carry an owner statement). */\nexport const PACKAGE_WIDGET_CLICK_SIGNAL_SOURCES: readonly string[] = [\n FRONTEND_WIDGET_CLICK_SIGNAL_SOURCE,\n OWNER_ACTION_CONFIRM_SIGNAL_SOURCE,\n];\n\n/** Every widget click source: the package's own and the app's further ones. */\nexport const getWidgetClickSignalSources = (): readonly string[] => [\n ...PACKAGE_WIDGET_CLICK_SIGNAL_SOURCES,\n ...getFurtherWidgetClickSignalSources(),\n];\n\nexport const isWidgetClickSignalSource = (signalSource: string | undefined | null): boolean =>\n signalSource != null && getWidgetClickSignalSources().includes(signalSource);\n","import { normalizeServerMessageToMarkdown } from \"./normalize-server-message-to-markdown\";\n\n/**\n * Whether the whole markdown is one bare JSON object with a string `type`. Normalization already fenced every\n * registered widget type, so what is left is a type no widget renders — model noise like `{\"type\":\"no-op\"}`\n * (GPT-6 Luna), never owner-facing text.\n */\nconst isBareUnknownTypeJson = (markdown: string): boolean => {\n if (!markdown.startsWith(\"{\")) return false;\n let parsed: unknown;\n try {\n parsed = JSON.parse(markdown);\n } catch {\n return false;\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) return false;\n return typeof (parsed as Record<string, unknown>).type === \"string\";\n};\n\n/**\n * True when an assistant message's stored content yields markdown that actually renders something.\n *\n * Not every ASSISTANT row carries text. An agentic run persists one ASSISTANT/TEXT turn per step, and a step\n * that only called a tool stores `message: \"\"` (13 such turns in a 30-message run); a payload whose parts are\n * all blank text likewise normalizes to \"\". Such a row would render an empty markdown container that still\n * consumed a full `$x3` gap in the message list, so ConversationMessageList drops it instead — the per-turn agentic\n * trace those rows carry is the admin console's job (AgentConversationInspector), not the customer chat's.\n *\n * The null/blank short-circuit also keeps normalizeServerMessageToMarkdown's \"unrecognized message shape\"\n * console warning from firing on every one of those rows.\n */\nexport const assistantMessageHasRenderableMarkdown = (message: unknown): boolean => {\n if (message == null) return false;\n if (typeof message === \"string\" && message.trim() === \"\") return false;\n const markdown = normalizeServerMessageToMarkdown(message).trim();\n return markdown !== \"\" && !isBareUnknownTypeJson(markdown);\n};\n","import type { ChatMessage } from \"./chat-message\";\n\n/** The phone call's started / ended markers — the call marker chip, structural context, never owed work. */\nexport const PHONE_CALL_LIFECYCLE_SIGNAL_SOURCES: readonly string[] = [\"PHONE_CALL_STARTED\", \"PHONE_CALL_ENDED\"];\n/** The loop dropped a repeat of an already delivered text — bookkeeping behind the agent's answer, never owed work. */\nconst REPEATED_TEXT_SUPPRESSED_SIGNAL_SOURCE = \"REPEATED_TEXT_SUPPRESSED\";\n/** The platform wrote a widget on its own (e.g. a follow-up question under the final text) — a notice, never owed work. */\nconst PLATFORM_WIDGET_EMITTED_SIGNAL_SOURCE = \"PLATFORM_WIDGET_EMITTED\";\n/** The frontend's render report of a message's widgets — context for the next turn, never owed work. */\nconst FRONTEND_WIDGET_RENDER_REPORT_SIGNAL_SOURCE = \"FRONTEND_WIDGET_RENDER_REPORT\";\n/**\n * The SIGNAL rows that owe no work — the backend's `owesWork() === false` on a SIGNAL row, pinned by\n * `signal-source-parity.json` (`_signal-source-parity.spec.ts`).\n */\nexport const SIGNAL_SOURCES_THAT_OWE_NO_WORK: readonly string[] = [\n ...PHONE_CALL_LIFECYCLE_SIGNAL_SOURCES,\n REPEATED_TEXT_SUPPRESSED_SIGNAL_SOURCE,\n PLATFORM_WIDGET_EMITTED_SIGNAL_SOURCE,\n FRONTEND_WIDGET_RENDER_REPORT_SIGNAL_SOURCE,\n];\n\n/**\n * Whether the newest WIRE message is the customer's own, still unanswered — the backend's `hasUnansweredInboundMessage`\n * seen from the frontend, walked the same way: ERROR / CHECKPOINT rows are bookkeeping (never rendered, never an\n * answer); a TOOL_MESSAGE (a tool's own widget to the customer, stored role ASSISTANT) is NOT the agent's answer — a\n * customer question followed only by a tool's status widget stays owed; the phone-call lifecycle chips are structural\n * context, never owed work, and so is the platform's notice that it wrote a widget on its own; any other trailing SIGNAL (a widget click, a memory-conflict clarify) IS owed work, exactly\n * like a user message. Only then does a failed run have something to RETRY — the persisted\n * message is re-driven server-side instead of asking the customer to send it again. A walk that diverges from the\n * backend's shows the error card without the button while the backend would accept the retry — keep the two in step.\n *\n * The slice keeps `messages` ordered by `indexInConversation`; the sort here is belt-and-suspenders for a caller that\n * hands in an unordered page, not a fix for an upstream ordering defect.\n */\nexport const lastWireMessageIsUnansweredUserMessage = (messages: ChatMessage[] | null): boolean => {\n if (!messages?.length) {\n return false;\n }\n const sorted = [...messages].sort((a, b) => (a.indexInConversation ?? 0) - (b.indexInConversation ?? 0));\n for (let position = sorted.length - 1; position >= 0; position--) {\n const message = sorted[position];\n if (\n message.messageType === \"ERROR\" ||\n message.messageType === \"CHECKPOINT\" ||\n message.messageType === \"TOOL_MESSAGE\"\n ) {\n continue;\n }\n if (message.messageType === \"SIGNAL\") {\n if (SIGNAL_SOURCES_THAT_OWE_NO_WORK.includes(message.signalSource ?? \"\")) {\n continue;\n }\n return true;\n }\n return message.authorType === \"USER\";\n }\n return false;\n};\n","import { Button, Icon, Text } from \"../../../../provider/components\";\nimport type { ChatAssistantCategory, ChatAssistantIdentity } from \"../../../chat-assistant-identity\";\nimport React, { memo, useMemo } from \"react\";\nimport styles from \"./ConversationMessageSystemHeader.module.scss\";\n\nexport interface ConversationMessageSystemHeaderProps {\n aiPromptExtensionCategory?: ChatAssistantCategory;\n aiPromptExtension?: ChatAssistantIdentity;\n onExpandContentClick?: () => void;\n isContentExpanded?: boolean;\n}\n\nexport const ConversationMessageSystemHeader = memo((props: ConversationMessageSystemHeaderProps) => {\n const { aiPromptExtensionCategory, aiPromptExtension, isContentExpanded, onExpandContentClick } = props;\n\n return (\n <div className={styles.responseHeaderLayout}>\n <div className={styles.responseHeader}>\n <span className={styles.iconContainer}>\n <Icon color=\"white\" name=\"wizard\" />\n </span>\n <div>\n <Text role=\"help\" weight=\"bold\">\n {aiPromptExtension?.name ?? \"AI Extensions\"}\n </Text>\n {aiPromptExtensionCategory !== undefined ? (\n <Text role=\"caption\">{aiPromptExtensionCategory?.name} System</Text>\n ) : null}\n {aiPromptExtensionCategory === undefined ? (\n <Text role=\"caption\">System</Text>\n ) : null}\n </div>\n </div>\n <div className={styles.responseHeaderAction}>\n <Button\n variant=\"muted\"\n icon={isContentExpanded ? \"caretUp\" : \"caretDown\"}\n onClick={onExpandContentClick}\n />\n </div>\n </div>\n );\n});\n","import React, { memo, useState } from \"react\";\nimport type { ChatAssistantCategory } from \"../../chat-assistant-identity\";\nimport styles from \"./ConversationMessageSystem.module.scss\";\nimport { Markdown } from \"../../../provider/components\";\nimport { ConversationMessageSystemHeader } from \"./ConversationMessageSystemHeader/ConversationMessageSystemHeader\";\n\nexport interface ConversationMessageSystemProps {\n aiPromptExtensionCategory?: ChatAssistantCategory;\n aiPromptExtension?: ChatAssistantCategory;\n message: string;\n}\n\nexport const ConversationMessageSystem = memo((props: ConversationMessageSystemProps) => {\n const { aiPromptExtensionCategory, aiPromptExtension, message } = props;\n\n const [isContentExpanded, setIsContentExpanded] = useState(false);\n\n return (\n <div className={styles.responseContainer}>\n <ConversationMessageSystemHeader\n aiPromptExtensionCategory={aiPromptExtensionCategory}\n aiPromptExtension={aiPromptExtension}\n onExpandContentClick={() => setIsContentExpanded((v) => !v)}\n isContentExpanded={isContentExpanded}\n />\n\n <div style={{ display: isContentExpanded ? undefined : \"none\" }}>\n <div className={styles.responseMessage}>\n <Markdown markdown={message} />\n </div>\n </div>\n </div>\n );\n});\n","import styles from \"./VoiceBarsIcon.module.scss\";\n\nexport interface VoiceBarsIconProps {\n /** Edge length in px; the bars take the current text colour. */\n size?: number;\n}\n\n/**\n * Five sound bars, the tallest in the middle — the mark of an in-app voice call with the agent.\n */\nexport const VoiceBarsIcon = (props: VoiceBarsIconProps) => {\n const { size = 18 } = props;\n return (\n <svg className={styles.icon} width={size} height={size} viewBox=\"0 0 24 24\" aria-hidden focusable=\"false\">\n <path d=\"M3.5 10.5v3M7.75 8v8M12 4.5v15M16.25 8v8M20.5 10.5v3\" />\n </svg>\n );\n};\n","import { Icon, Text } from \"../../../provider/components\";\nimport { getAgentDisplayName } from \"../../../provider/environment\";\nimport React, { memo } from \"react\";\nimport { VoiceBarsIcon } from \"./VoiceBarsIcon/VoiceBarsIcon\";\nimport styles from \"./ConversationCallMarkerChip.module.scss\";\n\nexport interface ConversationCallMarkerChipProps {\n /** PHONE_CALL_STARTED | PHONE_CALL_ENDED — the lifecycle signal's source. */\n signalSource: string;\n /** The signal's model-visible text — carries the duration as `NNNs` and the end reason as `reason <value>`. */\n signalText: string;\n}\n\n/** `123` → `2:03 min` for the chip; sub-minute stays as seconds. */\nconst formatCallDuration = (durationSeconds: number): string => {\n if (durationSeconds < 60) return `${durationSeconds}s`;\n const minutes = Math.floor(durationSeconds / 60);\n const seconds = durationSeconds % 60;\n return `${minutes}:${String(seconds).padStart(2, \"0\")} min`;\n};\n\ntype CallMarkerState = \"started\" | \"ended\" | \"declined\" | \"missed\" | \"notConnected\";\n\nconst MARKER_LABELS: Readonly<Record<CallMarkerState, { inApp: string; phoneLine: string }>> = {\n started: { inApp: \"Voice chat started\", phoneLine: \"Phone call started\" },\n ended: { inApp: \"Voice chat ended\", phoneLine: \"Phone call ended\" },\n declined: { inApp: \"You declined a voice chat with %agentName%\", phoneLine: \"Phone call declined\" },\n missed: { inApp: \"You missed a voice chat with %agentName%\", phoneLine: \"Phone call not answered\" },\n notConnected: { inApp: \"Voice chat did not connect\", phoneLine: \"Phone call not answered\" },\n};\n\n/** The agent's ring the owner turned down (DECLINED) or let run out (EXPIRED, NOT_ANSWERED) is its own mark in the chat. */\nconst callMarkerStateOf = (isCallEnd: boolean, endReason: string | undefined): CallMarkerState => {\n if (!isCallEnd) return \"started\";\n if (endReason === \"DECLINED\") return \"declined\";\n if (endReason === \"EXPIRED\" || endReason === \"NOT_ANSWERED\") return \"missed\";\n if (endReason === \"FAILED\") return \"notConnected\";\n return \"ended\";\n};\n\n/**\n * Centered marker for a call's lifecycle inside the chat timeline: started, ended (with its duration), declined, missed\n * or not connected; an in-app call is a voice chat with the agent (its voice bars), a phone call keeps the phone. Rendered\n * from the otherwise user-invisible PHONE_CALL_* SIGNAL messages — the label derives from the signal source + the small text contract of AgentVoiceCall::buildLifecycleSignalText (`In-app call …` vs\n * `Phone call …`, duration `NNNs`, reason `reason <value>`); an unparsable text degrades to the plain label.\n */\nexport const ConversationCallMarkerChip = memo(({ signalSource, signalText }: ConversationCallMarkerChipProps) => {\n const isCallEnd = signalSource === \"PHONE_CALL_ENDED\";\n const isInAppCall = signalText.startsWith(\"In-app call\");\n const endReason = /reason ([A-Za-z_-]+)/.exec(signalText)?.[1];\n const durationSeconds = /(\\d+)s\\b/.exec(signalText)?.[1];\n const markerState = callMarkerStateOf(isCallEnd, endReason);\n const markerLabel = isInAppCall ? MARKER_LABELS[markerState].inApp : MARKER_LABELS[markerState].phoneLine;\n\n return (\n <div className={styles.markerRow} data-testid=\"conversation-call-marker\">\n <span className={styles.markerChip} data-state={markerState}>\n <span className={styles.markerIcon}>\n {isInAppCall ? <VoiceBarsIcon size={16} /> : <Icon name=\"phone\" />}\n </span>\n <Text role=\"caption\" replacements={{ agentName: getAgentDisplayName() }}>\n {markerLabel}\n </Text>\n {markerState === \"ended\" && durationSeconds != null && (\n <span className={styles.markerDuration}>\n <Text role=\"caption\" translate={false}>\n {formatCallDuration(Number(durationSeconds))}\n </Text>\n </span>\n )}\n </span>\n </div>\n );\n});\n","import { Button, Text } from \"../../../provider/components\";\nimport React, { useState } from \"react\";\nimport styles from \"./ConversationTaskInstructionDisclosure.module.scss\";\n\nexport interface ConversationTaskInstructionDisclosureProps {\n /** The machine-composed seed instruction of a schedule-spawned run — the full text revealed on expand. */\n instructionText: string;\n}\n\n/**\n * Replaces the owner bubble for the FIRST user message of a SCHEDULE-spawned run conversation (UX overhaul W4\n * item 19). That message is the machine-composed task instruction (the schedule's task text + the previous run's\n * outcome line, see AIConversationSchedule::composeSeedUserMessageText) — NOT something the owner typed, so an\n * owner bubble would misattribute it as the owner's own words. Collapsed by default to a muted\n * \"Show task instruction\" disclosure; expanding reveals the full instruction text for inspection.\n */\nexport const ConversationTaskInstructionDisclosure = ({\n instructionText,\n}: ConversationTaskInstructionDisclosureProps) => {\n const [expanded, setExpanded] = useState(false);\n\n return (\n <div className={styles.disclosure}>\n {expanded ? (\n <div className={styles.instructionPanel}>\n <Text role=\"caption\" color=\"var(--agents-fg-light)\" allowNewLines translate={false}>\n {instructionText}\n </Text>\n </div>\n ) : null}\n <Button variant=\"muted\" onClick={() => setExpanded((wasExpanded) => !wasExpanded)}>\n {expanded ? \"Hide task instruction\" : \"Show task instruction\"}\n </Button>\n </div>\n );\n};\n","import { Text } from \"../../../../provider/components\";\nimport type { ChatAssistantCategory, ChatAssistantIdentity } from \"../../../chat-assistant-identity\";\nimport React, { memo, useState, useEffect } from \"react\";\nimport styles from \"./ConversationMessageThinking.module.scss\";\nimport { ConversationMessageHeader } from \"../ConversationMessageHeader/ConversationMessageHeader\";\n\nconst loadingStateMessages = [\n \"Gathering business data…\",\n \"Planning what to do…\",\n \"Working on it…\",\n \"Clarifying response…\",\n \"Almost ready…\",\n \"Something new came up…\",\n];\n\nexport interface ConversationMessageThinkingProps {\n aiPromptExtensionCategory?: ChatAssistantCategory;\n aiPromptExtension?: ChatAssistantIdentity;\n}\n\nexport const ConversationMessageThinking = memo((props: ConversationMessageThinkingProps) => {\n const { aiPromptExtensionCategory, aiPromptExtension } = props;\n const [currentIndex, setCurrentIndex] = useState(0);\n\n useEffect(() => {\n const interval = setInterval(() => {\n setCurrentIndex((prevIndex) => (prevIndex + 1) % loadingStateMessages.length);\n }, 2500);\n\n return () => clearInterval(interval);\n }, []);\n\n return (\n <div className={styles.responseContainer}>\n <ConversationMessageHeader\n aiPromptExtensionCategory={aiPromptExtensionCategory}\n aiPromptExtension={aiPromptExtension}\n />\n\n <div className={styles.thinkingMessage}>\n <div className={styles.thinkingDots}>\n <span />\n <span />\n <span />\n </div>\n <Text>{loadingStateMessages[currentIndex]}</Text>\n </div>\n </div>\n );\n});\n","import { Button, Icon, Text } from \"../../provider/components\";\nimport React, { useCallback, useMemo, useState } from \"react\";\nimport styles from \"./ConversationErrorCard.module.scss\";\nimport { ConversationErrorType } from \"../store/conversation-error-type\";\nimport { useAgentsBudgetLimit } from \"../../provider/AgentsProvider\";\n\n/** The first day of next month as `DD.MM.YYYY` — the day a monthly budget resets. */\nconst formatFirstDayOfNextMonth = (now: Date): string => {\n const firstDayOfNextMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1);\n const day = String(firstDayOfNextMonth.getDate()).padStart(2, \"0\");\n const month = String(firstDayOfNextMonth.getMonth() + 1).padStart(2, \"0\");\n return `${day}.${month}.${firstDayOfNextMonth.getFullYear()}`;\n};\n\nexport interface ConversationErrorCardProps {\n errorType: ConversationErrorType;\n /** Retry-After seconds of the 429 — only meaningful for RATE_LIMIT; picks the \"try again in …\" wording. */\n rateLimitRetryAfterSeconds?: number | null;\n /**\n * Retry action for a generic run failure whose customer message is persisted but unanswered: the HOST decides eligibility and binds the call to ITS chat slice (the AI-extensions chat and the agent\n * widget mount this card on separate Redux partitions), so the card only renders the action when it is given one.\n * Resolves when the retry was accepted; rejects when there is nothing to retry — the card then keeps showing.\n */\n onRetry?: () => Promise<void>;\n}\n\nexport const ConversationErrorCard = (props: ConversationErrorCardProps) => {\n const { errorType, rateLimitRetryAfterSeconds, onRetry } = props;\n const [retrying, setRetrying] = useState(false);\n // The app's budget: whether an upgrade lifts the limit, and the fair-use wording (resellers that present the AI\n // budget as a fair-use allowance).\n const budgetLimit = useAgentsBudgetLimit();\n const isUpSellPossible = budgetLimit.canUpgrade;\n const showFairUsageMessage = budgetLimit.showsFairUseWording;\n\n const canRetry = errorType === ConversationErrorType.AGENT_ERROR && onRetry !== undefined;\n\n const handleRetry = useCallback(async () => {\n if (!onRetry || retrying) {\n return;\n }\n setRetrying(true);\n try {\n await onRetry();\n // Accepted: the host cleared the card / flipped the thinking indicator; nothing left to do here.\n } catch (error) {\n // Rejected (nothing to retry / conversation gone / network): keep the card, re-enable the action.\n console.warn(\"Conversation retry rejected\", error);\n setRetrying(false);\n }\n }, [onRetry, retrying]);\n\n const { icon, title, description, replacements } = useMemo(() => {\n let title = \"Network error\";\n let description = \"We’re sorry, something went wrong. Please refresh the page or try again later.\";\n let replacements: Record<string, string> | undefined = undefined;\n if (errorType === ConversationErrorType.AI_BUDGET) {\n title = \"Limit reached\";\n if (showFairUsageMessage) {\n description = isUpSellPossible\n ? \"You've reached your fair use limit for this month. This limit will reset on %resetDate%, or you can upgrade now to continue without interruption.\"\n : \"You've reached your fair use limit for this month. This limit will reset on %resetDate%.\";\n replacements = { resetDate: formatFirstDayOfNextMonth(new Date()) };\n } else {\n description = isUpSellPossible\n ? \"You've reached your maximum monthly usage. This limit resets on %limitResetDate%, or you can upgrade now to continue without interruption.\"\n : \"You've reached your maximum monthly usage. This limit resets on %limitResetDate%.\";\n replacements = { limitResetDate: formatFirstDayOfNextMonth(new Date()) };\n }\n }\n if (errorType === ConversationErrorType.RATE_LIMIT) {\n title = \"Usage limit reached\";\n if (!rateLimitRetryAfterSeconds || rateLimitRetryAfterSeconds <= 90) {\n description = \"You've sent a lot of AI requests in a short time. Please wait a minute and try again.\";\n } else if (rateLimitRetryAfterSeconds <= 3600) {\n description =\n \"You've reached your AI usage limit for this hour. Please try again in about %retryAfterMinutes% minutes.\";\n replacements = { retryAfterMinutes: `${Math.ceil(rateLimitRetryAfterSeconds / 60)}` };\n } else {\n description = \"You've reached your AI usage limit for today. Please try again later.\";\n }\n }\n if (errorType === ConversationErrorType.AGENT_ERROR) {\n title = \"Something went wrong\";\n if (canRetry) {\n description = \"We’re sorry, something went wrong. Your message is saved — retry to have it answered.\";\n }\n }\n if (errorType === ConversationErrorType.CONVERSATION_CLOSED) {\n title = \"Conversation closed\";\n description = \"This conversation is completed and closed. Please start a new conversation.\";\n }\n return {\n icon: \"warning\" as const,\n title,\n description,\n replacements,\n };\n }, [errorType, isUpSellPossible, rateLimitRetryAfterSeconds, showFairUsageMessage, canRetry]);\n\n return (\n <div className={styles.errorMessageContainer}>\n <div>\n <div className={styles.errorMessageTitle}>\n <Icon name={icon} />\n <Text weight=\"bold\">{title}</Text>\n </div>\n <Text replacements={replacements}>{description}</Text>\n </div>\n {errorType === ConversationErrorType.AI_BUDGET && isUpSellPossible ? (\n <Button size=\"extraLarge\" onClick={budgetLimit.openUpgrade} variant=\"shimmer\">\n Upgrade Plan\n </Button>\n ) : null}\n {canRetry ? (\n <Button size=\"extraLarge\" onClick={handleRetry} variant=\"shimmer\" disabled={retrying}>\n Retry\n </Button>\n ) : null}\n </div>\n );\n};\n","import { classNames } from \"../../provider/class-names\";\nimport type { ChatAssistantCategory, ChatAssistantIdentity } from \"../chat-assistant-identity\";\nimport React, { Fragment, useMemo, useRef } from \"react\";\nimport {\n ConversationMessageUser,\n ConversationMessageUserTheme,\n} from \"../ConversationMessage/ConversationMessageUser/ConversationMessageUser\";\nimport { ConversationMessageWidgets } from \"../ConversationMessage/ConversationMessageWidgets/ConversationMessageWidgets\";\nimport { widgetNamesInMarkdown } from \"../../renderer/markdown-contains-widget\";\nimport { isWidgetClickSignalSource } from \"../../renderer/widget-click-signal-sources\";\nimport { normalizeServerMessageToMarkdown } from \"../normalize-server-message-to-markdown\";\nimport { assistantMessageHasRenderableMarkdown } from \"../assistant-message-has-renderable-markdown\";\nimport { lastWireMessageIsUnansweredUserMessage } from \"../last-wire-message-is-unanswered-user-message\";\nimport { ConversationMessageSystem } from \"../ConversationMessage/ConversationMessageSystem/ConversationMessageSystem\";\nimport { ConversationCallMarkerChip } from \"../ConversationMessage/ConversationCallMarkerChip/ConversationCallMarkerChip\";\nimport { ConversationTaskInstructionDisclosure } from \"../ConversationMessage/ConversationTaskInstructionDisclosure/ConversationTaskInstructionDisclosure\";\nimport { ConversationMessageThinking } from \"../ConversationMessage/ConversationMessageWidgets/ConversationMessageThinking/ConversationMessageThinking\";\nimport { ConversationErrorCard } from \"../ConversationErrorCard/ConversationErrorCard\";\nimport { ConversationErrorType } from \"../store/conversation-error-type\";\nimport type { ChatMessage } from \"../chat-message\";\nimport { PHONE_CALL_LIFECYCLE_SIGNAL_SOURCES } from \"../last-wire-message-is-unanswered-user-message\";\nimport styles from \"../Conversation.module.scss\";\nimport { Ref, RefObject } from \"react\";\n\ninterface ConversationMessageListProps {\n className?: string;\n messages: ChatMessage[] | null;\n currentCategory?: ChatAssistantCategory;\n aiPromptExtension?: ChatAssistantIdentity;\n isDebugMode: boolean;\n aiThinking: boolean;\n /**\n * Custom node mounted at the tail of the message list instead of the default\n * (thinking-only) ConversationMessageThinking. Rendered permanently — used by\n * A host to show the animated agent face, which idles between turns.\n */\n thinkingIndicator?: React.ReactNode;\n /**\n * Hide the assistant header (icon + name + \"Assistant\") and the copy button\n * on assistant messages. Used by a host, which renders bare messages.\n */\n hideAssistantMeta?: boolean;\n /**\n * Theme for user-message bubbles. When omitted, falls back to the legacy\n * behavior: \"light\" for generic (no-agent) chat, \"default\" otherwise. A host\n * forces \"dark\".\n */\n userMessageTheme?: ConversationMessageUserTheme;\n error: ConversationErrorType | null;\n /** Retry-After seconds of a 429 quota rejection — forwarded to the error card for the \"try again in …\" wording. */\n rateLimitRetryAfterSeconds?: number | null;\n /**\n * Host-bound retry of a FAILED run. Offered on the error card only while the newest wire\n * message is the customer's own, still unanswered — the persisted message is re-driven server-side.\n */\n onRetryFailedRun?: () => Promise<void>;\n messagesBottomRef: RefObject<HTMLDivElement | null>;\n messagesListRef?: Ref<HTMLDivElement | null>;\n /**\n * Opt-in staggered fade-in of a fresh reply's markdown blocks — see useFreshMessageBlockReveal;\n * forwarded to ConversationMessageWidgets. Default off.\n */\n enableFreshMessageBlockReveal?: boolean;\n /**\n * Id of the ONE user message to render as a collapsed task-instruction disclosure instead of an owner bubble:\n * the machine-composed seed instruction of a SCHEDULE-spawned run (UX overhaul W4 item 19). The host resolves\n * the id (AgentChat: first USER message once the history is loaded to its start); undefined renders every\n * user message normally.\n */\n taskInstructionMessageId?: number;\n /**\n * Host content rendered right after one message of the thread — also after a message the list itself does not show\n * (a work signal that started an agent turn). The agent chat puts each turn's activity card under the message that\n * started the turn. The content is a row of the list's flex column, so the list's gap stands above it; the list\n * publishes that gap as `--chat-messages-gap`. Undefined (every other host) renders the rows exactly as before.\n */\n renderAfterMessage?: (message: ChatMessage) => React.ReactNode;\n /** The backend refuses every widget signal of a closed conversation, so no widget renders its controls. */\n isConversationClosed?: boolean;\n}\n\nconst WIDGET_VISIBILITY_WINDOW = 15;\n\n/** The canonical widget names an assistant-role message carries (empty for every other row). */\nconst widgetNamesOfMessage = (message: ChatMessage): string[] =>\n message.role === \"ASSISTANT\" ? widgetNamesInMarkdown(normalizeServerMessageToMarkdown(message.message)) : [];\n\n/**\n * The id of the newest widget-carrying assistant-role message the owner has not answered yet — null when the newest\n * such message is followed by a USER message or by a widget-click signal (the owner moved on; its controls stay\n * read-only after a reload, as before). Walks from the end so the first widget hit is the newest.\n */\nexport const findUnansweredWidgetMessageId = (messages: ChatMessage[]): string | number | null => {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index];\n if (message.role === \"USER\") return null;\n if (message.messageType === \"SIGNAL\" && isWidgetClickSignalSource(message.signalSource)) return null;\n if (widgetNamesOfMessage(message).length > 0) return message.id ?? null;\n }\n return null;\n};\n\n/**\n * The ids of every widget-carrying assistant-role message that a LATER assistant-role message supersedes with a\n * widget of the same name. Pure, order-based: the newest widget of a type is the live one.\n */\nexport const findSupersededWidgetMessageIds = (messages: ChatMessage[]): Set<string | number> => {\n const supersededIds = new Set<string | number>();\n const newestMessageIdByWidgetName = new Map<string, string | number>();\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index];\n if (message.id == null) continue;\n for (const widgetName of widgetNamesOfMessage(message)) {\n const newestMessageId = newestMessageIdByWidgetName.get(widgetName);\n if (newestMessageId === undefined) {\n newestMessageIdByWidgetName.set(widgetName, message.id);\n } else if (newestMessageId !== message.id) {\n // a second fence of the same type INSIDE one message is not a successor of itself\n supersededIds.add(message.id);\n }\n }\n }\n return supersededIds;\n};\n\nexport const ConversationMessageList = ({\n className,\n messages,\n currentCategory,\n aiPromptExtension,\n isDebugMode,\n aiThinking,\n thinkingIndicator,\n hideAssistantMeta,\n userMessageTheme,\n error,\n rateLimitRetryAfterSeconds,\n onRetryFailedRun,\n messagesBottomRef,\n messagesListRef,\n enableFreshMessageBlockReveal,\n taskInstructionMessageId,\n renderAfterMessage,\n isConversationClosed = false,\n}: ConversationMessageListProps) => {\n const list = messages ?? [];\n const hideWidgetsBeforeIndex = Math.max(0, list.length - WIDGET_VISIBILITY_WINDOW);\n const userTheme: ConversationMessageUserTheme =\n userMessageTheme ?? (aiPromptExtension === undefined ? \"light\" : \"default\");\n\n // Reload promotion: on resume-from-history the slice never tags messages `fresh` (those are history), so the\n // newest reply's widgets would render read-only — a customer who reloads mid-setup would find the question's\n // buttons gone. Evaluated ONCE the first time messages arrive: the newest assistant-role message that carries a\n // widget is rendered INTERACTIVE, provided the owner has not answered it yet (no later USER message, no later\n // widget-click signal). \"Newest widget-carrying\", not \"newest assistant message\": a widget a TOOL writes (e.g. a\n // confirm widget of the app's first-run setup) is indexed before the agent's own line under it, so it is never the last\n // message — and read-only it would tell the owner the match \"was already handled\" while the setup waits for that\n // very click. It is NOT rendered `justDelivered`: that flag is the one-shot side-effect gate (canvas open, draft\n // creation) and must never re-fire on a reload — a canvas that was open comes back through the canvas model's own\n // restore, not through this promotion. (Detection lives here, in the render tree — importing the widget registry\n // into the store-init slice case forms a TDZ cycle.)\n const reloadReopenMessageIdRef = useRef<string | number | null | undefined>(undefined);\n if (reloadReopenMessageIdRef.current === undefined && list.length > 0) {\n reloadReopenMessageIdRef.current = findUnansweredWidgetMessageId(list);\n }\n // A widget the owner already moved past: a LATER assistant-role message carrying a widget of the SAME type retires\n // the earlier one's controls (the newest identify widget is the live one — the owner corrected the seed, a new\n // search wrote a new widget; the superseded one must not accept a confirm for the wrong business). Recomputed as\n // the list grows, so it also applies in-session where the slice keeps the earlier message `fresh`.\n const supersededWidgetMessageIds = useMemo(() => findSupersededWidgetMessageIds(messages ?? []), [messages]);\n const renderMessageRow = (message: ChatMessage, index: number): React.ReactNode => {\n const hideWidgets = index < hideWidgetsBeforeIndex;\n // Phone-call lifecycle signals are the ONE user-visible signal kind: the timeline renders them as a\n // centered call marker chip (started / ended + duration / not reached).\n if (message.messageType === \"SIGNAL\" && PHONE_CALL_LIFECYCLE_SIGNAL_SOURCES.includes(message.signalSource ?? \"\")) {\n return (\n <ConversationCallMarkerChip\n key={message.id ?? index}\n signalSource={message.signalSource ?? \"\"}\n signalText={message.message ?? \"\"}\n />\n );\n }\n // Every OTHER out-of-band SIGNAL message (nudge, memory-conflict, widget events) is model-only —\n // never shown to the customer. Stored role=SYSTEM, so this guard precedes the role branches.\n if (message.messageType === \"SIGNAL\") return null;\n // ERROR messages carry the raw internal failure (e.g. a provider 400 with the full error body) — never shown to\n // the customer. The user-facing \"something went wrong\" is the ConversationErrorCard below, rendered from runState=FAILED.\n if (message.messageType === \"ERROR\") return null;\n\n let messageNode: React.ReactNode = null;\n if (message.role === \"USER\") {\n // Hide tool-result reports from the chat UI (they are model-internal)\n if (message.message?.startsWith(\"/toolresult \")) return null;\n // The SCHEDULE-run seed instruction is machine-composed — render it as a collapsed disclosure, never as\n // the owner's own words (see taskInstructionMessageId).\n messageNode =\n taskInstructionMessageId != null && message.id === taskInstructionMessageId ? (\n <ConversationTaskInstructionDisclosure instructionText={message.message ?? \"\"} />\n ) : (\n <ConversationMessageUser text={message.message ?? \"\"} theme={userTheme} />\n );\n } else if (message.role === \"ASSISTANT\") {\n // A text-less assistant turn has nothing to show the customer, and rendering it anyway emits an empty\n // markdown container that still consumes a full `$x3` message-list gap. Its agentic trace fields are\n // the admin console's concern (AgentConversationInspector, its own timeline), not this chat's.\n if (!assistantMessageHasRenderableMarkdown(message.message)) return null;\n messageNode = (\n <ConversationMessageWidgets\n key={message.id}\n interactive={\n !isConversationClosed &&\n !(message.id != null && supersededWidgetMessageIds.has(message.id)) &&\n ((message.fresh ?? false) || (message.id != null && message.id === reloadReopenMessageIdRef.current))\n }\n justDelivered={message.fresh ?? false}\n aiPromptExtensionCategory={currentCategory}\n aiPromptExtension={aiPromptExtension}\n message={message.message}\n messageId={message.id}\n hideWidgets={hideWidgets}\n hideAssistantMeta={hideAssistantMeta}\n enableFreshMessageBlockReveal={enableFreshMessageBlockReveal}\n />\n );\n } else if (message.role === \"SYSTEM\" && isDebugMode) {\n messageNode = (\n <ConversationMessageSystem\n aiPromptExtensionCategory={currentCategory}\n aiPromptExtension={aiPromptExtension}\n message={message.message!}\n />\n );\n }\n if (messageNode === null) return null;\n\n // `display: contents`-free anchor row carrying the message id, so search results (and any other consumer) can\n // locate a message in the DOM and scroll it into view. The row is a flex column so user bubbles keep their\n // `align-self: flex-end` right-alignment and assistant messages keep their full width.\n return (\n <div\n key={message.id ?? `pending-${message.role}-${index}`}\n className={styles.messageRow}\n data-message-id={message.id ?? undefined}\n // Role marker so the scroll logic can locate the last USER row (the question) to pin it to the top on send.\n data-message-role={message.role}\n >\n {messageNode}\n </div>\n );\n };\n\n return (\n <div className={classNames(styles.chatMessages, className)} ref={messagesListRef}>\n {list.map((message, index) =>\n renderAfterMessage ? (\n // Keyed like the row itself and present for every row, so a node that appears after a message never\n // changes the row's position in the tree (and never remounts the message and its widgets).\n <Fragment key={message.id ?? `pending-${message.role}-${index}`}>\n {renderMessageRow(message, index)}\n {renderAfterMessage(message)}\n </Fragment>\n ) : (\n renderMessageRow(message, index)\n ),\n )}\n {/* A custom indicator is always mounted (it idles between turns); the\n default one only appears while the agent is thinking. */}\n {thinkingIndicator ?? (\n (aiThinking ? (\n <ConversationMessageThinking\n aiPromptExtensionCategory={currentCategory}\n aiPromptExtension={aiPromptExtension}\n />\n ) : null)\n )}\n\n {error !== null ? (\n <ConversationErrorCard\n errorType={error!}\n rateLimitRetryAfterSeconds={rateLimitRetryAfterSeconds}\n onRetry={\n error === ConversationErrorType.AGENT_ERROR && lastWireMessageIsUnansweredUserMessage(messages)\n ? onRetryFailedRun\n : undefined\n }\n />\n ) : null}\n <div className={styles.messagesBottom} ref={messagesBottomRef} />\n </div>\n );\n};\n","import { Icon, InteractiveSurface, Text } from \"../../provider/components\";\nimport type { ChatAssistantOpening } from \"../chat-assistant-identity\";\nimport React from \"react\";\nimport styles from \"./ConversationSuggestionCard.module.scss\";\nimport { classNames } from \"../../provider/class-names\";\nimport { useAgentsEnvironment } from \"../../provider/AgentsProvider\";\n\nexport interface ConversationSuggestionCardProps {\n suggestion: ChatAssistantOpening;\n onClick?: () => void;\n testId?: string;\n}\n\nexport const ConversationSuggestionCard = (props: ConversationSuggestionCardProps) => {\n const { suggestion, onClick, testId } = props;\n const { AssistantIconBadge } = useAgentsEnvironment();\n\n return (\n <InteractiveSurface hoverable className={classNames(styles.suggestionCard)} testId={testId}>\n <button type=\"button\" className={styles.suggestionCardButton} onClick={onClick}>\n {AssistantIconBadge ? (\n <AssistantIconBadge\n iconName={suggestion.iconClass ?? \"\"}\n color=\"white\"\n backgroundColor=\"rgba(var(--agents-neutral-000-rgb, white), 0.2)\"\n />\n ) : null}\n <div>\n <Text role=\"help\" weight=\"bold\" color={\"white\"}>\n {suggestion.name}\n </Text>\n <Text role=\"caption\" color={\"white\"}>\n {suggestion.description}\n </Text>\n </div>\n <Icon color=\"white\" className={styles.arrowIcon} name=\"arrowRight\" />\n </button>\n </InteractiveSurface>\n );\n};\n","import { Text } from \"../../provider/components\";\nimport type { ChatAssistantIdentity, ChatAssistantOpening } from \"../chat-assistant-identity\";\nimport React from \"react\";\nimport { useAgentsEnvironment } from \"../../provider/AgentsProvider\";\nimport styles from \"../Conversation.module.scss\";\nimport { ConversationSuggestionCard } from \"../ConversationSuggestionCard/ConversationSuggestionCard\";\n\ninterface ConversationIntroCardProps {\n aiPromptExtension: ChatAssistantIdentity;\n /** The icon class of the extension, an icon of the app's icon set. */\n aiPromptExtensionIcon: string;\n suggestions?: ChatAssistantOpening[];\n onSuggestionClick: (text: string) => void;\n}\n\nexport const ConversationIntroCard = ({\n aiPromptExtension,\n aiPromptExtensionIcon,\n suggestions,\n onSuggestionClick,\n}: ConversationIntroCardProps) => {\n const { AssistantIconBadge } = useAgentsEnvironment();\n return (\n <div className={styles.extensionIntro}>\n {AssistantIconBadge ? (\n <AssistantIconBadge\n iconName={aiPromptExtensionIcon}\n color=\"white\"\n backgroundColor=\"var(--agents-assistant-400)\"\n isLarge\n />\n ) : null}\n <Text\n role=\"intro\"\n weight=\"bold\"\n textAlign={\"center\"}\n className={styles.extensionIntroTitle}\n >\n {aiPromptExtension.name}\n </Text>\n <Text\n role=\"body\"\n textAlign={\"center\"}\n color=\"var(--agents-fg-light)\"\n className={styles.extensionIntroDescription}\n >\n {aiPromptExtension.description}\n </Text>\n <div className={styles.suggestions}>\n {suggestions?.map((suggestion, index) => (\n <ConversationSuggestionCard\n key={\"suggestion_prop_\" + index}\n suggestion={suggestion}\n onClick={() => onSuggestionClick(suggestion.openingText!)}\n />\n ))}\n </div>\n </div>\n );\n};\n","import { Button } from \"../../provider/components\";\nimport { classNames } from \"../../provider/class-names\";\nimport React from \"react\";\nimport styles from \"./ConversationScrollToLatestMessageButton.module.scss\";\n\ntype ConversationScrollToLatestMessageButtonProps = {\n visible: boolean;\n onClick: () => void;\n};\n\nexport const ConversationScrollToLatestMessageButton = (props: ConversationScrollToLatestMessageButtonProps) => {\n const { visible, onClick } = props;\n return (\n <div className={classNames(styles.scrollToLatest, visible ? styles.visible : undefined)}>\n <Button\n variant=\"muted\"\n icon=\"arrowDown\"\n rounded\n blurred\n size=\"extraLarge\"\n onClick={onClick}\n />\n </div>\n );\n};\n","import { RefObject, useEffect, useState } from \"react\";\n\n/** The button appears once the messagesBottom sentinel is this far below the visible bottom edge. */\nconst SHOW_BUTTON_BELOW_FOLD_PX = 300;\n\nexport const useIsMessagesBottomOutOfView = (\n scrollContainer: HTMLDivElement | null,\n messagesList: HTMLDivElement | null,\n messagesBottomRef: RefObject<HTMLDivElement | null>,\n): boolean => {\n const [isOutOfView, setIsOutOfView] = useState(false);\n\n useEffect(() => {\n const messagesBottomSentinel = messagesBottomRef.current;\n if (!scrollContainer || !messagesBottomSentinel) {\n setIsOutOfView(false);\n return undefined;\n }\n const observer = new IntersectionObserver(\n (entries) => setIsOutOfView(!entries[entries.length - 1].isIntersecting),\n // Extends the root's bottom edge by 300px: the sentinel counts as \"in view\" until it is\n // more than 300px below the fold, so the button only appears after real scrolling up.\n { root: scrollContainer, rootMargin: `0px 0px ${SHOW_BUTTON_BELOW_FOLD_PX}px 0px` },\n );\n observer.observe(messagesBottomSentinel);\n return () => {\n observer.disconnect();\n setIsOutOfView(false);\n };\n }, [scrollContainer, messagesList, messagesBottomRef]);\n\n return isOutOfView;\n};\n","import { MutableRefObject, RefObject, useCallback, useRef } from \"react\";\n\n/**\n * The pin-question-on-send machinery of the AW-107 long-answer reading flow, extracted out of ConversationViewBody\n * so the addon lives in its own file (mirrors hooks/useIsMessagesBottomOutOfView.ts). On send, the user's\n * question is pinned to the top with empty room reserved below it and bottom-follow is suspended for the turn,\n * so the reply reveals from its beginning instead of chasing the bottom.\n *\n * Every method no-ops when `enabled` is false, so the disabled state can never leak a layout write\n * (padding-bottom) or a scroll call even if a caller forgets a gate.\n */\nexport const usePinQuestionOnSend = (options: {\n enabled: boolean;\n scrollContainer: HTMLDivElement | null;\n getMessagesListEl: () => HTMLElement | null;\n messagesBottomRef: RefObject<HTMLDivElement | null>;\n /** ConversationViewBody's followBottom ref — pinning suspends bottom-follow for the turn. */\n followBottomRef: MutableRefObject<boolean>;\n}) => {\n const { enabled, scrollContainer, getMessagesListEl, messagesBottomRef, followBottomRef } = options;\n\n // While a turn's question is pinned at the top (from send until the next send / a manual scroll / a conversation\n // switch), we reserve empty room below it and stop chasing the bottom, so the reply flows in from its beginning.\n const questionPinnedThisTurn = useRef(false);\n\n const lastUserMessageRow = useCallback((): HTMLElement | null => {\n const list = getMessagesListEl();\n if (!list) return null;\n const rows = list.querySelectorAll<HTMLElement>('[data-message-role=\"USER\"]');\n return rows[rows.length - 1] ?? null;\n }, [getMessagesListEl]);\n\n const userMessageRowCount = useCallback((): number => {\n const list = getMessagesListEl();\n return list ? list.querySelectorAll('[data-message-role=\"USER\"]').length : 0;\n }, [getMessagesListEl]);\n\n // Reserve just enough empty room below the question that it can be scrolled fully to the top; the room shrinks to 0\n // once the reply alone fills the viewport. Pure layout write — no scrolling here.\n const reserveRoomBelowQuestion = useCallback(\n (questionRow: HTMLElement) => {\n const container = scrollContainer;\n const list = getMessagesListEl();\n const bottomMarker = messagesBottomRef.current;\n if (!container || !list || !bottomMarker) return;\n const topClearance = parseFloat(getComputedStyle(questionRow).scrollMarginTop) || 0;\n const realBelow = bottomMarker.offsetTop - questionRow.offsetTop; // question + reply/thinking so far\n const reserved = Math.max(0, container.clientHeight - topClearance - realBelow);\n list.style.paddingBottom = `${reserved}px`;\n },\n [scrollContainer, getMessagesListEl, messagesBottomRef],\n );\n\n // Pin the latest question row to the top and hand the space below to the reply. Returns false (no-op) when the\n // addon is disabled or there is no USER row to pin.\n const pinLatestQuestionToTop = useCallback(\n (behavior: ScrollBehavior): boolean => {\n if (!enabled) return false;\n const questionRow = lastUserMessageRow();\n if (!questionRow) return false;\n reserveRoomBelowQuestion(questionRow); // reserve first, so there's room to lift the question all the way up\n questionRow.scrollIntoView({ behavior, block: \"start\", inline: \"nearest\" });\n followBottomRef.current = false;\n questionPinnedThisTurn.current = true;\n return true;\n },\n [enabled, lastUserMessageRow, reserveRoomBelowQuestion, followBottomRef],\n );\n\n // Re-reserve room below the pinned question; no-op when disabled or not pinned. Used by the positioning layout\n // effect and the ResizeObserver as the reply grows.\n const reserveRoomBelowPinnedQuestion = useCallback(() => {\n if (!enabled || !questionPinnedThisTurn.current) return;\n const questionRow = lastUserMessageRow();\n if (questionRow) reserveRoomBelowQuestion(questionRow);\n }, [enabled, lastUserMessageRow, reserveRoomBelowQuestion]);\n\n const isQuestionPinnedThisTurn = useCallback(() => questionPinnedThisTurn.current, []);\n\n // Clear the pin + remove the reserved padding-bottom (conversation switch, scroll-to-latest).\n const endPinnedTurn = useCallback(() => {\n if (!enabled) return;\n questionPinnedThisTurn.current = false;\n getMessagesListEl()?.style.removeProperty(\"padding-bottom\");\n }, [enabled, getMessagesListEl]);\n\n return {\n pinLatestQuestionToTop,\n reserveRoomBelowPinnedQuestion,\n isQuestionPinnedThisTurn,\n endPinnedTurn,\n userMessageRowCount,\n };\n};\n","import { Text } from \"../../provider/components\";\nimport type { ChatAssistantCategory, ChatAssistantIdentity, ChatAssistantOpening } from \"../chat-assistant-identity\";\nimport { classNames } from \"../../provider/class-names\";\nimport React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport styles from \"./ConversationViewBody.module.scss\";\nimport {\n ConversationMessageInput,\n ConversationMessageInputHandle,\n} from \"../ConversationMessageInput/ConversationMessageInput\";\nimport { ConversationMessageInputComposerWidgets } from \"../ConversationMessageInput/ConversationMessageInputComposerWidgets/ConversationMessageInputComposerWidgets\";\nimport { useConversationWidgetContext } from \"../../renderer/ConversationWidgetContext\";\nimport type { ConversationAttachmentReference } from \"../../contract/conversation\";\nimport { SendMessageHandler } from \"../conversation-send-message\";\nimport { ConversationErrorType } from \"../store/conversation-error-type\";\nimport type { ChatMessage } from \"../chat-message\";\nimport { NoConversationLoading, ConversationLoading } from \"../ConversationLoadingStates\";\nimport { ConversationMessageList } from \"../ConversationMessageList/ConversationMessageList\";\nimport { WidgetWindowRootContext } from \"../../renderer/widget-windowing\";\nimport { ConversationIntroCard } from \"../ConversationIntroCard/ConversationIntroCard\";\nimport { useAgentsEnvironment } from \"../../provider/AgentsProvider\";\nimport { ConversationMessageUserTheme } from \"../ConversationMessage/ConversationMessageUser/ConversationMessageUser\";\nimport { ConversationScrollToLatestMessageButton } from \"../ConversationScrollToLatestMessageButton/ConversationScrollToLatestMessageButton\";\nimport { useIsMessagesBottomOutOfView } from \"./hooks/useIsMessagesBottomOutOfView\";\nimport { usePinQuestionOnSend } from \"./hooks/usePinQuestionOnSend\";\n\nexport interface ConversationViewProps {\n currentCategory?: ChatAssistantCategory; // Type from categories\n aiPromptExtension?: ChatAssistantIdentity; // Type from agents\n suggestions?: ChatAssistantOpening[]; // Type from conversationOpenings\n messages: ChatMessage[] | null; // Type from messages\n aiThinking: boolean;\n error: ConversationErrorType | null;\n /** Retry-After seconds of a 429 quota rejection — forwarded to the error card for the \"try again in …\" wording. */\n rateLimitRetryAfterSeconds?: number | null;\n /** Host-bound retry of a FAILED run — forwarded to the message list / error card. */\n onRetryFailedRun?: () => Promise<void>;\n isDebugMode: boolean;\n categoriesLoaded: boolean;\n conversationId?: number;\n startingNewConversation?: boolean;\n /**\n * Show the message list even when no conversation exists yet. Used by a host,\n * whose fresh (not-yet-created) chat renders its empty message list.\n */\n showMessagesWithoutConversation?: boolean;\n /**\n * Follow the conversation bottom (scroll-on-mount + resize follow). Default true.\n * A host disables it for a fresh chat that has no messages yet, so the view stays\n * anchored at the top.\n */\n autoScroll?: boolean;\n /**\n * Custom node mounted at the tail of the message list instead of the default\n * (thinking-only) ConversationMessageThinking. Rendered permanently — used by\n * A host to show the animated agent face, which idles between turns.\n */\n thinkingIndicator?: React.ReactNode;\n /**\n * Custom background slot rendered absolutely behind the chat content. When\n * provided, the default chat gradient and the AI-Extensions category\n * illustrations are suppressed so the caller owns the entire background\n * (e.g. A host's shader). Pair with a parent that supplies a solid base color\n * if the slot doesn't paint the full canvas itself.\n */\n background?: React.ReactNode;\n /**\n * Opt-in: the host paints the chat's background itself OUTSIDE this component (e.g. the agent's root shader).\n * Keeps the `customBg` visual mode (top blur band) that passing `background` would give, but with\n * no background node here and a transparent pane fill so the host's layer shows through.\n */\n backgroundPaintedByHost?: boolean;\n /**\n * The setup progress stepper (today's only consumer: the agent chat), mounted as a DIRECT CHILD of this\n * view's pane root — a `position: relative` box the size of the chat pane — and outside the scroll container,\n * so it stays put while messages scroll under it. Mounted here rather than in the host's floating top bar for\n * the same reason the top blur band is (see `.contentWrapper::before`): it has to track the chat content\n * column, so it follows the pane when a canvas or a side panel contracts it, which a viewport-positioned\n * overlay cannot — and that pane boundary exists only inside this view's split.\n *\n * The node brings its OWN positioning, stacking and query container (see the stepper's `.progressStepperRow`);\n * this view contributes nothing but the positioned pane root it is mounted in.\n */\n progressStepperSlot?: React.ReactNode;\n /**\n * Optional external ref to the scrollable content wrapper. Forwarded so\n * callers can drive scroll-linked effects (e.g. an animated background\n * that morphs with scroll position) without having to query the DOM.\n */\n scrollRef?: React.Ref<HTMLDivElement>;\n /**\n * Bump to force a scroll to the newest message (via {@link scrollToLatestMessage}, so it clears any pinned turn and\n * re-enables bottom-follow). Each distinct value triggers one scroll; the initial `0`/`undefined` is ignored. Used by\n * the agent to follow the turn a widget/CTA-click signal starts (which posts no visible message of its own).\n */\n scrollToBottomNonce?: number;\n /**\n * Optional external override of the internal bottom-follow flag. Provide it when the host runs its own programmatic\n * scroll away from the bottom (e.g. the agent's search-result jump, which pages older messages in and scrolls to\n * a message far above the bottom): the host sets it `false` for the jump so the resize/message-follow effects stop\n * re-anchoring to the bottom and yanking the view back mid-jump. Left undefined, ConversationViewBody uses its own ref and\n * behaves exactly as before. Shared with the AW-107 pin machinery, so both agree on one flag.\n */\n followBottomRef?: React.MutableRefObject<boolean>;\n /**\n * Optional \"a programmatic jump is in flight\" gate. Set by the host (the agent's search-result jump) for the duration of a\n * jump. While true, the near-bottom onScroll handler does NOT re-enable bottom-follow and the resize ResizeObserver\n * does NOT auto-scroll to the bottom — otherwise, right after the jump the container is still near the bottom, so\n * onScroll would flip follow back on and the follow-up content-resize would yank the view back down. Left undefined,\n * nothing is gated. Distinct from `followBottomRef`: that flag alone is clobbered by onScroll; this gate stops it.\n */\n scrollJumpActiveRef?: React.MutableRefObject<boolean>;\n /**\n * Hide the assistant header (icon + name + \"Assistant\") and the copy button\n * on assistant messages. Used by a host, which renders bare messages.\n */\n hideAssistantMeta?: boolean;\n /**\n * Theme for user-message bubbles, forwarded to ConversationMessageList. A host forces\n * \"dark\"; when omitted the list falls back to its legacy light/default logic.\n */\n userMessageTheme?: ConversationMessageUserTheme;\n /**\n * True when the host (the agent) is minimized to the compact side-panel; forwarded to ConversationViewBody.\n */\n isCompact?: boolean;\n /**\n * Opt-in (agent host only): confine the customBg top blur band to the chat pane instead of the whole viewport —\n * active only while a sibling side panel is docked beside the chat, so the band compresses together with it instead\n * of bleeding across the panel. See `.confineTopBlurBandToChatPane` below.\n */\n confineTopBlurBandToChatPane?: boolean;\n /**\n * Scroll-up older-message pagination (opt-in — mirrors the admin inspector). When `onLoadOlder` is provided, a top\n * sentinel + IntersectionObserver (root = the scroll container) prefetches the next older page ~300px before the\n * top, and the viewport is anchored across the prepend so it doesn't jump. `hasMoreOlder` gates the sentinel;\n * `loadingOlder` shows a small loading row. Left undefined (the default) → no pagination, behaviour unchanged.\n */\n hasMoreOlder?: boolean;\n loadingOlder?: boolean;\n onLoadOlder?: () => void;\n /**\n * Extra action controls rendered inside the chat input's action row, left of the send button —\n * forwarded to ConversationMessageInput (e.g. the agent model picker pill).\n */\n inputActionsSlot?: React.ReactNode;\n /**\n * Action controls pinned to the LEFT edge of the chat input's action row (`inputActionsSlot`\n * stays right) — forwarded to ConversationMessageInput's `actionsSlotStart` (e.g. the agent \"+\" attachments menu).\n */\n inputActionsSlotStart?: React.ReactNode;\n /**\n * Host-owned content rendered inside the chat input above its editable text area — attachment\n * preview cards, validation error boxes. Forwarded to ConversationMessageInput's `topSlot`.\n */\n inputTopSlot?: React.ReactNode;\n /**\n * Hard send gate for the chat input: blocks the send button AND Enter while true, but keeps the\n * text editable. The host sets it e.g. while attachment uploads run.\n */\n inputSendDisabled?: boolean;\n /**\n * Host-owned content rendered IN PLACE of the chat input — the closed-conversation bar of a completed\n * setup conversation (no further input possible). When set, the body renders it instead of ConversationMessageInput,\n * in a wrapper carrying the input's own width, bottom gap and stacking rule — so the node needs no layout of its\n * own, and the background layer cannot paint over it.\n */\n inputReplacementSlot?: React.ReactNode;\n /**\n * Host-owned band right above the chat input, the input overlapping its lower edge — e.g. the running voice chat\n * with its controls. Not rendered while `inputReplacementSlot` replaces the input.\n */\n inputBandSlot?: React.ReactNode;\n /** The chat input's placeholder; the input's own default otherwise. */\n inputPlaceholder?: string;\n /** Forwarded to ConversationMessageInput's `sendReplacementWhenEmpty`. */\n inputSendReplacementWhenEmpty?: React.ReactNode;\n /** Forwarded to ConversationMessageInput's `glowUnderlay`. */\n inputGlowUnderlay?: React.ReactNode;\n /**\n * Host-owned layer over the message column, the same size — e.g. the voice chat's focus mode. The message column\n * stays mounted under it (scroll position and widget state kept); `isContentOverlayShown` switches between them.\n */\n contentOverlaySlot?: React.ReactNode;\n isContentOverlayShown?: boolean;\n /**\n * Uploaded media-library images that ride along in `onSendMessage`'s options object (`attachments`) on the next\n * send. Owned and cleared by the host; forwarded to ConversationMessageInput.\n */\n outgoingAttachmentImages?: ConversationAttachmentReference[];\n /**\n * Imperative handle on the chat input (insertTextIntoInput) so host-owned controls — e.g. the\n * \"+\" menu's \"Generate image\" entry — can put text into it. Forwarded to ConversationMessageInput's `apiRef`.\n */\n conversationMessageInputApiRef?: React.Ref<ConversationMessageInputHandle>;\n /**\n * Keep the chat input editable and sendable while `aiThinking` is true (default false — the input locks for the\n * whole turn); only the first post of a new conversation still locks it, while no conversation id exists yet. Opt\n * in ONLY for a host whose post call stores the message and returns at once, and whose agent checks for unanswered\n * messages when its turn ends, so a message sent mid-turn is answered in the next turn. A host whose post call\n * generates the reply synchronously within the request has no such guard and must leave it off.\n */\n allowSendWhileAiThinking?: boolean;\n /**\n * Opt-in long-answer reading flow (AW-107), default false. When enabled:\n * (1) on send, the question is pinned to the top with room reserved below it so the reply\n * reveals from its beginning instead of chasing the bottom;\n * (2) a scroll-to-latest-message button floats above the input while the bottom is out of view;\n * (3) a fresh static reply's markdown blocks fade in staggered (useFreshMessageBlockReveal).\n * Left off, the chat keeps the classic bottom-follow behavior — AI Extensions stays off;\n * the agent chat opts in.\n */\n enablePinQuestionAndRevealAnswer?: boolean;\n /** New user messages are not sends (a voice chat saves the owner's spoken sentences): keep bottom-follow, no pin. */\n isQuestionPinSuspended?: boolean;\n /**\n * Id of the ONE user message ConversationMessageList renders as a collapsed task-instruction disclosure instead of an\n * owner bubble — the machine-composed seed instruction of a SCHEDULE-spawned run (UX overhaul W4 item 19).\n * Undefined (every host except the agent chat) changes nothing.\n */\n taskInstructionMessageId?: number;\n /**\n * Host content rendered right after one message of the thread, forwarded to ConversationMessageList (the agent chat's\n * activity card under the message that started each turn). Undefined changes nothing.\n */\n renderAfterMessage?: (message: ChatMessage) => React.ReactNode;\n /** Forwarded to ConversationMessageList: a closed conversation renders no widget controls. */\n isConversationClosed?: boolean;\n onSuggestionClick: (text: string) => void;\n onSendMessage: SendMessageHandler;\n}\n\nexport const ConversationViewBody = (props: ConversationViewProps) => {\n const {\n currentCategory,\n aiPromptExtension,\n suggestions,\n messages,\n aiThinking,\n error,\n rateLimitRetryAfterSeconds,\n onRetryFailedRun,\n isDebugMode,\n categoriesLoaded,\n startingNewConversation,\n conversationId,\n showMessagesWithoutConversation,\n autoScroll = true,\n thinkingIndicator,\n background,\n backgroundPaintedByHost,\n progressStepperSlot,\n scrollRef,\n scrollToBottomNonce,\n followBottomRef,\n scrollJumpActiveRef,\n hideAssistantMeta,\n userMessageTheme,\n isCompact,\n confineTopBlurBandToChatPane,\n hasMoreOlder,\n loadingOlder,\n onLoadOlder,\n inputActionsSlot,\n inputActionsSlotStart,\n inputTopSlot,\n inputSendDisabled,\n inputReplacementSlot,\n inputBandSlot,\n inputPlaceholder,\n inputSendReplacementWhenEmpty,\n inputGlowUnderlay,\n contentOverlaySlot,\n isContentOverlayShown = false,\n outgoingAttachmentImages,\n conversationMessageInputApiRef,\n allowSendWhileAiThinking,\n enablePinQuestionAndRevealAnswer = false,\n isQuestionPinSuspended = false,\n taskInstructionMessageId,\n renderAfterMessage,\n isConversationClosed,\n onSuggestionClick,\n onSendMessage,\n } = props;\n\n // Composer widgets ride the widget context itself (like the deleted selection chip did), so every\n // ConversationView host gets the strip, the outgoing-on-send value and the clear-on-send wiring for free.\n const { composerWidgets, clearComposerWidgets } = useConversationWidgetContext();\n\n const messagesBottom = useRef<HTMLDivElement | null>(null);\n const [scrollContainer, setScrollContainer] = useState<HTMLDivElement | null>(null);\n const [messagesList, setMessagesList] = useState<HTMLDivElement | null>(null);\n // Bottom-follow flag. Uses the host-supplied ref when given (so a host-driven jump can suspend follow, see\n // `followBottomRef` in props), otherwise a local one — identical behaviour when no host overrides it.\n const internalFollowBottom = useRef(true);\n const followBottom = followBottomRef ?? internalFollowBottom;\n // Rendered USER-row count at the last positioning pass — an increase within the same conversation means the user\n // just sent (the pin trigger). Counted from the DOM (not the messages array) so hidden USER messages (/toolresult\n // reports, SIGNAL/ERROR) never miscount and it always matches the row pinLatestQuestionToTop targets.\n const prevUserMessageCount = useRef(0);\n // Last conversationId the positioning effect saw, to tell a real switch / new-chat (re-anchor to bottom) apart from\n // the cold-start id landing mid-turn (undefined → a freshly-created id while pinned → keep the pin).\n const prevConversationId = useRef<number | undefined>(conversationId);\n // Scroll-up pagination (opt-in via onLoadOlder). Capturing the scrollHeight when an older-load starts lets the\n // prepend layout-effect restore the viewport so it doesn't jump when the older page merges in.\n const prependAnchorScrollHeight = useRef<number | null>(null);\n\n // Combined ref callback for the scrollable content wrapper: drives the\n // internal state ref (the auto-scroll / follow-bottom effects depend on it)\n // AND forwards to the caller-supplied `scrollRef` when present. Stable identity\n // so the ref isn't detached/reattached on every render.\n const handleContentWrapperRef = useCallback(\n (el: HTMLDivElement | null) => {\n setScrollContainer(el);\n if (typeof scrollRef === \"function\") scrollRef(el);\n else if (scrollRef) (scrollRef as React.MutableRefObject<HTMLDivElement | null>).current = el;\n },\n [scrollRef],\n );\n\n // The named assistant's theme and background come from the app; the agent chat shows none.\n const { readAssistantThemeClassName, AssistantBackground } = useAgentsEnvironment();\n const colorPalette = useMemo(\n () =>\n currentCategory?.alias\n ? (readAssistantThemeClassName?.(currentCategory.alias) ?? styles.genericChat)\n : styles.genericChat,\n [currentCategory, readAssistantThemeClassName],\n );\n\n const isMessageListVisible =\n (!!messages && (!!conversationId || !!showMessagesWithoutConversation)) ||\n startingNewConversation ||\n (!!error && error !== ConversationErrorType.CONVERSATION_NOT_FOUND);\n\n const isConversationIntroVisible = useMemo(\n () =>\n aiPromptExtension !== undefined &&\n !conversationId &&\n !startingNewConversation &&\n !messages &&\n !(!!error && error !== ConversationErrorType.CONVERSATION_NOT_FOUND),\n [aiPromptExtension, conversationId, startingNewConversation, messages, error],\n );\n\n // The list DOM node. Prefer the state ref (it drives the ResizeObserver effect); fall back to the bottom marker's\n // parent, which is always the .chatMessages list — needed in the layout effect on the render where the list first\n // mounts (setMessagesList hasn't flushed to state yet, but messagesBottom's ref is already committed).\n const getMessagesListEl = useCallback(\n (): HTMLElement | null => messagesList ?? messagesBottom.current?.parentElement ?? null,\n [messagesList],\n );\n\n const {\n pinLatestQuestionToTop,\n reserveRoomBelowPinnedQuestion,\n isQuestionPinnedThisTurn,\n endPinnedTurn,\n userMessageRowCount,\n } = usePinQuestionOnSend({\n enabled: enablePinQuestionAndRevealAnswer,\n scrollContainer,\n getMessagesListEl,\n messagesBottomRef: messagesBottom,\n followBottomRef: followBottom,\n });\n\n // First positioning is instant and pre-paint (layout effect + behavior \"auto\"):\n // opening a conversation lands directly at the bottom with no visible scroll\n // animation. Subsequent message updates scroll smoothly.\n const hasPositionedOnce = useRef(false);\n useLayoutEffect(() => {\n // Older-page prepend (scroll-up pagination): restore the viewport so the freshly-prepended messages don't shift\n // it, and do NOT auto-scroll to the bottom. The anchor is only set when an older-load started, so a conversation\n // switch / live tick never enters this branch.\n if (prependAnchorScrollHeight.current !== null) {\n if (scrollContainer) {\n const delta = scrollContainer.scrollHeight - prependAnchorScrollHeight.current;\n if (delta > 0) scrollContainer.scrollTop += delta;\n }\n prependAnchorScrollHeight.current = null;\n return;\n }\n if (!autoScroll) {\n hasPositionedOnce.current = false;\n return;\n }\n\n // Addon off (default): the classic bottom-follow — re-anchor to the bottom on every message update, exactly the\n // pre-AW-107 behavior (including the old quirk that it scrolls even before the list mounts, no messagesBottom guard).\n if (!enablePinQuestionAndRevealAnswer) {\n followBottom.current = true;\n messagesBottom.current?.scrollIntoView({\n behavior: hasPositionedOnce.current ? \"smooth\" : \"auto\",\n block: \"end\",\n inline: \"nearest\",\n });\n hasPositionedOnce.current = true;\n return;\n }\n\n // A real conversation switch / new-chat / first history load re-anchors to the bottom. The one exception is the\n // cold-start id landing (undefined → a freshly-created id) MID-TURN: the question was already pinned on send, so\n // keep it pinned instead of yanking back to the bottom.\n const conversationChanged = prevConversationId.current !== conversationId;\n const isColdStartIdLanding = prevConversationId.current === undefined && isQuestionPinnedThisTurn();\n prevConversationId.current = conversationId;\n if (conversationChanged && !isColdStartIdLanding) {\n endPinnedTurn();\n followBottom.current = true;\n hasPositionedOnce.current = false;\n }\n\n const userCount = userMessageRowCount();\n\n // First positioning for this conversation. If the list isn't mounted yet (still loading / intro screen), wait for\n // it. A fresh cold-start send (startingNewConversation, nothing positioned yet) pins the first question to the top;\n // opening an existing conversation lands at the bottom, as today.\n if (!hasPositionedOnce.current) {\n if (!messagesBottom.current) return; // list not in the DOM yet — nothing to position\n hasPositionedOnce.current = true;\n prevUserMessageCount.current = userCount;\n if (startingNewConversation && userCount > 0) {\n pinLatestQuestionToTop(\"auto\");\n } else {\n followBottom.current = true;\n messagesBottom.current.scrollIntoView({ behavior: \"auto\", block: \"end\", inline: \"nearest\" });\n }\n return;\n }\n\n // Same conversation, already positioned: a new USER row means the user just sent → pin the new question to the top.\n if (userCount > prevUserMessageCount.current && !isQuestionPinSuspended) {\n prevUserMessageCount.current = userCount;\n pinLatestQuestionToTop(\"smooth\");\n return;\n }\n prevUserMessageCount.current = userCount;\n\n // A later update within the same turn (the reply arriving / growing): keep the question pinned and just re-reserve\n // room — never scroll. Outside a pinned turn (e.g. a background refresh) keep today's bottom-follow.\n if (isQuestionPinnedThisTurn()) {\n reserveRoomBelowPinnedQuestion();\n } else if (followBottom.current) {\n messagesBottom.current?.scrollIntoView({ behavior: \"smooth\", block: \"end\", inline: \"nearest\" });\n }\n }, [\n messages,\n autoScroll,\n scrollContainer,\n conversationId,\n startingNewConversation,\n enablePinQuestionAndRevealAnswer,\n isQuestionPinSuspended,\n userMessageRowCount,\n reserveRoomBelowPinnedQuestion,\n pinLatestQuestionToTop,\n isQuestionPinnedThisTurn,\n endPinnedTurn,\n followBottom,\n ]);\n\n // Capture the scroll height the instant an older-load starts — consumed by the prepend layout-effect above to keep\n // the viewport anchored once the older page merges in.\n useEffect(() => {\n if (loadingOlder && scrollContainer) {\n prependAnchorScrollHeight.current = scrollContainer.scrollHeight;\n }\n }, [loadingOlder, scrollContainer]);\n\n // Scroll-up lazy-load: prefetch the next older page when the user scrolls within ~300px of the top. onLoadOlder is\n // self-guarded (in-flight + hasMoreOlder) by the caller, so repeated scroll ticks near the top don't double-fetch.\n // No-op unless the caller opts in via onLoadOlder.\n useEffect(() => {\n if (!onLoadOlder || !scrollContainer || !hasMoreOlder) return undefined;\n const onScrollNearTop = () => {\n if (scrollContainer.scrollTop <= 300) onLoadOlder();\n };\n scrollContainer.addEventListener(\"scroll\", onScrollNearTop, { passive: true });\n return () => scrollContainer.removeEventListener(\"scroll\", onScrollNearTop);\n }, [onLoadOlder, scrollContainer, hasMoreOlder]);\n\n useEffect(() => {\n if (!autoScroll) return;\n const container = scrollContainer;\n const list = messagesList;\n if (!container || !list) return;\n\n const NEAR_BOTTOM_PX = 80;\n const isNearBottom = () => container.scrollHeight - container.scrollTop - container.clientHeight <= NEAR_BOTTOM_PX;\n\n let animRaf: number | null = null;\n let isProgrammaticScroll = false;\n const cancelAnim = () => {\n if (animRaf !== null) {\n cancelAnimationFrame(animRaf);\n animRaf = null;\n }\n };\n\n // Non-interruptible smooth scroll: if already running, the existing\n // animation just re-reads the target each frame and chases it — no\n // restart, no blink. Only user interaction or unmount cancels it.\n const startSmoothToBottom = (durationMs: number) => {\n if (animRaf !== null) return; // in-flight; target re-read each frame\n const start = container.scrollTop;\n const initialTarget = container.scrollHeight - container.clientHeight;\n if (initialTarget - start <= 0) return;\n const t0 = performance.now();\n const easeOut = (t: number) => 1 - Math.pow(1 - t, 3);\n const tick = (now: number) => {\n const t = Math.min(1, (now - t0) / durationMs);\n const target = container.scrollHeight - container.clientHeight;\n isProgrammaticScroll = true;\n container.scrollTop = start + (target - start) * easeOut(t);\n isProgrammaticScroll = false;\n if (t < 1) {\n animRaf = requestAnimationFrame(tick);\n } else {\n animRaf = null;\n }\n };\n animRaf = requestAnimationFrame(tick);\n };\n\n const disableFollow = () => {\n followBottom.current = false;\n // A pinned turn (AW-107) keeps its pin + reserved room across a manual scroll — we deliberately do NOT clear them\n // here. Dropping the padding on scroll shrank the scroll height and made the viewport jump; and the empty space\n // below a short reply is intentional, so the question holds its place at the top for the whole turn.\n cancelAnim();\n };\n const onScroll = () => {\n if (isProgrammaticScroll) return;\n // A host-driven jump is in flight: don't re-enable follow just because the viewport is still/again near the\n // bottom — that clobbers the jump and lets the resize-follow yank the view back down.\n if (scrollJumpActiveRef?.current) return;\n if (isNearBottom()) followBottom.current = true;\n };\n\n container.addEventListener(\"wheel\", disableFollow, { passive: true });\n container.addEventListener(\"touchmove\", disableFollow, { passive: true });\n container.addEventListener(\"keydown\", disableFollow);\n container.addEventListener(\"scroll\", onScroll, { passive: true });\n\n const observer = new ResizeObserver(() => {\n // A host-driven jump is in flight (search-result jump): the messages settle their heights as they render, which\n // fires this observer — do NOT auto-scroll to the bottom, or the jump gets yanked back down.\n if (scrollJumpActiveRef?.current) return;\n // During a pinned turn the reply grows below the fold: re-reserve room (it shrinks to 0 as the reply fills the\n // viewport) — never scroll, so the question stays put at the top. Inert when the addon is off (pin never set).\n if (isQuestionPinnedThisTurn()) {\n reserveRoomBelowPinnedQuestion();\n return;\n }\n if (!followBottom.current) return;\n startSmoothToBottom(180);\n });\n observer.observe(list);\n // Also react to viewport/pane height changes (window resize, drawer open/close): the list observer won't fire when\n // only the container's height changes, but a pinned turn's reserved room depends on container.clientHeight. Only\n // needed for the addon — leaving it on for the default chat would newly trigger scroll-to-bottom on drawer toggle.\n if (enablePinQuestionAndRevealAnswer) observer.observe(container);\n\n return () => {\n container.removeEventListener(\"wheel\", disableFollow);\n container.removeEventListener(\"touchmove\", disableFollow);\n container.removeEventListener(\"keydown\", disableFollow);\n container.removeEventListener(\"scroll\", onScroll);\n observer.disconnect();\n cancelAnim();\n };\n }, [\n isMessageListVisible,\n scrollContainer,\n messagesList,\n autoScroll,\n enablePinQuestionAndRevealAnswer,\n isQuestionPinnedThisTurn,\n reserveRoomBelowPinnedQuestion,\n followBottom,\n scrollJumpActiveRef,\n ]);\n\n // Only observe the sentinel when the addon is on — a null container makes the hook return false + mount no observer.\n const isMessagesBottomOutOfView = useIsMessagesBottomOutOfView(\n enablePinQuestionAndRevealAnswer ? scrollContainer : null,\n messagesList,\n messagesBottom,\n );\n\n // Jump back to the newest message. Ends a pinned turn: the user is explicitly leaving the pinned\n // question, so bottom-follow takes over again (the ResizeObserver's pinned branch would otherwise\n // keep suppressing it, ConversationViewBody.tsx:396). Dropping the reserved padding here cannot jump the\n // viewport: the button only shows when the viewport is ≥300px ABOVE the sentinel, and the padding\n // lives entirely BELOW it — shrinking scrollHeight below the viewport doesn't move scrollTop.\n const scrollToLatestMessage = useCallback(() => {\n endPinnedTurn();\n followBottom.current = true;\n messagesBottom.current?.scrollIntoView({ behavior: \"smooth\", block: \"end\", inline: \"nearest\" });\n }, [endPinnedTurn, followBottom]);\n\n // Caller-driven scroll-to-bottom (scrollToBottomNonce bumped per widget/CTA-click signal). Goes through\n // scrollToLatestMessage so it clears any pinned turn and re-enables bottom-follow — a raw scrollTo would be\n // suppressed while a turn is pinned. Ignores the initial 0/undefined so it never scrolls on mount.\n useEffect(() => {\n if (!scrollToBottomNonce) return;\n scrollToLatestMessage();\n }, [scrollToBottomNonce, scrollToLatestMessage]);\n\n const messageColumn = (\n <div\n className={styles.contentWrapper}\n // A stable hook for a host's own stylesheet (the default chat drops the top blur band, it has no top bar).\n data-agents-chat-scroll=\"\"\n data-testid=\"chat-messages-scroll\"\n ref={handleContentWrapperRef}\n aria-hidden={isContentOverlayShown || undefined}\n inert={isContentOverlayShown || undefined}\n >\n {!conversationId && !categoriesLoaded && !aiPromptExtension ? (\n <NoConversationLoading />\n ) : null}\n\n {!!conversationId && !messages ? (\n <ConversationLoading />\n ) : null}\n\n {isConversationIntroVisible ? (\n <ConversationIntroCard\n aiPromptExtension={aiPromptExtension!}\n aiPromptExtensionIcon={aiPromptExtension!.iconClass as string}\n suggestions={suggestions}\n onSuggestionClick={onSuggestionClick}\n />\n ) : null}\n\n {!!loadingOlder ? (\n <span className={styles.olderLoading}>\n <Text>{\"Loading older messages…\"}</Text>\n </span>\n ) : null}\n\n {isMessageListVisible ? (\n <>\n {/* The scroll container (this contentWrapper) is the IntersectionObserver root for widget viewport-windowing. */}\n <WidgetWindowRootContext.Provider value={scrollContainer}>\n <ConversationMessageList\n className={\n enablePinQuestionAndRevealAnswer && (conversationId || startingNewConversation || aiThinking || error)\n ? styles.topAlignedMessages\n : undefined\n }\n messages={messages}\n currentCategory={currentCategory}\n aiPromptExtension={aiPromptExtension}\n isDebugMode={isDebugMode}\n aiThinking={aiThinking}\n thinkingIndicator={thinkingIndicator}\n hideAssistantMeta={hideAssistantMeta}\n userMessageTheme={userMessageTheme}\n error={error}\n rateLimitRetryAfterSeconds={rateLimitRetryAfterSeconds}\n onRetryFailedRun={onRetryFailedRun}\n messagesBottomRef={messagesBottom}\n messagesListRef={setMessagesList}\n enableFreshMessageBlockReveal={enablePinQuestionAndRevealAnswer}\n taskInstructionMessageId={taskInstructionMessageId}\n renderAfterMessage={renderAfterMessage}\n isConversationClosed={isConversationClosed}\n />\n </WidgetWindowRootContext.Provider>\n </>\n ) : null}\n </div>\n );\n\n return (\n <div\n className={classNames(\n styles.chat,\n background || backgroundPaintedByHost ? styles.customBg : colorPalette,\n backgroundPaintedByHost ? styles.backgroundPaintedByHost : undefined,\n isCompact ? styles.chatCompact : undefined,\n confineTopBlurBandToChatPane ? styles.confineTopBlurBandToChatPane : undefined,\n )}\n >\n {background ? (\n <div className={styles.backgroundLayer}>{background}</div>\n ) : backgroundPaintedByHost ? null : (\n AssistantBackground ? (\n <AssistantBackground category={currentCategory} categoryAlias={currentCategory?.alias ?? undefined} />\n ) : null\n )}\n\n {/* Positions itself against this .chat root (see the prop's doc) — no wrapper, no class from here. */}\n {progressStepperSlot}\n\n {contentOverlaySlot !== undefined ? (\n <div className={styles.contentLayers} data-overlay-shown={isContentOverlayShown || undefined}>\n {messageColumn}\n <div\n className={styles.contentOverlay}\n aria-hidden={!isContentOverlayShown || undefined}\n inert={!isContentOverlayShown || undefined}\n >\n {contentOverlaySlot}\n </div>\n </div>\n ) : (\n messageColumn\n )}\n {enablePinQuestionAndRevealAnswer && (\n <div className={styles.scrollToLatestAnchor}>\n <ConversationScrollToLatestMessageButton\n visible={isMessagesBottomOutOfView}\n onClick={scrollToLatestMessage}\n />\n </div>\n )}\n {/* With allowSendWhileAiThinking the input stays open during a turn, EXCEPT while the cold-start\n startWithMessage POST is in flight: no conversation id exists yet, so the host's send handler would drop a\n second message silently — keep the brief lock for exactly that window. */}\n {inputReplacementSlot ? (\n <div className={styles.inputReplacement}>{inputReplacementSlot}</div>\n ) : (\n <>\n {inputBandSlot}\n <ConversationMessageInput\n className={classNames(styles.input, currentCategory?.alias ? styles.inputInCategory : undefined)}\n isProcessing={allowSendWhileAiThinking ? !!startingNewConversation : aiThinking}\n onSendMessage={onSendMessage}\n actionsSlot={inputActionsSlot}\n actionsSlotStart={inputActionsSlotStart}\n // The composer-widgets strip is one topSlot block like any other — above the host's own blocks\n // (attachment previews sit closest to the text). It reads the widget context itself, so every\n // ConversationView host gets it for free.\n topSlot={\n <>\n <ConversationMessageInputComposerWidgets testId=\"chat-input\" />\n {inputTopSlot}\n </>\n }\n sendDisabled={inputSendDisabled}\n outgoingAttachmentImages={outgoingAttachmentImages}\n outgoingComposerWidgets={composerWidgets}\n onComposerWidgetsSent={clearComposerWidgets}\n apiRef={conversationMessageInputApiRef}\n placeholder={inputPlaceholder}\n sendReplacementWhenEmpty={inputSendReplacementWhenEmpty}\n glowUnderlay={inputGlowUnderlay}\n />\n </>\n )}\n </div>\n );\n};\n","import { Button, LoadingSpinner } from \"../../provider/components\";\nimport { classNames } from \"../../provider/class-names\";\nimport React, { Fragment, useEffect, useState } from \"react\";\nimport { useConversationWidgetContext } from \"../../renderer/ConversationWidgetContext\";\nimport { ConversationWidgetErrorBoundary } from \"../../renderer/ConversationWidgetErrorBoundary\";\nimport { canvasBehaviourOf } from \"../../renderer/canvas/canvas-behaviours\";\nimport {\n canvasBodies,\n canvasFeatureFlagOf,\n} from \"../../renderer/canvas/canvasBodies\";\nimport { ConversationWidgetFeatureGate } from \"../../renderer/_access/ConversationWidgetFeatureGate\";\nimport { normalizeServerMessageToMarkdown } from \"../normalize-server-message-to-markdown\";\nimport type { ChatMessage } from \"../chat-message\";\nimport { useAgentsEnvironment } from \"../../provider/AgentsProvider\";\nimport classes from \"./CanvasPane.module.scss\";\n\ninterface CanvasPaneProps {\n /** The host's loaded messages — a message-anchored canvas body parses its fence out of its anchor message. */\n messages: ChatMessage[] | null;\n /** Render as a rounded surface card (agent side-split — the pane is inset below the floating top bar). */\n boxed?: boolean;\n}\n\n// The canvas slides in via a main-thread react-spring animation. Mounting a heavy body (e.g. an app's full list\n// page) DURING that slide blocks the animation frames and makes opening stagger. So we hold the content for a beat — a\n// light loader fills the pane while it slides in, then the body mounts.\nconst CANVAS_OPEN_SETTLE_MS = 350;\n\nconst OpeningLoader = () => (\n // Cheap, content-agnostic placeholder shown while the canvas slides in, so the open animation isn't blocked by the\n // body's mount. A centered spinner (not a content-shaped skeleton) because the canvas hosts many different bodies.\n <div className={classes.openingLoader} aria-hidden>\n <LoadingSpinner />\n </div>\n);\n\n/**\n * The CANVAS PANE — renders the canvas the chat's slice points at (`canvas`, a pointer to its anchor): the body from\n * `canvasBodies`, keyed on `mountGeneration`, geometry from its behaviour (`canvasBehaviourOf`), the canvas's own feature gate and the\n * shared error boundary. A message-anchored body receives its anchor message's markdown and re-reads its widget's\n * payload from it — nothing but the pointer is stored anywhere.\n */\nexport const CanvasPane = ({ messages, boxed = false }: CanvasPaneProps) => {\n const CanvasBodyContainer = useAgentsEnvironment().CanvasBodyContainer ?? Fragment;\n const { canvas, closeCanvas, chatAgentSpaceKey } = useConversationWidgetContext();\n\n // Re-deferred on every open (the mountGeneration changes each time a canvas mounts).\n const mountGeneration = canvas?.mountGeneration;\n const [contentReady, setContentReady] = useState(false);\n useEffect(() => {\n setContentReady(false);\n const timer = window.setTimeout(() => setContentReady(true), CANVAS_OPEN_SETTLE_MS);\n return () => window.clearTimeout(timer);\n }, [mountGeneration]);\n\n if (!canvas) {\n return null;\n }\n const Body = canvasBodies[canvas.canvasId];\n\n const behaviour = canvasBehaviourOf(canvas.canvasId);\n const featureFlag = canvasFeatureFlagOf(canvas.canvasId);\n const anchor = canvas.anchor;\n const anchorMessage =\n anchor.kind === \"message\" ? (messages ?? []).find((message) => message.id === anchor.messageId) : undefined;\n const anchorMessageMarkdown =\n anchorMessage !== undefined ? normalizeServerMessageToMarkdown(anchorMessage.message) : null;\n const body = <Body canvas={canvas} agentSpaceKey={chatAgentSpaceKey} anchorMessageMarkdown={anchorMessageMarkdown} />;\n\n return (\n <div\n className={classNames(\n classes.root,\n boxed ? classes.rootBoxed : \"\",\n behaviour?.fullBleed ? classes.rootFullBleedContent : \"\",\n behaviour?.overlayCloseButton ? classes.rootOverlayCloseButton : \"\",\n )}\n data-testid=\"chat-canvas-pane\"\n data-canvas-id={canvas.canvasId}\n >\n <div className={classes.closeButtonRow}>\n <Button\n variant=\"muted\"\n // Floating over the body's dark hero image: the light trigger for dark surfaces.\n inverted={behaviour?.overlayCloseButton}\n onDark={behaviour?.overlayCloseButton}\n icon=\"closeCanvas\"\n iconPosition={\"right\"}\n rounded\n blurred\n // The customer's OWN close: a live re-delivery of this canvas is vetoed until the next send / click.\n onClick={() => closeCanvas(true)}\n >\n Close canvas\n </Button>\n </div>\n <div key={canvas.mountGeneration} className={classes.content}>\n {contentReady ? (\n // The app's wrapper of a canvas body (host application: canvas-hosted list pages must not rewrite the page URL).\n <CanvasBodyContainer>\n <ConversationWidgetErrorBoundary widgetName={`canvas:${canvas.canvasId}`}>\n {featureFlag ? (\n // The canvas owns its gate (not inherited from an opener): a restored or page-opened canvas has no\n // widget mount to inherit it from. A page anchor's agent space is the gate's agent space.\n <ConversationWidgetFeatureGate\n featureFlag={featureFlag}\n agentSpaceKeyAttribute={anchor.kind === \"page\" ? anchor.agentSpaceKey : undefined}\n >\n {body}\n </ConversationWidgetFeatureGate>\n ) : (\n body\n )}\n </ConversationWidgetErrorBoundary>\n </CanvasBodyContainer>\n ) : (\n <OpeningLoader />\n )}\n </div>\n </div>\n );\n};\n","import { KeyboardEvent, PointerEvent as ReactPointerEvent, RefObject, useEffect, useRef, useState } from \"react\";\nimport { flushSync } from \"react-dom\";\nimport {\n CANVAS_MIN_WIDTH_PX,\n CHAT_COLUMN_MIN_WIDTH_PX,\n} from \"../../renderer/canvas/canvas-behaviours\";\n\nconst KEY_STEP_PX = 24;\nconst NEAR_PX = 40;\nconst RUBBER_MAX_PX = 44;\nconst RUBBER_K = 70;\nconst MAGNET_PX = 6;\nconst MAGNET_X = 0.4;\nconst MAGNET_Y = 0.12;\nconst DRAG_TRAVEL_PX = 12;\nconst LEAN_PX = DRAG_TRAVEL_PX;\nconst CLOSE_PULL_PX = 64;\nconst ARM_DELAY_MS = 350;\nconst LEAN_REST_MS = 150;\n\n/** `idle` (hidden until the pointer comes near), `hover`, `drag`, `armed` (pulled past the narrowest and held: release closes). */\nexport type CanvasResizeTab = \"idle\" | \"hover\" | \"drag\" | \"armed\";\n\nexport const rubberBand = (distance: number): number =>\n Math.sign(distance) * RUBBER_MAX_PX * (1 - Math.exp(-Math.abs(distance) / RUBBER_K));\n\nexport const canvasWidthLimits = (rootWidth: number, inset: number): { min: number; max: number } => {\n const min = CANVAS_MIN_WIDTH_PX + inset * 2;\n return { min, max: Math.max(min, rootWidth - CHAT_COLUMN_MIN_WIDTH_PX) };\n};\n\nexport const clampCanvasWidth = (width: number, rootWidth: number, inset: number): number => {\n const { min, max } = canvasWidthLimits(rootWidth, inset);\n return Math.round(Math.min(max, Math.max(min, width)));\n};\n\ninterface UseCanvasResizeOptions {\n /** The split's root: the canvas pane's right edge is its right edge, and `--agents-canvas-width-drag` is written onto it while dragging. */\n rootRef: RefObject<HTMLElement | null>;\n /** The canvas card's inset from its pane edge — the handle sits on the card's edge. */\n inset: number;\n /** The pane width the customer set (px), or `null` for the canvas's default split. */\n width: number | null;\n onWidthChange: (width: number | null) => void;\n onClose: () => void;\n}\n\n/**\n * The canvas's left edge drags. The edge follows the pointer through a CSS variable on the split's root (no render per\n * move); the width is committed once on release. Pulled `CLOSE_PULL_PX` past the narrowest and held, the tab arms and\n * release closes the canvas.\n */\nexport const useCanvasResize = (options: UseCanvasResizeOptions) => {\n const { rootRef, inset, width, onWidthChange, onClose } = options;\n const handleRef = useRef<HTMLDivElement>(null);\n const [isHovered, setIsHovered] = useState(false);\n const [isDragging, setIsDragging] = useState(false);\n const [isArmed, setIsArmed] = useState(false);\n const [tabX, setTabX] = useState(0);\n const isDraggingRef = useRef(false);\n const timers = useRef({ arm: 0, lean: 0 });\n const latest = useRef({ inset, onWidthChange, onClose });\n latest.current = { inset, onWidthChange, onClose };\n\n useEffect(() => {\n const root = rootRef.current;\n const pendingTimers = timers.current;\n return () => {\n window.clearTimeout(pendingTimers.arm);\n window.clearTimeout(pendingTimers.lean);\n // Unmounted mid-drag (the canvas closed): the split must not keep following a pointer nobody holds.\n delete root?.dataset.resizing;\n root?.style.removeProperty(\"--agents-canvas-width-drag\");\n };\n }, [rootRef]);\n\n const magnetise = (clientX: number | null, clientY: number | null, reach = MAGNET_PX) => {\n const handle = handleRef.current;\n if (!handle) return;\n const rect = handle.getBoundingClientRect();\n const cap = (value: number) => Math.max(-reach, Math.min(reach, value));\n const x = clientX === null ? 0 : cap((clientX - (rect.left + rect.width / 2)) * MAGNET_X);\n const y = clientY === null ? 0 : cap((clientY - (rect.top + rect.height / 2)) * MAGNET_Y);\n handle.style.setProperty(\"--agents-resize-magnet-x\", `${x.toFixed(2)}px`);\n handle.style.setProperty(\"--agents-resize-magnet-y\", `${y.toFixed(2)}px`);\n };\n\n const setNear = (isNear: boolean) => {\n const handle = handleRef.current;\n if (handle && (handle.dataset.near === \"true\") !== isNear) {\n handle.dataset.near = String(isNear);\n }\n };\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onPointerMove = (event: PointerEvent) => {\n if (isDraggingRef.current) return;\n const rect = handleRef.current?.getBoundingClientRect();\n const isNear =\n !!rect &&\n event.clientY >= rect.top &&\n event.clientY <= rect.bottom &&\n Math.abs(event.clientX - (rect.left + rect.width / 2)) <= NEAR_PX;\n setNear(isNear);\n if (isNear) magnetise(event.clientX, event.clientY);\n else magnetise(null, null);\n };\n const onPointerLeave = () => {\n if (isDraggingRef.current) return;\n setNear(false);\n magnetise(null, null);\n };\n root.addEventListener(\"pointermove\", onPointerMove);\n root.addEventListener(\"pointerleave\", onPointerLeave);\n return () => {\n root.removeEventListener(\"pointermove\", onPointerMove);\n root.removeEventListener(\"pointerleave\", onPointerLeave);\n };\n }, [rootRef]);\n\n const rootWidth = () => rootRef.current?.clientWidth ?? 0;\n const currentWidth = () => {\n const root = rootRef.current;\n const handle = handleRef.current;\n if (!root || !handle) return CANVAS_MIN_WIDTH_PX;\n const handleCenter = handle.getBoundingClientRect().left + handle.offsetWidth / 2;\n return root.getBoundingClientRect().right - handleCenter + latest.current.inset;\n };\n\n const startDrag = (event: ReactPointerEvent<HTMLElement>) => {\n const root = rootRef.current;\n if (event.button !== 0 || !root) return;\n event.preventDefault();\n const handle = event.currentTarget;\n try {\n handle.setPointerCapture(event.pointerId);\n } catch {\n // A pointer the browser does not know (synthetic): the strip still gets its moves while over it.\n }\n const { min, max } = canvasWidthLimits(rootWidth(), latest.current.inset);\n const grabOffset = event.clientX - handle.getBoundingClientRect().left - handle.offsetWidth / 2;\n let lastX = event.clientX;\n let lean = 0;\n let isArmedNow = false;\n let draggedWidth: number | null = null;\n isDraggingRef.current = true;\n setIsDragging(true);\n // Resize mode reads `--agents-canvas-width-drag`: seed it, or a press without a move leaves the split without a width.\n root.style.setProperty(\"--agents-canvas-width-drag\", `${clampCanvasWidth(currentWidth(), rootWidth(), latest.current.inset)}px`);\n root.dataset.resizing = \"true\";\n\n const disarm = () => {\n window.clearTimeout(timers.current.arm);\n timers.current.arm = 0;\n if (isArmedNow) {\n isArmedNow = false;\n setIsArmed(false);\n }\n };\n\n magnetise(null, event.clientY, DRAG_TRAVEL_PX);\n const move = (moveEvent: PointerEvent) => {\n magnetise(null, moveEvent.clientY, DRAG_TRAVEL_PX);\n const desired = root.getBoundingClientRect().right - (moveEvent.clientX - grabOffset) + latest.current.inset;\n const next = clampCanvasWidth(desired, rootWidth(), latest.current.inset);\n draggedWidth = next;\n root.style.setProperty(\"--agents-canvas-width-drag\", `${next}px`);\n\n // Past the narrowest the edge stops and the tab stretches after the pointer; past the widest it holds its lean.\n const beyond = Math.min(max, Math.max(min, desired)) - desired;\n const moved = moveEvent.clientX - lastX;\n if (moved !== 0) lean = Math.sign(moved) * LEAN_PX;\n lastX = moveEvent.clientX;\n const tabFor = (currentLean: number) =>\n beyond > 0 ? Math.max(LEAN_PX, rubberBand(beyond)) : beyond < 0 ? -LEAN_PX : currentLean;\n setTabX(tabFor(lean));\n window.clearTimeout(timers.current.lean);\n timers.current.lean = window.setTimeout(() => {\n lean = 0;\n setTabX(tabFor(0));\n }, LEAN_REST_MS);\n\n if (next === min && beyond >= CLOSE_PULL_PX) {\n if (!isArmedNow && !timers.current.arm) {\n timers.current.arm = window.setTimeout(() => {\n timers.current.arm = 0;\n isArmedNow = true;\n setIsArmed(true);\n }, ARM_DELAY_MS);\n }\n } else {\n disarm();\n }\n };\n\n const finish = () => {\n handle.removeEventListener(\"pointermove\", move);\n handle.removeEventListener(\"pointerup\", finish);\n handle.removeEventListener(\"pointercancel\", finish);\n window.clearTimeout(timers.current.lean);\n const closes = isArmedNow;\n disarm();\n isDraggingRef.current = false;\n setIsDragging(false);\n setTabX(0);\n magnetise(null, null);\n delete root.dataset.resizing;\n // Committed in the same frame the drag variable goes, so the edge never flashes back to the old width.\n flushSync(() => latest.current.onWidthChange(closes ? null : (draggedWidth ?? width)));\n root.style.removeProperty(\"--agents-canvas-width-drag\");\n if (closes) {\n setNear(false);\n setIsHovered(false);\n latest.current.onClose();\n }\n };\n handle.addEventListener(\"pointermove\", move);\n handle.addEventListener(\"pointerup\", finish);\n handle.addEventListener(\"pointercancel\", finish);\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n const { min, max } = canvasWidthLimits(rootWidth(), inset);\n const next =\n event.key === \"ArrowLeft\"\n ? currentWidth() + KEY_STEP_PX\n : event.key === \"ArrowRight\"\n ? currentWidth() - KEY_STEP_PX\n : event.key === \"Home\"\n ? max\n : event.key === \"End\"\n ? min\n : null;\n if (next === null) return;\n event.preventDefault();\n onWidthChange(clampCanvasWidth(next, rootWidth(), inset));\n };\n\n const tab: CanvasResizeTab = isArmed ? \"armed\" : isDragging ? \"drag\" : isHovered ? \"hover\" : \"idle\";\n const { min, max } = canvasWidthLimits(rootWidth(), inset);\n return {\n tab,\n tabX,\n handleProps: {\n ref: handleRef,\n role: \"separator\",\n \"aria-orientation\": \"vertical\" as const,\n \"aria-valuemin\": min,\n \"aria-valuemax\": Math.round(max),\n \"aria-valuenow\": width ?? undefined,\n tabIndex: 0,\n onPointerDown: startDrag,\n onPointerEnter: () => setIsHovered(true),\n onPointerLeave: () => setIsHovered(false),\n onKeyDown,\n onDoubleClick: () => onWidthChange(null),\n },\n };\n};\n","import { CSSProperties, RefObject } from \"react\";\nimport { translateText } from \"../../provider/environment\";\nimport { useCanvasResize } from \"./use-canvas-resize\";\nimport styles from \"./CanvasResizeHandle.module.scss\";\n\nexport interface CanvasResizeHandleProps {\n rootRef: RefObject<HTMLElement | null>;\n inset: number;\n width: number | null;\n onWidthChange: (width: number | null) => void;\n onClose: () => void;\n}\n\n/** The pull tab on the canvas's left edge: drag to resize, pull past the narrowest and hold to close. */\nexport const CanvasResizeHandle = (props: CanvasResizeHandleProps) => {\n const { rootRef, inset, width, onWidthChange, onClose } = props;\n const { tab, tabX, handleProps } = useCanvasResize({ rootRef, inset, width, onWidthChange, onClose });\n\n return (\n <div\n {...handleProps}\n className={styles.handle}\n data-tab={tab}\n aria-label={translateText(\"Resize canvas\")}\n >\n <span className={styles.tab} style={{ \"--agents-resize-tab-x\": `${tabX}px` } as CSSProperties} aria-hidden=\"true\">\n <span className={styles.tabCircle} />\n <span className={styles.tabTop} />\n <span className={styles.tabBottom} />\n </span>\n </div>\n );\n};\n","import { useEffect, useRef } from \"react\";\nimport { canvasBehaviourOf } from \"../renderer/canvas/canvas-behaviours\";\nimport { useConversationWidgetContext } from \"../renderer/ConversationWidgetContext\";\n\nexport interface CanvasShellTransitionsInput {\n /** The conversation the view shows; undefined for a cold chat that has none yet. */\n conversationId?: number;\n /** The viewport is wide enough for the side split (`useCanvasViable`). */\n canvasViable: boolean;\n}\n\n/**\n * The two SHELL-driven transitions of the canvas model that `ConversationView` owns (the widget-driven ones go through\n * `openCanvas`; the send boundary is the hosts'):\n *\n * - **A conversation switch** (history select / new chat) makes the previous conversation's canvas stale — it is\n * cleared so the new conversation starts with a closed canvas. History replay never auto-opens a canvas (the\n * opening rule rejects replay), and the LEFT conversation keeps its stored pointer for when the customer comes\n * back. Only a REAL switch between two conversations counts. Not the first mount: a canvas the host opened on open\n * (the `?agentWidget=` deep-link, a restored canvas) mounts its widget BEFORE this parent effect runs, and an\n * unconditional reset would tear it down before the customer ever sees it. And not the id ARRIVING on a chat that\n * had none: that is the same conversation, just persisted now (the first send creates it) — closing the customer's\n * pinned work surface at that moment would be wrong.\n * - **The wide → narrow crossing** closes the side-split canvas as a shell close (re-openable, no customer veto).\n * Keyed on `canvasViable` ONLY (the canvas read through a ref) so it fires on the crossing, NOT when the mobile CTA\n * opens a canvas on an already narrow viewport. Skipped for a canvas whose behaviour says `narrowViewport:\n * \"overlay\"`: its widget renders nothing inline, so closing it on the crossing would leave the chat with no content\n * at all — it stays open as the full-screen overlay.\n */\nexport const useCanvasShellTransitions = ({ conversationId, canvasViable }: CanvasShellTransitionsInput): void => {\n const { canvas, closeCanvas, resetCanvas } = useConversationWidgetContext();\n const canvasBehaviour = canvas ? (canvasBehaviourOf(canvas.canvasId) ?? null) : null;\n\n const previousConversationIdRef = useRef(conversationId);\n useEffect(() => {\n const previousConversationId = previousConversationIdRef.current;\n previousConversationIdRef.current = conversationId;\n if (previousConversationId === undefined || previousConversationId === conversationId) return;\n resetCanvas();\n }, [conversationId, resetCanvas]);\n\n const canvasClosesOnNarrowRef = useRef(false);\n canvasClosesOnNarrowRef.current = canvasBehaviour !== null && canvasBehaviour.narrowViewport === \"none\";\n useEffect(() => {\n if (!canvasViable && canvasClosesOnNarrowRef.current) closeCanvas(false);\n }, [canvasViable, closeCanvas]);\n};\n","import { Button, SplitView } from \"../provider/components\";\nimport type { ChatAssistantCategory, ChatAssistantIdentity, ChatAssistantOpening } from \"./chat-assistant-identity\";\nimport { classNames } from \"../provider/class-names\";\nimport React, { CSSProperties, useRef, useState } from \"react\";\nimport { useCanvasViable } from \"../widgets/CanvasWidgetCard/use-canvas-viable\";\nimport {\n CANVAS_BOXED_INSET_PX,\n CANVAS_MIN_WIDTH_PX,\n CHAT_COLUMN_MIN_WIDTH_PX,\n DEFAULT_CANVAS_WIDTH_PERCENT,\n type CanvasId,\n canvasBehaviourOf,\n canvasWidthPercentOf,\n} from \"../renderer/canvas/canvas-behaviours\";\nimport { ConversationViewBody } from \"./ConversationViewBody/ConversationViewBody\";\nimport { ConversationErrorType } from \"./store/conversation-error-type\";\nimport type { ChatMessage } from \"./chat-message\";\nimport { ConversationMessageUserTheme } from \"./ConversationMessage/ConversationMessageUser/ConversationMessageUser\";\nimport { ConversationMessageInputHandle } from \"./ConversationMessageInput/ConversationMessageInput\";\nimport type { ConversationAttachmentReference } from \"../contract/conversation\";\nimport { SendMessageHandler } from \"./conversation-send-message\";\nimport { CanvasPane } from \"./CanvasPane/CanvasPane\";\nimport { CanvasResizeHandle } from \"./CanvasResizeHandle/CanvasResizeHandle\";\nimport { useCanvasShellTransitions } from \"./use-canvas-shell-transitions\";\nimport { useConversationWidgetContext } from \"../renderer/ConversationWidgetContext\";\nimport classes from \"./Conversation.module.scss\";\n\nexport interface ConversationViewProps {\n currentCategory?: ChatAssistantCategory; // Type from categories\n aiPromptExtension?: ChatAssistantIdentity; // Type from agents\n suggestions?: ChatAssistantOpening[]; // Type from conversationOpenings\n messages: ChatMessage[] | null; // Type from messages\n aiThinking: boolean;\n error: ConversationErrorType | null;\n /** Retry-After seconds of a 429 quota rejection — forwarded to the error card for the \"try again in …\" wording. */\n rateLimitRetryAfterSeconds?: number | null;\n /**\n * Host-bound retry of a FAILED run: re-drives the persisted, unanswered customer message\n * server-side. Each host binds it to ITS chat slice; forwarded through the body to the message list / error card.\n */\n onRetryFailedRun?: () => Promise<void>;\n /**\n * True when the host (the agent) is minimized to the compact side-panel; forwarded to ConversationViewBody.\n */\n isCompact?: boolean;\n isDebugMode: boolean;\n categoriesLoaded: boolean;\n conversationId?: number;\n startingNewConversation?: boolean;\n /**\n * Show the message list even when no conversation exists yet. Used by a host,\n * whose fresh (not-yet-created) chat renders its empty message list.\n */\n showMessagesWithoutConversation?: boolean;\n /**\n * Follow the conversation bottom (scroll-on-mount + resize follow). Default true.\n * A host disables it for a fresh chat that has no messages yet.\n */\n autoScroll?: boolean;\n /**\n * Custom node mounted at the tail of the message list instead of the default\n * (thinking-only) ConversationMessageThinking. Rendered permanently — used by\n * A host to show the animated agent face, which idles between turns.\n */\n thinkingIndicator?: React.ReactNode;\n /**\n * Hide the assistant header (icon + name + \"Assistant\") and the copy button\n * on assistant messages. Used by a host, which renders bare messages.\n */\n hideAssistantMeta?: boolean;\n /**\n * Theme for user-message bubbles, passed through to ConversationViewBody →\n * ConversationMessageList. A host forces \"dark\"; omitted elsewhere.\n */\n userMessageTheme?: ConversationMessageUserTheme;\n /**\n * Custom background slot passed through to ConversationViewBody. When provided, the\n * default chat gradient and AI-Extensions illustrations are suppressed.\n */\n background?: React.ReactNode;\n /**\n * Opt-in: the host paints the chat's background itself, OUTSIDE this component — passed through to ConversationViewBody,\n * which keeps the same `customBg` visual mode (transparent pane) `background` would give it, minus\n * a background node of its own to render (see ConversationViewBody's `backgroundPaintedByHost`).\n */\n backgroundPaintedByHost?: boolean;\n /**\n * Optional ref forwarded to ConversationViewBody's scrollable content wrapper.\n * Callers use this to drive scroll-linked effects on top of the chat — e.g.\n * `useShaderScrollMorph` for the animated background.\n */\n scrollRef?: React.Ref<HTMLDivElement>;\n /** Bump to force a scroll to the newest message — forwarded to ConversationViewBody (see its `scrollToBottomNonce`). */\n scrollToBottomNonce?: number;\n /**\n * Optional external bottom-follow flag, forwarded to ConversationViewBody (see its `followBottomRef`). A host sets it `false`\n * for a programmatic jump away from the bottom (e.g. the search-result jump) so the follow effects stop yanking the\n * view back. Left undefined, ConversationViewBody uses its own internal flag.\n */\n followBottomRef?: React.MutableRefObject<boolean>;\n /**\n * Optional \"a programmatic jump is in flight\" gate, forwarded to ConversationViewBody (see its `scrollJumpActiveRef`). While\n * true, ConversationViewBody neither re-enables bottom-follow on scroll nor auto-scrolls to the bottom on resize, so the\n * host's jump isn't fought by the near-bottom onScroll handler or the resize-follow. Left undefined → no gating.\n */\n scrollJumpActiveRef?: React.MutableRefObject<boolean>;\n /**\n * Render the chat body alone, without the two-pane ModalSplitView. The split view's\n * second pane is sized for a full-width modal, so in a narrow side panel (e.g. an\n * app's editor drawer) it overlays whatever sits beside the panel — and those hosts\n * don't use widget content-drawers anyway.\n */\n disableSplitView?: boolean;\n /**\n * Opt-in: show the round divider FAB that toggles the canvas (open → close, closed → re-open the last canvas).\n * Off by default — ConversationView is shared (generic AI-extensions chat, a host, agent); only hosts that want the\n * divider toggle (agent) enable it.\n */\n enableCanvasToggle?: boolean;\n /**\n * Opt-in (agent): render the side-split canvas as a rounded surface card (border-radius + surface background)\n * so it sits neatly alongside the left side of the host's floating top bar, instead of a flush full-height pane.\n * Only the card surface is gated here and only while the canvas is a side-split (viewport ≥ 850px); the small\n * inset that keeps the canvas clear of the pane edge applies to every canvas (see CanvasPane `.root`),\n * including the narrow full-screen overlay below 850px (where the host hides its top bar, AW-57).\n */\n boxCanvasBelowTopBar?: boolean;\n /**\n * Opt-in (agent host only): confine the customBg top blur band to the chat pane (position: sticky, sized to\n * the pane) instead of the whole viewport (position: fixed, the default for a custom `background` in full-screen\n * mode). Turn on only while a sibling side panel is docked beside the chat (sideCard mode) — otherwise the band\n * would stay full-viewport-width and bleed over the panel instead of compressing together with the chat. Forwarded\n * straight through to ConversationViewBody, which owns the actual CSS (see its `.confineTopBlurBandToChatPane`).\n */\n confineTopBlurBandToChatPane?: boolean;\n /**\n * The setup progress stepper (today's only consumer: the agent chat), forwarded straight through to\n * ConversationViewBody, which mounts it as a direct child of its positioned pane root — the node brings its own\n * positioning. It lands INSIDE this view's split, so an open canvas contracts it along with the chat; that pane\n * boundary is why the host cannot place the strip itself.\n */\n progressStepperSlot?: React.ReactNode;\n /**\n * Scroll-up older-message pagination (opt-in — forwarded to ConversationViewBody). When `onLoadOlder` is provided, a top\n * sentinel prefetches the next older page and the viewport is anchored across the prepend. Default off.\n */\n hasMoreOlder?: boolean;\n loadingOlder?: boolean;\n onLoadOlder?: () => void;\n /**\n * Extra action controls rendered inside the chat input's action row, left of the send button —\n * spread through to ConversationViewBody → ConversationMessageInput (e.g. the agent model picker pill).\n */\n inputActionsSlot?: React.ReactNode;\n /**\n * Action controls pinned to the LEFT edge of the chat input's action row (`inputActionsSlot`\n * stays right) — spread through to ConversationViewBody → ConversationMessageInput (e.g. the agent \"+\" attachments menu).\n */\n inputActionsSlotStart?: React.ReactNode;\n /**\n * Host-owned content rendered inside the chat input above its editable text area — attachment\n * preview cards, validation error boxes. Spread through to ConversationViewBody → ConversationMessageInput's `topSlot`.\n */\n inputTopSlot?: React.ReactNode;\n /**\n * Hard send gate for the chat input: blocks the send button AND Enter while true, but keeps the\n * text editable. The host sets it e.g. while attachment uploads run.\n */\n inputSendDisabled?: boolean;\n /**\n * Host-owned content rendered IN PLACE of the chat input — the closed-conversation bar of a completed\n * setup conversation (no further input possible). When set, ConversationViewBody renders it instead of ConversationMessageInput.\n */\n inputReplacementSlot?: React.ReactNode;\n /**\n * Host-owned band right above the chat input, the input overlapping its lower edge — e.g. the running voice chat\n * with its controls. Not rendered while `inputReplacementSlot` replaces the input.\n */\n inputBandSlot?: React.ReactNode;\n /** The chat input's placeholder; the input's own default otherwise. */\n inputPlaceholder?: string;\n /** Forwarded to ConversationMessageInput's `sendReplacementWhenEmpty`. */\n inputSendReplacementWhenEmpty?: React.ReactNode;\n /** Forwarded to ConversationMessageInput's `glowUnderlay`. */\n inputGlowUnderlay?: React.ReactNode;\n /**\n * Host-owned layer over the message column, the same size — e.g. the voice chat's focus mode. The message column\n * stays mounted under it (scroll position and widget state kept); `isContentOverlayShown` switches between them.\n */\n contentOverlaySlot?: React.ReactNode;\n isContentOverlayShown?: boolean;\n /**\n * Uploaded media-library images that ride along in `onSendMessage`'s options object (`attachments`) on the next\n * send. Owned and cleared by the host; ConversationView/ConversationMessageInput only forward them.\n */\n outgoingAttachmentImages?: ConversationAttachmentReference[];\n /**\n * Imperative handle on the chat input (insertTextIntoInput) so host-owned controls — e.g. the\n * \"+\" menu's \"Generate image\" entry — can put text into it.\n */\n conversationMessageInputApiRef?: React.Ref<ConversationMessageInputHandle>;\n /**\n * Keep the chat input editable and sendable while `aiThinking` is true (default false — the input locks for the\n * whole turn); only the first post of a new conversation still locks it, while no conversation id exists yet. Opt\n * in ONLY for a host whose post call stores the message and returns at once, and whose agent checks for unanswered\n * messages when its turn ends, so a message sent mid-turn is answered in the next turn. A host whose post call\n * generates the reply synchronously within the request has no such guard and must leave it off.\n */\n allowSendWhileAiThinking?: boolean;\n /**\n * Opt-in long-answer reading flow (AW-107), default false — spread through to ConversationViewBody. When enabled:\n * (1) on send, the question is pinned to the top with room reserved below it; (2) a scroll-to-latest-message\n * button floats above the input while the bottom is out of view; (3) a fresh static reply's markdown blocks\n * fade in staggered. AI Extensions stays off; the agent chat opts in.\n */\n enablePinQuestionAndRevealAnswer?: boolean;\n /** Forwarded to ConversationViewBody's `isQuestionPinSuspended`. */\n isQuestionPinSuspended?: boolean;\n /**\n * Id of the ONE user message ConversationMessageList renders as a collapsed task-instruction disclosure instead of an\n * owner bubble — the machine-composed seed instruction of a SCHEDULE-spawned run (UX overhaul W4 item 19).\n * Forwarded through ConversationViewBody. Undefined (every host except the agent chat) changes nothing.\n */\n taskInstructionMessageId?: number;\n /**\n * Host content rendered right after one message of the thread — the agent chat's activity card under the message that\n * started each turn. Forwarded through ConversationViewBody to ConversationMessageList. Undefined changes nothing.\n */\n renderAfterMessage?: (message: ChatMessage) => React.ReactNode;\n /** Forwarded through ConversationViewBody to ConversationMessageList: a closed conversation renders no widget controls. */\n isConversationClosed?: boolean;\n onSuggestionClick: (text: string) => void;\n onSendMessage: SendMessageHandler;\n}\n\nexport const ConversationView = (props: ConversationViewProps) => {\n const { enableCanvasToggle, boxCanvasBelowTopBar, onSendMessage, conversationId, messages } = props;\n const { canvas, lastClosedCanvas, closeCanvas, reopenLastClosedCanvas, resetCanvasForNewTurn } =\n useConversationWidgetContext();\n const canvasViable = useCanvasViable();\n // The canvas is a pointer in the chat's slice; its geometry comes from its behaviour (`canvas-behaviours.ts`).\n const canvasBehaviour = canvas ? (canvasBehaviourOf(canvas.canvasId) ?? null) : null;\n const isCanvasOpen = canvas !== null;\n // The conversation switch clears the canvas; the wide → narrow crossing closes a `narrowViewport: \"none\"` canvas.\n useCanvasShellTransitions({ conversationId, canvasViable });\n\n // A new user message starts a new turn — the previous turn's canvas is now stale, so tear it down AND drop the\n // divider FAB (the reset clears `canReopenDrawer`). Runs before the reply arrives, so a fresh canvas opened by the\n // new reply is unaffected. A PINNED canvas (a work surface for the whole conversation, not one\n // turn's result) survives — that is the only difference to `resetCanvas`.\n const handleSendMessage: SendMessageHandler = (message, options) => {\n resetCanvasForNewTurn();\n onSendMessage(message, options);\n };\n\n // The canvas pane starts at the width its behaviour declares; the customer can pull its left edge (`CanvasResizeHandle`).\n // The split reads the width from `--agents-canvas-width-set` (a percentage, or the clamped pull in px), so a drag moves the edge without a\n // render. The width of the last shown canvas is kept while the pane slides out. The divider toggle + slide derive from it.\n const lastCanvasWidthPercentRef = useRef(DEFAULT_CANVAS_WIDTH_PERCENT);\n if (canvasBehaviour) lastCanvasWidthPercentRef.current = canvasWidthPercentOf(canvasBehaviour);\n const canvasWidthPercent = lastCanvasWidthPercentRef.current;\n const boxCanvas = !!boxCanvasBelowTopBar && canvasViable;\n const canvasInset = boxCanvas ? CANVAS_BOXED_INSET_PX : 0;\n const splitViewRootRef = useRef<HTMLDivElement>(null);\n // The pull belongs to the canvas it was made on: another canvas starts at its own default split.\n const lastCanvasIdRef = useRef<CanvasId | null>(null);\n if (canvas) lastCanvasIdRef.current = canvas.canvasId;\n const [pulledCanvas, setPulledCanvas] = useState<{ canvasId: CanvasId; width: number } | null>(null);\n const pulledCanvasWidth = pulledCanvas?.canvasId === lastCanvasIdRef.current ? pulledCanvas.width : null;\n const setPulledCanvasWidth = (width: number | null) =>\n setPulledCanvas(width !== null && lastCanvasIdRef.current ? { canvasId: lastCanvasIdRef.current, width } : null);\n const canvasResizable = canvasBehaviour?.layout === \"split\" && canvasViable && !props.isCompact;\n const splitViewRootStyle = {\n \"--agents-canvas-width-set\":\n pulledCanvasWidth === null\n ? `${canvasWidthPercent}%`\n : `clamp(${CANVAS_MIN_WIDTH_PX + canvasInset * 2}px, ${pulledCanvasWidth}px, calc(100% - ${CHAT_COLUMN_MIN_WIDTH_PX}px))`,\n \"--agents-canvas-inset\": `${canvasInset}px`,\n } as CSSProperties;\n\n // The canvas toggle button on the divider: closes the open canvas (the customer's own close), re-opens the last\n // closed one.\n const toggleCanvas = () => {\n if (canvas) closeCanvas(true);\n else reopenLastClosedCanvas();\n };\n // On a narrow viewport the divider (and its toggle) does not exist: the canvas is a full-screen overlay. The canvases\n // whose widgets render nothing inline would have no way back after a close, so the shell offers its own re-open\n // control over the chat, fed by `lastClosedCanvas`, independent of transcript windowing.\n const showsNarrowReopenControl = !!enableCanvasToggle && !canvasViable && !isCanvasOpen && lastClosedCanvas !== null;\n\n if (props.disableSplitView) {\n return <ConversationViewBody {...props} />;\n }\n\n return (\n <div ref={splitViewRootRef} className={classes.splitViewRoot} style={splitViewRootStyle}>\n {showsNarrowReopenControl ? (\n <div className={classes.canvasReopenControl}>\n <Button\n variant=\"muted\"\n icon=\"maximize\"\n iconPosition={\"left\"}\n rounded\n blurred\n size=\"small\"\n onClick={reopenLastClosedCanvas}\n >\n Re-open canvas\n </Button>\n </div>\n ) : null}\n <SplitView\n isSideOpen={isCanvasOpen}\n coversBelowWidth={850}\n // The canvas toggle button on the divider: closes the open canvas, or re-opens the last closed one. Shown only\n // while a canvas is open or one was closed — otherwise there's nothing to toggle.\n sideToggle={\n enableCanvasToggle && !canvasResizable && (isCanvasOpen || lastClosedCanvas !== null)\n ? {\n onToggle: toggleCanvas,\n // Centered vertically on the divider.\n verticalPositionPercent: 50,\n }\n : undefined\n }\n main={{\n width: \"100%\",\n narrowedWidth: \"calc(100% - var(--agents-canvas-width))\",\n // The pane's default inline background (var(--agents-neutral-000)) would occlude a host-painted background layer.\n background: props.backgroundPaintedByHost ? \"transparent\" : undefined,\n content: <ConversationViewBody {...props} onSendMessage={handleSendMessage} />,\n }}\n side={{\n width: \"var(--agents-canvas-width)\",\n narrowedWidth: \"var(--agents-canvas-width)\",\n content: (\n // Boxed only while side-split: below 850px the canvas is a full-screen overlay and stays full-bleed.\n <div className={classNames(classes.splitViewPane, boxCanvas ? classes.splitViewPaneBoxed : \"\")}>\n <CanvasPane messages={messages} boxed={boxCanvas} />\n </div>\n ),\n }}\n />\n {canvasResizable ? (\n <CanvasResizeHandle\n rootRef={splitViewRootRef}\n inset={canvasInset}\n width={pulledCanvasWidth}\n onWidthChange={setPulledCanvasWidth}\n onClose={() => closeCanvas(true)}\n />\n ) : null}\n </div>\n );\n};\n","import type { ConversationMessageStatus } from \"../../contract/conversation\";\nimport type { ChatMessage } from \"../chat-message\";\n\n/** Mirrors App\\Domain\\AI\\Entities\\Conversations\\Agent\\Tools\\DelegateTool::NAME. */\nconst DELEGATE_TOOL_NAME = \"delegate\";\n\n/** Terminal tool-call statuses — mirrors ToolCall::isResolved (COMPLETED / FAILED / CANCELLED). */\nconst RESOLVED_STATUSES: string[] = [\"COMPLETED\", \"FAILED\", \"CANCELLED\"] satisfies ConversationMessageStatus[];\nconst isResolved = (status?: string | null): boolean => status != null && RESOLVED_STATUSES.includes(status);\n\n/** Terminal child run-states — mirrors AIConversationRunState::TERMINAL (COMPLETED / FAILED / CANCELLED). */\nconst TERMINAL_RUN_STATES: string[] = [\"COMPLETED\", \"FAILED\", \"CANCELLED\"];\n\n/** A sub-agent run still waiting to start or actively working (mirrors DelegationSubAgentRun::STATUS_*). */\nconst UNRESOLVED_RUN_STATUSES: string[] = [\"QUEUED\", \"RUNNING\"];\n\n/**\n * A tool call is EFFECTIVELY resolved when its stored status says so — or, for a `delegate` call, when every\n * expanded child already reached a terminal run state: the children are the truth of a fan-out, the stored\n * status is a mirror that a write race can leave stale (a delegate call left RUNNING kept the whole customer\n * chat \"thinking\" for the rest of a setup). An empty or missing expansion proves nothing and falls back to the status.\n */\nconst isEffectivelyResolved = (message: ChatMessage | undefined): boolean => {\n if (message == null) return false;\n if (isResolved(message.toolCall?.status)) return true;\n if (message.toolCall?.name !== DELEGATE_TOOL_NAME) return false;\n const children = message.delegatedConversations ?? [];\n if (children.length === 0) return false;\n // A partial expansion (page cap) proves nothing: the call declared N tasks, N children must be visible.\n const declaredTasks = (message.toolCall?.arguments as { tasks?: unknown[] } | undefined)?.tasks;\n if (Array.isArray(declaredTasks) && declaredTasks.length > children.length) return false;\n return children.every((child) => TERMINAL_RUN_STATES.includes(child.runState ?? \"\"));\n};\n\n/** How many sub-agents spawned by this message are still working. */\nexport const countOutstandingChildren = (message: ChatMessage): number => {\n const children = message.delegatedConversations ?? [];\n if (children.length > 0) {\n return children.filter((child) => !TERMINAL_RUN_STATES.includes(child.runState ?? \"\")).length;\n }\n if (message.toolCall?.name !== DELEGATE_TOOL_NAME) {\n return 0;\n }\n // A NEW row (one `delegate` call = one sub-agent) carries its own run record instead of\n // `delegatedConversations`; no child expanded, so the record's CURRENT run is the truth of whether it is done.\n const subAgent = message.subAgent;\n if (subAgent != null) {\n const currentRun = (subAgent.runs ?? []).find((run) => run.runNumber === subAgent.currentRunNumber);\n return currentRun != null && UNRESOLVED_RUN_STATUSES.includes(currentRun.status ?? \"\") ? 1 : 0;\n }\n if (isResolved(message.toolCall?.status)) {\n return 0;\n }\n const declaredTasks = (message.toolCall?.arguments as { tasks?: unknown[] } | undefined)?.tasks;\n return Array.isArray(declaredTasks) && declaredTasks.length > 0 ? declaredTasks.length : 1;\n};\n\n/** A tool call whose result is still to come — its progress lives in `toolCall.status`, never in the row's status. */\nexport const hasUnresolvedToolCall = (message: ChatMessage): boolean =>\n message.toolCall != null && !isEffectivelyResolved(message);\n\n/** Whether this message's delegation can still change: a child is still working, or the call is not resolved yet. */\nexport const hasDelegationInFlight = (message: ChatMessage): boolean =>\n countOutstandingChildren(message) > 0 ||\n (message.toolCall?.name === DELEGATE_TOOL_NAME && !isEffectivelyResolved(message));\n\nexport type AgentActivity = {\n /** Whether a turn is in flight — drives the indicator's visibility (ConversationView aiThinking). */\n active: boolean;\n /**\n * Translation source string for \"what the agent is doing right now\" (empty when nothing to show). Rendered through\n * the chat's `Text`, so this is the translatable key with %placeholders% — NOT pre-interpolated. The dynamic values\n * arrive via {@link AgentActivity.replacements} and plural form via {@link AgentActivity.context}.\n */\n label: string;\n /** Dynamic values substituted into the %placeholders% of {@link AgentActivity.label} by <Text>. */\n replacements?: Record<string, string | number>;\n /** Plural selector for <Text> when the label is count-dependent. */\n context?: \"one\" | \"other\";\n /** A non-delegate tool is running → show the ASCII wrench spinner instead of a text label. */\n toolRunning?: boolean;\n};\n\n/**\n * Derive \"what the agent is doing right now\" for the agent activity indicator PURELY from the\n * live conversation data: the message tree (refreshed on each realtime-signal tick / on open) plus the signal's\n * coarse runStatus (\"running\" | \"idle\" | \"failed\" | null). No network. Mirrors the backend truth —\n * AIConversation.runState (IDLE/RUNNING/AWAITING_DELEGATES/…) and per-message status/messageType/toolCall — but reads\n * it off the messages the FE already holds, so the indicator is correct even before the first signal emission.\n */\nexport const deriveAgentActivity = (\n messages: ChatMessage[] | null,\n runStatus: string | null,\n): AgentActivity => {\n if (runStatus === \"failed\") {\n // Failure surfaces via ConversationView's error state, not the activity line.\n return { active: false, label: \"\" };\n }\n\n // An optimistic (just-sent, not yet server-assigned) row carries no index — it belongs at the TAIL, the newest\n // thing in the conversation, never at the front, where `?? 0` would park it ahead of every real message.\n const sorted = [...(messages ?? [])].sort(\n (a, b) => (a.indexInConversation ?? Number.POSITIVE_INFINITY) - (b.indexInConversation ?? Number.POSITIVE_INFINITY),\n );\n const lastMessage = sorted[sorted.length - 1];\n\n // The owner just sent — the optimistic bubble, or a saved row still awaiting the first response — starts the face\n // thinking immediately, before the server's \"running\" ping arrives (which can lag the send by about a second);\n // checked before the `idle` short-circuit below, whose `runStatus` can still report the PREVIOUS turn's rest state.\n // A rejected POST resets `aiThinking` itself (conversations-message-post.case.ts), so no separate reset is needed.\n if (lastMessage?.role === \"USER\") {\n return { active: true, label: \"\" };\n }\n\n // Sub-agents still working → the orchestrator is parked on its children (backend runState AWAITING_DELEGATES).\n // Counted per CHILD, not per delegate message: a fan-out (`delegate` with N tasks, or a tool result carrying\n // `delegationTasks`, e.g. a LARGE website edit's page packets) is ONE message with N children. The expanded\n // `delegatedConversations` carry each child's runState; without the expand, a `delegate` call still awaiting its\n // verdict counts its declared tasks, else one.\n const unresolvedDelegateCount = sorted.reduce((count, message) => count + countOutstandingChildren(message), 0);\n\n // The turn is at rest and nothing is outstanding → idle, no indicator.\n if (runStatus === \"idle\" && unresolvedDelegateCount === 0) {\n return { active: false, label: \"\" };\n }\n\n if (unresolvedDelegateCount > 0) {\n return {\n active: true,\n label: \"Waiting for %count% delegations...\",\n replacements: { count: unresolvedDelegateCount },\n context: unresolvedDelegateCount === 1 ? \"one\" : \"other\",\n };\n }\n\n // A tool-call that hasn't resolved yet → that tool is running now (a delegate call whose children all finished\n // is resolved in effect, whatever its stored status says).\n if (lastMessage?.messageType === \"TOOL_CALL\" && !isEffectivelyResolved(lastMessage)) {\n return {\n active: true,\n label: \"Running: %tool%...\",\n replacements: { tool: lastMessage.toolCall?.name ?? \"Tool\" },\n toolRunning: true,\n };\n }\n\n // The model is producing a turn (signal running) — a trailing owner row was already caught above.\n if (runStatus === \"running\") {\n return { active: true, label: \"\" };\n }\n\n return { active: false, label: \"\" };\n};\n","/**\n * A start an agent entry point outside the widget handed over: a visible USER message (`message`), or the invisible\n * START signal behind an EMPTY visible message (`signal`), which makes the agent speak first.\n */\nexport interface PendingStartRequest {\n kind: \"message\" | \"signal\";\n text: string;\n}\n\n/**\n * The one start the chat should send from the two hand-over fields (the opening request's `userMessage` and\n * `startSignalText`). The app's openers keep at most one of the two set — each clears the\n * other's leftover on the way in — but the message still wins here if both somehow are, since the customer's own words\n * are the stronger request.\n */\nexport const pickPendingStartRequest = (\n pendingStartUserMessage: string | null,\n pendingStartSignalText: string | null,\n): PendingStartRequest | null => {\n if (pendingStartUserMessage) {\n return { kind: \"message\", text: pendingStartUserMessage };\n }\n if (pendingStartSignalText) {\n return { kind: \"signal\", text: pendingStartSignalText };\n }\n return null;\n};\n\nexport interface ChatStateForPendingStartRequest {\n /** The start an agent entry point outside the widget handed over (`pickPendingStartRequest`). */\n pendingStartRequest: PendingStartRequest | null;\n /** The conversation the widget currently wants open (the chat's `conversationId` prop). */\n activeConversationId: number | null | undefined;\n /** The conversation the chat slice currently holds — set until a new-chat reset clears it. */\n loadedConversationId: number | null | undefined;\n /** A start-with-message POST is already in flight (the chat store's `startingNewConversation`). */\n startingNewConversation: boolean;\n /** The start this chat has already sent, so the same one is never posted twice. */\n alreadySentStartRequest: PendingStartRequest | null;\n}\n\n/**\n * Whether the chat should send `pendingStartRequest` as the start of a NEW conversation right now. Called on every\n * relevant state change — NOT once on mount: the widget is often already open (and mounted) when an entry point\n * outside it hands over a request, which is what BUF-4009 was.\n *\n * \"Not yet\" (false) and \"never\" (false) deliberately look the same to the caller: the request stays in the slice, and\n * this is asked again on the next change. A conversation still being open is the \"not yet\" case — Agent drops to a new\n * chat when a request arrives, because every incentive CTA promises a fresh chat for its job, and this then says yes\n * on the cold chat that follows.\n */\nexport const isChatReadyToSendPendingStartRequest = ({\n pendingStartRequest,\n activeConversationId,\n loadedConversationId,\n startingNewConversation,\n alreadySentStartRequest,\n}: ChatStateForPendingStartRequest): boolean => {\n if (!pendingStartRequest) {\n return false;\n }\n // A conversation is open (or still loaded while the new chat is being set up) — the request must never be appended\n // to it. A start already in flight would be dropped by the send handler's own in-flight guard, losing the request.\n if (activeConversationId || loadedConversationId || startingNewConversation) {\n return false;\n }\n return (\n alreadySentStartRequest?.kind !== pendingStartRequest.kind ||\n alreadySentStartRequest.text !== pendingStartRequest.text\n );\n};\n","import type { ConversationActivityElement } from \"../../contract/conversation\";\n\n/**\n * What the activity read sends per element and the stack reads (`ConversationActivityElement`); every field may be\n * missing on an element a caller builds by hand (stories, specs).\n */\nexport type ActivityLineInput = Partial<ConversationActivityElement>;\n\n/** One row of the stack: the element as the endpoint sent it last, keyed by its tool-call message id. */\nexport type ActivityElement = ActivityLineInput & { toolCallMessageId: number };\n\nconst STATE_RANK: Record<string, number> = { queued: 0, running: 1, finished: 2, failed: 2, skipped: 2 };\n\n/**\n * Merge a response into the stack: a later state replaces an earlier one, a line never disappears once told (an\n * element answered without a line, because the narrator was late, keeps the sentence it already had), and an element\n * the response no longer carries stays (the turn snapshot is complete, so this only happens on a pinned turn that a\n * later turn pushed out of the window). Row order is the endpoint's: the order of the persisted rows.\n */\nexport const mergeActivityElements = (\n previous: ActivityElement[],\n incoming: ActivityLineInput[] | null | undefined,\n): ActivityElement[] => {\n const byId = new Map<number, ActivityElement>(previous.map((element) => [element.toolCallMessageId, element]));\n const ordered: number[] = [];\n (incoming ?? []).forEach((element) => {\n const id = element.toolCallMessageId;\n if (id == null || id <= 0) return;\n ordered.push(id);\n const known = byId.get(id);\n if (!known) {\n byId.set(id, { ...element, toolCallMessageId: id });\n return;\n }\n const knownRank = STATE_RANK[known.state ?? \"queued\"] ?? 0;\n const incomingRank = STATE_RANK[element.state ?? \"queued\"] ?? 0;\n // A state never moves backwards (a stale response after a fresher one).\n if (incomingRank < knownRank) return;\n const stateAdvanced = incomingRank > knownRank || known.state !== element.state;\n byId.set(id, {\n ...known,\n ...element,\n toolCallMessageId: id,\n // The finished line replaces the started one; a missing line keeps what was told, unless the state advanced\n // (then the started sentence would misdescribe a finished step, so the row shows its state alone).\n line: element.line ?? (stateAdvanced ? null : (known.line ?? null)),\n });\n });\n // Rows the response carried, in its order, then rows it no longer carries, in their previous order.\n const carried = new Set(ordered);\n const kept = previous\n .filter((element) => !carried.has(element.toolCallMessageId))\n .map((element) => element.toolCallMessageId);\n return [...kept, ...ordered].map((id) => byId.get(id)!);\n};\n\n/** The rows the customer reads: skipped elements carry nothing. */\nexport const visibleActivityElements = (elements: ActivityElement[]): ActivityElement[] =>\n elements.filter((element) => element.state !== \"skipped\");\n\n/**\n * Whether a response names a turn different from the one pinned locally — the stack must start clean instead of\n * merging the new turn's rows into the previous (already folded) one. False until a turn is pinned, and false while\n * the response has not named a turn yet (a request still in flight for the newly pinned, not-yet-answered turn).\n */\nexport const isDifferentTurn = (pinnedTurnMessageId: number | null, answeredTurnMessageId: number | null): boolean =>\n pinnedTurnMessageId !== null && answeredTurnMessageId !== null && answeredTurnMessageId !== pinnedTurnMessageId;\n","import { ActivityElement, visibleActivityElements } from \"./merge-activity-elements\";\n\n/**\n * The least time a line stands beside the face before the next one takes its place — the concept's rhythm (the agent\n * prototype's ThinkingIndicator swaps its line every 2.2 s), so every sentence can be read between two swaps.\n */\nexport const LINE_PACE_MS = 2200;\n/** The shorter wait while more than {@link BACKLOG_THRESHOLD} lines are queued, so a burst catches up. */\nexport const BACKLOG_LINE_PACE_MS = 1400;\nconst BACKLOG_THRESHOLD = 2;\n/**\n * More queued lines than this are shown at once, oldest first: one narrator answer carries up to 8 lines, and the\n * ticker must never trail the turn by more than a few seconds.\n */\nexport const MAX_WAITING_LINES = 4;\n/** Finished turns kept in the thread at most (keepActivityHistoryAfterAnswer); the oldest one falls away beyond. */\nexport const MAX_KEPT_TURNS = 30;\n\n/** One sentence the ticker shows: the started or the finished line of one step. */\nexport interface ActivityTickerLine {\n toolCallMessageId: number;\n /** The step's state when this line was told: queued / running / finished / failed. */\n state: string;\n line: string;\n kind: string | null;\n /** The delegation helper that ran the step (1-based); null for the assistant's own steps. */\n helperIndex: number | null;\n}\n\n/** Whether the line's step was still at work (queued or running) when the line was told, not finished or failed. */\nexport const isStepStillRunning = (line: ActivityTickerLine): boolean =>\n line.state === \"queued\" || line.state === \"running\";\n\n/**\n * What the ticker shows and what still waits to be shown. The endpoint delivers lines in bursts (one request per 3 s,\n * up to 8 lines per narrator answer); this queue hands them to the screen one at a time.\n */\nexport interface ActivityTickerQueue {\n /** The line each step shows right now, keyed by its tool-call message id. */\n shownLines: Record<number, ActivityTickerLine>;\n /** The steps shown before the newest one, oldest first; never contains {@link currentId}. */\n trailIds: number[];\n /** The step whose line was shown last: the one beside the face. */\n currentId: number | null;\n /** Lines not shown yet, oldest first, at most one per step (a newer line of the same step replaces the waiting one). */\n waitingLines: ActivityTickerLine[];\n /** When the newest line last changed (ms since epoch); the pace counts from here. */\n lastShownAt: number;\n}\n\nexport const EMPTY_ACTIVITY_TICKER_QUEUE: ActivityTickerQueue = {\n shownLines: {},\n trailIds: [],\n currentId: null,\n waitingLines: [],\n lastShownAt: Number.NEGATIVE_INFINITY,\n};\n\n/**\n * The element as a ticker line. An element without a sentence keeps the sentence already told for its step and takes\n * its new state: mergeActivityElements drops the started sentence when a step finishes before the narrator described\n * the finish, and the row must then show its check (or its failure) instead of running on. A step never told has\n * nothing to show.\n */\nconst toTickerLine = (\n element: ActivityElement,\n toldLine: ActivityTickerLine | undefined,\n): ActivityTickerLine | null => {\n const sentence = element.line ?? toldLine?.line;\n if (!sentence) return null;\n return {\n toolCallMessageId: element.toolCallMessageId,\n state: element.state ?? \"queued\",\n line: sentence,\n kind: element.kind ?? null,\n helperIndex: element.helperIndex ?? null,\n };\n};\n\nconst isSameLine = (shown: ActivityTickerLine | undefined, incoming: ActivityTickerLine) =>\n !!shown && shown.state === incoming.state && shown.line === incoming.line;\n\n/** Every shown line in the order it was told, oldest first. */\nexport const shownLinesInOrder = (queue: ActivityTickerQueue): ActivityTickerLine[] =>\n [...queue.trailIds, ...(queue.currentId !== null ? [queue.currentId] : [])]\n .map((stepId) => queue.shownLines[stepId])\n .filter(Boolean);\n\n/**\n * Put one line on screen. A line of a step already told replaces that step's line in place (the history keeps every\n * step once); any other line becomes the newest one, beside the face, and the previous newest one moves up into the\n * trail.\n */\nexport const showActivityLine = (\n queue: ActivityTickerQueue,\n line: ActivityTickerLine,\n now: number,\n): ActivityTickerQueue => {\n const stepId = line.toolCallMessageId;\n const shownLines = { ...queue.shownLines, [stepId]: line };\n if (queue.currentId === stepId) {\n return { ...queue, shownLines, lastShownAt: now };\n }\n if (queue.trailIds.includes(stepId)) {\n return { ...queue, shownLines };\n }\n const trailIds = queue.currentId !== null ? [...queue.trailIds, queue.currentId] : queue.trailIds;\n return { ...queue, shownLines, trailIds, currentId: stepId, lastShownAt: now };\n};\n\n/**\n * Take the endpoint's elements in: every line not shown yet waits in the queue, except the finished line of a step\n * already in the trail, which replaces that row in place at once. Skipped elements and elements of a step never told\n * a sentence never show (only generated sentences appear).\n */\nexport const receiveActivityElements = (\n queue: ActivityTickerQueue,\n elements: ActivityElement[],\n now: number,\n): ActivityTickerQueue => {\n let nextQueue = queue;\n visibleActivityElements(elements).forEach((element) => {\n const stepId = element.toolCallMessageId;\n const waitingLine = nextQueue.waitingLines.find((line) => line.toolCallMessageId === stepId);\n const incomingLine = toTickerLine(element, waitingLine ?? nextQueue.shownLines[stepId]);\n if (!incomingLine) return;\n if (waitingLine ? isSameLine(waitingLine, incomingLine) : isSameLine(nextQueue.shownLines[stepId], incomingLine)) {\n return;\n }\n if (nextQueue.trailIds.includes(stepId)) {\n nextQueue = {\n ...nextQueue,\n shownLines: { ...nextQueue.shownLines, [stepId]: incomingLine },\n waitingLines: nextQueue.waitingLines.filter((line) => line.toolCallMessageId !== stepId),\n };\n return;\n }\n nextQueue = {\n ...nextQueue,\n waitingLines: waitingLine\n ? nextQueue.waitingLines.map((line) => (line.toolCallMessageId === stepId ? incomingLine : line))\n : [...nextQueue.waitingLines, incomingLine],\n };\n });\n while (nextQueue.waitingLines.length > MAX_WAITING_LINES) {\n const [oldestWaitingLine, ...laterWaitingLines] = nextQueue.waitingLines;\n nextQueue = showActivityLine({ ...nextQueue, waitingLines: laterWaitingLines }, oldestWaitingLine, now);\n }\n return nextQueue;\n};\n\n/** Milliseconds until the next waiting line may be shown; null when nothing waits. */\nexport const msUntilNextActivityLine = (queue: ActivityTickerQueue, now: number): number | null => {\n if (queue.waitingLines.length === 0) return null;\n const pace = queue.waitingLines.length > BACKLOG_THRESHOLD ? BACKLOG_LINE_PACE_MS : LINE_PACE_MS;\n return Math.max(0, queue.lastShownAt + pace - now);\n};\n\n/** Show the oldest waiting line. The caller's timer decides when (see {@link msUntilNextActivityLine}). */\nexport const releaseNextActivityLine = (queue: ActivityTickerQueue, now: number): ActivityTickerQueue => {\n if (queue.waitingLines.length === 0) return queue;\n const [oldestWaitingLine, ...laterWaitingLines] = queue.waitingLines;\n return showActivityLine({ ...queue, waitingLines: laterWaitingLines }, oldestWaitingLine, now);\n};\n\n/** Show every waiting line at once, oldest first — the turn has ended and the answer is on screen. */\nexport const releaseAllActivityLines = (queue: ActivityTickerQueue, now: number): ActivityTickerQueue => {\n const queueWithoutWaitingLines: ActivityTickerQueue = { ...queue, waitingLines: [] };\n return queue.waitingLines.reduce(\n (nextQueue, waitingLine) => showActivityLine(nextQueue, waitingLine, now),\n queueWithoutWaitingLines,\n );\n};\n\n/** Steps the turn has finished or failed, the count the ticker reports. */\nexport const countFinishedSteps = (elements: ActivityElement[]): number =>\n visibleActivityElements(elements).filter((element) => element.state === \"finished\" || element.state === \"failed\")\n .length;\n\n/**\n * Delegation helpers with a step queued or running right now; a helper running several tool calls counts once. The\n * helper number restarts at 1 for every delegate call, so a helper is told apart by the delegate call its steps follow\n * (the endpoint lists each delegate call's helper steps right after it).\n */\nexport const countWorkingHelpers = (elements: ActivityElement[]): number => {\n const workingHelperKeys = new Set<string>();\n let delegateCallId: number | null = null;\n elements.forEach((element) => {\n if (element.helperIndex == null) {\n if (element.kind === \"delegation\" || element.kind === \"background\") delegateCallId = element.toolCallMessageId;\n return;\n }\n if (element.state === \"queued\" || element.state === \"running\") {\n workingHelperKeys.add(`${delegateCallId}:${element.helperIndex}`);\n }\n });\n return workingHelperKeys.size;\n};\n\n/** \"0:42\" / \"2:04\" — elapsed time as a clock, no unit to translate. */\nexport const formatElapsedClock = (elapsedMs: number): string => {\n const elapsedSeconds = Math.max(0, Math.floor(elapsedMs / 1000));\n return `${Math.floor(elapsedSeconds / 60)}:${String(elapsedSeconds % 60).padStart(2, \"0\")}`;\n};\n\n/** Nothing on screen · lines beside the face while the turn runs · the face's success beat after it ended. */\nexport type ActivityTickerPhase = \"idle\" | \"live\" | \"done\";\n\n/** A finished turn whose lines stay in the thread, above its answer, under the message that started it. */\nexport interface KeptActivityTurn {\n turnMessageId: number;\n /** Every line the turn told, oldest first. */\n lines: ActivityTickerLine[];\n}\n\nexport interface ActivityTickerState {\n queue: ActivityTickerQueue;\n phase: ActivityTickerPhase;\n /** The message that started the turn on screen, as the endpoint named it; null until it did. */\n turnMessageId: number | null;\n /** When the running turn started on this screen (ms since epoch), for the elapsed clock; null before any turn. */\n liveSince: number | null;\n /** Finished turns that stay in the thread, oldest first; filled only while the history is kept. */\n keptTurns: KeptActivityTurn[];\n /** The `running` of the previous input, to see where a turn starts and ends. */\n wasRunning: boolean;\n}\n\nexport const INITIAL_ACTIVITY_TICKER_STATE: ActivityTickerState = {\n queue: EMPTY_ACTIVITY_TICKER_QUEUE,\n phase: \"idle\",\n turnMessageId: null,\n liveSince: null,\n keptTurns: [],\n wasRunning: false,\n};\n\nexport type ActivityTickerAction =\n | {\n type: \"elementsReceived\";\n elements: ActivityElement[];\n running: boolean;\n failed: boolean;\n turnMessageId: number | null;\n /** A turn in its success beat that the next turn replaces goes into {@link ActivityTickerState.keptTurns} first. */\n keepHistory: boolean;\n now: number;\n }\n | { type: \"nextLineDue\"; now: number }\n | { type: \"doneBeatOver\"; keepHistory: boolean };\n\nconst hasShownStepsMissingFrom = (queue: ActivityTickerQueue, elements: ActivityElement[]) => {\n const elementIds = new Set(elements.map((element) => element.toolCallMessageId));\n return (\n Object.keys(queue.shownLines).some((stepId) => !elementIds.has(Number(stepId))) ||\n queue.waitingLines.some((line) => !elementIds.has(line.toolCallMessageId))\n );\n};\n\n/**\n * The kept turns plus the turn in its success beat, if it can be placed in the thread (the endpoint named the message\n * that started it). A turn kept before under the same message is replaced: its helpers ended and it ended again.\n */\nconst keptTurnsWithFinishedTurn = (state: ActivityTickerState): KeptActivityTurn[] => {\n const { phase, turnMessageId, queue, keptTurns } = state;\n if (phase !== \"done\" || turnMessageId === null) return keptTurns;\n const finishedTurn: KeptActivityTurn = { turnMessageId, lines: shownLinesInOrder(queue) };\n return [...keptTurns.filter((keptTurn) => keptTurn.turnMessageId !== turnMessageId), finishedTurn].slice(\n -MAX_KEPT_TURNS,\n );\n};\n\n/**\n * The ticker's life across turns. A turn starts when `running` rises: the queue starts clean and the phase goes live.\n * Within a turn the endpoint's snapshot only grows (mergeActivityElements keeps every row it has seen), so a shown step\n * missing from the elements means another turn or another conversation: the queue starts clean. When `running` falls,\n * the waiting lines are shown at once and the face's success beat follows — unless the turn failed or never told a\n * line. After the beat the turn's lines either stay in the thread ({@link KeptActivityTurn}) or are gone, as\n * `keepHistory` says.\n */\nexport const activityTickerReducer = (\n state: ActivityTickerState,\n action: ActivityTickerAction,\n): ActivityTickerState => {\n switch (action.type) {\n case \"elementsReceived\": {\n const { elements, running, failed, keepHistory, now } = action;\n const turnStarted = running && !state.wasRunning;\n const turnEnded = !running && state.wasRunning;\n const startsClean = turnStarted || hasShownStepsMissingFrom(state.queue, elements);\n const queue = receiveActivityElements(startsClean ? EMPTY_ACTIVITY_TICKER_QUEUE : state.queue, elements, now);\n let nextState: ActivityTickerState = {\n ...state,\n queue,\n turnMessageId: action.turnMessageId ?? (startsClean ? null : state.turnMessageId),\n wasRunning: running,\n };\n // The turn in its success beat leaves the screen before the next one starts clean: kept in the thread or gone.\n if (startsClean && state.phase === \"done\") {\n nextState = {\n ...nextState,\n phase: \"idle\",\n keptTurns: keepHistory ? keptTurnsWithFinishedTurn(state) : state.keptTurns,\n };\n }\n if (running && startsClean) {\n nextState = { ...nextState, phase: \"live\", liveSince: now };\n }\n if (turnEnded && state.phase === \"live\") {\n const finalQueue = releaseAllActivityLines(queue, now);\n const toldALine = finalQueue.currentId !== null;\n nextState = { ...nextState, queue: finalQueue, phase: toldALine && !failed ? \"done\" : \"idle\" };\n }\n if (failed && nextState.phase === \"done\") {\n nextState = { ...nextState, phase: \"idle\" };\n }\n return nextState;\n }\n case \"nextLineDue\":\n return state.phase === \"live\" ? { ...state, queue: releaseNextActivityLine(state.queue, action.now) } : state;\n case \"doneBeatOver\":\n return state.phase === \"done\"\n ? {\n ...state,\n phase: \"idle\",\n keptTurns: action.keepHistory ? keptTurnsWithFinishedTurn(state) : state.keptTurns,\n }\n : state;\n default:\n return state;\n }\n};\n","import { useEffect, useMemo, useReducer } from \"react\";\n\nimport {\n activityTickerReducer,\n ActivityTickerLine,\n countFinishedSteps,\n countWorkingHelpers,\n ActivityTickerPhase,\n INITIAL_ACTIVITY_TICKER_STATE,\n KeptActivityTurn,\n msUntilNextActivityLine,\n shownLinesInOrder,\n} from \"./activity-ticker\";\nimport { ActivityElement } from \"./merge-activity-elements\";\n\n/**\n * How long the face's success beat rests before the face turns back to idle — counted from the start of the success\n * roll. The ticker keeps or drops the turn's lines once it is over; an app's face times its success roll by it.\n */\nexport const SUCCESS_BEAT_MS = 1400;\n\ninterface UseActivityTickerParams {\n /** The rows of the running turn from {@link useConversationActivity}, in row order. */\n elements: ActivityElement[];\n /** Whether the endpoint says the turn is still in flight. */\n running: boolean;\n /** The message that started the turn, as the endpoint named it; null until it did. */\n turnMessageId?: number | null;\n /** The turn ended in an error: no success beat, nothing kept. */\n failed?: boolean;\n /**\n * After the answer, each finished turn's lines stay in the thread above its answer, under the message that started\n * it; false = they fade out with the line beside the face and nothing remains.\n */\n keepActivityHistoryAfterAnswer?: boolean;\n}\n\n/** One turn's lines — the turn running now, in the face's success beat, or kept in the thread. */\nexport interface ActivityTickerTurn {\n /** The message that started the turn: its kept history stands right after it. Null until the endpoint named it. */\n turnMessageId: number | null;\n /** live = the turn runs; done = it ended. */\n phase: \"live\" | \"done\";\n /** Every line told in the turn, oldest first; while live, the last one stands beside the face. */\n lines: ActivityTickerLine[];\n /** Steps finished or failed so far. */\n stepCount: number;\n /** Steps delegation helpers are running right now. */\n helperCount: number;\n /** When the running turn started on this screen (ms since epoch), for the elapsed clock; null once it ended. */\n liveSince: number | null;\n}\n\n/** What the ticker shows right now — read by AgentActivity (the face and its lines) and the history in the thread. */\nexport interface ActivityTicker {\n /** The phase of the turn on screen; the face keeps working while it is live. */\n phase: ActivityTickerPhase;\n /** The turn running now or in the face's success beat; null when idle and before the turn's first line. */\n currentTurn: ActivityTickerTurn | null;\n /**\n * The finished turns whose lines stand in the thread, under the message that started each, oldest first: every kept\n * turn plus the turn in the face's success beat. Empty unless the history is kept; a turn the endpoint never named\n * is never among them (it has no message to stand under).\n */\n historyTurns: ActivityTickerTurn[];\n}\n\nconst toKeptTurn = (keptTurn: KeptActivityTurn): ActivityTickerTurn => ({\n turnMessageId: keptTurn.turnMessageId,\n phase: \"done\",\n lines: keptTurn.lines,\n stepCount: keptTurn.lines.length,\n helperCount: 0,\n liveSince: null,\n});\n\n/**\n * Paces the activity lines of a running turn at the prototype's ThinkingIndicator rhythm. The\n * endpoint delivers lines in bursts; the ticker puts them beside the face one at a time — each stays at least 2.2 s,\n * or 1.4 s while more than two wait — and never falls more than a few lines behind. When the turn ends, the rest is\n * shown at once and the face plays its success beat ({@link SUCCESS_BEAT_MS}); the turn's lines then stay in the\n * thread or are gone, as `keepActivityHistoryAfterAnswer` says. The state machine lives in\n * {@link activityTickerReducer}; this hook only owns the timers.\n */\nexport const useActivityTicker = (params: UseActivityTickerParams): ActivityTicker => {\n const { elements, running, turnMessageId = null, failed = false, keepActivityHistoryAfterAnswer = false } = params;\n const [state, dispatch] = useReducer(activityTickerReducer, INITIAL_ACTIVITY_TICKER_STATE);\n\n useEffect(() => {\n dispatch({\n type: \"elementsReceived\",\n elements,\n running,\n failed,\n turnMessageId,\n keepHistory: keepActivityHistoryAfterAnswer,\n now: Date.now(),\n });\n // keepActivityHistoryAfterAnswer is read when a turn leaves the screen, it never triggers a receive of its own.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [elements, running, failed, turnMessageId]);\n\n // One timer for the next waiting line; every queue change re-plans it.\n useEffect(() => {\n if (state.phase !== \"live\") return undefined;\n const waitMs = msUntilNextActivityLine(state.queue, Date.now());\n if (waitMs === null) return undefined;\n const timer = setTimeout(() => dispatch({ type: \"nextLineDue\", now: Date.now() }), waitMs);\n return () => clearTimeout(timer);\n }, [state.phase, state.queue]);\n\n useEffect(() => {\n if (state.phase !== \"done\") return undefined;\n const timer = setTimeout(\n () => dispatch({ type: \"doneBeatOver\", keepHistory: keepActivityHistoryAfterAnswer }),\n SUCCESS_BEAT_MS,\n );\n return () => clearTimeout(timer);\n }, [state.phase, keepActivityHistoryAfterAnswer]);\n\n return useMemo(() => {\n const { queue, phase } = state;\n const lines = shownLinesInOrder(queue);\n const currentTurn: ActivityTickerTurn | null =\n phase !== \"idle\" && lines.length > 0\n ? {\n turnMessageId: state.turnMessageId,\n phase,\n lines,\n stepCount: countFinishedSteps(elements),\n helperCount: phase === \"live\" ? countWorkingHelpers(elements) : 0,\n liveSince: phase === \"live\" ? state.liveSince : null,\n }\n : null;\n // Switching the history off takes the kept lines away at once; a turn on screen is never shown twice. The turn in\n // its success beat already stands in the thread, so its lines grow in there as they leave the face.\n const keptTurns = state.keptTurns\n .filter((keptTurn) => currentTurn === null || keptTurn.turnMessageId !== currentTurn.turnMessageId)\n .map(toKeptTurn);\n const finishedTurnOnScreen =\n currentTurn?.phase === \"done\" && currentTurn.turnMessageId !== null ? currentTurn : null;\n const historyTurns = !keepActivityHistoryAfterAnswer\n ? []\n : finishedTurnOnScreen\n ? [...keptTurns, finishedTurnOnScreen]\n : keptTurns;\n return { phase, currentTurn, historyTurns };\n }, [state, elements, keepActivityHistoryAfterAnswer]);\n};\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useAgentsConversationClient } from \"../../client/AgentsConversationClientContext\";\nimport { ActivityElement, isDifferentTurn, mergeActivityElements } from \"./merge-activity-elements\";\n\n/** At most one activity request per this many milliseconds per conversation, whatever the tick rate. */\nexport const ACTIVITY_REQUEST_WINDOW_MS = 3000;\n/** While a turn runs, a request also fires on this cadence: the floor for a dead signal. */\nexport const RUNNING_FLOOR_MS = 4000;\n/**\n * Once the signal no longer says \"running\", this many failed requests in a row end the turn on screen: an endpoint that\n * keeps failing must not keep the face working and ask again every {@link RUNNING_FLOOR_MS} while the chat is open.\n */\nexport const MAX_FAILED_REQUESTS_AFTER_SIGNAL_ENDED = 3;\n\ntype Params = {\n conversationId: number;\n /** The signal's run status: \"running\" while a turn drives, \"idle\" / \"failed\" when it ended, null before the first read. */\n runStatus: string | null;\n /** Advances on every signal tick the host acts on (its message refetch); each advance asks for the activity too. */\n signalVersion: number;\n enabled?: boolean;\n};\n\ntype Result = {\n /** The rows of the stack in row order; empty before the first turn of this mount. */\n elements: ActivityElement[];\n /** Whether the narrated turn is still in flight. */\n running: boolean;\n /** The message that started the narrated turn, as the endpoint named it; null before its first answer. */\n turnMessageId: number | null;\n};\n\n/**\n * What the agent is doing for the customer right now: pulls the conversation client's\n * `getConversationActivity` (`AgentsConversationClientProvider`) while the customer watches a running turn and merges the answer into the stack. Frontend-driven on purpose: nothing is\n * generated for a hidden tab or a closed chat. The turn is pinned when it starts (`turnMessageId` from the first\n * answer), so a message the customer sends mid-turn does not move the window. Debounced to one request per\n * {@link ACTIVITY_REQUEST_WINDOW_MS} with one trailing request after a burst; a terminal status fires one request for\n * the finished lines, and the requests go on until the endpoint answers that the turn ended — then the stack rests\n * until the next turn. A failed run ends the turn with that last request, whatever it answers, and so do\n * {@link MAX_FAILED_REQUESTS_AFTER_SIGNAL_ENDED} failed requests in a row once the signal stopped saying \"running\".\n */\nexport const useConversationActivity = ({\n conversationId,\n runStatus,\n signalVersion,\n enabled = true,\n}: Params): Result => {\n const conversationClient = useAgentsConversationClient();\n const [elements, setElements] = useState<ActivityElement[]>([]);\n const [running, setRunning] = useState(false);\n const [turnMessageId, setTurnMessageId] = useState<number | null>(null);\n const turnMessageIdRef = useRef<number | null>(null);\n const elementsRef = useRef<ActivityElement[]>([]);\n const inFlightRef = useRef(false);\n const trailingRef = useRef(false);\n const lastRequestAtRef = useRef(0);\n const trailingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const runningRef = useRef(false);\n // What the endpoint said last about the pinned turn: still running or ended. The signal reports the parent\n // conversation IDLE while its helpers run, so a signal \"running\" after \"idle\" is not by itself a new turn.\n const answeredRunningRef = useRef(false);\n // The signal's run status as the answers read it: they land after the status may have changed.\n const runStatusRef = useRef<string | null>(runStatus);\n const consecutiveFailedRequestsRef = useRef(0);\n // The throttled entry, reachable from the request's own trailing call without a dependency cycle.\n const requestActivityRef = useRef<((immediate?: boolean) => void) | null>(null);\n\n useEffect(() => {\n elementsRef.current = elements;\n }, [elements]);\n\n const performRequest = useCallback(async () => {\n if (!enabled || !Number.isFinite(conversationId) || conversationId <= 0) return;\n if (inFlightRef.current) {\n trailingRef.current = true;\n return;\n }\n inFlightRef.current = true;\n lastRequestAtRef.current = Date.now();\n try {\n const response = await conversationClient.getConversationActivity(conversationId, turnMessageIdRef.current);\n const answeredTurn = response.turnMessageId;\n // A new turn started since the stack was pinned: the rows of the old turn fall away, the new turn starts clean.\n const newTurn = isDifferentTurn(turnMessageIdRef.current, answeredTurn);\n turnMessageIdRef.current = answeredTurn;\n setTurnMessageId(answeredTurn);\n const merged = mergeActivityElements(newTurn ? [] : elementsRef.current, response.elements);\n elementsRef.current = merged;\n setElements(merged);\n consecutiveFailedRequestsRef.current = 0;\n // A failed run has ended, whatever its steps say: a tool call the failure left open answers \"running\" forever.\n answeredRunningRef.current = response.turnRunning && runStatusRef.current !== \"failed\";\n setRunning(answeredRunningRef.current);\n } catch {\n // best-effort: the states of the next answer replace whatever is missing now — unless the turn is known to have\n // ended (the run failed) or the endpoint keeps failing after the signal stopped saying \"running\".\n consecutiveFailedRequestsRef.current += 1;\n if (\n runStatusRef.current === \"failed\" ||\n (runStatusRef.current !== \"running\" &&\n consecutiveFailedRequestsRef.current >= MAX_FAILED_REQUESTS_AFTER_SIGNAL_ENDED)\n ) {\n answeredRunningRef.current = false;\n setRunning(false);\n }\n } finally {\n inFlightRef.current = false;\n if (trailingRef.current) {\n trailingRef.current = false;\n requestActivityRef.current?.();\n }\n }\n }, [conversationId, conversationClient, enabled]);\n\n // The one throttle every trigger goes through: at most one request per window, one trailing call for a burst.\n const requestActivity = useCallback(\n (immediate = false) => {\n if (typeof document !== \"undefined\" && document.hidden) return;\n if (immediate) {\n if (trailingTimerRef.current !== null) {\n clearTimeout(trailingTimerRef.current);\n trailingTimerRef.current = null;\n }\n void performRequest();\n return;\n }\n const sinceLast = Date.now() - lastRequestAtRef.current;\n if (sinceLast >= ACTIVITY_REQUEST_WINDOW_MS) {\n void performRequest();\n return;\n }\n if (trailingTimerRef.current === null) {\n trailingTimerRef.current = setTimeout(() => {\n trailingTimerRef.current = null;\n void performRequest();\n }, ACTIVITY_REQUEST_WINDOW_MS - sinceLast);\n }\n },\n [performRequest],\n );\n useEffect(() => {\n requestActivityRef.current = requestActivity;\n }, [requestActivity]);\n\n // Turn start: drop whatever the previous (already rested) turn left on screen BEFORE asking again — otherwise the\n // first response for the new turn would merge its rows into the old ones still sitting in state (the bug: sending\n // a new message while the previous turn's stack is still shown left the old rows in place with the new ones\n // appended). Unpin, clear the stack, mark it running so the UI switches to the live ticker at once, then request.\n // Turn end: one last request for the finished lines (the response's own `turnRunning` flips `running` false once\n // that lands, not this effect — so the ticker keeps whatever it has until the terminal answer is in).\n useEffect(() => {\n runStatusRef.current = runStatus;\n const isRunning = runStatus === \"running\";\n const wasRunning = runningRef.current;\n runningRef.current = isRunning;\n if (isRunning && !wasRunning) {\n consecutiveFailedRequestsRef.current = 0;\n // A fresh turn only when the endpoint said the pinned one had ended; while helpers run the parent's signal\n // flips idle and back, and the ticker must keep its rows (a different answered turn resets it anyway).\n if (!answeredRunningRef.current) {\n turnMessageIdRef.current = null;\n setTurnMessageId(null);\n elementsRef.current = [];\n setElements([]);\n }\n setRunning(true);\n requestActivity(true);\n } else if (!isRunning && wasRunning) {\n requestActivity(true);\n }\n }, [runStatus, requestActivity]);\n\n // Every signal tick the host acts on asks for the activity too (throttled above).\n useEffect(() => {\n if (signalVersion > 0 && runningRef.current) {\n requestActivity();\n }\n }, [signalVersion, requestActivity]);\n\n // The floor while the turn is in flight (a dead signal, a long tool) and the catch-up on tab return. In flight until\n // the endpoint itself answers that the turn ended, not only while the signal says \"running\": the signal reports the\n // parent idle while its helpers work, a turn can end while the tab is hidden (the terminal request above is then\n // skipped), and that request can fail. Stopping at the signal would leave `running` true until the next turn. The\n // catch in performRequest bounds it: a failed run, or failed requests in a row, set `running` false.\n useEffect(() => {\n if (!enabled || (runStatus !== \"running\" && !running)) return;\n const interval = setInterval(() => requestActivity(), RUNNING_FLOOR_MS);\n const onVisibilityChange = () => {\n if (!document.hidden) requestActivity(true);\n };\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n return () => {\n clearInterval(interval);\n document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n };\n }, [enabled, runStatus, running, requestActivity]);\n\n // A different conversation: nothing of the old one is shown.\n useEffect(() => {\n turnMessageIdRef.current = null;\n setTurnMessageId(null);\n elementsRef.current = [];\n setElements([]);\n setRunning(false);\n consecutiveFailedRequestsRef.current = 0;\n return () => {\n if (trailingTimerRef.current !== null) {\n clearTimeout(trailingTimerRef.current);\n trailingTimerRef.current = null;\n }\n };\n }, [conversationId]);\n\n return { elements, running, turnMessageId };\n};\n","import type {\n ConversationChangeSignal,\n ConversationRunStatus,\n ConversationSubscriptionEndReason,\n} from \"../../contract/conversation\";\n\n/** While a turn streams, per-message signals arrive in bursts — the re-read is coalesced to one per this window. */\nexport const LIVE_READ_THROTTLE_MS = 1200;\n/** Grace before the first convergence re-read (lets the terminal status' own immediate read land first). */\nexport const CONVERGENCE_BASE_DELAY_MS = 700;\n/** Backoff ceiling between convergence re-reads. */\nexport const CONVERGENCE_MAX_DELAY_MS = 2500;\n/** Stop re-reading for a missing terminal `lastMessageId` after this many attempts (the next action recovers). */\nexport const CONVERGENCE_MAX_ATTEMPTS = 6;\n\n/** What the live-update rules make the chat do. */\nexport interface ConversationLiveUpdateReads {\n /** Read the rows that changed (the chat's changed-tail read). */\n readChangedMessages: () => void;\n /** Read the conversation's metadata again (title, run state). */\n readConversationMetadata: () => void;\n /** Whether a message is loaded — the convergence check of a terminal signal's `lastMessageId`. */\n hasLoadedMessage: (messageId: number) => boolean;\n /** The run status the chat shows; null once the subscription ended. */\n showRunStatus: (runStatus: ConversationRunStatus | null) => void;\n}\n\nconst isTerminalRunStatus = (runStatus: ConversationRunStatus | null): boolean =>\n runStatus === \"idle\" || runStatus === \"failed\";\n\n/**\n * The package's rules over a conversation's live updates — one instance per subscription:\n * - the first pushed signal reads once (a turn may have moved between the chat's open and the subscription), and is\n * the baseline of the counter and of `summaryUpdatedAt`;\n * - a counter advance reads again, throttled to one read per `LIVE_READ_THROTTLE_MS` with one trailing read;\n * - a terminal status reads at once, re-reads the metadata (on the baseline and on every terminal signal that\n * advances the counter: the server closes a conversation after the turn and publishes one more idle signal), and\n * keeps reading with backoff until the signal's `lastMessageId` is loaded (a reply that beat its own commit);\n * - a changed `summaryUpdatedAt` after the baseline re-reads the metadata only;\n * - a status-check emission (`isStatusCheck`) sets the status, and reads again — at once on a terminal status — only\n * while the push path is not alive (the binding's `isPushPathAlive`; without it: no pushed signal arrived yet) and\n * its status differs from the previous check's;\n * - an ended subscription clears the status.\n * The transport's health (reconnect, a dead path, token refresh) is the binding's.\n */\nexport class ConversationChangeSignalRules {\n protected lastChangeCounter: number | null = null;\n protected hasSummaryBaseline = false;\n protected lastSummaryUpdatedAt: number | null = null;\n protected hasPushedSignalArrived = false;\n protected lastStatusCheckRunStatus: ConversationRunStatus | null = null;\n protected lastReadAt = 0;\n protected trailingReadTimer: ReturnType<typeof setTimeout> | null = null;\n protected convergenceTimer: ReturnType<typeof setTimeout> | null = null;\n protected convergenceAttempt = 0;\n protected convergenceMessageId: number | null = null;\n protected disposed = false;\n\n constructor(protected readonly reads: ConversationLiveUpdateReads) {}\n\n receiveSignal = (signal: ConversationChangeSignal): void => {\n if (this.disposed) return;\n if (signal.isStatusCheck) {\n this.receiveStatusCheck(signal.runStatus, signal.isPushPathAlive ?? this.hasPushedSignalArrived);\n return;\n }\n this.hasPushedSignalArrived = true;\n if (signal.runStatus !== null) {\n this.reads.showRunStatus(signal.runStatus);\n }\n if (!this.hasSummaryBaseline) {\n this.hasSummaryBaseline = true;\n this.lastSummaryUpdatedAt = signal.summaryUpdatedAt;\n } else if (signal.summaryUpdatedAt !== null && signal.summaryUpdatedAt !== this.lastSummaryUpdatedAt) {\n this.lastSummaryUpdatedAt = signal.summaryUpdatedAt;\n this.reads.readConversationMetadata();\n }\n const isTerminal = isTerminalRunStatus(signal.runStatus);\n if (this.lastChangeCounter === null) {\n this.lastChangeCounter = signal.changeCounter;\n this.requestRead(isTerminal);\n if (isTerminal) {\n this.convergeOnMessage(signal.lastMessageId);\n this.reads.readConversationMetadata();\n }\n return;\n }\n const hasCounterAdvanced = signal.changeCounter !== this.lastChangeCounter;\n if (!hasCounterAdvanced && !isTerminal) return;\n this.lastChangeCounter = signal.changeCounter;\n this.requestRead(isTerminal);\n if (isTerminal) {\n this.convergeOnMessage(signal.lastMessageId);\n if (hasCounterAdvanced) this.reads.readConversationMetadata();\n }\n };\n\n receiveEnd = (_reason: ConversationSubscriptionEndReason): void => {\n if (this.disposed) return;\n this.reads.showRunStatus(null);\n };\n\n /** Reads now, bypassing the throttle (the chat's safety poll). */\n readNow = (): void => {\n if (this.disposed) return;\n this.reads.readChangedMessages();\n };\n\n dispose = (): void => {\n this.disposed = true;\n this.clearTrailingRead();\n this.clearConvergence();\n };\n\n protected receiveStatusCheck(runStatus: ConversationRunStatus | null, isPushPathAlive: boolean): void {\n if (runStatus === null) return;\n this.reads.showRunStatus(runStatus);\n if (!isPushPathAlive && runStatus !== this.lastStatusCheckRunStatus) {\n this.requestRead(isTerminalRunStatus(runStatus));\n }\n this.lastStatusCheckRunStatus = runStatus;\n }\n\n /** One coordinated read path: `immediate` (a terminal status) cancels a pending trailing read and reads now. */\n protected requestRead(immediate: boolean): void {\n if (immediate) {\n this.clearTrailingRead();\n this.readThrottled();\n return;\n }\n const sinceLastRead = Date.now() - this.lastReadAt;\n if (sinceLastRead >= LIVE_READ_THROTTLE_MS) {\n this.readThrottled();\n return;\n }\n if (this.trailingReadTimer === null) {\n this.trailingReadTimer = setTimeout(this.readTrailing, LIVE_READ_THROTTLE_MS - sinceLastRead);\n }\n }\n\n protected readThrottled(): void {\n this.lastReadAt = Date.now();\n this.reads.readChangedMessages();\n }\n\n protected readTrailing = (): void => {\n this.trailingReadTimer = null;\n if (this.disposed) return;\n this.readThrottled();\n };\n\n protected clearTrailingRead(): void {\n if (this.trailingReadTimer !== null) {\n clearTimeout(this.trailingReadTimer);\n this.trailingReadTimer = null;\n }\n }\n\n protected convergeOnMessage(messageId: number | null): void {\n this.clearConvergence();\n if (messageId === null) return;\n this.convergenceMessageId = messageId;\n this.convergenceAttempt = 0;\n this.convergenceTimer = setTimeout(this.checkConvergence, CONVERGENCE_BASE_DELAY_MS);\n }\n\n protected checkConvergence = (): void => {\n this.convergenceTimer = null;\n const messageId = this.convergenceMessageId;\n if (this.disposed || messageId === null) return;\n if (this.reads.hasLoadedMessage(messageId)) return;\n if (this.convergenceAttempt >= CONVERGENCE_MAX_ATTEMPTS) return;\n this.convergenceAttempt += 1;\n this.reads.readChangedMessages();\n this.convergenceTimer = setTimeout(\n this.checkConvergence,\n Math.min(CONVERGENCE_BASE_DELAY_MS * this.convergenceAttempt, CONVERGENCE_MAX_DELAY_MS),\n );\n };\n\n protected clearConvergence(): void {\n if (this.convergenceTimer !== null) {\n clearTimeout(this.convergenceTimer);\n this.convergenceTimer = null;\n }\n }\n}\n","import { type Dispatch, type RefObject, type SetStateAction, useCallback, useEffect, useRef, useState } from \"react\";\nimport type { AgentsConversationClient } from \"../../client/AgentsConversationClient\";\nimport type { ConversationRunStatus } from \"../../contract/conversation\";\nimport type { AgentsChatStore } from \"../store/create-agents-chat-store\";\nimport { ConversationChangeSignalRules, type ConversationLiveUpdateReads } from \"./conversation-change-signal-rules\";\nimport type { ConversationMessagePages } from \"./use-conversation-message-pages\";\n\n/** The chat's reads behind the live-update rules: the changed tail and the metadata of the chat store's conversation. */\nclass ChatStoreLiveUpdateReads implements ConversationLiveUpdateReads {\n constructor(\n protected readonly chatStore: AgentsChatStore,\n /** The latest reads of the chat (a ref: their object changes with the older-page load state, the subscription does not). */\n protected readonly messagePagesRef: RefObject<ConversationMessagePages>,\n protected readonly conversationId: number,\n protected readonly setLiveReadCount: Dispatch<SetStateAction<number>>,\n readonly showRunStatus: Dispatch<SetStateAction<ConversationRunStatus | null>>,\n ) {}\n\n readChangedMessages = (): void => {\n this.setLiveReadCount((count) => count + 1);\n this.messagePagesRef.current.refetchChangedTail(this.conversationId);\n };\n\n readConversationMetadata = (): void => {\n void this.messagePagesRef.current.readConversationMetadata(this.conversationId);\n };\n\n hasLoadedMessage = (messageId: number): boolean =>\n (this.chatStore.getState().messages ?? []).some((message) => message.id === messageId);\n}\n\n/** The run status alone, for code outside the chat: no reads behind the rules. */\nclass RunStatusOnlyLiveUpdateReads implements ConversationLiveUpdateReads {\n constructor(readonly showRunStatus: Dispatch<SetStateAction<ConversationRunStatus | null>>) {}\n\n readChangedMessages = (): void => undefined;\n\n readConversationMetadata = (): void => undefined;\n\n hasLoadedMessage = (): boolean => true;\n}\n\nexport interface ConversationLiveUpdatesInputs {\n client: AgentsConversationClient;\n chatStore: AgentsChatStore;\n /** The reads the rules trigger (`useConversationMessagePages`). */\n messagePages: ConversationMessagePages;\n /** The conversation to follow; undefined for a cold chat (no subscription). */\n conversationId: number | undefined;\n}\n\nexport interface ConversationLiveUpdates {\n /** The run status the signal (or the binding's run-state check) reported last; null before the first and once ended. */\n runStatus: ConversationRunStatus | null;\n /** Counts the live reads the rules triggered — the activity read follows it (one activity read per signal tick). */\n liveReadCount: number;\n /** Reads the changed tail now, bypassing the throttle, and counts it (the chat's safety poll). */\n readChangedTailNow: () => void;\n}\n\n/**\n * The chat's live updates over `subscribeToConversationChanges`: one subscription per conversation,\n * the rules of `ConversationChangeSignalRules`, the reads written into the chat store.\n */\nexport const useConversationLiveUpdates = (inputs: ConversationLiveUpdatesInputs): ConversationLiveUpdates => {\n const { client, chatStore, messagePages, conversationId } = inputs;\n const [runStatus, setRunStatus] = useState<ConversationRunStatus | null>(null);\n const [liveReadCount, setLiveReadCount] = useState(0);\n const messagePagesRef = useRef(messagePages);\n messagePagesRef.current = messagePages;\n\n useEffect(() => {\n // A client without a subscription follows nothing here; `AgentChat` fills in the polling default before.\n if (!conversationId || !client.subscribeToConversationChanges) return;\n const rules = new ConversationChangeSignalRules(\n new ChatStoreLiveUpdateReads(chatStore, messagePagesRef, conversationId, setLiveReadCount, setRunStatus),\n );\n const unsubscribe = client.subscribeToConversationChanges(conversationId, rules.receiveSignal, rules.receiveEnd);\n return () => {\n rules.dispose();\n unsubscribe();\n };\n }, [client, chatStore, conversationId]);\n\n const readChangedTailNow = useCallback(() => {\n if (!conversationId) return;\n setLiveReadCount((count) => count + 1);\n messagePagesRef.current.refetchChangedTail(conversationId);\n }, [conversationId]);\n\n return { runStatus, liveReadCount, readChangedTailNow };\n};\n\n/**\n * The run status of a conversation for code outside the chat (the launcher button, the end of a voice call), over the\n * same port function and the same status rules as the chat — without reading any message.\n */\nexport const useAgentsConversationRunStatus = (\n client: AgentsConversationClient,\n conversationId: number | undefined,\n): ConversationRunStatus | null => {\n const [runStatus, setRunStatus] = useState<ConversationRunStatus | null>(null);\n useEffect(() => {\n // A client without a subscription reports nothing; wrap it in `withPollingChangeSubscription` first.\n if (!conversationId || !client.subscribeToConversationChanges) return;\n const rules = new ConversationChangeSignalRules(new RunStatusOnlyLiveUpdateReads(setRunStatus));\n const unsubscribe = client.subscribeToConversationChanges(conversationId, rules.receiveSignal, rules.receiveEnd);\n return () => {\n rules.dispose();\n unsubscribe();\n };\n }, [client, conversationId]);\n return runStatus;\n};\n","import type { ChangedMessagesRequest, ConversationMessageStatus } from \"../../contract/conversation\";\nimport type { ChatMessage } from \"../chat-message\";\nimport { hasDelegationInFlight, hasUnresolvedToolCall } from \"./derive-agent-activity\";\n\n/** Row statuses that are not final yet (today only a waiting signal row carries one, PENDING until a turn reads it). */\nconst CHANGING_STATUSES: string[] = [\"PENDING\", \"RUNNING\", \"CANCEL_REQUESTED\"] satisfies ConversationMessageStatus[];\n\nconst canStillChange = (message: ChatMessage): boolean =>\n CHANGING_STATUSES.includes(message.status ?? \"\") || hasUnresolvedToolCall(message) || hasDelegationInFlight(message);\n\n/**\n * What a live update reads, given the loaded messages: every row written since the newest loaded one, plus every\n * message from the lowest one that can still change. \"Written since\" goes by id, not index: ids grow with every write,\n * while an index can be taken below the newest loaded one (a call row persisted after the row its execution emitted)\n * or reused (a re-drive after an admin delete-from). The index start never lies below the newest page (a newest-first\n * page of `pageSize` cannot return older rows anyway), so a row a dead worker left pending cannot pin it. Nothing\n * loaded yet → null: the caller reads the newest page as on open. How the request reaches the wire (the filter string)\n * is the binding's matter (`AgentsConversationClient.getChangedMessages`).\n */\nexport const buildChangedMessagesRequest = (\n messages: ChatMessage[] | null,\n pageSize: number,\n): ChangedMessagesRequest | null => {\n const loaded = (messages ?? []).filter((message) => message.indexInConversation != null);\n if (loaded.length === 0) {\n return null;\n }\n const indexes = (list: ChatMessage[]) => list.map((message) => message.indexInConversation as number);\n const highestIndex = Math.max(...indexes(loaded));\n const changing = loaded.filter(canStillChange);\n const fromIndex = Math.max(\n changing.length > 0 ? Math.min(...indexes(changing)) : highestIndex + 1,\n highestIndex - pageSize + 1,\n );\n const highestKnownId = Math.max(...loaded.map((message) => message.id ?? 0));\n return { highestKnownId, fromIndex, pageSize };\n};\n","import { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { AgentsConversationClient } from \"../../client/AgentsConversationClient\";\nimport type { AgentsChatStore } from \"../store/create-agents-chat-store\";\nimport { buildChangedMessagesRequest } from \"./changed-messages-request\";\n\n/** The page size of every message read of the chat: the newest page, the changed tail and an older page. */\nexport const CONVERSATION_MESSAGE_PAGE_SIZE = 50;\n\nexport interface ConversationMessagePages {\n /** Reads the conversation without messages into the store; resolves false when the read was refused. */\n readConversationMetadata: (conversationId: number) => Promise<boolean>;\n /** The newest page an open reads; a changed-tail read asked meanwhile waits for it. */\n fetchOpeningPage: (conversationId: number) => void;\n /** The rows written since the newest loaded one and the rows that can still change (a live update). */\n refetchChangedTail: (conversationId: number) => void;\n /** The page before the oldest loaded message (scroll-up); guarded against a second load and an exhausted history. */\n loadOlder: (conversationId: number | undefined) => Promise<void>;\n /** An older page is on its way. */\n loadingOlder: boolean;\n}\n\n/**\n * The chat's message reads over the conversation client port, written into the chat store: the newest page of an\n * open, the changed tail every live update asks for, an older page on scroll-up, and the metadata read. A changed-tail\n * read asked while the open's newest page is still on its way would read that same page again (nothing is loaded yet,\n * so it has no filter): it is held instead and sent once the page is in, reading past what the page brought, so a\n * reply stored in between still arrives. A failed page read changes nothing (the next live update reads again).\n */\nexport const useConversationMessagePages = (\n chatStore: AgentsChatStore,\n client: AgentsConversationClient,\n pageSize: number = CONVERSATION_MESSAGE_PAGE_SIZE,\n): ConversationMessagePages => {\n const openingPageRef = useRef<{ conversationId: number; refetchOwed: boolean } | null>(null);\n const [loadingOlder, setLoadingOlder] = useState(false);\n const loadingOlderRef = useRef(false);\n\n const readConversationMetadata = useCallback(\n async (conversationId: number): Promise<boolean> => {\n chatStore.dispatch({ type: \"conversationMetadataRequested\", conversationId });\n try {\n const conversation = await client.getConversationMetadata(conversationId);\n chatStore.dispatch({ type: \"conversationMetadataReceived\", conversationId, conversation });\n return true;\n } catch {\n chatStore.dispatch({ type: \"conversationMetadataFailed\", conversationId });\n return false;\n }\n },\n [chatStore, client],\n );\n\n // Reads the store, not the rendered messages: the held read runs before the opened page has re-rendered.\n const fetchChangedTail = useCallback(\n async (conversationId: number): Promise<void> => {\n const changedMessagesRequest = buildChangedMessagesRequest(chatStore.getState().messages, pageSize);\n chatStore.dispatch({ type: \"messagesPageRequested\", conversationId });\n try {\n if (changedMessagesRequest === null) {\n // Nothing loaded yet: the newest page, as on open.\n const page = await client.getNewestMessagesPage(conversationId, pageSize);\n chatStore.dispatch({\n type: \"messagesPageReceived\",\n conversationId,\n messages: page.messages,\n hasMoreOlder: page.hasMoreOlder,\n });\n return;\n }\n const changedMessages = await client.getChangedMessages(conversationId, changedMessagesRequest);\n chatStore.dispatch({\n type: \"messagesPageReceived\",\n conversationId,\n messages: changedMessages.messages,\n hasMoreOlder: null,\n });\n } catch {\n // A failed live read changes nothing; the next signal reads again.\n }\n },\n [chatStore, client, pageSize],\n );\n\n const fetchOpeningPage = useCallback(\n (conversationId: number) => {\n const openingPage = { conversationId, refetchOwed: false };\n openingPageRef.current = openingPage;\n chatStore.dispatch({ type: \"messagesPageRequested\", conversationId });\n const settleOpeningPage = () => {\n if (openingPageRef.current !== openingPage) return;\n openingPageRef.current = null;\n // A reset since (empty chat, closed window) has nothing left to read into.\n const isStillShown = chatStore.getState().requestedConversationId === conversationId;\n if (openingPage.refetchOwed && isStillShown) {\n void fetchChangedTail(conversationId);\n }\n };\n client\n .getNewestMessagesPage(conversationId, pageSize)\n .then((page) => {\n chatStore.dispatch({\n type: \"messagesPageReceived\",\n conversationId,\n messages: page.messages,\n hasMoreOlder: page.hasMoreOlder,\n });\n })\n .catch(() => undefined)\n .then(settleOpeningPage);\n },\n [chatStore, client, pageSize, fetchChangedTail],\n );\n\n const refetchChangedTail = useCallback(\n (conversationId: number) => {\n const openingPage = openingPageRef.current;\n if (openingPage?.conversationId === conversationId) {\n openingPage.refetchOwed = true;\n return;\n }\n void fetchChangedTail(conversationId);\n },\n [fetchChangedTail],\n );\n\n const loadOlder = useCallback(\n async (conversationId: number | undefined): Promise<void> => {\n const { hasMoreOlder, messages } = chatStore.getState();\n if (loadingOlderRef.current || !hasMoreOlder || !conversationId) return;\n const lowestLoadedIndex = (messages ?? [])[0]?.indexInConversation;\n if (lowestLoadedIndex == null) return;\n loadingOlderRef.current = true;\n setLoadingOlder(true);\n chatStore.dispatch({ type: \"messagesPageRequested\", conversationId });\n try {\n const page = await client.getOlderMessagesPage(conversationId, lowestLoadedIndex, pageSize);\n chatStore.dispatch({\n type: \"messagesPageReceived\",\n conversationId,\n messages: page.messages,\n hasMoreOlder: page.hasMoreOlder,\n });\n } catch {\n // The scroll-up load stays armed; the next sentinel hit tries again.\n } finally {\n loadingOlderRef.current = false;\n setLoadingOlder(false);\n }\n },\n [chatStore, client, pageSize],\n );\n\n return useMemo(\n () => ({ readConversationMetadata, fetchOpeningPage, refetchChangedTail, loadOlder, loadingOlder }),\n [readConversationMetadata, fetchOpeningPage, refetchChangedTail, loadOlder, loadingOlder],\n );\n};\n","import { RefObject, useCallback, useEffect, useRef } from \"react\";\nimport type { ChatMessage } from \"../chat-message\";\nimport type { ChatScrollTarget } from \"./chat-scroll-target\";\n\n/** What the scroll follow reads of the activity ticker: its phase and the lines of the turn on screen. */\nexport interface ConversationScrollActivityTicker {\n phase: string;\n currentTurn: { lines: { toolCallMessageId: number; state: string | null }[] } | null;\n}\n\nexport interface ConversationScrollJumpInputs {\n /** The top-bar search's request to scroll a message into view and flash it; re-applied whenever its nonce changes. */\n scrollTarget: ChatScrollTarget | undefined;\n /** The conversation the host asked for (the chat's `conversationId` prop) — the one a request is armed for. */\n conversationId: number | undefined;\n /** The conversation the chat store holds right now; differs from `conversationId` for a beat after a switch. */\n conversation: { id?: number | null } | null;\n /** The loaded messages: every change retries a pending request. */\n messages: ChatMessage[] | null;\n /** The loaded messages as of the last render, read where the retry runs. */\n messagesRef: RefObject<ChatMessage[] | null>;\n /** Whether an older page exists, as of the last render. */\n hasMoreOlderRef: RefObject<boolean>;\n /** Pages the next older page in (self-guarded against concurrent and exhausted loads). */\n loadOlder: () => Promise<void>;\n /** The chat's scroll container (the host may own it, e.g. for a scroll-linked background). */\n chatScrollRef: RefObject<HTMLDivElement | null>;\n /** What the activity ticker shows; a new line scrolls it into view while the chat follows the bottom. */\n activityTicker: ConversationScrollActivityTicker;\n}\n\nexport interface ConversationScrollJump {\n /** Bottom-follow flag shared with ConversationViewBody; false while a jump is armed and after it. */\n followBottomRef: RefObject<boolean>;\n /** \"A programmatic jump is in flight\" gate shared with ConversationViewBody. */\n scrollJumpActiveRef: RefObject<boolean>;\n}\n\n/**\n * The chat's programmatic scrolling: the jump to a requested message (paging older pages in until it is rendered, then\n * the flash once the smooth scroll settled) and keeping the activity ticker in view as it grows. Owns the two refs it\n * shares with ConversationViewBody, which suspend the bottom-follow while a jump is armed.\n */\nexport const useConversationScrollJump = (inputs: ConversationScrollJumpInputs): ConversationScrollJump => {\n const {\n scrollTarget,\n conversationId,\n conversation,\n messages,\n messagesRef,\n hasMoreOlderRef,\n loadOlder,\n chatScrollRef,\n activityTicker,\n } = inputs;\n\n // Scroll-to-message (top-bar search → Agent → here). The requested hit is held in a ref until its message is\n // actually in the DOM: if it sits on an older page that hasn't loaded yet, page older in and retry on each\n // `messages` change. `data-message-id` is stamped on every rendered message by ConversationMessageList; the flash is a\n // transient `data-flash` attribute styled in ConversationMessageList's stylesheet.\n // `armedForConversationId` stamps which conversation the request targeted, so a request that outlives its\n // conversation (e.g. the selected search hit's conversation 404s) can't page through an unrelated one later.\n const pendingScrollRef = useRef<(ChatScrollTarget & { armedForConversationId?: number }) | null>(null);\n // Bottom-follow flag shared with ConversationViewBody. A search-result jump scrolls to a message ABOVE the bottom, then the\n // messages settle their heights (images/markdown) which fires ConversationViewBody's resize bottom-follow. While a jump is\n // armed we hold this false so follow stays suspended for the whole jump (and afterwards — the user is now reading up\n // there, exactly as a manual scroll-up).\n const followBottomRef = useRef(true);\n // \"A programmatic jump is in flight\" gate, shared with ConversationViewBody. Distinct from followBottomRef because the jump\n // must ALSO stop ConversationViewBody's near-bottom onScroll handler from flipping followBottom back to true: right after the\n // click the container is still near the bottom (the smooth scrollIntoView hasn't physically moved yet), so onScroll\n // would re-enable follow and the resize-follow would then yank the view back down. While this is true ConversationViewBody\n // neither re-enables follow on scroll nor auto-scrolls to the bottom on resize. Cleared once the jump settles/disarms.\n const scrollJumpActiveRef = useRef(false);\n\n // Keep the activity ticker in view as it grows — a new line beside the face or a new row above it scrolls it\n // into view with the exact guarded pattern the search-result jump above uses (chatScrollRef query +\n // scrollIntoView), not a second scroller. Keyed on what the ticker SHOWS, not on the endpoint's elements: the ticker\n // releases lines one at a time after they arrive. Skipped while the user has scrolled away (followBottomRef false)\n // or a host jump is already in flight (scrollJumpActiveRef) — the scroll-to-latest button already covers that case,\n // exactly as it does for a growing final answer under the pinned-question addon.\n // `block: \"nearest\"`, never \"end\": during a pinned turn (enablePinQuestionAndRevealAnswer) the room reserved below\n // the question makes the pinned position the scroll bottom, so ConversationViewBody's near-bottom onScroll turns\n // followBottomRef back on. \"end\" then aligned the ticker with the viewport bottom — scrolling UP by the reserved room\n // and dropping the question off the top. \"nearest\" only scrolls once the ticker actually runs below the fold.\n const activitySignatureRef = useRef(\"\");\n useEffect(() => {\n const { phase, currentTurn } = activityTicker;\n const newestLine = currentTurn?.lines[currentTurn.lines.length - 1];\n const signature = `${phase}|${currentTurn?.lines.length}|${newestLine?.toolCallMessageId}:${newestLine?.state}`;\n if (signature === activitySignatureRef.current) return;\n activitySignatureRef.current = signature;\n if (phase !== \"live\" || !followBottomRef.current || scrollJumpActiveRef.current) return;\n const tickerElement = chatScrollRef.current?.querySelector<HTMLElement>('[data-testid=\"agent-activity-ticker\"]');\n tickerElement?.scrollIntoView({ behavior: \"smooth\", block: \"nearest\", inline: \"nearest\" });\n }, [activityTicker, chatScrollRef, followBottomRef, scrollJumpActiveRef]);\n\n // Tracks the in-flight \"remove the flash highlight\" timer so it can be cancelled if a new scroll lands first or the\n // widget unmounts mid-flash (otherwise the callback fires on a detached node).\n const flashTimerRef = useRef<number | null>(null);\n // Tracks the requestAnimationFrame loop that waits for the smooth scroll to settle before the flash starts, so a new\n // scroll request or an unmount can cancel a still-pending wait.\n const scrollSettleRafRef = useRef<number | null>(null);\n const tryScrollToPendingMessage = useCallback(() => {\n const target = pendingScrollRef.current;\n if (!target?.messageId) return;\n // The conversation the request was armed for is gone (load failed / user navigated elsewhere) — disarm rather\n // than hunting a foreign message id through whatever conversation is shown now.\n if (target.armedForConversationId !== conversationId) {\n pendingScrollRef.current = null;\n scrollJumpActiveRef.current = false;\n return;\n }\n // A jump is armed for the current conversation → suspend bottom-follow AND gate ConversationViewBody's follow-re-enable /\n // resize-follow for the whole jump. Re-asserted on every retry (each older-page merge), so even a conversation\n // switch that briefly re-enabled follow can't yank us down.\n followBottomRef.current = false;\n scrollJumpActiveRef.current = true;\n const messageElement = chatScrollRef.current?.querySelector<HTMLElement>(`[data-message-id=\"${target.messageId}\"]`);\n if (messageElement) {\n pendingScrollRef.current = null;\n // Cancel any flash still in flight from a previous jump (its removal timer + settle watcher) and clear the\n // attribute so the animation can restart cleanly on this message.\n if (flashTimerRef.current !== null) window.clearTimeout(flashTimerRef.current);\n if (scrollSettleRafRef.current !== null) window.cancelAnimationFrame(scrollSettleRafRef.current);\n messageElement.removeAttribute(\"data-flash\");\n\n messageElement.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\n // Start the flash only AFTER the smooth scroll settles, so the highlight plays on the at-rest message rather\n // than during the travel. Watch the element's viewport position each frame: once it holds still for 3\n // consecutive frames (covers both \"scrolled then stopped\" and \"already in view\"), or a 1.2s safety cap elapses,\n // the scroll has ended → toggle `data-flash` and arm its ~2s removal.\n let lastTop = messageElement.getBoundingClientRect().top;\n let stillFrames = 0;\n const startedAt = performance.now();\n const waitForScrollEnd = () => {\n if (!messageElement.isConnected) {\n scrollSettleRafRef.current = null;\n scrollJumpActiveRef.current = false;\n return;\n }\n const top = messageElement.getBoundingClientRect().top;\n stillFrames = Math.abs(top - lastTop) < 0.5 ? stillFrames + 1 : 0;\n lastTop = top;\n if (stillFrames >= 3 || performance.now() - startedAt > 1200) {\n scrollSettleRafRef.current = null;\n // The jump has come to rest → lift the gate. followBottomRef stays false, so a later content-resize won't\n // yank to the bottom; the user is parked at the target, exactly as after a manual scroll-up.\n scrollJumpActiveRef.current = false;\n messageElement.setAttribute(\"data-flash\", \"true\");\n flashTimerRef.current = window.setTimeout(() => {\n flashTimerRef.current = null;\n if (messageElement.isConnected) messageElement.removeAttribute(\"data-flash\");\n }, 2000);\n return;\n }\n scrollSettleRafRef.current = window.requestAnimationFrame(waitForScrollEnd);\n };\n scrollSettleRafRef.current = window.requestAnimationFrame(waitForScrollEnd);\n return;\n }\n // Conversation just switched (e.g. a cross-conversation search hit selected in the current tab): the slice still\n // holds the PREVIOUS conversation's messages/hasMoreOlder for a beat before the new fetch resolves. Evaluating\n // against that stale data below would misjudge \"exhausted\" and disarm the request too early — wait instead for the\n // messages-change effect to retry once the new conversation's own data actually lands.\n if (String(conversation?.id) !== String(conversationId) || messagesRef.current === null) return;\n // Not rendered yet — if the target is older than the oldest loaded message, page older in; the messages-change\n // effect below retries once that page merges. loadOlder self-guards against concurrent / exhausted loads.\n const lowestLoadedIndex = messagesRef.current[0]?.indexInConversation;\n const targetIsOlderThanLoaded =\n target.indexInConversation == null || lowestLoadedIndex == null || target.indexInConversation < lowestLoadedIndex;\n if (hasMoreOlderRef.current && targetIsOlderThanLoaded) {\n void loadOlder();\n return;\n }\n // Exhausted every older page and still no match (e.g. a deleted/stale message id) — disarm so we don't keep\n // re-querying on every future message change and can't later jump to an unrelated message that reuses the id.\n pendingScrollRef.current = null;\n scrollJumpActiveRef.current = false;\n }, [chatScrollRef, loadOlder, conversation, conversationId]);\n\n // A new scroll request (nonce changed) → arm it and attempt immediately.\n useEffect(() => {\n if (!scrollTarget?.messageId) return;\n // conversationId is read from this render's closure: Agent sets the target conversation and the scroll\n // request in the same state update, so at arm time the prop already names the conversation the hit belongs to.\n pendingScrollRef.current = { ...scrollTarget, armedForConversationId: conversationId };\n tryScrollToPendingMessage();\n // This effect closes over the nonce-triggering render's current conversation and pending message. The nonce bump\n // is deliberately the sole re-run trigger; it reads those current values when it fires.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [scrollTarget?.nonce]);\n\n // Cancel a pending flash-removal timer and scroll-settle watcher on unmount so neither fires after the widget closes.\n useEffect(\n () => () => {\n if (flashTimerRef.current !== null) window.clearTimeout(flashTimerRef.current);\n if (scrollSettleRafRef.current !== null) window.cancelAnimationFrame(scrollSettleRafRef.current);\n },\n [],\n );\n\n // Retry the pending scroll after each message-list change (an older page merged in, or the live reply landed).\n useEffect(() => {\n if (pendingScrollRef.current) tryScrollToPendingMessage();\n }, [messages, tryScrollToPendingMessage]);\n\n return { followBottomRef, scrollJumpActiveRef };\n};\n","import { findBalancedJsonObjectEnd } from \"./balanced-json-object\";\nimport { parseWidgetJsonObject } from \"./widget-fence-json\";\nimport { BARE_WIDGET_LINE_GLOBAL, WIDGET_FENCE_LANG } from \"./widget-fence-syntax\";\nimport { closestWidgetNames, resolveWidgetName } from \"./widget-name-resolver\";\n\n/**\n * The signal provenance for the widget-render-failure corrective signal — distinct from a widget CLICK. Mirrors the\n * backend {@link AIConversationMessage::SIGNAL_SOURCE_FRONTEND_WIDGET_INVALID}; the admin inspector shows it as the\n * signal source and the agent loop treats it as model-visible feedback.\n */\nexport const WIDGET_INVALID_SIGNAL_SOURCE = \"FRONTEND_WIDGET_INVALID\";\n\nexport interface UnrenderedWidget {\n /** The `type` the model wrote. */\n type: string;\n /**\n * What went wrong:\n * - `unknownType`: a widget block (fenced or bare `widget { … }` line) whose type is not a registered widget →\n * dropped, the customer sees NOTHING.\n * - `missingFence`: widget JSON emitted as PLAIN TEXT (no ```widget fence, no `widget` prefix) → the customer sees\n * raw JSON prose.\n */\n kind: \"unknownType\" | \"missingFence\";\n /** The closest valid widget names (deterministic similarity) for a \"did you mean\" — may be empty. */\n suggestions: string[];\n}\n\n// A ```widget fenced block: ```widget <optional trailing> \\n <json> \\n ``` . Non-greedy body; the `m`-less dotall\n// via [\\s\\S] so a multi-line JSON body is captured. Built from the shared lang constant so \"``` widget\" (a space\n// before the lang — remark accepts it) is recognized the same way as the render plugin and the repairer.\nconst WIDGET_FENCE = new RegExp(\"```[ \\\\t]*\" + WIDGET_FENCE_LANG + \"[^\\\\n]*\\\\n([\\\\s\\\\S]*?)```\", \"g\");\n\n// ANY fenced block — stripped before the naked-JSON scan so fenced content (handled widgets, legit code samples)\n// is never re-reported as fence-less.\nconst ANY_FENCE = /```[\\s\\S]*?```/g;\n\n/**\n * Parse a candidate JSON snippet into its `type` string, or null when it isn't an object with a string `type` — read\n * like the render plugin reads a block (`parseWidgetJsonObject`: a surplus trailing `}` / `]` is tolerated).\n */\nconst parseWidgetType = (jsonCandidate: string): string | null => {\n const type = parseWidgetJsonObject(jsonCandidate)?.type;\n return typeof type === \"string\" && type !== \"\" ? type : null;\n};\n\n/**\n * Extract balanced `{…}` JSON candidates that start at a line beginning (after whitespace) in PLAIN text — the shape\n * a model emits when it writes a widget payload without the fence (observed live: the final answer carried\n * `{ \"type\": \"ExampleWidget\", … }` as its own paragraph). A simple brace-balance walk (strings respected) keeps this\n * dependency-free; nested objects are fine. Bare `widget { … }` lines are NOT captured here (they start with the\n * `widget` prefix, not `{`) — remarkWidgetFencePlugin renders those, and the bare-line scan below covers their\n * unknown types.\n */\nconst extractNakedJsonCandidates = (plainText: string): string[] => {\n const candidates: string[] = [];\n const lines = plainText.split(\"\\n\");\n let offset = 0;\n for (const line of lines) {\n const lineStart = offset;\n offset += line.length + 1;\n if (!line.trimStart().startsWith(\"{\")) continue;\n const openIndex = lineStart + line.indexOf(\"{\");\n const jsonObjectEnd = findBalancedJsonObjectEnd(plainText, openIndex);\n if (jsonObjectEnd !== null) {\n candidates.push(plainText.slice(openIndex, jsonObjectEnd + 1));\n }\n }\n return candidates;\n};\n\n/**\n * Find widget-emission failures in an assistant message — the cases where the customer does NOT get the intended\n * widget. Uses the SAME resolveWidgetName as the render plugin, so \"valid\" means exactly \"would have rendered\".\n * Three scan surfaces, two failure classes:\n *\n * 1. `unknownType` — a ```widget fence OR a bare `widget { … }` line (both forms remarkWidgetFencePlugin renders)\n * whose `type` is not a registered widget: the plugin silently DROPS it, the customer sees nothing and the model\n * gets no feedback (a model wrote `type: \"example\"` instead of `ExampleWidget`). A malformed-JSON\n * block is skipped (the plugin drops it too).\n * 2. `missingFence` — widget JSON emitted as PLAIN prose (no fence, no `widget` prefix): nothing intercepts it, so\n * the customer sees raw JSON text (found live: `{ \"type\": \"ExampleWidget\", … }` as a paragraph of the final answer).\n * Reported when the naked JSON's `type` resolves to a VALID widget (the model clearly meant a widget) or is CLOSE\n * to one (similarity shortlist non-empty) — arbitrary JSON without a widget-like `type` is never flagged (no false\n * positives on legitimate JSON content).\n */\nexport const detectUnrenderedWidgets = (messageText: string): UnrenderedWidget[] => {\n const unrendered: UnrenderedWidget[] = [];\n const seenTypes = new Set<string>();\n const report = (type: string, kind: UnrenderedWidget[\"kind\"], suggestions: string[]) => {\n if (seenTypes.has(type)) return;\n seenTypes.add(type);\n unrendered.push({ type, kind, suggestions });\n };\n\n // 1) Rendered-form blocks (```widget fence + bare `widget { … }` line) with an unknown type.\n if (messageText.includes(\"widget\")) {\n for (const renderedFormRegex of [WIDGET_FENCE, BARE_WIDGET_LINE_GLOBAL]) {\n renderedFormRegex.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = renderedFormRegex.exec(messageText)) !== null) {\n const type = parseWidgetType(match[1]);\n if (type === null) continue;\n if (resolveWidgetName(type)) continue; // resolves → it rendered; nothing to report\n report(type, \"unknownType\", closestWidgetNames(type));\n }\n }\n }\n\n // 2) Naked widget JSON outside any fence.\n const plainText = messageText.replace(ANY_FENCE, \"\");\n if (plainText.includes('\"type\"')) {\n for (const jsonCandidate of extractNakedJsonCandidates(plainText)) {\n const type = parseWidgetType(jsonCandidate);\n if (type === null) continue;\n if (resolveWidgetName(type)) {\n report(type, \"missingFence\", []);\n continue;\n }\n const suggestions = closestWidgetNames(type);\n if (suggestions.length > 0) {\n report(type, \"unknownType\", suggestions);\n }\n // No resolution AND nothing close → arbitrary JSON, not a widget attempt — never flagged.\n }\n }\n\n return unrendered;\n};\n\n/**\n * The user-INVISIBLE corrective SIGNAL text for one or more widget-emission failures — tells the model exactly what\n * the customer experienced (nothing rendered / raw JSON prose) and how to fix it. Kept terse; the model already holds\n * the full widget catalog in its instructions.\n */\nexport const buildUnrenderedWidgetSignal = (widgets: UnrenderedWidget[]): string => {\n const parts = widgets.map((widget) => {\n if (widget.kind === \"missingFence\") {\n return `You emitted the \"${widget.type}\" widget as PLAIN JSON text — without the \\`\\`\\`widget code fence it does not render and the customer sees raw JSON prose.`;\n }\n const suggestion =\n widget.suggestions.length > 0 ? ` Closest valid widget names: ${widget.suggestions.join(\", \")}.` : \"\";\n return `\"${widget.type}\" is not a valid widget type, so it did not render and the customer sees nothing.${suggestion}`;\n });\n return (\n `A widget you emitted did not reach the customer. ${parts.join(\" \")} ` +\n \"Re-emit it as a ```widget code block (fence on its own lines) using the EXACT valid widget name that matches your intent.\"\n );\n};\n","import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useRef } from \"react\";\nimport type { AgentsConversationClient } from \"../../client/AgentsConversationClient\";\nimport { isAgentsClientError } from \"../../contract/conversation\";\nimport type { ChatMessage } from \"../chat-message\";\nimport type { SendMessageHandler } from \"../conversation-send-message\";\nimport { normalizeServerMessageToMarkdown } from \"../normalize-server-message-to-markdown\";\nimport type { InjectSignalOptions } from \"../../renderer/ConversationWidgetCallbacksContext\";\nimport {\n buildUnrenderedWidgetSignal,\n detectUnrenderedWidgets,\n WIDGET_INVALID_SIGNAL_SOURCE,\n} from \"../../renderer/detect-unrendered-widgets\";\nimport {\n buildWidgetRenderReportSignal,\n createWidgetRenderReportCollector,\n WIDGET_RENDER_REPORT_SIGNAL_SOURCE,\n type WidgetRenderBlockReport,\n} from \"../../renderer/render-report/widget-render-report-collector\";\n\n/** Max invalid-widget corrective signals per conversation — a too-weak model that keeps emitting bad widgets stops here. */\nconst WIDGET_CORRECTION_CAP = 2;\n\nexport interface ConversationSignalsInputs {\n /** The conversation client the signals go through (`injectSignal`). */\n client: AgentsConversationClient;\n /** The conversation the host asked for (the chat's `conversationId` prop); a signal targets it while none is loaded. */\n conversationId: number | undefined;\n /** The conversation the chat store holds right now. */\n conversation: { id?: number | null } | null;\n /** The loaded messages; the newest settled assistant message is checked for a dropped widget block. */\n messages: ChatMessage[] | null;\n /** The signal's run status (\"running\" | \"idle\" | \"failed\" | null). */\n runStatus: string | null;\n /** A start is in flight; a signal is not sent into a conversation that does not exist yet then. */\n startingNewConversation: boolean;\n /** The run is over; the backend refuses further input. */\n isConversationRunEnded: boolean;\n /** The chat's send — a signal without a conversation starts one through it. */\n handleSendMessage: SendMessageHandler;\n /** Shows the closed-conversation refusal on the error card. */\n rejectAsConversationClosed: () => void;\n /** Re-reads the conversation's metadata (the closed run state locks the widget controls). */\n refetchConversationSummary: () => void;\n /** The chat's jump-to-bottom request counter; a customer's click follows the turn it starts. */\n setScrollToBottomNonce: Dispatch<SetStateAction<number>>;\n}\n\nexport interface ConversationSignals {\n /** Injects a model-visible, customer-invisible signal into the current conversation (the widget context's `injectSignal`). */\n handleInjectSignal: (signalText: string, options?: InjectSignalOptions) => Promise<boolean>;\n /** The widget render report collector of the current conversation (one per conversation). */\n renderReportCollector: ReturnType<typeof createWidgetRenderReportCollector>;\n}\n\n/**\n * The signals the chat sends besides the customer's messages: a widget's injected signal (with the closed-conversation\n * guard and the cold start through a START signal), a context-only signal parked until a turn is observed running,\n * the corrective signal for a widget block the renderer dropped, and the widget render report.\n */\nexport const useConversationSignals = (inputs: ConversationSignalsInputs): ConversationSignals => {\n const {\n client,\n conversationId,\n conversation,\n messages,\n runStatus,\n startingNewConversation,\n isConversationRunEnded,\n handleSendMessage,\n rejectAsConversationClosed,\n refetchConversationSummary,\n setScrollToBottomNonce,\n } = inputs;\n // The conversation actually shown right now, kept in sync every render — read from an `injectSignal` `.catch`\n // so a late-landing rejection for a conversation the customer already left doesn't dispatch onto whatever is\n // open now (see the id check inside `handleInjectSignal`).\n const currentConversationIdRef = useRef<number | null>(null);\n currentConversationIdRef.current = conversation?.id ?? null;\n\n // A `withoutOwnAgentTurn` signal (context riding along a just-posted user message, e.g. advisor-extension\n // instructions) waiting for a turn to be OBSERVED running. Delivered mid-turn it lands in the conversation history\n // without re-driving the agent — the backend only re-drives when the LAST message is an unanswered inbound, and a\n // signal appended before the live turn's reply is no longer last (see the flush effect's strand explanation below;\n // here that strand behavior is exactly what we want: in context from the next turn on, never its own reply).\n // `conversationId` pins the signal to the conversation open at park time: null = parked on a cold start (the\n // paired message creates the conversation, deliver into whichever id lands); a non-null mismatch at flush time\n // means the user switched conversations first — the signal is dropped, never leaked into the other conversation.\n const signalAwaitingRunningTurnRef = useRef<{ signalText: string; conversationId: number | null } | null>(null);\n // Inject a model-visible / user-invisible SIGNAL into the CURRENT conversation and drive one agent turn — the\n // data-back path for a picker widget (\"User selected image A\"). Resilient (swallows errors, never throws\n // into render). The reply lands via the existing realtime signal, exactly like a posted message — no extra handling\n // needed here.\n //\n // NOTE: unlike the message-post path this carries NO admin overrides. The endpoint\n // (AIConversationsController::injectSignal) reads only `signalText` + `signalSource` — it has no `budgetBypass` /\n // `aiModelName` parameter, so the earlier hand-wired call was sending two query params the backend discarded. An\n // impersonating admin's model pick therefore does not apply to a widget-click-driven turn; the conversation's\n // stored model does.\n const handleInjectSignal = useCallback(\n async (signalText: string, options?: InjectSignalOptions): Promise<boolean> => {\n if (!signalText.trim()) return false;\n // CUSTOMER_CLICK (the default when options is omitted, e.g. the briefing widgets' plain signal text) is the\n // customer pressing a widget button; PROGRAMMATIC (park-then-flush ride-alongs, the widget-invalid corrective)\n // never came from a click — it must never surface a refusal or move the scroll position.\n const isCustomerClick = options?.signalOrigin !== \"PROGRAMMATIC\";\n // Same guard as the message path: a closed conversation rejects signals too, so no widget click posts one. A\n // genuine click gets the reason on the error card; a programmatic ride-along is simply dropped, as on any\n // other failure.\n if (isConversationRunEnded) {\n if (isCustomerClick) {\n rejectAsConversationClosed();\n }\n return false;\n }\n // On a real click, scroll the chat to the bottom to follow the turn this signal drives.\n if (isCustomerClick) {\n setScrollToBottomNonce((nonce) => nonce + 1);\n }\n // Context-only signal: deliver it ONLY while a turn is running, so it never drives its own turn / reply. Not\n // running yet (the paired user message's POST is still in flight, or the conversation is still being created on\n // a cold start) — park it until the flush effect below sees \"running\". Dropped if no turn ever starts.\n if (options?.withoutOwnAgentTurn && runStatus !== \"running\") {\n signalAwaitingRunningTurnRef.current = {\n signalText,\n conversationId: conversation?.id ?? conversationId ?? null,\n };\n return false;\n }\n const targetConversationId = conversation?.id ?? conversationId;\n // No conversation yet (e.g. a briefing CTA clicked on the cold dashboard greeting): start one via the\n // start-with-signal path — an EMPTY visible message carrying the click as the invisible START signal, so the\n // agent's first turn already has it and it speaks first (no visible bubble, no deferred re-inject). Once a\n // conversation exists we keep the silent injectSignal behavior below.\n if (!targetConversationId) {\n if (startingNewConversation) return false;\n handleSendMessage(\"\", { startSignalText: signalText });\n return false;\n }\n // No admin-debug overrides here on purpose — see the NOTE on this callback: the endpoint takes neither\n // `budgetBypass` nor `aiModelName`.\n // signalSource defaults server-side to FRONTEND_WIDGET_CLICK; the widget-failover passes FRONTEND_WIDGET_INVALID.\n return client\n .injectSignal(targetConversationId, { text: signalText, source: options?.signalSource })\n .then(() => true)\n .catch((signalRequestError: unknown) => {\n // Best-effort: a failed signal must not break the widget render. The turn simply doesn't advance. A\n // GENUINE widget click is the exception, and only for the one refusal reason the customer can act on\n // (the conversation closed meanwhile) — every other failure stays silent, exactly as before.\n const isConversationClosedRefusal =\n isAgentsClientError(signalRequestError) && signalRequestError.kind === \"closed\";\n // The request targeted a conversation the customer has since left (switched conversations before this\n // late-landing rejection arrived) — don't dispatch onto whatever conversation is open now.\n if (\n isCustomerClick &&\n isConversationClosedRefusal &&\n currentConversationIdRef.current === targetConversationId\n ) {\n rejectAsConversationClosed();\n // The closed runState locks the remaining widget controls.\n refetchConversationSummary();\n }\n return false;\n });\n },\n [\n client,\n conversation,\n conversationId,\n startingNewConversation,\n handleSendMessage,\n runStatus,\n isConversationRunEnded,\n rejectAsConversationClosed,\n refetchConversationSummary,\n setScrollToBottomNonce,\n ],\n );\n\n // Deliver a parked context-only signal the moment its paired turn is observed running (see\n // `signalAwaitingRunningTurnRef`). Re-entering handleInjectSignal with `withoutOwnAgentTurn` while \"running\"\n // passes its park-guard and POSTs. Tiny race stays open: if the turn settles between this observation and the\n // POST landing, the signal becomes the last message and drives one extra turn — rare and harmless (the agent\n // acknowledges the instructions), accepted for an FE-only build.\n useEffect(() => {\n if (runStatus !== \"running\" || !signalAwaitingRunningTurnRef.current) return;\n const parkedSignal = signalAwaitingRunningTurnRef.current;\n signalAwaitingRunningTurnRef.current = null;\n // Parked for a DIFFERENT conversation (the user switched before its turn ran) — drop, don't leak it here.\n if (parkedSignal.conversationId !== null && parkedSignal.conversationId !== conversation?.id) return;\n handleInjectSignal(parkedSignal.signalText, { withoutOwnAgentTurn: true, signalOrigin: \"PROGRAMMATIC\" });\n }, [runStatus, handleInjectSignal, conversation?.id]);\n\n // ── Widget failover ─────────────────────────────────────────────────────────────────────────────────────────────\n // A weaker model can emit a ```widget block whose `type` is not a real widget (a model wrote\n // `type: \"example\"`, not `ExampleWidget`). remarkWidgetFencePlugin silently DROPS it → the customer sees no\n // widget and the model gets no feedback, so it insists \"it should be visible\". Detect the dropped block on the newest\n // SETTLED assistant message and inject a silent corrective SIGNAL (with a similarity \"did you mean\" shortlist) so the\n // model re-emits with a valid name. Guards: (1) only after a live turn we OBSERVED running — never retroactively\n // \"correct\" widgets already in history when a conversation opens; (2) report each message id at most once; (3) cap\n // corrections per conversation so a too-weak model can't loop forever.\n const sawRunningForWidgetCheckRef = useRef(false);\n const reportedWidgetErrorMessageIdsRef = useRef<Set<number>>(new Set());\n const widgetCorrectionCountRef = useRef(0);\n\n useEffect(() => {\n if (runStatus === \"running\") sawRunningForWidgetCheckRef.current = true;\n }, [runStatus]);\n\n useEffect(() => {\n // A new conversation resets the live-turn sighting and the per-conversation correction budget. (The parked\n // context-only signal is NOT cleared here: a cold start legitimately transitions id null→new while the signal\n // waits; cross-conversation leaking is prevented by the id check in its flush effect instead.)\n sawRunningForWidgetCheckRef.current = false;\n reportedWidgetErrorMessageIdsRef.current = new Set();\n widgetCorrectionCountRef.current = 0;\n }, [conversation?.id]);\n\n useEffect(() => {\n if (runStatus !== \"idle\" || !sawRunningForWidgetCheckRef.current) return;\n if (widgetCorrectionCountRef.current >= WIDGET_CORRECTION_CAP || !messages) return;\n const latestAssistantMessage = [...messages]\n .reverse()\n .find(\n (message) => message.role === \"ASSISTANT\" && message.messageType === \"TEXT\",\n );\n const messageId = latestAssistantMessage?.id;\n if (!messageId || !latestAssistantMessage?.message) return;\n if (reportedWidgetErrorMessageIdsRef.current.has(messageId)) return;\n // Feed the SAME normalized text ConversationMarkdown renders (it repairs a fence the model broke mid-text) —\n // scanning the raw stored text here would report a fence the renderer already fixed as unrendered.\n const unrenderedWidgets = detectUnrenderedWidgets(normalizeServerMessageToMarkdown(latestAssistantMessage.message));\n if (unrenderedWidgets.length === 0) return;\n reportedWidgetErrorMessageIdsRef.current.add(messageId);\n widgetCorrectionCountRef.current += 1;\n handleInjectSignal(buildUnrenderedWidgetSignal(unrenderedWidgets), {\n signalSource: WIDGET_INVALID_SIGNAL_SOURCE,\n signalOrigin: \"PROGRAMMATIC\",\n });\n }, [messages, runStatus, handleInjectSignal]);\n\n // ── Widget render report ─────────────────────────────────────────────────────────────────────────\n // Every block of a live-delivered message reports mounted / empty / failed / pending; once a message's blocks settle,\n // ONE context signal tells the model which ones the owner did not see as intended. A clean render sends nothing; the\n // source owes no work, so the signal never drives a turn of its own (the next turn reads it). One collector per\n // conversation, so a switch never reports into the other one.\n const sendWidgetRenderReportRef = useRef<(messageId: string, blocks: WidgetRenderBlockReport[]) => void>(() => {});\n sendWidgetRenderReportRef.current = (messageId: string, blocks: WidgetRenderBlockReport[]) => {\n const renderReportSignalText = buildWidgetRenderReportSignal(messageId, blocks);\n if (renderReportSignalText === null) return;\n handleInjectSignal(renderReportSignalText, {\n signalSource: WIDGET_RENDER_REPORT_SIGNAL_SOURCE,\n signalOrigin: \"PROGRAMMATIC\",\n });\n };\n const renderReportCollector = useMemo(\n () =>\n createWidgetRenderReportCollector({\n onSettled: (messageId, blocks) => sendWidgetRenderReportRef.current(messageId, blocks),\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one collector per conversation\n [conversation?.id],\n );\n useEffect(() => () => renderReportCollector.dispose(), [renderReportCollector]);\n\n return { handleInjectSignal, renderReportCollector };\n};\n","import {\n type ComponentType,\n type ReactNode,\n type RefObject,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { AgentsConversationClient } from \"../../client/AgentsConversationClient\";\nimport {\n AgentsClientError,\n type ConversationAttachmentReference,\n isAgentsClientError,\n} from \"../../contract/conversation\";\nimport { reportRunningTurn } from \"../../provider/environment\";\nimport { CanvasRestoreOnMount } from \"../../renderer/canvas/CanvasRestoreOnMount\";\nimport { ConversationDeepLinkedWidget } from \"../../renderer/ConversationDeepLinkedWidget\";\nimport { ConversationWidgetProvider } from \"../../renderer/ConversationWidgetContext\";\nimport { WidgetRenderReportCollectorContext } from \"../../renderer/render-report/widget-render-report-collector\";\nimport { resolveDeepLinkableWidget } from \"../../renderer/resolve-deep-linkable-widget\";\nimport type { ChatMessage } from \"../chat-message\";\nimport type { SendMessageHandler } from \"../conversation-send-message\";\nimport type { ConversationMessageInputHandle } from \"../ConversationMessageInput/ConversationMessageInput\";\nimport { ConversationView } from \"../ConversationView\";\nimport type { AgentsChatState } from \"../store/agents-chat-state\";\nimport { ConversationErrorType } from \"../store/conversation-error-type\";\nimport type { AgentsChatStore } from \"../store/create-agents-chat-store\";\nimport { useAgentsChatSelector } from \"../store/use-agents-chat-store\";\nimport type { ChatScrollTarget } from \"./chat-scroll-target\";\nimport { deriveAgentActivity } from \"./derive-agent-activity\";\nimport { isChatReadyToSendPendingStartRequest, type PendingStartRequest, pickPendingStartRequest } from \"./pending-start-request\";\nimport { type ActivityTicker, type ActivityTickerTurn, useActivityTicker } from \"./use-activity-ticker\";\nimport { useConversationActivity } from \"./use-conversation-activity\";\nimport { useConversationLiveUpdates } from \"./use-conversation-live-updates\";\nimport { CONVERSATION_MESSAGE_PAGE_SIZE, useConversationMessagePages } from \"./use-conversation-message-pages\";\nimport { useConversationScrollJump } from \"./use-conversation-scroll-jump\";\nimport { useConversationSignals } from \"./use-conversation-signals\";\n\n/** What the host's activity components get: the turn's state and the paced lines of the activity ticker. */\nexport interface AgentChatActivityProps {\n /** A turn is in flight (derived from the messages and the run status). */\n active: boolean;\n /** The chat shows an error card. */\n failed: boolean;\n activityTicker: ActivityTicker;\n /** The agent works on a task: a turn is in flight or the ticker still shows its lines. */\n isAgentWorkingOnTask: boolean;\n /** The key of the agent space the chat is open on. */\n agentSpaceKey: number;\n}\n\n/** What the host's activity history under a message gets: the finished turn that message started, if any. */\nexport interface AgentChatActivityHistoryProps {\n turn: ActivityTickerTurn | null;\n}\n\n/** Where the model of the next send comes from — read at send time (an admin's pick can change between renders). */\nexport interface AgentChatSendModelSource {\n readModelForNextSend: () => string | undefined;\n}\n\nexport interface AgentChatControllerProps {\n /** The state handle of this chat instance (created and released by the app). */\n chatStore: AgentsChatStore;\n /** The conversation client this chat reads and sends through. */\n client: AgentsConversationClient;\n /** The key of the agent space new conversations start in. */\n agentSpaceKey: number;\n /** When set, load + resume this existing conversation. Absent = empty chat, lazy-starts on first send. */\n conversationId?: number;\n onNewConversation?: (conversationId: number) => void;\n /** The conversation could not be loaded (missing, deleted, not readable): the host drops to the empty chat. */\n onConversationNotFound?: () => void;\n /** Switch the active conversation — the ConversationSearchResults widget's open-arrow target. */\n onOpenConversation?: (conversationId: number, messageId?: number) => void;\n /** Switch this chat to another conversation of the same agent space in place (a widget linking to related work). */\n onSwitchConversation?: (conversationId: number) => void;\n /** Text inserted into the composer once on mount — never sent automatically. */\n initialComposerPrompt?: string;\n /** A request to scroll a message into view and flash it; re-applied whenever its `nonce` changes. */\n scrollTarget?: ChatScrollTarget;\n /** Each finished turn's activity lines stay in the thread above its answer (rendered by `ActivityHistory`). */\n keepActivityHistoryAfterAnswer?: boolean;\n /** The host shows one conversation the owner may not leave: no starting ideas (the agent speaks first). */\n isPinnedToSingleConversation?: boolean;\n /** App extension of the widget context: the chat is the app's guided first-run setup. */\n isOnboardingSetupConversation?: boolean;\n /** The model of the next send; without it the conversation's own model drives. */\n sendModelSource?: AgentChatSendModelSource;\n /** Called after a send that carried attachments, so the host clears them from the composer. */\n onAttachmentsSent?: () => void;\n /** The empty state of a new chat: a synthetic first assistant message (client state only), as markdown. */\n startingIdeasMarkdown?: string;\n /** While true the chat follows the bottom; a rising edge jumps to the newest message (a voice chat). */\n followBottom?: boolean;\n /** The chat's scroll container (the host may own it, e.g. for a scroll-linked background). */\n chatScrollRef: RefObject<HTMLDivElement | null>;\n isCompact?: boolean;\n confineTopBlurBandToChatPane?: boolean;\n /** Class names of the two wrappers around the conversation view (the host's layout). */\n chatAreaClassName?: string;\n chatWrapClassName?: string;\n /** Slots of the conversation view. */\n progressStepperSlot?: ReactNode;\n inputActionsSlot?: ReactNode;\n inputTopSlot?: ReactNode;\n inputSendDisabled?: boolean;\n inputReplacementSlot?: ReactNode;\n inputBandSlot?: ReactNode;\n inputPlaceholder?: string;\n inputSendReplacementWhenEmpty?: ReactNode;\n inputGlowUnderlay?: ReactNode;\n isContentOverlayShown?: boolean;\n isQuestionPinSuspended?: boolean;\n /** The attachments that ride along on the next send. */\n outgoingAttachments?: ConversationAttachmentReference[];\n /** The agent's activity at the tail of the thread (the agent avatar slot). */\n ThinkingIndicator: ComponentType<AgentChatActivityProps>;\n /** A layer over the message column (a voice chat's focus mode); shown while `isContentOverlayShown`. */\n ContentOverlay?: ComponentType<AgentChatActivityProps>;\n /** The kept activity lines under the message that started a turn (with `keepActivityHistoryAfterAnswer`). */\n ActivityHistory?: ComponentType<AgentChatActivityHistoryProps>;\n /** Rendered inside the chat's widget provider (the host's canvas syncs). */\n children?: ReactNode;\n}\n\n/** Returns true when the messages contain an ERROR message whose signalSource is AI_BUDGET_REACHED. */\nconst hasBudgetReachedError = (messages: ChatMessage[] | null): boolean =>\n (messages ?? []).some((message) => message.messageType === \"ERROR\" && message.signalSource === \"AI_BUDGET_REACHED\");\n\n/** Every port function rejects with `AgentsClientError`; anything else (a bug) reads as an `other` refusal. */\nconst toAgentsClientErrorOfPort = (failure: unknown): AgentsClientError =>\n isAgentsClientError(failure) ? failure : new AgentsClientError(\"other\");\n\nconst selectMessages = (state: AgentsChatState) => state.messages;\nconst selectError = (state: AgentsChatState) => state.error;\nconst selectRateLimitRetryAfterSeconds = (state: AgentsChatState) => state.rateLimitRetryAfterSeconds;\nconst selectConversation = (state: AgentsChatState) => state.conversation;\nconst selectStartingNewConversation = (state: AgentsChatState) => state.startingNewConversation;\nconst selectCanvas = (state: AgentsChatState) => state.canvas;\nconst selectHasMoreOlder = (state: AgentsChatState) => state.hasMoreOlder;\nconst selectOpeningRequest = (state: AgentsChatState) => state.openingRequest;\n\n/**\n * The agent chat: the controller over the conversation client port and the chat store — open, pages,\n * live updates, sends, signals, the error card and retry, the opening request, the deep-linked widget, the scroll jump\n * and the activity — rendering the conversation view with the host's slots. The drive is async: a reply never comes\n * in the start/post answer, it arrives through the live updates.\n */\nexport const AgentChatController = (props: AgentChatControllerProps) => {\n const {\n chatStore,\n client,\n agentSpaceKey,\n conversationId,\n onNewConversation,\n onConversationNotFound,\n onOpenConversation,\n onSwitchConversation,\n initialComposerPrompt,\n scrollTarget,\n keepActivityHistoryAfterAnswer = false,\n isPinnedToSingleConversation = false,\n isOnboardingSetupConversation = false,\n sendModelSource,\n onAttachmentsSent,\n startingIdeasMarkdown,\n followBottom = false,\n chatScrollRef,\n isCompact = false,\n confineTopBlurBandToChatPane = false,\n chatAreaClassName,\n chatWrapClassName,\n progressStepperSlot,\n inputActionsSlot,\n inputTopSlot,\n inputSendDisabled,\n inputReplacementSlot,\n inputBandSlot,\n inputPlaceholder,\n inputSendReplacementWhenEmpty,\n inputGlowUnderlay,\n isContentOverlayShown,\n isQuestionPinSuspended,\n outgoingAttachments,\n ThinkingIndicator,\n ContentOverlay,\n ActivityHistory,\n children,\n } = props;\n\n // The composer's handle: the initial prompt and a widget's `prefillComposer` put text into it without sending.\n const conversationMessageInputApiRef = useRef<ConversationMessageInputHandle>(null);\n useEffect(() => {\n if (!initialComposerPrompt) return;\n conversationMessageInputApiRef.current?.insertTextIntoInput(initialComposerPrompt);\n // Mount-only: a one-shot initial value, not a live prop to keep re-applying.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const messages = useAgentsChatSelector(chatStore, selectMessages);\n const error = useAgentsChatSelector(chatStore, selectError);\n const rateLimitRetryAfterSeconds = useAgentsChatSelector(chatStore, selectRateLimitRetryAfterSeconds);\n const conversation = useAgentsChatSelector(chatStore, selectConversation);\n const startingNewConversation = useAgentsChatSelector(chatStore, selectStartingNewConversation);\n const canvas = useAgentsChatSelector(chatStore, selectCanvas);\n const hasMoreOlder = useAgentsChatSelector(chatStore, selectHasMoreOlder);\n const openingRequest = useAgentsChatSelector(chatStore, selectOpeningRequest);\n const pendingStartUserMessage = openingRequest.userMessage;\n const pendingStartSignalText = openingRequest.startSignalText;\n const pendingStartEntryPointKey = openingRequest.entryPointKey;\n const pendingDeepLinkedWidgetName = openingRequest.deepLinkedWidgetName;\n\n // A terminal runState: the run is over, the server rejects further owner input (the closed bar replaces the input).\n const isConversationRunEnded = conversation?.runState === \"COMPLETED\" || conversation?.runState === \"CANCELLED\";\n\n // The message reads (newest page, changed tail, older page, metadata) over the client, written into the store.\n const messagePages = useConversationMessagePages(chatStore, client, CONVERSATION_MESSAGE_PAGE_SIZE);\n const { readConversationMetadata, fetchOpeningPage, loadOlder, loadingOlder } = messagePages;\n\n // Load an existing conversation: the metadata (no messages, non-destructive) apart from the newest page (merged,\n // never clears — no skeleton flash). A refused metadata read drops the host to the empty chat.\n useEffect(() => {\n if (!conversationId) {\n // Empty chat: reset so a stale conversation can't linger; the first owner message lazy-starts a new one.\n chatStore.dispatch({ type: \"reset\" });\n return;\n }\n if (String(conversation?.id) === String(conversationId)) return;\n chatStore.dispatch({ type: \"reset\" });\n void readConversationMetadata(conversationId).then((wasRead) => {\n if (!wasRead) onConversationNotFound?.();\n });\n fetchOpeningPage(conversationId);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [conversationId]);\n\n // Reset the chat state on unmount so reopening the chat starts clean.\n useEffect(() => {\n return () => {\n chatStore.dispatch({ type: \"reset\" });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const liveConversationId = conversation?.id ?? conversationId;\n\n // The deep-linked widget hand-over (the opening request's `deepLinkedWidgetName`): mounted ONCE per hand-over under\n // a fresh mount key, released once the canvas it declares (`opensCanvases`) is open — never just \"some canvas\" — or\n // after 15 s for a widget that opens none. A cold chat that remounts meanwhile keeps the request.\n const [deepLinkedWidget, setDeepLinkedWidget] = useState<{ name: string; mountKey: number } | undefined>(undefined);\n const mountedHandOverRef = useRef<string | null>(null);\n useEffect(() => {\n if (!pendingDeepLinkedWidgetName) {\n mountedHandOverRef.current = null;\n return;\n }\n if (mountedHandOverRef.current === pendingDeepLinkedWidgetName) return;\n mountedHandOverRef.current = pendingDeepLinkedWidgetName;\n setDeepLinkedWidget((previous) => ({ name: pendingDeepLinkedWidgetName, mountKey: (previous?.mountKey ?? 0) + 1 }));\n }, [pendingDeepLinkedWidgetName]);\n const handedOverOpensCanvas =\n canvas !== null &&\n (resolveDeepLinkableWidget(pendingDeepLinkedWidgetName)?.opensCanvases?.includes(canvas.canvasId) ?? false);\n useEffect(() => {\n if (!pendingDeepLinkedWidgetName) return;\n const releaseHandOver = () =>\n chatStore.dispatch({ type: \"openingRequestSet\", openingRequest: { deepLinkedWidgetName: null } });\n if (handedOverOpensCanvas) {\n releaseHandOver();\n return;\n }\n const release = window.setTimeout(releaseHandOver, 15000);\n return () => window.clearTimeout(release);\n }, [pendingDeepLinkedWidgetName, handedOverOpensCanvas, chatStore]);\n\n const hasMoreOlderRef = useRef(hasMoreOlder);\n hasMoreOlderRef.current = hasMoreOlder;\n const messagesRef = useRef(messages);\n messagesRef.current = messages;\n // Scroll-up pagination: reads the store, so its identity changes only with the conversation (the sentinel's\n // IntersectionObserver is not torn down on every message change).\n const loadOlderOfLiveConversation = useCallback(() => loadOlder(liveConversationId), [loadOlder, liveConversationId]);\n\n // The title lands asynchronously after the first turn: re-read the metadata only (the messages stay as they are).\n const refetchConversationSummary = useCallback(() => {\n if (!liveConversationId) return;\n void readConversationMetadata(liveConversationId);\n }, [readConversationMetadata, liveConversationId]);\n\n // Live updates: the package rules over the client's subscription; the binding owns the transport.\n const { runStatus, liveReadCount, readChangedTailNow } = useConversationLiveUpdates({\n client,\n chatStore,\n messagePages,\n conversationId: liveConversationId,\n });\n\n // What the agent is doing right now, one generated line per step, paced by the ticker.\n const {\n elements: activityElements,\n running: activityRunning,\n turnMessageId: activityTurnMessageId,\n } = useConversationActivity({\n conversationId: liveConversationId ?? 0,\n runStatus,\n // Every live read also asks for the activity lines; the hook throttles to one request per 3 s.\n signalVersion: liveReadCount,\n enabled: Boolean(liveConversationId),\n });\n const activityTicker = useActivityTicker({\n elements: activityElements,\n running: activityRunning,\n turnMessageId: activityTurnMessageId,\n failed: !!error,\n keepActivityHistoryAfterAnswer,\n });\n\n // The run status → the error card only (aiThinking follows the derived activity below). messages is a dependency:\n // the ERROR message with signalSource=AI_BUDGET_REACHED lands slightly after the status flips to \"failed\".\n useEffect(() => {\n if (runStatus === \"failed\") {\n chatStore.dispatch({\n type: \"errorSet\",\n error: hasBudgetReachedError(messages) ? ConversationErrorType.AI_BUDGET : ConversationErrorType.AGENT_ERROR,\n });\n } else if (runStatus === \"running\") {\n chatStore.dispatch({ type: \"errorSet\", error: null });\n }\n }, [runStatus, chatStore, messages]);\n\n // Retry of a FAILED run: re-drives the stored, unanswered owner message; accepted → the card goes.\n const handleRetryFailedRun = useCallback(async () => {\n const retryConversationId = conversationId ?? conversation?.id;\n if (!retryConversationId) {\n return;\n }\n await client.retryConversation(retryConversationId);\n chatStore.dispatch({ type: \"errorSet\", error: null });\n }, [conversationId, conversation?.id, client, chatStore]);\n\n // Bumped whenever the chat must jump to the newest content: a widget click that injects a signal, the closed refusal.\n const [scrollToBottomNonce, setScrollToBottomNonce] = useState(0);\n\n // While the host asks to follow the bottom (a voice chat), a question pinned by an earlier send must not keep the\n // new lines below the fold.\n useEffect(() => {\n if (followBottom) setScrollToBottomNonce((nonce) => nonce + 1);\n }, [followBottom]);\n\n /** The closed refusal on the error card, without any request — and the jump, so the card is in view. */\n const rejectAsConversationClosed = useCallback(() => {\n chatStore.dispatch({ type: \"errorSet\", error: ConversationErrorType.CONVERSATION_CLOSED });\n setScrollToBottomNonce((nonce) => nonce + 1);\n }, [chatStore]);\n\n const handleSendMessage = useCallback<SendMessageHandler>(\n (message, { startSignalText, attachments, entryPointKey }) => {\n // A closed conversation takes no further input — the typed input and every widget send.\n if (isConversationRunEnded) {\n rejectAsConversationClosed();\n return false;\n }\n // The canvas model's SEND boundary: a turn canvas ends, a pinned one stays.\n chatStore.dispatch({ type: \"canvasResetForNewTurn\" });\n const model = sendModelSource?.readModelForNextSend();\n // The optimistic bubble shows the text the server stores (attachments as the binding writes them).\n const sentMessageText = client.composeSentMessageText\n ? client.composeSentMessageText(message, attachments)\n : message;\n // Only a send that carried attachments clears them: a widget send and the cold-start greeting carry none and\n // must never wipe what the owner is still composing.\n const reportSentAttachments = () => {\n if (attachments?.length) onAttachmentsSent?.();\n };\n if (conversation?.id) {\n const postConversationId = conversation.id;\n chatStore.dispatch({ type: \"messagePostRequested\", message: { message: sentMessageText, role: \"USER\" } });\n client\n .postMessage(postConversationId, { text: message, model, attachments })\n .then((posted) =>\n chatStore.dispatch({ type: \"messagePostReceived\", conversationId: postConversationId, posted }),\n )\n .catch((failure: unknown) =>\n chatStore.dispatch({ type: \"messagePostFailed\", error: toAgentsClientErrorOfPort(failure) }),\n );\n reportSentAttachments();\n return true;\n }\n // No conversation yet — lazy start; `startSignalText` rides along as the invisible start context.\n if (startingNewConversation) return false;\n // The store hands out the start's token (it outlives a remount): its answer applies only while it is pending.\n chatStore.dispatch({ type: \"startRequested\", message: { message: sentMessageText, role: \"USER\" } });\n const startToken = chatStore.getState().pendingStartToken as number;\n client\n .startConversation({ agentSpaceKey, text: message, startSignalText, entryPointKey, model, attachments })\n .then((started) => {\n const isAnswerOfPendingStart = chatStore.getState().pendingStartToken === startToken;\n chatStore.dispatch({ type: \"startReceived\", startToken, started });\n if (!isAnswerOfPendingStart) return;\n // A start that reopened its entry point's recent conversation brings no messages: read its newest page.\n if (started.reopenedExisting) {\n fetchOpeningPage(started.conversation.id);\n }\n onNewConversation?.(started.conversation.id);\n })\n .catch((failure: unknown) => {\n chatStore.dispatch({ type: \"startFailed\", error: toAgentsClientErrorOfPort(failure) });\n });\n reportSentAttachments();\n return true;\n },\n [\n conversation,\n startingNewConversation,\n chatStore,\n client,\n agentSpaceKey,\n sendModelSource,\n onAttachmentsSent,\n isConversationRunEnded,\n rejectAsConversationClosed,\n fetchOpeningPage,\n onNewConversation,\n ],\n );\n\n // The opening request's message or start signal (an entry point outside the chat): posted as the first owner\n // message of a cold chat — keyed on the request, not on mount (the chat may already be open), never into an existing\n // conversation and never while a start is in flight. The send is read through a ref so it can't re-fire.\n const handleSendMessageRef = useRef(handleSendMessage);\n handleSendMessageRef.current = handleSendMessage;\n const sentStartRequestRef = useRef<PendingStartRequest | null>(null);\n const loadedConversationId = conversation?.id;\n useEffect(() => {\n const pendingStartRequest = pickPendingStartRequest(pendingStartUserMessage, pendingStartSignalText);\n if (!pendingStartRequest) {\n // Nothing pending — re-arm, so the SAME entry point can be clicked again.\n sentStartRequestRef.current = null;\n return;\n }\n const isReadyToSend = isChatReadyToSendPendingStartRequest({\n pendingStartRequest,\n activeConversationId: conversationId,\n loadedConversationId,\n startingNewConversation,\n alreadySentStartRequest: sentStartRequestRef.current,\n });\n if (!isReadyToSend) {\n return;\n }\n sentStartRequestRef.current = pendingStartRequest;\n // All cleared: a losing signal left behind would make the host drop the conversation this start just opened.\n chatStore.dispatch({\n type: \"openingRequestSet\",\n openingRequest: { userMessage: null, startSignalText: null, entryPointKey: null },\n });\n const entryPointKey = pendingStartEntryPointKey ?? undefined;\n if (pendingStartRequest.kind === \"signal\") {\n handleSendMessageRef.current(\"\", { startSignalText: pendingStartRequest.text, entryPointKey });\n } else {\n handleSendMessageRef.current(pendingStartRequest.text, { entryPointKey });\n }\n }, [\n pendingStartUserMessage,\n pendingStartSignalText,\n pendingStartEntryPointKey,\n conversationId,\n loadedConversationId,\n startingNewConversation,\n chatStore,\n ]);\n\n // The signals besides the owner's messages: a widget's signal, a parked context-only signal, the corrective signal\n // for a dropped widget block, the widget render report (one collector per conversation).\n const { handleInjectSignal, renderReportCollector } = useConversationSignals({\n client,\n conversationId,\n conversation,\n messages,\n runStatus,\n startingNewConversation,\n isConversationRunEnded,\n handleSendMessage,\n rejectAsConversationClosed,\n refetchConversationSummary,\n setScrollToBottomNonce,\n });\n\n // Scroll-to-message and keeping the activity ticker in view; owns the bottom-follow refs of the view body.\n const { followBottomRef, scrollJumpActiveRef } = useConversationScrollJump({\n scrollTarget,\n conversationId,\n conversation,\n messages,\n messagesRef,\n hasMoreOlderRef,\n loadOlder: loadOlderOfLiveConversation,\n chatScrollRef,\n activityTicker,\n });\n\n const widgetCallbacks = useMemo(\n () => ({\n // Message-only for widgets; resolves true only once the send was accepted (a widget locks on it).\n sendMessageToChat: (message: string) => Promise.resolve(handleSendMessage(message, {}) === true),\n injectSignal: handleInjectSignal,\n prefillComposer: (text: string) => conversationMessageInputApiRef.current?.insertTextIntoInput(text),\n chatAgentSpaceKey: agentSpaceKey,\n conversationId: liveConversationId,\n onOpenConversation,\n onSwitchConversation,\n isOnboardingSetupConversation,\n }),\n [\n handleSendMessage,\n handleInjectSignal,\n agentSpaceKey,\n liveConversationId,\n onOpenConversation,\n onSwitchConversation,\n isOnboardingSetupConversation,\n ],\n );\n\n // \"What is the agent doing right now\" — from the live messages and the run status.\n const agentActivity = useMemo(() => deriveAgentActivity(messages, runStatus), [messages, runStatus]);\n\n // The store's aiThinking (the launcher reads it) follows the activity the open chat shows.\n useEffect(() => {\n chatStore.dispatch({ type: \"aiThinkingSet\", aiThinking: agentActivity.active });\n }, [agentActivity.active, chatStore]);\n const isTurnRunning = agentActivity.active || runStatus === \"running\";\n useEffect(() => (isTurnRunning ? reportRunningTurn() : undefined), [isTurnRunning]);\n\n // Changes ONLY when a new message arrives (the merge re-allocates the list with identical content) — the clock of\n // the safety poll below.\n const latestMessageSignature = useMemo(() => {\n const list = messages ?? [];\n const last = list[list.length - 1];\n return `${list.length}:${last?.id ?? \"\"}`;\n }, [messages]);\n\n // The safety poll: one read after a full minute without a new message while a turn is in flight — the floor for a\n // dead live path, never the live path itself.\n useEffect(() => {\n if (!liveConversationId || !agentActivity.active) return;\n const intervalId = setInterval(readChangedTailNow, 60_000);\n return () => clearInterval(intervalId);\n }, [liveConversationId, agentActivity.active, readChangedTailNow, latestMessageSignature]);\n\n // A conversation of a scheduled run: its first owner message is the task's seed, shown collapsed — only when the\n // history is loaded down to its start.\n const isScheduleRunConversation = conversation?.initiatorType === \"SCHEDULE\";\n const taskInstructionMessageId = useMemo(() => {\n if (!isScheduleRunConversation || hasMoreOlder || !messages?.length) return undefined;\n return messages.find((message) => message.role === \"USER\")?.id ?? undefined;\n }, [isScheduleRunConversation, hasMoreOlder, messages]);\n\n // With keepActivityHistoryAfterAnswer, each finished turn's lines stand under the message that started the turn.\n const activityHistoryByTurnMessageId = useMemo(() => {\n const historyTurnsByTurnMessageId = new Map<number, ActivityTickerTurn>();\n activityTicker.historyTurns.forEach((historyTurn) => {\n if (historyTurn.turnMessageId !== null) historyTurnsByTurnMessageId.set(historyTurn.turnMessageId, historyTurn);\n });\n return historyTurnsByTurnMessageId;\n }, [activityTicker.historyTurns]);\n\n // The starting ideas: not on a host pinned to one conversation, not beside an open canvas.\n const showsStartingIdeas =\n startingIdeasMarkdown !== undefined &&\n !conversationId &&\n !messages?.length &&\n !isPinnedToSingleConversation &&\n canvas === null;\n const chatMessages = useMemo(() => {\n if (!showsStartingIdeas) return messages;\n const startingIdeasMessage: ChatMessage = { id: -1, role: \"ASSISTANT\", message: startingIdeasMarkdown, fresh: true };\n return [startingIdeasMessage];\n }, [showsStartingIdeas, startingIdeasMarkdown, messages]);\n\n const renderActivityHistoryAfterMessage = useCallback(\n (message: ChatMessage) =>\n ActivityHistory ? (\n <ActivityHistory\n turn={typeof message.id === \"number\" ? (activityHistoryByTurnMessageId.get(message.id) ?? null) : null}\n />\n ) : null,\n [ActivityHistory, activityHistoryByTurnMessageId],\n );\n\n const activityProps: AgentChatActivityProps = {\n active: agentActivity.active,\n failed: !!error,\n activityTicker,\n isAgentWorkingOnTask: agentActivity.active || activityTicker.phase === \"live\",\n agentSpaceKey,\n };\n\n return (\n <WidgetRenderReportCollectorContext.Provider value={renderReportCollector}>\n <ConversationWidgetProvider callbacks={widgetCallbacks} chatStore={chatStore}>\n {/* The tool a page handed over: renders nothing in the chat itself — such a widget lives in the canvas. */}\n <ConversationDeepLinkedWidget\n key={deepLinkedWidget?.mountKey ?? 0}\n widgetName={deepLinkedWidget?.name}\n agentSpaceKey={agentSpaceKey}\n />\n {/* The canvas that was open in this conversation comes back once ITS messages are in. */}\n <CanvasRestoreOnMount conversationId={liveConversationId} messages={messages} />\n {children}\n <div className={chatAreaClassName}>\n <div className={chatWrapClassName}>\n <ConversationView\n enableCanvasToggle\n boxCanvasBelowTopBar\n confineTopBlurBandToChatPane={confineTopBlurBandToChatPane}\n progressStepperSlot={progressStepperSlot}\n enablePinQuestionAndRevealAnswer\n isQuestionPinSuspended={isQuestionPinSuspended}\n scrollRef={chatScrollRef}\n scrollToBottomNonce={scrollToBottomNonce}\n followBottomRef={followBottomRef}\n scrollJumpActiveRef={scrollJumpActiveRef}\n // The host paints the background at its own root; this pane keeps the transparent custom-background mode.\n backgroundPaintedByHost\n currentCategory={undefined}\n aiPromptExtension={undefined}\n suggestions={undefined}\n messages={chatMessages}\n aiThinking={agentActivity.active}\n // The input never locks while the agent works: a message posted mid-turn is answered in the next turn.\n allowSendWhileAiThinking\n thinkingIndicator={<ThinkingIndicator {...activityProps} />}\n // Only while the history is kept: otherwise every row would carry an empty history slot.\n renderAfterMessage={keepActivityHistoryAfterAnswer ? renderActivityHistoryAfterMessage : undefined}\n hideAssistantMeta\n userMessageTheme=\"dark\"\n error={error}\n rateLimitRetryAfterSeconds={rateLimitRetryAfterSeconds}\n onRetryFailedRun={handleRetryFailedRun}\n isDebugMode={false}\n categoriesLoaded={true}\n conversationId={conversationId ?? conversation?.id ?? undefined}\n startingNewConversation={startingNewConversation}\n showMessagesWithoutConversation\n hasMoreOlder={hasMoreOlder}\n loadingOlder={loadingOlder}\n onLoadOlder={loadOlderOfLiveConversation}\n autoScroll={!!conversationId || (messages?.length ?? 0) > 0}\n isCompact={isCompact}\n inputActionsSlot={inputActionsSlot}\n inputTopSlot={inputTopSlot}\n inputSendDisabled={inputSendDisabled}\n inputReplacementSlot={inputReplacementSlot}\n inputBandSlot={inputBandSlot}\n inputPlaceholder={inputPlaceholder}\n inputSendReplacementWhenEmpty={inputSendReplacementWhenEmpty}\n inputGlowUnderlay={inputGlowUnderlay}\n contentOverlaySlot={ContentOverlay ? <ContentOverlay {...activityProps} /> : undefined}\n isContentOverlayShown={isContentOverlayShown}\n outgoingAttachmentImages={outgoingAttachments}\n taskInstructionMessageId={taskInstructionMessageId}\n isConversationClosed={isConversationRunEnded}\n conversationMessageInputApiRef={conversationMessageInputApiRef}\n onSuggestionClick={() => {}}\n onSendMessage={handleSendMessage}\n />\n </div>\n </div>\n </ConversationWidgetProvider>\n </WidgetRenderReportCollectorContext.Provider>\n );\n};\n","import { type ComponentType, type RefObject, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { AgentsConversationClient } from \"../../client/AgentsConversationClient\";\nimport { useOptionalAgentsConversationClient } from \"../../client/AgentsConversationClientContext\";\nimport type { PollingConversationChangeSubscriptionOptions } from \"../../client/polling/createPollingConversationChangeSubscription\";\nimport { withPollingChangeSubscription } from \"../../client/polling/withPollingChangeSubscription\";\nimport { useAgentsActiveAgentSpaceKey } from \"../../provider/AgentsProvider\";\nimport { AgentChatActivityHistory } from \"../AgentChatActivityHistory/AgentChatActivityHistory\";\nimport { AgentChatActivityIndicator } from \"../AgentChatActivityIndicator/AgentChatActivityIndicator\";\nimport { ConversationClosedBar } from \"../ConversationClosedBar/ConversationClosedBar\";\nimport {\n type AgentChatActivityHistoryProps,\n type AgentChatActivityProps,\n AgentChatController,\n type AgentChatControllerProps,\n} from \"../controller/AgentChatController\";\nimport type { AgentsChatState } from \"../store/agents-chat-state\";\nimport { type AgentsChatStore, createAgentsChatStore } from \"../store/create-agents-chat-store\";\nimport { useAgentsChatSelector } from \"../store/use-agents-chat-store\";\nimport styles from \"./AgentChat.module.scss\";\n\n/** The controller's inputs that `AgentChat` fills itself when the app leaves them out. */\ntype DefaultedControllerProps = \"chatStore\" | \"client\" | \"agentSpaceKey\" | \"chatScrollRef\" | \"ThinkingIndicator\";\n\nexport interface AgentChatProps extends Omit<AgentChatControllerProps, DefaultedControllerProps> {\n /** This chat instance's state handle. Default: a store of its own, created on mount and dropped on unmount. */\n chatStore?: AgentsChatStore;\n /**\n * The conversation client. Default: the one `AgentsProvider` (or `AgentsConversationClientProvider`) mounted. A client\n * without `subscribeToConversationChanges` gets the polling default (`withPollingChangeSubscription`).\n */\n client?: AgentsConversationClient;\n /**\n * The intervals of the polling default, for a client without a subscription of its own. Read once per client (when\n * the chat wraps it); ignored for a client that brings its own subscription.\n */\n pollingOptions?: PollingConversationChangeSubscriptionOptions;\n /** The key of the agent space new conversations start in. Default: `AgentsEnvironment.useActiveAgentSpaceKey()`, else 0 (no agent space). */\n agentSpaceKey?: number;\n /** The scroll container of the message list, when the app needs it (a scroll-linked background). Default: internal. */\n chatScrollRef?: RefObject<HTMLDivElement | null>;\n /** The agent's activity at the tail of the thread. Default: `AgentChatActivityIndicator` (dots and the ticker line). */\n ThinkingIndicator?: ComponentType<AgentChatActivityProps>;\n}\n\nconst selectConversation = (state: AgentsChatState) => state.conversation;\n\n/**\n * The agent chat with the package defaults (`./chat`): `<AgentsProvider client={client}><AgentChat\n * /></AgentsProvider>` is a working chat — message list, composer, activity indicator, error card with retry, canvas\n * pane, quick replies, older pages on scroll-up, the closed bar, live updates over the client's own subscription or,\n * without one, the polling default. Every slot and input of `AgentChatController` can still\n * be passed; what is left out gets its default here. Without `conversationId` the chat starts empty, lazy-starts a\n * conversation on the first send and then follows that conversation by itself (`onNewConversation` reports it).\n */\nexport const AgentChat = (props: AgentChatProps) => {\n const {\n chatStore: appChatStore,\n client: appClient,\n pollingOptions,\n agentSpaceKey: appAgentSpaceKey,\n chatScrollRef: appChatScrollRef,\n ThinkingIndicator = AgentChatActivityIndicator,\n ActivityHistory = AgentChatActivityHistory,\n conversationId: appConversationId,\n onNewConversation,\n inputReplacementSlot,\n chatAreaClassName = styles.chatArea,\n chatWrapClassName = styles.chatWrap,\n ...controllerProps\n } = props;\n\n const mountedClient = useOptionalAgentsConversationClient();\n const givenClient = appClient ?? mountedClient;\n // Live updates for a client without push: the polling default, one wrapper per given client. A client with its\n // own subscription is passed through unchanged.\n const client = useMemo(\n () => (givenClient === null ? null : withPollingChangeSubscription(givenClient, pollingOptions)),\n // The options are read once per client: an inline object must not re-create the subscription on every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [givenClient],\n );\n const [ownChatStore] = useState(createAgentsChatStore);\n const chatStore = appChatStore ?? ownChatStore;\n const environmentAgentSpaceKey = useAgentsActiveAgentSpaceKey();\n const agentSpaceKey = appAgentSpaceKey ?? environmentAgentSpaceKey ?? 0;\n const ownChatScrollRef = useRef<HTMLDivElement | null>(null);\n const chatScrollRef = appChatScrollRef ?? ownChatScrollRef;\n\n // The conversation a lazy start opened, followed until the app names another one.\n const [startedConversationId, setStartedConversationId] = useState<number | undefined>(undefined);\n useEffect(() => {\n setStartedConversationId(undefined);\n }, [appConversationId]);\n const conversationId = appConversationId ?? startedConversationId;\n const handleNewConversation = useCallback(\n (newConversationId: number) => {\n setStartedConversationId(newConversationId);\n onNewConversation?.(newConversationId);\n },\n [onNewConversation],\n );\n\n // A terminal run state: the server takes no further input, the closed bar stands in the composer's place.\n const conversation = useAgentsChatSelector(chatStore, selectConversation);\n const isConversationRunEnded = conversation?.runState === \"COMPLETED\" || conversation?.runState === \"CANCELLED\";\n\n if (client === null) {\n throw new Error(\"AgentChat has no conversation client: pass `client`, or mount it in <AgentsProvider client={…}>.\");\n }\n return (\n <AgentChatController\n {...controllerProps}\n chatStore={chatStore}\n client={client}\n agentSpaceKey={agentSpaceKey}\n conversationId={conversationId}\n onNewConversation={handleNewConversation}\n chatScrollRef={chatScrollRef}\n ThinkingIndicator={ThinkingIndicator}\n ActivityHistory={ActivityHistory}\n inputReplacementSlot={inputReplacementSlot ?? (isConversationRunEnded ? <ConversationClosedBar /> : undefined)}\n chatAreaClassName={chatAreaClassName}\n chatWrapClassName={chatWrapClassName}\n />\n );\n};\n\nexport type { AgentChatActivityHistoryProps, AgentChatActivityProps };\n","import { useCallback, useEffect, useState } from \"react\";\n\n/**\n * Shared semantic-search state with a hard 700ms debounce and a minimum three-character query because every request\n * runs an LLM reranker and consumes AI credits. `enabled` mirrors whether the owning panel is open; the caller runs\n * `clearSearch` in the open handler so it renders clean immediately, while a rising-edge reset effect would flash the\n * previous session's query and results.\n */\nexport const useDebouncedSemanticSearch = <TResult>(\n fetcher: (trimmedQuery: string) => Promise<TResult[]>,\n enabled: boolean,\n) => {\n const [query, setQuery] = useState(\"\");\n const [results, setResults] = useState<TResult[] | null>(null);\n const [searching, setSearching] = useState(false);\n const trimmedQuery = query.trim();\n\n const clearSearch = useCallback(() => {\n setQuery(\"\");\n setResults(null);\n }, []);\n\n useEffect(() => {\n if (!enabled || trimmedQuery.length < 3) {\n setResults(null);\n setSearching(false);\n return;\n }\n let cancelled = false;\n setSearching(true);\n // Drop the previous query's results the moment a new search starts, so the loading skeleton shows again — the\n // same as a first search from an empty input — instead of the stale hits lingering until the new ones arrive.\n setResults(null);\n const timer = setTimeout(() => {\n void (async () => {\n try {\n const fetchedResults = await fetcher(trimmedQuery);\n if (!cancelled) {\n setResults(fetchedResults);\n }\n } catch {\n if (!cancelled) {\n setResults([]);\n }\n } finally {\n if (!cancelled) {\n setSearching(false);\n }\n }\n })();\n }, 700);\n return () => {\n cancelled = true;\n clearTimeout(timer);\n };\n }, [trimmedQuery, enabled, fetcher]);\n\n return {\n query,\n setQuery,\n trimmedQuery,\n results,\n searching,\n isSearchActive: enabled && trimmedQuery.length >= 3,\n clearSearch,\n };\n};\n","import { useCallback } from \"react\";\nimport { useAgentsConversationClient } from \"../../client/AgentsConversationClientContext\";\nimport type {\n ConversationMessageSearchHit,\n ConversationSearchGroup,\n ConversationSearchOptions,\n} from \"../../contract/conversation\";\nimport { useDebouncedSemanticSearch } from \"./use-debounced-semantic-search\";\n\n/** The size of every chat search answer: ten matches, excerpts of 420 characters. */\nconst CHAT_SEARCH_OPTIONS: ConversationSearchOptions = { limit: 10, excerptLength: 420 };\n\nexport interface ConversationMessageSearchState {\n query: string;\n setQuery: (query: string) => void;\n trimmedQuery: string;\n hits: ConversationMessageSearchHit[] | null;\n searching: boolean;\n isSearchActive: boolean;\n /** Wipe query + hits. The caller runs this in the click handler that OPENS the panel (see hook doc). */\n clearSearch: () => void;\n}\n\nexport interface ConversationsSearchState {\n query: string;\n setQuery: (query: string) => void;\n trimmedQuery: string;\n searchGroups: ConversationSearchGroup[] | null;\n searching: boolean;\n isSearchActive: boolean;\n /** Wipe query + results. The caller runs this in the click handler that OPENS the panel (see hook doc). */\n clearSearch: () => void;\n}\n\n/**\n * In-conversation \"find in this chat\" search state: the conversation client's `searchConversationMessages` returns\n * the matched hits of one conversation.\n */\nexport const useConversationMessageSearch = (\n conversationId: number,\n enabled: boolean,\n): ConversationMessageSearchState => {\n const conversationClient = useAgentsConversationClient();\n const fetchConversationMessageHits = useCallback(\n (trimmedQuery: string) =>\n conversationClient.searchConversationMessages(conversationId, trimmedQuery, CHAT_SEARCH_OPTIONS),\n [conversationClient, conversationId],\n );\n const { results: hits, ...searchState } = useDebouncedSemanticSearch<ConversationMessageSearchHit>(\n fetchConversationMessageHits,\n enabled,\n );\n\n return {\n hits,\n ...searchState,\n };\n};\n\n/**\n * Cross-conversation \"search all my chats\" state: the conversation client's `searchConversations` returns the\n * matches grouped by conversation.\n */\nexport const useConversationsSearch = (enabled: boolean): ConversationsSearchState => {\n const conversationClient = useAgentsConversationClient();\n const fetchConversationSearchGroups = useCallback(\n (trimmedQuery: string) => conversationClient.searchConversations(trimmedQuery, CHAT_SEARCH_OPTIONS),\n [conversationClient],\n );\n const { results: searchGroups, ...searchState } = useDebouncedSemanticSearch<ConversationSearchGroup>(\n fetchConversationSearchGroups,\n enabled,\n );\n\n return {\n searchGroups,\n ...searchState,\n };\n};\n","import { Button, Text, type AgentsTextReplacements } from \"../../../provider/components\";\nimport React, { useEffect, useRef } from \"react\";\nimport styles from \"./ConversationMessageInputAttachmentsErrorBox.module.scss\";\n\nexport const ERROR_BOX_AUTO_DISMISS_AFTER_MS = 5000;\n\nexport interface ConversationMessageInputAttachmentsErrorBoxProps {\n /** Validation errors of ONE invalid selection, straight from `MediaItemFile.validationErrors`. */\n errors: { text: string; replacements?: AgentsTextReplacements }[];\n onDismiss: () => void;\n /** Stories pass a short value so play-tests don't wait 5 real seconds. */\n autoDismissAfterMs?: number;\n testId?: string;\n}\n\n/**\n * The single error box above the chat input for an invalid file selection. Auto-dismisses after\n * `autoDismissAfterMs` with a visible countdown bar (CSS animation — the timeout and the bar share\n * the duration and pause/resume together), or immediately via the ✕. Hovering the box pauses the\n * countdown (timer AND bar) so the user can finish reading; leaving resumes with the remaining\n * time. The HOST re-arms it for a new invalid selection by remounting (`key` bump) — mount is the\n * only moment the timer starts.\n */\nexport const ConversationMessageInputAttachmentsErrorBox = (\n props: ConversationMessageInputAttachmentsErrorBoxProps,\n) => {\n const {\n errors,\n onDismiss,\n autoDismissAfterMs = ERROR_BOX_AUTO_DISMISS_AFTER_MS,\n testId = \"chat-input-attachments\",\n } = props;\n\n const remainingDismissMsRef = useRef(autoDismissAfterMs);\n const dismissTimeoutIdRef = useRef<number | undefined>(undefined);\n const dismissTimeoutStartedAtMsRef = useRef(0);\n\n const startDismissTimeout = () => {\n dismissTimeoutStartedAtMsRef.current = Date.now();\n dismissTimeoutIdRef.current = window.setTimeout(onDismiss, remainingDismissMsRef.current);\n };\n\n const pauseDismissTimeout = () => {\n window.clearTimeout(dismissTimeoutIdRef.current);\n remainingDismissMsRef.current -= Date.now() - dismissTimeoutStartedAtMsRef.current;\n };\n\n useEffect(() => {\n startDismissTimeout();\n return () => window.clearTimeout(dismissTimeoutIdRef.current);\n // Mount-only by design: the host re-arms via remount.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return (\n <div\n className={styles.errorBox}\n onMouseEnter={pauseDismissTimeout}\n onMouseLeave={startDismissTimeout}\n data-testid={`${testId}-error-box`}\n >\n <div className={styles.errorLines}>\n {errors.map((error, errorIndex) => (\n <Text key={errorIndex} role=\"caption\" color=\"var(--agents-danger-500)\" replacements={error.replacements}>\n {error.text}\n </Text>\n ))}\n </div>\n <Button\n className={styles.dismissButton}\n variant=\"muted\"\n size=\"small\"\n icon=\"close\"\n onClick={onDismiss}\n testId={`${testId}-error-box-dismiss`}\n rounded\n />\n <div\n className={styles.countdownBar}\n style={{ animationDuration: `${autoDismissAfterMs}ms` }}\n data-testid={`${testId}-error-box-countdown`}\n />\n </div>\n );\n};\n","/** A file size as the attachment card shows it (\"1.5 MB\"); the same wording as the app's file helpers. */\nexport const convertBytesToSizeString = (bytes: number): string => {\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n if (bytes === 0) {\n return \"0 Byte\";\n }\n const sizeIndex = Math.floor(Math.log(bytes) / Math.log(1024));\n return parseFloat((bytes / Math.pow(1024, sizeIndex)).toFixed(2)) + \" \" + sizes[sizeIndex];\n};\n","import { Button, ProgressBar, Text } from \"../../../provider/components\";\nimport React from \"react\";\nimport styles from \"./ConversationMessageInputAttachmentPreviewCard.module.scss\";\nimport { classNames } from \"../../../provider/class-names\";\nimport { convertBytesToSizeString } from \"./convert-bytes-to-size-string\";\n\nexport type ConversationMessageInputAttachmentUploadStatus = \"uploading\" | \"uploaded\" | \"failed\";\n\n/**\n * Presentational shape of one attachment above the chat input. The host's attachment state\n * (e.g. `useAgentChatAttachments`' `AgentChatAttachment`) is a superset of this.\n */\nexport type ConversationMessageInputAttachmentPreview = {\n attachmentId: string;\n /** Data-URI of the image — renders the thumbnail card. Absent → generic file card. */\n previewImageSrc?: string;\n /** Generic-card fields (image cards show the thumbnail instead). */\n fileName?: string;\n fileSizeBytes?: number;\n mimeType?: string;\n uploadStatus: ConversationMessageInputAttachmentUploadStatus;\n /** 0-100; only meaningful while `uploadStatus` is \"uploading\". */\n uploadProgressPercent: number;\n};\n\nexport interface ConversationMessageInputAttachmentPreviewCardProps {\n attachment: ConversationMessageInputAttachmentPreview;\n /** The card's ✕ — the host drops the attachment (and discards its in-flight upload result). */\n onRemove: () => void;\n testId?: string;\n}\n\n/**\n * One attachment card above the chat input: an image thumbnail (or a generic name/type/size card\n * for future non-image types), an upload progress bar while the media-library upload runs, a\n * failed state, and a ✕ to remove.\n *\n * While uploading, a white caption with a black shadow sits over the image bottom:\n * \"Uploading (45%)...\" while bytes are still going out, \"Processing...\" once all bytes are sent\n * (progress 100%) but the media-library call hasn't resolved — the bar goes indeterminate for that\n * stretch. Both disappear when the upload settles.\n */\nexport const ConversationMessageInputAttachmentPreviewCard = (\n props: ConversationMessageInputAttachmentPreviewCardProps,\n) => {\n const { attachment, onRemove, testId = \"chat-input-attachment\" } = props;\n\n const isUploading = attachment.uploadStatus === \"uploading\";\n const hasFailed = attachment.uploadStatus === \"failed\";\n // All bytes sent, but the media-library call hasn't resolved yet.\n const isProcessingAfterUpload = isUploading && attachment.uploadProgressPercent >= 100;\n const fileTypeLabel = attachment.mimeType?.split(\"/\").pop()?.toUpperCase();\n\n return (\n <div\n className={classNames(styles.card, {\n [styles.cardFailed]: hasFailed,\n [styles.cardGenericFile]: !attachment.previewImageSrc,\n })}\n data-testid={`${testId}-card`}\n >\n {attachment.previewImageSrc ? (\n <img className={styles.thumbnail} src={attachment.previewImageSrc} alt={attachment.fileName ?? \"\"} />\n ) : (\n <div className={styles.fileFacts}>\n <Text role=\"help\" color=\"var(--agents-fg)\" textWrap=\"nowrap\" translate={false}>\n {attachment.fileName ?? \"\"}\n </Text>\n <Text role=\"caption\" color=\"var(--agents-fg-light)\" textWrap=\"nowrap\" translate={false}>\n {[fileTypeLabel, attachment.fileSizeBytes ? convertBytesToSizeString(attachment.fileSizeBytes) : undefined]\n .filter(Boolean)\n .join(\" • \")}\n </Text>\n </div>\n )}\n {isUploading && (\n <div className={styles.uploadProgress} data-testid={`${testId}-upload-progress`}>\n <Text\n className={styles.uploadStatusCaption}\n role=\"caption\"\n color=\"var(--agents-neutral-000)\"\n replacements={{ uploadProgressPercent: `${attachment.uploadProgressPercent}%` }}\n testId={`${testId}-upload-status`}\n >\n {isProcessingAfterUpload ? \"Processing...\" : \"Uploading (%uploadProgressPercent%)...\"}\n </Text>\n <ProgressBar\n value={attachment.uploadProgressPercent}\n indeterminate={!(attachment.uploadProgressPercent > 0 && !isProcessingAfterUpload)}\n color=\"var(--agents-primary-500)\"\n height=\"4px\"\n />\n </div>\n )}\n {hasFailed && (\n <Text\n className={styles.failedCaption}\n role=\"caption\"\n color=\"var(--agents-danger-500)\"\n testId={`${testId}-failed-caption`}\n >\n Upload failed\n </Text>\n )}\n {/* Own holder because the component library's Button puts `className` on the inner <button>, not on its\n in-flow wrapper span — the holder is what actually positions the ✕. */}\n <span className={styles.removeButtonHolder}>\n <Button\n className={styles.removeButton}\n variant=\"muted\"\n size=\"small\"\n icon=\"close\"\n onClick={onRemove}\n testId={`${testId}-remove-button`}\n rounded\n />\n </span>\n </div>\n );\n};\n","import React from \"react\";\nimport styles from \"./ConversationMessageInputAttachmentsPreviewStrip.module.scss\";\nimport { classNames } from \"../../../provider/class-names\";\nimport {\n ConversationMessageInputAttachmentPreview,\n ConversationMessageInputAttachmentPreviewCard,\n} from \"./ConversationMessageInputAttachmentPreviewCard\";\n\nexport interface ConversationMessageInputAttachmentsPreviewStripProps {\n attachments: ConversationMessageInputAttachmentPreview[];\n onRemoveAttachment: (attachmentId: string) => void;\n testId?: string;\n}\n\n/**\n * Horizontal strip of attachment cards above the chat input's text area. Grows in / shrinks out\n * with the grid-rows 0fr→1fr reveal (same trick as the selection chip) instead of popping; scrolls\n * horizontally when the cards outgrow the input's width.\n */\nexport const ConversationMessageInputAttachmentsPreviewStrip = (\n props: ConversationMessageInputAttachmentsPreviewStripProps,\n) => {\n const { attachments, onRemoveAttachment, testId = \"chat-input-attachments\" } = props;\n\n return (\n <div\n className={classNames(styles.stripReveal, { [styles.stripRevealOpen]: attachments.length > 0 })}\n data-testid={`${testId}-strip`}\n >\n <div className={styles.stripRevealInner}>\n <div className={styles.strip}>\n {attachments.map((attachment) => (\n <ConversationMessageInputAttachmentPreviewCard\n key={attachment.attachmentId}\n attachment={attachment}\n onRemove={() => onRemoveAttachment(attachment.attachmentId)}\n testId={`${testId}-${attachment.attachmentId}`}\n />\n ))}\n </div>\n </div>\n </div>\n );\n};\n"],"names":["styles","content","message","selectCanvas","selectMessages","actions","compact","collapsed","collapsedPillSizer","icon","title","description","replacements","Fragment","suggestions","visible","backgroundPaintedByHost","confineTopBlurBandToChatPane","messagesBottom","root","handle","min","max","tab","classes","selectConversation","chatMessages"],"mappings":"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"}
|