@tambo-ai/react 0.73.1 → 0.74.1

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 (407) hide show
  1. package/README.md +12 -12
  2. package/dist/hooks/use-component-state.d.ts +1 -1
  3. package/dist/hooks/use-component-state.js.map +1 -1
  4. package/dist/hooks/use-streaming-props.d.ts +1 -1
  5. package/dist/hooks/use-streaming-props.js +1 -1
  6. package/dist/hooks/use-streaming-props.js.map +1 -1
  7. package/dist/hooks/use-tambo-stream-status.d.ts +1 -1
  8. package/dist/hooks/use-tambo-stream-status.js +1 -1
  9. package/dist/hooks/use-tambo-stream-status.js.map +1 -1
  10. package/dist/mcp/mcp-hooks.d.ts.map +1 -1
  11. package/dist/mcp/mcp-hooks.js +70 -16
  12. package/dist/mcp/mcp-hooks.js.map +1 -1
  13. package/dist/providers/tambo-interactable-provider-partial-updates.test.js +3 -3
  14. package/dist/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
  15. package/dist/providers/tambo-interactable-provider.js +2 -2
  16. package/dist/providers/tambo-interactable-provider.js.map +1 -1
  17. package/dist/providers/tambo-interactable-provider.test.js +3 -3
  18. package/dist/providers/tambo-interactable-provider.test.js.map +1 -1
  19. package/dist/providers/tambo-thread-input-provider.d.ts.map +1 -1
  20. package/dist/providers/tambo-thread-input-provider.js +1 -0
  21. package/dist/providers/tambo-thread-input-provider.js.map +1 -1
  22. package/dist/v1/__tests__/v1-interactables.test.d.ts +2 -0
  23. package/dist/v1/__tests__/v1-interactables.test.d.ts.map +1 -0
  24. package/dist/v1/__tests__/v1-interactables.test.js +135 -0
  25. package/dist/v1/__tests__/v1-interactables.test.js.map +1 -0
  26. package/dist/v1/components/v1-component-renderer.d.ts +48 -0
  27. package/dist/v1/components/v1-component-renderer.d.ts.map +1 -0
  28. package/dist/v1/components/v1-component-renderer.js +137 -0
  29. package/dist/v1/components/v1-component-renderer.js.map +1 -0
  30. package/dist/v1/components/v1-component-renderer.test.d.ts +2 -0
  31. package/dist/v1/components/v1-component-renderer.test.d.ts.map +1 -0
  32. package/dist/v1/components/v1-component-renderer.test.js +270 -0
  33. package/dist/v1/components/v1-component-renderer.test.js.map +1 -0
  34. package/dist/v1/hooks/use-tambo-v1-component-state.d.ts.map +1 -1
  35. package/dist/v1/hooks/use-tambo-v1-component-state.js +2 -25
  36. package/dist/v1/hooks/use-tambo-v1-component-state.js.map +1 -1
  37. package/dist/v1/hooks/use-tambo-v1-component-state.test.js +2 -1
  38. package/dist/v1/hooks/use-tambo-v1-component-state.test.js.map +1 -1
  39. package/dist/v1/hooks/use-tambo-v1-messages.test.js +25 -1
  40. package/dist/v1/hooks/use-tambo-v1-messages.test.js.map +1 -1
  41. package/dist/v1/hooks/use-tambo-v1-send-message.d.ts +18 -0
  42. package/dist/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -1
  43. package/dist/v1/hooks/use-tambo-v1-send-message.js +204 -17
  44. package/dist/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
  45. package/dist/v1/hooks/use-tambo-v1-send-message.test.js +261 -7
  46. package/dist/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -1
  47. package/dist/v1/hooks/use-tambo-v1-stream-status.d.ts +90 -0
  48. package/dist/v1/hooks/use-tambo-v1-stream-status.d.ts.map +1 -0
  49. package/dist/v1/hooks/use-tambo-v1-stream-status.js +179 -0
  50. package/dist/v1/hooks/use-tambo-v1-stream-status.js.map +1 -0
  51. package/dist/v1/hooks/use-tambo-v1-stream-status.test.d.ts +2 -0
  52. package/dist/v1/hooks/use-tambo-v1-stream-status.test.d.ts.map +1 -0
  53. package/dist/v1/hooks/use-tambo-v1-stream-status.test.js +371 -0
  54. package/dist/v1/hooks/use-tambo-v1-stream-status.test.js.map +1 -0
  55. package/dist/v1/hooks/use-tambo-v1-suggestions.d.ts +78 -54
  56. package/dist/v1/hooks/use-tambo-v1-suggestions.d.ts.map +1 -1
  57. package/dist/v1/hooks/use-tambo-v1-suggestions.js +153 -87
  58. package/dist/v1/hooks/use-tambo-v1-suggestions.js.map +1 -1
  59. package/dist/v1/hooks/use-tambo-v1-suggestions.test.js +213 -134
  60. package/dist/v1/hooks/use-tambo-v1-suggestions.test.js.map +1 -1
  61. package/dist/v1/hooks/use-tambo-v1-thread-input.test.js +148 -13
  62. package/dist/v1/hooks/use-tambo-v1-thread-input.test.js.map +1 -1
  63. package/dist/v1/hooks/use-tambo-v1-thread-list.d.ts +8 -21
  64. package/dist/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -1
  65. package/dist/v1/hooks/use-tambo-v1-thread-list.js +11 -10
  66. package/dist/v1/hooks/use-tambo-v1-thread-list.js.map +1 -1
  67. package/dist/v1/hooks/use-tambo-v1-thread-list.test.js +37 -2
  68. package/dist/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -1
  69. package/dist/v1/hooks/use-tambo-v1-thread.d.ts +1 -1
  70. package/dist/v1/hooks/use-tambo-v1-thread.d.ts.map +1 -1
  71. package/dist/v1/hooks/use-tambo-v1-thread.js +2 -7
  72. package/dist/v1/hooks/use-tambo-v1-thread.js.map +1 -1
  73. package/dist/v1/hooks/use-tambo-v1-thread.test.js +2 -0
  74. package/dist/v1/hooks/use-tambo-v1-thread.test.js.map +1 -1
  75. package/dist/v1/hooks/use-tambo-v1.d.ts +12 -28
  76. package/dist/v1/hooks/use-tambo-v1.d.ts.map +1 -1
  77. package/dist/v1/hooks/use-tambo-v1.js +164 -31
  78. package/dist/v1/hooks/use-tambo-v1.js.map +1 -1
  79. package/dist/v1/hooks/use-tambo-v1.test.js +891 -18
  80. package/dist/v1/hooks/use-tambo-v1.test.js.map +1 -1
  81. package/dist/v1/index.d.ts +7 -1
  82. package/dist/v1/index.d.ts.map +1 -1
  83. package/dist/v1/index.js +18 -1
  84. package/dist/v1/index.js.map +1 -1
  85. package/dist/v1/providers/tambo-v1-provider.d.ts +16 -6
  86. package/dist/v1/providers/tambo-v1-provider.d.ts.map +1 -1
  87. package/dist/v1/providers/tambo-v1-provider.js +14 -19
  88. package/dist/v1/providers/tambo-v1-provider.js.map +1 -1
  89. package/dist/v1/providers/tambo-v1-provider.test.js +34 -20
  90. package/dist/v1/providers/tambo-v1-provider.test.js.map +1 -1
  91. package/dist/v1/providers/tambo-v1-stream-context.d.ts +3 -3
  92. package/dist/v1/providers/tambo-v1-stream-context.d.ts.map +1 -1
  93. package/dist/v1/providers/tambo-v1-stream-context.js +60 -12
  94. package/dist/v1/providers/tambo-v1-stream-context.js.map +1 -1
  95. package/dist/v1/providers/tambo-v1-stream-context.test.js +49 -20
  96. package/dist/v1/providers/tambo-v1-stream-context.test.js.map +1 -1
  97. package/dist/v1/providers/tambo-v1-stub-provider.d.ts.map +1 -1
  98. package/dist/v1/providers/tambo-v1-stub-provider.js +2 -0
  99. package/dist/v1/providers/tambo-v1-stub-provider.js.map +1 -1
  100. package/dist/v1/providers/tambo-v1-stub-provider.test.js +7 -6
  101. package/dist/v1/providers/tambo-v1-stub-provider.test.js.map +1 -1
  102. package/dist/v1/providers/tambo-v1-thread-input-provider.d.ts +1 -6
  103. package/dist/v1/providers/tambo-v1-thread-input-provider.d.ts.map +1 -1
  104. package/dist/v1/providers/tambo-v1-thread-input-provider.js +14 -12
  105. package/dist/v1/providers/tambo-v1-thread-input-provider.js.map +1 -1
  106. package/dist/v1/types/event.d.ts +9 -1
  107. package/dist/v1/types/event.d.ts.map +1 -1
  108. package/dist/v1/types/event.js.map +1 -1
  109. package/dist/v1/types/event.test.js +5 -1
  110. package/dist/v1/types/event.test.js.map +1 -1
  111. package/dist/v1/types/message.d.ts +65 -7
  112. package/dist/v1/types/message.d.ts.map +1 -1
  113. package/dist/v1/types/message.js.map +1 -1
  114. package/dist/v1/types/thread.d.ts +4 -0
  115. package/dist/v1/types/thread.d.ts.map +1 -1
  116. package/dist/v1/types/thread.js.map +1 -1
  117. package/dist/v1/utils/event-accumulator.d.ts +40 -4
  118. package/dist/v1/utils/event-accumulator.d.ts.map +1 -1
  119. package/dist/v1/utils/event-accumulator.js +444 -35
  120. package/dist/v1/utils/event-accumulator.js.map +1 -1
  121. package/dist/v1/utils/event-accumulator.test.js +1041 -28
  122. package/dist/v1/utils/event-accumulator.test.js.map +1 -1
  123. package/dist/v1/utils/registry-conversion.d.ts +9 -9
  124. package/dist/v1/utils/registry-conversion.d.ts.map +1 -1
  125. package/dist/v1/utils/registry-conversion.js +10 -11
  126. package/dist/v1/utils/registry-conversion.js.map +1 -1
  127. package/dist/v1/utils/registry-conversion.test.js +39 -11
  128. package/dist/v1/utils/registry-conversion.test.js.map +1 -1
  129. package/dist/v1/utils/thread-utils.d.ts +16 -0
  130. package/dist/v1/utils/thread-utils.d.ts.map +1 -0
  131. package/dist/v1/utils/thread-utils.js +34 -0
  132. package/dist/v1/utils/thread-utils.js.map +1 -0
  133. package/dist/v1/utils/tool-executor.d.ts.map +1 -1
  134. package/dist/v1/utils/tool-executor.js +2 -0
  135. package/dist/v1/utils/tool-executor.js.map +1 -1
  136. package/dist/v1/utils/tool-executor.test.js +5 -0
  137. package/dist/v1/utils/tool-executor.test.js.map +1 -1
  138. package/esm/context-helpers/context-helpers-provider.test.js +2 -2
  139. package/esm/context-helpers/context-helpers.test.js +1 -1
  140. package/esm/context-helpers/current-interactables-context-helper.d.ts +1 -1
  141. package/esm/context-helpers/current-page-context-helper.d.ts +1 -1
  142. package/esm/context-helpers/current-time-context-helper.d.ts +1 -1
  143. package/esm/context-helpers/index.d.ts +4 -4
  144. package/esm/context-helpers/index.js +4 -4
  145. package/esm/hoc/with-tambo-interactable.d.ts +1 -1
  146. package/esm/hoc/with-tambo-interactable.js +2 -2
  147. package/esm/hoc/with-tambo-interactable.test.js +3 -3
  148. package/esm/hooks/index.d.ts +8 -8
  149. package/esm/hooks/index.js +8 -8
  150. package/esm/hooks/react-query-hooks.js +1 -1
  151. package/esm/hooks/use-component-state.d.ts +1 -1
  152. package/esm/hooks/use-component-state.js +3 -3
  153. package/esm/hooks/use-component-state.js.map +1 -1
  154. package/esm/hooks/use-component-state.test.js +5 -5
  155. package/esm/hooks/use-current-message.d.ts +1 -1
  156. package/esm/hooks/use-current-message.test.js +1 -1
  157. package/esm/hooks/use-message-images.test.js +1 -1
  158. package/esm/hooks/use-streaming-props.d.ts +1 -1
  159. package/esm/hooks/use-streaming-props.js +1 -1
  160. package/esm/hooks/use-streaming-props.js.map +1 -1
  161. package/esm/hooks/use-suggestions.d.ts +2 -2
  162. package/esm/hooks/use-suggestions.js +10 -10
  163. package/esm/hooks/use-suggestions.test.js +7 -7
  164. package/esm/hooks/use-tambo-stream-status.d.ts +1 -1
  165. package/esm/hooks/use-tambo-stream-status.js +4 -4
  166. package/esm/hooks/use-tambo-stream-status.js.map +1 -1
  167. package/esm/hooks/use-tambo-stream-status.test.js +4 -4
  168. package/esm/hooks/use-tambo-threads.js +3 -3
  169. package/esm/hooks/use-tambo-threads.test.js +3 -3
  170. package/esm/hooks/use-tambo-voice.js +2 -2
  171. package/esm/hooks/use-tambo-voice.test.js +3 -3
  172. package/esm/index.d.ts +22 -22
  173. package/esm/index.js +15 -15
  174. package/esm/mcp/elicitation.d.ts +1 -1
  175. package/esm/mcp/elicitation.test.js +1 -1
  176. package/esm/mcp/index.d.ts +7 -7
  177. package/esm/mcp/index.js +3 -3
  178. package/esm/mcp/mcp-client.d.ts +1 -1
  179. package/esm/mcp/mcp-client.js +1 -1
  180. package/esm/mcp/mcp-client.test.js +1 -1
  181. package/esm/mcp/mcp-hooks.d.ts +1 -1
  182. package/esm/mcp/mcp-hooks.d.ts.map +1 -1
  183. package/esm/mcp/mcp-hooks.js +74 -20
  184. package/esm/mcp/mcp-hooks.js.map +1 -1
  185. package/esm/mcp/mcp-hooks.test.js +6 -6
  186. package/esm/mcp/tambo-mcp-provider.d.ts +4 -4
  187. package/esm/mcp/tambo-mcp-provider.js +7 -7
  188. package/esm/mcp/tambo-mcp-provider.test.js +5 -5
  189. package/esm/mcp/use-mcp-servers.test.js +4 -4
  190. package/esm/model/generate-component-response.d.ts +1 -1
  191. package/esm/model/tambo-interactable.d.ts +1 -1
  192. package/esm/model/tambo-thread.d.ts +1 -1
  193. package/esm/providers/__tests__/thread-input-resource-resolution.test.js +3 -3
  194. package/esm/providers/hooks/use-tambo-session-token.test.js +1 -1
  195. package/esm/providers/index.d.ts +12 -12
  196. package/esm/providers/index.js +10 -10
  197. package/esm/providers/tambo-client-provider.js +1 -1
  198. package/esm/providers/tambo-client-provider.test.js +2 -2
  199. package/esm/providers/tambo-component-provider.d.ts +1 -1
  200. package/esm/providers/tambo-component-provider.js +2 -2
  201. package/esm/providers/tambo-context-attachment-provider.js +1 -1
  202. package/esm/providers/tambo-context-attachment-provider.test.js +2 -2
  203. package/esm/providers/tambo-context-helpers-provider.d.ts +1 -1
  204. package/esm/providers/tambo-context-helpers-provider.js +1 -1
  205. package/esm/providers/tambo-context-helpers-provider.test.js +2 -2
  206. package/esm/providers/tambo-interactable-provider-partial-updates.test.js +4 -4
  207. package/esm/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
  208. package/esm/providers/tambo-interactable-provider.d.ts +5 -5
  209. package/esm/providers/tambo-interactable-provider.js +6 -6
  210. package/esm/providers/tambo-interactable-provider.js.map +1 -1
  211. package/esm/providers/tambo-interactable-provider.test.js +4 -4
  212. package/esm/providers/tambo-interactable-provider.test.js.map +1 -1
  213. package/esm/providers/tambo-interactables-additional-context-edge-cases.test.js +4 -4
  214. package/esm/providers/tambo-interactables-additional-context.test.js +4 -4
  215. package/esm/providers/tambo-mcp-token-provider.js +2 -2
  216. package/esm/providers/tambo-prop-stream-provider/index.d.ts +8 -8
  217. package/esm/providers/tambo-prop-stream-provider/index.js +9 -9
  218. package/esm/providers/tambo-prop-stream-provider/pending.d.ts +1 -1
  219. package/esm/providers/tambo-prop-stream-provider/pending.js +2 -2
  220. package/esm/providers/tambo-prop-stream-provider/provider.d.ts +1 -1
  221. package/esm/providers/tambo-prop-stream-provider/provider.js +2 -2
  222. package/esm/providers/tambo-prop-stream-provider/streaming.d.ts +1 -1
  223. package/esm/providers/tambo-prop-stream-provider/streaming.js +2 -2
  224. package/esm/providers/tambo-prop-stream-provider/success.d.ts +1 -1
  225. package/esm/providers/tambo-prop-stream-provider/success.js +2 -2
  226. package/esm/providers/tambo-prop-stream-provider/types.d.ts +1 -1
  227. package/esm/providers/tambo-prop-stream-provider.test.js +4 -4
  228. package/esm/providers/tambo-provider.d.ts +7 -7
  229. package/esm/providers/tambo-provider.js +10 -10
  230. package/esm/providers/tambo-registry-provider.d.ts +3 -3
  231. package/esm/providers/tambo-registry-provider.js +3 -3
  232. package/esm/providers/tambo-registry-provider.test.js +2 -2
  233. package/esm/providers/tambo-registry-schema-compat.test.js +2 -2
  234. package/esm/providers/tambo-stubs.d.ts +4 -4
  235. package/esm/providers/tambo-stubs.js +9 -9
  236. package/esm/providers/tambo-stubs.test.js +2 -2
  237. package/esm/providers/tambo-thread-input-provider.d.ts +2 -2
  238. package/esm/providers/tambo-thread-input-provider.d.ts.map +1 -1
  239. package/esm/providers/tambo-thread-input-provider.js +11 -10
  240. package/esm/providers/tambo-thread-input-provider.js.map +1 -1
  241. package/esm/providers/tambo-thread-provider-initial-messages.test.js +6 -6
  242. package/esm/providers/tambo-thread-provider.d.ts +2 -2
  243. package/esm/providers/tambo-thread-provider.js +8 -8
  244. package/esm/providers/tambo-thread-provider.test.js +7 -7
  245. package/esm/schema/index.d.ts +4 -4
  246. package/esm/schema/index.js +4 -4
  247. package/esm/schema/json-schema.test.js +1 -1
  248. package/esm/schema/schema.d.ts +1 -1
  249. package/esm/schema/schema.js +2 -2
  250. package/esm/schema/schema.test.js +3 -3
  251. package/esm/schema/standard-schema.test.js +1 -1
  252. package/esm/schema/validate.js +2 -2
  253. package/esm/schema/validate.test.js +1 -1
  254. package/esm/testing/tools.d.ts +3 -3
  255. package/esm/testing/tools.js +2 -2
  256. package/esm/util/content-parts.test.js +1 -1
  257. package/esm/util/generate-component.d.ts +2 -2
  258. package/esm/util/generate-component.js +4 -4
  259. package/esm/util/generate-component.test.js +2 -2
  260. package/esm/util/is-promise.test.js +1 -1
  261. package/esm/util/mcp-server-utils.d.ts +1 -1
  262. package/esm/util/mcp-server-utils.js +1 -1
  263. package/esm/util/mcp-server-utils.test.js +2 -2
  264. package/esm/util/message-builder.d.ts +1 -1
  265. package/esm/util/message-builder.test.js +1 -1
  266. package/esm/util/query-utils.test.js +1 -1
  267. package/esm/util/registry-validators.d.ts +1 -1
  268. package/esm/util/registry-validators.js +2 -2
  269. package/esm/util/registry-validators.test.js +1 -1
  270. package/esm/util/registry.d.ts +1 -1
  271. package/esm/util/registry.js +1 -1
  272. package/esm/util/registry.test.js +2 -2
  273. package/esm/util/resource-content-resolver.d.ts +2 -2
  274. package/esm/util/resource-content-resolver.js +1 -1
  275. package/esm/util/resource-content-resolver.test.js +3 -3
  276. package/esm/util/resource-validators.d.ts +1 -1
  277. package/esm/util/resource-validators.test.js +1 -1
  278. package/esm/util/tool-caller.d.ts +1 -1
  279. package/esm/util/tool-caller.js +1 -1
  280. package/esm/util/validate-component-name.test.js +1 -1
  281. package/esm/v1/__tests__/v1-interactables.test.d.ts +2 -0
  282. package/esm/v1/__tests__/v1-interactables.test.d.ts.map +1 -0
  283. package/esm/v1/__tests__/v1-interactables.test.js +130 -0
  284. package/esm/v1/__tests__/v1-interactables.test.js.map +1 -0
  285. package/esm/v1/components/v1-component-renderer.d.ts +48 -0
  286. package/esm/v1/components/v1-component-renderer.d.ts.map +1 -0
  287. package/esm/v1/components/v1-component-renderer.js +100 -0
  288. package/esm/v1/components/v1-component-renderer.js.map +1 -0
  289. package/esm/v1/components/v1-component-renderer.test.d.ts +2 -0
  290. package/esm/v1/components/v1-component-renderer.test.d.ts.map +1 -0
  291. package/esm/v1/components/v1-component-renderer.test.js +265 -0
  292. package/esm/v1/components/v1-component-renderer.test.js.map +1 -0
  293. package/esm/v1/hooks/use-tambo-v1-component-state.d.ts.map +1 -1
  294. package/esm/v1/hooks/use-tambo-v1-component-state.js +4 -27
  295. package/esm/v1/hooks/use-tambo-v1-component-state.js.map +1 -1
  296. package/esm/v1/hooks/use-tambo-v1-component-state.test.js +6 -5
  297. package/esm/v1/hooks/use-tambo-v1-component-state.test.js.map +1 -1
  298. package/esm/v1/hooks/use-tambo-v1-messages.d.ts +1 -1
  299. package/esm/v1/hooks/use-tambo-v1-messages.js +1 -1
  300. package/esm/v1/hooks/use-tambo-v1-messages.test.js +27 -3
  301. package/esm/v1/hooks/use-tambo-v1-messages.test.js.map +1 -1
  302. package/esm/v1/hooks/use-tambo-v1-send-message.d.ts +20 -2
  303. package/esm/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -1
  304. package/esm/v1/hooks/use-tambo-v1-send-message.js +213 -26
  305. package/esm/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
  306. package/esm/v1/hooks/use-tambo-v1-send-message.test.js +266 -12
  307. package/esm/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -1
  308. package/esm/v1/hooks/use-tambo-v1-stream-status.d.ts +90 -0
  309. package/esm/v1/hooks/use-tambo-v1-stream-status.d.ts.map +1 -0
  310. package/esm/v1/hooks/use-tambo-v1-stream-status.js +176 -0
  311. package/esm/v1/hooks/use-tambo-v1-stream-status.js.map +1 -0
  312. package/esm/v1/hooks/use-tambo-v1-stream-status.test.d.ts +2 -0
  313. package/esm/v1/hooks/use-tambo-v1-stream-status.test.d.ts.map +1 -0
  314. package/esm/v1/hooks/use-tambo-v1-stream-status.test.js +369 -0
  315. package/esm/v1/hooks/use-tambo-v1-stream-status.test.js.map +1 -0
  316. package/esm/v1/hooks/use-tambo-v1-suggestions.d.ts +78 -54
  317. package/esm/v1/hooks/use-tambo-v1-suggestions.d.ts.map +1 -1
  318. package/esm/v1/hooks/use-tambo-v1-suggestions.js +157 -91
  319. package/esm/v1/hooks/use-tambo-v1-suggestions.js.map +1 -1
  320. package/esm/v1/hooks/use-tambo-v1-suggestions.test.js +218 -139
  321. package/esm/v1/hooks/use-tambo-v1-suggestions.test.js.map +1 -1
  322. package/esm/v1/hooks/use-tambo-v1-thread-input.d.ts +1 -1
  323. package/esm/v1/hooks/use-tambo-v1-thread-input.js +1 -1
  324. package/esm/v1/hooks/use-tambo-v1-thread-input.test.js +151 -16
  325. package/esm/v1/hooks/use-tambo-v1-thread-input.test.js.map +1 -1
  326. package/esm/v1/hooks/use-tambo-v1-thread-list.d.ts +8 -21
  327. package/esm/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -1
  328. package/esm/v1/hooks/use-tambo-v1-thread-list.js +12 -11
  329. package/esm/v1/hooks/use-tambo-v1-thread-list.js.map +1 -1
  330. package/esm/v1/hooks/use-tambo-v1-thread-list.test.js +39 -4
  331. package/esm/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -1
  332. package/esm/v1/hooks/use-tambo-v1-thread.d.ts +1 -1
  333. package/esm/v1/hooks/use-tambo-v1-thread.d.ts.map +1 -1
  334. package/esm/v1/hooks/use-tambo-v1-thread.js +3 -8
  335. package/esm/v1/hooks/use-tambo-v1-thread.js.map +1 -1
  336. package/esm/v1/hooks/use-tambo-v1-thread.test.js +4 -2
  337. package/esm/v1/hooks/use-tambo-v1-thread.test.js.map +1 -1
  338. package/esm/v1/hooks/use-tambo-v1.d.ts +15 -31
  339. package/esm/v1/hooks/use-tambo-v1.d.ts.map +1 -1
  340. package/esm/v1/hooks/use-tambo-v1.js +134 -34
  341. package/esm/v1/hooks/use-tambo-v1.js.map +1 -1
  342. package/esm/v1/hooks/use-tambo-v1.test.js +862 -19
  343. package/esm/v1/hooks/use-tambo-v1.test.js.map +1 -1
  344. package/esm/v1/index.d.ts +28 -22
  345. package/esm/v1/index.d.ts.map +1 -1
  346. package/esm/v1/index.js +30 -18
  347. package/esm/v1/index.js.map +1 -1
  348. package/esm/v1/providers/tambo-v1-provider.d.ts +21 -11
  349. package/esm/v1/providers/tambo-v1-provider.d.ts.map +1 -1
  350. package/esm/v1/providers/tambo-v1-provider.js +20 -25
  351. package/esm/v1/providers/tambo-v1-provider.js.map +1 -1
  352. package/esm/v1/providers/tambo-v1-provider.test.js +40 -26
  353. package/esm/v1/providers/tambo-v1-provider.test.js.map +1 -1
  354. package/esm/v1/providers/tambo-v1-stream-context.d.ts +4 -4
  355. package/esm/v1/providers/tambo-v1-stream-context.d.ts.map +1 -1
  356. package/esm/v1/providers/tambo-v1-stream-context.js +62 -14
  357. package/esm/v1/providers/tambo-v1-stream-context.js.map +1 -1
  358. package/esm/v1/providers/tambo-v1-stream-context.test.js +50 -21
  359. package/esm/v1/providers/tambo-v1-stream-context.test.js.map +1 -1
  360. package/esm/v1/providers/tambo-v1-stub-provider.d.ts +3 -3
  361. package/esm/v1/providers/tambo-v1-stub-provider.d.ts.map +1 -1
  362. package/esm/v1/providers/tambo-v1-stub-provider.js +7 -5
  363. package/esm/v1/providers/tambo-v1-stub-provider.js.map +1 -1
  364. package/esm/v1/providers/tambo-v1-stub-provider.test.js +12 -11
  365. package/esm/v1/providers/tambo-v1-stub-provider.test.js.map +1 -1
  366. package/esm/v1/providers/tambo-v1-thread-input-provider.d.ts +3 -8
  367. package/esm/v1/providers/tambo-v1-thread-input-provider.d.ts.map +1 -1
  368. package/esm/v1/providers/tambo-v1-thread-input-provider.js +18 -16
  369. package/esm/v1/providers/tambo-v1-thread-input-provider.js.map +1 -1
  370. package/esm/v1/types/event.d.ts +9 -1
  371. package/esm/v1/types/event.d.ts.map +1 -1
  372. package/esm/v1/types/event.js.map +1 -1
  373. package/esm/v1/types/event.test.js +6 -2
  374. package/esm/v1/types/event.test.js.map +1 -1
  375. package/esm/v1/types/message.d.ts +65 -7
  376. package/esm/v1/types/message.d.ts.map +1 -1
  377. package/esm/v1/types/message.js.map +1 -1
  378. package/esm/v1/types/thread.d.ts +5 -1
  379. package/esm/v1/types/thread.d.ts.map +1 -1
  380. package/esm/v1/types/thread.js.map +1 -1
  381. package/esm/v1/utils/component-renderer.test.js +1 -1
  382. package/esm/v1/utils/event-accumulator.d.ts +41 -5
  383. package/esm/v1/utils/event-accumulator.d.ts.map +1 -1
  384. package/esm/v1/utils/event-accumulator.js +444 -36
  385. package/esm/v1/utils/event-accumulator.js.map +1 -1
  386. package/esm/v1/utils/event-accumulator.test.js +1042 -29
  387. package/esm/v1/utils/event-accumulator.test.js.map +1 -1
  388. package/esm/v1/utils/json-patch.test.js +1 -1
  389. package/esm/v1/utils/registry-conversion.d.ts +9 -9
  390. package/esm/v1/utils/registry-conversion.d.ts.map +1 -1
  391. package/esm/v1/utils/registry-conversion.js +11 -12
  392. package/esm/v1/utils/registry-conversion.js.map +1 -1
  393. package/esm/v1/utils/registry-conversion.test.js +40 -12
  394. package/esm/v1/utils/registry-conversion.test.js.map +1 -1
  395. package/esm/v1/utils/stream-handler.test.js +1 -1
  396. package/esm/v1/utils/thread-utils.d.ts +16 -0
  397. package/esm/v1/utils/thread-utils.d.ts.map +1 -0
  398. package/esm/v1/utils/thread-utils.js +31 -0
  399. package/esm/v1/utils/thread-utils.js.map +1 -0
  400. package/esm/v1/utils/tool-call-tracker.d.ts +1 -1
  401. package/esm/v1/utils/tool-executor.d.ts +1 -1
  402. package/esm/v1/utils/tool-executor.d.ts.map +1 -1
  403. package/esm/v1/utils/tool-executor.js +2 -0
  404. package/esm/v1/utils/tool-executor.js.map +1 -1
  405. package/esm/v1/utils/tool-executor.test.js +6 -1
  406. package/esm/v1/utils/tool-executor.test.js.map +1 -1
  407. package/package.json +11 -10
@@ -0,0 +1,130 @@
1
+ import { act, render, renderHook, screen } from "@testing-library/react";
2
+ import React from "react";
3
+ import { z } from "zod/v3";
4
+ import { withTamboInteractable } from "../../hoc/with-tambo-interactable.js";
5
+ import { useTamboInteractable } from "../../providers/tambo-interactable-provider.js";
6
+ import { useTamboContextHelpers } from "../../providers/tambo-context-helpers-provider.js";
7
+ import { TamboRegistryContext, } from "../../providers/tambo-registry-provider.js";
8
+ import { TamboContextHelpersProvider } from "../../providers/tambo-context-helpers-provider.js";
9
+ import { TamboInteractableProvider } from "../../providers/tambo-interactable-provider.js";
10
+ // Minimal registry mock that captures registered tools
11
+ function createMockRegistry() {
12
+ const toolRegistry = {};
13
+ return {
14
+ value: {
15
+ componentList: {},
16
+ toolRegistry,
17
+ componentToolAssociations: {},
18
+ mcpServerInfos: [],
19
+ resources: [],
20
+ resourceSource: null,
21
+ onCallUnregisteredTool: undefined,
22
+ registerComponent: jest.fn(),
23
+ registerTool: jest.fn((tool) => {
24
+ toolRegistry[tool.name] = tool;
25
+ }),
26
+ registerTools: jest.fn(),
27
+ addToolAssociation: jest.fn(),
28
+ registerMcpServer: jest.fn(),
29
+ registerMcpServers: jest.fn(),
30
+ registerResource: jest.fn(),
31
+ registerResources: jest.fn(),
32
+ registerResourceSource: jest.fn(),
33
+ },
34
+ getRegisteredToolNames: () => Object.keys(toolRegistry),
35
+ };
36
+ }
37
+ /**
38
+ * Wrapper that provides the minimal V1-compatible provider tree for interactables:
39
+ * TamboRegistryContext > TamboContextHelpersProvider > TamboInteractableProvider
40
+ */
41
+ function V1InteractableWrapper({ children, registry, }) {
42
+ return (React.createElement(TamboRegistryContext.Provider, { value: registry },
43
+ React.createElement(TamboContextHelpersProvider, null,
44
+ React.createElement(TamboInteractableProvider, null, children))));
45
+ }
46
+ describe("V1 Interactables Integration", () => {
47
+ it("registers update_component_props and update_component_state tools when an interactable is added", () => {
48
+ const mockRegistry = createMockRegistry();
49
+ const { result } = renderHook(() => useTamboInteractable(), {
50
+ wrapper: ({ children }) => (React.createElement(V1InteractableWrapper, { registry: mockRegistry.value }, children)),
51
+ });
52
+ act(() => {
53
+ result.current.addInteractableComponent({
54
+ name: "TestWidget",
55
+ description: "A test widget",
56
+ component: () => React.createElement("div", null, "widget"),
57
+ props: { label: "hello" },
58
+ propsSchema: z.object({ label: z.string() }),
59
+ });
60
+ });
61
+ const toolNames = mockRegistry.getRegisteredToolNames();
62
+ expect(toolNames.some((n) => n.startsWith("update_component_props_TestWidget"))).toBe(true);
63
+ expect(toolNames.some((n) => n.startsWith("update_component_state_TestWidget"))).toBe(true);
64
+ });
65
+ it("registers interactables context helper that includes component info", async () => {
66
+ const mockRegistry = createMockRegistry();
67
+ const { result } = renderHook(() => ({
68
+ interactable: useTamboInteractable(),
69
+ helpers: useTamboContextHelpers(),
70
+ }), {
71
+ wrapper: ({ children }) => (React.createElement(V1InteractableWrapper, { registry: mockRegistry.value }, children)),
72
+ });
73
+ // Add an interactable component
74
+ act(() => {
75
+ result.current.interactable.addInteractableComponent({
76
+ name: "InfoCard",
77
+ description: "An info card",
78
+ component: () => React.createElement("div", null, "card"),
79
+ props: { title: "Test" },
80
+ });
81
+ });
82
+ // Get additional context - should include interactable info
83
+ const contexts = await act(async () => {
84
+ return await result.current.helpers.getAdditionalContext();
85
+ });
86
+ const interactablesContext = contexts.find((c) => c.name === "interactables");
87
+ expect(interactablesContext).toBeDefined();
88
+ expect(interactablesContext?.context).toBeDefined();
89
+ });
90
+ it("renders an interactable component via withTamboInteractable HOC", () => {
91
+ const mockRegistry = createMockRegistry();
92
+ const Card = ({ title }) => (React.createElement("div", { "data-testid": "card-title" }, title));
93
+ const InteractableCard = withTamboInteractable(Card, {
94
+ componentName: "Card",
95
+ description: "A card component",
96
+ propsSchema: z.object({ title: z.string() }),
97
+ });
98
+ render(React.createElement(V1InteractableWrapper, { registry: mockRegistry.value },
99
+ React.createElement(InteractableCard, { title: "Hello V1" })));
100
+ expect(screen.getByTestId("card-title")).toHaveTextContent("Hello V1");
101
+ });
102
+ it("updates component props via the interactable provider", () => {
103
+ const mockRegistry = createMockRegistry();
104
+ const Counter = ({ count }) => (React.createElement("div", { "data-testid": "count" }, count));
105
+ const InteractableCounter = withTamboInteractable(Counter, {
106
+ componentName: "Counter",
107
+ description: "A counter",
108
+ propsSchema: z.object({ count: z.number() }),
109
+ });
110
+ // Inner component that triggers prop updates
111
+ function TestHarness() {
112
+ const { interactableComponents, updateInteractableComponentProps } = useTamboInteractable();
113
+ const component = interactableComponents[0];
114
+ return (React.createElement("div", null,
115
+ React.createElement(InteractableCounter, { count: 0 }),
116
+ component && (React.createElement("button", { "data-testid": "update-btn", onClick: () => updateInteractableComponentProps(component.id, { count: 42 }) }, "Update"))));
117
+ }
118
+ render(React.createElement(V1InteractableWrapper, { registry: mockRegistry.value },
119
+ React.createElement(TestHarness, null)));
120
+ // Initial render
121
+ expect(screen.getByTestId("count")).toHaveTextContent("0");
122
+ // Update props via the interactable provider
123
+ act(() => {
124
+ screen.getByTestId("update-btn").click();
125
+ });
126
+ // The interactable should reflect updated props
127
+ expect(screen.getByTestId("count")).toHaveTextContent("42");
128
+ });
129
+ });
130
+ //# sourceMappingURL=v1-interactables.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v1-interactables.test.js","sourceRoot":"","sources":["../../../src/v1/__tests__/v1-interactables.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,6CAA6C,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;AACxF,OAAO,EACL,oBAAoB,GAErB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,2BAA2B,EAAE,MAAM,gDAAgD,CAAC;AAC7F,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAExF,uDAAuD;AACvD,SAAS,kBAAkB;IACzB,MAAM,YAAY,GAA4B,EAAE,CAAC;IACjD,OAAO;QACL,KAAK,EAAE;YACL,aAAa,EAAE,EAAE;YACjB,YAAY;YACZ,yBAAyB,EAAE,EAAE;YAC7B,cAAc,EAAE,EAAE;YAClB,SAAS,EAAE,EAAE;YACb,cAAc,EAAE,IAAI;YACpB,sBAAsB,EAAE,SAAS;YACjC,iBAAiB,EAAE,IAAI,CAAC,EAAE,EAAE;YAC5B,YAAY,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,IAAsB,EAAE,EAAE;gBAC/C,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;YACjC,CAAC,CAAC;YACF,aAAa,EAAE,IAAI,CAAC,EAAE,EAAE;YACxB,kBAAkB,EAAE,IAAI,CAAC,EAAE,EAAE;YAC7B,iBAAiB,EAAE,IAAI,CAAC,EAAE,EAAE;YAC5B,kBAAkB,EAAE,IAAI,CAAC,EAAE,EAAE;YAC7B,gBAAgB,EAAE,IAAI,CAAC,EAAE,EAAE;YAC3B,iBAAiB,EAAE,IAAI,CAAC,EAAE,EAAE;YAC5B,sBAAsB,EAAE,IAAI,CAAC,EAAE,EAAE;SACK;QACxC,sBAAsB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC;KACxD,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,qBAAqB,CAAC,EAC7B,QAAQ,EACR,QAAQ,GAIT;IACC,OAAO,CACL,oBAAC,oBAAoB,CAAC,QAAQ,IAAC,KAAK,EAAE,QAAQ;QAC5C,oBAAC,2BAA2B;YAC1B,oBAAC,yBAAyB,QAAE,QAAQ,CAA6B,CACrC,CACA,CACjC,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,iGAAiG,EAAE,GAAG,EAAE;QACzG,MAAM,YAAY,GAAG,kBAAkB,EAAE,CAAC;QAE1C,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,oBAAoB,EAAE,EAAE;YAC1D,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CACzB,oBAAC,qBAAqB,IAAC,QAAQ,EAAE,YAAY,CAAC,KAAK,IAChD,QAAQ,CACa,CACzB;SACF,CAAC,CAAC;QAEH,GAAG,CAAC,GAAG,EAAE;YACP,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC;gBACtC,IAAI,EAAE,YAAY;gBAClB,WAAW,EAAE,eAAe;gBAC5B,SAAS,EAAE,GAAG,EAAE,CAAC,0CAAiB;gBAClC,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;gBACzB,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;aAC7C,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,YAAY,CAAC,sBAAsB,EAAE,CAAC;QACxD,MAAM,CACJ,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,mCAAmC,CAAC,CAAC,CACzE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,CACJ,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,mCAAmC,CAAC,CAAC,CACzE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,YAAY,GAAG,kBAAkB,EAAE,CAAC;QAE1C,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAC3B,GAAG,EAAE,CAAC,CAAC;YACL,YAAY,EAAE,oBAAoB,EAAE;YACpC,OAAO,EAAE,sBAAsB,EAAE;SAClC,CAAC,EACF;YACE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CACzB,oBAAC,qBAAqB,IAAC,QAAQ,EAAE,YAAY,CAAC,KAAK,IAChD,QAAQ,CACa,CACzB;SACF,CACF,CAAC;QAEF,gCAAgC;QAChC,GAAG,CAAC,GAAG,EAAE;YACP,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,wBAAwB,CAAC;gBACnD,IAAI,EAAE,UAAU;gBAChB,WAAW,EAAE,cAAc;gBAC3B,SAAS,EAAE,GAAG,EAAE,CAAC,wCAAe;gBAChC,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;aACzB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,4DAA4D;QAC5D,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,KAAK,IAAI,EAAE;YACpC,OAAO,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;QAC7D,CAAC,CAAC,CAAC;QAEH,MAAM,oBAAoB,GAAG,QAAQ,CAAC,IAAI,CACxC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,eAAe,CAClC,CAAC;QACF,MAAM,CAAC,oBAAoB,CAAC,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,YAAY,GAAG,kBAAkB,EAAE,CAAC;QAM1C,MAAM,IAAI,GAAwB,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAC/C,4CAAiB,YAAY,IAAE,KAAK,CAAO,CAC5C,CAAC;QAEF,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,IAAI,EAAE;YACnD,aAAa,EAAE,MAAM;YACrB,WAAW,EAAE,kBAAkB;YAC/B,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;SAC7C,CAAC,CAAC;QAEH,MAAM,CACJ,oBAAC,qBAAqB,IAAC,QAAQ,EAAE,YAAY,CAAC,KAAK;YACjD,oBAAC,gBAAgB,IAAC,KAAK,EAAC,UAAU,GAAG,CACf,CACzB,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,YAAY,GAAG,kBAAkB,EAAE,CAAC;QAM1C,MAAM,OAAO,GAA2B,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CACrD,4CAAiB,OAAO,IAAE,KAAK,CAAO,CACvC,CAAC;QAEF,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,OAAO,EAAE;YACzD,aAAa,EAAE,SAAS;YACxB,WAAW,EAAE,WAAW;YACxB,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;SAC7C,CAAC,CAAC;QAEH,6CAA6C;QAC7C,SAAS,WAAW;YAClB,MAAM,EAAE,sBAAsB,EAAE,gCAAgC,EAAE,GAChE,oBAAoB,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;YAE5C,OAAO,CACL;gBACE,oBAAC,mBAAmB,IAAC,KAAK,EAAE,CAAC,GAAI;gBAChC,SAAS,IAAI,CACZ,+CACc,YAAY,EACxB,OAAO,EAAE,GAAG,EAAE,CACZ,gCAAgC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,aAIxD,CACV,CACG,CACP,CAAC;QACJ,CAAC;QAED,MAAM,CACJ,oBAAC,qBAAqB,IAAC,QAAQ,EAAE,YAAY,CAAC,KAAK;YACjD,oBAAC,WAAW,OAAG,CACO,CACzB,CAAC;QAEF,iBAAiB;QACjB,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;QAE3D,6CAA6C;QAC7C,GAAG,CAAC,GAAG,EAAE;YACP,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,gDAAgD;QAChD,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { act, render, renderHook, screen } from \"@testing-library/react\";\nimport React from \"react\";\nimport { z } from \"zod/v3\";\nimport { withTamboInteractable } from \"../../hoc/with-tambo-interactable\";\nimport { useTamboInteractable } from \"../../providers/tambo-interactable-provider\";\nimport { useTamboContextHelpers } from \"../../providers/tambo-context-helpers-provider\";\nimport {\n TamboRegistryContext,\n type TamboRegistryContext as TamboRegistryContextType,\n} from \"../../providers/tambo-registry-provider\";\nimport { TamboContextHelpersProvider } from \"../../providers/tambo-context-helpers-provider\";\nimport { TamboInteractableProvider } from \"../../providers/tambo-interactable-provider\";\n\n// Minimal registry mock that captures registered tools\nfunction createMockRegistry() {\n const toolRegistry: Record<string, unknown> = {};\n return {\n value: {\n componentList: {},\n toolRegistry,\n componentToolAssociations: {},\n mcpServerInfos: [],\n resources: [],\n resourceSource: null,\n onCallUnregisteredTool: undefined,\n registerComponent: jest.fn(),\n registerTool: jest.fn((tool: { name: string }) => {\n toolRegistry[tool.name] = tool;\n }),\n registerTools: jest.fn(),\n addToolAssociation: jest.fn(),\n registerMcpServer: jest.fn(),\n registerMcpServers: jest.fn(),\n registerResource: jest.fn(),\n registerResources: jest.fn(),\n registerResourceSource: jest.fn(),\n } as unknown as TamboRegistryContextType,\n getRegisteredToolNames: () => Object.keys(toolRegistry),\n };\n}\n\n/**\n * Wrapper that provides the minimal V1-compatible provider tree for interactables:\n * TamboRegistryContext > TamboContextHelpersProvider > TamboInteractableProvider\n */\nfunction V1InteractableWrapper({\n children,\n registry,\n}: {\n children: React.ReactNode;\n registry: TamboRegistryContextType;\n}) {\n return (\n <TamboRegistryContext.Provider value={registry}>\n <TamboContextHelpersProvider>\n <TamboInteractableProvider>{children}</TamboInteractableProvider>\n </TamboContextHelpersProvider>\n </TamboRegistryContext.Provider>\n );\n}\n\ndescribe(\"V1 Interactables Integration\", () => {\n it(\"registers update_component_props and update_component_state tools when an interactable is added\", () => {\n const mockRegistry = createMockRegistry();\n\n const { result } = renderHook(() => useTamboInteractable(), {\n wrapper: ({ children }) => (\n <V1InteractableWrapper registry={mockRegistry.value}>\n {children}\n </V1InteractableWrapper>\n ),\n });\n\n act(() => {\n result.current.addInteractableComponent({\n name: \"TestWidget\",\n description: \"A test widget\",\n component: () => <div>widget</div>,\n props: { label: \"hello\" },\n propsSchema: z.object({ label: z.string() }),\n });\n });\n\n const toolNames = mockRegistry.getRegisteredToolNames();\n expect(\n toolNames.some((n) => n.startsWith(\"update_component_props_TestWidget\")),\n ).toBe(true);\n expect(\n toolNames.some((n) => n.startsWith(\"update_component_state_TestWidget\")),\n ).toBe(true);\n });\n\n it(\"registers interactables context helper that includes component info\", async () => {\n const mockRegistry = createMockRegistry();\n\n const { result } = renderHook(\n () => ({\n interactable: useTamboInteractable(),\n helpers: useTamboContextHelpers(),\n }),\n {\n wrapper: ({ children }) => (\n <V1InteractableWrapper registry={mockRegistry.value}>\n {children}\n </V1InteractableWrapper>\n ),\n },\n );\n\n // Add an interactable component\n act(() => {\n result.current.interactable.addInteractableComponent({\n name: \"InfoCard\",\n description: \"An info card\",\n component: () => <div>card</div>,\n props: { title: \"Test\" },\n });\n });\n\n // Get additional context - should include interactable info\n const contexts = await act(async () => {\n return await result.current.helpers.getAdditionalContext();\n });\n\n const interactablesContext = contexts.find(\n (c) => c.name === \"interactables\",\n );\n expect(interactablesContext).toBeDefined();\n expect(interactablesContext?.context).toBeDefined();\n });\n\n it(\"renders an interactable component via withTamboInteractable HOC\", () => {\n const mockRegistry = createMockRegistry();\n\n interface CardProps {\n title: string;\n }\n\n const Card: React.FC<CardProps> = ({ title }) => (\n <div data-testid=\"card-title\">{title}</div>\n );\n\n const InteractableCard = withTamboInteractable(Card, {\n componentName: \"Card\",\n description: \"A card component\",\n propsSchema: z.object({ title: z.string() }),\n });\n\n render(\n <V1InteractableWrapper registry={mockRegistry.value}>\n <InteractableCard title=\"Hello V1\" />\n </V1InteractableWrapper>,\n );\n\n expect(screen.getByTestId(\"card-title\")).toHaveTextContent(\"Hello V1\");\n });\n\n it(\"updates component props via the interactable provider\", () => {\n const mockRegistry = createMockRegistry();\n\n interface CounterProps {\n count: number;\n }\n\n const Counter: React.FC<CounterProps> = ({ count }) => (\n <div data-testid=\"count\">{count}</div>\n );\n\n const InteractableCounter = withTamboInteractable(Counter, {\n componentName: \"Counter\",\n description: \"A counter\",\n propsSchema: z.object({ count: z.number() }),\n });\n\n // Inner component that triggers prop updates\n function TestHarness() {\n const { interactableComponents, updateInteractableComponentProps } =\n useTamboInteractable();\n const component = interactableComponents[0];\n\n return (\n <div>\n <InteractableCounter count={0} />\n {component && (\n <button\n data-testid=\"update-btn\"\n onClick={() =>\n updateInteractableComponentProps(component.id, { count: 42 })\n }\n >\n Update\n </button>\n )}\n </div>\n );\n }\n\n render(\n <V1InteractableWrapper registry={mockRegistry.value}>\n <TestHarness />\n </V1InteractableWrapper>,\n );\n\n // Initial render\n expect(screen.getByTestId(\"count\")).toHaveTextContent(\"0\");\n\n // Update props via the interactable provider\n act(() => {\n screen.getByTestId(\"update-btn\").click();\n });\n\n // The interactable should reflect updated props\n expect(screen.getByTestId(\"count\")).toHaveTextContent(\"42\");\n });\n});\n"]}
@@ -0,0 +1,48 @@
1
+ import React, { type FC } from "react";
2
+ import type { V1ComponentContent } from "../types/message.js";
3
+ export interface V1ComponentRendererProps {
4
+ /**
5
+ * The component content block from a v1 message
6
+ */
7
+ content: V1ComponentContent;
8
+ /**
9
+ * The thread ID the component belongs to
10
+ */
11
+ threadId: string;
12
+ /**
13
+ * The message ID the component belongs to
14
+ */
15
+ messageId: string;
16
+ /**
17
+ * Optional fallback to render if component is not found in registry
18
+ */
19
+ fallback?: React.ReactNode;
20
+ }
21
+ /**
22
+ * Renders a component from the registry based on component content block data.
23
+ *
24
+ * Use this component in your message renderer to display AI-generated components.
25
+ * The component instance is preserved across re-renders as long as React's
26
+ * reconciliation keeps this wrapper mounted (use content.id as key).
27
+ *
28
+ * Wraps the rendered component with V1ComponentContentProvider so that hooks
29
+ * like useTamboV1ComponentState can access component context.
30
+ * @returns The rendered component wrapped in V1ComponentContentProvider, or fallback if not found
31
+ * @example
32
+ * ```tsx
33
+ * function MessageContent({ content }: { content: Content }) {
34
+ * if (content.type === 'component') {
35
+ * return (
36
+ * <V1ComponentRenderer
37
+ * key={content.id}
38
+ * content={content}
39
+ * fallback={<div>Unknown component: {content.name}</div>}
40
+ * />
41
+ * );
42
+ * }
43
+ * // ... handle other content types
44
+ * }
45
+ * ```
46
+ */
47
+ export declare const V1ComponentRenderer: FC<V1ComponentRendererProps>;
48
+ //# sourceMappingURL=v1-component-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v1-component-renderer.d.ts","sourceRoot":"","sources":["../../../src/v1/components/v1-component-renderer.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,EAAE,KAAK,EAAE,EAAuB,MAAM,OAAO,CAAC;AAK5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3D,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,OAAO,EAAE,kBAAkB,CAAC;IAE5B;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAAC,wBAAwB,CAoF5D,CAAC"}
@@ -0,0 +1,100 @@
1
+ "use client";
2
+ /**
3
+ * V1 Component Renderer
4
+ *
5
+ * A wrapper component that renders a component from the registry based on
6
+ * component content block data. Uses React's normal reconciliation to maintain
7
+ * component identity - as long as the key stays stable, the component instance
8
+ * is preserved.
9
+ *
10
+ * Wraps the component with V1ComponentContentProvider so that hooks like
11
+ * useTamboV1ComponentState can access component context.
12
+ */
13
+ import { parse } from "partial-json";
14
+ import React, { useMemo, useContext } from "react";
15
+ import { TamboRegistryContext } from "../../providers/tambo-registry-provider.js";
16
+ import { isStandardSchema } from "../../schema/index.js";
17
+ import { isPromise } from "../../util/is-promise.js";
18
+ import { getComponentFromRegistry } from "../../util/registry.js";
19
+ import { V1ComponentContentProvider } from "../utils/component-renderer.js";
20
+ /**
21
+ * Renders a component from the registry based on component content block data.
22
+ *
23
+ * Use this component in your message renderer to display AI-generated components.
24
+ * The component instance is preserved across re-renders as long as React's
25
+ * reconciliation keeps this wrapper mounted (use content.id as key).
26
+ *
27
+ * Wraps the rendered component with V1ComponentContentProvider so that hooks
28
+ * like useTamboV1ComponentState can access component context.
29
+ * @returns The rendered component wrapped in V1ComponentContentProvider, or fallback if not found
30
+ * @example
31
+ * ```tsx
32
+ * function MessageContent({ content }: { content: Content }) {
33
+ * if (content.type === 'component') {
34
+ * return (
35
+ * <V1ComponentRenderer
36
+ * key={content.id}
37
+ * content={content}
38
+ * fallback={<div>Unknown component: {content.name}</div>}
39
+ * />
40
+ * );
41
+ * }
42
+ * // ... handle other content types
43
+ * }
44
+ * ```
45
+ */
46
+ export const V1ComponentRenderer = ({ content, threadId, messageId, fallback = null, }) => {
47
+ const registry = useContext(TamboRegistryContext);
48
+ // Memoize the rendered element - only recreates when props change
49
+ const element = useMemo(() => {
50
+ try {
51
+ const registeredComponent = getComponentFromRegistry(content.name, registry.componentList);
52
+ // Parse props (handles partial JSON during streaming)
53
+ const propsJson = JSON.stringify(content.props ?? {});
54
+ const parsedProps = parse(propsJson);
55
+ let validatedProps = parsedProps;
56
+ // Validate props if schema is present
57
+ if (isStandardSchema(registeredComponent.props)) {
58
+ const result = registeredComponent.props["~standard"].validate(parsedProps);
59
+ if (isPromise(result)) {
60
+ // Async validation not supported - skip validation
61
+ console.warn(`Async schema validation not supported for component ${content.name}`);
62
+ }
63
+ else if ("value" in result) {
64
+ validatedProps = result.value;
65
+ }
66
+ else {
67
+ // Validation failed - log warning but still render with raw props
68
+ console.warn(`Props validation failed for component ${content.name}:`, result.issues?.[0]?.message);
69
+ }
70
+ }
71
+ return React.createElement(registeredComponent.component, validatedProps);
72
+ }
73
+ catch (error) {
74
+ console.error("[V1ComponentRenderer] Failed to render component", {
75
+ threadId,
76
+ messageId,
77
+ componentId: content.id,
78
+ componentName: content.name,
79
+ streamingState: content.streamingState,
80
+ props: content.props,
81
+ error,
82
+ });
83
+ return null;
84
+ }
85
+ }, [
86
+ content.id,
87
+ content.name,
88
+ content.props,
89
+ content.streamingState,
90
+ messageId,
91
+ threadId,
92
+ registry.componentList,
93
+ ]);
94
+ if (element === null) {
95
+ return React.createElement(React.Fragment, null, fallback);
96
+ }
97
+ // Wrap with provider so hooks like useTamboV1ComponentState work
98
+ return (React.createElement(V1ComponentContentProvider, { componentId: content.id, threadId: threadId, messageId: messageId, componentName: content.name }, element));
99
+ };
100
+ //# sourceMappingURL=v1-component-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v1-component-renderer.js","sourceRoot":"","sources":["../../../src/v1/components/v1-component-renderer.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * V1 Component Renderer\n *\n * A wrapper component that renders a component from the registry based on\n * component content block data. Uses React's normal reconciliation to maintain\n * component identity - as long as the key stays stable, the component instance\n * is preserved.\n *\n * Wraps the component with V1ComponentContentProvider so that hooks like\n * useTamboV1ComponentState can access component context.\n */\n\nimport { parse } from \"partial-json\";\nimport React, { type FC, useMemo, useContext } from \"react\";\nimport { TamboRegistryContext } from \"../../providers/tambo-registry-provider\";\nimport { isStandardSchema } from \"../../schema\";\nimport { isPromise } from \"../../util/is-promise\";\nimport { getComponentFromRegistry } from \"../../util/registry\";\nimport type { V1ComponentContent } from \"../types/message\";\nimport { V1ComponentContentProvider } from \"../utils/component-renderer\";\n\nexport interface V1ComponentRendererProps {\n /**\n * The component content block from a v1 message\n */\n content: V1ComponentContent;\n\n /**\n * The thread ID the component belongs to\n */\n threadId: string;\n\n /**\n * The message ID the component belongs to\n */\n messageId: string;\n\n /**\n * Optional fallback to render if component is not found in registry\n */\n fallback?: React.ReactNode;\n}\n\n/**\n * Renders a component from the registry based on component content block data.\n *\n * Use this component in your message renderer to display AI-generated components.\n * The component instance is preserved across re-renders as long as React's\n * reconciliation keeps this wrapper mounted (use content.id as key).\n *\n * Wraps the rendered component with V1ComponentContentProvider so that hooks\n * like useTamboV1ComponentState can access component context.\n * @returns The rendered component wrapped in V1ComponentContentProvider, or fallback if not found\n * @example\n * ```tsx\n * function MessageContent({ content }: { content: Content }) {\n * if (content.type === 'component') {\n * return (\n * <V1ComponentRenderer\n * key={content.id}\n * content={content}\n * fallback={<div>Unknown component: {content.name}</div>}\n * />\n * );\n * }\n * // ... handle other content types\n * }\n * ```\n */\nexport const V1ComponentRenderer: FC<V1ComponentRendererProps> = ({\n content,\n threadId,\n messageId,\n fallback = null,\n}) => {\n const registry = useContext(TamboRegistryContext);\n\n // Memoize the rendered element - only recreates when props change\n const element = useMemo(() => {\n try {\n const registeredComponent = getComponentFromRegistry(\n content.name,\n registry.componentList,\n );\n\n // Parse props (handles partial JSON during streaming)\n const propsJson = JSON.stringify(content.props ?? {});\n const parsedProps = parse(propsJson);\n\n let validatedProps: Record<string, unknown> = parsedProps as Record<\n string,\n unknown\n >;\n\n // Validate props if schema is present\n if (isStandardSchema(registeredComponent.props)) {\n const result =\n registeredComponent.props[\"~standard\"].validate(parsedProps);\n\n if (isPromise(result)) {\n // Async validation not supported - skip validation\n console.warn(\n `Async schema validation not supported for component ${content.name}`,\n );\n } else if (\"value\" in result) {\n validatedProps = result.value as Record<string, unknown>;\n } else {\n // Validation failed - log warning but still render with raw props\n console.warn(\n `Props validation failed for component ${content.name}:`,\n result.issues?.[0]?.message,\n );\n }\n }\n\n return React.createElement(registeredComponent.component, validatedProps);\n } catch (error) {\n console.error(\"[V1ComponentRenderer] Failed to render component\", {\n threadId,\n messageId,\n componentId: content.id,\n componentName: content.name,\n streamingState: content.streamingState,\n props: content.props,\n error,\n });\n return null;\n }\n }, [\n content.id,\n content.name,\n content.props,\n content.streamingState,\n messageId,\n threadId,\n registry.componentList,\n ]);\n\n if (element === null) {\n return <>{fallback}</>;\n }\n\n // Wrap with provider so hooks like useTamboV1ComponentState work\n return (\n <V1ComponentContentProvider\n componentId={content.id}\n threadId={threadId}\n messageId={messageId}\n componentName={content.name}\n >\n {element}\n </V1ComponentContentProvider>\n );\n};\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=v1-component-renderer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v1-component-renderer.test.d.ts","sourceRoot":"","sources":["../../../src/v1/components/v1-component-renderer.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,265 @@
1
+ import React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { z } from "zod";
4
+ import { V1ComponentRenderer } from "./v1-component-renderer.js";
5
+ import { TamboRegistryContext } from "../../providers/tambo-registry-provider.js";
6
+ // Simple test component
7
+ const TestComponent = ({ title, count, }) => (React.createElement("div", { "data-testid": "test-component" },
8
+ React.createElement("span", { "data-testid": "title" }, title),
9
+ count !== undefined && React.createElement("span", { "data-testid": "count" }, count)));
10
+ // Component with Zod schema for validation
11
+ const ValidatedComponent = ({ name, age, }) => (React.createElement("div", { "data-testid": "validated-component" },
12
+ React.createElement("span", { "data-testid": "name" }, name),
13
+ React.createElement("span", { "data-testid": "age" }, age)));
14
+ const validatedComponentSchema = z.object({
15
+ name: z.string(),
16
+ age: z.number(),
17
+ });
18
+ // Create a mock registry
19
+ function createMockRegistry(componentList = {}) {
20
+ return {
21
+ componentList,
22
+ toolRegistry: {},
23
+ componentToolAssociations: {},
24
+ mcpServerInfos: [],
25
+ resources: [],
26
+ resourceSource: null,
27
+ registerComponent: jest.fn(),
28
+ registerTool: jest.fn(),
29
+ registerTools: jest.fn(),
30
+ addToolAssociation: jest.fn(),
31
+ registerMcpServer: jest.fn(),
32
+ registerMcpServers: jest.fn(),
33
+ registerResource: jest.fn(),
34
+ registerResources: jest.fn(),
35
+ registerResourceSource: jest.fn(),
36
+ };
37
+ }
38
+ describe("V1ComponentRenderer", () => {
39
+ function withMockedConsoleError(fn) {
40
+ const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
41
+ try {
42
+ return fn(consoleErrorSpy);
43
+ }
44
+ finally {
45
+ consoleErrorSpy.mockRestore();
46
+ }
47
+ }
48
+ const baseContent = {
49
+ type: "component",
50
+ id: "comp_123",
51
+ name: "TestComponent",
52
+ props: { title: "Hello World" },
53
+ streamingState: "done",
54
+ };
55
+ it("renders component from registry with props", () => {
56
+ const registry = createMockRegistry({
57
+ TestComponent: {
58
+ name: "TestComponent",
59
+ description: "A test component",
60
+ component: TestComponent,
61
+ props: { type: "object" },
62
+ contextTools: [],
63
+ },
64
+ });
65
+ render(React.createElement(TamboRegistryContext.Provider, { value: registry },
66
+ React.createElement(V1ComponentRenderer, { content: baseContent, threadId: "thread_123", messageId: "msg_456" })));
67
+ expect(screen.getByTestId("test-component")).toBeInTheDocument();
68
+ expect(screen.getByTestId("title")).toHaveTextContent("Hello World");
69
+ });
70
+ it("renders fallback when component not found in registry", () => {
71
+ const registry = createMockRegistry({});
72
+ withMockedConsoleError((consoleErrorSpy) => {
73
+ render(React.createElement(TamboRegistryContext.Provider, { value: registry },
74
+ React.createElement(V1ComponentRenderer, { content: baseContent, threadId: "thread_123", messageId: "msg_456", fallback: React.createElement("div", { "data-testid": "fallback" }, "Not found") })));
75
+ expect(screen.getByTestId("fallback")).toBeInTheDocument();
76
+ expect(screen.queryByTestId("test-component")).not.toBeInTheDocument();
77
+ expect(consoleErrorSpy).toHaveBeenCalledWith("[V1ComponentRenderer] Failed to render component", expect.objectContaining({
78
+ componentId: baseContent.id,
79
+ componentName: baseContent.name,
80
+ }));
81
+ });
82
+ });
83
+ it("renders nothing (null fallback) when component not found and no fallback provided", () => {
84
+ const registry = createMockRegistry({});
85
+ withMockedConsoleError((consoleErrorSpy) => {
86
+ const { container } = render(React.createElement(TamboRegistryContext.Provider, { value: registry },
87
+ React.createElement(V1ComponentRenderer, { content: baseContent, threadId: "thread_123", messageId: "msg_456" })));
88
+ expect(container.firstChild).toBeNull();
89
+ expect(consoleErrorSpy).toHaveBeenCalledWith("[V1ComponentRenderer] Failed to render component", expect.objectContaining({
90
+ componentId: baseContent.id,
91
+ componentName: baseContent.name,
92
+ }));
93
+ });
94
+ });
95
+ it("handles props with undefined values", () => {
96
+ const registry = createMockRegistry({
97
+ TestComponent: {
98
+ name: "TestComponent",
99
+ description: "A test component",
100
+ component: TestComponent,
101
+ props: { type: "object" },
102
+ contextTools: [],
103
+ },
104
+ });
105
+ const content = {
106
+ type: "component",
107
+ id: "comp_123",
108
+ name: "TestComponent",
109
+ props: { title: "Test", count: undefined },
110
+ streamingState: "done",
111
+ };
112
+ render(React.createElement(TamboRegistryContext.Provider, { value: registry },
113
+ React.createElement(V1ComponentRenderer, { content: content, threadId: "thread_123", messageId: "msg_456" })));
114
+ expect(screen.getByTestId("title")).toHaveTextContent("Test");
115
+ expect(screen.queryByTestId("count")).not.toBeInTheDocument();
116
+ });
117
+ it("handles null props", () => {
118
+ const registry = createMockRegistry({
119
+ TestComponent: {
120
+ name: "TestComponent",
121
+ description: "A test component",
122
+ component: TestComponent,
123
+ props: { type: "object" },
124
+ contextTools: [],
125
+ },
126
+ });
127
+ const content = {
128
+ type: "component",
129
+ id: "comp_123",
130
+ name: "TestComponent",
131
+ props: null,
132
+ streamingState: "done",
133
+ };
134
+ render(React.createElement(TamboRegistryContext.Provider, { value: registry },
135
+ React.createElement(V1ComponentRenderer, { content: content, threadId: "thread_123", messageId: "msg_456" })));
136
+ // Component should render with empty props
137
+ expect(screen.getByTestId("test-component")).toBeInTheDocument();
138
+ });
139
+ it("validates props with StandardSchema and uses validated values", () => {
140
+ const registry = createMockRegistry({
141
+ ValidatedComponent: {
142
+ name: "ValidatedComponent",
143
+ description: "A validated component",
144
+ component: ValidatedComponent,
145
+ // Cast as unknown to satisfy TypeScript while still providing a Zod schema
146
+ props: validatedComponentSchema,
147
+ contextTools: [],
148
+ },
149
+ });
150
+ const content = {
151
+ type: "component",
152
+ id: "comp_123",
153
+ name: "ValidatedComponent",
154
+ props: { name: "Alice", age: 30 },
155
+ streamingState: "done",
156
+ };
157
+ render(React.createElement(TamboRegistryContext.Provider, { value: registry },
158
+ React.createElement(V1ComponentRenderer, { content: content, threadId: "thread_123", messageId: "msg_456" })));
159
+ expect(screen.getByTestId("name")).toHaveTextContent("Alice");
160
+ expect(screen.getByTestId("age")).toHaveTextContent("30");
161
+ });
162
+ it("logs warning and renders with raw props when schema validation fails", () => {
163
+ const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
164
+ const registry = createMockRegistry({
165
+ ValidatedComponent: {
166
+ name: "ValidatedComponent",
167
+ description: "A validated component",
168
+ component: ValidatedComponent,
169
+ // Cast as unknown to satisfy TypeScript while still providing a Zod schema
170
+ props: validatedComponentSchema,
171
+ contextTools: [],
172
+ },
173
+ });
174
+ const content = {
175
+ type: "component",
176
+ id: "comp_123",
177
+ name: "ValidatedComponent",
178
+ props: { name: "Bob", age: "not a number" }, // Invalid: age should be number
179
+ streamingState: "done",
180
+ };
181
+ render(React.createElement(TamboRegistryContext.Provider, { value: registry },
182
+ React.createElement(V1ComponentRenderer, { content: content, threadId: "thread_123", messageId: "msg_456" })));
183
+ // Should still render with raw props
184
+ expect(screen.getByTestId("name")).toHaveTextContent("Bob");
185
+ expect(screen.getByTestId("age")).toHaveTextContent("not a number");
186
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Props validation failed"), expect.any(String));
187
+ consoleSpy.mockRestore();
188
+ });
189
+ it("logs warning for async schema validation", () => {
190
+ const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
191
+ // Create a mock async schema
192
+ const asyncSchema = {
193
+ "~standard": {
194
+ version: 1,
195
+ vendor: "test",
196
+ validate: async () => {
197
+ return await Promise.resolve({ value: {} });
198
+ },
199
+ },
200
+ };
201
+ const registry = createMockRegistry({
202
+ TestComponent: {
203
+ name: "TestComponent",
204
+ description: "A test component",
205
+ component: TestComponent,
206
+ props: asyncSchema,
207
+ contextTools: [],
208
+ },
209
+ });
210
+ render(React.createElement(TamboRegistryContext.Provider, { value: registry },
211
+ React.createElement(V1ComponentRenderer, { content: baseContent, threadId: "thread_123", messageId: "msg_456" })));
212
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Async schema validation not supported"));
213
+ consoleSpy.mockRestore();
214
+ });
215
+ it("handles partial JSON during streaming", () => {
216
+ const registry = createMockRegistry({
217
+ TestComponent: {
218
+ name: "TestComponent",
219
+ description: "A test component",
220
+ component: TestComponent,
221
+ props: { type: "object" },
222
+ contextTools: [],
223
+ },
224
+ });
225
+ // partial-json library handles incomplete JSON gracefully
226
+ const content = {
227
+ type: "component",
228
+ id: "comp_123",
229
+ name: "TestComponent",
230
+ props: { title: "Partial" },
231
+ streamingState: "streaming",
232
+ };
233
+ render(React.createElement(TamboRegistryContext.Provider, { value: registry },
234
+ React.createElement(V1ComponentRenderer, { content: content, threadId: "thread_123", messageId: "msg_456" })));
235
+ expect(screen.getByTestId("title")).toHaveTextContent("Partial");
236
+ });
237
+ it("provides component context to rendered components via V1ComponentContentProvider", () => {
238
+ // Create a component that uses the context
239
+ const ContextAwareComponent = () => {
240
+ // We can't directly test the context without importing useV1ComponentContent
241
+ // but we can verify the component renders which means the provider works
242
+ return React.createElement("div", { "data-testid": "context-aware" }, "Rendered");
243
+ };
244
+ const registry = createMockRegistry({
245
+ ContextAwareComponent: {
246
+ name: "ContextAwareComponent",
247
+ description: "A context aware component",
248
+ component: ContextAwareComponent,
249
+ props: { type: "object" },
250
+ contextTools: [],
251
+ },
252
+ });
253
+ const content = {
254
+ type: "component",
255
+ id: "comp_789",
256
+ name: "ContextAwareComponent",
257
+ props: {},
258
+ streamingState: "done",
259
+ };
260
+ render(React.createElement(TamboRegistryContext.Provider, { value: registry },
261
+ React.createElement(V1ComponentRenderer, { content: content, threadId: "thread_abc", messageId: "msg_def" })));
262
+ expect(screen.getByTestId("context-aware")).toBeInTheDocument();
263
+ });
264
+ });
265
+ //# sourceMappingURL=v1-component-renderer.test.js.map