stream-chat-react-native-core 9.12.1 → 9.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/a2ui/package.json +6 -0
- package/ai-components/conversation/package.json +6 -0
- package/lib/commonjs/a11y/a11yUtils.js.map +1 -1
- package/lib/commonjs/a11y/hooks/useResolvedModalAccessibilityProps.js.map +1 -1
- package/lib/commonjs/a2ui/A2UIContexts.js +8 -0
- package/lib/commonjs/a2ui/A2UIContexts.js.map +1 -0
- package/lib/commonjs/a2ui/A2UIInstructions.js +49 -0
- package/lib/commonjs/a2ui/A2UIInstructions.js.map +1 -0
- package/lib/commonjs/a2ui/A2UIPlaceholder.js +37 -0
- package/lib/commonjs/a2ui/A2UIPlaceholder.js.map +1 -0
- package/lib/commonjs/a2ui/A2UIRenderer.js +363 -0
- package/lib/commonjs/a2ui/A2UIRenderer.js.map +1 -0
- package/lib/commonjs/a2ui/A2UIStream.js +91 -0
- package/lib/commonjs/a2ui/A2UIStream.js.map +1 -0
- package/lib/commonjs/a2ui/A2UISurface.js +237 -0
- package/lib/commonjs/a2ui/A2UISurface.js.map +1 -0
- package/lib/commonjs/a2ui/basicCatalog.json +1206 -0
- package/lib/commonjs/a2ui/basicFunctions.js +398 -0
- package/lib/commonjs/a2ui/basicFunctions.js.map +1 -0
- package/lib/commonjs/a2ui/catalogDocument.js +80 -0
- package/lib/commonjs/a2ui/catalogDocument.js.map +1 -0
- package/lib/commonjs/a2ui/catalogs.js +105 -0
- package/lib/commonjs/a2ui/catalogs.js.map +1 -0
- package/lib/commonjs/a2ui/components/DateTimeInput.js +445 -0
- package/lib/commonjs/a2ui/components/DateTimeInput.js.map +1 -0
- package/lib/commonjs/a2ui/components/Media.js +316 -0
- package/lib/commonjs/a2ui/components/Media.js.map +1 -0
- package/lib/commonjs/a2ui/components/basicComponents.js +975 -0
- package/lib/commonjs/a2ui/components/basicComponents.js.map +1 -0
- package/lib/commonjs/a2ui/components/iconPaths.js +66 -0
- package/lib/commonjs/a2ui/components/iconPaths.js.map +1 -0
- package/lib/commonjs/a2ui/components/types.js +4 -0
- package/lib/commonjs/a2ui/components/types.js.map +1 -0
- package/lib/commonjs/a2ui/index.js +70 -0
- package/lib/commonjs/a2ui/index.js.map +1 -0
- package/lib/commonjs/a2ui/instructions.js +268 -0
- package/lib/commonjs/a2ui/instructions.js.map +1 -0
- package/lib/commonjs/a2ui/secrets.js +169 -0
- package/lib/commonjs/a2ui/secrets.js.map +1 -0
- package/lib/commonjs/a2ui/useReplyState.js +35 -0
- package/lib/commonjs/a2ui/useReplyState.js.map +1 -0
- package/lib/commonjs/ai-components/AITypingIndicatorView.js +8 -18
- package/lib/commonjs/ai-components/AITypingIndicatorView.js.map +1 -1
- package/lib/commonjs/ai-components/README.md +64 -3
- package/lib/commonjs/ai-components/StreamingMessageView.js +26 -0
- package/lib/commonjs/ai-components/StreamingMessageView.js.map +1 -1
- package/lib/commonjs/ai-components/charts/components/PieLegend.js +2 -1
- package/lib/commonjs/ai-components/charts/components/PieLegend.js.map +1 -1
- package/lib/commonjs/ai-components/charts/components/VictoryChart.js +7 -2
- package/lib/commonjs/ai-components/charts/components/VictoryChart.js.map +1 -1
- package/lib/commonjs/ai-components/charts/utils/format-time-label.js +15 -0
- package/lib/commonjs/ai-components/charts/utils/format-time-label.js.map +1 -0
- package/lib/commonjs/ai-components/charts/vega-lite/schema.js +17 -2
- package/lib/commonjs/ai-components/charts/vega-lite/schema.js.map +1 -1
- package/lib/commonjs/ai-components/contexts/composer-context.js +13 -6
- package/lib/commonjs/ai-components/contexts/composer-context.js.map +1 -1
- package/lib/commonjs/ai-components/contexts/index.js +11 -0
- package/lib/commonjs/ai-components/contexts/index.js.map +1 -1
- package/lib/commonjs/ai-components/contexts/theme-context.js +33 -2
- package/lib/commonjs/ai-components/contexts/theme-context.js.map +1 -1
- package/lib/commonjs/ai-components/contexts/translation-context.js +29 -0
- package/lib/commonjs/ai-components/contexts/translation-context.js.map +1 -0
- package/lib/commonjs/ai-components/conversation/AIConversation.js +374 -0
- package/lib/commonjs/ai-components/conversation/AIConversation.js.map +1 -0
- package/lib/commonjs/ai-components/conversation/entries.js +98 -0
- package/lib/commonjs/ai-components/conversation/entries.js.map +1 -0
- package/lib/commonjs/ai-components/conversation/index.js +15 -0
- package/lib/commonjs/ai-components/conversation/index.js.map +1 -0
- package/lib/commonjs/ai-components/conversation/list/AIChatList.js +224 -0
- package/lib/commonjs/ai-components/conversation/list/AIChatList.js.map +1 -0
- package/lib/commonjs/ai-components/conversation/list/useChatListController.js +78 -0
- package/lib/commonjs/ai-components/conversation/list/useChatListController.js.map +1 -0
- package/lib/commonjs/ai-components/conversation/list/useEndInView.js +51 -0
- package/lib/commonjs/ai-components/conversation/list/useEndInView.js.map +1 -0
- package/lib/commonjs/ai-components/conversation/list/useKeyboardOverlap.js +53 -0
- package/lib/commonjs/ai-components/conversation/list/useKeyboardOverlap.js.map +1 -0
- package/lib/commonjs/ai-components/conversation/list/useLoadOlder.js +73 -0
- package/lib/commonjs/ai-components/conversation/list/useLoadOlder.js.map +1 -0
- package/lib/commonjs/ai-components/conversation/list/usePinnedAnchor.js +84 -0
- package/lib/commonjs/ai-components/conversation/list/usePinnedAnchor.js.map +1 -0
- package/lib/commonjs/ai-components/conversation/useConversationRows.js +117 -0
- package/lib/commonjs/ai-components/conversation/useConversationRows.js.map +1 -0
- package/lib/commonjs/ai-components/conversation-state/controller/AIAgentError.js +13 -0
- package/lib/commonjs/ai-components/conversation-state/controller/AIAgentError.js.map +1 -0
- package/lib/commonjs/ai-components/conversation-state/controller/AIConversationController.js +431 -0
- package/lib/commonjs/ai-components/conversation-state/controller/AIConversationController.js.map +1 -0
- package/lib/commonjs/ai-components/conversation-state/controller/AIConversationExchange.js +89 -0
- package/lib/commonjs/ai-components/conversation-state/controller/AIConversationExchange.js.map +1 -0
- package/lib/commonjs/ai-components/conversation-state/controller/index.js +48 -0
- package/lib/commonjs/ai-components/conversation-state/controller/index.js.map +1 -0
- package/lib/commonjs/ai-components/conversation-state/controller/offers.js +35 -0
- package/lib/commonjs/ai-components/conversation-state/controller/offers.js.map +1 -0
- package/lib/commonjs/ai-components/conversation-state/controller/types.js +13 -0
- package/lib/commonjs/ai-components/conversation-state/controller/types.js.map +1 -0
- package/lib/commonjs/ai-components/conversation-state/controller/useAIConversationState.js +8 -0
- package/lib/commonjs/ai-components/conversation-state/controller/useAIConversationState.js.map +1 -0
- package/lib/commonjs/ai-components/conversation-state/fromStreamMessage.js +67 -0
- package/lib/commonjs/ai-components/conversation-state/fromStreamMessage.js.map +1 -0
- package/lib/commonjs/ai-components/conversation-state/index.js +37 -0
- package/lib/commonjs/ai-components/conversation-state/index.js.map +1 -0
- package/lib/commonjs/ai-components/conversation-state/types.js +4 -0
- package/lib/commonjs/ai-components/conversation-state/types.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIAction.js +30 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIAction.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIArguments.js +128 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIArguments.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIComponent.js +50 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIComponent.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIComponentDefinition.js +134 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIComponentDefinition.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIDocument.js +32 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIDocument.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIEntrance.js +197 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIEntrance.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIErrorBoundary.js +30 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIErrorBoundary.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIHelpers.js +54 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIHelpers.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIHost.js +259 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIHost.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIMedia.js +24 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIMedia.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIRenderer.js +10 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIRenderer.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIView.js +57 -0
- package/lib/commonjs/ai-components/generative-ui/AIUIView.js.map +1 -0
- package/lib/commonjs/ai-components/generative-ui/index.js +181 -0
- package/lib/commonjs/ai-components/generative-ui/index.js.map +1 -0
- package/lib/commonjs/ai-components/icons.js +44 -0
- package/lib/commonjs/ai-components/icons.js.map +1 -0
- package/lib/commonjs/ai-components/index.js +96 -0
- package/lib/commonjs/ai-components/index.js.map +1 -1
- package/lib/commonjs/ai-components/internal/Shimmer.js +36 -0
- package/lib/commonjs/ai-components/internal/Shimmer.js.map +1 -0
- package/lib/commonjs/ai-components/internal/format.js +66 -0
- package/lib/commonjs/ai-components/internal/format.js.map +1 -0
- package/lib/commonjs/ai-components/internal/hooks/useBottomSafeArea.js +12 -0
- package/lib/commonjs/ai-components/internal/hooks/useBottomSafeArea.js.map +1 -0
- package/lib/commonjs/ai-components/internal/icons/AgentIcons.js +74 -0
- package/lib/commonjs/ai-components/internal/icons/AgentIcons.js.map +1 -0
- package/lib/commonjs/ai-components/internal/keyboard.js +40 -0
- package/lib/commonjs/ai-components/internal/keyboard.js.map +1 -0
- package/lib/commonjs/ai-components/internal/tokens.js +11 -0
- package/lib/commonjs/ai-components/internal/tokens.js.map +1 -0
- package/lib/commonjs/ai-components/internal/uuid.js +21 -0
- package/lib/commonjs/ai-components/internal/uuid.js.map +1 -0
- package/lib/commonjs/ai-components/markdown/components/CodeBlock.js +4 -2
- package/lib/commonjs/ai-components/markdown/components/CodeBlock.js.map +1 -1
- package/lib/commonjs/ai-components/markdown/components/InlineCode.js +1 -0
- package/lib/commonjs/ai-components/markdown/components/InlineCode.js.map +1 -1
- package/lib/commonjs/ai-components/markdown/components/Text.js +11 -2
- package/lib/commonjs/ai-components/markdown/components/Text.js.map +1 -1
- package/lib/commonjs/ai-components/markdown/styles.js +27 -29
- package/lib/commonjs/ai-components/markdown/styles.js.map +1 -1
- package/lib/commonjs/ai-components/message/AIAssistantMessage.js +64 -0
- package/lib/commonjs/ai-components/message/AIAssistantMessage.js.map +1 -0
- package/lib/commonjs/ai-components/message/AIMessageAttachment.js +189 -0
- package/lib/commonjs/ai-components/message/AIMessageAttachment.js.map +1 -0
- package/lib/commonjs/ai-components/message/AIReplyActions.js +132 -0
- package/lib/commonjs/ai-components/message/AIReplyActions.js.map +1 -0
- package/lib/commonjs/ai-components/message/AIUserMessage.js +104 -0
- package/lib/commonjs/ai-components/message/AIUserMessage.js.map +1 -0
- package/lib/commonjs/ai-components/message/UserMessageMenu.js +150 -0
- package/lib/commonjs/ai-components/message/UserMessageMenu.js.map +1 -0
- package/lib/commonjs/ai-components/message/index.js +48 -0
- package/lib/commonjs/ai-components/message/index.js.map +1 -0
- package/lib/commonjs/ai-components/message-composer/ActionButton.js +4 -2
- package/lib/commonjs/ai-components/message-composer/ActionButton.js.map +1 -1
- package/lib/commonjs/ai-components/message-composer/ActionSheet.js +22 -6
- package/lib/commonjs/ai-components/message-composer/ActionSheet.js.map +1 -1
- package/lib/commonjs/ai-components/message-composer/AttachmentButton.js +5 -1
- package/lib/commonjs/ai-components/message-composer/AttachmentButton.js.map +1 -1
- package/lib/commonjs/ai-components/message-composer/ChatOptions.js +147 -0
- package/lib/commonjs/ai-components/message-composer/ChatOptions.js.map +1 -0
- package/lib/commonjs/ai-components/message-composer/ComposerView.js +146 -20
- package/lib/commonjs/ai-components/message-composer/ComposerView.js.map +1 -1
- package/lib/commonjs/ai-components/message-composer/EditingMessageChip.js +81 -0
- package/lib/commonjs/ai-components/message-composer/EditingMessageChip.js.map +1 -0
- package/lib/commonjs/ai-components/message-composer/MediaPreviewList.js +26 -1
- package/lib/commonjs/ai-components/message-composer/MediaPreviewList.js.map +1 -1
- package/lib/commonjs/ai-components/message-composer/index.js +11 -0
- package/lib/commonjs/ai-components/message-composer/index.js.map +1 -1
- package/lib/commonjs/ai-components/message-parts/AIMessageParts.js +133 -0
- package/lib/commonjs/ai-components/message-parts/AIMessageParts.js.map +1 -0
- package/lib/commonjs/ai-components/message-parts/AIToolApproval.js +197 -0
- package/lib/commonjs/ai-components/message-parts/AIToolApproval.js.map +1 -0
- package/lib/commonjs/ai-components/message-parts/AIToolAuthorization.js +250 -0
- package/lib/commonjs/ai-components/message-parts/AIToolAuthorization.js.map +1 -0
- package/lib/commonjs/ai-components/message-parts/AIToolCall.js +159 -0
- package/lib/commonjs/ai-components/message-parts/AIToolCall.js.map +1 -0
- package/lib/commonjs/ai-components/message-parts/callStates.js +54 -0
- package/lib/commonjs/ai-components/message-parts/callStates.js.map +1 -0
- package/lib/commonjs/ai-components/message-parts/index.js +70 -0
- package/lib/commonjs/ai-components/message-parts/index.js.map +1 -0
- package/lib/commonjs/ai-components/message-parts/parseMessageParts.js +126 -0
- package/lib/commonjs/ai-components/message-parts/parseMessageParts.js.map +1 -0
- package/lib/commonjs/ai-components/message-parts/types.js +2 -0
- package/lib/commonjs/ai-components/message-parts/types.js.map +1 -0
- package/lib/commonjs/ai-components/native-specs/NativeAIAgent.js +7 -0
- package/lib/commonjs/ai-components/native-specs/NativeAIAgent.js.map +1 -0
- package/lib/commonjs/ai-components/reasoning/AIReasoning.js +401 -0
- package/lib/commonjs/ai-components/reasoning/AIReasoning.js.map +1 -0
- package/lib/commonjs/ai-components/reasoning/index.js +26 -0
- package/lib/commonjs/ai-components/reasoning/index.js.map +1 -0
- package/lib/commonjs/ai-components/reasoning/thinkingClock.js +63 -0
- package/lib/commonjs/ai-components/reasoning/thinkingClock.js.map +1 -0
- package/lib/commonjs/ai-components/services/clipboard-service/expo.js +7 -2
- package/lib/commonjs/ai-components/services/clipboard-service/expo.js.map +1 -1
- package/lib/commonjs/ai-components/store/composer/store.js.map +1 -1
- package/lib/commonjs/ai-components/store/composer/useComposerChatOption.js +18 -0
- package/lib/commonjs/ai-components/store/composer/useComposerChatOption.js.map +1 -0
- package/lib/commonjs/ai-components/store/composer/useComposerIsEditing.js +18 -0
- package/lib/commonjs/ai-components/store/composer/useComposerIsEditing.js.map +1 -0
- package/lib/commonjs/ai-components/store/index.js +22 -0
- package/lib/commonjs/ai-components/store/index.js.map +1 -1
- package/lib/commonjs/ai-components/suggestions/AISuggestions.js +66 -0
- package/lib/commonjs/ai-components/suggestions/AISuggestions.js.map +1 -0
- package/lib/commonjs/ai-components/suggestions/index.js +15 -0
- package/lib/commonjs/ai-components/suggestions/index.js.map +1 -0
- package/lib/commonjs/ai-components/syntax-highlighting/utils/flatten-nodes.js +2 -1
- package/lib/commonjs/ai-components/syntax-highlighting/utils/flatten-nodes.js.map +1 -1
- package/lib/commonjs/ai-components/syntax-highlighting/utils/renderer.js +3 -2
- package/lib/commonjs/ai-components/syntax-highlighting/utils/renderer.js.map +1 -1
- package/lib/commonjs/ai-components/theme.js +137 -1
- package/lib/commonjs/ai-components/theme.js.map +1 -1
- package/lib/commonjs/ai-components/tool-calls/AIClientIdentity.js +22 -0
- package/lib/commonjs/ai-components/tool-calls/AIClientIdentity.js.map +1 -0
- package/lib/commonjs/ai-components/tool-calls/AIClientToolRunner.js +109 -0
- package/lib/commonjs/ai-components/tool-calls/AIClientToolRunner.js.map +1 -0
- package/lib/commonjs/ai-components/tool-calls/AIToolApprover.js +23 -0
- package/lib/commonjs/ai-components/tool-calls/AIToolApprover.js.map +1 -0
- package/lib/commonjs/ai-components/tool-calls/authSessionAuthorizer.js +88 -0
- package/lib/commonjs/ai-components/tool-calls/authSessionAuthorizer.js.map +1 -0
- package/lib/commonjs/ai-components/tool-calls/index.js +48 -0
- package/lib/commonjs/ai-components/tool-calls/index.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/glowShader.js.map +1 -1
- package/lib/commonjs/components/Accessibility/useAccessibilityAnnouncer.js.map +1 -1
- package/lib/commonjs/components/Message/MessageItemView/utils/renderText.js.map +1 -1
- package/lib/commonjs/contexts/translationContext/TranslationContext.js +2 -2
- package/lib/commonjs/contexts/translationContext/TranslationContext.js.map +1 -1
- package/lib/commonjs/i18n/ar.json +40 -1
- package/lib/commonjs/i18n/en.json +40 -1
- package/lib/commonjs/i18n/es.json +40 -1
- package/lib/commonjs/i18n/fr.json +40 -1
- package/lib/commonjs/i18n/he.json +40 -1
- package/lib/commonjs/i18n/hi.json +40 -1
- package/lib/commonjs/i18n/it.json +40 -1
- package/lib/commonjs/i18n/ja.json +40 -1
- package/lib/commonjs/i18n/ko.json +40 -1
- package/lib/commonjs/i18n/nl.json +40 -1
- package/lib/commonjs/i18n/pt-br.json +40 -1
- package/lib/commonjs/i18n/ru.json +40 -1
- package/lib/commonjs/i18n/tr.json +40 -1
- package/lib/commonjs/mock-builders/a2ui/ohrid.js +84 -0
- package/lib/commonjs/mock-builders/a2ui/ohrid.js.map +1 -0
- package/lib/commonjs/utils/i18n/Streami18n.js +9 -4
- package/lib/commonjs/utils/i18n/Streami18n.js.map +1 -1
- package/lib/commonjs/utils/i18n/utils.js +7 -0
- package/lib/commonjs/utils/i18n/utils.js.map +1 -0
- package/lib/commonjs/version.json +1 -1
- package/lib/module/a11y/a11yUtils.js.map +1 -1
- package/lib/module/a11y/hooks/useResolvedModalAccessibilityProps.js.map +1 -1
- package/lib/module/a2ui/A2UIContexts.js +8 -0
- package/lib/module/a2ui/A2UIContexts.js.map +1 -0
- package/lib/module/a2ui/A2UIInstructions.js +49 -0
- package/lib/module/a2ui/A2UIInstructions.js.map +1 -0
- package/lib/module/a2ui/A2UIPlaceholder.js +37 -0
- package/lib/module/a2ui/A2UIPlaceholder.js.map +1 -0
- package/lib/module/a2ui/A2UIRenderer.js +363 -0
- package/lib/module/a2ui/A2UIRenderer.js.map +1 -0
- package/lib/module/a2ui/A2UIStream.js +91 -0
- package/lib/module/a2ui/A2UIStream.js.map +1 -0
- package/lib/module/a2ui/A2UISurface.js +237 -0
- package/lib/module/a2ui/A2UISurface.js.map +1 -0
- package/lib/module/a2ui/basicCatalog.json +1206 -0
- package/lib/module/a2ui/basicFunctions.js +398 -0
- package/lib/module/a2ui/basicFunctions.js.map +1 -0
- package/lib/module/a2ui/catalogDocument.js +80 -0
- package/lib/module/a2ui/catalogDocument.js.map +1 -0
- package/lib/module/a2ui/catalogs.js +105 -0
- package/lib/module/a2ui/catalogs.js.map +1 -0
- package/lib/module/a2ui/components/DateTimeInput.js +445 -0
- package/lib/module/a2ui/components/DateTimeInput.js.map +1 -0
- package/lib/module/a2ui/components/Media.js +316 -0
- package/lib/module/a2ui/components/Media.js.map +1 -0
- package/lib/module/a2ui/components/basicComponents.js +975 -0
- package/lib/module/a2ui/components/basicComponents.js.map +1 -0
- package/lib/module/a2ui/components/iconPaths.js +66 -0
- package/lib/module/a2ui/components/iconPaths.js.map +1 -0
- package/lib/module/a2ui/components/types.js +4 -0
- package/lib/module/a2ui/components/types.js.map +1 -0
- package/lib/module/a2ui/index.js +70 -0
- package/lib/module/a2ui/index.js.map +1 -0
- package/lib/module/a2ui/instructions.js +268 -0
- package/lib/module/a2ui/instructions.js.map +1 -0
- package/lib/module/a2ui/secrets.js +169 -0
- package/lib/module/a2ui/secrets.js.map +1 -0
- package/lib/module/a2ui/useReplyState.js +35 -0
- package/lib/module/a2ui/useReplyState.js.map +1 -0
- package/lib/module/ai-components/AITypingIndicatorView.js +8 -18
- package/lib/module/ai-components/AITypingIndicatorView.js.map +1 -1
- package/lib/module/ai-components/README.md +64 -3
- package/lib/module/ai-components/StreamingMessageView.js +26 -0
- package/lib/module/ai-components/StreamingMessageView.js.map +1 -1
- package/lib/module/ai-components/charts/components/PieLegend.js +2 -1
- package/lib/module/ai-components/charts/components/PieLegend.js.map +1 -1
- package/lib/module/ai-components/charts/components/VictoryChart.js +7 -2
- package/lib/module/ai-components/charts/components/VictoryChart.js.map +1 -1
- package/lib/module/ai-components/charts/utils/format-time-label.js +15 -0
- package/lib/module/ai-components/charts/utils/format-time-label.js.map +1 -0
- package/lib/module/ai-components/charts/vega-lite/schema.js +17 -2
- package/lib/module/ai-components/charts/vega-lite/schema.js.map +1 -1
- package/lib/module/ai-components/contexts/composer-context.js +13 -6
- package/lib/module/ai-components/contexts/composer-context.js.map +1 -1
- package/lib/module/ai-components/contexts/index.js +11 -0
- package/lib/module/ai-components/contexts/index.js.map +1 -1
- package/lib/module/ai-components/contexts/theme-context.js +33 -2
- package/lib/module/ai-components/contexts/theme-context.js.map +1 -1
- package/lib/module/ai-components/contexts/translation-context.js +29 -0
- package/lib/module/ai-components/contexts/translation-context.js.map +1 -0
- package/lib/module/ai-components/conversation/AIConversation.js +374 -0
- package/lib/module/ai-components/conversation/AIConversation.js.map +1 -0
- package/lib/module/ai-components/conversation/entries.js +98 -0
- package/lib/module/ai-components/conversation/entries.js.map +1 -0
- package/lib/module/ai-components/conversation/index.js +15 -0
- package/lib/module/ai-components/conversation/index.js.map +1 -0
- package/lib/module/ai-components/conversation/list/AIChatList.js +224 -0
- package/lib/module/ai-components/conversation/list/AIChatList.js.map +1 -0
- package/lib/module/ai-components/conversation/list/useChatListController.js +78 -0
- package/lib/module/ai-components/conversation/list/useChatListController.js.map +1 -0
- package/lib/module/ai-components/conversation/list/useEndInView.js +51 -0
- package/lib/module/ai-components/conversation/list/useEndInView.js.map +1 -0
- package/lib/module/ai-components/conversation/list/useKeyboardOverlap.js +53 -0
- package/lib/module/ai-components/conversation/list/useKeyboardOverlap.js.map +1 -0
- package/lib/module/ai-components/conversation/list/useLoadOlder.js +73 -0
- package/lib/module/ai-components/conversation/list/useLoadOlder.js.map +1 -0
- package/lib/module/ai-components/conversation/list/usePinnedAnchor.js +84 -0
- package/lib/module/ai-components/conversation/list/usePinnedAnchor.js.map +1 -0
- package/lib/module/ai-components/conversation/useConversationRows.js +117 -0
- package/lib/module/ai-components/conversation/useConversationRows.js.map +1 -0
- package/lib/module/ai-components/conversation-state/controller/AIAgentError.js +13 -0
- package/lib/module/ai-components/conversation-state/controller/AIAgentError.js.map +1 -0
- package/lib/module/ai-components/conversation-state/controller/AIConversationController.js +431 -0
- package/lib/module/ai-components/conversation-state/controller/AIConversationController.js.map +1 -0
- package/lib/module/ai-components/conversation-state/controller/AIConversationExchange.js +89 -0
- package/lib/module/ai-components/conversation-state/controller/AIConversationExchange.js.map +1 -0
- package/lib/module/ai-components/conversation-state/controller/index.js +48 -0
- package/lib/module/ai-components/conversation-state/controller/index.js.map +1 -0
- package/lib/module/ai-components/conversation-state/controller/offers.js +35 -0
- package/lib/module/ai-components/conversation-state/controller/offers.js.map +1 -0
- package/lib/module/ai-components/conversation-state/controller/types.js +13 -0
- package/lib/module/ai-components/conversation-state/controller/types.js.map +1 -0
- package/lib/module/ai-components/conversation-state/controller/useAIConversationState.js +8 -0
- package/lib/module/ai-components/conversation-state/controller/useAIConversationState.js.map +1 -0
- package/lib/module/ai-components/conversation-state/fromStreamMessage.js +67 -0
- package/lib/module/ai-components/conversation-state/fromStreamMessage.js.map +1 -0
- package/lib/module/ai-components/conversation-state/index.js +37 -0
- package/lib/module/ai-components/conversation-state/index.js.map +1 -0
- package/lib/module/ai-components/conversation-state/types.js +4 -0
- package/lib/module/ai-components/conversation-state/types.js.map +1 -0
- package/lib/module/ai-components/generative-ui/AIUIAction.js +30 -0
- package/lib/module/ai-components/generative-ui/AIUIAction.js.map +1 -0
- package/lib/module/ai-components/generative-ui/AIUIArguments.js +128 -0
- package/lib/module/ai-components/generative-ui/AIUIArguments.js.map +1 -0
- package/lib/module/ai-components/generative-ui/AIUIComponent.js +50 -0
- package/lib/module/ai-components/generative-ui/AIUIComponent.js.map +1 -0
- package/lib/module/ai-components/generative-ui/AIUIComponentDefinition.js +134 -0
- package/lib/module/ai-components/generative-ui/AIUIComponentDefinition.js.map +1 -0
- package/lib/module/ai-components/generative-ui/AIUIDocument.js +32 -0
- package/lib/module/ai-components/generative-ui/AIUIDocument.js.map +1 -0
- package/lib/module/ai-components/generative-ui/AIUIEntrance.js +197 -0
- package/lib/module/ai-components/generative-ui/AIUIEntrance.js.map +1 -0
- package/lib/module/ai-components/generative-ui/AIUIErrorBoundary.js +30 -0
- package/lib/module/ai-components/generative-ui/AIUIErrorBoundary.js.map +1 -0
- package/lib/module/ai-components/generative-ui/AIUIHelpers.js +54 -0
- package/lib/module/ai-components/generative-ui/AIUIHelpers.js.map +1 -0
- package/lib/module/ai-components/generative-ui/AIUIHost.js +259 -0
- package/lib/module/ai-components/generative-ui/AIUIHost.js.map +1 -0
- package/lib/module/ai-components/generative-ui/AIUIMedia.js +24 -0
- package/lib/module/ai-components/generative-ui/AIUIMedia.js.map +1 -0
- package/lib/module/ai-components/generative-ui/AIUIRenderer.js +10 -0
- package/lib/module/ai-components/generative-ui/AIUIRenderer.js.map +1 -0
- package/lib/module/ai-components/generative-ui/AIUIView.js +57 -0
- package/lib/module/ai-components/generative-ui/AIUIView.js.map +1 -0
- package/lib/module/ai-components/generative-ui/index.js +181 -0
- package/lib/module/ai-components/generative-ui/index.js.map +1 -0
- package/lib/module/ai-components/icons.js +44 -0
- package/lib/module/ai-components/icons.js.map +1 -0
- package/lib/module/ai-components/index.js +96 -0
- package/lib/module/ai-components/index.js.map +1 -1
- package/lib/module/ai-components/internal/Shimmer.js +36 -0
- package/lib/module/ai-components/internal/Shimmer.js.map +1 -0
- package/lib/module/ai-components/internal/format.js +66 -0
- package/lib/module/ai-components/internal/format.js.map +1 -0
- package/lib/module/ai-components/internal/hooks/useBottomSafeArea.js +12 -0
- package/lib/module/ai-components/internal/hooks/useBottomSafeArea.js.map +1 -0
- package/lib/module/ai-components/internal/icons/AgentIcons.js +74 -0
- package/lib/module/ai-components/internal/icons/AgentIcons.js.map +1 -0
- package/lib/module/ai-components/internal/keyboard.js +40 -0
- package/lib/module/ai-components/internal/keyboard.js.map +1 -0
- package/lib/module/ai-components/internal/tokens.js +11 -0
- package/lib/module/ai-components/internal/tokens.js.map +1 -0
- package/lib/module/ai-components/internal/uuid.js +21 -0
- package/lib/module/ai-components/internal/uuid.js.map +1 -0
- package/lib/module/ai-components/markdown/components/CodeBlock.js +4 -2
- package/lib/module/ai-components/markdown/components/CodeBlock.js.map +1 -1
- package/lib/module/ai-components/markdown/components/InlineCode.js +1 -0
- package/lib/module/ai-components/markdown/components/InlineCode.js.map +1 -1
- package/lib/module/ai-components/markdown/components/Text.js +11 -2
- package/lib/module/ai-components/markdown/components/Text.js.map +1 -1
- package/lib/module/ai-components/markdown/styles.js +27 -29
- package/lib/module/ai-components/markdown/styles.js.map +1 -1
- package/lib/module/ai-components/message/AIAssistantMessage.js +64 -0
- package/lib/module/ai-components/message/AIAssistantMessage.js.map +1 -0
- package/lib/module/ai-components/message/AIMessageAttachment.js +189 -0
- package/lib/module/ai-components/message/AIMessageAttachment.js.map +1 -0
- package/lib/module/ai-components/message/AIReplyActions.js +132 -0
- package/lib/module/ai-components/message/AIReplyActions.js.map +1 -0
- package/lib/module/ai-components/message/AIUserMessage.js +104 -0
- package/lib/module/ai-components/message/AIUserMessage.js.map +1 -0
- package/lib/module/ai-components/message/UserMessageMenu.js +150 -0
- package/lib/module/ai-components/message/UserMessageMenu.js.map +1 -0
- package/lib/module/ai-components/message/index.js +48 -0
- package/lib/module/ai-components/message/index.js.map +1 -0
- package/lib/module/ai-components/message-composer/ActionButton.js +4 -2
- package/lib/module/ai-components/message-composer/ActionButton.js.map +1 -1
- package/lib/module/ai-components/message-composer/ActionSheet.js +22 -6
- package/lib/module/ai-components/message-composer/ActionSheet.js.map +1 -1
- package/lib/module/ai-components/message-composer/AttachmentButton.js +5 -1
- package/lib/module/ai-components/message-composer/AttachmentButton.js.map +1 -1
- package/lib/module/ai-components/message-composer/ChatOptions.js +147 -0
- package/lib/module/ai-components/message-composer/ChatOptions.js.map +1 -0
- package/lib/module/ai-components/message-composer/ComposerView.js +146 -20
- package/lib/module/ai-components/message-composer/ComposerView.js.map +1 -1
- package/lib/module/ai-components/message-composer/EditingMessageChip.js +81 -0
- package/lib/module/ai-components/message-composer/EditingMessageChip.js.map +1 -0
- package/lib/module/ai-components/message-composer/MediaPreviewList.js +26 -1
- package/lib/module/ai-components/message-composer/MediaPreviewList.js.map +1 -1
- package/lib/module/ai-components/message-composer/index.js +11 -0
- package/lib/module/ai-components/message-composer/index.js.map +1 -1
- package/lib/module/ai-components/message-parts/AIMessageParts.js +133 -0
- package/lib/module/ai-components/message-parts/AIMessageParts.js.map +1 -0
- package/lib/module/ai-components/message-parts/AIToolApproval.js +197 -0
- package/lib/module/ai-components/message-parts/AIToolApproval.js.map +1 -0
- package/lib/module/ai-components/message-parts/AIToolAuthorization.js +250 -0
- package/lib/module/ai-components/message-parts/AIToolAuthorization.js.map +1 -0
- package/lib/module/ai-components/message-parts/AIToolCall.js +159 -0
- package/lib/module/ai-components/message-parts/AIToolCall.js.map +1 -0
- package/lib/module/ai-components/message-parts/callStates.js +54 -0
- package/lib/module/ai-components/message-parts/callStates.js.map +1 -0
- package/lib/module/ai-components/message-parts/index.js +70 -0
- package/lib/module/ai-components/message-parts/index.js.map +1 -0
- package/lib/module/ai-components/message-parts/parseMessageParts.js +126 -0
- package/lib/module/ai-components/message-parts/parseMessageParts.js.map +1 -0
- package/lib/module/ai-components/message-parts/types.js +2 -0
- package/lib/module/ai-components/message-parts/types.js.map +1 -0
- package/lib/module/ai-components/native-specs/NativeAIAgent.js +7 -0
- package/lib/module/ai-components/native-specs/NativeAIAgent.js.map +1 -0
- package/lib/module/ai-components/reasoning/AIReasoning.js +401 -0
- package/lib/module/ai-components/reasoning/AIReasoning.js.map +1 -0
- package/lib/module/ai-components/reasoning/index.js +26 -0
- package/lib/module/ai-components/reasoning/index.js.map +1 -0
- package/lib/module/ai-components/reasoning/thinkingClock.js +63 -0
- package/lib/module/ai-components/reasoning/thinkingClock.js.map +1 -0
- package/lib/module/ai-components/services/clipboard-service/expo.js +7 -2
- package/lib/module/ai-components/services/clipboard-service/expo.js.map +1 -1
- package/lib/module/ai-components/store/composer/store.js.map +1 -1
- package/lib/module/ai-components/store/composer/useComposerChatOption.js +18 -0
- package/lib/module/ai-components/store/composer/useComposerChatOption.js.map +1 -0
- package/lib/module/ai-components/store/composer/useComposerIsEditing.js +18 -0
- package/lib/module/ai-components/store/composer/useComposerIsEditing.js.map +1 -0
- package/lib/module/ai-components/store/index.js +22 -0
- package/lib/module/ai-components/store/index.js.map +1 -1
- package/lib/module/ai-components/suggestions/AISuggestions.js +66 -0
- package/lib/module/ai-components/suggestions/AISuggestions.js.map +1 -0
- package/lib/module/ai-components/suggestions/index.js +15 -0
- package/lib/module/ai-components/suggestions/index.js.map +1 -0
- package/lib/module/ai-components/syntax-highlighting/utils/flatten-nodes.js +2 -1
- package/lib/module/ai-components/syntax-highlighting/utils/flatten-nodes.js.map +1 -1
- package/lib/module/ai-components/syntax-highlighting/utils/renderer.js +3 -2
- package/lib/module/ai-components/syntax-highlighting/utils/renderer.js.map +1 -1
- package/lib/module/ai-components/theme.js +137 -1
- package/lib/module/ai-components/theme.js.map +1 -1
- package/lib/module/ai-components/tool-calls/AIClientIdentity.js +22 -0
- package/lib/module/ai-components/tool-calls/AIClientIdentity.js.map +1 -0
- package/lib/module/ai-components/tool-calls/AIClientToolRunner.js +109 -0
- package/lib/module/ai-components/tool-calls/AIClientToolRunner.js.map +1 -0
- package/lib/module/ai-components/tool-calls/AIToolApprover.js +23 -0
- package/lib/module/ai-components/tool-calls/AIToolApprover.js.map +1 -0
- package/lib/module/ai-components/tool-calls/authSessionAuthorizer.js +88 -0
- package/lib/module/ai-components/tool-calls/authSessionAuthorizer.js.map +1 -0
- package/lib/module/ai-components/tool-calls/index.js +48 -0
- package/lib/module/ai-components/tool-calls/index.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/glowShader.js.map +1 -1
- package/lib/module/components/Accessibility/useAccessibilityAnnouncer.js.map +1 -1
- package/lib/module/components/Message/MessageItemView/utils/renderText.js.map +1 -1
- package/lib/module/contexts/translationContext/TranslationContext.js +2 -2
- package/lib/module/contexts/translationContext/TranslationContext.js.map +1 -1
- package/lib/module/i18n/ar.json +40 -1
- package/lib/module/i18n/en.json +40 -1
- package/lib/module/i18n/es.json +40 -1
- package/lib/module/i18n/fr.json +40 -1
- package/lib/module/i18n/he.json +40 -1
- package/lib/module/i18n/hi.json +40 -1
- package/lib/module/i18n/it.json +40 -1
- package/lib/module/i18n/ja.json +40 -1
- package/lib/module/i18n/ko.json +40 -1
- package/lib/module/i18n/nl.json +40 -1
- package/lib/module/i18n/pt-br.json +40 -1
- package/lib/module/i18n/ru.json +40 -1
- package/lib/module/i18n/tr.json +40 -1
- package/lib/module/mock-builders/a2ui/ohrid.js +84 -0
- package/lib/module/mock-builders/a2ui/ohrid.js.map +1 -0
- package/lib/module/utils/i18n/Streami18n.js +9 -4
- package/lib/module/utils/i18n/Streami18n.js.map +1 -1
- package/lib/module/utils/i18n/utils.js +7 -0
- package/lib/module/utils/i18n/utils.js.map +1 -0
- package/lib/module/version.json +1 -1
- package/lib/typescript/a11y/a11yUtils.d.ts +0 -1
- package/lib/typescript/a11y/a11yUtils.d.ts.map +1 -1
- package/lib/typescript/a11y/hooks/useResolvedModalAccessibilityProps.d.ts +2 -3
- package/lib/typescript/a11y/hooks/useResolvedModalAccessibilityProps.d.ts.map +1 -1
- package/lib/typescript/a2ui/A2UIContexts.d.ts +9 -0
- package/lib/typescript/a2ui/A2UIContexts.d.ts.map +1 -0
- package/lib/typescript/a2ui/A2UIInstructions.d.ts +47 -0
- package/lib/typescript/a2ui/A2UIInstructions.d.ts.map +1 -0
- package/lib/typescript/a2ui/A2UIPlaceholder.d.ts +4 -0
- package/lib/typescript/a2ui/A2UIPlaceholder.d.ts.map +1 -0
- package/lib/typescript/a2ui/A2UIRenderer.d.ts +130 -0
- package/lib/typescript/a2ui/A2UIRenderer.d.ts.map +1 -0
- package/lib/typescript/a2ui/A2UIStream.d.ts +23 -0
- package/lib/typescript/a2ui/A2UIStream.d.ts.map +1 -0
- package/lib/typescript/a2ui/A2UISurface.d.ts +19 -0
- package/lib/typescript/a2ui/A2UISurface.d.ts.map +1 -0
- package/lib/typescript/a2ui/basicFunctions.d.ts +10 -0
- package/lib/typescript/a2ui/basicFunctions.d.ts.map +1 -0
- package/lib/typescript/a2ui/catalogDocument.d.ts +56 -0
- package/lib/typescript/a2ui/catalogDocument.d.ts.map +1 -0
- package/lib/typescript/a2ui/catalogs.d.ts +42 -0
- package/lib/typescript/a2ui/catalogs.d.ts.map +1 -0
- package/lib/typescript/a2ui/components/DateTimeInput.d.ts +44 -0
- package/lib/typescript/a2ui/components/DateTimeInput.d.ts.map +1 -0
- package/lib/typescript/a2ui/components/Media.d.ts +27 -0
- package/lib/typescript/a2ui/components/Media.d.ts.map +1 -0
- package/lib/typescript/a2ui/components/basicComponents.d.ts +4 -0
- package/lib/typescript/a2ui/components/basicComponents.d.ts.map +1 -0
- package/lib/typescript/a2ui/components/iconPaths.d.ts +2 -0
- package/lib/typescript/a2ui/components/iconPaths.d.ts.map +1 -0
- package/lib/typescript/a2ui/components/types.d.ts +23 -0
- package/lib/typescript/a2ui/components/types.d.ts.map +1 -0
- package/lib/typescript/a2ui/index.d.ts +8 -0
- package/lib/typescript/a2ui/index.d.ts.map +1 -0
- package/lib/typescript/a2ui/instructions.d.ts +39 -0
- package/lib/typescript/a2ui/instructions.d.ts.map +1 -0
- package/lib/typescript/a2ui/secrets.d.ts +20 -0
- package/lib/typescript/a2ui/secrets.d.ts.map +1 -0
- package/lib/typescript/a2ui/useReplyState.d.ts +4 -0
- package/lib/typescript/a2ui/useReplyState.d.ts.map +1 -0
- package/lib/typescript/ai-components/AITypingIndicatorView.d.ts.map +1 -1
- package/lib/typescript/ai-components/StreamingMessageView.d.ts +4 -1
- package/lib/typescript/ai-components/StreamingMessageView.d.ts.map +1 -1
- package/lib/typescript/ai-components/charts/components/PieLegend.d.ts.map +1 -1
- package/lib/typescript/ai-components/charts/components/VictoryChart.d.ts.map +1 -1
- package/lib/typescript/ai-components/charts/utils/format-time-label.d.ts +7 -0
- package/lib/typescript/ai-components/charts/utils/format-time-label.d.ts.map +1 -0
- package/lib/typescript/ai-components/charts/vega-lite/schema.d.ts +8 -8
- package/lib/typescript/ai-components/charts/vega-lite/schema.d.ts.map +1 -1
- package/lib/typescript/ai-components/contexts/composer-context.d.ts +3 -0
- package/lib/typescript/ai-components/contexts/composer-context.d.ts.map +1 -1
- package/lib/typescript/ai-components/contexts/index.d.ts +1 -0
- package/lib/typescript/ai-components/contexts/index.d.ts.map +1 -1
- package/lib/typescript/ai-components/contexts/theme-context.d.ts.map +1 -1
- package/lib/typescript/ai-components/contexts/translation-context.d.ts +19 -0
- package/lib/typescript/ai-components/contexts/translation-context.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation/AIConversation.d.ts +133 -0
- package/lib/typescript/ai-components/conversation/AIConversation.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation/entries.d.ts +47 -0
- package/lib/typescript/ai-components/conversation/entries.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation/index.d.ts +3 -0
- package/lib/typescript/ai-components/conversation/index.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation/list/AIChatList.d.ts +30 -0
- package/lib/typescript/ai-components/conversation/list/AIChatList.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation/list/useChatListController.d.ts +45 -0
- package/lib/typescript/ai-components/conversation/list/useChatListController.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation/list/useEndInView.d.ts +26 -0
- package/lib/typescript/ai-components/conversation/list/useEndInView.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation/list/useKeyboardOverlap.d.ts +17 -0
- package/lib/typescript/ai-components/conversation/list/useKeyboardOverlap.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation/list/useLoadOlder.d.ts +24 -0
- package/lib/typescript/ai-components/conversation/list/useLoadOlder.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation/list/usePinnedAnchor.d.ts +26 -0
- package/lib/typescript/ai-components/conversation/list/usePinnedAnchor.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation/useConversationRows.d.ts +66 -0
- package/lib/typescript/ai-components/conversation/useConversationRows.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation-state/controller/AIAgentError.d.ts +10 -0
- package/lib/typescript/ai-components/conversation-state/controller/AIAgentError.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation-state/controller/AIConversationController.d.ts +173 -0
- package/lib/typescript/ai-components/conversation-state/controller/AIConversationController.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation-state/controller/AIConversationExchange.d.ts +23 -0
- package/lib/typescript/ai-components/conversation-state/controller/AIConversationExchange.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation-state/controller/index.d.ts +5 -0
- package/lib/typescript/ai-components/conversation-state/controller/index.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation-state/controller/offers.d.ts +25 -0
- package/lib/typescript/ai-components/conversation-state/controller/offers.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation-state/controller/types.d.ts +142 -0
- package/lib/typescript/ai-components/conversation-state/controller/types.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation-state/controller/useAIConversationState.d.ts +9 -0
- package/lib/typescript/ai-components/conversation-state/controller/useAIConversationState.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation-state/fromStreamMessage.d.ts +25 -0
- package/lib/typescript/ai-components/conversation-state/fromStreamMessage.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation-state/index.d.ts +4 -0
- package/lib/typescript/ai-components/conversation-state/index.d.ts.map +1 -0
- package/lib/typescript/ai-components/conversation-state/types.d.ts +55 -0
- package/lib/typescript/ai-components/conversation-state/types.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/AIUIAction.d.ts +48 -0
- package/lib/typescript/ai-components/generative-ui/AIUIAction.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/AIUIArguments.d.ts +15 -0
- package/lib/typescript/ai-components/generative-ui/AIUIArguments.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/AIUIComponent.d.ts +51 -0
- package/lib/typescript/ai-components/generative-ui/AIUIComponent.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/AIUIComponentDefinition.d.ts +84 -0
- package/lib/typescript/ai-components/generative-ui/AIUIComponentDefinition.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/AIUIDocument.d.ts +50 -0
- package/lib/typescript/ai-components/generative-ui/AIUIDocument.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/AIUIEntrance.d.ts +76 -0
- package/lib/typescript/ai-components/generative-ui/AIUIEntrance.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/AIUIErrorBoundary.d.ts +26 -0
- package/lib/typescript/ai-components/generative-ui/AIUIErrorBoundary.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/AIUIHelpers.d.ts +23 -0
- package/lib/typescript/ai-components/generative-ui/AIUIHelpers.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/AIUIHost.d.ts +160 -0
- package/lib/typescript/ai-components/generative-ui/AIUIHost.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/AIUIMedia.d.ts +22 -0
- package/lib/typescript/ai-components/generative-ui/AIUIMedia.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/AIUIRenderer.d.ts +53 -0
- package/lib/typescript/ai-components/generative-ui/AIUIRenderer.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/AIUIView.d.ts +35 -0
- package/lib/typescript/ai-components/generative-ui/AIUIView.d.ts.map +1 -0
- package/lib/typescript/ai-components/generative-ui/index.d.ts +11 -0
- package/lib/typescript/ai-components/generative-ui/index.d.ts.map +1 -0
- package/lib/typescript/ai-components/icons.d.ts +66 -0
- package/lib/typescript/ai-components/icons.d.ts.map +1 -0
- package/lib/typescript/ai-components/index.d.ts +8 -0
- package/lib/typescript/ai-components/index.d.ts.map +1 -1
- package/lib/typescript/ai-components/internal/Shimmer.d.ts +14 -0
- package/lib/typescript/ai-components/internal/Shimmer.d.ts.map +1 -0
- package/lib/typescript/ai-components/internal/format.d.ts +7 -0
- package/lib/typescript/ai-components/internal/format.d.ts.map +1 -0
- package/lib/typescript/ai-components/internal/hooks/useBottomSafeArea.d.ts +3 -0
- package/lib/typescript/ai-components/internal/hooks/useBottomSafeArea.d.ts.map +1 -0
- package/lib/typescript/ai-components/internal/icons/AgentIcons.d.ts +31 -0
- package/lib/typescript/ai-components/internal/icons/AgentIcons.d.ts.map +1 -0
- package/lib/typescript/ai-components/internal/keyboard.d.ts +9 -0
- package/lib/typescript/ai-components/internal/keyboard.d.ts.map +1 -0
- package/lib/typescript/ai-components/internal/tokens.d.ts +2 -0
- package/lib/typescript/ai-components/internal/tokens.d.ts.map +1 -0
- package/lib/typescript/ai-components/internal/uuid.d.ts +2 -0
- package/lib/typescript/ai-components/internal/uuid.d.ts.map +1 -0
- package/lib/typescript/ai-components/markdown/components/CodeBlock.d.ts.map +1 -1
- package/lib/typescript/ai-components/markdown/components/InlineCode.d.ts.map +1 -1
- package/lib/typescript/ai-components/markdown/components/Text.d.ts.map +1 -1
- package/lib/typescript/ai-components/markdown/styles.d.ts +23 -4
- package/lib/typescript/ai-components/markdown/styles.d.ts.map +1 -1
- package/lib/typescript/ai-components/markdown/types.d.ts +1 -0
- package/lib/typescript/ai-components/markdown/types.d.ts.map +1 -1
- package/lib/typescript/ai-components/message/AIAssistantMessage.d.ts +40 -0
- package/lib/typescript/ai-components/message/AIAssistantMessage.d.ts.map +1 -0
- package/lib/typescript/ai-components/message/AIMessageAttachment.d.ts +33 -0
- package/lib/typescript/ai-components/message/AIMessageAttachment.d.ts.map +1 -0
- package/lib/typescript/ai-components/message/AIReplyActions.d.ts +19 -0
- package/lib/typescript/ai-components/message/AIReplyActions.d.ts.map +1 -0
- package/lib/typescript/ai-components/message/AIUserMessage.d.ts +17 -0
- package/lib/typescript/ai-components/message/AIUserMessage.d.ts.map +1 -0
- package/lib/typescript/ai-components/message/UserMessageMenu.d.ts +31 -0
- package/lib/typescript/ai-components/message/UserMessageMenu.d.ts.map +1 -0
- package/lib/typescript/ai-components/message/index.d.ts +5 -0
- package/lib/typescript/ai-components/message/index.d.ts.map +1 -0
- package/lib/typescript/ai-components/message-composer/ActionButton.d.ts +1 -1
- package/lib/typescript/ai-components/message-composer/ActionButton.d.ts.map +1 -1
- package/lib/typescript/ai-components/message-composer/ActionSheet.d.ts +2 -2
- package/lib/typescript/ai-components/message-composer/ActionSheet.d.ts.map +1 -1
- package/lib/typescript/ai-components/message-composer/AttachmentButton.d.ts.map +1 -1
- package/lib/typescript/ai-components/message-composer/ChatOptions.d.ts +35 -0
- package/lib/typescript/ai-components/message-composer/ChatOptions.d.ts.map +1 -0
- package/lib/typescript/ai-components/message-composer/ComposerView.d.ts +39 -3
- package/lib/typescript/ai-components/message-composer/ComposerView.d.ts.map +1 -1
- package/lib/typescript/ai-components/message-composer/EditingMessageChip.d.ts +7 -0
- package/lib/typescript/ai-components/message-composer/EditingMessageChip.d.ts.map +1 -0
- package/lib/typescript/ai-components/message-composer/MediaPreviewList.d.ts.map +1 -1
- package/lib/typescript/ai-components/message-composer/index.d.ts +1 -0
- package/lib/typescript/ai-components/message-composer/index.d.ts.map +1 -1
- package/lib/typescript/ai-components/message-parts/AIMessageParts.d.ts +40 -0
- package/lib/typescript/ai-components/message-parts/AIMessageParts.d.ts.map +1 -0
- package/lib/typescript/ai-components/message-parts/AIToolApproval.d.ts +38 -0
- package/lib/typescript/ai-components/message-parts/AIToolApproval.d.ts.map +1 -0
- package/lib/typescript/ai-components/message-parts/AIToolAuthorization.d.ts +48 -0
- package/lib/typescript/ai-components/message-parts/AIToolAuthorization.d.ts.map +1 -0
- package/lib/typescript/ai-components/message-parts/AIToolCall.d.ts +11 -0
- package/lib/typescript/ai-components/message-parts/AIToolCall.d.ts.map +1 -0
- package/lib/typescript/ai-components/message-parts/callStates.d.ts +9 -0
- package/lib/typescript/ai-components/message-parts/callStates.d.ts.map +1 -0
- package/lib/typescript/ai-components/message-parts/index.d.ts +7 -0
- package/lib/typescript/ai-components/message-parts/index.d.ts.map +1 -0
- package/lib/typescript/ai-components/message-parts/parseMessageParts.d.ts +47 -0
- package/lib/typescript/ai-components/message-parts/parseMessageParts.d.ts.map +1 -0
- package/lib/typescript/ai-components/message-parts/types.d.ts +137 -0
- package/lib/typescript/ai-components/message-parts/types.d.ts.map +1 -0
- package/lib/typescript/ai-components/native-specs/NativeAIAgent.d.ts +22 -0
- package/lib/typescript/ai-components/native-specs/NativeAIAgent.d.ts.map +1 -0
- package/lib/typescript/ai-components/reasoning/AIReasoning.d.ts +63 -0
- package/lib/typescript/ai-components/reasoning/AIReasoning.d.ts.map +1 -0
- package/lib/typescript/ai-components/reasoning/index.d.ts +3 -0
- package/lib/typescript/ai-components/reasoning/index.d.ts.map +1 -0
- package/lib/typescript/ai-components/reasoning/thinkingClock.d.ts +29 -0
- package/lib/typescript/ai-components/reasoning/thinkingClock.d.ts.map +1 -0
- package/lib/typescript/ai-components/services/clipboard-service/expo.d.ts.map +1 -1
- package/lib/typescript/ai-components/store/composer/store.d.ts +8 -0
- package/lib/typescript/ai-components/store/composer/store.d.ts.map +1 -1
- package/lib/typescript/ai-components/store/composer/useComposerChatOption.d.ts +5 -0
- package/lib/typescript/ai-components/store/composer/useComposerChatOption.d.ts.map +1 -0
- package/lib/typescript/ai-components/store/composer/useComposerIsEditing.d.ts +5 -0
- package/lib/typescript/ai-components/store/composer/useComposerIsEditing.d.ts.map +1 -0
- package/lib/typescript/ai-components/store/index.d.ts +2 -0
- package/lib/typescript/ai-components/store/index.d.ts.map +1 -1
- package/lib/typescript/ai-components/suggestions/AISuggestions.d.ts +13 -0
- package/lib/typescript/ai-components/suggestions/AISuggestions.d.ts.map +1 -0
- package/lib/typescript/ai-components/suggestions/index.d.ts +2 -0
- package/lib/typescript/ai-components/suggestions/index.d.ts.map +1 -0
- package/lib/typescript/ai-components/syntax-highlighting/utils/flatten-nodes.d.ts.map +1 -1
- package/lib/typescript/ai-components/syntax-highlighting/utils/renderer.d.ts +1 -1
- package/lib/typescript/ai-components/syntax-highlighting/utils/renderer.d.ts.map +1 -1
- package/lib/typescript/ai-components/theme.d.ts +200 -0
- package/lib/typescript/ai-components/theme.d.ts.map +1 -1
- package/lib/typescript/ai-components/tool-calls/AIClientIdentity.d.ts +14 -0
- package/lib/typescript/ai-components/tool-calls/AIClientIdentity.d.ts.map +1 -0
- package/lib/typescript/ai-components/tool-calls/AIClientToolRunner.d.ts +109 -0
- package/lib/typescript/ai-components/tool-calls/AIClientToolRunner.d.ts.map +1 -0
- package/lib/typescript/ai-components/tool-calls/AIToolApprover.d.ts +48 -0
- package/lib/typescript/ai-components/tool-calls/AIToolApprover.d.ts.map +1 -0
- package/lib/typescript/ai-components/tool-calls/authSessionAuthorizer.d.ts +57 -0
- package/lib/typescript/ai-components/tool-calls/authSessionAuthorizer.d.ts.map +1 -0
- package/lib/typescript/ai-components/tool-calls/index.d.ts +5 -0
- package/lib/typescript/ai-components/tool-calls/index.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/glowShader.d.ts.map +1 -1
- package/lib/typescript/ai-components/voice-visualization/simplex.d.ts +1 -1
- package/lib/typescript/components/Accessibility/NotificationAnnouncer.d.ts +5 -5
- package/lib/typescript/components/Accessibility/hooks/useIncomingMessageAnnouncements.d.ts +1 -1
- package/lib/typescript/components/Accessibility/useAccessibilityAnnouncer.d.ts +0 -2
- package/lib/typescript/components/Accessibility/useAccessibilityAnnouncer.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageItemView/utils/renderText.d.ts.map +1 -1
- package/lib/typescript/contexts/translationContext/TranslationContext.d.ts +1 -1
- package/lib/typescript/contexts/translationContext/TranslationContext.d.ts.map +1 -1
- package/lib/typescript/i18n/ar.json +40 -1
- package/lib/typescript/i18n/en.json +40 -1
- package/lib/typescript/i18n/es.json +40 -1
- package/lib/typescript/i18n/fr.json +40 -1
- package/lib/typescript/i18n/he.json +40 -1
- package/lib/typescript/i18n/hi.json +40 -1
- package/lib/typescript/i18n/it.json +40 -1
- package/lib/typescript/i18n/ja.json +40 -1
- package/lib/typescript/i18n/ko.json +40 -1
- package/lib/typescript/i18n/nl.json +40 -1
- package/lib/typescript/i18n/pt-br.json +40 -1
- package/lib/typescript/i18n/ru.json +40 -1
- package/lib/typescript/i18n/tr.json +40 -1
- package/lib/typescript/utils/i18n/Streami18n.d.ts +41 -2
- package/lib/typescript/utils/i18n/Streami18n.d.ts.map +1 -1
- package/lib/typescript/utils/i18n/utils.d.ts +3 -0
- package/lib/typescript/utils/i18n/utils.d.ts.map +1 -0
- package/package.json +10 -2
- package/src/a11y/a11yUtils.ts +0 -1
- package/src/a11y/hooks/useResolvedModalAccessibilityProps.ts +2 -3
- package/src/a2ui/A2UIContexts.ts +15 -0
- package/src/a2ui/A2UIInstructions.ts +102 -0
- package/src/a2ui/A2UIPlaceholder.tsx +24 -0
- package/src/a2ui/A2UIRenderer.tsx +504 -0
- package/src/a2ui/A2UIStream.ts +105 -0
- package/src/a2ui/A2UISurface.tsx +270 -0
- package/src/a2ui/__tests__/A2UIInstructions.test.ts +233 -0
- package/src/a2ui/__tests__/A2UIRenderer.test.tsx +1559 -0
- package/src/a2ui/__tests__/conformance.test.ts +579 -0
- package/src/a2ui/basicCatalog.json +1206 -0
- package/src/a2ui/basicFunctions.ts +429 -0
- package/src/a2ui/catalogDocument.ts +119 -0
- package/src/a2ui/catalogs.ts +185 -0
- package/src/a2ui/components/DateTimeInput.tsx +381 -0
- package/src/a2ui/components/Media.tsx +278 -0
- package/src/a2ui/components/basicComponents.tsx +832 -0
- package/src/a2ui/components/iconPaths.ts +116 -0
- package/src/a2ui/components/types.ts +24 -0
- package/src/a2ui/index.ts +18 -0
- package/src/a2ui/instructions.ts +341 -0
- package/src/a2ui/secrets.ts +154 -0
- package/src/a2ui/useReplyState.ts +26 -0
- package/src/ai-components/AITypingIndicatorView.tsx +6 -22
- package/src/ai-components/README.md +64 -3
- package/src/ai-components/StreamingMessageView.tsx +27 -3
- package/src/ai-components/__tests__/ComposerView.test.tsx +247 -2
- package/src/ai-components/__tests__/Markdown.test.tsx +13 -0
- package/src/ai-components/__tests__/StreamingMessageView.test.tsx +69 -0
- package/src/ai-components/__tests__/charts.test.ts +75 -0
- package/src/ai-components/__tests__/clientToolRunner.test.ts +239 -0
- package/src/ai-components/__tests__/conversation.test.tsx +787 -0
- package/src/ai-components/__tests__/conversationController.test.ts +1561 -0
- package/src/ai-components/__tests__/conversationExchange.test.ts +187 -0
- package/src/ai-components/__tests__/conversationRows.test.ts +207 -0
- package/src/ai-components/__tests__/conversationUI.test.tsx +606 -0
- package/src/ai-components/__tests__/format.test.ts +32 -0
- package/src/ai-components/__tests__/generativeUI.test.ts +634 -0
- package/src/ai-components/__tests__/keyboard.test.ts +14 -0
- package/src/ai-components/__tests__/messagePartViews.test.tsx +385 -0
- package/src/ai-components/__tests__/messageParts.test.ts +336 -0
- package/src/ai-components/__tests__/messageViews.test.tsx +321 -0
- package/src/ai-components/__tests__/signIn.test.tsx +324 -0
- package/src/ai-components/__tests__/theme.test.tsx +18 -0
- package/src/ai-components/__tests__/translation.test.tsx +85 -0
- package/src/ai-components/__tests__/voiceDriver.test.ts +0 -3
- package/src/ai-components/__tests__/voicePalette.test.ts +1 -1
- package/src/ai-components/__tests__/withoutKeyboardController.test.tsx +60 -0
- package/src/ai-components/charts/components/PieLegend.tsx +2 -1
- package/src/ai-components/charts/components/VictoryChart.tsx +10 -2
- package/src/ai-components/charts/utils/format-time-label.ts +18 -0
- package/src/ai-components/charts/vega-lite/schema.ts +19 -2
- package/src/ai-components/contexts/composer-context.tsx +13 -5
- package/src/ai-components/contexts/index.ts +1 -0
- package/src/ai-components/contexts/theme-context.tsx +19 -4
- package/src/ai-components/contexts/translation-context.tsx +44 -0
- package/src/ai-components/conversation/AIConversation.tsx +612 -0
- package/src/ai-components/conversation/entries.ts +177 -0
- package/src/ai-components/conversation/index.ts +2 -0
- package/src/ai-components/conversation/list/AIChatList.tsx +227 -0
- package/src/ai-components/conversation/list/useChatListController.ts +91 -0
- package/src/ai-components/conversation/list/useEndInView.ts +61 -0
- package/src/ai-components/conversation/list/useKeyboardOverlap.ts +52 -0
- package/src/ai-components/conversation/list/useLoadOlder.ts +97 -0
- package/src/ai-components/conversation/list/usePinnedAnchor.ts +113 -0
- package/src/ai-components/conversation/useConversationRows.ts +231 -0
- package/src/ai-components/conversation-state/controller/AIAgentError.ts +14 -0
- package/src/ai-components/conversation-state/controller/AIConversationController.ts +628 -0
- package/src/ai-components/conversation-state/controller/AIConversationExchange.ts +132 -0
- package/src/ai-components/conversation-state/controller/index.ts +4 -0
- package/src/ai-components/conversation-state/controller/offers.ts +53 -0
- package/src/ai-components/conversation-state/controller/types.ts +154 -0
- package/src/ai-components/conversation-state/controller/useAIConversationState.ts +21 -0
- package/src/ai-components/conversation-state/fromStreamMessage.ts +89 -0
- package/src/ai-components/conversation-state/index.ts +3 -0
- package/src/ai-components/conversation-state/types.ts +57 -0
- package/src/ai-components/generative-ui/AIUIAction.ts +73 -0
- package/src/ai-components/generative-ui/AIUIArguments.ts +117 -0
- package/src/ai-components/generative-ui/AIUIComponent.tsx +106 -0
- package/src/ai-components/generative-ui/AIUIComponentDefinition.ts +163 -0
- package/src/ai-components/generative-ui/AIUIDocument.ts +77 -0
- package/src/ai-components/generative-ui/AIUIEntrance.tsx +229 -0
- package/src/ai-components/generative-ui/AIUIErrorBoundary.tsx +39 -0
- package/src/ai-components/generative-ui/AIUIHelpers.ts +86 -0
- package/src/ai-components/generative-ui/AIUIHost.ts +358 -0
- package/src/ai-components/generative-ui/AIUIMedia.ts +45 -0
- package/src/ai-components/generative-ui/AIUIRenderer.ts +60 -0
- package/src/ai-components/generative-ui/AIUIView.tsx +87 -0
- package/src/ai-components/generative-ui/index.ts +48 -0
- package/src/ai-components/icons.ts +101 -0
- package/src/ai-components/index.ts +10 -0
- package/src/ai-components/internal/Shimmer.tsx +40 -0
- package/src/ai-components/internal/format.ts +69 -0
- package/src/ai-components/internal/hooks/useBottomSafeArea.ts +6 -0
- package/src/ai-components/internal/icons/AgentIcons.tsx +138 -0
- package/src/ai-components/internal/keyboard.tsx +34 -0
- package/src/ai-components/internal/tokens.ts +4 -0
- package/src/ai-components/internal/uuid.ts +17 -0
- package/src/ai-components/markdown/components/CodeBlock.tsx +13 -2
- package/src/ai-components/markdown/components/InlineCode.tsx +1 -0
- package/src/ai-components/markdown/components/Text.tsx +7 -2
- package/src/ai-components/markdown/styles.ts +29 -27
- package/src/ai-components/markdown/types.ts +1 -0
- package/src/ai-components/message/AIAssistantMessage.tsx +100 -0
- package/src/ai-components/message/AIMessageAttachment.tsx +245 -0
- package/src/ai-components/message/AIReplyActions.tsx +139 -0
- package/src/ai-components/message/AIUserMessage.tsx +121 -0
- package/src/ai-components/message/UserMessageMenu.tsx +149 -0
- package/src/ai-components/message/index.ts +4 -0
- package/src/ai-components/message-composer/ActionButton.tsx +10 -5
- package/src/ai-components/message-composer/ActionSheet.tsx +28 -6
- package/src/ai-components/message-composer/AttachmentButton.tsx +9 -1
- package/src/ai-components/message-composer/ChatOptions.tsx +144 -0
- package/src/ai-components/message-composer/ComposerView.tsx +194 -30
- package/src/ai-components/message-composer/EditingMessageChip.tsx +62 -0
- package/src/ai-components/message-composer/MediaPreviewList.tsx +34 -8
- package/src/ai-components/message-composer/index.ts +1 -0
- package/src/ai-components/message-parts/AIMessageParts.tsx +145 -0
- package/src/ai-components/message-parts/AIToolApproval.tsx +196 -0
- package/src/ai-components/message-parts/AIToolAuthorization.tsx +279 -0
- package/src/ai-components/message-parts/AIToolCall.tsx +117 -0
- package/src/ai-components/message-parts/callStates.ts +44 -0
- package/src/ai-components/message-parts/index.ts +6 -0
- package/src/ai-components/message-parts/parseMessageParts.ts +195 -0
- package/src/ai-components/message-parts/types.ts +157 -0
- package/src/ai-components/native-specs/NativeAIAgent.ts +25 -0
- package/src/ai-components/reasoning/AIReasoning.tsx +509 -0
- package/src/ai-components/reasoning/index.ts +2 -0
- package/src/ai-components/reasoning/thinkingClock.ts +109 -0
- package/src/ai-components/services/clipboard-service/expo.ts +18 -4
- package/src/ai-components/store/composer/store.ts +9 -0
- package/src/ai-components/store/composer/useComposerChatOption.ts +15 -0
- package/src/ai-components/store/composer/useComposerIsEditing.ts +15 -0
- package/src/ai-components/store/index.ts +2 -0
- package/src/ai-components/suggestions/AISuggestions.tsx +73 -0
- package/src/ai-components/suggestions/index.ts +1 -0
- package/src/ai-components/syntax-highlighting/utils/flatten-nodes.ts +2 -1
- package/src/ai-components/syntax-highlighting/utils/renderer.tsx +4 -2
- package/src/ai-components/theme.ts +334 -0
- package/src/ai-components/tool-calls/AIClientIdentity.ts +33 -0
- package/src/ai-components/tool-calls/AIClientToolRunner.ts +200 -0
- package/src/ai-components/tool-calls/AIToolApprover.tsx +60 -0
- package/src/ai-components/tool-calls/authSessionAuthorizer.ts +137 -0
- package/src/ai-components/tool-calls/index.ts +4 -0
- package/src/ai-components/voice-visualization/glowShader.ts +1 -3
- package/src/ai-components/voice-visualization/simplex.ts +1 -1
- package/src/components/Accessibility/NotificationAnnouncer.tsx +5 -5
- package/src/components/Accessibility/hooks/useIncomingMessageAnnouncements.ts +1 -1
- package/src/components/Accessibility/useAccessibilityAnnouncer.ts +0 -2
- package/src/components/Message/MessageItemView/utils/renderText.tsx +1 -2
- package/src/contexts/translationContext/TranslationContext.tsx +2 -2
- package/src/i18n/ar.json +40 -1
- package/src/i18n/en.json +40 -1
- package/src/i18n/es.json +40 -1
- package/src/i18n/fr.json +40 -1
- package/src/i18n/he.json +40 -1
- package/src/i18n/hi.json +40 -1
- package/src/i18n/it.json +40 -1
- package/src/i18n/ja.json +40 -1
- package/src/i18n/ko.json +40 -1
- package/src/i18n/nl.json +40 -1
- package/src/i18n/pt-br.json +40 -1
- package/src/i18n/ru.json +40 -1
- package/src/i18n/tr.json +40 -1
- package/src/mock-builders/a2ui/ohrid.ts +106 -0
- package/src/utils/i18n/Streami18n.ts +4 -1
- package/src/utils/i18n/utils.ts +3 -0
- package/src/version.json +1 -1
|
@@ -0,0 +1,1561 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AIAgentActivityStates,
|
|
3
|
+
AIAgentError,
|
|
4
|
+
AIConversationController,
|
|
5
|
+
type AIConversationControllerOptions,
|
|
6
|
+
type AIConversationFeed,
|
|
7
|
+
type AIConversationRequest,
|
|
8
|
+
type AIConversationTransport,
|
|
9
|
+
type AIMessage,
|
|
10
|
+
type AIMessageRating,
|
|
11
|
+
createNewComposerStore,
|
|
12
|
+
isAIConversationAnswering,
|
|
13
|
+
} from '..';
|
|
14
|
+
import type { MediaAsset } from '../services/media-picker-service/AbstractMediaPickerService';
|
|
15
|
+
|
|
16
|
+
const user = (id: string, text = 'Plan a weekend in Lisbon'): AIMessage => ({
|
|
17
|
+
id,
|
|
18
|
+
role: 'user',
|
|
19
|
+
text,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const reply = (id: string, text = 'Here’s a plan', generating = false): AIMessage => ({
|
|
23
|
+
id,
|
|
24
|
+
isGenerating: generating,
|
|
25
|
+
role: 'assistant',
|
|
26
|
+
text,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const photo: MediaAsset = { name: 'alfama.jpg', type: 'image/jpeg', uri: 'file:///tmp/alfama.jpg' };
|
|
30
|
+
const plan: MediaAsset = {
|
|
31
|
+
name: 'itinerary.pdf',
|
|
32
|
+
type: 'application/pdf',
|
|
33
|
+
uri: 'file:///tmp/itinerary.pdf',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
class TransportError extends Error {}
|
|
37
|
+
|
|
38
|
+
const abortError = () => Object.assign(new Error('Aborted'), { name: 'AbortError' });
|
|
39
|
+
|
|
40
|
+
type Held = { reject: (error: unknown) => void; resolve: () => void };
|
|
41
|
+
|
|
42
|
+
const hold = (list: Held[]) =>
|
|
43
|
+
new Promise<void>((resolve, reject) => {
|
|
44
|
+
list.push({ reject, resolve });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Holds each send, stop, load and rating until the test finishes it.
|
|
48
|
+
class ConversationTransport implements AIConversationTransport {
|
|
49
|
+
feed: AIConversationFeed | undefined;
|
|
50
|
+
requests: AIConversationRequest[] = [];
|
|
51
|
+
stops = 0;
|
|
52
|
+
loads = 0;
|
|
53
|
+
ratings: { id: string; rating: AIMessageRating | null }[] = [];
|
|
54
|
+
sendError: unknown;
|
|
55
|
+
stopError: unknown;
|
|
56
|
+
loadError: unknown;
|
|
57
|
+
failsRating: (rating: AIMessageRating | null) => boolean = () => false;
|
|
58
|
+
holdsSends = false;
|
|
59
|
+
holdsStops = false;
|
|
60
|
+
holdsLoads = false;
|
|
61
|
+
holdsRatings = false;
|
|
62
|
+
disconnects = 0;
|
|
63
|
+
private heldSends: Held[] = [];
|
|
64
|
+
private heldStops: Held[] = [];
|
|
65
|
+
private heldLoads: Held[] = [];
|
|
66
|
+
private heldRatings: Held[] = [];
|
|
67
|
+
|
|
68
|
+
constructor(private initialMessages: AIMessage[] = []) {}
|
|
69
|
+
|
|
70
|
+
get lastRequest() {
|
|
71
|
+
return this.requests[this.requests.length - 1];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
connect = (feed: AIConversationFeed) => {
|
|
75
|
+
this.feed = feed;
|
|
76
|
+
feed.updateMessages(this.initialMessages);
|
|
77
|
+
return () => {
|
|
78
|
+
this.disconnects += 1;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
send = async (request: AIConversationRequest) => {
|
|
83
|
+
this.requests.push(request);
|
|
84
|
+
if (this.holdsSends) await hold(this.heldSends);
|
|
85
|
+
if (this.sendError) throw this.sendError;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
stop = async () => {
|
|
89
|
+
this.stops += 1;
|
|
90
|
+
if (this.holdsStops) await hold(this.heldStops);
|
|
91
|
+
if (this.stopError) throw this.stopError;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
loadOlderMessages = async () => {
|
|
95
|
+
this.loads += 1;
|
|
96
|
+
if (this.holdsLoads) await hold(this.heldLoads);
|
|
97
|
+
if (this.loadError) throw this.loadError;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
rate = async (message: AIMessage, rating: AIMessageRating | null) => {
|
|
101
|
+
this.ratings.push({ id: message.id, rating });
|
|
102
|
+
if (this.holdsRatings) await hold(this.heldRatings);
|
|
103
|
+
if (this.failsRating(rating)) throw new TransportError();
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
finishSend(index: number, error?: unknown) {
|
|
107
|
+
if (error) this.heldSends[index].reject(error);
|
|
108
|
+
else this.heldSends[index].resolve();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
finishStop(index: number) {
|
|
112
|
+
this.heldStops[index].resolve();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
finishRating(index: number, error?: unknown) {
|
|
116
|
+
if (error) this.heldRatings[index].reject(error);
|
|
117
|
+
else this.heldRatings[index].resolve();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
finishLoads() {
|
|
121
|
+
this.heldLoads.forEach(({ resolve }) => resolve());
|
|
122
|
+
this.heldLoads = [];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
update(messages: AIMessage[]) {
|
|
126
|
+
this.feed?.updateMessages(messages);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
updateActivity(activity: Parameters<AIConversationFeed['updateActivity']>[0]) {
|
|
130
|
+
this.feed?.updateActivity(activity);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const controllers: AIConversationController[] = [];
|
|
135
|
+
|
|
136
|
+
const makeController = (
|
|
137
|
+
transport: AIConversationTransport,
|
|
138
|
+
options: Omit<AIConversationControllerOptions, 'transport'> = {},
|
|
139
|
+
) => {
|
|
140
|
+
const controller = new AIConversationController({ transport, ...options });
|
|
141
|
+
controllers.push(controller);
|
|
142
|
+
return controller;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
afterEach(() => {
|
|
146
|
+
controllers.splice(0).forEach((controller) => controller.dispose());
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const settle = async () => {
|
|
150
|
+
for (let index = 0; index < 20; index++) await Promise.resolve();
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
154
|
+
|
|
155
|
+
const waitUntil = async (condition: () => boolean, timeout = 2000) => {
|
|
156
|
+
const deadline = Date.now() + timeout;
|
|
157
|
+
while (!condition()) {
|
|
158
|
+
if (Date.now() > deadline) throw new Error('The condition wasn’t met in time');
|
|
159
|
+
await sleep(10);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const ids = (messages: readonly AIMessage[]) => messages.map(({ id }) => id);
|
|
164
|
+
|
|
165
|
+
const stateOf = (controller: AIConversationController) => controller.state.getLatestValue();
|
|
166
|
+
|
|
167
|
+
describe('AIConversationController following the conversation', () => {
|
|
168
|
+
it('connects to the transport and shows its messages', () => {
|
|
169
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
170
|
+
const controller = makeController(transport);
|
|
171
|
+
|
|
172
|
+
expect(transport.feed).toBeDefined();
|
|
173
|
+
const state = stateOf(controller);
|
|
174
|
+
expect(ids(state.messages)).toEqual(['u1', 'a1']);
|
|
175
|
+
expect(state.status).toBe('ready');
|
|
176
|
+
expect(state.pendingMessage).toBeUndefined();
|
|
177
|
+
expect(state.error).toBeUndefined();
|
|
178
|
+
expect(state.hasOlderMessages).toBe(false);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('keeps the composer and approver it is given', () => {
|
|
182
|
+
const composer = createNewComposerStore();
|
|
183
|
+
const approver = { clientId: 'ios-1', decide: async () => {}, userId: 'u_1' };
|
|
184
|
+
const controller = makeController(new ConversationTransport(), { approver, composer });
|
|
185
|
+
|
|
186
|
+
expect(controller.composer).toBe(composer);
|
|
187
|
+
expect(controller.approver?.userId).toBe('u_1');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('updates the messages and whether older ones can load', () => {
|
|
191
|
+
const transport = new ConversationTransport();
|
|
192
|
+
const controller = makeController(transport);
|
|
193
|
+
|
|
194
|
+
transport.update([user('u1'), reply('a1')]);
|
|
195
|
+
transport.feed?.updateHasOlderMessages(true);
|
|
196
|
+
|
|
197
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1']);
|
|
198
|
+
expect(stateOf(controller).hasOlderMessages).toBe(true);
|
|
199
|
+
|
|
200
|
+
transport.feed?.updateHasOlderMessages(false);
|
|
201
|
+
|
|
202
|
+
expect(stateOf(controller).hasOlderMessages).toBe(false);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('lets go of the transport once disposed, and its updates go nowhere', () => {
|
|
206
|
+
const transport = new ConversationTransport();
|
|
207
|
+
const controller = makeController(transport);
|
|
208
|
+
|
|
209
|
+
expect(transport.feed?.isConnected).toBe(true);
|
|
210
|
+
controller.dispose();
|
|
211
|
+
|
|
212
|
+
expect(transport.disconnects).toBe(1);
|
|
213
|
+
expect(transport.feed?.isConnected).toBe(false);
|
|
214
|
+
transport.update([user('u1')]);
|
|
215
|
+
transport.updateActivity({ state: AIAgentActivityStates.thinking });
|
|
216
|
+
transport.feed?.updateHasOlderMessages(true);
|
|
217
|
+
expect(stateOf(controller).messages).toEqual([]);
|
|
218
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
219
|
+
|
|
220
|
+
controller.dispose();
|
|
221
|
+
expect(transport.disconnects).toBe(1);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('keeps its state when nothing it shows changed', () => {
|
|
225
|
+
const messages = [user('u1'), reply('a1')];
|
|
226
|
+
const transport = new ConversationTransport(messages);
|
|
227
|
+
const controller = makeController(transport);
|
|
228
|
+
const before = stateOf(controller);
|
|
229
|
+
|
|
230
|
+
transport.update([...messages]);
|
|
231
|
+
|
|
232
|
+
expect(stateOf(controller)).toBe(before);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe('AIConversationController sending', () => {
|
|
237
|
+
it('shows the message at once and asks the transport', async () => {
|
|
238
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
239
|
+
const controller = makeController(transport);
|
|
240
|
+
const option = { id: 'think_harder', title: 'Think harder' };
|
|
241
|
+
|
|
242
|
+
controller.send({
|
|
243
|
+
attachments: [photo, plan],
|
|
244
|
+
chatOption: option,
|
|
245
|
+
text: 'And the restaurants?',
|
|
246
|
+
});
|
|
247
|
+
await settle();
|
|
248
|
+
|
|
249
|
+
const state = stateOf(controller);
|
|
250
|
+
expect(state.status).toBe('submitted');
|
|
251
|
+
expect(state.pendingMessage?.text).toBe('And the restaurants?');
|
|
252
|
+
expect(state.pendingMessage?.attachments?.map(({ kind }) => kind)).toEqual(['image', 'file']);
|
|
253
|
+
expect(state.pendingMessage?.attachments?.map(({ url }) => url)).toEqual([photo.uri, plan.uri]);
|
|
254
|
+
const request = transport.lastRequest;
|
|
255
|
+
expect(transport.requests).toHaveLength(1);
|
|
256
|
+
expect(request?.kind).toBe('message');
|
|
257
|
+
expect(request?.text).toBe('And the restaurants?');
|
|
258
|
+
expect(request?.attachments).toEqual([photo, plan]);
|
|
259
|
+
expect(request?.chatOption).toEqual(option);
|
|
260
|
+
expect(request?.message).toBeUndefined();
|
|
261
|
+
// The transport can give the message the id its copy is found by.
|
|
262
|
+
expect(request?.messageId).toBe(state.pendingMessage?.id);
|
|
263
|
+
expect(ids(state.messages)).toEqual(['u1', 'a1']);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('ignores a message with nothing in it', async () => {
|
|
267
|
+
const transport = new ConversationTransport();
|
|
268
|
+
const controller = makeController(transport);
|
|
269
|
+
|
|
270
|
+
controller.send({ text: ' \n ' });
|
|
271
|
+
await settle();
|
|
272
|
+
|
|
273
|
+
expect(transport.requests).toEqual([]);
|
|
274
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
275
|
+
expect(stateOf(controller).pendingMessage).toBeUndefined();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('sends only attachments', async () => {
|
|
279
|
+
const transport = new ConversationTransport();
|
|
280
|
+
const controller = makeController(transport);
|
|
281
|
+
|
|
282
|
+
controller.send({ attachments: [photo], text: '' });
|
|
283
|
+
await settle();
|
|
284
|
+
|
|
285
|
+
expect(transport.lastRequest?.attachments).toEqual([photo]);
|
|
286
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('waits until the reply starts', () => {
|
|
290
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
291
|
+
const controller = makeController(transport);
|
|
292
|
+
|
|
293
|
+
controller.send({ text: 'And the restaurants?' });
|
|
294
|
+
transport.update([user('u1'), reply('a1'), user('u2', 'And the restaurants?')]);
|
|
295
|
+
|
|
296
|
+
// Kept until its reply starts, so the conversation can match its copy.
|
|
297
|
+
expect(stateOf(controller).pendingMessage).toBeDefined();
|
|
298
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
299
|
+
|
|
300
|
+
transport.update([user('u1'), reply('a1'), user('u2'), reply('a2', '', true)]);
|
|
301
|
+
|
|
302
|
+
// A reply that shows nothing hasn't started.
|
|
303
|
+
expect(stateOf(controller).pendingMessage).toBeDefined();
|
|
304
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
305
|
+
|
|
306
|
+
transport.update([user('u1'), reply('a1'), user('u2'), reply('a2', 'Try', true)]);
|
|
307
|
+
|
|
308
|
+
expect(stateOf(controller).pendingMessage).toBeUndefined();
|
|
309
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
310
|
+
|
|
311
|
+
transport.update([user('u1'), reply('a1'), user('u2'), reply('a2', 'Try Taberna da Rua')]);
|
|
312
|
+
|
|
313
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
314
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1', 'u2', 'a2']);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('ends when the reply arrives finished', () => {
|
|
318
|
+
const transport = new ConversationTransport();
|
|
319
|
+
const controller = makeController(transport);
|
|
320
|
+
|
|
321
|
+
controller.send({ text: 'Hi' });
|
|
322
|
+
transport.update([user('u1', 'Hi'), reply('a1', 'Hello!')]);
|
|
323
|
+
|
|
324
|
+
expect(stateOf(controller).pendingMessage).toBeUndefined();
|
|
325
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('ends when a reply finishes with nothing in it', () => {
|
|
329
|
+
const transport = new ConversationTransport();
|
|
330
|
+
const controller = makeController(transport);
|
|
331
|
+
|
|
332
|
+
controller.send({ text: 'Hi' });
|
|
333
|
+
transport.update([user('u1', 'Hi'), reply('a1', '')]);
|
|
334
|
+
|
|
335
|
+
expect(stateOf(controller).pendingMessage).toBeUndefined();
|
|
336
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('waits for its own reply while another is written', () => {
|
|
340
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', 'Day one', true)]);
|
|
341
|
+
const controller = makeController(transport);
|
|
342
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
343
|
+
|
|
344
|
+
controller.send({ text: 'Make it three days' });
|
|
345
|
+
transport.update([user('u1'), reply('a1', 'Day one, day two', true)]);
|
|
346
|
+
|
|
347
|
+
expect(stateOf(controller).pendingMessage).toBeDefined();
|
|
348
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
349
|
+
|
|
350
|
+
transport.update([
|
|
351
|
+
user('u1'),
|
|
352
|
+
reply('a1', 'Day one, day two'),
|
|
353
|
+
user('u2'),
|
|
354
|
+
reply('a2', 'Three days', true),
|
|
355
|
+
]);
|
|
356
|
+
|
|
357
|
+
expect(stateOf(controller).pendingMessage).toBeUndefined();
|
|
358
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('shows the error when the transport fails, and retry sends it again', async () => {
|
|
362
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
363
|
+
transport.sendError = new TransportError();
|
|
364
|
+
const controller = makeController(transport);
|
|
365
|
+
|
|
366
|
+
controller.send({ attachments: [photo], text: 'And the restaurants?' });
|
|
367
|
+
await settle();
|
|
368
|
+
|
|
369
|
+
expect(stateOf(controller).status).toBe('error');
|
|
370
|
+
expect(stateOf(controller).error).toBeInstanceOf(TransportError);
|
|
371
|
+
// The message stays, to try again.
|
|
372
|
+
expect(stateOf(controller).pendingMessage?.text).toBe('And the restaurants?');
|
|
373
|
+
const failed = transport.lastRequest;
|
|
374
|
+
|
|
375
|
+
transport.sendError = undefined;
|
|
376
|
+
controller.retry();
|
|
377
|
+
await settle();
|
|
378
|
+
|
|
379
|
+
expect(transport.requests).toHaveLength(2);
|
|
380
|
+
expect(transport.lastRequest?.kind).toBe('message');
|
|
381
|
+
expect(transport.lastRequest?.text).toBe('And the restaurants?');
|
|
382
|
+
expect(transport.lastRequest?.attachments).toEqual([photo]);
|
|
383
|
+
// The transport can tell whether the message arrived the first time.
|
|
384
|
+
expect(transport.lastRequest?.messageId).toBe(failed?.messageId);
|
|
385
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
386
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
387
|
+
expect(stateOf(controller).pendingMessage?.text).toBe('And the restaurants?');
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('shows no error when the send is aborted', async () => {
|
|
391
|
+
const transport = new ConversationTransport();
|
|
392
|
+
transport.sendError = abortError();
|
|
393
|
+
const controller = makeController(transport);
|
|
394
|
+
|
|
395
|
+
controller.send({ text: 'Hi' });
|
|
396
|
+
await settle();
|
|
397
|
+
|
|
398
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
399
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
400
|
+
expect(stateOf(controller).pendingMessage).toBeUndefined();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('wraps a rejection that isn’t an Error', async () => {
|
|
404
|
+
const transport = new ConversationTransport();
|
|
405
|
+
transport.sendError = 'offline';
|
|
406
|
+
const controller = makeController(transport);
|
|
407
|
+
|
|
408
|
+
controller.send({ text: 'Hi' });
|
|
409
|
+
await settle();
|
|
410
|
+
|
|
411
|
+
expect(stateOf(controller).error?.message).toBe('offline');
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('ignores the failure of a request that was overtaken', async () => {
|
|
415
|
+
const transport = new ConversationTransport();
|
|
416
|
+
transport.holdsSends = true;
|
|
417
|
+
const controller = makeController(transport);
|
|
418
|
+
|
|
419
|
+
controller.send({ text: 'First' });
|
|
420
|
+
controller.send({ text: 'Second' });
|
|
421
|
+
await settle();
|
|
422
|
+
transport.finishSend(0, new TransportError());
|
|
423
|
+
await settle();
|
|
424
|
+
|
|
425
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
426
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
427
|
+
expect(stateOf(controller).pendingMessage?.text).toBe('Second');
|
|
428
|
+
|
|
429
|
+
transport.finishSend(1);
|
|
430
|
+
await settle();
|
|
431
|
+
|
|
432
|
+
// A request that reaches the agent waits for its reply.
|
|
433
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('streams a reply that started before the transport returned', async () => {
|
|
437
|
+
const transport = new ConversationTransport();
|
|
438
|
+
transport.holdsSends = true;
|
|
439
|
+
const controller = makeController(transport);
|
|
440
|
+
|
|
441
|
+
controller.send({ text: 'Hi' });
|
|
442
|
+
await settle();
|
|
443
|
+
transport.update([user('u1', 'Hi'), reply('a1', 'Hello', true)]);
|
|
444
|
+
transport.finishSend(0);
|
|
445
|
+
await settle();
|
|
446
|
+
|
|
447
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
448
|
+
expect(stateOf(controller).pendingMessage).toBeUndefined();
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('ignores the failure of a request whose reply arrived first', async () => {
|
|
452
|
+
const transport = new ConversationTransport();
|
|
453
|
+
transport.holdsSends = true;
|
|
454
|
+
const controller = makeController(transport);
|
|
455
|
+
|
|
456
|
+
controller.send({ text: 'Hi' });
|
|
457
|
+
await settle();
|
|
458
|
+
transport.update([user('u1', 'Hi'), reply('a1', 'Hello')]);
|
|
459
|
+
transport.finishSend(0, new TransportError());
|
|
460
|
+
await settle();
|
|
461
|
+
|
|
462
|
+
// The agent had it.
|
|
463
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
464
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('shows a failure while a reply is written once that reply is finished', async () => {
|
|
468
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', 'Day one', true)]);
|
|
469
|
+
transport.sendError = new TransportError();
|
|
470
|
+
const controller = makeController(transport);
|
|
471
|
+
|
|
472
|
+
controller.send({ text: 'Make it three days' });
|
|
473
|
+
await settle();
|
|
474
|
+
|
|
475
|
+
expect(stateOf(controller).error).toBeInstanceOf(TransportError);
|
|
476
|
+
// The agent is still answering.
|
|
477
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
478
|
+
|
|
479
|
+
transport.update([user('u1'), reply('a1', 'Day one, day two')]);
|
|
480
|
+
|
|
481
|
+
expect(stateOf(controller).status).toBe('error');
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('forgets the error when the failed request is answered after all', async () => {
|
|
485
|
+
const transport = new ConversationTransport();
|
|
486
|
+
transport.sendError = new TransportError();
|
|
487
|
+
const controller = makeController(transport);
|
|
488
|
+
controller.send({ text: 'Hi' });
|
|
489
|
+
await settle();
|
|
490
|
+
|
|
491
|
+
transport.update([user('u1', 'Hi'), reply('a1', 'Hello', true)]);
|
|
492
|
+
|
|
493
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
494
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('shows the agent’s error when the reply doesn’t start in time', async () => {
|
|
498
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
499
|
+
const controller = makeController(transport, { replyTimeout: 50 });
|
|
500
|
+
|
|
501
|
+
controller.send({ text: 'Hi' });
|
|
502
|
+
await waitUntil(() => stateOf(controller).status === 'error');
|
|
503
|
+
|
|
504
|
+
const { error } = stateOf(controller);
|
|
505
|
+
expect(error).toBeInstanceOf(AIAgentError);
|
|
506
|
+
expect((error as AIAgentError).agentMessage).toBeUndefined();
|
|
507
|
+
expect(stateOf(controller).pendingMessage?.text).toBe('Hi');
|
|
508
|
+
|
|
509
|
+
transport.update([user('u1'), reply('a1'), user('u2', 'Hi'), reply('a2', 'Hello', true)]);
|
|
510
|
+
|
|
511
|
+
// A late reply.
|
|
512
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
513
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it('waits for the reply while the agent says it is working', async () => {
|
|
517
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
518
|
+
const controller = makeController(transport, { replyTimeout: 100 });
|
|
519
|
+
controller.send({ text: 'Hi' });
|
|
520
|
+
await settle();
|
|
521
|
+
|
|
522
|
+
transport.updateActivity({ state: AIAgentActivityStates.thinking });
|
|
523
|
+
await sleep(300);
|
|
524
|
+
|
|
525
|
+
// However long it thinks.
|
|
526
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
527
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
528
|
+
|
|
529
|
+
transport.updateActivity(undefined);
|
|
530
|
+
await waitUntil(() => stateOf(controller).status === 'error');
|
|
531
|
+
|
|
532
|
+
// It stopped saying it was working, without answering.
|
|
533
|
+
expect(stateOf(controller).error).toBeInstanceOf(AIAgentError);
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
it('waits for the reply when the agent says it is working before the transport returns', async () => {
|
|
537
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
538
|
+
transport.holdsSends = true;
|
|
539
|
+
const controller = makeController(transport, { replyTimeout: 100 });
|
|
540
|
+
controller.send({ text: 'Hi' });
|
|
541
|
+
await settle();
|
|
542
|
+
|
|
543
|
+
transport.updateActivity({ state: AIAgentActivityStates.thinking });
|
|
544
|
+
transport.finishSend(0);
|
|
545
|
+
await settle();
|
|
546
|
+
await sleep(300);
|
|
547
|
+
|
|
548
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it('waits as long as it takes without a timeout', async () => {
|
|
552
|
+
for (const replyTimeout of [null, Infinity, 1e30]) {
|
|
553
|
+
const controller = makeController(new ConversationTransport(), { replyTimeout });
|
|
554
|
+
|
|
555
|
+
controller.send({ text: 'Hi' });
|
|
556
|
+
await sleep(50);
|
|
557
|
+
|
|
558
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it('forgets the previous error', async () => {
|
|
563
|
+
const transport = new ConversationTransport();
|
|
564
|
+
transport.sendError = new TransportError();
|
|
565
|
+
const controller = makeController(transport);
|
|
566
|
+
controller.send({ text: 'Hi' });
|
|
567
|
+
await settle();
|
|
568
|
+
expect(stateOf(controller).error).toBeDefined();
|
|
569
|
+
|
|
570
|
+
transport.sendError = undefined;
|
|
571
|
+
controller.send({ text: 'Hi again' });
|
|
572
|
+
|
|
573
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
574
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
describe('AIConversationController regenerating', () => {
|
|
579
|
+
it('asks for a new reply in place of the old', async () => {
|
|
580
|
+
const transport = new ConversationTransport([
|
|
581
|
+
user('u1', 'Lisbon?'),
|
|
582
|
+
reply('a1'),
|
|
583
|
+
user('u2', 'Porto?'),
|
|
584
|
+
reply('a2'),
|
|
585
|
+
]);
|
|
586
|
+
const controller = makeController(transport);
|
|
587
|
+
|
|
588
|
+
controller.regenerate(reply('a2'));
|
|
589
|
+
await settle();
|
|
590
|
+
|
|
591
|
+
expect(transport.lastRequest?.kind).toBe('regenerate');
|
|
592
|
+
// A transport that can only send messages asks again.
|
|
593
|
+
expect(transport.lastRequest?.text).toBe('Porto?');
|
|
594
|
+
expect(transport.lastRequest?.message?.id).toBe('a2');
|
|
595
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1', 'u2']);
|
|
596
|
+
expect(stateOf(controller).pendingMessage).toBeUndefined();
|
|
597
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it('sends the reply as the conversation has it', async () => {
|
|
601
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', 'The latest text')]);
|
|
602
|
+
const controller = makeController(transport);
|
|
603
|
+
|
|
604
|
+
controller.regenerate(reply('a1', 'What a view once showed'));
|
|
605
|
+
await settle();
|
|
606
|
+
|
|
607
|
+
expect(transport.lastRequest?.message?.text).toBe('The latest text');
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
it('leaves out the replies after an earlier one', () => {
|
|
611
|
+
const transport = new ConversationTransport([user('u1'), reply('a1'), user('u2'), reply('a2')]);
|
|
612
|
+
const controller = makeController(transport);
|
|
613
|
+
|
|
614
|
+
controller.regenerate(reply('a1'));
|
|
615
|
+
|
|
616
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1']);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it('shows the new reply once it starts', () => {
|
|
620
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
621
|
+
const controller = makeController(transport);
|
|
622
|
+
|
|
623
|
+
controller.regenerate(reply('a1'));
|
|
624
|
+
transport.update([user('u1')]);
|
|
625
|
+
|
|
626
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
627
|
+
|
|
628
|
+
transport.update([user('u1'), reply('a2', 'Another plan', true)]);
|
|
629
|
+
|
|
630
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a2']);
|
|
631
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it('shows a reply rewritten in place once it starts', () => {
|
|
635
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
636
|
+
const controller = makeController(transport);
|
|
637
|
+
|
|
638
|
+
controller.regenerate(reply('a1'));
|
|
639
|
+
transport.update([user('u1'), reply('a1', '', true)]);
|
|
640
|
+
|
|
641
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1']);
|
|
642
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
643
|
+
|
|
644
|
+
transport.update([user('u1'), reply('a1', 'Another', true)]);
|
|
645
|
+
|
|
646
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1']);
|
|
647
|
+
expect(stateOf(controller).messages[1]?.text).toBe('Another');
|
|
648
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it('brings the reply back when the transport fails', async () => {
|
|
652
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
653
|
+
transport.sendError = new TransportError();
|
|
654
|
+
const controller = makeController(transport);
|
|
655
|
+
|
|
656
|
+
controller.regenerate(reply('a1'));
|
|
657
|
+
await settle();
|
|
658
|
+
|
|
659
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1']);
|
|
660
|
+
expect(stateOf(controller).status).toBe('error');
|
|
661
|
+
|
|
662
|
+
transport.sendError = undefined;
|
|
663
|
+
controller.retry();
|
|
664
|
+
await settle();
|
|
665
|
+
|
|
666
|
+
expect(transport.lastRequest?.kind).toBe('regenerate');
|
|
667
|
+
expect(transport.lastRequest?.message?.id).toBe('a1');
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
it('ignores the person’s messages and ones not in the conversation', async () => {
|
|
671
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
672
|
+
const controller = makeController(transport);
|
|
673
|
+
|
|
674
|
+
controller.regenerate(user('u1'));
|
|
675
|
+
controller.regenerate(reply('gone'));
|
|
676
|
+
await settle();
|
|
677
|
+
|
|
678
|
+
expect(transport.requests).toEqual([]);
|
|
679
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it('regenerates a reply with no message before it', async () => {
|
|
683
|
+
const transport = new ConversationTransport([reply('a0', 'Hi! How can I help?')]);
|
|
684
|
+
const controller = makeController(transport);
|
|
685
|
+
|
|
686
|
+
controller.regenerate(reply('a0'));
|
|
687
|
+
await settle();
|
|
688
|
+
|
|
689
|
+
expect(transport.lastRequest?.text).toBe('');
|
|
690
|
+
expect(transport.lastRequest?.message?.id).toBe('a0');
|
|
691
|
+
});
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
describe('AIConversationController editing', () => {
|
|
695
|
+
it('puts the message in the composer', () => {
|
|
696
|
+
const controller = makeController(
|
|
697
|
+
new ConversationTransport([user('u1', 'Plan Lisbon'), reply('a1')]),
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
controller.edit(user('u1', 'Plan Lisbon'));
|
|
701
|
+
|
|
702
|
+
expect(controller.editingMessage?.id).toBe('u1');
|
|
703
|
+
expect(controller.composer.getLatestValue().text).toBe('Plan Lisbon');
|
|
704
|
+
// The composer shows the person is editing.
|
|
705
|
+
expect(controller.composer.getLatestValue().isEditing).toBe(true);
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it('sends a new message once editing is cancelled in the composer', async () => {
|
|
709
|
+
const transport = new ConversationTransport([
|
|
710
|
+
user('u1'),
|
|
711
|
+
reply('a1'),
|
|
712
|
+
user('u2', 'Plan Porto'),
|
|
713
|
+
reply('a2'),
|
|
714
|
+
]);
|
|
715
|
+
const controller = makeController(transport);
|
|
716
|
+
controller.edit(user('u2', 'Plan Porto'));
|
|
717
|
+
|
|
718
|
+
controller.composer.partialNext({ isEditing: false, text: '' });
|
|
719
|
+
controller.send({ text: 'What’s the weather in Tokyo?' });
|
|
720
|
+
await settle();
|
|
721
|
+
|
|
722
|
+
expect(controller.editingMessage).toBeUndefined();
|
|
723
|
+
expect(transport.lastRequest?.kind).toBe('message');
|
|
724
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1', 'u2', 'a2']);
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
it('brings the messages back when the transport fails', async () => {
|
|
728
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
729
|
+
transport.sendError = new TransportError();
|
|
730
|
+
const controller = makeController(transport);
|
|
731
|
+
controller.edit(user('u1'));
|
|
732
|
+
|
|
733
|
+
controller.send({ text: 'Plan Porto' });
|
|
734
|
+
await settle();
|
|
735
|
+
|
|
736
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1']);
|
|
737
|
+
expect(stateOf(controller).pendingMessage).toBeUndefined();
|
|
738
|
+
expect(stateOf(controller).status).toBe('error');
|
|
739
|
+
|
|
740
|
+
transport.sendError = undefined;
|
|
741
|
+
controller.retry();
|
|
742
|
+
await settle();
|
|
743
|
+
|
|
744
|
+
expect(transport.lastRequest?.kind).toBe('edit');
|
|
745
|
+
expect(transport.lastRequest?.text).toBe('Plan Porto');
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
it('ignores replies', () => {
|
|
749
|
+
const controller = makeController(new ConversationTransport([user('u1'), reply('a1')]));
|
|
750
|
+
|
|
751
|
+
controller.edit(reply('a1'));
|
|
752
|
+
|
|
753
|
+
expect(controller.editingMessage).toBeUndefined();
|
|
754
|
+
expect(controller.composer.getLatestValue().text).toBe('');
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
it('replaces the message and what follows', async () => {
|
|
758
|
+
const original: AIMessage = {
|
|
759
|
+
attachments: [{ kind: 'image', url: 'https://example.com/porto.jpg' }],
|
|
760
|
+
id: 'u2',
|
|
761
|
+
role: 'user',
|
|
762
|
+
text: 'Plan Porto',
|
|
763
|
+
};
|
|
764
|
+
const transport = new ConversationTransport([user('u1'), reply('a1'), original, reply('a2')]);
|
|
765
|
+
const controller = makeController(transport);
|
|
766
|
+
|
|
767
|
+
controller.edit(original);
|
|
768
|
+
controller.send({ attachments: [photo], text: 'Plan Porto for two' });
|
|
769
|
+
await settle();
|
|
770
|
+
|
|
771
|
+
expect(controller.editingMessage).toBeUndefined();
|
|
772
|
+
expect(controller.composer.getLatestValue().isEditing).toBe(false);
|
|
773
|
+
expect(transport.lastRequest?.kind).toBe('edit');
|
|
774
|
+
expect(transport.lastRequest?.message?.id).toBe('u2');
|
|
775
|
+
expect(transport.lastRequest?.text).toBe('Plan Porto for two');
|
|
776
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1']);
|
|
777
|
+
expect(stateOf(controller).pendingMessage?.text).toBe('Plan Porto for two');
|
|
778
|
+
// The edit keeps what the message carried.
|
|
779
|
+
expect(stateOf(controller).pendingMessage?.attachments?.map(({ url }) => url)).toEqual([
|
|
780
|
+
'https://example.com/porto.jpg',
|
|
781
|
+
photo.uri,
|
|
782
|
+
]);
|
|
783
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
784
|
+
|
|
785
|
+
transport.update([
|
|
786
|
+
user('u1'),
|
|
787
|
+
reply('a1'),
|
|
788
|
+
user('u2', 'Plan Porto for two'),
|
|
789
|
+
reply('a3', 'For two', true),
|
|
790
|
+
]);
|
|
791
|
+
|
|
792
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1', 'u2', 'a3']);
|
|
793
|
+
expect(stateOf(controller).pendingMessage).toBeUndefined();
|
|
794
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
it('empties the composer once cancelled', async () => {
|
|
798
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
799
|
+
const controller = makeController(transport);
|
|
800
|
+
controller.edit(user('u1'));
|
|
801
|
+
|
|
802
|
+
controller.cancelEditing();
|
|
803
|
+
controller.send({ text: 'Something else' });
|
|
804
|
+
await settle();
|
|
805
|
+
|
|
806
|
+
expect(controller.editingMessage).toBeUndefined();
|
|
807
|
+
expect(controller.composer.getLatestValue().text).toBe('');
|
|
808
|
+
expect(transport.lastRequest?.kind).toBe('message');
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
it('keeps the composer when cancelled while not editing', () => {
|
|
812
|
+
const controller = makeController(new ConversationTransport());
|
|
813
|
+
controller.composer.partialNext({ text: 'Half a thought' });
|
|
814
|
+
|
|
815
|
+
controller.cancelEditing();
|
|
816
|
+
|
|
817
|
+
expect(controller.composer.getLatestValue().text).toBe('Half a thought');
|
|
818
|
+
});
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
describe('AIConversationController stopping', () => {
|
|
822
|
+
it('ends the reply and tells the transport', async () => {
|
|
823
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', 'Day one', true)]);
|
|
824
|
+
const controller = makeController(transport);
|
|
825
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
826
|
+
|
|
827
|
+
controller.stop();
|
|
828
|
+
await settle();
|
|
829
|
+
|
|
830
|
+
expect(transport.stops).toBe(1);
|
|
831
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
832
|
+
|
|
833
|
+
transport.update([user('u1'), reply('a1', 'Day one', true)]);
|
|
834
|
+
|
|
835
|
+
// A stopped reply is finished, however long the conversation says otherwise.
|
|
836
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
837
|
+
// What it wrote so far stays.
|
|
838
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1']);
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
it('keeps the person’s message while waiting', async () => {
|
|
842
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
843
|
+
const controller = makeController(transport);
|
|
844
|
+
controller.send({ text: 'Hi' });
|
|
845
|
+
|
|
846
|
+
controller.stop();
|
|
847
|
+
await settle();
|
|
848
|
+
|
|
849
|
+
// It is on its way until its copy arrives.
|
|
850
|
+
expect(stateOf(controller).pendingMessage?.text).toBe('Hi');
|
|
851
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
852
|
+
expect(transport.stops).toBe(1);
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
it('finishes a reply that arrives after a stop while waiting', async () => {
|
|
856
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
857
|
+
const controller = makeController(transport);
|
|
858
|
+
controller.send({ text: 'Hi' });
|
|
859
|
+
controller.stop();
|
|
860
|
+
await settle();
|
|
861
|
+
|
|
862
|
+
transport.update([user('u1'), reply('a1'), user('u2', 'Hi'), reply('a2', '', true)]);
|
|
863
|
+
|
|
864
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
865
|
+
// It has nothing to show.
|
|
866
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1', 'u2']);
|
|
867
|
+
|
|
868
|
+
transport.update([user('u1'), reply('a1'), user('u2', 'Hi'), reply('a2', 'Hello', true)]);
|
|
869
|
+
|
|
870
|
+
// The person stopped it.
|
|
871
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
872
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1', 'u2', 'a2']);
|
|
873
|
+
expect(stateOf(controller).pendingMessage).toBeUndefined();
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
it('lets the next request’s reply stream after a stop while waiting', async () => {
|
|
877
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
878
|
+
const controller = makeController(transport);
|
|
879
|
+
controller.send({ text: 'Hi' });
|
|
880
|
+
controller.stop();
|
|
881
|
+
await settle();
|
|
882
|
+
|
|
883
|
+
controller.send({ text: 'Porto?' });
|
|
884
|
+
transport.update([
|
|
885
|
+
user('u1'),
|
|
886
|
+
reply('a1'),
|
|
887
|
+
user('u2', 'Hi'),
|
|
888
|
+
user('u3', 'Porto?'),
|
|
889
|
+
reply('a3', 'Sure', true),
|
|
890
|
+
]);
|
|
891
|
+
|
|
892
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
it('brings the reply back when stopped while waiting for it to be regenerated', () => {
|
|
896
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
897
|
+
const controller = makeController(transport);
|
|
898
|
+
controller.regenerate(reply('a1'));
|
|
899
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1']);
|
|
900
|
+
|
|
901
|
+
controller.stop();
|
|
902
|
+
|
|
903
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1']);
|
|
904
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
it('sends after a stop only once the stop reached the transport', async () => {
|
|
908
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', 'Day one', true)]);
|
|
909
|
+
transport.holdsStops = true;
|
|
910
|
+
const controller = makeController(transport);
|
|
911
|
+
|
|
912
|
+
controller.stop();
|
|
913
|
+
controller.send({ text: 'Something else' });
|
|
914
|
+
await settle();
|
|
915
|
+
|
|
916
|
+
expect(transport.stops).toBe(1);
|
|
917
|
+
// The stop would stop it too.
|
|
918
|
+
expect(transport.requests).toEqual([]);
|
|
919
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
920
|
+
|
|
921
|
+
transport.finishStop(0);
|
|
922
|
+
await settle();
|
|
923
|
+
|
|
924
|
+
expect(transport.lastRequest?.text).toBe('Something else');
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
it('leaves out a reply that shows nothing', () => {
|
|
928
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', '', true)]);
|
|
929
|
+
const controller = makeController(transport);
|
|
930
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
931
|
+
|
|
932
|
+
controller.stop();
|
|
933
|
+
|
|
934
|
+
// The conversation would think it is still on its way.
|
|
935
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1']);
|
|
936
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
it('does nothing when the agent isn’t answering', async () => {
|
|
940
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
941
|
+
const controller = makeController(transport);
|
|
942
|
+
|
|
943
|
+
controller.stop();
|
|
944
|
+
await settle();
|
|
945
|
+
|
|
946
|
+
expect(transport.stops).toBe(0);
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
it('ignores what the agent says until it stops saying', () => {
|
|
950
|
+
const transport = new ConversationTransport([user('u1')]);
|
|
951
|
+
const controller = makeController(transport);
|
|
952
|
+
transport.updateActivity({ state: AIAgentActivityStates.thinking });
|
|
953
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
954
|
+
|
|
955
|
+
controller.stop();
|
|
956
|
+
transport.updateActivity({ state: AIAgentActivityStates.generating });
|
|
957
|
+
|
|
958
|
+
expect(stateOf(controller).activity).toBeUndefined();
|
|
959
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
960
|
+
|
|
961
|
+
transport.updateActivity(undefined);
|
|
962
|
+
transport.updateActivity({ state: AIAgentActivityStates.thinking });
|
|
963
|
+
|
|
964
|
+
expect(stateOf(controller).activity).toEqual({ state: AIAgentActivityStates.thinking });
|
|
965
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
it('shows the reply still being written when the transport can’t stop it', async () => {
|
|
969
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', 'Day one', true)]);
|
|
970
|
+
transport.stopError = new TransportError();
|
|
971
|
+
const controller = makeController(transport);
|
|
972
|
+
|
|
973
|
+
controller.stop();
|
|
974
|
+
await settle();
|
|
975
|
+
|
|
976
|
+
// Nothing to try again.
|
|
977
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
978
|
+
// The composer offers to stop it again.
|
|
979
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
980
|
+
|
|
981
|
+
transport.update([user('u1'), reply('a1', 'Day one, day two')]);
|
|
982
|
+
|
|
983
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
it('waits for the reply again when the transport can’t stop it while waiting', async () => {
|
|
987
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
988
|
+
transport.stopError = new TransportError();
|
|
989
|
+
const controller = makeController(transport);
|
|
990
|
+
controller.send({ text: 'Hi' });
|
|
991
|
+
|
|
992
|
+
controller.stop();
|
|
993
|
+
await settle();
|
|
994
|
+
|
|
995
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
996
|
+
|
|
997
|
+
transport.update([user('u1'), reply('a1'), user('u2', 'Hi'), reply('a2', 'Hello', true)]);
|
|
998
|
+
|
|
999
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
it('ignores a stop’s failure after a new request', async () => {
|
|
1003
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', 'Day one', true)]);
|
|
1004
|
+
transport.stopError = new TransportError();
|
|
1005
|
+
const controller = makeController(transport);
|
|
1006
|
+
|
|
1007
|
+
controller.stop();
|
|
1008
|
+
controller.send({ text: 'Something else' });
|
|
1009
|
+
await settle();
|
|
1010
|
+
|
|
1011
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
1012
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
1013
|
+
});
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
describe('AIConversationController and what the agent says it is doing', () => {
|
|
1017
|
+
it('shows the agent working while the person’s message waits', () => {
|
|
1018
|
+
const transport = new ConversationTransport([user('u1')]);
|
|
1019
|
+
const controller = makeController(transport);
|
|
1020
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
1021
|
+
|
|
1022
|
+
transport.updateActivity({
|
|
1023
|
+
state: AIAgentActivityStates.checkingExternalSources,
|
|
1024
|
+
text: 'Searching the web',
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
expect(stateOf(controller).activity?.text).toBe('Searching the web');
|
|
1028
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
1029
|
+
|
|
1030
|
+
transport.updateActivity(undefined);
|
|
1031
|
+
|
|
1032
|
+
expect(stateOf(controller).activity).toBeUndefined();
|
|
1033
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
it('shows the agent working in an empty conversation', () => {
|
|
1037
|
+
const transport = new ConversationTransport();
|
|
1038
|
+
const controller = makeController(transport);
|
|
1039
|
+
|
|
1040
|
+
transport.updateActivity({ state: AIAgentActivityStates.thinking });
|
|
1041
|
+
|
|
1042
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
it('doesn’t take an activity after a finished reply as the agent answering', () => {
|
|
1046
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1047
|
+
const controller = makeController(transport);
|
|
1048
|
+
|
|
1049
|
+
transport.updateActivity({ state: AIAgentActivityStates.generating });
|
|
1050
|
+
|
|
1051
|
+
expect(stateOf(controller).activity?.state).toBe(AIAgentActivityStates.generating);
|
|
1052
|
+
// An indicator that wasn't cleared yet.
|
|
1053
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
it('ends the request with the agent’s error', async () => {
|
|
1057
|
+
const transport = new ConversationTransport([user('u1', 'Lisbon?'), reply('a1')]);
|
|
1058
|
+
const controller = makeController(transport);
|
|
1059
|
+
controller.send({ text: 'Porto?' });
|
|
1060
|
+
transport.update([user('u1', 'Lisbon?'), reply('a1'), user('u2', 'Porto?')]);
|
|
1061
|
+
|
|
1062
|
+
transport.updateActivity({ state: AIAgentActivityStates.error, text: 'Rate limited' });
|
|
1063
|
+
|
|
1064
|
+
// Its copy takes its place.
|
|
1065
|
+
expect(stateOf(controller).pendingMessage?.text).toBe('Porto?');
|
|
1066
|
+
expect(stateOf(controller).status).toBe('error');
|
|
1067
|
+
const { error } = stateOf(controller);
|
|
1068
|
+
expect(error).toBeInstanceOf(AIAgentError);
|
|
1069
|
+
expect((error as AIAgentError).agentMessage).toBe('Rate limited');
|
|
1070
|
+
expect(error?.message).toBe('Rate limited');
|
|
1071
|
+
|
|
1072
|
+
transport.updateActivity(undefined);
|
|
1073
|
+
|
|
1074
|
+
// It stays until the next request.
|
|
1075
|
+
expect(stateOf(controller).status).toBe('error');
|
|
1076
|
+
|
|
1077
|
+
controller.retry();
|
|
1078
|
+
await settle();
|
|
1079
|
+
|
|
1080
|
+
expect(transport.lastRequest?.kind).toBe('regenerate');
|
|
1081
|
+
expect(transport.lastRequest?.text).toBe('Porto?');
|
|
1082
|
+
// The message has no reply to replace.
|
|
1083
|
+
expect(transport.lastRequest?.message).toBeUndefined();
|
|
1084
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
1085
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
it('finishes the reply being written on an error', async () => {
|
|
1089
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', 'Day one', true)]);
|
|
1090
|
+
const controller = makeController(transport);
|
|
1091
|
+
|
|
1092
|
+
transport.updateActivity({ state: AIAgentActivityStates.error });
|
|
1093
|
+
|
|
1094
|
+
expect(stateOf(controller).status).toBe('error');
|
|
1095
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1']);
|
|
1096
|
+
|
|
1097
|
+
controller.retry();
|
|
1098
|
+
await settle();
|
|
1099
|
+
|
|
1100
|
+
expect(transport.lastRequest?.kind).toBe('regenerate');
|
|
1101
|
+
expect(transport.lastRequest?.message?.id).toBe('a1');
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
it('ignores the failure of the request an error ended', async () => {
|
|
1105
|
+
const transport = new ConversationTransport();
|
|
1106
|
+
transport.holdsSends = true;
|
|
1107
|
+
const controller = makeController(transport);
|
|
1108
|
+
controller.send({ text: 'Hi' });
|
|
1109
|
+
await settle();
|
|
1110
|
+
|
|
1111
|
+
transport.updateActivity({ state: AIAgentActivityStates.error, text: 'Rate limited' });
|
|
1112
|
+
transport.finishSend(0, new TransportError());
|
|
1113
|
+
await settle();
|
|
1114
|
+
|
|
1115
|
+
expect((stateOf(controller).error as AIAgentError).agentMessage).toBe('Rate limited');
|
|
1116
|
+
});
|
|
1117
|
+
|
|
1118
|
+
it('sends the person’s message again on retry when the error came before it arrived', async () => {
|
|
1119
|
+
const transport = new ConversationTransport([user('u1', 'Lisbon?'), reply('a1')]);
|
|
1120
|
+
const controller = makeController(transport);
|
|
1121
|
+
controller.send({ text: 'Porto?' });
|
|
1122
|
+
await settle();
|
|
1123
|
+
const sent = transport.lastRequest;
|
|
1124
|
+
|
|
1125
|
+
transport.updateActivity({ state: AIAgentActivityStates.error, text: 'Couldn’t read it' });
|
|
1126
|
+
transport.updateActivity(undefined);
|
|
1127
|
+
|
|
1128
|
+
expect(stateOf(controller).pendingMessage?.text).toBe('Porto?');
|
|
1129
|
+
expect(ids(stateOf(controller).messages)).toEqual(['u1', 'a1']);
|
|
1130
|
+
|
|
1131
|
+
controller.retry();
|
|
1132
|
+
await settle();
|
|
1133
|
+
|
|
1134
|
+
// Not a1 written again.
|
|
1135
|
+
expect(transport.lastRequest?.kind).toBe('message');
|
|
1136
|
+
expect(transport.lastRequest?.messageId).toBe(sent?.messageId);
|
|
1137
|
+
expect(transport.lastRequest?.text).toBe('Porto?');
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
it('asks for the reply on retry when the person’s message arrived after the error', async () => {
|
|
1141
|
+
const transport = new ConversationTransport([user('u1', 'Lisbon?'), reply('a1')]);
|
|
1142
|
+
const controller = makeController(transport);
|
|
1143
|
+
controller.send({ text: 'Porto?' });
|
|
1144
|
+
await settle();
|
|
1145
|
+
|
|
1146
|
+
transport.updateActivity({ state: AIAgentActivityStates.error });
|
|
1147
|
+
transport.update([user('u1', 'Lisbon?'), reply('a1'), user('u2', 'Porto?')]);
|
|
1148
|
+
controller.retry();
|
|
1149
|
+
await settle();
|
|
1150
|
+
|
|
1151
|
+
expect(transport.lastRequest?.kind).toBe('regenerate');
|
|
1152
|
+
expect(transport.lastRequest?.text).toBe('Porto?');
|
|
1153
|
+
expect(transport.lastRequest?.message).toBeUndefined();
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
it('forgets the error once a new reply starts', () => {
|
|
1157
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1158
|
+
const controller = makeController(transport);
|
|
1159
|
+
controller.send({ text: 'Porto?' });
|
|
1160
|
+
transport.update([user('u1'), reply('a1'), user('u2', 'Porto?')]);
|
|
1161
|
+
transport.updateActivity({ state: AIAgentActivityStates.error });
|
|
1162
|
+
transport.updateActivity(undefined);
|
|
1163
|
+
expect(stateOf(controller).status).toBe('error');
|
|
1164
|
+
|
|
1165
|
+
transport.update([
|
|
1166
|
+
user('u1'),
|
|
1167
|
+
reply('a1'),
|
|
1168
|
+
user('u2', 'Porto?'),
|
|
1169
|
+
reply('a2', 'Porto it is', true),
|
|
1170
|
+
]);
|
|
1171
|
+
|
|
1172
|
+
// The agent answered after all.
|
|
1173
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
1174
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
1175
|
+
|
|
1176
|
+
transport.update([user('u1'), reply('a1'), user('u2', 'Porto?'), reply('a2', 'Porto it is')]);
|
|
1177
|
+
|
|
1178
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
1179
|
+
expect(stateOf(controller).pendingMessage).toBeUndefined();
|
|
1180
|
+
});
|
|
1181
|
+
|
|
1182
|
+
it('forgets an error while a reply is written once the agent writes on', () => {
|
|
1183
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', 'Day one', true)]);
|
|
1184
|
+
const controller = makeController(transport);
|
|
1185
|
+
transport.updateActivity({ state: AIAgentActivityStates.error });
|
|
1186
|
+
transport.updateActivity(undefined);
|
|
1187
|
+
|
|
1188
|
+
transport.update([user('u1'), reply('a1', 'Day one')]);
|
|
1189
|
+
|
|
1190
|
+
// The same reply, finished.
|
|
1191
|
+
expect(stateOf(controller).status).toBe('error');
|
|
1192
|
+
|
|
1193
|
+
transport.update([user('u1'), reply('a1'), user('u2'), reply('a2', 'Day two', true)]);
|
|
1194
|
+
|
|
1195
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
1196
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
it('forgets what the agent said it was doing on a new request', () => {
|
|
1200
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1201
|
+
const controller = makeController(transport);
|
|
1202
|
+
transport.updateActivity({ state: AIAgentActivityStates.checkingExternalSources });
|
|
1203
|
+
|
|
1204
|
+
controller.send({ text: 'Hi' });
|
|
1205
|
+
|
|
1206
|
+
expect(stateOf(controller).activity).toBeUndefined();
|
|
1207
|
+
});
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
describe('AIConversationController errors', () => {
|
|
1211
|
+
it('does nothing on retry without an error', async () => {
|
|
1212
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1213
|
+
const controller = makeController(transport);
|
|
1214
|
+
|
|
1215
|
+
controller.retry();
|
|
1216
|
+
await settle();
|
|
1217
|
+
|
|
1218
|
+
expect(transport.requests).toEqual([]);
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
it('has nothing to retry after a stop failed', async () => {
|
|
1222
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', 'Day one', true)]);
|
|
1223
|
+
transport.stopError = new TransportError();
|
|
1224
|
+
const controller = makeController(transport);
|
|
1225
|
+
controller.stop();
|
|
1226
|
+
await settle();
|
|
1227
|
+
|
|
1228
|
+
controller.retry();
|
|
1229
|
+
await settle();
|
|
1230
|
+
|
|
1231
|
+
expect(transport.requests).toEqual([]);
|
|
1232
|
+
});
|
|
1233
|
+
|
|
1234
|
+
it('does nothing on retry after the agent failed in an empty conversation', async () => {
|
|
1235
|
+
const transport = new ConversationTransport();
|
|
1236
|
+
const controller = makeController(transport);
|
|
1237
|
+
transport.updateActivity({ state: AIAgentActivityStates.error });
|
|
1238
|
+
|
|
1239
|
+
controller.retry();
|
|
1240
|
+
await settle();
|
|
1241
|
+
|
|
1242
|
+
expect(transport.requests).toEqual([]);
|
|
1243
|
+
});
|
|
1244
|
+
|
|
1245
|
+
it('forgets the error and the request that failed', async () => {
|
|
1246
|
+
const transport = new ConversationTransport();
|
|
1247
|
+
transport.sendError = new TransportError();
|
|
1248
|
+
const controller = makeController(transport);
|
|
1249
|
+
controller.send({ text: 'Hi' });
|
|
1250
|
+
await settle();
|
|
1251
|
+
|
|
1252
|
+
controller.clearError();
|
|
1253
|
+
controller.retry();
|
|
1254
|
+
await settle();
|
|
1255
|
+
|
|
1256
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
1257
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
1258
|
+
expect(transport.requests).toHaveLength(1);
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
it('describes an agent error in the agent’s words, else its own', () => {
|
|
1262
|
+
expect(new AIAgentError('Rate limited').message).toBe('Rate limited');
|
|
1263
|
+
expect(new AIAgentError().message).toBe('The agent couldn’t answer. Try again.');
|
|
1264
|
+
expect(new AIAgentError('').agentMessage).toBeUndefined();
|
|
1265
|
+
});
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1268
|
+
describe('AIConversationController older messages', () => {
|
|
1269
|
+
it('asks the transport only while there are any', async () => {
|
|
1270
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1271
|
+
const controller = makeController(transport);
|
|
1272
|
+
|
|
1273
|
+
await controller.loadOlderMessages();
|
|
1274
|
+
expect(transport.loads).toBe(0);
|
|
1275
|
+
|
|
1276
|
+
transport.feed?.updateHasOlderMessages(true);
|
|
1277
|
+
await controller.loadOlderMessages();
|
|
1278
|
+
expect(transport.loads).toBe(1);
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
it('loads once at a time', async () => {
|
|
1282
|
+
const transport = new ConversationTransport();
|
|
1283
|
+
transport.holdsLoads = true;
|
|
1284
|
+
const controller = makeController(transport);
|
|
1285
|
+
transport.feed?.updateHasOlderMessages(true);
|
|
1286
|
+
|
|
1287
|
+
const first = controller.loadOlderMessages();
|
|
1288
|
+
const second = controller.loadOlderMessages();
|
|
1289
|
+
await settle();
|
|
1290
|
+
transport.finishLoads();
|
|
1291
|
+
await Promise.all([first, second]);
|
|
1292
|
+
|
|
1293
|
+
expect(transport.loads).toBe(1);
|
|
1294
|
+
|
|
1295
|
+
transport.holdsLoads = false;
|
|
1296
|
+
await controller.loadOlderMessages();
|
|
1297
|
+
|
|
1298
|
+
expect(transport.loads).toBe(2);
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
it('can load again when the transport fails', async () => {
|
|
1302
|
+
const transport = new ConversationTransport();
|
|
1303
|
+
transport.loadError = new TransportError();
|
|
1304
|
+
const controller = makeController(transport);
|
|
1305
|
+
transport.feed?.updateHasOlderMessages(true);
|
|
1306
|
+
|
|
1307
|
+
await controller.loadOlderMessages();
|
|
1308
|
+
await controller.loadOlderMessages();
|
|
1309
|
+
|
|
1310
|
+
expect(transport.loads).toBe(2);
|
|
1311
|
+
// Not a request of the person's.
|
|
1312
|
+
expect(stateOf(controller).error).toBeUndefined();
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
it('loads nothing and keeps ratings on the device with a transport that has neither', async () => {
|
|
1316
|
+
let feed: AIConversationFeed | undefined;
|
|
1317
|
+
const controller = makeController({
|
|
1318
|
+
connect: (connected) => {
|
|
1319
|
+
feed = connected;
|
|
1320
|
+
},
|
|
1321
|
+
send: async () => {},
|
|
1322
|
+
stop: async () => {},
|
|
1323
|
+
});
|
|
1324
|
+
|
|
1325
|
+
feed?.updateHasOlderMessages(true);
|
|
1326
|
+
await controller.loadOlderMessages();
|
|
1327
|
+
controller.rate(reply('a1'), 'negative');
|
|
1328
|
+
await settle();
|
|
1329
|
+
|
|
1330
|
+
expect(stateOf(controller).ratings.a1).toBe('negative');
|
|
1331
|
+
});
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
describe('AIConversationController ratings', () => {
|
|
1335
|
+
it('keeps the rating and tells the transport', async () => {
|
|
1336
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1337
|
+
const controller = makeController(transport);
|
|
1338
|
+
|
|
1339
|
+
controller.rate(reply('a1'), 'positive');
|
|
1340
|
+
expect(stateOf(controller).ratings.a1).toBe('positive');
|
|
1341
|
+
await settle();
|
|
1342
|
+
controller.rate(reply('a1'), null);
|
|
1343
|
+
await settle();
|
|
1344
|
+
|
|
1345
|
+
expect(stateOf(controller).ratings.a1).toBeUndefined();
|
|
1346
|
+
expect(transport.ratings).toEqual([
|
|
1347
|
+
{ id: 'a1', rating: 'positive' },
|
|
1348
|
+
{ id: 'a1', rating: null },
|
|
1349
|
+
]);
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
it('does nothing when rated the same again', async () => {
|
|
1353
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1354
|
+
const controller = makeController(transport);
|
|
1355
|
+
|
|
1356
|
+
controller.rate(reply('a1'), 'positive');
|
|
1357
|
+
controller.rate(reply('a1'), 'positive');
|
|
1358
|
+
await settle();
|
|
1359
|
+
|
|
1360
|
+
expect(transport.ratings).toHaveLength(1);
|
|
1361
|
+
});
|
|
1362
|
+
|
|
1363
|
+
it('puts the rating back when the transport fails', async () => {
|
|
1364
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1365
|
+
const controller = makeController(transport);
|
|
1366
|
+
controller.rate(reply('a1'), 'positive');
|
|
1367
|
+
await settle();
|
|
1368
|
+
|
|
1369
|
+
transport.failsRating = () => true;
|
|
1370
|
+
controller.rate(reply('a1'), 'negative');
|
|
1371
|
+
await settle();
|
|
1372
|
+
|
|
1373
|
+
expect(stateOf(controller).ratings.a1).toBe('positive');
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1376
|
+
it('keeps the new rating when the transport fails after it changed again', async () => {
|
|
1377
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1378
|
+
transport.failsRating = (rating) => rating === 'positive';
|
|
1379
|
+
const controller = makeController(transport);
|
|
1380
|
+
|
|
1381
|
+
controller.rate(reply('a1'), 'positive');
|
|
1382
|
+
controller.rate(reply('a1'), 'negative');
|
|
1383
|
+
await settle();
|
|
1384
|
+
|
|
1385
|
+
expect(stateOf(controller).ratings.a1).toBe('negative');
|
|
1386
|
+
// The rating it replaced before it was sent isn't.
|
|
1387
|
+
expect(transport.ratings.map(({ rating }) => rating)).toEqual(['negative']);
|
|
1388
|
+
});
|
|
1389
|
+
|
|
1390
|
+
it('sends each reply’s ratings in order', async () => {
|
|
1391
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1392
|
+
transport.holdsRatings = true;
|
|
1393
|
+
const controller = makeController(transport);
|
|
1394
|
+
|
|
1395
|
+
controller.rate(reply('a1'), 'positive');
|
|
1396
|
+
await settle();
|
|
1397
|
+
controller.rate(reply('a1'), 'negative');
|
|
1398
|
+
await settle();
|
|
1399
|
+
|
|
1400
|
+
// The next one waits.
|
|
1401
|
+
expect(transport.ratings.map(({ rating }) => rating)).toEqual(['positive']);
|
|
1402
|
+
|
|
1403
|
+
transport.finishRating(0);
|
|
1404
|
+
await settle();
|
|
1405
|
+
transport.finishRating(1);
|
|
1406
|
+
await settle();
|
|
1407
|
+
|
|
1408
|
+
expect(transport.ratings.map(({ rating }) => rating)).toEqual(['positive', 'negative']);
|
|
1409
|
+
expect(stateOf(controller).ratings.a1).toBe('negative');
|
|
1410
|
+
});
|
|
1411
|
+
|
|
1412
|
+
it('keeps the latest when an earlier rating fails', async () => {
|
|
1413
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1414
|
+
transport.holdsRatings = true;
|
|
1415
|
+
const controller = makeController(transport);
|
|
1416
|
+
|
|
1417
|
+
controller.rate(reply('a1'), 'positive');
|
|
1418
|
+
await settle();
|
|
1419
|
+
controller.rate(reply('a1'), 'negative');
|
|
1420
|
+
transport.finishRating(0, new TransportError());
|
|
1421
|
+
await settle();
|
|
1422
|
+
|
|
1423
|
+
expect(stateOf(controller).ratings.a1).toBe('negative');
|
|
1424
|
+
|
|
1425
|
+
transport.finishRating(1, new TransportError());
|
|
1426
|
+
await settle();
|
|
1427
|
+
|
|
1428
|
+
// What the transport has.
|
|
1429
|
+
expect(stateOf(controller).ratings.a1).toBeUndefined();
|
|
1430
|
+
});
|
|
1431
|
+
|
|
1432
|
+
it('shows the replies’ own until the person rates them', async () => {
|
|
1433
|
+
const rated: AIMessage = { ...reply('a1'), rating: 'positive' };
|
|
1434
|
+
const transport = new ConversationTransport([user('u1'), rated, reply('a2')]);
|
|
1435
|
+
const controller = makeController(transport);
|
|
1436
|
+
|
|
1437
|
+
expect(stateOf(controller).ratings).toEqual({ a1: 'positive' });
|
|
1438
|
+
|
|
1439
|
+
controller.rate(rated, null);
|
|
1440
|
+
await settle();
|
|
1441
|
+
|
|
1442
|
+
expect(stateOf(controller).ratings).toEqual({});
|
|
1443
|
+
expect(transport.ratings.map(({ rating }) => rating)).toEqual([null]);
|
|
1444
|
+
|
|
1445
|
+
transport.update([user('u1'), rated, reply('a2')]);
|
|
1446
|
+
|
|
1447
|
+
// The person's rating stays over the one the reply still has.
|
|
1448
|
+
expect(stateOf(controller).ratings).toEqual({});
|
|
1449
|
+
});
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
describe('AIConversationController offers', () => {
|
|
1453
|
+
it('offers regenerating the latest reply while the agent isn’t answering', async () => {
|
|
1454
|
+
const transport = new ConversationTransport([user('u1'), reply('a1'), user('u2'), reply('a2')]);
|
|
1455
|
+
const controller = makeController(transport);
|
|
1456
|
+
|
|
1457
|
+
expect(controller.replyActions(reply('a1'), false).onRegenerate).toBeUndefined();
|
|
1458
|
+
const latest = controller.replyActions(reply('a2'), true);
|
|
1459
|
+
expect(latest.onRegenerate).toBeDefined();
|
|
1460
|
+
|
|
1461
|
+
latest.onRegenerate?.();
|
|
1462
|
+
await settle();
|
|
1463
|
+
|
|
1464
|
+
expect(transport.lastRequest?.kind).toBe('regenerate');
|
|
1465
|
+
expect(transport.lastRequest?.message?.id).toBe('a2');
|
|
1466
|
+
// The agent is answering.
|
|
1467
|
+
expect(controller.replyActions(reply('a2'), true).onRegenerate).toBeUndefined();
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1470
|
+
it('rates and shows the rating', async () => {
|
|
1471
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1472
|
+
const controller = makeController(transport);
|
|
1473
|
+
|
|
1474
|
+
controller.replyActions(reply('a1'), true).onRate?.('negative');
|
|
1475
|
+
await settle();
|
|
1476
|
+
|
|
1477
|
+
expect(controller.replyActions(reply('a1'), true).rating).toBe('negative');
|
|
1478
|
+
expect(transport.ratings[0]?.rating).toBe('negative');
|
|
1479
|
+
});
|
|
1480
|
+
|
|
1481
|
+
it('offers editing the person’s latest message while the agent isn’t answering', () => {
|
|
1482
|
+
const transport = new ConversationTransport([
|
|
1483
|
+
user('u1', 'Lisbon'),
|
|
1484
|
+
reply('a1'),
|
|
1485
|
+
user('u2', 'Porto'),
|
|
1486
|
+
reply('a2'),
|
|
1487
|
+
]);
|
|
1488
|
+
const controller = makeController(transport);
|
|
1489
|
+
|
|
1490
|
+
expect(controller.editAction(user('u1'))).toBeUndefined();
|
|
1491
|
+
expect(controller.editAction(reply('a2'))).toBeUndefined();
|
|
1492
|
+
const edit = controller.editAction(user('u2', 'Porto'));
|
|
1493
|
+
expect(edit).toBeDefined();
|
|
1494
|
+
|
|
1495
|
+
edit?.();
|
|
1496
|
+
|
|
1497
|
+
expect(controller.editingMessage?.id).toBe('u2');
|
|
1498
|
+
expect(controller.composer.getLatestValue().text).toBe('Porto');
|
|
1499
|
+
|
|
1500
|
+
transport.update([user('u1'), reply('a1'), user('u2'), reply('a2', 'Porto it is', true)]);
|
|
1501
|
+
|
|
1502
|
+
// The agent is answering.
|
|
1503
|
+
expect(controller.editAction(user('u2'))).toBeUndefined();
|
|
1504
|
+
});
|
|
1505
|
+
|
|
1506
|
+
it('leaves out what the app doesn’t offer', () => {
|
|
1507
|
+
const transport = new ConversationTransport([user('u1'), reply('a1')]);
|
|
1508
|
+
const controller = makeController(transport, { actions: { edit: false, regenerate: false } });
|
|
1509
|
+
|
|
1510
|
+
expect(controller.replyActions(reply('a1'), true).onRegenerate).toBeUndefined();
|
|
1511
|
+
expect(controller.replyActions(reply('a1'), true).onRate).toBeDefined();
|
|
1512
|
+
expect(controller.editAction(user('u1'))).toBeUndefined();
|
|
1513
|
+
|
|
1514
|
+
const nothing = makeController(transport, {
|
|
1515
|
+
actions: { edit: false, rate: false, regenerate: false },
|
|
1516
|
+
});
|
|
1517
|
+
|
|
1518
|
+
expect(nothing.replyActions(reply('a1'), true).onRate).toBeUndefined();
|
|
1519
|
+
expect(makeController(transport).actions).toEqual({ edit: true, rate: true, regenerate: true });
|
|
1520
|
+
});
|
|
1521
|
+
});
|
|
1522
|
+
|
|
1523
|
+
describe('AIConversationController status', () => {
|
|
1524
|
+
it('follows the latest reply', () => {
|
|
1525
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', '', true)]);
|
|
1526
|
+
const controller = makeController(transport);
|
|
1527
|
+
|
|
1528
|
+
expect(stateOf(controller).status).toBe('submitted');
|
|
1529
|
+
|
|
1530
|
+
transport.update([user('u1'), reply('a1', 'Day', true)]);
|
|
1531
|
+
expect(stateOf(controller).status).toBe('streaming');
|
|
1532
|
+
|
|
1533
|
+
transport.update([user('u1'), reply('a1', 'Day one')]);
|
|
1534
|
+
expect(stateOf(controller).status).toBe('ready');
|
|
1535
|
+
});
|
|
1536
|
+
|
|
1537
|
+
it('is answering while submitted or streaming', () => {
|
|
1538
|
+
expect(isAIConversationAnswering('submitted')).toBe(true);
|
|
1539
|
+
expect(isAIConversationAnswering('streaming')).toBe(true);
|
|
1540
|
+
expect(isAIConversationAnswering('ready')).toBe(false);
|
|
1541
|
+
expect(isAIConversationAnswering('error')).toBe(false);
|
|
1542
|
+
});
|
|
1543
|
+
|
|
1544
|
+
it('notifies a status follower only when the status changes', () => {
|
|
1545
|
+
const transport = new ConversationTransport([user('u1'), reply('a1', 'Day', true)]);
|
|
1546
|
+
const controller = makeController(transport);
|
|
1547
|
+
const statuses: string[] = [];
|
|
1548
|
+
const unsubscribe = controller.state.subscribeWithSelector(
|
|
1549
|
+
({ status }) => ({ status }),
|
|
1550
|
+
({ status }) => statuses.push(status),
|
|
1551
|
+
);
|
|
1552
|
+
statuses.splice(0);
|
|
1553
|
+
|
|
1554
|
+
transport.update([user('u1'), reply('a1', 'Day one', true)]);
|
|
1555
|
+
transport.update([user('u1'), reply('a1', 'Day one, day two', true)]);
|
|
1556
|
+
transport.update([user('u1'), reply('a1', 'Day one, day two')]);
|
|
1557
|
+
|
|
1558
|
+
expect(statuses).toEqual(['ready']);
|
|
1559
|
+
unsubscribe();
|
|
1560
|
+
});
|
|
1561
|
+
});
|