@assistant-ui/core 0.3.17 → 0.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/dist/adapters/attachment.d.ts.map +1 -1
  2. package/dist/adapters/attachment.js +5 -4
  3. package/dist/adapters/attachment.js.map +1 -1
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal.d.ts +3 -2
  7. package/dist/internal.js +3 -2
  8. package/dist/model-context/frame/host.d.ts.map +1 -1
  9. package/dist/model-context/frame/host.js +10 -4
  10. package/dist/model-context/frame/host.js.map +1 -1
  11. package/dist/model-context/frame/provider.d.ts +2 -0
  12. package/dist/model-context/frame/provider.d.ts.map +1 -1
  13. package/dist/model-context/frame/provider.js +30 -7
  14. package/dist/model-context/frame/provider.js.map +1 -1
  15. package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
  16. package/dist/model-context/interactable-composer-metadata.js +16 -9
  17. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  18. package/dist/model-context/registry.d.ts.map +1 -1
  19. package/dist/model-context/registry.js +3 -5
  20. package/dist/model-context/registry.js.map +1 -1
  21. package/dist/model-context/types.d.ts.map +1 -1
  22. package/dist/model-context/types.js +5 -4
  23. package/dist/model-context/types.js.map +1 -1
  24. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  25. package/dist/react/client/InMemoryThreadList.js +62 -56
  26. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  27. package/dist/react/client/Interactables.d.ts.map +1 -1
  28. package/dist/react/client/Interactables.js +24 -27
  29. package/dist/react/client/Interactables.js.map +1 -1
  30. package/dist/react/client/RemoteThreadList.d.ts +1 -0
  31. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  32. package/dist/react/client/RemoteThreadList.js +151 -141
  33. package/dist/react/client/RemoteThreadList.js.map +1 -1
  34. package/dist/react/client/Tools.d.ts.map +1 -1
  35. package/dist/react/client/Tools.js +18 -16
  36. package/dist/react/client/Tools.js.map +1 -1
  37. package/dist/react/interactables-legacy/Interactables.js +242 -121
  38. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  39. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
  40. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +11 -9
  41. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
  42. package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
  43. package/dist/react/model-context/makeAssistantTool.js +1 -1
  44. package/dist/react/model-context/makeAssistantTool.js.map +1 -1
  45. package/dist/react/model-context/useAssistantTool.d.ts +2 -2
  46. package/dist/react/model-context/useAssistantTool.js +1 -1
  47. package/dist/react/model-context/useAssistantTool.js.map +1 -1
  48. package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
  49. package/dist/react/model-context/useToolArgsStatus.js +2 -1
  50. package/dist/react/model-context/useToolArgsStatus.js.map +1 -1
  51. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  52. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +16 -8
  53. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  54. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +5 -1
  55. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  56. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +103 -103
  57. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  58. package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -0
  59. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  60. package/dist/react/runtimes/RemoteThreadResource.js +13 -11
  61. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  62. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +4 -1
  63. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  64. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +401 -34
  65. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  66. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
  67. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +2 -1
  68. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -1
  69. package/dist/react/runtimes/useExternalStoreRuntime.js +75 -46
  70. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  71. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  72. package/dist/react/runtimes/useRemoteThreadListRuntime.js +63 -23
  73. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  74. package/dist/runtime/api/thread-list-item-runtime.d.ts +7 -3
  75. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  76. package/dist/runtime/api/thread-list-item-runtime.js +2 -2
  77. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  78. package/dist/runtime/api/thread-list-runtime.d.ts +3 -0
  79. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  80. package/dist/runtime/api/thread-list-runtime.js +18 -14
  81. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  82. package/dist/runtime/api/thread-runtime.d.ts +4 -0
  83. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  84. package/dist/runtime/api/thread-runtime.js +1 -1
  85. package/dist/runtime/api/thread-runtime.js.map +1 -1
  86. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  87. package/dist/runtime/base/base-composer-runtime-core.js +16 -12
  88. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  89. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  90. package/dist/runtime/base/base-thread-runtime-core.js +6 -5
  91. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  92. package/dist/runtime/interfaces/composer-runtime-core.d.ts +12 -5
  93. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  94. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +4 -1
  95. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  96. package/dist/runtime/utils/external-message-conversion.js +1 -1
  97. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  98. package/dist/runtime/utils/message-repository.d.ts +1 -0
  99. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  100. package/dist/runtime/utils/message-repository.js +15 -6
  101. package/dist/runtime/utils/message-repository.js.map +1 -1
  102. package/dist/runtime/utils/thread-message-like.js +1 -1
  103. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  104. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  105. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +21 -27
  106. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  107. package/dist/runtimes/internal.d.ts +2 -2
  108. package/dist/runtimes/internal.js +2 -2
  109. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +18 -1
  110. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  111. package/dist/runtimes/remote-thread-list/remote-thread-state.js +130 -42
  112. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  113. package/dist/runtimes/remote-thread-list/title-generation.d.ts +49 -0
  114. package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -0
  115. package/dist/runtimes/remote-thread-list/title-generation.js +197 -0
  116. package/dist/runtimes/remote-thread-list/title-generation.js.map +1 -0
  117. package/dist/runtimes/remote-thread-list/title.d.ts +3 -1
  118. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -1
  119. package/dist/runtimes/remote-thread-list/title.js +9 -1
  120. package/dist/runtimes/remote-thread-list/title.js.map +1 -1
  121. package/dist/runtimes/remote-thread-list/types.d.ts +10 -0
  122. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  123. package/dist/store/clients/external-thread.d.ts.map +1 -1
  124. package/dist/store/clients/external-thread.js +19 -3
  125. package/dist/store/clients/external-thread.js.map +1 -1
  126. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  127. package/dist/store/clients/model-context-client.js +2 -1
  128. package/dist/store/clients/model-context-client.js.map +1 -1
  129. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  130. package/dist/store/clients/single-thread-list.js +1 -0
  131. package/dist/store/clients/single-thread-list.js.map +1 -1
  132. package/dist/store/clients/suggestions.d.ts.map +1 -1
  133. package/dist/store/clients/suggestions.js +5 -4
  134. package/dist/store/clients/suggestions.js.map +1 -1
  135. package/dist/store/runtime-clients/composer-runtime-client.d.ts +1 -0
  136. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  137. package/dist/store/runtime-clients/composer-runtime-client.js +97 -61
  138. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  139. package/dist/store/runtime-clients/message-runtime-client.d.ts +1 -0
  140. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  141. package/dist/store/runtime-clients/message-runtime-client.js +221 -144
  142. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  143. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  144. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  145. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  146. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  147. package/dist/store/runtime-clients/thread-list-runtime-client.js +54 -52
  148. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  149. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  150. package/dist/store/runtime-clients/thread-runtime-client.js +193 -121
  151. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  152. package/dist/store/scopes/composer.d.ts +10 -2
  153. package/dist/store/scopes/composer.d.ts.map +1 -1
  154. package/dist/store/scopes/message.d.ts +30 -1
  155. package/dist/store/scopes/message.d.ts.map +1 -1
  156. package/dist/store/scopes/thread-list-item.d.ts +2 -2
  157. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  158. package/dist/store/scopes/thread.d.ts +8 -0
  159. package/dist/store/scopes/thread.d.ts.map +1 -1
  160. package/dist/store/scopes/threads.d.ts +1 -0
  161. package/dist/store/scopes/threads.d.ts.map +1 -1
  162. package/dist/types/attachment.d.ts +2 -1
  163. package/dist/types/attachment.d.ts.map +1 -1
  164. package/dist/types/attachment.js +2 -1
  165. package/dist/types/attachment.js.map +1 -1
  166. package/dist/utils/abortable-iterable.d.ts +19 -0
  167. package/dist/utils/abortable-iterable.d.ts.map +1 -0
  168. package/dist/utils/abortable-iterable.js +72 -0
  169. package/dist/utils/abortable-iterable.js.map +1 -0
  170. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  171. package/dist/utils/composite-context-provider.js +20 -2
  172. package/dist/utils/composite-context-provider.js.map +1 -1
  173. package/dist/utils/record.d.ts +11 -0
  174. package/dist/utils/record.d.ts.map +1 -0
  175. package/dist/utils/record.js +12 -0
  176. package/dist/utils/record.js.map +1 -0
  177. package/package.json +10 -10
  178. package/src/adapters/attachment.test.ts +50 -1
  179. package/src/adapters/attachment.ts +7 -4
  180. package/src/index.ts +1 -0
  181. package/src/internal.ts +4 -0
  182. package/src/model-context/frame/host.test.ts +24 -0
  183. package/src/model-context/frame/host.ts +10 -4
  184. package/src/model-context/frame/provider.test.ts +71 -0
  185. package/src/model-context/frame/provider.ts +37 -7
  186. package/src/model-context/interactable-composer-metadata.test.ts +28 -0
  187. package/src/model-context/interactable-composer-metadata.ts +9 -3
  188. package/src/model-context/registry.test.ts +12 -0
  189. package/src/model-context/registry.ts +3 -2
  190. package/src/model-context/types.test.ts +25 -0
  191. package/src/model-context/types.ts +9 -4
  192. package/src/react/RuntimeAdapter.test.ts +34 -0
  193. package/src/react/client/InMemoryThreadList.ts +1 -0
  194. package/src/react/client/Interactables.test.ts +28 -0
  195. package/src/react/client/Interactables.ts +15 -13
  196. package/src/react/client/RemoteThreadList.background.test.ts +148 -6
  197. package/src/react/client/RemoteThreadList.concurrent.test.tsx +42 -1
  198. package/src/react/client/RemoteThreadList.test.ts +345 -3
  199. package/src/react/client/RemoteThreadList.ts +166 -110
  200. package/src/react/client/Tools.ts +22 -11
  201. package/src/react/interactables-legacy/Interactables.ts +16 -16
  202. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
  203. package/src/react/model-context/makeAssistantTool.ts +2 -2
  204. package/src/react/model-context/useAssistantTool.ts +2 -2
  205. package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
  206. package/src/react/model-context/useToolArgsStatus.ts +2 -1
  207. package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
  208. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +33 -0
  209. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
  210. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +28 -5
  211. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
  212. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +105 -0
  213. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +136 -111
  214. package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
  215. package/src/react/runtimes/RemoteThreadResource.ts +19 -12
  216. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +483 -6
  217. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +504 -68
  218. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +15 -0
  219. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +1 -0
  220. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +41 -0
  221. package/src/react/runtimes/useExternalStoreRuntime.ts +10 -4
  222. package/src/react/runtimes/useLocalRuntime.test.tsx +20 -0
  223. package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
  224. package/src/react/runtimes/useRemoteThreadListRuntime.ts +33 -0
  225. package/src/runtime/api/message-runtime.test.ts +75 -0
  226. package/src/runtime/api/thread-list-item-runtime.ts +10 -3
  227. package/src/runtime/api/thread-list-runtime.ts +21 -11
  228. package/src/runtime/api/thread-runtime.test.ts +78 -0
  229. package/src/runtime/api/thread-runtime.ts +8 -1
  230. package/src/runtime/base/base-composer-runtime-core.ts +36 -8
  231. package/src/runtime/base/base-thread-runtime-core.test.ts +55 -1
  232. package/src/runtime/base/base-thread-runtime-core.ts +6 -4
  233. package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
  234. package/src/runtime/interfaces/thread-list-runtime-core.ts +5 -1
  235. package/src/runtime/utils/external-message-conversion.test.ts +68 -0
  236. package/src/runtime/utils/external-message-conversion.ts +1 -1
  237. package/src/runtime/utils/message-repository.ts +29 -17
  238. package/src/runtime/utils/thread-message-like.ts +1 -1
  239. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +18 -13
  240. package/src/runtimes/internal.ts +1 -0
  241. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +128 -2
  242. package/src/runtimes/remote-thread-list/remote-thread-state.ts +182 -42
  243. package/src/runtimes/remote-thread-list/title-generation.test.ts +602 -0
  244. package/src/runtimes/remote-thread-list/title-generation.ts +329 -0
  245. package/src/runtimes/remote-thread-list/title.test.ts +62 -0
  246. package/src/runtimes/remote-thread-list/title.ts +13 -0
  247. package/src/runtimes/remote-thread-list/types.ts +10 -0
  248. package/src/store/clients/external-thread.ts +48 -3
  249. package/src/store/clients/model-context-client.ts +5 -1
  250. package/src/store/clients/single-thread-list.ts +1 -0
  251. package/src/store/clients/suggestions.test.ts +40 -0
  252. package/src/store/clients/suggestions.ts +13 -10
  253. package/src/store/runtime-clients/composer-runtime-client.ts +42 -9
  254. package/src/store/runtime-clients/message-runtime-client.ts +54 -6
  255. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +4 -2
  256. package/src/store/runtime-clients/thread-list-runtime-client.ts +1 -0
  257. package/src/store/runtime-clients/thread-runtime-client.ts +58 -10
  258. package/src/store/scopes/composer.ts +16 -4
  259. package/src/store/scopes/message.ts +14 -0
  260. package/src/store/scopes/thread-list-item.ts +5 -2
  261. package/src/store/scopes/thread.ts +4 -0
  262. package/src/store/scopes/threads.ts +1 -0
  263. package/src/tests/MessageRepository.test.ts +116 -0
  264. package/src/tests/OptimisticState-list-race.test.ts +1 -0
  265. package/src/tests/RemoteThreadListThreadListRuntimeCore-initialize-adapter-change.test.ts +38 -0
  266. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +47 -0
  267. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
  268. package/src/tests/attachment-adapters.test.ts +14 -0
  269. package/src/tests/base-composer-runtime-core.test.ts +39 -0
  270. package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
  271. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +27 -18
  272. package/src/tests/external-thread-attachments.test.tsx +139 -0
  273. package/src/tests/external-thread-parity.test.tsx +23 -0
  274. package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
  275. package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
  276. package/src/tests/thread-switch-events.test.tsx +78 -0
  277. package/src/tests/tools-scope-migration.test.tsx +28 -0
  278. package/src/types/attachment.ts +4 -0
  279. package/src/utils/abortable-iterable.test.ts +213 -0
  280. package/src/utils/abortable-iterable.ts +93 -0
  281. package/src/utils/composite-context-provider.test.ts +25 -0
  282. package/src/utils/composite-context-provider.ts +23 -2
  283. package/src/utils/record.test.ts +48 -0
  284. package/src/utils/record.ts +11 -0
@@ -43,6 +43,7 @@ type ClassifyAccumulator = {
43
43
  declare const classifyThreads: (threads: readonly RemoteThreadMetadata[], acc: ClassifyAccumulator) => ClassifyAccumulator;
44
44
  type RemoteThreadState = {
45
45
  readonly isLoading: boolean;
46
+ readonly loadError: unknown;
46
47
  readonly isLoadingMore: boolean;
47
48
  readonly cursor: string | undefined;
48
49
  readonly newThreadId: string | undefined;
@@ -59,7 +60,23 @@ declare const seedNewThread: (state: RemoteThreadState) => {
59
60
  declare const preserveMidLoadTransitions: (state: RemoteThreadState, priorOrder: Pick<RemoteThreadState, "threadIds" | "archivedThreadIds">, statusAtRequest: ReadonlyMap<string, RemoteThreadData["status"]>) => RemoteThreadState;
60
61
  declare const statusSnapshot: (state: RemoteThreadState) => ReadonlyMap<string, RemoteThreadData["status"]>;
61
62
  declare const getThreadData: (state: RemoteThreadState, threadIdOrRemoteId: string) => RemoteThreadData | undefined;
63
+ declare const reconcileInitializedThread: (state: RemoteThreadState, threadId: string, remoteId: string, externalId: string | undefined, retainedThreadId?: string) => {
64
+ state: RemoteThreadState;
65
+ removedMappingId: THREAD_MAPPING_ID | undefined;
66
+ survivorMappingId: THREAD_MAPPING_ID;
67
+ };
68
+ /**
69
+ * Transitions a thread that already exists remotely. Promoting the local draft
70
+ * off `new` needs the initialization promise its destination variant requires,
71
+ * so that goes through `promoteNewThreadReducer`; asking for it here leaves the
72
+ * state unchanged.
73
+ */
62
74
  declare const updateStatusReducer: (state: RemoteThreadState, threadIdOrRemoteId: string, newStatus: "regular" | "archived" | "deleted") => RemoteThreadState;
75
+ /**
76
+ * Promotes the local draft to `regular`, carrying the initialization promise
77
+ * onto the slot.
78
+ */
79
+ declare const promoteNewThreadReducer: (state: RemoteThreadState, threadIdOrRemoteId: string, initializeTask: Promise<RemoteThreadInitializeResponse>) => RemoteThreadState;
63
80
  //#endregion
64
- export { ClassifyAccumulator, LOCAL_THREAD_ID_PREFIX, RemoteThreadData, RemoteThreadState, THREAD_MAPPING_ID, classifyThreads, createEmptyRemoteThreadState, createThreadMappingId, getThreadData, normalizeCursor, preserveMidLoadTransitions, seedNewThread, statusSnapshot, updateStatusReducer };
81
+ export { ClassifyAccumulator, LOCAL_THREAD_ID_PREFIX, RemoteThreadData, RemoteThreadState, THREAD_MAPPING_ID, classifyThreads, createEmptyRemoteThreadState, createThreadMappingId, getThreadData, normalizeCursor, preserveMidLoadTransitions, promoteNewThreadReducer, reconcileInitializedThread, seedNewThread, statusSnapshot, updateStatusReducer };
65
82
  //# sourceMappingURL=remote-thread-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remote-thread-state.d.ts","names":[],"sources":["../../../src/runtimes/remote-thread-list/remote-thread-state.ts"],"mappings":";;KAMY;WAEG;WACA;WACA;WACA;WACA;WACA;WACA;;WAGA;WACA,gBAAgB,QAAQ;WACxB;WACA;WACA;WACA;WACA;WACA;;WAGA;WACA,gBAAgB,QAAQ;WACxB;WACA;WACA;WACA;WACA,gBAAgB;WAChB,SAAS;WACT;;KAGH;EAA+B;;iBAE3B,sBAAsB,aAAa;cAItC;cAEA,kBAAmB;KAGpB;EACV;EACA;EACA,aAAa,eAAe;EAC5B,YAAY,OAAO,mBAAmB;;cAS3B,kBACX,kBAAkB,wBAClB,KAAK,wBACJ;KAiES;WACD;WACA;WACA;WACA;WACA;WACA;WACA,aAAa,SAAS,eAAe;WACrC,YAAY,SAAS,OAAO,mBAAmB;;cAG7C,oCAAmC;cAWnC,gBACX,OAAO;EACJ;EAAY,OAAO;;cA2CX,6BACX,OAAO,mBACP,YAAY,KAAK,uDACjB,iBAAiB,oBAAoB,gCACpC;cAoDU,iBACX,OAAO,sBACN,oBAAoB;cAGV,gBACX,OAAO,mBACP,+BAA0B;cAOf,sBACX,OAAO,mBACP,4BACA,kDAA6C"}
1
+ {"version":3,"file":"remote-thread-state.d.ts","names":[],"sources":["../../../src/runtimes/remote-thread-list/remote-thread-state.ts"],"mappings":";;KAOY;WAEG;WACA;WACA;WACA;WACA;WACA;WACA;;WAGA;WACA,gBAAgB,QAAQ;WACxB;WACA;WACA;WACA;WACA;WACA;;WAGA;WACA,gBAAgB,QAAQ;WACxB;WACA;WACA;WACA;WACA,gBAAgB;WAChB,SAAS;WACT;;KAGH;EAA+B;;iBAE3B,sBAAsB,aAAa;cAItC;cAEA,kBAAmB;KAGpB;EACV;EACA;EACA,aAAa,eAAe;EAC5B,YAAY,OAAO,mBAAmB;;cAS3B,kBACX,kBAAkB,wBAClB,KAAK,wBACJ;KAqES;WACD;WACA;WACA;WACA;WACA;WACA;WACA;WACA,aAAa,SAAS,eAAe;WACrC,YAAY,SAAS,OAAO,mBAAmB;;cAG7C,oCAAmC;cAYnC,gBACX,OAAO;EACJ;EAAY,OAAO;;cAyCX,6BACX,OAAO,mBACP,YAAY,KAAK,uDACjB,iBAAiB,oBAAoB,gCACpC;cAoDU,iBACX,OAAO,sBACN,oBAAoB;cAGV,gBACX,OAAO,mBACP,+BAA0B;cAWf,6BACX,OAAO,mBACP,kBACA,kBACA,gCACA;EAEA,OAAO;EACP,kBAAkB;EAClB,mBAAmB;;;;;;;;cAuLR,sBACX,OAAO,mBACP,4BACA,kDAA6C;;;;;cAOlC,0BACX,OAAO,mBACP,4BACA,gBAAgB,QAAQ,oCAA+B"}
@@ -1,3 +1,4 @@
1
+ import { nullProtoRecord } from "../../utils/record.js";
1
2
  import { generateId } from "../../utils/id.js";
2
3
  //#region src/runtimes/remote-thread-list/remote-thread-state.ts
3
4
  function createThreadMappingId(id) {
@@ -6,7 +7,11 @@ function createThreadMappingId(id) {
6
7
  const LOCAL_THREAD_ID_PREFIX = "__LOCALID_";
7
8
  const normalizeCursor = (c) => c || void 0;
8
9
  const classifyThreads = (threads, acc) => {
9
- const listed = /* @__PURE__ */ new Set([...acc.threadIds, ...acc.archivedThreadIds]);
10
+ let threadIds = [...acc.threadIds];
11
+ let archivedThreadIds = [...acc.archivedThreadIds];
12
+ const threadIdMap = nullProtoRecord(acc.threadIdMap);
13
+ const threadData = nullProtoRecord(acc.threadData);
14
+ const listed = /* @__PURE__ */ new Set([...threadIds, ...archivedThreadIds]);
10
15
  for (const thread of threads) {
11
16
  switch (thread.status) {
12
17
  case "regular":
@@ -16,27 +21,27 @@ const classifyThreads = (threads, acc) => {
16
21
  throw new Error(`Unsupported state: ${_exhaustiveCheck}`);
17
22
  }
18
23
  }
19
- const existingMappingId = acc.threadIdMap[thread.remoteId];
20
- const existing = existingMappingId !== void 0 ? acc.threadData[existingMappingId] : void 0;
24
+ const existingMappingId = threadIdMap[thread.remoteId];
25
+ const existing = existingMappingId !== void 0 ? threadData[existingMappingId] : void 0;
21
26
  const id = existing?.id ?? thread.remoteId;
22
27
  const mappingId = existingMappingId ?? createThreadMappingId(id);
23
28
  const existingTask = existing !== void 0 && existing.status !== "new" ? existing.initializeTask : void 0;
24
29
  if (!listed.has(id)) {
25
30
  listed.add(id);
26
- if (thread.status === "regular") acc.threadIds.push(id);
27
- else acc.archivedThreadIds.push(id);
31
+ if (thread.status === "regular") threadIds.push(id);
32
+ else archivedThreadIds.push(id);
28
33
  } else if (existing !== void 0 && existing.status !== thread.status) {
29
34
  if (thread.status === "regular") {
30
- acc.archivedThreadIds = acc.archivedThreadIds.filter((t) => t !== id);
31
- acc.threadIds.push(id);
35
+ archivedThreadIds = archivedThreadIds.filter((t) => t !== id);
36
+ threadIds.push(id);
32
37
  } else {
33
- acc.threadIds = acc.threadIds.filter((t) => t !== id);
34
- acc.archivedThreadIds.push(id);
38
+ threadIds = threadIds.filter((t) => t !== id);
39
+ archivedThreadIds.push(id);
35
40
  }
36
41
  }
37
- acc.threadIdMap[id] = mappingId;
38
- acc.threadIdMap[thread.remoteId] = mappingId;
39
- acc.threadData[mappingId] = {
42
+ threadIdMap[id] = mappingId;
43
+ threadIdMap[thread.remoteId] = mappingId;
44
+ threadData[mappingId] = {
40
45
  ...existing?.localOrigin === true ? { localOrigin: true } : {},
41
46
  id,
42
47
  remoteId: thread.remoteId,
@@ -51,17 +56,23 @@ const classifyThreads = (threads, acc) => {
51
56
  })
52
57
  };
53
58
  }
54
- return acc;
59
+ return {
60
+ threadIds,
61
+ archivedThreadIds,
62
+ threadIdMap,
63
+ threadData
64
+ };
55
65
  };
56
66
  const createEmptyRemoteThreadState = () => ({
57
67
  isLoading: true,
68
+ loadError: void 0,
58
69
  isLoadingMore: false,
59
70
  cursor: void 0,
60
71
  newThreadId: void 0,
61
72
  threadIds: [],
62
73
  archivedThreadIds: [],
63
- threadIdMap: {},
64
- threadData: {}
74
+ threadIdMap: nullProtoRecord(),
75
+ threadData: nullProtoRecord()
65
76
  });
66
77
  const seedNewThread = (state) => {
67
78
  let id;
@@ -74,22 +85,16 @@ const seedNewThread = (state) => {
74
85
  state: {
75
86
  ...state,
76
87
  newThreadId: id,
77
- threadIdMap: {
78
- ...state.threadIdMap,
79
- [id]: mappingId
80
- },
81
- threadData: {
82
- ...state.threadData,
83
- [mappingId]: {
84
- status: "new",
85
- id,
86
- remoteId: void 0,
87
- externalId: void 0,
88
- title: void 0,
89
- custom: void 0,
90
- localOrigin: true
91
- }
92
- }
88
+ threadIdMap: nullProtoRecord(state.threadIdMap, { [id]: mappingId }),
89
+ threadData: nullProtoRecord(state.threadData, { [mappingId]: {
90
+ status: "new",
91
+ id,
92
+ remoteId: void 0,
93
+ externalId: void 0,
94
+ title: void 0,
95
+ custom: void 0,
96
+ localOrigin: true
97
+ } })
93
98
  }
94
99
  };
95
100
  };
@@ -128,15 +133,85 @@ const preserveMidLoadTransitions = (state, priorOrder, statusAtRequest) => {
128
133
  };
129
134
  const statusSnapshot = (state) => new Map(Object.values(state.threadData).map((d) => [d.id, d.status]));
130
135
  const getThreadData = (state, threadIdOrRemoteId) => {
131
- const idx = state.threadIdMap[threadIdOrRemoteId];
136
+ const idx = Object.hasOwn(state.threadIdMap, threadIdOrRemoteId) ? state.threadIdMap[threadIdOrRemoteId] : void 0;
132
137
  if (idx === void 0) return void 0;
133
- return state.threadData[idx];
138
+ return Object.hasOwn(state.threadData, idx) ? state.threadData[idx] : void 0;
134
139
  };
135
- const updateStatusReducer = (state, threadIdOrRemoteId, newStatus) => {
140
+ const reconcileInitializedThread = (state, threadId, remoteId, externalId, retainedThreadId) => {
141
+ const mappingId = createThreadMappingId(threadId);
142
+ const data = Object.hasOwn(state.threadData, mappingId) ? state.threadData[mappingId] : void 0;
143
+ if (!data) return {
144
+ state,
145
+ removedMappingId: void 0,
146
+ survivorMappingId: mappingId
147
+ };
148
+ const listedMappingId = Object.hasOwn(state.threadIdMap, remoteId) ? state.threadIdMap[remoteId] : void 0;
149
+ const listedData = listedMappingId !== void 0 && listedMappingId !== mappingId && Object.hasOwn(state.threadData, listedMappingId) ? state.threadData[listedMappingId] : void 0;
150
+ const listedSlot = listedMappingId !== void 0 && listedData !== void 0 ? {
151
+ mappingId: listedMappingId,
152
+ data: listedData
153
+ } : void 0;
154
+ const retainedMappingId = retainedThreadId !== void 0 && Object.hasOwn(state.threadIdMap, retainedThreadId) ? state.threadIdMap[retainedThreadId] : void 0;
155
+ const survivorMappingId = listedSlot !== void 0 && retainedMappingId === listedSlot.mappingId ? listedSlot.mappingId : mappingId;
156
+ const removedMappingId = listedSlot === void 0 ? void 0 : survivorMappingId === mappingId ? listedSlot.mappingId : mappingId;
157
+ const resolvedExternalId = externalId ?? listedSlot?.data.externalId;
158
+ const threadData = nullProtoRecord(state.threadData);
159
+ if (removedMappingId !== void 0) delete threadData[removedMappingId];
160
+ const initializedData = {
161
+ ...data,
162
+ id: survivorMappingId,
163
+ initializeTask: Promise.resolve({
164
+ remoteId,
165
+ externalId: resolvedExternalId
166
+ }),
167
+ remoteId,
168
+ externalId: resolvedExternalId
169
+ };
170
+ threadData[survivorMappingId] = {
171
+ ...listedSlot?.data,
172
+ ...initializedData,
173
+ title: data.title ?? listedSlot?.data.title,
174
+ lastMessageAt: ("lastMessageAt" in data ? data.lastMessageAt : void 0) ?? (listedSlot && "lastMessageAt" in listedSlot.data ? listedSlot.data.lastMessageAt : void 0),
175
+ custom: data.custom ?? listedSlot?.data.custom
176
+ };
177
+ const threadIdMap = nullProtoRecord(state.threadIdMap);
178
+ if (removedMappingId !== void 0) {
179
+ for (const [id, target] of Object.entries(threadIdMap)) if (target === removedMappingId) threadIdMap[id] = survivorMappingId;
180
+ }
181
+ threadIdMap[threadId] = survivorMappingId;
182
+ threadIdMap[remoteId] = survivorMappingId;
183
+ const rewire = (ids) => listedSlot === void 0 ? ids : ids.filter((id) => id !== listedSlot.data.id).map((id) => id === data.id ? survivorMappingId : id);
184
+ return {
185
+ state: {
186
+ ...state,
187
+ threadIds: rewire(state.threadIds),
188
+ archivedThreadIds: rewire(state.archivedThreadIds),
189
+ threadIdMap,
190
+ threadData
191
+ },
192
+ removedMappingId,
193
+ survivorMappingId
194
+ };
195
+ };
196
+ const transitionReducer = (state, threadIdOrRemoteId, newStatus, initializeTask) => {
136
197
  const data = getThreadData(state, threadIdOrRemoteId);
137
198
  if (!data) return state;
138
199
  const { id, status: lastStatus } = data;
139
200
  if (lastStatus === newStatus) return state;
201
+ let nextData;
202
+ if (newStatus !== "deleted") {
203
+ if (data.status === "new") {
204
+ if (initializeTask === void 0) return state;
205
+ nextData = {
206
+ ...data,
207
+ initializeTask,
208
+ status: newStatus
209
+ };
210
+ } else nextData = {
211
+ ...data,
212
+ status: newStatus
213
+ };
214
+ }
140
215
  const newState = { ...state };
141
216
  switch (lastStatus) {
142
217
  case "new":
@@ -159,22 +234,35 @@ const updateStatusReducer = (state, threadIdOrRemoteId, newStatus) => {
159
234
  break;
160
235
  case "deleted": {
161
236
  const mappingId = state.threadIdMap[threadIdOrRemoteId];
162
- newState.threadData = Object.fromEntries(Object.entries(newState.threadData).filter(([key]) => key !== mappingId));
163
- newState.threadIdMap = Object.fromEntries(Object.entries(newState.threadIdMap).filter(([, value]) => value !== mappingId));
237
+ const threadData = nullProtoRecord(newState.threadData);
238
+ delete threadData[mappingId];
239
+ newState.threadData = threadData;
240
+ const threadIdMap = nullProtoRecord(newState.threadIdMap);
241
+ for (const [key, value] of Object.entries(threadIdMap)) if (value === mappingId) delete threadIdMap[key];
242
+ newState.threadIdMap = threadIdMap;
164
243
  break;
165
244
  }
166
245
  default: throw new Error(`Unsupported state: ${newStatus}`);
167
246
  }
168
- if (newStatus !== "deleted") newState.threadData = {
247
+ if (nextData !== void 0) newState.threadData = {
169
248
  ...newState.threadData,
170
- [id]: {
171
- ...data,
172
- status: newStatus
173
- }
249
+ [id]: nextData
174
250
  };
175
251
  return newState;
176
252
  };
253
+ /**
254
+ * Transitions a thread that already exists remotely. Promoting the local draft
255
+ * off `new` needs the initialization promise its destination variant requires,
256
+ * so that goes through `promoteNewThreadReducer`; asking for it here leaves the
257
+ * state unchanged.
258
+ */
259
+ const updateStatusReducer = (state, threadIdOrRemoteId, newStatus) => transitionReducer(state, threadIdOrRemoteId, newStatus, void 0);
260
+ /**
261
+ * Promotes the local draft to `regular`, carrying the initialization promise
262
+ * onto the slot.
263
+ */
264
+ const promoteNewThreadReducer = (state, threadIdOrRemoteId, initializeTask) => transitionReducer(state, threadIdOrRemoteId, "regular", initializeTask);
177
265
  //#endregion
178
- export { LOCAL_THREAD_ID_PREFIX, classifyThreads, createEmptyRemoteThreadState, createThreadMappingId, getThreadData, normalizeCursor, preserveMidLoadTransitions, seedNewThread, statusSnapshot, updateStatusReducer };
266
+ export { LOCAL_THREAD_ID_PREFIX, classifyThreads, createEmptyRemoteThreadState, createThreadMappingId, getThreadData, normalizeCursor, preserveMidLoadTransitions, promoteNewThreadReducer, reconcileInitializedThread, seedNewThread, statusSnapshot, updateStatusReducer };
179
267
 
180
268
  //# sourceMappingURL=remote-thread-state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"remote-thread-state.js","names":["RemoteThreadInitializeResponse","RemoteThreadMetadata","generateId","RemoteThreadData","id","remoteId","externalId","status","title","custom","localOrigin","initializeTask","Promise","lastMessageAt","Date","Record","THREAD_MAPPING_ID","__brand","createThreadMappingId","LOCAL_THREAD_ID_PREFIX","normalizeCursor","c","undefined","ClassifyAccumulator","threadIds","archivedThreadIds","threadIdMap","threadData","classifyThreads","threads","acc","listed","Set","thread","_exhaustiveCheck","Error","existingMappingId","existing","mappingId","existingTask","has","add","push","filter","t","const","resolve","RemoteThreadState","isLoading","isLoadingMore","cursor","newThreadId","Readonly","createEmptyRemoteThreadState","seedNewThread","state","preserveMidLoadTransitions","priorOrder","Pick","statusAtRequest","ReadonlyMap","regular","archived","rescuedRegular","rescuedArchived","contains","ids","ReadonlySet","data","Object","values","before","get","length","position","byId","indexOf","byRemoteId","Number","MAX_SAFE_INTEGER","sort","a","b","map","d","statusSnapshot","Map","getThreadData","threadIdOrRemoteId","idx","updateStatusReducer","newStatus","lastStatus","newState","fromEntries","entries","key","value"],"sources":["../../../src/runtimes/remote-thread-list/remote-thread-state.ts"],"sourcesContent":["import type {\n RemoteThreadInitializeResponse,\n RemoteThreadMetadata,\n} from \"./types\";\nimport { generateId } from \"../../utils/id\";\n\nexport type RemoteThreadData =\n | {\n readonly id: string;\n readonly remoteId: undefined;\n readonly externalId: undefined;\n readonly status: \"new\";\n readonly title: undefined;\n readonly custom: undefined;\n readonly localOrigin?: true;\n }\n | {\n readonly id: string;\n readonly initializeTask: Promise<RemoteThreadInitializeResponse>;\n readonly remoteId: undefined;\n readonly externalId: undefined;\n readonly status: \"regular\" | \"archived\";\n readonly title?: string | undefined;\n readonly custom: undefined;\n readonly localOrigin?: true;\n }\n | {\n readonly id: string;\n readonly initializeTask: Promise<RemoteThreadInitializeResponse>;\n readonly remoteId: string;\n readonly externalId: string | undefined;\n readonly status: \"regular\" | \"archived\";\n readonly title?: string | undefined;\n readonly lastMessageAt?: Date | undefined;\n readonly custom?: Record<string, unknown> | undefined;\n readonly localOrigin?: true;\n };\n\nexport type THREAD_MAPPING_ID = string & { __brand: \"THREAD_MAPPING_ID\" };\n\nexport function createThreadMappingId(id: string): THREAD_MAPPING_ID {\n return id as THREAD_MAPPING_ID;\n}\n\nexport const LOCAL_THREAD_ID_PREFIX = \"__LOCALID_\";\n\nexport const normalizeCursor = (c: string | undefined): string | undefined =>\n c || undefined;\n\nexport type ClassifyAccumulator = {\n threadIds: string[];\n archivedThreadIds: string[];\n threadIdMap: Record<string, THREAD_MAPPING_ID>;\n threadData: Record<THREAD_MAPPING_ID, RemoteThreadData>;\n};\n\n// A slot's `id` is fixed when it is minted: the hook instance manager keys live\n// thread runtimes by it, so renaming a slot would detach the runtime the user is\n// chatting in. A listed thread whose `remoteId` already maps to a slot therefore\n// refreshes that slot instead of minting a second one, and a slot minted by a\n// list() that raced ahead of the remote id collapses when `initialize()` settles.\n// `threadIds`/`archivedThreadIds` carry slot ids, never remote ids.\nexport const classifyThreads = (\n threads: readonly RemoteThreadMetadata[],\n acc: ClassifyAccumulator,\n): ClassifyAccumulator => {\n const listed = new Set([...acc.threadIds, ...acc.archivedThreadIds]);\n\n for (const thread of threads) {\n switch (thread.status) {\n case \"regular\":\n case \"archived\":\n break;\n default: {\n const _exhaustiveCheck: never = thread.status;\n throw new Error(`Unsupported state: ${_exhaustiveCheck}`);\n }\n }\n\n const existingMappingId = acc.threadIdMap[thread.remoteId];\n const existing =\n existingMappingId !== undefined\n ? acc.threadData[existingMappingId]\n : undefined;\n const id = existing?.id ?? thread.remoteId;\n const mappingId = existingMappingId ?? createThreadMappingId(id);\n const existingTask =\n existing !== undefined && existing.status !== \"new\"\n ? existing.initializeTask\n : undefined;\n\n if (!listed.has(id)) {\n listed.add(id);\n if (thread.status === \"regular\") {\n acc.threadIds.push(id);\n } else {\n acc.archivedThreadIds.push(id);\n }\n } else if (existing !== undefined && existing.status !== thread.status) {\n if (thread.status === \"regular\") {\n acc.archivedThreadIds = acc.archivedThreadIds.filter((t) => t !== id);\n acc.threadIds.push(id);\n } else {\n acc.threadIds = acc.threadIds.filter((t) => t !== id);\n acc.archivedThreadIds.push(id);\n }\n }\n\n acc.threadIdMap[id] = mappingId;\n acc.threadIdMap[thread.remoteId] = mappingId;\n acc.threadData[mappingId] = {\n ...(existing?.localOrigin === true ? { localOrigin: true as const } : {}),\n id,\n remoteId: thread.remoteId,\n externalId: thread.externalId,\n status: thread.status,\n title: thread.title,\n lastMessageAt: thread.lastMessageAt,\n custom: thread.custom,\n initializeTask:\n existingTask ??\n Promise.resolve({\n remoteId: thread.remoteId,\n externalId: thread.externalId,\n }),\n };\n }\n return acc;\n};\n\nexport type RemoteThreadState = {\n readonly isLoading: boolean;\n readonly isLoadingMore: boolean;\n readonly cursor: string | undefined;\n readonly newThreadId: string | undefined;\n readonly threadIds: readonly string[];\n readonly archivedThreadIds: readonly string[];\n readonly threadIdMap: Readonly<Record<string, THREAD_MAPPING_ID>>;\n readonly threadData: Readonly<Record<THREAD_MAPPING_ID, RemoteThreadData>>;\n};\n\nexport const createEmptyRemoteThreadState = (): RemoteThreadState => ({\n isLoading: true,\n isLoadingMore: false,\n cursor: undefined,\n newThreadId: undefined,\n threadIds: [],\n archivedThreadIds: [],\n threadIdMap: {},\n threadData: {},\n});\n\nexport const seedNewThread = (\n state: RemoteThreadState,\n): { id: string; state: RemoteThreadState } => {\n let id: string;\n do {\n id = `${LOCAL_THREAD_ID_PREFIX}${generateId()}`;\n } while (state.threadIdMap[id]);\n const mappingId = createThreadMappingId(id);\n return {\n id,\n state: {\n ...state,\n newThreadId: id,\n threadIdMap: {\n ...state.threadIdMap,\n [id]: mappingId,\n },\n threadData: {\n ...state.threadData,\n [mappingId]: {\n status: \"new\",\n id,\n remoteId: undefined,\n externalId: undefined,\n title: undefined,\n custom: undefined,\n localOrigin: true,\n } satisfies RemoteThreadData,\n },\n },\n };\n};\n\n// A list() response predating a mid-flight local transition (a thread\n// initialized while the request was running) omits that thread, and the\n// completed-optimistic replay cannot re-add it because the merged threadData\n// already carries the final status. Threads whose status moved from new (or\n// nonexistent) at request time to regular/archived are re-inserted at the\n// top, in the order the pre-response lists carried them: updateStatusReducer\n// prepends as transitions happen, so the pre-response lists hold newest\n// first, which threadData insertion order need not match. Threads the server already knew stay governed by the response, so\n// a server-side deletion is not resurrected — and a thread unknown to the\n// snapshot is only rescued when it carries the localOrigin marker stamped at\n// draft creation, so a deep-linked thread the fetch path deliberately\n// appended at the tail is not hoisted, whatever its remote id looks like.\nexport const preserveMidLoadTransitions = (\n state: RemoteThreadState,\n priorOrder: Pick<RemoteThreadState, \"threadIds\" | \"archivedThreadIds\">,\n statusAtRequest: ReadonlyMap<string, RemoteThreadData[\"status\"]>,\n): RemoteThreadState => {\n const regular = new Set(state.threadIds);\n const archived = new Set(state.archivedThreadIds);\n const rescuedRegular: RemoteThreadData[] = [];\n const rescuedArchived: RemoteThreadData[] = [];\n\n const contains = (ids: ReadonlySet<string>, data: RemoteThreadData) =>\n ids.has(data.id) || (data.remoteId !== undefined && ids.has(data.remoteId));\n\n for (const data of Object.values(state.threadData)) {\n const before = statusAtRequest.get(data.id);\n if (before !== undefined && before !== \"new\") continue;\n if (before === undefined && data.localOrigin !== true) continue;\n\n if (data.status === \"regular\" && !contains(regular, data)) {\n rescuedRegular.push(data);\n regular.add(data.id);\n } else if (data.status === \"archived\" && !contains(archived, data)) {\n rescuedArchived.push(data);\n archived.add(data.id);\n }\n }\n\n if (rescuedRegular.length === 0 && rescuedArchived.length === 0) return state;\n\n const position = (ids: readonly string[], data: RemoteThreadData) => {\n const byId = ids.indexOf(data.id);\n if (byId !== -1) return byId;\n const byRemoteId =\n data.remoteId !== undefined ? ids.indexOf(data.remoteId) : -1;\n return byRemoteId !== -1 ? byRemoteId : Number.MAX_SAFE_INTEGER;\n };\n rescuedRegular.sort(\n (a, b) =>\n position(priorOrder.threadIds, a) - position(priorOrder.threadIds, b),\n );\n rescuedArchived.sort(\n (a, b) =>\n position(priorOrder.archivedThreadIds, a) -\n position(priorOrder.archivedThreadIds, b),\n );\n\n return {\n ...state,\n threadIds: [...rescuedRegular.map((d) => d.id), ...state.threadIds],\n archivedThreadIds: [\n ...rescuedArchived.map((d) => d.id),\n ...state.archivedThreadIds,\n ],\n };\n};\n\nexport const statusSnapshot = (\n state: RemoteThreadState,\n): ReadonlyMap<string, RemoteThreadData[\"status\"]> =>\n new Map(Object.values(state.threadData).map((d) => [d.id, d.status]));\n\nexport const getThreadData = (\n state: RemoteThreadState,\n threadIdOrRemoteId: string,\n) => {\n const idx = state.threadIdMap[threadIdOrRemoteId];\n if (idx === undefined) return undefined;\n return state.threadData[idx];\n};\n\nexport const updateStatusReducer = (\n state: RemoteThreadState,\n threadIdOrRemoteId: string,\n newStatus: \"regular\" | \"archived\" | \"deleted\",\n) => {\n const data = getThreadData(state, threadIdOrRemoteId);\n if (!data) return state;\n\n const { id, status: lastStatus } = data;\n if (lastStatus === newStatus) return state;\n\n const newState = { ...state };\n\n // lastStatus\n switch (lastStatus) {\n case \"new\":\n newState.newThreadId = undefined;\n break;\n case \"regular\":\n newState.threadIds = newState.threadIds.filter((t) => t !== id);\n break;\n case \"archived\":\n newState.archivedThreadIds = newState.archivedThreadIds.filter(\n (t) => t !== id,\n );\n break;\n\n default: {\n const _exhaustiveCheck: never = lastStatus;\n throw new Error(`Unsupported state: ${_exhaustiveCheck}`);\n }\n }\n\n // newStatus\n switch (newStatus) {\n case \"regular\":\n newState.threadIds = [id, ...newState.threadIds];\n break;\n\n case \"archived\":\n newState.archivedThreadIds = [id, ...newState.archivedThreadIds];\n break;\n\n case \"deleted\": {\n const mappingId = state.threadIdMap[threadIdOrRemoteId]!;\n newState.threadData = Object.fromEntries(\n Object.entries(newState.threadData).filter(\n ([key]) => key !== mappingId,\n ),\n );\n newState.threadIdMap = Object.fromEntries(\n Object.entries(newState.threadIdMap).filter(\n ([, value]) => value !== mappingId,\n ),\n );\n break;\n }\n\n default: {\n const _exhaustiveCheck: never = newStatus;\n throw new Error(`Unsupported state: ${_exhaustiveCheck}`);\n }\n }\n\n if (newStatus !== \"deleted\") {\n newState.threadData = {\n ...newState.threadData,\n [id]: {\n ...data,\n status: newStatus,\n },\n };\n }\n\n return newState;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"remote-thread-state.js","names":["RemoteThreadInitializeResponse","RemoteThreadMetadata","generateId","nullProtoRecord","RemoteThreadData","id","remoteId","externalId","status","title","custom","localOrigin","initializeTask","Promise","lastMessageAt","Date","Record","THREAD_MAPPING_ID","__brand","createThreadMappingId","LOCAL_THREAD_ID_PREFIX","normalizeCursor","c","undefined","ClassifyAccumulator","threadIds","archivedThreadIds","threadIdMap","threadData","classifyThreads","threads","acc","listed","Set","thread","_exhaustiveCheck","Error","existingMappingId","existing","mappingId","existingTask","has","add","push","filter","t","const","resolve","RemoteThreadState","isLoading","loadError","isLoadingMore","cursor","newThreadId","Readonly","createEmptyRemoteThreadState","seedNewThread","state","preserveMidLoadTransitions","priorOrder","Pick","statusAtRequest","ReadonlyMap","regular","archived","rescuedRegular","rescuedArchived","contains","ids","ReadonlySet","data","Object","values","before","get","length","position","byId","indexOf","byRemoteId","Number","MAX_SAFE_INTEGER","sort","a","b","map","d","statusSnapshot","Map","getThreadData","threadIdOrRemoteId","idx","hasOwn","reconcileInitializedThread","threadId","retainedThreadId","removedMappingId","survivorMappingId","listedMappingId","listedData","listedSlot","retainedMappingId","resolvedExternalId","initializedData","target","entries","rewire","transitionReducer","newStatus","lastStatus","nextData","newState","key","value","updateStatusReducer","promoteNewThreadReducer"],"sources":["../../../src/runtimes/remote-thread-list/remote-thread-state.ts"],"sourcesContent":["import type {\n RemoteThreadInitializeResponse,\n RemoteThreadMetadata,\n} from \"./types\";\nimport { generateId } from \"../../utils/id\";\nimport { nullProtoRecord } from \"../../utils/record\";\n\nexport type RemoteThreadData =\n | {\n readonly id: string;\n readonly remoteId: undefined;\n readonly externalId: undefined;\n readonly status: \"new\";\n readonly title: undefined;\n readonly custom: undefined;\n readonly localOrigin?: true;\n }\n | {\n readonly id: string;\n readonly initializeTask: Promise<RemoteThreadInitializeResponse>;\n readonly remoteId: undefined;\n readonly externalId: undefined;\n readonly status: \"regular\" | \"archived\";\n readonly title?: string | undefined;\n readonly custom: undefined;\n readonly localOrigin?: true;\n }\n | {\n readonly id: string;\n readonly initializeTask: Promise<RemoteThreadInitializeResponse>;\n readonly remoteId: string;\n readonly externalId: string | undefined;\n readonly status: \"regular\" | \"archived\";\n readonly title?: string | undefined;\n readonly lastMessageAt?: Date | undefined;\n readonly custom?: Record<string, unknown> | undefined;\n readonly localOrigin?: true;\n };\n\nexport type THREAD_MAPPING_ID = string & { __brand: \"THREAD_MAPPING_ID\" };\n\nexport function createThreadMappingId(id: string): THREAD_MAPPING_ID {\n return id as THREAD_MAPPING_ID;\n}\n\nexport const LOCAL_THREAD_ID_PREFIX = \"__LOCALID_\";\n\nexport const normalizeCursor = (c: string | undefined): string | undefined =>\n c || undefined;\n\nexport type ClassifyAccumulator = {\n threadIds: string[];\n archivedThreadIds: string[];\n threadIdMap: Record<string, THREAD_MAPPING_ID>;\n threadData: Record<THREAD_MAPPING_ID, RemoteThreadData>;\n};\n\n// A slot's `id` is fixed when it is minted: the hook instance manager keys live\n// thread runtimes by it, so renaming a slot would detach the runtime the user is\n// chatting in. A listed thread whose `remoteId` already maps to a slot therefore\n// refreshes that slot instead of minting a second one, and a slot minted by a\n// list() that raced ahead of the remote id collapses when `initialize()` settles.\n// `threadIds`/`archivedThreadIds` carry slot ids, never remote ids.\nexport const classifyThreads = (\n threads: readonly RemoteThreadMetadata[],\n acc: ClassifyAccumulator,\n): ClassifyAccumulator => {\n let threadIds = [...acc.threadIds];\n let archivedThreadIds = [...acc.archivedThreadIds];\n const threadIdMap = nullProtoRecord(acc.threadIdMap);\n const threadData = nullProtoRecord(acc.threadData);\n const listed = new Set([...threadIds, ...archivedThreadIds]);\n\n for (const thread of threads) {\n switch (thread.status) {\n case \"regular\":\n case \"archived\":\n break;\n default: {\n const _exhaustiveCheck: never = thread.status;\n throw new Error(`Unsupported state: ${_exhaustiveCheck}`);\n }\n }\n\n const existingMappingId = threadIdMap[thread.remoteId];\n const existing =\n existingMappingId !== undefined\n ? threadData[existingMappingId]\n : undefined;\n const id = existing?.id ?? thread.remoteId;\n const mappingId = existingMappingId ?? createThreadMappingId(id);\n const existingTask =\n existing !== undefined && existing.status !== \"new\"\n ? existing.initializeTask\n : undefined;\n\n if (!listed.has(id)) {\n listed.add(id);\n if (thread.status === \"regular\") {\n threadIds.push(id);\n } else {\n archivedThreadIds.push(id);\n }\n } else if (existing !== undefined && existing.status !== thread.status) {\n if (thread.status === \"regular\") {\n archivedThreadIds = archivedThreadIds.filter((t) => t !== id);\n threadIds.push(id);\n } else {\n threadIds = threadIds.filter((t) => t !== id);\n archivedThreadIds.push(id);\n }\n }\n\n threadIdMap[id] = mappingId;\n threadIdMap[thread.remoteId] = mappingId;\n threadData[mappingId] = {\n ...(existing?.localOrigin === true ? { localOrigin: true as const } : {}),\n id,\n remoteId: thread.remoteId,\n externalId: thread.externalId,\n status: thread.status,\n title: thread.title,\n lastMessageAt: thread.lastMessageAt,\n custom: thread.custom,\n initializeTask:\n existingTask ??\n Promise.resolve({\n remoteId: thread.remoteId,\n externalId: thread.externalId,\n }),\n };\n }\n return { threadIds, archivedThreadIds, threadIdMap, threadData };\n};\n\nexport type RemoteThreadState = {\n readonly isLoading: boolean;\n readonly loadError: unknown;\n readonly isLoadingMore: boolean;\n readonly cursor: string | undefined;\n readonly newThreadId: string | undefined;\n readonly threadIds: readonly string[];\n readonly archivedThreadIds: readonly string[];\n readonly threadIdMap: Readonly<Record<string, THREAD_MAPPING_ID>>;\n readonly threadData: Readonly<Record<THREAD_MAPPING_ID, RemoteThreadData>>;\n};\n\nexport const createEmptyRemoteThreadState = (): RemoteThreadState => ({\n isLoading: true,\n loadError: undefined,\n isLoadingMore: false,\n cursor: undefined,\n newThreadId: undefined,\n threadIds: [],\n archivedThreadIds: [],\n threadIdMap: nullProtoRecord(),\n threadData: nullProtoRecord(),\n});\n\nexport const seedNewThread = (\n state: RemoteThreadState,\n): { id: string; state: RemoteThreadState } => {\n let id: string;\n do {\n id = `${LOCAL_THREAD_ID_PREFIX}${generateId()}`;\n } while (state.threadIdMap[id]);\n const mappingId = createThreadMappingId(id);\n return {\n id,\n state: {\n ...state,\n newThreadId: id,\n threadIdMap: nullProtoRecord(state.threadIdMap, {\n [id]: mappingId,\n }),\n threadData: nullProtoRecord(state.threadData, {\n [mappingId]: {\n status: \"new\",\n id,\n remoteId: undefined,\n externalId: undefined,\n title: undefined,\n custom: undefined,\n localOrigin: true,\n } satisfies RemoteThreadData,\n }),\n },\n };\n};\n\n// A list() response predating a mid-flight local transition (a thread\n// initialized while the request was running) omits that thread, and the\n// completed-optimistic replay cannot re-add it because the merged threadData\n// already carries the final status. Threads whose status moved from new (or\n// nonexistent) at request time to regular/archived are re-inserted at the\n// top, in the order the pre-response lists carried them: updateStatusReducer\n// prepends as transitions happen, so the pre-response lists hold newest\n// first, which threadData insertion order need not match. Threads the server already knew stay governed by the response, so\n// a server-side deletion is not resurrected — and a thread unknown to the\n// snapshot is only rescued when it carries the localOrigin marker stamped at\n// draft creation, so a deep-linked thread the fetch path deliberately\n// appended at the tail is not hoisted, whatever its remote id looks like.\nexport const preserveMidLoadTransitions = (\n state: RemoteThreadState,\n priorOrder: Pick<RemoteThreadState, \"threadIds\" | \"archivedThreadIds\">,\n statusAtRequest: ReadonlyMap<string, RemoteThreadData[\"status\"]>,\n): RemoteThreadState => {\n const regular = new Set(state.threadIds);\n const archived = new Set(state.archivedThreadIds);\n const rescuedRegular: RemoteThreadData[] = [];\n const rescuedArchived: RemoteThreadData[] = [];\n\n const contains = (ids: ReadonlySet<string>, data: RemoteThreadData) =>\n ids.has(data.id) || (data.remoteId !== undefined && ids.has(data.remoteId));\n\n for (const data of Object.values(state.threadData)) {\n const before = statusAtRequest.get(data.id);\n if (before !== undefined && before !== \"new\") continue;\n if (before === undefined && data.localOrigin !== true) continue;\n\n if (data.status === \"regular\" && !contains(regular, data)) {\n rescuedRegular.push(data);\n regular.add(data.id);\n } else if (data.status === \"archived\" && !contains(archived, data)) {\n rescuedArchived.push(data);\n archived.add(data.id);\n }\n }\n\n if (rescuedRegular.length === 0 && rescuedArchived.length === 0) return state;\n\n const position = (ids: readonly string[], data: RemoteThreadData) => {\n const byId = ids.indexOf(data.id);\n if (byId !== -1) return byId;\n const byRemoteId =\n data.remoteId !== undefined ? ids.indexOf(data.remoteId) : -1;\n return byRemoteId !== -1 ? byRemoteId : Number.MAX_SAFE_INTEGER;\n };\n rescuedRegular.sort(\n (a, b) =>\n position(priorOrder.threadIds, a) - position(priorOrder.threadIds, b),\n );\n rescuedArchived.sort(\n (a, b) =>\n position(priorOrder.archivedThreadIds, a) -\n position(priorOrder.archivedThreadIds, b),\n );\n\n return {\n ...state,\n threadIds: [...rescuedRegular.map((d) => d.id), ...state.threadIds],\n archivedThreadIds: [\n ...rescuedArchived.map((d) => d.id),\n ...state.archivedThreadIds,\n ],\n };\n};\n\nexport const statusSnapshot = (\n state: RemoteThreadState,\n): ReadonlyMap<string, RemoteThreadData[\"status\"]> =>\n new Map(Object.values(state.threadData).map((d) => [d.id, d.status]));\n\nexport const getThreadData = (\n state: RemoteThreadState,\n threadIdOrRemoteId: string,\n) => {\n const idx = Object.hasOwn(state.threadIdMap, threadIdOrRemoteId)\n ? state.threadIdMap[threadIdOrRemoteId]\n : undefined;\n if (idx === undefined) return undefined;\n return Object.hasOwn(state.threadData, idx)\n ? state.threadData[idx]\n : undefined;\n};\n\nexport const reconcileInitializedThread = (\n state: RemoteThreadState,\n threadId: string,\n remoteId: string,\n externalId: string | undefined,\n retainedThreadId?: string,\n): {\n state: RemoteThreadState;\n removedMappingId: THREAD_MAPPING_ID | undefined;\n survivorMappingId: THREAD_MAPPING_ID;\n} => {\n const mappingId = createThreadMappingId(threadId);\n const data = Object.hasOwn(state.threadData, mappingId)\n ? state.threadData[mappingId]\n : undefined;\n if (!data) {\n return { state, removedMappingId: undefined, survivorMappingId: mappingId };\n }\n\n // A concurrent list cannot associate the remote ID with this initializing\n // slot and may mint a duplicate. Retain the slot whose mounted runtime owns\n // local state; initialized metadata remains authoritative over list metadata.\n const listedMappingId = Object.hasOwn(state.threadIdMap, remoteId)\n ? state.threadIdMap[remoteId]\n : undefined;\n const listedData =\n listedMappingId !== undefined &&\n listedMappingId !== mappingId &&\n Object.hasOwn(state.threadData, listedMappingId)\n ? state.threadData[listedMappingId]\n : undefined;\n const listedSlot =\n listedMappingId !== undefined && listedData !== undefined\n ? { mappingId: listedMappingId, data: listedData }\n : undefined;\n const retainedMappingId =\n retainedThreadId !== undefined &&\n Object.hasOwn(state.threadIdMap, retainedThreadId)\n ? state.threadIdMap[retainedThreadId]\n : undefined;\n const survivorMappingId =\n listedSlot !== undefined && retainedMappingId === listedSlot.mappingId\n ? listedSlot.mappingId\n : mappingId;\n const removedMappingId =\n listedSlot === undefined\n ? undefined\n : survivorMappingId === mappingId\n ? listedSlot.mappingId\n : mappingId;\n const resolvedExternalId = externalId ?? listedSlot?.data.externalId;\n const threadData = nullProtoRecord(state.threadData);\n if (removedMappingId !== undefined) delete threadData[removedMappingId];\n const initializedData = {\n ...data,\n id: survivorMappingId,\n initializeTask: Promise.resolve({\n remoteId,\n externalId: resolvedExternalId,\n }),\n remoteId,\n externalId: resolvedExternalId,\n } as RemoteThreadData;\n threadData[survivorMappingId] = {\n ...listedSlot?.data,\n ...initializedData,\n title: data.title ?? listedSlot?.data.title,\n lastMessageAt:\n (\"lastMessageAt\" in data ? data.lastMessageAt : undefined) ??\n (listedSlot && \"lastMessageAt\" in listedSlot.data\n ? listedSlot.data.lastMessageAt\n : undefined),\n custom: data.custom ?? listedSlot?.data.custom,\n } as RemoteThreadData;\n\n const threadIdMap = nullProtoRecord(state.threadIdMap);\n if (removedMappingId !== undefined) {\n for (const [id, target] of Object.entries(threadIdMap)) {\n if (target === removedMappingId) threadIdMap[id] = survivorMappingId;\n }\n }\n threadIdMap[threadId] = survivorMappingId;\n threadIdMap[remoteId] = survivorMappingId;\n\n const rewire = (ids: readonly string[]) =>\n listedSlot === undefined\n ? ids\n : ids\n .filter((id) => id !== listedSlot.data.id)\n .map((id) => (id === data.id ? survivorMappingId : id));\n\n return {\n state: {\n ...state,\n threadIds: rewire(state.threadIds),\n archivedThreadIds: rewire(state.archivedThreadIds),\n threadIdMap,\n threadData,\n },\n removedMappingId,\n survivorMappingId,\n };\n};\n\nconst transitionReducer = (\n state: RemoteThreadState,\n threadIdOrRemoteId: string,\n newStatus: \"regular\" | \"archived\" | \"deleted\",\n initializeTask: Promise<RemoteThreadInitializeResponse> | undefined,\n) => {\n const data = getThreadData(state, threadIdOrRemoteId);\n if (!data) return state;\n\n const { id, status: lastStatus } = data;\n if (lastStatus === newStatus) return state;\n\n let nextData: RemoteThreadData | undefined;\n if (newStatus !== \"deleted\") {\n if (data.status === \"new\") {\n // The new variant holds no initialization promise, so the destination\n // union member cannot be built unless the caller supplies one.\n if (initializeTask === undefined) return state;\n nextData = { ...data, initializeTask, status: newStatus };\n } else {\n nextData = { ...data, status: newStatus };\n }\n }\n\n const newState = { ...state };\n\n // lastStatus\n switch (lastStatus) {\n case \"new\":\n newState.newThreadId = undefined;\n break;\n case \"regular\":\n newState.threadIds = newState.threadIds.filter((t) => t !== id);\n break;\n case \"archived\":\n newState.archivedThreadIds = newState.archivedThreadIds.filter(\n (t) => t !== id,\n );\n break;\n\n default: {\n const _exhaustiveCheck: never = lastStatus;\n throw new Error(`Unsupported state: ${_exhaustiveCheck}`);\n }\n }\n\n // newStatus\n switch (newStatus) {\n case \"regular\":\n newState.threadIds = [id, ...newState.threadIds];\n break;\n\n case \"archived\":\n newState.archivedThreadIds = [id, ...newState.archivedThreadIds];\n break;\n\n case \"deleted\": {\n const mappingId = state.threadIdMap[threadIdOrRemoteId]!;\n const threadData = nullProtoRecord(newState.threadData);\n delete threadData[mappingId];\n newState.threadData = threadData;\n const threadIdMap = nullProtoRecord(newState.threadIdMap);\n for (const [key, value] of Object.entries(threadIdMap)) {\n if (value === mappingId) delete threadIdMap[key];\n }\n newState.threadIdMap = threadIdMap;\n break;\n }\n\n default: {\n const _exhaustiveCheck: never = newStatus;\n throw new Error(`Unsupported state: ${_exhaustiveCheck}`);\n }\n }\n\n if (nextData !== undefined) {\n newState.threadData = { ...newState.threadData, [id]: nextData };\n }\n\n return newState;\n};\n\n/**\n * Transitions a thread that already exists remotely. Promoting the local draft\n * off `new` needs the initialization promise its destination variant requires,\n * so that goes through `promoteNewThreadReducer`; asking for it here leaves the\n * state unchanged.\n */\nexport const updateStatusReducer = (\n state: RemoteThreadState,\n threadIdOrRemoteId: string,\n newStatus: \"regular\" | \"archived\" | \"deleted\",\n) => transitionReducer(state, threadIdOrRemoteId, newStatus, undefined);\n\n/**\n * Promotes the local draft to `regular`, carrying the initialization promise\n * onto the slot.\n */\nexport const promoteNewThreadReducer = (\n state: RemoteThreadState,\n threadIdOrRemoteId: string,\n initializeTask: Promise<RemoteThreadInitializeResponse>,\n) => transitionReducer(state, threadIdOrRemoteId, \"regular\", initializeTask);\n"],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ //#region src/runtimes/remote-thread-list/title-generation.d.ts
2
+ type ThreadTitleClaim = {
3
+ readonly title: string;
4
+ readonly order: number;
5
+ readonly settled: Promise<boolean>;
6
+ readonly settle: (renamed: boolean) => void;
7
+ };
8
+ type ThreadTitleGeneration = {
9
+ readonly automatic: boolean;
10
+ readonly order: number;
11
+ claim: ThreadTitleClaim | null;
12
+ beforeGenerationClaims: readonly ThreadTitleClaim[];
13
+ superseded: boolean;
14
+ readonly persisted: Promise<string | undefined>;
15
+ readonly settlePersisted: (title: string | undefined) => void;
16
+ };
17
+ type ThreadTitleState = {
18
+ generations: Set<ThreadTitleGeneration>;
19
+ pendingClaim: ThreadTitleClaim | null;
20
+ inFlightClaims: Set<ThreadTitleClaim>;
21
+ manualTitle: string | undefined;
22
+ latestExplicit: ThreadTitleGeneration | null;
23
+ nextOrder: number;
24
+ };
25
+ type ThreadTitleGenerationRun = {
26
+ states: Map<string, ThreadTitleState>;
27
+ threadId: string;
28
+ automatic: boolean;
29
+ generate: (onTitle: (title: string | undefined) => Promise<void>) => Promise<void>;
30
+ rename: (title: string) => Promise<void>;
31
+ applyTitle: (title: string | undefined) => Promise<void>;
32
+ };
33
+ declare function startThreadTitleRename(states: Map<string, ThreadTitleState>, threadId: string, title: string): ThreadTitleClaim;
34
+ declare function finishThreadTitleRename(states: Map<string, ThreadTitleState>, threadId: string, claim: ThreadTitleClaim, renamed: boolean): void;
35
+ declare function clearThreadTitleState(states: Map<string, ThreadTitleState>, threadId: string): void;
36
+ /**
37
+ * Runs one title generation under the per-thread rename claims and generation
38
+ * ordering, so the title that wins locally is also the title left on the
39
+ * server.
40
+ *
41
+ * A generated run persists the title itself, and the adapter exposes no
42
+ * compare-and-set, so the run that loses reasserts the winner through `rename`
43
+ * only after `generate` has resolved. Reasserting mid-stream would race the
44
+ * run's own write and lose the winning title on the server.
45
+ */
46
+ declare function runThreadTitleGeneration({ states, threadId, automatic, generate, rename, applyTitle }: ThreadTitleGenerationRun): Promise<void>;
47
+ //#endregion
48
+ export { ThreadTitleClaim, ThreadTitleGenerationRun, ThreadTitleState, clearThreadTitleState, finishThreadTitleRename, runThreadTitleGeneration, startThreadTitleRename };
49
+ //# sourceMappingURL=title-generation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"title-generation.d.ts","names":[],"sources":["../../../src/runtimes/remote-thread-list/title-generation.ts"],"mappings":";KAAY;WACD;WACA;WACA,SAAS;WACT,SAAS;;KAGf;WACM;WACA;EACT,OAAO;EACP,iCAAiC;EACjC;WACS,WAAW;WACX,kBAAkB;;KAGjB;EACV,aAAa,IAAI;EACjB,cAAc;EACd,gBAAgB,IAAI;EACpB;EACA,gBAAgB;EAChB;;KAGU;EACV,QAAQ,YAAY;EACpB;EACA;EACA,WACE,UAAU,8BAA8B,kBACrC;EACL,SAAS,kBAAkB;EAC3B,aAAa,8BAA8B;;iBAuD7B,uBACd,QAAQ,YAAY,mBACpB,kBACA,gBACC;iBAoBa,wBACd,QAAQ,YAAY,mBACpB,kBACA,OAAO,kBACP;iBA2Bc,sBACd,QAAQ,YAAY,mBACpB;;;;;;;;;;;iBAsDoB,2BACpB,QACA,UACA,WACA,UACA,QACA,cACC,2BAA2B"}
@@ -0,0 +1,197 @@
1
+ //#region src/runtimes/remote-thread-list/title-generation.ts
2
+ function getThreadTitleState(states, threadId) {
3
+ let state = states.get(threadId);
4
+ if (state === void 0) {
5
+ state = {
6
+ generations: /* @__PURE__ */ new Set(),
7
+ pendingClaim: null,
8
+ inFlightClaims: /* @__PURE__ */ new Set(),
9
+ manualTitle: void 0,
10
+ latestExplicit: null,
11
+ nextOrder: 0
12
+ };
13
+ states.set(threadId, state);
14
+ }
15
+ return state;
16
+ }
17
+ function pruneThreadTitleState(states, threadId, state) {
18
+ if (state.generations.size === 0 && state.pendingClaim === null && state.inFlightClaims.size === 0 && state.manualTitle === void 0 && states.get(threadId) === state) states.delete(threadId);
19
+ }
20
+ function isCurrentGeneration(state, generation) {
21
+ return !generation.superseded && (state.latestExplicit?.order ?? 0) <= generation.order;
22
+ }
23
+ function isCurrentClaim(state, claim) {
24
+ return (state.latestExplicit?.order ?? 0) < claim.order;
25
+ }
26
+ function startThreadTitleRename(states, threadId, title) {
27
+ const state = getThreadTitleState(states, threadId);
28
+ let settle;
29
+ const settled = new Promise((resolve) => {
30
+ settle = resolve;
31
+ });
32
+ const claim = {
33
+ title,
34
+ order: ++state.nextOrder,
35
+ settled,
36
+ settle
37
+ };
38
+ state.pendingClaim = claim;
39
+ state.inFlightClaims.add(claim);
40
+ for (const generation of state.generations) if (generation.order < claim.order) generation.claim = claim;
41
+ return claim;
42
+ }
43
+ function finishThreadTitleRename(states, threadId, claim, renamed) {
44
+ claim.settle(renamed);
45
+ const state = states.get(threadId);
46
+ if (state === void 0) return;
47
+ state.inFlightClaims.delete(claim);
48
+ if (state.pendingClaim === claim) {
49
+ state.pendingClaim = null;
50
+ if (renamed) state.manualTitle = claim.title;
51
+ }
52
+ pruneThreadTitleState(states, threadId, state);
53
+ }
54
+ function takeManualTitle(states, threadId, state) {
55
+ const retained = state.manualTitle;
56
+ if (retained === void 0) return void 0;
57
+ state.manualTitle = void 0;
58
+ pruneThreadTitleState(states, threadId, state);
59
+ return retained;
60
+ }
61
+ function clearThreadTitleState(states, threadId) {
62
+ states.delete(threadId);
63
+ }
64
+ function startThreadTitleGeneration(states, threadId, automatic) {
65
+ const state = getThreadTitleState(states, threadId);
66
+ if (automatic && state.pendingClaim === null && takeManualTitle(states, threadId, state) !== void 0) return null;
67
+ let settlePersisted;
68
+ const persisted = new Promise((resolve) => {
69
+ settlePersisted = resolve;
70
+ });
71
+ const generation = {
72
+ automatic,
73
+ order: ++state.nextOrder,
74
+ claim: automatic ? state.pendingClaim : null,
75
+ beforeGenerationClaims: automatic ? [] : [...state.inFlightClaims],
76
+ superseded: false,
77
+ persisted,
78
+ settlePersisted
79
+ };
80
+ if (!automatic) {
81
+ state.pendingClaim = null;
82
+ state.manualTitle = void 0;
83
+ state.latestExplicit = generation;
84
+ for (const active of state.generations) if (active.automatic) active.superseded = true;
85
+ }
86
+ state.generations.add(generation);
87
+ return generation;
88
+ }
89
+ /**
90
+ * Runs one title generation under the per-thread rename claims and generation
91
+ * ordering, so the title that wins locally is also the title left on the
92
+ * server.
93
+ *
94
+ * A generated run persists the title itself, and the adapter exposes no
95
+ * compare-and-set, so the run that loses reasserts the winner through `rename`
96
+ * only after `generate` has resolved. Reasserting mid-stream would race the
97
+ * run's own write and lose the winning title on the server.
98
+ */
99
+ async function runThreadTitleGeneration({ states, threadId, automatic, generate, rename, applyTitle }) {
100
+ const state = getThreadTitleState(states, threadId);
101
+ const generation = startThreadTitleGeneration(states, threadId, automatic);
102
+ if (generation === null) return;
103
+ let persistedTitle;
104
+ let persistedOrder = generation.order;
105
+ const settleClaim = async (claim) => {
106
+ const renamed = await claim.settled;
107
+ if (generation.claim !== claim) return void 0;
108
+ if (!renamed) {
109
+ generation.claim = null;
110
+ if (state.pendingClaim === claim) state.pendingClaim = null;
111
+ }
112
+ return renamed;
113
+ };
114
+ const retainManualTitle = () => automatic && takeManualTitle(states, threadId, state) !== void 0;
115
+ const repairLostRace = async () => {
116
+ if (persistedTitle === void 0) return;
117
+ while (true) {
118
+ const claim = generation.claim;
119
+ if (claim !== null && claim.order > persistedOrder && isCurrentClaim(state, claim)) {
120
+ if (await settleClaim(claim) === true) {
121
+ persistedTitle = claim.title;
122
+ persistedOrder = claim.order;
123
+ await rename(claim.title);
124
+ }
125
+ continue;
126
+ }
127
+ const winner = state.latestExplicit;
128
+ if (winner === null || winner.order <= persistedOrder) return;
129
+ const title = await winner.persisted;
130
+ if (state.latestExplicit !== winner) continue;
131
+ persistedOrder = winner.order;
132
+ if (title === void 0 || title === persistedTitle) return;
133
+ persistedTitle = title;
134
+ await rename(title);
135
+ }
136
+ };
137
+ const runGeneration = async () => {
138
+ if (generation.beforeGenerationClaims.length > 0) await Promise.all(generation.beforeGenerationClaims.map((claim) => claim.settled));
139
+ if (generation.claim !== null) {
140
+ const renamed = await settleClaim(generation.claim);
141
+ if (renamed === true) {
142
+ generation.superseded = true;
143
+ return;
144
+ }
145
+ if (renamed === false && retainManualTitle()) return;
146
+ }
147
+ if (!isCurrentGeneration(state, generation)) return;
148
+ let sawTitle = false;
149
+ let lastTitle;
150
+ await generate(async (title) => {
151
+ sawTitle = true;
152
+ lastTitle = title;
153
+ if (title !== void 0) persistedTitle = title;
154
+ const claim = generation.claim;
155
+ if (claim !== null) {
156
+ if (await settleClaim(claim) !== false) return;
157
+ if (retainManualTitle()) {
158
+ generation.superseded = true;
159
+ return;
160
+ }
161
+ }
162
+ if (isCurrentGeneration(state, generation)) await applyTitle(lastTitle);
163
+ });
164
+ while (true) {
165
+ const claim = generation.claim;
166
+ if (claim === null) return;
167
+ const renamed = await settleClaim(claim);
168
+ if (renamed === void 0) continue;
169
+ if (renamed === false) {
170
+ if (retainManualTitle()) return;
171
+ if (sawTitle && isCurrentGeneration(state, generation)) await applyTitle(lastTitle);
172
+ return;
173
+ }
174
+ if (!isCurrentClaim(state, claim)) return;
175
+ await rename(claim.title);
176
+ persistedTitle = claim.title;
177
+ persistedOrder = claim.order;
178
+ if (generation.claim !== claim) continue;
179
+ if (!isCurrentGeneration(state, generation)) return;
180
+ await applyTitle(claim.title);
181
+ generation.superseded = true;
182
+ return;
183
+ }
184
+ };
185
+ try {
186
+ await runGeneration();
187
+ await repairLostRace();
188
+ } finally {
189
+ generation.settlePersisted(persistedTitle);
190
+ state.generations.delete(generation);
191
+ pruneThreadTitleState(states, threadId, state);
192
+ }
193
+ }
194
+ //#endregion
195
+ export { clearThreadTitleState, finishThreadTitleRename, runThreadTitleGeneration, startThreadTitleRename };
196
+
197
+ //# sourceMappingURL=title-generation.js.map