@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
package/README.md CHANGED
@@ -241,20 +241,18 @@ const InteractableNote = withInteractable(Note, {
241
241
  Connect to Linear, Slack, databases, or your own MCP servers. Tambo supports the full MCP protocol: tools, prompts, elicitations, and sampling.
242
242
 
243
243
  ```tsx
244
- import { TamboMcpProvider, MCPTransport } from "@tambo-ai/react/mcp";
244
+ import { MCPTransport } from "@tambo-ai/react/mcp";
245
245
 
246
246
  const mcpServers = [
247
247
  {
248
248
  name: "filesystem",
249
- url: "http://localhost:3001/mcp",
249
+ url: "http://localhost:8261/mcp",
250
250
  transport: MCPTransport.HTTP,
251
251
  },
252
252
  ];
253
253
 
254
254
  <TamboProvider components={components} mcpServers={mcpServers}>
255
- <TamboMcpProvider>
256
- <App />
257
- </TamboMcpProvider>
255
+ <App />
258
256
  </TamboProvider>;
259
257
  ```
260
258
 
@@ -13,11 +13,17 @@ describe("Context Helpers (prebuilt functions)", () => {
13
13
  const context = (0, index_1.currentTimeContextHelper)();
14
14
  // Should not be null (error case)
15
15
  expect(context).not.toBeNull();
16
+ // Type guard: ensure context is an object
17
+ expect(typeof context).toBe("object");
18
+ if (typeof context !== "object" || context === null) {
19
+ throw new Error("Expected context to be a non-null object");
20
+ }
16
21
  // Shape: { timestamp: string }
17
22
  expect(context).toHaveProperty("timestamp");
18
- expect(typeof context.timestamp).toBe("string");
23
+ const contextObj = context;
24
+ expect(typeof contextObj.timestamp).toBe("string");
19
25
  // Verify timestamp string parses
20
- expect(() => new Date(context.timestamp)).not.toThrow();
26
+ expect(() => new Date(contextObj.timestamp)).not.toThrow();
21
27
  });
22
28
  });
23
29
  describe("currentPageContextHelper", () => {
@@ -28,11 +34,17 @@ describe("Context Helpers (prebuilt functions)", () => {
28
34
  expect(context).toBeNull();
29
35
  return;
30
36
  }
37
+ // Type guard: ensure context is an object
38
+ expect(typeof context).toBe("object");
39
+ if (typeof context !== "object") {
40
+ throw new Error("Expected context to be an object");
41
+ }
31
42
  // Shape: { url: string, title: string }
32
43
  expect(context).toHaveProperty("url");
33
44
  expect(context).toHaveProperty("title");
34
- expect(typeof context.url).toBe("string");
35
- expect(typeof context.title).toBe("string");
45
+ const contextObj = context;
46
+ expect(typeof contextObj.url).toBe("string");
47
+ expect(typeof contextObj.title).toBe("string");
36
48
  });
37
49
  });
38
50
  });
@@ -1 +1 @@
1
- {"version":3,"file":"context-helpers.test.js","sourceRoot":"","sources":["../../src/context-helpers/context-helpers.test.ts"],"names":[],"mappings":";;AAAA,mCAA6E;AAE7E;;;;;GAKG;AACH,QAAQ,CAAC,sCAAsC,EAAE,GAAG,EAAE;IACpD,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACxC,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC9D,MAAM,OAAO,GAAG,IAAA,gCAAwB,GAAE,CAAC;YAE3C,kCAAkC;YAClC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAE/B,+BAA+B;YAC/B,MAAM,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;YAC5C,MAAM,CAAC,OAAO,OAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAEjD,iCAAiC;YACjC,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,OAAQ,CAAC,SAAmB,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACrE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACxC,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;YAClE,MAAM,OAAO,GAAG,IAAA,gCAAwB,GAAE,CAAC;YAE3C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACrB,sDAAsD;gBACtD,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC3B,OAAO;YACT,CAAC;YAED,wCAAwC;YACxC,MAAM,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;YACtC,MAAM,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YAExC,MAAM,CAAC,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC1C,MAAM,CAAC,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { currentPageContextHelper, currentTimeContextHelper } from \"./index\";\n\n/**\n * Tests for prebuilt context helper functions.\n *\n * These helpers now return raw values (or null) instead of { name, context }.\n * The provider is responsible for wrapping values as { name, context }.\n */\ndescribe(\"Context Helpers (prebuilt functions)\", () => {\n describe(\"currentTimeContextHelper\", () => {\n it(\"should return user time context with required fields\", () => {\n const context = currentTimeContextHelper();\n\n // Should not be null (error case)\n expect(context).not.toBeNull();\n\n // Shape: { timestamp: string }\n expect(context).toHaveProperty(\"timestamp\");\n expect(typeof context!.timestamp).toBe(\"string\");\n\n // Verify timestamp string parses\n expect(() => new Date(context!.timestamp as string)).not.toThrow();\n });\n });\n\n describe(\"currentPageContextHelper\", () => {\n it(\"should return page context in browser, or null otherwise\", () => {\n const context = currentPageContextHelper();\n\n if (context === null) {\n // Non-browser environments should return null to skip\n expect(context).toBeNull();\n return;\n }\n\n // Shape: { url: string, title: string }\n expect(context).toHaveProperty(\"url\");\n expect(context).toHaveProperty(\"title\");\n\n expect(typeof context.url).toBe(\"string\");\n expect(typeof context.title).toBe(\"string\");\n });\n });\n});\n"]}
1
+ {"version":3,"file":"context-helpers.test.js","sourceRoot":"","sources":["../../src/context-helpers/context-helpers.test.ts"],"names":[],"mappings":";;AAAA,mCAA6E;AAE7E;;;;;GAKG;AACH,QAAQ,CAAC,sCAAsC,EAAE,GAAG,EAAE;IACpD,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACxC,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC9D,MAAM,OAAO,GAAG,IAAA,gCAAwB,GAAE,CAAC;YAE3C,kCAAkC;YAClC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YAE/B,0CAA0C;YAC1C,MAAM,CAAC,OAAO,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACpD,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAC9D,CAAC;YAED,+BAA+B;YAC/B,MAAM,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;YAC5C,MAAM,UAAU,GAAG,OAAkC,CAAC;YACtD,MAAM,CAAC,OAAO,UAAU,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAEnD,iCAAiC;YACjC,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,SAAmB,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACvE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACxC,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;YAClE,MAAM,OAAO,GAAG,IAAA,gCAAwB,GAAE,CAAC;YAE3C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACrB,sDAAsD;gBACtD,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC3B,OAAO;YACT,CAAC;YAED,0CAA0C;YAC1C,MAAM,CAAC,OAAO,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;YACtD,CAAC;YAED,wCAAwC;YACxC,MAAM,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;YACtC,MAAM,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YAExC,MAAM,UAAU,GAAG,OAAkC,CAAC;YACtD,MAAM,CAAC,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7C,MAAM,CAAC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { currentPageContextHelper, currentTimeContextHelper } from \"./index\";\n\n/**\n * Tests for prebuilt context helper functions.\n *\n * These helpers now return raw values (or null) instead of { name, context }.\n * The provider is responsible for wrapping values as { name, context }.\n */\ndescribe(\"Context Helpers (prebuilt functions)\", () => {\n describe(\"currentTimeContextHelper\", () => {\n it(\"should return user time context with required fields\", () => {\n const context = currentTimeContextHelper();\n\n // Should not be null (error case)\n expect(context).not.toBeNull();\n\n // Type guard: ensure context is an object\n expect(typeof context).toBe(\"object\");\n if (typeof context !== \"object\" || context === null) {\n throw new Error(\"Expected context to be a non-null object\");\n }\n\n // Shape: { timestamp: string }\n expect(context).toHaveProperty(\"timestamp\");\n const contextObj = context as Record<string, unknown>;\n expect(typeof contextObj.timestamp).toBe(\"string\");\n\n // Verify timestamp string parses\n expect(() => new Date(contextObj.timestamp as string)).not.toThrow();\n });\n });\n\n describe(\"currentPageContextHelper\", () => {\n it(\"should return page context in browser, or null otherwise\", () => {\n const context = currentPageContextHelper();\n\n if (context === null) {\n // Non-browser environments should return null to skip\n expect(context).toBeNull();\n return;\n }\n\n // Type guard: ensure context is an object\n expect(typeof context).toBe(\"object\");\n if (typeof context !== \"object\") {\n throw new Error(\"Expected context to be an object\");\n }\n\n // Shape: { url: string, title: string }\n expect(context).toHaveProperty(\"url\");\n expect(context).toHaveProperty(\"title\");\n\n const contextObj = context as Record<string, unknown>;\n expect(typeof contextObj.url).toBe(\"string\");\n expect(typeof contextObj.title).toBe(\"string\");\n });\n });\n});\n"]}
@@ -22,7 +22,7 @@ export declare const currentInteractablesContextHelper: ContextHelperFn;
22
22
  * Creates an interactables context helper with access to the current components.
23
23
  * This is used internally by TamboInteractableProvider.
24
24
  * @param components Array of interactable components
25
- * @returns Context helper function
25
+ * @returns A context helper function that returns component metadata or null if no components exist
26
26
  */
27
- export declare const createInteractablesContextHelper: (components: any[]) => ContextHelperFn;
27
+ export declare const createInteractablesContextHelper: (components: unknown[]) => ContextHelperFn;
28
28
  //# sourceMappingURL=current-interactables-context-helper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"current-interactables-context-helper.d.ts","sourceRoot":"","sources":["../../src/context-helpers/current-interactables-context-helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAI/C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC3C,YAAY,GAAG,EAAE,KAChB,eAqBF,CAAC"}
1
+ {"version":3,"file":"current-interactables-context-helper.d.ts","sourceRoot":"","sources":["../../src/context-helpers/current-interactables-context-helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAI/C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC3C,YAAY,OAAO,EAAE,KACpB,eAyCF,CAAC"}
@@ -29,7 +29,7 @@ exports.currentInteractablesContextHelper = currentInteractablesContextHelper;
29
29
  * Creates an interactables context helper with access to the current components.
30
30
  * This is used internally by TamboInteractableProvider.
31
31
  * @param components Array of interactable components
32
- * @returns Context helper function
32
+ * @returns A context helper function that returns component metadata or null if no components exist
33
33
  */
34
34
  const createInteractablesContextHelper = (components) => {
35
35
  return () => {
@@ -37,17 +37,36 @@ const createInteractablesContextHelper = (components) => {
37
37
  return null; // No interactable components on the page
38
38
  }
39
39
  return {
40
- description: "These are the interactable components currently visible on the page that you can read and modify. Each component has an id, componentName, current props, current state,and optional schema. You can use tools to update these components on behalf of the user. Don't tell the user the ID of the components, only the name, unless they ask for it.",
41
- components: components.map((component) => ({
42
- id: component.id,
43
- componentName: component.name,
44
- description: component.description,
45
- props: component.props,
46
- propsSchema: component.propsSchema
47
- ? "Available - use component-specific update tools"
48
- : "Not specified",
49
- state: component.state,
50
- })),
40
+ components: components.map((component) => {
41
+ // Type guard: ensure component is an object with the expected properties
42
+ if (typeof component !== "object" || component === null) {
43
+ return {
44
+ id: "unknown",
45
+ componentName: "unknown",
46
+ description: "invalid component",
47
+ props: undefined,
48
+ propsSchema: "Not specified",
49
+ state: undefined,
50
+ isSelectedForInteraction: false,
51
+ stateSchema: "Not specified",
52
+ };
53
+ }
54
+ const comp = component;
55
+ return {
56
+ id: String(comp.id ?? "unknown"),
57
+ componentName: String(comp.name ?? "unknown"),
58
+ description: String(comp.description ?? ""),
59
+ props: comp.props,
60
+ propsSchema: comp.propsSchema
61
+ ? "Available - use component-specific update tools"
62
+ : "Not specified",
63
+ state: comp.state,
64
+ isSelectedForInteraction: comp.isSelectedForInteraction ?? false,
65
+ stateSchema: comp.stateSchema
66
+ ? "Available - use component-specific update tools"
67
+ : "Not specified",
68
+ };
69
+ }),
51
70
  };
52
71
  };
53
72
  };
@@ -1 +1 @@
1
- {"version":3,"file":"current-interactables-context-helper.js","sourceRoot":"","sources":["../../src/context-helpers/current-interactables-context-helper.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;GAiBG;AACI,MAAM,iCAAiC,GAAoB,GAAG,EAAE;IACrE,mFAAmF;IACnF,wEAAwE;IACxE,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAJW,QAAA,iCAAiC,qCAI5C;AAEF;;;;;GAKG;AACI,MAAM,gCAAgC,GAAG,CAC9C,UAAiB,EACA,EAAE;IACnB,OAAO,GAAG,EAAE;QACV,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC,CAAC,yCAAyC;QACxD,CAAC;QAED,OAAO;YACL,WAAW,EACT,uVAAuV;YACzV,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;gBACzC,EAAE,EAAE,SAAS,CAAC,EAAE;gBAChB,aAAa,EAAE,SAAS,CAAC,IAAI;gBAC7B,WAAW,EAAE,SAAS,CAAC,WAAW;gBAClC,KAAK,EAAE,SAAS,CAAC,KAAK;gBACtB,WAAW,EAAE,SAAS,CAAC,WAAW;oBAChC,CAAC,CAAC,iDAAiD;oBACnD,CAAC,CAAC,eAAe;gBACnB,KAAK,EAAE,SAAS,CAAC,KAAK;aACvB,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC,CAAC;AAvBW,QAAA,gCAAgC,oCAuB3C","sourcesContent":["import { ContextHelperFn } from \"./types\";\n\n/**\n * Prebuilt context helper that provides information about all interactable components currently on the page.\n * This gives the AI awareness of what components it can interact with and their current state.\n * @returns an object with description and components, or null to skip including this context.\n * To disable this helper, override it with a function that returns null:\n * @example\n * ```tsx\n * // To disable the default interactables context\n * const { addContextHelper } = useTamboContextHelpers();\n * addContextHelper(\"interactables\", () => null);\n *\n * // To customize the context\n * addContextHelper(\"interactables\", () => ({\n * description: \"Custom description\",\n * components: getCustomComponentsSubset()\n * }));\n * ```\n */\nexport const currentInteractablesContextHelper: ContextHelperFn = () => {\n // This will be provided by the interactable provider when it registers this helper\n // Since we're provider-only now, this function gets replaced at runtime\n return null;\n};\n\n/**\n * Creates an interactables context helper with access to the current components.\n * This is used internally by TamboInteractableProvider.\n * @param components Array of interactable components\n * @returns Context helper function\n */\nexport const createInteractablesContextHelper = (\n components: any[],\n): ContextHelperFn => {\n return () => {\n if (!Array.isArray(components) || components.length === 0) {\n return null; // No interactable components on the page\n }\n\n return {\n description:\n \"These are the interactable components currently visible on the page that you can read and modify. Each component has an id, componentName, current props, current state,and optional schema. You can use tools to update these components on behalf of the user. Don't tell the user the ID of the components, only the name, unless they ask for it.\",\n components: components.map((component) => ({\n id: component.id,\n componentName: component.name,\n description: component.description,\n props: component.props,\n propsSchema: component.propsSchema\n ? \"Available - use component-specific update tools\"\n : \"Not specified\",\n state: component.state,\n })),\n };\n };\n};\n"]}
1
+ {"version":3,"file":"current-interactables-context-helper.js","sourceRoot":"","sources":["../../src/context-helpers/current-interactables-context-helper.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;GAiBG;AACI,MAAM,iCAAiC,GAAoB,GAAG,EAAE;IACrE,mFAAmF;IACnF,wEAAwE;IACxE,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAJW,QAAA,iCAAiC,qCAI5C;AAEF;;;;;GAKG;AACI,MAAM,gCAAgC,GAAG,CAC9C,UAAqB,EACJ,EAAE;IACnB,OAAO,GAAG,EAAE;QACV,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC,CAAC,yCAAyC;QACxD,CAAC;QAED,OAAO;YACL,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE;gBACvC,yEAAyE;gBACzE,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;oBACxD,OAAO;wBACL,EAAE,EAAE,SAAS;wBACb,aAAa,EAAE,SAAS;wBACxB,WAAW,EAAE,mBAAmB;wBAChC,KAAK,EAAE,SAAS;wBAChB,WAAW,EAAE,eAAe;wBAC5B,KAAK,EAAE,SAAS;wBAChB,wBAAwB,EAAE,KAAK;wBAC/B,WAAW,EAAE,eAAe;qBAC7B,CAAC;gBACJ,CAAC;gBAED,MAAM,IAAI,GAAG,SAAoC,CAAC;gBAClD,OAAO;oBACL,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,SAAS,CAAC;oBAChC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC;oBAC7C,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;oBAC3C,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,WAAW,EAAE,IAAI,CAAC,WAAW;wBAC3B,CAAC,CAAC,iDAAiD;wBACnD,CAAC,CAAC,eAAe;oBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,wBAAwB,EACrB,IAAI,CAAC,wBAAgD,IAAI,KAAK;oBACjE,WAAW,EAAE,IAAI,CAAC,WAAW;wBAC3B,CAAC,CAAC,iDAAiD;wBACnD,CAAC,CAAC,eAAe;iBACpB,CAAC;YACJ,CAAC,CAAC;SACH,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC,CAAC;AA3CW,QAAA,gCAAgC,oCA2C3C","sourcesContent":["import { ContextHelperFn } from \"./types\";\n\n/**\n * Prebuilt context helper that provides information about all interactable components currently on the page.\n * This gives the AI awareness of what components it can interact with and their current state.\n * @returns an object with description and components, or null to skip including this context.\n * To disable this helper, override it with a function that returns null:\n * @example\n * ```tsx\n * // To disable the default interactables context\n * const { addContextHelper } = useTamboContextHelpers();\n * addContextHelper(\"interactables\", () => null);\n *\n * // To customize the context\n * addContextHelper(\"interactables\", () => ({\n * description: \"Custom description\",\n * components: getCustomComponentsSubset()\n * }));\n * ```\n */\nexport const currentInteractablesContextHelper: ContextHelperFn = () => {\n // This will be provided by the interactable provider when it registers this helper\n // Since we're provider-only now, this function gets replaced at runtime\n return null;\n};\n\n/**\n * Creates an interactables context helper with access to the current components.\n * This is used internally by TamboInteractableProvider.\n * @param components Array of interactable components\n * @returns A context helper function that returns component metadata or null if no components exist\n */\nexport const createInteractablesContextHelper = (\n components: unknown[],\n): ContextHelperFn => {\n return () => {\n if (!Array.isArray(components) || components.length === 0) {\n return null; // No interactable components on the page\n }\n\n return {\n components: components.map((component) => {\n // Type guard: ensure component is an object with the expected properties\n if (typeof component !== \"object\" || component === null) {\n return {\n id: \"unknown\",\n componentName: \"unknown\",\n description: \"invalid component\",\n props: undefined,\n propsSchema: \"Not specified\",\n state: undefined,\n isSelectedForInteraction: false,\n stateSchema: \"Not specified\",\n };\n }\n\n const comp = component as Record<string, unknown>;\n return {\n id: String(comp.id ?? \"unknown\"),\n componentName: String(comp.name ?? \"unknown\"),\n description: String(comp.description ?? \"\"),\n props: comp.props,\n propsSchema: comp.propsSchema\n ? \"Available - use component-specific update tools\"\n : \"Not specified\",\n state: comp.state,\n isSelectedForInteraction:\n (comp.isSelectedForInteraction as boolean | undefined) ?? false,\n stateSchema: comp.stateSchema\n ? \"Available - use component-specific update tools\"\n : \"Not specified\",\n };\n }),\n };\n };\n};\n"]}
@@ -2,13 +2,13 @@
2
2
  * Global context helpers registry.
3
3
  * Consumers can add/remove helpers and resolve additional context anywhere.
4
4
  */
5
- export type HelperFn = () => any | null | undefined | Promise<any | null | undefined>;
5
+ export type HelperFn = () => unknown | null | undefined | Promise<unknown | null | undefined>;
6
6
  /**
7
7
  * Resolve all helpers to AdditionalContext entries, skipping null/undefined and errors.
8
8
  * @returns The resolved additional context.
9
9
  */
10
10
  export declare function resolveAdditionalContext(helpers: Record<string, HelperFn>): Promise<{
11
11
  name: string;
12
- context: any;
12
+ context: unknown;
13
13
  }[]>;
14
14
  //# sourceMappingURL=registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/context-helpers/registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,QAAQ,GAAG,MACnB,GAAG,GACH,IAAI,GACJ,SAAS,GACT,OAAO,CAAC,GAAG,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAEpC;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAChC,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,EAAE,CAAC,CAkB3C"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/context-helpers/registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,QAAQ,GAAG,MACnB,OAAO,GACP,IAAI,GACJ,SAAS,GACT,OAAO,CAAC,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAExC;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAChC,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,EAAE,CAAC,CAkB/C"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/context-helpers/registry.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAYH,4DAoBC;AAxBD;;;GAGG;AACI,KAAK,UAAU,wBAAwB,CAC5C,OAAiC;IAEjC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE;QAC/B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,EAAE,EAAE,CAAC;YACzB,IAAI,KAAK,IAAI,IAAI;gBAAE,OAAO,IAAI,CAAC;YAC/B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,IAAI,GAAG,EAAE,KAAK,CAAC,CAAC;YAC9D,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,OAAO,CAAC,MAAM,CAAC,OAAO,CAAqC,CAAC;AACrE,CAAC","sourcesContent":["/**\n * Global context helpers registry.\n * Consumers can add/remove helpers and resolve additional context anywhere.\n */\n\nexport type HelperFn = () =>\n | any\n | null\n | undefined\n | Promise<any | null | undefined>;\n\n/**\n * Resolve all helpers to AdditionalContext entries, skipping null/undefined and errors.\n * @returns The resolved additional context.\n */\nexport async function resolveAdditionalContext(\n helpers: Record<string, HelperFn>,\n): Promise<{ name: string; context: any }[]> {\n const entries = Object.entries(helpers);\n if (entries.length === 0) return [];\n\n const results = await Promise.all(\n entries.map(async ([name, fn]) => {\n try {\n const value = await fn();\n if (value == null) return null;\n return { name, context: value };\n } catch (error) {\n console.error(`Error running context helper ${name}:`, error);\n return null;\n }\n }),\n );\n\n return results.filter(Boolean) as { name: string; context: any }[];\n}\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/context-helpers/registry.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAYH,4DAoBC;AAxBD;;;GAGG;AACI,KAAK,UAAU,wBAAwB,CAC5C,OAAiC;IAEjC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE;QAC/B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,EAAE,EAAE,CAAC;YACzB,IAAI,KAAK,IAAI,IAAI;gBAAE,OAAO,IAAI,CAAC;YAC/B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,IAAI,GAAG,EAAE,KAAK,CAAC,CAAC;YAC9D,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,OAAO,CAAC,MAAM,CAAC,OAAO,CAAyC,CAAC;AACzE,CAAC","sourcesContent":["/**\n * Global context helpers registry.\n * Consumers can add/remove helpers and resolve additional context anywhere.\n */\n\nexport type HelperFn = () =>\n | unknown\n | null\n | undefined\n | Promise<unknown | null | undefined>;\n\n/**\n * Resolve all helpers to AdditionalContext entries, skipping null/undefined and errors.\n * @returns The resolved additional context.\n */\nexport async function resolveAdditionalContext(\n helpers: Record<string, HelperFn>,\n): Promise<{ name: string; context: unknown }[]> {\n const entries = Object.entries(helpers);\n if (entries.length === 0) return [];\n\n const results = await Promise.all(\n entries.map(async ([name, fn]) => {\n try {\n const value = await fn();\n if (value == null) return null;\n return { name, context: value };\n } catch (error) {\n console.error(`Error running context helper ${name}:`, error);\n return null;\n }\n }),\n );\n\n return results.filter(Boolean) as { name: string; context: unknown }[];\n}\n"]}
@@ -5,13 +5,13 @@ export interface AdditionalContext {
5
5
  /** The name of the context type */
6
6
  name: string;
7
7
  /** The context data */
8
- context: any;
8
+ context: unknown;
9
9
  }
10
10
  /**
11
11
  * A context helper is a function that returns data to include in the context,
12
12
  * or null/undefined to skip including anything.
13
13
  */
14
- export type ContextHelperFn = () => any | null | undefined | Promise<any | null | undefined>;
14
+ export type ContextHelperFn = () => unknown | null | undefined | Promise<unknown | null | undefined>;
15
15
  /**
16
16
  * A collection of context helpers keyed by their context name.
17
17
  * The key becomes the AdditionalContext.name sent to the model.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/context-helpers/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,OAAO,EAAE,GAAG,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAC1B,GAAG,GACH,IAAI,GACJ,SAAS,GACT,OAAO,CAAC,GAAG,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAEpC;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/context-helpers/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAC1B,OAAO,GACP,IAAI,GACJ,SAAS,GACT,OAAO,CAAC,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAExC;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/context-helpers/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Interface for additional context that can be added to messages\n */\nexport interface AdditionalContext {\n /** The name of the context type */\n name: string;\n /** The context data */\n context: any;\n}\n\n/**\n * A context helper is a function that returns data to include in the context,\n * or null/undefined to skip including anything.\n */\nexport type ContextHelperFn = () =>\n | any\n | null\n | undefined\n | Promise<any | null | undefined>;\n\n/**\n * A collection of context helpers keyed by their context name.\n * The key becomes the AdditionalContext.name sent to the model.\n */\nexport type ContextHelpers = Record<string, ContextHelperFn>;\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/context-helpers/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Interface for additional context that can be added to messages\n */\nexport interface AdditionalContext {\n /** The name of the context type */\n name: string;\n /** The context data */\n context: unknown;\n}\n\n/**\n * A context helper is a function that returns data to include in the context,\n * or null/undefined to skip including anything.\n */\nexport type ContextHelperFn = () =>\n | unknown\n | null\n | undefined\n | Promise<unknown | null | undefined>;\n\n/**\n * A collection of context helpers keyed by their context name.\n * The key becomes the AdditionalContext.name sent to the model.\n */\nexport type ContextHelpers = Record<string, ContextHelperFn>;\n"]}
@@ -1,14 +1,47 @@
1
1
  import React from "react";
2
2
  import { SupportedSchema } from "../schema";
3
- export interface InteractableConfig {
3
+ export interface InteractableConfig<Props = Record<string, unknown>, State = Record<string, unknown>> {
4
+ /**
5
+ * The name of the component, used for identification in Tambo.
6
+ */
4
7
  componentName: string;
8
+ /**
9
+ * A brief description of the component's purpose and functionality. LLM will
10
+ * use this to understand how to interact with it.
11
+ */
5
12
  description: string;
6
- propsSchema?: SupportedSchema;
13
+ /**
14
+ * Optional schema for component props. If provided, prop updates will be
15
+ * validated against this schema.
16
+ */
17
+ propsSchema?: SupportedSchema<Props>;
18
+ /**
19
+ * Optional schema for component state. If provided, state updates will be
20
+ * validated against this schema.
21
+ */
22
+ stateSchema?: SupportedSchema<State>;
7
23
  }
24
+ /**
25
+ * Props injected by withTamboInteractable HOC. These can be passed to the wrapped
26
+ * component to customize interactable behavior.
27
+ */
8
28
  export interface WithTamboInteractableProps {
29
+ /**
30
+ * Optional ID to use for this interactable component instance.
31
+ * If not provided, a unique ID will be generated automatically.
32
+ */
9
33
  interactableId?: string;
34
+ /**
35
+ * Callback fired when the component has been registered as interactable.
36
+ * @param id - The assigned interactable component ID
37
+ */
10
38
  onInteractableReady?: (id: string) => void;
11
- onPropsUpdate?: (newProps: Record<string, any>) => void;
39
+ /**
40
+ * Callback fired when the component's serializable props are updated by Tambo
41
+ * through a tool call. Note: Only serializable props are tracked.
42
+ * @param newProps - The updated serializable props
43
+ */
44
+ onPropsUpdate?: (newProps: Record<string, unknown>) => void;
12
45
  }
13
46
  /**
14
47
  * Higher-Order Component that makes any component interactable by tambo.
@@ -17,6 +50,16 @@ export interface WithTamboInteractableProps {
17
50
  * @returns A new component that is automatically registered as interactable
18
51
  * @example
19
52
  * ```tsx
53
+ * const MyNote: React.FC<{ title: string; content: string }> = ({ title, content }) => {
54
+ * const [isPinned, setIsPinned] = useTamboComponentState("isPinned", false);
55
+ * return (
56
+ * <div style={{ border: isPinned ? "2px solid gold" : "1px solid gray", order: isPinned ? -1 : 0 }}>
57
+ * <h2>{title}</h2>
58
+ * <p>{content}</p>
59
+ * </div>
60
+ * );
61
+ * };
62
+ *
20
63
  * const MyInteractableNote = withTamboInteractable(MyNote, {
21
64
  * componentName: "MyNote",
22
65
  * description: "A note component",
@@ -24,11 +67,14 @@ export interface WithTamboInteractableProps {
24
67
  * title: z.string(),
25
68
  * content: z.string(),
26
69
  * }),
70
+ * stateSchema: z.object({
71
+ * isPinned: z.boolean(),
72
+ * }),
27
73
  * });
28
74
  *
29
75
  * // Usage
30
76
  * <MyInteractableNote title="My Note" content="This is my note" />
31
77
  * ```
32
78
  */
33
- export declare function withTamboInteractable<P extends object>(WrappedComponent: React.ComponentType<P>, config: InteractableConfig): React.FC<P & WithTamboInteractableProps>;
79
+ export declare function withTamboInteractable<ComponentProps extends object>(WrappedComponent: React.ComponentType<ComponentProps>, config: InteractableConfig): React.FC<ComponentProps & WithTamboInteractableProps>;
34
80
  //# sourceMappingURL=with-tambo-interactable.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"with-tambo-interactable.d.ts","sourceRoot":"","sources":["../../src/hoc/with-tambo-interactable.tsx"],"names":[],"mappings":"AACA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAIxE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED,MAAM,WAAW,0BAA0B;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,EACpD,gBAAgB,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EACxC,MAAM,EAAE,kBAAkB,4CAgH3B"}
1
+ {"version":3,"file":"with-tambo-interactable.d.ts","sourceRoot":"","sources":["../../src/hoc/with-tambo-interactable.tsx"],"names":[],"mappings":"AACA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAIxE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,MAAM,WAAW,kBAAkB,CACjC,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAE/B;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;IACrC;;;OAGG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;CACtC;AAED;;;GAGG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CAC7D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,qBAAqB,CAAC,cAAc,SAAS,MAAM,EACjE,gBAAgB,EAAE,KAAK,CAAC,aAAa,CAAC,cAAc,CAAC,EACrD,MAAM,EAAE,kBAAkB,yDAsH3B"}
@@ -45,6 +45,16 @@ const tambo_interactable_provider_1 = require("../providers/tambo-interactable-p
45
45
  * @returns A new component that is automatically registered as interactable
46
46
  * @example
47
47
  * ```tsx
48
+ * const MyNote: React.FC<{ title: string; content: string }> = ({ title, content }) => {
49
+ * const [isPinned, setIsPinned] = useTamboComponentState("isPinned", false);
50
+ * return (
51
+ * <div style={{ border: isPinned ? "2px solid gold" : "1px solid gray", order: isPinned ? -1 : 0 }}>
52
+ * <h2>{title}</h2>
53
+ * <p>{content}</p>
54
+ * </div>
55
+ * );
56
+ * };
57
+ *
48
58
  * const MyInteractableNote = withTamboInteractable(MyNote, {
49
59
  * componentName: "MyNote",
50
60
  * description: "A note component",
@@ -52,6 +62,9 @@ const tambo_interactable_provider_1 = require("../providers/tambo-interactable-p
52
62
  * title: z.string(),
53
63
  * content: z.string(),
54
64
  * }),
65
+ * stateSchema: z.object({
66
+ * isPinned: z.boolean(),
67
+ * }),
55
68
  * });
56
69
  *
57
70
  * // Usage
@@ -64,9 +77,10 @@ function withTamboInteractable(WrappedComponent, config) {
64
77
  const { addInteractableComponent, updateInteractableComponentProps, getInteractableComponent, } = (0, tambo_interactable_provider_1.useTamboInteractable)();
65
78
  const [interactableId, setInteractableId] = (0, react_1.useState)(null);
66
79
  const isInitialized = (0, react_1.useRef)(false);
67
- const lastParentProps = (0, react_1.useRef)({});
68
- // Extract interactable-specific props
69
- const { onInteractableReady, onPropsUpdate, ...componentProps } = props;
80
+ const lastSerializedProps = (0, react_1.useRef)({});
81
+ // Extract interactable-specific props from component props
82
+ const { interactableId: _providedId, // Reserved for future use
83
+ onInteractableReady, onPropsUpdate, ...componentProps } = props;
70
84
  // Get the current interactable component to track prop updates
71
85
  const currentInteractable = interactableId
72
86
  ? getInteractableComponent(interactableId)
@@ -83,6 +97,7 @@ function withTamboInteractable(WrappedComponent, config) {
83
97
  component: WrappedComponent,
84
98
  props: componentProps,
85
99
  propsSchema: config.propsSchema,
100
+ stateSchema: config.stateSchema,
86
101
  });
87
102
  setInteractableId(id);
88
103
  onInteractableReady?.(id);
@@ -97,12 +112,12 @@ function withTamboInteractable(WrappedComponent, config) {
97
112
  (0, react_1.useEffect)(() => {
98
113
  if (interactableId && isInitialized.current) {
99
114
  // Only update if the props are different from what we last sent
100
- const lastPropsString = JSON.stringify(lastParentProps.current);
115
+ const lastPropsString = JSON.stringify(lastSerializedProps.current);
101
116
  const currentPropsString = JSON.stringify(componentProps);
102
117
  if (lastPropsString !== currentPropsString) {
103
118
  updateInteractableComponentProps(interactableId, componentProps);
104
119
  onPropsUpdate?.(componentProps);
105
- lastParentProps.current = componentProps;
120
+ lastSerializedProps.current = componentProps;
106
121
  }
107
122
  }
108
123
  }, [
@@ -1 +1 @@
1
- {"version":3,"file":"with-tambo-interactable.js","sourceRoot":"","sources":["../../src/hoc/with-tambo-interactable.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCb,sDAkHC;AAxJD,+CAAwE;AACxE,sEAAoE;AAEpE,0FAAgF;AAehF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,qBAAqB,CACnC,gBAAwC,EACxC,MAA0B;IAE1B,MAAM,WAAW,GACf,gBAAgB,CAAC,WAAW,IAAI,gBAAgB,CAAC,IAAI,IAAI,WAAW,CAAC;IAEvE,MAAM,wBAAwB,GAA6C,CACzE,KAAK,EACL,EAAE;QACF,MAAM,EACJ,wBAAwB,EACxB,gCAAgC,EAChC,wBAAwB,GACzB,GAAG,IAAA,kDAAoB,GAAE,CAAC;QAE3B,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,IAAA,gBAAQ,EAAgB,IAAI,CAAC,CAAC;QAC1E,MAAM,aAAa,GAAG,IAAA,cAAM,EAAC,KAAK,CAAC,CAAC;QACpC,MAAM,eAAe,GAAG,IAAA,cAAM,EAAsB,EAAE,CAAC,CAAC;QAExD,sCAAsC;QACtC,MAAM,EAAE,mBAAmB,EAAE,aAAa,EAAE,GAAG,cAAc,EAAE,GAAG,KAAK,CAAC;QAExE,+DAA+D;QAC/D,MAAM,mBAAmB,GAAG,cAAc;YACxC,CAAC,CAAC,wBAAwB,CAAC,cAAc,CAAC;YAC1C,CAAC,CAAC,IAAI,CAAC;QAET,6FAA6F;QAC7F,iHAAiH;QACjH,MAAM,cAAc,GAAG,mBAAmB,EAAE,KAAK,IAAI,cAAc,CAAC;QAEpE,oCAAoC;QACpC,MAAM,iBAAiB,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;YACzC,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;gBAC3B,MAAM,EAAE,GAAG,wBAAwB,CAAC;oBAClC,IAAI,EAAE,MAAM,CAAC,aAAa;oBAC1B,WAAW,EAAE,MAAM,CAAC,WAAW;oBAC/B,SAAS,EAAE,gBAAgB;oBAC3B,KAAK,EAAE,cAAc;oBACrB,WAAW,EAAE,MAAM,CAAC,WAAW;iBAChC,CAAC,CAAC;gBAEH,iBAAiB,CAAC,EAAE,CAAC,CAAC;gBACtB,mBAAmB,EAAE,CAAC,EAAE,CAAC,CAAC;gBAC1B,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;YAC/B,CAAC;QACH,CAAC,EAAE,CAAC,wBAAwB,EAAE,cAAc,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAEpE,8DAA8D;QAC9D,IAAA,iBAAS,EAAC,GAAG,EAAE;YACb,iBAAiB,EAAE,CAAC;QACtB,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC;QAExB,kEAAkE;QAClE,IAAA,iBAAS,EAAC,GAAG,EAAE;YACb,IAAI,cAAc,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;gBAC5C,gEAAgE;gBAChE,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;gBAChE,MAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;gBAE1D,IAAI,eAAe,KAAK,kBAAkB,EAAE,CAAC;oBAC3C,gCAAgC,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;oBACjE,aAAa,EAAE,CAAC,cAAc,CAAC,CAAC;oBAChC,eAAe,CAAC,OAAO,GAAG,cAAc,CAAC;gBAC3C,CAAC;YACH,CAAC;QACH,CAAC,EAAE;YACD,cAAc;YACd,cAAc;YACd,gCAAgC;YAChC,aAAa;SACd,CAAC,CAAC;QAEH,+EAA+E;QAC/E,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,OAAO,8BAAC,gBAAgB,OAAM,cAAoB,GAAI,CAAC;QACzD,CAAC;QAED,sDAAsD;QACtD,uFAAuF;QACvF,MAAM,cAAc,GAAuB;YACzC,EAAE,EAAE,cAAc;YAClB,IAAI,EAAE,WAAoB;YAC1B,OAAO,EAAE,EAAE;YACX,QAAQ,EAAE,EAAE;YACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,SAAS,EAAE;gBACT,aAAa,EAAE,MAAM,CAAC,aAAa;gBACnC,cAAc,EAAE,EAAE;gBAClB,OAAO,EAAE,EAAE;gBACX,KAAK,EAAE,cAAc;aACtB;YACD,cAAc,EAAE,EAAE;SACnB,CAAC;QAEF,iEAAiE;QACjE,OAAO,CACL,8BAAC,0CAAoB,IACnB,OAAO,EAAE,cAAc,EACvB,oBAAoB,EAAE;gBACpB,EAAE,EAAE,cAAc;gBAClB,aAAa,EAAE,MAAM,CAAC,aAAa;gBACnC,WAAW,EAAE,MAAM,CAAC,WAAW;aAChC;YAED,8BAAC,gBAAgB,OAAM,cAAoB,GAAI,CAC1B,CACxB,CAAC;IACJ,CAAC,CAAC;IAEF,wBAAwB,CAAC,WAAW,GAAG,yBAAyB,WAAW,GAAG,CAAC;IAE/E,OAAO,wBAAwB,CAAC;AAClC,CAAC","sourcesContent":["\"use client\";\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { TamboMessageProvider } from \"../hooks/use-current-message\";\nimport { TamboThreadMessage } from \"../model/generate-component-response\";\nimport { useTamboInteractable } from \"../providers/tambo-interactable-provider\";\nimport { SupportedSchema } from \"../schema\";\n\nexport interface InteractableConfig {\n componentName: string;\n description: string;\n propsSchema?: SupportedSchema;\n}\n\nexport interface WithTamboInteractableProps {\n interactableId?: string;\n onInteractableReady?: (id: string) => void;\n onPropsUpdate?: (newProps: Record<string, any>) => void;\n}\n\n/**\n * Higher-Order Component that makes any component interactable by tambo.\n * @param WrappedComponent - The component to make interactable\n * @param config - Configuration for the interactable component\n * @returns A new component that is automatically registered as interactable\n * @example\n * ```tsx\n * const MyInteractableNote = withTamboInteractable(MyNote, {\n * componentName: \"MyNote\",\n * description: \"A note component\",\n * propsSchema: z.object({\n * title: z.string(),\n * content: z.string(),\n * }),\n * });\n *\n * // Usage\n * <MyInteractableNote title=\"My Note\" content=\"This is my note\" />\n * ```\n */\nexport function withTamboInteractable<P extends object>(\n WrappedComponent: React.ComponentType<P>,\n config: InteractableConfig,\n) {\n const displayName =\n WrappedComponent.displayName ?? WrappedComponent.name ?? \"Component\";\n\n const TamboInteractableWrapper: React.FC<P & WithTamboInteractableProps> = (\n props,\n ) => {\n const {\n addInteractableComponent,\n updateInteractableComponentProps,\n getInteractableComponent,\n } = useTamboInteractable();\n\n const [interactableId, setInteractableId] = useState<string | null>(null);\n const isInitialized = useRef(false);\n const lastParentProps = useRef<Record<string, any>>({});\n\n // Extract interactable-specific props\n const { onInteractableReady, onPropsUpdate, ...componentProps } = props;\n\n // Get the current interactable component to track prop updates\n const currentInteractable = interactableId\n ? getInteractableComponent(interactableId)\n : null;\n\n // Use the props from the interactable component if available, otherwise use the passed props\n // We need to be careful not to create a loop, so we only use stored props if they're different from passed props\n const effectiveProps = currentInteractable?.props ?? componentProps;\n\n // Memoize the registration function\n const registerComponent = useCallback(() => {\n if (!isInitialized.current) {\n const id = addInteractableComponent({\n name: config.componentName,\n description: config.description,\n component: WrappedComponent,\n props: componentProps,\n propsSchema: config.propsSchema,\n });\n\n setInteractableId(id);\n onInteractableReady?.(id);\n isInitialized.current = true;\n }\n }, [addInteractableComponent, componentProps, onInteractableReady]);\n\n // Register the component as interactable on mount (only once)\n useEffect(() => {\n registerComponent();\n }, [registerComponent]);\n\n // Update the interactable component when props change from parent\n useEffect(() => {\n if (interactableId && isInitialized.current) {\n // Only update if the props are different from what we last sent\n const lastPropsString = JSON.stringify(lastParentProps.current);\n const currentPropsString = JSON.stringify(componentProps);\n\n if (lastPropsString !== currentPropsString) {\n updateInteractableComponentProps(interactableId, componentProps);\n onPropsUpdate?.(componentProps);\n lastParentProps.current = componentProps;\n }\n }\n }, [\n interactableId,\n componentProps,\n updateInteractableComponentProps,\n onPropsUpdate,\n ]);\n\n // If the interactable ID is not yet set, render the component without provider\n if (!interactableId) {\n return <WrappedComponent {...(effectiveProps as P)} />;\n }\n\n // Create a minimal message with interactable metadata\n // This allows useTamboCurrentComponent to work with standalone interactable components\n const minimalMessage: TamboThreadMessage = {\n id: interactableId,\n role: \"assistant\" as const,\n content: [],\n threadId: \"\",\n createdAt: new Date().toISOString(),\n component: {\n componentName: config.componentName,\n componentState: {},\n message: \"\",\n props: effectiveProps,\n },\n componentState: {},\n };\n\n // Wrap with TamboMessageProvider including interactable metadata\n return (\n <TamboMessageProvider\n message={minimalMessage}\n interactableMetadata={{\n id: interactableId,\n componentName: config.componentName,\n description: config.description,\n }}\n >\n <WrappedComponent {...(effectiveProps as P)} />\n </TamboMessageProvider>\n );\n };\n\n TamboInteractableWrapper.displayName = `withTamboInteractable(${displayName})`;\n\n return TamboInteractableWrapper;\n}\n"]}
1
+ {"version":3,"file":"with-tambo-interactable.js","sourceRoot":"","sources":["../../src/hoc/with-tambo-interactable.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { TamboMessageProvider } from \"../hooks/use-current-message\";\nimport { TamboThreadMessage } from \"../model/generate-component-response\";\nimport { useTamboInteractable } from \"../providers/tambo-interactable-provider\";\nimport { SupportedSchema } from \"../schema\";\n\nexport interface InteractableConfig<\n Props = Record<string, unknown>,\n State = Record<string, unknown>,\n> {\n /**\n * The name of the component, used for identification in Tambo.\n */\n componentName: string;\n /**\n * A brief description of the component's purpose and functionality. LLM will\n * use this to understand how to interact with it.\n */\n description: string;\n /**\n * Optional schema for component props. If provided, prop updates will be\n * validated against this schema.\n */\n propsSchema?: SupportedSchema<Props>;\n /**\n * Optional schema for component state. If provided, state updates will be\n * validated against this schema.\n */\n stateSchema?: SupportedSchema<State>;\n}\n\n/**\n * Props injected by withTamboInteractable HOC. These can be passed to the wrapped\n * component to customize interactable behavior.\n */\nexport interface WithTamboInteractableProps {\n /**\n * Optional ID to use for this interactable component instance.\n * If not provided, a unique ID will be generated automatically.\n */\n interactableId?: string;\n /**\n * Callback fired when the component has been registered as interactable.\n * @param id - The assigned interactable component ID\n */\n onInteractableReady?: (id: string) => void;\n /**\n * Callback fired when the component's serializable props are updated by Tambo\n * through a tool call. Note: Only serializable props are tracked.\n * @param newProps - The updated serializable props\n */\n onPropsUpdate?: (newProps: Record<string, unknown>) => void;\n}\n\n/**\n * Higher-Order Component that makes any component interactable by tambo.\n * @param WrappedComponent - The component to make interactable\n * @param config - Configuration for the interactable component\n * @returns A new component that is automatically registered as interactable\n * @example\n * ```tsx\n * const MyNote: React.FC<{ title: string; content: string }> = ({ title, content }) => {\n * const [isPinned, setIsPinned] = useTamboComponentState(\"isPinned\", false);\n * return (\n * <div style={{ border: isPinned ? \"2px solid gold\" : \"1px solid gray\", order: isPinned ? -1 : 0 }}>\n * <h2>{title}</h2>\n * <p>{content}</p>\n * </div>\n * );\n * };\n *\n * const MyInteractableNote = withTamboInteractable(MyNote, {\n * componentName: \"MyNote\",\n * description: \"A note component\",\n * propsSchema: z.object({\n * title: z.string(),\n * content: z.string(),\n * }),\n * stateSchema: z.object({\n * isPinned: z.boolean(),\n * }),\n * });\n *\n * // Usage\n * <MyInteractableNote title=\"My Note\" content=\"This is my note\" />\n * ```\n */\nexport function withTamboInteractable<ComponentProps extends object>(\n WrappedComponent: React.ComponentType<ComponentProps>,\n config: InteractableConfig,\n) {\n const displayName =\n WrappedComponent.displayName ?? WrappedComponent.name ?? \"Component\";\n\n const TamboInteractableWrapper: React.FC<\n ComponentProps & WithTamboInteractableProps\n > = (props) => {\n const {\n addInteractableComponent,\n updateInteractableComponentProps,\n getInteractableComponent,\n } = useTamboInteractable();\n\n const [interactableId, setInteractableId] = useState<string | null>(null);\n const isInitialized = useRef(false);\n const lastSerializedProps = useRef<Record<string, unknown>>({});\n\n // Extract interactable-specific props from component props\n const {\n interactableId: _providedId, // Reserved for future use\n onInteractableReady,\n onPropsUpdate,\n ...componentProps\n } = props;\n\n // Get the current interactable component to track prop updates\n const currentInteractable = interactableId\n ? getInteractableComponent(interactableId)\n : null;\n\n // Use the props from the interactable component if available, otherwise use the passed props\n // We need to be careful not to create a loop, so we only use stored props if they're different from passed props\n const effectiveProps = currentInteractable?.props ?? componentProps;\n\n // Memoize the registration function\n const registerComponent = useCallback(() => {\n if (!isInitialized.current) {\n const id = addInteractableComponent({\n name: config.componentName,\n description: config.description,\n component: WrappedComponent,\n props: componentProps,\n propsSchema: config.propsSchema,\n stateSchema: config.stateSchema,\n });\n\n setInteractableId(id);\n onInteractableReady?.(id);\n isInitialized.current = true;\n }\n }, [addInteractableComponent, componentProps, onInteractableReady]);\n\n // Register the component as interactable on mount (only once)\n useEffect(() => {\n registerComponent();\n }, [registerComponent]);\n\n // Update the interactable component when props change from parent\n useEffect(() => {\n if (interactableId && isInitialized.current) {\n // Only update if the props are different from what we last sent\n const lastPropsString = JSON.stringify(lastSerializedProps.current);\n const currentPropsString = JSON.stringify(componentProps);\n\n if (lastPropsString !== currentPropsString) {\n updateInteractableComponentProps(interactableId, componentProps);\n onPropsUpdate?.(componentProps);\n lastSerializedProps.current = componentProps;\n }\n }\n }, [\n interactableId,\n componentProps,\n updateInteractableComponentProps,\n onPropsUpdate,\n ]);\n\n // If the interactable ID is not yet set, render the component without provider\n if (!interactableId) {\n return <WrappedComponent {...(effectiveProps as ComponentProps)} />;\n }\n\n // Create a minimal message with interactable metadata\n // This allows useTamboCurrentComponent to work with standalone interactable components\n const minimalMessage: TamboThreadMessage = {\n id: interactableId,\n role: \"assistant\" as const,\n content: [],\n threadId: \"\",\n createdAt: new Date().toISOString(),\n component: {\n componentName: config.componentName,\n componentState: {},\n message: \"\",\n props: effectiveProps,\n },\n componentState: {},\n };\n\n // Wrap with TamboMessageProvider including interactable metadata\n return (\n <TamboMessageProvider\n message={minimalMessage}\n interactableMetadata={{\n id: interactableId,\n componentName: config.componentName,\n description: config.description,\n }}\n >\n <WrappedComponent {...(effectiveProps as ComponentProps)} />\n </TamboMessageProvider>\n );\n };\n\n TamboInteractableWrapper.displayName = `withTamboInteractable(${displayName})`;\n\n return TamboInteractableWrapper;\n}\n"]}
@@ -11,11 +11,11 @@ type StateUpdateResult<T> = [currentState: T, setState: (newState: T) => void];
11
11
  * @param initialValue - Optional initial value for the state, used if no componentState value exists in the Tambo message containing this hook usage.
12
12
  * @param setFromProp - Optional value used to set the state value, only while no componentState value exists in the Tambo message containing this hook usage. Use this to allow streaming updates from a prop to the state value.
13
13
  * @param debounceTime - Optional debounce time in milliseconds (default: 500ms) to limit API calls.
14
- * @returns A tuple containing:
15
- * - The current state value
16
- * - A setter function to update the state (updates UI immediately, debounces server sync)
14
+ * @returns A tuple of [currentState, setState] similar to React's useState
17
15
  * @example
16
+ * ```tsx
18
17
  * const [count, setCount] = useTamboComponentState("counter", 0);
18
+ * ```
19
19
  *
20
20
  * Use `setFromProp` to seed state from streamed props. During streaming,
21
21
  * state updates as new prop values arrive. Once streaming completes,
@@ -23,11 +23,6 @@ type StateUpdateResult<T> = [currentState: T, setState: (newState: T) => void];
23
23
  *
24
24
  * Pair with `useTamboStreamStatus` to disable inputs while streaming.
25
25
  * @see {@link https://docs.tambo.co/concepts/streaming/streaming-best-practices}
26
- * @param keyName - Unique key within the message's componentState
27
- * @param initialValue - Default value if no componentState exists
28
- * @param setFromProp - Seeds state from props (updates during streaming, then user edits take over)
29
- * @param debounceTime - Server sync debounce in ms (default: 500)
30
- * @returns A tuple of [currentState, setState] similar to React's useState
31
26
  */
32
27
  export declare function useTamboComponentState<S = undefined>(keyName: string, initialValue?: S, setFromProp?: S, debounceTime?: number): StateUpdateResult<S | undefined>;
33
28
  export declare function useTamboComponentState<S>(keyName: string, initialValue: S, setFromProp?: S, debounceTime?: number): StateUpdateResult<S>;
@@ -1 +1 @@
1
- {"version":3,"file":"use-component-state.d.ts","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"AAOA,KAAK,iBAAiB,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,GAAG,SAAS,EAClD,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,CAAC,EAChB,WAAW,CAAC,EAAE,CAAC,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AACpC,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,EACf,WAAW,CAAC,EAAE,CAAC,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"use-component-state.d.ts","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"AAQA,KAAK,iBAAiB,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,GAAG,SAAS,EAClD,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,CAAC,EAChB,WAAW,CAAC,EAAE,CAAC,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AACpC,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,EACf,WAAW,CAAC,EAAE,CAAC,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,CAAC,CAAC,CAAC,CAAC"}
@@ -2,6 +2,7 @@
2
2
  "use client";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.useTamboComponentState = useTamboComponentState;
5
+ const fast_equals_1 = require("fast-equals");
5
6
  const react_1 = require("react");
6
7
  const use_debounce_1 = require("use-debounce");
7
8
  const __1 = require("..");
@@ -101,6 +102,13 @@ function useTamboComponentState(keyName, initialValue, setFromProp, debounceTime
101
102
  setInteractableState,
102
103
  componentId,
103
104
  ]);
105
+ // Sync from interactable provider to local state when state changes externally (e.g., from Tambo tool call)
106
+ (0, react_1.useEffect)(() => {
107
+ if (!componentId)
108
+ return;
109
+ // only update if different
110
+ setLocalState((prev) => (0, fast_equals_1.deepEqual)(prev, interactableState) ? prev : interactableState);
111
+ }, [componentId, interactableState]);
104
112
  // For editable fields that are set from a prop to allow streaming updates, don't overwrite a fetched state value set from the thread message with prop value on initial load.
105
113
  (0, react_1.useEffect)(() => {
106
114
  if (setFromProp !== undefined && !initializedFromThreadMessage) {
@@ -1 +1 @@
1
- {"version":3,"file":"use-component-state.js","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\nimport { useCallback, useContext, useEffect, useState } from \"react\";\nimport { useDebouncedCallback } from \"use-debounce\";\nimport { TamboThreadMessage, useTamboClient, useTamboThread } from \"..\";\nimport { useTamboInteractable } from \"../providers/tambo-interactable-provider\";\nimport { TamboMessageContext } from \"./use-current-message\";\n\ntype StateUpdateResult<T> = [currentState: T, setState: (newState: T) => void];\n\n/**\n * A React hook that acts like useState, but also automatically updates the thread message's componentState.\n * If used within an interactable component (wrapped with withTamboInteractable), it updates the\n * interactable provider's global state (sent to Tambo on every request) instead of the remote thread message state.\n * For generated components, it updates both the local and remote thread message's componentState.\n *\n * Benefits: Passes user changes to AI, and when threads are returned, state is preserved.\n * Works in both generative and interactable component contexts.\n * @param keyName - The unique key to identify this state value within the message's componentState object\n * @param initialValue - Optional initial value for the state, used if no componentState value exists in the Tambo message containing this hook usage.\n * @param setFromProp - Optional value used to set the state value, only while no componentState value exists in the Tambo message containing this hook usage. Use this to allow streaming updates from a prop to the state value.\n * @param debounceTime - Optional debounce time in milliseconds (default: 500ms) to limit API calls.\n * @returns A tuple containing:\n * - The current state value\n * - A setter function to update the state (updates UI immediately, debounces server sync)\n * @example\n * const [count, setCount] = useTamboComponentState(\"counter\", 0);\n *\n * Use `setFromProp` to seed state from streamed props. During streaming,\n * state updates as new prop values arrive. Once streaming completes,\n * user edits take precedence over the original prop value.\n *\n * Pair with `useTamboStreamStatus` to disable inputs while streaming.\n * @see {@link https://docs.tambo.co/concepts/streaming/streaming-best-practices}\n * @param keyName - Unique key within the message's componentState\n * @param initialValue - Default value if no componentState exists\n * @param setFromProp - Seeds state from props (updates during streaming, then user edits take over)\n * @param debounceTime - Server sync debounce in ms (default: 500)\n * @returns A tuple of [currentState, setState] similar to React's useState\n */\nexport function useTamboComponentState<S = undefined>(\n keyName: string,\n initialValue?: S,\n setFromProp?: S,\n debounceTime?: number,\n): StateUpdateResult<S | undefined>;\nexport function useTamboComponentState<S>(\n keyName: string,\n initialValue: S,\n setFromProp?: S,\n debounceTime?: number,\n): StateUpdateResult<S>;\nexport function useTamboComponentState<S>(\n keyName: string,\n initialValue?: S,\n setFromProp?: S,\n debounceTime = 500,\n): StateUpdateResult<S> {\n const message = useContext(TamboMessageContext);\n const { updateThreadMessage } = useTamboThread();\n const client = useTamboClient();\n const componentId = message?.interactableMetadata?.id ?? null;\n const { setInteractableState, getInteractableComponentState } =\n useTamboInteractable();\n const messageState = message?.componentState?.[keyName];\n const interactableState = componentId\n ? getInteractableComponentState(componentId)?.[keyName]\n : undefined;\n const initialState =\n (interactableState as S) ?? (messageState as S) ?? initialValue;\n const [localState, setLocalState] = useState<S | undefined>(initialState);\n const [initializedFromThreadMessage, setInitializedFromThreadMessage] =\n useState(messageState ? true : false);\n\n // Optimistically update the local thread message's componentState\n const updateLocalThreadMessage = useCallback(\n async (newState: S, existingMessage: TamboThreadMessage | null) => {\n if (!existingMessage) {\n return;\n }\n const updatedMessage = {\n threadId: existingMessage.threadId,\n componentState: {\n ...existingMessage.componentState,\n [keyName]: newState,\n },\n };\n await updateThreadMessage(existingMessage.id, updatedMessage, false);\n },\n [updateThreadMessage, keyName],\n );\n\n // Debounced callback to update the remote thread message's componentState\n const updateRemoteThreadMessage = useDebouncedCallback(\n async (newState: S, existingMessage: TamboThreadMessage | null) => {\n if (!existingMessage) {\n return;\n }\n await client.beta.threads.messages.updateComponentState(\n existingMessage.id,\n {\n id: existingMessage.threadId,\n state: { [keyName]: newState },\n },\n );\n },\n debounceTime,\n );\n\n const setValue = useCallback(\n (newState: S) => {\n setLocalState(newState);\n if (componentId) {\n // For interactable components, update the interactable provider's state\n setInteractableState(componentId, keyName, newState);\n } else if (message) {\n // For generated components, update both local and remote thread message state\n void updateLocalThreadMessage(newState, message);\n void updateRemoteThreadMessage(newState, message);\n }\n },\n [\n message,\n updateLocalThreadMessage,\n updateRemoteThreadMessage,\n setInteractableState,\n componentId,\n keyName,\n ],\n );\n\n const existingInteractableState = componentId\n ? getInteractableComponentState(componentId)?.[keyName]\n : undefined;\n const shouldUpdateInteractableInitial =\n !!componentId &&\n existingInteractableState === undefined &&\n initialValue !== undefined;\n\n // Set initial value in interactable state if we're in an interactable context and there's no existing state\n useEffect(() => {\n if (!shouldUpdateInteractableInitial) {\n return;\n }\n setInteractableState(componentId, keyName, initialValue!);\n }, [\n shouldUpdateInteractableInitial,\n componentId,\n keyName,\n initialValue,\n setInteractableState,\n ]);\n\n const shouldSyncFromMessage =\n !!message && messageState !== undefined && messageState !== null;\n\n // Mirror the thread message's componentState value to the local state and interactable state\n useEffect(() => {\n if (!shouldSyncFromMessage) {\n return;\n }\n setInitializedFromThreadMessage(true);\n const stateValue = messageState as S;\n setLocalState(stateValue);\n if (componentId) {\n setInteractableState(componentId, keyName, stateValue);\n }\n }, [\n shouldSyncFromMessage,\n messageState,\n keyName,\n setInteractableState,\n componentId,\n ]);\n\n // For editable fields that are set from a prop to allow streaming updates, don't overwrite a fetched state value set from the thread message with prop value on initial load.\n useEffect(() => {\n if (setFromProp !== undefined && !initializedFromThreadMessage) {\n setLocalState(setFromProp as S);\n }\n }, [setFromProp, initializedFromThreadMessage]);\n\n // Ensure pending changes are flushed on unmount (only for generated components)\n useEffect(() => {\n // Only flush remote updates for generated components, not interactable components\n if (componentId) {\n return;\n }\n return () => {\n async function flushUpdates() {\n try {\n await updateRemoteThreadMessage.flush();\n } catch (error) {\n console.error(\n \"Failed to flush pending thread message updates:\",\n error,\n );\n }\n }\n // Fire-and-forget cleanup (errors handled inside)\n void flushUpdates();\n };\n }, [updateRemoteThreadMessage, componentId]);\n\n return [localState as S, setValue];\n}\n"]}
1
+ {"version":3,"file":"use-component-state.js","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\nimport { deepEqual } from \"fast-equals\";\nimport { useCallback, useContext, useEffect, useState } from \"react\";\nimport { useDebouncedCallback } from \"use-debounce\";\nimport { TamboThreadMessage, useTamboClient, useTamboThread } from \"..\";\nimport { useTamboInteractable } from \"../providers/tambo-interactable-provider\";\nimport { TamboMessageContext } from \"./use-current-message\";\n\ntype StateUpdateResult<T> = [currentState: T, setState: (newState: T) => void];\n\n/**\n * A React hook that acts like useState, but also automatically updates the thread message's componentState.\n * If used within an interactable component (wrapped with withTamboInteractable), it updates the\n * interactable provider's global state (sent to Tambo on every request) instead of the remote thread message state.\n * For generated components, it updates both the local and remote thread message's componentState.\n *\n * Benefits: Passes user changes to AI, and when threads are returned, state is preserved.\n * Works in both generative and interactable component contexts.\n * @param keyName - The unique key to identify this state value within the message's componentState object\n * @param initialValue - Optional initial value for the state, used if no componentState value exists in the Tambo message containing this hook usage.\n * @param setFromProp - Optional value used to set the state value, only while no componentState value exists in the Tambo message containing this hook usage. Use this to allow streaming updates from a prop to the state value.\n * @param debounceTime - Optional debounce time in milliseconds (default: 500ms) to limit API calls.\n * @returns A tuple of [currentState, setState] similar to React's useState\n * @example\n * ```tsx\n * const [count, setCount] = useTamboComponentState(\"counter\", 0);\n * ```\n *\n * Use `setFromProp` to seed state from streamed props. During streaming,\n * state updates as new prop values arrive. Once streaming completes,\n * user edits take precedence over the original prop value.\n *\n * Pair with `useTamboStreamStatus` to disable inputs while streaming.\n * @see {@link https://docs.tambo.co/concepts/streaming/streaming-best-practices}\n */\nexport function useTamboComponentState<S = undefined>(\n keyName: string,\n initialValue?: S,\n setFromProp?: S,\n debounceTime?: number,\n): StateUpdateResult<S | undefined>;\nexport function useTamboComponentState<S>(\n keyName: string,\n initialValue: S,\n setFromProp?: S,\n debounceTime?: number,\n): StateUpdateResult<S>;\nexport function useTamboComponentState<S>(\n keyName: string,\n initialValue?: S,\n setFromProp?: S,\n debounceTime = 500,\n): StateUpdateResult<S> {\n const message = useContext(TamboMessageContext);\n const { updateThreadMessage } = useTamboThread();\n const client = useTamboClient();\n const componentId = message?.interactableMetadata?.id ?? null;\n const { setInteractableState, getInteractableComponentState } =\n useTamboInteractable();\n const messageState = message?.componentState?.[keyName];\n const interactableState = componentId\n ? getInteractableComponentState(componentId)?.[keyName]\n : undefined;\n const initialState =\n (interactableState as S) ?? (messageState as S) ?? initialValue;\n const [localState, setLocalState] = useState<S | undefined>(initialState);\n const [initializedFromThreadMessage, setInitializedFromThreadMessage] =\n useState(messageState ? true : false);\n\n // Optimistically update the local thread message's componentState\n const updateLocalThreadMessage = useCallback(\n async (newState: S, existingMessage: TamboThreadMessage | null) => {\n if (!existingMessage) {\n return;\n }\n const updatedMessage = {\n threadId: existingMessage.threadId,\n componentState: {\n ...existingMessage.componentState,\n [keyName]: newState,\n },\n };\n await updateThreadMessage(existingMessage.id, updatedMessage, false);\n },\n [updateThreadMessage, keyName],\n );\n\n // Debounced callback to update the remote thread message's componentState\n const updateRemoteThreadMessage = useDebouncedCallback(\n async (newState: S, existingMessage: TamboThreadMessage | null) => {\n if (!existingMessage) {\n return;\n }\n await client.beta.threads.messages.updateComponentState(\n existingMessage.id,\n {\n id: existingMessage.threadId,\n state: { [keyName]: newState },\n },\n );\n },\n debounceTime,\n );\n\n const setValue = useCallback(\n (newState: S) => {\n setLocalState(newState);\n if (componentId) {\n // For interactable components, update the interactable provider's state\n setInteractableState(componentId, keyName, newState);\n } else if (message) {\n // For generated components, update both local and remote thread message state\n void updateLocalThreadMessage(newState, message);\n void updateRemoteThreadMessage(newState, message);\n }\n },\n [\n message,\n updateLocalThreadMessage,\n updateRemoteThreadMessage,\n setInteractableState,\n componentId,\n keyName,\n ],\n );\n\n const existingInteractableState = componentId\n ? getInteractableComponentState(componentId)?.[keyName]\n : undefined;\n const shouldUpdateInteractableInitial =\n !!componentId &&\n existingInteractableState === undefined &&\n initialValue !== undefined;\n\n // Set initial value in interactable state if we're in an interactable context and there's no existing state\n useEffect(() => {\n if (!shouldUpdateInteractableInitial) {\n return;\n }\n setInteractableState(componentId, keyName, initialValue!);\n }, [\n shouldUpdateInteractableInitial,\n componentId,\n keyName,\n initialValue,\n setInteractableState,\n ]);\n\n const shouldSyncFromMessage =\n !!message && messageState !== undefined && messageState !== null;\n\n // Mirror the thread message's componentState value to the local state and interactable state\n useEffect(() => {\n if (!shouldSyncFromMessage) {\n return;\n }\n setInitializedFromThreadMessage(true);\n const stateValue = messageState as S;\n setLocalState(stateValue);\n if (componentId) {\n setInteractableState(componentId, keyName, stateValue);\n }\n }, [\n shouldSyncFromMessage,\n messageState,\n keyName,\n setInteractableState,\n componentId,\n ]);\n\n // Sync from interactable provider to local state when state changes externally (e.g., from Tambo tool call)\n useEffect(() => {\n if (!componentId) return;\n // only update if different\n setLocalState((prev) =>\n deepEqual(prev, interactableState) ? prev : (interactableState as S),\n );\n }, [componentId, interactableState]);\n\n // For editable fields that are set from a prop to allow streaming updates, don't overwrite a fetched state value set from the thread message with prop value on initial load.\n useEffect(() => {\n if (setFromProp !== undefined && !initializedFromThreadMessage) {\n setLocalState(setFromProp as S);\n }\n }, [setFromProp, initializedFromThreadMessage]);\n\n // Ensure pending changes are flushed on unmount (only for generated components)\n useEffect(() => {\n // Only flush remote updates for generated components, not interactable components\n if (componentId) {\n return;\n }\n return () => {\n async function flushUpdates() {\n try {\n await updateRemoteThreadMessage.flush();\n } catch (error) {\n console.error(\n \"Failed to flush pending thread message updates:\",\n error,\n );\n }\n }\n // Fire-and-forget cleanup (errors handled inside)\n void flushUpdates();\n };\n }, [updateRemoteThreadMessage, componentId]);\n\n return [localState as S, setValue];\n}\n"]}