@assistant-ui/core 0.3.16 → 0.3.18

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 (474) 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/index.d.ts +3 -3
  5. package/dist/index.js +2 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/internal.d.ts +4 -2
  8. package/dist/internal.js +4 -2
  9. package/dist/model-context/frame/host.d.ts.map +1 -1
  10. package/dist/model-context/frame/host.js +10 -4
  11. package/dist/model-context/frame/host.js.map +1 -1
  12. package/dist/model-context/frame/provider.d.ts +5 -0
  13. package/dist/model-context/frame/provider.d.ts.map +1 -1
  14. package/dist/model-context/frame/provider.js +101 -28
  15. package/dist/model-context/frame/provider.js.map +1 -1
  16. package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
  17. package/dist/model-context/interactable-composer-metadata.js +16 -9
  18. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  19. package/dist/model-context/registry.d.ts.map +1 -1
  20. package/dist/model-context/registry.js +3 -5
  21. package/dist/model-context/registry.js.map +1 -1
  22. package/dist/model-context/tool-call-text.d.ts +26 -0
  23. package/dist/model-context/tool-call-text.d.ts.map +1 -0
  24. package/dist/model-context/tool-call-text.js +18 -0
  25. package/dist/model-context/tool-call-text.js.map +1 -0
  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/RuntimeAdapter.js +1 -1
  30. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  31. package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
  32. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  33. package/dist/react/client/AdaptedRemoteThread.js +1 -1
  34. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  35. package/dist/react/client/InMemoryThreadList.js +63 -57
  36. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  37. package/dist/react/client/Interactables.d.ts.map +1 -1
  38. package/dist/react/client/Interactables.js +25 -28
  39. package/dist/react/client/Interactables.js.map +1 -1
  40. package/dist/react/client/RemoteThreadList.d.ts +16 -0
  41. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  42. package/dist/react/client/RemoteThreadList.js +354 -242
  43. package/dist/react/client/RemoteThreadList.js.map +1 -1
  44. package/dist/react/client/Tools.d.ts.map +1 -1
  45. package/dist/react/client/Tools.js +24 -18
  46. package/dist/react/client/Tools.js.map +1 -1
  47. package/dist/react/index.d.ts +2 -1
  48. package/dist/react/index.js +2 -1
  49. package/dist/react/interactables-legacy/Interactables.js +246 -125
  50. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  51. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
  52. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +11 -9
  53. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
  54. package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
  55. package/dist/react/model-context/makeAssistantTool.js +1 -1
  56. package/dist/react/model-context/makeAssistantTool.js.map +1 -1
  57. package/dist/react/model-context/toolbox.d.ts +2 -14
  58. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  59. package/dist/react/model-context/toolbox.js +1 -13
  60. package/dist/react/model-context/toolbox.js.map +1 -1
  61. package/dist/react/model-context/useAssistantContext.js +37 -7
  62. package/dist/react/model-context/useAssistantContext.js.map +1 -1
  63. package/dist/react/model-context/useAssistantTool.d.ts +2 -2
  64. package/dist/react/model-context/useAssistantTool.js +1 -1
  65. package/dist/react/model-context/useAssistantTool.js.map +1 -1
  66. package/dist/react/model-context/useAuiToolOverrides.js +31 -5
  67. package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
  68. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  69. package/dist/react/model-context/useInlineRender.js +10 -36
  70. package/dist/react/model-context/useInlineRender.js.map +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/primitive-hooks/useMessageError.d.ts.map +1 -1
  75. package/dist/react/primitive-hooks/useMessageError.js +2 -8
  76. package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
  77. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  78. package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -4
  79. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  80. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  81. package/dist/react/primitives/composer/ComposerAttachments.js +4 -3
  82. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  83. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  84. package/dist/react/primitives/composer/ComposerQueue.js +4 -3
  85. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  86. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  87. package/dist/react/primitives/message/MessageAttachments.js +7 -6
  88. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  89. package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
  90. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  91. package/dist/react/primitives/message/MessageParts.js +3 -3
  92. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  93. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  94. package/dist/react/primitives/threadList/ThreadListItems.js +5 -4
  95. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  96. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +6 -4
  97. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  98. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +59 -29
  99. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  100. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +11 -6
  101. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  102. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +160 -143
  103. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  104. package/dist/react/runtimes/RemoteThreadResource.d.ts +2 -6
  105. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  106. package/dist/react/runtimes/RemoteThreadResource.js +15 -13
  107. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  108. package/dist/react/runtimes/assistant-transport/useLatestRef.js +2 -2
  109. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -1
  110. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +4 -1
  111. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  112. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +424 -52
  113. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  114. package/dist/react/runtimes/cloud/auiV0.d.ts +5 -1
  115. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  116. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  117. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +29 -0
  118. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -0
  119. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +160 -0
  120. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -0
  121. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +1 -9
  122. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  123. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +16 -153
  124. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  125. package/dist/react/runtimes/external-message-converter.js +1 -1
  126. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  127. package/dist/react/runtimes/useExternalStoreRuntime.js +75 -46
  128. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  129. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  130. package/dist/react/runtimes/useRemoteThreadListRuntime.js +77 -29
  131. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  132. package/dist/react/types/MessagePartComponentTypes.d.ts +7 -3
  133. package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
  134. package/dist/react/types/scopes/tools.d.ts +2 -0
  135. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  136. package/dist/runtime/api/message-part-runtime.d.ts +2 -2
  137. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  138. package/dist/runtime/api/message-part-runtime.js +1 -1
  139. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  140. package/dist/runtime/api/thread-list-item-runtime.d.ts +7 -3
  141. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  142. package/dist/runtime/api/thread-list-item-runtime.js +2 -2
  143. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  144. package/dist/runtime/api/thread-list-runtime.d.ts +12 -1
  145. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  146. package/dist/runtime/api/thread-list-runtime.js +23 -14
  147. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  148. package/dist/runtime/api/thread-runtime.d.ts +5 -1
  149. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  150. package/dist/runtime/api/thread-runtime.js +1 -1
  151. package/dist/runtime/api/thread-runtime.js.map +1 -1
  152. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  153. package/dist/runtime/base/base-composer-runtime-core.js +16 -12
  154. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  155. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -2
  156. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  157. package/dist/runtime/base/base-thread-runtime-core.js +8 -7
  158. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  159. package/dist/runtime/interfaces/composer-runtime-core.d.ts +12 -5
  160. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  161. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +18 -3
  162. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  163. package/dist/runtime/interfaces/thread-runtime-core.d.ts +15 -1
  164. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  165. package/dist/runtime/utils/auto-status.d.ts +1 -1
  166. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  167. package/dist/runtime/utils/auto-status.js +6 -2
  168. package/dist/runtime/utils/auto-status.js.map +1 -1
  169. package/dist/runtime/utils/external-message-conversion.d.ts +3 -2
  170. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  171. package/dist/runtime/utils/external-message-conversion.js +5 -4
  172. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  173. package/dist/runtime/utils/message-repository.d.ts +1 -0
  174. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  175. package/dist/runtime/utils/message-repository.js +15 -6
  176. package/dist/runtime/utils/message-repository.js.map +1 -1
  177. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -3
  178. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  179. package/dist/runtime/utils/resolveToolApprovalResponse.js +12 -3
  180. package/dist/runtime/utils/resolveToolApprovalResponse.js.map +1 -1
  181. package/dist/runtime/utils/thread-message-like.d.ts +2 -10
  182. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  183. package/dist/runtime/utils/thread-message-like.js +1 -1
  184. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  185. package/dist/runtime/utils/tool-call-tree.d.ts +39 -0
  186. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -0
  187. package/dist/runtime/utils/tool-call-tree.js +85 -0
  188. package/dist/runtime/utils/tool-call-tree.js.map +1 -0
  189. package/dist/runtimes/external-store/external-store-adapter.d.ts +29 -2
  190. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  191. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  192. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +21 -27
  193. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  194. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +2 -1
  195. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  196. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +34 -28
  197. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  198. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +1 -1
  199. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  200. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  201. package/dist/runtimes/internal.d.ts +2 -2
  202. package/dist/runtimes/internal.js +2 -2
  203. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  204. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  205. package/dist/runtimes/local/local-thread-runtime-core.js +3 -1
  206. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  207. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +18 -1
  208. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  209. package/dist/runtimes/remote-thread-list/remote-thread-state.js +148 -43
  210. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  211. package/dist/runtimes/remote-thread-list/title-generation.d.ts +49 -0
  212. package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -0
  213. package/dist/runtimes/remote-thread-list/title-generation.js +197 -0
  214. package/dist/runtimes/remote-thread-list/title-generation.js.map +1 -0
  215. package/dist/runtimes/remote-thread-list/title.d.ts +11 -0
  216. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -0
  217. package/dist/runtimes/remote-thread-list/title.js +14 -0
  218. package/dist/runtimes/remote-thread-list/title.js.map +1 -0
  219. package/dist/runtimes/remote-thread-list/types.d.ts +16 -5
  220. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  221. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +31 -8
  222. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  223. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +97 -60
  224. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  225. package/dist/store/clients/external-thread.d.ts +1 -1
  226. package/dist/store/clients/external-thread.d.ts.map +1 -1
  227. package/dist/store/clients/external-thread.js +30 -7
  228. package/dist/store/clients/external-thread.js.map +1 -1
  229. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  230. package/dist/store/clients/model-context-client.js +2 -1
  231. package/dist/store/clients/model-context-client.js.map +1 -1
  232. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  233. package/dist/store/clients/single-thread-list.js +2 -1
  234. package/dist/store/clients/single-thread-list.js.map +1 -1
  235. package/dist/store/clients/suggestions.d.ts.map +1 -1
  236. package/dist/store/clients/suggestions.js +19 -6
  237. package/dist/store/clients/suggestions.js.map +1 -1
  238. package/dist/store/clients/thread-message-client.js +1 -1
  239. package/dist/store/clients/thread-selection-events.js +1 -1
  240. package/dist/store/index.d.ts +2 -1
  241. package/dist/store/index.js +2 -1
  242. package/dist/store/internal.d.ts +2 -2
  243. package/dist/store/internal.js +2 -2
  244. package/dist/store/primitive-predicates.d.ts +4 -1
  245. package/dist/store/primitive-predicates.d.ts.map +1 -1
  246. package/dist/store/primitive-predicates.js +9 -1
  247. package/dist/store/primitive-predicates.js.map +1 -1
  248. package/dist/store/runtime-clients/composer-runtime-client.d.ts +1 -0
  249. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  250. package/dist/store/runtime-clients/composer-runtime-client.js +98 -62
  251. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  252. package/dist/store/runtime-clients/message-runtime-client.d.ts +1 -0
  253. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  254. package/dist/store/runtime-clients/message-runtime-client.js +221 -144
  255. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  256. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  257. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +2 -2
  258. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  259. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  260. package/dist/store/runtime-clients/thread-list-runtime-client.js +128 -107
  261. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  262. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  263. package/dist/store/runtime-clients/thread-runtime-client.js +194 -122
  264. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  265. package/dist/store/runtime-clients/useSubscribable.d.ts +3 -1
  266. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  267. package/dist/store/runtime-clients/useSubscribable.js +1 -1
  268. package/dist/store/runtime-clients/useSubscribable.js.map +1 -1
  269. package/dist/store/scopes/composer.d.ts +10 -2
  270. package/dist/store/scopes/composer.d.ts.map +1 -1
  271. package/dist/store/scopes/message.d.ts +30 -1
  272. package/dist/store/scopes/message.d.ts.map +1 -1
  273. package/dist/store/scopes/part.d.ts +6 -3
  274. package/dist/store/scopes/part.d.ts.map +1 -1
  275. package/dist/store/scopes/thread-list-item.d.ts +2 -2
  276. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  277. package/dist/store/scopes/thread.d.ts +8 -0
  278. package/dist/store/scopes/thread.d.ts.map +1 -1
  279. package/dist/store/scopes/threads.d.ts +1 -0
  280. package/dist/store/scopes/threads.d.ts.map +1 -1
  281. package/dist/subscribable/subscribable.d.ts +8 -1
  282. package/dist/subscribable/subscribable.d.ts.map +1 -1
  283. package/dist/subscribable/subscribable.js +24 -16
  284. package/dist/subscribable/subscribable.js.map +1 -1
  285. package/dist/types/attachment.d.ts +2 -1
  286. package/dist/types/attachment.d.ts.map +1 -1
  287. package/dist/types/attachment.js +2 -1
  288. package/dist/types/attachment.js.map +1 -1
  289. package/dist/types/message.d.ts +40 -1
  290. package/dist/types/message.d.ts.map +1 -1
  291. package/dist/types/message.js +7 -1
  292. package/dist/types/message.js.map +1 -1
  293. package/dist/utils/abortable-iterable.d.ts +19 -0
  294. package/dist/utils/abortable-iterable.d.ts.map +1 -0
  295. package/dist/utils/abortable-iterable.js +72 -0
  296. package/dist/utils/abortable-iterable.js.map +1 -0
  297. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  298. package/dist/utils/composite-context-provider.js +20 -2
  299. package/dist/utils/composite-context-provider.js.map +1 -1
  300. package/dist/utils/record.d.ts +11 -0
  301. package/dist/utils/record.d.ts.map +1 -0
  302. package/dist/utils/record.js +12 -0
  303. package/dist/utils/record.js.map +1 -0
  304. package/package.json +12 -17
  305. package/src/adapters/attachment.test.ts +50 -1
  306. package/src/adapters/attachment.ts +7 -4
  307. package/src/index.ts +7 -1
  308. package/src/internal.ts +12 -0
  309. package/src/model-context/frame/host.test.ts +24 -0
  310. package/src/model-context/frame/host.ts +10 -4
  311. package/src/model-context/frame/provider.test.ts +288 -1
  312. package/src/model-context/frame/provider.ts +148 -30
  313. package/src/model-context/interactable-composer-metadata.test.ts +28 -0
  314. package/src/model-context/interactable-composer-metadata.ts +9 -3
  315. package/src/model-context/registry.test.ts +12 -0
  316. package/src/model-context/registry.ts +3 -2
  317. package/src/model-context/tool-call-text.test.ts +67 -0
  318. package/src/model-context/tool-call-text.ts +67 -0
  319. package/src/model-context/types.test.ts +25 -0
  320. package/src/model-context/types.ts +9 -4
  321. package/src/react/AssistantRuntimeProvider.test.tsx +66 -2
  322. package/src/react/RuntimeAdapter.test.ts +34 -0
  323. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +1 -3
  324. package/src/react/client/InMemoryThreadList.ts +1 -0
  325. package/src/react/client/Interactables.test.ts +28 -0
  326. package/src/react/client/Interactables.ts +15 -13
  327. package/src/react/client/RemoteThreadList.background.test.ts +526 -0
  328. package/src/react/client/RemoteThreadList.concurrent.test.tsx +315 -0
  329. package/src/react/client/RemoteThreadList.test.ts +617 -3
  330. package/src/react/client/RemoteThreadList.ts +433 -167
  331. package/src/react/client/Tools.ts +29 -12
  332. package/src/react/index.ts +4 -0
  333. package/src/react/interactables-legacy/Interactables.ts +16 -16
  334. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
  335. package/src/react/model-context/makeAssistantTool.ts +2 -2
  336. package/src/react/model-context/modelContextHooks.test.tsx +131 -0
  337. package/src/react/model-context/toolbox.test.ts +9 -9
  338. package/src/react/model-context/toolbox.ts +9 -36
  339. package/src/react/model-context/useAssistantContext.ts +4 -2
  340. package/src/react/model-context/useAssistantTool.ts +2 -2
  341. package/src/react/model-context/useAuiToolOverrides.ts +4 -2
  342. package/src/react/model-context/useInlineRender.test.tsx +48 -0
  343. package/src/react/model-context/useInlineRender.ts +14 -10
  344. package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
  345. package/src/react/model-context/useToolArgsStatus.ts +2 -1
  346. package/src/react/primitive-hooks/useMessageError.ts +2 -19
  347. package/src/react/primitive-hooks/useThreadListLoadMore.ts +2 -3
  348. package/src/react/primitives/composer/ComposerAttachments.test.tsx +76 -0
  349. package/src/react/primitives/composer/ComposerAttachments.tsx +9 -4
  350. package/src/react/primitives/composer/ComposerQueue.test.tsx +72 -0
  351. package/src/react/primitives/composer/ComposerQueue.tsx +7 -4
  352. package/src/react/primitives/message/MessageAttachments.test.tsx +107 -37
  353. package/src/react/primitives/message/MessageAttachments.tsx +10 -7
  354. package/src/react/primitives/message/MessageGroupedParts.tsx +2 -2
  355. package/src/react/primitives/message/MessageParts.tsx +3 -3
  356. package/src/react/primitives/threadList/ThreadListItems.test.tsx +99 -0
  357. package/src/react/primitives/threadList/ThreadListItems.tsx +8 -5
  358. package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
  359. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +127 -1
  360. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
  361. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +99 -35
  362. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
  363. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +274 -0
  364. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +223 -152
  365. package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
  366. package/src/react/runtimes/RemoteThreadResource.ts +20 -16
  367. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +51 -2
  368. package/src/react/runtimes/assistant-transport/useLatestRef.ts +2 -2
  369. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +45 -0
  370. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +546 -5
  371. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +546 -103
  372. package/src/react/runtimes/cloud/auiV0.ts +5 -0
  373. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +103 -0
  374. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +237 -0
  375. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +90 -1
  376. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +33 -238
  377. package/src/react/runtimes/external-message-converter.ts +1 -0
  378. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +41 -0
  379. package/src/react/runtimes/useExternalStoreRuntime.ts +10 -4
  380. package/src/react/runtimes/useLocalRuntime.test.tsx +20 -0
  381. package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +216 -0
  382. package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
  383. package/src/react/runtimes/useRemoteThreadListRuntime.ts +57 -6
  384. package/src/react/types/MessagePartComponentTypes.ts +7 -3
  385. package/src/react/types/scopes/tools.ts +2 -0
  386. package/src/runtime/api/message-part-runtime.ts +3 -3
  387. package/src/runtime/api/message-runtime.test.ts +75 -0
  388. package/src/runtime/api/thread-list-item-runtime.ts +10 -3
  389. package/src/runtime/api/thread-list-runtime.ts +46 -12
  390. package/src/runtime/api/thread-runtime.test.ts +78 -0
  391. package/src/runtime/api/thread-runtime.ts +8 -1
  392. package/src/runtime/base/base-composer-runtime-core.ts +36 -8
  393. package/src/runtime/base/base-thread-runtime-core.test.ts +149 -4
  394. package/src/runtime/base/base-thread-runtime-core.ts +16 -8
  395. package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
  396. package/src/runtime/interfaces/thread-list-runtime-core.ts +25 -2
  397. package/src/runtime/interfaces/thread-runtime-core.ts +17 -1
  398. package/src/runtime/utils/auto-status.test.ts +57 -0
  399. package/src/runtime/utils/auto-status.ts +13 -1
  400. package/src/runtime/utils/external-message-conversion.test.ts +165 -0
  401. package/src/runtime/utils/external-message-conversion.ts +18 -2
  402. package/src/runtime/utils/message-repository.ts +29 -17
  403. package/src/runtime/utils/resolveToolApprovalResponse.test.ts +73 -0
  404. package/src/runtime/utils/resolveToolApprovalResponse.ts +27 -6
  405. package/src/runtime/utils/thread-message-like.ts +3 -11
  406. package/src/runtime/utils/tool-call-tree.test.ts +191 -0
  407. package/src/runtime/utils/tool-call-tree.ts +97 -0
  408. package/src/runtimes/external-store/external-store-adapter.ts +38 -2
  409. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +18 -13
  410. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +56 -33
  411. package/src/runtimes/inert/InertThreadRuntimeCore.ts +1 -1
  412. package/src/runtimes/internal.ts +1 -0
  413. package/src/runtimes/local/local-thread-runtime-core.test.ts +29 -3
  414. package/src/runtimes/local/local-thread-runtime-core.ts +5 -1
  415. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +238 -0
  416. package/src/runtimes/remote-thread-list/remote-thread-state.ts +223 -44
  417. package/src/runtimes/remote-thread-list/title-generation.test.ts +602 -0
  418. package/src/runtimes/remote-thread-list/title-generation.ts +329 -0
  419. package/src/runtimes/remote-thread-list/title.test.ts +62 -0
  420. package/src/runtimes/remote-thread-list/title.ts +16 -0
  421. package/src/runtimes/remote-thread-list/types.ts +16 -5
  422. package/src/runtimes/tool-invocations/EDGE_CASES.md +110 -5
  423. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +750 -136
  424. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +144 -91
  425. package/src/store/clients/external-thread.ts +62 -11
  426. package/src/store/clients/model-context-client.ts +5 -1
  427. package/src/store/clients/single-thread-list.ts +1 -0
  428. package/src/store/clients/suggestions.test.ts +195 -1
  429. package/src/store/clients/suggestions.ts +45 -34
  430. package/src/store/index.ts +4 -0
  431. package/src/store/internal.ts +2 -0
  432. package/src/store/primitive-predicates.ts +35 -0
  433. package/src/store/runtime-clients/composer-runtime-client.ts +42 -9
  434. package/src/store/runtime-clients/message-runtime-client.ts +54 -6
  435. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +4 -2
  436. package/src/store/runtime-clients/thread-list-runtime-client.ts +17 -2
  437. package/src/store/runtime-clients/thread-runtime-client.ts +58 -10
  438. package/src/store/runtime-clients/useSubscribable.ts +8 -2
  439. package/src/store/scopes/composer.ts +16 -4
  440. package/src/store/scopes/message.ts +14 -0
  441. package/src/store/scopes/part.ts +6 -3
  442. package/src/store/scopes/thread-list-item.ts +5 -2
  443. package/src/store/scopes/thread.ts +4 -0
  444. package/src/store/scopes/threads.ts +1 -0
  445. package/src/subscribable/subscribable.test.ts +50 -0
  446. package/src/subscribable/subscribable.ts +33 -21
  447. package/src/tests/MessageRepository.test.ts +116 -0
  448. package/src/tests/OptimisticState-list-race.test.ts +1 -0
  449. package/src/tests/RemoteThreadListThreadListRuntimeCore-initialize-adapter-change.test.ts +38 -0
  450. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +158 -0
  451. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +78 -0
  452. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +52 -0
  453. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
  454. package/src/tests/attachment-adapters.test.ts +14 -0
  455. package/src/tests/base-composer-runtime-core.test.ts +39 -0
  456. package/src/tests/external-store-optimistic-id.test.ts +79 -0
  457. package/src/tests/external-store-repository-instance.test.ts +166 -0
  458. package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
  459. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +49 -34
  460. package/src/tests/external-thread-attachments.test.tsx +224 -1
  461. package/src/tests/external-thread-parity.test.tsx +23 -0
  462. package/src/tests/remote-thread-list-adapter-switch.test.ts +66 -1
  463. package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
  464. package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
  465. package/src/tests/thread-switch-events.test.tsx +475 -2
  466. package/src/tests/tools-scope-migration.test.tsx +28 -0
  467. package/src/types/attachment.ts +4 -0
  468. package/src/types/message.ts +50 -2
  469. package/src/utils/abortable-iterable.test.ts +213 -0
  470. package/src/utils/abortable-iterable.ts +93 -0
  471. package/src/utils/composite-context-provider.test.ts +25 -0
  472. package/src/utils/composite-context-provider.ts +23 -2
  473. package/src/utils/record.test.ts +48 -0
  474. package/src/utils/record.ts +11 -0
@@ -1,4 +1,5 @@
1
1
  import {
2
+ isAttachmentComplete,
2
3
  isCreateAttachment,
3
4
  type Attachment,
4
5
  type CompleteAttachment,
@@ -34,9 +35,6 @@ import {
34
35
  drainAttachmentAdd,
35
36
  } from "../utils/attachment-add-operations";
36
37
 
37
- const isAttachmentComplete = (a: Attachment): a is CompleteAttachment =>
38
- a.status.type === "complete";
39
-
40
38
  export abstract class BaseComposerRuntimeCore
41
39
  extends BaseSubscribable
42
40
  implements ComposerRuntimeCore
@@ -161,6 +159,7 @@ export abstract class BaseComposerRuntimeCore
161
159
  private _emptyTextAndAttachments() {
162
160
  this._attachments = [];
163
161
  this._text = "";
162
+ this._rebaseDictation("");
164
163
  this._notifySubscribers();
165
164
  }
166
165
 
@@ -301,7 +300,10 @@ export abstract class BaseComposerRuntimeCore
301
300
  void sendTask.catch((error) => {
302
301
  this._restoreUnsentDraft(error, generation, draft);
303
302
  });
304
- this._notifyEventSubscribers("send", {});
303
+ this._notifyEventSubscribers("send", {
304
+ chars: text.length,
305
+ attachments: finalAttachments.length,
306
+ });
305
307
  }
306
308
 
307
309
  /**
@@ -417,6 +419,7 @@ export abstract class BaseComposerRuntimeCore
417
419
  message,
418
420
  undefined,
419
421
  err,
422
+ fileOrAttachment.contentType,
420
423
  );
421
424
  throw err;
422
425
  }
@@ -431,7 +434,9 @@ export abstract class BaseComposerRuntimeCore
431
434
  };
432
435
  this._attachments = [...this._attachments, a];
433
436
  this._notifySubscribers();
434
- this._notifyEventSubscribers("attachmentAdd", {});
437
+ this._notifyEventSubscribers("attachmentAdd", {
438
+ ...(a.contentType ? { contentType: a.contentType } : undefined),
439
+ });
435
440
  return;
436
441
  }
437
442
 
@@ -439,7 +444,13 @@ export abstract class BaseComposerRuntimeCore
439
444
  if (!adapter) {
440
445
  const message = "Attachments are not supported";
441
446
  const err = new Error(message);
442
- this._safeEmitAttachmentAddError("no-adapter", message, undefined, err);
447
+ this._safeEmitAttachmentAddError(
448
+ "no-adapter",
449
+ message,
450
+ undefined,
451
+ err,
452
+ fileOrAttachment.type,
453
+ );
443
454
  throw err;
444
455
  }
445
456
 
@@ -451,7 +462,13 @@ export abstract class BaseComposerRuntimeCore
451
462
  ) {
452
463
  const message = `File type ${fileOrAttachment.type || "unknown"} is not accepted. Accepted types: ${adapter.accept}`;
453
464
  const err = new Error(message);
454
- this._safeEmitAttachmentAddError("not-accepted", message, undefined, err);
465
+ this._safeEmitAttachmentAddError(
466
+ "not-accepted",
467
+ message,
468
+ undefined,
469
+ err,
470
+ fileOrAttachment.type,
471
+ );
455
472
  throw err;
456
473
  }
457
474
 
@@ -501,6 +518,7 @@ export abstract class BaseComposerRuntimeCore
501
518
  e instanceof Error ? e.message : String(e),
502
519
  lastAttachment?.id,
503
520
  e instanceof Error ? e : undefined,
521
+ lastAttachment?.contentType || fileOrAttachment.type,
504
522
  );
505
523
  throw e;
506
524
  } finally {
@@ -517,9 +535,17 @@ export abstract class BaseComposerRuntimeCore
517
535
  lastAttachment.status.message ??
518
536
  "Attachment upload did not complete successfully.",
519
537
  lastAttachment.id,
538
+ undefined,
539
+ lastAttachment.contentType || fileOrAttachment.type,
520
540
  );
521
541
  } else {
522
- this._notifyEventSubscribers("attachmentAdd", {});
542
+ this._notifyEventSubscribers("attachmentAdd", {
543
+ ...(lastAttachment?.contentType
544
+ ? { contentType: lastAttachment.contentType }
545
+ : fileOrAttachment.type
546
+ ? { contentType: fileOrAttachment.type }
547
+ : undefined),
548
+ });
523
549
  }
524
550
  }
525
551
 
@@ -528,6 +554,7 @@ export abstract class BaseComposerRuntimeCore
528
554
  message: string,
529
555
  attachmentId?: string,
530
556
  error?: Error,
557
+ contentType?: string,
531
558
  ) {
532
559
  try {
533
560
  this._notifyEventSubscribers("attachmentAddError", {
@@ -535,6 +562,7 @@ export abstract class BaseComposerRuntimeCore
535
562
  message,
536
563
  ...(attachmentId !== undefined && { attachmentId }),
537
564
  ...(error !== undefined && { error }),
565
+ ...(contentType ? { contentType } : undefined),
538
566
  });
539
567
  } catch (subscriberError) {
540
568
  console.error(
@@ -1,6 +1,8 @@
1
1
  import { afterEach, describe, expect, it, vi } from "vitest";
2
2
  import type { RealtimeVoiceAdapter } from "../../adapters/voice";
3
+ import type { ModelContextProvider } from "../../model-context/types";
3
4
  import type { AppendMessage } from "../../types/message";
5
+ import { CompositeContextProvider } from "../../utils/composite-context-provider";
4
6
  import type {
5
7
  AddToolResultOptions,
6
8
  ResumeRunConfig,
@@ -14,6 +16,9 @@ import { BaseThreadRuntimeCore } from "./base-thread-runtime-core";
14
16
 
15
17
  const createVoiceAdapter = () => {
16
18
  let volumeCallback: ((volume: number) => void) | undefined;
19
+ let transcriptCallback:
20
+ | ((transcript: RealtimeVoiceAdapter.TranscriptItem) => void)
21
+ | undefined;
17
22
  const session: RealtimeVoiceAdapter.Session = {
18
23
  status: { type: "running" },
19
24
  isMuted: false,
@@ -21,7 +26,12 @@ const createVoiceAdapter = () => {
21
26
  mute: vi.fn(),
22
27
  unmute: vi.fn(),
23
28
  onStatusChange: () => () => {},
24
- onTranscript: () => () => {},
29
+ onTranscript: (callback) => {
30
+ transcriptCallback = callback;
31
+ return () => {
32
+ transcriptCallback = undefined;
33
+ };
34
+ },
25
35
  onModeChange: () => () => {},
26
36
  onVolumeChange: (callback) => {
27
37
  volumeCallback = callback;
@@ -34,10 +44,13 @@ const createVoiceAdapter = () => {
34
44
  return {
35
45
  adapter: { connect: () => session },
36
46
  emitVolume: (volume: number) => volumeCallback?.(volume),
47
+ emitTranscript: (transcript: RealtimeVoiceAdapter.TranscriptItem) =>
48
+ transcriptCallback?.(transcript),
37
49
  session,
38
50
  } satisfies {
39
51
  adapter: RealtimeVoiceAdapter;
40
52
  emitVolume: (volume: number) => void;
53
+ emitTranscript: (transcript: RealtimeVoiceAdapter.TranscriptItem) => void;
41
54
  session: RealtimeVoiceAdapter.Session;
42
55
  };
43
56
  };
@@ -45,8 +58,11 @@ const createVoiceAdapter = () => {
45
58
  class TestRuntime extends BaseThreadRuntimeCore {
46
59
  private readonly voiceAdapter: ReturnType<typeof createVoiceAdapter>;
47
60
 
48
- constructor(voiceAdapter: ReturnType<typeof createVoiceAdapter>) {
49
- super({ getModelContext: () => ({}) });
61
+ constructor(
62
+ voiceAdapter: ReturnType<typeof createVoiceAdapter>,
63
+ contextProvider: ModelContextProvider = { getModelContext: () => ({}) },
64
+ ) {
65
+ super(contextProvider);
50
66
  this.voiceAdapter = voiceAdapter;
51
67
  }
52
68
 
@@ -99,7 +115,7 @@ class TestRuntime extends BaseThreadRuntimeCore {
99
115
  resumeRun(_config: ResumeRunConfig) {}
100
116
  addToolResult(_options: AddToolResultOptions) {}
101
117
  resumeToolCall(_options: ResumeToolCallOptions) {}
102
- respondToToolApproval(_options: RespondToToolApprovalOptions) {}
118
+ async respondToToolApproval(_options: RespondToToolApprovalOptions) {}
103
119
  cancelRun() {}
104
120
  exportExternalState() {
105
121
  return {};
@@ -126,6 +142,92 @@ describe("BaseThreadRuntimeCore subscriptions", () => {
126
142
  expect(() => runtime.reset()).toThrow(error);
127
143
  expect(laterSubscriber).toHaveBeenCalledOnce();
128
144
  });
145
+
146
+ it("isolates initialize listener errors during late replay", async () => {
147
+ const consoleError = vi
148
+ .spyOn(console, "error")
149
+ .mockImplementation(() => {});
150
+ const runtime = new TestRuntime(createVoiceAdapter());
151
+ runtime.reset();
152
+
153
+ const syncError = new Error("sync listener failed");
154
+ const asyncError = new Error("async listener failed");
155
+ runtime.unstable_on("initialize", () => {
156
+ throw syncError;
157
+ });
158
+ runtime.unstable_on("initialize", async () => {
159
+ throw asyncError;
160
+ });
161
+
162
+ await vi.waitFor(() => {
163
+ expect(consoleError).toHaveBeenCalledTimes(2);
164
+ expect(consoleError).toHaveBeenCalledWith(
165
+ '[assistant-ui] Thread runtime "initialize" listener threw an error',
166
+ syncError,
167
+ );
168
+ expect(consoleError).toHaveBeenCalledWith(
169
+ '[assistant-ui] Thread runtime "initialize" listener threw an error',
170
+ asyncError,
171
+ );
172
+ });
173
+ });
174
+
175
+ it("isolates modelContextUpdate listener errors during context updates", () => {
176
+ const consoleError = vi
177
+ .spyOn(console, "error")
178
+ .mockImplementation(() => {});
179
+ const provider = new CompositeContextProvider();
180
+ const runtime = new TestRuntime(createVoiceAdapter(), provider);
181
+ const listenerError = new Error("model context listener failed");
182
+ const laterListener = vi.fn();
183
+
184
+ runtime.unstable_on("modelContextUpdate", () => {
185
+ throw listenerError;
186
+ });
187
+ runtime.unstable_on("modelContextUpdate", laterListener);
188
+
189
+ const unregister = provider.registerModelContextProvider({
190
+ getModelContext: () => ({}),
191
+ });
192
+
193
+ expect(laterListener).toHaveBeenCalledOnce();
194
+ expect(consoleError).toHaveBeenNthCalledWith(
195
+ 1,
196
+ '[assistant-ui] Thread runtime "modelContextUpdate" listener threw an error',
197
+ listenerError,
198
+ );
199
+ expect(() => unregister()).not.toThrow();
200
+ expect(laterListener).toHaveBeenCalledTimes(2);
201
+ expect(consoleError).toHaveBeenNthCalledWith(
202
+ 2,
203
+ '[assistant-ui] Thread runtime "modelContextUpdate" listener threw an error',
204
+ listenerError,
205
+ );
206
+ });
207
+
208
+ it("observes rejected modelContextUpdate listener promises", async () => {
209
+ const consoleError = vi
210
+ .spyOn(console, "error")
211
+ .mockImplementation(() => {});
212
+ const provider = new CompositeContextProvider();
213
+ const runtime = new TestRuntime(createVoiceAdapter(), provider);
214
+ const listenerError = new Error("async model context listener failed");
215
+
216
+ runtime.unstable_on("modelContextUpdate", async () => {
217
+ throw listenerError;
218
+ });
219
+
220
+ expect(() =>
221
+ provider.registerModelContextProvider({ getModelContext: () => ({}) }),
222
+ ).not.toThrow();
223
+
224
+ await vi.waitFor(() => {
225
+ expect(consoleError).toHaveBeenCalledWith(
226
+ '[assistant-ui] Thread runtime "modelContextUpdate" listener threw an error',
227
+ listenerError,
228
+ );
229
+ });
230
+ });
129
231
  });
130
232
 
131
233
  describe("BaseThreadRuntimeCore voice volume subscriptions", () => {
@@ -205,3 +307,46 @@ describe("BaseThreadRuntimeCore voice volume subscriptions", () => {
205
307
  });
206
308
  });
207
309
  });
310
+
311
+ describe("BaseThreadRuntimeCore voice transcripts", () => {
312
+ it("completes a final-only reply before the next streamed reply", () => {
313
+ const voiceAdapter = createVoiceAdapter();
314
+ const runtime = new TestRuntime(voiceAdapter);
315
+ runtime.connectVoice();
316
+
317
+ try {
318
+ voiceAdapter.emitTranscript({
319
+ role: "assistant",
320
+ text: "Finished reply",
321
+ isFinal: true,
322
+ });
323
+ expect(runtime.messages).toMatchObject([
324
+ {
325
+ content: [{ type: "text", text: "Finished reply" }],
326
+ status: { type: "complete", reason: "stop" },
327
+ },
328
+ ]);
329
+
330
+ voiceAdapter.emitTranscript({ role: "assistant", text: "Next" });
331
+ expect(runtime.messages.at(-1)?.status).toEqual({ type: "running" });
332
+
333
+ voiceAdapter.emitTranscript({
334
+ role: "assistant",
335
+ text: "Next reply",
336
+ isFinal: true,
337
+ });
338
+ expect(runtime.messages).toMatchObject([
339
+ {
340
+ content: [{ type: "text", text: "Finished reply" }],
341
+ status: { type: "complete", reason: "stop" },
342
+ },
343
+ {
344
+ content: [{ type: "text", text: "Next reply" }],
345
+ status: { type: "complete", reason: "stop" },
346
+ },
347
+ ]);
348
+ } finally {
349
+ runtime.disconnectVoice();
350
+ }
351
+ });
352
+ });
@@ -51,7 +51,7 @@ export abstract class BaseThreadRuntimeCore
51
51
  {
52
52
  private _isInitialized = false;
53
53
 
54
- protected readonly repository = new MessageRepository();
54
+ protected repository = new MessageRepository();
55
55
  public abstract get adapters(): BaseThreadAdapters | undefined;
56
56
  public abstract get isDisabled(): boolean;
57
57
  public abstract get isSendDisabled(): boolean;
@@ -68,7 +68,7 @@ export abstract class BaseThreadRuntimeCore
68
68
  public abstract resumeToolCall(options: ResumeToolCallOptions): void;
69
69
  public abstract respondToToolApproval(
70
70
  options: RespondToToolApprovalOptions,
71
- ): void;
71
+ ): Promise<void>;
72
72
  public abstract cancelRun(): void;
73
73
  public abstract exportExternalState(): any;
74
74
  public abstract importExternalState(state: any): void;
@@ -393,6 +393,10 @@ export abstract class BaseThreadRuntimeCore
393
393
  this._notifySubscribers();
394
394
  }
395
395
  } else {
396
+ const status: ThreadAssistantMessage["status"] = transcript.isFinal
397
+ ? { type: "complete", reason: "stop" }
398
+ : { type: "running" };
399
+
396
400
  if (!this._currentAssistantMsg) {
397
401
  this._currentAssistantMsg = {
398
402
  id: generateId(),
@@ -405,7 +409,7 @@ export abstract class BaseThreadRuntimeCore
405
409
  steps: [],
406
410
  custom: {},
407
411
  },
408
- status: { type: "running" },
412
+ status,
409
413
  createdAt: new Date(),
410
414
  };
411
415
  this._voiceMessages.push(this._currentAssistantMsg);
@@ -415,9 +419,7 @@ export abstract class BaseThreadRuntimeCore
415
419
  const updated: ThreadAssistantMessage = {
416
420
  ...this._currentAssistantMsg,
417
421
  content: [{ type: "text", text: transcript.text }],
418
- ...(transcript.isFinal
419
- ? { status: { type: "complete", reason: "stop" } }
420
- : {}),
422
+ status,
421
423
  };
422
424
  this._voiceMessages[idx] = updated;
423
425
  this._currentAssistantMsg = updated;
@@ -518,7 +520,11 @@ export abstract class BaseThreadRuntimeCore
518
520
  const wrapped = callback as (payload?: unknown) => void;
519
521
  if (event === "modelContextUpdate") {
520
522
  // provider.subscribe is `() => void`; pump the typed empty payload to the user callback.
521
- return this._contextProvider.subscribe?.(() => wrapped({})) ?? (() => {});
523
+ return (
524
+ this._contextProvider.subscribe?.(() =>
525
+ notifyEventListeners([wrapped], {}, `Thread runtime "${event}"`),
526
+ ) ?? (() => {})
527
+ );
522
528
  }
523
529
 
524
530
  let subscribers = this._eventSubscribers.get(event);
@@ -532,7 +538,9 @@ export abstract class BaseThreadRuntimeCore
532
538
  // after the thread already initialized, mirroring a BehaviorSubject.
533
539
  if (event === "initialize" && this._isInitialized) {
534
540
  queueMicrotask(() => {
535
- if (subscribers.has(wrapped)) wrapped({});
541
+ if (subscribers.has(wrapped)) {
542
+ notifyEventListeners([wrapped], {}, `Thread runtime "${event}"`);
543
+ }
536
544
  });
537
545
  }
538
546
 
@@ -21,16 +21,21 @@ export type AttachmentAddErrorEvent = {
21
21
 
22
22
  export type ComposerRuntimeEventPayload = {
23
23
  /**
24
- * @deprecated State-derivable. Observe `state.text` clearing via
25
- * `subscribe` + `getState` instead. Kept for backward compatibility.
24
+ * Fired after a send with the size of what went out. The composer state is
25
+ * already cleared when it fires, so the counts are only available here.
26
26
  */
27
- send: Record<string, never>;
27
+ send: {
28
+ readonly chars: number;
29
+ readonly attachments: number;
30
+ };
28
31
  /**
29
32
  * @deprecated State-derivable. Observe `state.attachments` via `subscribe` +
30
33
  * `getState` instead. Kept for backward compatibility.
31
34
  */
32
- attachmentAdd: Record<string, never>;
33
- attachmentAddError: AttachmentAddErrorEvent;
35
+ attachmentAdd: { readonly contentType?: string | undefined };
36
+ attachmentAddError: AttachmentAddErrorEvent & {
37
+ readonly contentType?: string | undefined;
38
+ };
34
39
  };
35
40
 
36
41
  export type ComposerRuntimeEventType = keyof ComposerRuntimeEventPayload;
@@ -1,5 +1,8 @@
1
1
  import type { Unsubscribe } from "../../types/unsubscribe";
2
- import type { ThreadRuntimeCore } from "./thread-runtime-core";
2
+ import type {
3
+ ThreadRuntimeCore,
4
+ ThreadRuntimeEventType,
5
+ } from "./thread-runtime-core";
3
6
 
4
7
  export type ThreadListItemStatus = "archived" | "regular" | "new" | "deleted";
5
8
 
@@ -16,8 +19,14 @@ export type ThreadListItemCoreState = {
16
19
  readonly runtime?: ThreadRuntimeCore | undefined;
17
20
  };
18
21
 
22
+ export type ThreadListRuntimeEvent = {
23
+ readonly threadId: string;
24
+ readonly type: ThreadRuntimeEventType;
25
+ };
26
+
19
27
  export type ThreadListRuntimeCore = {
20
28
  readonly isLoading: boolean;
29
+ readonly loadError?: unknown;
21
30
  readonly isLoadingMore?: boolean;
22
31
  readonly hasMore?: boolean;
23
32
  mainThreadId: string;
@@ -41,6 +50,17 @@ export type ThreadListRuntimeCore = {
41
50
  */
42
51
  unstable_isThreadRunning?(threadId: string): boolean;
43
52
 
53
+ /**
54
+ * Lifecycle events from every thread this list keeps alive, including
55
+ * threads that are not the main one. Implemented by thread lists that keep
56
+ * runtimes alive for non-main threads. A thread list that mounts only the
57
+ * main thread leaves this undefined: its other threads have no runtime and so
58
+ * emit nothing, and the main thread's runtime is observed directly.
59
+ */
60
+ unstable_subscribeThreadEvents?(
61
+ callback: (event: ThreadListRuntimeEvent) => void,
62
+ ): Unsubscribe;
63
+
44
64
  getItemById(threadId: string): ThreadListItemCoreState | undefined;
45
65
 
46
66
  switchToThread(
@@ -67,7 +87,10 @@ export type ThreadListRuntimeCore = {
67
87
  initialize(
68
88
  threadId: string,
69
89
  ): Promise<{ remoteId: string; externalId: string | undefined }>;
70
- generateTitle(threadId: string): Promise<void>;
90
+ generateTitle(
91
+ threadId: string,
92
+ options?: { automatic?: boolean },
93
+ ): Promise<void>;
71
94
 
72
95
  subscribe(callback: () => void): Unsubscribe;
73
96
  };
@@ -63,6 +63,8 @@ export type RespondToToolApprovalOptions = {
63
63
  approved: boolean;
64
64
  /** The approval option that produced this decision, when the request carried options. */
65
65
  optionId?: string;
66
+ /** The free-form answer, when the request asked for one. */
67
+ text?: string;
66
68
  reason?: string;
67
69
  };
68
70
 
@@ -165,7 +167,21 @@ export type ThreadRuntimeCore = Readonly<{
165
167
 
166
168
  addToolResult: (options: AddToolResultOptions) => void;
167
169
  resumeToolCall: (options: ResumeToolCallOptions) => void;
168
- respondToToolApproval: (options: RespondToToolApprovalOptions) => void;
170
+ /**
171
+ * Records a decision on a tool approval gate. Resolves once the runtime has
172
+ * accepted the response and rejects when it could not be recorded, so a
173
+ * caller can leave the gate retryable rather than spending it. A capability
174
+ * or state precondition still throws synchronously; a failure to record
175
+ * arrives as a rejection, including one an adapter raises synchronously.
176
+ *
177
+ * Acceptance is as far as the runtime can see the response: one that records
178
+ * the decision locally settles on the record, while one that answers by
179
+ * resuming a run settles on the resume. A failure of the work the decision
180
+ * unblocks is reported through the runtime's own error channel, not here.
181
+ */
182
+ respondToToolApproval: (
183
+ options: RespondToToolApprovalOptions,
184
+ ) => Promise<void>;
169
185
 
170
186
  speak: (messageId: string) => void;
171
187
  stopSpeaking: () => void;
@@ -0,0 +1,57 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { getAutoStatus } from "./auto-status";
3
+
4
+ describe("getAutoStatus", () => {
5
+ it("reports a cancelled message as incomplete", () => {
6
+ expect(
7
+ getAutoStatus(true, false, false, false, undefined, true),
8
+ ).toMatchObject({ type: "incomplete", reason: "cancelled" });
9
+ });
10
+
11
+ it("keeps a cancelled message incomplete once a later message arrives", () => {
12
+ expect(
13
+ getAutoStatus(false, false, false, false, undefined, true),
14
+ ).toMatchObject({ type: "incomplete", reason: "cancelled" });
15
+ });
16
+
17
+ it("reports an uncancelled message as complete", () => {
18
+ expect(
19
+ getAutoStatus(true, false, false, false, undefined, false),
20
+ ).toMatchObject({ type: "complete", reason: "unknown" });
21
+ });
22
+
23
+ it.each([
24
+ ["running", { type: "running" }, true, false, false, undefined],
25
+ [
26
+ "an interrupted tool call",
27
+ { type: "requires-action", reason: "interrupt" },
28
+ false,
29
+ true,
30
+ true,
31
+ undefined,
32
+ ],
33
+ [
34
+ "a pending tool call",
35
+ { type: "requires-action", reason: "tool-calls" },
36
+ false,
37
+ false,
38
+ true,
39
+ undefined,
40
+ ],
41
+ [
42
+ "an error",
43
+ { type: "incomplete", reason: "error", error: "boom" },
44
+ false,
45
+ false,
46
+ false,
47
+ "boom",
48
+ ],
49
+ ])(
50
+ "keeps %s ahead of cancellation",
51
+ (_label, expected, isRunning, interrupted, pending, error) => {
52
+ expect(
53
+ getAutoStatus(true, isRunning, interrupted, pending, error, true),
54
+ ).toMatchObject(expected);
55
+ },
56
+ );
57
+ });
@@ -36,6 +36,15 @@ const AUTO_STATUS_COMPLETE = Object.freeze(
36
36
  { [symbolAutoStatus]: true },
37
37
  ),
38
38
  );
39
+ const AUTO_STATUS_CANCELLED = Object.freeze(
40
+ Object.assign(
41
+ {
42
+ type: "incomplete" as const,
43
+ reason: "cancelled" as const,
44
+ },
45
+ { [symbolAutoStatus]: true },
46
+ ),
47
+ );
39
48
 
40
49
  const AUTO_STATUS_PENDING = Object.freeze(
41
50
  Object.assign(
@@ -66,6 +75,7 @@ export const getAutoStatus = (
66
75
  hasInterruptedToolCalls: boolean,
67
76
  hasPendingToolCalls: boolean,
68
77
  error?: ReadonlyJSONValue,
78
+ isCancelled?: boolean,
69
79
  ): MessageStatus => {
70
80
  if (isLast && error) {
71
81
  return Object.assign(
@@ -84,7 +94,9 @@ export const getAutoStatus = (
84
94
  ? AUTO_STATUS_INTERRUPT
85
95
  : hasPendingToolCalls
86
96
  ? AUTO_STATUS_PENDING
87
- : AUTO_STATUS_COMPLETE;
97
+ : isCancelled
98
+ ? AUTO_STATUS_CANCELLED
99
+ : AUTO_STATUS_COMPLETE;
88
100
  };
89
101
 
90
102
  export const getContentAutoStatus = (