@assistant-ui/core 0.3.16 → 0.3.17

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 (305) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +2 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/internal.d.ts +2 -1
  5. package/dist/internal.js +2 -1
  6. package/dist/model-context/frame/provider.d.ts +3 -0
  7. package/dist/model-context/frame/provider.d.ts.map +1 -1
  8. package/dist/model-context/frame/provider.js +71 -21
  9. package/dist/model-context/frame/provider.js.map +1 -1
  10. package/dist/model-context/tool-call-text.d.ts +26 -0
  11. package/dist/model-context/tool-call-text.d.ts.map +1 -0
  12. package/dist/model-context/tool-call-text.js +18 -0
  13. package/dist/model-context/tool-call-text.js.map +1 -0
  14. package/dist/react/RuntimeAdapter.js +1 -1
  15. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  16. package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
  17. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  18. package/dist/react/client/AdaptedRemoteThread.js +1 -1
  19. package/dist/react/client/InMemoryThreadList.js +1 -1
  20. package/dist/react/client/Interactables.js +1 -1
  21. package/dist/react/client/RemoteThreadList.d.ts +15 -0
  22. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  23. package/dist/react/client/RemoteThreadList.js +240 -138
  24. package/dist/react/client/RemoteThreadList.js.map +1 -1
  25. package/dist/react/client/Tools.d.ts.map +1 -1
  26. package/dist/react/client/Tools.js +6 -2
  27. package/dist/react/client/Tools.js.map +1 -1
  28. package/dist/react/index.d.ts +2 -1
  29. package/dist/react/index.js +2 -1
  30. package/dist/react/interactables-legacy/Interactables.js +4 -4
  31. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  32. package/dist/react/model-context/toolbox.d.ts +2 -14
  33. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  34. package/dist/react/model-context/toolbox.js +1 -13
  35. package/dist/react/model-context/toolbox.js.map +1 -1
  36. package/dist/react/model-context/useAssistantContext.js +37 -7
  37. package/dist/react/model-context/useAssistantContext.js.map +1 -1
  38. package/dist/react/model-context/useAuiToolOverrides.js +31 -5
  39. package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
  40. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  41. package/dist/react/model-context/useInlineRender.js +10 -36
  42. package/dist/react/model-context/useInlineRender.js.map +1 -1
  43. package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
  44. package/dist/react/primitive-hooks/useMessageError.js +2 -8
  45. package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
  46. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  47. package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -4
  48. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  49. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  50. package/dist/react/primitives/composer/ComposerAttachments.js +4 -3
  51. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  52. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  53. package/dist/react/primitives/composer/ComposerQueue.js +4 -3
  54. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  55. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  56. package/dist/react/primitives/message/MessageAttachments.js +7 -6
  57. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  58. package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
  59. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  60. package/dist/react/primitives/message/MessageParts.js +3 -3
  61. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  62. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  63. package/dist/react/primitives/threadList/ThreadListItems.js +5 -4
  64. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  65. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +6 -4
  66. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  67. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +44 -22
  68. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  69. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +6 -5
  70. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  71. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +79 -62
  72. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  73. package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -6
  74. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  75. package/dist/react/runtimes/RemoteThreadResource.js +3 -3
  76. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  77. package/dist/react/runtimes/assistant-transport/useLatestRef.js +2 -2
  78. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -1
  79. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  80. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +23 -18
  81. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  82. package/dist/react/runtimes/cloud/auiV0.d.ts +5 -1
  83. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  84. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  85. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +29 -0
  86. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -0
  87. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +159 -0
  88. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -0
  89. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +1 -9
  90. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  91. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +16 -153
  92. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  93. package/dist/react/runtimes/external-message-converter.js +1 -1
  94. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  95. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  96. package/dist/react/runtimes/useRemoteThreadListRuntime.js +14 -6
  97. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  98. package/dist/react/types/MessagePartComponentTypes.d.ts +7 -3
  99. package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
  100. package/dist/react/types/scopes/tools.d.ts +2 -0
  101. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  102. package/dist/runtime/api/message-part-runtime.d.ts +2 -2
  103. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  104. package/dist/runtime/api/message-part-runtime.js +1 -1
  105. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  106. package/dist/runtime/api/thread-list-runtime.d.ts +9 -1
  107. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  108. package/dist/runtime/api/thread-list-runtime.js +5 -0
  109. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  110. package/dist/runtime/api/thread-runtime.d.ts +1 -1
  111. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -2
  112. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  113. package/dist/runtime/base/base-thread-runtime-core.js +2 -2
  114. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  115. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +14 -2
  116. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  117. package/dist/runtime/interfaces/thread-runtime-core.d.ts +15 -1
  118. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  119. package/dist/runtime/utils/auto-status.d.ts +1 -1
  120. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  121. package/dist/runtime/utils/auto-status.js +6 -2
  122. package/dist/runtime/utils/auto-status.js.map +1 -1
  123. package/dist/runtime/utils/external-message-conversion.d.ts +3 -2
  124. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  125. package/dist/runtime/utils/external-message-conversion.js +4 -3
  126. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  127. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -3
  128. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  129. package/dist/runtime/utils/resolveToolApprovalResponse.js +12 -3
  130. package/dist/runtime/utils/resolveToolApprovalResponse.js.map +1 -1
  131. package/dist/runtime/utils/thread-message-like.d.ts +2 -10
  132. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  133. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  134. package/dist/runtime/utils/tool-call-tree.d.ts +39 -0
  135. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -0
  136. package/dist/runtime/utils/tool-call-tree.js +85 -0
  137. package/dist/runtime/utils/tool-call-tree.js.map +1 -0
  138. package/dist/runtimes/external-store/external-store-adapter.d.ts +29 -2
  139. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  140. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +2 -1
  141. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  142. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +34 -28
  143. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  144. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +1 -1
  145. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  146. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  147. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  148. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  149. package/dist/runtimes/local/local-thread-runtime-core.js +3 -1
  150. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  151. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +1 -1
  152. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  153. package/dist/runtimes/remote-thread-list/remote-thread-state.js +30 -13
  154. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  155. package/dist/runtimes/remote-thread-list/title.d.ts +9 -0
  156. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -0
  157. package/dist/runtimes/remote-thread-list/title.js +6 -0
  158. package/dist/runtimes/remote-thread-list/title.js.map +1 -0
  159. package/dist/runtimes/remote-thread-list/types.d.ts +6 -5
  160. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  161. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +31 -8
  162. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  163. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +97 -60
  164. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  165. package/dist/store/clients/external-thread.d.ts +1 -1
  166. package/dist/store/clients/external-thread.d.ts.map +1 -1
  167. package/dist/store/clients/external-thread.js +11 -4
  168. package/dist/store/clients/external-thread.js.map +1 -1
  169. package/dist/store/clients/single-thread-list.js +1 -1
  170. package/dist/store/clients/suggestions.d.ts.map +1 -1
  171. package/dist/store/clients/suggestions.js +18 -6
  172. package/dist/store/clients/suggestions.js.map +1 -1
  173. package/dist/store/clients/thread-message-client.js +1 -1
  174. package/dist/store/clients/thread-selection-events.js +1 -1
  175. package/dist/store/index.d.ts +2 -1
  176. package/dist/store/index.js +2 -1
  177. package/dist/store/internal.d.ts +2 -2
  178. package/dist/store/internal.js +2 -2
  179. package/dist/store/primitive-predicates.d.ts +4 -1
  180. package/dist/store/primitive-predicates.d.ts.map +1 -1
  181. package/dist/store/primitive-predicates.js +9 -1
  182. package/dist/store/primitive-predicates.js.map +1 -1
  183. package/dist/store/runtime-clients/composer-runtime-client.js +1 -1
  184. package/dist/store/runtime-clients/message-runtime-client.js +1 -1
  185. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  186. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  187. package/dist/store/runtime-clients/thread-list-runtime-client.js +124 -105
  188. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  189. package/dist/store/runtime-clients/thread-runtime-client.js +1 -1
  190. package/dist/store/runtime-clients/useSubscribable.d.ts +3 -1
  191. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  192. package/dist/store/runtime-clients/useSubscribable.js +1 -1
  193. package/dist/store/runtime-clients/useSubscribable.js.map +1 -1
  194. package/dist/store/scopes/part.d.ts +6 -3
  195. package/dist/store/scopes/part.d.ts.map +1 -1
  196. package/dist/subscribable/subscribable.d.ts +8 -1
  197. package/dist/subscribable/subscribable.d.ts.map +1 -1
  198. package/dist/subscribable/subscribable.js +24 -16
  199. package/dist/subscribable/subscribable.js.map +1 -1
  200. package/dist/types/message.d.ts +40 -1
  201. package/dist/types/message.d.ts.map +1 -1
  202. package/dist/types/message.js +7 -1
  203. package/dist/types/message.js.map +1 -1
  204. package/package.json +12 -17
  205. package/src/index.ts +6 -1
  206. package/src/internal.ts +8 -0
  207. package/src/model-context/frame/provider.test.ts +217 -1
  208. package/src/model-context/frame/provider.ts +111 -23
  209. package/src/model-context/tool-call-text.test.ts +67 -0
  210. package/src/model-context/tool-call-text.ts +67 -0
  211. package/src/react/AssistantRuntimeProvider.test.tsx +66 -2
  212. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +1 -3
  213. package/src/react/client/RemoteThreadList.background.test.ts +384 -0
  214. package/src/react/client/RemoteThreadList.concurrent.test.tsx +274 -0
  215. package/src/react/client/RemoteThreadList.test.ts +273 -1
  216. package/src/react/client/RemoteThreadList.ts +293 -83
  217. package/src/react/client/Tools.ts +7 -1
  218. package/src/react/index.ts +4 -0
  219. package/src/react/model-context/modelContextHooks.test.tsx +131 -0
  220. package/src/react/model-context/toolbox.test.ts +9 -9
  221. package/src/react/model-context/toolbox.ts +9 -36
  222. package/src/react/model-context/useAssistantContext.ts +4 -2
  223. package/src/react/model-context/useAuiToolOverrides.ts +4 -2
  224. package/src/react/model-context/useInlineRender.test.tsx +48 -0
  225. package/src/react/model-context/useInlineRender.ts +14 -10
  226. package/src/react/primitive-hooks/useMessageError.ts +2 -19
  227. package/src/react/primitive-hooks/useThreadListLoadMore.ts +2 -3
  228. package/src/react/primitives/composer/ComposerAttachments.test.tsx +76 -0
  229. package/src/react/primitives/composer/ComposerAttachments.tsx +9 -4
  230. package/src/react/primitives/composer/ComposerQueue.test.tsx +72 -0
  231. package/src/react/primitives/composer/ComposerQueue.tsx +7 -4
  232. package/src/react/primitives/message/MessageAttachments.test.tsx +107 -37
  233. package/src/react/primitives/message/MessageAttachments.tsx +10 -7
  234. package/src/react/primitives/message/MessageGroupedParts.tsx +2 -2
  235. package/src/react/primitives/message/MessageParts.tsx +3 -3
  236. package/src/react/primitives/threadList/ThreadListItems.test.tsx +99 -0
  237. package/src/react/primitives/threadList/ThreadListItems.tsx +8 -5
  238. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +94 -1
  239. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +72 -31
  240. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +169 -0
  241. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +114 -68
  242. package/src/react/runtimes/RemoteThreadResource.ts +1 -4
  243. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +51 -2
  244. package/src/react/runtimes/assistant-transport/useLatestRef.ts +2 -2
  245. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +45 -0
  246. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +65 -1
  247. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +42 -35
  248. package/src/react/runtimes/cloud/auiV0.ts +5 -0
  249. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +88 -0
  250. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +236 -0
  251. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +90 -1
  252. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +33 -238
  253. package/src/react/runtimes/external-message-converter.ts +1 -0
  254. package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +216 -0
  255. package/src/react/runtimes/useRemoteThreadListRuntime.ts +24 -6
  256. package/src/react/types/MessagePartComponentTypes.ts +7 -3
  257. package/src/react/types/scopes/tools.ts +2 -0
  258. package/src/runtime/api/message-part-runtime.ts +3 -3
  259. package/src/runtime/api/thread-list-runtime.ts +25 -1
  260. package/src/runtime/base/base-thread-runtime-core.test.ts +94 -3
  261. package/src/runtime/base/base-thread-runtime-core.ts +10 -4
  262. package/src/runtime/interfaces/thread-list-runtime-core.ts +20 -1
  263. package/src/runtime/interfaces/thread-runtime-core.ts +17 -1
  264. package/src/runtime/utils/auto-status.test.ts +57 -0
  265. package/src/runtime/utils/auto-status.ts +13 -1
  266. package/src/runtime/utils/external-message-conversion.test.ts +97 -0
  267. package/src/runtime/utils/external-message-conversion.ts +17 -1
  268. package/src/runtime/utils/resolveToolApprovalResponse.test.ts +73 -0
  269. package/src/runtime/utils/resolveToolApprovalResponse.ts +27 -6
  270. package/src/runtime/utils/thread-message-like.ts +2 -10
  271. package/src/runtime/utils/tool-call-tree.test.ts +191 -0
  272. package/src/runtime/utils/tool-call-tree.ts +97 -0
  273. package/src/runtimes/external-store/external-store-adapter.ts +38 -2
  274. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +56 -33
  275. package/src/runtimes/inert/InertThreadRuntimeCore.ts +1 -1
  276. package/src/runtimes/local/local-thread-runtime-core.test.ts +29 -3
  277. package/src/runtimes/local/local-thread-runtime-core.ts +5 -1
  278. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +112 -0
  279. package/src/runtimes/remote-thread-list/remote-thread-state.ts +55 -16
  280. package/src/runtimes/remote-thread-list/title.ts +3 -0
  281. package/src/runtimes/remote-thread-list/types.ts +6 -5
  282. package/src/runtimes/tool-invocations/EDGE_CASES.md +110 -5
  283. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +750 -136
  284. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +144 -91
  285. package/src/store/clients/external-thread.ts +14 -8
  286. package/src/store/clients/suggestions.test.ts +155 -1
  287. package/src/store/clients/suggestions.ts +42 -34
  288. package/src/store/index.ts +4 -0
  289. package/src/store/internal.ts +2 -0
  290. package/src/store/primitive-predicates.ts +35 -0
  291. package/src/store/runtime-clients/thread-list-runtime-client.ts +16 -2
  292. package/src/store/runtime-clients/useSubscribable.ts +8 -2
  293. package/src/store/scopes/part.ts +6 -3
  294. package/src/subscribable/subscribable.test.ts +50 -0
  295. package/src/subscribable/subscribable.ts +33 -21
  296. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +111 -0
  297. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +78 -0
  298. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +52 -0
  299. package/src/tests/external-store-optimistic-id.test.ts +79 -0
  300. package/src/tests/external-store-repository-instance.test.ts +166 -0
  301. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +22 -16
  302. package/src/tests/external-thread-attachments.test.tsx +85 -1
  303. package/src/tests/remote-thread-list-adapter-switch.test.ts +66 -1
  304. package/src/tests/thread-switch-events.test.tsx +397 -2
  305. package/src/types/message.ts +50 -2
@@ -0,0 +1,236 @@
1
+ declare const process: { env: Record<string, string | undefined> };
2
+
3
+ import { type RefObject, useMemo, useState } from "react";
4
+ import { AssistantCloud } from "assistant-cloud";
5
+ import type {
6
+ RemoteThreadListAdapter,
7
+ RuntimeAdapters,
8
+ } from "../../../runtimes/remote-thread-list/types";
9
+ import { InMemoryThreadListAdapter } from "../../../runtimes/remote-thread-list/adapter/in-memory";
10
+ import { useAssistantCloudThreadHistoryAdapter } from "./AssistantCloudThreadHistoryAdapter";
11
+ import { CloudFileAttachmentAdapter } from "./CloudFileAttachmentAdapter";
12
+ import { isRecord } from "../../../utils/json/is-json";
13
+
14
+ type ThreadData = {
15
+ externalId: string | undefined;
16
+ };
17
+
18
+ export type CloudThreadListAdapterOptions = {
19
+ cloud?: AssistantCloud | undefined;
20
+
21
+ create?: (() => Promise<ThreadData>) | undefined;
22
+ delete?: ((threadId: string) => Promise<void>) | undefined;
23
+ };
24
+
25
+ const toCustom = (value: unknown): Record<string, unknown> | undefined =>
26
+ isRecord(value) ? value : undefined;
27
+
28
+ const baseUrl =
29
+ typeof process !== "undefined" &&
30
+ process?.env?.NEXT_PUBLIC_ASSISTANT_BASE_URL;
31
+ export const autoCloud = baseUrl
32
+ ? new AssistantCloud({ baseUrl, anonymous: true })
33
+ : undefined;
34
+
35
+ export const useCloudRuntimeAdapters = (
36
+ cloudRef: RefObject<AssistantCloud>,
37
+ ): RuntimeAdapters => {
38
+ const history = useAssistantCloudThreadHistoryAdapter(cloudRef);
39
+ const [attachments] = useState(
40
+ () => new CloudFileAttachmentAdapter(() => cloudRef.current),
41
+ );
42
+ return useMemo(
43
+ () => ({
44
+ history,
45
+ attachments,
46
+ }),
47
+ [history, attachments],
48
+ );
49
+ };
50
+
51
+ const CLOUD_THREAD_PAGE_SIZE = 20;
52
+
53
+ type CloudListCursor = {
54
+ activeCursor: string | undefined;
55
+ archivedCursor: string | undefined;
56
+ activeExhausted: boolean;
57
+ archivedExhausted: boolean;
58
+ };
59
+
60
+ const parseListCursor = (after: string | undefined): CloudListCursor => {
61
+ const fallback: CloudListCursor = {
62
+ activeCursor: after,
63
+ archivedCursor: undefined,
64
+ activeExhausted: false,
65
+ archivedExhausted: false,
66
+ };
67
+ if (!after || !after.startsWith("{")) return fallback;
68
+ try {
69
+ const parsed = JSON.parse(after);
70
+ if (!isRecord(parsed)) return fallback;
71
+ return {
72
+ activeCursor: typeof parsed.a === "string" ? parsed.a : undefined,
73
+ archivedCursor: typeof parsed.r === "string" ? parsed.r : undefined,
74
+ activeExhausted: parsed.ae === true,
75
+ archivedExhausted: parsed.re === true,
76
+ };
77
+ } catch {
78
+ return fallback;
79
+ }
80
+ };
81
+
82
+ /**
83
+ * Builds the `RemoteThreadListAdapter` for an assistant-cloud backend without
84
+ * requiring a hook call site, so plain code (a Vue or Svelte setup function,
85
+ * a module-level config) can construct it. Options are read through the
86
+ * getter on every call, so a stable adapter can follow changing `create` and
87
+ * `delete` callbacks; swapping to a different `cloud` instance requires a new
88
+ * adapter (and `reload()` on the list). Without a `cloud` instance (and
89
+ * without `NEXT_PUBLIC_ASSISTANT_BASE_URL`), the adapter falls back to an
90
+ * in-memory list. `useCloudThreadListAdapter` wraps this for the React
91
+ * hook signature.
92
+ */
93
+ export const createCloudThreadListAdapter = (
94
+ options:
95
+ | CloudThreadListAdapterOptions
96
+ | (() => CloudThreadListAdapterOptions),
97
+ ): RemoteThreadListAdapter => {
98
+ const getOptions = typeof options === "function" ? options : () => options;
99
+
100
+ const unstable_useAdapters = function useCloudAdapters(): RuntimeAdapters {
101
+ return useCloudRuntimeAdapters({
102
+ get current() {
103
+ return getOptions().cloud ?? autoCloud!;
104
+ },
105
+ });
106
+ };
107
+
108
+ const cloud = getOptions().cloud ?? autoCloud;
109
+ if (!cloud) {
110
+ const inMemory = new InMemoryThreadListAdapter();
111
+ inMemory.initialize = async (threadId: string) => {
112
+ const result = await getOptions().create?.();
113
+ return { remoteId: threadId, externalId: result?.externalId };
114
+ };
115
+ return inMemory;
116
+ }
117
+
118
+ return {
119
+ list: async ({ after } = {}) => {
120
+ const {
121
+ activeCursor,
122
+ archivedCursor,
123
+ activeExhausted,
124
+ archivedExhausted,
125
+ } = parseListCursor(after);
126
+ const [{ threads: activeThreads }, { threads: archivedThreads }] =
127
+ await Promise.all([
128
+ activeExhausted
129
+ ? Promise.resolve({ threads: [] })
130
+ : cloud.threads.list({
131
+ limit: CLOUD_THREAD_PAGE_SIZE,
132
+ ...(activeCursor ? { after: activeCursor } : {}),
133
+ }),
134
+ archivedExhausted
135
+ ? Promise.resolve({ threads: [] })
136
+ : cloud.threads.list({
137
+ is_archived: true,
138
+ limit: CLOUD_THREAD_PAGE_SIZE,
139
+ ...(archivedCursor ? { after: archivedCursor } : {}),
140
+ }),
141
+ ]);
142
+ const activeNext =
143
+ !activeExhausted && activeThreads.length === CLOUD_THREAD_PAGE_SIZE
144
+ ? activeThreads.at(-1)?.id
145
+ : undefined;
146
+ const archivedNext =
147
+ !archivedExhausted && archivedThreads.length === CLOUD_THREAD_PAGE_SIZE
148
+ ? archivedThreads.at(-1)?.id
149
+ : undefined;
150
+ const threads = [...activeThreads, ...archivedThreads];
151
+ return {
152
+ threads: threads.map((t) => ({
153
+ status: t.is_archived ? ("archived" as const) : ("regular" as const),
154
+ remoteId: t.id,
155
+ title: t.title,
156
+ lastMessageAt: t.last_message_at
157
+ ? new Date(t.last_message_at)
158
+ : undefined,
159
+ externalId: t.external_id ?? undefined,
160
+ custom: toCustom(t.metadata),
161
+ })),
162
+ nextCursor:
163
+ activeNext || archivedNext
164
+ ? JSON.stringify({
165
+ a: activeNext,
166
+ r: archivedNext,
167
+ ...(activeNext === undefined ? { ae: true } : {}),
168
+ ...(archivedNext === undefined ? { re: true } : {}),
169
+ })
170
+ : undefined,
171
+ };
172
+ },
173
+
174
+ initialize: async () => {
175
+ const createTask = getOptions().create?.() ?? Promise.resolve();
176
+ const t = await createTask;
177
+ const external_id = t ? t.externalId : undefined;
178
+ const { thread_id: remoteId } = await cloud.threads.create({
179
+ last_message_at: new Date(),
180
+ external_id,
181
+ });
182
+
183
+ return { externalId: external_id, remoteId: remoteId };
184
+ },
185
+
186
+ rename: async (threadId, newTitle) => {
187
+ return cloud.threads.update(threadId, { title: newTitle });
188
+ },
189
+ updateCustom: async (threadId, custom) => {
190
+ return cloud.threads.update(threadId, { metadata: custom ?? null });
191
+ },
192
+ archive: async (threadId) => {
193
+ return cloud.threads.update(threadId, { is_archived: true });
194
+ },
195
+ unarchive: async (threadId) => {
196
+ return cloud.threads.update(threadId, { is_archived: false });
197
+ },
198
+ delete: async (threadId) => {
199
+ await getOptions().delete?.(threadId);
200
+ return cloud.threads.delete(threadId);
201
+ },
202
+
203
+ generateTitle: async (threadId, messages) => {
204
+ const filteredMessages = messages.map((msg) => ({
205
+ ...msg,
206
+ content: msg.content.filter(
207
+ (part) => part.type === "text" || part.type === "tool-call",
208
+ ),
209
+ }));
210
+
211
+ return cloud.runs.stream({
212
+ thread_id: threadId,
213
+ assistant_id: "system/thread_title",
214
+ messages: filteredMessages,
215
+ });
216
+ },
217
+
218
+ fetch: async (threadId: string) => {
219
+ const thread = await cloud.threads.get(threadId);
220
+ return {
221
+ status: thread.is_archived
222
+ ? ("archived" as const)
223
+ : ("regular" as const),
224
+ remoteId: thread.id,
225
+ title: thread.title,
226
+ lastMessageAt: thread.last_message_at
227
+ ? new Date(thread.last_message_at)
228
+ : undefined,
229
+ externalId: thread.external_id ?? undefined,
230
+ custom: toCustom(thread.metadata),
231
+ };
232
+ },
233
+
234
+ unstable_useAdapters,
235
+ };
236
+ };
@@ -1,8 +1,15 @@
1
1
  // @vitest-environment jsdom
2
2
 
3
- import { renderHook } from "@testing-library/react";
3
+ import { act, render, renderHook } from "@testing-library/react";
4
4
  import type { AssistantCloud } from "assistant-cloud";
5
+ import {
6
+ startTransition,
7
+ Suspense,
8
+ useInsertionEffect,
9
+ type ReactNode,
10
+ } from "react";
5
11
  import { describe, expect, it, vi } from "vitest";
12
+ import type { RemoteThreadListAdapter } from "../../../runtimes/remote-thread-list/types";
6
13
  import { useCloudThreadListAdapter } from "./useCloudThreadListAdapter";
7
14
 
8
15
  const makeThread = (id: string) => ({
@@ -18,7 +25,89 @@ const makeThread = (id: string) => ({
18
25
  workspace_id: "workspace-1",
19
26
  });
20
27
 
28
+ const Suspend = ({ pending }: { pending: Promise<never> }) => {
29
+ throw pending;
30
+ };
31
+
21
32
  describe("useCloudThreadListAdapter", () => {
33
+ it("keeps operations scoped to the committed Cloud options", async () => {
34
+ const deleteA = vi.fn(async () => {});
35
+ const deleteB = vi.fn(async () => {});
36
+ const cloudDeleteA = vi.fn(async () => {});
37
+ const cloudDeleteB = vi.fn(async () => {});
38
+ const cloudA = {
39
+ threads: { delete: cloudDeleteA },
40
+ } as unknown as AssistantCloud;
41
+ const cloudB = {
42
+ threads: { delete: cloudDeleteB },
43
+ } as unknown as AssistantCloud;
44
+ const committed: { current: RemoteThreadListAdapter | null } = {
45
+ current: null,
46
+ };
47
+ const renderB = vi.fn();
48
+ let suspend = false;
49
+ const pending = new Promise<never>(() => {});
50
+
51
+ const App = ({
52
+ cloud,
53
+ onDelete,
54
+ }: {
55
+ cloud: AssistantCloud;
56
+ onDelete: (threadId: string) => Promise<void>;
57
+ }) => {
58
+ const adapter = useCloudThreadListAdapter({
59
+ cloud,
60
+ delete: onDelete,
61
+ });
62
+ if (cloud === cloudB) renderB();
63
+ useInsertionEffect(() => {
64
+ committed.current = adapter;
65
+ }, [adapter]);
66
+ return null;
67
+ };
68
+ const Boundary = ({ children }: { children: ReactNode }) => (
69
+ <Suspense fallback={null}>
70
+ {children}
71
+ {suspend ? <Suspend pending={pending} /> : null}
72
+ </Suspense>
73
+ );
74
+ const view = render(
75
+ <Boundary>
76
+ <App cloud={cloudA} onDelete={deleteA} />
77
+ </Boundary>,
78
+ );
79
+
80
+ act(() => {
81
+ suspend = true;
82
+ startTransition(() => {
83
+ view.rerender(
84
+ <Boundary>
85
+ <App cloud={cloudB} onDelete={deleteB} />
86
+ </Boundary>,
87
+ );
88
+ });
89
+ });
90
+ expect(renderB).toHaveBeenCalled();
91
+
92
+ await committed.current!.delete!("thread-1");
93
+
94
+ expect(deleteA).toHaveBeenCalledWith("thread-1");
95
+ expect(deleteB).not.toHaveBeenCalled();
96
+ expect(cloudDeleteA).toHaveBeenCalledWith("thread-1");
97
+ expect(cloudDeleteB).not.toHaveBeenCalled();
98
+
99
+ suspend = false;
100
+ view.rerender(
101
+ <Boundary>
102
+ <App cloud={cloudB} onDelete={deleteB} />
103
+ </Boundary>,
104
+ );
105
+ await committed.current!.delete!("thread-2");
106
+
107
+ expect(deleteB).toHaveBeenCalledWith("thread-2");
108
+ expect(cloudDeleteB).toHaveBeenCalledWith("thread-2");
109
+ });
110
+
22
111
  it("loads archived Cloud threads alongside regular threads", async () => {
23
112
  const activeThreads = [makeThread("active-1")];
24
113
  const archivedThreads = [
@@ -1,257 +1,52 @@
1
- declare const process: { env: Record<string, string | undefined> };
2
-
3
- import {
4
- type FC,
5
- type PropsWithChildren,
6
- type RefObject,
7
- useCallback,
8
- useEffect,
9
- useMemo,
10
- useRef,
11
- useState,
12
- } from "react";
13
- import { AssistantCloud } from "assistant-cloud";
1
+ import { useCallback, useInsertionEffect, useMemo, useRef } from "react";
14
2
  import type { RemoteThreadListAdapter } from "../../../runtimes/remote-thread-list/types";
15
- import { InMemoryThreadListAdapter } from "../../../runtimes/remote-thread-list/adapter/in-memory";
16
- import { useAssistantCloudThreadHistoryAdapter } from "./AssistantCloudThreadHistoryAdapter";
17
3
  import {
18
- RuntimeAdapterProvider,
19
- type RuntimeAdapters,
20
- } from "../RuntimeAdapterProvider";
21
- import { CloudFileAttachmentAdapter } from "./CloudFileAttachmentAdapter";
22
- import { isRecord } from "../../../utils/json/is-json";
23
-
24
- type ThreadData = {
25
- externalId: string | undefined;
26
- };
27
-
28
- type CloudThreadListAdapterOptions = {
29
- cloud?: AssistantCloud | undefined;
30
-
31
- create?: (() => Promise<ThreadData>) | undefined;
32
- delete?: ((threadId: string) => Promise<void>) | undefined;
33
- };
34
-
35
- const toCustom = (value: unknown): Record<string, unknown> | undefined =>
36
- isRecord(value) ? value : undefined;
37
-
38
- const baseUrl =
39
- typeof process !== "undefined" &&
40
- process?.env?.NEXT_PUBLIC_ASSISTANT_BASE_URL;
41
- const autoCloud = baseUrl
42
- ? new AssistantCloud({ baseUrl, anonymous: true })
43
- : undefined;
44
-
45
- const CLOUD_THREAD_PAGE_SIZE = 20;
46
-
47
- type CloudListCursor = {
48
- activeCursor: string | undefined;
49
- archivedCursor: string | undefined;
50
- activeExhausted: boolean;
51
- archivedExhausted: boolean;
52
- };
53
-
54
- const parseListCursor = (after: string | undefined): CloudListCursor => {
55
- const fallback: CloudListCursor = {
56
- activeCursor: after,
57
- archivedCursor: undefined,
58
- activeExhausted: false,
59
- archivedExhausted: false,
60
- };
61
- if (!after || !after.startsWith("{")) return fallback;
62
- try {
63
- const parsed = JSON.parse(after);
64
- if (!isRecord(parsed)) return fallback;
65
- return {
66
- activeCursor: typeof parsed.a === "string" ? parsed.a : undefined,
67
- archivedCursor: typeof parsed.r === "string" ? parsed.r : undefined,
68
- activeExhausted: parsed.ae === true,
69
- archivedExhausted: parsed.re === true,
70
- };
71
- } catch {
72
- return fallback;
73
- }
74
- };
75
-
76
- const useCloudThreadListAdapters = (
77
- adapterRef: RefObject<CloudThreadListAdapterOptions>,
78
- ): RuntimeAdapters => {
79
- const history = useAssistantCloudThreadHistoryAdapter({
80
- get current() {
81
- return adapterRef.current.cloud ?? autoCloud!;
82
- },
83
- });
84
- const [attachments] = useState(
85
- () =>
86
- new CloudFileAttachmentAdapter(
87
- () => adapterRef.current.cloud ?? autoCloud!,
88
- ),
89
- );
90
- return useMemo(
91
- () => ({
92
- history,
93
- attachments,
94
- }),
95
- [history, attachments],
96
- );
97
- };
4
+ autoCloud,
5
+ createCloudThreadListAdapter,
6
+ type CloudThreadListAdapterOptions,
7
+ useCloudRuntimeAdapters,
8
+ } from "./createCloudThreadListAdapter";
98
9
 
99
10
  export const useCloudThreadListAdapter = (
100
11
  adapter: CloudThreadListAdapterOptions,
101
12
  ): RemoteThreadListAdapter => {
102
13
  const adapterRef = useRef(adapter);
103
- useEffect(() => {
14
+ useInsertionEffect(() => {
104
15
  adapterRef.current = adapter;
105
16
  }, [adapter]);
106
17
 
107
- const unstable_useAdapters = useCallback(function useCloudAdapters() {
108
- return useCloudThreadListAdapters(adapterRef);
109
- }, []);
18
+ const cloud = adapter.cloud ?? autoCloud;
19
+ const base = useMemo(
20
+ () =>
21
+ createCloudThreadListAdapter(() => ({
22
+ ...adapterRef.current,
23
+ cloud,
24
+ })),
25
+ // oxlint-disable-next-line react-hooks/exhaustive-deps -- the factory pins the cloud instance; changing callbacks are read from the committed ref
26
+ [cloud],
27
+ );
28
+
29
+ const cloudRef = useMemo(
30
+ () => ({
31
+ get current() {
32
+ return adapterRef.current.cloud ?? autoCloud!;
33
+ },
34
+ }),
35
+ [],
36
+ );
110
37
 
111
- const unstable_Provider = useCallback<FC<PropsWithChildren>>(
112
- function Provider({ children }) {
113
- const adapters = useCloudThreadListAdapters(adapterRef);
114
- return (
115
- <RuntimeAdapterProvider adapters={adapters}>
116
- {children}
117
- </RuntimeAdapterProvider>
118
- );
38
+ const unstable_useAdapters = useCallback(
39
+ function useCloudAdapters() {
40
+ return useCloudRuntimeAdapters(cloudRef);
119
41
  },
120
- [],
42
+ [cloudRef],
121
43
  );
122
44
 
123
- const cloud = adapter.cloud ?? autoCloud;
124
45
  return useMemo<RemoteThreadListAdapter>(() => {
125
- if (!cloud) {
126
- const ref = adapterRef;
127
- const inMemory = new InMemoryThreadListAdapter();
128
- inMemory.initialize = async (threadId: string) => {
129
- const result = await ref.current.create?.();
130
- return { remoteId: threadId, externalId: result?.externalId };
131
- };
132
- return inMemory;
133
- }
134
-
46
+ if (base.unstable_useAdapters === undefined) return base;
135
47
  return {
136
- list: async ({ after } = {}) => {
137
- const {
138
- activeCursor,
139
- archivedCursor,
140
- activeExhausted,
141
- archivedExhausted,
142
- } = parseListCursor(after);
143
- const [{ threads: activeThreads }, { threads: archivedThreads }] =
144
- await Promise.all([
145
- activeExhausted
146
- ? Promise.resolve({ threads: [] })
147
- : cloud.threads.list({
148
- limit: CLOUD_THREAD_PAGE_SIZE,
149
- ...(activeCursor ? { after: activeCursor } : {}),
150
- }),
151
- archivedExhausted
152
- ? Promise.resolve({ threads: [] })
153
- : cloud.threads.list({
154
- is_archived: true,
155
- limit: CLOUD_THREAD_PAGE_SIZE,
156
- ...(archivedCursor ? { after: archivedCursor } : {}),
157
- }),
158
- ]);
159
- const activeNext =
160
- !activeExhausted && activeThreads.length === CLOUD_THREAD_PAGE_SIZE
161
- ? activeThreads.at(-1)?.id
162
- : undefined;
163
- const archivedNext =
164
- !archivedExhausted &&
165
- archivedThreads.length === CLOUD_THREAD_PAGE_SIZE
166
- ? archivedThreads.at(-1)?.id
167
- : undefined;
168
- const threads = [...activeThreads, ...archivedThreads];
169
- return {
170
- threads: threads.map((t) => ({
171
- status: t.is_archived ? "archived" : "regular",
172
- remoteId: t.id,
173
- title: t.title,
174
- lastMessageAt: t.last_message_at
175
- ? new Date(t.last_message_at)
176
- : undefined,
177
- externalId: t.external_id ?? undefined,
178
- custom: toCustom(t.metadata),
179
- })),
180
- nextCursor:
181
- activeNext || archivedNext
182
- ? JSON.stringify({
183
- a: activeNext,
184
- r: archivedNext,
185
- ...(activeNext === undefined ? { ae: true } : {}),
186
- ...(archivedNext === undefined ? { re: true } : {}),
187
- })
188
- : undefined,
189
- };
190
- },
191
-
192
- initialize: async () => {
193
- const createTask = adapterRef.current.create?.() ?? Promise.resolve();
194
- const t = await createTask;
195
- const external_id = t ? t.externalId : undefined;
196
- const { thread_id: remoteId } = await cloud.threads.create({
197
- last_message_at: new Date(),
198
- external_id,
199
- });
200
-
201
- return { externalId: external_id, remoteId: remoteId };
202
- },
203
-
204
- rename: async (threadId, newTitle) => {
205
- return cloud.threads.update(threadId, { title: newTitle });
206
- },
207
- updateCustom: async (threadId, custom) => {
208
- return cloud.threads.update(threadId, { metadata: custom ?? null });
209
- },
210
- archive: async (threadId) => {
211
- return cloud.threads.update(threadId, { is_archived: true });
212
- },
213
- unarchive: async (threadId) => {
214
- return cloud.threads.update(threadId, { is_archived: false });
215
- },
216
- delete: async (threadId) => {
217
- await adapterRef.current.delete?.(threadId);
218
- return cloud.threads.delete(threadId);
219
- },
220
-
221
- generateTitle: async (threadId, messages) => {
222
- // Filter messages to only include content types the title generator understands
223
- // (reasoning, source, etc. are not needed for title generation)
224
- // TODO serialize these to a more efficient format
225
- const filteredMessages = messages.map((msg) => ({
226
- ...msg,
227
- content: msg.content.filter(
228
- (part) => part.type === "text" || part.type === "tool-call",
229
- ),
230
- }));
231
-
232
- return cloud.runs.stream({
233
- thread_id: threadId,
234
- assistant_id: "system/thread_title",
235
- messages: filteredMessages,
236
- });
237
- },
238
-
239
- fetch: async (threadId: string) => {
240
- const thread = await cloud.threads.get(threadId);
241
- return {
242
- status: thread.is_archived ? "archived" : "regular",
243
- remoteId: thread.id,
244
- title: thread.title,
245
- lastMessageAt: thread.last_message_at
246
- ? new Date(thread.last_message_at)
247
- : undefined,
248
- externalId: thread.external_id ?? undefined,
249
- custom: toCustom(thread.metadata),
250
- };
251
- },
252
-
253
- unstable_Provider,
48
+ ...base,
254
49
  unstable_useAdapters,
255
50
  };
256
- }, [cloud, unstable_Provider, unstable_useAdapters]);
51
+ }, [base, unstable_useAdapters]);
257
52
  };
@@ -131,6 +131,7 @@ export const useExternalMessageConverter = <T extends WeakKey>({
131
131
  message: cache,
132
132
  generatedFallbackMessages,
133
133
  },
134
+ state.metadata.cancelledMessageIds,
134
135
  ),
135
136
  );
136
137