stream-chat-react-native-core 9.12.1-beta.1 → 9.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (922) hide show
  1. package/a2ui/package.json +6 -0
  2. package/lib/commonjs/a11y/a11yUtils.js.map +1 -1
  3. package/lib/commonjs/a11y/hooks/useResolvedModalAccessibilityProps.js.map +1 -1
  4. package/lib/commonjs/a2ui/A2UIContexts.js +8 -0
  5. package/lib/commonjs/a2ui/A2UIContexts.js.map +1 -0
  6. package/lib/commonjs/a2ui/A2UIInstructions.js +49 -0
  7. package/lib/commonjs/a2ui/A2UIInstructions.js.map +1 -0
  8. package/lib/commonjs/a2ui/A2UIPlaceholder.js +37 -0
  9. package/lib/commonjs/a2ui/A2UIPlaceholder.js.map +1 -0
  10. package/lib/commonjs/a2ui/A2UIRenderer.js +363 -0
  11. package/lib/commonjs/a2ui/A2UIRenderer.js.map +1 -0
  12. package/lib/commonjs/a2ui/A2UIStream.js +91 -0
  13. package/lib/commonjs/a2ui/A2UIStream.js.map +1 -0
  14. package/lib/commonjs/a2ui/A2UISurface.js +212 -0
  15. package/lib/commonjs/a2ui/A2UISurface.js.map +1 -0
  16. package/lib/commonjs/a2ui/basicCatalog.json +1206 -0
  17. package/lib/commonjs/a2ui/basicFunctions.js +398 -0
  18. package/lib/commonjs/a2ui/basicFunctions.js.map +1 -0
  19. package/lib/commonjs/a2ui/catalogDocument.js +80 -0
  20. package/lib/commonjs/a2ui/catalogDocument.js.map +1 -0
  21. package/lib/commonjs/a2ui/catalogs.js +105 -0
  22. package/lib/commonjs/a2ui/catalogs.js.map +1 -0
  23. package/lib/commonjs/a2ui/components/DateTimeInput.js +445 -0
  24. package/lib/commonjs/a2ui/components/DateTimeInput.js.map +1 -0
  25. package/lib/commonjs/a2ui/components/Media.js +316 -0
  26. package/lib/commonjs/a2ui/components/Media.js.map +1 -0
  27. package/lib/commonjs/a2ui/components/basicComponents.js +975 -0
  28. package/lib/commonjs/a2ui/components/basicComponents.js.map +1 -0
  29. package/lib/commonjs/a2ui/components/iconPaths.js +66 -0
  30. package/lib/commonjs/a2ui/components/iconPaths.js.map +1 -0
  31. package/lib/commonjs/a2ui/components/types.js +4 -0
  32. package/lib/commonjs/a2ui/components/types.js.map +1 -0
  33. package/lib/commonjs/a2ui/index.js +70 -0
  34. package/lib/commonjs/a2ui/index.js.map +1 -0
  35. package/lib/commonjs/a2ui/instructions.js +268 -0
  36. package/lib/commonjs/a2ui/instructions.js.map +1 -0
  37. package/lib/commonjs/a2ui/secrets.js +169 -0
  38. package/lib/commonjs/a2ui/secrets.js.map +1 -0
  39. package/lib/commonjs/a2ui/useReplyState.js +35 -0
  40. package/lib/commonjs/a2ui/useReplyState.js.map +1 -0
  41. package/lib/commonjs/ai-components/AITypingIndicatorView.js +8 -18
  42. package/lib/commonjs/ai-components/AITypingIndicatorView.js.map +1 -1
  43. package/lib/commonjs/ai-components/README.md +55 -3
  44. package/lib/commonjs/ai-components/StreamingMessageView.js +26 -0
  45. package/lib/commonjs/ai-components/StreamingMessageView.js.map +1 -1
  46. package/lib/commonjs/ai-components/charts/components/PieLegend.js +2 -1
  47. package/lib/commonjs/ai-components/charts/components/PieLegend.js.map +1 -1
  48. package/lib/commonjs/ai-components/charts/components/VictoryChart.js +7 -2
  49. package/lib/commonjs/ai-components/charts/components/VictoryChart.js.map +1 -1
  50. package/lib/commonjs/ai-components/charts/utils/format-time-label.js +15 -0
  51. package/lib/commonjs/ai-components/charts/utils/format-time-label.js.map +1 -0
  52. package/lib/commonjs/ai-components/charts/vega-lite/schema.js +17 -2
  53. package/lib/commonjs/ai-components/charts/vega-lite/schema.js.map +1 -1
  54. package/lib/commonjs/ai-components/contexts/composer-context.js +13 -6
  55. package/lib/commonjs/ai-components/contexts/composer-context.js.map +1 -1
  56. package/lib/commonjs/ai-components/contexts/index.js +11 -0
  57. package/lib/commonjs/ai-components/contexts/index.js.map +1 -1
  58. package/lib/commonjs/ai-components/contexts/theme-context.js +33 -2
  59. package/lib/commonjs/ai-components/contexts/theme-context.js.map +1 -1
  60. package/lib/commonjs/ai-components/contexts/translation-context.js +29 -0
  61. package/lib/commonjs/ai-components/contexts/translation-context.js.map +1 -0
  62. package/lib/commonjs/ai-components/conversation/AIConversation.js +366 -0
  63. package/lib/commonjs/ai-components/conversation/AIConversation.js.map +1 -0
  64. package/lib/commonjs/ai-components/conversation/controller/AIAgentError.js +13 -0
  65. package/lib/commonjs/ai-components/conversation/controller/AIAgentError.js.map +1 -0
  66. package/lib/commonjs/ai-components/conversation/controller/AIConversationController.js +425 -0
  67. package/lib/commonjs/ai-components/conversation/controller/AIConversationController.js.map +1 -0
  68. package/lib/commonjs/ai-components/conversation/controller/AIConversationExchange.js +89 -0
  69. package/lib/commonjs/ai-components/conversation/controller/AIConversationExchange.js.map +1 -0
  70. package/lib/commonjs/ai-components/conversation/controller/index.js +48 -0
  71. package/lib/commonjs/ai-components/conversation/controller/index.js.map +1 -0
  72. package/lib/commonjs/ai-components/conversation/controller/offers.js +29 -0
  73. package/lib/commonjs/ai-components/conversation/controller/offers.js.map +1 -0
  74. package/lib/commonjs/ai-components/conversation/controller/types.js +13 -0
  75. package/lib/commonjs/ai-components/conversation/controller/types.js.map +1 -0
  76. package/lib/commonjs/ai-components/conversation/controller/useAIConversationState.js +8 -0
  77. package/lib/commonjs/ai-components/conversation/controller/useAIConversationState.js.map +1 -0
  78. package/lib/commonjs/ai-components/conversation/entries.js +98 -0
  79. package/lib/commonjs/ai-components/conversation/entries.js.map +1 -0
  80. package/lib/commonjs/ai-components/conversation/fromStreamMessage.js +66 -0
  81. package/lib/commonjs/ai-components/conversation/fromStreamMessage.js.map +1 -0
  82. package/lib/commonjs/ai-components/conversation/index.js +48 -0
  83. package/lib/commonjs/ai-components/conversation/index.js.map +1 -0
  84. package/lib/commonjs/ai-components/conversation/list/AIChatList.js +224 -0
  85. package/lib/commonjs/ai-components/conversation/list/AIChatList.js.map +1 -0
  86. package/lib/commonjs/ai-components/conversation/list/useChatListController.js +78 -0
  87. package/lib/commonjs/ai-components/conversation/list/useChatListController.js.map +1 -0
  88. package/lib/commonjs/ai-components/conversation/list/useEndInView.js +51 -0
  89. package/lib/commonjs/ai-components/conversation/list/useEndInView.js.map +1 -0
  90. package/lib/commonjs/ai-components/conversation/list/useKeyboardOverlap.js +54 -0
  91. package/lib/commonjs/ai-components/conversation/list/useKeyboardOverlap.js.map +1 -0
  92. package/lib/commonjs/ai-components/conversation/list/useLoadOlder.js +73 -0
  93. package/lib/commonjs/ai-components/conversation/list/useLoadOlder.js.map +1 -0
  94. package/lib/commonjs/ai-components/conversation/list/usePinnedAnchor.js +84 -0
  95. package/lib/commonjs/ai-components/conversation/list/usePinnedAnchor.js.map +1 -0
  96. package/lib/commonjs/ai-components/conversation/types.js +4 -0
  97. package/lib/commonjs/ai-components/conversation/types.js.map +1 -0
  98. package/lib/commonjs/ai-components/conversation/useConversationRows.js +117 -0
  99. package/lib/commonjs/ai-components/conversation/useConversationRows.js.map +1 -0
  100. package/lib/commonjs/ai-components/generative-ui/AIUIAction.js +30 -0
  101. package/lib/commonjs/ai-components/generative-ui/AIUIAction.js.map +1 -0
  102. package/lib/commonjs/ai-components/generative-ui/AIUIArguments.js +128 -0
  103. package/lib/commonjs/ai-components/generative-ui/AIUIArguments.js.map +1 -0
  104. package/lib/commonjs/ai-components/generative-ui/AIUIComponent.js +50 -0
  105. package/lib/commonjs/ai-components/generative-ui/AIUIComponent.js.map +1 -0
  106. package/lib/commonjs/ai-components/generative-ui/AIUIComponentDefinition.js +134 -0
  107. package/lib/commonjs/ai-components/generative-ui/AIUIComponentDefinition.js.map +1 -0
  108. package/lib/commonjs/ai-components/generative-ui/AIUIDocument.js +32 -0
  109. package/lib/commonjs/ai-components/generative-ui/AIUIDocument.js.map +1 -0
  110. package/lib/commonjs/ai-components/generative-ui/AIUIEntrance.js +193 -0
  111. package/lib/commonjs/ai-components/generative-ui/AIUIEntrance.js.map +1 -0
  112. package/lib/commonjs/ai-components/generative-ui/AIUIErrorBoundary.js +30 -0
  113. package/lib/commonjs/ai-components/generative-ui/AIUIErrorBoundary.js.map +1 -0
  114. package/lib/commonjs/ai-components/generative-ui/AIUIHelpers.js +54 -0
  115. package/lib/commonjs/ai-components/generative-ui/AIUIHelpers.js.map +1 -0
  116. package/lib/commonjs/ai-components/generative-ui/AIUIHost.js +259 -0
  117. package/lib/commonjs/ai-components/generative-ui/AIUIHost.js.map +1 -0
  118. package/lib/commonjs/ai-components/generative-ui/AIUIMedia.js +24 -0
  119. package/lib/commonjs/ai-components/generative-ui/AIUIMedia.js.map +1 -0
  120. package/lib/commonjs/ai-components/generative-ui/AIUIRenderer.js +10 -0
  121. package/lib/commonjs/ai-components/generative-ui/AIUIRenderer.js.map +1 -0
  122. package/lib/commonjs/ai-components/generative-ui/AIUIView.js +57 -0
  123. package/lib/commonjs/ai-components/generative-ui/AIUIView.js.map +1 -0
  124. package/lib/commonjs/ai-components/generative-ui/index.js +181 -0
  125. package/lib/commonjs/ai-components/generative-ui/index.js.map +1 -0
  126. package/lib/commonjs/ai-components/icons.js +44 -0
  127. package/lib/commonjs/ai-components/icons.js.map +1 -0
  128. package/lib/commonjs/ai-components/index.js +96 -0
  129. package/lib/commonjs/ai-components/index.js.map +1 -1
  130. package/lib/commonjs/ai-components/internal/Shimmer.js +36 -0
  131. package/lib/commonjs/ai-components/internal/Shimmer.js.map +1 -0
  132. package/lib/commonjs/ai-components/internal/format.js +66 -0
  133. package/lib/commonjs/ai-components/internal/format.js.map +1 -0
  134. package/lib/commonjs/ai-components/internal/hooks/useBottomSafeArea.js +12 -0
  135. package/lib/commonjs/ai-components/internal/hooks/useBottomSafeArea.js.map +1 -0
  136. package/lib/commonjs/ai-components/internal/icons/AgentIcons.js +74 -0
  137. package/lib/commonjs/ai-components/internal/icons/AgentIcons.js.map +1 -0
  138. package/lib/commonjs/ai-components/internal/tokens.js +11 -0
  139. package/lib/commonjs/ai-components/internal/tokens.js.map +1 -0
  140. package/lib/commonjs/ai-components/internal/uuid.js +21 -0
  141. package/lib/commonjs/ai-components/internal/uuid.js.map +1 -0
  142. package/lib/commonjs/ai-components/markdown/components/CodeBlock.js +4 -2
  143. package/lib/commonjs/ai-components/markdown/components/CodeBlock.js.map +1 -1
  144. package/lib/commonjs/ai-components/markdown/components/InlineCode.js +1 -0
  145. package/lib/commonjs/ai-components/markdown/components/InlineCode.js.map +1 -1
  146. package/lib/commonjs/ai-components/markdown/components/Text.js +11 -2
  147. package/lib/commonjs/ai-components/markdown/components/Text.js.map +1 -1
  148. package/lib/commonjs/ai-components/markdown/styles.js +27 -29
  149. package/lib/commonjs/ai-components/markdown/styles.js.map +1 -1
  150. package/lib/commonjs/ai-components/message/AIAssistantMessage.js +63 -0
  151. package/lib/commonjs/ai-components/message/AIAssistantMessage.js.map +1 -0
  152. package/lib/commonjs/ai-components/message/AIMessageAttachment.js +189 -0
  153. package/lib/commonjs/ai-components/message/AIMessageAttachment.js.map +1 -0
  154. package/lib/commonjs/ai-components/message/AIReplyActions.js +132 -0
  155. package/lib/commonjs/ai-components/message/AIReplyActions.js.map +1 -0
  156. package/lib/commonjs/ai-components/message/AIUserMessage.js +104 -0
  157. package/lib/commonjs/ai-components/message/AIUserMessage.js.map +1 -0
  158. package/lib/commonjs/ai-components/message/UserMessageMenu.js +150 -0
  159. package/lib/commonjs/ai-components/message/UserMessageMenu.js.map +1 -0
  160. package/lib/commonjs/ai-components/message/index.js +48 -0
  161. package/lib/commonjs/ai-components/message/index.js.map +1 -0
  162. package/lib/commonjs/ai-components/message-composer/ActionButton.js +2 -1
  163. package/lib/commonjs/ai-components/message-composer/ActionButton.js.map +1 -1
  164. package/lib/commonjs/ai-components/message-composer/ActionSheet.js +22 -6
  165. package/lib/commonjs/ai-components/message-composer/ActionSheet.js.map +1 -1
  166. package/lib/commonjs/ai-components/message-composer/AttachmentButton.js +5 -1
  167. package/lib/commonjs/ai-components/message-composer/AttachmentButton.js.map +1 -1
  168. package/lib/commonjs/ai-components/message-composer/ChatOptions.js +147 -0
  169. package/lib/commonjs/ai-components/message-composer/ChatOptions.js.map +1 -0
  170. package/lib/commonjs/ai-components/message-composer/ComposerView.js +136 -20
  171. package/lib/commonjs/ai-components/message-composer/ComposerView.js.map +1 -1
  172. package/lib/commonjs/ai-components/message-composer/EditingMessageChip.js +80 -0
  173. package/lib/commonjs/ai-components/message-composer/EditingMessageChip.js.map +1 -0
  174. package/lib/commonjs/ai-components/message-composer/MediaPreviewList.js +26 -1
  175. package/lib/commonjs/ai-components/message-composer/MediaPreviewList.js.map +1 -1
  176. package/lib/commonjs/ai-components/message-composer/index.js +11 -0
  177. package/lib/commonjs/ai-components/message-composer/index.js.map +1 -1
  178. package/lib/commonjs/ai-components/message-parts/AIMessageParts.js +110 -0
  179. package/lib/commonjs/ai-components/message-parts/AIMessageParts.js.map +1 -0
  180. package/lib/commonjs/ai-components/message-parts/AIToolApproval.js +197 -0
  181. package/lib/commonjs/ai-components/message-parts/AIToolApproval.js.map +1 -0
  182. package/lib/commonjs/ai-components/message-parts/AIToolAuthorization.js +250 -0
  183. package/lib/commonjs/ai-components/message-parts/AIToolAuthorization.js.map +1 -0
  184. package/lib/commonjs/ai-components/message-parts/AIToolCall.js +159 -0
  185. package/lib/commonjs/ai-components/message-parts/AIToolCall.js.map +1 -0
  186. package/lib/commonjs/ai-components/message-parts/callStates.js +54 -0
  187. package/lib/commonjs/ai-components/message-parts/callStates.js.map +1 -0
  188. package/lib/commonjs/ai-components/message-parts/index.js +70 -0
  189. package/lib/commonjs/ai-components/message-parts/index.js.map +1 -0
  190. package/lib/commonjs/ai-components/message-parts/parseMessageParts.js +126 -0
  191. package/lib/commonjs/ai-components/message-parts/parseMessageParts.js.map +1 -0
  192. package/lib/commonjs/ai-components/message-parts/types.js +2 -0
  193. package/lib/commonjs/ai-components/message-parts/types.js.map +1 -0
  194. package/lib/commonjs/ai-components/native-specs/NativeAIAgent.js +7 -0
  195. package/lib/commonjs/ai-components/native-specs/NativeAIAgent.js.map +1 -0
  196. package/lib/commonjs/ai-components/reasoning/AIReasoning.js +401 -0
  197. package/lib/commonjs/ai-components/reasoning/AIReasoning.js.map +1 -0
  198. package/lib/commonjs/ai-components/reasoning/index.js +26 -0
  199. package/lib/commonjs/ai-components/reasoning/index.js.map +1 -0
  200. package/lib/commonjs/ai-components/reasoning/thinkingClock.js +63 -0
  201. package/lib/commonjs/ai-components/reasoning/thinkingClock.js.map +1 -0
  202. package/lib/commonjs/ai-components/services/clipboard-service/expo.js +7 -2
  203. package/lib/commonjs/ai-components/services/clipboard-service/expo.js.map +1 -1
  204. package/lib/commonjs/ai-components/store/composer/store.js.map +1 -1
  205. package/lib/commonjs/ai-components/store/composer/useComposerChatOption.js +18 -0
  206. package/lib/commonjs/ai-components/store/composer/useComposerChatOption.js.map +1 -0
  207. package/lib/commonjs/ai-components/store/composer/useComposerIsEditing.js +18 -0
  208. package/lib/commonjs/ai-components/store/composer/useComposerIsEditing.js.map +1 -0
  209. package/lib/commonjs/ai-components/store/index.js +22 -0
  210. package/lib/commonjs/ai-components/store/index.js.map +1 -1
  211. package/lib/commonjs/ai-components/suggestions/AISuggestions.js +66 -0
  212. package/lib/commonjs/ai-components/suggestions/AISuggestions.js.map +1 -0
  213. package/lib/commonjs/ai-components/suggestions/index.js +15 -0
  214. package/lib/commonjs/ai-components/suggestions/index.js.map +1 -0
  215. package/lib/commonjs/ai-components/syntax-highlighting/utils/flatten-nodes.js +2 -1
  216. package/lib/commonjs/ai-components/syntax-highlighting/utils/flatten-nodes.js.map +1 -1
  217. package/lib/commonjs/ai-components/syntax-highlighting/utils/renderer.js +3 -2
  218. package/lib/commonjs/ai-components/syntax-highlighting/utils/renderer.js.map +1 -1
  219. package/lib/commonjs/ai-components/theme.js +137 -1
  220. package/lib/commonjs/ai-components/theme.js.map +1 -1
  221. package/lib/commonjs/ai-components/tool-calls/AIClientIdentity.js +22 -0
  222. package/lib/commonjs/ai-components/tool-calls/AIClientIdentity.js.map +1 -0
  223. package/lib/commonjs/ai-components/tool-calls/AIClientToolRunner.js +109 -0
  224. package/lib/commonjs/ai-components/tool-calls/AIClientToolRunner.js.map +1 -0
  225. package/lib/commonjs/ai-components/tool-calls/AIToolApprover.js +23 -0
  226. package/lib/commonjs/ai-components/tool-calls/AIToolApprover.js.map +1 -0
  227. package/lib/commonjs/ai-components/tool-calls/authSessionAuthorizer.js +88 -0
  228. package/lib/commonjs/ai-components/tool-calls/authSessionAuthorizer.js.map +1 -0
  229. package/lib/commonjs/ai-components/tool-calls/index.js +48 -0
  230. package/lib/commonjs/ai-components/tool-calls/index.js.map +1 -0
  231. package/lib/commonjs/ai-components/voice-visualization/glowShader.js.map +1 -1
  232. package/lib/commonjs/components/Accessibility/useAccessibilityAnnouncer.js.map +1 -1
  233. package/lib/commonjs/components/Message/MessageItemView/utils/renderText.js.map +1 -1
  234. package/lib/commonjs/contexts/translationContext/TranslationContext.js +2 -2
  235. package/lib/commonjs/contexts/translationContext/TranslationContext.js.map +1 -1
  236. package/lib/commonjs/i18n/ar.json +39 -1
  237. package/lib/commonjs/i18n/en.json +39 -1
  238. package/lib/commonjs/i18n/es.json +39 -1
  239. package/lib/commonjs/i18n/fr.json +39 -1
  240. package/lib/commonjs/i18n/he.json +39 -1
  241. package/lib/commonjs/i18n/hi.json +39 -1
  242. package/lib/commonjs/i18n/it.json +39 -1
  243. package/lib/commonjs/i18n/ja.json +39 -1
  244. package/lib/commonjs/i18n/ko.json +39 -1
  245. package/lib/commonjs/i18n/nl.json +39 -1
  246. package/lib/commonjs/i18n/pt-br.json +39 -1
  247. package/lib/commonjs/i18n/ru.json +39 -1
  248. package/lib/commonjs/i18n/tr.json +39 -1
  249. package/lib/commonjs/mock-builders/a2ui/ohrid.js +84 -0
  250. package/lib/commonjs/mock-builders/a2ui/ohrid.js.map +1 -0
  251. package/lib/commonjs/utils/i18n/Streami18n.js +9 -4
  252. package/lib/commonjs/utils/i18n/Streami18n.js.map +1 -1
  253. package/lib/commonjs/utils/i18n/utils.js +7 -0
  254. package/lib/commonjs/utils/i18n/utils.js.map +1 -0
  255. package/lib/commonjs/version.json +1 -1
  256. package/lib/module/a11y/a11yUtils.js.map +1 -1
  257. package/lib/module/a11y/hooks/useResolvedModalAccessibilityProps.js.map +1 -1
  258. package/lib/module/a2ui/A2UIContexts.js +8 -0
  259. package/lib/module/a2ui/A2UIContexts.js.map +1 -0
  260. package/lib/module/a2ui/A2UIInstructions.js +49 -0
  261. package/lib/module/a2ui/A2UIInstructions.js.map +1 -0
  262. package/lib/module/a2ui/A2UIPlaceholder.js +37 -0
  263. package/lib/module/a2ui/A2UIPlaceholder.js.map +1 -0
  264. package/lib/module/a2ui/A2UIRenderer.js +363 -0
  265. package/lib/module/a2ui/A2UIRenderer.js.map +1 -0
  266. package/lib/module/a2ui/A2UIStream.js +91 -0
  267. package/lib/module/a2ui/A2UIStream.js.map +1 -0
  268. package/lib/module/a2ui/A2UISurface.js +212 -0
  269. package/lib/module/a2ui/A2UISurface.js.map +1 -0
  270. package/lib/module/a2ui/basicCatalog.json +1206 -0
  271. package/lib/module/a2ui/basicFunctions.js +398 -0
  272. package/lib/module/a2ui/basicFunctions.js.map +1 -0
  273. package/lib/module/a2ui/catalogDocument.js +80 -0
  274. package/lib/module/a2ui/catalogDocument.js.map +1 -0
  275. package/lib/module/a2ui/catalogs.js +105 -0
  276. package/lib/module/a2ui/catalogs.js.map +1 -0
  277. package/lib/module/a2ui/components/DateTimeInput.js +445 -0
  278. package/lib/module/a2ui/components/DateTimeInput.js.map +1 -0
  279. package/lib/module/a2ui/components/Media.js +316 -0
  280. package/lib/module/a2ui/components/Media.js.map +1 -0
  281. package/lib/module/a2ui/components/basicComponents.js +975 -0
  282. package/lib/module/a2ui/components/basicComponents.js.map +1 -0
  283. package/lib/module/a2ui/components/iconPaths.js +66 -0
  284. package/lib/module/a2ui/components/iconPaths.js.map +1 -0
  285. package/lib/module/a2ui/components/types.js +4 -0
  286. package/lib/module/a2ui/components/types.js.map +1 -0
  287. package/lib/module/a2ui/index.js +70 -0
  288. package/lib/module/a2ui/index.js.map +1 -0
  289. package/lib/module/a2ui/instructions.js +268 -0
  290. package/lib/module/a2ui/instructions.js.map +1 -0
  291. package/lib/module/a2ui/secrets.js +169 -0
  292. package/lib/module/a2ui/secrets.js.map +1 -0
  293. package/lib/module/a2ui/useReplyState.js +35 -0
  294. package/lib/module/a2ui/useReplyState.js.map +1 -0
  295. package/lib/module/ai-components/AITypingIndicatorView.js +8 -18
  296. package/lib/module/ai-components/AITypingIndicatorView.js.map +1 -1
  297. package/lib/module/ai-components/README.md +55 -3
  298. package/lib/module/ai-components/StreamingMessageView.js +26 -0
  299. package/lib/module/ai-components/StreamingMessageView.js.map +1 -1
  300. package/lib/module/ai-components/charts/components/PieLegend.js +2 -1
  301. package/lib/module/ai-components/charts/components/PieLegend.js.map +1 -1
  302. package/lib/module/ai-components/charts/components/VictoryChart.js +7 -2
  303. package/lib/module/ai-components/charts/components/VictoryChart.js.map +1 -1
  304. package/lib/module/ai-components/charts/utils/format-time-label.js +15 -0
  305. package/lib/module/ai-components/charts/utils/format-time-label.js.map +1 -0
  306. package/lib/module/ai-components/charts/vega-lite/schema.js +17 -2
  307. package/lib/module/ai-components/charts/vega-lite/schema.js.map +1 -1
  308. package/lib/module/ai-components/contexts/composer-context.js +13 -6
  309. package/lib/module/ai-components/contexts/composer-context.js.map +1 -1
  310. package/lib/module/ai-components/contexts/index.js +11 -0
  311. package/lib/module/ai-components/contexts/index.js.map +1 -1
  312. package/lib/module/ai-components/contexts/theme-context.js +33 -2
  313. package/lib/module/ai-components/contexts/theme-context.js.map +1 -1
  314. package/lib/module/ai-components/contexts/translation-context.js +29 -0
  315. package/lib/module/ai-components/contexts/translation-context.js.map +1 -0
  316. package/lib/module/ai-components/conversation/AIConversation.js +366 -0
  317. package/lib/module/ai-components/conversation/AIConversation.js.map +1 -0
  318. package/lib/module/ai-components/conversation/controller/AIAgentError.js +13 -0
  319. package/lib/module/ai-components/conversation/controller/AIAgentError.js.map +1 -0
  320. package/lib/module/ai-components/conversation/controller/AIConversationController.js +425 -0
  321. package/lib/module/ai-components/conversation/controller/AIConversationController.js.map +1 -0
  322. package/lib/module/ai-components/conversation/controller/AIConversationExchange.js +89 -0
  323. package/lib/module/ai-components/conversation/controller/AIConversationExchange.js.map +1 -0
  324. package/lib/module/ai-components/conversation/controller/index.js +48 -0
  325. package/lib/module/ai-components/conversation/controller/index.js.map +1 -0
  326. package/lib/module/ai-components/conversation/controller/offers.js +29 -0
  327. package/lib/module/ai-components/conversation/controller/offers.js.map +1 -0
  328. package/lib/module/ai-components/conversation/controller/types.js +13 -0
  329. package/lib/module/ai-components/conversation/controller/types.js.map +1 -0
  330. package/lib/module/ai-components/conversation/controller/useAIConversationState.js +8 -0
  331. package/lib/module/ai-components/conversation/controller/useAIConversationState.js.map +1 -0
  332. package/lib/module/ai-components/conversation/entries.js +98 -0
  333. package/lib/module/ai-components/conversation/entries.js.map +1 -0
  334. package/lib/module/ai-components/conversation/fromStreamMessage.js +66 -0
  335. package/lib/module/ai-components/conversation/fromStreamMessage.js.map +1 -0
  336. package/lib/module/ai-components/conversation/index.js +48 -0
  337. package/lib/module/ai-components/conversation/index.js.map +1 -0
  338. package/lib/module/ai-components/conversation/list/AIChatList.js +224 -0
  339. package/lib/module/ai-components/conversation/list/AIChatList.js.map +1 -0
  340. package/lib/module/ai-components/conversation/list/useChatListController.js +78 -0
  341. package/lib/module/ai-components/conversation/list/useChatListController.js.map +1 -0
  342. package/lib/module/ai-components/conversation/list/useEndInView.js +51 -0
  343. package/lib/module/ai-components/conversation/list/useEndInView.js.map +1 -0
  344. package/lib/module/ai-components/conversation/list/useKeyboardOverlap.js +54 -0
  345. package/lib/module/ai-components/conversation/list/useKeyboardOverlap.js.map +1 -0
  346. package/lib/module/ai-components/conversation/list/useLoadOlder.js +73 -0
  347. package/lib/module/ai-components/conversation/list/useLoadOlder.js.map +1 -0
  348. package/lib/module/ai-components/conversation/list/usePinnedAnchor.js +84 -0
  349. package/lib/module/ai-components/conversation/list/usePinnedAnchor.js.map +1 -0
  350. package/lib/module/ai-components/conversation/types.js +4 -0
  351. package/lib/module/ai-components/conversation/types.js.map +1 -0
  352. package/lib/module/ai-components/conversation/useConversationRows.js +117 -0
  353. package/lib/module/ai-components/conversation/useConversationRows.js.map +1 -0
  354. package/lib/module/ai-components/generative-ui/AIUIAction.js +30 -0
  355. package/lib/module/ai-components/generative-ui/AIUIAction.js.map +1 -0
  356. package/lib/module/ai-components/generative-ui/AIUIArguments.js +128 -0
  357. package/lib/module/ai-components/generative-ui/AIUIArguments.js.map +1 -0
  358. package/lib/module/ai-components/generative-ui/AIUIComponent.js +50 -0
  359. package/lib/module/ai-components/generative-ui/AIUIComponent.js.map +1 -0
  360. package/lib/module/ai-components/generative-ui/AIUIComponentDefinition.js +134 -0
  361. package/lib/module/ai-components/generative-ui/AIUIComponentDefinition.js.map +1 -0
  362. package/lib/module/ai-components/generative-ui/AIUIDocument.js +32 -0
  363. package/lib/module/ai-components/generative-ui/AIUIDocument.js.map +1 -0
  364. package/lib/module/ai-components/generative-ui/AIUIEntrance.js +193 -0
  365. package/lib/module/ai-components/generative-ui/AIUIEntrance.js.map +1 -0
  366. package/lib/module/ai-components/generative-ui/AIUIErrorBoundary.js +30 -0
  367. package/lib/module/ai-components/generative-ui/AIUIErrorBoundary.js.map +1 -0
  368. package/lib/module/ai-components/generative-ui/AIUIHelpers.js +54 -0
  369. package/lib/module/ai-components/generative-ui/AIUIHelpers.js.map +1 -0
  370. package/lib/module/ai-components/generative-ui/AIUIHost.js +259 -0
  371. package/lib/module/ai-components/generative-ui/AIUIHost.js.map +1 -0
  372. package/lib/module/ai-components/generative-ui/AIUIMedia.js +24 -0
  373. package/lib/module/ai-components/generative-ui/AIUIMedia.js.map +1 -0
  374. package/lib/module/ai-components/generative-ui/AIUIRenderer.js +10 -0
  375. package/lib/module/ai-components/generative-ui/AIUIRenderer.js.map +1 -0
  376. package/lib/module/ai-components/generative-ui/AIUIView.js +57 -0
  377. package/lib/module/ai-components/generative-ui/AIUIView.js.map +1 -0
  378. package/lib/module/ai-components/generative-ui/index.js +181 -0
  379. package/lib/module/ai-components/generative-ui/index.js.map +1 -0
  380. package/lib/module/ai-components/icons.js +44 -0
  381. package/lib/module/ai-components/icons.js.map +1 -0
  382. package/lib/module/ai-components/index.js +96 -0
  383. package/lib/module/ai-components/index.js.map +1 -1
  384. package/lib/module/ai-components/internal/Shimmer.js +36 -0
  385. package/lib/module/ai-components/internal/Shimmer.js.map +1 -0
  386. package/lib/module/ai-components/internal/format.js +66 -0
  387. package/lib/module/ai-components/internal/format.js.map +1 -0
  388. package/lib/module/ai-components/internal/hooks/useBottomSafeArea.js +12 -0
  389. package/lib/module/ai-components/internal/hooks/useBottomSafeArea.js.map +1 -0
  390. package/lib/module/ai-components/internal/icons/AgentIcons.js +74 -0
  391. package/lib/module/ai-components/internal/icons/AgentIcons.js.map +1 -0
  392. package/lib/module/ai-components/internal/tokens.js +11 -0
  393. package/lib/module/ai-components/internal/tokens.js.map +1 -0
  394. package/lib/module/ai-components/internal/uuid.js +21 -0
  395. package/lib/module/ai-components/internal/uuid.js.map +1 -0
  396. package/lib/module/ai-components/markdown/components/CodeBlock.js +4 -2
  397. package/lib/module/ai-components/markdown/components/CodeBlock.js.map +1 -1
  398. package/lib/module/ai-components/markdown/components/InlineCode.js +1 -0
  399. package/lib/module/ai-components/markdown/components/InlineCode.js.map +1 -1
  400. package/lib/module/ai-components/markdown/components/Text.js +11 -2
  401. package/lib/module/ai-components/markdown/components/Text.js.map +1 -1
  402. package/lib/module/ai-components/markdown/styles.js +27 -29
  403. package/lib/module/ai-components/markdown/styles.js.map +1 -1
  404. package/lib/module/ai-components/message/AIAssistantMessage.js +63 -0
  405. package/lib/module/ai-components/message/AIAssistantMessage.js.map +1 -0
  406. package/lib/module/ai-components/message/AIMessageAttachment.js +189 -0
  407. package/lib/module/ai-components/message/AIMessageAttachment.js.map +1 -0
  408. package/lib/module/ai-components/message/AIReplyActions.js +132 -0
  409. package/lib/module/ai-components/message/AIReplyActions.js.map +1 -0
  410. package/lib/module/ai-components/message/AIUserMessage.js +104 -0
  411. package/lib/module/ai-components/message/AIUserMessage.js.map +1 -0
  412. package/lib/module/ai-components/message/UserMessageMenu.js +150 -0
  413. package/lib/module/ai-components/message/UserMessageMenu.js.map +1 -0
  414. package/lib/module/ai-components/message/index.js +48 -0
  415. package/lib/module/ai-components/message/index.js.map +1 -0
  416. package/lib/module/ai-components/message-composer/ActionButton.js +2 -1
  417. package/lib/module/ai-components/message-composer/ActionButton.js.map +1 -1
  418. package/lib/module/ai-components/message-composer/ActionSheet.js +22 -6
  419. package/lib/module/ai-components/message-composer/ActionSheet.js.map +1 -1
  420. package/lib/module/ai-components/message-composer/AttachmentButton.js +5 -1
  421. package/lib/module/ai-components/message-composer/AttachmentButton.js.map +1 -1
  422. package/lib/module/ai-components/message-composer/ChatOptions.js +147 -0
  423. package/lib/module/ai-components/message-composer/ChatOptions.js.map +1 -0
  424. package/lib/module/ai-components/message-composer/ComposerView.js +136 -20
  425. package/lib/module/ai-components/message-composer/ComposerView.js.map +1 -1
  426. package/lib/module/ai-components/message-composer/EditingMessageChip.js +80 -0
  427. package/lib/module/ai-components/message-composer/EditingMessageChip.js.map +1 -0
  428. package/lib/module/ai-components/message-composer/MediaPreviewList.js +26 -1
  429. package/lib/module/ai-components/message-composer/MediaPreviewList.js.map +1 -1
  430. package/lib/module/ai-components/message-composer/index.js +11 -0
  431. package/lib/module/ai-components/message-composer/index.js.map +1 -1
  432. package/lib/module/ai-components/message-parts/AIMessageParts.js +110 -0
  433. package/lib/module/ai-components/message-parts/AIMessageParts.js.map +1 -0
  434. package/lib/module/ai-components/message-parts/AIToolApproval.js +197 -0
  435. package/lib/module/ai-components/message-parts/AIToolApproval.js.map +1 -0
  436. package/lib/module/ai-components/message-parts/AIToolAuthorization.js +250 -0
  437. package/lib/module/ai-components/message-parts/AIToolAuthorization.js.map +1 -0
  438. package/lib/module/ai-components/message-parts/AIToolCall.js +159 -0
  439. package/lib/module/ai-components/message-parts/AIToolCall.js.map +1 -0
  440. package/lib/module/ai-components/message-parts/callStates.js +54 -0
  441. package/lib/module/ai-components/message-parts/callStates.js.map +1 -0
  442. package/lib/module/ai-components/message-parts/index.js +70 -0
  443. package/lib/module/ai-components/message-parts/index.js.map +1 -0
  444. package/lib/module/ai-components/message-parts/parseMessageParts.js +126 -0
  445. package/lib/module/ai-components/message-parts/parseMessageParts.js.map +1 -0
  446. package/lib/module/ai-components/message-parts/types.js +2 -0
  447. package/lib/module/ai-components/message-parts/types.js.map +1 -0
  448. package/lib/module/ai-components/native-specs/NativeAIAgent.js +7 -0
  449. package/lib/module/ai-components/native-specs/NativeAIAgent.js.map +1 -0
  450. package/lib/module/ai-components/reasoning/AIReasoning.js +401 -0
  451. package/lib/module/ai-components/reasoning/AIReasoning.js.map +1 -0
  452. package/lib/module/ai-components/reasoning/index.js +26 -0
  453. package/lib/module/ai-components/reasoning/index.js.map +1 -0
  454. package/lib/module/ai-components/reasoning/thinkingClock.js +63 -0
  455. package/lib/module/ai-components/reasoning/thinkingClock.js.map +1 -0
  456. package/lib/module/ai-components/services/clipboard-service/expo.js +7 -2
  457. package/lib/module/ai-components/services/clipboard-service/expo.js.map +1 -1
  458. package/lib/module/ai-components/store/composer/store.js.map +1 -1
  459. package/lib/module/ai-components/store/composer/useComposerChatOption.js +18 -0
  460. package/lib/module/ai-components/store/composer/useComposerChatOption.js.map +1 -0
  461. package/lib/module/ai-components/store/composer/useComposerIsEditing.js +18 -0
  462. package/lib/module/ai-components/store/composer/useComposerIsEditing.js.map +1 -0
  463. package/lib/module/ai-components/store/index.js +22 -0
  464. package/lib/module/ai-components/store/index.js.map +1 -1
  465. package/lib/module/ai-components/suggestions/AISuggestions.js +66 -0
  466. package/lib/module/ai-components/suggestions/AISuggestions.js.map +1 -0
  467. package/lib/module/ai-components/suggestions/index.js +15 -0
  468. package/lib/module/ai-components/suggestions/index.js.map +1 -0
  469. package/lib/module/ai-components/syntax-highlighting/utils/flatten-nodes.js +2 -1
  470. package/lib/module/ai-components/syntax-highlighting/utils/flatten-nodes.js.map +1 -1
  471. package/lib/module/ai-components/syntax-highlighting/utils/renderer.js +3 -2
  472. package/lib/module/ai-components/syntax-highlighting/utils/renderer.js.map +1 -1
  473. package/lib/module/ai-components/theme.js +137 -1
  474. package/lib/module/ai-components/theme.js.map +1 -1
  475. package/lib/module/ai-components/tool-calls/AIClientIdentity.js +22 -0
  476. package/lib/module/ai-components/tool-calls/AIClientIdentity.js.map +1 -0
  477. package/lib/module/ai-components/tool-calls/AIClientToolRunner.js +109 -0
  478. package/lib/module/ai-components/tool-calls/AIClientToolRunner.js.map +1 -0
  479. package/lib/module/ai-components/tool-calls/AIToolApprover.js +23 -0
  480. package/lib/module/ai-components/tool-calls/AIToolApprover.js.map +1 -0
  481. package/lib/module/ai-components/tool-calls/authSessionAuthorizer.js +88 -0
  482. package/lib/module/ai-components/tool-calls/authSessionAuthorizer.js.map +1 -0
  483. package/lib/module/ai-components/tool-calls/index.js +48 -0
  484. package/lib/module/ai-components/tool-calls/index.js.map +1 -0
  485. package/lib/module/ai-components/voice-visualization/glowShader.js.map +1 -1
  486. package/lib/module/components/Accessibility/useAccessibilityAnnouncer.js.map +1 -1
  487. package/lib/module/components/Message/MessageItemView/utils/renderText.js.map +1 -1
  488. package/lib/module/contexts/translationContext/TranslationContext.js +2 -2
  489. package/lib/module/contexts/translationContext/TranslationContext.js.map +1 -1
  490. package/lib/module/i18n/ar.json +39 -1
  491. package/lib/module/i18n/en.json +39 -1
  492. package/lib/module/i18n/es.json +39 -1
  493. package/lib/module/i18n/fr.json +39 -1
  494. package/lib/module/i18n/he.json +39 -1
  495. package/lib/module/i18n/hi.json +39 -1
  496. package/lib/module/i18n/it.json +39 -1
  497. package/lib/module/i18n/ja.json +39 -1
  498. package/lib/module/i18n/ko.json +39 -1
  499. package/lib/module/i18n/nl.json +39 -1
  500. package/lib/module/i18n/pt-br.json +39 -1
  501. package/lib/module/i18n/ru.json +39 -1
  502. package/lib/module/i18n/tr.json +39 -1
  503. package/lib/module/mock-builders/a2ui/ohrid.js +84 -0
  504. package/lib/module/mock-builders/a2ui/ohrid.js.map +1 -0
  505. package/lib/module/utils/i18n/Streami18n.js +9 -4
  506. package/lib/module/utils/i18n/Streami18n.js.map +1 -1
  507. package/lib/module/utils/i18n/utils.js +7 -0
  508. package/lib/module/utils/i18n/utils.js.map +1 -0
  509. package/lib/module/version.json +1 -1
  510. package/lib/typescript/a11y/a11yUtils.d.ts +0 -1
  511. package/lib/typescript/a11y/a11yUtils.d.ts.map +1 -1
  512. package/lib/typescript/a11y/hooks/useResolvedModalAccessibilityProps.d.ts +2 -3
  513. package/lib/typescript/a11y/hooks/useResolvedModalAccessibilityProps.d.ts.map +1 -1
  514. package/lib/typescript/a2ui/A2UIContexts.d.ts +9 -0
  515. package/lib/typescript/a2ui/A2UIContexts.d.ts.map +1 -0
  516. package/lib/typescript/a2ui/A2UIInstructions.d.ts +47 -0
  517. package/lib/typescript/a2ui/A2UIInstructions.d.ts.map +1 -0
  518. package/lib/typescript/a2ui/A2UIPlaceholder.d.ts +4 -0
  519. package/lib/typescript/a2ui/A2UIPlaceholder.d.ts.map +1 -0
  520. package/lib/typescript/a2ui/A2UIRenderer.d.ts +130 -0
  521. package/lib/typescript/a2ui/A2UIRenderer.d.ts.map +1 -0
  522. package/lib/typescript/a2ui/A2UIStream.d.ts +23 -0
  523. package/lib/typescript/a2ui/A2UIStream.d.ts.map +1 -0
  524. package/lib/typescript/a2ui/A2UISurface.d.ts +19 -0
  525. package/lib/typescript/a2ui/A2UISurface.d.ts.map +1 -0
  526. package/lib/typescript/a2ui/basicFunctions.d.ts +10 -0
  527. package/lib/typescript/a2ui/basicFunctions.d.ts.map +1 -0
  528. package/lib/typescript/a2ui/catalogDocument.d.ts +56 -0
  529. package/lib/typescript/a2ui/catalogDocument.d.ts.map +1 -0
  530. package/lib/typescript/a2ui/catalogs.d.ts +42 -0
  531. package/lib/typescript/a2ui/catalogs.d.ts.map +1 -0
  532. package/lib/typescript/a2ui/components/DateTimeInput.d.ts +44 -0
  533. package/lib/typescript/a2ui/components/DateTimeInput.d.ts.map +1 -0
  534. package/lib/typescript/a2ui/components/Media.d.ts +27 -0
  535. package/lib/typescript/a2ui/components/Media.d.ts.map +1 -0
  536. package/lib/typescript/a2ui/components/basicComponents.d.ts +4 -0
  537. package/lib/typescript/a2ui/components/basicComponents.d.ts.map +1 -0
  538. package/lib/typescript/a2ui/components/iconPaths.d.ts +2 -0
  539. package/lib/typescript/a2ui/components/iconPaths.d.ts.map +1 -0
  540. package/lib/typescript/a2ui/components/types.d.ts +23 -0
  541. package/lib/typescript/a2ui/components/types.d.ts.map +1 -0
  542. package/lib/typescript/a2ui/index.d.ts +8 -0
  543. package/lib/typescript/a2ui/index.d.ts.map +1 -0
  544. package/lib/typescript/a2ui/instructions.d.ts +39 -0
  545. package/lib/typescript/a2ui/instructions.d.ts.map +1 -0
  546. package/lib/typescript/a2ui/secrets.d.ts +20 -0
  547. package/lib/typescript/a2ui/secrets.d.ts.map +1 -0
  548. package/lib/typescript/a2ui/useReplyState.d.ts +4 -0
  549. package/lib/typescript/a2ui/useReplyState.d.ts.map +1 -0
  550. package/lib/typescript/ai-components/AITypingIndicatorView.d.ts.map +1 -1
  551. package/lib/typescript/ai-components/StreamingMessageView.d.ts +4 -1
  552. package/lib/typescript/ai-components/StreamingMessageView.d.ts.map +1 -1
  553. package/lib/typescript/ai-components/charts/components/PieLegend.d.ts.map +1 -1
  554. package/lib/typescript/ai-components/charts/components/VictoryChart.d.ts.map +1 -1
  555. package/lib/typescript/ai-components/charts/utils/format-time-label.d.ts +7 -0
  556. package/lib/typescript/ai-components/charts/utils/format-time-label.d.ts.map +1 -0
  557. package/lib/typescript/ai-components/charts/vega-lite/schema.d.ts +8 -8
  558. package/lib/typescript/ai-components/charts/vega-lite/schema.d.ts.map +1 -1
  559. package/lib/typescript/ai-components/contexts/composer-context.d.ts +3 -0
  560. package/lib/typescript/ai-components/contexts/composer-context.d.ts.map +1 -1
  561. package/lib/typescript/ai-components/contexts/index.d.ts +1 -0
  562. package/lib/typescript/ai-components/contexts/index.d.ts.map +1 -1
  563. package/lib/typescript/ai-components/contexts/theme-context.d.ts.map +1 -1
  564. package/lib/typescript/ai-components/contexts/translation-context.d.ts +19 -0
  565. package/lib/typescript/ai-components/contexts/translation-context.d.ts.map +1 -0
  566. package/lib/typescript/ai-components/conversation/AIConversation.d.ts +130 -0
  567. package/lib/typescript/ai-components/conversation/AIConversation.d.ts.map +1 -0
  568. package/lib/typescript/ai-components/conversation/controller/AIAgentError.d.ts +10 -0
  569. package/lib/typescript/ai-components/conversation/controller/AIAgentError.d.ts.map +1 -0
  570. package/lib/typescript/ai-components/conversation/controller/AIConversationController.d.ts +173 -0
  571. package/lib/typescript/ai-components/conversation/controller/AIConversationController.d.ts.map +1 -0
  572. package/lib/typescript/ai-components/conversation/controller/AIConversationExchange.d.ts +23 -0
  573. package/lib/typescript/ai-components/conversation/controller/AIConversationExchange.d.ts.map +1 -0
  574. package/lib/typescript/ai-components/conversation/controller/index.d.ts +5 -0
  575. package/lib/typescript/ai-components/conversation/controller/index.d.ts.map +1 -0
  576. package/lib/typescript/ai-components/conversation/controller/offers.d.ts +23 -0
  577. package/lib/typescript/ai-components/conversation/controller/offers.d.ts.map +1 -0
  578. package/lib/typescript/ai-components/conversation/controller/types.d.ts +142 -0
  579. package/lib/typescript/ai-components/conversation/controller/types.d.ts.map +1 -0
  580. package/lib/typescript/ai-components/conversation/controller/useAIConversationState.d.ts +9 -0
  581. package/lib/typescript/ai-components/conversation/controller/useAIConversationState.d.ts.map +1 -0
  582. package/lib/typescript/ai-components/conversation/entries.d.ts +47 -0
  583. package/lib/typescript/ai-components/conversation/entries.d.ts.map +1 -0
  584. package/lib/typescript/ai-components/conversation/fromStreamMessage.d.ts +25 -0
  585. package/lib/typescript/ai-components/conversation/fromStreamMessage.d.ts.map +1 -0
  586. package/lib/typescript/ai-components/conversation/index.d.ts +6 -0
  587. package/lib/typescript/ai-components/conversation/index.d.ts.map +1 -0
  588. package/lib/typescript/ai-components/conversation/list/AIChatList.d.ts +30 -0
  589. package/lib/typescript/ai-components/conversation/list/AIChatList.d.ts.map +1 -0
  590. package/lib/typescript/ai-components/conversation/list/useChatListController.d.ts +45 -0
  591. package/lib/typescript/ai-components/conversation/list/useChatListController.d.ts.map +1 -0
  592. package/lib/typescript/ai-components/conversation/list/useEndInView.d.ts +26 -0
  593. package/lib/typescript/ai-components/conversation/list/useEndInView.d.ts.map +1 -0
  594. package/lib/typescript/ai-components/conversation/list/useKeyboardOverlap.d.ts +17 -0
  595. package/lib/typescript/ai-components/conversation/list/useKeyboardOverlap.d.ts.map +1 -0
  596. package/lib/typescript/ai-components/conversation/list/useLoadOlder.d.ts +24 -0
  597. package/lib/typescript/ai-components/conversation/list/useLoadOlder.d.ts.map +1 -0
  598. package/lib/typescript/ai-components/conversation/list/usePinnedAnchor.d.ts +26 -0
  599. package/lib/typescript/ai-components/conversation/list/usePinnedAnchor.d.ts.map +1 -0
  600. package/lib/typescript/ai-components/conversation/types.d.ts +55 -0
  601. package/lib/typescript/ai-components/conversation/types.d.ts.map +1 -0
  602. package/lib/typescript/ai-components/conversation/useConversationRows.d.ts +66 -0
  603. package/lib/typescript/ai-components/conversation/useConversationRows.d.ts.map +1 -0
  604. package/lib/typescript/ai-components/generative-ui/AIUIAction.d.ts +48 -0
  605. package/lib/typescript/ai-components/generative-ui/AIUIAction.d.ts.map +1 -0
  606. package/lib/typescript/ai-components/generative-ui/AIUIArguments.d.ts +15 -0
  607. package/lib/typescript/ai-components/generative-ui/AIUIArguments.d.ts.map +1 -0
  608. package/lib/typescript/ai-components/generative-ui/AIUIComponent.d.ts +51 -0
  609. package/lib/typescript/ai-components/generative-ui/AIUIComponent.d.ts.map +1 -0
  610. package/lib/typescript/ai-components/generative-ui/AIUIComponentDefinition.d.ts +84 -0
  611. package/lib/typescript/ai-components/generative-ui/AIUIComponentDefinition.d.ts.map +1 -0
  612. package/lib/typescript/ai-components/generative-ui/AIUIDocument.d.ts +50 -0
  613. package/lib/typescript/ai-components/generative-ui/AIUIDocument.d.ts.map +1 -0
  614. package/lib/typescript/ai-components/generative-ui/AIUIEntrance.d.ts +75 -0
  615. package/lib/typescript/ai-components/generative-ui/AIUIEntrance.d.ts.map +1 -0
  616. package/lib/typescript/ai-components/generative-ui/AIUIErrorBoundary.d.ts +26 -0
  617. package/lib/typescript/ai-components/generative-ui/AIUIErrorBoundary.d.ts.map +1 -0
  618. package/lib/typescript/ai-components/generative-ui/AIUIHelpers.d.ts +23 -0
  619. package/lib/typescript/ai-components/generative-ui/AIUIHelpers.d.ts.map +1 -0
  620. package/lib/typescript/ai-components/generative-ui/AIUIHost.d.ts +160 -0
  621. package/lib/typescript/ai-components/generative-ui/AIUIHost.d.ts.map +1 -0
  622. package/lib/typescript/ai-components/generative-ui/AIUIMedia.d.ts +22 -0
  623. package/lib/typescript/ai-components/generative-ui/AIUIMedia.d.ts.map +1 -0
  624. package/lib/typescript/ai-components/generative-ui/AIUIRenderer.d.ts +53 -0
  625. package/lib/typescript/ai-components/generative-ui/AIUIRenderer.d.ts.map +1 -0
  626. package/lib/typescript/ai-components/generative-ui/AIUIView.d.ts +35 -0
  627. package/lib/typescript/ai-components/generative-ui/AIUIView.d.ts.map +1 -0
  628. package/lib/typescript/ai-components/generative-ui/index.d.ts +11 -0
  629. package/lib/typescript/ai-components/generative-ui/index.d.ts.map +1 -0
  630. package/lib/typescript/ai-components/icons.d.ts +66 -0
  631. package/lib/typescript/ai-components/icons.d.ts.map +1 -0
  632. package/lib/typescript/ai-components/index.d.ts +8 -0
  633. package/lib/typescript/ai-components/index.d.ts.map +1 -1
  634. package/lib/typescript/ai-components/internal/Shimmer.d.ts +14 -0
  635. package/lib/typescript/ai-components/internal/Shimmer.d.ts.map +1 -0
  636. package/lib/typescript/ai-components/internal/format.d.ts +7 -0
  637. package/lib/typescript/ai-components/internal/format.d.ts.map +1 -0
  638. package/lib/typescript/ai-components/internal/hooks/useBottomSafeArea.d.ts +3 -0
  639. package/lib/typescript/ai-components/internal/hooks/useBottomSafeArea.d.ts.map +1 -0
  640. package/lib/typescript/ai-components/internal/icons/AgentIcons.d.ts +31 -0
  641. package/lib/typescript/ai-components/internal/icons/AgentIcons.d.ts.map +1 -0
  642. package/lib/typescript/ai-components/internal/tokens.d.ts +2 -0
  643. package/lib/typescript/ai-components/internal/tokens.d.ts.map +1 -0
  644. package/lib/typescript/ai-components/internal/uuid.d.ts +2 -0
  645. package/lib/typescript/ai-components/internal/uuid.d.ts.map +1 -0
  646. package/lib/typescript/ai-components/markdown/components/CodeBlock.d.ts.map +1 -1
  647. package/lib/typescript/ai-components/markdown/components/InlineCode.d.ts.map +1 -1
  648. package/lib/typescript/ai-components/markdown/components/Text.d.ts.map +1 -1
  649. package/lib/typescript/ai-components/markdown/styles.d.ts +23 -4
  650. package/lib/typescript/ai-components/markdown/styles.d.ts.map +1 -1
  651. package/lib/typescript/ai-components/markdown/types.d.ts +1 -0
  652. package/lib/typescript/ai-components/markdown/types.d.ts.map +1 -1
  653. package/lib/typescript/ai-components/message/AIAssistantMessage.d.ts +40 -0
  654. package/lib/typescript/ai-components/message/AIAssistantMessage.d.ts.map +1 -0
  655. package/lib/typescript/ai-components/message/AIMessageAttachment.d.ts +33 -0
  656. package/lib/typescript/ai-components/message/AIMessageAttachment.d.ts.map +1 -0
  657. package/lib/typescript/ai-components/message/AIReplyActions.d.ts +19 -0
  658. package/lib/typescript/ai-components/message/AIReplyActions.d.ts.map +1 -0
  659. package/lib/typescript/ai-components/message/AIUserMessage.d.ts +17 -0
  660. package/lib/typescript/ai-components/message/AIUserMessage.d.ts.map +1 -0
  661. package/lib/typescript/ai-components/message/UserMessageMenu.d.ts +31 -0
  662. package/lib/typescript/ai-components/message/UserMessageMenu.d.ts.map +1 -0
  663. package/lib/typescript/ai-components/message/index.d.ts +5 -0
  664. package/lib/typescript/ai-components/message/index.d.ts.map +1 -0
  665. package/lib/typescript/ai-components/message-composer/ActionButton.d.ts.map +1 -1
  666. package/lib/typescript/ai-components/message-composer/ActionSheet.d.ts +2 -2
  667. package/lib/typescript/ai-components/message-composer/ActionSheet.d.ts.map +1 -1
  668. package/lib/typescript/ai-components/message-composer/AttachmentButton.d.ts.map +1 -1
  669. package/lib/typescript/ai-components/message-composer/ChatOptions.d.ts +35 -0
  670. package/lib/typescript/ai-components/message-composer/ChatOptions.d.ts.map +1 -0
  671. package/lib/typescript/ai-components/message-composer/ComposerView.d.ts +34 -3
  672. package/lib/typescript/ai-components/message-composer/ComposerView.d.ts.map +1 -1
  673. package/lib/typescript/ai-components/message-composer/EditingMessageChip.d.ts +7 -0
  674. package/lib/typescript/ai-components/message-composer/EditingMessageChip.d.ts.map +1 -0
  675. package/lib/typescript/ai-components/message-composer/MediaPreviewList.d.ts.map +1 -1
  676. package/lib/typescript/ai-components/message-composer/index.d.ts +1 -0
  677. package/lib/typescript/ai-components/message-composer/index.d.ts.map +1 -1
  678. package/lib/typescript/ai-components/message-parts/AIMessageParts.d.ts +38 -0
  679. package/lib/typescript/ai-components/message-parts/AIMessageParts.d.ts.map +1 -0
  680. package/lib/typescript/ai-components/message-parts/AIToolApproval.d.ts +38 -0
  681. package/lib/typescript/ai-components/message-parts/AIToolApproval.d.ts.map +1 -0
  682. package/lib/typescript/ai-components/message-parts/AIToolAuthorization.d.ts +48 -0
  683. package/lib/typescript/ai-components/message-parts/AIToolAuthorization.d.ts.map +1 -0
  684. package/lib/typescript/ai-components/message-parts/AIToolCall.d.ts +11 -0
  685. package/lib/typescript/ai-components/message-parts/AIToolCall.d.ts.map +1 -0
  686. package/lib/typescript/ai-components/message-parts/callStates.d.ts +9 -0
  687. package/lib/typescript/ai-components/message-parts/callStates.d.ts.map +1 -0
  688. package/lib/typescript/ai-components/message-parts/index.d.ts +7 -0
  689. package/lib/typescript/ai-components/message-parts/index.d.ts.map +1 -0
  690. package/lib/typescript/ai-components/message-parts/parseMessageParts.d.ts +47 -0
  691. package/lib/typescript/ai-components/message-parts/parseMessageParts.d.ts.map +1 -0
  692. package/lib/typescript/ai-components/message-parts/types.d.ts +137 -0
  693. package/lib/typescript/ai-components/message-parts/types.d.ts.map +1 -0
  694. package/lib/typescript/ai-components/native-specs/NativeAIAgent.d.ts +22 -0
  695. package/lib/typescript/ai-components/native-specs/NativeAIAgent.d.ts.map +1 -0
  696. package/lib/typescript/ai-components/reasoning/AIReasoning.d.ts +63 -0
  697. package/lib/typescript/ai-components/reasoning/AIReasoning.d.ts.map +1 -0
  698. package/lib/typescript/ai-components/reasoning/index.d.ts +3 -0
  699. package/lib/typescript/ai-components/reasoning/index.d.ts.map +1 -0
  700. package/lib/typescript/ai-components/reasoning/thinkingClock.d.ts +29 -0
  701. package/lib/typescript/ai-components/reasoning/thinkingClock.d.ts.map +1 -0
  702. package/lib/typescript/ai-components/services/clipboard-service/expo.d.ts.map +1 -1
  703. package/lib/typescript/ai-components/store/composer/store.d.ts +8 -0
  704. package/lib/typescript/ai-components/store/composer/store.d.ts.map +1 -1
  705. package/lib/typescript/ai-components/store/composer/useComposerChatOption.d.ts +5 -0
  706. package/lib/typescript/ai-components/store/composer/useComposerChatOption.d.ts.map +1 -0
  707. package/lib/typescript/ai-components/store/composer/useComposerIsEditing.d.ts +5 -0
  708. package/lib/typescript/ai-components/store/composer/useComposerIsEditing.d.ts.map +1 -0
  709. package/lib/typescript/ai-components/store/index.d.ts +2 -0
  710. package/lib/typescript/ai-components/store/index.d.ts.map +1 -1
  711. package/lib/typescript/ai-components/suggestions/AISuggestions.d.ts +13 -0
  712. package/lib/typescript/ai-components/suggestions/AISuggestions.d.ts.map +1 -0
  713. package/lib/typescript/ai-components/suggestions/index.d.ts +2 -0
  714. package/lib/typescript/ai-components/suggestions/index.d.ts.map +1 -0
  715. package/lib/typescript/ai-components/syntax-highlighting/utils/flatten-nodes.d.ts.map +1 -1
  716. package/lib/typescript/ai-components/syntax-highlighting/utils/renderer.d.ts +1 -1
  717. package/lib/typescript/ai-components/syntax-highlighting/utils/renderer.d.ts.map +1 -1
  718. package/lib/typescript/ai-components/theme.d.ts +200 -0
  719. package/lib/typescript/ai-components/theme.d.ts.map +1 -1
  720. package/lib/typescript/ai-components/tool-calls/AIClientIdentity.d.ts +14 -0
  721. package/lib/typescript/ai-components/tool-calls/AIClientIdentity.d.ts.map +1 -0
  722. package/lib/typescript/ai-components/tool-calls/AIClientToolRunner.d.ts +109 -0
  723. package/lib/typescript/ai-components/tool-calls/AIClientToolRunner.d.ts.map +1 -0
  724. package/lib/typescript/ai-components/tool-calls/AIToolApprover.d.ts +48 -0
  725. package/lib/typescript/ai-components/tool-calls/AIToolApprover.d.ts.map +1 -0
  726. package/lib/typescript/ai-components/tool-calls/authSessionAuthorizer.d.ts +57 -0
  727. package/lib/typescript/ai-components/tool-calls/authSessionAuthorizer.d.ts.map +1 -0
  728. package/lib/typescript/ai-components/tool-calls/index.d.ts +5 -0
  729. package/lib/typescript/ai-components/tool-calls/index.d.ts.map +1 -0
  730. package/lib/typescript/ai-components/voice-visualization/glowShader.d.ts.map +1 -1
  731. package/lib/typescript/ai-components/voice-visualization/simplex.d.ts +1 -1
  732. package/lib/typescript/components/Accessibility/NotificationAnnouncer.d.ts +5 -5
  733. package/lib/typescript/components/Accessibility/hooks/useIncomingMessageAnnouncements.d.ts +1 -1
  734. package/lib/typescript/components/Accessibility/useAccessibilityAnnouncer.d.ts +0 -2
  735. package/lib/typescript/components/Accessibility/useAccessibilityAnnouncer.d.ts.map +1 -1
  736. package/lib/typescript/components/Message/MessageItemView/utils/renderText.d.ts.map +1 -1
  737. package/lib/typescript/contexts/translationContext/TranslationContext.d.ts +1 -1
  738. package/lib/typescript/contexts/translationContext/TranslationContext.d.ts.map +1 -1
  739. package/lib/typescript/i18n/ar.json +39 -1
  740. package/lib/typescript/i18n/en.json +39 -1
  741. package/lib/typescript/i18n/es.json +39 -1
  742. package/lib/typescript/i18n/fr.json +39 -1
  743. package/lib/typescript/i18n/he.json +39 -1
  744. package/lib/typescript/i18n/hi.json +39 -1
  745. package/lib/typescript/i18n/it.json +39 -1
  746. package/lib/typescript/i18n/ja.json +39 -1
  747. package/lib/typescript/i18n/ko.json +39 -1
  748. package/lib/typescript/i18n/nl.json +39 -1
  749. package/lib/typescript/i18n/pt-br.json +39 -1
  750. package/lib/typescript/i18n/ru.json +39 -1
  751. package/lib/typescript/i18n/tr.json +39 -1
  752. package/lib/typescript/utils/i18n/Streami18n.d.ts +40 -2
  753. package/lib/typescript/utils/i18n/Streami18n.d.ts.map +1 -1
  754. package/lib/typescript/utils/i18n/utils.d.ts +3 -0
  755. package/lib/typescript/utils/i18n/utils.d.ts.map +1 -0
  756. package/package.json +10 -2
  757. package/src/a11y/a11yUtils.ts +0 -1
  758. package/src/a11y/hooks/useResolvedModalAccessibilityProps.ts +2 -3
  759. package/src/a2ui/A2UIContexts.ts +15 -0
  760. package/src/a2ui/A2UIInstructions.ts +102 -0
  761. package/src/a2ui/A2UIPlaceholder.tsx +24 -0
  762. package/src/a2ui/A2UIRenderer.tsx +504 -0
  763. package/src/a2ui/A2UIStream.ts +105 -0
  764. package/src/a2ui/A2UISurface.tsx +241 -0
  765. package/src/a2ui/__tests__/A2UIInstructions.test.ts +233 -0
  766. package/src/a2ui/__tests__/A2UIRenderer.test.tsx +1514 -0
  767. package/src/a2ui/__tests__/conformance.test.ts +579 -0
  768. package/src/a2ui/basicCatalog.json +1206 -0
  769. package/src/a2ui/basicFunctions.ts +429 -0
  770. package/src/a2ui/catalogDocument.ts +119 -0
  771. package/src/a2ui/catalogs.ts +185 -0
  772. package/src/a2ui/components/DateTimeInput.tsx +381 -0
  773. package/src/a2ui/components/Media.tsx +278 -0
  774. package/src/a2ui/components/basicComponents.tsx +832 -0
  775. package/src/a2ui/components/iconPaths.ts +116 -0
  776. package/src/a2ui/components/types.ts +24 -0
  777. package/src/a2ui/index.ts +18 -0
  778. package/src/a2ui/instructions.ts +341 -0
  779. package/src/a2ui/secrets.ts +154 -0
  780. package/src/a2ui/useReplyState.ts +26 -0
  781. package/src/ai-components/AITypingIndicatorView.tsx +6 -22
  782. package/src/ai-components/README.md +55 -3
  783. package/src/ai-components/StreamingMessageView.tsx +27 -3
  784. package/src/ai-components/__tests__/ComposerView.test.tsx +227 -2
  785. package/src/ai-components/__tests__/Markdown.test.tsx +13 -0
  786. package/src/ai-components/__tests__/StreamingMessageView.test.tsx +69 -0
  787. package/src/ai-components/__tests__/charts.test.ts +75 -0
  788. package/src/ai-components/__tests__/clientToolRunner.test.ts +239 -0
  789. package/src/ai-components/__tests__/conversation.test.tsx +705 -0
  790. package/src/ai-components/__tests__/conversationController.test.ts +1561 -0
  791. package/src/ai-components/__tests__/conversationExchange.test.ts +187 -0
  792. package/src/ai-components/__tests__/conversationRows.test.ts +207 -0
  793. package/src/ai-components/__tests__/conversationUI.test.tsx +586 -0
  794. package/src/ai-components/__tests__/format.test.ts +32 -0
  795. package/src/ai-components/__tests__/generativeUI.test.ts +634 -0
  796. package/src/ai-components/__tests__/keyboard.test.ts +14 -0
  797. package/src/ai-components/__tests__/messagePartViews.test.tsx +343 -0
  798. package/src/ai-components/__tests__/messageParts.test.ts +336 -0
  799. package/src/ai-components/__tests__/messageViews.test.tsx +321 -0
  800. package/src/ai-components/__tests__/signIn.test.tsx +324 -0
  801. package/src/ai-components/__tests__/theme.test.tsx +18 -0
  802. package/src/ai-components/__tests__/translation.test.tsx +85 -0
  803. package/src/ai-components/__tests__/voiceDriver.test.ts +0 -3
  804. package/src/ai-components/__tests__/voicePalette.test.ts +1 -1
  805. package/src/ai-components/charts/components/PieLegend.tsx +2 -1
  806. package/src/ai-components/charts/components/VictoryChart.tsx +10 -2
  807. package/src/ai-components/charts/utils/format-time-label.ts +18 -0
  808. package/src/ai-components/charts/vega-lite/schema.ts +19 -2
  809. package/src/ai-components/contexts/composer-context.tsx +13 -5
  810. package/src/ai-components/contexts/index.ts +1 -0
  811. package/src/ai-components/contexts/theme-context.tsx +19 -4
  812. package/src/ai-components/contexts/translation-context.tsx +44 -0
  813. package/src/ai-components/conversation/AIConversation.tsx +590 -0
  814. package/src/ai-components/conversation/controller/AIAgentError.ts +14 -0
  815. package/src/ai-components/conversation/controller/AIConversationController.ts +623 -0
  816. package/src/ai-components/conversation/controller/AIConversationExchange.ts +132 -0
  817. package/src/ai-components/conversation/controller/index.ts +4 -0
  818. package/src/ai-components/conversation/controller/offers.ts +43 -0
  819. package/src/ai-components/conversation/controller/types.ts +154 -0
  820. package/src/ai-components/conversation/controller/useAIConversationState.ts +21 -0
  821. package/src/ai-components/conversation/entries.ts +177 -0
  822. package/src/ai-components/conversation/fromStreamMessage.ts +87 -0
  823. package/src/ai-components/conversation/index.ts +5 -0
  824. package/src/ai-components/conversation/list/AIChatList.tsx +227 -0
  825. package/src/ai-components/conversation/list/useChatListController.ts +91 -0
  826. package/src/ai-components/conversation/list/useEndInView.ts +61 -0
  827. package/src/ai-components/conversation/list/useKeyboardOverlap.ts +51 -0
  828. package/src/ai-components/conversation/list/useLoadOlder.ts +97 -0
  829. package/src/ai-components/conversation/list/usePinnedAnchor.ts +113 -0
  830. package/src/ai-components/conversation/types.ts +57 -0
  831. package/src/ai-components/conversation/useConversationRows.ts +230 -0
  832. package/src/ai-components/generative-ui/AIUIAction.ts +73 -0
  833. package/src/ai-components/generative-ui/AIUIArguments.ts +117 -0
  834. package/src/ai-components/generative-ui/AIUIComponent.tsx +106 -0
  835. package/src/ai-components/generative-ui/AIUIComponentDefinition.ts +163 -0
  836. package/src/ai-components/generative-ui/AIUIDocument.ts +77 -0
  837. package/src/ai-components/generative-ui/AIUIEntrance.tsx +223 -0
  838. package/src/ai-components/generative-ui/AIUIErrorBoundary.tsx +39 -0
  839. package/src/ai-components/generative-ui/AIUIHelpers.ts +86 -0
  840. package/src/ai-components/generative-ui/AIUIHost.ts +358 -0
  841. package/src/ai-components/generative-ui/AIUIMedia.ts +45 -0
  842. package/src/ai-components/generative-ui/AIUIRenderer.ts +60 -0
  843. package/src/ai-components/generative-ui/AIUIView.tsx +87 -0
  844. package/src/ai-components/generative-ui/index.ts +48 -0
  845. package/src/ai-components/icons.ts +101 -0
  846. package/src/ai-components/index.ts +10 -0
  847. package/src/ai-components/internal/Shimmer.tsx +40 -0
  848. package/src/ai-components/internal/format.ts +69 -0
  849. package/src/ai-components/internal/hooks/useBottomSafeArea.ts +6 -0
  850. package/src/ai-components/internal/icons/AgentIcons.tsx +138 -0
  851. package/src/ai-components/internal/tokens.ts +4 -0
  852. package/src/ai-components/internal/uuid.ts +17 -0
  853. package/src/ai-components/markdown/components/CodeBlock.tsx +13 -2
  854. package/src/ai-components/markdown/components/InlineCode.tsx +1 -0
  855. package/src/ai-components/markdown/components/Text.tsx +7 -2
  856. package/src/ai-components/markdown/styles.ts +29 -27
  857. package/src/ai-components/markdown/types.ts +1 -0
  858. package/src/ai-components/message/AIAssistantMessage.tsx +99 -0
  859. package/src/ai-components/message/AIMessageAttachment.tsx +245 -0
  860. package/src/ai-components/message/AIReplyActions.tsx +139 -0
  861. package/src/ai-components/message/AIUserMessage.tsx +121 -0
  862. package/src/ai-components/message/UserMessageMenu.tsx +149 -0
  863. package/src/ai-components/message/index.ts +4 -0
  864. package/src/ai-components/message-composer/ActionButton.tsx +5 -1
  865. package/src/ai-components/message-composer/ActionSheet.tsx +28 -6
  866. package/src/ai-components/message-composer/AttachmentButton.tsx +9 -1
  867. package/src/ai-components/message-composer/ChatOptions.tsx +144 -0
  868. package/src/ai-components/message-composer/ComposerView.tsx +179 -29
  869. package/src/ai-components/message-composer/EditingMessageChip.tsx +61 -0
  870. package/src/ai-components/message-composer/MediaPreviewList.tsx +34 -8
  871. package/src/ai-components/message-composer/index.ts +1 -0
  872. package/src/ai-components/message-parts/AIMessageParts.tsx +120 -0
  873. package/src/ai-components/message-parts/AIToolApproval.tsx +196 -0
  874. package/src/ai-components/message-parts/AIToolAuthorization.tsx +279 -0
  875. package/src/ai-components/message-parts/AIToolCall.tsx +117 -0
  876. package/src/ai-components/message-parts/callStates.ts +44 -0
  877. package/src/ai-components/message-parts/index.ts +6 -0
  878. package/src/ai-components/message-parts/parseMessageParts.ts +195 -0
  879. package/src/ai-components/message-parts/types.ts +157 -0
  880. package/src/ai-components/native-specs/NativeAIAgent.ts +25 -0
  881. package/src/ai-components/reasoning/AIReasoning.tsx +509 -0
  882. package/src/ai-components/reasoning/index.ts +2 -0
  883. package/src/ai-components/reasoning/thinkingClock.ts +109 -0
  884. package/src/ai-components/services/clipboard-service/expo.ts +18 -4
  885. package/src/ai-components/store/composer/store.ts +9 -0
  886. package/src/ai-components/store/composer/useComposerChatOption.ts +15 -0
  887. package/src/ai-components/store/composer/useComposerIsEditing.ts +15 -0
  888. package/src/ai-components/store/index.ts +2 -0
  889. package/src/ai-components/suggestions/AISuggestions.tsx +73 -0
  890. package/src/ai-components/suggestions/index.ts +1 -0
  891. package/src/ai-components/syntax-highlighting/utils/flatten-nodes.ts +2 -1
  892. package/src/ai-components/syntax-highlighting/utils/renderer.tsx +4 -2
  893. package/src/ai-components/theme.ts +334 -0
  894. package/src/ai-components/tool-calls/AIClientIdentity.ts +33 -0
  895. package/src/ai-components/tool-calls/AIClientToolRunner.ts +200 -0
  896. package/src/ai-components/tool-calls/AIToolApprover.tsx +60 -0
  897. package/src/ai-components/tool-calls/authSessionAuthorizer.ts +137 -0
  898. package/src/ai-components/tool-calls/index.ts +4 -0
  899. package/src/ai-components/voice-visualization/glowShader.ts +1 -3
  900. package/src/ai-components/voice-visualization/simplex.ts +1 -1
  901. package/src/components/Accessibility/NotificationAnnouncer.tsx +5 -5
  902. package/src/components/Accessibility/hooks/useIncomingMessageAnnouncements.ts +1 -1
  903. package/src/components/Accessibility/useAccessibilityAnnouncer.ts +0 -2
  904. package/src/components/Message/MessageItemView/utils/renderText.tsx +1 -2
  905. package/src/contexts/translationContext/TranslationContext.tsx +2 -2
  906. package/src/i18n/ar.json +39 -1
  907. package/src/i18n/en.json +39 -1
  908. package/src/i18n/es.json +39 -1
  909. package/src/i18n/fr.json +39 -1
  910. package/src/i18n/he.json +39 -1
  911. package/src/i18n/hi.json +39 -1
  912. package/src/i18n/it.json +39 -1
  913. package/src/i18n/ja.json +39 -1
  914. package/src/i18n/ko.json +39 -1
  915. package/src/i18n/nl.json +39 -1
  916. package/src/i18n/pt-br.json +39 -1
  917. package/src/i18n/ru.json +39 -1
  918. package/src/i18n/tr.json +39 -1
  919. package/src/mock-builders/a2ui/ohrid.ts +106 -0
  920. package/src/utils/i18n/Streami18n.ts +4 -1
  921. package/src/utils/i18n/utils.ts +3 -0
  922. package/src/version.json +1 -1
@@ -0,0 +1,1514 @@
1
+ import React from 'react';
2
+ import { Pressable, ScrollView, StyleSheet, Text, TextInput } from 'react-native';
3
+
4
+ import { act, fireEvent, render, screen, waitFor } from '@testing-library/react-native';
5
+
6
+ import {
7
+ type AIUIAction,
8
+ AIUIHost,
9
+ AIUIView,
10
+ defineAIUIComponent,
11
+ parseAIUIComponentDefinition,
12
+ parseAIUIDocument,
13
+ StreamTheme,
14
+ } from '../../ai-components';
15
+ import { aiUIMediaSource, isPublicWebURL } from '../../ai-components/generative-ui/AIUIMedia';
16
+ import type {
17
+ AIClientTool,
18
+ AIClientToolDefinition,
19
+ } from '../../ai-components/tool-calls/AIClientToolRunner';
20
+ import { ohridLines, uiComponents } from '../../mock-builders/a2ui/ohrid';
21
+ import { NativeHandlers } from '../../native';
22
+ import { a2uiMessagesIn, normalizeA2UIMessage, supportsA2UIVersion } from '../A2UIStream';
23
+ import basicCatalogJSON from '../basicCatalog.json';
24
+ import { a2uiAppCatalogDocument, a2uiTypeName } from '../catalogDocument';
25
+ import { formatMoment, parseMoment } from '../components/DateTimeInput';
26
+ import { A2UI_ICON_PATHS } from '../components/iconPaths';
27
+ import { A2UI_DEFAULT_CATALOG_ID, type A2UIRendererOptions, createA2UIRenderer } from '../index';
28
+ import { withoutPaths, withoutSecrets } from '../secrets';
29
+
30
+ const definitionOf = (name: string) => {
31
+ const definition = parseAIUIComponentDefinition(
32
+ uiComponents.find((component) => component.name === name),
33
+ );
34
+ if (!definition) throw new Error(`no ${name}`);
35
+ return definition;
36
+ };
37
+
38
+ const placeCard = defineAIUIComponent<{ note?: string; title: string }>(
39
+ definitionOf('place_card'),
40
+ ({ args, context }) => (
41
+ <Pressable
42
+ onPress={() =>
43
+ context.emit('ask_about', `Tell me more about ${args.title}`, { title: args.title })
44
+ }
45
+ testID='place-card'
46
+ >
47
+ <Text>
48
+ {args.title}
49
+ {args.note ? ` (${args.note})` : ''}
50
+ </Text>
51
+ </Pressable>
52
+ ),
53
+ );
54
+
55
+ const tripMap = defineAIUIComponent<{ places: { name: string }[] }>(
56
+ definitionOf('trip_map'),
57
+ ({ args }) => <Text testID='trip-map'>{args.places.map(({ name }) => name).join(', ')}</Text>,
58
+ );
59
+
60
+ const calendarCheck = defineAIUIComponent<{ end: Date; start: Date }>(
61
+ definitionOf('calendar_check'),
62
+ ({ args }) => (
63
+ <Text testID='calendar-check'>
64
+ {args.start.getDate()}–{args.end.getDate()}
65
+ </Text>
66
+ ),
67
+ );
68
+
69
+ const setup = (options: A2UIRendererOptions = {}) => {
70
+ const sent: AIUIAction[] = [];
71
+ const errors: { code: string; message: string; surfaceId?: string }[] = [];
72
+ const open = jest.fn();
73
+ const ui = new AIUIHost({
74
+ components: [placeCard, tripMap, calendarCheck],
75
+ open,
76
+ renderers: [createA2UIRenderer({ ...options, onError: (error) => errors.push(error) })],
77
+ send: (action) => {
78
+ sent.push(action);
79
+ },
80
+ });
81
+ return { errors, open, sent, ui };
82
+ };
83
+
84
+ const documentOf = (lines: string[], status = 'complete') => {
85
+ const document = parseAIUIDocument({
86
+ content: lines.join('\n'),
87
+ format: 'a2ui',
88
+ status,
89
+ v: 1,
90
+ version: '1.0',
91
+ });
92
+ if (!document) throw new Error('no document');
93
+ return document;
94
+ };
95
+
96
+ // Renders one reply's UI, and lets what it tells the host after the render land.
97
+ const renderReply = async (ui: AIUIHost, lines: string[], status = 'complete') => {
98
+ const result = render(
99
+ <StreamTheme>
100
+ <AIUIView document={documentOf(lines, status)} host={ui} messageId='a1' />
101
+ </StreamTheme>,
102
+ );
103
+ await act(() => Promise.resolve());
104
+ return result;
105
+ };
106
+
107
+ const line = (message: Record<string, unknown>) => JSON.stringify({ version: '1.0', ...message });
108
+
109
+ const button = (id: string, label: string, action: Record<string, unknown>, extra = {}) => [
110
+ { action, child: `${id}_label`, component: 'Button', id, ...extra },
111
+ { component: 'Text', id: `${id}_label`, text: label },
112
+ ];
113
+
114
+ const surfaceOf = (components: Record<string, unknown>[], dataModel?: Record<string, unknown>) => [
115
+ line({ createSurface: { surfaceId: 's', ...(dataModel ? { dataModel } : {}) } }),
116
+ line({ updateComponents: { components, surfaceId: 's' } }),
117
+ ];
118
+
119
+ describe('a2uiMessagesIn', () => {
120
+ it('reads the messages one per line, in an array, pretty-printed, or among words', () => {
121
+ const one = '{"version":"1.0","deleteSurface":{"surfaceId":"a"}}';
122
+ const two = '{"version":"1.0","deleteSurface":{"surfaceId":"b"}}';
123
+ expect(a2uiMessagesIn(`${one}\n${two}`)).toEqual([one, two]);
124
+ expect(a2uiMessagesIn(`[${one},${two}]`)).toEqual([one, two]);
125
+ expect(a2uiMessagesIn(`Here it is:\n${one}\nand more.`)).toEqual([one]);
126
+ expect(
127
+ a2uiMessagesIn('{\n "version": "1.0",\n "deleteSurface": {"surfaceId": "a"}\n}'),
128
+ ).toHaveLength(1);
129
+ });
130
+
131
+ it('leaves out a message still being written', () => {
132
+ expect(a2uiMessagesIn('{"version":"1.0"}\n{"version":"1.0","updateComp')).toEqual([
133
+ '{"version":"1.0"}',
134
+ ]);
135
+ });
136
+
137
+ it('is not misled by braces and quotes inside strings', () => {
138
+ const tricky = '{"text":"a } and a \\" and a {"}';
139
+ expect(a2uiMessagesIn(tricky)).toEqual([tricky]);
140
+ });
141
+
142
+ const parsed = (message: string): unknown => {
143
+ try {
144
+ return JSON.parse(message);
145
+ } catch {
146
+ return undefined;
147
+ }
148
+ };
149
+
150
+ const surfaceIdOf = (message: string) =>
151
+ (parsed(message) as { createSurface: { surfaceId: string } }).createSurface.surfaceId;
152
+
153
+ it('reads a pretty-printed message across its lines', () => {
154
+ const text = [
155
+ '{',
156
+ ' "version": "1.0",',
157
+ ' "updateComponents": {',
158
+ ' "surfaceId": "s",',
159
+ ' "components": [',
160
+ ' {"id": "root", "component": "Column", "children": ["a"]},',
161
+ ' {',
162
+ ' "id": "a", "component": "Text", "text": "{ not a brace }"',
163
+ ' }',
164
+ ' ]',
165
+ ' }',
166
+ '}',
167
+ '{"version": "1.0", "deleteSurface": {"surfaceId": "s"}}',
168
+ ].join('\n');
169
+ const messages = a2uiMessagesIn(text);
170
+ expect(messages).toHaveLength(2);
171
+ expect(messages.map(parsed)).toEqual([expect.any(Object), expect.any(Object)]);
172
+ });
173
+
174
+ it('ends a message with a stray quote at its line, and the messages after it still count', () => {
175
+ const lines = [
176
+ '{"version": "1.0", "createSurface": {"surfaceId": "a"}}',
177
+ '{"version": "1.0", "updateComponents": {"surfaceId": "a", "components": [{"id": "t", "text">Day 1"}]}}',
178
+ '{"version": "1.0", "createSurface": {"surfaceId": "b"}}',
179
+ '{"version": "1.0", "createSurface": {"surfaceId": "c", "note": "a } and a \\" in a string"}}',
180
+ ];
181
+ const messages = a2uiMessagesIn(lines.join('\n'));
182
+ expect(messages).toEqual(lines);
183
+ expect(parsed(messages[1])).toBeUndefined();
184
+ expect([0, 2, 3].map((index) => surfaceIdOf(messages[index]))).toEqual(['a', 'b', 'c']);
185
+ });
186
+
187
+ it('ends a message missing a brace where the next one starts', () => {
188
+ const lines = [
189
+ '{"version": "1.0", "createSurface": {"surfaceId": "a"}',
190
+ '{"version": "1.0", "createSurface": {"surfaceId": "b"}}',
191
+ '{"version": "1.0", "createSurface": {"surfaceId": "c"}}',
192
+ ];
193
+ const messages = a2uiMessagesIn(lines.join('\n'));
194
+ expect(messages).toEqual(lines);
195
+ expect(parsed(messages[0])).toBeUndefined();
196
+ expect(messages.slice(1).map(surfaceIdOf)).toEqual(['b', 'c']);
197
+ });
198
+
199
+ it('skips the rest of a broken pretty-printed message instead of reading it as messages', () => {
200
+ const broken = [
201
+ '{"version": "1.0", "updateComponents": {"surfaceId": "a", "components": [',
202
+ ' {"id": "t", "text">Day 1"},',
203
+ ' {"id": "u", "component": "Text"},',
204
+ ' {"id": "w", "component": "Text"}',
205
+ ']}}',
206
+ ].join('\n');
207
+ const newline = [
208
+ '{"version": "1.0", "updateComponents": {"surfaceId": "a", "components": [',
209
+ ' {"id": "t", "component": "Text", "text": "Day 1',
210
+ 'Use {name} here"}',
211
+ ']}}',
212
+ ].join('\n');
213
+ const next = '{"version": "1.0", "createSurface": {"surfaceId": "b"}}';
214
+ for (const text of [broken, newline]) {
215
+ const messages = a2uiMessagesIn(`${text}\n${next}`);
216
+ expect(messages).toHaveLength(2);
217
+ expect(parsed(messages[0])).toBeUndefined();
218
+ expect(surfaceIdOf(messages[1])).toBe('b');
219
+ }
220
+ });
221
+
222
+ it('keeps a missing comma between indented objects inside its message', () => {
223
+ const text = [
224
+ '{"version": "1.0", "updateComponents": {"surfaceId": "a", "components": [',
225
+ ' {"id": "t", "component": "Text"}',
226
+ ' {"id": "u", "component": "Text"}',
227
+ ']}}',
228
+ '{"version": "1.0", "createSurface": {"surfaceId": "b"}}',
229
+ ].join('\n');
230
+ const messages = a2uiMessagesIn(text);
231
+ expect(messages).toHaveLength(2);
232
+ expect(parsed(messages[0])).toBeUndefined();
233
+ expect(surfaceIdOf(messages[1])).toBe('b');
234
+ });
235
+
236
+ it('leaves out the line still being written after a broken message', () => {
237
+ const broken = '{"version": "1.0", "updateComponents": {"surfaceId": "a", "text">x"}}';
238
+ const next = '{"version": "1.0", "createSurface": {"surfaceId": "b"}}';
239
+ for (let length = 0; length < next.length; length++) {
240
+ expect(a2uiMessagesIn(`${broken}\n${next.slice(0, length)}`)).toEqual([broken]);
241
+ }
242
+ expect(a2uiMessagesIn(`${broken}\n${next}`)).toEqual([broken, next]);
243
+ });
244
+
245
+ it('lets a string still being written hold braces and quotes', () => {
246
+ const partial =
247
+ '{"version": "1.0", "createSurface": {"surfaceId": "s", "note": "a { and a \\" so far';
248
+ expect(a2uiMessagesIn(partial)).toEqual([]);
249
+ expect(a2uiMessagesIn(`${partial}"}}`)).toHaveLength(1);
250
+ });
251
+ });
252
+
253
+ describe('the A2UI versions and catalog it reads', () => {
254
+ it('reads 1.0 in the spellings an agent writes, and no other version', () => {
255
+ for (const version of [undefined, '1', '1.0', '1.0.0', 'v1', 'v1.0']) {
256
+ expect(supportsA2UIVersion(version)).toBe(true);
257
+ }
258
+ for (const version of ['0.9', '2.0', 'v0.8']) {
259
+ expect(supportsA2UIVersion(version)).toBe(false);
260
+ }
261
+ });
262
+
263
+ it('writes every message as 1.0, and names the app’s catalog for a surface naming none', () => {
264
+ expect(normalizeA2UIMessage({ version: 'v1.0' }, 'app').version).toBe('1.0');
265
+ expect(
266
+ normalizeA2UIMessage({ createSurface: { surfaceId: 's' }, version: '1.0' }, 'app'),
267
+ ).toEqual({ createSurface: { catalogId: 'app', surfaceId: 's' }, version: '1.0' });
268
+ expect(
269
+ normalizeA2UIMessage(
270
+ { createSurface: { catalogId: 'other', surfaceId: 's' }, version: '1.0' },
271
+ 'app',
272
+ ).createSurface,
273
+ ).toEqual({ catalogId: 'other', surfaceId: 's' });
274
+ });
275
+
276
+ it('describes the app’s components to the agent', () => {
277
+ expect(a2uiTypeName('place_card')).toBe('PlaceCard');
278
+ const document = a2uiAppCatalogDocument(A2UI_DEFAULT_CATALOG_ID, [placeCard]);
279
+ expect(document.components.PlaceCard).toMatchObject({
280
+ description:
281
+ 'One place of a plan, with its photo and a short summary, which the phone loads from Wikipedia, so it needs no image URL. Use one per sight, beach, monastery, village or day trip. Tapping the card asks you about the place.',
282
+ required: ['component', 'title'],
283
+ });
284
+ });
285
+
286
+ it('tells the agent’s backend its fence, catalog and instructions', async () => {
287
+ const { ui } = setup();
288
+ const { renderers } = await ui.capabilities();
289
+ expect(renderers).toEqual([
290
+ {
291
+ catalog_id: A2UI_DEFAULT_CATALOG_ID,
292
+ fence: 'ui:a2ui@1.0',
293
+ format: 'a2ui',
294
+ instructions: expect.any(String),
295
+ },
296
+ ]);
297
+ const { instructions } = renderers[0] as unknown as { instructions: string };
298
+ expect(instructions).toContain('whose info string is `ui:a2ui@1.0`');
299
+ expect(instructions).toContain(`## The catalog \`${A2UI_DEFAULT_CATALOG_ID}\``);
300
+ expect(instructions).toContain('"PlaceCard"');
301
+ });
302
+
303
+ it('leaves what it removes out of what it shows and of what the agent is told', async () => {
304
+ const { errors, ui } = setup({ removing: ['Divider', 'formatCurrency'] });
305
+ const { renderers } = await ui.capabilities();
306
+ const { instructions } = renderers[0] as unknown as { instructions: string };
307
+ expect(instructions).not.toContain('"Divider"');
308
+ expect(instructions).not.toContain('"formatCurrency"');
309
+ expect(instructions).toContain('"Card"');
310
+ await renderReply(
311
+ ui,
312
+ surfaceOf([
313
+ { children: ['line', 'words'], component: 'Column', id: 'root' },
314
+ { component: 'Divider', id: 'line' },
315
+ { component: 'Text', id: 'words', text: 'Still here' },
316
+ ]),
317
+ );
318
+ await waitFor(() => expect(screen.getByText(/Still here/)).toBeTruthy());
319
+ expect(screen.queryByTestId('a2ui-line')).toBeNull();
320
+ expect(errors.map(({ code }) => code)).not.toContain('EXECUTION_ERROR');
321
+ });
322
+ });
323
+
324
+ describe('a reply written in A2UI', () => {
325
+ it('shows the Ohrid plan with the basic catalog and the app’s components', async () => {
326
+ const { errors, ui } = setup();
327
+ await renderReply(ui, ohridLines);
328
+ await waitFor(() =>
329
+ expect(screen.getByTestId('trip-map')).toHaveTextContent(/Church of St\. John at Kaneo/),
330
+ );
331
+ expect(screen.getByTestId('calendar-check')).toHaveTextContent('14–15');
332
+ expect(screen.getAllByTestId('place-card')[0]).toHaveTextContent('Plaošnik (1 hour)');
333
+ expect(screen.getByText(/Two easy days by the lake/)).toBeTruthy();
334
+ expect(screen.getByRole('tab', { name: 'Saturday' })).toBeTruthy();
335
+ expect(errors).toEqual([]);
336
+ });
337
+
338
+ it('tells the agent what the person did, in the words of the button they tapped', async () => {
339
+ const { sent, ui } = setup();
340
+ await renderReply(ui, ohridLines);
341
+ fireEvent.press(await screen.findByRole('radio', { name: 'Packed' }));
342
+ fireEvent.press(screen.getByRole('button', { name: 'Update the plan' }));
343
+ await waitFor(() => expect(sent).toHaveLength(1));
344
+ expect(sent[0]).toEqual({
345
+ format: 'a2ui',
346
+ label: 'Update the plan',
347
+ messageId: 'a1',
348
+ name: 'update_plan',
349
+ payload: {
350
+ context: { interests: [], pace: ['packed'] },
351
+ sourceComponentId: 'update',
352
+ surfaceId: 'ohrid-weekend',
353
+ userMessage: 'Update the plan',
354
+ },
355
+ });
356
+
357
+ fireEvent.press(screen.getAllByTestId('place-card')[0]);
358
+ await waitFor(() => expect(sent).toHaveLength(2));
359
+ expect(sent[1]).toEqual({
360
+ format: 'a2ui',
361
+ label: 'Tell me more about Plaošnik',
362
+ messageId: 'a1',
363
+ name: 'ask_about',
364
+ payload: { component: 'place_card', title: 'Plaošnik' },
365
+ });
366
+ });
367
+
368
+ it('never sends the agent’s words for an action in place of what its button says', async () => {
369
+ const { sent, ui } = setup();
370
+ await renderReply(
371
+ ui,
372
+ surfaceOf(
373
+ button('root', 'Book the boat', {
374
+ event: { name: 'book', userMessage: 'Cancel my whole trip' },
375
+ }),
376
+ ),
377
+ );
378
+ fireEvent.press(await screen.findByRole('button', { name: 'Book the boat' }));
379
+ await waitFor(() => expect(sent).toHaveLength(1));
380
+ expect(sent[0]?.label).toBe('Book the boat');
381
+ expect(sent[0]?.payload.userMessage).toBe('Cancel my whole trip');
382
+ });
383
+
384
+ it('names an action by what its button says when bound, else by its name in words', async () => {
385
+ const { sent, ui } = setup();
386
+ await renderReply(
387
+ ui,
388
+ surfaceOf(
389
+ [
390
+ { children: ['bound', 'nameless'], component: 'Column', id: 'root' },
391
+ {
392
+ action: { event: { name: 'book' } },
393
+ child: 'bound_label',
394
+ component: 'Button',
395
+ id: 'bound',
396
+ },
397
+ { component: 'Text', id: 'bound_label', text: { '@path': '/cta' } },
398
+ {
399
+ action: { event: { name: 'submit_form' } },
400
+ child: 'icon',
401
+ component: 'Button',
402
+ id: 'nameless',
403
+ },
404
+ { component: 'Icon', id: 'icon', name: 'check' },
405
+ ],
406
+ { cta: 'Book for two' },
407
+ ),
408
+ );
409
+ fireEvent.press(await screen.findByRole('button', { name: 'Book for two' }));
410
+ await waitFor(() => expect(sent[0]?.label).toBe('Book for two'));
411
+ fireEvent.press(screen.getAllByRole('button')[1]);
412
+ await waitFor(() => expect(sent[1]?.label).toBe('Submit form'));
413
+ });
414
+
415
+ it('follows a reply as the agent writes it, a message at a time', async () => {
416
+ const { ui } = setup();
417
+ const view = (lines: string[]) => (
418
+ <StreamTheme>
419
+ <AIUIView document={documentOf(lines, 'streaming')} host={ui} messageId='a1' />
420
+ </StreamTheme>
421
+ );
422
+ const { rerender } = render(view([...ohridLines.slice(0, 2), '{"version":"1.0","upd']));
423
+ await waitFor(() => expect(screen.getByText(/Two easy days by the lake/)).toBeTruthy());
424
+ expect(screen.queryByTestId('trip-map')).toBeNull();
425
+
426
+ rerender(view(ohridLines.slice(0, 3)));
427
+ await waitFor(() => expect(screen.getByTestId('trip-map')).toBeTruthy());
428
+ });
429
+
430
+ it('shows a placeholder while the first surface is being written', async () => {
431
+ const { ui } = setup();
432
+ await renderReply(ui, ['{"version":"1.0","createSur'], 'streaming');
433
+ expect(screen.getByTestId('a2ui-placeholder')).toBeTruthy();
434
+ });
435
+
436
+ it('starts the surfaces again when the agent rewrites what it had written', async () => {
437
+ const { ui } = setup();
438
+ const text = (words: string) => surfaceOf([{ component: 'Text', id: 'root', text: words }]);
439
+ const view = (lines: string[]) => (
440
+ <StreamTheme>
441
+ <AIUIView document={documentOf(lines)} host={ui} messageId='a1' />
442
+ </StreamTheme>
443
+ );
444
+ const { rerender } = render(view(text('First draft')));
445
+ await waitFor(() => expect(screen.getByText(/First draft/)).toBeTruthy());
446
+ rerender(view(text('Second draft')));
447
+ await waitFor(() => expect(screen.getByText(/Second draft/)).toBeTruthy());
448
+ expect(screen.queryByText(/First draft/)).toBeNull();
449
+ });
450
+
451
+ it('shows the rest of a batch that one bad component spoils, and says what went wrong', async () => {
452
+ const { errors, ui } = setup();
453
+ await renderReply(
454
+ ui,
455
+ surfaceOf([
456
+ { children: ['good', 'bad'], component: 'Column', id: 'root' },
457
+ { component: 'Text', id: 'good', text: 'Still here' },
458
+ { component: 'Slider', id: 'bad', value: 'not a number' },
459
+ ]),
460
+ );
461
+ await waitFor(() => expect(screen.getByText(/Still here/)).toBeTruthy());
462
+ await waitFor(() =>
463
+ expect(errors).toEqual([
464
+ expect.objectContaining({ code: 'VALIDATION_FAILED', surfaceId: 's' }),
465
+ ]),
466
+ );
467
+ });
468
+
469
+ it('says a message is not JSON, and shows the rest', async () => {
470
+ const { errors, ui } = setup();
471
+ await renderReply(ui, [
472
+ line({ createSurface: { surfaceId: 's' } }),
473
+ '{not json}',
474
+ line({
475
+ updateComponents: {
476
+ components: [{ component: 'Text', id: 'root', text: 'After the bad one' }],
477
+ surfaceId: 's',
478
+ },
479
+ }),
480
+ ]);
481
+ await waitFor(() => expect(screen.getByText(/After the bad one/)).toBeTruthy());
482
+ await waitFor(() =>
483
+ expect(errors.map(({ message }) => message)).toContain('A message isn’t a JSON object'),
484
+ );
485
+ });
486
+
487
+ it('shows the messages after one the agent broke, and reports the broken one', async () => {
488
+ const { errors, ui } = setup();
489
+ const text = (surfaceId: string, words: string) => [
490
+ line({ createSurface: { surfaceId } }),
491
+ line({
492
+ updateComponents: {
493
+ components: [{ component: 'Text', id: 'root', text: words }],
494
+ surfaceId,
495
+ },
496
+ }),
497
+ ];
498
+ await renderReply(ui, [
499
+ ...text('a', 'Day 1'),
500
+ line({ createSurface: { surfaceId: 'b' } }).slice(0, -1),
501
+ ...text('c', 'Day 2'),
502
+ ]);
503
+ await waitFor(() => expect(screen.getByText('Day 2')).toBeTruthy());
504
+ expect(screen.getByText('Day 1')).toBeTruthy();
505
+ expect(errors).toEqual([
506
+ expect.objectContaining({
507
+ code: 'VALIDATION_FAILED',
508
+ message: 'A message isn’t a JSON object',
509
+ }),
510
+ ]);
511
+ });
512
+
513
+ it('opens links the way the host allows, from the person’s tap only', async () => {
514
+ const { open, ui } = setup();
515
+ await renderReply(
516
+ ui,
517
+ surfaceOf(
518
+ button('root', 'Read about Ohrid', {
519
+ functionCall: {
520
+ '@call': 'openUrl',
521
+ args: { url: 'https://en.wikipedia.org/wiki/Ohrid' },
522
+ },
523
+ }),
524
+ ),
525
+ );
526
+ const read = await screen.findByRole('button', { name: 'Read about Ohrid' });
527
+ expect(open).not.toHaveBeenCalled();
528
+ fireEvent.press(read);
529
+ await waitFor(() => expect(open).toHaveBeenCalledWith('https://en.wikipedia.org/wiki/Ohrid'));
530
+ });
531
+
532
+ it('shows nothing for a finished reply whose A2UI made no surface, so its text shows', async () => {
533
+ const { ui } = setup();
534
+ await renderReply(ui, ['{"version":"1.0","deleteSurface":{"surfaceId":"none"}}']);
535
+ expect(
536
+ ui.content(documentOf(['{"version":"1.0","deleteSurface":{"surfaceId":"none"}}']), 'a1'),
537
+ ).toBeNull();
538
+ expect(screen.toJSON()).toBeNull();
539
+ });
540
+ });
541
+
542
+ describe('what the person types into an obscured field', () => {
543
+ // A sign-in form: an email, a password, and a button that sends both, and the password inside
544
+ // a sentence, as an agent may bind it.
545
+ const signIn = ({ sendDataModel = false } = {}) => [
546
+ line({
547
+ createSurface: {
548
+ dataModel: { account: { email: '', password: '' } },
549
+ sendDataModel,
550
+ surfaceId: 's',
551
+ },
552
+ }),
553
+ line({
554
+ updateComponents: {
555
+ components: [
556
+ { children: ['email', 'password', 'go'], component: 'Column', id: 'root' },
557
+ {
558
+ component: 'TextField',
559
+ id: 'email',
560
+ label: 'Email',
561
+ value: { '@path': '/account/email' },
562
+ },
563
+ {
564
+ component: 'TextField',
565
+ id: 'password',
566
+ label: 'Password',
567
+ value: { '@path': '/account/password' },
568
+ variant: 'obscured',
569
+ },
570
+ ...button('go', 'Sign in', {
571
+ event: {
572
+ context: {
573
+ email: { '@path': '/account/email' },
574
+ password: { '@path': '/account/password' },
575
+ said: {
576
+ '@call': 'formatString',
577
+ args: { value: 'My password is ${/account/password}' },
578
+ },
579
+ },
580
+ name: 'sign_in',
581
+ userMessage: {
582
+ '@call': 'formatString',
583
+ args: { value: 'Signing in with ${/account/password}' },
584
+ },
585
+ },
586
+ }),
587
+ ],
588
+ surfaceId: 's',
589
+ },
590
+ }),
591
+ ];
592
+
593
+ const fillIn = async () => {
594
+ fireEvent.changeText(await screen.findByLabelText('Email'), 'ana@example.com');
595
+ fireEvent.changeText(screen.getByLabelText('Password'), 'hunter22');
596
+ fireEvent.press(screen.getByRole('button', { name: 'Sign in' }));
597
+ };
598
+
599
+ it('never reaches the agent in an action’s context or its message, bound or within text', async () => {
600
+ const { sent, ui } = setup();
601
+ await renderReply(ui, signIn());
602
+ await fillIn();
603
+ await waitFor(() => expect(sent).toHaveLength(1));
604
+ // The key bound to the field goes; text written with the password goes without it.
605
+ expect(sent[0]?.payload.context).toEqual({
606
+ email: 'ana@example.com',
607
+ said: 'My password is ',
608
+ });
609
+ expect(sent[0]?.payload.userMessage).toBe('Signing in with');
610
+ expect(sent[0]?.payload).not.toHaveProperty('dataModel');
611
+ expect(JSON.stringify(sent[0])).not.toContain('hunter22');
612
+ });
613
+
614
+ it('is left out of the data model a surface sends with its actions', async () => {
615
+ const { sent, ui } = setup();
616
+ await renderReply(ui, signIn({ sendDataModel: true }));
617
+ await fillIn();
618
+ await waitFor(() => expect(sent).toHaveLength(1));
619
+ expect(sent[0]?.payload.dataModel).toEqual({ account: { email: 'ana@example.com' } });
620
+ expect(JSON.stringify(sent[0])).not.toContain('hunter22');
621
+ });
622
+
623
+ it('stays the person’s whichever view the app draws the field with', async () => {
624
+ const { sent, ui } = setup({
625
+ components: {
626
+ TextField: ({ props }) => (
627
+ <TextInput
628
+ accessibilityLabel={String(props.label)}
629
+ onChangeText={(text) => (props.setValue as (value: string) => void)(text)}
630
+ value={String(props.value ?? '')}
631
+ />
632
+ ),
633
+ },
634
+ });
635
+ await renderReply(ui, signIn());
636
+ await fillIn();
637
+ await waitFor(() => expect(sent).toHaveLength(1));
638
+ expect(JSON.stringify(sent[0])).not.toContain('hunter22');
639
+ });
640
+
641
+ it('stays the person’s in an obscured field of a list’s row', async () => {
642
+ const { sent, ui } = setup();
643
+ await renderReply(ui, [
644
+ line({
645
+ createSurface: {
646
+ dataModel: { accounts: [{ name: 'Main', pin: '' }] },
647
+ sendDataModel: true,
648
+ surfaceId: 's',
649
+ },
650
+ }),
651
+ line({
652
+ updateComponents: {
653
+ components: [
654
+ { children: ['accounts', 'go'], component: 'Column', id: 'root' },
655
+ {
656
+ children: { componentId: 'pin', path: '/accounts' },
657
+ component: 'List',
658
+ id: 'accounts',
659
+ },
660
+ {
661
+ component: 'TextField',
662
+ id: 'pin',
663
+ label: 'PIN',
664
+ value: { '@path': 'pin' },
665
+ variant: 'obscured',
666
+ },
667
+ ...button('go', 'Save', { event: { name: 'save' } }),
668
+ ],
669
+ surfaceId: 's',
670
+ },
671
+ }),
672
+ ]);
673
+ fireEvent.changeText(await screen.findByLabelText('PIN'), '4821');
674
+ fireEvent.press(screen.getByRole('button', { name: 'Save' }));
675
+ await waitFor(() => expect(sent).toHaveLength(1));
676
+ expect(sent[0]?.payload.dataModel).toEqual({ accounts: [{ name: 'Main' }] });
677
+ expect(JSON.stringify(sent[0])).not.toContain('4821');
678
+ });
679
+
680
+ it('leaves the context key a row’s button binds to the row’s obscured field', async () => {
681
+ const { sent, ui } = setup();
682
+ await renderReply(ui, [
683
+ line({
684
+ createSurface: {
685
+ dataModel: {
686
+ accounts: [
687
+ { name: 'Main', pin: '' },
688
+ { name: 'Savings', pin: '' },
689
+ ],
690
+ },
691
+ surfaceId: 's',
692
+ },
693
+ }),
694
+ line({
695
+ updateComponents: {
696
+ components: [
697
+ {
698
+ children: { componentId: 'row', path: '/accounts' },
699
+ component: 'List',
700
+ id: 'root',
701
+ },
702
+ { children: ['pin', 'go'], component: 'Row', id: 'row' },
703
+ {
704
+ component: 'TextField',
705
+ id: 'pin',
706
+ label: 'PIN',
707
+ value: { '@path': 'pin' },
708
+ variant: 'obscured',
709
+ },
710
+ ...button('go', 'Save', {
711
+ event: {
712
+ context: {
713
+ account: { '@path': 'name' },
714
+ pin: { '@path': 'pin' },
715
+ said: { '@call': 'formatString', args: { value: 'My PIN is ${pin}' } },
716
+ },
717
+ name: 'save',
718
+ },
719
+ }),
720
+ ],
721
+ surfaceId: 's',
722
+ },
723
+ }),
724
+ ]);
725
+ const [, savings] = await screen.findAllByLabelText('PIN');
726
+ fireEvent.changeText(savings, '4821');
727
+ fireEvent.press(screen.getAllByRole('button', { name: 'Save' })[1]);
728
+ await waitFor(() => expect(sent).toHaveLength(1));
729
+ expect(sent[0]?.payload.context).toEqual({ account: 'Savings', said: 'My PIN is ' });
730
+ expect(JSON.stringify(sent[0])).not.toContain('4821');
731
+ });
732
+ });
733
+
734
+ describe('parseMoment and formatMoment', () => {
735
+ it('read and write the days and times an agent writes', () => {
736
+ expect(parseMoment('2026-11-14')).toEqual({ day: { date: 14, month: 10, year: 2026 } });
737
+ expect(parseMoment('09:30')).toEqual({ hours: 9, minutes: 30 });
738
+ expect(parseMoment('2026-11-14T09:30:00Z')).toEqual({
739
+ day: { date: 14, month: 10, year: 2026 },
740
+ hours: 9,
741
+ minutes: 30,
742
+ });
743
+ const both = { enableDate: true, enableTime: true };
744
+ expect(formatMoment(parseMoment('2026-01-02T03:04'), both)).toBe('2026-01-02T03:04');
745
+ expect(formatMoment(parseMoment('2026-01-02'), { ...both, enableTime: false })).toBe(
746
+ '2026-01-02',
747
+ );
748
+ expect(formatMoment(parseMoment('03:04'), { ...both, enableDate: false })).toBe('03:04');
749
+ });
750
+ });
751
+
752
+ describe('withoutSecrets and withoutPaths', () => {
753
+ it('cut a secret out of every string, in objects and lists', () => {
754
+ expect(
755
+ withoutSecrets({ a: 'hunter22', b: ['x', 'my hunter22'], c: { d: 'keep', e: 1 } }, [
756
+ 'hunter22',
757
+ '',
758
+ ]),
759
+ ).toEqual({ a: '', b: ['x', 'my '], c: { d: 'keep', e: 1 } });
760
+ expect(withoutSecrets('Signing in with abc', ['abc'])).toBe('Signing in with ');
761
+ expect(withoutSecrets({ a: 1 }, [])).toEqual({ a: 1 });
762
+ });
763
+
764
+ it('remove what is at JSON Pointers, escaped segments and list items included', () => {
765
+ expect(
766
+ withoutPaths({ 'a/b': 1, list: [1, 2, 3], nested: { keep: true, secret: 's' } }, [
767
+ '/a~1b',
768
+ '/list/1',
769
+ '/nested/secret',
770
+ '/missing/path',
771
+ ]),
772
+ ).toEqual({ list: [1, 3], nested: { keep: true } });
773
+ });
774
+ });
775
+
776
+ describe('the app’s tools in a reply’s UI', () => {
777
+ const weather = {
778
+ annotations: { readOnlyHint: true },
779
+ description: 'The weather in a city today.',
780
+ inputSchema: {
781
+ properties: { city: { description: 'The city', type: 'string' } },
782
+ required: ['city'],
783
+ type: 'object',
784
+ },
785
+ name: 'weather',
786
+ };
787
+ // A tool as the app gives it, whose output is what `run` resolves to.
788
+ const toolOf = (
789
+ definition: AIClientToolDefinition,
790
+ run: (args: Record<string, unknown>) => unknown,
791
+ ): AIClientTool => ({
792
+ definition,
793
+ run: async (call) => ({ output: await run(call.arguments as Record<string, unknown>) }),
794
+ });
795
+
796
+ const withTools = (tools: ConstructorParameters<typeof AIUIHost>[0]['tools']) => {
797
+ const errors: { code: string; message: string }[] = [];
798
+ const ui = new AIUIHost({
799
+ renderers: [createA2UIRenderer({ onError: (error) => errors.push(error) })],
800
+ send: jest.fn(),
801
+ tools,
802
+ });
803
+ return { errors, ui };
804
+ };
805
+
806
+ const forecast = surfaceOf(
807
+ [
808
+ { children: ['title', 'sky'], component: 'Column', id: 'root' },
809
+ { component: 'Text', id: 'title', text: 'Ohrid today' },
810
+ {
811
+ component: 'Text',
812
+ id: 'sky',
813
+ text: { '@call': 'weather', args: { city: { '@path': '/city' } } },
814
+ },
815
+ ],
816
+ { city: 'Ohrid' },
817
+ );
818
+
819
+ it('runs a tool the agent calls as a function, with its arguments worked out', async () => {
820
+ const run = jest.fn(({ city }: Record<string, unknown>) =>
821
+ Promise.resolve(`Sunny in ${String(city)}`),
822
+ );
823
+ const { errors, ui } = withTools([toolOf(weather, run)]);
824
+ await renderReply(ui, forecast);
825
+ await waitFor(() => expect(screen.getByText(/Sunny in Ohrid/)).toBeTruthy());
826
+ expect(run).toHaveBeenCalledWith({ city: 'Ohrid' });
827
+ expect(errors).toEqual([]);
828
+ });
829
+
830
+ it('tells the agent about the tools it can call, with what they take', async () => {
831
+ const { ui } = withTools([toolOf(weather, () => 'Sunny')]);
832
+ const { renderers, tools } = await ui.capabilities();
833
+ expect(tools).toEqual([weather]);
834
+ const { instructions } = renderers[0] as unknown as { instructions: string };
835
+ expect(instructions).toContain('"weather"');
836
+ expect(instructions).toContain('The weather in a city today.');
837
+ expect(instructions).toContain('"city"');
838
+ });
839
+
840
+ it('shows the rest of the UI when a tool fails, and says so', async () => {
841
+ const { errors, ui } = withTools([
842
+ toolOf(weather, () => Promise.reject(new Error('No forecast'))),
843
+ ]);
844
+ await renderReply(ui, forecast);
845
+ await waitFor(() => expect(screen.getByText(/Ohrid today/)).toBeTruthy());
846
+ await waitFor(() => expect(errors.length).toBeGreaterThan(0));
847
+ expect(screen.queryByText(/Sunny/)).toBeNull();
848
+ });
849
+
850
+ it('tells the model the app’s own instructions first', async () => {
851
+ const ui = new AIUIHost({
852
+ renderers: [createA2UIRenderer({ instructions: ' Write every trip plan as UI. ' })],
853
+ send: jest.fn(),
854
+ });
855
+ const { renderers } = await ui.capabilities();
856
+ const { instructions } = renderers[0] as unknown as { instructions: string };
857
+ expect(instructions.startsWith('Write every trip plan as UI.')).toBe(true);
858
+ });
859
+
860
+ it('runs a tool that changes something only when the person asks, never as the reply shows', async () => {
861
+ const run = jest.fn(() => 'Sunny');
862
+ const { errors, ui } = withTools([
863
+ toolOf({ ...weather, annotations: { readOnlyHint: false } }, run),
864
+ ]);
865
+ await renderReply(ui, forecast);
866
+ await waitFor(() => expect(screen.getByText(/Ohrid today/)).toBeTruthy());
867
+ await waitFor(() => expect(errors.length).toBeGreaterThan(0));
868
+ expect(errors[0]?.message).toMatch(/isn't read-only/);
869
+ expect(run).not.toHaveBeenCalled();
870
+ });
871
+
872
+ it('gives a tool none of what the person typed into an obscured field', async () => {
873
+ const run = jest.fn((args: Record<string, unknown>) => `Sunny in ${String(args.city)}`);
874
+ const { ui } = withTools([toolOf(weather, run)]);
875
+ await renderReply(
876
+ ui,
877
+ surfaceOf(
878
+ [
879
+ { children: ['secret', 'sky'], component: 'Column', id: 'root' },
880
+ {
881
+ component: 'TextField',
882
+ id: 'secret',
883
+ label: 'Password',
884
+ value: { '@path': '/password' },
885
+ variant: 'obscured',
886
+ },
887
+ {
888
+ component: 'Text',
889
+ id: 'sky',
890
+ text: { '@call': 'weather', args: { city: { '@path': '/password' } } },
891
+ },
892
+ ],
893
+ { password: 'hunter22' },
894
+ ),
895
+ );
896
+ await waitFor(() => expect(run).toHaveBeenCalled());
897
+ expect(JSON.stringify(run.mock.calls)).not.toContain('hunter22');
898
+ });
899
+
900
+ it('leaves out a tool that would take the name of one of the basic catalog’s functions', async () => {
901
+ const { errors, ui } = withTools([toolOf({ name: 'required' }, () => true)]);
902
+ await renderReply(ui, surfaceOf([{ component: 'Text', id: 'root', text: 'Here' }]));
903
+ await waitFor(() => expect(screen.getByText(/Here/)).toBeTruthy());
904
+ await waitFor(() =>
905
+ expect(errors).toEqual([expect.objectContaining({ code: 'CATALOG_ERROR' })]),
906
+ );
907
+ });
908
+ });
909
+
910
+ describe('the basic catalog’s icons', () => {
911
+ const pathOf = (id: string) =>
912
+ screen.getByTestId(`a2ui-${id}`).findAll((node) => typeof node.props.d === 'string')[0]?.props
913
+ .d as string | undefined;
914
+
915
+ it('draws every icon name the catalog has', () => {
916
+ const document = basicCatalogJSON.components.Icon as unknown as Record<string, unknown>;
917
+ const enumOf = (value: unknown): string[] | undefined => {
918
+ if (Array.isArray(value)) {
919
+ for (const item of value) {
920
+ const found = enumOf(item);
921
+ if (found) return found;
922
+ }
923
+ return undefined;
924
+ }
925
+ if (typeof value !== 'object' || value === null) return undefined;
926
+ const record = value as Record<string, unknown>;
927
+ if (Array.isArray(record.enum)) return record.enum as string[];
928
+ for (const item of Object.values(record)) {
929
+ const found = enumOf(item);
930
+ if (found) return found;
931
+ }
932
+ return undefined;
933
+ };
934
+ const catalogNames = enumOf(document) ?? [];
935
+ expect(catalogNames.length).toBeGreaterThan(50);
936
+ expect(catalogNames.filter((name) => !A2UI_ICON_PATHS[name])).toEqual([]);
937
+ });
938
+
939
+ // A name the catalog doesn't have fails validation before it is drawn, so only a path that
940
+ // isn't one falls back to the neutral icon.
941
+ it('draws a name’s Material icon, the agent’s own path, and a neutral one for a bad path', async () => {
942
+ const { ui } = setup();
943
+ await renderReply(
944
+ ui,
945
+ surfaceOf([
946
+ { children: ['cart', 'drawn', 'bad'], component: 'Row', id: 'root' },
947
+ { component: 'Icon', id: 'cart', name: 'shoppingCart' },
948
+ { component: 'Icon', id: 'drawn', name: { svgPath: 'M0 0h24v24H0z' } },
949
+ { component: 'Icon', id: 'bad', name: { svgPath: '<script>' } },
950
+ ]),
951
+ );
952
+ await waitFor(() => expect(pathOf('cart')).toBe(A2UI_ICON_PATHS.shoppingCart));
953
+ expect(pathOf('drawn')).toBe('M0 0h24v24H0z');
954
+ expect(pathOf('bad')).toBe(A2UI_ICON_PATHS.help);
955
+ });
956
+
957
+ it('draws the app’s own icon for a name it gives one', async () => {
958
+ const { ui } = setup({
959
+ icons: { shoppingCart: ({ size }) => <Text testID='my-cart'>{size}</Text> },
960
+ });
961
+ await renderReply(ui, surfaceOf([{ component: 'Icon', id: 'root', name: 'shoppingCart' }]));
962
+ await waitFor(() => expect(screen.getByTestId('my-cart')).toHaveTextContent('24'));
963
+ });
964
+ });
965
+
966
+ describe('video and audio in a reply’s UI', () => {
967
+ const media = surfaceOf([
968
+ { children: ['clip', 'note'], component: 'Column', id: 'root' },
969
+ {
970
+ component: 'Video',
971
+ id: 'clip',
972
+ posterUrl: 'https://example.com/poster.jpg',
973
+ url: 'https://example.com/clip.mp4',
974
+ },
975
+ {
976
+ component: 'AudioPlayer',
977
+ description: 'A song by the lake',
978
+ id: 'note',
979
+ url: 'https://example.com/song.mp3',
980
+ },
981
+ ]);
982
+ const handlers = { ...NativeHandlers };
983
+
984
+ afterEach(() => {
985
+ NativeHandlers.Sound = handlers.Sound;
986
+ NativeHandlers.Video = handlers.Video;
987
+ });
988
+
989
+ it('plays a video in place with the app’s player, from a tap', async () => {
990
+ const { ui } = setup();
991
+ await renderReply(ui, media);
992
+ const play = (await screen.findAllByRole('button', { name: 'Play' }))[0];
993
+ expect(screen.queryByTestId('a2ui-video')).toBeNull();
994
+ fireEvent.press(play);
995
+ expect(screen.getByTestId('a2ui-video').props).toMatchObject({
996
+ paused: false,
997
+ uri: 'https://example.com/clip.mp4',
998
+ });
999
+ });
1000
+
1001
+ it('plays and pauses a recording with the app’s audio player', async () => {
1002
+ const playAsync = jest.fn(() => Promise.resolve());
1003
+ const pauseAsync = jest.fn(() => Promise.resolve());
1004
+ const initializeSound = jest.fn(() =>
1005
+ Promise.resolve({ pauseAsync, playAsync, testID: 'sound' }),
1006
+ );
1007
+ NativeHandlers.Sound = { initializeSound, Player: null };
1008
+ const { ui } = setup();
1009
+ await renderReply(ui, media);
1010
+ expect(screen.getByText('A song by the lake')).toBeTruthy();
1011
+ fireEvent.press(screen.getAllByRole('button', { name: 'Play' })[1]);
1012
+ // Lets the player load and start, which it does asynchronously.
1013
+ await act(() => new Promise<void>((resolve) => setTimeout(resolve, 0)));
1014
+ expect(initializeSound).toHaveBeenCalledWith(
1015
+ { uri: 'https://example.com/song.mp3' },
1016
+ expect.any(Object),
1017
+ expect.any(Function),
1018
+ );
1019
+ expect(playAsync).toHaveBeenCalled();
1020
+ fireEvent.press(screen.getByRole('button', { name: 'Pause' }));
1021
+ // Lets the player load and start, which it does asynchronously.
1022
+ await act(() => new Promise<void>((resolve) => setTimeout(resolve, 0)));
1023
+ expect(pauseAsync).toHaveBeenCalled();
1024
+ });
1025
+
1026
+ it('links to them when the app has no player', async () => {
1027
+ NativeHandlers.Video = undefined;
1028
+ NativeHandlers.Sound = undefined;
1029
+ const { open, ui } = setup();
1030
+ await renderReply(ui, media);
1031
+ const links = await screen.findAllByRole('link');
1032
+ expect(links).toHaveLength(2);
1033
+ fireEvent.press(links[1]);
1034
+ expect(open).toHaveBeenCalledWith('https://example.com/song.mp3');
1035
+ });
1036
+ });
1037
+
1038
+ describe('a reply’s UI shown in the views of another’s, as a list reuses them', () => {
1039
+ const handlers = { ...NativeHandlers };
1040
+
1041
+ afterEach(() => {
1042
+ NativeHandlers.Sound = handlers.Sound;
1043
+ NativeHandlers.Video = handlers.Video;
1044
+ });
1045
+
1046
+ const view = (ui: AIUIHost, lines: string[], messageId: string) => (
1047
+ <StreamTheme>
1048
+ <AIUIView document={documentOf(lines)} host={ui} messageId={messageId} />
1049
+ </StreamTheme>
1050
+ );
1051
+
1052
+ const media = surfaceOf([
1053
+ { children: ['clip', 'note'], component: 'Column', id: 'root' },
1054
+ { component: 'Video', id: 'clip', url: 'https://example.com/clip.mp4' },
1055
+ { component: 'AudioPlayer', id: 'note', url: 'https://example.com/song.mp3' },
1056
+ ]);
1057
+
1058
+ // Lets a player load and start, which it does asynchronously.
1059
+ const loaded = () => act(() => new Promise<void>((resolve) => setTimeout(resolve, 0)));
1060
+
1061
+ it('opens tabs on the first for another reply, in the same views', async () => {
1062
+ const { ui } = setup();
1063
+ const tabs = (first: string, second: string) =>
1064
+ surfaceOf([
1065
+ {
1066
+ component: 'Tabs',
1067
+ id: 'root',
1068
+ tabs: [
1069
+ { child: 'one', title: 'One' },
1070
+ { child: 'two', title: 'Two' },
1071
+ ],
1072
+ },
1073
+ { component: 'Text', id: 'one', text: first },
1074
+ { component: 'Text', id: 'two', text: second },
1075
+ ]);
1076
+ const { rerender } = render(view(ui, tabs('Lake', 'Old town'), 'a1'));
1077
+ const tabsView = await screen.findByTestId('a2ui-root');
1078
+ fireEvent.press(screen.getByRole('tab', { name: 'Two' }));
1079
+ expect(screen.getByText('Old town')).toBeTruthy();
1080
+
1081
+ rerender(view(ui, tabs('Bridge', 'Market'), 'a2'));
1082
+ expect(await screen.findByText('Bridge')).toBeTruthy();
1083
+ expect(screen.queryByText('Market')).toBeNull();
1084
+ expect(screen.getByTestId('a2ui-root')).toBe(tabsView);
1085
+ });
1086
+
1087
+ it('stops what played for another reply', async () => {
1088
+ const stopAsync = jest.fn(() => Promise.resolve());
1089
+ const unloadAsync = jest.fn(() => Promise.resolve());
1090
+ const initializeSound = jest.fn(() =>
1091
+ Promise.resolve({
1092
+ pauseAsync: jest.fn(),
1093
+ playAsync: jest.fn(),
1094
+ stopAsync,
1095
+ testID: 'sound',
1096
+ unloadAsync,
1097
+ }),
1098
+ );
1099
+ NativeHandlers.Sound = { initializeSound, Player: null };
1100
+ NativeHandlers.Video = handlers.Video ?? (() => null);
1101
+ const { ui } = setup();
1102
+ const { rerender } = render(view(ui, media, 'a1'));
1103
+ const [playVideo, playSound] = await screen.findAllByRole('button', { name: 'Play' });
1104
+ fireEvent.press(playVideo);
1105
+ fireEvent.press(playSound);
1106
+ await loaded();
1107
+ expect(screen.getByTestId('a2ui-video')).toBeTruthy();
1108
+ expect(screen.getAllByRole('button', { name: 'Pause' })).toHaveLength(2);
1109
+
1110
+ rerender(view(ui, media, 'a2'));
1111
+ await loaded();
1112
+ expect(stopAsync).toHaveBeenCalled();
1113
+ expect(unloadAsync).toHaveBeenCalled();
1114
+ expect(screen.queryByTestId('a2ui-video')).toBeNull();
1115
+ expect(screen.queryByRole('button', { name: 'Pause' })).toBeNull();
1116
+ expect(screen.getAllByRole('button', { name: 'Play' })).toHaveLength(2);
1117
+ });
1118
+
1119
+ it('lets go of a recording that loads once it shows another reply', async () => {
1120
+ const playAsync = jest.fn(() => Promise.resolve());
1121
+ const stopAsync = jest.fn(() => Promise.resolve());
1122
+ const unloadAsync = jest.fn(() => Promise.resolve());
1123
+ type Loaded = Record<string, unknown> & { testID: string };
1124
+ let finishLoading: (sound: Loaded) => void = () => undefined;
1125
+ const initializeSound = jest.fn(
1126
+ () =>
1127
+ new Promise<Loaded>((resolve) => {
1128
+ finishLoading = resolve;
1129
+ }),
1130
+ );
1131
+ NativeHandlers.Sound = { initializeSound, Player: null };
1132
+ const { ui } = setup();
1133
+ const { rerender } = render(view(ui, media, 'a1'));
1134
+ const buttons = await screen.findAllByRole('button', { name: 'Play' });
1135
+ fireEvent.press(buttons[buttons.length - 1]);
1136
+
1137
+ rerender(view(ui, media, 'a2'));
1138
+ await act(async () => {
1139
+ finishLoading({ playAsync, stopAsync, testID: 'sound', unloadAsync });
1140
+ await Promise.resolve();
1141
+ });
1142
+ await loaded();
1143
+ expect(playAsync).not.toHaveBeenCalled();
1144
+ expect(stopAsync).toHaveBeenCalled();
1145
+ expect(unloadAsync).toHaveBeenCalled();
1146
+ expect(screen.queryByRole('button', { name: 'Pause' })).toBeNull();
1147
+ });
1148
+ });
1149
+
1150
+ describe('the media a reply’s UI loads', () => {
1151
+ it('tells public web links from others', () => {
1152
+ for (const url of [
1153
+ 'https://example.com/a.jpg',
1154
+ 'http://upload.wikimedia.org/a.jpg',
1155
+ 'https://user@cdn.example.com:8443/a.mp4',
1156
+ 'https://172.32.0.1/a.jpg',
1157
+ ]) {
1158
+ expect([url, isPublicWebURL(url)]).toEqual([url, true]);
1159
+ }
1160
+ for (const url of [
1161
+ 'http://localhost:8080/a.jpg',
1162
+ 'http://127.0.0.1/a.jpg',
1163
+ 'http://10.0.0.2/a.jpg',
1164
+ 'http://172.16.4.1/a.jpg',
1165
+ 'http://192.168.1.1/admin.png',
1166
+ 'http://169.254.169.254/latest',
1167
+ 'http://printer.local/a.jpg',
1168
+ 'http://router.internal./a.jpg',
1169
+ 'http://[::1]/a.jpg',
1170
+ 'http://[fe80::1]/a.jpg',
1171
+ 'http://user@192.168.1.1/a.jpg',
1172
+ 'data:image/png;base64,AAAA',
1173
+ 'file:///etc/hosts',
1174
+ 'https:///nohost.jpg',
1175
+ ]) {
1176
+ expect([url, isPublicWebURL(url)]).toEqual([url, false]);
1177
+ }
1178
+ expect(aiUIMediaSource(' https://example.com/a.jpg ')).toBe('https://example.com/a.jpg');
1179
+ expect(aiUIMediaSource(42)).toBeUndefined();
1180
+ });
1181
+
1182
+ const image = (url: string) => surfaceOf([{ component: 'Image', id: 'root', url }]);
1183
+ const imagesShown = () =>
1184
+ screen.UNSAFE_root.findAll(
1185
+ (node) => typeof node.props.source === 'object' && node.props.source?.uri !== undefined,
1186
+ );
1187
+
1188
+ it('loads an image from a public link, never from the person’s network', async () => {
1189
+ const { ui } = setup();
1190
+ await renderReply(ui, image('http://192.168.1.1/admin.png'));
1191
+ expect(imagesShown()).toHaveLength(0);
1192
+ const shown = setup();
1193
+ await renderReply(shown.ui, image('https://example.com/a.jpg'));
1194
+ expect(imagesShown()[0]?.props.source).toEqual({ uri: 'https://example.com/a.jpg' });
1195
+ });
1196
+
1197
+ it('loads what the app’s mediaUrl decides', async () => {
1198
+ const mediaUrl = jest.fn((url: string) => (url.startsWith('http://192.168.') ? url : null));
1199
+ const { ui } = setup({ mediaUrl });
1200
+ await renderReply(ui, image('http://192.168.1.5/photo.jpg'));
1201
+ expect(mediaUrl).toHaveBeenCalledWith('http://192.168.1.5/photo.jpg');
1202
+ expect(imagesShown()[0]?.props.source).toEqual({ uri: 'http://192.168.1.5/photo.jpg' });
1203
+ });
1204
+
1205
+ it('plays no video or sound from the person’s network', async () => {
1206
+ const { ui } = setup();
1207
+ await renderReply(
1208
+ ui,
1209
+ surfaceOf([
1210
+ { children: ['clip', 'note'], component: 'Column', id: 'root' },
1211
+ { component: 'Video', id: 'clip', url: 'http://10.0.0.2/clip.mp4' },
1212
+ { component: 'AudioPlayer', id: 'note', url: 'http://localhost/song.mp3' },
1213
+ ]),
1214
+ );
1215
+ expect(screen.queryAllByRole('button', { name: 'Play' })).toHaveLength(0);
1216
+ });
1217
+ });
1218
+
1219
+ describe('the basic catalog', () => {
1220
+ it('works out its functions, such as formatting text, numbers and dates', async () => {
1221
+ const { ui } = setup();
1222
+ await renderReply(
1223
+ ui,
1224
+ surfaceOf(
1225
+ [
1226
+ { children: ['greeting', 'price', 'day'], component: 'Column', id: 'root' },
1227
+ {
1228
+ component: 'Text',
1229
+ id: 'greeting',
1230
+ text: { '@call': 'formatString', args: { value: 'Hello, ${/name}' } },
1231
+ },
1232
+ {
1233
+ component: 'Text',
1234
+ id: 'price',
1235
+ text: {
1236
+ '@call': 'formatCurrency',
1237
+ args: { currency: 'eur', value: { '@path': '/price' } },
1238
+ },
1239
+ },
1240
+ {
1241
+ component: 'Text',
1242
+ id: 'day',
1243
+ text: { '@call': 'formatDate', args: { format: 'EEEE d MMMM', value: '2026-11-14' } },
1244
+ },
1245
+ ],
1246
+ { name: 'Ana', price: 42 },
1247
+ ),
1248
+ );
1249
+ await waitFor(() => expect(screen.getByText(/Hello, Ana/)).toBeTruthy());
1250
+ expect(screen.getByText(/€42\.00/)).toBeTruthy();
1251
+ expect(screen.getByText(/Saturday 14 November/)).toBeTruthy();
1252
+ });
1253
+
1254
+ it('keeps what the person types in the data model, and holds a button until its checks pass', async () => {
1255
+ const { sent, ui } = setup();
1256
+ await renderReply(
1257
+ ui,
1258
+ surfaceOf(
1259
+ [
1260
+ { children: ['email', 'send'], component: 'Column', id: 'root' },
1261
+ {
1262
+ checks: [
1263
+ {
1264
+ condition: { '@call': 'email', args: { value: { '@path': '/email' } } },
1265
+ message: 'An email, please',
1266
+ },
1267
+ ],
1268
+ component: 'TextField',
1269
+ id: 'email',
1270
+ label: 'Email',
1271
+ value: { '@path': '/email' },
1272
+ },
1273
+ ...button(
1274
+ 'send',
1275
+ 'Send',
1276
+ { event: { context: { email: { '@path': '/email' } }, name: 'subscribe' } },
1277
+ {
1278
+ checks: [
1279
+ {
1280
+ condition: { '@call': 'email', args: { value: { '@path': '/email' } } },
1281
+ message: 'An email, please',
1282
+ },
1283
+ ],
1284
+ variant: 'primary',
1285
+ },
1286
+ ),
1287
+ ],
1288
+ { email: '' },
1289
+ ),
1290
+ );
1291
+ const send = await screen.findByRole('button', { name: 'Send' });
1292
+ expect(send).toBeDisabled();
1293
+ fireEvent.changeText(screen.getByLabelText('Email'), 'ana@example.com');
1294
+ await waitFor(() => expect(screen.getByRole('button', { name: 'Send' })).toBeEnabled());
1295
+ fireEvent.press(screen.getByRole('button', { name: 'Send' }));
1296
+ await waitFor(() => expect(sent).toHaveLength(1));
1297
+ expect(sent[0]?.payload.context).toEqual({ email: 'ana@example.com' });
1298
+ });
1299
+
1300
+ it('chooses one option or several, as chips or check marks', async () => {
1301
+ const { sent, ui } = setup();
1302
+ await renderReply(
1303
+ ui,
1304
+ surfaceOf(
1305
+ [
1306
+ { children: ['one', 'many', 'go'], component: 'Column', id: 'root' },
1307
+ {
1308
+ component: 'ChoicePicker',
1309
+ displayStyle: 'chips',
1310
+ id: 'one',
1311
+ options: [
1312
+ { label: 'Relaxed', value: 'relaxed' },
1313
+ { label: 'Packed', value: 'packed' },
1314
+ ],
1315
+ value: { '@path': '/pace' },
1316
+ variant: 'mutuallyExclusive',
1317
+ },
1318
+ {
1319
+ component: 'ChoicePicker',
1320
+ id: 'many',
1321
+ options: [
1322
+ { label: 'Food', value: 'food' },
1323
+ { label: 'History', value: 'history' },
1324
+ ],
1325
+ value: { '@path': '/more' },
1326
+ variant: 'multipleSelection',
1327
+ },
1328
+ ...button('go', 'Go', {
1329
+ event: {
1330
+ context: { more: { '@path': '/more' }, pace: { '@path': '/pace' } },
1331
+ name: 'go',
1332
+ },
1333
+ }),
1334
+ ],
1335
+ { more: [], pace: ['relaxed'] },
1336
+ ),
1337
+ );
1338
+ fireEvent.press(await screen.findByRole('radio', { name: 'Packed' }));
1339
+ fireEvent.press(screen.getByRole('checkbox', { name: 'Food' }));
1340
+ fireEvent.press(screen.getByRole('checkbox', { name: 'History' }));
1341
+ fireEvent.press(screen.getByRole('checkbox', { name: 'Food' }));
1342
+ fireEvent.press(screen.getByRole('button', { name: 'Go' }));
1343
+ await waitFor(() => expect(sent).toHaveLength(1));
1344
+ expect(sent[0]?.payload.context).toEqual({ more: ['history'], pace: ['packed'] });
1345
+ });
1346
+
1347
+ it('switches its tabs', async () => {
1348
+ const { ui } = setup();
1349
+ await renderReply(ui, ohridLines);
1350
+ await waitFor(() => expect(screen.getByText(/coffee by the harbour/)).toBeTruthy());
1351
+ fireEvent.press(screen.getByRole('tab', { name: 'Sunday' }));
1352
+ expect(screen.getByText(/the boat from the harbour to St\. Naum/)).toBeTruthy();
1353
+ expect(screen.queryByText(/coffee by the harbour/)).toBeNull();
1354
+ });
1355
+
1356
+ it('parts the borderless buttons of a Column, its follow-ups, with hairlines', async () => {
1357
+ const { ui } = setup();
1358
+ await renderReply(ui, [
1359
+ ...ohridLines,
1360
+ line({
1361
+ updateComponents: {
1362
+ components: [
1363
+ { children: ['inline'], component: 'Row', id: 'tip' },
1364
+ ...button('inline', 'A link', { event: { name: 'ask' } }, { variant: 'borderless' }),
1365
+ ],
1366
+ surfaceId: 'ohrid-weekend',
1367
+ },
1368
+ }),
1369
+ ]);
1370
+ const hairlineOf = (id: string) =>
1371
+ StyleSheet.flatten(
1372
+ screen.getByTestId(`a2ui-${id}`).findAll((node) => node.props.style !== undefined)[1]?.props
1373
+ .style,
1374
+ )?.borderTopWidth;
1375
+ await waitFor(() => expect(screen.getByTestId('a2ui-hotel')).toBeTruthy());
1376
+ expect(hairlineOf('hotel')).toBeUndefined();
1377
+ expect(hairlineOf('eat')).toBe(StyleSheet.hairlineWidth);
1378
+ expect(hairlineOf('longer')).toBe(StyleSheet.hairlineWidth);
1379
+ expect(hairlineOf('inline')).toBeUndefined();
1380
+ });
1381
+
1382
+ it('picks a date and a time in a sheet, within its bounds, and writes them as ISO 8601', async () => {
1383
+ const { sent, ui } = setup();
1384
+ await renderReply(
1385
+ ui,
1386
+ surfaceOf(
1387
+ [
1388
+ { children: ['when', 'book'], component: 'Column', id: 'root' },
1389
+ {
1390
+ component: 'DateTimeInput',
1391
+ enableDate: true,
1392
+ enableTime: true,
1393
+ id: 'when',
1394
+ label: 'Departure',
1395
+ min: '2026-11-10',
1396
+ value: { '@path': '/when' },
1397
+ },
1398
+ ...button('book', 'Book', {
1399
+ event: { context: { when: { '@path': '/when' } }, name: 'book' },
1400
+ }),
1401
+ ],
1402
+ { when: '2026-11-12T08:00' },
1403
+ ),
1404
+ );
1405
+ fireEvent.press(await screen.findByRole('button', { name: /Nov 12, 2026/ }));
1406
+ expect(screen.getByText('November 2026')).toBeTruthy();
1407
+ expect(screen.getByRole('button', { name: 'November 9, 2026' })).toBeDisabled();
1408
+ fireEvent.press(screen.getByRole('button', { name: 'November 14, 2026' }));
1409
+ fireEvent.press(screen.getByRole('button', { name: '09' }));
1410
+ // The minutes come after the calendar, whose 30th says 30 too.
1411
+ fireEvent.press(screen.getAllByRole('button', { name: '30' }).at(-1)!);
1412
+ fireEvent.press(screen.getAllByRole('button', { name: 'Close' })[0]);
1413
+ fireEvent.press(screen.getByRole('button', { name: 'Book' }));
1414
+ await waitFor(() => expect(sent).toHaveLength(1));
1415
+ expect(sent[0]?.payload.context).toEqual({ when: '2026-11-14T09:30' });
1416
+ });
1417
+
1418
+ it('opens the time lists on the time it holds', async () => {
1419
+ const { ui } = setup();
1420
+ await renderReply(
1421
+ ui,
1422
+ surfaceOf(
1423
+ [
1424
+ {
1425
+ component: 'DateTimeInput',
1426
+ enableTime: true,
1427
+ id: 'root',
1428
+ label: 'Departure',
1429
+ value: { '@path': '/when' },
1430
+ },
1431
+ ],
1432
+ { when: '09:00' },
1433
+ ),
1434
+ );
1435
+ fireEvent.press(await screen.findByRole('button', { name: /09:00|9:00/ }));
1436
+ const [hours] = screen.UNSAFE_getAllByType(ScrollView);
1437
+ const scrollTo = hours.instance.scrollTo as jest.Mock;
1438
+ scrollTo.mockClear();
1439
+ const nine = screen.getByRole('button', { name: '09', selected: true });
1440
+ const layout = { nativeEvent: { layout: { height: 36, width: 100, x: 0, y: 342 } } };
1441
+ fireEvent(nine, 'layout', layout);
1442
+ // The chosen hour lands in the middle of the 180 point tall list, and only the first time.
1443
+ expect(scrollTo).toHaveBeenCalledWith({ animated: false, y: 342 - (180 - 36) / 2 });
1444
+ fireEvent(nine, 'layout', layout);
1445
+ expect(scrollTo).toHaveBeenCalledTimes(1);
1446
+ });
1447
+
1448
+ it('takes its colors and the look of its parts from the theme', async () => {
1449
+ const { ui } = setup();
1450
+ render(
1451
+ <StreamTheme style={{ a2ui: { card: { borderWidth: 3 }, colors: { accent: '#ff0000' } } }}>
1452
+ <AIUIView
1453
+ document={documentOf(
1454
+ surfaceOf([
1455
+ { child: 'go', component: 'Card', id: 'root' },
1456
+ ...button('go', 'Go', { event: { name: 'go' } }, { variant: 'primary' }),
1457
+ ]),
1458
+ )}
1459
+ host={ui}
1460
+ messageId='a1'
1461
+ />
1462
+ </StreamTheme>,
1463
+ );
1464
+ await act(() => Promise.resolve());
1465
+ const go = await screen.findByRole('button', { name: 'Go' });
1466
+ expect(StyleSheet.flatten(go.props.style)).toMatchObject({ backgroundColor: '#ff0000' });
1467
+ const card = screen
1468
+ .getByTestId('a2ui-root')
1469
+ .findAll((node) => StyleSheet.flatten(node.props.style)?.borderWidth === 3);
1470
+ expect(card.length).toBeGreaterThan(0);
1471
+ });
1472
+
1473
+ it('takes the app’s own views for its components', async () => {
1474
+ const { ui } = setup({
1475
+ components: {
1476
+ Card: ({ props }) => <Text testID='my-card'>{String(Boolean(props.child))}</Text>,
1477
+ },
1478
+ });
1479
+ await renderReply(
1480
+ ui,
1481
+ surfaceOf([
1482
+ { child: 'inner', component: 'Card', id: 'root' },
1483
+ { component: 'Text', id: 'inner', text: 'x' },
1484
+ ]),
1485
+ );
1486
+ await waitFor(() => expect(screen.getByTestId('my-card')).toHaveTextContent('true'));
1487
+ });
1488
+
1489
+ it('leaves out an app’s component that would take one of the basic catalog’s names', async () => {
1490
+ const definition = parseAIUIComponentDefinition({
1491
+ description: 'A card.',
1492
+ name: 'card',
1493
+ parameters: {},
1494
+ });
1495
+ if (!definition) throw new Error('no definition');
1496
+ const errors: { code: string }[] = [];
1497
+ const ui = new AIUIHost({
1498
+ components: [defineAIUIComponent(definition, () => <Text testID='app-card'>App</Text>)],
1499
+ renderers: [createA2UIRenderer({ onError: (error) => errors.push(error) })],
1500
+ send: jest.fn(),
1501
+ });
1502
+ await renderReply(
1503
+ ui,
1504
+ surfaceOf([
1505
+ { child: 'inner', component: 'Card', id: 'root' },
1506
+ { component: 'Text', id: 'inner', text: 'Basic card' },
1507
+ ]),
1508
+ );
1509
+ await waitFor(() => expect(screen.getByText(/Basic card/)).toBeTruthy());
1510
+ expect(screen.queryByTestId('app-card')).toBeNull();
1511
+ await act(() => Promise.resolve());
1512
+ expect(errors).toEqual([expect.objectContaining({ code: 'CATALOG_ERROR' })]);
1513
+ });
1514
+ });