@assistant-ui/core 0.3.17 → 0.3.19

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 (470) hide show
  1. package/dist/adapters/attachment.d.ts.map +1 -1
  2. package/dist/adapters/attachment.js +5 -4
  3. package/dist/adapters/attachment.js.map +1 -1
  4. package/dist/adapters/speech.d.ts.map +1 -1
  5. package/dist/adapters/speech.js +13 -3
  6. package/dist/adapters/speech.js.map +1 -1
  7. package/dist/adapters/thread-history.d.ts +4 -0
  8. package/dist/adapters/thread-history.d.ts.map +1 -1
  9. package/dist/index.d.ts +3 -3
  10. package/dist/index.js.map +1 -1
  11. package/dist/internal.d.ts +4 -3
  12. package/dist/internal.js +3 -2
  13. package/dist/model-context/frame/host.d.ts.map +1 -1
  14. package/dist/model-context/frame/host.js +35 -14
  15. package/dist/model-context/frame/host.js.map +1 -1
  16. package/dist/model-context/frame/provider.d.ts +2 -0
  17. package/dist/model-context/frame/provider.d.ts.map +1 -1
  18. package/dist/model-context/frame/provider.js +30 -7
  19. package/dist/model-context/frame/provider.js.map +1 -1
  20. package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
  21. package/dist/model-context/interactable-composer-metadata.js +27 -18
  22. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  23. package/dist/model-context/registry.d.ts.map +1 -1
  24. package/dist/model-context/registry.js +3 -5
  25. package/dist/model-context/registry.js.map +1 -1
  26. package/dist/model-context/types.d.ts.map +1 -1
  27. package/dist/model-context/types.js +5 -4
  28. package/dist/model-context/types.js.map +1 -1
  29. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  30. package/dist/react/adapters/LocalStorageThreadListAdapter.js +45 -7
  31. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  32. package/dist/react/client/DataRenderers.d.ts.map +1 -1
  33. package/dist/react/client/DataRenderers.js +20 -15
  34. package/dist/react/client/DataRenderers.js.map +1 -1
  35. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  36. package/dist/react/client/InMemoryThreadList.js +62 -56
  37. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  38. package/dist/react/client/Interactables.d.ts.map +1 -1
  39. package/dist/react/client/Interactables.js +35 -32
  40. package/dist/react/client/Interactables.js.map +1 -1
  41. package/dist/react/client/RemoteThreadList.d.ts +1 -0
  42. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  43. package/dist/react/client/RemoteThreadList.js +154 -143
  44. package/dist/react/client/RemoteThreadList.js.map +1 -1
  45. package/dist/react/client/Tools.d.ts.map +1 -1
  46. package/dist/react/client/Tools.js +18 -16
  47. package/dist/react/client/Tools.js.map +1 -1
  48. package/dist/react/client/interactable-model-context.d.ts.map +1 -1
  49. package/dist/react/client/interactable-model-context.js +3 -2
  50. package/dist/react/client/interactable-model-context.js.map +1 -1
  51. package/dist/react/index.d.ts +2 -2
  52. package/dist/react/index.js +2 -2
  53. package/dist/react/interactables-legacy/Interactables.js +242 -121
  54. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  55. package/dist/react/interactables-legacy/useAssistantInteractable.d.ts.map +1 -1
  56. package/dist/react/interactables-legacy/useAssistantInteractable.js +3 -0
  57. package/dist/react/interactables-legacy/useAssistantInteractable.js.map +1 -1
  58. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
  59. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +11 -9
  60. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
  61. package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
  62. package/dist/react/model-context/makeAssistantTool.js +1 -1
  63. package/dist/react/model-context/makeAssistantTool.js.map +1 -1
  64. package/dist/react/model-context/useAssistantTool.d.ts +2 -2
  65. package/dist/react/model-context/useAssistantTool.js +1 -1
  66. package/dist/react/model-context/useAssistantTool.js.map +1 -1
  67. package/dist/react/model-context/useInteractable.d.ts.map +1 -1
  68. package/dist/react/model-context/useInteractable.js +4 -1
  69. package/dist/react/model-context/useInteractable.js.map +1 -1
  70. package/dist/react/model-context/useInteractableVersions.js +1 -1
  71. package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
  72. package/dist/react/model-context/useToolArgsStatus.js +2 -1
  73. package/dist/react/model-context/useToolArgsStatus.js.map +1 -1
  74. package/dist/react/primitives/message/MessageGroupedParts.d.ts +11 -2
  75. package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
  76. package/dist/react/primitives/message/MessageGroupedParts.js +3 -2
  77. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  78. package/dist/react/primitives/part/PartMessages.d.ts +1 -0
  79. package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
  80. package/dist/react/primitives/part/PartMessages.js +1 -0
  81. package/dist/react/primitives/part/PartMessages.js.map +1 -1
  82. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  83. package/dist/react/primitives/thread/ThreadMessages.js +6 -5
  84. package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
  85. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  86. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +16 -8
  87. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  88. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +5 -1
  89. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  90. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +105 -104
  91. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  92. package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -0
  93. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  94. package/dist/react/runtimes/RemoteThreadResource.js +13 -11
  95. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  96. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +5 -24
  97. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  98. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +354 -226
  99. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  100. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +1 -0
  101. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  102. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +22 -2
  103. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  104. package/dist/react/runtimes/cloud/auiV0.d.ts +40 -15
  105. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  106. package/dist/react/runtimes/cloud/auiV0.js +62 -15
  107. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  108. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +2 -1
  109. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
  110. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +6 -1
  111. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -1
  112. package/dist/react/runtimes/cloud/sdkIdentity.d.ts +6 -0
  113. package/dist/react/runtimes/cloud/sdkIdentity.d.ts.map +1 -0
  114. package/dist/react/runtimes/cloud/sdkIdentity.js +9 -0
  115. package/dist/react/runtimes/cloud/sdkIdentity.js.map +1 -0
  116. package/dist/react/runtimes/external-message-converter.d.ts +12 -2
  117. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  118. package/dist/react/runtimes/external-message-converter.js +26 -61
  119. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  120. package/dist/react/runtimes/useExternalStoreRuntime.js +75 -46
  121. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  122. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  123. package/dist/react/runtimes/useRemoteThreadListRuntime.js +64 -24
  124. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  125. package/dist/react/utils/groupParts.d.ts +6 -1
  126. package/dist/react/utils/groupParts.d.ts.map +1 -1
  127. package/dist/react/utils/groupParts.js +6 -0
  128. package/dist/react/utils/groupParts.js.map +1 -1
  129. package/dist/react/utils/useJSONEqual.d.ts +3 -1
  130. package/dist/react/utils/useJSONEqual.d.ts.map +1 -1
  131. package/dist/react/utils/useJSONEqual.js +6 -1
  132. package/dist/react/utils/useJSONEqual.js.map +1 -1
  133. package/dist/react/utils/useJSONSchemaDependency.d.ts +7 -0
  134. package/dist/react/utils/useJSONSchemaDependency.d.ts.map +1 -0
  135. package/dist/react/utils/useJSONSchemaDependency.js +20 -0
  136. package/dist/react/utils/useJSONSchemaDependency.js.map +1 -0
  137. package/dist/runtime/api/thread-list-item-runtime.d.ts +7 -3
  138. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  139. package/dist/runtime/api/thread-list-item-runtime.js +2 -2
  140. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  141. package/dist/runtime/api/thread-list-runtime.d.ts +3 -0
  142. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  143. package/dist/runtime/api/thread-list-runtime.js +18 -14
  144. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  145. package/dist/runtime/api/thread-runtime.d.ts +4 -0
  146. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  147. package/dist/runtime/api/thread-runtime.js +3 -6
  148. package/dist/runtime/api/thread-runtime.js.map +1 -1
  149. package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -0
  150. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  151. package/dist/runtime/base/base-composer-runtime-core.js +62 -22
  152. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  153. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -0
  154. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  155. package/dist/runtime/base/base-thread-runtime-core.js +161 -69
  156. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  157. package/dist/runtime/interfaces/composer-runtime-core.d.ts +12 -5
  158. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  159. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +4 -1
  160. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  161. package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
  162. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  163. package/dist/runtime/queue/message-queue.d.ts +5 -0
  164. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  165. package/dist/runtime/queue/message-queue.js +61 -6
  166. package/dist/runtime/queue/message-queue.js.map +1 -1
  167. package/dist/runtime/utils/auto-status.d.ts +6 -2
  168. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  169. package/dist/runtime/utils/auto-status.js +12 -4
  170. package/dist/runtime/utils/auto-status.js.map +1 -1
  171. package/dist/runtime/utils/external-message-conversion.d.ts +16 -4
  172. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  173. package/dist/runtime/utils/external-message-conversion.js +49 -13
  174. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  175. package/dist/runtime/utils/message-repository.d.ts +1 -0
  176. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  177. package/dist/runtime/utils/message-repository.js +18 -9
  178. package/dist/runtime/utils/message-repository.js.map +1 -1
  179. package/dist/runtime/utils/thread-message-like.d.ts +2 -1
  180. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  181. package/dist/runtime/utils/thread-message-like.js +5 -3
  182. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  183. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  184. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +21 -27
  185. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  186. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  187. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +8 -1
  188. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  189. package/dist/runtimes/internal.d.ts +3 -3
  190. package/dist/runtimes/internal.js +2 -2
  191. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  192. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  193. package/dist/runtimes/local/local-thread-runtime-core.js +4 -1
  194. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  195. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +18 -1
  196. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  197. package/dist/runtimes/remote-thread-list/remote-thread-state.js +130 -42
  198. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  199. package/dist/runtimes/remote-thread-list/title-generation.d.ts +49 -0
  200. package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -0
  201. package/dist/runtimes/remote-thread-list/title-generation.js +197 -0
  202. package/dist/runtimes/remote-thread-list/title-generation.js.map +1 -0
  203. package/dist/runtimes/remote-thread-list/title.d.ts +3 -1
  204. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -1
  205. package/dist/runtimes/remote-thread-list/title.js +9 -1
  206. package/dist/runtimes/remote-thread-list/title.js.map +1 -1
  207. package/dist/runtimes/remote-thread-list/types.d.ts +10 -0
  208. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  209. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  210. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +2 -1
  211. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  212. package/dist/store/clients/external-thread.d.ts.map +1 -1
  213. package/dist/store/clients/external-thread.js +260 -203
  214. package/dist/store/clients/external-thread.js.map +1 -1
  215. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  216. package/dist/store/clients/model-context-client.js +2 -1
  217. package/dist/store/clients/model-context-client.js.map +1 -1
  218. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  219. package/dist/store/clients/single-thread-list.js +1 -0
  220. package/dist/store/clients/single-thread-list.js.map +1 -1
  221. package/dist/store/clients/suggestions.d.ts.map +1 -1
  222. package/dist/store/clients/suggestions.js +5 -4
  223. package/dist/store/clients/suggestions.js.map +1 -1
  224. package/dist/store/clients/thread-tasks.d.ts +13 -0
  225. package/dist/store/clients/thread-tasks.d.ts.map +1 -0
  226. package/dist/store/clients/thread-tasks.js +69 -0
  227. package/dist/store/clients/thread-tasks.js.map +1 -0
  228. package/dist/store/index.d.ts +3 -2
  229. package/dist/store/index.js +3 -3
  230. package/dist/store/primitive-predicates.d.ts.map +1 -1
  231. package/dist/store/primitive-predicates.js +4 -4
  232. package/dist/store/primitive-predicates.js.map +1 -1
  233. package/dist/store/runtime-clients/composer-runtime-client.d.ts +1 -0
  234. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  235. package/dist/store/runtime-clients/composer-runtime-client.js +97 -61
  236. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  237. package/dist/store/runtime-clients/message-runtime-client.d.ts +1 -0
  238. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  239. package/dist/store/runtime-clients/message-runtime-client.js +221 -144
  240. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  241. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  242. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  243. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  244. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  245. package/dist/store/runtime-clients/thread-list-runtime-client.js +54 -52
  246. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  247. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  248. package/dist/store/runtime-clients/thread-runtime-client.js +246 -126
  249. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  250. package/dist/store/scope-registration.d.ts +2 -0
  251. package/dist/store/scope-registration.d.ts.map +1 -1
  252. package/dist/store/scopes/composer.d.ts +10 -2
  253. package/dist/store/scopes/composer.d.ts.map +1 -1
  254. package/dist/store/scopes/message.d.ts +30 -1
  255. package/dist/store/scopes/message.d.ts.map +1 -1
  256. package/dist/store/scopes/task.d.ts +36 -0
  257. package/dist/store/scopes/task.d.ts.map +1 -0
  258. package/dist/store/scopes/task.js +0 -0
  259. package/dist/store/scopes/thread-list-item.d.ts +2 -2
  260. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  261. package/dist/store/scopes/thread.d.ts +28 -0
  262. package/dist/store/scopes/thread.d.ts.map +1 -1
  263. package/dist/store/scopes/threads.d.ts +1 -0
  264. package/dist/store/scopes/threads.d.ts.map +1 -1
  265. package/dist/subscribable/subscribable.d.ts +2 -1
  266. package/dist/subscribable/subscribable.d.ts.map +1 -1
  267. package/dist/subscribable/subscribable.js +23 -15
  268. package/dist/subscribable/subscribable.js.map +1 -1
  269. package/dist/types/attachment.d.ts +2 -1
  270. package/dist/types/attachment.d.ts.map +1 -1
  271. package/dist/types/attachment.js +2 -1
  272. package/dist/types/attachment.js.map +1 -1
  273. package/dist/types/index.d.ts +2 -2
  274. package/dist/types/message.d.ts +8 -1
  275. package/dist/types/message.d.ts.map +1 -1
  276. package/dist/types/message.js.map +1 -1
  277. package/dist/utils/abortable-iterable.d.ts +19 -0
  278. package/dist/utils/abortable-iterable.d.ts.map +1 -0
  279. package/dist/utils/abortable-iterable.js +88 -0
  280. package/dist/utils/abortable-iterable.js.map +1 -0
  281. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  282. package/dist/utils/composite-context-provider.js +20 -2
  283. package/dist/utils/composite-context-provider.js.map +1 -1
  284. package/dist/utils/getGroupStatus.d.ts +18 -2
  285. package/dist/utils/getGroupStatus.d.ts.map +1 -1
  286. package/dist/utils/getGroupStatus.js +32 -7
  287. package/dist/utils/getGroupStatus.js.map +1 -1
  288. package/dist/utils/record.d.ts +11 -0
  289. package/dist/utils/record.d.ts.map +1 -0
  290. package/dist/utils/record.js +12 -0
  291. package/dist/utils/record.js.map +1 -0
  292. package/dist/utils/wire-media.d.ts.map +1 -1
  293. package/dist/utils/wire-media.js +2 -2
  294. package/dist/utils/wire-media.js.map +1 -1
  295. package/package.json +11 -11
  296. package/src/adapters/attachment.test.ts +50 -1
  297. package/src/adapters/attachment.ts +7 -4
  298. package/src/adapters/speech.test.ts +236 -0
  299. package/src/adapters/speech.ts +17 -10
  300. package/src/adapters/thread-history.ts +3 -0
  301. package/src/index.ts +2 -0
  302. package/src/internal.ts +4 -0
  303. package/src/model-context/frame/host.test.ts +137 -1
  304. package/src/model-context/frame/host.ts +49 -16
  305. package/src/model-context/frame/provider.test.ts +71 -0
  306. package/src/model-context/frame/provider.ts +37 -7
  307. package/src/model-context/interactable-composer-metadata.test.ts +58 -0
  308. package/src/model-context/interactable-composer-metadata.ts +19 -13
  309. package/src/model-context/registry.test.ts +12 -0
  310. package/src/model-context/registry.ts +3 -2
  311. package/src/model-context/types.test.ts +25 -0
  312. package/src/model-context/types.ts +9 -4
  313. package/src/react/RuntimeAdapter.test.ts +34 -0
  314. package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +244 -0
  315. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +104 -7
  316. package/src/react/client/DataRenderers.test.tsx +45 -0
  317. package/src/react/client/DataRenderers.ts +13 -9
  318. package/src/react/client/InMemoryThreadList.ts +1 -0
  319. package/src/react/client/Interactables.test.ts +72 -1
  320. package/src/react/client/Interactables.ts +22 -14
  321. package/src/react/client/RemoteThreadList.background.test.ts +148 -6
  322. package/src/react/client/RemoteThreadList.concurrent.test.tsx +42 -1
  323. package/src/react/client/RemoteThreadList.test.ts +386 -4
  324. package/src/react/client/RemoteThreadList.ts +181 -112
  325. package/src/react/client/Tools.ts +22 -11
  326. package/src/react/client/interactable-model-context.test.ts +44 -0
  327. package/src/react/client/interactable-model-context.ts +3 -2
  328. package/src/react/index.ts +5 -1
  329. package/src/react/interactables-legacy/Interactables.ts +16 -16
  330. package/src/react/interactables-legacy/useAssistantInteractable.test.tsx +132 -0
  331. package/src/react/interactables-legacy/useAssistantInteractable.ts +10 -1
  332. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
  333. package/src/react/model-context/makeAssistantTool.ts +2 -2
  334. package/src/react/model-context/useAssistantTool.ts +2 -2
  335. package/src/react/model-context/useInteractable.test.tsx +177 -0
  336. package/src/react/model-context/useInteractable.ts +3 -0
  337. package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
  338. package/src/react/model-context/useToolArgsStatus.ts +2 -1
  339. package/src/react/primitive-hooks/useActionBarCopy.test.ts +0 -1
  340. package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +0 -1
  341. package/src/react/primitives/message/MessageGroupedParts.test.tsx +96 -0
  342. package/src/react/primitives/message/MessageGroupedParts.tsx +15 -4
  343. package/src/react/primitives/part/PartMessages.test.tsx +104 -1
  344. package/src/react/primitives/part/PartMessages.tsx +1 -0
  345. package/src/react/primitives/thread/ThreadMessages.test.tsx +142 -0
  346. package/src/react/primitives/thread/ThreadMessages.tsx +8 -5
  347. package/src/react/providers/ReadonlyThreadProvider.test.tsx +56 -0
  348. package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
  349. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +33 -0
  350. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
  351. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +28 -5
  352. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
  353. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +105 -0
  354. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +143 -112
  355. package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
  356. package/src/react/runtimes/RemoteThreadResource.ts +19 -12
  357. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +20 -14
  358. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +703 -6
  359. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +492 -402
  360. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +77 -0
  361. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +27 -1
  362. package/src/react/runtimes/cloud/auiV0.ts +160 -25
  363. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +36 -0
  364. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +8 -1
  365. package/src/react/runtimes/cloud/sdkIdentity.ts +9 -0
  366. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +18 -4
  367. package/src/react/runtimes/external-message-converter.test.ts +136 -1
  368. package/src/react/runtimes/external-message-converter.ts +39 -93
  369. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +41 -0
  370. package/src/react/runtimes/useExternalStoreRuntime.ts +10 -4
  371. package/src/react/runtimes/useLocalRuntime.test.tsx +20 -0
  372. package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
  373. package/src/react/runtimes/useRemoteThreadListRuntime.ts +34 -1
  374. package/src/react/utils/groupParts.ts +12 -1
  375. package/src/react/utils/useJSONEqual.ts +9 -0
  376. package/src/react/utils/useJSONSchemaDependency.ts +22 -0
  377. package/src/runtime/api/message-runtime.test.ts +75 -0
  378. package/src/runtime/api/thread-list-item-runtime.ts +10 -3
  379. package/src/runtime/api/thread-list-runtime.ts +21 -11
  380. package/src/runtime/api/thread-runtime.test.ts +136 -0
  381. package/src/runtime/api/thread-runtime.ts +10 -5
  382. package/src/runtime/base/base-composer-runtime-core.ts +105 -24
  383. package/src/runtime/base/base-thread-runtime-core.test.ts +1063 -2
  384. package/src/runtime/base/base-thread-runtime-core.ts +228 -87
  385. package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
  386. package/src/runtime/interfaces/thread-list-runtime-core.ts +5 -1
  387. package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
  388. package/src/runtime/queue/message-queue.ts +68 -6
  389. package/src/runtime/utils/auto-status.test.ts +112 -1
  390. package/src/runtime/utils/auto-status.ts +31 -5
  391. package/src/runtime/utils/external-message-conversion.test.ts +202 -0
  392. package/src/runtime/utils/external-message-conversion.ts +123 -32
  393. package/src/runtime/utils/message-repository.test.ts +49 -0
  394. package/src/runtime/utils/message-repository.ts +32 -20
  395. package/src/runtime/utils/thread-message-like.test.ts +52 -0
  396. package/src/runtime/utils/thread-message-like.ts +5 -1
  397. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +18 -13
  398. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +31 -1
  399. package/src/runtimes/internal.ts +1 -0
  400. package/src/runtimes/local/local-thread-runtime-core.test.ts +21 -0
  401. package/src/runtimes/local/local-thread-runtime-core.ts +11 -1
  402. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +128 -2
  403. package/src/runtimes/remote-thread-list/remote-thread-state.ts +182 -42
  404. package/src/runtimes/remote-thread-list/title-generation.test.ts +602 -0
  405. package/src/runtimes/remote-thread-list/title-generation.ts +329 -0
  406. package/src/runtimes/remote-thread-list/title.test.ts +62 -0
  407. package/src/runtimes/remote-thread-list/title.ts +13 -0
  408. package/src/runtimes/remote-thread-list/types.ts +10 -0
  409. package/src/runtimes/tool-invocations/EDGE_CASES.md +2 -6
  410. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +133 -0
  411. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +10 -6
  412. package/src/store/clients/external-thread.ts +67 -4
  413. package/src/store/clients/model-context-client.ts +5 -1
  414. package/src/store/clients/single-thread-list.ts +1 -0
  415. package/src/store/clients/suggestions.test.ts +40 -0
  416. package/src/store/clients/suggestions.ts +13 -10
  417. package/src/store/clients/thread-tasks.test.ts +274 -0
  418. package/src/store/clients/thread-tasks.ts +138 -0
  419. package/src/store/index.ts +8 -0
  420. package/src/store/primitive-predicates.test.ts +48 -5
  421. package/src/store/primitive-predicates.ts +7 -2
  422. package/src/store/runtime-clients/composer-runtime-client.ts +42 -9
  423. package/src/store/runtime-clients/message-runtime-client.ts +54 -6
  424. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +4 -2
  425. package/src/store/runtime-clients/thread-list-runtime-client.ts +1 -0
  426. package/src/store/runtime-clients/thread-runtime-client.ts +89 -11
  427. package/src/store/scope-registration.ts +2 -0
  428. package/src/store/scopes/composer.ts +16 -4
  429. package/src/store/scopes/message.ts +14 -0
  430. package/src/store/scopes/task.ts +34 -0
  431. package/src/store/scopes/thread-list-item.ts +5 -2
  432. package/src/store/scopes/thread.ts +20 -0
  433. package/src/store/scopes/threads.ts +1 -0
  434. package/src/store/thread-tasks.test.tsx +209 -0
  435. package/src/subscribable/subscribable.test.ts +156 -1
  436. package/src/subscribable/subscribable.ts +25 -15
  437. package/src/tests/MessageRepository.test.ts +117 -5
  438. package/src/tests/OptimisticState-list-race.test.ts +1 -0
  439. package/src/tests/RemoteThreadListThreadListRuntimeCore-initialize-adapter-change.test.ts +38 -0
  440. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +47 -0
  441. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
  442. package/src/tests/attachment-adapters.test.ts +14 -0
  443. package/src/tests/auiV0Encode.test.ts +300 -0
  444. package/src/tests/base-composer-runtime-core-send.test.ts +51 -0
  445. package/src/tests/base-composer-runtime-core.test.ts +353 -2
  446. package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
  447. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +178 -18
  448. package/src/tests/external-thread-attachments.test.tsx +208 -0
  449. package/src/tests/external-thread-parity.test.tsx +57 -0
  450. package/src/tests/groupParts.test.ts +71 -0
  451. package/src/tests/message-queue.test.ts +253 -0
  452. package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
  453. package/src/tests/remote-thread-list-reactive-threadId.test.ts +51 -0
  454. package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
  455. package/src/tests/thread-switch-events.test.tsx +78 -0
  456. package/src/tests/tools-scope-migration.test.tsx +28 -0
  457. package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +43 -0
  458. package/src/types/attachment.ts +4 -0
  459. package/src/types/index.ts +1 -0
  460. package/src/types/message.ts +8 -0
  461. package/src/utils/abortable-iterable.test.ts +328 -0
  462. package/src/utils/abortable-iterable.ts +104 -0
  463. package/src/utils/composite-context-provider.test.ts +25 -0
  464. package/src/utils/composite-context-provider.ts +23 -2
  465. package/src/utils/getGroupStatus.test.ts +70 -4
  466. package/src/utils/getGroupStatus.ts +35 -12
  467. package/src/utils/record.test.ts +48 -0
  468. package/src/utils/record.ts +11 -0
  469. package/src/utils/wire-media.test.ts +10 -0
  470. package/src/utils/wire-media.ts +6 -2
@@ -120,6 +120,42 @@ describe("AssistantFrameProvider", () => {
120
120
  await vi.waitFor(() => expect(execute).toHaveBeenCalledOnce());
121
121
  });
122
122
 
123
+ it("does not broadcast from a disposed provider", async () => {
124
+ vi.useFakeTimers();
125
+
126
+ try {
127
+ AssistantFrameProvider.addModelContextProvider({
128
+ getModelContext: () => ({ system: "disposed context" }),
129
+ });
130
+ AssistantFrameProvider.dispose();
131
+
132
+ AssistantFrameProvider.addModelContextProvider({
133
+ getModelContext: () => ({ system: "current context" }),
134
+ });
135
+ vi.mocked(parentWindow.postMessage).mockClear();
136
+
137
+ await vi.runAllTimersAsync();
138
+
139
+ expect(parentWindow.postMessage).toHaveBeenCalledOnce();
140
+ expect(parentWindow.postMessage).toHaveBeenCalledWith(
141
+ {
142
+ channel: FRAME_MESSAGE_CHANNEL,
143
+ message: {
144
+ type: "model-context-update",
145
+ context: {
146
+ system: "current context",
147
+ tools: {},
148
+ },
149
+ },
150
+ },
151
+ window.location.origin,
152
+ );
153
+ } finally {
154
+ AssistantFrameProvider.dispose();
155
+ vi.useRealTimers();
156
+ }
157
+ });
158
+
123
159
  it("reports a failure even when the thrown error has an empty message", async () => {
124
160
  const execute = vi.fn(async () => {
125
161
  throw new Error();
@@ -146,6 +182,41 @@ describe("AssistantFrameProvider", () => {
146
182
  });
147
183
  });
148
184
 
185
+ it("reports tool results that cannot cross the frame boundary", async () => {
186
+ const consoleError = vi
187
+ .spyOn(console, "error")
188
+ .mockImplementation(() => undefined);
189
+ const execute = vi.fn(async () => () => undefined);
190
+ vi.mocked(parentWindow.postMessage).mockImplementation((data) => {
191
+ structuredClone(data);
192
+ });
193
+ AssistantFrameProvider.addModelContextProvider({
194
+ getModelContext: () => ({
195
+ tools: { sensitiveTool: { execute } },
196
+ }),
197
+ });
198
+
199
+ dispatchToolCall(window.location.origin);
200
+
201
+ await vi.waitFor(() => {
202
+ expect(consoleError).toHaveBeenCalledWith(
203
+ "[assistant-ui] AssistantFrame tool result could not be sent.",
204
+ expect.objectContaining({ name: "DataCloneError" }),
205
+ );
206
+ expect(parentWindow.postMessage).toHaveBeenCalledWith(
207
+ {
208
+ channel: FRAME_MESSAGE_CHANNEL,
209
+ message: {
210
+ type: "tool-result",
211
+ id: "tool-call-1",
212
+ error: "Tool result could not be sent across the frame boundary",
213
+ },
214
+ },
215
+ { targetOrigin: window.location.origin },
216
+ );
217
+ });
218
+ });
219
+
149
220
  it("aborts in-flight tool calls when the parent cancels them", async () => {
150
221
  let toolSignal: AbortSignal | undefined;
151
222
  const execute = vi.fn(
@@ -47,13 +47,18 @@ export class AssistantFrameProvider {
47
47
  private _targetOrigin: string;
48
48
  private _strictRegistrations = 0;
49
49
  private _wildcardRegistrations = 0;
50
+ private _startupTimer: ReturnType<typeof setTimeout> | undefined;
51
+ private _disposed = false;
50
52
 
51
53
  private constructor(targetOrigin: string = getDefaultTargetOrigin()) {
52
54
  this._targetOrigin = targetOrigin;
53
55
  this.handleMessage = this.handleMessage.bind(this);
54
56
  window.addEventListener("message", this.handleMessage);
55
57
 
56
- setTimeout(() => this.broadcastUpdate(), 0);
58
+ this._startupTimer = setTimeout(() => {
59
+ this._startupTimer = undefined;
60
+ this.broadcastUpdate();
61
+ }, 0);
57
62
  }
58
63
 
59
64
  private static getInstance(targetOrigin?: string): AssistantFrameProvider {
@@ -106,7 +111,12 @@ export class AssistantFrameProvider {
106
111
  break;
107
112
 
108
113
  case "tool-call":
109
- this.handleToolCall(message, event);
114
+ void this.handleToolCall(message, event).catch((error: unknown) => {
115
+ console.error(
116
+ "[assistant-ui] AssistantFrame tool call failed.",
117
+ error,
118
+ );
119
+ });
110
120
  break;
111
121
 
112
122
  case "tool-cancel":
@@ -156,11 +166,25 @@ export class AssistantFrameProvider {
156
166
  if (this._activeToolCalls.get(message.id) !== activeCall) return;
157
167
  this._activeToolCalls.delete(message.id);
158
168
 
159
- this.sendMessage(event, {
160
- type: "tool-result",
161
- id: message.id,
162
- ...(error !== undefined ? { error } : { result }),
163
- });
169
+ try {
170
+ this.sendMessage(event, {
171
+ type: "tool-result",
172
+ id: message.id,
173
+ ...(error !== undefined ? { error } : { result }),
174
+ });
175
+ } catch (sendError) {
176
+ if (error !== undefined) throw sendError;
177
+
178
+ console.error(
179
+ "[assistant-ui] AssistantFrame tool result could not be sent.",
180
+ sendError,
181
+ );
182
+ this.sendMessage(event, {
183
+ type: "tool-result",
184
+ id: message.id,
185
+ error: "Tool result could not be sent across the frame boundary",
186
+ });
187
+ }
164
188
  }
165
189
 
166
190
  private cancelToolCall(id: string) {
@@ -244,6 +268,7 @@ export class AssistantFrameProvider {
244
268
  }
245
269
 
246
270
  private broadcastUpdate() {
271
+ if (this._disposed) return;
247
272
  if (window.parent && window.parent !== window) {
248
273
  const updateMessage: FrameMessage = {
249
274
  type: "model-context-update",
@@ -375,6 +400,11 @@ export class AssistantFrameProvider {
375
400
  static dispose() {
376
401
  if (AssistantFrameProvider._instance) {
377
402
  const instance = AssistantFrameProvider._instance;
403
+ instance._disposed = true;
404
+ if (instance._startupTimer !== undefined) {
405
+ clearTimeout(instance._startupTimer);
406
+ instance._startupTimer = undefined;
407
+ }
378
408
  window.removeEventListener("message", instance.handleMessage);
379
409
 
380
410
  let cleanupFailed = false;
@@ -104,6 +104,18 @@ describe("interactableToolName", () => {
104
104
  });
105
105
 
106
106
  describe("shallowMergeInteractableState", () => {
107
+ it("adds prototype-named fields as own properties", () => {
108
+ const value = { enabled: true };
109
+ const result = shallowMergeInteractableState(
110
+ { title: "Example" },
111
+ Object.fromEntries([["__proto__", value]]),
112
+ ) as Record<string, unknown>;
113
+
114
+ expect(Object.getPrototypeOf(result)).toBe(Object.prototype);
115
+ expect(Object.hasOwn(result, "__proto__")).toBe(true);
116
+ expect(result["__proto__"]).toBe(value);
117
+ });
118
+
107
119
  it("applies array operations from a baseline", () => {
108
120
  const prev = {
109
121
  tasks: [
@@ -131,6 +143,34 @@ describe("shallowMergeInteractableState", () => {
131
143
  });
132
144
  });
133
145
 
146
+ it("uses the first valid patch for each array item id", () => {
147
+ expect(
148
+ shallowMergeInteractableState(
149
+ { tasks: [{ id: 1, title: "Original", done: false }] },
150
+ {
151
+ tasks: {
152
+ update: [
153
+ null,
154
+ { title: "Missing id" },
155
+ { id: "1", title: "String id" },
156
+ { id: 1, title: "First", done: true },
157
+ { id: 1, title: "Second" },
158
+ ],
159
+ },
160
+ },
161
+ ),
162
+ ).toEqual({ tasks: [{ id: 1, title: "First", done: true }] });
163
+ });
164
+
165
+ it("does not match NaN array item ids", () => {
166
+ expect(
167
+ shallowMergeInteractableState(
168
+ { tasks: [{ id: Number.NaN, title: "Original" }] },
169
+ { tasks: { update: [{ id: Number.NaN, title: "Patched" }] } },
170
+ ),
171
+ ).toEqual({ tasks: [{ id: Number.NaN, title: "Original" }] });
172
+ });
173
+
134
174
  it("keeps raw array replacement semantics", () => {
135
175
  expect(
136
176
  shallowMergeInteractableState(
@@ -425,6 +465,24 @@ describe("gateInteractableComposerMetadata", () => {
425
465
  expect(gated?.interactables).toEqual([entry("a", { v: 1 })]);
426
466
  });
427
467
 
468
+ it.each(["__proto__", "toString"])(
469
+ "treats a removed prototype-named field %s as a full snapshot",
470
+ (field) => {
471
+ const known = Object.fromEntries([
472
+ [field, { enabled: true }],
473
+ ["title", "draft"],
474
+ ["stable", true],
475
+ ]);
476
+ const current = { title: "edited", stable: true };
477
+ const meta = { interactables: [entry("a", current)] };
478
+ const history = [userMsg([entry("a", known)])];
479
+
480
+ const gated = gateInteractableComposerMetadata(meta, history);
481
+
482
+ expect(gated?.interactables).toEqual([entry("a", current)]);
483
+ },
484
+ );
485
+
428
486
  it("omits an interactable the model already knows via its own update_* call", () => {
429
487
  const meta = { interactables: [entry("a", { v: 2 }, "note")] };
430
488
  const history = [
@@ -94,12 +94,18 @@ function applyArrayUpdate(
94
94
 
95
95
  const patches = update.update;
96
96
  if (Array.isArray(patches) && patches.length > 0) {
97
+ const patchesById = new Map<string | number, Record<string, unknown>>();
98
+ for (const candidate of patches) {
99
+ const id = getArrayItemId(candidate);
100
+ if (id !== undefined && !Number.isNaN(id) && !patchesById.has(id)) {
101
+ patchesById.set(id, candidate as Record<string, unknown>);
102
+ }
103
+ }
104
+
97
105
  next = next.map((item) => {
98
106
  const id = getArrayItemId(item);
99
107
  if (id === undefined || !isRecord(item)) return item;
100
- const patch = patches.find(
101
- (candidate) => isRecord(candidate) && candidate.id === id,
102
- );
108
+ const patch = patchesById.get(id);
103
109
  return patch ? { ...item, ...patch } : item;
104
110
  });
105
111
  }
@@ -143,7 +149,7 @@ export function shallowMergeInteractableState(
143
149
  const baseline = isRecord(options?.arrayBaseline)
144
150
  ? options.arrayBaseline
145
151
  : prev;
146
- const next = { ...prev };
152
+ const next = Object.entries(prev);
147
153
  for (const [key, value] of Object.entries(partial)) {
148
154
  const baseValue = baseline[key];
149
155
  if (Array.isArray(baseValue) && isRecord(value)) {
@@ -152,12 +158,12 @@ export function shallowMergeInteractableState(
152
158
  (options.idKeyedFields === undefined || options.idKeyedFields.has(key))
153
159
  ? () => options.idFactory?.(key)
154
160
  : undefined;
155
- next[key] = applyArrayUpdate(baseValue, value, mintId);
161
+ next.push([key, applyArrayUpdate(baseValue, value, mintId)]);
156
162
  } else {
157
- next[key] = value;
163
+ next.push([key, value]);
158
164
  }
159
165
  }
160
- return next;
166
+ return Object.fromEntries(next);
161
167
  }
162
168
 
163
169
  /**
@@ -173,17 +179,17 @@ function shallowDiffInteractableState(
173
179
  ): Record<string, unknown> | undefined {
174
180
  if (!isRecord(known) || !isRecord(next)) return undefined;
175
181
  for (const key of Object.keys(known)) {
176
- if (!(key in next)) return undefined;
182
+ if (!Object.hasOwn(next, key)) return undefined;
177
183
  }
178
- const diff: Record<string, unknown> = {};
184
+ const diff: [string, unknown][] = [];
179
185
  for (const [key, value] of Object.entries(next)) {
180
- if (!(key in known) || !isJSONValueEqual(known[key], value)) {
181
- diff[key] = value;
186
+ if (!Object.hasOwn(known, key) || !isJSONValueEqual(known[key], value)) {
187
+ diff.push([key, value]);
182
188
  }
183
189
  }
184
- const changed = Object.keys(diff).length;
190
+ const changed = diff.length;
185
191
  if (changed === 0 || changed === Object.keys(next).length) return undefined;
186
- return diff;
192
+ return Object.fromEntries(diff);
187
193
  }
188
194
 
189
195
  type ToolCallLikePart = {
@@ -2,6 +2,18 @@ import { describe, expect, it, vi } from "vitest";
2
2
  import { ModelContextRegistry } from "./registry";
3
3
 
4
4
  describe("ModelContextRegistry", () => {
5
+ it.each(["__proto__", "constructor", "toString"])(
6
+ "retains a tool named %s",
7
+ (toolName) => {
8
+ const registry = new ModelContextRegistry();
9
+ registry.addTool({ toolName });
10
+
11
+ const tools = registry.getModelContext().tools!;
12
+ expect(Object.hasOwn(tools, toolName)).toBe(true);
13
+ expect(Object.keys(tools)).toEqual([toolName]);
14
+ },
15
+ );
16
+
5
17
  it("notifies every subscriber and rethrows when a registration subscriber throws", () => {
6
18
  const registry = new ModelContextRegistry();
7
19
  const error = new Error("subscriber failed");
@@ -13,6 +13,7 @@ import type {
13
13
  ModelContextRegistryInstructionHandle,
14
14
  ModelContextRegistryProviderHandle,
15
15
  } from "./registry-handles";
16
+ import { nullProtoRecord } from "../utils/record";
16
17
 
17
18
  export class ModelContextRegistry implements ModelContextProvider {
18
19
  private _tools = new Map<symbol, AssistantToolProps<any, any>>();
@@ -34,7 +35,7 @@ export class ModelContextRegistry implements ModelContextProvider {
34
35
  const system =
35
36
  instructions.length > 0 ? instructions.join("\n\n") : undefined;
36
37
 
37
- const tools: Record<string, Tool<any, any>> = {};
38
+ const tools = nullProtoRecord<Tool<any, any>>();
38
39
  for (const toolProps of this._tools.values()) {
39
40
  const { toolName, render, ...tool } = toolProps;
40
41
  tools[toolName] = tool;
@@ -54,7 +55,7 @@ export class ModelContextRegistry implements ModelContextProvider {
54
55
  }
55
56
 
56
57
  if (providerContexts.tools) {
57
- context.tools = { ...(context.tools || {}), ...providerContexts.tools };
58
+ context.tools = nullProtoRecord(context.tools, providerContexts.tools);
58
59
  }
59
60
 
60
61
  if (providerContexts.callSettings) {
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { mergeModelContexts } from "./types";
3
+
4
+ const provider = (tools: Record<string, any>, priority?: number) =>
5
+ ({ getModelContext: () => ({ tools, priority }) }) as any;
6
+
7
+ describe("mergeModelContexts", () => {
8
+ it.each(["__proto__", "constructor", "toString"])(
9
+ "retains and prioritizes a tool named %s",
10
+ (name) => {
11
+ const highPriorityTool = { description: "high", parameters: {} };
12
+ const lowPriorityTool = { description: "low", parameters: {} };
13
+ const otherTool = { description: "other", parameters: {} };
14
+ const result = mergeModelContexts(
15
+ new Set([
16
+ provider({ [name]: highPriorityTool, ok: otherTool }, 1),
17
+ provider({ [name]: lowPriorityTool }, 0),
18
+ ]),
19
+ );
20
+
21
+ expect(Object.keys(result.tools ?? {})).toEqual([name, "ok"]);
22
+ expect(result.tools?.[name]?.description).toBe("high");
23
+ },
24
+ );
25
+ });
@@ -1,5 +1,6 @@
1
1
  import type { Unsubscribe } from "../types/unsubscribe";
2
2
  import type { Tool } from "assistant-stream";
3
+ import { nullProtoRecord } from "../utils/record";
3
4
 
4
5
  export type LanguageModelV1CallSettings = {
5
6
  maxTokens?: number;
@@ -68,7 +69,7 @@ export const mergeModelContexts = (
68
69
  .map((c) => c.getModelContext())
69
70
  .sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
70
71
 
71
- const toolPriorities: Record<string, number> = {};
72
+ const toolPriorities = nullProtoRecord<number>();
72
73
 
73
74
  return configs.reduce((acc, config) => {
74
75
  const priority = config.priority ?? 0;
@@ -81,7 +82,9 @@ export const mergeModelContexts = (
81
82
  }
82
83
  if (config.tools) {
83
84
  for (const [name, tool] of Object.entries(config.tools)) {
84
- const existing = acc.tools?.[name];
85
+ const hasExisting =
86
+ acc.tools !== undefined && Object.hasOwn(acc.tools, name);
87
+ const existing = hasExisting ? acc.tools![name] : undefined;
85
88
  if (existing && existing !== tool) {
86
89
  const existingPriority = toolPriorities[name]!;
87
90
  if (existingPriority === priority) {
@@ -106,9 +109,11 @@ export const mergeModelContexts = (
106
109
  continue;
107
110
  }
108
111
 
109
- if (!acc.tools) acc.tools = {};
112
+ if (!acc.tools) acc.tools = nullProtoRecord();
110
113
  acc.tools[name] = stripOverwrite(tool);
111
- toolPriorities[name] ??= priority;
114
+ if (!Object.hasOwn(toolPriorities, name)) {
115
+ toolPriorities[name] = priority;
116
+ }
112
117
  }
113
118
  }
114
119
  if (config.config) {
@@ -69,6 +69,40 @@ describe("RuntimeAdapter via the neutral store entry", () => {
69
69
  handle.destroy();
70
70
  });
71
71
 
72
+ it("emits composer.send for a thread.append, flagging a suggestion", async () => {
73
+ const { runtime, onNew } = createRuntime({
74
+ suggestions: [{ prompt: "hi there" }],
75
+ });
76
+ const handle = createAssistantClient(
77
+ AuiConfig({ threads: RuntimeAdapter(runtime) }),
78
+ );
79
+ handle.subscribe(() => {});
80
+ const aui = handle.getClient();
81
+ const sent = vi.fn();
82
+ aui.on({ scope: "thread", event: "composer.send" }, sent);
83
+
84
+ flushTapSync(() =>
85
+ aui.thread.append({ content: [{ type: "text", text: "hi there" }] }),
86
+ );
87
+ flushTapSync(() => aui.thread.append("plain text"));
88
+ await new Promise((resolve) => setTimeout(resolve));
89
+
90
+ expect(onNew).toHaveBeenCalledTimes(2);
91
+ expect(sent).toHaveBeenCalledTimes(2);
92
+ expect(sent.mock.calls[0]![0]).toMatchObject({
93
+ chars: 8,
94
+ attachments: 0,
95
+ suggestion: true,
96
+ });
97
+ expect(sent.mock.calls[1]![0]).toMatchObject({
98
+ chars: 10,
99
+ attachments: 0,
100
+ });
101
+ expect(sent.mock.calls[1]![0]).not.toHaveProperty("suggestion");
102
+
103
+ handle.destroy();
104
+ });
105
+
72
106
  it("registers the client's model context on the runtime", () => {
73
107
  const { runtime } = createRuntime();
74
108
  const register = vi.spyOn(runtime, "registerModelContextProvider");