@tambo-ai/react 0.67.1 → 0.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (424) hide show
  1. package/README.md +3 -5
  2. package/dist/context-helpers/context-helpers.test.js +16 -4
  3. package/dist/context-helpers/context-helpers.test.js.map +1 -1
  4. package/dist/context-helpers/current-interactables-context-helper.d.ts +2 -2
  5. package/dist/context-helpers/current-interactables-context-helper.d.ts.map +1 -1
  6. package/dist/context-helpers/current-interactables-context-helper.js +31 -12
  7. package/dist/context-helpers/current-interactables-context-helper.js.map +1 -1
  8. package/dist/context-helpers/registry.d.ts +2 -2
  9. package/dist/context-helpers/registry.d.ts.map +1 -1
  10. package/dist/context-helpers/registry.js.map +1 -1
  11. package/dist/context-helpers/types.d.ts +2 -2
  12. package/dist/context-helpers/types.d.ts.map +1 -1
  13. package/dist/context-helpers/types.js.map +1 -1
  14. package/dist/hoc/with-tambo-interactable.d.ts +50 -4
  15. package/dist/hoc/with-tambo-interactable.d.ts.map +1 -1
  16. package/dist/hoc/with-tambo-interactable.js +20 -5
  17. package/dist/hoc/with-tambo-interactable.js.map +1 -1
  18. package/dist/hooks/use-component-state.d.ts +3 -8
  19. package/dist/hooks/use-component-state.d.ts.map +1 -1
  20. package/dist/hooks/use-component-state.js +8 -0
  21. package/dist/hooks/use-component-state.js.map +1 -1
  22. package/dist/hooks/use-component-state.test.js +37 -0
  23. package/dist/hooks/use-component-state.test.js.map +1 -1
  24. package/dist/hooks/use-message-images.test.js +174 -37
  25. package/dist/hooks/use-message-images.test.js.map +1 -1
  26. package/dist/hooks/use-tambo-threads.js +1 -1
  27. package/dist/hooks/use-tambo-threads.js.map +1 -1
  28. package/dist/hooks/use-tambo-voice.d.ts +1 -1
  29. package/dist/hooks/use-tambo-voice.js +1 -1
  30. package/dist/hooks/use-tambo-voice.js.map +1 -1
  31. package/dist/hooks/use-tambo-voice.test.d.ts +2 -0
  32. package/dist/hooks/use-tambo-voice.test.d.ts.map +1 -0
  33. package/dist/hooks/use-tambo-voice.test.js +239 -0
  34. package/dist/hooks/use-tambo-voice.test.js.map +1 -0
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/mcp/elicitation.d.ts.map +1 -1
  39. package/dist/mcp/elicitation.js +12 -0
  40. package/dist/mcp/elicitation.js.map +1 -1
  41. package/dist/mcp/elicitation.test.js +8 -1
  42. package/dist/mcp/elicitation.test.js.map +1 -1
  43. package/dist/mcp/mcp-client.d.ts +6 -10
  44. package/dist/mcp/mcp-client.d.ts.map +1 -1
  45. package/dist/mcp/mcp-client.js.map +1 -1
  46. package/dist/mcp/mcp-constants.d.ts +19 -0
  47. package/dist/mcp/mcp-constants.d.ts.map +1 -0
  48. package/dist/mcp/mcp-constants.js +21 -0
  49. package/dist/mcp/mcp-constants.js.map +1 -0
  50. package/dist/mcp/mcp-hooks.d.ts +21 -40
  51. package/dist/mcp/mcp-hooks.d.ts.map +1 -1
  52. package/dist/mcp/mcp-hooks.js +130 -39
  53. package/dist/mcp/mcp-hooks.js.map +1 -1
  54. package/dist/mcp/mcp-hooks.test.js +431 -5
  55. package/dist/mcp/mcp-hooks.test.js.map +1 -1
  56. package/dist/mcp/tambo-mcp-provider.d.ts +7 -0
  57. package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
  58. package/dist/mcp/tambo-mcp-provider.js +205 -155
  59. package/dist/mcp/tambo-mcp-provider.js.map +1 -1
  60. package/dist/mcp/tambo-mcp-provider.test.js +37 -0
  61. package/dist/mcp/tambo-mcp-provider.test.js.map +1 -1
  62. package/dist/model/component-metadata.d.ts +54 -21
  63. package/dist/model/component-metadata.d.ts.map +1 -1
  64. package/dist/model/component-metadata.js.map +1 -1
  65. package/dist/model/tambo-interactable.d.ts +13 -5
  66. package/dist/model/tambo-interactable.d.ts.map +1 -1
  67. package/dist/model/tambo-interactable.js.map +1 -1
  68. package/dist/providers/__tests__/thread-input-resource-resolution.test.d.ts +2 -0
  69. package/dist/providers/__tests__/thread-input-resource-resolution.test.d.ts.map +1 -0
  70. package/dist/providers/__tests__/thread-input-resource-resolution.test.js +592 -0
  71. package/dist/providers/__tests__/thread-input-resource-resolution.test.js.map +1 -0
  72. package/dist/providers/index.d.ts +1 -1
  73. package/dist/providers/index.d.ts.map +1 -1
  74. package/dist/providers/index.js.map +1 -1
  75. package/dist/providers/tambo-client-provider.d.ts +8 -0
  76. package/dist/providers/tambo-client-provider.d.ts.map +1 -1
  77. package/dist/providers/tambo-client-provider.js +10 -11
  78. package/dist/providers/tambo-client-provider.js.map +1 -1
  79. package/dist/providers/tambo-client-provider.test.d.ts +2 -0
  80. package/dist/providers/tambo-client-provider.test.d.ts.map +1 -0
  81. package/dist/providers/tambo-client-provider.test.js +208 -0
  82. package/dist/providers/tambo-client-provider.test.js.map +1 -0
  83. package/dist/providers/tambo-context-attachment-provider.d.ts +34 -92
  84. package/dist/providers/tambo-context-attachment-provider.d.ts.map +1 -1
  85. package/dist/providers/tambo-context-attachment-provider.js +62 -105
  86. package/dist/providers/tambo-context-attachment-provider.js.map +1 -1
  87. package/dist/providers/tambo-context-attachment-provider.test.js +229 -463
  88. package/dist/providers/tambo-context-attachment-provider.test.js.map +1 -1
  89. package/dist/providers/tambo-interactable-provider-partial-updates.test.js +22 -21
  90. package/dist/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
  91. package/dist/providers/tambo-interactable-provider.d.ts +5 -2
  92. package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
  93. package/dist/providers/tambo-interactable-provider.js +126 -17
  94. package/dist/providers/tambo-interactable-provider.js.map +1 -1
  95. package/dist/providers/tambo-interactable-provider.test.js +242 -0
  96. package/dist/providers/tambo-interactable-provider.test.js.map +1 -1
  97. package/dist/providers/tambo-interactables-additional-context.test.js +2 -5
  98. package/dist/providers/tambo-interactables-additional-context.test.js.map +1 -1
  99. package/dist/providers/tambo-provider.d.ts +2 -3
  100. package/dist/providers/tambo-provider.d.ts.map +1 -1
  101. package/dist/providers/tambo-provider.js +6 -5
  102. package/dist/providers/tambo-provider.js.map +1 -1
  103. package/dist/providers/tambo-registry-provider.test.js +16 -0
  104. package/dist/providers/tambo-registry-provider.test.js.map +1 -1
  105. package/dist/providers/tambo-registry-schema-compat.test.js +31 -0
  106. package/dist/providers/tambo-registry-schema-compat.test.js.map +1 -1
  107. package/dist/providers/tambo-thread-input-provider.d.ts +1 -1
  108. package/dist/providers/tambo-thread-input-provider.d.ts.map +1 -1
  109. package/dist/providers/tambo-thread-input-provider.js +26 -4
  110. package/dist/providers/tambo-thread-input-provider.js.map +1 -1
  111. package/dist/providers/tambo-thread-provider-initial-messages.test.js +84 -2
  112. package/dist/providers/tambo-thread-provider-initial-messages.test.js.map +1 -1
  113. package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
  114. package/dist/providers/tambo-thread-provider.js +53 -42
  115. package/dist/providers/tambo-thread-provider.js.map +1 -1
  116. package/dist/providers/tambo-thread-provider.test.js +368 -262
  117. package/dist/providers/tambo-thread-provider.test.js.map +1 -1
  118. package/dist/schema/index.d.ts +1 -1
  119. package/dist/schema/index.d.ts.map +1 -1
  120. package/dist/schema/index.js +2 -1
  121. package/dist/schema/index.js.map +1 -1
  122. package/dist/schema/json-schema.d.ts +7 -0
  123. package/dist/schema/json-schema.d.ts.map +1 -1
  124. package/dist/schema/json-schema.js +40 -29
  125. package/dist/schema/json-schema.js.map +1 -1
  126. package/dist/schema/json-schema.test.d.ts +2 -0
  127. package/dist/schema/json-schema.test.d.ts.map +1 -0
  128. package/dist/schema/json-schema.test.js +204 -0
  129. package/dist/schema/json-schema.test.js.map +1 -0
  130. package/dist/schema/schema.test.js +237 -0
  131. package/dist/schema/schema.test.js.map +1 -1
  132. package/dist/schema/standard-schema.d.ts +1 -0
  133. package/dist/schema/standard-schema.d.ts.map +1 -1
  134. package/dist/schema/standard-schema.js +18 -13
  135. package/dist/schema/standard-schema.js.map +1 -1
  136. package/dist/schema/standard-schema.test.d.ts +2 -0
  137. package/dist/schema/standard-schema.test.d.ts.map +1 -0
  138. package/dist/schema/standard-schema.test.js +165 -0
  139. package/dist/schema/standard-schema.test.js.map +1 -0
  140. package/dist/schema/validate.test.js +149 -0
  141. package/dist/schema/validate.test.js.map +1 -1
  142. package/dist/schema/zod.d.ts +7 -4
  143. package/dist/schema/zod.d.ts.map +1 -1
  144. package/dist/schema/zod.js +65 -22
  145. package/dist/schema/zod.js.map +1 -1
  146. package/dist/schema/zod.test.js +112 -0
  147. package/dist/schema/zod.test.js.map +1 -1
  148. package/dist/setupTests.js +3 -0
  149. package/dist/setupTests.js.map +1 -1
  150. package/dist/testing/tools.d.ts +4 -1
  151. package/dist/testing/tools.d.ts.map +1 -1
  152. package/dist/testing/tools.js +6 -1
  153. package/dist/testing/tools.js.map +1 -1
  154. package/dist/util/generate-component.d.ts.map +1 -1
  155. package/dist/util/generate-component.js +18 -3
  156. package/dist/util/generate-component.js.map +1 -1
  157. package/dist/util/generate-component.test.d.ts +2 -0
  158. package/dist/util/generate-component.test.d.ts.map +1 -0
  159. package/dist/util/generate-component.test.js +340 -0
  160. package/dist/util/generate-component.test.js.map +1 -0
  161. package/dist/util/is-promise.d.ts +9 -0
  162. package/dist/util/is-promise.d.ts.map +1 -0
  163. package/dist/util/is-promise.js +20 -0
  164. package/dist/util/is-promise.js.map +1 -0
  165. package/dist/util/is-promise.test.d.ts +2 -0
  166. package/dist/util/is-promise.test.d.ts.map +1 -0
  167. package/dist/util/is-promise.test.js +48 -0
  168. package/dist/util/is-promise.test.js.map +1 -0
  169. package/dist/util/message-builder.d.ts +3 -1
  170. package/dist/util/message-builder.d.ts.map +1 -1
  171. package/dist/util/message-builder.js +20 -3
  172. package/dist/util/message-builder.js.map +1 -1
  173. package/dist/util/message-builder.test.js +269 -0
  174. package/dist/util/message-builder.test.js.map +1 -1
  175. package/dist/util/query-utils.test.d.ts +2 -0
  176. package/dist/util/query-utils.test.d.ts.map +1 -0
  177. package/dist/util/query-utils.test.js +382 -0
  178. package/dist/util/query-utils.test.js.map +1 -0
  179. package/dist/util/registry-validators.d.ts.map +1 -1
  180. package/dist/util/registry-validators.js +7 -0
  181. package/dist/util/registry-validators.js.map +1 -1
  182. package/dist/util/registry-validators.test.js +57 -0
  183. package/dist/util/registry-validators.test.js.map +1 -1
  184. package/dist/util/registry.d.ts.map +1 -1
  185. package/dist/util/registry.js +9 -0
  186. package/dist/util/registry.js.map +1 -1
  187. package/dist/util/registry.test.js +323 -1
  188. package/dist/util/registry.test.js.map +1 -1
  189. package/dist/util/resource-content-resolver.d.ts +20 -0
  190. package/dist/util/resource-content-resolver.d.ts.map +1 -0
  191. package/dist/util/resource-content-resolver.js +93 -0
  192. package/dist/util/resource-content-resolver.js.map +1 -0
  193. package/dist/util/resource-content-resolver.test.d.ts +2 -0
  194. package/dist/util/resource-content-resolver.test.d.ts.map +1 -0
  195. package/dist/util/resource-content-resolver.test.js +254 -0
  196. package/dist/util/resource-content-resolver.test.js.map +1 -0
  197. package/dist/util/resource-validators.test.d.ts +2 -0
  198. package/dist/util/resource-validators.test.d.ts.map +1 -0
  199. package/dist/util/resource-validators.test.js +90 -0
  200. package/dist/util/resource-validators.test.js.map +1 -0
  201. package/dist/util/tool-caller.d.ts +2 -2
  202. package/dist/util/tool-caller.d.ts.map +1 -1
  203. package/dist/util/tool-caller.js +8 -8
  204. package/dist/util/tool-caller.js.map +1 -1
  205. package/dist/util/validate-component-name.test.d.ts +2 -0
  206. package/dist/util/validate-component-name.test.d.ts.map +1 -0
  207. package/dist/util/validate-component-name.test.js +35 -0
  208. package/dist/util/validate-component-name.test.js.map +1 -0
  209. package/esm/context-helpers/context-helpers.test.js +16 -4
  210. package/esm/context-helpers/context-helpers.test.js.map +1 -1
  211. package/esm/context-helpers/current-interactables-context-helper.d.ts +2 -2
  212. package/esm/context-helpers/current-interactables-context-helper.d.ts.map +1 -1
  213. package/esm/context-helpers/current-interactables-context-helper.js +31 -12
  214. package/esm/context-helpers/current-interactables-context-helper.js.map +1 -1
  215. package/esm/context-helpers/registry.d.ts +2 -2
  216. package/esm/context-helpers/registry.d.ts.map +1 -1
  217. package/esm/context-helpers/registry.js.map +1 -1
  218. package/esm/context-helpers/types.d.ts +2 -2
  219. package/esm/context-helpers/types.d.ts.map +1 -1
  220. package/esm/context-helpers/types.js.map +1 -1
  221. package/esm/hoc/with-tambo-interactable.d.ts +50 -4
  222. package/esm/hoc/with-tambo-interactable.d.ts.map +1 -1
  223. package/esm/hoc/with-tambo-interactable.js +20 -5
  224. package/esm/hoc/with-tambo-interactable.js.map +1 -1
  225. package/esm/hooks/use-component-state.d.ts +3 -8
  226. package/esm/hooks/use-component-state.d.ts.map +1 -1
  227. package/esm/hooks/use-component-state.js +8 -0
  228. package/esm/hooks/use-component-state.js.map +1 -1
  229. package/esm/hooks/use-component-state.test.js +37 -0
  230. package/esm/hooks/use-component-state.test.js.map +1 -1
  231. package/esm/hooks/use-message-images.test.js +174 -37
  232. package/esm/hooks/use-message-images.test.js.map +1 -1
  233. package/esm/hooks/use-tambo-threads.js +1 -1
  234. package/esm/hooks/use-tambo-threads.js.map +1 -1
  235. package/esm/hooks/use-tambo-voice.d.ts +1 -1
  236. package/esm/hooks/use-tambo-voice.js +1 -1
  237. package/esm/hooks/use-tambo-voice.js.map +1 -1
  238. package/esm/hooks/use-tambo-voice.test.d.ts +2 -0
  239. package/esm/hooks/use-tambo-voice.test.d.ts.map +1 -0
  240. package/esm/hooks/use-tambo-voice.test.js +234 -0
  241. package/esm/hooks/use-tambo-voice.test.js.map +1 -0
  242. package/esm/index.d.ts +2 -2
  243. package/esm/index.d.ts.map +1 -1
  244. package/esm/index.js.map +1 -1
  245. package/esm/mcp/elicitation.d.ts.map +1 -1
  246. package/esm/mcp/elicitation.js +12 -0
  247. package/esm/mcp/elicitation.js.map +1 -1
  248. package/esm/mcp/elicitation.test.js +8 -1
  249. package/esm/mcp/elicitation.test.js.map +1 -1
  250. package/esm/mcp/mcp-client.d.ts +6 -10
  251. package/esm/mcp/mcp-client.d.ts.map +1 -1
  252. package/esm/mcp/mcp-client.js.map +1 -1
  253. package/esm/mcp/mcp-constants.d.ts +19 -0
  254. package/esm/mcp/mcp-constants.d.ts.map +1 -0
  255. package/esm/mcp/mcp-constants.js +18 -0
  256. package/esm/mcp/mcp-constants.js.map +1 -0
  257. package/esm/mcp/mcp-hooks.d.ts +21 -40
  258. package/esm/mcp/mcp-hooks.d.ts.map +1 -1
  259. package/esm/mcp/mcp-hooks.js +97 -40
  260. package/esm/mcp/mcp-hooks.js.map +1 -1
  261. package/esm/mcp/mcp-hooks.test.js +431 -5
  262. package/esm/mcp/mcp-hooks.test.js.map +1 -1
  263. package/esm/mcp/tambo-mcp-provider.d.ts +7 -0
  264. package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
  265. package/esm/mcp/tambo-mcp-provider.js +204 -154
  266. package/esm/mcp/tambo-mcp-provider.js.map +1 -1
  267. package/esm/mcp/tambo-mcp-provider.test.js +37 -0
  268. package/esm/mcp/tambo-mcp-provider.test.js.map +1 -1
  269. package/esm/model/component-metadata.d.ts +54 -21
  270. package/esm/model/component-metadata.d.ts.map +1 -1
  271. package/esm/model/component-metadata.js.map +1 -1
  272. package/esm/model/tambo-interactable.d.ts +13 -5
  273. package/esm/model/tambo-interactable.d.ts.map +1 -1
  274. package/esm/model/tambo-interactable.js.map +1 -1
  275. package/esm/providers/__tests__/thread-input-resource-resolution.test.d.ts +2 -0
  276. package/esm/providers/__tests__/thread-input-resource-resolution.test.d.ts.map +1 -0
  277. package/esm/providers/__tests__/thread-input-resource-resolution.test.js +587 -0
  278. package/esm/providers/__tests__/thread-input-resource-resolution.test.js.map +1 -0
  279. package/esm/providers/index.d.ts +1 -1
  280. package/esm/providers/index.d.ts.map +1 -1
  281. package/esm/providers/index.js.map +1 -1
  282. package/esm/providers/tambo-client-provider.d.ts +8 -0
  283. package/esm/providers/tambo-client-provider.d.ts.map +1 -1
  284. package/esm/providers/tambo-client-provider.js +11 -12
  285. package/esm/providers/tambo-client-provider.js.map +1 -1
  286. package/esm/providers/tambo-client-provider.test.d.ts +2 -0
  287. package/esm/providers/tambo-client-provider.test.d.ts.map +1 -0
  288. package/esm/providers/tambo-client-provider.test.js +203 -0
  289. package/esm/providers/tambo-client-provider.test.js.map +1 -0
  290. package/esm/providers/tambo-context-attachment-provider.d.ts +34 -92
  291. package/esm/providers/tambo-context-attachment-provider.d.ts.map +1 -1
  292. package/esm/providers/tambo-context-attachment-provider.js +63 -106
  293. package/esm/providers/tambo-context-attachment-provider.js.map +1 -1
  294. package/esm/providers/tambo-context-attachment-provider.test.js +230 -464
  295. package/esm/providers/tambo-context-attachment-provider.test.js.map +1 -1
  296. package/esm/providers/tambo-interactable-provider-partial-updates.test.js +22 -21
  297. package/esm/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
  298. package/esm/providers/tambo-interactable-provider.d.ts +5 -2
  299. package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
  300. package/esm/providers/tambo-interactable-provider.js +126 -17
  301. package/esm/providers/tambo-interactable-provider.js.map +1 -1
  302. package/esm/providers/tambo-interactable-provider.test.js +242 -0
  303. package/esm/providers/tambo-interactable-provider.test.js.map +1 -1
  304. package/esm/providers/tambo-interactables-additional-context.test.js +2 -5
  305. package/esm/providers/tambo-interactables-additional-context.test.js.map +1 -1
  306. package/esm/providers/tambo-provider.d.ts +2 -3
  307. package/esm/providers/tambo-provider.d.ts.map +1 -1
  308. package/esm/providers/tambo-provider.js +6 -5
  309. package/esm/providers/tambo-provider.js.map +1 -1
  310. package/esm/providers/tambo-registry-provider.test.js +16 -0
  311. package/esm/providers/tambo-registry-provider.test.js.map +1 -1
  312. package/esm/providers/tambo-registry-schema-compat.test.js +31 -0
  313. package/esm/providers/tambo-registry-schema-compat.test.js.map +1 -1
  314. package/esm/providers/tambo-thread-input-provider.d.ts +1 -1
  315. package/esm/providers/tambo-thread-input-provider.d.ts.map +1 -1
  316. package/esm/providers/tambo-thread-input-provider.js +26 -4
  317. package/esm/providers/tambo-thread-input-provider.js.map +1 -1
  318. package/esm/providers/tambo-thread-provider-initial-messages.test.js +84 -2
  319. package/esm/providers/tambo-thread-provider-initial-messages.test.js.map +1 -1
  320. package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
  321. package/esm/providers/tambo-thread-provider.js +53 -42
  322. package/esm/providers/tambo-thread-provider.js.map +1 -1
  323. package/esm/providers/tambo-thread-provider.test.js +368 -262
  324. package/esm/providers/tambo-thread-provider.test.js.map +1 -1
  325. package/esm/schema/index.d.ts +1 -1
  326. package/esm/schema/index.d.ts.map +1 -1
  327. package/esm/schema/index.js +1 -1
  328. package/esm/schema/index.js.map +1 -1
  329. package/esm/schema/json-schema.d.ts +7 -0
  330. package/esm/schema/json-schema.d.ts.map +1 -1
  331. package/esm/schema/json-schema.js +11 -1
  332. package/esm/schema/json-schema.js.map +1 -1
  333. package/esm/schema/json-schema.test.d.ts +2 -0
  334. package/esm/schema/json-schema.test.d.ts.map +1 -0
  335. package/esm/schema/json-schema.test.js +202 -0
  336. package/esm/schema/json-schema.test.js.map +1 -0
  337. package/esm/schema/schema.test.js +238 -1
  338. package/esm/schema/schema.test.js.map +1 -1
  339. package/esm/schema/standard-schema.d.ts +1 -0
  340. package/esm/schema/standard-schema.d.ts.map +1 -1
  341. package/esm/schema/standard-schema.js +18 -13
  342. package/esm/schema/standard-schema.js.map +1 -1
  343. package/esm/schema/standard-schema.test.d.ts +2 -0
  344. package/esm/schema/standard-schema.test.d.ts.map +1 -0
  345. package/esm/schema/standard-schema.test.js +130 -0
  346. package/esm/schema/standard-schema.test.js.map +1 -0
  347. package/esm/schema/validate.test.js +149 -0
  348. package/esm/schema/validate.test.js.map +1 -1
  349. package/esm/schema/zod.d.ts +7 -4
  350. package/esm/schema/zod.d.ts.map +1 -1
  351. package/esm/schema/zod.js +65 -22
  352. package/esm/schema/zod.js.map +1 -1
  353. package/esm/schema/zod.test.js +113 -1
  354. package/esm/schema/zod.test.js.map +1 -1
  355. package/esm/setupTests.js +3 -0
  356. package/esm/setupTests.js.map +1 -1
  357. package/esm/testing/tools.d.ts +4 -1
  358. package/esm/testing/tools.d.ts.map +1 -1
  359. package/esm/testing/tools.js +6 -1
  360. package/esm/testing/tools.js.map +1 -1
  361. package/esm/util/generate-component.d.ts.map +1 -1
  362. package/esm/util/generate-component.js +18 -3
  363. package/esm/util/generate-component.js.map +1 -1
  364. package/esm/util/generate-component.test.d.ts +2 -0
  365. package/esm/util/generate-component.test.d.ts.map +1 -0
  366. package/esm/util/generate-component.test.js +302 -0
  367. package/esm/util/generate-component.test.js.map +1 -0
  368. package/esm/util/is-promise.d.ts +9 -0
  369. package/esm/util/is-promise.d.ts.map +1 -0
  370. package/esm/util/is-promise.js +17 -0
  371. package/esm/util/is-promise.js.map +1 -0
  372. package/esm/util/is-promise.test.d.ts +2 -0
  373. package/esm/util/is-promise.test.d.ts.map +1 -0
  374. package/esm/util/is-promise.test.js +46 -0
  375. package/esm/util/is-promise.test.js.map +1 -0
  376. package/esm/util/message-builder.d.ts +3 -1
  377. package/esm/util/message-builder.d.ts.map +1 -1
  378. package/esm/util/message-builder.js +20 -3
  379. package/esm/util/message-builder.js.map +1 -1
  380. package/esm/util/message-builder.test.js +269 -0
  381. package/esm/util/message-builder.test.js.map +1 -1
  382. package/esm/util/query-utils.test.d.ts +2 -0
  383. package/esm/util/query-utils.test.d.ts.map +1 -0
  384. package/esm/util/query-utils.test.js +380 -0
  385. package/esm/util/query-utils.test.js.map +1 -0
  386. package/esm/util/registry-validators.d.ts.map +1 -1
  387. package/esm/util/registry-validators.js +7 -0
  388. package/esm/util/registry-validators.js.map +1 -1
  389. package/esm/util/registry-validators.test.js +57 -0
  390. package/esm/util/registry-validators.test.js.map +1 -1
  391. package/esm/util/registry.d.ts.map +1 -1
  392. package/esm/util/registry.js +9 -0
  393. package/esm/util/registry.js.map +1 -1
  394. package/esm/util/registry.test.js +324 -2
  395. package/esm/util/registry.test.js.map +1 -1
  396. package/esm/util/resource-content-resolver.d.ts +20 -0
  397. package/esm/util/resource-content-resolver.d.ts.map +1 -0
  398. package/esm/util/resource-content-resolver.js +89 -0
  399. package/esm/util/resource-content-resolver.js.map +1 -0
  400. package/esm/util/resource-content-resolver.test.d.ts +2 -0
  401. package/esm/util/resource-content-resolver.test.d.ts.map +1 -0
  402. package/esm/util/resource-content-resolver.test.js +252 -0
  403. package/esm/util/resource-content-resolver.test.js.map +1 -0
  404. package/esm/util/resource-validators.test.d.ts +2 -0
  405. package/esm/util/resource-validators.test.d.ts.map +1 -0
  406. package/esm/util/resource-validators.test.js +88 -0
  407. package/esm/util/resource-validators.test.js.map +1 -0
  408. package/esm/util/tool-caller.d.ts +2 -2
  409. package/esm/util/tool-caller.d.ts.map +1 -1
  410. package/esm/util/tool-caller.js +8 -8
  411. package/esm/util/tool-caller.js.map +1 -1
  412. package/esm/util/validate-component-name.test.d.ts +2 -0
  413. package/esm/util/validate-component-name.test.d.ts.map +1 -0
  414. package/esm/util/validate-component-name.test.js +33 -0
  415. package/esm/util/validate-component-name.test.js.map +1 -0
  416. package/package.json +15 -23
  417. package/dist/schema/alias.d.ts +0 -3
  418. package/dist/schema/alias.d.ts.map +0 -1
  419. package/dist/schema/alias.js +0 -6
  420. package/dist/schema/alias.js.map +0 -1
  421. package/esm/schema/alias.d.ts +0 -3
  422. package/esm/schema/alias.d.ts.map +0 -1
  423. package/esm/schema/alias.js +0 -13
  424. package/esm/schema/alias.js.map +0 -1
@@ -1,29 +1,33 @@
1
1
  import { act, renderHook, waitFor } from "@testing-library/react";
2
2
  import React from "react";
3
3
  import { TamboContextAttachmentProvider, useTamboContextAttachment, } from "./tambo-context-attachment-provider";
4
- import { TamboContextHelpersProvider, useTamboContextHelpers, } from "./tambo-context-helpers-provider";
4
+ import { useTamboContextHelpers } from "./tambo-context-helpers-provider";
5
+ // Mock the context helpers provider
6
+ jest.mock("./tambo-context-helpers-provider");
7
+ const mockAddContextHelper = jest.fn();
8
+ const mockRemoveContextHelper = jest.fn();
9
+ const CONTEXT_ATTACHMENTS_HELPER_KEY = "contextAttachments";
10
+ beforeEach(() => {
11
+ jest.clearAllMocks();
12
+ useTamboContextHelpers.mockReturnValue({
13
+ addContextHelper: mockAddContextHelper,
14
+ removeContextHelper: mockRemoveContextHelper,
15
+ });
16
+ });
5
17
  /**
6
18
  * Test suite for TamboContextAttachmentProvider
7
19
  *
8
20
  * Tests the context attachment feature which allows:
9
- * - Visual context badges above message input
10
- * - Automatic context helper registration/unregistration
11
- * - Custom suggestions that override auto-generated ones
12
- * - Dynamic context data customization via getContextHelperData
21
+ * - Adding context attachments that will be sent with the next message
22
+ * - Automatic registration/deregistration of context helpers
13
23
  */
14
24
  describe("TamboContextAttachmentProvider", () => {
15
- beforeEach(() => {
16
- jest.clearAllMocks();
17
- });
18
25
  /**
19
- * Base wrapper with both TamboContextAttachmentProvider and TamboContextHelpersProvider
20
- * since context attachments need access to context helpers API
21
- * @param getContextHelperData - Optional custom function to get context helper data
22
- * @returns A React component that wraps children with the necessary providers
26
+ * Base wrapper with TamboContextAttachmentProvider
27
+ * @returns A React component that wraps children with the provider
23
28
  */
24
- const createWrapper = (getContextHelperData) => {
25
- const Wrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null,
26
- React.createElement(TamboContextAttachmentProvider, { getContextHelperData: getContextHelperData }, children)));
29
+ const createWrapper = () => {
30
+ const Wrapper = ({ children }) => (React.createElement(TamboContextAttachmentProvider, null, children));
27
31
  Wrapper.displayName = "TestWrapper";
28
32
  return Wrapper;
29
33
  };
@@ -39,8 +43,6 @@ describe("TamboContextAttachmentProvider", () => {
39
43
  expect(result.current).toHaveProperty("addContextAttachment");
40
44
  expect(result.current).toHaveProperty("removeContextAttachment");
41
45
  expect(result.current).toHaveProperty("clearContextAttachments");
42
- expect(result.current).toHaveProperty("customSuggestions");
43
- expect(result.current).toHaveProperty("setCustomSuggestions");
44
46
  });
45
47
  /**
46
48
  * Hook should throw error when used outside of provider
@@ -58,154 +60,318 @@ describe("TamboContextAttachmentProvider", () => {
58
60
  });
59
61
  describe("Adding Context Attachments", () => {
60
62
  /**
61
- * Should add a context attachment with auto-generated ID
63
+ * Should add a context attachment and register/update the merged context helper
62
64
  */
63
- it("should add a context attachment", () => {
65
+ it("should add a context attachment", async () => {
64
66
  const { result } = renderHook(() => useTamboContextAttachment(), {
65
67
  wrapper: createWrapper(),
66
68
  });
69
+ let attachment;
67
70
  act(() => {
68
- result.current.addContextAttachment({
69
- name: "Button.tsx",
70
- metadata: { filePath: "/src/Button.tsx" },
71
+ attachment = result.current.addContextAttachment({
72
+ context: "selectedFile",
73
+ displayName: "Button.tsx",
74
+ type: "file",
71
75
  });
72
76
  });
73
77
  expect(result.current.attachments).toHaveLength(1);
74
78
  expect(result.current.attachments[0]).toMatchObject({
75
- name: "Button.tsx",
76
- metadata: { filePath: "/src/Button.tsx" },
79
+ displayName: "Button.tsx",
80
+ context: "selectedFile",
81
+ type: "file",
77
82
  });
78
83
  expect(result.current.attachments[0].id).toBeDefined();
84
+ expect(attachment.id).toBe(result.current.attachments[0].id);
85
+ // Wait for useEffect to run and register the merged helper with the attachment
86
+ await waitFor(() => {
87
+ const lastCall = mockAddContextHelper.mock.calls
88
+ .filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
89
+ .pop();
90
+ if (lastCall) {
91
+ const helperFn = lastCall[1];
92
+ const helperResult = helperFn();
93
+ expect(helperResult).not.toBeNull();
94
+ expect(helperResult).toHaveLength(1);
95
+ }
96
+ else {
97
+ throw new Error("Helper not registered");
98
+ }
99
+ });
100
+ // Verify the helper function returns the merged attachments array
101
+ const helperFn = mockAddContextHelper.mock.calls
102
+ .filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
103
+ .pop()?.[1];
104
+ expect(helperFn).toBeDefined();
105
+ const helperResult = helperFn();
106
+ expect(helperResult).toEqual([
107
+ {
108
+ id: attachment.id,
109
+ displayName: "Button.tsx",
110
+ context: "selectedFile",
111
+ type: "file",
112
+ },
113
+ ]);
79
114
  });
80
115
  /**
81
- * Should add multiple different attachments
116
+ * Should add a context attachment without displayName
82
117
  */
83
- it("should add multiple context attachments", () => {
118
+ it("should add a context attachment without displayName", () => {
119
+ const { result } = renderHook(() => useTamboContextAttachment(), {
120
+ wrapper: createWrapper(),
121
+ });
122
+ let attachment;
123
+ act(() => {
124
+ attachment = result.current.addContextAttachment({
125
+ context: "selectedFile",
126
+ });
127
+ });
128
+ expect(result.current.attachments).toHaveLength(1);
129
+ expect(result.current.attachments[0]).toMatchObject({
130
+ context: "selectedFile",
131
+ });
132
+ expect(result.current.attachments[0].displayName).toBeUndefined();
133
+ expect(attachment.id).toBe(result.current.attachments[0].id);
134
+ });
135
+ /**
136
+ * Should add multiple different context attachments and update the merged helper
137
+ */
138
+ it("should add multiple context attachments", async () => {
84
139
  const { result } = renderHook(() => useTamboContextAttachment(), {
85
140
  wrapper: createWrapper(),
86
141
  });
87
142
  act(() => {
88
143
  result.current.addContextAttachment({
89
- name: "Button.tsx",
144
+ context: "file1",
145
+ displayName: "Button.tsx",
146
+ type: "file",
90
147
  });
91
148
  result.current.addContextAttachment({
92
- name: "Card.tsx",
149
+ context: "file2",
150
+ displayName: "Card.tsx",
151
+ type: "file",
93
152
  });
94
153
  });
95
154
  expect(result.current.attachments).toHaveLength(2);
96
- expect(result.current.attachments[0].name).toBe("Button.tsx");
97
- expect(result.current.attachments[1].name).toBe("Card.tsx");
155
+ expect(result.current.attachments[0].displayName).toBe("Button.tsx");
156
+ expect(result.current.attachments[1].displayName).toBe("Card.tsx");
157
+ // Wait for useEffect to run and update the merged helper
158
+ await waitFor(() => {
159
+ // The helper should be called/updated for each attachment change
160
+ expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
161
+ });
162
+ // Verify the helper function returns all attachments
163
+ const helperFn = mockAddContextHelper.mock.calls
164
+ .filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
165
+ .pop()?.[1];
166
+ expect(helperFn).toBeDefined();
167
+ const helperResult = helperFn();
168
+ expect(helperResult).toHaveLength(2);
169
+ expect(helperResult[0].displayName).toBe("Button.tsx");
170
+ expect(helperResult[1].displayName).toBe("Card.tsx");
98
171
  });
99
172
  /**
100
- * Should prevent duplicates with the same name
173
+ * Should allow multiple attachments with the same context value
101
174
  */
102
- it("should prevent duplicate attachments with same name", () => {
175
+ it("should allow multiple attachments with same context value", async () => {
103
176
  const { result } = renderHook(() => useTamboContextAttachment(), {
104
177
  wrapper: createWrapper(),
105
178
  });
106
179
  act(() => {
107
180
  result.current.addContextAttachment({
108
- name: "Button.tsx",
181
+ context: "selectedFile",
182
+ displayName: "Button.tsx",
183
+ type: "file",
109
184
  });
110
185
  result.current.addContextAttachment({
111
- name: "Button.tsx",
186
+ context: "selectedFile",
187
+ displayName: "Card.tsx",
188
+ type: "file",
112
189
  });
113
190
  });
114
- expect(result.current.attachments).toHaveLength(1);
191
+ expect(result.current.attachments).toHaveLength(2);
192
+ // Wait for useEffect to update the merged helper
193
+ await waitFor(() => {
194
+ expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
195
+ });
196
+ // Verify both attachments are included in the merged result
197
+ const helperFn = mockAddContextHelper.mock.calls
198
+ .filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
199
+ .pop()?.[1];
200
+ expect(helperFn).toBeDefined();
201
+ const helperResult = helperFn();
202
+ expect(helperResult).toHaveLength(2);
115
203
  });
116
204
  /**
117
- * Should support optional icon property
205
+ * Should support optional type property
118
206
  */
119
- it("should support icon property", () => {
207
+ it("should support optional type property", () => {
120
208
  const { result } = renderHook(() => useTamboContextAttachment(), {
121
209
  wrapper: createWrapper(),
122
210
  });
123
- const icon = React.createElement("span", null, "\uD83D\uDCC4");
124
211
  act(() => {
125
212
  result.current.addContextAttachment({
126
- name: "File.txt",
127
- icon,
213
+ context: "file1",
214
+ displayName: "Button.tsx",
215
+ type: "file",
216
+ });
217
+ result.current.addContextAttachment({
218
+ context: "page1",
219
+ displayName: "Dashboard",
220
+ type: "page",
128
221
  });
129
222
  });
130
- expect(result.current.attachments[0].icon).toBe(icon);
223
+ expect(result.current.attachments[0].type).toBe("file");
224
+ expect(result.current.attachments[1].type).toBe("page");
131
225
  });
132
226
  });
133
227
  describe("Removing Context Attachments", () => {
134
228
  /**
135
- * Should remove a specific attachment by ID
229
+ * Should remove a specific context attachment by ID and update the merged helper
136
230
  */
137
- it("should remove context attachment by ID", () => {
231
+ it("should remove context attachment by ID", async () => {
138
232
  const { result } = renderHook(() => useTamboContextAttachment(), {
139
233
  wrapper: createWrapper(),
140
234
  });
235
+ let attachmentId = "";
141
236
  act(() => {
142
- result.current.addContextAttachment({
143
- name: "Button.tsx",
237
+ const attachment = result.current.addContextAttachment({
238
+ context: "selectedFile",
239
+ displayName: "Button.tsx",
144
240
  });
241
+ attachmentId = attachment.id;
145
242
  });
146
243
  expect(result.current.attachments).toHaveLength(1);
147
- const attachmentId = result.current.attachments[0].id;
244
+ // Wait for initial helper registration
245
+ await waitFor(() => {
246
+ expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
247
+ });
148
248
  act(() => {
149
249
  result.current.removeContextAttachment(attachmentId);
150
250
  });
151
251
  expect(result.current.attachments).toHaveLength(0);
252
+ // Wait for useEffect to update the helper (should return null when empty)
253
+ await waitFor(() => {
254
+ const lastCall = mockAddContextHelper.mock.calls
255
+ .filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
256
+ .pop();
257
+ if (lastCall) {
258
+ const helperFn = lastCall[1];
259
+ expect(helperFn()).toBeNull();
260
+ }
261
+ });
152
262
  });
153
263
  /**
154
264
  * Should only remove the specified attachment when multiple exist
155
265
  */
156
- it("should only remove specified attachment", () => {
266
+ it("should only remove specified attachment", async () => {
157
267
  const { result } = renderHook(() => useTamboContextAttachment(), {
158
268
  wrapper: createWrapper(),
159
269
  });
270
+ let firstId = "";
160
271
  act(() => {
161
- result.current.addContextAttachment({ name: "First.tsx" });
162
- result.current.addContextAttachment({ name: "Second.tsx" });
272
+ const first = result.current.addContextAttachment({
273
+ context: "file1",
274
+ displayName: "First.tsx",
275
+ });
276
+ result.current.addContextAttachment({
277
+ context: "file2",
278
+ displayName: "Second.tsx",
279
+ });
280
+ firstId = first.id;
163
281
  });
164
282
  expect(result.current.attachments).toHaveLength(2);
165
- const firstId = result.current.attachments[0].id;
283
+ // Wait for initial helper registration
284
+ await waitFor(() => {
285
+ expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
286
+ });
166
287
  act(() => {
167
288
  result.current.removeContextAttachment(firstId);
168
289
  });
169
290
  expect(result.current.attachments).toHaveLength(1);
170
- expect(result.current.attachments[0].name).toBe("Second.tsx");
291
+ expect(result.current.attachments[0].displayName).toBe("Second.tsx");
292
+ // Wait for useEffect to update the helper with remaining attachment
293
+ await waitFor(() => {
294
+ const lastCall = mockAddContextHelper.mock.calls
295
+ .filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
296
+ .pop();
297
+ if (lastCall) {
298
+ const helperFn = lastCall[1];
299
+ const helperResult = helperFn();
300
+ expect(helperResult).toHaveLength(1);
301
+ expect(helperResult[0].displayName).toBe("Second.tsx");
302
+ }
303
+ });
171
304
  });
172
305
  /**
173
306
  * Should handle removing non-existent attachment gracefully
174
307
  */
175
- it("should handle removing non-existent attachment gracefully", () => {
308
+ it("should handle removing non-existent attachment gracefully", async () => {
176
309
  const { result } = renderHook(() => useTamboContextAttachment(), {
177
310
  wrapper: createWrapper(),
178
311
  });
312
+ // Wait for initial helper registration (useEffect runs on mount)
313
+ await waitFor(() => {
314
+ expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
315
+ });
179
316
  expect(() => {
180
317
  act(() => {
181
318
  result.current.removeContextAttachment("non-existent-id");
182
319
  });
183
320
  }).not.toThrow();
321
+ // The helper is registered on mount, so removeContextHelper will be called
322
+ // when the component unmounts or when attachments change, but not for
323
+ // removing a non-existent attachment since attachments didn't change
324
+ // However, the cleanup function from the initial useEffect registration
325
+ // may be called. Let's just verify the attachment list is still empty.
326
+ expect(result.current.attachments).toHaveLength(0);
184
327
  });
185
328
  });
186
- describe("Clearing All Attachments", () => {
329
+ describe("Clearing All Context Attachments", () => {
187
330
  /**
188
- * Should clear all attachments at once
331
+ * Should clear all context attachments and update the merged helper to return null
189
332
  */
190
- it("should clear all context attachments", () => {
333
+ it("should clear all context attachments", async () => {
191
334
  const { result } = renderHook(() => useTamboContextAttachment(), {
192
335
  wrapper: createWrapper(),
193
336
  });
194
337
  act(() => {
195
- result.current.addContextAttachment({ name: "First.tsx" });
196
- result.current.addContextAttachment({ name: "Second.tsx" });
197
- result.current.addContextAttachment({ name: "Third.tsx" });
338
+ result.current.addContextAttachment({
339
+ context: "file1",
340
+ displayName: "First.tsx",
341
+ });
342
+ result.current.addContextAttachment({
343
+ context: "file2",
344
+ displayName: "Second.tsx",
345
+ });
346
+ result.current.addContextAttachment({
347
+ context: "file3",
348
+ displayName: "Third.tsx",
349
+ });
198
350
  });
199
351
  expect(result.current.attachments).toHaveLength(3);
352
+ // Wait for initial helper registration
353
+ await waitFor(() => {
354
+ expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
355
+ });
200
356
  act(() => {
201
357
  result.current.clearContextAttachments();
202
358
  });
203
359
  expect(result.current.attachments).toHaveLength(0);
360
+ // Wait for useEffect to update the helper (should return null when empty)
361
+ await waitFor(() => {
362
+ const lastCall = mockAddContextHelper.mock.calls
363
+ .filter((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)
364
+ .pop();
365
+ if (lastCall) {
366
+ const helperFn = lastCall[1];
367
+ expect(helperFn()).toBeNull();
368
+ }
369
+ });
204
370
  });
205
371
  /**
206
372
  * Should handle clearing when no attachments exist
207
373
  */
208
- it("should handle clearing when no attachments exist", () => {
374
+ it("should handle clearing when no attachments exist", async () => {
209
375
  const { result } = renderHook(() => useTamboContextAttachment(), {
210
376
  wrapper: createWrapper(),
211
377
  });
@@ -215,413 +381,13 @@ describe("TamboContextAttachmentProvider", () => {
215
381
  });
216
382
  }).not.toThrow();
217
383
  expect(result.current.attachments).toHaveLength(0);
218
- });
219
- });
220
- describe("Context Helpers Integration", () => {
221
- /**
222
- * Should automatically register context helpers when attachments are added
223
- */
224
- it("should register context helpers for attachments", async () => {
225
- const wrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null,
226
- React.createElement(TamboContextAttachmentProvider, null, children)));
227
- const { result } = renderHook(() => ({
228
- attachment: useTamboContextAttachment(),
229
- helpers: useTamboContextHelpers(),
230
- }), { wrapper });
231
- // Add attachment
232
- act(() => {
233
- result.current.attachment.addContextAttachment({
234
- name: "Button.tsx",
235
- metadata: { filePath: "/src/Button.tsx" },
236
- });
237
- });
238
- // Wait for effect to run
239
- await waitFor(async () => {
240
- const contexts = await result.current.helpers.getAdditionalContext();
241
- expect(contexts.length).toBeGreaterThan(0);
242
- });
243
- const contexts = await result.current.helpers.getAdditionalContext();
244
- const attachmentContext = contexts.find((c) => c.name.includes(result.current.attachment.attachments[0].id));
245
- expect(attachmentContext).toBeDefined();
246
- expect(attachmentContext?.context).toHaveProperty("selectedComponent");
247
- });
248
- /**
249
- * Should unregister context helpers when attachments are removed
250
- */
251
- it("should unregister context helpers when attachments are removed", async () => {
252
- const wrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null,
253
- React.createElement(TamboContextAttachmentProvider, null, children)));
254
- const { result } = renderHook(() => ({
255
- attachment: useTamboContextAttachment(),
256
- helpers: useTamboContextHelpers(),
257
- }), { wrapper });
258
- // Add attachment
259
- act(() => {
260
- result.current.attachment.addContextAttachment({
261
- name: "Button.tsx",
262
- });
263
- });
264
- // Wait for context helper to be registered
265
- await waitFor(async () => {
266
- const contexts = await result.current.helpers.getAdditionalContext();
267
- expect(contexts.length).toBeGreaterThan(0);
268
- });
269
- const initialContexts = await result.current.helpers.getAdditionalContext();
270
- const initialCount = initialContexts.length;
271
- const attachmentId = result.current.attachment.attachments[0].id;
272
- // Remove attachment
273
- act(() => {
274
- result.current.attachment.removeContextAttachment(attachmentId);
275
- });
276
- // Wait for context helper to be unregistered
277
- await waitFor(async () => {
278
- const contexts = await result.current.helpers.getAdditionalContext();
279
- expect(contexts.length).toBeLessThan(initialCount);
280
- });
281
- });
282
- /**
283
- * Should use default context data structure when no custom function provided
284
- */
285
- it("should use default context data structure", async () => {
286
- const wrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null,
287
- React.createElement(TamboContextAttachmentProvider, null, children)));
288
- const { result } = renderHook(() => ({
289
- attachment: useTamboContextAttachment(),
290
- helpers: useTamboContextHelpers(),
291
- }), { wrapper });
292
- act(() => {
293
- result.current.attachment.addContextAttachment({
294
- name: "Button.tsx",
295
- metadata: { filePath: "/src/Button.tsx", type: "component" },
296
- });
297
- });
298
- await waitFor(async () => {
299
- const contexts = await result.current.helpers.getAdditionalContext();
300
- expect(contexts.length).toBeGreaterThan(0);
301
- });
302
- const contexts = await result.current.helpers.getAdditionalContext();
303
- const attachmentContext = contexts.find((c) => c.name.includes(result.current.attachment.attachments[0].id));
304
- expect(attachmentContext?.context).toMatchObject({
305
- selectedComponent: {
306
- name: "Button.tsx",
307
- instruction: expect.stringContaining("Tambo interactable component"),
308
- filePath: "/src/Button.tsx",
309
- type: "component",
310
- },
311
- });
312
- });
313
- /**
314
- * Should use custom getContextHelperData function when provided
315
- */
316
- it("should use custom getContextHelperData function", async () => {
317
- const customGetContextHelperData = jest.fn(async (context) => ({
318
- selectedFile: {
319
- name: context.name,
320
- path: context.metadata?.filePath,
321
- customField: "custom value",
322
- },
323
- }));
324
- const wrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null,
325
- React.createElement(TamboContextAttachmentProvider, { getContextHelperData: customGetContextHelperData }, children)));
326
- const { result } = renderHook(() => ({
327
- attachment: useTamboContextAttachment(),
328
- helpers: useTamboContextHelpers(),
329
- }), { wrapper });
330
- act(() => {
331
- result.current.attachment.addContextAttachment({
332
- name: "Button.tsx",
333
- metadata: { filePath: "/src/Button.tsx" },
334
- });
335
- });
336
- // Wait for context helper to be registered and called
337
- await waitFor(async () => {
338
- const contexts = await result.current.helpers.getAdditionalContext();
339
- expect(contexts.length).toBeGreaterThan(0);
340
- }, { timeout: 2000 });
341
- expect(customGetContextHelperData).toHaveBeenCalled();
342
- const contexts = await result.current.helpers.getAdditionalContext();
343
- const attachmentContext = contexts.find((c) => c.name.includes(result.current.attachment.attachments[0].id));
344
- expect(attachmentContext?.context).toEqual({
345
- selectedFile: {
346
- name: "Button.tsx",
347
- path: "/src/Button.tsx",
348
- customField: "custom value",
349
- },
350
- });
351
- });
352
- /**
353
- * Should update context helpers when getContextHelperData function changes
354
- * This tests the bug fix for stale closure issue
355
- */
356
- it("should update existing context helpers when getContextHelperData changes", async () => {
357
- // Create a wrapper component with state to manage the function
358
- const TestWrapper = ({ children }) => {
359
- const [version, setVersion] = React.useState("v1");
360
- const getContextHelperData = React.useCallback(async (context) => ({
361
- version,
362
- name: context.name,
363
- }), [version]);
364
- // Expose setVersion for the test
365
- TestWrapper.setVersion = setVersion;
366
- return (React.createElement(TamboContextHelpersProvider, null,
367
- React.createElement(TamboContextAttachmentProvider, { getContextHelperData: getContextHelperData }, children)));
368
- };
369
- const { result } = renderHook(() => ({
370
- attachment: useTamboContextAttachment(),
371
- helpers: useTamboContextHelpers(),
372
- }), { wrapper: TestWrapper });
373
- // Add attachment with first version
374
- act(() => {
375
- result.current.attachment.addContextAttachment({
376
- name: "Button.tsx",
377
- });
378
- });
379
- // Wait for context helper to be registered
380
- await waitFor(async () => {
381
- const contexts = await result.current.helpers.getAdditionalContext();
382
- expect(contexts.length).toBeGreaterThan(0);
383
- }, { timeout: 2000 });
384
- // Verify v1 context
385
- let contexts = await result.current.helpers.getAdditionalContext();
386
- let attachmentContext = contexts.find((c) => c.name.includes(result.current.attachment.attachments[0].id));
387
- expect(attachmentContext?.context).toMatchObject({ version: "v1" });
388
- // Change the version which will trigger a new getContextHelperData function
389
- act(() => {
390
- TestWrapper.setVersion("v2");
391
- });
392
- // Wait for context to update to v2
393
- await waitFor(async () => {
394
- const contexts = await result.current.helpers.getAdditionalContext();
395
- const context = contexts.find((c) => c.name.includes(result.current.attachment.attachments[0].id));
396
- return context?.context.version === "v2";
397
- }, { timeout: 2000 });
398
- // Verify v2 context - should be updated, not stale
399
- contexts = await result.current.helpers.getAdditionalContext();
400
- attachmentContext = contexts.find((c) => c.name.includes(result.current.attachment.attachments[0].id));
401
- expect(attachmentContext?.context).toMatchObject({ version: "v2" });
402
- });
403
- /**
404
- * Should handle errors in getContextHelperData gracefully
405
- */
406
- it("should handle errors in getContextHelperData gracefully", async () => {
407
- const consoleErrorSpy = jest
408
- .spyOn(console, "error")
409
- .mockImplementation(() => { });
410
- const errorGetData = jest.fn(async () => {
411
- throw new Error("Custom data error");
412
- });
413
- const wrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null,
414
- React.createElement(TamboContextAttachmentProvider, { getContextHelperData: errorGetData }, children)));
415
- const { result } = renderHook(() => ({
416
- attachment: useTamboContextAttachment(),
417
- helpers: useTamboContextHelpers(),
418
- }), { wrapper });
419
- act(() => {
420
- result.current.attachment.addContextAttachment({
421
- name: "Button.tsx",
422
- });
423
- });
424
- // Wait for effect to run and try to call the function
384
+ // Wait for useEffect to run - helper should be registered and return null
425
385
  await waitFor(() => {
426
- expect(result.current.attachment.attachments.length).toBeGreaterThan(0);
427
- }, { timeout: 2000 });
428
- // Try to get contexts, which will trigger the error
429
- await result.current.helpers.getAdditionalContext();
430
- expect(errorGetData).toHaveBeenCalled();
431
- expect(consoleErrorSpy).toHaveBeenCalled();
432
- consoleErrorSpy.mockRestore();
433
- });
434
- });
435
- describe("Custom Suggestions", () => {
436
- /**
437
- * Should start with null custom suggestions
438
- */
439
- it("should initialize with null custom suggestions", () => {
440
- const { result } = renderHook(() => useTamboContextAttachment(), {
441
- wrapper: createWrapper(),
442
- });
443
- expect(result.current.customSuggestions).toBeNull();
444
- });
445
- /**
446
- * Should set custom suggestions
447
- */
448
- it("should set custom suggestions", () => {
449
- const { result } = renderHook(() => useTamboContextAttachment(), {
450
- wrapper: createWrapper(),
451
- });
452
- const suggestions = [
453
- {
454
- id: "1",
455
- title: "Edit component",
456
- detailedSuggestion: "Modify the Button component",
457
- messageId: "",
458
- },
459
- {
460
- id: "2",
461
- title: "Add feature",
462
- detailedSuggestion: "Add a new feature",
463
- messageId: "",
464
- },
465
- ];
466
- act(() => {
467
- result.current.setCustomSuggestions(suggestions);
468
- });
469
- expect(result.current.customSuggestions).toEqual(suggestions);
470
- });
471
- /**
472
- * Should clear custom suggestions by setting to null
473
- */
474
- it("should clear custom suggestions", () => {
475
- const { result } = renderHook(() => useTamboContextAttachment(), {
476
- wrapper: createWrapper(),
477
- });
478
- const suggestions = [
479
- {
480
- id: "1",
481
- title: "Test",
482
- detailedSuggestion: "Test suggestion",
483
- messageId: "",
484
- },
485
- ];
486
- act(() => {
487
- result.current.setCustomSuggestions(suggestions);
488
- });
489
- expect(result.current.customSuggestions).toEqual(suggestions);
490
- act(() => {
491
- result.current.setCustomSuggestions(null);
492
- });
493
- expect(result.current.customSuggestions).toBeNull();
494
- });
495
- /**
496
- * Should update custom suggestions when changed
497
- */
498
- it("should update custom suggestions", () => {
499
- const { result } = renderHook(() => useTamboContextAttachment(), {
500
- wrapper: createWrapper(),
501
- });
502
- const firstSuggestions = [
503
- {
504
- id: "1",
505
- title: "First",
506
- detailedSuggestion: "First suggestion",
507
- messageId: "",
508
- },
509
- ];
510
- const secondSuggestions = [
511
- {
512
- id: "2",
513
- title: "Second",
514
- detailedSuggestion: "Second suggestion",
515
- messageId: "",
516
- },
517
- ];
518
- act(() => {
519
- result.current.setCustomSuggestions(firstSuggestions);
520
- });
521
- expect(result.current.customSuggestions).toEqual(firstSuggestions);
522
- act(() => {
523
- result.current.setCustomSuggestions(secondSuggestions);
524
- });
525
- expect(result.current.customSuggestions).toEqual(secondSuggestions);
526
- });
527
- });
528
- describe("Combined Workflows", () => {
529
- /**
530
- * Should handle adding attachment and setting custom suggestions together
531
- */
532
- it("should handle attachment and custom suggestions together", () => {
533
- const { result } = renderHook(() => useTamboContextAttachment(), {
534
- wrapper: createWrapper(),
535
- });
536
- const suggestions = [
537
- {
538
- id: "1",
539
- title: "Edit file",
540
- detailedSuggestion: "Edit this file",
541
- messageId: "",
542
- },
543
- ];
544
- act(() => {
545
- result.current.addContextAttachment({
546
- name: "Button.tsx",
547
- metadata: { filePath: "/src/Button.tsx" },
548
- });
549
- result.current.setCustomSuggestions(suggestions);
550
- });
551
- expect(result.current.attachments).toHaveLength(1);
552
- expect(result.current.customSuggestions).toEqual(suggestions);
553
- });
554
- /**
555
- * Should clear suggestions when clearing attachments
556
- */
557
- it("should independently manage attachments and suggestions", () => {
558
- const { result } = renderHook(() => useTamboContextAttachment(), {
559
- wrapper: createWrapper(),
560
- });
561
- const suggestions = [
562
- {
563
- id: "1",
564
- title: "Test",
565
- detailedSuggestion: "Test suggestion",
566
- messageId: "",
567
- },
568
- ];
569
- act(() => {
570
- result.current.addContextAttachment({ name: "File.tsx" });
571
- result.current.setCustomSuggestions(suggestions);
572
- });
573
- // Clear attachments
574
- act(() => {
575
- result.current.clearContextAttachments();
576
- });
577
- // Suggestions should remain
578
- expect(result.current.attachments).toHaveLength(0);
579
- expect(result.current.customSuggestions).toEqual(suggestions);
580
- // Can clear suggestions separately
581
- act(() => {
582
- result.current.setCustomSuggestions(null);
583
- });
584
- expect(result.current.customSuggestions).toBeNull();
585
- });
586
- });
587
- describe("Cleanup", () => {
588
- /**
589
- * Should cleanup context helpers on unmount
590
- */
591
- it("should cleanup context helpers on unmount", async () => {
592
- // Use a shared provider wrapper so context helpers persist
593
- const SharedWrapper = ({ children }) => (React.createElement(TamboContextHelpersProvider, null, children));
594
- // First render with attachment provider
595
- const { result: attachmentResult, unmount } = renderHook(() => ({
596
- attachment: useTamboContextAttachment(),
597
- helpers: useTamboContextHelpers(),
598
- }), {
599
- wrapper: ({ children }) => (React.createElement(SharedWrapper, null,
600
- React.createElement(TamboContextAttachmentProvider, null, children))),
601
- });
602
- // Add attachment
603
- act(() => {
604
- attachmentResult.current.attachment.addContextAttachment({
605
- name: "Button.tsx",
606
- });
386
+ expect(mockAddContextHelper).toHaveBeenCalledWith(CONTEXT_ATTACHMENTS_HELPER_KEY, expect.any(Function));
607
387
  });
608
- const attachmentId = attachmentResult.current.attachment.attachments[0].id;
609
- // Wait for context helper to be registered
610
- await waitFor(async () => {
611
- const contexts = await attachmentResult.current.helpers.getAdditionalContext();
612
- const hasContext = contexts.some((c) => c.name.includes(attachmentId));
613
- expect(hasContext).toBe(true);
614
- });
615
- // Unmount the attachment provider
616
- unmount();
617
- // Create new hook to check cleanup
618
- const { result: helpersResult } = renderHook(() => useTamboContextHelpers(), { wrapper: SharedWrapper });
619
- // Wait a bit for cleanup effect to run
620
- await new Promise((resolve) => setTimeout(resolve, 100));
621
- const contexts = await helpersResult.current.getAdditionalContext();
622
- // Should not contain the attachment context after unmount
623
- const hasAttachmentContext = contexts.some((c) => c.name.includes(attachmentId));
624
- expect(hasAttachmentContext).toBe(false);
388
+ const helperFn = mockAddContextHelper.mock.calls.find((call) => call[0] === CONTEXT_ATTACHMENTS_HELPER_KEY)?.[1];
389
+ expect(helperFn).toBeDefined();
390
+ expect(helperFn()).toBeNull();
625
391
  });
626
392
  });
627
393
  });