@assistant-ui/core 0.3.14 → 0.3.16

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 (341) hide show
  1. package/dist/adapters/voice.js +11 -0
  2. package/dist/adapters/voice.js.map +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/internal.d.ts +8 -2
  5. package/dist/internal.js +9 -3
  6. package/dist/model-context/frame/host.d.ts +1 -1
  7. package/dist/model-context/frame/host.d.ts.map +1 -1
  8. package/dist/model-context/frame/host.js +20 -5
  9. package/dist/model-context/frame/host.js.map +1 -1
  10. package/dist/model-context/frame/provider.d.ts +6 -0
  11. package/dist/model-context/frame/provider.d.ts.map +1 -1
  12. package/dist/model-context/frame/provider.js +126 -16
  13. package/dist/model-context/frame/provider.js.map +1 -1
  14. package/dist/model-context/frame/types.d.ts +4 -1
  15. package/dist/model-context/frame/types.d.ts.map +1 -1
  16. package/dist/model-context/frame/types.js.map +1 -1
  17. package/dist/model-context/interactable-composer-metadata.d.ts +1 -1
  18. package/dist/model-context/interactable-composer-metadata.js +1 -1
  19. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  20. package/dist/model-context/registry.d.ts +2 -2
  21. package/dist/model-context/registry.d.ts.map +1 -1
  22. package/dist/model-context/registry.js +9 -28
  23. package/dist/model-context/registry.js.map +1 -1
  24. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  25. package/dist/react/client/InMemoryThreadList.js +24 -20
  26. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  27. package/dist/react/client/Interactables.d.ts.map +1 -1
  28. package/dist/react/client/Interactables.js +59 -177
  29. package/dist/react/client/Interactables.js.map +1 -1
  30. package/dist/react/client/RemoteThreadList.d.ts +1 -1
  31. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  32. package/dist/react/client/RemoteThreadList.js +117 -78
  33. package/dist/react/client/RemoteThreadList.js.map +1 -1
  34. package/dist/react/index.d.ts +2 -1
  35. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  36. package/dist/react/interactables-legacy/Interactables.js +56 -174
  37. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  38. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts +24 -0
  39. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -0
  40. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +138 -0
  41. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -0
  42. package/dist/react/primitive-hooks/useActionBarCopy.js +7 -1
  43. package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
  44. package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts +3 -0
  45. package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts.map +1 -1
  46. package/dist/react/primitive-hooks/useThreadIsEmpty.js +3 -0
  47. package/dist/react/primitive-hooks/useThreadIsEmpty.js.map +1 -1
  48. package/dist/react/primitive-hooks/useThreadIsRunning.d.ts +3 -0
  49. package/dist/react/primitive-hooks/useThreadIsRunning.d.ts.map +1 -1
  50. package/dist/react/primitive-hooks/useThreadIsRunning.js +3 -0
  51. package/dist/react/primitive-hooks/useThreadIsRunning.js.map +1 -1
  52. package/dist/react/primitives/composer/ComposerIf.js.map +1 -1
  53. package/dist/react/primitives/composer/ComposerQueue.d.ts +2 -1
  54. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  55. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  56. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +3 -1
  57. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  58. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +6 -0
  59. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  60. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +7 -1
  61. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  62. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +174 -64
  63. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  64. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  65. package/dist/react/runtimes/RemoteThreadResource.js +2 -0
  66. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  67. package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -1
  68. package/dist/react/runtimes/assistant-transport/runManager.js +2 -12
  69. package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -1
  70. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
  71. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js +1 -1
  72. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  73. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +3 -10
  74. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  75. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +20 -67
  76. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  77. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts +12 -0
  78. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts.map +1 -0
  79. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js +10 -0
  80. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js.map +1 -0
  81. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  82. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +38 -5
  83. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  84. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -1
  85. package/dist/react/runtimes/createMessageConverter.d.ts +2 -1
  86. package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
  87. package/dist/react/runtimes/external-message-converter.d.ts +6 -25
  88. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  89. package/dist/react/runtimes/external-message-converter.js +33 -233
  90. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  91. package/dist/runtime/api/composer-runtime.d.ts +1 -1
  92. package/dist/runtime/api/composer-runtime.js.map +1 -1
  93. package/dist/runtime/api/thread-runtime.d.ts +1 -2
  94. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  95. package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -1
  96. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  97. package/dist/runtime/base/base-composer-runtime-core.js +14 -29
  98. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  99. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -4
  100. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  101. package/dist/runtime/base/base-thread-runtime-core.js +3 -9
  102. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  103. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -1
  104. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  105. package/dist/runtime/base/default-thread-composer-runtime-core.js +1 -1
  106. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  107. package/dist/runtime/interfaces/composer-runtime-core.d.ts +1 -1
  108. package/dist/runtime/interfaces/thread-runtime-core.d.ts +1 -1
  109. package/dist/runtime/internal.d.ts +2 -1
  110. package/dist/runtime/internal.js +2 -1
  111. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +1 -1
  112. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  113. package/dist/runtime/queue/message-queue.js +1 -1
  114. package/dist/runtime/queue/message-queue.js.map +1 -1
  115. package/dist/runtime/queue/queue-item.d.ts +12 -0
  116. package/dist/runtime/queue/queue-item.d.ts.map +1 -0
  117. package/dist/runtime/queue/queue-item.js +6 -0
  118. package/dist/runtime/queue/queue-item.js.map +1 -0
  119. package/dist/runtime/utils/abortable-thread-load.d.ts +15 -0
  120. package/dist/runtime/utils/abortable-thread-load.d.ts.map +1 -0
  121. package/dist/runtime/utils/abortable-thread-load.js +35 -0
  122. package/dist/runtime/utils/abortable-thread-load.js.map +1 -0
  123. package/dist/runtime/utils/attachment-add-operations.d.ts +18 -0
  124. package/dist/runtime/utils/attachment-add-operations.d.ts.map +1 -0
  125. package/dist/runtime/utils/attachment-add-operations.js +43 -0
  126. package/dist/runtime/utils/attachment-add-operations.js.map +1 -0
  127. package/dist/runtime/utils/auto-status.d.ts +6 -1
  128. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  129. package/dist/runtime/utils/auto-status.js +7 -1
  130. package/dist/runtime/utils/auto-status.js.map +1 -1
  131. package/dist/runtime/utils/external-message-conversion.d.ts +47 -0
  132. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -0
  133. package/dist/runtime/utils/external-message-conversion.js +221 -0
  134. package/dist/runtime/utils/external-message-conversion.js.map +1 -0
  135. package/dist/runtime/utils/external-store-message.d.ts +2 -1
  136. package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
  137. package/dist/runtime/utils/external-store-message.js +2 -1
  138. package/dist/runtime/utils/external-store-message.js.map +1 -1
  139. package/dist/runtime/utils/message-repository-session.d.ts +30 -0
  140. package/dist/runtime/utils/message-repository-session.d.ts.map +1 -0
  141. package/dist/runtime/utils/message-repository-session.js +130 -0
  142. package/dist/runtime/utils/message-repository-session.js.map +1 -0
  143. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  144. package/dist/runtime/utils/message-repository.js +30 -22
  145. package/dist/runtime/utils/message-repository.js.map +1 -1
  146. package/dist/runtime/utils/pending-tool-calls.d.ts +20 -0
  147. package/dist/runtime/utils/pending-tool-calls.d.ts.map +1 -0
  148. package/dist/runtime/utils/pending-tool-calls.js +22 -0
  149. package/dist/runtime/utils/pending-tool-calls.js.map +1 -0
  150. package/dist/runtime/utils/thread-message-like.d.ts +2 -1
  151. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  152. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  153. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +2 -5
  154. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  155. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +3 -10
  156. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  157. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +19 -1
  158. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  159. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +124 -20
  160. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  161. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +102 -0
  162. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -0
  163. package/dist/runtimes/inert/InertThreadRuntimeCore.js +156 -0
  164. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -0
  165. package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -1
  166. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  167. package/dist/runtimes/local/local-thread-runtime-core.js +45 -8
  168. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  169. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +12 -60
  170. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  171. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +10 -145
  172. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
  173. package/dist/runtimes/remote-thread-list/adapter-changed.d.ts +9 -0
  174. package/dist/runtimes/remote-thread-list/adapter-changed.d.ts.map +1 -0
  175. package/dist/runtimes/remote-thread-list/adapter-changed.js +13 -0
  176. package/dist/runtimes/remote-thread-list/adapter-changed.js.map +1 -0
  177. package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
  178. package/dist/runtimes/remote-thread-list/empty-thread-core.js +16 -155
  179. package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
  180. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts +2 -0
  181. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
  182. package/dist/runtimes/remote-thread-list/optimistic-state.js +11 -0
  183. package/dist/runtimes/remote-thread-list/optimistic-state.js.map +1 -1
  184. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +12 -1
  185. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  186. package/dist/runtimes/remote-thread-list/remote-thread-state.js +77 -1
  187. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  188. package/dist/runtimes/remote-thread-list/types.d.ts +3 -0
  189. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  190. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  191. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +2 -0
  192. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  193. package/dist/store/clients/external-thread.d.ts.map +1 -1
  194. package/dist/store/clients/external-thread.js +75 -100
  195. package/dist/store/clients/external-thread.js.map +1 -1
  196. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  197. package/dist/store/clients/single-thread-list.js +1 -1
  198. package/dist/store/clients/single-thread-list.js.map +1 -1
  199. package/dist/store/clients/suggestions.d.ts.map +1 -1
  200. package/dist/store/clients/suggestions.js +16 -7
  201. package/dist/store/clients/suggestions.js.map +1 -1
  202. package/dist/store/index.d.ts +4 -2
  203. package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -1
  204. package/dist/store/runtime-clients/handle-runtime-action.js +2 -0
  205. package/dist/store/runtime-clients/handle-runtime-action.js.map +1 -1
  206. package/dist/store/scope-registration.d.ts +1 -1
  207. package/dist/store/scopes/composer.d.ts +2 -1
  208. package/dist/store/scopes/composer.d.ts.map +1 -1
  209. package/dist/store/scopes/message.d.ts +1 -1
  210. package/dist/store/scopes/queue-item.d.ts +2 -9
  211. package/dist/store/scopes/queue-item.d.ts.map +1 -1
  212. package/dist/store/scopes/queue-item.js +0 -6
  213. package/dist/types/message.d.ts +8 -4
  214. package/dist/types/message.d.ts.map +1 -1
  215. package/dist/types/message.js.map +1 -1
  216. package/dist/utils/data-url.d.ts +14 -1
  217. package/dist/utils/data-url.d.ts.map +1 -1
  218. package/dist/utils/data-url.js +13 -1
  219. package/dist/utils/data-url.js.map +1 -1
  220. package/dist/utils/invoke-user-callback.d.ts +5 -0
  221. package/dist/utils/invoke-user-callback.d.ts.map +1 -0
  222. package/dist/utils/invoke-user-callback.js +19 -0
  223. package/dist/utils/invoke-user-callback.js.map +1 -0
  224. package/dist/utils/normalizePartStatus.js.map +1 -1
  225. package/dist/utils/silent-runtime-action.d.ts +6 -0
  226. package/dist/utils/silent-runtime-action.d.ts.map +1 -0
  227. package/dist/utils/silent-runtime-action.js +7 -0
  228. package/dist/utils/silent-runtime-action.js.map +1 -0
  229. package/package.json +10 -10
  230. package/src/adapters/voice.test.ts +66 -1
  231. package/src/adapters/voice.ts +8 -0
  232. package/src/internal.ts +13 -0
  233. package/src/model-context/frame/host.test.ts +152 -9
  234. package/src/model-context/frame/host.ts +23 -5
  235. package/src/model-context/frame/provider.test.ts +601 -19
  236. package/src/model-context/frame/provider.ts +158 -20
  237. package/src/model-context/frame/types.ts +5 -0
  238. package/src/model-context/interactable-composer-metadata.ts +1 -1
  239. package/src/model-context/registry.test.ts +44 -0
  240. package/src/model-context/registry.ts +13 -35
  241. package/src/react/client/InMemoryThreadList.test.tsx +49 -0
  242. package/src/react/client/InMemoryThreadList.ts +7 -1
  243. package/src/react/client/Interactables.ts +19 -178
  244. package/src/react/client/RemoteThreadList.test.ts +292 -0
  245. package/src/react/client/RemoteThreadList.ts +119 -82
  246. package/src/react/interactables-legacy/Interactables.ts +20 -177
  247. package/src/react/interactables-shared/useInteractablePersistenceQueue.test.tsx +161 -0
  248. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +206 -0
  249. package/src/react/primitive-hooks/useActionBarCopy.test.ts +15 -0
  250. package/src/react/primitive-hooks/useActionBarCopy.ts +13 -3
  251. package/src/react/primitive-hooks/useThreadIsEmpty.ts +3 -0
  252. package/src/react/primitive-hooks/useThreadIsRunning.ts +3 -0
  253. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +12 -0
  254. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.republish.test.ts +87 -0
  255. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +251 -76
  256. package/src/react/runtimes/RemoteThreadResource.ts +2 -0
  257. package/src/react/runtimes/assistant-transport/runManager.ts +4 -17
  258. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +58 -0
  259. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +3 -1
  260. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +92 -0
  261. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +40 -141
  262. package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.test.ts +37 -0
  263. package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.ts +18 -0
  264. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +98 -3
  265. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +68 -6
  266. package/src/react/runtimes/external-message-converter.test.ts +0 -14
  267. package/src/react/runtimes/external-message-converter.test.tsx +77 -0
  268. package/src/react/runtimes/external-message-converter.ts +58 -481
  269. package/src/react/runtimes/useLocalRuntime.test.tsx +5 -5
  270. package/src/runtime/api/composer-runtime.ts +1 -1
  271. package/src/runtime/api/message-runtime.test.ts +28 -1
  272. package/src/runtime/base/base-composer-runtime-core.ts +20 -40
  273. package/src/runtime/base/base-thread-runtime-core.test.ts +16 -0
  274. package/src/runtime/base/base-thread-runtime-core.ts +6 -11
  275. package/src/runtime/base/default-thread-composer-runtime-core.ts +1 -4
  276. package/src/runtime/interfaces/composer-runtime-core.ts +1 -1
  277. package/src/runtime/interfaces/thread-runtime-core.ts +1 -1
  278. package/src/runtime/internal.ts +5 -0
  279. package/src/runtime/queue/external-thread-queue-adapter.ts +1 -1
  280. package/src/runtime/queue/message-queue.ts +1 -4
  281. package/src/runtime/queue/queue-item.ts +10 -0
  282. package/src/runtime/utils/abortable-thread-load.test.ts +87 -0
  283. package/src/runtime/utils/abortable-thread-load.ts +53 -0
  284. package/src/runtime/utils/attachment-add-operations.ts +59 -0
  285. package/src/runtime/utils/auto-status.ts +33 -0
  286. package/src/runtime/utils/external-message-conversion.test.ts +113 -0
  287. package/src/runtime/utils/external-message-conversion.ts +494 -0
  288. package/src/runtime/utils/external-store-message.ts +2 -0
  289. package/src/runtime/utils/message-repository-session.test.ts +195 -0
  290. package/src/runtime/utils/message-repository-session.ts +176 -0
  291. package/src/runtime/utils/message-repository.ts +33 -22
  292. package/src/runtime/utils/pending-tool-calls.test.ts +57 -0
  293. package/src/runtime/utils/pending-tool-calls.ts +35 -0
  294. package/src/runtime/utils/thread-message-like.ts +2 -0
  295. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +6 -14
  296. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +179 -28
  297. package/src/runtimes/inert/InertThreadRuntimeCore.test.ts +253 -0
  298. package/src/runtimes/inert/InertThreadRuntimeCore.ts +232 -0
  299. package/src/runtimes/local/local-thread-runtime-core.test.ts +491 -1
  300. package/src/runtimes/local/local-thread-runtime-core.ts +86 -11
  301. package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +17 -209
  302. package/src/runtimes/remote-thread-list/adapter-changed.ts +10 -0
  303. package/src/runtimes/remote-thread-list/empty-thread-core.ts +28 -221
  304. package/src/runtimes/remote-thread-list/optimistic-state.test.ts +18 -0
  305. package/src/runtimes/remote-thread-list/optimistic-state.ts +13 -0
  306. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +37 -0
  307. package/src/runtimes/remote-thread-list/remote-thread-state.ts +123 -0
  308. package/src/runtimes/remote-thread-list/types.ts +4 -0
  309. package/src/runtimes/tool-invocations/EDGE_CASES.md +17 -0
  310. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +140 -0
  311. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +3 -0
  312. package/src/store/clients/external-thread.ts +17 -62
  313. package/src/store/clients/single-thread-list.ts +4 -1
  314. package/src/store/clients/suggestions.test.ts +31 -0
  315. package/src/store/clients/suggestions.ts +18 -19
  316. package/src/store/runtime-clients/handle-runtime-action.test.ts +40 -0
  317. package/src/store/runtime-clients/handle-runtime-action.ts +3 -0
  318. package/src/store/scopes/message.ts +1 -1
  319. package/src/store/scopes/queue-item.ts +2 -9
  320. package/src/tests/MessageRepository.test.ts +125 -0
  321. package/src/tests/RemoteThreadListThreadListRuntimeCore-errors.test.ts +2 -2
  322. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +194 -0
  323. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +8 -6
  324. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +158 -0
  325. package/src/tests/empty-thread-core.test.ts +6 -0
  326. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +184 -0
  327. package/src/tests/external-store-thread-runtime-core.test.ts +642 -0
  328. package/src/tests/external-thread-attachments.test.tsx +173 -0
  329. package/src/tests/external-thread-isEmpty.test.tsx +100 -0
  330. package/src/tests/remote-thread-list-adapter-switch.test.ts +383 -0
  331. package/src/tests/single-thread-list-item.test.tsx +47 -0
  332. package/src/tests/thread-message-like.test.ts +39 -1
  333. package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +67 -0
  334. package/src/types/message.ts +9 -4
  335. package/src/utils/data-url.test.ts +52 -1
  336. package/src/utils/data-url.ts +21 -0
  337. package/src/utils/invoke-user-callback.test.ts +82 -0
  338. package/src/utils/invoke-user-callback.ts +31 -0
  339. package/src/utils/normalizePartStatus.ts +1 -1
  340. package/src/utils/silent-runtime-action.ts +6 -0
  341. package/dist/store/scopes/queue-item.js.map +0 -1
@@ -0,0 +1,161 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { act, cleanup, renderHook } from "@testing-library/react";
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { useInteractablePersistenceQueue } from "./useInteractablePersistenceQueue";
6
+
7
+ type TestState = Record<string, number>;
8
+
9
+ type PersistenceStatus = {
10
+ isPending: boolean;
11
+ error: unknown;
12
+ };
13
+
14
+ type PersistenceStatusMap = Record<string, PersistenceStatus>;
15
+
16
+ const createDeferred = () => {
17
+ let resolve!: () => void;
18
+ let reject!: (reason: unknown) => void;
19
+ const promise = new Promise<void>((res, rej) => {
20
+ resolve = res;
21
+ reject = rej;
22
+ });
23
+ return { promise, resolve, reject };
24
+ };
25
+
26
+ const renderQueue = (save: (state: TestState) => void | Promise<void>) => {
27
+ let snapshot: TestState = {};
28
+ let persistence: PersistenceStatusMap = {};
29
+ const adapterRef = { current: { save } };
30
+ const updatePersistenceStatus = (
31
+ updater: (prev: PersistenceStatusMap) => PersistenceStatusMap,
32
+ ) => {
33
+ persistence = updater(persistence);
34
+ };
35
+ const hook = renderHook(() =>
36
+ useInteractablePersistenceQueue({
37
+ adapterRef,
38
+ snapshot: () => snapshot,
39
+ updatePersistenceStatus,
40
+ }),
41
+ );
42
+
43
+ return {
44
+ ...hook,
45
+ setState(id: string, value: number) {
46
+ snapshot = { ...snapshot, [id]: value };
47
+ },
48
+ removeStatus(id: string) {
49
+ const { [id]: _, ...rest } = persistence;
50
+ persistence = rest;
51
+ },
52
+ getStatus() {
53
+ return persistence;
54
+ },
55
+ };
56
+ };
57
+
58
+ const flushMicrotasks = () => vi.advanceTimersByTimeAsync(0);
59
+
60
+ beforeEach(() => {
61
+ vi.useFakeTimers();
62
+ });
63
+
64
+ afterEach(() => {
65
+ cleanup();
66
+ vi.useRealTimers();
67
+ vi.restoreAllMocks();
68
+ });
69
+
70
+ describe("useInteractablePersistenceQueue", () => {
71
+ it("coalesces dirty marks inside the debounce window into one save", async () => {
72
+ const save = vi.fn<(state: TestState) => void>();
73
+ const queue = renderQueue(save);
74
+
75
+ queue.setState("a", 1);
76
+ act(() => queue.result.current.schedulePersistence("a"));
77
+ await act(() => vi.advanceTimersByTimeAsync(200));
78
+
79
+ queue.setState("b", 2);
80
+ act(() => queue.result.current.schedulePersistence("b"));
81
+ await act(() => vi.advanceTimersByTimeAsync(200));
82
+
83
+ queue.setState("a", 3);
84
+ act(() => queue.result.current.schedulePersistence("a"));
85
+ await act(() => vi.advanceTimersByTimeAsync(499));
86
+
87
+ expect(save).not.toHaveBeenCalled();
88
+
89
+ await act(() => vi.advanceTimersByTimeAsync(1));
90
+
91
+ expect(save).toHaveBeenCalledOnce();
92
+ expect(save).toHaveBeenCalledWith({ a: 3, b: 2 });
93
+ });
94
+
95
+ it("attributes errors by batch sequence and lets a newer save supersede an older error", async () => {
96
+ const first = createDeferred();
97
+ const second = createDeferred();
98
+ const firstError = new Error("first save failed");
99
+ const secondError = new Error("second save failed");
100
+ const save = vi
101
+ .fn<(state: TestState) => Promise<void>>()
102
+ .mockImplementationOnce(() => first.promise)
103
+ .mockImplementationOnce(() => second.promise);
104
+ const queue = renderQueue(save);
105
+
106
+ queue.setState("a", 1);
107
+ queue.setState("b", 1);
108
+ act(() => {
109
+ queue.result.current.schedulePersistence("a");
110
+ queue.result.current.schedulePersistence("b");
111
+ });
112
+ await act(() => vi.advanceTimersByTimeAsync(500));
113
+
114
+ queue.setState("a", 2);
115
+ act(() => queue.result.current.schedulePersistence("a"));
116
+ let flushPromise!: Promise<void>;
117
+ act(() => {
118
+ flushPromise = queue.result.current.flush();
119
+ });
120
+
121
+ first.reject(firstError);
122
+ await act(flushMicrotasks);
123
+
124
+ expect(save).toHaveBeenCalledTimes(2);
125
+ expect(queue.getStatus()).toEqual({
126
+ a: { isPending: true, error: undefined },
127
+ b: { isPending: false, error: firstError },
128
+ });
129
+
130
+ second.reject(secondError);
131
+ await act(flushMicrotasks);
132
+ await flushPromise;
133
+
134
+ expect(queue.getStatus()).toEqual({
135
+ a: { isPending: false, error: secondError },
136
+ b: { isPending: false, error: firstError },
137
+ });
138
+ });
139
+
140
+ it("does not recreate a removed status when an in-flight save rejects", async () => {
141
+ const pending = createDeferred();
142
+ const save = vi
143
+ .fn<(state: TestState) => Promise<void>>()
144
+ .mockImplementationOnce(() => pending.promise);
145
+ const queue = renderQueue(save);
146
+
147
+ queue.setState("removed", 1);
148
+ act(() => queue.result.current.schedulePersistence("removed"));
149
+ await act(() => vi.advanceTimersByTimeAsync(500));
150
+
151
+ expect(queue.getStatus()).toEqual({
152
+ removed: { isPending: true, error: undefined },
153
+ });
154
+
155
+ queue.removeStatus("removed");
156
+ pending.reject(new Error("save failed"));
157
+ await act(flushMicrotasks);
158
+
159
+ expect(queue.getStatus()).toEqual({});
160
+ });
161
+ });
@@ -0,0 +1,206 @@
1
+ import { useCallback, useRef, type RefObject } from "react";
2
+
3
+ const PERSISTENCE_DEBOUNCE_MS = 500;
4
+
5
+ type PersistenceAdapter<State> = {
6
+ save(state: State): void | Promise<void>;
7
+ };
8
+
9
+ type PersistenceStatus = {
10
+ isPending: boolean;
11
+ error: unknown;
12
+ };
13
+
14
+ type PersistenceStatusMap = Record<string, PersistenceStatus>;
15
+
16
+ type PersistenceStatusUpdater = (
17
+ updater: (prev: PersistenceStatusMap) => PersistenceStatusMap,
18
+ ) => void;
19
+
20
+ type UseInteractablePersistenceQueueOptions<State> = {
21
+ adapterRef: RefObject<PersistenceAdapter<State> | undefined>;
22
+ snapshot: () => State;
23
+ updatePersistenceStatus: PersistenceStatusUpdater;
24
+ };
25
+
26
+ export const useInteractablePersistenceQueue = <State>({
27
+ adapterRef,
28
+ snapshot,
29
+ updatePersistenceStatus,
30
+ }: UseInteractablePersistenceQueueOptions<State>) => {
31
+ const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(
32
+ undefined,
33
+ );
34
+ const syncSeqRef = useRef(0);
35
+ const latestSyncSeqByIdRef = useRef(new Map<string, number>());
36
+ const inFlightPersistenceRef = useRef(0);
37
+ const flushResolversRef = useRef<Array<() => void>>([]);
38
+ const dirtyIdsRef = useRef(new Set<string>());
39
+
40
+ type PersistenceBatch = {
41
+ adapter: PersistenceAdapter<State>;
42
+ payload: State;
43
+ dirtyIds: Set<string>;
44
+ seq: number;
45
+ };
46
+
47
+ const outgoingQueueRef = useRef<PersistenceBatch[]>([]);
48
+ const runPersistenceRef = useRef<(batch?: PersistenceBatch) => void>(
49
+ () => {},
50
+ );
51
+
52
+ const takeDirtyBatch = useCallback(
53
+ (adapter: PersistenceAdapter<State>): PersistenceBatch | undefined => {
54
+ if (dirtyIdsRef.current.size === 0) return;
55
+ const dirtyIds = new Set(dirtyIdsRef.current);
56
+ dirtyIdsRef.current.clear();
57
+ const seq = ++syncSeqRef.current;
58
+ for (const id of dirtyIds) latestSyncSeqByIdRef.current.set(id, seq);
59
+ return { adapter, payload: snapshot(), dirtyIds, seq };
60
+ },
61
+ [snapshot],
62
+ );
63
+
64
+ const enqueuePersistence = useCallback(
65
+ (adapter: PersistenceAdapter<State>) => {
66
+ const batch = takeDirtyBatch(adapter);
67
+ if (!batch) return;
68
+ if (inFlightPersistenceRef.current === 0) {
69
+ runPersistenceRef.current(batch);
70
+ } else {
71
+ outgoingQueueRef.current.push(batch);
72
+ }
73
+ },
74
+ [takeDirtyBatch],
75
+ );
76
+
77
+ const runPersistence = useCallback(
78
+ async (batch?: PersistenceBatch) => {
79
+ const resolved =
80
+ batch ??
81
+ (adapterRef.current ? takeDirtyBatch(adapterRef.current) : undefined);
82
+ if (!resolved) {
83
+ if (inFlightPersistenceRef.current === 0) {
84
+ for (const resolve of flushResolversRef.current) resolve();
85
+ flushResolversRef.current = [];
86
+ }
87
+ return;
88
+ }
89
+
90
+ const { adapter, payload, dirtyIds, seq } = resolved;
91
+ inFlightPersistenceRef.current += 1;
92
+
93
+ updatePersistenceStatus((prev) => ({
94
+ ...prev,
95
+ ...Object.fromEntries(
96
+ [...dirtyIds].map((id) => [
97
+ id,
98
+ { isPending: true, error: undefined },
99
+ ]),
100
+ ),
101
+ }));
102
+
103
+ const settleBatch = (status: PersistenceStatus | undefined) => {
104
+ const settledIds: string[] = [];
105
+ for (const id of dirtyIds) {
106
+ if (
107
+ latestSyncSeqByIdRef.current.get(id) !== seq ||
108
+ dirtyIdsRef.current.has(id)
109
+ )
110
+ continue;
111
+ latestSyncSeqByIdRef.current.delete(id);
112
+ settledIds.push(id);
113
+ }
114
+ if (settledIds.length === 0) return;
115
+ updatePersistenceStatus((prev) => {
116
+ let changed = false;
117
+ const persistence = { ...prev };
118
+ for (const id of settledIds) {
119
+ if (prev[id] === undefined) continue;
120
+ if (status === undefined) delete persistence[id];
121
+ else persistence[id] = status;
122
+ changed = true;
123
+ }
124
+ return changed ? persistence : prev;
125
+ });
126
+ };
127
+
128
+ try {
129
+ await adapter.save(payload);
130
+ settleBatch(undefined);
131
+ } catch (e) {
132
+ settleBatch({ isPending: false, error: e });
133
+ } finally {
134
+ inFlightPersistenceRef.current -= 1;
135
+ const next =
136
+ outgoingQueueRef.current.shift() ??
137
+ (adapterRef.current && dirtyIdsRef.current.size > 0
138
+ ? takeDirtyBatch(adapterRef.current)
139
+ : undefined);
140
+ if (next) {
141
+ if (debounceTimerRef.current !== undefined) {
142
+ clearTimeout(debounceTimerRef.current);
143
+ debounceTimerRef.current = undefined;
144
+ }
145
+ runPersistenceRef.current(next);
146
+ } else if (inFlightPersistenceRef.current === 0) {
147
+ for (const resolve of flushResolversRef.current) resolve();
148
+ flushResolversRef.current = [];
149
+ }
150
+ }
151
+ },
152
+ [adapterRef, takeDirtyBatch, updatePersistenceStatus],
153
+ );
154
+ runPersistenceRef.current = (nextBatch) => {
155
+ void runPersistence(nextBatch);
156
+ };
157
+
158
+ const flushIfPending = useCallback(() => {
159
+ if (debounceTimerRef.current !== undefined) {
160
+ clearTimeout(debounceTimerRef.current);
161
+ debounceTimerRef.current = undefined;
162
+ }
163
+ if (adapterRef.current) enqueuePersistence(adapterRef.current);
164
+ }, [adapterRef, enqueuePersistence]);
165
+
166
+ const schedulePersistence = useCallback(
167
+ (id: string) => {
168
+ if (!adapterRef.current) return;
169
+ dirtyIdsRef.current.add(id);
170
+ if (debounceTimerRef.current !== undefined) {
171
+ clearTimeout(debounceTimerRef.current);
172
+ }
173
+ debounceTimerRef.current = setTimeout(() => {
174
+ debounceTimerRef.current = undefined;
175
+ if (inFlightPersistenceRef.current === 0 && adapterRef.current) {
176
+ enqueuePersistence(adapterRef.current);
177
+ } else {
178
+ debounceTimerRef.current = setTimeout(() => {
179
+ debounceTimerRef.current = undefined;
180
+ if (adapterRef.current) enqueuePersistence(adapterRef.current);
181
+ }, PERSISTENCE_DEBOUNCE_MS);
182
+ }
183
+ }, PERSISTENCE_DEBOUNCE_MS);
184
+ },
185
+ [adapterRef, enqueuePersistence],
186
+ );
187
+
188
+ const flush = useCallback(async () => {
189
+ if (debounceTimerRef.current !== undefined) {
190
+ clearTimeout(debounceTimerRef.current);
191
+ debounceTimerRef.current = undefined;
192
+ }
193
+ const hasWork =
194
+ inFlightPersistenceRef.current > 0 ||
195
+ dirtyIdsRef.current.size > 0 ||
196
+ outgoingQueueRef.current.length > 0;
197
+ if (!hasWork) return;
198
+ const p = new Promise<void>((resolve) => {
199
+ flushResolversRef.current.push(resolve);
200
+ });
201
+ if (adapterRef.current) enqueuePersistence(adapterRef.current);
202
+ return p;
203
+ }, [adapterRef, enqueuePersistence]);
204
+
205
+ return { flushIfPending, schedulePersistence, flush };
206
+ };
@@ -82,6 +82,21 @@ describe("useActionBarCopy", () => {
82
82
  expect(mocks.setIsCopied).not.toHaveBeenCalled();
83
83
  });
84
84
 
85
+ it("does not report copy success when the clipboard handler throws synchronously", async () => {
86
+ const copyToClipboard = vi.fn(() => {
87
+ throw new TypeError(
88
+ "Cannot read properties of undefined (reading 'writeText')",
89
+ );
90
+ });
91
+ const { result } = renderHook(() => useActionBarCopy({ copyToClipboard }));
92
+
93
+ expect(() => result.current.copy()).not.toThrow();
94
+ await Promise.resolve();
95
+
96
+ expect(copyToClipboard).toHaveBeenCalledWith("Hello");
97
+ expect(mocks.setIsCopied).not.toHaveBeenCalled();
98
+ });
99
+
85
100
  it("keeps copy success visible for the full duration after copying again", async () => {
86
101
  vi.useFakeTimers();
87
102
  const copyToClipboard = vi.fn();
@@ -40,9 +40,19 @@ export const useActionBarCopy = ({
40
40
  if (!valueToCopy) return;
41
41
  const scopeGeneration = scopeGenerationRef.current;
42
42
 
43
- // The rejection handler swallows clipboard write failures (permission denied,
44
- // API unavailable) so they don't surface as unhandled promise rejections.
45
- Promise.resolve(copyToClipboard(valueToCopy)).then(
43
+ // The writer runs synchronously inside the press so the user-gesture gating
44
+ // of navigator.clipboard survives (WebKit scopes it to the stack); the try
45
+ // contains a synchronously throwing caller-supplied writer, and the rejection
46
+ // handler swallows clipboard failures (permission denied, API unavailable) so
47
+ // they don't surface as unhandled rejections.
48
+ let write: void | Promise<void>;
49
+ try {
50
+ write = copyToClipboard(valueToCopy);
51
+ } catch {
52
+ return;
53
+ }
54
+
55
+ Promise.resolve(write).then(
46
56
  () => {
47
57
  if (scopeGeneration !== scopeGenerationRef.current) return;
48
58
 
@@ -1,5 +1,8 @@
1
1
  import { useAuiState } from "@assistant-ui/store";
2
2
 
3
+ /**
4
+ * @deprecated Use `useAuiState((s) => s.thread.isEmpty)` instead.
5
+ */
3
6
  export const useThreadIsEmpty = (): boolean => {
4
7
  return useAuiState((s) => s.thread.isEmpty);
5
8
  };
@@ -1,5 +1,8 @@
1
1
  import { useAuiState } from "@assistant-ui/store";
2
2
 
3
+ /**
4
+ * @deprecated Use `useAuiState((s) => s.thread.isRunning)` instead.
5
+ */
3
6
  export const useThreadIsRunning = (): boolean => {
4
7
  return useAuiState((s) => s.thread.isRunning);
5
8
  };
@@ -173,6 +173,18 @@ export class RemoteThreadListHookInstanceManager extends BaseSubscribable {
173
173
  this._trackRunning(instance);
174
174
  }
175
175
  this._notifySubscribers();
176
+ if (previousRuntime !== undefined && previousRuntime !== runtime) {
177
+ for (const callback of this.replacedSubscribers) callback();
178
+ }
179
+ }
180
+
181
+ private replacedSubscribers = new Set<() => void>();
182
+
183
+ public __internal_subscribeRuntimeReplaced(
184
+ callback: () => void,
185
+ ): Unsubscribe {
186
+ this.replacedSubscribers.add(callback);
187
+ return () => this.replacedSubscribers.delete(callback);
176
188
  }
177
189
 
178
190
  private _trackRunning(instance: RemoteThreadListHookInstance) {
@@ -0,0 +1,87 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { RemoteThreadListThreadListRuntimeCore } from "./RemoteThreadListThreadListRuntimeCore";
3
+ import { ThreadListRuntimeImpl } from "../../runtime/api/thread-list-runtime";
4
+ import { ReadonlyThreadRuntimeCore } from "../../runtimes/readonly/ReadonlyThreadRuntimeCore";
5
+ import {
6
+ contextProvider,
7
+ makeAdapter,
8
+ } from "../../tests/remote-thread-list-test-helpers";
9
+ import type { ThreadMessage } from "../../types/message";
10
+
11
+ const message = (id: string): ThreadMessage =>
12
+ ({
13
+ id,
14
+ role: "user",
15
+ content: [{ type: "text", text: id }],
16
+ createdAt: new Date(0),
17
+ attachments: [],
18
+ metadata: { custom: {} },
19
+ }) as unknown as ThreadMessage;
20
+
21
+ const setup = () => {
22
+ const core = new RemoteThreadListThreadListRuntimeCore(
23
+ { adapter: makeAdapter(), runtimeHook: () => ({}) as never },
24
+ contextProvider,
25
+ );
26
+ const hookManager = (
27
+ core as unknown as {
28
+ _hookManager: {
29
+ _publishThreadRuntime: (
30
+ id: string,
31
+ runtime: ReadonlyThreadRuntimeCore,
32
+ generation: number,
33
+ ) => void;
34
+ instances: Map<string, { generation: number }>;
35
+ };
36
+ }
37
+ )._hookManager;
38
+ const threadId = core.mainThreadId!;
39
+ const publish = (runtime: ReadonlyThreadRuntimeCore) =>
40
+ hookManager._publishThreadRuntime(
41
+ threadId,
42
+ runtime,
43
+ hookManager.instances.get(threadId)!.generation,
44
+ );
45
+ return { core, publish };
46
+ };
47
+
48
+ describe("RemoteThreadListThreadListRuntimeCore republish", () => {
49
+ it("keeps the subscribed main thread state on the republished core", async () => {
50
+ const { core, publish } = setup();
51
+ const first = new ReadonlyThreadRuntimeCore();
52
+ first.setMessages([message("m1")]);
53
+ publish(first);
54
+
55
+ const thread = new ThreadListRuntimeImpl(core).main;
56
+ thread.subscribe(() => {});
57
+ expect(thread.getState().messages.map((m) => m.id)).toEqual(["m1"]);
58
+
59
+ const second = new ReadonlyThreadRuntimeCore();
60
+ second.setMessages([message("m2")]);
61
+ publish(second);
62
+ await Promise.resolve();
63
+
64
+ const ids = thread.getState().messages.map((m) => m.id);
65
+ expect(ids).toEqual(["m2"]);
66
+ for (const id of ids) {
67
+ expect(() => thread.getMessageById(id).getState()).not.toThrow();
68
+ }
69
+ });
70
+
71
+ it("notifies thread subscribers when the core is republished", async () => {
72
+ const { core, publish } = setup();
73
+ publish(new ReadonlyThreadRuntimeCore());
74
+
75
+ const thread = new ThreadListRuntimeImpl(core).main;
76
+ let notified = 0;
77
+ thread.subscribe(() => notified++);
78
+
79
+ const next = new ReadonlyThreadRuntimeCore();
80
+ next.setMessages([message("m1")]);
81
+ publish(next);
82
+ await Promise.resolve();
83
+
84
+ expect(notified).toBeGreaterThan(0);
85
+ expect(thread.getState().messages.map((m) => m.id)).toEqual(["m1"]);
86
+ });
87
+ });