@athenaintel/react 0.22.4 → 0.23.0

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 (350) hide show
  1. package/README.md +390 -4
  2. package/dist/chat/AthenaChat.cjs +346 -244
  3. package/dist/chat/AthenaChat.cjs.map +1 -1
  4. package/dist/chat/AthenaChat.d.ts +19 -8
  5. package/dist/chat/AthenaChat.js +350 -248
  6. package/dist/chat/AthenaChat.js.map +1 -1
  7. package/dist/chat/StatewireApprovalCard.cjs +2 -1
  8. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  9. package/dist/chat/StatewireApprovalCard.js +2 -1
  10. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  11. package/dist/chat/StatewireClientToolBridge.cjs +1 -0
  12. package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
  13. package/dist/chat/StatewireClientToolBridge.js +1 -0
  14. package/dist/chat/StatewireClientToolBridge.js.map +1 -1
  15. package/dist/chat/StatewireQueuedMessages.cjs +1 -0
  16. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  17. package/dist/chat/StatewireQueuedMessages.js +1 -0
  18. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  19. package/dist/chat/WaitingOnYouBanner.cjs +100 -0
  20. package/dist/chat/WaitingOnYouBanner.cjs.map +1 -0
  21. package/dist/chat/WaitingOnYouBanner.d.ts +29 -0
  22. package/dist/chat/WaitingOnYouBanner.js +100 -0
  23. package/dist/chat/WaitingOnYouBanner.js.map +1 -0
  24. package/dist/chat/athena-chat-welcome.cjs +148 -0
  25. package/dist/chat/athena-chat-welcome.cjs.map +1 -0
  26. package/dist/chat/athena-chat-welcome.d.ts +54 -0
  27. package/dist/chat/athena-chat-welcome.js +148 -0
  28. package/dist/chat/athena-chat-welcome.js.map +1 -0
  29. package/dist/chat/athena-composer-controls.cjs +76 -0
  30. package/dist/chat/athena-composer-controls.cjs.map +1 -0
  31. package/dist/chat/athena-composer-controls.d.ts +21 -0
  32. package/dist/chat/athena-composer-controls.js +76 -0
  33. package/dist/chat/athena-composer-controls.js.map +1 -0
  34. package/dist/chat/composer-keyboard-hint.cjs +61 -0
  35. package/dist/chat/composer-keyboard-hint.cjs.map +1 -0
  36. package/dist/chat/composer-keyboard-hint.d.ts +33 -0
  37. package/dist/chat/composer-keyboard-hint.js +61 -0
  38. package/dist/chat/composer-keyboard-hint.js.map +1 -0
  39. package/dist/chat/message-time.cjs +64 -0
  40. package/dist/chat/message-time.cjs.map +1 -0
  41. package/dist/chat/message-time.d.ts +45 -0
  42. package/dist/chat/message-time.js +64 -0
  43. package/dist/chat/message-time.js.map +1 -0
  44. package/dist/chat/run-activity.cjs +38 -7
  45. package/dist/chat/run-activity.cjs.map +1 -1
  46. package/dist/chat/run-activity.d.ts +14 -3
  47. package/dist/chat/run-activity.js +39 -8
  48. package/dist/chat/run-activity.js.map +1 -1
  49. package/dist/chat/statewire-client-tools.cjs +2 -1
  50. package/dist/chat/statewire-client-tools.cjs.map +1 -1
  51. package/dist/chat/statewire-client-tools.js +2 -1
  52. package/dist/chat/statewire-client-tools.js.map +1 -1
  53. package/dist/chat/super-grouping/activity-timeline.cjs +2 -1
  54. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  55. package/dist/chat/super-grouping/activity-timeline.js +2 -1
  56. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  57. package/dist/chat/thread-scroll-to-bottom.cjs +24 -0
  58. package/dist/chat/thread-scroll-to-bottom.cjs.map +1 -0
  59. package/dist/chat/thread-scroll-to-bottom.d.ts +11 -0
  60. package/dist/chat/thread-scroll-to-bottom.js +24 -0
  61. package/dist/chat/thread-scroll-to-bottom.js.map +1 -0
  62. package/dist/chat/tiptap-text.cjs +26 -95
  63. package/dist/chat/tiptap-text.cjs.map +1 -1
  64. package/dist/chat/tiptap-text.d.ts +13 -0
  65. package/dist/chat/tiptap-text.js +26 -95
  66. package/dist/chat/tiptap-text.js.map +1 -1
  67. package/dist/chat/use-athena-run-live.cjs +2 -1
  68. package/dist/chat/use-athena-run-live.cjs.map +1 -1
  69. package/dist/chat/use-athena-run-live.js +2 -1
  70. package/dist/chat/use-athena-run-live.js.map +1 -1
  71. package/dist/chat/use-statewire-approval-request.cjs +1 -0
  72. package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
  73. package/dist/chat/use-statewire-approval-request.js +1 -0
  74. package/dist/chat/use-statewire-approval-request.js.map +1 -1
  75. package/dist/chat-ui/composer/tiptap-composer.cjs +39 -4
  76. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  77. package/dist/chat-ui/composer/tiptap-composer.d.ts +21 -0
  78. package/dist/chat-ui/composer/tiptap-composer.js +39 -4
  79. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  80. package/dist/chat-ui/index.cjs +15 -1
  81. package/dist/chat-ui/index.cjs.map +1 -1
  82. package/dist/chat-ui/index.d.ts +6 -3
  83. package/dist/chat-ui/index.js +16 -2
  84. package/dist/chat-ui/index.js.map +1 -1
  85. package/dist/chat-ui/mentions/mention-chip.cjs +31 -4
  86. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  87. package/dist/chat-ui/mentions/mention-chip.js +31 -4
  88. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  89. package/dist/chat-ui/messages/message-components.cjs +19 -1
  90. package/dist/chat-ui/messages/message-components.cjs.map +1 -1
  91. package/dist/chat-ui/messages/message-components.d.ts +13 -0
  92. package/dist/chat-ui/messages/message-components.js +19 -1
  93. package/dist/chat-ui/messages/message-components.js.map +1 -1
  94. package/dist/chat-ui/messages/runtime-message.cjs +2 -1
  95. package/dist/chat-ui/messages/runtime-message.cjs.map +1 -1
  96. package/dist/chat-ui/messages/runtime-message.js +2 -1
  97. package/dist/chat-ui/messages/runtime-message.js.map +1 -1
  98. package/dist/chat-ui/messages/statewire-continue-run.cjs +1 -0
  99. package/dist/chat-ui/messages/statewire-continue-run.cjs.map +1 -1
  100. package/dist/chat-ui/messages/statewire-continue-run.js +1 -0
  101. package/dist/chat-ui/messages/statewire-continue-run.js.map +1 -1
  102. package/dist/chat-ui/messages/user-edit-composer.cjs +3 -1
  103. package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
  104. package/dist/chat-ui/messages/user-edit-composer.js +3 -1
  105. package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
  106. package/dist/chat-ui/messages/wake-marker.cjs +84 -0
  107. package/dist/chat-ui/messages/wake-marker.cjs.map +1 -0
  108. package/dist/chat-ui/messages/wake-marker.d.ts +16 -0
  109. package/dist/chat-ui/messages/wake-marker.js +84 -0
  110. package/dist/chat-ui/messages/wake-marker.js.map +1 -0
  111. package/dist/chat-ui/rich-text/chat-blocks.cjs +177 -0
  112. package/dist/chat-ui/rich-text/chat-blocks.cjs.map +1 -0
  113. package/dist/chat-ui/rich-text/chat-blocks.d.ts +58 -0
  114. package/dist/chat-ui/rich-text/chat-blocks.js +177 -0
  115. package/dist/chat-ui/rich-text/chat-blocks.js.map +1 -0
  116. package/dist/chat-ui/rich-text/chat-link.cjs +69 -0
  117. package/dist/chat-ui/rich-text/chat-link.cjs.map +1 -0
  118. package/dist/chat-ui/rich-text/chat-link.d.ts +24 -0
  119. package/dist/chat-ui/rich-text/chat-link.js +69 -0
  120. package/dist/chat-ui/rich-text/chat-link.js.map +1 -0
  121. package/dist/chat-ui/rich-text/chat-message-body.cjs +144 -18
  122. package/dist/chat-ui/rich-text/chat-message-body.cjs.map +1 -1
  123. package/dist/chat-ui/rich-text/chat-message-body.d.ts +10 -1
  124. package/dist/chat-ui/rich-text/chat-message-body.js +149 -23
  125. package/dist/chat-ui/rich-text/chat-message-body.js.map +1 -1
  126. package/dist/chat-ui/rich-text/chat-node-views.cjs +38 -5
  127. package/dist/chat-ui/rich-text/chat-node-views.cjs.map +1 -1
  128. package/dist/chat-ui/rich-text/chat-node-views.d.ts +11 -0
  129. package/dist/chat-ui/rich-text/chat-node-views.js +39 -6
  130. package/dist/chat-ui/rich-text/chat-node-views.js.map +1 -1
  131. package/dist/chat-ui/rich-text/chat-text-schema.cjs +20 -3
  132. package/dist/chat-ui/rich-text/chat-text-schema.cjs.map +1 -1
  133. package/dist/chat-ui/rich-text/chat-text-schema.d.ts +15 -2
  134. package/dist/chat-ui/rich-text/chat-text-schema.js +20 -3
  135. package/dist/chat-ui/rich-text/chat-text-schema.js.map +1 -1
  136. package/dist/chat-ui/rich-text/citation-anchors.cjs +139 -0
  137. package/dist/chat-ui/rich-text/citation-anchors.cjs.map +1 -0
  138. package/dist/chat-ui/rich-text/citation-anchors.d.ts +74 -0
  139. package/dist/chat-ui/rich-text/citation-anchors.js +139 -0
  140. package/dist/chat-ui/rich-text/citation-anchors.js.map +1 -0
  141. package/dist/chat-ui/statewire.cjs +8 -1
  142. package/dist/chat-ui/statewire.cjs.map +1 -1
  143. package/dist/chat-ui/statewire.d.ts +2 -0
  144. package/dist/chat-ui/statewire.js +8 -1
  145. package/dist/chat-ui/statewire.js.map +1 -1
  146. package/dist/chat-ui/thread/chat-composer-dock.cjs +27 -6
  147. package/dist/chat-ui/thread/chat-composer-dock.cjs.map +1 -1
  148. package/dist/chat-ui/thread/chat-composer-dock.d.ts +21 -1
  149. package/dist/chat-ui/thread/chat-composer-dock.js +28 -7
  150. package/dist/chat-ui/thread/chat-composer-dock.js.map +1 -1
  151. package/dist/chat-ui/thread/statewire-history-edge.cjs +12 -13
  152. package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
  153. package/dist/chat-ui/thread/statewire-history-edge.d.ts +11 -10
  154. package/dist/chat-ui/thread/statewire-history-edge.js +13 -14
  155. package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
  156. package/dist/chat-ui/thread/thread-loading-overlay.cjs +31 -0
  157. package/dist/chat-ui/thread/thread-loading-overlay.cjs.map +1 -0
  158. package/dist/chat-ui/thread/thread-loading-overlay.d.ts +14 -0
  159. package/dist/chat-ui/thread/thread-loading-overlay.js +31 -0
  160. package/dist/chat-ui/thread/thread-loading-overlay.js.map +1 -0
  161. package/dist/composer/ComposerDropZone.cjs +8 -1
  162. package/dist/composer/ComposerDropZone.cjs.map +1 -1
  163. package/dist/composer/ComposerDropZone.d.ts +6 -0
  164. package/dist/composer/ComposerDropZone.js +9 -2
  165. package/dist/composer/ComposerDropZone.js.map +1 -1
  166. package/dist/diagnostics/errors.cjs +20 -0
  167. package/dist/diagnostics/errors.cjs.map +1 -1
  168. package/dist/diagnostics/errors.d.ts +20 -0
  169. package/dist/diagnostics/errors.js +20 -0
  170. package/dist/diagnostics/errors.js.map +1 -1
  171. package/dist/index.cjs +47 -1
  172. package/dist/index.cjs.map +1 -1
  173. package/dist/index.d.ts +26 -7
  174. package/dist/index.js +51 -5
  175. package/dist/index.js.map +1 -1
  176. package/dist/lib/use-prefers-reduced-motion.cjs +29 -0
  177. package/dist/lib/use-prefers-reduced-motion.cjs.map +1 -0
  178. package/dist/lib/use-prefers-reduced-motion.d.ts +6 -0
  179. package/dist/lib/use-prefers-reduced-motion.js +29 -0
  180. package/dist/lib/use-prefers-reduced-motion.js.map +1 -0
  181. package/dist/packages/agent-runtime/src/conversation-index.cjs.map +1 -1
  182. package/dist/packages/agent-runtime/src/conversation-index.js.map +1 -1
  183. package/dist/packages/agent-runtime/src/conversation-list.cjs +59 -0
  184. package/dist/packages/agent-runtime/src/conversation-list.cjs.map +1 -1
  185. package/dist/packages/agent-runtime/src/conversation-list.js +60 -1
  186. package/dist/packages/agent-runtime/src/conversation-list.js.map +1 -1
  187. package/dist/packages/agent-runtime/src/deep-agent-transport-recipe.cjs +5 -3
  188. package/dist/packages/agent-runtime/src/deep-agent-transport-recipe.cjs.map +1 -1
  189. package/dist/packages/agent-runtime/src/deep-agent-transport-recipe.js +5 -3
  190. package/dist/packages/agent-runtime/src/deep-agent-transport-recipe.js.map +1 -1
  191. package/dist/packages/agent-runtime/src/edit-command.cjs +21 -17
  192. package/dist/packages/agent-runtime/src/edit-command.cjs.map +1 -1
  193. package/dist/packages/agent-runtime/src/edit-command.js +22 -18
  194. package/dist/packages/agent-runtime/src/edit-command.js.map +1 -1
  195. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +9 -11
  196. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  197. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +9 -11
  198. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  199. package/dist/packages/agent-runtime/src/github-wake-event.cjs +86 -0
  200. package/dist/packages/agent-runtime/src/github-wake-event.cjs.map +1 -0
  201. package/dist/packages/agent-runtime/src/github-wake-event.js +86 -0
  202. package/dist/packages/agent-runtime/src/github-wake-event.js.map +1 -0
  203. package/dist/packages/agent-runtime/src/message-window.cjs +150 -42
  204. package/dist/packages/agent-runtime/src/message-window.cjs.map +1 -1
  205. package/dist/packages/agent-runtime/src/message-window.js +151 -43
  206. package/dist/packages/agent-runtime/src/message-window.js.map +1 -1
  207. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs +13 -9
  208. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
  209. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js +13 -9
  210. package/dist/packages/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
  211. package/dist/packages/agent-runtime/src/statewire-suspended.cjs +1 -3
  212. package/dist/packages/agent-runtime/src/statewire-suspended.cjs.map +1 -1
  213. package/dist/packages/agent-runtime/src/statewire-suspended.js +1 -3
  214. package/dist/packages/agent-runtime/src/statewire-suspended.js.map +1 -1
  215. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.cjs.map +1 -1
  216. package/dist/packages/agent-runtime/src/use-deep-agent-edit-submit.js.map +1 -1
  217. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs +61 -30
  218. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  219. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js +59 -28
  220. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  221. package/dist/packages/agent-runtime/src/use-deep-agent-window-sync.cjs +56 -36
  222. package/dist/packages/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -1
  223. package/dist/packages/agent-runtime/src/use-deep-agent-window-sync.js +59 -39
  224. package/dist/packages/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -1
  225. package/dist/packages/react/package.json.cjs +1 -1
  226. package/dist/packages/react/package.json.js +1 -1
  227. package/dist/provider/AthenaContext.cjs.map +1 -1
  228. package/dist/provider/AthenaContext.d.ts +3 -0
  229. package/dist/provider/AthenaContext.js.map +1 -1
  230. package/dist/provider/AthenaProvider.cjs +93 -45
  231. package/dist/provider/AthenaProvider.cjs.map +1 -1
  232. package/dist/provider/AthenaProvider.d.ts +6 -1
  233. package/dist/provider/AthenaProvider.js +97 -49
  234. package/dist/provider/AthenaProvider.js.map +1 -1
  235. package/dist/runtime/statewire-lifecycle.cjs +2 -1
  236. package/dist/runtime/statewire-lifecycle.cjs.map +1 -1
  237. package/dist/runtime/statewire-lifecycle.js +2 -1
  238. package/dist/runtime/statewire-lifecycle.js.map +1 -1
  239. package/dist/runtime/useAthenaStatewireRuntime.cjs +3 -2
  240. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  241. package/dist/runtime/useAthenaStatewireRuntime.d.ts +8 -12
  242. package/dist/runtime/useAthenaStatewireRuntime.js +3 -2
  243. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  244. package/dist/search/useAthenaRepoSearch.cjs +94 -0
  245. package/dist/search/useAthenaRepoSearch.cjs.map +1 -0
  246. package/dist/search/useAthenaRepoSearch.d.ts +95 -0
  247. package/dist/search/useAthenaRepoSearch.js +94 -0
  248. package/dist/search/useAthenaRepoSearch.js.map +1 -0
  249. package/dist/styles.css +1 -1
  250. package/dist/threads/SessionList.cjs +486 -0
  251. package/dist/threads/SessionList.cjs.map +1 -0
  252. package/dist/threads/SessionList.d.ts +60 -0
  253. package/dist/threads/SessionList.js +486 -0
  254. package/dist/threads/SessionList.js.map +1 -0
  255. package/dist/threads/api.cjs +112 -4
  256. package/dist/threads/api.cjs.map +1 -1
  257. package/dist/threads/api.d.ts +128 -1
  258. package/dist/threads/api.js +113 -5
  259. package/dist/threads/api.js.map +1 -1
  260. package/dist/threads/conversation-list-fetcher.cjs +27 -3
  261. package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
  262. package/dist/threads/conversation-list-fetcher.d.ts +16 -3
  263. package/dist/threads/conversation-list-fetcher.js +27 -3
  264. package/dist/threads/conversation-list-fetcher.js.map +1 -1
  265. package/dist/threads/index.d.ts +5 -1
  266. package/dist/threads/session-list-model.cjs +130 -0
  267. package/dist/threads/session-list-model.cjs.map +1 -0
  268. package/dist/threads/session-list-model.d.ts +62 -0
  269. package/dist/threads/session-list-model.js +130 -0
  270. package/dist/threads/session-list-model.js.map +1 -0
  271. package/dist/threads/statewire-thread-list.cjs +220 -38
  272. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  273. package/dist/threads/statewire-thread-list.d.ts +152 -2
  274. package/dist/threads/statewire-thread-list.js +222 -40
  275. package/dist/threads/statewire-thread-list.js.map +1 -1
  276. package/dist/threads/thread-prefetcher.cjs +63 -0
  277. package/dist/threads/thread-prefetcher.cjs.map +1 -0
  278. package/dist/threads/thread-prefetcher.d.ts +54 -0
  279. package/dist/threads/thread-prefetcher.js +63 -0
  280. package/dist/threads/thread-prefetcher.js.map +1 -0
  281. package/dist/threads/use-athena-session-attention.cjs +22 -0
  282. package/dist/threads/use-athena-session-attention.cjs.map +1 -0
  283. package/dist/threads/use-athena-session-attention.d.ts +24 -0
  284. package/dist/threads/use-athena-session-attention.js +22 -0
  285. package/dist/threads/use-athena-session-attention.js.map +1 -0
  286. package/dist/threads/use-rest-conversation-history.cjs +25 -2
  287. package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
  288. package/dist/threads/use-rest-conversation-history.d.ts +8 -1
  289. package/dist/threads/use-rest-conversation-history.js +25 -2
  290. package/dist/threads/use-rest-conversation-history.js.map +1 -1
  291. package/dist/threads/use-statewire-conversation-history.cjs +40 -16
  292. package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
  293. package/dist/threads/use-statewire-conversation-history.d.ts +13 -0
  294. package/dist/threads/use-statewire-conversation-history.js +40 -16
  295. package/dist/threads/use-statewire-conversation-history.js.map +1 -1
  296. package/dist/threads/use-thread-archive.cjs +128 -0
  297. package/dist/threads/use-thread-archive.cjs.map +1 -0
  298. package/dist/threads/use-thread-archive.d.ts +26 -0
  299. package/dist/threads/use-thread-archive.js +128 -0
  300. package/dist/threads/use-thread-archive.js.map +1 -0
  301. package/dist/threads/use-thread-favorite.cjs +53 -0
  302. package/dist/threads/use-thread-favorite.cjs.map +1 -0
  303. package/dist/threads/use-thread-favorite.d.ts +49 -0
  304. package/dist/threads/use-thread-favorite.js +53 -0
  305. package/dist/threads/use-thread-favorite.js.map +1 -0
  306. package/dist/threads/use-thread-field-override.cjs +184 -0
  307. package/dist/threads/use-thread-field-override.cjs.map +1 -0
  308. package/dist/threads/use-thread-field-override.d.ts +55 -0
  309. package/dist/threads/use-thread-field-override.js +184 -0
  310. package/dist/threads/use-thread-field-override.js.map +1 -0
  311. package/dist/threads/use-thread-rename.cjs +53 -0
  312. package/dist/threads/use-thread-rename.cjs.map +1 -0
  313. package/dist/threads/use-thread-rename.d.ts +48 -0
  314. package/dist/threads/use-thread-rename.js +53 -0
  315. package/dist/threads/use-thread-rename.js.map +1 -0
  316. package/dist/tools/index.cjs +5 -1
  317. package/dist/tools/index.cjs.map +1 -1
  318. package/dist/tools/index.d.ts +1 -1
  319. package/dist/tools/index.js +7 -3
  320. package/dist/tools/index.js.map +1 -1
  321. package/dist/tools/misc-tool-uis.cjs +2 -1
  322. package/dist/tools/misc-tool-uis.cjs.map +1 -1
  323. package/dist/tools/misc-tool-uis.js +2 -1
  324. package/dist/tools/misc-tool-uis.js.map +1 -1
  325. package/dist/tools/primitives.cjs +25 -2
  326. package/dist/tools/primitives.cjs.map +1 -1
  327. package/dist/tools/primitives.d.ts +9 -2
  328. package/dist/tools/primitives.js +26 -3
  329. package/dist/tools/primitives.js.map +1 -1
  330. package/dist/tools/tool-appearance.cjs +11 -0
  331. package/dist/tools/tool-appearance.cjs.map +1 -0
  332. package/dist/tools/tool-appearance.d.ts +29 -0
  333. package/dist/tools/tool-appearance.js +11 -0
  334. package/dist/tools/tool-appearance.js.map +1 -0
  335. package/dist/tools/tool-fallback.cjs +211 -13
  336. package/dist/tools/tool-fallback.cjs.map +1 -1
  337. package/dist/tools/tool-fallback.d.ts +31 -4
  338. package/dist/tools/tool-fallback.js +214 -16
  339. package/dist/tools/tool-fallback.js.map +1 -1
  340. package/dist/tools/use-elapsed-seconds.cjs +24 -0
  341. package/dist/tools/use-elapsed-seconds.cjs.map +1 -0
  342. package/dist/tools/use-elapsed-seconds.d.ts +10 -0
  343. package/dist/tools/use-elapsed-seconds.js +24 -0
  344. package/dist/tools/use-elapsed-seconds.js.map +1 -0
  345. package/dist/tools/voice-update-tool-ui.cjs +2 -1
  346. package/dist/tools/voice-update-tool-ui.cjs.map +1 -1
  347. package/dist/tools/voice-update-tool-ui.js +2 -1
  348. package/dist/tools/voice-update-tool-ui.js.map +1 -1
  349. package/package.json +1 -1
  350. package/src/chat-ui/tailwind-preset.js +14 -1
@@ -0,0 +1,49 @@
1
+ import type { RestConversationAuth } from './use-rest-conversation-history';
2
+ import { resetThreadFieldUnavailableWarnings } from './use-thread-field-override';
3
+ export interface ThreadFavoriteTarget {
4
+ threadId: string;
5
+ isFavorited: boolean;
6
+ }
7
+ /** The two fields the override layer reads and writes on a list row. */
8
+ export interface FavoriteRow {
9
+ threadId: string;
10
+ isFavorited?: boolean;
11
+ }
12
+ export interface UseThreadFavoriteOptions {
13
+ backendUrl: string;
14
+ /** Non-secret identity in the mutation key (as the read-state mutation keys). */
15
+ principalId: string;
16
+ getAuth: () => RestConversationAuth | Promise<RestConversationAuth>;
17
+ /**
18
+ * The rows as the list sources currently report them, BEFORE overrides. An
19
+ * override is dropped once its row reports the same value.
20
+ */
21
+ rows: readonly FavoriteRow[];
22
+ /** Write the server's answer into the list caches (`patchConversation`). */
23
+ onServerTruth?: (result: ThreadFavoriteTarget) => void;
24
+ }
25
+ export interface ThreadFavoriteToggle {
26
+ /** Optimistic, then server-confirmed, values layered over the list sources, keyed by thread id. */
27
+ overrides: Readonly<Record<string, boolean>>;
28
+ /**
29
+ * Pin or unpin a thread. Flips the row at once; while that thread's write
30
+ * is in flight the call only records the desired value (see module doc).
31
+ */
32
+ setFavorited: (target: ThreadFavoriteTarget) => void;
33
+ /** True while any toggle is in flight. */
34
+ isPending: boolean;
35
+ /** The threads whose toggle is in flight — a pin control disables itself for these. */
36
+ pendingThreadIds: ReadonlySet<string>;
37
+ /**
38
+ * `false` once the backend answered 404 / 405: the route is not deployed
39
+ * there, and the UI shows the read-only pin instead of a toggle.
40
+ */
41
+ isAvailable: boolean;
42
+ }
43
+ /** Test seam: forget which backends were warned about (shared with the other field writes). */
44
+ export declare const resetThreadFavoriteUnavailableWarnings: typeof resetThreadFieldUnavailableWarnings;
45
+ export declare function useThreadFavorite({ backendUrl, principalId, getAuth, rows, onServerTruth, }: UseThreadFavoriteOptions): ThreadFavoriteToggle;
46
+ /** The row with its override applied, or the row itself when it has none. */
47
+ export declare function withFavoriteOverride<T extends FavoriteRow>(row: T, overrides: Readonly<Record<string, boolean>>): T;
48
+ /** Every row with its override applied. */
49
+ export declare function withFavoriteOverrides<T extends FavoriteRow>(rows: readonly T[], overrides: Readonly<Record<string, boolean>>): T[];
@@ -0,0 +1,53 @@
1
+ import { useCallback } from "react";
2
+ import { setThreadFavorite, isThreadFavoriteUnavailableError } from "./api.js";
3
+ import { useThreadFieldOverride } from "./use-thread-field-override.js";
4
+ const FAVORITE_UNAVAILABLE_WARNING = "[AthenaSDK] This backend has no POST /api/conversations/threads/favorite route yet; session pins stay read-only until it deploys.";
5
+ const readFavorite = (row) => row.isFavorited ?? false;
6
+ function useThreadFavorite({
7
+ backendUrl,
8
+ principalId,
9
+ getAuth,
10
+ rows,
11
+ onServerTruth
12
+ }) {
13
+ const field = useThreadFieldOverride({
14
+ field: "favorite",
15
+ backendUrl,
16
+ principalId,
17
+ getAuth,
18
+ rows,
19
+ readRow: readFavorite,
20
+ write: async (auth, { threadId, value }) => {
21
+ const result = await setThreadFavorite(backendUrl, auth, { threadId, isFavorited: value });
22
+ return { threadId: result.thread_id, value: result.is_favorited };
23
+ },
24
+ isUnavailableError: isThreadFavoriteUnavailableError,
25
+ unavailableWarning: FAVORITE_UNAVAILABLE_WARNING,
26
+ onServerTruth: onServerTruth ? ({ threadId, value }) => onServerTruth({ threadId, isFavorited: value }) : void 0
27
+ });
28
+ const set = field.set;
29
+ const setFavorited = useCallback(
30
+ ({ threadId, isFavorited }) => set({ threadId, value: isFavorited }),
31
+ [set]
32
+ );
33
+ return {
34
+ overrides: field.overrides,
35
+ setFavorited,
36
+ isPending: field.isPending,
37
+ pendingThreadIds: field.pendingThreadIds,
38
+ isAvailable: field.isAvailable
39
+ };
40
+ }
41
+ function withFavoriteOverride(row, overrides) {
42
+ const override = overrides[row.threadId];
43
+ return override === void 0 || override === row.isFavorited ? row : { ...row, isFavorited: override };
44
+ }
45
+ function withFavoriteOverrides(rows, overrides) {
46
+ return rows.map((row) => withFavoriteOverride(row, overrides));
47
+ }
48
+ export {
49
+ useThreadFavorite,
50
+ withFavoriteOverride,
51
+ withFavoriteOverrides
52
+ };
53
+ //# sourceMappingURL=use-thread-favorite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-thread-favorite.js","sources":["../../src/threads/use-thread-favorite.ts"],"sourcesContent":["/**\n * The pin toggle behind `SessionList` and the conversation-history hooks:\n * `useThreadFieldOverride` over `POST /api/conversations/threads/favorite`\n * (see that module for the optimistic-override, rollback, serialization and\n * identity-scoping contract). A 404 / 405 marks the toggle unavailable — the\n * backend predates the route — so the UI falls back to the read-only pin\n * instead of failing on every click.\n */\nimport { useCallback } from 'react';\nimport { isThreadFavoriteUnavailableError, setThreadFavorite } from './api';\nimport type { RestConversationAuth } from './use-rest-conversation-history';\nimport {\n resetThreadFieldUnavailableWarnings,\n type ThreadFieldTarget,\n useThreadFieldOverride,\n} from './use-thread-field-override';\n\nexport interface ThreadFavoriteTarget {\n threadId: string;\n isFavorited: boolean;\n}\n\n/** The two fields the override layer reads and writes on a list row. */\nexport interface FavoriteRow {\n threadId: string;\n isFavorited?: boolean;\n}\n\nexport interface UseThreadFavoriteOptions {\n backendUrl: string;\n /** Non-secret identity in the mutation key (as the read-state mutation keys). */\n principalId: string;\n getAuth: () => RestConversationAuth | Promise<RestConversationAuth>;\n /**\n * The rows as the list sources currently report them, BEFORE overrides. An\n * override is dropped once its row reports the same value.\n */\n rows: readonly FavoriteRow[];\n /** Write the server's answer into the list caches (`patchConversation`). */\n onServerTruth?: (result: ThreadFavoriteTarget) => void;\n}\n\nexport interface ThreadFavoriteToggle {\n /** Optimistic, then server-confirmed, values layered over the list sources, keyed by thread id. */\n overrides: Readonly<Record<string, boolean>>;\n /**\n * Pin or unpin a thread. Flips the row at once; while that thread's write\n * is in flight the call only records the desired value (see module doc).\n */\n setFavorited: (target: ThreadFavoriteTarget) => void;\n /** True while any toggle is in flight. */\n isPending: boolean;\n /** The threads whose toggle is in flight — a pin control disables itself for these. */\n pendingThreadIds: ReadonlySet<string>;\n /**\n * `false` once the backend answered 404 / 405: the route is not deployed\n * there, and the UI shows the read-only pin instead of a toggle.\n */\n isAvailable: boolean;\n}\n\n/** Test seam: forget which backends were warned about (shared with the other field writes). */\nexport const resetThreadFavoriteUnavailableWarnings = resetThreadFieldUnavailableWarnings;\n\nconst FAVORITE_UNAVAILABLE_WARNING =\n '[AthenaSDK] This backend has no POST /api/conversations/threads/favorite route yet; session pins stay read-only until it deploys.';\n\nconst readFavorite = (row: FavoriteRow): boolean => row.isFavorited ?? false;\n\nexport function useThreadFavorite({\n backendUrl,\n principalId,\n getAuth,\n rows,\n onServerTruth,\n}: UseThreadFavoriteOptions): ThreadFavoriteToggle {\n const field = useThreadFieldOverride<boolean, FavoriteRow>({\n field: 'favorite',\n backendUrl,\n principalId,\n getAuth,\n rows,\n readRow: readFavorite,\n write: async (auth, { threadId, value }) => {\n const result = await setThreadFavorite(backendUrl, auth, { threadId, isFavorited: value });\n return { threadId: result.thread_id, value: result.is_favorited };\n },\n isUnavailableError: isThreadFavoriteUnavailableError,\n unavailableWarning: FAVORITE_UNAVAILABLE_WARNING,\n onServerTruth: onServerTruth\n ? ({ threadId, value }: ThreadFieldTarget<boolean>) =>\n onServerTruth({ threadId, isFavorited: value })\n : undefined,\n });\n const set = field.set;\n const setFavorited = useCallback(\n ({ threadId, isFavorited }: ThreadFavoriteTarget) => set({ threadId, value: isFavorited }),\n [set],\n );\n return {\n overrides: field.overrides,\n setFavorited,\n isPending: field.isPending,\n pendingThreadIds: field.pendingThreadIds,\n isAvailable: field.isAvailable,\n };\n}\n\n/** The row with its override applied, or the row itself when it has none. */\nexport function withFavoriteOverride<T extends FavoriteRow>(\n row: T,\n overrides: Readonly<Record<string, boolean>>,\n): T {\n const override = overrides[row.threadId];\n return override === undefined || override === row.isFavorited\n ? row\n : { ...row, isFavorited: override };\n}\n\n/** Every row with its override applied. */\nexport function withFavoriteOverrides<T extends FavoriteRow>(\n rows: readonly T[],\n overrides: Readonly<Record<string, boolean>>,\n): T[] {\n return rows.map((row) => withFavoriteOverride(row, overrides));\n}\n"],"names":[],"mappings":";;;AAgEA,MAAM,+BACJ;AAEF,MAAM,eAAe,CAAC,QAA8B,IAAI,eAAe;AAEhE,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAmD;AACjD,QAAM,QAAQ,uBAA6C;AAAA,IACzD,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,OAAO,OAAO,MAAM,EAAE,UAAU,YAAY;AAC1C,YAAM,SAAS,MAAM,kBAAkB,YAAY,MAAM,EAAE,UAAU,aAAa,OAAO;AACzF,aAAO,EAAE,UAAU,OAAO,WAAW,OAAO,OAAO,aAAA;AAAA,IACrD;AAAA,IACA,oBAAoB;AAAA,IACpB,oBAAoB;AAAA,IACpB,eAAe,gBACX,CAAC,EAAE,UAAU,MAAA,MACX,cAAc,EAAE,UAAU,aAAa,MAAA,CAAO,IAChD;AAAA,EAAA,CACL;AACD,QAAM,MAAM,MAAM;AAClB,QAAM,eAAe;AAAA,IACnB,CAAC,EAAE,UAAU,kBAAwC,IAAI,EAAE,UAAU,OAAO,aAAa;AAAA,IACzF,CAAC,GAAG;AAAA,EAAA;AAEN,SAAO;AAAA,IACL,WAAW,MAAM;AAAA,IACjB;AAAA,IACA,WAAW,MAAM;AAAA,IACjB,kBAAkB,MAAM;AAAA,IACxB,aAAa,MAAM;AAAA,EAAA;AAEvB;AAGO,SAAS,qBACd,KACA,WACG;AACH,QAAM,WAAW,UAAU,IAAI,QAAQ;AACvC,SAAO,aAAa,UAAa,aAAa,IAAI,cAC9C,MACA,EAAE,GAAG,KAAK,aAAa,SAAA;AAC7B;AAGO,SAAS,sBACd,MACA,WACK;AACL,SAAO,KAAK,IAAI,CAAC,QAAQ,qBAAqB,KAAK,SAAS,CAAC;AAC/D;"}
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const reactQuery = require("@tanstack/react-query");
4
+ const react = require("react");
5
+ const EMPTY_PENDING = /* @__PURE__ */ new Set();
6
+ function fieldScope(backendUrl, principalId) {
7
+ return `${backendUrl}::${principalId}`;
8
+ }
9
+ function withPending(pending, threadId, isPending) {
10
+ if (pending.has(threadId) === isPending) return pending;
11
+ const next = new Set(pending);
12
+ if (isPending) next.add(threadId);
13
+ else next.delete(threadId);
14
+ return next;
15
+ }
16
+ const strictEquals = (a, b) => a === b;
17
+ const warnedUnavailableBackends = /* @__PURE__ */ new Set();
18
+ function useThreadFieldOverride({
19
+ field,
20
+ backendUrl,
21
+ principalId,
22
+ getAuth,
23
+ rows,
24
+ readRow,
25
+ write,
26
+ isUnavailableError,
27
+ unavailableWarning,
28
+ onServerTruth,
29
+ equals = strictEquals
30
+ }) {
31
+ const scope = fieldScope(backendUrl, principalId);
32
+ const [state, setState] = react.useState(() => ({
33
+ scope,
34
+ overrides: {},
35
+ pending: EMPTY_PENDING
36
+ }));
37
+ if (state.scope !== scope) {
38
+ setState({ scope, overrides: {}, pending: EMPTY_PENDING });
39
+ }
40
+ const emptyOverridesRef = react.useRef(Object.freeze({}));
41
+ const overrides = state.scope === scope ? state.overrides : emptyOverridesRef.current;
42
+ const pendingThreadIds = state.scope === scope ? state.pending : EMPTY_PENDING;
43
+ const [unavailableBackend, setUnavailableBackend] = react.useState(null);
44
+ const scopeRef = react.useRef(scope);
45
+ scopeRef.current = scope;
46
+ const overridesRef = react.useRef(overrides);
47
+ overridesRef.current = overrides;
48
+ const readRowRef = react.useRef(readRow);
49
+ readRowRef.current = readRow;
50
+ const writeRef = react.useRef(write);
51
+ writeRef.current = write;
52
+ const isUnavailableErrorRef = react.useRef(isUnavailableError);
53
+ isUnavailableErrorRef.current = isUnavailableError;
54
+ const unavailableWarningRef = react.useRef(unavailableWarning);
55
+ unavailableWarningRef.current = unavailableWarning;
56
+ const onServerTruthRef = react.useRef(onServerTruth);
57
+ onServerTruthRef.current = onServerTruth;
58
+ const equalsRef = react.useRef(equals);
59
+ equalsRef.current = equals;
60
+ const inFlightRef = react.useRef(/* @__PURE__ */ new Map());
61
+ const queuedRef = react.useRef(/* @__PURE__ */ new Map());
62
+ const seqRef = react.useRef(0);
63
+ const bookkeepingScopeRef = react.useRef(scope);
64
+ if (bookkeepingScopeRef.current !== scope) {
65
+ bookkeepingScopeRef.current = scope;
66
+ inFlightRef.current = /* @__PURE__ */ new Map();
67
+ queuedRef.current = /* @__PURE__ */ new Map();
68
+ }
69
+ const update = react.useCallback(
70
+ (forScope, patch) => {
71
+ setState((current) => {
72
+ const base = current.scope === forScope ? current : { scope: forScope, overrides: {}, pending: EMPTY_PENDING };
73
+ const next = patch(base);
74
+ return next === base && current.scope === forScope ? current : next;
75
+ });
76
+ },
77
+ []
78
+ );
79
+ react.useEffect(() => {
80
+ const caughtUp = Object.keys(overrides).filter((threadId) => {
81
+ if (pendingThreadIds.has(threadId)) return false;
82
+ const row = rows.find((candidate) => candidate.threadId === threadId);
83
+ if (row === void 0) return false;
84
+ const reported = readRowRef.current(row);
85
+ const override = overrides[threadId];
86
+ return reported !== void 0 && override !== void 0 && equalsRef.current(reported, override);
87
+ });
88
+ if (caughtUp.length === 0) return;
89
+ update(scope, (current) => {
90
+ const next = { ...current.overrides };
91
+ for (const threadId of caughtUp) delete next[threadId];
92
+ return { ...current, overrides: next };
93
+ });
94
+ }, [overrides, pendingThreadIds, rows, scope, update]);
95
+ const mutation = reactQuery.useMutation({
96
+ mutationKey: ["conversation-history", field, backendUrl, principalId],
97
+ mutationFn: async ({ threadId, value }) => writeRef.current(await getAuth(), { threadId, value }),
98
+ onError: (error, write2) => {
99
+ if (isUnavailableErrorRef.current(error)) {
100
+ setUnavailableBackend(backendUrl);
101
+ const warnKey = `${field}::${backendUrl}`;
102
+ if (!warnedUnavailableBackends.has(warnKey)) {
103
+ warnedUnavailableBackends.add(warnKey);
104
+ console.warn(unavailableWarningRef.current);
105
+ }
106
+ }
107
+ if (!isCurrentWrite(write2)) return;
108
+ inFlightRef.current.delete(write2.threadId);
109
+ queuedRef.current.delete(write2.threadId);
110
+ update(write2.scope, (current) => {
111
+ const overridesNext = { ...current.overrides };
112
+ if (write2.previous === void 0) delete overridesNext[write2.threadId];
113
+ else overridesNext[write2.threadId] = write2.previous;
114
+ return {
115
+ ...current,
116
+ overrides: overridesNext,
117
+ pending: withPending(current.pending, write2.threadId, false)
118
+ };
119
+ });
120
+ },
121
+ onSuccess: (truth, write2) => {
122
+ var _a;
123
+ if (!isCurrentWrite(write2)) return;
124
+ inFlightRef.current.delete(write2.threadId);
125
+ const queued = queuedRef.current.get(write2.threadId);
126
+ const hadQueued = queuedRef.current.has(write2.threadId);
127
+ queuedRef.current.delete(write2.threadId);
128
+ (_a = onServerTruthRef.current) == null ? void 0 : _a.call(onServerTruthRef, truth);
129
+ if (hadQueued && queued !== void 0 && !equalsRef.current(queued, truth.value)) {
130
+ startWrite(write2.scope, {
131
+ threadId: write2.threadId,
132
+ value: queued,
133
+ previous: truth.value
134
+ });
135
+ return;
136
+ }
137
+ update(write2.scope, (current) => ({
138
+ ...current,
139
+ overrides: { ...current.overrides, [truth.threadId]: truth.value },
140
+ pending: withPending(current.pending, truth.threadId, false)
141
+ }));
142
+ }
143
+ });
144
+ const mutate = mutation.mutate;
145
+ function isCurrentWrite(write2) {
146
+ return scopeRef.current === write2.scope && inFlightRef.current.get(write2.threadId) === write2.seq;
147
+ }
148
+ function startWrite(forScope, write2) {
149
+ const seq = ++seqRef.current;
150
+ inFlightRef.current.set(write2.threadId, seq);
151
+ update(forScope, (current) => ({
152
+ ...current,
153
+ pending: withPending(current.pending, write2.threadId, true)
154
+ }));
155
+ mutate({ ...write2, scope: forScope, seq });
156
+ }
157
+ const startWriteRef = react.useRef(startWrite);
158
+ startWriteRef.current = startWrite;
159
+ const set = react.useCallback(
160
+ ({ threadId, value }) => {
161
+ const forScope = scopeRef.current;
162
+ const previous = overridesRef.current[threadId];
163
+ update(forScope, (current) => ({
164
+ ...current,
165
+ overrides: { ...current.overrides, [threadId]: value }
166
+ }));
167
+ if (inFlightRef.current.has(threadId)) {
168
+ queuedRef.current.set(threadId, value);
169
+ return;
170
+ }
171
+ startWriteRef.current(forScope, { threadId, value, previous });
172
+ },
173
+ [update]
174
+ );
175
+ return {
176
+ overrides,
177
+ set,
178
+ isPending: pendingThreadIds.size > 0,
179
+ pendingThreadIds,
180
+ isAvailable: unavailableBackend !== backendUrl
181
+ };
182
+ }
183
+ exports.useThreadFieldOverride = useThreadFieldOverride;
184
+ //# sourceMappingURL=use-thread-field-override.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-thread-field-override.cjs","sources":["../../src/threads/use-thread-field-override.ts"],"sourcesContent":["/**\n * The optimistic per-thread field write behind the pin (`useThreadFavorite`)\n * and the rename (`useThreadRename`): one TanStack mutation over a REST\n * route with an override map layered over the list rows.\n *\n * Why overrides rather than a cache patch alone: a row's field is folded from\n * two sources — the fetched page and the pushed conversation index — and a\n * push that predates the write would clobber a patched page. The override\n * sits over both until the sources report the same value, then drops away so\n * they own the field again (a later write from another client is never\n * masked). A failure restores the previous value; a 404 / 405 marks the\n * write unavailable — the backend predates the route — so the UI withdraws\n * the control instead of failing on every use.\n *\n * Same-thread writes are serialized: while one is in flight, a further write\n * for that thread flips the row at once but only records the desired value,\n * and at most one follow-up write is sent — with that value — once the\n * in-flight one has answered. Two independent writes for one thread never\n * race, so the server cannot end on an earlier request's value.\n *\n * Everything the hook holds is scoped to `(backendUrl, principalId)`: a\n * credential swap under the same list reads an empty override map, and a\n * write that was in flight for the previous identity is ignored when it\n * answers, so a same-id row under the new identity never inherits the old\n * one's value.\n */\nimport { useMutation } from '@tanstack/react-query';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { RestConversationAuth } from './use-rest-conversation-history';\n\nexport interface ThreadFieldTarget<T> {\n threadId: string;\n value: T;\n}\n\n/** The one field every row must carry: its identity. */\nexport interface ThreadFieldRow {\n threadId: string;\n}\n\nexport interface UseThreadFieldOverrideOptions<T, R extends ThreadFieldRow> {\n /** Names the field: part of the mutation key and of the one-per-backend warning. */\n field: string;\n backendUrl: string;\n /** Non-secret identity in the mutation key (as the read-state mutation keys). */\n principalId: string;\n getAuth: () => RestConversationAuth | Promise<RestConversationAuth>;\n /**\n * The rows as the list sources currently report them, BEFORE overrides. An\n * override is dropped once its row reports the same value.\n */\n rows: readonly R[];\n /** The field as a source row reports it; `undefined` when the row does not carry it. */\n readRow: (row: R) => T | undefined;\n /** The write; resolves to the server's truth after it. */\n write: (auth: RestConversationAuth, target: ThreadFieldTarget<T>) => Promise<ThreadFieldTarget<T>>;\n /** True for the rejection that means the backend has no such route (404 / 405). */\n isUnavailableError: (error: unknown) => boolean;\n /** Logged once per backend when the route is found missing. */\n unavailableWarning: string;\n /** Write the server's answer into the list caches (`patchConversation`). */\n onServerTruth?: (truth: ThreadFieldTarget<T>) => void;\n /** Value equality (default `===`). */\n equals?: (a: T, b: T) => boolean;\n}\n\nexport interface ThreadFieldOverride<T> {\n /** Optimistic, then server-confirmed, values layered over the list sources, keyed by thread id. */\n overrides: Readonly<Record<string, T>>;\n /**\n * Write the field. Flips the row at once; while that thread's write is in\n * flight the call only records the desired value (see module doc).\n */\n set: (target: ThreadFieldTarget<T>) => void;\n /** True while any write is in flight. */\n isPending: boolean;\n /** The threads whose write is in flight — a control disables itself for these. */\n pendingThreadIds: ReadonlySet<string>;\n /**\n * `false` once the backend answered 404 / 405: the route is not deployed\n * there, and the UI withdraws the control.\n */\n isAvailable: boolean;\n}\n\n/** The hook's state, tagged with the identity it belongs to. */\ninterface ScopedFieldState<T> {\n scope: string;\n overrides: Record<string, T>;\n pending: ReadonlySet<string>;\n}\n\ninterface FieldWrite<T> extends ThreadFieldTarget<T> {\n /** The override before this chain of writes began; `undefined` restores the source value. */\n previous: T | undefined;\n /** The identity the write was issued for; an answer for another one is ignored. */\n scope: string;\n /** This thread's write generation; an answer for an older one is ignored. */\n seq: number;\n}\n\nconst EMPTY_PENDING: ReadonlySet<string> = new Set();\n\nfunction fieldScope(backendUrl: string, principalId: string): string {\n return `${backendUrl}::${principalId}`;\n}\n\nfunction withPending(pending: ReadonlySet<string>, threadId: string, isPending: boolean): ReadonlySet<string> {\n if (pending.has(threadId) === isPending) return pending;\n const next = new Set(pending);\n if (isPending) next.add(threadId);\n else next.delete(threadId);\n return next;\n}\n\nconst strictEquals = <T,>(a: T, b: T): boolean => a === b;\n\n/** `(field, backend)` pairs already warned about, so an older agora logs one line per page load, not one per use. */\nconst warnedUnavailableBackends = new Set<string>();\n\n/** Test seam: forget which backends were warned about. */\nexport function resetThreadFieldUnavailableWarnings(): void {\n warnedUnavailableBackends.clear();\n}\n\nexport function useThreadFieldOverride<T, R extends ThreadFieldRow>({\n field,\n backendUrl,\n principalId,\n getAuth,\n rows,\n readRow,\n write,\n isUnavailableError,\n unavailableWarning,\n onServerTruth,\n equals = strictEquals,\n}: UseThreadFieldOverrideOptions<T, R>): ThreadFieldOverride<T> {\n const scope = fieldScope(backendUrl, principalId);\n const [state, setState] = useState<ScopedFieldState<T>>(() => ({\n scope,\n overrides: {},\n pending: EMPTY_PENDING,\n }));\n // An identity swap discards everything held for the previous identity —\n // overrides AND pending rows — rather than merely hiding it: a round trip\n // back to that identity must not revive a row pinned for a write whose\n // answer has since been dropped (it would stay disabled for good). Setting\n // state during render is React's derived-state reset; the render re-runs\n // with the empty state before anything is committed.\n if (state.scope !== scope) {\n setState({ scope, overrides: {}, pending: EMPTY_PENDING });\n }\n const emptyOverridesRef = useRef<Readonly<Record<string, T>>>(Object.freeze({}) as Record<string, T>);\n const overrides = state.scope === scope ? state.overrides : emptyOverridesRef.current;\n const pendingThreadIds = state.scope === scope ? state.pending : EMPTY_PENDING;\n // The backend the route was found missing on. The route is a property of\n // the backend, not of who is asking, so this is keyed by backend alone; a\n // different backend is presumed to have it until it says otherwise.\n const [unavailableBackend, setUnavailableBackend] = useState<string | null>(null);\n\n const scopeRef = useRef(scope);\n scopeRef.current = scope;\n const overridesRef = useRef(overrides);\n overridesRef.current = overrides;\n // The per-render option closures ride refs so a consumer's fresh functions\n // never re-key the mutation or the caught-up effect.\n const readRowRef = useRef(readRow);\n readRowRef.current = readRow;\n const writeRef = useRef(write);\n writeRef.current = write;\n const isUnavailableErrorRef = useRef(isUnavailableError);\n isUnavailableErrorRef.current = isUnavailableError;\n const unavailableWarningRef = useRef(unavailableWarning);\n unavailableWarningRef.current = unavailableWarning;\n const onServerTruthRef = useRef(onServerTruth);\n onServerTruthRef.current = onServerTruth;\n const equalsRef = useRef(equals);\n equalsRef.current = equals;\n // Per-thread write bookkeeping for the CURRENT identity: the generation of\n // the write in flight, and the value a further write asked for meanwhile.\n const inFlightRef = useRef<Map<string, number>>(new Map());\n const queuedRef = useRef<Map<string, T>>(new Map());\n const seqRef = useRef(0);\n const bookkeepingScopeRef = useRef(scope);\n if (bookkeepingScopeRef.current !== scope) {\n // The previous identity's writes are abandoned with its state: their\n // answers are dropped by the scope guard below, and nothing queued\n // behind them is sent.\n bookkeepingScopeRef.current = scope;\n inFlightRef.current = new Map();\n queuedRef.current = new Map();\n }\n\n const update = useCallback(\n (forScope: string, patch: (current: ScopedFieldState<T>) => ScopedFieldState<T>) => {\n setState((current) => {\n const base: ScopedFieldState<T> =\n current.scope === forScope\n ? current\n : { scope: forScope, overrides: {}, pending: EMPTY_PENDING };\n const next = patch(base);\n return next === base && current.scope === forScope ? current : next;\n });\n },\n [],\n );\n\n useEffect(() => {\n const caughtUp = Object.keys(overrides).filter((threadId) => {\n // A row with a write in flight keeps its override whatever the sources\n // say: the first write of a chain patches the page with a value the\n // user has since moved away from, and the row must not flash it.\n if (pendingThreadIds.has(threadId)) return false;\n const row = rows.find((candidate) => candidate.threadId === threadId);\n if (row === undefined) return false;\n const reported = readRowRef.current(row);\n const override = overrides[threadId];\n return reported !== undefined && override !== undefined && equalsRef.current(reported, override);\n });\n if (caughtUp.length === 0) return;\n update(scope, (current) => {\n const next = { ...current.overrides };\n for (const threadId of caughtUp) delete next[threadId];\n return { ...current, overrides: next };\n });\n }, [overrides, pendingThreadIds, rows, scope, update]);\n\n const mutation = useMutation({\n mutationKey: ['conversation-history', field, backendUrl, principalId],\n mutationFn: async ({ threadId, value }: FieldWrite<T>) =>\n writeRef.current(await getAuth(), { threadId, value }),\n onError: (error, write) => {\n if (isUnavailableErrorRef.current(error)) {\n setUnavailableBackend(backendUrl);\n const warnKey = `${field}::${backendUrl}`;\n if (!warnedUnavailableBackends.has(warnKey)) {\n warnedUnavailableBackends.add(warnKey);\n console.warn(unavailableWarningRef.current);\n }\n }\n if (!isCurrentWrite(write)) return;\n inFlightRef.current.delete(write.threadId);\n // Whatever was asked for meanwhile is dropped with the failure: the row\n // goes back to what it showed before the chain began.\n queuedRef.current.delete(write.threadId);\n update(write.scope, (current) => {\n const overridesNext = { ...current.overrides };\n if (write.previous === undefined) delete overridesNext[write.threadId];\n else overridesNext[write.threadId] = write.previous;\n return {\n ...current,\n overrides: overridesNext,\n pending: withPending(current.pending, write.threadId, false),\n };\n });\n },\n onSuccess: (truth, write) => {\n if (!isCurrentWrite(write)) return;\n inFlightRef.current.delete(write.threadId);\n const queued = queuedRef.current.get(write.threadId);\n const hadQueued = queuedRef.current.has(write.threadId);\n queuedRef.current.delete(write.threadId);\n onServerTruthRef.current?.(truth);\n if (hadQueued && queued !== undefined && !equalsRef.current(queued, truth.value)) {\n // The row already shows the value asked for meanwhile; the server\n // does not have it yet, so send it now — one write, the latest value.\n startWrite(write.scope, {\n threadId: write.threadId,\n value: queued,\n previous: truth.value,\n });\n return;\n }\n update(write.scope, (current) => ({\n ...current,\n overrides: { ...current.overrides, [truth.threadId]: truth.value },\n pending: withPending(current.pending, truth.threadId, false),\n }));\n },\n });\n const mutate = mutation.mutate;\n\n /** True when the answer belongs to the current identity and to this thread's latest write. */\n function isCurrentWrite(write: FieldWrite<T>): boolean {\n return (\n scopeRef.current === write.scope && inFlightRef.current.get(write.threadId) === write.seq\n );\n }\n\n function startWrite(forScope: string, write: Omit<FieldWrite<T>, 'scope' | 'seq'>): void {\n const seq = ++seqRef.current;\n inFlightRef.current.set(write.threadId, seq);\n update(forScope, (current) => ({\n ...current,\n pending: withPending(current.pending, write.threadId, true),\n }));\n mutate({ ...write, scope: forScope, seq });\n }\n\n const startWriteRef = useRef(startWrite);\n startWriteRef.current = startWrite;\n\n const set = useCallback(\n ({ threadId, value }: ThreadFieldTarget<T>) => {\n const forScope = scopeRef.current;\n const previous = overridesRef.current[threadId];\n // The row flips at once whether the write goes out now or after the\n // one in flight.\n update(forScope, (current) => ({\n ...current,\n overrides: { ...current.overrides, [threadId]: value },\n }));\n if (inFlightRef.current.has(threadId)) {\n queuedRef.current.set(threadId, value);\n return;\n }\n startWriteRef.current(forScope, { threadId, value, previous });\n },\n [update],\n );\n\n return {\n overrides,\n set,\n isPending: pendingThreadIds.size > 0,\n pendingThreadIds,\n isAvailable: unavailableBackend !== backendUrl,\n };\n}\n"],"names":["useState","useRef","useCallback","useEffect","useMutation","write"],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { RestConversationAuth } from './use-rest-conversation-history';
2
+ export interface ThreadFieldTarget<T> {
3
+ threadId: string;
4
+ value: T;
5
+ }
6
+ /** The one field every row must carry: its identity. */
7
+ export interface ThreadFieldRow {
8
+ threadId: string;
9
+ }
10
+ export interface UseThreadFieldOverrideOptions<T, R extends ThreadFieldRow> {
11
+ /** Names the field: part of the mutation key and of the one-per-backend warning. */
12
+ field: string;
13
+ backendUrl: string;
14
+ /** Non-secret identity in the mutation key (as the read-state mutation keys). */
15
+ principalId: string;
16
+ getAuth: () => RestConversationAuth | Promise<RestConversationAuth>;
17
+ /**
18
+ * The rows as the list sources currently report them, BEFORE overrides. An
19
+ * override is dropped once its row reports the same value.
20
+ */
21
+ rows: readonly R[];
22
+ /** The field as a source row reports it; `undefined` when the row does not carry it. */
23
+ readRow: (row: R) => T | undefined;
24
+ /** The write; resolves to the server's truth after it. */
25
+ write: (auth: RestConversationAuth, target: ThreadFieldTarget<T>) => Promise<ThreadFieldTarget<T>>;
26
+ /** True for the rejection that means the backend has no such route (404 / 405). */
27
+ isUnavailableError: (error: unknown) => boolean;
28
+ /** Logged once per backend when the route is found missing. */
29
+ unavailableWarning: string;
30
+ /** Write the server's answer into the list caches (`patchConversation`). */
31
+ onServerTruth?: (truth: ThreadFieldTarget<T>) => void;
32
+ /** Value equality (default `===`). */
33
+ equals?: (a: T, b: T) => boolean;
34
+ }
35
+ export interface ThreadFieldOverride<T> {
36
+ /** Optimistic, then server-confirmed, values layered over the list sources, keyed by thread id. */
37
+ overrides: Readonly<Record<string, T>>;
38
+ /**
39
+ * Write the field. Flips the row at once; while that thread's write is in
40
+ * flight the call only records the desired value (see module doc).
41
+ */
42
+ set: (target: ThreadFieldTarget<T>) => void;
43
+ /** True while any write is in flight. */
44
+ isPending: boolean;
45
+ /** The threads whose write is in flight — a control disables itself for these. */
46
+ pendingThreadIds: ReadonlySet<string>;
47
+ /**
48
+ * `false` once the backend answered 404 / 405: the route is not deployed
49
+ * there, and the UI withdraws the control.
50
+ */
51
+ isAvailable: boolean;
52
+ }
53
+ /** Test seam: forget which backends were warned about. */
54
+ export declare function resetThreadFieldUnavailableWarnings(): void;
55
+ export declare function useThreadFieldOverride<T, R extends ThreadFieldRow>({ field, backendUrl, principalId, getAuth, rows, readRow, write, isUnavailableError, unavailableWarning, onServerTruth, equals, }: UseThreadFieldOverrideOptions<T, R>): ThreadFieldOverride<T>;
@@ -0,0 +1,184 @@
1
+ import { useMutation } from "@tanstack/react-query";
2
+ import { useState, useRef, useCallback, useEffect } from "react";
3
+ const EMPTY_PENDING = /* @__PURE__ */ new Set();
4
+ function fieldScope(backendUrl, principalId) {
5
+ return `${backendUrl}::${principalId}`;
6
+ }
7
+ function withPending(pending, threadId, isPending) {
8
+ if (pending.has(threadId) === isPending) return pending;
9
+ const next = new Set(pending);
10
+ if (isPending) next.add(threadId);
11
+ else next.delete(threadId);
12
+ return next;
13
+ }
14
+ const strictEquals = (a, b) => a === b;
15
+ const warnedUnavailableBackends = /* @__PURE__ */ new Set();
16
+ function useThreadFieldOverride({
17
+ field,
18
+ backendUrl,
19
+ principalId,
20
+ getAuth,
21
+ rows,
22
+ readRow,
23
+ write,
24
+ isUnavailableError,
25
+ unavailableWarning,
26
+ onServerTruth,
27
+ equals = strictEquals
28
+ }) {
29
+ const scope = fieldScope(backendUrl, principalId);
30
+ const [state, setState] = useState(() => ({
31
+ scope,
32
+ overrides: {},
33
+ pending: EMPTY_PENDING
34
+ }));
35
+ if (state.scope !== scope) {
36
+ setState({ scope, overrides: {}, pending: EMPTY_PENDING });
37
+ }
38
+ const emptyOverridesRef = useRef(Object.freeze({}));
39
+ const overrides = state.scope === scope ? state.overrides : emptyOverridesRef.current;
40
+ const pendingThreadIds = state.scope === scope ? state.pending : EMPTY_PENDING;
41
+ const [unavailableBackend, setUnavailableBackend] = useState(null);
42
+ const scopeRef = useRef(scope);
43
+ scopeRef.current = scope;
44
+ const overridesRef = useRef(overrides);
45
+ overridesRef.current = overrides;
46
+ const readRowRef = useRef(readRow);
47
+ readRowRef.current = readRow;
48
+ const writeRef = useRef(write);
49
+ writeRef.current = write;
50
+ const isUnavailableErrorRef = useRef(isUnavailableError);
51
+ isUnavailableErrorRef.current = isUnavailableError;
52
+ const unavailableWarningRef = useRef(unavailableWarning);
53
+ unavailableWarningRef.current = unavailableWarning;
54
+ const onServerTruthRef = useRef(onServerTruth);
55
+ onServerTruthRef.current = onServerTruth;
56
+ const equalsRef = useRef(equals);
57
+ equalsRef.current = equals;
58
+ const inFlightRef = useRef(/* @__PURE__ */ new Map());
59
+ const queuedRef = useRef(/* @__PURE__ */ new Map());
60
+ const seqRef = useRef(0);
61
+ const bookkeepingScopeRef = useRef(scope);
62
+ if (bookkeepingScopeRef.current !== scope) {
63
+ bookkeepingScopeRef.current = scope;
64
+ inFlightRef.current = /* @__PURE__ */ new Map();
65
+ queuedRef.current = /* @__PURE__ */ new Map();
66
+ }
67
+ const update = useCallback(
68
+ (forScope, patch) => {
69
+ setState((current) => {
70
+ const base = current.scope === forScope ? current : { scope: forScope, overrides: {}, pending: EMPTY_PENDING };
71
+ const next = patch(base);
72
+ return next === base && current.scope === forScope ? current : next;
73
+ });
74
+ },
75
+ []
76
+ );
77
+ useEffect(() => {
78
+ const caughtUp = Object.keys(overrides).filter((threadId) => {
79
+ if (pendingThreadIds.has(threadId)) return false;
80
+ const row = rows.find((candidate) => candidate.threadId === threadId);
81
+ if (row === void 0) return false;
82
+ const reported = readRowRef.current(row);
83
+ const override = overrides[threadId];
84
+ return reported !== void 0 && override !== void 0 && equalsRef.current(reported, override);
85
+ });
86
+ if (caughtUp.length === 0) return;
87
+ update(scope, (current) => {
88
+ const next = { ...current.overrides };
89
+ for (const threadId of caughtUp) delete next[threadId];
90
+ return { ...current, overrides: next };
91
+ });
92
+ }, [overrides, pendingThreadIds, rows, scope, update]);
93
+ const mutation = useMutation({
94
+ mutationKey: ["conversation-history", field, backendUrl, principalId],
95
+ mutationFn: async ({ threadId, value }) => writeRef.current(await getAuth(), { threadId, value }),
96
+ onError: (error, write2) => {
97
+ if (isUnavailableErrorRef.current(error)) {
98
+ setUnavailableBackend(backendUrl);
99
+ const warnKey = `${field}::${backendUrl}`;
100
+ if (!warnedUnavailableBackends.has(warnKey)) {
101
+ warnedUnavailableBackends.add(warnKey);
102
+ console.warn(unavailableWarningRef.current);
103
+ }
104
+ }
105
+ if (!isCurrentWrite(write2)) return;
106
+ inFlightRef.current.delete(write2.threadId);
107
+ queuedRef.current.delete(write2.threadId);
108
+ update(write2.scope, (current) => {
109
+ const overridesNext = { ...current.overrides };
110
+ if (write2.previous === void 0) delete overridesNext[write2.threadId];
111
+ else overridesNext[write2.threadId] = write2.previous;
112
+ return {
113
+ ...current,
114
+ overrides: overridesNext,
115
+ pending: withPending(current.pending, write2.threadId, false)
116
+ };
117
+ });
118
+ },
119
+ onSuccess: (truth, write2) => {
120
+ var _a;
121
+ if (!isCurrentWrite(write2)) return;
122
+ inFlightRef.current.delete(write2.threadId);
123
+ const queued = queuedRef.current.get(write2.threadId);
124
+ const hadQueued = queuedRef.current.has(write2.threadId);
125
+ queuedRef.current.delete(write2.threadId);
126
+ (_a = onServerTruthRef.current) == null ? void 0 : _a.call(onServerTruthRef, truth);
127
+ if (hadQueued && queued !== void 0 && !equalsRef.current(queued, truth.value)) {
128
+ startWrite(write2.scope, {
129
+ threadId: write2.threadId,
130
+ value: queued,
131
+ previous: truth.value
132
+ });
133
+ return;
134
+ }
135
+ update(write2.scope, (current) => ({
136
+ ...current,
137
+ overrides: { ...current.overrides, [truth.threadId]: truth.value },
138
+ pending: withPending(current.pending, truth.threadId, false)
139
+ }));
140
+ }
141
+ });
142
+ const mutate = mutation.mutate;
143
+ function isCurrentWrite(write2) {
144
+ return scopeRef.current === write2.scope && inFlightRef.current.get(write2.threadId) === write2.seq;
145
+ }
146
+ function startWrite(forScope, write2) {
147
+ const seq = ++seqRef.current;
148
+ inFlightRef.current.set(write2.threadId, seq);
149
+ update(forScope, (current) => ({
150
+ ...current,
151
+ pending: withPending(current.pending, write2.threadId, true)
152
+ }));
153
+ mutate({ ...write2, scope: forScope, seq });
154
+ }
155
+ const startWriteRef = useRef(startWrite);
156
+ startWriteRef.current = startWrite;
157
+ const set = useCallback(
158
+ ({ threadId, value }) => {
159
+ const forScope = scopeRef.current;
160
+ const previous = overridesRef.current[threadId];
161
+ update(forScope, (current) => ({
162
+ ...current,
163
+ overrides: { ...current.overrides, [threadId]: value }
164
+ }));
165
+ if (inFlightRef.current.has(threadId)) {
166
+ queuedRef.current.set(threadId, value);
167
+ return;
168
+ }
169
+ startWriteRef.current(forScope, { threadId, value, previous });
170
+ },
171
+ [update]
172
+ );
173
+ return {
174
+ overrides,
175
+ set,
176
+ isPending: pendingThreadIds.size > 0,
177
+ pendingThreadIds,
178
+ isAvailable: unavailableBackend !== backendUrl
179
+ };
180
+ }
181
+ export {
182
+ useThreadFieldOverride
183
+ };
184
+ //# sourceMappingURL=use-thread-field-override.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-thread-field-override.js","sources":["../../src/threads/use-thread-field-override.ts"],"sourcesContent":["/**\n * The optimistic per-thread field write behind the pin (`useThreadFavorite`)\n * and the rename (`useThreadRename`): one TanStack mutation over a REST\n * route with an override map layered over the list rows.\n *\n * Why overrides rather than a cache patch alone: a row's field is folded from\n * two sources — the fetched page and the pushed conversation index — and a\n * push that predates the write would clobber a patched page. The override\n * sits over both until the sources report the same value, then drops away so\n * they own the field again (a later write from another client is never\n * masked). A failure restores the previous value; a 404 / 405 marks the\n * write unavailable — the backend predates the route — so the UI withdraws\n * the control instead of failing on every use.\n *\n * Same-thread writes are serialized: while one is in flight, a further write\n * for that thread flips the row at once but only records the desired value,\n * and at most one follow-up write is sent — with that value — once the\n * in-flight one has answered. Two independent writes for one thread never\n * race, so the server cannot end on an earlier request's value.\n *\n * Everything the hook holds is scoped to `(backendUrl, principalId)`: a\n * credential swap under the same list reads an empty override map, and a\n * write that was in flight for the previous identity is ignored when it\n * answers, so a same-id row under the new identity never inherits the old\n * one's value.\n */\nimport { useMutation } from '@tanstack/react-query';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { RestConversationAuth } from './use-rest-conversation-history';\n\nexport interface ThreadFieldTarget<T> {\n threadId: string;\n value: T;\n}\n\n/** The one field every row must carry: its identity. */\nexport interface ThreadFieldRow {\n threadId: string;\n}\n\nexport interface UseThreadFieldOverrideOptions<T, R extends ThreadFieldRow> {\n /** Names the field: part of the mutation key and of the one-per-backend warning. */\n field: string;\n backendUrl: string;\n /** Non-secret identity in the mutation key (as the read-state mutation keys). */\n principalId: string;\n getAuth: () => RestConversationAuth | Promise<RestConversationAuth>;\n /**\n * The rows as the list sources currently report them, BEFORE overrides. An\n * override is dropped once its row reports the same value.\n */\n rows: readonly R[];\n /** The field as a source row reports it; `undefined` when the row does not carry it. */\n readRow: (row: R) => T | undefined;\n /** The write; resolves to the server's truth after it. */\n write: (auth: RestConversationAuth, target: ThreadFieldTarget<T>) => Promise<ThreadFieldTarget<T>>;\n /** True for the rejection that means the backend has no such route (404 / 405). */\n isUnavailableError: (error: unknown) => boolean;\n /** Logged once per backend when the route is found missing. */\n unavailableWarning: string;\n /** Write the server's answer into the list caches (`patchConversation`). */\n onServerTruth?: (truth: ThreadFieldTarget<T>) => void;\n /** Value equality (default `===`). */\n equals?: (a: T, b: T) => boolean;\n}\n\nexport interface ThreadFieldOverride<T> {\n /** Optimistic, then server-confirmed, values layered over the list sources, keyed by thread id. */\n overrides: Readonly<Record<string, T>>;\n /**\n * Write the field. Flips the row at once; while that thread's write is in\n * flight the call only records the desired value (see module doc).\n */\n set: (target: ThreadFieldTarget<T>) => void;\n /** True while any write is in flight. */\n isPending: boolean;\n /** The threads whose write is in flight — a control disables itself for these. */\n pendingThreadIds: ReadonlySet<string>;\n /**\n * `false` once the backend answered 404 / 405: the route is not deployed\n * there, and the UI withdraws the control.\n */\n isAvailable: boolean;\n}\n\n/** The hook's state, tagged with the identity it belongs to. */\ninterface ScopedFieldState<T> {\n scope: string;\n overrides: Record<string, T>;\n pending: ReadonlySet<string>;\n}\n\ninterface FieldWrite<T> extends ThreadFieldTarget<T> {\n /** The override before this chain of writes began; `undefined` restores the source value. */\n previous: T | undefined;\n /** The identity the write was issued for; an answer for another one is ignored. */\n scope: string;\n /** This thread's write generation; an answer for an older one is ignored. */\n seq: number;\n}\n\nconst EMPTY_PENDING: ReadonlySet<string> = new Set();\n\nfunction fieldScope(backendUrl: string, principalId: string): string {\n return `${backendUrl}::${principalId}`;\n}\n\nfunction withPending(pending: ReadonlySet<string>, threadId: string, isPending: boolean): ReadonlySet<string> {\n if (pending.has(threadId) === isPending) return pending;\n const next = new Set(pending);\n if (isPending) next.add(threadId);\n else next.delete(threadId);\n return next;\n}\n\nconst strictEquals = <T,>(a: T, b: T): boolean => a === b;\n\n/** `(field, backend)` pairs already warned about, so an older agora logs one line per page load, not one per use. */\nconst warnedUnavailableBackends = new Set<string>();\n\n/** Test seam: forget which backends were warned about. */\nexport function resetThreadFieldUnavailableWarnings(): void {\n warnedUnavailableBackends.clear();\n}\n\nexport function useThreadFieldOverride<T, R extends ThreadFieldRow>({\n field,\n backendUrl,\n principalId,\n getAuth,\n rows,\n readRow,\n write,\n isUnavailableError,\n unavailableWarning,\n onServerTruth,\n equals = strictEquals,\n}: UseThreadFieldOverrideOptions<T, R>): ThreadFieldOverride<T> {\n const scope = fieldScope(backendUrl, principalId);\n const [state, setState] = useState<ScopedFieldState<T>>(() => ({\n scope,\n overrides: {},\n pending: EMPTY_PENDING,\n }));\n // An identity swap discards everything held for the previous identity —\n // overrides AND pending rows — rather than merely hiding it: a round trip\n // back to that identity must not revive a row pinned for a write whose\n // answer has since been dropped (it would stay disabled for good). Setting\n // state during render is React's derived-state reset; the render re-runs\n // with the empty state before anything is committed.\n if (state.scope !== scope) {\n setState({ scope, overrides: {}, pending: EMPTY_PENDING });\n }\n const emptyOverridesRef = useRef<Readonly<Record<string, T>>>(Object.freeze({}) as Record<string, T>);\n const overrides = state.scope === scope ? state.overrides : emptyOverridesRef.current;\n const pendingThreadIds = state.scope === scope ? state.pending : EMPTY_PENDING;\n // The backend the route was found missing on. The route is a property of\n // the backend, not of who is asking, so this is keyed by backend alone; a\n // different backend is presumed to have it until it says otherwise.\n const [unavailableBackend, setUnavailableBackend] = useState<string | null>(null);\n\n const scopeRef = useRef(scope);\n scopeRef.current = scope;\n const overridesRef = useRef(overrides);\n overridesRef.current = overrides;\n // The per-render option closures ride refs so a consumer's fresh functions\n // never re-key the mutation or the caught-up effect.\n const readRowRef = useRef(readRow);\n readRowRef.current = readRow;\n const writeRef = useRef(write);\n writeRef.current = write;\n const isUnavailableErrorRef = useRef(isUnavailableError);\n isUnavailableErrorRef.current = isUnavailableError;\n const unavailableWarningRef = useRef(unavailableWarning);\n unavailableWarningRef.current = unavailableWarning;\n const onServerTruthRef = useRef(onServerTruth);\n onServerTruthRef.current = onServerTruth;\n const equalsRef = useRef(equals);\n equalsRef.current = equals;\n // Per-thread write bookkeeping for the CURRENT identity: the generation of\n // the write in flight, and the value a further write asked for meanwhile.\n const inFlightRef = useRef<Map<string, number>>(new Map());\n const queuedRef = useRef<Map<string, T>>(new Map());\n const seqRef = useRef(0);\n const bookkeepingScopeRef = useRef(scope);\n if (bookkeepingScopeRef.current !== scope) {\n // The previous identity's writes are abandoned with its state: their\n // answers are dropped by the scope guard below, and nothing queued\n // behind them is sent.\n bookkeepingScopeRef.current = scope;\n inFlightRef.current = new Map();\n queuedRef.current = new Map();\n }\n\n const update = useCallback(\n (forScope: string, patch: (current: ScopedFieldState<T>) => ScopedFieldState<T>) => {\n setState((current) => {\n const base: ScopedFieldState<T> =\n current.scope === forScope\n ? current\n : { scope: forScope, overrides: {}, pending: EMPTY_PENDING };\n const next = patch(base);\n return next === base && current.scope === forScope ? current : next;\n });\n },\n [],\n );\n\n useEffect(() => {\n const caughtUp = Object.keys(overrides).filter((threadId) => {\n // A row with a write in flight keeps its override whatever the sources\n // say: the first write of a chain patches the page with a value the\n // user has since moved away from, and the row must not flash it.\n if (pendingThreadIds.has(threadId)) return false;\n const row = rows.find((candidate) => candidate.threadId === threadId);\n if (row === undefined) return false;\n const reported = readRowRef.current(row);\n const override = overrides[threadId];\n return reported !== undefined && override !== undefined && equalsRef.current(reported, override);\n });\n if (caughtUp.length === 0) return;\n update(scope, (current) => {\n const next = { ...current.overrides };\n for (const threadId of caughtUp) delete next[threadId];\n return { ...current, overrides: next };\n });\n }, [overrides, pendingThreadIds, rows, scope, update]);\n\n const mutation = useMutation({\n mutationKey: ['conversation-history', field, backendUrl, principalId],\n mutationFn: async ({ threadId, value }: FieldWrite<T>) =>\n writeRef.current(await getAuth(), { threadId, value }),\n onError: (error, write) => {\n if (isUnavailableErrorRef.current(error)) {\n setUnavailableBackend(backendUrl);\n const warnKey = `${field}::${backendUrl}`;\n if (!warnedUnavailableBackends.has(warnKey)) {\n warnedUnavailableBackends.add(warnKey);\n console.warn(unavailableWarningRef.current);\n }\n }\n if (!isCurrentWrite(write)) return;\n inFlightRef.current.delete(write.threadId);\n // Whatever was asked for meanwhile is dropped with the failure: the row\n // goes back to what it showed before the chain began.\n queuedRef.current.delete(write.threadId);\n update(write.scope, (current) => {\n const overridesNext = { ...current.overrides };\n if (write.previous === undefined) delete overridesNext[write.threadId];\n else overridesNext[write.threadId] = write.previous;\n return {\n ...current,\n overrides: overridesNext,\n pending: withPending(current.pending, write.threadId, false),\n };\n });\n },\n onSuccess: (truth, write) => {\n if (!isCurrentWrite(write)) return;\n inFlightRef.current.delete(write.threadId);\n const queued = queuedRef.current.get(write.threadId);\n const hadQueued = queuedRef.current.has(write.threadId);\n queuedRef.current.delete(write.threadId);\n onServerTruthRef.current?.(truth);\n if (hadQueued && queued !== undefined && !equalsRef.current(queued, truth.value)) {\n // The row already shows the value asked for meanwhile; the server\n // does not have it yet, so send it now — one write, the latest value.\n startWrite(write.scope, {\n threadId: write.threadId,\n value: queued,\n previous: truth.value,\n });\n return;\n }\n update(write.scope, (current) => ({\n ...current,\n overrides: { ...current.overrides, [truth.threadId]: truth.value },\n pending: withPending(current.pending, truth.threadId, false),\n }));\n },\n });\n const mutate = mutation.mutate;\n\n /** True when the answer belongs to the current identity and to this thread's latest write. */\n function isCurrentWrite(write: FieldWrite<T>): boolean {\n return (\n scopeRef.current === write.scope && inFlightRef.current.get(write.threadId) === write.seq\n );\n }\n\n function startWrite(forScope: string, write: Omit<FieldWrite<T>, 'scope' | 'seq'>): void {\n const seq = ++seqRef.current;\n inFlightRef.current.set(write.threadId, seq);\n update(forScope, (current) => ({\n ...current,\n pending: withPending(current.pending, write.threadId, true),\n }));\n mutate({ ...write, scope: forScope, seq });\n }\n\n const startWriteRef = useRef(startWrite);\n startWriteRef.current = startWrite;\n\n const set = useCallback(\n ({ threadId, value }: ThreadFieldTarget<T>) => {\n const forScope = scopeRef.current;\n const previous = overridesRef.current[threadId];\n // The row flips at once whether the write goes out now or after the\n // one in flight.\n update(forScope, (current) => ({\n ...current,\n overrides: { ...current.overrides, [threadId]: value },\n }));\n if (inFlightRef.current.has(threadId)) {\n queuedRef.current.set(threadId, value);\n return;\n }\n startWriteRef.current(forScope, { threadId, value, previous });\n },\n [update],\n );\n\n return {\n overrides,\n set,\n isPending: pendingThreadIds.size > 0,\n pendingThreadIds,\n isAvailable: unavailableBackend !== backendUrl,\n };\n}\n"],"names":["write"],"mappings":"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"}