@tambo-ai/react 0.75.0 → 1.0.0-rc.4

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 (613) hide show
  1. package/dist/hoc/with-tambo-interactable.d.ts.map +1 -1
  2. package/dist/hoc/with-tambo-interactable.js +13 -13
  3. package/dist/hoc/with-tambo-interactable.js.map +1 -1
  4. package/dist/hoc/with-tambo-interactable.test.js +3 -3
  5. package/dist/hoc/with-tambo-interactable.test.js.map +1 -1
  6. package/dist/index.d.ts +7 -25
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +7 -59
  9. package/dist/index.js.map +1 -1
  10. package/dist/mcp/mcp-hooks.js +5 -5
  11. package/dist/mcp/mcp-hooks.js.map +1 -1
  12. package/dist/model/component-metadata.d.ts +4 -4
  13. package/dist/model/component-metadata.js.map +1 -1
  14. package/dist/providers/tambo-client-provider.d.ts +6 -0
  15. package/dist/providers/tambo-client-provider.d.ts.map +1 -1
  16. package/dist/providers/tambo-client-provider.js +4 -2
  17. package/dist/providers/tambo-client-provider.js.map +1 -1
  18. package/dist/providers/tambo-interactable-provider.d.ts +1 -1
  19. package/dist/providers/tambo-interactables-additional-context-edge-cases.test.js +2 -10
  20. package/dist/providers/tambo-interactables-additional-context-edge-cases.test.js.map +1 -1
  21. package/dist/providers/tambo-interactables-additional-context.test.js +3 -19
  22. package/dist/providers/tambo-interactables-additional-context.test.js.map +1 -1
  23. package/dist/providers/tambo-mcp-token-provider.d.ts +8 -17
  24. package/dist/providers/tambo-mcp-token-provider.d.ts.map +1 -1
  25. package/dist/providers/tambo-mcp-token-provider.js +20 -97
  26. package/dist/providers/tambo-mcp-token-provider.js.map +1 -1
  27. package/dist/testing/tools.d.ts +3 -3
  28. package/dist/testing/tools.d.ts.map +1 -1
  29. package/dist/testing/tools.js.map +1 -1
  30. package/dist/util/registry-validators.js +1 -1
  31. package/dist/util/registry-validators.js.map +1 -1
  32. package/dist/v1/__tests__/v1-interactables.test.js +1 -1
  33. package/dist/v1/__tests__/v1-interactables.test.js.map +1 -1
  34. package/dist/v1/components/v1-component-renderer.d.ts +9 -9
  35. package/dist/v1/components/v1-component-renderer.d.ts.map +1 -1
  36. package/dist/v1/components/v1-component-renderer.js +13 -13
  37. package/dist/v1/components/v1-component-renderer.js.map +1 -1
  38. package/dist/v1/components/v1-component-renderer.test.js +15 -15
  39. package/dist/v1/components/v1-component-renderer.test.js.map +1 -1
  40. package/{esm/hooks/use-current-message.d.ts → dist/v1/hooks/use-tambo-current-message.d.ts} +30 -9
  41. package/dist/v1/hooks/use-tambo-current-message.d.ts.map +1 -0
  42. package/dist/{hooks/use-current-message.js → v1/hooks/use-tambo-current-message.js} +13 -8
  43. package/dist/v1/hooks/use-tambo-current-message.js.map +1 -0
  44. package/dist/v1/hooks/use-tambo-v1-auth-state.d.ts +5 -5
  45. package/dist/v1/hooks/use-tambo-v1-auth-state.d.ts.map +1 -1
  46. package/dist/v1/hooks/use-tambo-v1-auth-state.js +8 -8
  47. package/dist/v1/hooks/use-tambo-v1-auth-state.js.map +1 -1
  48. package/dist/v1/hooks/use-tambo-v1-auth-state.test.js +12 -12
  49. package/dist/v1/hooks/use-tambo-v1-auth-state.test.js.map +1 -1
  50. package/dist/v1/hooks/use-tambo-v1-component-state.d.ts +7 -6
  51. package/dist/v1/hooks/use-tambo-v1-component-state.d.ts.map +1 -1
  52. package/dist/v1/hooks/use-tambo-v1-component-state.js +50 -24
  53. package/dist/v1/hooks/use-tambo-v1-component-state.js.map +1 -1
  54. package/dist/v1/hooks/use-tambo-v1-component-state.test.js +60 -35
  55. package/dist/v1/hooks/use-tambo-v1-component-state.test.js.map +1 -1
  56. package/dist/v1/hooks/use-tambo-v1-messages.d.ts +9 -9
  57. package/dist/v1/hooks/use-tambo-v1-messages.d.ts.map +1 -1
  58. package/dist/v1/hooks/use-tambo-v1-messages.js +4 -4
  59. package/dist/v1/hooks/use-tambo-v1-messages.js.map +1 -1
  60. package/dist/v1/hooks/use-tambo-v1-messages.test.js +7 -7
  61. package/dist/v1/hooks/use-tambo-v1-messages.test.js.map +1 -1
  62. package/dist/v1/hooks/use-tambo-v1-send-message.d.ts +3 -3
  63. package/dist/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -1
  64. package/dist/v1/hooks/use-tambo-v1-send-message.js +20 -22
  65. package/dist/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
  66. package/dist/v1/hooks/use-tambo-v1-send-message.test.js +51 -51
  67. package/dist/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -1
  68. package/dist/v1/hooks/use-tambo-v1-stream-status.d.ts +5 -5
  69. package/dist/v1/hooks/use-tambo-v1-stream-status.d.ts.map +1 -1
  70. package/dist/v1/hooks/use-tambo-v1-stream-status.js +12 -14
  71. package/dist/v1/hooks/use-tambo-v1-stream-status.js.map +1 -1
  72. package/dist/v1/hooks/use-tambo-v1-stream-status.test.js +19 -19
  73. package/dist/v1/hooks/use-tambo-v1-stream-status.test.js.map +1 -1
  74. package/dist/v1/hooks/use-tambo-v1-suggestions.d.ts +7 -7
  75. package/dist/v1/hooks/use-tambo-v1-suggestions.d.ts.map +1 -1
  76. package/dist/v1/hooks/use-tambo-v1-suggestions.js +9 -9
  77. package/dist/v1/hooks/use-tambo-v1-suggestions.js.map +1 -1
  78. package/dist/v1/hooks/use-tambo-v1-suggestions.test.js +44 -44
  79. package/dist/v1/hooks/use-tambo-v1-suggestions.test.js.map +1 -1
  80. package/dist/v1/hooks/use-tambo-v1-thread-input.d.ts +3 -3
  81. package/dist/v1/hooks/use-tambo-v1-thread-input.d.ts.map +1 -1
  82. package/dist/v1/hooks/use-tambo-v1-thread-input.js +4 -4
  83. package/dist/v1/hooks/use-tambo-v1-thread-input.js.map +1 -1
  84. package/dist/v1/hooks/use-tambo-v1-thread-input.test.js +29 -29
  85. package/dist/v1/hooks/use-tambo-v1-thread-input.test.js.map +1 -1
  86. package/dist/v1/hooks/use-tambo-v1-thread-list.d.ts +4 -4
  87. package/dist/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -1
  88. package/dist/v1/hooks/use-tambo-v1-thread-list.js +5 -5
  89. package/dist/v1/hooks/use-tambo-v1-thread-list.js.map +1 -1
  90. package/dist/v1/hooks/use-tambo-v1-thread-list.test.js +13 -17
  91. package/dist/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -1
  92. package/dist/v1/hooks/use-tambo-v1-thread.d.ts +3 -3
  93. package/dist/v1/hooks/use-tambo-v1-thread.d.ts.map +1 -1
  94. package/dist/v1/hooks/use-tambo-v1-thread.js +4 -4
  95. package/dist/v1/hooks/use-tambo-v1-thread.js.map +1 -1
  96. package/dist/v1/hooks/use-tambo-v1-thread.test.js +5 -5
  97. package/dist/v1/hooks/use-tambo-v1-thread.test.js.map +1 -1
  98. package/dist/v1/hooks/use-tambo-v1.d.ts +16 -7
  99. package/dist/v1/hooks/use-tambo-v1.d.ts.map +1 -1
  100. package/dist/v1/hooks/use-tambo-v1.js +41 -10
  101. package/dist/v1/hooks/use-tambo-v1.js.map +1 -1
  102. package/dist/v1/hooks/use-tambo-v1.test.js +176 -48
  103. package/dist/v1/hooks/use-tambo-v1.test.js.map +1 -1
  104. package/dist/v1/index.d.ts +31 -27
  105. package/dist/v1/index.d.ts.map +1 -1
  106. package/dist/v1/index.js +45 -35
  107. package/dist/v1/index.js.map +1 -1
  108. package/dist/v1/providers/tambo-v1-provider.d.ts +20 -20
  109. package/dist/v1/providers/tambo-v1-provider.d.ts.map +1 -1
  110. package/dist/v1/providers/tambo-v1-provider.js +32 -32
  111. package/dist/v1/providers/tambo-v1-provider.js.map +1 -1
  112. package/dist/v1/providers/tambo-v1-provider.test.js +22 -22
  113. package/dist/v1/providers/tambo-v1-provider.test.js.map +1 -1
  114. package/dist/v1/providers/tambo-v1-stream-context.d.ts +15 -15
  115. package/dist/v1/providers/tambo-v1-stream-context.d.ts.map +1 -1
  116. package/dist/v1/providers/tambo-v1-stream-context.js +17 -17
  117. package/dist/v1/providers/tambo-v1-stream-context.js.map +1 -1
  118. package/dist/v1/providers/tambo-v1-stream-context.test.js +9 -9
  119. package/dist/v1/providers/tambo-v1-stream-context.test.js.map +1 -1
  120. package/dist/v1/providers/tambo-v1-stub-provider.d.ts +9 -9
  121. package/dist/v1/providers/tambo-v1-stub-provider.d.ts.map +1 -1
  122. package/dist/v1/providers/tambo-v1-stub-provider.js +7 -7
  123. package/dist/v1/providers/tambo-v1-stub-provider.js.map +1 -1
  124. package/dist/v1/providers/tambo-v1-stub-provider.test.js +25 -25
  125. package/dist/v1/providers/tambo-v1-stub-provider.test.js.map +1 -1
  126. package/dist/v1/providers/tambo-v1-thread-input-provider.d.ts +9 -9
  127. package/dist/v1/providers/tambo-v1-thread-input-provider.d.ts.map +1 -1
  128. package/dist/v1/providers/tambo-v1-thread-input-provider.js +16 -16
  129. package/dist/v1/providers/tambo-v1-thread-input-provider.js.map +1 -1
  130. package/dist/v1/types/auth.d.ts +2 -2
  131. package/dist/v1/types/auth.d.ts.map +1 -1
  132. package/dist/v1/types/auth.js.map +1 -1
  133. package/dist/v1/types/component.d.ts +3 -3
  134. package/dist/v1/types/component.d.ts.map +1 -1
  135. package/dist/v1/types/component.js +2 -2
  136. package/dist/v1/types/component.js.map +1 -1
  137. package/dist/v1/types/event.d.ts +1 -1
  138. package/dist/v1/types/event.js +1 -1
  139. package/dist/v1/types/event.js.map +1 -1
  140. package/dist/v1/types/message.d.ts +17 -24
  141. package/dist/v1/types/message.d.ts.map +1 -1
  142. package/dist/v1/types/message.js +1 -1
  143. package/dist/v1/types/message.js.map +1 -1
  144. package/dist/v1/types/thread.d.ts +10 -8
  145. package/dist/v1/types/thread.d.ts.map +1 -1
  146. package/dist/v1/types/thread.js +1 -1
  147. package/dist/v1/types/thread.js.map +1 -1
  148. package/dist/v1/types/tool-choice.d.ts +1 -1
  149. package/dist/v1/types/tool-choice.js.map +1 -1
  150. package/dist/v1/utils/component-renderer.d.ts +11 -5
  151. package/dist/v1/utils/component-renderer.d.ts.map +1 -1
  152. package/dist/v1/utils/component-renderer.js +16 -7
  153. package/dist/v1/utils/component-renderer.js.map +1 -1
  154. package/dist/v1/utils/component-renderer.test.js +7 -7
  155. package/dist/v1/utils/component-renderer.test.js.map +1 -1
  156. package/dist/v1/utils/event-accumulator.d.ts +13 -13
  157. package/dist/v1/utils/event-accumulator.d.ts.map +1 -1
  158. package/dist/v1/utils/event-accumulator.js +26 -15
  159. package/dist/v1/utils/event-accumulator.js.map +1 -1
  160. package/dist/v1/utils/event-accumulator.test.js +54 -19
  161. package/dist/v1/utils/event-accumulator.test.js.map +1 -1
  162. package/dist/v1/utils/registry-conversion.d.ts +18 -18
  163. package/dist/v1/utils/registry-conversion.js +23 -23
  164. package/dist/v1/utils/registry-conversion.js.map +1 -1
  165. package/dist/v1/utils/stream-handler.d.ts +1 -1
  166. package/dist/v1/utils/stream-handler.js +1 -1
  167. package/dist/v1/utils/stream-handler.js.map +1 -1
  168. package/dist/v1/utils/thread-utils.d.ts +2 -2
  169. package/dist/v1/utils/thread-utils.d.ts.map +1 -1
  170. package/dist/v1/utils/thread-utils.js.map +1 -1
  171. package/dist/v1/utils/tool-call-tracker.d.ts +1 -1
  172. package/dist/v1/utils/tool-call-tracker.js +1 -1
  173. package/dist/v1/utils/tool-call-tracker.js.map +1 -1
  174. package/dist/v1/utils/tool-executor.d.ts +1 -1
  175. package/dist/v1/utils/tool-executor.js +2 -2
  176. package/dist/v1/utils/tool-executor.js.map +1 -1
  177. package/esm/hoc/with-tambo-interactable.d.ts.map +1 -1
  178. package/esm/hoc/with-tambo-interactable.js +13 -13
  179. package/esm/hoc/with-tambo-interactable.js.map +1 -1
  180. package/esm/hoc/with-tambo-interactable.test.js +1 -1
  181. package/esm/hoc/with-tambo-interactable.test.js.map +1 -1
  182. package/esm/index.d.ts +7 -25
  183. package/esm/index.d.ts.map +1 -1
  184. package/esm/index.js +7 -21
  185. package/esm/index.js.map +1 -1
  186. package/esm/mcp/mcp-hooks.js +1 -1
  187. package/esm/mcp/mcp-hooks.js.map +1 -1
  188. package/esm/model/component-metadata.d.ts +4 -4
  189. package/esm/model/component-metadata.js.map +1 -1
  190. package/esm/providers/tambo-client-provider.d.ts +6 -0
  191. package/esm/providers/tambo-client-provider.d.ts.map +1 -1
  192. package/esm/providers/tambo-client-provider.js +4 -2
  193. package/esm/providers/tambo-client-provider.js.map +1 -1
  194. package/esm/providers/tambo-interactable-provider.d.ts +1 -1
  195. package/esm/providers/tambo-interactables-additional-context-edge-cases.test.js +2 -10
  196. package/esm/providers/tambo-interactables-additional-context-edge-cases.test.js.map +1 -1
  197. package/esm/providers/tambo-interactables-additional-context.test.js +3 -19
  198. package/esm/providers/tambo-interactables-additional-context.test.js.map +1 -1
  199. package/esm/providers/tambo-mcp-token-provider.d.ts +8 -17
  200. package/esm/providers/tambo-mcp-token-provider.d.ts.map +1 -1
  201. package/esm/providers/tambo-mcp-token-provider.js +20 -97
  202. package/esm/providers/tambo-mcp-token-provider.js.map +1 -1
  203. package/esm/testing/tools.d.ts +3 -3
  204. package/esm/testing/tools.d.ts.map +1 -1
  205. package/esm/testing/tools.js.map +1 -1
  206. package/esm/util/registry-validators.js +1 -1
  207. package/esm/util/registry-validators.js.map +1 -1
  208. package/esm/v1/__tests__/v1-interactables.test.js +1 -1
  209. package/esm/v1/__tests__/v1-interactables.test.js.map +1 -1
  210. package/esm/v1/components/v1-component-renderer.d.ts +9 -9
  211. package/esm/v1/components/v1-component-renderer.d.ts.map +1 -1
  212. package/esm/v1/components/v1-component-renderer.js +12 -12
  213. package/esm/v1/components/v1-component-renderer.js.map +1 -1
  214. package/esm/v1/components/v1-component-renderer.test.js +16 -16
  215. package/esm/v1/components/v1-component-renderer.test.js.map +1 -1
  216. package/{dist/hooks/use-current-message.d.ts → esm/v1/hooks/use-tambo-current-message.d.ts} +30 -9
  217. package/esm/v1/hooks/use-tambo-current-message.d.ts.map +1 -0
  218. package/esm/{hooks/use-current-message.js → v1/hooks/use-tambo-current-message.js} +13 -8
  219. package/esm/v1/hooks/use-tambo-current-message.js.map +1 -0
  220. package/esm/v1/hooks/use-tambo-v1-auth-state.d.ts +5 -5
  221. package/esm/v1/hooks/use-tambo-v1-auth-state.d.ts.map +1 -1
  222. package/esm/v1/hooks/use-tambo-v1-auth-state.js +8 -8
  223. package/esm/v1/hooks/use-tambo-v1-auth-state.js.map +1 -1
  224. package/esm/v1/hooks/use-tambo-v1-auth-state.test.js +14 -14
  225. package/esm/v1/hooks/use-tambo-v1-auth-state.test.js.map +1 -1
  226. package/esm/v1/hooks/use-tambo-v1-component-state.d.ts +7 -6
  227. package/esm/v1/hooks/use-tambo-v1-component-state.d.ts.map +1 -1
  228. package/esm/v1/hooks/use-tambo-v1-component-state.js +50 -24
  229. package/esm/v1/hooks/use-tambo-v1-component-state.js.map +1 -1
  230. package/esm/v1/hooks/use-tambo-v1-component-state.test.js +62 -37
  231. package/esm/v1/hooks/use-tambo-v1-component-state.test.js.map +1 -1
  232. package/esm/v1/hooks/use-tambo-v1-messages.d.ts +9 -9
  233. package/esm/v1/hooks/use-tambo-v1-messages.d.ts.map +1 -1
  234. package/esm/v1/hooks/use-tambo-v1-messages.js +3 -3
  235. package/esm/v1/hooks/use-tambo-v1-messages.js.map +1 -1
  236. package/esm/v1/hooks/use-tambo-v1-messages.test.js +9 -9
  237. package/esm/v1/hooks/use-tambo-v1-messages.test.js.map +1 -1
  238. package/esm/v1/hooks/use-tambo-v1-send-message.d.ts +3 -3
  239. package/esm/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -1
  240. package/esm/v1/hooks/use-tambo-v1-send-message.js +21 -23
  241. package/esm/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
  242. package/esm/v1/hooks/use-tambo-v1-send-message.test.js +54 -54
  243. package/esm/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -1
  244. package/esm/v1/hooks/use-tambo-v1-stream-status.d.ts +5 -5
  245. package/esm/v1/hooks/use-tambo-v1-stream-status.d.ts.map +1 -1
  246. package/esm/v1/hooks/use-tambo-v1-stream-status.js +12 -14
  247. package/esm/v1/hooks/use-tambo-v1-stream-status.js.map +1 -1
  248. package/esm/v1/hooks/use-tambo-v1-stream-status.test.js +21 -21
  249. package/esm/v1/hooks/use-tambo-v1-stream-status.test.js.map +1 -1
  250. package/esm/v1/hooks/use-tambo-v1-suggestions.d.ts +7 -7
  251. package/esm/v1/hooks/use-tambo-v1-suggestions.d.ts.map +1 -1
  252. package/esm/v1/hooks/use-tambo-v1-suggestions.js +11 -11
  253. package/esm/v1/hooks/use-tambo-v1-suggestions.js.map +1 -1
  254. package/esm/v1/hooks/use-tambo-v1-suggestions.test.js +48 -48
  255. package/esm/v1/hooks/use-tambo-v1-suggestions.test.js.map +1 -1
  256. package/esm/v1/hooks/use-tambo-v1-thread-input.d.ts +3 -3
  257. package/esm/v1/hooks/use-tambo-v1-thread-input.d.ts.map +1 -1
  258. package/esm/v1/hooks/use-tambo-v1-thread-input.js +3 -3
  259. package/esm/v1/hooks/use-tambo-v1-thread-input.js.map +1 -1
  260. package/esm/v1/hooks/use-tambo-v1-thread-input.test.js +32 -32
  261. package/esm/v1/hooks/use-tambo-v1-thread-input.test.js.map +1 -1
  262. package/esm/v1/hooks/use-tambo-v1-thread-list.d.ts +4 -4
  263. package/esm/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -1
  264. package/esm/v1/hooks/use-tambo-v1-thread-list.js +6 -6
  265. package/esm/v1/hooks/use-tambo-v1-thread-list.js.map +1 -1
  266. package/esm/v1/hooks/use-tambo-v1-thread-list.test.js +15 -19
  267. package/esm/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -1
  268. package/esm/v1/hooks/use-tambo-v1-thread.d.ts +3 -3
  269. package/esm/v1/hooks/use-tambo-v1-thread.d.ts.map +1 -1
  270. package/esm/v1/hooks/use-tambo-v1-thread.js +4 -4
  271. package/esm/v1/hooks/use-tambo-v1-thread.js.map +1 -1
  272. package/esm/v1/hooks/use-tambo-v1-thread.test.js +6 -6
  273. package/esm/v1/hooks/use-tambo-v1-thread.test.js.map +1 -1
  274. package/esm/v1/hooks/use-tambo-v1.d.ts +16 -7
  275. package/esm/v1/hooks/use-tambo-v1.d.ts.map +1 -1
  276. package/esm/v1/hooks/use-tambo-v1.js +43 -12
  277. package/esm/v1/hooks/use-tambo-v1.js.map +1 -1
  278. package/esm/v1/hooks/use-tambo-v1.test.js +178 -50
  279. package/esm/v1/hooks/use-tambo-v1.test.js.map +1 -1
  280. package/esm/v1/index.d.ts +31 -27
  281. package/esm/v1/index.d.ts.map +1 -1
  282. package/esm/v1/index.js +38 -33
  283. package/esm/v1/index.js.map +1 -1
  284. package/esm/v1/providers/tambo-v1-provider.d.ts +20 -20
  285. package/esm/v1/providers/tambo-v1-provider.d.ts.map +1 -1
  286. package/esm/v1/providers/tambo-v1-provider.js +32 -32
  287. package/esm/v1/providers/tambo-v1-provider.js.map +1 -1
  288. package/esm/v1/providers/tambo-v1-provider.test.js +23 -23
  289. package/esm/v1/providers/tambo-v1-provider.test.js.map +1 -1
  290. package/esm/v1/providers/tambo-v1-stream-context.d.ts +15 -15
  291. package/esm/v1/providers/tambo-v1-stream-context.d.ts.map +1 -1
  292. package/esm/v1/providers/tambo-v1-stream-context.js +17 -17
  293. package/esm/v1/providers/tambo-v1-stream-context.js.map +1 -1
  294. package/esm/v1/providers/tambo-v1-stream-context.test.js +10 -10
  295. package/esm/v1/providers/tambo-v1-stream-context.test.js.map +1 -1
  296. package/esm/v1/providers/tambo-v1-stub-provider.d.ts +9 -9
  297. package/esm/v1/providers/tambo-v1-stub-provider.d.ts.map +1 -1
  298. package/esm/v1/providers/tambo-v1-stub-provider.js +9 -9
  299. package/esm/v1/providers/tambo-v1-stub-provider.js.map +1 -1
  300. package/esm/v1/providers/tambo-v1-stub-provider.test.js +28 -28
  301. package/esm/v1/providers/tambo-v1-stub-provider.test.js.map +1 -1
  302. package/esm/v1/providers/tambo-v1-thread-input-provider.d.ts +9 -9
  303. package/esm/v1/providers/tambo-v1-thread-input-provider.d.ts.map +1 -1
  304. package/esm/v1/providers/tambo-v1-thread-input-provider.js +15 -15
  305. package/esm/v1/providers/tambo-v1-thread-input-provider.js.map +1 -1
  306. package/esm/v1/types/auth.d.ts +2 -2
  307. package/esm/v1/types/auth.d.ts.map +1 -1
  308. package/esm/v1/types/auth.js.map +1 -1
  309. package/esm/v1/types/component.d.ts +3 -3
  310. package/esm/v1/types/component.d.ts.map +1 -1
  311. package/esm/v1/types/component.js +2 -2
  312. package/esm/v1/types/component.js.map +1 -1
  313. package/esm/v1/types/event.d.ts +1 -1
  314. package/esm/v1/types/event.js +1 -1
  315. package/esm/v1/types/event.js.map +1 -1
  316. package/esm/v1/types/message.d.ts +17 -24
  317. package/esm/v1/types/message.d.ts.map +1 -1
  318. package/esm/v1/types/message.js +1 -1
  319. package/esm/v1/types/message.js.map +1 -1
  320. package/esm/v1/types/thread.d.ts +10 -8
  321. package/esm/v1/types/thread.d.ts.map +1 -1
  322. package/esm/v1/types/thread.js +1 -1
  323. package/esm/v1/types/thread.js.map +1 -1
  324. package/esm/v1/types/tool-choice.d.ts +1 -1
  325. package/esm/v1/types/tool-choice.js.map +1 -1
  326. package/esm/v1/utils/component-renderer.d.ts +11 -5
  327. package/esm/v1/utils/component-renderer.d.ts.map +1 -1
  328. package/esm/v1/utils/component-renderer.js +13 -5
  329. package/esm/v1/utils/component-renderer.js.map +1 -1
  330. package/esm/v1/utils/component-renderer.test.js +8 -8
  331. package/esm/v1/utils/component-renderer.test.js.map +1 -1
  332. package/esm/v1/utils/event-accumulator.d.ts +13 -13
  333. package/esm/v1/utils/event-accumulator.d.ts.map +1 -1
  334. package/esm/v1/utils/event-accumulator.js +26 -15
  335. package/esm/v1/utils/event-accumulator.js.map +1 -1
  336. package/esm/v1/utils/event-accumulator.test.js +54 -19
  337. package/esm/v1/utils/event-accumulator.test.js.map +1 -1
  338. package/esm/v1/utils/registry-conversion.d.ts +18 -18
  339. package/esm/v1/utils/registry-conversion.js +23 -23
  340. package/esm/v1/utils/registry-conversion.js.map +1 -1
  341. package/esm/v1/utils/stream-handler.d.ts +1 -1
  342. package/esm/v1/utils/stream-handler.js +1 -1
  343. package/esm/v1/utils/stream-handler.js.map +1 -1
  344. package/esm/v1/utils/thread-utils.d.ts +2 -2
  345. package/esm/v1/utils/thread-utils.d.ts.map +1 -1
  346. package/esm/v1/utils/thread-utils.js.map +1 -1
  347. package/esm/v1/utils/tool-call-tracker.d.ts +1 -1
  348. package/esm/v1/utils/tool-call-tracker.js +1 -1
  349. package/esm/v1/utils/tool-call-tracker.js.map +1 -1
  350. package/esm/v1/utils/tool-executor.d.ts +1 -1
  351. package/esm/v1/utils/tool-executor.js +2 -2
  352. package/esm/v1/utils/tool-executor.js.map +1 -1
  353. package/package.json +4 -9
  354. package/dist/hooks/index.d.ts +0 -9
  355. package/dist/hooks/index.d.ts.map +0 -1
  356. package/dist/hooks/index.js +0 -34
  357. package/dist/hooks/index.js.map +0 -1
  358. package/dist/hooks/use-component-state.d.ts +0 -30
  359. package/dist/hooks/use-component-state.d.ts.map +0 -1
  360. package/dist/hooks/use-component-state.js +0 -139
  361. package/dist/hooks/use-component-state.js.map +0 -1
  362. package/dist/hooks/use-component-state.test.d.ts +0 -2
  363. package/dist/hooks/use-component-state.test.d.ts.map +0 -1
  364. package/dist/hooks/use-component-state.test.js +0 -406
  365. package/dist/hooks/use-component-state.test.js.map +0 -1
  366. package/dist/hooks/use-current-message.d.ts.map +0 -1
  367. package/dist/hooks/use-current-message.js.map +0 -1
  368. package/dist/hooks/use-current-message.test.d.ts +0 -2
  369. package/dist/hooks/use-current-message.test.d.ts.map +0 -1
  370. package/dist/hooks/use-current-message.test.js +0 -269
  371. package/dist/hooks/use-current-message.test.js.map +0 -1
  372. package/dist/hooks/use-streaming-props.d.ts +0 -11
  373. package/dist/hooks/use-streaming-props.d.ts.map +0 -1
  374. package/dist/hooks/use-streaming-props.js +0 -37
  375. package/dist/hooks/use-streaming-props.js.map +0 -1
  376. package/dist/hooks/use-suggestions.d.ts +0 -46
  377. package/dist/hooks/use-suggestions.d.ts.map +0 -1
  378. package/dist/hooks/use-suggestions.js +0 -118
  379. package/dist/hooks/use-suggestions.js.map +0 -1
  380. package/dist/hooks/use-suggestions.test.d.ts +0 -2
  381. package/dist/hooks/use-suggestions.test.d.ts.map +0 -1
  382. package/dist/hooks/use-suggestions.test.js +0 -247
  383. package/dist/hooks/use-suggestions.test.js.map +0 -1
  384. package/dist/hooks/use-tambo-stream-status.d.ts +0 -90
  385. package/dist/hooks/use-tambo-stream-status.d.ts.map +0 -1
  386. package/dist/hooks/use-tambo-stream-status.js +0 -213
  387. package/dist/hooks/use-tambo-stream-status.js.map +0 -1
  388. package/dist/hooks/use-tambo-stream-status.test.d.ts +0 -2
  389. package/dist/hooks/use-tambo-stream-status.test.d.ts.map +0 -1
  390. package/dist/hooks/use-tambo-stream-status.test.js +0 -378
  391. package/dist/hooks/use-tambo-stream-status.test.js.map +0 -1
  392. package/dist/hooks/use-tambo-threads.d.ts +0 -158
  393. package/dist/hooks/use-tambo-threads.d.ts.map +0 -1
  394. package/dist/hooks/use-tambo-threads.js +0 -45
  395. package/dist/hooks/use-tambo-threads.js.map +0 -1
  396. package/dist/hooks/use-tambo-threads.test.d.ts +0 -2
  397. package/dist/hooks/use-tambo-threads.test.d.ts.map +0 -1
  398. package/dist/hooks/use-tambo-threads.test.js +0 -214
  399. package/dist/hooks/use-tambo-threads.test.js.map +0 -1
  400. package/dist/model/generate-component-response.d.ts +0 -37
  401. package/dist/model/generate-component-response.d.ts.map +0 -1
  402. package/dist/model/generate-component-response.js +0 -29
  403. package/dist/model/generate-component-response.js.map +0 -1
  404. package/dist/model/tambo-thread.d.ts +0 -15
  405. package/dist/model/tambo-thread.d.ts.map +0 -1
  406. package/dist/model/tambo-thread.js +0 -3
  407. package/dist/model/tambo-thread.js.map +0 -1
  408. package/dist/providers/__tests__/thread-input-resource-resolution.test.d.ts +0 -2
  409. package/dist/providers/__tests__/thread-input-resource-resolution.test.d.ts.map +0 -1
  410. package/dist/providers/__tests__/thread-input-resource-resolution.test.js +0 -592
  411. package/dist/providers/__tests__/thread-input-resource-resolution.test.js.map +0 -1
  412. package/dist/providers/index.d.ts +0 -13
  413. package/dist/providers/index.d.ts.map +0 -1
  414. package/dist/providers/index.js +0 -41
  415. package/dist/providers/index.js.map +0 -1
  416. package/dist/providers/tambo-component-provider.d.ts +0 -23
  417. package/dist/providers/tambo-component-provider.d.ts.map +0 -1
  418. package/dist/providers/tambo-component-provider.js +0 -88
  419. package/dist/providers/tambo-component-provider.js.map +0 -1
  420. package/dist/providers/tambo-prop-stream-provider/index.d.ts +0 -19
  421. package/dist/providers/tambo-prop-stream-provider/index.d.ts.map +0 -1
  422. package/dist/providers/tambo-prop-stream-provider/index.js +0 -43
  423. package/dist/providers/tambo-prop-stream-provider/index.js.map +0 -1
  424. package/dist/providers/tambo-prop-stream-provider/pending.d.ts +0 -12
  425. package/dist/providers/tambo-prop-stream-provider/pending.d.ts.map +0 -1
  426. package/dist/providers/tambo-prop-stream-provider/pending.js +0 -31
  427. package/dist/providers/tambo-prop-stream-provider/pending.js.map +0 -1
  428. package/dist/providers/tambo-prop-stream-provider/provider.d.ts +0 -17
  429. package/dist/providers/tambo-prop-stream-provider/provider.d.ts.map +0 -1
  430. package/dist/providers/tambo-prop-stream-provider/provider.js +0 -107
  431. package/dist/providers/tambo-prop-stream-provider/provider.js.map +0 -1
  432. package/dist/providers/tambo-prop-stream-provider/streaming.d.ts +0 -12
  433. package/dist/providers/tambo-prop-stream-provider/streaming.d.ts.map +0 -1
  434. package/dist/providers/tambo-prop-stream-provider/streaming.js +0 -28
  435. package/dist/providers/tambo-prop-stream-provider/streaming.js.map +0 -1
  436. package/dist/providers/tambo-prop-stream-provider/success.d.ts +0 -12
  437. package/dist/providers/tambo-prop-stream-provider/success.d.ts.map +0 -1
  438. package/dist/providers/tambo-prop-stream-provider/success.js +0 -28
  439. package/dist/providers/tambo-prop-stream-provider/success.js.map +0 -1
  440. package/dist/providers/tambo-prop-stream-provider/types.d.ts +0 -25
  441. package/dist/providers/tambo-prop-stream-provider/types.d.ts.map +0 -1
  442. package/dist/providers/tambo-prop-stream-provider/types.js +0 -6
  443. package/dist/providers/tambo-prop-stream-provider/types.js.map +0 -1
  444. package/dist/providers/tambo-prop-stream-provider.test.d.ts +0 -2
  445. package/dist/providers/tambo-prop-stream-provider.test.d.ts.map +0 -1
  446. package/dist/providers/tambo-prop-stream-provider.test.js +0 -275
  447. package/dist/providers/tambo-prop-stream-provider.test.js.map +0 -1
  448. package/dist/providers/tambo-provider.d.ts +0 -53
  449. package/dist/providers/tambo-provider.d.ts.map +0 -1
  450. package/dist/providers/tambo-provider.js +0 -133
  451. package/dist/providers/tambo-provider.js.map +0 -1
  452. package/dist/providers/tambo-stubs.d.ts +0 -89
  453. package/dist/providers/tambo-stubs.d.ts.map +0 -1
  454. package/dist/providers/tambo-stubs.js +0 -279
  455. package/dist/providers/tambo-stubs.js.map +0 -1
  456. package/dist/providers/tambo-stubs.test.d.ts +0 -2
  457. package/dist/providers/tambo-stubs.test.d.ts.map +0 -1
  458. package/dist/providers/tambo-stubs.test.js +0 -97
  459. package/dist/providers/tambo-stubs.test.js.map +0 -1
  460. package/dist/providers/tambo-thread-input-provider.d.ts +0 -65
  461. package/dist/providers/tambo-thread-input-provider.d.ts.map +0 -1
  462. package/dist/providers/tambo-thread-input-provider.js +0 -179
  463. package/dist/providers/tambo-thread-input-provider.js.map +0 -1
  464. package/dist/providers/tambo-thread-provider-initial-messages.test.d.ts +0 -2
  465. package/dist/providers/tambo-thread-provider-initial-messages.test.d.ts.map +0 -1
  466. package/dist/providers/tambo-thread-provider-initial-messages.test.js +0 -278
  467. package/dist/providers/tambo-thread-provider-initial-messages.test.js.map +0 -1
  468. package/dist/providers/tambo-thread-provider.d.ts +0 -126
  469. package/dist/providers/tambo-thread-provider.d.ts.map +0 -1
  470. package/dist/providers/tambo-thread-provider.js +0 -931
  471. package/dist/providers/tambo-thread-provider.js.map +0 -1
  472. package/dist/providers/tambo-thread-provider.test.d.ts +0 -2
  473. package/dist/providers/tambo-thread-provider.test.d.ts.map +0 -1
  474. package/dist/providers/tambo-thread-provider.test.js +0 -1591
  475. package/dist/providers/tambo-thread-provider.test.js.map +0 -1
  476. package/dist/util/generate-component.d.ts +0 -12
  477. package/dist/util/generate-component.d.ts.map +0 -1
  478. package/dist/util/generate-component.js +0 -58
  479. package/dist/util/generate-component.js.map +0 -1
  480. package/dist/util/generate-component.test.d.ts +0 -2
  481. package/dist/util/generate-component.test.d.ts.map +0 -1
  482. package/dist/util/generate-component.test.js +0 -340
  483. package/dist/util/generate-component.test.js.map +0 -1
  484. package/esm/hooks/index.d.ts +0 -9
  485. package/esm/hooks/index.d.ts.map +0 -1
  486. package/esm/hooks/index.js +0 -10
  487. package/esm/hooks/index.js.map +0 -1
  488. package/esm/hooks/use-component-state.d.ts +0 -30
  489. package/esm/hooks/use-component-state.d.ts.map +0 -1
  490. package/esm/hooks/use-component-state.js +0 -136
  491. package/esm/hooks/use-component-state.js.map +0 -1
  492. package/esm/hooks/use-component-state.test.d.ts +0 -2
  493. package/esm/hooks/use-component-state.test.d.ts.map +0 -1
  494. package/esm/hooks/use-component-state.test.js +0 -401
  495. package/esm/hooks/use-component-state.test.js.map +0 -1
  496. package/esm/hooks/use-current-message.d.ts.map +0 -1
  497. package/esm/hooks/use-current-message.js.map +0 -1
  498. package/esm/hooks/use-current-message.test.d.ts +0 -2
  499. package/esm/hooks/use-current-message.test.d.ts.map +0 -1
  500. package/esm/hooks/use-current-message.test.js +0 -264
  501. package/esm/hooks/use-current-message.test.js.map +0 -1
  502. package/esm/hooks/use-streaming-props.d.ts +0 -11
  503. package/esm/hooks/use-streaming-props.d.ts.map +0 -1
  504. package/esm/hooks/use-streaming-props.js +0 -34
  505. package/esm/hooks/use-streaming-props.js.map +0 -1
  506. package/esm/hooks/use-suggestions.d.ts +0 -46
  507. package/esm/hooks/use-suggestions.d.ts.map +0 -1
  508. package/esm/hooks/use-suggestions.js +0 -115
  509. package/esm/hooks/use-suggestions.js.map +0 -1
  510. package/esm/hooks/use-suggestions.test.d.ts +0 -2
  511. package/esm/hooks/use-suggestions.test.d.ts.map +0 -1
  512. package/esm/hooks/use-suggestions.test.js +0 -245
  513. package/esm/hooks/use-suggestions.test.js.map +0 -1
  514. package/esm/hooks/use-tambo-stream-status.d.ts +0 -90
  515. package/esm/hooks/use-tambo-stream-status.d.ts.map +0 -1
  516. package/esm/hooks/use-tambo-stream-status.js +0 -210
  517. package/esm/hooks/use-tambo-stream-status.js.map +0 -1
  518. package/esm/hooks/use-tambo-stream-status.test.d.ts +0 -2
  519. package/esm/hooks/use-tambo-stream-status.test.d.ts.map +0 -1
  520. package/esm/hooks/use-tambo-stream-status.test.js +0 -376
  521. package/esm/hooks/use-tambo-stream-status.test.js.map +0 -1
  522. package/esm/hooks/use-tambo-threads.d.ts +0 -158
  523. package/esm/hooks/use-tambo-threads.d.ts.map +0 -1
  524. package/esm/hooks/use-tambo-threads.js +0 -42
  525. package/esm/hooks/use-tambo-threads.js.map +0 -1
  526. package/esm/hooks/use-tambo-threads.test.d.ts +0 -2
  527. package/esm/hooks/use-tambo-threads.test.d.ts.map +0 -1
  528. package/esm/hooks/use-tambo-threads.test.js +0 -212
  529. package/esm/hooks/use-tambo-threads.test.js.map +0 -1
  530. package/esm/model/generate-component-response.d.ts +0 -37
  531. package/esm/model/generate-component-response.d.ts.map +0 -1
  532. package/esm/model/generate-component-response.js +0 -25
  533. package/esm/model/generate-component-response.js.map +0 -1
  534. package/esm/model/tambo-thread.d.ts +0 -15
  535. package/esm/model/tambo-thread.d.ts.map +0 -1
  536. package/esm/model/tambo-thread.js +0 -2
  537. package/esm/model/tambo-thread.js.map +0 -1
  538. package/esm/providers/__tests__/thread-input-resource-resolution.test.d.ts +0 -2
  539. package/esm/providers/__tests__/thread-input-resource-resolution.test.d.ts.map +0 -1
  540. package/esm/providers/__tests__/thread-input-resource-resolution.test.js +0 -587
  541. package/esm/providers/__tests__/thread-input-resource-resolution.test.js.map +0 -1
  542. package/esm/providers/index.d.ts +0 -13
  543. package/esm/providers/index.d.ts.map +0 -1
  544. package/esm/providers/index.js +0 -11
  545. package/esm/providers/index.js.map +0 -1
  546. package/esm/providers/tambo-component-provider.d.ts +0 -23
  547. package/esm/providers/tambo-component-provider.d.ts.map +0 -1
  548. package/esm/providers/tambo-component-provider.js +0 -50
  549. package/esm/providers/tambo-component-provider.js.map +0 -1
  550. package/esm/providers/tambo-prop-stream-provider/index.d.ts +0 -19
  551. package/esm/providers/tambo-prop-stream-provider/index.d.ts.map +0 -1
  552. package/esm/providers/tambo-prop-stream-provider/index.js +0 -22
  553. package/esm/providers/tambo-prop-stream-provider/index.js.map +0 -1
  554. package/esm/providers/tambo-prop-stream-provider/pending.d.ts +0 -12
  555. package/esm/providers/tambo-prop-stream-provider/pending.d.ts.map +0 -1
  556. package/esm/providers/tambo-prop-stream-provider/pending.js +0 -24
  557. package/esm/providers/tambo-prop-stream-provider/pending.js.map +0 -1
  558. package/esm/providers/tambo-prop-stream-provider/provider.d.ts +0 -17
  559. package/esm/providers/tambo-prop-stream-provider/provider.d.ts.map +0 -1
  560. package/esm/providers/tambo-prop-stream-provider/provider.js +0 -70
  561. package/esm/providers/tambo-prop-stream-provider/provider.js.map +0 -1
  562. package/esm/providers/tambo-prop-stream-provider/streaming.d.ts +0 -12
  563. package/esm/providers/tambo-prop-stream-provider/streaming.d.ts.map +0 -1
  564. package/esm/providers/tambo-prop-stream-provider/streaming.js +0 -21
  565. package/esm/providers/tambo-prop-stream-provider/streaming.js.map +0 -1
  566. package/esm/providers/tambo-prop-stream-provider/success.d.ts +0 -12
  567. package/esm/providers/tambo-prop-stream-provider/success.d.ts.map +0 -1
  568. package/esm/providers/tambo-prop-stream-provider/success.js +0 -21
  569. package/esm/providers/tambo-prop-stream-provider/success.js.map +0 -1
  570. package/esm/providers/tambo-prop-stream-provider/types.d.ts +0 -25
  571. package/esm/providers/tambo-prop-stream-provider/types.d.ts.map +0 -1
  572. package/esm/providers/tambo-prop-stream-provider/types.js +0 -3
  573. package/esm/providers/tambo-prop-stream-provider/types.js.map +0 -1
  574. package/esm/providers/tambo-prop-stream-provider.test.d.ts +0 -2
  575. package/esm/providers/tambo-prop-stream-provider.test.d.ts.map +0 -1
  576. package/esm/providers/tambo-prop-stream-provider.test.js +0 -270
  577. package/esm/providers/tambo-prop-stream-provider.test.js.map +0 -1
  578. package/esm/providers/tambo-provider.d.ts +0 -53
  579. package/esm/providers/tambo-provider.d.ts.map +0 -1
  580. package/esm/providers/tambo-provider.js +0 -94
  581. package/esm/providers/tambo-provider.js.map +0 -1
  582. package/esm/providers/tambo-stubs.d.ts +0 -89
  583. package/esm/providers/tambo-stubs.d.ts.map +0 -1
  584. package/esm/providers/tambo-stubs.js +0 -242
  585. package/esm/providers/tambo-stubs.js.map +0 -1
  586. package/esm/providers/tambo-stubs.test.d.ts +0 -2
  587. package/esm/providers/tambo-stubs.test.d.ts.map +0 -1
  588. package/esm/providers/tambo-stubs.test.js +0 -62
  589. package/esm/providers/tambo-stubs.test.js.map +0 -1
  590. package/esm/providers/tambo-thread-input-provider.d.ts +0 -65
  591. package/esm/providers/tambo-thread-input-provider.d.ts.map +0 -1
  592. package/esm/providers/tambo-thread-input-provider.js +0 -141
  593. package/esm/providers/tambo-thread-input-provider.js.map +0 -1
  594. package/esm/providers/tambo-thread-provider-initial-messages.test.d.ts +0 -2
  595. package/esm/providers/tambo-thread-provider-initial-messages.test.d.ts.map +0 -1
  596. package/esm/providers/tambo-thread-provider-initial-messages.test.js +0 -273
  597. package/esm/providers/tambo-thread-provider-initial-messages.test.js.map +0 -1
  598. package/esm/providers/tambo-thread-provider.d.ts +0 -126
  599. package/esm/providers/tambo-thread-provider.d.ts.map +0 -1
  600. package/esm/providers/tambo-thread-provider.js +0 -891
  601. package/esm/providers/tambo-thread-provider.js.map +0 -1
  602. package/esm/providers/tambo-thread-provider.test.d.ts +0 -2
  603. package/esm/providers/tambo-thread-provider.test.d.ts.map +0 -1
  604. package/esm/providers/tambo-thread-provider.test.js +0 -1553
  605. package/esm/providers/tambo-thread-provider.test.js.map +0 -1
  606. package/esm/util/generate-component.d.ts +0 -12
  607. package/esm/util/generate-component.d.ts.map +0 -1
  608. package/esm/util/generate-component.js +0 -52
  609. package/esm/util/generate-component.js.map +0 -1
  610. package/esm/util/generate-component.test.d.ts +0 -2
  611. package/esm/util/generate-component.test.d.ts.map +0 -1
  612. package/esm/util/generate-component.test.js +0 -302
  613. package/esm/util/generate-component.test.js.map +0 -1
@@ -1,1553 +0,0 @@
1
- import TamboAI, { advanceStream } from "@tambo-ai/typescript-sdk";
2
- import { act, renderHook } from "@testing-library/react";
3
- import React from "react";
4
- import { z } from "zod/v4";
5
- import { GenerationStage, } from "../model/generate-component-response.js";
6
- import { serializeRegistry } from "../testing/tools.js";
7
- import { TamboClientContext, useTamboClient, useTamboQueryClient, } from "./tambo-client-provider.js";
8
- import { TamboContextHelpersProvider } from "./tambo-context-helpers-provider.js";
9
- import { TamboMcpTokenProvider } from "./tambo-mcp-token-provider.js";
10
- import { TamboRegistryProvider } from "./tambo-registry-provider.js";
11
- import { TamboThreadProvider, useTamboThread } from "./tambo-thread-provider.js";
12
- // Mock crypto.randomUUID
13
- Object.defineProperty(global, "crypto", {
14
- value: {
15
- randomUUID: jest.fn().mockReturnValue("test-uuid"),
16
- },
17
- });
18
- // Mock the required providers
19
- jest.mock("./tambo-client-provider", () => {
20
- return {
21
- useTamboClient: jest.fn(),
22
- useTamboQueryClient: jest.fn(),
23
- TamboClientContext: React.createContext(undefined),
24
- };
25
- });
26
- jest.mock("@tambo-ai/typescript-sdk", () => {
27
- const actual = jest.requireActual("@tambo-ai/typescript-sdk");
28
- return {
29
- __esModule: true,
30
- ...actual,
31
- advanceStream: jest.fn(),
32
- };
33
- });
34
- // Mock the getCustomContext
35
- jest.mock("../util/registry", () => ({
36
- ...jest.requireActual("../util/registry"),
37
- getCustomContext: () => ({
38
- message: "additional instructions",
39
- }),
40
- }));
41
- // Test utilities
42
- const createMockMessage = (overrides = {}) => ({
43
- id: "test-message-1",
44
- content: [{ type: "text", text: "Hello" }],
45
- role: "user",
46
- threadId: "test-thread-1",
47
- createdAt: new Date().toISOString(),
48
- componentState: {},
49
- ...overrides,
50
- });
51
- const createMockThread = (overrides = {}) => ({
52
- id: "test-thread-1",
53
- messages: [],
54
- createdAt: "2024-01-01T00:00:00Z",
55
- projectId: "test-project",
56
- updatedAt: "2024-01-01T00:00:00Z",
57
- metadata: {},
58
- ...overrides,
59
- });
60
- const createMockAdvanceResponse = (overrides = {}) => ({
61
- responseMessageDto: {
62
- id: "test-uuid",
63
- content: [{ type: "text", text: "Default response" }],
64
- role: "assistant",
65
- threadId: "test-thread-1",
66
- component: undefined,
67
- componentState: {},
68
- createdAt: new Date().toISOString(),
69
- },
70
- generationStage: GenerationStage.COMPLETE,
71
- mcpAccessToken: "test-mcp-access-token",
72
- ...overrides,
73
- });
74
- describe("TamboThreadProvider", () => {
75
- const mockThread = createMockThread();
76
- let mockTamboAI;
77
- let mockThreadsApi;
78
- let mockProjectsApi;
79
- let mockQueryClient;
80
- const mockRegistry = [
81
- {
82
- name: "TestOnly",
83
- component: () => React.createElement("div", null, "TestOnly"),
84
- description: "TestOnly",
85
- propsSchema: z.object({
86
- test: z.string(),
87
- }),
88
- associatedTools: [
89
- {
90
- name: "test-tool",
91
- tool: jest.fn().mockResolvedValue("test-tool"),
92
- description: "test-tool",
93
- inputSchema: z.object({
94
- param: z.string().describe("test-param-description"),
95
- }),
96
- outputSchema: z.string(),
97
- },
98
- ],
99
- },
100
- ];
101
- /**
102
- * Creates a test wrapper component with configurable options.
103
- * Reduces duplication across tests by centralizing provider setup.
104
- * @param options - Configuration options for the wrapper
105
- * @param options.components - The Tambo components to register
106
- * @param options.streaming - Whether to enable streaming responses
107
- * @param options.onCallUnregisteredTool - Handler for unregistered tool calls
108
- * @param options.autoGenerateThreadName - Whether to auto-generate thread names
109
- * @param options.autoGenerateNameThreshold - Token threshold for auto-generating names
110
- * @returns A React component that wraps children with the necessary providers
111
- */
112
- const createWrapper = ({ components = mockRegistry, streaming = false, onCallUnregisteredTool, autoGenerateThreadName, autoGenerateNameThreshold, } = {}) => function TestWrapper({ children }) {
113
- const client = useTamboClient();
114
- const queryClient = useTamboQueryClient();
115
- return (React.createElement(TamboClientContext.Provider, { value: {
116
- client,
117
- queryClient,
118
- isUpdatingToken: false,
119
- tokenExchangeError: null,
120
- userToken: undefined,
121
- hasValidToken: false,
122
- } },
123
- React.createElement(TamboRegistryProvider, { components: components, onCallUnregisteredTool: onCallUnregisteredTool },
124
- React.createElement(TamboContextHelpersProvider, { contextHelpers: {
125
- currentTimeContextHelper: () => null,
126
- currentPageContextHelper: () => null,
127
- } },
128
- React.createElement(TamboMcpTokenProvider, null,
129
- React.createElement(TamboThreadProvider, { streaming: streaming, autoGenerateThreadName: autoGenerateThreadName, autoGenerateNameThreshold: autoGenerateNameThreshold }, children))))));
130
- };
131
- // Default wrapper for most tests
132
- const Wrapper = createWrapper();
133
- afterEach(() => {
134
- jest.restoreAllMocks();
135
- });
136
- beforeEach(() => {
137
- jest.clearAllMocks();
138
- mockTamboAI = new TamboAI({
139
- apiKey: "",
140
- fetch: () => {
141
- throw new Error("Unexpected network call in test");
142
- },
143
- });
144
- mockThreadsApi = mockTamboAI.beta.threads;
145
- mockProjectsApi = mockTamboAI.beta.projects;
146
- // Setup mock query client
147
- mockQueryClient = {
148
- invalidateQueries: jest.fn().mockResolvedValue(undefined),
149
- setQueryData: jest.fn(),
150
- };
151
- jest
152
- .mocked(useTamboQueryClient)
153
- .mockReturnValue(mockQueryClient);
154
- jest.spyOn(mockThreadsApi, "retrieve").mockResolvedValue(mockThread);
155
- jest
156
- .spyOn(mockThreadsApi.messages, "create")
157
- .mockResolvedValue(createMockMessage());
158
- jest
159
- .spyOn(mockThreadsApi, "advance")
160
- .mockResolvedValue(createMockAdvanceResponse());
161
- jest
162
- .spyOn(mockThreadsApi, "advanceByID")
163
- .mockResolvedValue(createMockAdvanceResponse());
164
- jest.spyOn(mockThreadsApi, "generateName").mockResolvedValue({
165
- ...mockThread,
166
- name: "Generated Thread Name",
167
- });
168
- jest.spyOn(mockThreadsApi, "update").mockResolvedValue({});
169
- jest.spyOn(mockProjectsApi, "getCurrent").mockResolvedValue({
170
- id: "test-project-id",
171
- name: "Test Project",
172
- isTokenRequired: false,
173
- providerType: "llm",
174
- userId: "test-user-id",
175
- });
176
- jest.mocked(useTamboClient).mockReturnValue(mockTamboAI);
177
- });
178
- it("should initialize with placeholder thread", () => {
179
- const { result } = renderHook(() => useTamboThread(), { wrapper: Wrapper });
180
- expect(result.current.thread.id).toBe("placeholder");
181
- expect(result.current.isIdle).toBe(true);
182
- expect(result.current.generationStage).toBe(GenerationStage.IDLE);
183
- });
184
- it("should switch to a new thread", async () => {
185
- const { result } = renderHook(() => useTamboThread(), { wrapper: Wrapper });
186
- await act(async () => {
187
- await result.current.switchCurrentThread("test-thread-1");
188
- });
189
- expect(mockThreadsApi.retrieve).toHaveBeenCalledWith("test-thread-1");
190
- expect(result.current.thread.id).toBe("test-thread-1");
191
- });
192
- it("should start a new thread", async () => {
193
- const { result } = renderHook(() => useTamboThread(), { wrapper: Wrapper });
194
- await act(async () => {
195
- result.current.startNewThread();
196
- });
197
- expect(result.current.thread.id).toBe("placeholder");
198
- expect(result.current.isIdle).toBe(true);
199
- });
200
- it("should add a message to the thread", async () => {
201
- const { result } = renderHook(() => useTamboThread(), { wrapper: Wrapper });
202
- const testMessage = {
203
- id: "test-message-1",
204
- content: [{ type: "text", text: "Hello" }],
205
- role: "user",
206
- threadId: "test-thread-1",
207
- createdAt: new Date().toISOString(),
208
- componentState: {},
209
- };
210
- await act(async () => {
211
- await result.current.addThreadMessage(testMessage, true);
212
- });
213
- expect(mockThreadsApi.messages.create).toHaveBeenCalledWith("test-thread-1", {
214
- content: testMessage.content,
215
- role: testMessage.role,
216
- });
217
- });
218
- it("should update a message in the thread", async () => {
219
- const { result } = renderHook(() => useTamboThread(), { wrapper: Wrapper });
220
- const testMessage = {
221
- id: "test-message-1",
222
- content: [{ type: "text", text: "Updated message" }],
223
- role: "user",
224
- threadId: "test-thread-1",
225
- createdAt: new Date().toISOString(),
226
- componentState: {},
227
- };
228
- await act(async () => {
229
- await result.current.updateThreadMessage("test-message-1", testMessage, true);
230
- });
231
- expect(mockThreadsApi.messages.create).toHaveBeenCalledWith("test-thread-1", {
232
- content: testMessage.content,
233
- role: testMessage.role,
234
- });
235
- });
236
- it("should send a message and update thread state", async () => {
237
- const mockStreamResponse = {
238
- responseMessageDto: {
239
- id: "response-1",
240
- content: [{ type: "text", text: "Response" }],
241
- role: "assistant",
242
- threadId: "test-thread-1",
243
- component: undefined,
244
- componentState: {},
245
- createdAt: new Date().toISOString(),
246
- },
247
- generationStage: GenerationStage.COMPLETE,
248
- mcpAccessToken: "test-mcp-access-token",
249
- };
250
- const mockAsyncIterator = {
251
- [Symbol.asyncIterator]: async function* () {
252
- yield mockStreamResponse;
253
- },
254
- };
255
- jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
256
- const { result } = renderHook(() => useTamboThread(), { wrapper: Wrapper });
257
- await act(async () => {
258
- await result.current.sendThreadMessage("Hello", {
259
- threadId: "test-thread-1",
260
- streamResponse: true,
261
- additionalContext: {
262
- custom: {
263
- message: "additional instructions",
264
- },
265
- },
266
- });
267
- });
268
- expect(advanceStream).toHaveBeenCalledWith(expect.anything(), {
269
- messageToAppend: {
270
- content: [{ type: "text", text: "Hello" }],
271
- role: "user",
272
- additionalContext: {
273
- custom: {
274
- message: "additional instructions",
275
- },
276
- },
277
- },
278
- availableComponents: serializeRegistry(mockRegistry),
279
- contextKey: undefined,
280
- clientTools: [],
281
- toolCallCounts: {},
282
- }, "test-thread-1");
283
- expect(result.current.generationStage).toBe(GenerationStage.COMPLETE);
284
- });
285
- it("should handle streaming responses", async () => {
286
- const mockStreamResponse = {
287
- responseMessageDto: {
288
- id: "stream-1",
289
- content: [{ type: "text", text: "Streaming response" }],
290
- role: "assistant",
291
- threadId: "test-thread-1",
292
- component: undefined,
293
- componentState: {},
294
- createdAt: new Date().toISOString(),
295
- },
296
- generationStage: GenerationStage.COMPLETE,
297
- mcpAccessToken: "test-mcp-access-token",
298
- };
299
- // Create an async iterator mock
300
- const mockAsyncIterator = {
301
- [Symbol.asyncIterator]: async function* () {
302
- yield mockStreamResponse;
303
- },
304
- };
305
- // Mock advanceStream to return our async iterator
306
- jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
307
- const { result } = renderHook(() => useTamboThread(), { wrapper: Wrapper });
308
- await act(async () => {
309
- await result.current.sendThreadMessage("Hello", {
310
- threadId: "test-thread-1",
311
- streamResponse: true,
312
- });
313
- });
314
- expect(result.current.generationStage).toBe(GenerationStage.COMPLETE);
315
- });
316
- it("should handle tool calls during message processing.", async () => {
317
- const mockToolCallChunk = {
318
- responseMessageDto: {
319
- id: "tool-call-1",
320
- content: [{ type: "text", text: "Tool response" }],
321
- role: "tool",
322
- threadId: "test-thread-1",
323
- toolCallRequest: {
324
- toolName: "test-tool",
325
- parameters: [{ parameterName: "test", parameterValue: "test" }],
326
- },
327
- componentState: {},
328
- createdAt: new Date().toISOString(),
329
- },
330
- generationStage: GenerationStage.COMPLETE,
331
- mcpAccessToken: "test-mcp-access-token",
332
- };
333
- const mockFinalChunk = {
334
- responseMessageDto: {
335
- id: "advance-response2",
336
- content: [{ type: "text", text: "response 2" }],
337
- role: "user",
338
- threadId: "test-thread-1",
339
- componentState: {},
340
- createdAt: new Date().toISOString(),
341
- },
342
- generationStage: GenerationStage.COMPLETE,
343
- mcpAccessToken: "test-mcp-access-token",
344
- };
345
- const mockAsyncIterator = {
346
- [Symbol.asyncIterator]: async function* () {
347
- yield mockToolCallChunk;
348
- },
349
- };
350
- const mockAsyncIterator2 = {
351
- [Symbol.asyncIterator]: async function* () {
352
- yield mockFinalChunk;
353
- },
354
- };
355
- jest
356
- .mocked(advanceStream)
357
- .mockResolvedValueOnce(mockAsyncIterator)
358
- .mockResolvedValueOnce(mockAsyncIterator2);
359
- const { result } = renderHook(() => useTamboThread(), { wrapper: Wrapper });
360
- await act(async () => {
361
- await result.current.sendThreadMessage("Use tool", {
362
- threadId: "test-thread-1",
363
- streamResponse: true,
364
- });
365
- });
366
- expect(result.current.generationStage).toBe(GenerationStage.COMPLETE);
367
- // New inputSchema interface: tool receives single object arg
368
- expect(mockRegistry[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith({
369
- test: "test",
370
- });
371
- });
372
- it("should handle unregistered tool calls with onCallUnregisteredTool", async () => {
373
- const mockOnCallUnregisteredTool = jest
374
- .fn()
375
- .mockResolvedValue("unregistered-tool-result");
376
- const mockUnregisteredToolCallChunk = {
377
- responseMessageDto: {
378
- id: "unregistered-tool-call-1",
379
- content: [{ type: "text", text: "Unregistered tool response" }],
380
- role: "tool",
381
- threadId: "test-thread-1",
382
- toolCallRequest: {
383
- toolName: "unregistered-tool",
384
- parameters: [
385
- { parameterName: "input", parameterValue: "test-input" },
386
- ],
387
- },
388
- componentState: {},
389
- createdAt: new Date().toISOString(),
390
- },
391
- generationStage: GenerationStage.COMPLETE,
392
- mcpAccessToken: "test-mcp-access-token",
393
- };
394
- const mockFinalChunk = {
395
- responseMessageDto: {
396
- id: "advance-response2",
397
- content: [{ type: "text", text: "response 2" }],
398
- role: "user",
399
- threadId: "test-thread-1",
400
- componentState: {},
401
- createdAt: new Date().toISOString(),
402
- },
403
- generationStage: GenerationStage.COMPLETE,
404
- mcpAccessToken: "test-mcp-access-token",
405
- };
406
- const mockAsyncIterator = {
407
- [Symbol.asyncIterator]: async function* () {
408
- yield mockUnregisteredToolCallChunk;
409
- },
410
- };
411
- const mockAsyncIterator2 = {
412
- [Symbol.asyncIterator]: async function* () {
413
- yield mockFinalChunk;
414
- },
415
- };
416
- jest
417
- .mocked(advanceStream)
418
- .mockResolvedValueOnce(mockAsyncIterator)
419
- .mockResolvedValueOnce(mockAsyncIterator2);
420
- const { result } = renderHook(() => useTamboThread(), {
421
- wrapper: createWrapper({
422
- onCallUnregisteredTool: mockOnCallUnregisteredTool,
423
- }),
424
- });
425
- await act(async () => {
426
- await result.current.sendThreadMessage("Use unregistered tool", {
427
- threadId: "test-thread-1",
428
- streamResponse: true,
429
- });
430
- });
431
- expect(result.current.generationStage).toBe(GenerationStage.COMPLETE);
432
- expect(mockOnCallUnregisteredTool).toHaveBeenCalledWith("unregistered-tool", [{ parameterName: "input", parameterValue: "test-input" }]);
433
- });
434
- it("should handle unregistered tool calls without onCallUnregisteredTool", async () => {
435
- const mockUnregisteredToolCallChunk = {
436
- responseMessageDto: {
437
- id: "unregistered-tool-call-1",
438
- content: [{ type: "text", text: "Unregistered tool response" }],
439
- role: "tool",
440
- threadId: "test-thread-1",
441
- toolCallRequest: {
442
- toolName: "unregistered-tool",
443
- parameters: [
444
- { parameterName: "input", parameterValue: "test-input" },
445
- ],
446
- },
447
- componentState: {},
448
- createdAt: new Date().toISOString(),
449
- },
450
- generationStage: GenerationStage.COMPLETE,
451
- mcpAccessToken: "test-mcp-access-token",
452
- };
453
- const mockFinalChunk = {
454
- responseMessageDto: {
455
- id: "advance-response2",
456
- content: [{ type: "text", text: "response 2" }],
457
- role: "user",
458
- threadId: "test-thread-1",
459
- componentState: {},
460
- createdAt: new Date().toISOString(),
461
- },
462
- generationStage: GenerationStage.COMPLETE,
463
- mcpAccessToken: "test-mcp-access-token",
464
- };
465
- const mockAsyncIterator = {
466
- [Symbol.asyncIterator]: async function* () {
467
- yield mockUnregisteredToolCallChunk;
468
- },
469
- };
470
- const mockAsyncIterator2 = {
471
- [Symbol.asyncIterator]: async function* () {
472
- yield mockFinalChunk;
473
- },
474
- };
475
- jest
476
- .mocked(advanceStream)
477
- .mockResolvedValueOnce(mockAsyncIterator)
478
- .mockResolvedValueOnce(mockAsyncIterator2);
479
- const { result } = renderHook(() => useTamboThread(), { wrapper: Wrapper });
480
- await act(async () => {
481
- await result.current.sendThreadMessage("Use unregistered tool", {
482
- threadId: "test-thread-1",
483
- streamResponse: true,
484
- });
485
- });
486
- expect(result.current.generationStage).toBe(GenerationStage.COMPLETE);
487
- // Should not throw an error, but the tool call should fail gracefully
488
- });
489
- describe("streaming behavior", () => {
490
- it("should call advanceStream when streamResponse=true", async () => {
491
- const mockStreamResponse = {
492
- responseMessageDto: {
493
- id: "stream-response",
494
- content: [{ type: "text", text: "Streaming response" }],
495
- role: "assistant",
496
- threadId: "test-thread-1",
497
- component: undefined,
498
- componentState: {},
499
- createdAt: new Date().toISOString(),
500
- },
501
- generationStage: GenerationStage.COMPLETE,
502
- mcpAccessToken: "test-mcp-access-token",
503
- };
504
- const mockAsyncIterator = {
505
- [Symbol.asyncIterator]: async function* () {
506
- yield mockStreamResponse;
507
- },
508
- };
509
- jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
510
- const { result } = renderHook(() => useTamboThread(), {
511
- wrapper: createWrapper({ streaming: true }),
512
- });
513
- await act(async () => {
514
- await result.current.sendThreadMessage("Hello streaming", {
515
- threadId: "test-thread-1",
516
- streamResponse: true,
517
- additionalContext: {
518
- custom: {
519
- message: "additional instructions",
520
- },
521
- },
522
- });
523
- });
524
- expect(advanceStream).toHaveBeenCalledWith(mockTamboAI, {
525
- messageToAppend: {
526
- content: [{ type: "text", text: "Hello streaming" }],
527
- role: "user",
528
- additionalContext: {
529
- custom: {
530
- message: "additional instructions",
531
- },
532
- },
533
- },
534
- availableComponents: serializeRegistry(mockRegistry),
535
- contextKey: undefined,
536
- clientTools: [],
537
- forceToolChoice: undefined,
538
- toolCallCounts: {},
539
- }, "test-thread-1");
540
- // Should not call advance or advanceById
541
- expect(mockThreadsApi.advance).not.toHaveBeenCalled();
542
- expect(mockThreadsApi.advanceByID).not.toHaveBeenCalled();
543
- });
544
- it("should throw error when streamResponse=false (non-streaming not supported)", async () => {
545
- const { result } = renderHook(() => useTamboThread(), {
546
- wrapper: createWrapper({ streaming: true }),
547
- });
548
- await act(async () => {
549
- await expect(result.current.sendThreadMessage("Hello non-streaming", {
550
- threadId: "test-thread-1",
551
- streamResponse: false,
552
- })).rejects.toThrow();
553
- });
554
- });
555
- it("should call advanceStream when streamResponse is undefined and provider streaming=true (default)", async () => {
556
- const mockStreamResponse = {
557
- responseMessageDto: {
558
- id: "stream-response",
559
- content: [{ type: "text", text: "Streaming response" }],
560
- role: "assistant",
561
- threadId: "test-thread-1",
562
- component: undefined,
563
- componentState: {},
564
- createdAt: new Date().toISOString(),
565
- },
566
- generationStage: GenerationStage.COMPLETE,
567
- mcpAccessToken: "test-mcp-access-token",
568
- };
569
- const mockAsyncIterator = {
570
- [Symbol.asyncIterator]: async function* () {
571
- yield mockStreamResponse;
572
- },
573
- };
574
- jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
575
- const { result } = renderHook(() => useTamboThread(), {
576
- wrapper: createWrapper({ streaming: true }),
577
- });
578
- await act(async () => {
579
- await result.current.sendThreadMessage("Hello default streaming", {
580
- threadId: "test-thread-1",
581
- // streamResponse is undefined, should use provider's streaming=true (default)
582
- additionalContext: {
583
- custom: {
584
- message: "additional instructions",
585
- },
586
- },
587
- });
588
- });
589
- expect(advanceStream).toHaveBeenCalledWith(mockTamboAI, {
590
- messageToAppend: {
591
- content: [{ type: "text", text: "Hello default streaming" }],
592
- role: "user",
593
- additionalContext: {
594
- custom: {
595
- message: "additional instructions",
596
- },
597
- },
598
- },
599
- availableComponents: serializeRegistry(mockRegistry),
600
- contextKey: undefined,
601
- clientTools: [],
602
- forceToolChoice: undefined,
603
- toolCallCounts: {},
604
- }, "test-thread-1");
605
- // Should not call advance or advanceById
606
- expect(mockThreadsApi.advance).not.toHaveBeenCalled();
607
- expect(mockThreadsApi.advanceByID).not.toHaveBeenCalled();
608
- });
609
- it("should call advanceStream when streamResponse=true for placeholder thread", async () => {
610
- const mockStreamResponse = {
611
- responseMessageDto: {
612
- id: "stream-response",
613
- content: [{ type: "text", text: "Streaming response" }],
614
- role: "assistant",
615
- threadId: "new-thread-1",
616
- component: undefined,
617
- componentState: {},
618
- createdAt: new Date().toISOString(),
619
- },
620
- generationStage: GenerationStage.COMPLETE,
621
- mcpAccessToken: "test-mcp-access-token",
622
- };
623
- const mockAsyncIterator = {
624
- [Symbol.asyncIterator]: async function* () {
625
- yield mockStreamResponse;
626
- },
627
- };
628
- jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
629
- const { result } = renderHook(() => useTamboThread(), {
630
- wrapper: createWrapper({ streaming: false }),
631
- });
632
- // Start with placeholder thread (which is the default state)
633
- expect(result.current.thread.id).toBe("placeholder");
634
- await act(async () => {
635
- await result.current.sendThreadMessage("Hello streaming new thread", {
636
- threadId: "placeholder",
637
- streamResponse: true,
638
- additionalContext: {
639
- custom: {
640
- message: "additional instructions",
641
- },
642
- },
643
- });
644
- });
645
- expect(advanceStream).toHaveBeenCalledWith(mockTamboAI, {
646
- messageToAppend: {
647
- content: [{ type: "text", text: "Hello streaming new thread" }],
648
- role: "user",
649
- additionalContext: {
650
- custom: {
651
- message: "additional instructions",
652
- },
653
- },
654
- },
655
- availableComponents: serializeRegistry(mockRegistry),
656
- contextKey: undefined,
657
- clientTools: [],
658
- forceToolChoice: undefined,
659
- toolCallCounts: {},
660
- }, undefined);
661
- // Should not call advance or advanceById
662
- expect(mockThreadsApi.advance).not.toHaveBeenCalled();
663
- expect(mockThreadsApi.advanceByID).not.toHaveBeenCalled();
664
- });
665
- it("should handle multiple sequential messages during streaming (server tool scenario)", async () => {
666
- // This test verifies the fix for the bug where the second message doesn't render
667
- // during server tool response streaming. The scenario:
668
- // 1. First message: "I will call the tool..." with statusMessage
669
- // 2. Second message: The tool result response streaming in
670
- // First message - tool announcement (server tools don't have componentName set during streaming)
671
- const mockFirstMessage = {
672
- responseMessageDto: {
673
- id: "msg-first",
674
- content: [{ type: "text", text: "I will search the docs..." }],
675
- role: "assistant",
676
- threadId: "test-thread-1",
677
- component: {
678
- componentName: "",
679
- componentState: {},
680
- message: "",
681
- props: {},
682
- statusMessage: "searching the Tambo docs...",
683
- },
684
- componentState: {},
685
- createdAt: new Date().toISOString(),
686
- },
687
- generationStage: GenerationStage.STREAMING_RESPONSE,
688
- mcpAccessToken: "test-mcp-access-token",
689
- };
690
- // Second message - tool result (different ID!)
691
- const mockSecondMessageChunk1 = {
692
- responseMessageDto: {
693
- id: "msg-second",
694
- content: [{ type: "text", text: "Here's what I found..." }],
695
- role: "assistant",
696
- threadId: "test-thread-1",
697
- componentState: {},
698
- createdAt: new Date().toISOString(),
699
- },
700
- generationStage: GenerationStage.STREAMING_RESPONSE,
701
- mcpAccessToken: "test-mcp-access-token",
702
- };
703
- const mockSecondMessageChunk2 = {
704
- responseMessageDto: {
705
- id: "msg-second",
706
- content: [
707
- {
708
- type: "text",
709
- text: "Here's what I found in the documentation about that topic.",
710
- },
711
- ],
712
- role: "assistant",
713
- threadId: "test-thread-1",
714
- componentState: {},
715
- createdAt: new Date().toISOString(),
716
- },
717
- generationStage: GenerationStage.COMPLETE,
718
- mcpAccessToken: "test-mcp-access-token",
719
- };
720
- const mockAsyncIterator = {
721
- [Symbol.asyncIterator]: async function* () {
722
- yield mockFirstMessage;
723
- yield mockSecondMessageChunk1;
724
- yield mockSecondMessageChunk2;
725
- },
726
- };
727
- jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
728
- const { result } = renderHook(() => useTamboThread(), {
729
- wrapper: createWrapper({ streaming: true }),
730
- });
731
- await act(async () => {
732
- await result.current.sendThreadMessage("Search the docs", {
733
- threadId: "test-thread-1",
734
- streamResponse: true,
735
- });
736
- });
737
- // Thread should have 3 messages: user message + 2 assistant messages
738
- expect(result.current.thread.messages).toHaveLength(3);
739
- // Filter to assistant messages only
740
- const assistantMessages = result.current.thread.messages.filter((m) => m.role === "assistant");
741
- expect(assistantMessages).toHaveLength(2);
742
- // First assistant message should have the tool status
743
- const firstMsg = result.current.thread.messages.find((m) => m.id === "msg-first");
744
- expect(firstMsg).toBeDefined();
745
- expect(firstMsg?.content[0]?.text).toContain("search the docs");
746
- // Second assistant message should have the final content
747
- const secondMsg = result.current.thread.messages.find((m) => m.id === "msg-second");
748
- expect(secondMsg).toBeDefined();
749
- expect(secondMsg?.content[0]?.text).toContain("what I found in the documentation");
750
- // Generation should be complete
751
- expect(result.current.generationStage).toBe(GenerationStage.COMPLETE);
752
- });
753
- });
754
- describe("error handling", () => {
755
- it("should set generation stage to ERROR when streaming sendThreadMessage fails", async () => {
756
- const testError = new Error("Streaming API call failed");
757
- // Mock advanceStream to throw an error
758
- jest.mocked(advanceStream).mockRejectedValue(testError);
759
- const { result } = renderHook(() => useTamboThread(), {
760
- wrapper: Wrapper,
761
- });
762
- // Expect the error to be thrown
763
- await act(async () => {
764
- await result.current.switchCurrentThread("test-thread-1");
765
- await expect(result.current.sendThreadMessage("Hello", {
766
- threadId: "test-thread-1",
767
- streamResponse: true,
768
- })).rejects.toThrow("Streaming API call failed");
769
- });
770
- // Verify generation stage is set to ERROR
771
- expect(result.current.generationStage).toBe(GenerationStage.ERROR);
772
- });
773
- it("should rollback optimistic user message when sendThreadMessage fails", async () => {
774
- const testError = new Error("API call failed");
775
- jest.mocked(advanceStream).mockRejectedValue(testError);
776
- const { result } = renderHook(() => useTamboThread(), {
777
- wrapper: Wrapper,
778
- });
779
- await act(async () => {
780
- await result.current.switchCurrentThread("test-thread-1");
781
- });
782
- const initialMessageCount = result.current.thread.messages.length;
783
- await act(async () => {
784
- await expect(result.current.sendThreadMessage("Hello", {
785
- threadId: "test-thread-1",
786
- streamResponse: true,
787
- })).rejects.toThrow("API call failed");
788
- });
789
- // Verify user message was rolled back
790
- expect(result.current.thread.messages.length).toBe(initialMessageCount);
791
- });
792
- it("should rollback optimistic message when addThreadMessage fails", async () => {
793
- const testError = new Error("Create message failed");
794
- jest.mocked(mockThreadsApi.messages.create).mockRejectedValue(testError);
795
- const { result } = renderHook(() => useTamboThread(), {
796
- wrapper: Wrapper,
797
- });
798
- await act(async () => {
799
- await result.current.switchCurrentThread("test-thread-1");
800
- });
801
- const initialMessageCount = result.current.thread.messages.length;
802
- const newMessage = createMockMessage({ threadId: "test-thread-1" });
803
- await act(async () => {
804
- await expect(result.current.addThreadMessage(newMessage, true)).rejects.toThrow("Create message failed");
805
- });
806
- // Verify message was rolled back
807
- expect(result.current.thread.messages.length).toBe(initialMessageCount);
808
- });
809
- it("should rollback optimistic update when updateThreadMessage fails", async () => {
810
- const testError = new Error("Update message failed");
811
- jest.mocked(mockThreadsApi.messages.create).mockRejectedValue(testError);
812
- const { result } = renderHook(() => useTamboThread(), {
813
- wrapper: Wrapper,
814
- });
815
- await act(async () => {
816
- await result.current.switchCurrentThread("test-thread-1");
817
- });
818
- const existingMessage = createMockMessage({
819
- id: "existing-msg",
820
- threadId: "test-thread-1",
821
- content: [{ type: "text", text: "Old content" }],
822
- });
823
- await act(async () => {
824
- await result.current.addThreadMessage(existingMessage, false);
825
- });
826
- const initialMessageCount = result.current.thread.messages.length;
827
- await act(async () => {
828
- await expect(result.current.updateThreadMessage("existing-msg", {
829
- threadId: "test-thread-1",
830
- content: [{ type: "text", text: "New content" }],
831
- role: "assistant",
832
- }, true)).rejects.toThrow("Update message failed");
833
- });
834
- // Verify message was rolled back
835
- expect(result.current.thread.messages.length).toBe(initialMessageCount - 1);
836
- });
837
- it("should rollback optimistic name update when updateThreadName fails", async () => {
838
- const testError = new Error("Update name failed");
839
- jest.mocked(mockThreadsApi.update).mockRejectedValue(testError);
840
- const { result } = renderHook(() => useTamboThread(), {
841
- wrapper: Wrapper,
842
- });
843
- await act(async () => {
844
- await result.current.switchCurrentThread("test-thread-1");
845
- });
846
- const initialName = result.current.thread.name;
847
- await act(async () => {
848
- await expect(result.current.updateThreadName("New Name", "test-thread-1")).rejects.toThrow("Update name failed");
849
- });
850
- // Verify name was rolled back
851
- expect(result.current.thread.name).toBe(initialName);
852
- });
853
- });
854
- describe("refetch threads list behavior", () => {
855
- it("should refetch threads list when creating a new thread via sendThreadMessage", async () => {
856
- const { result } = renderHook(() => useTamboThread(), {
857
- wrapper: Wrapper,
858
- });
859
- // Mock the stream response to return a new thread ID
860
- const mockStreamResponse = {
861
- responseMessageDto: {
862
- id: "response-1",
863
- content: [{ type: "text", text: "Response" }],
864
- role: "assistant",
865
- threadId: "new-thread-123",
866
- component: undefined,
867
- componentState: {},
868
- createdAt: new Date().toISOString(),
869
- },
870
- generationStage: GenerationStage.COMPLETE,
871
- mcpAccessToken: "test-mcp-access-token",
872
- };
873
- const mockAsyncIterator = {
874
- [Symbol.asyncIterator]: async function* () {
875
- yield mockStreamResponse;
876
- },
877
- };
878
- jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
879
- // Start with placeholder thread
880
- expect(result.current.thread.id).toBe("placeholder");
881
- // Send a message which will create a new thread with contextKey
882
- await act(async () => {
883
- await result.current.sendThreadMessage("Hello", {
884
- threadId: "placeholder",
885
- streamResponse: true,
886
- contextKey: "test-context-key",
887
- });
888
- });
889
- // Verify that setQueryData was called first (optimistic update)
890
- expect(mockQueryClient.setQueryData).toHaveBeenCalledWith(["threads", "test-project-id", "test-context-key"], expect.any(Function));
891
- // Verify that refetchQueries was called when the new thread was created
892
- expect(mockQueryClient.invalidateQueries).toHaveBeenCalledWith({
893
- queryKey: ["threads"],
894
- });
895
- });
896
- it("should not refetch threads list when switching between existing threads", async () => {
897
- const { result } = renderHook(() => useTamboThread(), {
898
- wrapper: Wrapper,
899
- });
900
- // Start with placeholder thread
901
- expect(result.current.thread.id).toBe("placeholder");
902
- // Clear any previous mock calls
903
- jest.clearAllMocks();
904
- // Mock the retrieve call to return the expected thread
905
- const existingThread = createMockThread({ id: "existing-thread-123" });
906
- jest
907
- .mocked(mockThreadsApi.retrieve)
908
- .mockResolvedValueOnce(existingThread);
909
- // Switch to an existing thread (this should not trigger refetch)
910
- await act(async () => {
911
- await result.current.switchCurrentThread("existing-thread-123");
912
- });
913
- // Verify that the thread retrieval was called
914
- expect(mockThreadsApi.retrieve).toHaveBeenCalledWith("existing-thread-123");
915
- // Verify that neither setQueryData nor refetchQueries were called
916
- expect(mockQueryClient.setQueryData).not.toHaveBeenCalled();
917
- expect(mockQueryClient.invalidateQueries).not.toHaveBeenCalled();
918
- // Verify the thread was switched correctly
919
- expect(result.current.thread.id).toBe("existing-thread-123");
920
- });
921
- });
922
- describe("transformToContent", () => {
923
- it("should use custom async transformToContent when provided (streaming)", async () => {
924
- const mockTransformToContent = jest
925
- .fn()
926
- .mockResolvedValue([
927
- { type: "text", text: "Async transformed content" },
928
- ]);
929
- const customToolRegistry = [
930
- {
931
- name: "TestComponent",
932
- component: () => React.createElement("div", null, "Test"),
933
- description: "Test",
934
- propsSchema: z.object({ test: z.string() }),
935
- associatedTools: [
936
- {
937
- name: "async-tool",
938
- tool: jest.fn().mockResolvedValue({ data: "async tool result" }),
939
- description: "Tool with async transform",
940
- inputSchema: z.object({ input: z.string() }),
941
- outputSchema: z.object({ data: z.string() }),
942
- transformToContent: mockTransformToContent,
943
- },
944
- ],
945
- },
946
- ];
947
- const mockToolCallChunk = {
948
- responseMessageDto: {
949
- id: "tool-call-chunk",
950
- content: [{ type: "text", text: "Tool call" }],
951
- role: "tool",
952
- threadId: "test-thread-1",
953
- toolCallRequest: {
954
- toolName: "async-tool",
955
- parameters: [
956
- { parameterName: "input", parameterValue: "async-test" },
957
- ],
958
- },
959
- componentState: {},
960
- createdAt: new Date().toISOString(),
961
- },
962
- generationStage: GenerationStage.COMPLETE,
963
- mcpAccessToken: "test-mcp-access-token",
964
- };
965
- const mockFinalChunk = {
966
- responseMessageDto: {
967
- id: "final-chunk",
968
- content: [{ type: "text", text: "Final streaming response" }],
969
- role: "assistant",
970
- threadId: "test-thread-1",
971
- componentState: {},
972
- createdAt: new Date().toISOString(),
973
- },
974
- generationStage: GenerationStage.COMPLETE,
975
- mcpAccessToken: "test-mcp-access-token",
976
- };
977
- const mockAsyncIterator = {
978
- [Symbol.asyncIterator]: async function* () {
979
- yield mockToolCallChunk;
980
- yield mockFinalChunk;
981
- },
982
- };
983
- jest
984
- .mocked(advanceStream)
985
- .mockResolvedValueOnce(mockAsyncIterator)
986
- .mockResolvedValueOnce({
987
- [Symbol.asyncIterator]: async function* () {
988
- yield mockFinalChunk;
989
- },
990
- });
991
- const { result } = renderHook(() => useTamboThread(), {
992
- wrapper: createWrapper({
993
- components: customToolRegistry,
994
- streaming: true,
995
- }),
996
- });
997
- await act(async () => {
998
- await result.current.sendThreadMessage("Use async tool", {
999
- threadId: "test-thread-1",
1000
- streamResponse: true,
1001
- });
1002
- });
1003
- // Verify the tool was called with single object arg (new inputSchema interface)
1004
- expect(customToolRegistry[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith({ input: "async-test" });
1005
- // Verify transformToContent was called
1006
- expect(mockTransformToContent).toHaveBeenCalledWith({
1007
- data: "async tool result",
1008
- });
1009
- // Verify advanceStream was called twice (initial request and tool response)
1010
- expect(advanceStream).toHaveBeenCalledTimes(2);
1011
- // Verify the second advanceStream call included the transformed content
1012
- expect(advanceStream).toHaveBeenLastCalledWith(mockTamboAI, expect.objectContaining({
1013
- messageToAppend: expect.objectContaining({
1014
- content: [{ type: "text", text: "Async transformed content" }],
1015
- role: "tool",
1016
- }),
1017
- }), "test-thread-1");
1018
- });
1019
- });
1020
- describe("tamboStreamableHint streaming behavior", () => {
1021
- it("should call streamable tool during streaming when tamboStreamableHint is true", async () => {
1022
- const streamableToolFn = jest
1023
- .fn()
1024
- .mockResolvedValue({ data: "streamed" });
1025
- const customToolRegistry = [
1026
- {
1027
- name: "TestComponent",
1028
- component: () => React.createElement("div", null, "Test"),
1029
- description: "Test",
1030
- propsSchema: z.object({ test: z.string() }),
1031
- associatedTools: [
1032
- {
1033
- name: "streamable-tool",
1034
- tool: streamableToolFn,
1035
- description: "Tool safe for streaming",
1036
- inputSchema: z.object({ input: z.string() }),
1037
- outputSchema: z.object({ data: z.string() }),
1038
- annotations: { tamboStreamableHint: true },
1039
- },
1040
- ],
1041
- },
1042
- ];
1043
- // First chunk initializes finalMessage
1044
- const mockInitialChunk = {
1045
- responseMessageDto: {
1046
- id: "initial-chunk",
1047
- content: [{ type: "text", text: "Starting..." }],
1048
- role: "assistant",
1049
- threadId: "test-thread-1",
1050
- componentState: {},
1051
- createdAt: new Date().toISOString(),
1052
- },
1053
- generationStage: GenerationStage.STREAMING_RESPONSE,
1054
- mcpAccessToken: "test-mcp-access-token",
1055
- };
1056
- // Second chunk has the tool call - this triggers streaming tool handling
1057
- const mockToolCallChunk = {
1058
- responseMessageDto: {
1059
- id: "initial-chunk", // Same ID as initial - it's an update
1060
- content: [{ type: "text", text: "Streaming..." }],
1061
- role: "assistant",
1062
- threadId: "test-thread-1",
1063
- component: {
1064
- componentName: "",
1065
- componentState: {},
1066
- message: "",
1067
- props: {},
1068
- toolCallRequest: {
1069
- toolName: "streamable-tool",
1070
- parameters: [
1071
- { parameterName: "input", parameterValue: "stream-test" },
1072
- ],
1073
- },
1074
- },
1075
- componentState: {},
1076
- createdAt: new Date().toISOString(),
1077
- },
1078
- generationStage: GenerationStage.STREAMING_RESPONSE,
1079
- mcpAccessToken: "test-mcp-access-token",
1080
- };
1081
- const mockFinalChunk = {
1082
- responseMessageDto: {
1083
- id: "initial-chunk",
1084
- content: [{ type: "text", text: "Complete" }],
1085
- role: "assistant",
1086
- threadId: "test-thread-1",
1087
- componentState: {},
1088
- createdAt: new Date().toISOString(),
1089
- },
1090
- generationStage: GenerationStage.COMPLETE,
1091
- mcpAccessToken: "test-mcp-access-token",
1092
- };
1093
- const mockAsyncIterator = {
1094
- [Symbol.asyncIterator]: async function* () {
1095
- yield mockInitialChunk;
1096
- yield mockToolCallChunk;
1097
- yield mockFinalChunk;
1098
- },
1099
- };
1100
- jest.mocked(advanceStream).mockResolvedValueOnce(mockAsyncIterator);
1101
- const { result } = renderHook(() => useTamboThread(), {
1102
- wrapper: createWrapper({
1103
- components: customToolRegistry,
1104
- streaming: true,
1105
- }),
1106
- });
1107
- await act(async () => {
1108
- await result.current.sendThreadMessage("Test streamable tool", {
1109
- threadId: "test-thread-1",
1110
- streamResponse: true,
1111
- });
1112
- });
1113
- // Streamable tool should be called during streaming
1114
- expect(streamableToolFn).toHaveBeenCalledWith({ input: "stream-test" });
1115
- });
1116
- it("should NOT call non-streamable tool during streaming", async () => {
1117
- const nonStreamableToolFn = jest
1118
- .fn()
1119
- .mockResolvedValue({ data: "result" });
1120
- const customToolRegistry = [
1121
- {
1122
- name: "TestComponent",
1123
- component: () => React.createElement("div", null, "Test"),
1124
- description: "Test",
1125
- propsSchema: z.object({ test: z.string() }),
1126
- associatedTools: [
1127
- {
1128
- name: "non-streamable-tool",
1129
- tool: nonStreamableToolFn,
1130
- description: "Tool not safe for streaming",
1131
- inputSchema: z.object({ input: z.string() }),
1132
- outputSchema: z.object({ data: z.string() }),
1133
- // No tamboStreamableHint - defaults to false
1134
- },
1135
- ],
1136
- },
1137
- ];
1138
- // First chunk initializes finalMessage
1139
- const mockInitialChunk = {
1140
- responseMessageDto: {
1141
- id: "streaming-chunk",
1142
- content: [{ type: "text", text: "Starting..." }],
1143
- role: "assistant",
1144
- threadId: "test-thread-1",
1145
- componentState: {},
1146
- createdAt: new Date().toISOString(),
1147
- },
1148
- generationStage: GenerationStage.STREAMING_RESPONSE,
1149
- mcpAccessToken: "test-mcp-access-token",
1150
- };
1151
- // Second chunk has the tool call - but tool is NOT streamable
1152
- const mockToolCallChunk = {
1153
- responseMessageDto: {
1154
- id: "streaming-chunk",
1155
- content: [{ type: "text", text: "Streaming..." }],
1156
- role: "assistant",
1157
- threadId: "test-thread-1",
1158
- component: {
1159
- componentName: "",
1160
- componentState: {},
1161
- message: "",
1162
- props: {},
1163
- toolCallRequest: {
1164
- toolName: "non-streamable-tool",
1165
- parameters: [{ parameterName: "input", parameterValue: "test" }],
1166
- },
1167
- },
1168
- componentState: {},
1169
- createdAt: new Date().toISOString(),
1170
- },
1171
- generationStage: GenerationStage.STREAMING_RESPONSE,
1172
- mcpAccessToken: "test-mcp-access-token",
1173
- };
1174
- const mockFinalChunk = {
1175
- responseMessageDto: {
1176
- id: "streaming-chunk",
1177
- content: [{ type: "text", text: "Complete" }],
1178
- role: "assistant",
1179
- threadId: "test-thread-1",
1180
- componentState: {},
1181
- createdAt: new Date().toISOString(),
1182
- },
1183
- generationStage: GenerationStage.COMPLETE,
1184
- mcpAccessToken: "test-mcp-access-token",
1185
- };
1186
- const mockAsyncIterator = {
1187
- [Symbol.asyncIterator]: async function* () {
1188
- yield mockInitialChunk;
1189
- yield mockToolCallChunk;
1190
- yield mockFinalChunk;
1191
- },
1192
- };
1193
- jest.mocked(advanceStream).mockResolvedValueOnce(mockAsyncIterator);
1194
- const { result } = renderHook(() => useTamboThread(), {
1195
- wrapper: createWrapper({
1196
- components: customToolRegistry,
1197
- streaming: true,
1198
- }),
1199
- });
1200
- await act(async () => {
1201
- await result.current.sendThreadMessage("Test non-streamable tool", {
1202
- threadId: "test-thread-1",
1203
- streamResponse: true,
1204
- });
1205
- });
1206
- // Non-streamable tool should NOT be called during the streaming chunk phase
1207
- // (it would only be called when generationStage is COMPLETE with a toolCallRequest)
1208
- expect(nonStreamableToolFn).not.toHaveBeenCalled();
1209
- });
1210
- it("should only call streamable tools during streaming when mixed", async () => {
1211
- const streamableToolFn = jest
1212
- .fn()
1213
- .mockResolvedValue({ data: "streamed" });
1214
- const nonStreamableToolFn = jest
1215
- .fn()
1216
- .mockResolvedValue({ data: "not-streamed" });
1217
- const customToolRegistry = [
1218
- {
1219
- name: "TestComponent",
1220
- component: () => React.createElement("div", null, "Test"),
1221
- description: "Test",
1222
- propsSchema: z.object({ test: z.string() }),
1223
- associatedTools: [
1224
- {
1225
- name: "streamable-tool",
1226
- tool: streamableToolFn,
1227
- description: "Tool safe for streaming",
1228
- inputSchema: z.object({ input: z.string() }),
1229
- outputSchema: z.object({ data: z.string() }),
1230
- annotations: { tamboStreamableHint: true },
1231
- },
1232
- {
1233
- name: "non-streamable-tool",
1234
- tool: nonStreamableToolFn,
1235
- description: "Tool not safe for streaming",
1236
- inputSchema: z.object({ input: z.string() }),
1237
- outputSchema: z.object({ data: z.string() }),
1238
- annotations: { tamboStreamableHint: false },
1239
- },
1240
- ],
1241
- },
1242
- ];
1243
- // First chunk initializes finalMessage
1244
- const mockInitialChunk = {
1245
- responseMessageDto: {
1246
- id: "streaming-chunk",
1247
- content: [{ type: "text", text: "Starting..." }],
1248
- role: "assistant",
1249
- threadId: "test-thread-1",
1250
- componentState: {},
1251
- createdAt: new Date().toISOString(),
1252
- },
1253
- generationStage: GenerationStage.STREAMING_RESPONSE,
1254
- mcpAccessToken: "test-mcp-access-token",
1255
- };
1256
- // Second chunk calls the streamable tool
1257
- const mockStreamableToolChunk = {
1258
- responseMessageDto: {
1259
- id: "streaming-chunk",
1260
- content: [{ type: "text", text: "Calling streamable..." }],
1261
- role: "assistant",
1262
- threadId: "test-thread-1",
1263
- component: {
1264
- componentName: "",
1265
- componentState: {},
1266
- message: "",
1267
- props: {},
1268
- toolCallRequest: {
1269
- toolName: "streamable-tool",
1270
- parameters: [
1271
- { parameterName: "input", parameterValue: "streamed-input" },
1272
- ],
1273
- },
1274
- },
1275
- componentState: {},
1276
- createdAt: new Date().toISOString(),
1277
- },
1278
- generationStage: GenerationStage.STREAMING_RESPONSE,
1279
- mcpAccessToken: "test-mcp-access-token",
1280
- };
1281
- // Third chunk calls the non-streamable tool
1282
- const mockNonStreamableToolChunk = {
1283
- responseMessageDto: {
1284
- id: "streaming-chunk",
1285
- content: [{ type: "text", text: "Calling non-streamable..." }],
1286
- role: "assistant",
1287
- threadId: "test-thread-1",
1288
- component: {
1289
- componentName: "",
1290
- componentState: {},
1291
- message: "",
1292
- props: {},
1293
- toolCallRequest: {
1294
- toolName: "non-streamable-tool",
1295
- parameters: [
1296
- {
1297
- parameterName: "input",
1298
- parameterValue: "non-streamed-input",
1299
- },
1300
- ],
1301
- },
1302
- },
1303
- componentState: {},
1304
- createdAt: new Date().toISOString(),
1305
- },
1306
- generationStage: GenerationStage.STREAMING_RESPONSE,
1307
- mcpAccessToken: "test-mcp-access-token",
1308
- };
1309
- const mockFinalChunk = {
1310
- responseMessageDto: {
1311
- id: "streaming-chunk",
1312
- content: [{ type: "text", text: "Complete" }],
1313
- role: "assistant",
1314
- threadId: "test-thread-1",
1315
- componentState: {},
1316
- createdAt: new Date().toISOString(),
1317
- },
1318
- generationStage: GenerationStage.COMPLETE,
1319
- mcpAccessToken: "test-mcp-access-token",
1320
- };
1321
- const mockAsyncIterator = {
1322
- [Symbol.asyncIterator]: async function* () {
1323
- yield mockInitialChunk;
1324
- yield mockStreamableToolChunk;
1325
- yield mockNonStreamableToolChunk;
1326
- yield mockFinalChunk;
1327
- },
1328
- };
1329
- jest.mocked(advanceStream).mockResolvedValueOnce(mockAsyncIterator);
1330
- const { result } = renderHook(() => useTamboThread(), {
1331
- wrapper: createWrapper({
1332
- components: customToolRegistry,
1333
- streaming: true,
1334
- }),
1335
- });
1336
- await act(async () => {
1337
- await result.current.sendThreadMessage("Test mixed tools", {
1338
- threadId: "test-thread-1",
1339
- streamResponse: true,
1340
- });
1341
- });
1342
- // Only the streamable tool should be called during streaming
1343
- expect(streamableToolFn).toHaveBeenCalledWith({
1344
- input: "streamed-input",
1345
- });
1346
- expect(nonStreamableToolFn).not.toHaveBeenCalled();
1347
- });
1348
- });
1349
- describe("auto-generate thread name", () => {
1350
- it("should auto-generate thread name after reaching threshold", async () => {
1351
- const mockStreamResponse = {
1352
- responseMessageDto: {
1353
- id: "response-1",
1354
- content: [{ type: "text", text: "Response" }],
1355
- role: "assistant",
1356
- threadId: "test-thread-1",
1357
- component: undefined,
1358
- componentState: {},
1359
- createdAt: new Date().toISOString(),
1360
- },
1361
- generationStage: GenerationStage.COMPLETE,
1362
- mcpAccessToken: "test-mcp-access-token",
1363
- };
1364
- const mockAsyncIterator = {
1365
- [Symbol.asyncIterator]: async function* () {
1366
- yield mockStreamResponse;
1367
- },
1368
- };
1369
- jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
1370
- const { result } = renderHook(() => useTamboThread(), {
1371
- wrapper: createWrapper({ autoGenerateNameThreshold: 2 }),
1372
- });
1373
- const existingThread = createMockThread({
1374
- id: "test-thread-1",
1375
- name: undefined,
1376
- });
1377
- jest
1378
- .mocked(mockThreadsApi.retrieve)
1379
- .mockResolvedValueOnce(existingThread);
1380
- await act(async () => {
1381
- await result.current.switchCurrentThread("test-thread-1");
1382
- });
1383
- // Add first message
1384
- await act(async () => {
1385
- await result.current.addThreadMessage(createMockMessage({
1386
- id: "msg-1",
1387
- role: "user",
1388
- threadId: "test-thread-1",
1389
- }), false);
1390
- });
1391
- expect(mockThreadsApi.generateName).not.toHaveBeenCalled();
1392
- // Add second message and send to reach threshold
1393
- await act(async () => {
1394
- await result.current.addThreadMessage(createMockMessage({
1395
- id: "msg-2",
1396
- role: "assistant",
1397
- threadId: "test-thread-1",
1398
- }), false);
1399
- });
1400
- await act(async () => {
1401
- await result.current.sendThreadMessage("Test message", {
1402
- streamResponse: true,
1403
- });
1404
- });
1405
- expect(mockThreadsApi.generateName).toHaveBeenCalledWith("test-thread-1");
1406
- expect(result.current.thread.name).toBe("Generated Thread Name");
1407
- expect(mockQueryClient.setQueryData).toHaveBeenCalledWith(["threads", "test-project-id", undefined], expect.any(Function));
1408
- });
1409
- it("should NOT auto-generate when autoGenerateThreadName is false", async () => {
1410
- const mockStreamResponse = {
1411
- responseMessageDto: {
1412
- id: "response-1",
1413
- content: [{ type: "text", text: "Response" }],
1414
- role: "assistant",
1415
- threadId: "test-thread-1",
1416
- component: undefined,
1417
- componentState: {},
1418
- createdAt: new Date().toISOString(),
1419
- },
1420
- generationStage: GenerationStage.COMPLETE,
1421
- mcpAccessToken: "test-mcp-access-token",
1422
- };
1423
- const mockAsyncIterator = {
1424
- [Symbol.asyncIterator]: async function* () {
1425
- yield mockStreamResponse;
1426
- },
1427
- };
1428
- jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
1429
- const { result } = renderHook(() => useTamboThread(), {
1430
- wrapper: createWrapper({
1431
- autoGenerateThreadName: false,
1432
- autoGenerateNameThreshold: 2,
1433
- }),
1434
- });
1435
- const existingThread = createMockThread({
1436
- id: "test-thread-1",
1437
- name: undefined,
1438
- });
1439
- jest
1440
- .mocked(mockThreadsApi.retrieve)
1441
- .mockResolvedValueOnce(existingThread);
1442
- await act(async () => {
1443
- await result.current.switchCurrentThread("test-thread-1");
1444
- });
1445
- await act(async () => {
1446
- await result.current.addThreadMessage(createMockMessage({
1447
- id: "msg-1",
1448
- role: "user",
1449
- threadId: "test-thread-1",
1450
- }), false);
1451
- });
1452
- await act(async () => {
1453
- await result.current.addThreadMessage(createMockMessage({
1454
- id: "msg-2",
1455
- role: "assistant",
1456
- threadId: "test-thread-1",
1457
- }), false);
1458
- });
1459
- await act(async () => {
1460
- await result.current.sendThreadMessage("Test message", {
1461
- streamResponse: true,
1462
- });
1463
- });
1464
- // Should NOT generate name because feature is disabled
1465
- expect(mockThreadsApi.generateName).not.toHaveBeenCalled();
1466
- });
1467
- it("should NOT auto-generate when thread already has a name", async () => {
1468
- const mockStreamResponse = {
1469
- responseMessageDto: {
1470
- id: "response-1",
1471
- content: [{ type: "text", text: "Response" }],
1472
- role: "assistant",
1473
- threadId: "test-thread-1",
1474
- component: undefined,
1475
- componentState: {},
1476
- createdAt: new Date().toISOString(),
1477
- },
1478
- generationStage: GenerationStage.COMPLETE,
1479
- mcpAccessToken: "test-mcp-access-token",
1480
- };
1481
- const mockAsyncIterator = {
1482
- [Symbol.asyncIterator]: async function* () {
1483
- yield mockStreamResponse;
1484
- },
1485
- };
1486
- jest.mocked(advanceStream).mockResolvedValue(mockAsyncIterator);
1487
- const { result } = renderHook(() => useTamboThread(), {
1488
- wrapper: createWrapper({ autoGenerateNameThreshold: 2 }),
1489
- });
1490
- const threadWithName = createMockThread({
1491
- id: "test-thread-1",
1492
- name: "Existing Thread Name",
1493
- });
1494
- jest
1495
- .mocked(mockThreadsApi.retrieve)
1496
- .mockResolvedValueOnce(threadWithName);
1497
- await act(async () => {
1498
- await result.current.switchCurrentThread("test-thread-1");
1499
- });
1500
- // Verify thread has existing name
1501
- expect(result.current.thread.name).toBe("Existing Thread Name");
1502
- // Add messages to build up state
1503
- await act(async () => {
1504
- await result.current.addThreadMessage(createMockMessage({
1505
- id: "msg-1",
1506
- role: "user",
1507
- threadId: "test-thread-1",
1508
- }), false);
1509
- });
1510
- await act(async () => {
1511
- await result.current.addThreadMessage(createMockMessage({
1512
- id: "msg-2",
1513
- role: "assistant",
1514
- threadId: "test-thread-1",
1515
- }), false);
1516
- });
1517
- expect(result.current.thread.messages).toHaveLength(2);
1518
- // Send another message to reach threshold (3 messages total)
1519
- await act(async () => {
1520
- await result.current.sendThreadMessage("Test message", {
1521
- streamResponse: true,
1522
- });
1523
- });
1524
- // Should NOT generate name because thread already has one
1525
- expect(mockThreadsApi.generateName).not.toHaveBeenCalled();
1526
- });
1527
- it("should NOT auto-generate for placeholder thread", async () => {
1528
- const { result } = renderHook(() => useTamboThread(), {
1529
- wrapper: createWrapper({ autoGenerateNameThreshold: 2 }),
1530
- });
1531
- // Stay on placeholder thread
1532
- expect(result.current.thread.id).toBe("placeholder");
1533
- // Add messages to placeholder thread
1534
- await act(async () => {
1535
- await result.current.addThreadMessage(createMockMessage({
1536
- id: "msg-1",
1537
- role: "user",
1538
- threadId: "placeholder",
1539
- }), false);
1540
- });
1541
- await act(async () => {
1542
- await result.current.addThreadMessage(createMockMessage({
1543
- id: "msg-2",
1544
- role: "assistant",
1545
- threadId: "placeholder",
1546
- }), false);
1547
- });
1548
- // Should NOT generate name for placeholder thread
1549
- expect(mockThreadsApi.generateName).not.toHaveBeenCalled();
1550
- });
1551
- });
1552
- });
1553
- //# sourceMappingURL=tambo-thread-provider.test.js.map