@assistant-ui/core 0.3.18 → 0.3.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/dist/adapters/speech.d.ts.map +1 -1
  2. package/dist/adapters/speech.js +13 -3
  3. package/dist/adapters/speech.js.map +1 -1
  4. package/dist/adapters/thread-history.d.ts +4 -0
  5. package/dist/adapters/thread-history.d.ts.map +1 -1
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.js.map +1 -1
  8. package/dist/internal.d.ts +1 -1
  9. package/dist/model-context/frame/host.d.ts.map +1 -1
  10. package/dist/model-context/frame/host.js +26 -11
  11. package/dist/model-context/frame/host.js.map +1 -1
  12. package/dist/model-context/interactable-composer-metadata.js +11 -9
  13. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  14. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  15. package/dist/react/adapters/LocalStorageThreadListAdapter.js +45 -7
  16. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  17. package/dist/react/client/DataRenderers.d.ts.map +1 -1
  18. package/dist/react/client/DataRenderers.js +20 -15
  19. package/dist/react/client/DataRenderers.js.map +1 -1
  20. package/dist/react/client/Interactables.d.ts.map +1 -1
  21. package/dist/react/client/Interactables.js +11 -5
  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 +3 -2
  25. package/dist/react/client/RemoteThreadList.js.map +1 -1
  26. package/dist/react/client/interactable-model-context.d.ts.map +1 -1
  27. package/dist/react/client/interactable-model-context.js +3 -2
  28. package/dist/react/client/interactable-model-context.js.map +1 -1
  29. package/dist/react/index.d.ts +2 -2
  30. package/dist/react/index.js +2 -2
  31. package/dist/react/interactables-legacy/useAssistantInteractable.d.ts.map +1 -1
  32. package/dist/react/interactables-legacy/useAssistantInteractable.js +3 -0
  33. package/dist/react/interactables-legacy/useAssistantInteractable.js.map +1 -1
  34. package/dist/react/model-context/useInteractable.d.ts.map +1 -1
  35. package/dist/react/model-context/useInteractable.js +4 -1
  36. package/dist/react/model-context/useInteractable.js.map +1 -1
  37. package/dist/react/model-context/useInteractableVersions.js +1 -1
  38. package/dist/react/primitives/message/MessageGroupedParts.d.ts +11 -2
  39. package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
  40. package/dist/react/primitives/message/MessageGroupedParts.js +3 -2
  41. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  42. package/dist/react/primitives/part/PartMessages.d.ts +1 -0
  43. package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
  44. package/dist/react/primitives/part/PartMessages.js +1 -0
  45. package/dist/react/primitives/part/PartMessages.js.map +1 -1
  46. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  47. package/dist/react/primitives/thread/ThreadMessages.js +6 -5
  48. package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
  49. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  50. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +2 -1
  51. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  52. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +2 -24
  53. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  54. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +135 -374
  55. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  56. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +1 -0
  57. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  58. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +22 -2
  59. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  60. package/dist/react/runtimes/cloud/auiV0.d.ts +40 -15
  61. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  62. package/dist/react/runtimes/cloud/auiV0.js +62 -15
  63. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  64. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +2 -1
  65. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
  66. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +4 -0
  67. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -1
  68. package/dist/react/runtimes/cloud/sdkIdentity.d.ts +6 -0
  69. package/dist/react/runtimes/cloud/sdkIdentity.d.ts.map +1 -0
  70. package/dist/react/runtimes/cloud/sdkIdentity.js +9 -0
  71. package/dist/react/runtimes/cloud/sdkIdentity.js.map +1 -0
  72. package/dist/react/runtimes/external-message-converter.d.ts +12 -2
  73. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  74. package/dist/react/runtimes/external-message-converter.js +26 -61
  75. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  76. package/dist/react/runtimes/useRemoteThreadListRuntime.js +1 -1
  77. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  78. package/dist/react/utils/groupParts.d.ts +6 -1
  79. package/dist/react/utils/groupParts.d.ts.map +1 -1
  80. package/dist/react/utils/groupParts.js +6 -0
  81. package/dist/react/utils/groupParts.js.map +1 -1
  82. package/dist/react/utils/useJSONEqual.d.ts +3 -1
  83. package/dist/react/utils/useJSONEqual.d.ts.map +1 -1
  84. package/dist/react/utils/useJSONEqual.js +6 -1
  85. package/dist/react/utils/useJSONEqual.js.map +1 -1
  86. package/dist/react/utils/useJSONSchemaDependency.d.ts +7 -0
  87. package/dist/react/utils/useJSONSchemaDependency.d.ts.map +1 -0
  88. package/dist/react/utils/useJSONSchemaDependency.js +20 -0
  89. package/dist/react/utils/useJSONSchemaDependency.js.map +1 -0
  90. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  91. package/dist/runtime/api/thread-runtime.js +2 -5
  92. package/dist/runtime/api/thread-runtime.js.map +1 -1
  93. package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -0
  94. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  95. package/dist/runtime/base/base-composer-runtime-core.js +46 -10
  96. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  97. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -0
  98. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  99. package/dist/runtime/base/base-thread-runtime-core.js +155 -64
  100. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  101. package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
  102. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  103. package/dist/runtime/queue/message-queue.d.ts +5 -0
  104. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  105. package/dist/runtime/queue/message-queue.js +61 -6
  106. package/dist/runtime/queue/message-queue.js.map +1 -1
  107. package/dist/runtime/utils/auto-status.d.ts +6 -2
  108. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  109. package/dist/runtime/utils/auto-status.js +12 -4
  110. package/dist/runtime/utils/auto-status.js.map +1 -1
  111. package/dist/runtime/utils/external-message-conversion.d.ts +16 -4
  112. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  113. package/dist/runtime/utils/external-message-conversion.js +48 -12
  114. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  115. package/dist/runtime/utils/message-repository.js +3 -3
  116. package/dist/runtime/utils/message-repository.js.map +1 -1
  117. package/dist/runtime/utils/thread-message-like.d.ts +2 -1
  118. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  119. package/dist/runtime/utils/thread-message-like.js +4 -2
  120. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  121. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  122. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +8 -1
  123. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  124. package/dist/runtimes/internal.d.ts +1 -1
  125. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  126. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  127. package/dist/runtimes/local/local-thread-runtime-core.js +4 -1
  128. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  129. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  130. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +2 -1
  131. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  132. package/dist/store/clients/external-thread.d.ts.map +1 -1
  133. package/dist/store/clients/external-thread.js +241 -200
  134. package/dist/store/clients/external-thread.js.map +1 -1
  135. package/dist/store/clients/thread-tasks.d.ts +13 -0
  136. package/dist/store/clients/thread-tasks.d.ts.map +1 -0
  137. package/dist/store/clients/thread-tasks.js +69 -0
  138. package/dist/store/clients/thread-tasks.js.map +1 -0
  139. package/dist/store/index.d.ts +3 -2
  140. package/dist/store/index.js +3 -3
  141. package/dist/store/primitive-predicates.d.ts.map +1 -1
  142. package/dist/store/primitive-predicates.js +4 -4
  143. package/dist/store/primitive-predicates.js.map +1 -1
  144. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  145. package/dist/store/runtime-clients/thread-runtime-client.js +176 -128
  146. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  147. package/dist/store/scope-registration.d.ts +2 -0
  148. package/dist/store/scope-registration.d.ts.map +1 -1
  149. package/dist/store/scopes/task.d.ts +36 -0
  150. package/dist/store/scopes/task.d.ts.map +1 -0
  151. package/dist/store/scopes/task.js +0 -0
  152. package/dist/store/scopes/thread.d.ts +20 -0
  153. package/dist/store/scopes/thread.d.ts.map +1 -1
  154. package/dist/subscribable/subscribable.d.ts +2 -1
  155. package/dist/subscribable/subscribable.d.ts.map +1 -1
  156. package/dist/subscribable/subscribable.js +23 -15
  157. package/dist/subscribable/subscribable.js.map +1 -1
  158. package/dist/types/index.d.ts +2 -2
  159. package/dist/types/message.d.ts +8 -1
  160. package/dist/types/message.d.ts.map +1 -1
  161. package/dist/types/message.js.map +1 -1
  162. package/dist/utils/abortable-iterable.d.ts.map +1 -1
  163. package/dist/utils/abortable-iterable.js +30 -14
  164. package/dist/utils/abortable-iterable.js.map +1 -1
  165. package/dist/utils/getGroupStatus.d.ts +18 -2
  166. package/dist/utils/getGroupStatus.d.ts.map +1 -1
  167. package/dist/utils/getGroupStatus.js +32 -7
  168. package/dist/utils/getGroupStatus.js.map +1 -1
  169. package/dist/utils/wire-media.d.ts.map +1 -1
  170. package/dist/utils/wire-media.js +2 -2
  171. package/dist/utils/wire-media.js.map +1 -1
  172. package/package.json +6 -6
  173. package/src/adapters/speech.test.ts +236 -0
  174. package/src/adapters/speech.ts +17 -10
  175. package/src/adapters/thread-history.ts +3 -0
  176. package/src/index.ts +1 -0
  177. package/src/model-context/frame/host.test.ts +113 -1
  178. package/src/model-context/frame/host.ts +39 -12
  179. package/src/model-context/interactable-composer-metadata.test.ts +30 -0
  180. package/src/model-context/interactable-composer-metadata.ts +10 -10
  181. package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +244 -0
  182. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +104 -7
  183. package/src/react/client/DataRenderers.test.tsx +45 -0
  184. package/src/react/client/DataRenderers.ts +13 -9
  185. package/src/react/client/Interactables.test.ts +44 -1
  186. package/src/react/client/Interactables.ts +7 -1
  187. package/src/react/client/RemoteThreadList.test.ts +41 -1
  188. package/src/react/client/RemoteThreadList.ts +15 -2
  189. package/src/react/client/interactable-model-context.test.ts +44 -0
  190. package/src/react/client/interactable-model-context.ts +3 -2
  191. package/src/react/index.ts +5 -1
  192. package/src/react/interactables-legacy/useAssistantInteractable.test.tsx +132 -0
  193. package/src/react/interactables-legacy/useAssistantInteractable.ts +10 -1
  194. package/src/react/model-context/useInteractable.test.tsx +177 -0
  195. package/src/react/model-context/useInteractable.ts +3 -0
  196. package/src/react/primitive-hooks/useActionBarCopy.test.ts +0 -1
  197. package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +0 -1
  198. package/src/react/primitives/message/MessageGroupedParts.test.tsx +96 -0
  199. package/src/react/primitives/message/MessageGroupedParts.tsx +15 -4
  200. package/src/react/primitives/part/PartMessages.test.tsx +104 -1
  201. package/src/react/primitives/part/PartMessages.tsx +1 -0
  202. package/src/react/primitives/thread/ThreadMessages.test.tsx +142 -0
  203. package/src/react/primitives/thread/ThreadMessages.tsx +8 -5
  204. package/src/react/providers/ReadonlyThreadProvider.test.tsx +56 -0
  205. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +7 -1
  206. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +20 -14
  207. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +221 -1
  208. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +166 -512
  209. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +77 -0
  210. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +27 -1
  211. package/src/react/runtimes/cloud/auiV0.ts +160 -25
  212. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +21 -0
  213. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +7 -1
  214. package/src/react/runtimes/cloud/sdkIdentity.ts +9 -0
  215. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +18 -4
  216. package/src/react/runtimes/external-message-converter.test.ts +136 -1
  217. package/src/react/runtimes/external-message-converter.ts +39 -93
  218. package/src/react/runtimes/useRemoteThreadListRuntime.ts +1 -1
  219. package/src/react/utils/groupParts.ts +12 -1
  220. package/src/react/utils/useJSONEqual.ts +9 -0
  221. package/src/react/utils/useJSONSchemaDependency.ts +22 -0
  222. package/src/runtime/api/thread-runtime.test.ts +58 -0
  223. package/src/runtime/api/thread-runtime.ts +2 -4
  224. package/src/runtime/base/base-composer-runtime-core.ts +69 -16
  225. package/src/runtime/base/base-thread-runtime-core.test.ts +1008 -1
  226. package/src/runtime/base/base-thread-runtime-core.ts +222 -83
  227. package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
  228. package/src/runtime/queue/message-queue.ts +68 -6
  229. package/src/runtime/utils/auto-status.test.ts +112 -1
  230. package/src/runtime/utils/auto-status.ts +31 -5
  231. package/src/runtime/utils/external-message-conversion.test.ts +134 -0
  232. package/src/runtime/utils/external-message-conversion.ts +122 -31
  233. package/src/runtime/utils/message-repository.test.ts +49 -0
  234. package/src/runtime/utils/message-repository.ts +3 -3
  235. package/src/runtime/utils/thread-message-like.test.ts +52 -0
  236. package/src/runtime/utils/thread-message-like.ts +4 -0
  237. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +31 -1
  238. package/src/runtimes/local/local-thread-runtime-core.test.ts +21 -0
  239. package/src/runtimes/local/local-thread-runtime-core.ts +11 -1
  240. package/src/runtimes/tool-invocations/EDGE_CASES.md +2 -6
  241. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +133 -0
  242. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +10 -6
  243. package/src/store/clients/external-thread.ts +19 -1
  244. package/src/store/clients/thread-tasks.test.ts +274 -0
  245. package/src/store/clients/thread-tasks.ts +138 -0
  246. package/src/store/index.ts +8 -0
  247. package/src/store/primitive-predicates.test.ts +48 -5
  248. package/src/store/primitive-predicates.ts +7 -2
  249. package/src/store/runtime-clients/thread-runtime-client.ts +31 -1
  250. package/src/store/scope-registration.ts +2 -0
  251. package/src/store/scopes/task.ts +34 -0
  252. package/src/store/scopes/thread.ts +16 -0
  253. package/src/store/thread-tasks.test.tsx +209 -0
  254. package/src/subscribable/subscribable.test.ts +156 -1
  255. package/src/subscribable/subscribable.ts +25 -15
  256. package/src/tests/MessageRepository.test.ts +1 -5
  257. package/src/tests/auiV0Encode.test.ts +300 -0
  258. package/src/tests/base-composer-runtime-core-send.test.ts +51 -0
  259. package/src/tests/base-composer-runtime-core.test.ts +314 -2
  260. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +151 -0
  261. package/src/tests/external-thread-attachments.test.tsx +69 -0
  262. package/src/tests/external-thread-parity.test.tsx +34 -0
  263. package/src/tests/groupParts.test.ts +71 -0
  264. package/src/tests/message-queue.test.ts +253 -0
  265. package/src/tests/remote-thread-list-reactive-threadId.test.ts +51 -0
  266. package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +43 -0
  267. package/src/types/index.ts +1 -0
  268. package/src/types/message.ts +8 -0
  269. package/src/utils/abortable-iterable.test.ts +115 -0
  270. package/src/utils/abortable-iterable.ts +34 -23
  271. package/src/utils/getGroupStatus.test.ts +70 -4
  272. package/src/utils/getGroupStatus.ts +35 -12
  273. package/src/utils/wire-media.test.ts +10 -0
  274. package/src/utils/wire-media.ts +6 -2
@@ -2,7 +2,8 @@ import { isJSONValueEqual } from "../../utils/json/is-json-equal.js";
2
2
  import { generateErrorMessageId } from "../../utils/id.js";
3
3
  import { fromThreadMessageLike } from "./thread-message-like.js";
4
4
  import { FALLBACK_ID_PREFIX, bindExternalStoreMessage, getExternalStoreMessages, symbolInnerMessage } from "./external-store-message.js";
5
- import { getAutoStatus, isAutoStatus, isInterruptedToolCall, isPendingToolCall } from "./auto-status.js";
5
+ import { getAutoStatus, isAutoStatus, isBackgroundToolCall, isInterruptedToolCall, isPendingToolCall } from "./auto-status.js";
6
+ import { ThreadMessageConverter } from "../../runtimes/external-store/thread-message-converter.js";
6
7
  //#region src/runtime/utils/external-message-conversion.ts
7
8
  const stringifyForError = (value) => {
8
9
  let text;
@@ -31,13 +32,16 @@ const convertExternalMessageCallback = (input, callback, metadata) => ({
31
32
  outputs: toCallbackOutputs(callback(input, metadata), input)
32
33
  });
33
34
  const mergeInnerMessages = (existing, incoming) => ({ [symbolInnerMessage]: [...existing[symbolInnerMessage] ?? [], ...incoming[symbolInnerMessage] ?? []] });
35
+ const isNaNToolCallId = (toolCallId) => typeof toolCallId === "number" && Number.isNaN(toolCallId);
34
36
  const joinExternalMessages = (messages) => {
35
37
  const assistantMessage = {
36
38
  role: "assistant",
37
39
  content: []
38
40
  };
41
+ const toolCallIndices = /* @__PURE__ */ new Map();
42
+ const reasoningIndices = /* @__PURE__ */ new Map();
39
43
  for (const output of messages) if (output.role === "tool") {
40
- const toolCallIdx = assistantMessage.content.findIndex((c) => c.type === "tool-call" && c.toolCallId === output.toolCallId);
44
+ const toolCallIdx = !isNaNToolCallId(output.toolCallId) ? toolCallIndices.get(output.toolCallId) ?? -1 : -1;
41
45
  if (toolCallIdx !== -1) {
42
46
  const toolCall = assistantMessage.content[toolCallIdx];
43
47
  if (output.toolName != null) {
@@ -85,13 +89,14 @@ const joinExternalMessages = (messages) => {
85
89
  ...output.metadata.custom
86
90
  };
87
91
  if (output.metadata.timing) assistantMessage.metadata.timing = output.metadata.timing;
92
+ if (output.metadata.modality) assistantMessage.metadata.modality = output.metadata.modality;
88
93
  if (output.metadata.submittedFeedback) assistantMessage.metadata.submittedFeedback = output.metadata.submittedFeedback;
89
94
  if (output.metadata.isOptimistic) assistantMessage.metadata.isOptimistic = true;
90
95
  }
91
96
  for (const part of content) {
92
97
  if (part.type === "tool-call" && part.toolCallId) {
93
- const existingIdx = assistantMessage.content.findIndex((c) => c.type === "tool-call" && c.toolCallId === part.toolCallId);
94
- if (existingIdx !== -1) {
98
+ const existingIdx = toolCallIndices.get(part.toolCallId);
99
+ if (existingIdx !== void 0) {
95
100
  const existing = assistantMessage.content[existingIdx];
96
101
  assistantMessage.content[existingIdx] = {
97
102
  ...existing,
@@ -102,8 +107,8 @@ const joinExternalMessages = (messages) => {
102
107
  }
103
108
  }
104
109
  if (part.type === "reasoning" && "parentId" in part && part.parentId) {
105
- const existingIdx = assistantMessage.content.findIndex((c) => c.type === "reasoning" && "parentId" in c && c.parentId === part.parentId);
106
- if (existingIdx !== -1) {
110
+ const existingIdx = reasoningIndices.get(part.parentId);
111
+ if (existingIdx !== void 0) {
107
112
  const existing = assistantMessage.content[existingIdx];
108
113
  assistantMessage.content[existingIdx] = {
109
114
  ...existing,
@@ -113,7 +118,10 @@ const joinExternalMessages = (messages) => {
113
118
  continue;
114
119
  }
115
120
  }
121
+ const partIdx = assistantMessage.content.length;
116
122
  assistantMessage.content.push(part);
123
+ if (part.type === "tool-call" && !isNaNToolCallId(part.toolCallId) && !toolCallIndices.has(part.toolCallId)) toolCallIndices.set(part.toolCallId, partIdx);
124
+ else if (part.type === "reasoning" && "parentId" in part && part.parentId) reasoningIndices.set(part.parentId, partIdx);
117
125
  }
118
126
  break;
119
127
  default: throw new Error(`Unknown message role: ${role}`);
@@ -152,13 +160,20 @@ const shallowArrayEqual = (a, b) => {
152
160
  for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
153
161
  return true;
154
162
  };
163
+ const createExternalMessageConversionCache = () => ({
164
+ callbackCache: /* @__PURE__ */ new WeakMap(),
165
+ chunkCache: /* @__PURE__ */ new WeakMap(),
166
+ converterCache: new ThreadMessageConverter(),
167
+ generatedFallbackMessages: /* @__PURE__ */ new WeakSet()
168
+ });
155
169
  const convertExternalMessageChunk = (message, idx, chunkCount, isRunning, error, cache, cancelledMessageIds) => {
156
170
  const isLast = idx === chunkCount - 1;
157
171
  const joined = joinExternalMessages(message.outputs);
158
172
  const isCancelled = cancelledMessageIds !== void 0 && message.outputs.some((output) => output.role !== "tool" && output.id != null && cancelledMessageIds.has(output.id));
159
173
  const hasInterruptedToolCalls = typeof joined.content === "object" && joined.content.some(isInterruptedToolCall);
160
174
  const hasPendingToolCalls = typeof joined.content === "object" && joined.content.some(isPendingToolCall);
161
- const autoStatus = getAutoStatus(isLast, isRunning, hasInterruptedToolCalls, hasPendingToolCalls, isLast ? error : void 0, isCancelled);
175
+ const hasBackgroundToolCalls = typeof joined.content === "object" && joined.content.some(isBackgroundToolCall);
176
+ const autoStatus = getAutoStatus(isLast, isRunning, hasInterruptedToolCalls, hasPendingToolCalls, isLast ? error : void 0, isCancelled, hasBackgroundToolCalls);
162
177
  const fallbackId = `${FALLBACK_ID_PREFIX}${idx}`;
163
178
  const cachedMessage = cache?.message;
164
179
  if (cachedMessage && (cachedMessage.role !== "assistant" || !isAutoStatus(cachedMessage.status) || cachedMessage.status === autoStatus || cachedMessage.status.type === "incomplete" && cachedMessage.status.reason === "error" && cachedMessage.status.error !== void 0 && autoStatus.type === "incomplete" && autoStatus.reason === "error" && autoStatus.error !== void 0 && (cachedMessage.status.error === autoStatus.error || isJSONValueEqual(cachedMessage.status.error, autoStatus.error)))) {
@@ -210,13 +225,34 @@ const completeExternalMessageConversion = (messages, error) => {
210
225
  }
211
226
  return messages;
212
227
  };
213
- const convertExternalMessages = (messages, callback, isRunning, metadata) => {
214
- const callbackResults = messages.map((message) => convertExternalMessageCallback(message, callback, metadata));
215
- const chunks = chunkExternalMessages(callbackResults);
216
- const result = chunks.map((message, idx) => convertExternalMessageChunk(message, idx, chunks.length, isRunning, metadata.error, void 0, metadata.cancelledMessageIds));
228
+ const convertExternalMessages = (messages, callback, isRunning, metadata, joinStrategy, cache) => {
229
+ const callbackResults = messages.map((message) => {
230
+ let result = cache?.callbackCache.get(message);
231
+ if (!result || result.metadata !== metadata || result.callback !== callback) {
232
+ result = {
233
+ ...convertExternalMessageCallback(message, callback, metadata),
234
+ metadata,
235
+ callback
236
+ };
237
+ cache?.callbackCache.set(message, result);
238
+ }
239
+ return result;
240
+ });
241
+ const chunks = chunkExternalMessages(callbackResults, joinStrategy).map((message) => {
242
+ const key = message.outputs[0];
243
+ if (!key || !cache) return message;
244
+ const cached = cache.chunkCache.get(key);
245
+ if (cached && shallowArrayEqual(cached.outputs, message.outputs)) return cached;
246
+ cache.chunkCache.set(key, message);
247
+ return message;
248
+ });
249
+ const result = cache ? cache.converterCache.convertMessages(chunks, (cached, message, idx) => convertExternalMessageChunk(message, idx, chunks.length, isRunning, metadata.error, {
250
+ message: cached,
251
+ generatedFallbackMessages: cache.generatedFallbackMessages
252
+ }, metadata.cancelledMessageIds)) : chunks.map((message, idx) => convertExternalMessageChunk(message, idx, chunks.length, isRunning, metadata.error, void 0, metadata.cancelledMessageIds));
217
253
  return completeExternalMessageConversion(result, metadata.error);
218
254
  };
219
255
  //#endregion
220
- export { chunkExternalMessages, completeExternalMessageConversion, convertExternalMessageCallback, convertExternalMessageChunk, convertExternalMessages, joinExternalMessages, shallowArrayEqual };
256
+ export { chunkExternalMessages, completeExternalMessageConversion, convertExternalMessageCallback, convertExternalMessageChunk, convertExternalMessages, createExternalMessageConversionCache, joinExternalMessages, shallowArrayEqual };
221
257
 
222
258
  //# sourceMappingURL=external-message-conversion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"external-message-conversion.js","names":["ReadonlyJSONValue","ToolExecutionStatus","ThreadAssistantMessage","ThreadMessage","ToolCallMessagePart","MessageTiming","generateErrorMessageId","isJSONValueEqual","getAutoStatus","isAutoStatus","isInterruptedToolCall","isPendingToolCall","bindExternalStoreMessage","FALLBACK_ID_PREFIX","getExternalStoreMessages","symbolInnerMessage","fromThreadMessageLike","ThreadMessageLike","JoinStrategy","ExternalMessageConverterMessage","convertConfig","joinStrategy","role","toolCallId","toolName","result","artifact","isError","messages","ExternalMessageConverterMetadata","toolStatuses","Record","error","cancelledMessageIds","ReadonlySet","messageTiming","ExternalMessageConverterCallback","message","T","metadata","ExternalMessageConverterCallbackResult","input","outputs","ExternalMessageConverterChunk","inputs","Mutable","P","stringifyForError","value","text","Error","String","JSON","stringify","length","slice","toCallbackOutputs","output","Array","isArray","o","valid","content","filter","warnedDroppedToolResults","has","add","console","warn","WeakSet","convertExternalMessageCallback","callback","mergeInnerMessages","existing","incoming","joinExternalMessages","assistantMessage","Omit","Exclude","toolCallIdx","findIndex","c","type","toolCall","const","map","id","createdAt","status","attachments","unstable_state","undefined","unstable_annotations","unstable_data","steps","custom","timing","submittedFeedback","isOptimistic","part","existingIdx","parentId","push","unsupportedRole","chunkExternalMessages","callbackResults","results","isAssistant","pendingNone","flush","callbackResult","at","shallowArrayEqual","a","b","i","ExternalMessageConversionCache","generatedFallbackMessages","convertExternalMessageChunk","idx","chunkCount","isRunning","cache","isLast","joined","isCancelled","some","hasInterruptedToolCalls","hasPendingToolCalls","autoStatus","fallbackId","cachedMessage","reason","updated","newMessage","createErrorAssistantMessage","msg","Date","completeExternalMessageConversion","lastMessage","convertExternalMessages","WeakKey","chunks"],"sources":["../../../src/runtime/utils/external-message-conversion.ts"],"sourcesContent":["import type { ReadonlyJSONValue } from \"assistant-stream/utils\";\nimport type { ToolExecutionStatus } from \"../../runtimes/tool-invocations/ToolInvocationTracker\";\nimport type {\n ThreadAssistantMessage,\n ThreadMessage,\n ToolCallMessagePart,\n} from \"../../types/message\";\nimport type { MessageTiming } from \"../../types/message\";\nimport { generateErrorMessageId } from \"../../utils/id\";\nimport { isJSONValueEqual } from \"../../utils/json/is-json-equal\";\nimport {\n getAutoStatus,\n isAutoStatus,\n isInterruptedToolCall,\n isPendingToolCall,\n} from \"./auto-status\";\nimport {\n bindExternalStoreMessage,\n FALLBACK_ID_PREFIX,\n getExternalStoreMessages,\n symbolInnerMessage,\n} from \"./external-store-message\";\nimport {\n fromThreadMessageLike,\n type ThreadMessageLike,\n} from \"./thread-message-like\";\n\nexport type JoinStrategy = \"concat-content\" | \"none\";\n\nexport type ExternalMessageConverterMessage =\n | (ThreadMessageLike & {\n readonly convertConfig?: {\n readonly joinStrategy?: JoinStrategy;\n };\n })\n | {\n role: \"tool\";\n toolCallId: string;\n toolName?: string | undefined;\n result: any;\n artifact?: any;\n isError?: boolean;\n messages?: readonly ThreadMessage[];\n };\n\nexport type ExternalMessageConverterMetadata = {\n readonly toolStatuses?: Record<string, ToolExecutionStatus>;\n readonly error?: ReadonlyJSONValue;\n readonly cancelledMessageIds?: ReadonlySet<string>;\n readonly messageTiming?: Record<string, MessageTiming>;\n};\n\nexport type ExternalMessageConverterCallback<T> = (\n message: T,\n metadata: ExternalMessageConverterMetadata,\n) => ExternalMessageConverterMessage | ExternalMessageConverterMessage[];\n\nexport type ExternalMessageConverterCallbackResult<T> = {\n input: T;\n outputs: ExternalMessageConverterMessage[];\n};\n\nexport type ExternalMessageConverterChunk<T> = {\n inputs: T[];\n outputs: ExternalMessageConverterMessage[];\n};\n\ntype Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\nconst stringifyForError = (value: unknown) => {\n let text;\n try {\n text = value instanceof Error ? String(value) : JSON.stringify(value);\n } catch {\n /* circular */\n }\n text ??= String(value);\n return text.length > 200 ? `${text.slice(0, 200)}…` : text;\n};\n\nconst toCallbackOutputs = (\n output: ExternalMessageConverterMessage | ExternalMessageConverterMessage[],\n input: unknown,\n): ExternalMessageConverterMessage[] => {\n const outputs = Array.isArray(output) ? output : [output];\n for (const o of outputs) {\n const valid =\n typeof o === \"object\" &&\n o !== null &&\n (o.role === \"tool\" ||\n ((o.role === \"assistant\" || o.role === \"user\" || o.role === \"system\") &&\n (typeof o.content === \"string\" || Array.isArray(o.content))));\n if (!valid)\n throw new Error(\n `External message converter: the converter callback returned an invalid message (${stringifyForError(o)}) for input ${stringifyForError(input)}. Return an empty array to skip a message.`,\n );\n }\n // Providers can emit tool results without a usable id (react-langchain maps\n // a missing tool_call_id to \"\"); converters must not throw on those, so the\n // result is dropped like any other orphaned tool output.\n return outputs.filter((o) => {\n if (o.role !== \"tool\") return true;\n if (typeof o.toolCallId === \"string\" && o.toolCallId.length > 0)\n return true;\n if (!warnedDroppedToolResults.has(o)) {\n warnedDroppedToolResults.add(o);\n console.warn(\n `External message converter: dropping a tool result without a toolCallId (${stringifyForError(o)}) for input ${stringifyForError(input)}.`,\n );\n }\n return false;\n });\n};\n\nconst warnedDroppedToolResults = new WeakSet<object>();\n\nexport const convertExternalMessageCallback = <T>(\n input: T,\n callback: ExternalMessageConverterCallback<T>,\n metadata: ExternalMessageConverterMetadata,\n): ExternalMessageConverterCallbackResult<T> => ({\n input,\n outputs: toCallbackOutputs(callback(input, metadata), input),\n});\n\nconst mergeInnerMessages = (existing: object, incoming: object) => ({\n [symbolInnerMessage]: [\n ...((existing as any)[symbolInnerMessage] ?? []),\n ...((incoming as any)[symbolInnerMessage] ?? []),\n ],\n});\n\nexport const joinExternalMessages = (\n messages: readonly ExternalMessageConverterMessage[],\n): ThreadMessageLike => {\n const assistantMessage: Mutable<Omit<ThreadMessageLike, \"metadata\">> & {\n content: Exclude<ThreadMessageLike[\"content\"][0], string>[];\n metadata?: Mutable<ThreadMessageLike[\"metadata\"]>;\n } = {\n role: \"assistant\",\n content: [],\n };\n for (const output of messages) {\n if (output.role === \"tool\") {\n const toolCallIdx = assistantMessage.content.findIndex(\n (c) => c.type === \"tool-call\" && c.toolCallId === output.toolCallId,\n );\n // Ignore orphaned tool results so one bad tool message does not\n // prevent rendering the rest of the conversation.\n if (toolCallIdx !== -1) {\n const toolCall = assistantMessage.content[\n toolCallIdx\n ]! as ToolCallMessagePart;\n if (output.toolName != null) {\n if (toolCall.toolName !== output.toolName)\n throw new Error(\n `Tool call name ${output.toolCallId} ${output.toolName} does not match existing tool call ${toolCall.toolName}`,\n );\n }\n assistantMessage.content[toolCallIdx] = {\n ...toolCall,\n ...{\n [symbolInnerMessage]: [\n ...((toolCall as any)[symbolInnerMessage] ?? []),\n output,\n ],\n },\n result: output.result,\n artifact: output.artifact,\n isError: output.isError,\n messages: output.messages,\n };\n }\n } else {\n const role = output.role;\n const content = (\n typeof output.content === \"string\"\n ? [{ type: \"text\" as const, text: output.content }]\n : output.content\n ).map((c) => ({\n ...c,\n ...{ [symbolInnerMessage]: [output] },\n }));\n switch (role) {\n case \"system\":\n case \"user\":\n return {\n ...output,\n content,\n };\n case \"assistant\":\n if (assistantMessage.content.length === 0) {\n assistantMessage.id = output.id;\n assistantMessage.createdAt ??= output.createdAt;\n assistantMessage.status ??= output.status;\n\n if (output.attachments) {\n assistantMessage.attachments = [\n ...(assistantMessage.attachments ?? []),\n ...output.attachments,\n ];\n }\n }\n\n if (output.metadata) {\n assistantMessage.metadata ??= {};\n if (output.metadata.unstable_state !== undefined) {\n assistantMessage.metadata.unstable_state =\n output.metadata.unstable_state;\n }\n if (output.metadata.unstable_annotations) {\n assistantMessage.metadata.unstable_annotations = [\n ...(assistantMessage.metadata.unstable_annotations ?? []),\n ...output.metadata.unstable_annotations,\n ];\n }\n if (output.metadata.unstable_data) {\n assistantMessage.metadata.unstable_data = [\n ...(assistantMessage.metadata.unstable_data ?? []),\n ...output.metadata.unstable_data,\n ];\n }\n if (output.metadata.steps) {\n assistantMessage.metadata.steps = [\n ...(assistantMessage.metadata.steps ?? []),\n ...output.metadata.steps,\n ];\n }\n if (output.metadata.custom) {\n assistantMessage.metadata.custom = {\n ...(assistantMessage.metadata.custom ?? {}),\n ...output.metadata.custom,\n };\n }\n\n if (output.metadata.timing) {\n assistantMessage.metadata.timing = output.metadata.timing;\n }\n\n if (output.metadata.submittedFeedback) {\n assistantMessage.metadata.submittedFeedback =\n output.metadata.submittedFeedback;\n }\n\n if (output.metadata.isOptimistic) {\n assistantMessage.metadata.isOptimistic = true;\n }\n // TODO keep this in sync with ThreadMessageLike[\"metadata\"] / fromThreadMessageLike\n }\n\n // Add content parts, merging reasoning parts with same parentId\n for (const part of content) {\n if (part.type === \"tool-call\" && part.toolCallId) {\n const existingIdx = assistantMessage.content.findIndex(\n (c) =>\n c.type === \"tool-call\" && c.toolCallId === part.toolCallId,\n );\n if (existingIdx !== -1) {\n const existing = assistantMessage.content[\n existingIdx\n ] as typeof part;\n assistantMessage.content[existingIdx] = {\n ...existing,\n ...part,\n ...mergeInnerMessages(existing, part),\n };\n continue;\n }\n }\n\n if (\n part.type === \"reasoning\" &&\n \"parentId\" in part &&\n part.parentId\n ) {\n const existingIdx = assistantMessage.content.findIndex(\n (c) =>\n c.type === \"reasoning\" &&\n \"parentId\" in c &&\n c.parentId === part.parentId,\n );\n if (existingIdx !== -1) {\n const existing = assistantMessage.content[\n existingIdx\n ] as typeof part;\n assistantMessage.content[existingIdx] = {\n ...existing,\n text: `${existing.text}\\n\\n${part.text}`,\n ...mergeInnerMessages(existing, part),\n };\n continue;\n }\n }\n assistantMessage.content.push(part);\n }\n break;\n default: {\n const unsupportedRole: never = role;\n throw new Error(`Unknown message role: ${unsupportedRole}`);\n }\n }\n }\n }\n return assistantMessage;\n};\n\nexport const chunkExternalMessages = <T>(\n callbackResults: ExternalMessageConverterCallbackResult<T>[],\n joinStrategy?: JoinStrategy,\n) => {\n const results: ExternalMessageConverterChunk<T>[] = [];\n let isAssistant = false;\n let pendingNone = false; // true if the previous assistant message had joinStrategy \"none\"\n let inputs: T[] = [];\n let outputs: ExternalMessageConverterMessage[] = [];\n\n const flush = () => {\n if (outputs.length) {\n results.push({\n inputs,\n outputs,\n });\n }\n inputs = [];\n outputs = [];\n isAssistant = false;\n pendingNone = false;\n };\n\n for (const callbackResult of callbackResults) {\n for (const output of callbackResult.outputs) {\n if (\n (pendingNone && output.role !== \"tool\") ||\n !isAssistant ||\n output.role === \"user\" ||\n output.role === \"system\"\n ) {\n flush();\n }\n isAssistant = output.role === \"assistant\" || output.role === \"tool\";\n\n if (inputs.at(-1) !== callbackResult.input) {\n inputs.push(callbackResult.input);\n }\n outputs.push(output);\n\n if (\n output.role === \"assistant\" &&\n (output.convertConfig?.joinStrategy === \"none\" ||\n joinStrategy === \"none\")\n ) {\n pendingNone = true;\n }\n }\n }\n flush();\n return results;\n};\n\nexport const shallowArrayEqual = (\n a: readonly unknown[],\n b: readonly unknown[],\n) => {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n};\n\ntype ExternalMessageConversionCache = {\n message: ThreadMessage | undefined;\n generatedFallbackMessages: WeakSet<object>;\n};\n\nexport const convertExternalMessageChunk = <T>(\n message: ExternalMessageConverterChunk<T>,\n idx: number,\n chunkCount: number,\n isRunning: boolean,\n error: ReadonlyJSONValue | undefined,\n cache?: ExternalMessageConversionCache,\n cancelledMessageIds?: ReadonlySet<string>,\n) => {\n const isLast = idx === chunkCount - 1;\n const joined = joinExternalMessages(message.outputs);\n const isCancelled =\n cancelledMessageIds !== undefined &&\n message.outputs.some(\n (output) =>\n output.role !== \"tool\" &&\n output.id != null &&\n cancelledMessageIds.has(output.id),\n );\n const hasInterruptedToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isInterruptedToolCall);\n const hasPendingToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isPendingToolCall);\n const autoStatus = getAutoStatus(\n isLast,\n isRunning,\n hasInterruptedToolCalls,\n hasPendingToolCalls,\n isLast ? error : undefined,\n isCancelled,\n );\n const fallbackId = `${FALLBACK_ID_PREFIX}${idx}`;\n\n const cachedMessage = cache?.message;\n if (\n cachedMessage &&\n (cachedMessage.role !== \"assistant\" ||\n !isAutoStatus(cachedMessage.status) ||\n cachedMessage.status === autoStatus ||\n (cachedMessage.status.type === \"incomplete\" &&\n cachedMessage.status.reason === \"error\" &&\n cachedMessage.status.error !== undefined &&\n autoStatus.type === \"incomplete\" &&\n autoStatus.reason === \"error\" &&\n autoStatus.error !== undefined &&\n (cachedMessage.status.error === autoStatus.error ||\n isJSONValueEqual(cachedMessage.status.error, autoStatus.error))))\n ) {\n const inputs = getExternalStoreMessages<T>(cachedMessage);\n if (shallowArrayEqual(inputs, message.inputs)) {\n // A positional fallback id goes stale when messages are prepended\n // or reordered; serving it unchanged makes two messages collide on\n // one id and the dedup downstream drops one of them.\n if (\n cache.generatedFallbackMessages.has(cachedMessage) &&\n cachedMessage.id !== fallbackId\n ) {\n const updated = { ...cachedMessage, id: fallbackId };\n cache.generatedFallbackMessages.add(updated);\n bindExternalStoreMessage(updated, message.inputs);\n return updated;\n }\n return cachedMessage;\n }\n }\n\n const newMessage = fromThreadMessageLike(joined, fallbackId, autoStatus);\n if (cache && joined.id == null) {\n cache.generatedFallbackMessages.add(newMessage);\n }\n bindExternalStoreMessage(newMessage, message.inputs);\n return newMessage;\n};\n\nfunction createErrorAssistantMessage(\n error: ReadonlyJSONValue,\n): ThreadAssistantMessage {\n const msg: ThreadAssistantMessage = {\n id: generateErrorMessageId(),\n role: \"assistant\",\n content: [],\n status: { type: \"incomplete\", reason: \"error\", error },\n createdAt: new Date(),\n metadata: {\n unstable_state: null,\n unstable_annotations: [],\n unstable_data: [],\n custom: {},\n steps: [],\n },\n };\n bindExternalStoreMessage(msg, []);\n return msg;\n}\n\nexport const completeExternalMessageConversion = (\n messages: ThreadMessage[],\n error: ReadonlyJSONValue | undefined,\n) => {\n if (error) {\n const lastMessage = messages.at(-1);\n if (!lastMessage || lastMessage.role !== \"assistant\") {\n messages.push(createErrorAssistantMessage(error));\n }\n }\n return messages;\n};\n\nexport const convertExternalMessages = <T extends WeakKey>(\n messages: T[],\n callback: ExternalMessageConverterCallback<T>,\n isRunning: boolean,\n metadata: ExternalMessageConverterMetadata,\n) => {\n const callbackResults = messages.map((message) =>\n convertExternalMessageCallback(message, callback, metadata),\n );\n const chunks = chunkExternalMessages(callbackResults);\n const result = chunks.map((message, idx) =>\n convertExternalMessageChunk(\n message,\n idx,\n chunks.length,\n isRunning,\n metadata.error,\n undefined,\n metadata.cancelledMessageIds,\n ),\n );\n return completeExternalMessageConversion(result, metadata.error);\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"external-message-conversion.js","names":["ReadonlyJSONValue","ToolExecutionStatus","ThreadMessageConverter","ThreadAssistantMessage","ThreadMessage","ToolCallMessagePart","MessageTiming","generateErrorMessageId","isJSONValueEqual","getAutoStatus","isAutoStatus","isBackgroundToolCall","isInterruptedToolCall","isPendingToolCall","bindExternalStoreMessage","FALLBACK_ID_PREFIX","getExternalStoreMessages","symbolInnerMessage","fromThreadMessageLike","ThreadMessageLike","JoinStrategy","ExternalMessageConverterMessage","convertConfig","joinStrategy","role","toolCallId","toolName","result","artifact","isError","messages","ExternalMessageConverterMetadata","toolStatuses","Record","error","cancelledMessageIds","ReadonlySet","messageTiming","ExternalMessageConverterCallback","message","T","metadata","ExternalMessageConverterCallbackResult","input","outputs","ExternalMessageConverterChunk","inputs","Mutable","P","stringifyForError","value","text","Error","String","JSON","stringify","length","slice","toCallbackOutputs","output","Array","isArray","o","valid","content","filter","warnedDroppedToolResults","has","add","console","warn","WeakSet","convertExternalMessageCallback","callback","mergeInnerMessages","existing","incoming","isNaNToolCallId","Number","isNaN","joinExternalMessages","assistantMessage","Omit","Exclude","toolCallIndices","Map","reasoningIndices","toolCallIdx","get","toolCall","type","const","map","c","id","createdAt","status","attachments","unstable_state","undefined","unstable_annotations","unstable_data","steps","custom","timing","modality","submittedFeedback","isOptimistic","part","existingIdx","parentId","partIdx","push","set","unsupportedRole","chunkExternalMessages","callbackResults","results","isAssistant","pendingNone","flush","callbackResult","at","shallowArrayEqual","a","b","i","ExternalMessageChunkConversionCache","generatedFallbackMessages","ExternalMessageConversionCallbackCacheEntry","InternalExternalMessageConversionCache","WeakKey","callbackCache","WeakMap","chunkCache","converterCache","createExternalMessageConversionCache","convertExternalMessageChunk","idx","chunkCount","isRunning","cache","isLast","joined","isCancelled","some","hasInterruptedToolCalls","hasPendingToolCalls","hasBackgroundToolCalls","autoStatus","fallbackId","cachedMessage","reason","updated","newMessage","createErrorAssistantMessage","msg","Date","completeExternalMessageConversion","lastMessage","convertExternalMessages","chunks","key","cached","convertMessages"],"sources":["../../../src/runtime/utils/external-message-conversion.ts"],"sourcesContent":["import type { ReadonlyJSONValue } from \"assistant-stream/utils\";\nimport type { ToolExecutionStatus } from \"../../runtimes/tool-invocations/ToolInvocationTracker\";\nimport { ThreadMessageConverter } from \"../../runtimes/external-store/thread-message-converter\";\nimport type {\n ThreadAssistantMessage,\n ThreadMessage,\n ToolCallMessagePart,\n} from \"../../types/message\";\nimport type { MessageTiming } from \"../../types/message\";\nimport { generateErrorMessageId } from \"../../utils/id\";\nimport { isJSONValueEqual } from \"../../utils/json/is-json-equal\";\nimport {\n getAutoStatus,\n isAutoStatus,\n isBackgroundToolCall,\n isInterruptedToolCall,\n isPendingToolCall,\n} from \"./auto-status\";\nimport {\n bindExternalStoreMessage,\n FALLBACK_ID_PREFIX,\n getExternalStoreMessages,\n symbolInnerMessage,\n} from \"./external-store-message\";\nimport {\n fromThreadMessageLike,\n type ThreadMessageLike,\n} from \"./thread-message-like\";\n\nexport type JoinStrategy = \"concat-content\" | \"none\";\n\nexport type ExternalMessageConverterMessage =\n | (ThreadMessageLike & {\n readonly convertConfig?: {\n readonly joinStrategy?: JoinStrategy;\n };\n })\n | {\n role: \"tool\";\n toolCallId: string;\n toolName?: string | undefined;\n result: any;\n artifact?: any;\n isError?: boolean;\n messages?: readonly ThreadMessage[];\n };\n\nexport type ExternalMessageConverterMetadata = {\n readonly toolStatuses?: Record<string, ToolExecutionStatus>;\n readonly error?: ReadonlyJSONValue;\n readonly cancelledMessageIds?: ReadonlySet<string>;\n readonly messageTiming?: Record<string, MessageTiming>;\n};\n\nexport type ExternalMessageConverterCallback<T> = (\n message: T,\n metadata: ExternalMessageConverterMetadata,\n) => ExternalMessageConverterMessage | ExternalMessageConverterMessage[];\n\nexport type ExternalMessageConverterCallbackResult<T> = {\n input: T;\n outputs: ExternalMessageConverterMessage[];\n};\n\nexport type ExternalMessageConverterChunk<T> = {\n inputs: T[];\n outputs: ExternalMessageConverterMessage[];\n};\n\ntype Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\nconst stringifyForError = (value: unknown) => {\n let text;\n try {\n text = value instanceof Error ? String(value) : JSON.stringify(value);\n } catch {\n /* circular */\n }\n text ??= String(value);\n return text.length > 200 ? `${text.slice(0, 200)}…` : text;\n};\n\nconst toCallbackOutputs = (\n output: ExternalMessageConverterMessage | ExternalMessageConverterMessage[],\n input: unknown,\n): ExternalMessageConverterMessage[] => {\n const outputs = Array.isArray(output) ? output : [output];\n for (const o of outputs) {\n const valid =\n typeof o === \"object\" &&\n o !== null &&\n (o.role === \"tool\" ||\n ((o.role === \"assistant\" || o.role === \"user\" || o.role === \"system\") &&\n (typeof o.content === \"string\" || Array.isArray(o.content))));\n if (!valid)\n throw new Error(\n `External message converter: the converter callback returned an invalid message (${stringifyForError(o)}) for input ${stringifyForError(input)}. Return an empty array to skip a message.`,\n );\n }\n // Providers can emit tool results without a usable id (react-langchain maps\n // a missing tool_call_id to \"\"); converters must not throw on those, so the\n // result is dropped like any other orphaned tool output.\n return outputs.filter((o) => {\n if (o.role !== \"tool\") return true;\n if (typeof o.toolCallId === \"string\" && o.toolCallId.length > 0)\n return true;\n if (!warnedDroppedToolResults.has(o)) {\n warnedDroppedToolResults.add(o);\n console.warn(\n `External message converter: dropping a tool result without a toolCallId (${stringifyForError(o)}) for input ${stringifyForError(input)}.`,\n );\n }\n return false;\n });\n};\n\nconst warnedDroppedToolResults = new WeakSet<object>();\n\nexport const convertExternalMessageCallback = <T>(\n input: T,\n callback: ExternalMessageConverterCallback<T>,\n metadata: ExternalMessageConverterMetadata,\n): ExternalMessageConverterCallbackResult<T> => ({\n input,\n outputs: toCallbackOutputs(callback(input, metadata), input),\n});\n\nconst mergeInnerMessages = (existing: object, incoming: object) => ({\n [symbolInnerMessage]: [\n ...((existing as any)[symbolInnerMessage] ?? []),\n ...((incoming as any)[symbolInnerMessage] ?? []),\n ],\n});\n\nconst isNaNToolCallId = (toolCallId: unknown) =>\n typeof toolCallId === \"number\" && Number.isNaN(toolCallId);\n\nexport const joinExternalMessages = (\n messages: readonly ExternalMessageConverterMessage[],\n): ThreadMessageLike => {\n const assistantMessage: Mutable<Omit<ThreadMessageLike, \"metadata\">> & {\n content: Exclude<ThreadMessageLike[\"content\"][0], string>[];\n metadata?: Mutable<ThreadMessageLike[\"metadata\"]>;\n } = {\n role: \"assistant\",\n content: [],\n };\n const toolCallIndices = new Map<unknown, number>();\n const reasoningIndices = new Map<string, number>();\n for (const output of messages) {\n if (output.role === \"tool\") {\n const toolCallIdx = !isNaNToolCallId(output.toolCallId)\n ? (toolCallIndices.get(output.toolCallId) ?? -1)\n : -1;\n // Ignore orphaned tool results so one bad tool message does not\n // prevent rendering the rest of the conversation.\n if (toolCallIdx !== -1) {\n const toolCall = assistantMessage.content[\n toolCallIdx\n ]! as ToolCallMessagePart;\n if (output.toolName != null) {\n if (toolCall.toolName !== output.toolName)\n throw new Error(\n `Tool call name ${output.toolCallId} ${output.toolName} does not match existing tool call ${toolCall.toolName}`,\n );\n }\n assistantMessage.content[toolCallIdx] = {\n ...toolCall,\n ...{\n [symbolInnerMessage]: [\n ...((toolCall as any)[symbolInnerMessage] ?? []),\n output,\n ],\n },\n result: output.result,\n artifact: output.artifact,\n isError: output.isError,\n messages: output.messages,\n };\n }\n } else {\n const role = output.role;\n const content = (\n typeof output.content === \"string\"\n ? [{ type: \"text\" as const, text: output.content }]\n : output.content\n ).map((c) => ({\n ...c,\n ...{ [symbolInnerMessage]: [output] },\n }));\n switch (role) {\n case \"system\":\n case \"user\":\n return {\n ...output,\n content,\n };\n case \"assistant\":\n if (assistantMessage.content.length === 0) {\n assistantMessage.id = output.id;\n assistantMessage.createdAt ??= output.createdAt;\n assistantMessage.status ??= output.status;\n\n if (output.attachments) {\n assistantMessage.attachments = [\n ...(assistantMessage.attachments ?? []),\n ...output.attachments,\n ];\n }\n }\n\n if (output.metadata) {\n assistantMessage.metadata ??= {};\n if (output.metadata.unstable_state !== undefined) {\n assistantMessage.metadata.unstable_state =\n output.metadata.unstable_state;\n }\n if (output.metadata.unstable_annotations) {\n assistantMessage.metadata.unstable_annotations = [\n ...(assistantMessage.metadata.unstable_annotations ?? []),\n ...output.metadata.unstable_annotations,\n ];\n }\n if (output.metadata.unstable_data) {\n assistantMessage.metadata.unstable_data = [\n ...(assistantMessage.metadata.unstable_data ?? []),\n ...output.metadata.unstable_data,\n ];\n }\n if (output.metadata.steps) {\n assistantMessage.metadata.steps = [\n ...(assistantMessage.metadata.steps ?? []),\n ...output.metadata.steps,\n ];\n }\n if (output.metadata.custom) {\n assistantMessage.metadata.custom = {\n ...(assistantMessage.metadata.custom ?? {}),\n ...output.metadata.custom,\n };\n }\n\n if (output.metadata.timing) {\n assistantMessage.metadata.timing = output.metadata.timing;\n }\n\n if (output.metadata.modality) {\n assistantMessage.metadata.modality = output.metadata.modality;\n }\n\n if (output.metadata.submittedFeedback) {\n assistantMessage.metadata.submittedFeedback =\n output.metadata.submittedFeedback;\n }\n\n if (output.metadata.isOptimistic) {\n assistantMessage.metadata.isOptimistic = true;\n }\n // TODO keep this in sync with ThreadMessageLike[\"metadata\"] / fromThreadMessageLike\n }\n\n // Add content parts, merging reasoning parts with same parentId\n for (const part of content) {\n if (part.type === \"tool-call\" && part.toolCallId) {\n const existingIdx = toolCallIndices.get(part.toolCallId);\n if (existingIdx !== undefined) {\n const existing = assistantMessage.content[\n existingIdx\n ] as typeof part;\n assistantMessage.content[existingIdx] = {\n ...existing,\n ...part,\n ...mergeInnerMessages(existing, part),\n };\n continue;\n }\n }\n\n if (\n part.type === \"reasoning\" &&\n \"parentId\" in part &&\n part.parentId\n ) {\n const existingIdx = reasoningIndices.get(part.parentId);\n if (existingIdx !== undefined) {\n const existing = assistantMessage.content[\n existingIdx\n ] as typeof part;\n assistantMessage.content[existingIdx] = {\n ...existing,\n text: `${existing.text}\\n\\n${part.text}`,\n ...mergeInnerMessages(existing, part),\n };\n continue;\n }\n }\n const partIdx = assistantMessage.content.length;\n assistantMessage.content.push(part);\n if (\n part.type === \"tool-call\" &&\n !isNaNToolCallId(part.toolCallId) &&\n !toolCallIndices.has(part.toolCallId)\n ) {\n toolCallIndices.set(part.toolCallId, partIdx);\n } else if (\n part.type === \"reasoning\" &&\n \"parentId\" in part &&\n part.parentId\n ) {\n reasoningIndices.set(part.parentId, partIdx);\n }\n }\n break;\n default: {\n const unsupportedRole: never = role;\n throw new Error(`Unknown message role: ${unsupportedRole}`);\n }\n }\n }\n }\n return assistantMessage;\n};\n\nexport const chunkExternalMessages = <T>(\n callbackResults: ExternalMessageConverterCallbackResult<T>[],\n joinStrategy?: JoinStrategy,\n) => {\n const results: ExternalMessageConverterChunk<T>[] = [];\n let isAssistant = false;\n let pendingNone = false; // true if the previous assistant message had joinStrategy \"none\"\n let inputs: T[] = [];\n let outputs: ExternalMessageConverterMessage[] = [];\n\n const flush = () => {\n if (outputs.length) {\n results.push({\n inputs,\n outputs,\n });\n }\n inputs = [];\n outputs = [];\n isAssistant = false;\n pendingNone = false;\n };\n\n for (const callbackResult of callbackResults) {\n for (const output of callbackResult.outputs) {\n if (\n (pendingNone && output.role !== \"tool\") ||\n !isAssistant ||\n output.role === \"user\" ||\n output.role === \"system\"\n ) {\n flush();\n }\n isAssistant = output.role === \"assistant\" || output.role === \"tool\";\n\n if (inputs.at(-1) !== callbackResult.input) {\n inputs.push(callbackResult.input);\n }\n outputs.push(output);\n\n if (\n output.role === \"assistant\" &&\n (output.convertConfig?.joinStrategy === \"none\" ||\n joinStrategy === \"none\")\n ) {\n pendingNone = true;\n }\n }\n }\n flush();\n return results;\n};\n\nexport const shallowArrayEqual = (\n a: readonly unknown[],\n b: readonly unknown[],\n) => {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n};\n\ntype ExternalMessageChunkConversionCache = {\n message: ThreadMessage | undefined;\n generatedFallbackMessages: WeakSet<object>;\n};\n\ntype ExternalMessageConversionCallbackCacheEntry<T> =\n ExternalMessageConverterCallbackResult<T> & {\n metadata: ExternalMessageConverterMetadata;\n callback: ExternalMessageConverterCallback<T>;\n };\n\nexport type InternalExternalMessageConversionCache<\n T extends WeakKey = WeakKey,\n> = {\n callbackCache: WeakMap<T, ExternalMessageConversionCallbackCacheEntry<T>>;\n chunkCache: WeakMap<\n ExternalMessageConverterMessage,\n ExternalMessageConverterChunk<T>\n >;\n converterCache: ThreadMessageConverter;\n // Generatedness is tracked by identity, not by id shape: a caller-supplied id that happens to match the generated pattern must never be rewritten.\n generatedFallbackMessages: WeakSet<object>;\n};\n\nexport const createExternalMessageConversionCache = <\n T extends WeakKey = WeakKey,\n>(): InternalExternalMessageConversionCache<T> => ({\n callbackCache: new WeakMap(),\n chunkCache: new WeakMap(),\n converterCache: new ThreadMessageConverter(),\n generatedFallbackMessages: new WeakSet(),\n});\n\nexport const convertExternalMessageChunk = <T>(\n message: ExternalMessageConverterChunk<T>,\n idx: number,\n chunkCount: number,\n isRunning: boolean,\n error: ReadonlyJSONValue | undefined,\n cache?: ExternalMessageChunkConversionCache,\n cancelledMessageIds?: ReadonlySet<string>,\n) => {\n const isLast = idx === chunkCount - 1;\n const joined = joinExternalMessages(message.outputs);\n const isCancelled =\n cancelledMessageIds !== undefined &&\n message.outputs.some(\n (output) =>\n output.role !== \"tool\" &&\n output.id != null &&\n cancelledMessageIds.has(output.id),\n );\n const hasInterruptedToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isInterruptedToolCall);\n const hasPendingToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isPendingToolCall);\n const hasBackgroundToolCalls =\n typeof joined.content === \"object\" &&\n joined.content.some(isBackgroundToolCall);\n const autoStatus = getAutoStatus(\n isLast,\n isRunning,\n hasInterruptedToolCalls,\n hasPendingToolCalls,\n isLast ? error : undefined,\n isCancelled,\n hasBackgroundToolCalls,\n );\n const fallbackId = `${FALLBACK_ID_PREFIX}${idx}`;\n\n const cachedMessage = cache?.message;\n if (\n cachedMessage &&\n (cachedMessage.role !== \"assistant\" ||\n !isAutoStatus(cachedMessage.status) ||\n cachedMessage.status === autoStatus ||\n (cachedMessage.status.type === \"incomplete\" &&\n cachedMessage.status.reason === \"error\" &&\n cachedMessage.status.error !== undefined &&\n autoStatus.type === \"incomplete\" &&\n autoStatus.reason === \"error\" &&\n autoStatus.error !== undefined &&\n (cachedMessage.status.error === autoStatus.error ||\n isJSONValueEqual(cachedMessage.status.error, autoStatus.error))))\n ) {\n const inputs = getExternalStoreMessages<T>(cachedMessage);\n if (shallowArrayEqual(inputs, message.inputs)) {\n // A positional fallback id goes stale when messages are prepended\n // or reordered; serving it unchanged makes two messages collide on\n // one id and the dedup downstream drops one of them.\n if (\n cache.generatedFallbackMessages.has(cachedMessage) &&\n cachedMessage.id !== fallbackId\n ) {\n const updated = { ...cachedMessage, id: fallbackId };\n cache.generatedFallbackMessages.add(updated);\n bindExternalStoreMessage(updated, message.inputs);\n return updated;\n }\n return cachedMessage;\n }\n }\n\n const newMessage = fromThreadMessageLike(joined, fallbackId, autoStatus);\n if (cache && joined.id == null) {\n cache.generatedFallbackMessages.add(newMessage);\n }\n bindExternalStoreMessage(newMessage, message.inputs);\n return newMessage;\n};\n\nfunction createErrorAssistantMessage(\n error: ReadonlyJSONValue,\n): ThreadAssistantMessage {\n const msg: ThreadAssistantMessage = {\n id: generateErrorMessageId(),\n role: \"assistant\",\n content: [],\n status: { type: \"incomplete\", reason: \"error\", error },\n createdAt: new Date(),\n metadata: {\n unstable_state: null,\n unstable_annotations: [],\n unstable_data: [],\n custom: {},\n steps: [],\n },\n };\n bindExternalStoreMessage(msg, []);\n return msg;\n}\n\nexport const completeExternalMessageConversion = (\n messages: ThreadMessage[],\n error: ReadonlyJSONValue | undefined,\n) => {\n if (error) {\n const lastMessage = messages.at(-1);\n if (!lastMessage || lastMessage.role !== \"assistant\") {\n messages.push(createErrorAssistantMessage(error));\n }\n }\n return messages;\n};\n\nexport const convertExternalMessages = <T extends WeakKey>(\n messages: T[],\n callback: ExternalMessageConverterCallback<T>,\n isRunning: boolean,\n metadata: ExternalMessageConverterMetadata,\n joinStrategy?: JoinStrategy,\n cache?: InternalExternalMessageConversionCache<T>,\n) => {\n const callbackResults = messages.map((message) => {\n let result = cache?.callbackCache.get(message);\n if (\n !result ||\n result.metadata !== metadata ||\n result.callback !== callback\n ) {\n result = {\n ...convertExternalMessageCallback(message, callback, metadata),\n metadata,\n callback,\n };\n cache?.callbackCache.set(message, result);\n }\n return result;\n });\n const chunks = chunkExternalMessages(callbackResults, joinStrategy).map(\n (message) => {\n const key = message.outputs[0];\n if (!key || !cache) return message;\n\n const cached = cache.chunkCache.get(key);\n if (cached && shallowArrayEqual(cached.outputs, message.outputs)) {\n return cached;\n }\n cache.chunkCache.set(key, message);\n return message;\n },\n );\n const result = cache\n ? cache.converterCache.convertMessages(chunks, (cached, message, idx) =>\n convertExternalMessageChunk(\n message,\n idx,\n chunks.length,\n isRunning,\n metadata.error,\n {\n message: cached,\n generatedFallbackMessages: cache.generatedFallbackMessages,\n },\n metadata.cancelledMessageIds,\n ),\n )\n : chunks.map((message, idx) =>\n convertExternalMessageChunk(\n message,\n idx,\n chunks.length,\n isRunning,\n metadata.error,\n undefined,\n metadata.cancelledMessageIds,\n ),\n );\n return completeExternalMessageConversion(result, metadata.error);\n};\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  import { generateId } from "../../utils/id.js";
2
2
  import { fromThreadMessageLike } from "./thread-message-like.js";
3
- import { getContentAutoStatus } from "./auto-status.js";
3
+ import { getRepositoryContentAutoStatus } from "./auto-status.js";
4
4
  //#region src/runtime/utils/message-repository.ts
5
5
  const ExportedMessageRepository = {
6
6
  fromArray: (messages) => {
7
- const conv = messages.map((m) => fromThreadMessageLike(m, generateId(), getContentAutoStatus(m.content, false, false)));
7
+ const conv = messages.map((m) => fromThreadMessageLike(m, generateId(), getRepositoryContentAutoStatus(m.content)));
8
8
  return { messages: conv.map((m, idx) => ({
9
9
  parentId: idx > 0 ? conv[idx - 1].id : null,
10
10
  message: m
@@ -17,7 +17,7 @@ const ExportedMessageRepository = {
17
17
  if (!message.id) throw new Error("ExportedMessageRepository.fromBranchableArray: Each message must have an 'id' field set.");
18
18
  return {
19
19
  parentId,
20
- message: fromThreadMessageLike(message, message.id, getContentAutoStatus(message.content, false, false))
20
+ message: fromThreadMessageLike(message, message.id, getRepositoryContentAutoStatus(message.content))
21
21
  };
22
22
  })
23
23
  };
@@ -1 +1 @@
1
- {"version":3,"file":"message-repository.js","names":["ThreadMessage","RunConfig","generateId","ThreadMessageLike","getContentAutoStatus","fromThreadMessageLike","ExportedMessageRepositoryItem","message","parentId","runConfig","ExportedMessageRepository","headId","messages","Array","fromArray","conv","map","m","content","idx","id","fromBranchableArray","items","options","undefined","Error","RepositoryParent","children","next","RepositoryMessage","prev","current","level","findHead","CachedValue","_value","T","func","constructor","value","dirty","MessageRepository","Map","head","root","updateLevels","newLevel","pending","length","pop","childId","childMessage","get","push","selectPathTo","performOp","newParent","child","operation","parentOrRoot","newParentOrRoot","filter","fallbackId","at","fallback","_messages","canonicalHeadId","metadata","isOptimistic","getMessages","headMessage","addOrUpdateMessage","existingItem","newItem","set","getMessage","messageId","index","deleteMessage","replacementId","replacement","delete","getBranches","evictOffBranchOptimisticMessages","previousHead","currentHead","onHeadBranch","Set","add","stale","has","switchToBranch","resetHead","clear","descendantId","export","exportItems","reverse","i","import"],"sources":["../../../src/runtime/utils/message-repository.ts"],"sourcesContent":["import type { ThreadMessage } from \"../../types/message\";\nimport type { RunConfig } from \"../../types/message\";\nimport { generateId } from \"../../utils/id\";\nimport type { ThreadMessageLike } from \"./thread-message-like\";\nimport { getContentAutoStatus } from \"./auto-status\";\nimport { fromThreadMessageLike } from \"./thread-message-like\";\n\nexport type ExportedMessageRepositoryItem = {\n message: ThreadMessage;\n parentId: string | null;\n runConfig?: RunConfig;\n};\n\nexport type ExportedMessageRepository = {\n headId?: string | null;\n messages: Array<{\n message: ThreadMessage;\n parentId: string | null;\n runConfig?: RunConfig;\n }>;\n};\n\nexport const ExportedMessageRepository = {\n fromArray: (\n messages: readonly ThreadMessageLike[],\n ): ExportedMessageRepository => {\n const conv = messages.map((m) =>\n fromThreadMessageLike(\n m,\n generateId(),\n getContentAutoStatus(m.content, false, false),\n ),\n );\n\n return {\n messages: conv.map((m, idx) => ({\n parentId: idx > 0 ? conv[idx - 1]!.id : null,\n message: m,\n })),\n };\n },\n\n fromBranchableArray: (\n items: readonly {\n message: ThreadMessageLike;\n parentId: string | null;\n }[],\n options?: { headId?: string | null },\n ): ExportedMessageRepository => {\n return {\n ...(options?.headId !== undefined\n ? { headId: options.headId }\n : undefined),\n messages: items.map(({ message, parentId }) => {\n if (!message.id) {\n throw new Error(\n \"ExportedMessageRepository.fromBranchableArray: Each message must have an 'id' field set.\",\n );\n }\n return {\n parentId,\n message: fromThreadMessageLike(\n message,\n message.id,\n getContentAutoStatus(message.content, false, false),\n ),\n };\n }),\n };\n },\n};\n\ntype RepositoryParent = {\n children: string[];\n next: RepositoryMessage | null;\n};\n\ntype RepositoryMessage = RepositoryParent & {\n prev: RepositoryMessage | null;\n current: ThreadMessage;\n level: number;\n};\n\nconst findHead = (\n message: RepositoryMessage | RepositoryParent,\n): RepositoryMessage | null => {\n let current = message;\n while (current.next) current = current.next;\n return \"current\" in current ? current : null;\n};\n\nclass CachedValue<T> {\n private _value: T | null = null;\n\n private func: () => T;\n\n constructor(func: () => T) {\n this.func = func;\n }\n\n get value() {\n if (this._value === null) {\n this._value = this.func();\n }\n return this._value;\n }\n\n dirty() {\n this._value = null;\n }\n}\n\nexport class MessageRepository {\n private messages = new Map<string, RepositoryMessage>();\n private head: RepositoryMessage | null = null;\n private root: RepositoryParent = {\n children: [],\n next: null,\n };\n\n private updateLevels(message: RepositoryMessage, newLevel: number) {\n const pending = [{ message, level: newLevel }];\n\n while (pending.length > 0) {\n const current = pending.pop()!;\n current.message.level = current.level;\n\n for (const childId of current.message.children) {\n const childMessage = this.messages.get(childId);\n if (childMessage) {\n pending.push({ message: childMessage, level: current.level + 1 });\n }\n }\n }\n }\n\n private selectPathTo(message: RepositoryMessage) {\n for (\n let current: RepositoryMessage | null = message;\n current;\n current = current.prev\n ) {\n (current.prev ?? this.root).next = current;\n }\n }\n\n private performOp(\n newParent: RepositoryMessage | null,\n child: RepositoryMessage,\n operation: \"cut\" | \"link\" | \"relink\",\n ) {\n const parentOrRoot = child.prev ?? this.root;\n const newParentOrRoot = newParent ?? this.root;\n\n if (operation === \"relink\" && parentOrRoot === newParentOrRoot) return;\n\n // `link` receives a fresh ID from `addOrUpdateMessage`; only `relink` can introduce a cycle.\n if (operation === \"relink\") {\n for (\n let current: RepositoryMessage | null = newParent;\n current;\n current = current.prev\n ) {\n if (current.current.id === child.current.id) {\n throw new Error(\n \"MessageRepository(performOp/relink): A message with the same id already exists in the parent tree. This error occurs if the same message id is found multiple times. This is likely an internal bug in assistant-ui.\",\n );\n }\n }\n }\n\n if (operation !== \"link\") {\n parentOrRoot.children = parentOrRoot.children.filter(\n (m) => m !== child.current.id,\n );\n\n if (parentOrRoot.next === child) {\n const fallbackId = parentOrRoot.children.at(-1);\n const fallback = fallbackId ? this.messages.get(fallbackId) : null;\n if (fallback === undefined) {\n throw new Error(\n \"MessageRepository(performOp/cut): Fallback sibling message not found. This is likely an internal bug in assistant-ui.\",\n );\n }\n parentOrRoot.next = fallback;\n }\n }\n\n if (operation !== \"cut\") {\n newParentOrRoot.children = [\n ...newParentOrRoot.children,\n child.current.id,\n ];\n\n child.prev = newParent;\n\n if (findHead(child) === this.head) {\n this.selectPathTo(child);\n } else if (newParentOrRoot.next === null) {\n newParentOrRoot.next = child;\n if (this.head === newParentOrRoot) {\n this.head = findHead(child);\n }\n }\n\n const newLevel = newParent ? newParent.level + 1 : 0;\n this.updateLevels(child, newLevel);\n }\n }\n\n private _messages = new CachedValue<readonly ThreadMessage[]>(() => {\n const messages = new Array<ThreadMessage>((this.head?.level ?? -1) + 1);\n for (let current = this.head; current; current = current.prev) {\n messages[current.level] = current.current;\n }\n return messages;\n });\n\n get headId() {\n return this.head?.current.id ?? null;\n }\n\n get canonicalHeadId() {\n // Optimistic messages are ephemeral, so persisted callers need the nearest\n // non-optimistic ancestor rather than the raw head.\n let head = this.head;\n while (head?.current.metadata?.isOptimistic) {\n head = head.prev;\n }\n return head?.current.id ?? null;\n }\n\n getMessages(headId?: string) {\n if (headId === undefined || headId === this.head?.current.id) {\n return this._messages.value;\n }\n\n const headMessage = this.messages.get(headId);\n if (!headMessage) {\n throw new Error(\n \"MessageRepository(getMessages): Head message not found. This is likely an internal bug in assistant-ui.\",\n );\n }\n\n const messages = new Array<ThreadMessage>(headMessage.level + 1);\n for (\n let current: RepositoryMessage | null = headMessage;\n current;\n current = current.prev\n ) {\n messages[current.level] = current.current;\n }\n return messages;\n }\n\n addOrUpdateMessage(parentId: string | null, message: ThreadMessage) {\n const existingItem = this.messages.get(message.id);\n const prev = parentId ? this.messages.get(parentId) : null;\n if (prev === undefined)\n throw new Error(\n \"MessageRepository(addOrUpdateMessage): Parent message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n if (existingItem) {\n existingItem.current = message;\n this.performOp(prev, existingItem, \"relink\");\n this._messages.dirty();\n return;\n }\n\n const newItem: RepositoryMessage = {\n prev,\n current: message,\n next: null,\n children: [],\n level: prev ? prev.level + 1 : 0,\n };\n\n this.messages.set(message.id, newItem);\n this.performOp(prev, newItem, \"link\");\n\n if (this.head === prev) {\n this.head = newItem;\n }\n\n this._messages.dirty();\n }\n\n getMessage(messageId: string) {\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(updateMessage): Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n return {\n parentId: message.prev?.current.id ?? null,\n message: message.current,\n index: message.level,\n };\n }\n\n deleteMessage(messageId: string, replacementId?: string | null | undefined) {\n const message = this.messages.get(messageId);\n\n if (!message)\n throw new Error(\n \"MessageRepository(deleteMessage): Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const replacement =\n replacementId === undefined\n ? message.prev\n : replacementId === null\n ? null\n : this.messages.get(replacementId);\n if (replacement === undefined)\n throw new Error(\n \"MessageRepository(deleteMessage): Replacement not found. This is likely an internal bug in assistant-ui.\",\n );\n\n for (const child of message.children) {\n const childMessage = this.messages.get(child);\n if (!childMessage)\n throw new Error(\n \"MessageRepository(deleteMessage): Child message not found. This is likely an internal bug in assistant-ui.\",\n );\n this.performOp(replacement, childMessage, \"relink\");\n }\n\n this.performOp(null, message, \"cut\");\n this.messages.delete(messageId);\n\n if (this.head === message) {\n this.head = findHead(replacement ?? this.root);\n }\n\n this._messages.dirty();\n }\n\n getBranches(messageId: string) {\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(getBranches): Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const { children } = message.prev ?? this.root;\n return children;\n }\n\n /**\n * Evicts optimistic messages (`metadata.isOptimistic`) the head just moved\n * away from. Since eviction runs on every head move, the only optimistic\n * messages in the repository live on the branch the head previously pointed\n * at — so we walk just that branch rather than the whole repository. Keeps a\n * client→server id swap from leaving a phantom sibling, and drops off-branch\n * placeholders.\n */\n private evictOffBranchOptimisticMessages(\n previousHead: RepositoryMessage | null,\n currentHead: RepositoryMessage | null,\n ) {\n if (!previousHead) return;\n\n const onHeadBranch = new Set<string>();\n for (let current = currentHead; current; current = current.prev) {\n onHeadBranch.add(current.current.id);\n }\n\n const stale: string[] = [];\n for (\n let current: RepositoryMessage | null = previousHead;\n current;\n current = current.prev\n ) {\n // Stop at the first node shared with the current head branch: every\n // ancestor above it is shared too, so nothing further can be off-branch.\n if (onHeadBranch.has(current.current.id)) break;\n if (current.current.metadata?.isOptimistic) {\n stale.push(current.current.id);\n }\n }\n\n for (const id of stale) {\n // A prior deletion may have already removed this node.\n if (this.messages.has(id)) this.deleteMessage(id);\n }\n }\n\n switchToBranch(messageId: string) {\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(switchToBranch): Branch not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const previousHead = this.head;\n this.selectPathTo(message);\n\n this.head = findHead(message);\n\n this.evictOffBranchOptimisticMessages(previousHead, this.head);\n\n this._messages.dirty();\n }\n\n resetHead(messageId: string | null) {\n if (messageId === null) {\n this.clear();\n return;\n }\n\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(resetHead): Branch not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const previousHead = this.head;\n\n if (message.children.length > 0) {\n const pending = [...message.children];\n while (pending.length > 0) {\n const childId = pending.pop()!;\n const childMessage = this.messages.get(childId);\n if (childMessage) {\n for (const descendantId of childMessage.children) {\n pending.push(descendantId);\n }\n this.messages.delete(childId);\n }\n }\n\n message.children = [];\n message.next = null;\n }\n\n this.head = message;\n this.selectPathTo(message);\n\n this.evictOffBranchOptimisticMessages(previousHead, this.head);\n\n this._messages.dirty();\n }\n\n clear(): void {\n this.messages.clear();\n this.head = null;\n this.root = {\n children: [],\n next: null,\n };\n this._messages.dirty();\n }\n\n export(): ExportedMessageRepository {\n const exportItems: ExportedMessageRepository[\"messages\"] = [];\n\n // Optimistic messages are ephemeral and never persisted. A persisted child\n // of an optimistic node is re-parented onto its nearest persisted ancestor\n // so the exported tree never references a skipped id.\n // Import and external-state conversion require parents before children, so\n // the tree is walked in pre-order rather than iterated in insertion order.\n const pending = [...this.root.children].reverse();\n while (pending.length > 0) {\n const message = this.messages.get(pending.pop()!);\n if (!message) continue;\n for (let i = message.children.length - 1; i >= 0; i--) {\n pending.push(message.children[i]!);\n }\n if (message.current.metadata?.isOptimistic) continue;\n let prev = message.prev;\n while (prev && prev.current.metadata?.isOptimistic) {\n prev = prev.prev;\n }\n exportItems.push({\n message: message.current,\n parentId: prev?.current.id ?? null,\n });\n }\n\n return {\n headId: this.canonicalHeadId,\n messages: exportItems,\n };\n }\n\n import({ headId, messages }: ExportedMessageRepository) {\n for (const { message, parentId } of messages) {\n this.addOrUpdateMessage(parentId, message);\n }\n\n this.resetHead(headId ?? messages.at(-1)?.message.id ?? null);\n }\n}\n"],"mappings":";;;;AAsBA,MAAaU,4BAA4B;CACvCI,YACEF,aAC8B;EAC9B,MAAMG,OAAOH,SAASI,KAAKC,MACzBZ,sBACEY,GACAf,WAAW,GACXE,qBAAqBa,EAAEC,SAAS,OAAO,KAAK,CAC9C,CACF;EAEA,OAAO,EACLN,UAAUG,KAAKC,KAAKC,GAAGE,SAAS;GAC9BX,UAAUW,MAAM,IAAIJ,KAAKI,MAAM,EAAE,CAAEC,KAAK;GACxCb,SAASU;EACX,EAAE,EACJ;CACF;CAEAI,sBACEC,OAIAC,YAC8B;EAC9B,OAAO;GACL,GAAIA,SAASZ,WAAWa,KAAAA,IACpB,EAAEb,QAAQY,QAAQZ,OAAO,IACzBa,KAAAA;GACJZ,UAAUU,MAAMN,KAAK,EAAET,SAASC,eAAe;IAC7C,IAAI,CAACD,QAAQa,IACX,MAAM,IAAIK,MACR,0FACF;IAEF,OAAO;KACLjB;KACAD,SAASF,sBACPE,SACAA,QAAQa,IACRhB,qBAAqBG,QAAQW,SAAS,OAAO,KAAK,CACpD;IACF;GACF,CAAC;EACH;CACF;AACF;AAaA,MAAMe,YACJ1B,YAC6B;CAC7B,IAAIwB,UAAUxB;CACd,OAAOwB,QAAQH,MAAMG,UAAUA,QAAQH;CACvC,OAAO,aAAaG,UAAUA,UAAU;AAC1C;AAEA,IAAMG,cAAN,MAAqB;CACnB,SAA2B;CAE3B;CAEAI,YAAYD,MAAe;EACzB,KAAKA,OAAOA;CACd;CAEA,IAAIE,QAAQ;EACV,IAAI,KAAKJ,WAAW,MAClB,KAAKA,SAAS,KAAKE,KAAK;EAE1B,OAAO,KAAKF;CACd;CAEAK,QAAQ;EACN,KAAKL,SAAS;CAChB;AACF;AAEA,IAAaM,oBAAb,MAA+B;CAC7B,2BAAmB,IAAIC,IAA+B;CACtD,OAAyC;CACzC,OAAiC;EAC/Bf,UAAU,CAAA;EACVC,MAAM;CACR;CAEA,aAAqBrB,SAA4BuC,UAAkB;EACjE,MAAMC,UAAU,CAAC;GAAExC;GAASyB,OAAOc;EAAS,CAAC;EAE7C,OAAOC,QAAQC,SAAS,GAAG;GACzB,MAAMjB,UAAUgB,QAAQE,IAAI;GAC5BlB,QAAQxB,QAAQyB,QAAQD,QAAQC;GAEhC,KAAK,MAAMkB,WAAWnB,QAAQxB,QAAQoB,UAAU;IAC9C,MAAMwB,eAAe,KAAKvC,SAASwC,IAAIF,OAAO;IAC9C,IAAIC,cACFJ,QAAQM,KAAK;KAAE9C,SAAS4C;KAAcnB,OAAOD,QAAQC,QAAQ;IAAE,CAAC;GAEpE;EACF;CACF;CAEA,aAAqBzB,SAA4B;EAC/C,KACE,IAAIwB,UAAoCxB,SACxCwB,SACAA,UAAUA,QAAQD,MAElB,CAACC,QAAQD,QAAQ,KAAKc,KAAAA,CAAMhB,OAAOG;CAEvC;CAEA,UACEyB,WACAC,OACAC,WACA;EACA,MAAMC,eAAeF,MAAM3B,QAAQ,KAAKc;EACxC,MAAMgB,kBAAkBJ,aAAa,KAAKZ;EAE1C,IAAIc,cAAc,YAAYC,iBAAiBC,iBAAiB;EAGhE,IAAIF,cAAc,UAEd;QAAA,IAAI3B,UAAoCyB,WACxCzB,SACAA,UAAUA,QAAQD,MAElB,IAAIC,QAAQA,QAAQX,OAAOqC,MAAM1B,QAAQX,IACvC,MAAM,IAAIK,MACR,sNACF;EAAA;EAKN,IAAIiC,cAAc,QAAQ;GACxBC,aAAahC,WAAWgC,aAAahC,SAASkC,QAC3C5C,MAAMA,MAAMwC,MAAM1B,QAAQX,EAC7B;GAEA,IAAIuC,aAAa/B,SAAS6B,OAAO;IAC/B,MAAMK,aAAaH,aAAahC,SAASoC,GAAG,EAAE;IAC9C,MAAMC,WAAWF,aAAa,KAAKlD,SAASwC,IAAIU,UAAU,IAAI;IAC9D,IAAIE,aAAaxC,KAAAA,GACf,MAAM,IAAIC,MACR,uHACF;IAEFkC,aAAa/B,OAAOoC;GACtB;EACF;EAEA,IAAIN,cAAc,OAAO;GACvBE,gBAAgBjC,WAAW,CACzB,GAAGiC,gBAAgBjC,UACnB8B,MAAM1B,QAAQX,EAAE;GAGlBqC,MAAM3B,OAAO0B;GAEb,IAAIvB,SAASwB,KAAK,MAAM,KAAKd,MAC3B,KAAKW,aAAaG,KAAK;QAClB,IAAIG,gBAAgBhC,SAAS,MAAM;IACxCgC,gBAAgBhC,OAAO6B;IACvB,IAAI,KAAKd,SAASiB,iBAChB,KAAKjB,OAAOV,SAASwB,KAAK;GAE9B;GAEA,MAAMX,WAAWU,YAAYA,UAAUxB,QAAQ,IAAI;GACnD,KAAKa,aAAaY,OAAOX,QAAQ;EACnC;CACF;CAEA,YAAoB,IAAIZ,kBAA4C;EAClE,MAAMtB,WAAW,IAAIC,OAAsB,KAAK8B,MAAMX,SAAS,MAAM,CAAC;EACtE,KAAK,IAAID,UAAU,KAAKY,MAAMZ,SAASA,UAAUA,QAAQD,MACvDlB,SAASmB,QAAQC,SAASD,QAAQA;EAEpC,OAAOnB;CACT,CAAC;CAED,IAAID,SAAS;EACX,OAAO,KAAKgC,MAAMZ,QAAQX,MAAM;CAClC;CAEA,IAAI8C,kBAAkB;EAGpB,IAAIvB,OAAO,KAAKA;EAChB,OAAOA,MAAMZ,QAAQoC,UAAUC,cAC7BzB,OAAOA,KAAKb;EAEd,OAAOa,MAAMZ,QAAQX,MAAM;CAC7B;CAEAiD,YAAY1D,QAAiB;EAC3B,IAAIA,WAAWa,KAAAA,KAAab,WAAW,KAAKgC,MAAMZ,QAAQX,IACxD,OAAO,KAAK6C,UAAU1B;EAGxB,MAAM+B,cAAc,KAAK1D,SAASwC,IAAIzC,MAAM;EAC5C,IAAI,CAAC2D,aACH,MAAM,IAAI7C,MACR,yGACF;EAGF,MAAMb,WAAW,IAAIC,MAAqByD,YAAYtC,QAAQ,CAAC;EAC/D,KACE,IAAID,UAAoCuC,aACxCvC,SACAA,UAAUA,QAAQD,MAElBlB,SAASmB,QAAQC,SAASD,QAAQA;EAEpC,OAAOnB;CACT;CAEA2D,mBAAmB/D,UAAyBD,SAAwB;EAClE,MAAMiE,eAAe,KAAK5D,SAASwC,IAAI7C,QAAQa,EAAE;EACjD,MAAMU,OAAOtB,WAAW,KAAKI,SAASwC,IAAI5C,QAAQ,IAAI;EACtD,IAAIsB,SAASN,KAAAA,GACX,MAAM,IAAIC,MACR,kHACF;EAEF,IAAI+C,cAAc;GAChBA,aAAazC,UAAUxB;GACvB,KAAKgD,UAAUzB,MAAM0C,cAAc,QAAQ;GAC3C,KAAKP,UAAUzB,MAAM;GACrB;EACF;EAEA,MAAMiC,UAA6B;GACjC3C;GACAC,SAASxB;GACTqB,MAAM;GACND,UAAU,CAAA;GACVK,OAAOF,OAAOA,KAAKE,QAAQ,IAAI;EACjC;EAEA,KAAKpB,SAAS8D,IAAInE,QAAQa,IAAIqD,OAAO;EACrC,KAAKlB,UAAUzB,MAAM2C,SAAS,MAAM;EAEpC,IAAI,KAAK9B,SAASb,MAChB,KAAKa,OAAO8B;EAGd,KAAKR,UAAUzB,MAAM;CACvB;CAEAmC,WAAWC,WAAmB;EAC5B,MAAMrE,UAAU,KAAKK,SAASwC,IAAIwB,SAAS;EAC3C,IAAI,CAACrE,SACH,MAAM,IAAIkB,MACR,sGACF;EAEF,OAAO;GACLjB,UAAUD,QAAQuB,MAAMC,QAAQX,MAAM;GACtCb,SAASA,QAAQwB;GACjB8C,OAAOtE,QAAQyB;EACjB;CACF;CAEA8C,cAAcF,WAAmBG,eAA2C;EAC1E,MAAMxE,UAAU,KAAKK,SAASwC,IAAIwB,SAAS;EAE3C,IAAI,CAACrE,SACH,MAAM,IAAIkB,MACR,sGACF;EAEF,MAAMuD,cACJD,kBAAkBvD,KAAAA,IACdjB,QAAQuB,OACRiD,kBAAkB,OAChB,OACA,KAAKnE,SAASwC,IAAI2B,aAAa;EACvC,IAAIC,gBAAgBxD,KAAAA,GAClB,MAAM,IAAIC,MACR,0GACF;EAEF,KAAK,MAAMgC,SAASlD,QAAQoB,UAAU;GACpC,MAAMwB,eAAe,KAAKvC,SAASwC,IAAIK,KAAK;GAC5C,IAAI,CAACN,cACH,MAAM,IAAI1B,MACR,4GACF;GACF,KAAK8B,UAAUyB,aAAa7B,cAAc,QAAQ;EACpD;EAEA,KAAKI,UAAU,MAAMhD,SAAS,KAAK;EACnC,KAAKK,SAASqE,OAAOL,SAAS;EAE9B,IAAI,KAAKjC,SAASpC,SAChB,KAAKoC,OAAOV,SAAS+C,eAAe,KAAKpC,IAAI;EAG/C,KAAKqB,UAAUzB,MAAM;CACvB;CAEA0C,YAAYN,WAAmB;EAC7B,MAAMrE,UAAU,KAAKK,SAASwC,IAAIwB,SAAS;EAC3C,IAAI,CAACrE,SACH,MAAM,IAAIkB,MACR,oGACF;EAEF,MAAM,EAAEE,aAAapB,QAAQuB,QAAQ,KAAKc;EAC1C,OAAOjB;CACT;;;;;;;;;CAUA,iCACEyD,cACAC,aACA;EACA,IAAI,CAACD,cAAc;EAEnB,MAAME,+BAAe,IAAIC,IAAY;EACrC,KAAK,IAAIxD,UAAUsD,aAAatD,SAASA,UAAUA,QAAQD,MACzDwD,aAAaE,IAAIzD,QAAQA,QAAQX,EAAE;EAGrC,MAAMqE,QAAkB,CAAA;EACxB,KACE,IAAI1D,UAAoCqD,cACxCrD,SACAA,UAAUA,QAAQD,MAClB;GAGA,IAAIwD,aAAaI,IAAI3D,QAAQA,QAAQX,EAAE,GAAG;GAC1C,IAAIW,QAAQA,QAAQoC,UAAUC,cAC5BqB,MAAMpC,KAAKtB,QAAQA,QAAQX,EAAE;EAEjC;EAEA,KAAK,MAAMA,MAAMqE,OAEf,IAAI,KAAK7E,SAAS8E,IAAItE,EAAE,GAAG,KAAK0D,cAAc1D,EAAE;CAEpD;CAEAuE,eAAef,WAAmB;EAChC,MAAMrE,UAAU,KAAKK,SAASwC,IAAIwB,SAAS;EAC3C,IAAI,CAACrE,SACH,MAAM,IAAIkB,MACR,sGACF;EAEF,MAAM2D,eAAe,KAAKzC;EAC1B,KAAKW,aAAa/C,OAAO;EAEzB,KAAKoC,OAAOV,SAAS1B,OAAO;EAE5B,KAAK4E,iCAAiCC,cAAc,KAAKzC,IAAI;EAE7D,KAAKsB,UAAUzB,MAAM;CACvB;CAEAoD,UAAUhB,WAA0B;EAClC,IAAIA,cAAc,MAAM;GACtB,KAAKiB,MAAM;GACX;EACF;EAEA,MAAMtF,UAAU,KAAKK,SAASwC,IAAIwB,SAAS;EAC3C,IAAI,CAACrE,SACH,MAAM,IAAIkB,MACR,iGACF;EAEF,MAAM2D,eAAe,KAAKzC;EAE1B,IAAIpC,QAAQoB,SAASqB,SAAS,GAAG;GAC/B,MAAMD,UAAU,CAAC,GAAGxC,QAAQoB,QAAQ;GACpC,OAAOoB,QAAQC,SAAS,GAAG;IACzB,MAAME,UAAUH,QAAQE,IAAI;IAC5B,MAAME,eAAe,KAAKvC,SAASwC,IAAIF,OAAO;IAC9C,IAAIC,cAAc;KAChB,KAAK,MAAM2C,gBAAgB3C,aAAaxB,UACtCoB,QAAQM,KAAKyC,YAAY;KAE3B,KAAKlF,SAASqE,OAAO/B,OAAO;IAC9B;GACF;GAEA3C,QAAQoB,WAAW,CAAA;GACnBpB,QAAQqB,OAAO;EACjB;EAEA,KAAKe,OAAOpC;EACZ,KAAK+C,aAAa/C,OAAO;EAEzB,KAAK4E,iCAAiCC,cAAc,KAAKzC,IAAI;EAE7D,KAAKsB,UAAUzB,MAAM;CACvB;CAEAqD,QAAc;EACZ,KAAKjF,SAASiF,MAAM;EACpB,KAAKlD,OAAO;EACZ,KAAKC,OAAO;GACVjB,UAAU,CAAA;GACVC,MAAM;EACR;EACA,KAAKqC,UAAUzB,MAAM;CACvB;CAEAuD,SAAoC;EAClC,MAAMC,cAAqD,CAAA;EAO3D,MAAMjD,UAAU,CAAC,GAAG,KAAKH,KAAKjB,QAAQ,CAAC,CAACsE,QAAQ;EAChD,OAAOlD,QAAQC,SAAS,GAAG;GACzB,MAAMzC,UAAU,KAAKK,SAASwC,IAAIL,QAAQE,IAAI,CAAE;GAChD,IAAI,CAAC1C,SAAS;GACd,KAAK,IAAI2F,IAAI3F,QAAQoB,SAASqB,SAAS,GAAGkD,KAAK,GAAGA,KAChDnD,QAAQM,KAAK9C,QAAQoB,SAASuE,EAAG;GAEnC,IAAI3F,QAAQwB,QAAQoC,UAAUC,cAAc;GAC5C,IAAItC,OAAOvB,QAAQuB;GACnB,OAAOA,QAAQA,KAAKC,QAAQoC,UAAUC,cACpCtC,OAAOA,KAAKA;GAEdkE,YAAY3C,KAAK;IACf9C,SAASA,QAAQwB;IACjBvB,UAAUsB,MAAMC,QAAQX,MAAM;GAChC,CAAC;EACH;EAEA,OAAO;GACLT,QAAQ,KAAKuD;GACbtD,UAAUoF;EACZ;CACF;CAEAG,OAAO,EAAExF,QAAQC,YAAuC;EACtD,KAAK,MAAM,EAAEL,SAASC,cAAcI,UAClC,KAAK2D,mBAAmB/D,UAAUD,OAAO;EAG3C,KAAKqF,UAAUjF,UAAUC,SAASmD,GAAG,EAAE,CAAC,EAAExD,QAAQa,MAAM,IAAI;CAC9D;AACF"}
1
+ {"version":3,"file":"message-repository.js","names":["ThreadMessage","RunConfig","generateId","ThreadMessageLike","getRepositoryContentAutoStatus","fromThreadMessageLike","ExportedMessageRepositoryItem","message","parentId","runConfig","ExportedMessageRepository","headId","messages","Array","fromArray","conv","map","m","content","idx","id","fromBranchableArray","items","options","undefined","Error","RepositoryParent","children","next","RepositoryMessage","prev","current","level","findHead","CachedValue","_value","T","func","constructor","value","dirty","MessageRepository","Map","head","root","updateLevels","newLevel","pending","length","pop","childId","childMessage","get","push","selectPathTo","performOp","newParent","child","operation","parentOrRoot","newParentOrRoot","filter","fallbackId","at","fallback","_messages","canonicalHeadId","metadata","isOptimistic","getMessages","headMessage","addOrUpdateMessage","existingItem","newItem","set","getMessage","messageId","index","deleteMessage","replacementId","replacement","delete","getBranches","evictOffBranchOptimisticMessages","previousHead","currentHead","onHeadBranch","Set","add","stale","has","switchToBranch","resetHead","clear","descendantId","export","exportItems","reverse","i","import"],"sources":["../../../src/runtime/utils/message-repository.ts"],"sourcesContent":["import type { ThreadMessage } from \"../../types/message\";\nimport type { RunConfig } from \"../../types/message\";\nimport { generateId } from \"../../utils/id\";\nimport type { ThreadMessageLike } from \"./thread-message-like\";\nimport { getRepositoryContentAutoStatus } from \"./auto-status\";\nimport { fromThreadMessageLike } from \"./thread-message-like\";\n\nexport type ExportedMessageRepositoryItem = {\n message: ThreadMessage;\n parentId: string | null;\n runConfig?: RunConfig;\n};\n\nexport type ExportedMessageRepository = {\n headId?: string | null;\n messages: Array<{\n message: ThreadMessage;\n parentId: string | null;\n runConfig?: RunConfig;\n }>;\n};\n\nexport const ExportedMessageRepository = {\n fromArray: (\n messages: readonly ThreadMessageLike[],\n ): ExportedMessageRepository => {\n const conv = messages.map((m) =>\n fromThreadMessageLike(\n m,\n generateId(),\n getRepositoryContentAutoStatus(m.content),\n ),\n );\n\n return {\n messages: conv.map((m, idx) => ({\n parentId: idx > 0 ? conv[idx - 1]!.id : null,\n message: m,\n })),\n };\n },\n\n fromBranchableArray: (\n items: readonly {\n message: ThreadMessageLike;\n parentId: string | null;\n }[],\n options?: { headId?: string | null },\n ): ExportedMessageRepository => {\n return {\n ...(options?.headId !== undefined\n ? { headId: options.headId }\n : undefined),\n messages: items.map(({ message, parentId }) => {\n if (!message.id) {\n throw new Error(\n \"ExportedMessageRepository.fromBranchableArray: Each message must have an 'id' field set.\",\n );\n }\n return {\n parentId,\n message: fromThreadMessageLike(\n message,\n message.id,\n getRepositoryContentAutoStatus(message.content),\n ),\n };\n }),\n };\n },\n};\n\ntype RepositoryParent = {\n children: string[];\n next: RepositoryMessage | null;\n};\n\ntype RepositoryMessage = RepositoryParent & {\n prev: RepositoryMessage | null;\n current: ThreadMessage;\n level: number;\n};\n\nconst findHead = (\n message: RepositoryMessage | RepositoryParent,\n): RepositoryMessage | null => {\n let current = message;\n while (current.next) current = current.next;\n return \"current\" in current ? current : null;\n};\n\nclass CachedValue<T> {\n private _value: T | null = null;\n\n private func: () => T;\n\n constructor(func: () => T) {\n this.func = func;\n }\n\n get value() {\n if (this._value === null) {\n this._value = this.func();\n }\n return this._value;\n }\n\n dirty() {\n this._value = null;\n }\n}\n\nexport class MessageRepository {\n private messages = new Map<string, RepositoryMessage>();\n private head: RepositoryMessage | null = null;\n private root: RepositoryParent = {\n children: [],\n next: null,\n };\n\n private updateLevels(message: RepositoryMessage, newLevel: number) {\n const pending = [{ message, level: newLevel }];\n\n while (pending.length > 0) {\n const current = pending.pop()!;\n current.message.level = current.level;\n\n for (const childId of current.message.children) {\n const childMessage = this.messages.get(childId);\n if (childMessage) {\n pending.push({ message: childMessage, level: current.level + 1 });\n }\n }\n }\n }\n\n private selectPathTo(message: RepositoryMessage) {\n for (\n let current: RepositoryMessage | null = message;\n current;\n current = current.prev\n ) {\n (current.prev ?? this.root).next = current;\n }\n }\n\n private performOp(\n newParent: RepositoryMessage | null,\n child: RepositoryMessage,\n operation: \"cut\" | \"link\" | \"relink\",\n ) {\n const parentOrRoot = child.prev ?? this.root;\n const newParentOrRoot = newParent ?? this.root;\n\n if (operation === \"relink\" && parentOrRoot === newParentOrRoot) return;\n\n // `link` receives a fresh ID from `addOrUpdateMessage`; only `relink` can introduce a cycle.\n if (operation === \"relink\") {\n for (\n let current: RepositoryMessage | null = newParent;\n current;\n current = current.prev\n ) {\n if (current.current.id === child.current.id) {\n throw new Error(\n \"MessageRepository(performOp/relink): A message with the same id already exists in the parent tree. This error occurs if the same message id is found multiple times. This is likely an internal bug in assistant-ui.\",\n );\n }\n }\n }\n\n if (operation !== \"link\") {\n parentOrRoot.children = parentOrRoot.children.filter(\n (m) => m !== child.current.id,\n );\n\n if (parentOrRoot.next === child) {\n const fallbackId = parentOrRoot.children.at(-1);\n const fallback = fallbackId ? this.messages.get(fallbackId) : null;\n if (fallback === undefined) {\n throw new Error(\n \"MessageRepository(performOp/cut): Fallback sibling message not found. This is likely an internal bug in assistant-ui.\",\n );\n }\n parentOrRoot.next = fallback;\n }\n }\n\n if (operation !== \"cut\") {\n newParentOrRoot.children = [\n ...newParentOrRoot.children,\n child.current.id,\n ];\n\n child.prev = newParent;\n\n if (findHead(child) === this.head) {\n this.selectPathTo(child);\n } else if (newParentOrRoot.next === null) {\n newParentOrRoot.next = child;\n if (this.head === newParentOrRoot) {\n this.head = findHead(child);\n }\n }\n\n const newLevel = newParent ? newParent.level + 1 : 0;\n this.updateLevels(child, newLevel);\n }\n }\n\n private _messages = new CachedValue<readonly ThreadMessage[]>(() => {\n const messages = new Array<ThreadMessage>((this.head?.level ?? -1) + 1);\n for (let current = this.head; current; current = current.prev) {\n messages[current.level] = current.current;\n }\n return messages;\n });\n\n get headId() {\n return this.head?.current.id ?? null;\n }\n\n get canonicalHeadId() {\n // Optimistic messages are ephemeral, so persisted callers need the nearest\n // non-optimistic ancestor rather than the raw head.\n let head = this.head;\n while (head?.current.metadata?.isOptimistic) {\n head = head.prev;\n }\n return head?.current.id ?? null;\n }\n\n getMessages(headId?: string) {\n if (headId === undefined || headId === this.head?.current.id) {\n return this._messages.value;\n }\n\n const headMessage = this.messages.get(headId);\n if (!headMessage) {\n throw new Error(\n \"MessageRepository(getMessages): Head message not found. This is likely an internal bug in assistant-ui.\",\n );\n }\n\n const messages = new Array<ThreadMessage>(headMessage.level + 1);\n for (\n let current: RepositoryMessage | null = headMessage;\n current;\n current = current.prev\n ) {\n messages[current.level] = current.current;\n }\n return messages;\n }\n\n addOrUpdateMessage(parentId: string | null, message: ThreadMessage) {\n const existingItem = this.messages.get(message.id);\n const prev = parentId ? this.messages.get(parentId) : null;\n if (prev === undefined)\n throw new Error(\n \"MessageRepository(addOrUpdateMessage): Parent message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n if (existingItem) {\n existingItem.current = message;\n this.performOp(prev, existingItem, \"relink\");\n this._messages.dirty();\n return;\n }\n\n const newItem: RepositoryMessage = {\n prev,\n current: message,\n next: null,\n children: [],\n level: prev ? prev.level + 1 : 0,\n };\n\n this.messages.set(message.id, newItem);\n this.performOp(prev, newItem, \"link\");\n\n if (this.head === prev) {\n this.head = newItem;\n }\n\n this._messages.dirty();\n }\n\n getMessage(messageId: string) {\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(updateMessage): Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n return {\n parentId: message.prev?.current.id ?? null,\n message: message.current,\n index: message.level,\n };\n }\n\n deleteMessage(messageId: string, replacementId?: string | null | undefined) {\n const message = this.messages.get(messageId);\n\n if (!message)\n throw new Error(\n \"MessageRepository(deleteMessage): Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const replacement =\n replacementId === undefined\n ? message.prev\n : replacementId === null\n ? null\n : this.messages.get(replacementId);\n if (replacement === undefined)\n throw new Error(\n \"MessageRepository(deleteMessage): Replacement not found. This is likely an internal bug in assistant-ui.\",\n );\n\n for (const child of message.children) {\n const childMessage = this.messages.get(child);\n if (!childMessage)\n throw new Error(\n \"MessageRepository(deleteMessage): Child message not found. This is likely an internal bug in assistant-ui.\",\n );\n this.performOp(replacement, childMessage, \"relink\");\n }\n\n this.performOp(null, message, \"cut\");\n this.messages.delete(messageId);\n\n if (this.head === message) {\n this.head = findHead(replacement ?? this.root);\n }\n\n this._messages.dirty();\n }\n\n getBranches(messageId: string) {\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(getBranches): Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const { children } = message.prev ?? this.root;\n return children;\n }\n\n /**\n * Evicts optimistic messages (`metadata.isOptimistic`) the head just moved\n * away from. Since eviction runs on every head move, the only optimistic\n * messages in the repository live on the branch the head previously pointed\n * at — so we walk just that branch rather than the whole repository. Keeps a\n * client→server id swap from leaving a phantom sibling, and drops off-branch\n * placeholders.\n */\n private evictOffBranchOptimisticMessages(\n previousHead: RepositoryMessage | null,\n currentHead: RepositoryMessage | null,\n ) {\n if (!previousHead) return;\n\n const onHeadBranch = new Set<string>();\n for (let current = currentHead; current; current = current.prev) {\n onHeadBranch.add(current.current.id);\n }\n\n const stale: string[] = [];\n for (\n let current: RepositoryMessage | null = previousHead;\n current;\n current = current.prev\n ) {\n // Stop at the first node shared with the current head branch: every\n // ancestor above it is shared too, so nothing further can be off-branch.\n if (onHeadBranch.has(current.current.id)) break;\n if (current.current.metadata?.isOptimistic) {\n stale.push(current.current.id);\n }\n }\n\n for (const id of stale) {\n // A prior deletion may have already removed this node.\n if (this.messages.has(id)) this.deleteMessage(id);\n }\n }\n\n switchToBranch(messageId: string) {\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(switchToBranch): Branch not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const previousHead = this.head;\n this.selectPathTo(message);\n\n this.head = findHead(message);\n\n this.evictOffBranchOptimisticMessages(previousHead, this.head);\n\n this._messages.dirty();\n }\n\n resetHead(messageId: string | null) {\n if (messageId === null) {\n this.clear();\n return;\n }\n\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(resetHead): Branch not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const previousHead = this.head;\n\n if (message.children.length > 0) {\n const pending = [...message.children];\n while (pending.length > 0) {\n const childId = pending.pop()!;\n const childMessage = this.messages.get(childId);\n if (childMessage) {\n for (const descendantId of childMessage.children) {\n pending.push(descendantId);\n }\n this.messages.delete(childId);\n }\n }\n\n message.children = [];\n message.next = null;\n }\n\n this.head = message;\n this.selectPathTo(message);\n\n this.evictOffBranchOptimisticMessages(previousHead, this.head);\n\n this._messages.dirty();\n }\n\n clear(): void {\n this.messages.clear();\n this.head = null;\n this.root = {\n children: [],\n next: null,\n };\n this._messages.dirty();\n }\n\n export(): ExportedMessageRepository {\n const exportItems: ExportedMessageRepository[\"messages\"] = [];\n\n // Optimistic messages are ephemeral and never persisted. A persisted child\n // of an optimistic node is re-parented onto its nearest persisted ancestor\n // so the exported tree never references a skipped id.\n // Import and external-state conversion require parents before children, so\n // the tree is walked in pre-order rather than iterated in insertion order.\n const pending = [...this.root.children].reverse();\n while (pending.length > 0) {\n const message = this.messages.get(pending.pop()!);\n if (!message) continue;\n for (let i = message.children.length - 1; i >= 0; i--) {\n pending.push(message.children[i]!);\n }\n if (message.current.metadata?.isOptimistic) continue;\n let prev = message.prev;\n while (prev && prev.current.metadata?.isOptimistic) {\n prev = prev.prev;\n }\n exportItems.push({\n message: message.current,\n parentId: prev?.current.id ?? null,\n });\n }\n\n return {\n headId: this.canonicalHeadId,\n messages: exportItems,\n };\n }\n\n import({ headId, messages }: ExportedMessageRepository) {\n for (const { message, parentId } of messages) {\n this.addOrUpdateMessage(parentId, message);\n }\n\n this.resetHead(headId ?? messages.at(-1)?.message.id ?? null);\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { DataMessagePart, FileMessagePart, GenerativeUIMessagePart, ImageMessagePart, MessageStatus, MessageTiming, PartProviderMetadata, ReasoningMessagePart, SourceMessagePart, TextMessagePart, ThreadMessage, ThreadStep, ThreadUserMessagePart, ToolCallMessagePart, ToolCallMessagePartMcpMetadata, ToolCallTiming, Unstable_AudioMessagePart } from "../../types/message.js";
1
+ import { DataMessagePart, FileMessagePart, GenerativeUIMessagePart, ImageMessagePart, MessageModality, MessageStatus, MessageTiming, PartProviderMetadata, ReasoningMessagePart, SourceMessagePart, TextMessagePart, ThreadMessage, ThreadStep, ThreadUserMessagePart, ToolCallMessagePart, ToolCallMessagePartMcpMetadata, ToolCallTiming, Unstable_AudioMessagePart } from "../../types/message.js";
2
2
  import { CompleteAttachment } from "../../types/attachment.js";
3
3
  import { ReadonlyJSONObject, ReadonlyJSONValue } from "assistant-stream/utils";
4
4
  //#region src/runtime/utils/thread-message-like.d.ts
@@ -44,6 +44,7 @@ type ThreadMessageLike = {
44
44
  readonly type: "positive" | "negative";
45
45
  };
46
46
  readonly isOptimistic?: boolean | undefined;
47
+ readonly modality?: MessageModality | undefined;
47
48
  readonly custom?: Record<string, unknown> | undefined;
48
49
  } | undefined;
49
50
  };
@@ -1 +1 @@
1
- {"version":3,"file":"thread-message-like.d.ts","names":[],"sources":["../../../src/runtime/utils/thread-message-like.ts"],"mappings":";;;;KAkCK;WACM;WACA;;KAGC;WACD;WACA,4BAGD,kBACA,uBACA,oBACA,mBACA,kBACA,kBACA,0BACA,4BACA;aAEW;aACA;aACA;aACA,OAAO;aACP;aACA;aACA;aACA;aACA;aACA,oBAAoB;aACpB;MAAc;MAAe;;aAC7B,SAAS;aACT,MAAM;aACN,mBAAmB;aACnB,WAAW,YAAY;;WAGjC;WACA,YAAY;WACZ,SAAS;WACT,wBACK,KAAK;aACJ,mBAAmB,wBAAwB;;WAGjD;aAEM,iBAAiB;aACjB,gCACI;aAEJ,yBAAyB;aACzB,iBAAiB;aACjB,SAAS;aACT;eAA+B;;aAC/B;aACA,SAAS;;;;;;cAgBb,wBACX,MAAM,mBACN,oBACA,gBAAgB,kBACf"}
1
+ {"version":3,"file":"thread-message-like.d.ts","names":[],"sources":["../../../src/runtime/utils/thread-message-like.ts"],"mappings":";;;;KAmCK;WACM;WACA;;KAGC;WACD;WACA,4BAGD,kBACA,uBACA,oBACA,mBACA,kBACA,kBACA,0BACA,4BACA;aAEW;aACA;aACA;aACA,OAAO;aACP;aACA;aACA;aACA;aACA;aACA,oBAAoB;aACpB;MAAc;MAAe;;aAC7B,SAAS;aACT,MAAM;aACN,mBAAmB;aACnB,WAAW,YAAY;;WAGjC;WACA,YAAY;WACZ,SAAS;WACT,wBACK,KAAK;aACJ,mBAAmB,wBAAwB;;WAGjD;aAEM,iBAAiB;aACjB,gCACI;aAEJ,yBAAyB;aACzB,iBAAiB;aACjB,SAAS;aACT;eAA+B;;aAC/B;aACA,WAAW;aACX,SAAS;;;;;;cAgBb,wBACX,MAAM,mBACN,oBACA,gBAAgB,kBACf"}
@@ -92,7 +92,8 @@ const fromThreadMessageLike = (like, fallbackId, fallbackStatus) => {
92
92
  steps: metadata?.steps ?? [],
93
93
  ...metadata?.timing && { timing: metadata.timing },
94
94
  ...metadata?.submittedFeedback && { submittedFeedback: metadata.submittedFeedback },
95
- ...metadata?.isOptimistic && { isOptimistic: true }
95
+ ...metadata?.isOptimistic && { isOptimistic: true },
96
+ ...metadata?.modality && { modality: metadata.modality }
96
97
  }
97
98
  };
98
99
  case "user": return {
@@ -121,7 +122,8 @@ const fromThreadMessageLike = (like, fallbackId, fallbackStatus) => {
121
122
  })),
122
123
  metadata: {
123
124
  custom: metadata?.custom ?? {},
124
- ...metadata?.isOptimistic && { isOptimistic: true }
125
+ ...metadata?.isOptimistic && { isOptimistic: true },
126
+ ...metadata?.modality && { modality: metadata.modality }
125
127
  }
126
128
  };
127
129
  case "system":
@@ -1 +1 @@
1
- {"version":3,"file":"thread-message-like.js","names":["parsePartialJsonObject","generateId","parseDataUrl","ReasoningMessagePart","SourceMessagePart","ThreadStep","MessageStatus","ImageMessagePart","ThreadMessage","ThreadAssistantMessagePart","ThreadAssistantMessage","ThreadUserMessagePart","ThreadUserMessage","ThreadSystemMessage","FileMessagePart","DataMessagePart","GenerativeUIMessagePart","Unstable_AudioMessagePart","CompleteAttachment","MessageTiming","PartProviderMetadata","TextMessagePart","ToolCallTiming","ToolCallMessagePart","ToolCallMessagePartMcpMetadata","ReadonlyJSONObject","ReadonlyJSONValue","DataPrefixedPart","type","data","ThreadMessageLike","role","content","toolCallId","toolName","args","argsText","artifact","result","isError","parentId","messages","interrupt","payload","timing","mcp","providerMetadata","approval","NonNullable","id","createdAt","Date","status","attachments","Omit","metadata","unstable_state","unstable_annotations","unstable_data","steps","submittedFeedback","isOptimistic","custom","Record","convertDataPrefixedPart","startsWith","undefined","name","substring","fromThreadMessageLike","like","fallbackId","fallbackStatus","common","const","text","sanitizeImageContent","image","rest","mimeType","test","console","warn","length","Error","map","part","trim","unstable_summary","basePart","commonProps","JSON","stringify","converted","filter","c","att","unsupportedRole"],"sources":["../../../src/runtime/utils/thread-message-like.ts"],"sourcesContent":["import { parsePartialJsonObject } from \"assistant-stream/utils\";\nimport { generateId } from \"../../utils/id\";\nimport { parseDataUrl } from \"../../utils/data-url\";\nimport type {\n ReasoningMessagePart,\n SourceMessagePart,\n ThreadStep,\n MessageStatus,\n ImageMessagePart,\n ThreadMessage,\n ThreadAssistantMessagePart,\n ThreadAssistantMessage,\n ThreadUserMessagePart,\n ThreadUserMessage,\n ThreadSystemMessage,\n FileMessagePart,\n DataMessagePart,\n GenerativeUIMessagePart,\n Unstable_AudioMessagePart,\n} from \"../../types/message\";\nimport type { CompleteAttachment } from \"../../types/attachment\";\nimport type {\n MessageTiming,\n PartProviderMetadata,\n TextMessagePart,\n ToolCallTiming,\n ToolCallMessagePart,\n ToolCallMessagePartMcpMetadata,\n} from \"../../types/message\";\nimport type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\n\ntype DataPrefixedPart = {\n readonly type: `data-${string}`;\n readonly data: any;\n};\n\nexport type ThreadMessageLike = {\n readonly role: \"assistant\" | \"user\" | \"system\";\n readonly content:\n | string\n | readonly (\n | TextMessagePart\n | ReasoningMessagePart\n | SourceMessagePart\n | ImageMessagePart\n | FileMessagePart\n | DataMessagePart\n | GenerativeUIMessagePart\n | Unstable_AudioMessagePart\n | DataPrefixedPart\n | {\n readonly type: \"tool-call\";\n readonly toolCallId?: string;\n readonly toolName: string;\n readonly args?: ReadonlyJSONObject;\n readonly argsText?: string;\n readonly artifact?: any;\n readonly result?: any | undefined;\n readonly isError?: boolean | undefined;\n readonly parentId?: string | undefined;\n readonly messages?: readonly ThreadMessage[] | undefined;\n readonly interrupt?: { type: \"human\"; payload: unknown };\n readonly timing?: ToolCallTiming;\n readonly mcp?: ToolCallMessagePartMcpMetadata;\n readonly providerMetadata?: PartProviderMetadata;\n readonly approval?: NonNullable<ToolCallMessagePart[\"approval\"]>;\n }\n )[];\n readonly id?: string | undefined;\n readonly createdAt?: Date | undefined;\n readonly status?: MessageStatus | undefined;\n readonly attachments?:\n | readonly (Omit<CompleteAttachment, \"content\"> & {\n readonly content: readonly (ThreadUserMessagePart | DataPrefixedPart)[];\n })[]\n | undefined;\n readonly metadata?:\n | {\n readonly unstable_state?: ReadonlyJSONValue;\n readonly unstable_annotations?:\n | readonly ReadonlyJSONValue[]\n | undefined;\n readonly unstable_data?: readonly ReadonlyJSONValue[] | undefined;\n readonly steps?: readonly ThreadStep[] | undefined;\n readonly timing?: MessageTiming | undefined;\n readonly submittedFeedback?: { readonly type: \"positive\" | \"negative\" };\n readonly isOptimistic?: boolean | undefined;\n readonly custom?: Record<string, unknown> | undefined;\n }\n | undefined;\n};\n\nconst convertDataPrefixedPart = (\n type: string,\n data: unknown,\n): DataMessagePart | undefined => {\n if (!type.startsWith(\"data-\")) return undefined;\n return { type: \"data\", name: type.substring(5), data };\n};\n\n/**\n * @deprecated This API is experimental and may change without notice.\n */\nexport const fromThreadMessageLike = (\n like: ThreadMessageLike,\n fallbackId: string,\n fallbackStatus: MessageStatus,\n): ThreadMessage => {\n const { role, id, createdAt, attachments, status, metadata } = like;\n const common = {\n id: id ?? fallbackId,\n createdAt: createdAt ?? new Date(),\n };\n\n const content =\n typeof like.content === \"string\"\n ? [{ type: \"text\" as const, text: like.content }]\n : like.content;\n\n const sanitizeImageContent = ({\n image,\n ...rest\n }: ImageMessagePart): ImageMessagePart | null => {\n if (typeof image !== \"string\") return null;\n if (parseDataUrl(image)?.mimeType.startsWith(\"image/\")) {\n return { ...rest, image };\n }\n if (/^(https:\\/\\/|blob:)/i.test(image)) {\n return { ...rest, image };\n }\n console.warn(`Invalid image data format detected`);\n return null;\n };\n\n if (role !== \"user\" && attachments?.length)\n throw new Error(\"attachments are only supported for user messages\");\n\n if (role !== \"assistant\" && status)\n throw new Error(\"status is only supported for assistant messages\");\n\n if (role !== \"assistant\" && metadata?.steps)\n throw new Error(\"metadata.steps is only supported for assistant messages\");\n\n switch (role) {\n case \"assistant\":\n return {\n ...common,\n role,\n content: content\n .map((part): ThreadAssistantMessagePart | null => {\n const type = part.type;\n switch (type) {\n case \"text\":\n if (!part.text?.trim()) return null;\n return part;\n\n case \"reasoning\":\n if (!part.text?.trim() && !part.unstable_summary?.trim())\n return null;\n return part;\n\n case \"file\":\n case \"source\":\n return part;\n\n case \"image\":\n return sanitizeImageContent(part);\n\n case \"data\":\n return part;\n\n case \"generative-ui\":\n return part;\n\n case \"tool-call\": {\n const { parentId, messages, ...basePart } = part;\n const commonProps = {\n ...basePart,\n toolCallId: part.toolCallId || `tool-${generateId()}`,\n ...(parentId !== undefined && { parentId }),\n ...(messages !== undefined && { messages }),\n };\n\n if (part.args) {\n return {\n ...commonProps,\n args: part.args,\n argsText: part.argsText ?? JSON.stringify(part.args),\n };\n }\n return {\n ...commonProps,\n args: parsePartialJsonObject(part.argsText ?? \"\") ?? {},\n argsText: part.argsText ?? \"\",\n };\n }\n\n default: {\n const converted = convertDataPrefixedPart(\n type,\n (part as DataPrefixedPart).data,\n );\n if (converted) return converted;\n throw new Error(\n `Unsupported assistant message part type: ${type}`,\n );\n }\n }\n })\n .filter((c) => !!c),\n status: status ?? fallbackStatus,\n metadata: {\n unstable_state: metadata?.unstable_state ?? null,\n unstable_annotations: metadata?.unstable_annotations ?? [],\n unstable_data: metadata?.unstable_data ?? [],\n custom: metadata?.custom ?? {},\n steps: metadata?.steps ?? [],\n ...(metadata?.timing && { timing: metadata.timing }),\n ...(metadata?.submittedFeedback && {\n submittedFeedback: metadata.submittedFeedback,\n }),\n ...(metadata?.isOptimistic && { isOptimistic: true }),\n },\n } satisfies ThreadAssistantMessage;\n\n case \"user\":\n return {\n ...common,\n role,\n content: content.map((part): ThreadUserMessagePart => {\n const type = part.type;\n switch (type) {\n case \"text\":\n case \"image\":\n case \"audio\":\n case \"file\":\n case \"data\":\n return part;\n\n default: {\n const converted = convertDataPrefixedPart(\n type,\n (part as DataPrefixedPart).data,\n );\n if (converted) return converted;\n throw new Error(`Unsupported user message part type: ${type}`);\n }\n }\n }),\n attachments: (attachments ?? []).map((att) => ({\n ...att,\n content: att.content.map((part): ThreadUserMessagePart => {\n const converted = convertDataPrefixedPart(\n part.type,\n (part as DataPrefixedPart).data,\n );\n return converted ?? (part as ThreadUserMessagePart);\n }),\n })),\n metadata: {\n custom: metadata?.custom ?? {},\n ...(metadata?.isOptimistic && { isOptimistic: true }),\n },\n } satisfies ThreadUserMessage;\n\n case \"system\":\n if (content.length !== 1 || content[0]!.type !== \"text\")\n throw new Error(\n \"System messages must have exactly one text message part.\",\n );\n\n return {\n ...common,\n role,\n content: content as [TextMessagePart],\n metadata: {\n custom: metadata?.custom ?? {},\n },\n } satisfies ThreadSystemMessage;\n\n default: {\n const unsupportedRole: never = role;\n throw new Error(`Unknown message role: ${unsupportedRole}`);\n }\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"thread-message-like.js","names":["parsePartialJsonObject","generateId","parseDataUrl","ReasoningMessagePart","SourceMessagePart","ThreadStep","MessageStatus","ImageMessagePart","ThreadMessage","ThreadAssistantMessagePart","ThreadAssistantMessage","ThreadUserMessagePart","ThreadUserMessage","ThreadSystemMessage","FileMessagePart","DataMessagePart","GenerativeUIMessagePart","Unstable_AudioMessagePart","CompleteAttachment","MessageModality","MessageTiming","PartProviderMetadata","TextMessagePart","ToolCallTiming","ToolCallMessagePart","ToolCallMessagePartMcpMetadata","ReadonlyJSONObject","ReadonlyJSONValue","DataPrefixedPart","type","data","ThreadMessageLike","role","content","toolCallId","toolName","args","argsText","artifact","result","isError","parentId","messages","interrupt","payload","timing","mcp","providerMetadata","approval","NonNullable","id","createdAt","Date","status","attachments","Omit","metadata","unstable_state","unstable_annotations","unstable_data","steps","submittedFeedback","isOptimistic","modality","custom","Record","convertDataPrefixedPart","startsWith","undefined","name","substring","fromThreadMessageLike","like","fallbackId","fallbackStatus","common","const","text","sanitizeImageContent","image","rest","mimeType","test","console","warn","length","Error","map","part","trim","unstable_summary","basePart","commonProps","JSON","stringify","converted","filter","c","att","unsupportedRole"],"sources":["../../../src/runtime/utils/thread-message-like.ts"],"sourcesContent":["import { parsePartialJsonObject } from \"assistant-stream/utils\";\nimport { generateId } from \"../../utils/id\";\nimport { parseDataUrl } from \"../../utils/data-url\";\nimport type {\n ReasoningMessagePart,\n SourceMessagePart,\n ThreadStep,\n MessageStatus,\n ImageMessagePart,\n ThreadMessage,\n ThreadAssistantMessagePart,\n ThreadAssistantMessage,\n ThreadUserMessagePart,\n ThreadUserMessage,\n ThreadSystemMessage,\n FileMessagePart,\n DataMessagePart,\n GenerativeUIMessagePart,\n Unstable_AudioMessagePart,\n} from \"../../types/message\";\nimport type { CompleteAttachment } from \"../../types/attachment\";\nimport type {\n MessageModality,\n MessageTiming,\n PartProviderMetadata,\n TextMessagePart,\n ToolCallTiming,\n ToolCallMessagePart,\n ToolCallMessagePartMcpMetadata,\n} from \"../../types/message\";\nimport type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\n\ntype DataPrefixedPart = {\n readonly type: `data-${string}`;\n readonly data: any;\n};\n\nexport type ThreadMessageLike = {\n readonly role: \"assistant\" | \"user\" | \"system\";\n readonly content:\n | string\n | readonly (\n | TextMessagePart\n | ReasoningMessagePart\n | SourceMessagePart\n | ImageMessagePart\n | FileMessagePart\n | DataMessagePart\n | GenerativeUIMessagePart\n | Unstable_AudioMessagePart\n | DataPrefixedPart\n | {\n readonly type: \"tool-call\";\n readonly toolCallId?: string;\n readonly toolName: string;\n readonly args?: ReadonlyJSONObject;\n readonly argsText?: string;\n readonly artifact?: any;\n readonly result?: any | undefined;\n readonly isError?: boolean | undefined;\n readonly parentId?: string | undefined;\n readonly messages?: readonly ThreadMessage[] | undefined;\n readonly interrupt?: { type: \"human\"; payload: unknown };\n readonly timing?: ToolCallTiming;\n readonly mcp?: ToolCallMessagePartMcpMetadata;\n readonly providerMetadata?: PartProviderMetadata;\n readonly approval?: NonNullable<ToolCallMessagePart[\"approval\"]>;\n }\n )[];\n readonly id?: string | undefined;\n readonly createdAt?: Date | undefined;\n readonly status?: MessageStatus | undefined;\n readonly attachments?:\n | readonly (Omit<CompleteAttachment, \"content\"> & {\n readonly content: readonly (ThreadUserMessagePart | DataPrefixedPart)[];\n })[]\n | undefined;\n readonly metadata?:\n | {\n readonly unstable_state?: ReadonlyJSONValue;\n readonly unstable_annotations?:\n | readonly ReadonlyJSONValue[]\n | undefined;\n readonly unstable_data?: readonly ReadonlyJSONValue[] | undefined;\n readonly steps?: readonly ThreadStep[] | undefined;\n readonly timing?: MessageTiming | undefined;\n readonly submittedFeedback?: { readonly type: \"positive\" | \"negative\" };\n readonly isOptimistic?: boolean | undefined;\n readonly modality?: MessageModality | undefined;\n readonly custom?: Record<string, unknown> | undefined;\n }\n | undefined;\n};\n\nconst convertDataPrefixedPart = (\n type: string,\n data: unknown,\n): DataMessagePart | undefined => {\n if (!type.startsWith(\"data-\")) return undefined;\n return { type: \"data\", name: type.substring(5), data };\n};\n\n/**\n * @deprecated This API is experimental and may change without notice.\n */\nexport const fromThreadMessageLike = (\n like: ThreadMessageLike,\n fallbackId: string,\n fallbackStatus: MessageStatus,\n): ThreadMessage => {\n const { role, id, createdAt, attachments, status, metadata } = like;\n const common = {\n id: id ?? fallbackId,\n createdAt: createdAt ?? new Date(),\n };\n\n const content =\n typeof like.content === \"string\"\n ? [{ type: \"text\" as const, text: like.content }]\n : like.content;\n\n const sanitizeImageContent = ({\n image,\n ...rest\n }: ImageMessagePart): ImageMessagePart | null => {\n if (typeof image !== \"string\") return null;\n if (parseDataUrl(image)?.mimeType.startsWith(\"image/\")) {\n return { ...rest, image };\n }\n if (/^(https:\\/\\/|blob:)/i.test(image)) {\n return { ...rest, image };\n }\n console.warn(`Invalid image data format detected`);\n return null;\n };\n\n if (role !== \"user\" && attachments?.length)\n throw new Error(\"attachments are only supported for user messages\");\n\n if (role !== \"assistant\" && status)\n throw new Error(\"status is only supported for assistant messages\");\n\n if (role !== \"assistant\" && metadata?.steps)\n throw new Error(\"metadata.steps is only supported for assistant messages\");\n\n switch (role) {\n case \"assistant\":\n return {\n ...common,\n role,\n content: content\n .map((part): ThreadAssistantMessagePart | null => {\n const type = part.type;\n switch (type) {\n case \"text\":\n if (!part.text?.trim()) return null;\n return part;\n\n case \"reasoning\":\n if (!part.text?.trim() && !part.unstable_summary?.trim())\n return null;\n return part;\n\n case \"file\":\n case \"source\":\n return part;\n\n case \"image\":\n return sanitizeImageContent(part);\n\n case \"data\":\n return part;\n\n case \"generative-ui\":\n return part;\n\n case \"tool-call\": {\n const { parentId, messages, ...basePart } = part;\n const commonProps = {\n ...basePart,\n toolCallId: part.toolCallId || `tool-${generateId()}`,\n ...(parentId !== undefined && { parentId }),\n ...(messages !== undefined && { messages }),\n };\n\n if (part.args) {\n return {\n ...commonProps,\n args: part.args,\n argsText: part.argsText ?? JSON.stringify(part.args),\n };\n }\n return {\n ...commonProps,\n args: parsePartialJsonObject(part.argsText ?? \"\") ?? {},\n argsText: part.argsText ?? \"\",\n };\n }\n\n default: {\n const converted = convertDataPrefixedPart(\n type,\n (part as DataPrefixedPart).data,\n );\n if (converted) return converted;\n throw new Error(\n `Unsupported assistant message part type: ${type}`,\n );\n }\n }\n })\n .filter((c) => !!c),\n status: status ?? fallbackStatus,\n metadata: {\n unstable_state: metadata?.unstable_state ?? null,\n unstable_annotations: metadata?.unstable_annotations ?? [],\n unstable_data: metadata?.unstable_data ?? [],\n custom: metadata?.custom ?? {},\n steps: metadata?.steps ?? [],\n ...(metadata?.timing && { timing: metadata.timing }),\n ...(metadata?.submittedFeedback && {\n submittedFeedback: metadata.submittedFeedback,\n }),\n ...(metadata?.isOptimistic && { isOptimistic: true }),\n ...(metadata?.modality && { modality: metadata.modality }),\n },\n } satisfies ThreadAssistantMessage;\n\n case \"user\":\n return {\n ...common,\n role,\n content: content.map((part): ThreadUserMessagePart => {\n const type = part.type;\n switch (type) {\n case \"text\":\n case \"image\":\n case \"audio\":\n case \"file\":\n case \"data\":\n return part;\n\n default: {\n const converted = convertDataPrefixedPart(\n type,\n (part as DataPrefixedPart).data,\n );\n if (converted) return converted;\n throw new Error(`Unsupported user message part type: ${type}`);\n }\n }\n }),\n attachments: (attachments ?? []).map((att) => ({\n ...att,\n content: att.content.map((part): ThreadUserMessagePart => {\n const converted = convertDataPrefixedPart(\n part.type,\n (part as DataPrefixedPart).data,\n );\n return converted ?? (part as ThreadUserMessagePart);\n }),\n })),\n metadata: {\n custom: metadata?.custom ?? {},\n ...(metadata?.isOptimistic && { isOptimistic: true }),\n ...(metadata?.modality && { modality: metadata.modality }),\n },\n } satisfies ThreadUserMessage;\n\n case \"system\":\n if (content.length !== 1 || content[0]!.type !== \"text\")\n throw new Error(\n \"System messages must have exactly one text message part.\",\n );\n\n return {\n ...common,\n role,\n content: content as [TextMessagePart],\n metadata: {\n custom: metadata?.custom ?? {},\n },\n } satisfies ThreadSystemMessage;\n\n default: {\n const unsupportedRole: never = role;\n throw new Error(`Unknown message role: ${unsupportedRole}`);\n }\n }\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"external-store-thread-runtime-core.d.ts","names":[],"sources":["../../../src/runtimes/external-store/external-store-thread-runtime-core.ts"],"mappings":";;;;;;;;;;;;;;;;cAyEa,qBACX,oBACA,mBAAmB;cAKR,uCACH,iCACG;UAEH;MAiBG,gBAAY;UAIf;EACD;EACA;MACI;MAIA;YAKQ,6BAA6B;MAI5B,sEAAK,sDAAA;MAId;kBARqC;;;;;;;MAarC,iCAAiC;EAKrC,sBAAsB;EACtB;UAEC;UAUA;UAKA;UAEA;UAEA;EAED,mCACL,kBAAkB;UAKZ;;;;;UAMA;UACA;UACA;UACA;UACA;;;;;;;;UASA;UAaA;UAQA;UASA;EAIQ,UAAU;EAQxB,YAAA,iBAAiB,sBACjB,OAAO;EAMF,sBAAsB,OAAO;UAM5B;;;;;;;UA4PA;;;;;UAoFA;UACA;;;;;;;UAQA;EAWQ,eAAe;;;;;;;;;UA8BvB;EAaK,OAAO,YAAY,gBAAgB;EAqEnC,cAAc,oBAAoB;UA0CvC;EAkBD,0BA5DwC;EAkExC,+BANa;EAUb,cAAc,qBAAqB,WAAW;EAI9C,gBAAgB;EAIV,SAAS,QAAQ,iBAAiB;EAclC,UAAU,QAAQ,kBAAkB;EAO1C;EAOA,oBAAoB;;;;;;EAqBpB;EAKA;UAmFC;EAOD,cAAc,SAAS;EASvB,eAAe,SAAS;EAoBxB,sBACL,SAAS,+BACR;EAUa,MAAM,2BAA2B;EAMjC,OAAO,MAAM;UAQrB"}
1
+ {"version":3,"file":"external-store-thread-runtime-core.d.ts","names":[],"sources":["../../../src/runtimes/external-store/external-store-thread-runtime-core.ts"],"mappings":";;;;;;;;;;;;;;;;cAyEa,qBACX,oBACA,mBAAmB;cAKR,uCACH,iCACG;UAEH;MAiBG,gBAAY;UAIf;EACD;EACA;MACI;MAIA;YAKQ,6BAA6B;MAI5B,sEAAK,sDAAA;MAId;kBARqC;;;;;;;MAarC,iCAAiC;EAKrC,sBAAsB;EACtB;UAEC;UAUA;UAKA;UAEA;UAEA;EAED,mCACL,kBAAkB;UAKZ;;;;;UAMA;UACA;UACA;UACA;UACA;;;;;;;;UASA;UAaA;UAQA;UASA;EAIQ,UAAU;EAQxB,YAAA,iBAAiB,sBACjB,OAAO;EAMF,sBAAsB,OAAO;UAM5B;;;;;;;UA4PA;;;;;UAoFA;UACA;;;;;;;UAQA;EAWQ,eAAe;;;;;;;;;UA8BvB;EAaK,OAAO,YAAY,gBAAgB;EAuEnC,cAAc,oBAAoB;UA0CvC;EAkBD,0BA5DwC;EAkExC,+BANa;EAUb,cAAc,qBAAqB,WAAW;EAI9C,gBAAgB;EAIV,SAAS,QAAQ,iBAAiB;EAgBlC,UAAU,QAAQ,kBAAkB;EAS1C;EAOA,oBAAoB;;;;;;EAqBpB;EAKA;UAmFC;EAOD,cAAc,SAAS;EASvB,eAAe,SAAS;EAoBxB,sBACL,SAAS,+BACR;EAkCa,MAAM,2BAA2B;EAMjC,OAAO,MAAM;UAQrB"}
@@ -382,6 +382,7 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
382
382
  });
383
383
  }
384
384
  async append(rawMessage) {
385
+ if (this._isVoiceMessage(rawMessage.sourceId)) throw new Error("Voice transcript messages cannot be edited");
385
386
  const isEdit = rawMessage.sourceId != null || rawMessage.parentId !== (this.messages.at(-1)?.id ?? null);
386
387
  const message = !isEdit && this._store.queue && this._store.queue === this._transformedQueue ? rawMessage : this.enrichAppendMetadata(rawMessage);
387
388
  const generation = captureThreadRuntimeGeneration(this);
@@ -447,12 +448,14 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
447
448
  }
448
449
  async startRun(config) {
449
450
  if (!this._store.onReload) throw new Error("Runtime does not support reloading messages.");
451
+ if (this._isVoiceMessage(config.sourceId)) throw new Error("Voice transcript messages cannot be reloaded");
450
452
  this._pendingDeleteEvictions.clear();
451
453
  await this._toolInvocations?.abort({ discardPending: true });
452
454
  await this._store.onReload(config.parentId, config);
453
455
  }
454
456
  async resumeRun(config) {
455
457
  if (!this._store.onResume) throw new Error("Runtime does not support resuming runs.");
458
+ if (this._isVoiceMessage(config.sourceId)) throw new Error("Voice transcript messages cannot be reloaded");
456
459
  await this._store.onResume(config);
457
460
  }
458
461
  exportExternalState() {
@@ -528,8 +531,12 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
528
531
  }
529
532
  respondToToolApproval(options) {
530
533
  if (!this._store.onRespondToToolApproval) throw new Error("Runtime does not support tool approvals.");
534
+ const message = this.messages.findLast((candidate) => candidate.role === "assistant" && candidate.content.some((part) => part.type === "tool-call" && part.approval?.id === options.approvalId));
535
+ const toolCall = message?.content.find((part) => part.type === "tool-call" && part.approval?.id === options.approvalId);
531
536
  try {
532
- return Promise.resolve(this._store.onRespondToToolApproval(options));
537
+ return Promise.resolve(this._store.onRespondToToolApproval(options)).then(() => {
538
+ if (message && toolCall?.type === "tool-call") this._notifyToolApprovalAnswered(message.id, toolCall.toolCallId, toolCall.toolName, options.approved);
539
+ });
533
540
  } catch (error) {
534
541
  return Promise.reject(error);
535
542
  }