@assistant-ui/core 0.3.15 → 0.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/dist/internal.d.ts +7 -2
  2. package/dist/internal.js +8 -3
  3. package/dist/model-context/frame/host.d.ts.map +1 -1
  4. package/dist/model-context/frame/host.js +3 -2
  5. package/dist/model-context/frame/host.js.map +1 -1
  6. package/dist/model-context/frame/provider.d.ts +2 -0
  7. package/dist/model-context/frame/provider.d.ts.map +1 -1
  8. package/dist/model-context/frame/provider.js +88 -25
  9. package/dist/model-context/frame/provider.js.map +1 -1
  10. package/dist/model-context/interactable-composer-metadata.d.ts +1 -1
  11. package/dist/model-context/interactable-composer-metadata.js +1 -1
  12. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  13. package/dist/model-context/registry.d.ts +2 -2
  14. package/dist/model-context/registry.d.ts.map +1 -1
  15. package/dist/model-context/registry.js +9 -28
  16. package/dist/model-context/registry.js.map +1 -1
  17. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  18. package/dist/react/client/InMemoryThreadList.js +24 -20
  19. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  20. package/dist/react/client/Interactables.d.ts.map +1 -1
  21. package/dist/react/client/Interactables.js +59 -177
  22. package/dist/react/client/Interactables.js.map +1 -1
  23. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  24. package/dist/react/client/RemoteThreadList.js +23 -56
  25. package/dist/react/client/RemoteThreadList.js.map +1 -1
  26. package/dist/react/index.d.ts +2 -1
  27. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  28. package/dist/react/interactables-legacy/Interactables.js +56 -174
  29. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  30. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts +24 -0
  31. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -0
  32. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +138 -0
  33. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -0
  34. package/dist/react/primitives/composer/ComposerIf.js.map +1 -1
  35. package/dist/react/primitives/composer/ComposerQueue.d.ts +2 -1
  36. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  37. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  38. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +1 -1
  39. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +1 -1
  40. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  41. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +23 -55
  42. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  43. package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -1
  44. package/dist/react/runtimes/assistant-transport/runManager.js +2 -12
  45. package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -1
  46. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +3 -10
  47. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  48. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +20 -67
  49. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  50. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts +12 -0
  51. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts.map +1 -0
  52. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js +10 -0
  53. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js.map +1 -0
  54. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -1
  55. package/dist/react/runtimes/createMessageConverter.d.ts +2 -1
  56. package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
  57. package/dist/react/runtimes/external-message-converter.d.ts +6 -25
  58. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  59. package/dist/react/runtimes/external-message-converter.js +33 -233
  60. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  61. package/dist/runtime/api/composer-runtime.d.ts +1 -1
  62. package/dist/runtime/api/composer-runtime.js.map +1 -1
  63. package/dist/runtime/api/thread-runtime.d.ts +1 -2
  64. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  65. package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -1
  66. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  67. package/dist/runtime/base/base-composer-runtime-core.js +14 -29
  68. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  69. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -4
  70. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  71. package/dist/runtime/base/base-thread-runtime-core.js +3 -9
  72. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  73. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -1
  74. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  75. package/dist/runtime/base/default-thread-composer-runtime-core.js +1 -1
  76. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  77. package/dist/runtime/interfaces/composer-runtime-core.d.ts +1 -1
  78. package/dist/runtime/interfaces/thread-runtime-core.d.ts +1 -1
  79. package/dist/runtime/internal.d.ts +2 -1
  80. package/dist/runtime/internal.js +2 -1
  81. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +1 -1
  82. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  83. package/dist/runtime/queue/message-queue.js +1 -1
  84. package/dist/runtime/queue/message-queue.js.map +1 -1
  85. package/dist/runtime/queue/queue-item.d.ts +12 -0
  86. package/dist/runtime/queue/queue-item.d.ts.map +1 -0
  87. package/dist/runtime/queue/queue-item.js +6 -0
  88. package/dist/runtime/queue/queue-item.js.map +1 -0
  89. package/dist/runtime/utils/abortable-thread-load.d.ts +15 -0
  90. package/dist/runtime/utils/abortable-thread-load.d.ts.map +1 -0
  91. package/dist/runtime/utils/abortable-thread-load.js +35 -0
  92. package/dist/runtime/utils/abortable-thread-load.js.map +1 -0
  93. package/dist/runtime/utils/attachment-add-operations.d.ts +18 -0
  94. package/dist/runtime/utils/attachment-add-operations.d.ts.map +1 -0
  95. package/dist/runtime/utils/attachment-add-operations.js +43 -0
  96. package/dist/runtime/utils/attachment-add-operations.js.map +1 -0
  97. package/dist/runtime/utils/auto-status.d.ts +6 -1
  98. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  99. package/dist/runtime/utils/auto-status.js +7 -1
  100. package/dist/runtime/utils/auto-status.js.map +1 -1
  101. package/dist/runtime/utils/external-message-conversion.d.ts +47 -0
  102. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -0
  103. package/dist/runtime/utils/external-message-conversion.js +221 -0
  104. package/dist/runtime/utils/external-message-conversion.js.map +1 -0
  105. package/dist/runtime/utils/external-store-message.d.ts +2 -1
  106. package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
  107. package/dist/runtime/utils/external-store-message.js +2 -1
  108. package/dist/runtime/utils/external-store-message.js.map +1 -1
  109. package/dist/runtime/utils/message-repository-session.d.ts +30 -0
  110. package/dist/runtime/utils/message-repository-session.d.ts.map +1 -0
  111. package/dist/runtime/utils/message-repository-session.js +130 -0
  112. package/dist/runtime/utils/message-repository-session.js.map +1 -0
  113. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  114. package/dist/runtime/utils/message-repository.js +3 -4
  115. package/dist/runtime/utils/message-repository.js.map +1 -1
  116. package/dist/runtime/utils/pending-tool-calls.d.ts +20 -0
  117. package/dist/runtime/utils/pending-tool-calls.d.ts.map +1 -0
  118. package/dist/runtime/utils/pending-tool-calls.js +22 -0
  119. package/dist/runtime/utils/pending-tool-calls.js.map +1 -0
  120. package/dist/runtime/utils/thread-message-like.d.ts +2 -1
  121. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  122. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  123. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +2 -5
  124. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  125. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +3 -10
  126. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  127. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +19 -1
  128. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  129. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +122 -20
  130. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  131. package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -1
  132. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  133. package/dist/runtimes/local/local-thread-runtime-core.js +20 -2
  134. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  135. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +12 -1
  136. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  137. package/dist/runtimes/remote-thread-list/remote-thread-state.js +77 -1
  138. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  139. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  140. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +2 -0
  141. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  142. package/dist/store/clients/external-thread.d.ts.map +1 -1
  143. package/dist/store/clients/external-thread.js +74 -99
  144. package/dist/store/clients/external-thread.js.map +1 -1
  145. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  146. package/dist/store/clients/single-thread-list.js +1 -1
  147. package/dist/store/clients/single-thread-list.js.map +1 -1
  148. package/dist/store/clients/suggestions.d.ts.map +1 -1
  149. package/dist/store/clients/suggestions.js +16 -7
  150. package/dist/store/clients/suggestions.js.map +1 -1
  151. package/dist/store/index.d.ts +4 -2
  152. package/dist/store/scope-registration.d.ts +1 -1
  153. package/dist/store/scopes/composer.d.ts +2 -1
  154. package/dist/store/scopes/composer.d.ts.map +1 -1
  155. package/dist/store/scopes/message.d.ts +1 -1
  156. package/dist/store/scopes/queue-item.d.ts +2 -9
  157. package/dist/store/scopes/queue-item.d.ts.map +1 -1
  158. package/dist/store/scopes/queue-item.js +0 -6
  159. package/dist/types/message.d.ts +7 -3
  160. package/dist/types/message.d.ts.map +1 -1
  161. package/dist/types/message.js.map +1 -1
  162. package/dist/utils/data-url.d.ts +14 -1
  163. package/dist/utils/data-url.d.ts.map +1 -1
  164. package/dist/utils/data-url.js +13 -1
  165. package/dist/utils/data-url.js.map +1 -1
  166. package/dist/utils/invoke-user-callback.d.ts +5 -0
  167. package/dist/utils/invoke-user-callback.d.ts.map +1 -0
  168. package/dist/utils/invoke-user-callback.js +19 -0
  169. package/dist/utils/invoke-user-callback.js.map +1 -0
  170. package/dist/utils/normalizePartStatus.js.map +1 -1
  171. package/package.json +10 -10
  172. package/src/internal.ts +12 -0
  173. package/src/model-context/frame/host.test.ts +91 -7
  174. package/src/model-context/frame/host.ts +7 -2
  175. package/src/model-context/frame/provider.test.ts +267 -12
  176. package/src/model-context/frame/provider.ts +113 -31
  177. package/src/model-context/interactable-composer-metadata.ts +1 -1
  178. package/src/model-context/registry.test.ts +44 -0
  179. package/src/model-context/registry.ts +13 -35
  180. package/src/react/client/InMemoryThreadList.test.tsx +49 -0
  181. package/src/react/client/InMemoryThreadList.ts +7 -1
  182. package/src/react/client/Interactables.ts +19 -178
  183. package/src/react/client/RemoteThreadList.test.ts +96 -0
  184. package/src/react/client/RemoteThreadList.ts +27 -59
  185. package/src/react/interactables-legacy/Interactables.ts +20 -177
  186. package/src/react/interactables-shared/useInteractablePersistenceQueue.test.tsx +161 -0
  187. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +206 -0
  188. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +26 -56
  189. package/src/react/runtimes/assistant-transport/runManager.ts +4 -17
  190. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +92 -0
  191. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +40 -141
  192. package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.test.ts +37 -0
  193. package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.ts +18 -0
  194. package/src/react/runtimes/external-message-converter.test.ts +0 -14
  195. package/src/react/runtimes/external-message-converter.test.tsx +77 -0
  196. package/src/react/runtimes/external-message-converter.ts +58 -481
  197. package/src/runtime/api/composer-runtime.ts +1 -1
  198. package/src/runtime/api/message-runtime.test.ts +28 -1
  199. package/src/runtime/base/base-composer-runtime-core.ts +20 -40
  200. package/src/runtime/base/base-thread-runtime-core.test.ts +16 -0
  201. package/src/runtime/base/base-thread-runtime-core.ts +6 -11
  202. package/src/runtime/base/default-thread-composer-runtime-core.ts +1 -4
  203. package/src/runtime/interfaces/composer-runtime-core.ts +1 -1
  204. package/src/runtime/interfaces/thread-runtime-core.ts +1 -1
  205. package/src/runtime/internal.ts +5 -0
  206. package/src/runtime/queue/external-thread-queue-adapter.ts +1 -1
  207. package/src/runtime/queue/message-queue.ts +1 -4
  208. package/src/runtime/queue/queue-item.ts +10 -0
  209. package/src/runtime/utils/abortable-thread-load.test.ts +87 -0
  210. package/src/runtime/utils/abortable-thread-load.ts +53 -0
  211. package/src/runtime/utils/attachment-add-operations.ts +59 -0
  212. package/src/runtime/utils/auto-status.ts +33 -0
  213. package/src/runtime/utils/external-message-conversion.test.ts +113 -0
  214. package/src/runtime/utils/external-message-conversion.ts +494 -0
  215. package/src/runtime/utils/external-store-message.ts +2 -0
  216. package/src/runtime/utils/message-repository-session.test.ts +195 -0
  217. package/src/runtime/utils/message-repository-session.ts +176 -0
  218. package/src/runtime/utils/message-repository.ts +7 -4
  219. package/src/runtime/utils/pending-tool-calls.test.ts +57 -0
  220. package/src/runtime/utils/pending-tool-calls.ts +35 -0
  221. package/src/runtime/utils/thread-message-like.ts +2 -0
  222. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +6 -14
  223. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +175 -28
  224. package/src/runtimes/local/local-thread-runtime-core.test.ts +172 -1
  225. package/src/runtimes/local/local-thread-runtime-core.ts +48 -3
  226. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +37 -0
  227. package/src/runtimes/remote-thread-list/remote-thread-state.ts +123 -0
  228. package/src/runtimes/tool-invocations/EDGE_CASES.md +17 -0
  229. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +140 -0
  230. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +3 -0
  231. package/src/store/clients/external-thread.ts +16 -61
  232. package/src/store/clients/single-thread-list.ts +4 -1
  233. package/src/store/clients/suggestions.test.ts +31 -0
  234. package/src/store/clients/suggestions.ts +18 -19
  235. package/src/store/scopes/message.ts +1 -1
  236. package/src/store/scopes/queue-item.ts +2 -9
  237. package/src/tests/MessageRepository.test.ts +77 -0
  238. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +194 -0
  239. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +158 -0
  240. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +164 -0
  241. package/src/tests/external-store-thread-runtime-core.test.ts +642 -0
  242. package/src/tests/external-thread-attachments.test.tsx +173 -0
  243. package/src/tests/single-thread-list-item.test.tsx +47 -0
  244. package/src/tests/thread-message-like.test.ts +39 -1
  245. package/src/types/message.ts +8 -3
  246. package/src/utils/data-url.test.ts +52 -1
  247. package/src/utils/data-url.ts +21 -0
  248. package/src/utils/invoke-user-callback.test.ts +82 -0
  249. package/src/utils/invoke-user-callback.ts +31 -0
  250. package/src/utils/normalizePartStatus.ts +1 -1
  251. package/dist/store/scopes/queue-item.js.map +0 -1
@@ -1,5 +1,4 @@
1
1
  import type { ThreadListRuntimeCore } from "../../runtime/interfaces/thread-list-runtime-core";
2
- import { generateId } from "../../utils/id";
3
2
  import { BaseSubscribable } from "../../subscribable/subscribable";
4
3
  import { OptimisticState } from "../../runtimes/remote-thread-list/optimistic-state";
5
4
  import { EMPTY_THREAD_CORE } from "../../runtimes/remote-thread-list/empty-thread-core";
@@ -9,10 +8,14 @@ import type {
9
8
  } from "../../runtimes/remote-thread-list/remote-thread-state";
10
9
  import {
11
10
  classifyThreads,
11
+ createEmptyRemoteThreadState,
12
12
  createThreadMappingId,
13
13
  getThreadData,
14
14
  normalizeCursor,
15
15
  updateStatusReducer,
16
+ preserveMidLoadTransitions,
17
+ seedNewThread,
18
+ statusSnapshot,
16
19
  } from "../../runtimes/remote-thread-list/remote-thread-state";
17
20
  import type {
18
21
  RemoteThreadListAdapter,
@@ -49,47 +52,7 @@ const threadStatusError = (
49
52
  `Thread "${threadIdOrRemoteId}" has status "${status}", so it cannot ${action}.`,
50
53
  );
51
54
 
52
- const EMPTY_REMOTE_STATE: RemoteThreadState = {
53
- isLoading: true,
54
- isLoadingMore: false,
55
- cursor: undefined,
56
- newThreadId: undefined,
57
- threadIds: [],
58
- archivedThreadIds: [],
59
- threadIdMap: {},
60
- threadData: {},
61
- };
62
-
63
- const addNewThread = (state: RemoteThreadState) => {
64
- let id: string;
65
- do {
66
- id = `__LOCALID_${generateId()}`;
67
- } while (state.threadIdMap[id]);
68
-
69
- const mappingId = createThreadMappingId(id);
70
- return {
71
- id,
72
- state: {
73
- ...state,
74
- newThreadId: id,
75
- threadIdMap: {
76
- ...state.threadIdMap,
77
- [id]: mappingId,
78
- },
79
- threadData: {
80
- ...state.threadData,
81
- [mappingId]: {
82
- status: "new",
83
- id,
84
- remoteId: undefined,
85
- externalId: undefined,
86
- title: undefined,
87
- custom: undefined,
88
- } satisfies RemoteThreadData,
89
- },
90
- },
91
- };
92
- };
55
+ const EMPTY_REMOTE_STATE = createEmptyRemoteThreadState();
93
56
 
94
57
  export class RemoteThreadListThreadListRuntimeCore
95
58
  extends BaseSubscribable
@@ -158,6 +121,7 @@ export class RemoteThreadListThreadListRuntimeCore
158
121
  if (!this._loadThreadsPromise) {
159
122
  const generation = this._loadGeneration;
160
123
  let replacedList = false;
124
+ const statusAtRequest = statusSnapshot(this._state.baseValue);
161
125
  this._loadThreadsPromise = this._state
162
126
  .optimisticUpdate({
163
127
  execute: () => this._options.adapter.list(),
@@ -186,7 +150,7 @@ export class RemoteThreadListThreadListRuntimeCore
186
150
  threadIdMap: {},
187
151
  threadData: {},
188
152
  });
189
- return {
153
+ const merged = {
190
154
  ...state,
191
155
  isLoading: false,
192
156
  cursor: normalizeCursor(l.nextCursor),
@@ -195,6 +159,7 @@ export class RemoteThreadListThreadListRuntimeCore
195
159
  threadIdMap: { ...state.threadIdMap, ...fresh.threadIdMap },
196
160
  threadData: { ...state.threadData, ...fresh.threadData },
197
161
  };
162
+ return preserveMidLoadTransitions(merged, state, statusAtRequest);
198
163
  },
199
164
  })
200
165
  .catch((error: unknown) => {
@@ -437,7 +402,7 @@ export class RemoteThreadListThreadListRuntimeCore
437
402
  if (preservedDraft !== undefined) {
438
403
  this._mainThreadId = preservedDraft;
439
404
  } else {
440
- const seeded = addNewThread(nextState);
405
+ const seeded = seedNewThread(nextState);
441
406
  this._mainThreadId = seeded.id;
442
407
  nextState = seeded.state;
443
408
  }
@@ -762,7 +727,7 @@ export class RemoteThreadListThreadListRuntimeCore
762
727
  const state = this._state.baseValue;
763
728
  let id: string | undefined = this._state.value.newThreadId;
764
729
  if (id === undefined) {
765
- const next = addNewThread(state);
730
+ const next = seedNewThread(state);
766
731
  id = next.id;
767
732
  this._state.update(next.state);
768
733
  }
@@ -857,17 +822,22 @@ export class RemoteThreadListThreadListRuntimeCore
857
822
  for await (const result of messageStream) {
858
823
  if (adapterGeneration !== this._adapterGeneration) return;
859
824
  const newTitle = result.parts.filter((c) => c.type === "text")[0]?.text;
860
- const state = this._state.baseValue;
861
- const currentData = getThreadData(state, data.id);
862
- if (!currentData) continue;
863
- this._state.update({
864
- ...state,
865
- threadData: {
866
- ...state.threadData,
867
- [currentData.id]: {
868
- ...currentData,
869
- title: newTitle,
870
- },
825
+ await this._state.optimisticUpdate({
826
+ execute: async () => {},
827
+ optimistic: (state) => {
828
+ if (adapterGeneration !== this._adapterGeneration) return state;
829
+ const currentData = getThreadData(state, data.id);
830
+ if (!currentData) return state;
831
+ return {
832
+ ...state,
833
+ threadData: {
834
+ ...state.threadData,
835
+ [currentData.id]: {
836
+ ...currentData,
837
+ title: newTitle,
838
+ },
839
+ },
840
+ };
871
841
  },
872
842
  });
873
843
  }
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { invokeUserCallback } from "../../../utils/invoke-user-callback";
2
3
  import { useLatestRef } from "./useLatestRef";
3
4
 
4
5
  export type RunManager = Readonly<{
@@ -11,25 +12,11 @@ const disposeReason = Symbol("assistant-transport-dispose");
11
12
 
12
13
  type LifecycleCallbackName = "onCancel" | "onError" | "onFinish";
13
14
 
14
- const reportCallbackError = (name: LifecycleCallbackName, error: unknown) => {
15
- console.error(
16
- `[assistant-ui] Assistant transport ${name} callback threw an error`,
17
- error,
18
- );
19
- };
20
-
21
- const invokeCallback = async (
15
+ const invokeCallback = (
22
16
  name: LifecycleCallbackName,
23
17
  callback: (() => unknown) | undefined,
24
- ): Promise<void> => {
25
- try {
26
- await callback?.();
27
- } catch (error) {
28
- try {
29
- reportCallbackError(name, error);
30
- } catch {}
31
- }
32
- };
18
+ ) =>
19
+ invokeUserCallback("assistant-ui", `Assistant transport ${name}`, callback);
33
20
 
34
21
  export function useRunManager(config: {
35
22
  onRun: (signal: AbortSignal) => Promise<void>;
@@ -188,6 +188,98 @@ describe("useAssistantCloudThreadHistoryAdapter", () => {
188
188
  );
189
189
  });
190
190
 
191
+ it("reports frontend and MCP sources for ai-sdk/v6 tool calls", () => {
192
+ mocks.aui = mocks.makeClient("thread-1");
193
+ const cloud = makeCloud();
194
+ const cloudRef = { current: cloud };
195
+ const { result } = renderHook(() =>
196
+ useAssistantCloudThreadHistoryAdapter(cloudRef),
197
+ );
198
+ const formatted = result.current.withFormat({
199
+ format: "ai-sdk/v6",
200
+ encode: ({ message }) => message,
201
+ decode: ({ parent_id, content }) => ({
202
+ parentId: parent_id,
203
+ message: content as { id: string },
204
+ }),
205
+ getId: (message: { id: string }) => message.id,
206
+ });
207
+
208
+ formatted.reportTelemetry([
209
+ {
210
+ parentId: null,
211
+ message: {
212
+ id: "message-1",
213
+ role: "assistant",
214
+ parts: [
215
+ { type: "step-start" },
216
+ {
217
+ type: "tool-search",
218
+ toolCallId: "static-1",
219
+ input: { query: "test" },
220
+ output: { result: "ok" },
221
+ },
222
+ {
223
+ type: "dynamic-tool",
224
+ toolName: "mcp-search",
225
+ toolCallId: "dynamic-1",
226
+ input: { query: "test" },
227
+ output: { result: "ok" },
228
+ },
229
+ { type: "step-start" },
230
+ {
231
+ type: "tool-search",
232
+ toolCallId: "static-2",
233
+ input: { query: "again" },
234
+ output: { result: "ok" },
235
+ },
236
+ ],
237
+ },
238
+ },
239
+ ]);
240
+
241
+ expect(cloud.runs.report).toHaveBeenCalledWith(
242
+ expect.objectContaining({
243
+ tool_calls: [
244
+ expect.objectContaining({
245
+ tool_call_id: "static-1",
246
+ tool_source: "frontend",
247
+ }),
248
+ expect.objectContaining({
249
+ tool_call_id: "dynamic-1",
250
+ tool_source: "mcp",
251
+ }),
252
+ expect.objectContaining({
253
+ tool_call_id: "static-2",
254
+ tool_source: "frontend",
255
+ }),
256
+ ],
257
+ steps: [
258
+ {
259
+ tool_calls: [
260
+ expect.objectContaining({
261
+ tool_call_id: "static-1",
262
+ tool_source: "frontend",
263
+ }),
264
+ expect.objectContaining({
265
+ tool_call_id: "dynamic-1",
266
+ tool_source: "mcp",
267
+ }),
268
+ ],
269
+ },
270
+ {
271
+ tool_calls: [
272
+ expect.objectContaining({
273
+ tool_call_id: "static-2",
274
+ tool_source: "frontend",
275
+ }),
276
+ ],
277
+ },
278
+ ],
279
+ }),
280
+ );
281
+ });
282
+
191
283
  it("initializes the pinned item instead of the new main thread", async () => {
192
284
  mocks.aui = mocks.makeClient(undefined, "new-thread", "thread-1");
193
285
  const cloud = makeCloud();
@@ -9,8 +9,13 @@ import type {
9
9
  import type { ExportedMessageRepositoryItem } from "../../../runtime/utils/message-repository";
10
10
  import {
11
11
  type AssistantCloud,
12
+ type AssistantCloudRunReportToolCall,
12
13
  CloudMessagePersistence,
13
14
  createFormattedPersistence,
15
+ createRunTelemetryToolCall,
16
+ normalizeRunTelemetryUsage,
17
+ type RunTelemetryUsageInit,
18
+ truncateRunTelemetryText,
14
19
  } from "assistant-cloud";
15
20
  import { auiV0Decode, auiV0Encode } from "./auiV0";
16
21
  import { type AssistantClient, getClientId, useAui } from "@assistant-ui/store";
@@ -268,85 +273,12 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
268
273
  }
269
274
  }
270
275
 
271
- const MAX_SPAN_CONTENT = 50_000;
272
-
273
- function truncateStr(value: string): string {
274
- if (value.length <= MAX_SPAN_CONTENT) return value;
275
- return value.slice(0, MAX_SPAN_CONTENT);
276
- }
277
-
278
- function safeStringify(value: unknown): string | undefined {
279
- if (value == null) return undefined;
280
- try {
281
- return truncateStr(JSON.stringify(value));
282
- } catch {
283
- return undefined;
284
- }
285
- }
286
-
287
- type TelemetryToolCall = {
288
- tool_name: string;
289
- tool_call_id: string;
290
- tool_args?: string;
291
- tool_result?: string;
292
- tool_source?: "mcp" | "frontend" | "backend";
293
- };
294
-
295
- const BASE64_PATTERN = /^[A-Za-z0-9+/]{100,}={0,2}$/;
296
-
297
- function summarizeMcpResult(value: unknown): string | undefined {
298
- if (value == null) return undefined;
299
- try {
300
- const parsed = typeof value === "string" ? JSON.parse(value) : value;
301
- if (Array.isArray(parsed)) {
302
- const summarized = parsed.map((item) => {
303
- if (item && typeof item === "object" && item.type) {
304
- if (
305
- (item.type === "image" || item.type === "audio") &&
306
- typeof item.data === "string" &&
307
- BASE64_PATTERN.test(item.data.slice(0, 200))
308
- ) {
309
- const sizeKB = ((item.data.length * 3) / 4 / 1024).toFixed(1);
310
- return { ...item, data: `[${item.type}: ${sizeKB}KB]` };
311
- }
312
- }
313
- return item;
314
- });
315
- return truncateStr(JSON.stringify(summarized));
316
- }
317
- } catch {
318
- // not JSON array, fall through
319
- }
320
- return safeStringify(value);
321
- }
322
-
323
- function buildToolCall(
324
- toolName: string,
325
- toolCallId: string,
326
- args: unknown,
327
- result: unknown,
328
- argsText?: string,
329
- toolSource?: "mcp" | "frontend" | "backend",
330
- ): TelemetryToolCall {
331
- const call: TelemetryToolCall = {
332
- tool_name: toolName,
333
- tool_call_id: toolCallId,
334
- };
335
- const toolArgs = argsText ?? safeStringify(args);
336
- if (toolArgs !== undefined) call.tool_args = toolArgs;
337
- const toolResult =
338
- toolSource === "mcp" ? summarizeMcpResult(result) : safeStringify(result);
339
- if (toolResult !== undefined) call.tool_result = toolResult;
340
- if (toolSource) call.tool_source = toolSource;
341
- return call;
342
- }
343
-
344
276
  type TelemetryStepData = {
345
277
  input_tokens?: number;
346
278
  output_tokens?: number;
347
279
  reasoning_tokens?: number;
348
280
  cached_input_tokens?: number;
349
- tool_calls?: TelemetryToolCall[];
281
+ tool_calls?: AssistantCloudRunReportToolCall[];
350
282
  start_ms?: number;
351
283
  end_ms?: number;
352
284
  };
@@ -369,7 +301,7 @@ function mergeStepTimestamps(
369
301
 
370
302
  type TelemetryData = {
371
303
  status: "completed" | "incomplete" | "error";
372
- toolCalls?: TelemetryToolCall[];
304
+ toolCalls?: AssistantCloudRunReportToolCall[];
373
305
  totalSteps?: number;
374
306
  inputTokens?: number;
375
307
  outputTokens?: number;
@@ -446,13 +378,19 @@ export function extractAuiV0<T>(content: T): TelemetryData | null {
446
378
  const toolCalls = msg.content
447
379
  ?.filter((p) => p.type === "tool-call" && p.toolName && p.toolCallId)
448
380
  .map((p) =>
449
- buildToolCall(p.toolName!, p.toolCallId!, p.args, p.result, p.argsText),
381
+ createRunTelemetryToolCall({
382
+ toolName: p.toolName!,
383
+ toolCallId: p.toolCallId!,
384
+ args: p.args,
385
+ result: p.result,
386
+ argsText: p.argsText,
387
+ }),
450
388
  );
451
389
 
452
390
  const textParts = msg.content?.filter((p) => p.type === "text" && p.text);
453
391
  const outputText =
454
392
  textParts && textParts.length > 0
455
- ? truncateStr(textParts.map((p) => p.text).join(""))
393
+ ? truncateRunTelemetryText(textParts.map((p) => p.text).join(""))
456
394
  : undefined;
457
395
 
458
396
  const steps = msg.metadata?.steps;
@@ -564,29 +502,28 @@ function isDynamicToolPart(p: AiSdkV6Part): boolean {
564
502
  return p.type === "dynamic-tool" || p.type.startsWith("dynamic-tool-");
565
503
  }
566
504
 
567
- function partToToolCall(p: AiSdkV6Part): TelemetryToolCall {
568
- const toolSource: "mcp" | undefined = isDynamicToolPart(p)
505
+ function partToToolCall(p: AiSdkV6Part): AssistantCloudRunReportToolCall {
506
+ const toolSource: "mcp" | "frontend" = isDynamicToolPart(p)
569
507
  ? "mcp"
570
- : undefined;
571
- return buildToolCall(
572
- p.toolName ?? p.type.slice(5),
573
- p.toolCallId!,
574
- p.args ?? p.input,
575
- p.result ?? p.output,
576
- undefined,
508
+ : "frontend";
509
+ return createRunTelemetryToolCall({
510
+ toolName: p.toolName ?? p.type.slice(5),
511
+ toolCallId: p.toolCallId!,
512
+ args: p.args ?? p.input,
513
+ result: p.result ?? p.output,
577
514
  toolSource,
578
- );
515
+ });
579
516
  }
580
517
 
581
518
  function collectAiSdkV6Parts(parts: readonly AiSdkV6Part[]): {
582
519
  textParts: string[];
583
- toolCalls: TelemetryToolCall[];
584
- stepsData: { tool_calls: TelemetryToolCall[] }[];
520
+ toolCalls: AssistantCloudRunReportToolCall[];
521
+ stepsData: { tool_calls: AssistantCloudRunReportToolCall[] }[];
585
522
  } {
586
523
  const textParts: string[] = [];
587
- const toolCalls: TelemetryToolCall[] = [];
588
- const stepsData: { tool_calls: TelemetryToolCall[] }[] = [];
589
- let currentStepToolCalls: TelemetryToolCall[] | null = null;
524
+ const toolCalls: AssistantCloudRunReportToolCall[] = [];
525
+ const stepsData: { tool_calls: AssistantCloudRunReportToolCall[] }[] = [];
526
+ let currentStepToolCalls: AssistantCloudRunReportToolCall[] | null = null;
590
527
 
591
528
  for (const p of parts) {
592
529
  if (p.type === "step-start") {
@@ -624,10 +561,10 @@ function extractModelId(
624
561
 
625
562
  function buildAiSdkV6Result(
626
563
  textParts: string[],
627
- toolCalls: TelemetryToolCall[],
564
+ toolCalls: AssistantCloudRunReportToolCall[],
628
565
  totalSteps: number,
629
566
  metadata?: Record<string, unknown>,
630
- stepsData?: { tool_calls: TelemetryToolCall[] }[],
567
+ stepsData?: { tool_calls: AssistantCloudRunReportToolCall[] }[],
631
568
  usage?: {
632
569
  inputTokens?: number;
633
570
  outputTokens?: number;
@@ -636,7 +573,9 @@ function buildAiSdkV6Result(
636
573
  },
637
574
  ): TelemetryData {
638
575
  const hasText = textParts.length > 0;
639
- const outputText = hasText ? truncateStr(textParts.join("")) : undefined;
576
+ const outputText = hasText
577
+ ? truncateRunTelemetryText(textParts.join(""))
578
+ : undefined;
640
579
  const modelId = extractModelId(metadata);
641
580
 
642
581
  const steps: TelemetryStepData[] | undefined =
@@ -671,46 +610,6 @@ function buildAiSdkV6Result(
671
610
  };
672
611
  }
673
612
 
674
- type UsageFields = {
675
- inputTokens?: number;
676
- outputTokens?: number;
677
- promptTokens?: number;
678
- completionTokens?: number;
679
- reasoningTokens?: number;
680
- cachedInputTokens?: number;
681
- };
682
-
683
- function normalizeUsage(u: UsageFields):
684
- | {
685
- inputTokens?: number;
686
- outputTokens?: number;
687
- reasoningTokens?: number;
688
- cachedInputTokens?: number;
689
- }
690
- | undefined {
691
- const input = u.inputTokens ?? u.promptTokens;
692
- const output = u.outputTokens ?? u.completionTokens;
693
- if (
694
- input == null &&
695
- output == null &&
696
- u.reasoningTokens == null &&
697
- u.cachedInputTokens == null
698
- ) {
699
- return undefined;
700
- }
701
-
702
- return {
703
- ...(input != null ? { inputTokens: input } : undefined),
704
- ...(output != null ? { outputTokens: output } : undefined),
705
- ...(u.reasoningTokens != null
706
- ? { reasoningTokens: u.reasoningTokens }
707
- : undefined),
708
- ...(u.cachedInputTokens != null
709
- ? { cachedInputTokens: u.cachedInputTokens }
710
- : undefined),
711
- };
712
- }
713
-
714
613
  function extractAiSdkV6Usage(metadata?: Record<string, unknown>):
715
614
  | {
716
615
  inputTokens?: number;
@@ -720,15 +619,15 @@ function extractAiSdkV6Usage(metadata?: Record<string, unknown>):
720
619
  }
721
620
  | undefined {
722
621
  // Try top-level metadata.usage
723
- const usage = metadata?.usage as UsageFields | undefined;
622
+ const usage = metadata?.usage as RunTelemetryUsageInit | undefined;
724
623
  if (usage) {
725
- const normalized = normalizeUsage(usage);
624
+ const normalized = normalizeRunTelemetryUsage(usage);
726
625
  if (normalized) return normalized;
727
626
  }
728
627
 
729
628
  // Try aggregating from metadata.steps[].usage
730
629
  const steps = metadata?.steps as
731
- | readonly { usage?: UsageFields }[]
630
+ | readonly { usage?: RunTelemetryUsageInit }[]
732
631
  | undefined;
733
632
  if (steps && steps.length > 0) {
734
633
  let inputTokens = 0;
@@ -742,7 +641,7 @@ function extractAiSdkV6Usage(metadata?: Record<string, unknown>):
742
641
  let hasAny = false;
743
642
  for (const s of steps) {
744
643
  if (!s.usage) continue;
745
- const n = normalizeUsage(s.usage);
644
+ const n = normalizeRunTelemetryUsage(s.usage);
746
645
  if (n) {
747
646
  if (n.inputTokens != null) {
748
647
  inputTokens += n.inputTokens;
@@ -795,8 +694,8 @@ function extractAiSdkV6<T>(content: T): TelemetryData | null {
795
694
 
796
695
  function aggregateAiSdkV6RunSteps<T>(stepMessages: T[]): TelemetryData | null {
797
696
  const allTextParts: string[] = [];
798
- const allToolCalls: TelemetryToolCall[] = [];
799
- const allStepsData: { tool_calls: TelemetryToolCall[] }[] = [];
697
+ const allToolCalls: AssistantCloudRunReportToolCall[] = [];
698
+ const allStepsData: { tool_calls: AssistantCloudRunReportToolCall[] }[] = [];
800
699
  let hasAssistant = false;
801
700
  let metadata: Record<string, unknown> | undefined;
802
701
  let inputTokens = 0;
@@ -0,0 +1,37 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { createCloudThreadListAdapterCreateFallback } from "./createCloudThreadListAdapterCreateFallback";
3
+
4
+ describe("createCloudThreadListAdapterCreateFallback", () => {
5
+ it("prefers the custom create function", async () => {
6
+ const create = vi.fn().mockResolvedValue({ externalId: "custom" });
7
+ const initialize = vi.fn().mockResolvedValue({ externalId: "nested" });
8
+ const fallback = createCloudThreadListAdapterCreateFallback(create, {
9
+ source: {},
10
+ initialize,
11
+ });
12
+
13
+ await expect(fallback()).resolves.toEqual({ externalId: "custom" });
14
+ expect(initialize).not.toHaveBeenCalled();
15
+ });
16
+
17
+ it("initializes a sourced thread list item", async () => {
18
+ const result = { externalId: "nested" };
19
+ const fallback = createCloudThreadListAdapterCreateFallback(undefined, {
20
+ source: {},
21
+ initialize: vi.fn().mockResolvedValue(result),
22
+ });
23
+
24
+ await expect(fallback()).resolves.toBe(result);
25
+ });
26
+
27
+ it("falls back to an undefined external ID", async () => {
28
+ const initialize = vi.fn();
29
+ const fallback = createCloudThreadListAdapterCreateFallback(undefined, {
30
+ source: null,
31
+ initialize,
32
+ });
33
+
34
+ await expect(fallback()).resolves.toEqual({ externalId: undefined });
35
+ expect(initialize).not.toHaveBeenCalled();
36
+ });
37
+ });
@@ -0,0 +1,18 @@
1
+ type ThreadData = {
2
+ externalId: string | undefined;
3
+ };
4
+
5
+ type ThreadListItem = {
6
+ source: unknown;
7
+ initialize: () => Promise<ThreadData>;
8
+ };
9
+
10
+ export const createCloudThreadListAdapterCreateFallback = (
11
+ create: (() => Promise<ThreadData>) | undefined,
12
+ threadListItem: ThreadListItem,
13
+ ): (() => Promise<ThreadData>) =>
14
+ async function createThread() {
15
+ if (create) return create();
16
+ if (threadListItem.source) return threadListItem.initialize();
17
+ return { externalId: undefined };
18
+ };
@@ -542,19 +542,5 @@ describe("convertExternalMessages", () => {
542
542
  convertExternalMessages(messages, callback, false, {}),
543
543
  ).toThrowError(/returned an invalid message \(\{"role":"user"\}\)/);
544
544
  });
545
-
546
- it("tolerates a tool message without toolCallId as an orphaned tool result", () => {
547
- const messages = [{ id: "m1", role: "user" as const, content: "hi" }];
548
- const callback = ((msg: (typeof messages)[number]) => [
549
- msg,
550
- { role: "tool", result: "ok" },
551
- ]) as unknown as useExternalMessageConverter.Callback<
552
- (typeof messages)[number]
553
- >;
554
-
555
- const result = convertExternalMessages(messages, callback, false, {});
556
- expect(result[0]!.role).toBe("user");
557
- expect(result[1]!.content).toHaveLength(0);
558
- });
559
545
  });
560
546
  });