@athenaintel/react 0.22.4 → 0.23.1

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 +394 -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 +3 -3
  350. package/src/chat-ui/tailwind-preset.js +14 -1
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Hover prefetch for the session list: warm the provider's thread snapshot
3
+ * cache with a thread's REST state before the user selects it, so the switch
4
+ * paints the transcript on its first frame instead of a spinner while the
5
+ * statewire attach lands (see `DeepAgentThreadSnapshotCache` and
6
+ * `toThreadSnapshotSeed`: the attach snapshot replaces the seed).
7
+ *
8
+ * React-free so the scheduling rules — once per thread, in-flight requests
9
+ * deduplicated, at most `maxConcurrent` requests at a time with the rest
10
+ * queued in arrival order, failures swallowed — are testable on their own.
11
+ */
12
+ import type { DeepAgentThreadSnapshotCache, DeepAgentStreamValues } from '@athenaintel/agent-runtime';
13
+ import type { ThreadStateResponse } from './api';
14
+ /** How many hover prefetches may be in flight at once. */
15
+ export declare const THREAD_PREFETCH_MAX_CONCURRENT = 2;
16
+ /** How long a pointer must rest on a row before its prefetch starts. */
17
+ export declare const THREAD_PREFETCH_HOVER_DELAY_MS = 120;
18
+ /**
19
+ * Shape a REST thread state as a snapshot the runtime can seed a mount from.
20
+ * `null` when there is nothing to paint (an empty or not-yet-persisted
21
+ * thread): an empty seed would render the same empty thread the attach
22
+ * covers, so it buys no paint. The REST-only keys (`thread_id`, the legacy
23
+ * `status`) are not wire state and are dropped; every channel value the
24
+ * server returned rides along, as it does on the wire.
25
+ */
26
+ export declare function threadStateToSnapshot(state: ThreadStateResponse): DeepAgentStreamValues | null;
27
+ export interface ThreadPrefetcherOptions {
28
+ /** Fetch the thread's REST state (`getThreadState` bound to the current credential). */
29
+ fetchState: (threadId: string) => Promise<ThreadStateResponse>;
30
+ /** The cache the runtime seeds a mount from. */
31
+ cache: Pick<DeepAgentThreadSnapshotCache, 'remember' | 'peek'>;
32
+ maxConcurrent?: number;
33
+ /**
34
+ * Whether a resolved fetch may still be remembered: the identity that
35
+ * started it must still be the acting one (a credential swap clears the
36
+ * cache, and a late answer must not re-seed it under another account).
37
+ */
38
+ isCurrent?: () => boolean;
39
+ }
40
+ export interface ThreadPrefetcher {
41
+ /**
42
+ * Ask for a thread's snapshot. A no-op for a thread already warm in the
43
+ * cache, already fetched (or attempted) here, or already in flight or
44
+ * queued; otherwise the fetch starts now, or once a slot frees.
45
+ */
46
+ prefetch: (threadId: string) => void;
47
+ /** The threads whose fetch is in flight (tests, diagnostics). */
48
+ inFlight: () => ReadonlySet<string>;
49
+ /** The threads waiting for a slot, in arrival order (tests, diagnostics). */
50
+ queued: () => readonly string[];
51
+ /** Drop the queue and forget every attempt; in-flight answers are still ignored by `isCurrent`. */
52
+ reset: () => void;
53
+ }
54
+ export declare function createThreadPrefetcher({ fetchState, cache, maxConcurrent, isCurrent, }: ThreadPrefetcherOptions): ThreadPrefetcher;
@@ -0,0 +1,63 @@
1
+ const THREAD_PREFETCH_MAX_CONCURRENT = 2;
2
+ const THREAD_PREFETCH_HOVER_DELAY_MS = 120;
3
+ function threadStateToSnapshot(state) {
4
+ const { thread_id: _threadId, status: _status, messages, ...channelValues } = state;
5
+ if (!Array.isArray(messages) || messages.length === 0) return null;
6
+ return { ...channelValues, messages };
7
+ }
8
+ function createThreadPrefetcher({
9
+ fetchState,
10
+ cache,
11
+ maxConcurrent = THREAD_PREFETCH_MAX_CONCURRENT,
12
+ isCurrent = () => true
13
+ }) {
14
+ const attempted = /* @__PURE__ */ new Set();
15
+ const inFlight = /* @__PURE__ */ new Set();
16
+ const queue = [];
17
+ let generation = 0;
18
+ const pump = () => {
19
+ while (inFlight.size < maxConcurrent && queue.length > 0) {
20
+ const threadId = queue.shift();
21
+ if (threadId === void 0) break;
22
+ start(threadId);
23
+ }
24
+ };
25
+ const start = (threadId) => {
26
+ const startedIn = generation;
27
+ attempted.add(threadId);
28
+ inFlight.add(threadId);
29
+ fetchState(threadId).then((state) => {
30
+ if (startedIn !== generation || !isCurrent()) return;
31
+ const snapshot = threadStateToSnapshot(state);
32
+ if (snapshot) cache.remember(threadId, snapshot);
33
+ }).catch(() => {
34
+ }).finally(() => {
35
+ inFlight.delete(threadId);
36
+ if (startedIn === generation) pump();
37
+ });
38
+ };
39
+ return {
40
+ prefetch(threadId) {
41
+ var _a;
42
+ if (!threadId) return;
43
+ if (attempted.has(threadId) || inFlight.has(threadId) || queue.includes(threadId)) return;
44
+ if ((_a = cache.peek) == null ? void 0 : _a.call(cache, threadId)) return;
45
+ queue.push(threadId);
46
+ pump();
47
+ },
48
+ inFlight: () => inFlight,
49
+ queued: () => queue,
50
+ reset() {
51
+ generation += 1;
52
+ attempted.clear();
53
+ queue.length = 0;
54
+ }
55
+ };
56
+ }
57
+ export {
58
+ THREAD_PREFETCH_HOVER_DELAY_MS,
59
+ THREAD_PREFETCH_MAX_CONCURRENT,
60
+ createThreadPrefetcher,
61
+ threadStateToSnapshot
62
+ };
63
+ //# sourceMappingURL=thread-prefetcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-prefetcher.js","sources":["../../src/threads/thread-prefetcher.ts"],"sourcesContent":["/**\n * Hover prefetch for the session list: warm the provider's thread snapshot\n * cache with a thread's REST state before the user selects it, so the switch\n * paints the transcript on its first frame instead of a spinner while the\n * statewire attach lands (see `DeepAgentThreadSnapshotCache` and\n * `toThreadSnapshotSeed`: the attach snapshot replaces the seed).\n *\n * React-free so the scheduling rules — once per thread, in-flight requests\n * deduplicated, at most `maxConcurrent` requests at a time with the rest\n * queued in arrival order, failures swallowed — are testable on their own.\n */\nimport type {\n DeepAgentThreadSnapshotCache,\n DeepAgentStreamValues,\n} from '@athenaintel/agent-runtime';\nimport type { ThreadStateResponse } from './api';\n\n/** How many hover prefetches may be in flight at once. */\nexport const THREAD_PREFETCH_MAX_CONCURRENT = 2;\n\n/** How long a pointer must rest on a row before its prefetch starts. */\nexport const THREAD_PREFETCH_HOVER_DELAY_MS = 120;\n\n/**\n * Shape a REST thread state as a snapshot the runtime can seed a mount from.\n * `null` when there is nothing to paint (an empty or not-yet-persisted\n * thread): an empty seed would render the same empty thread the attach\n * covers, so it buys no paint. The REST-only keys (`thread_id`, the legacy\n * `status`) are not wire state and are dropped; every channel value the\n * server returned rides along, as it does on the wire.\n */\nexport function threadStateToSnapshot(state: ThreadStateResponse): DeepAgentStreamValues | null {\n const { thread_id: _threadId, status: _status, messages, ...channelValues } = state;\n if (!Array.isArray(messages) || messages.length === 0) return null;\n // The REST messages are LangChain messages deserialized to their plain\n // shape — the same records the wire's `messages` lane carries — so the\n // cast mirrors `readDeepAgentStreamValues`, which types a wire state the\n // same way.\n return { ...channelValues, messages } as DeepAgentStreamValues;\n}\n\nexport interface ThreadPrefetcherOptions {\n /** Fetch the thread's REST state (`getThreadState` bound to the current credential). */\n fetchState: (threadId: string) => Promise<ThreadStateResponse>;\n /** The cache the runtime seeds a mount from. */\n cache: Pick<DeepAgentThreadSnapshotCache, 'remember' | 'peek'>;\n maxConcurrent?: number;\n /**\n * Whether a resolved fetch may still be remembered: the identity that\n * started it must still be the acting one (a credential swap clears the\n * cache, and a late answer must not re-seed it under another account).\n */\n isCurrent?: () => boolean;\n}\n\nexport interface ThreadPrefetcher {\n /**\n * Ask for a thread's snapshot. A no-op for a thread already warm in the\n * cache, already fetched (or attempted) here, or already in flight or\n * queued; otherwise the fetch starts now, or once a slot frees.\n */\n prefetch: (threadId: string) => void;\n /** The threads whose fetch is in flight (tests, diagnostics). */\n inFlight: () => ReadonlySet<string>;\n /** The threads waiting for a slot, in arrival order (tests, diagnostics). */\n queued: () => readonly string[];\n /** Drop the queue and forget every attempt; in-flight answers are still ignored by `isCurrent`. */\n reset: () => void;\n}\n\nexport function createThreadPrefetcher({\n fetchState,\n cache,\n maxConcurrent = THREAD_PREFETCH_MAX_CONCURRENT,\n isCurrent = () => true,\n}: ThreadPrefetcherOptions): ThreadPrefetcher {\n // Every thread ever started here: a prefetch is one attempt per thread per\n // identity, whether it succeeded or failed — a hover must not turn into a\n // retry storm against a thread the server keeps refusing.\n const attempted = new Set<string>();\n const inFlight = new Set<string>();\n const queue: string[] = [];\n // A reset abandons the generation: answers from before it are dropped.\n let generation = 0;\n\n const pump = (): void => {\n while (inFlight.size < maxConcurrent && queue.length > 0) {\n const threadId = queue.shift();\n if (threadId === undefined) break;\n start(threadId);\n }\n };\n\n const start = (threadId: string): void => {\n const startedIn = generation;\n attempted.add(threadId);\n inFlight.add(threadId);\n fetchState(threadId)\n .then((state) => {\n if (startedIn !== generation || !isCurrent()) return;\n const snapshot = threadStateToSnapshot(state);\n if (snapshot) cache.remember(threadId, snapshot);\n })\n .catch(() => {\n // Best effort: a refused or failed warm-up changes nothing the user\n // sees — the selection still attaches and loads on its own.\n })\n .finally(() => {\n inFlight.delete(threadId);\n if (startedIn === generation) pump();\n });\n };\n\n return {\n prefetch(threadId) {\n if (!threadId) return;\n if (attempted.has(threadId) || inFlight.has(threadId) || queue.includes(threadId)) return;\n if (cache.peek?.(threadId)) return;\n queue.push(threadId);\n pump();\n },\n inFlight: () => inFlight,\n queued: () => queue,\n reset() {\n generation += 1;\n attempted.clear();\n queue.length = 0;\n },\n };\n}\n"],"names":[],"mappings":"AAkBO,MAAM,iCAAiC;AAGvC,MAAM,iCAAiC;AAUvC,SAAS,sBAAsB,OAA0D;AAC9F,QAAM,EAAE,WAAW,WAAW,QAAQ,SAAS,UAAU,GAAG,kBAAkB;AAC9E,MAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,SAAS,WAAW,EAAG,QAAO;AAK9D,SAAO,EAAE,GAAG,eAAe,SAAA;AAC7B;AA+BO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB,YAAY,MAAM;AACpB,GAA8C;AAI5C,QAAM,gCAAgB,IAAA;AACtB,QAAM,+BAAe,IAAA;AACrB,QAAM,QAAkB,CAAA;AAExB,MAAI,aAAa;AAEjB,QAAM,OAAO,MAAY;AACvB,WAAO,SAAS,OAAO,iBAAiB,MAAM,SAAS,GAAG;AACxD,YAAM,WAAW,MAAM,MAAA;AACvB,UAAI,aAAa,OAAW;AAC5B,YAAM,QAAQ;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,QAAQ,CAAC,aAA2B;AACxC,UAAM,YAAY;AAClB,cAAU,IAAI,QAAQ;AACtB,aAAS,IAAI,QAAQ;AACrB,eAAW,QAAQ,EAChB,KAAK,CAAC,UAAU;AACf,UAAI,cAAc,cAAc,CAAC,YAAa;AAC9C,YAAM,WAAW,sBAAsB,KAAK;AAC5C,UAAI,SAAU,OAAM,SAAS,UAAU,QAAQ;AAAA,IACjD,CAAC,EACA,MAAM,MAAM;AAAA,IAGb,CAAC,EACA,QAAQ,MAAM;AACb,eAAS,OAAO,QAAQ;AACxB,UAAI,cAAc,WAAY,MAAA;AAAA,IAChC,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL,SAAS,UAAU;AAhGhB;AAiGD,UAAI,CAAC,SAAU;AACf,UAAI,UAAU,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,MAAM,SAAS,QAAQ,EAAG;AACnF,WAAI,WAAM,SAAN,+BAAa,UAAW;AAC5B,YAAM,KAAK,QAAQ;AACnB,WAAA;AAAA,IACF;AAAA,IACA,UAAU,MAAM;AAAA,IAChB,QAAQ,MAAM;AAAA,IACd,QAAQ;AACN,oBAAc;AACd,gBAAU,MAAA;AACV,YAAM,SAAS;AAAA,IACjB;AAAA,EAAA;AAEJ;"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const react = require("react");
4
+ const statewireThreadList = require("./statewire-thread-list.cjs");
5
+ function useAthenaSessionAttention(threadId) {
6
+ const list = statewireThreadList.useAthenaStatewireThreadList();
7
+ const targetId = threadId ?? (list == null ? void 0 : list.activeThreadId);
8
+ const thread = list && targetId ? targetId === list.activeThreadId ? list.activeThread ?? list.threads.find((candidate) => candidate.id === targetId) : list.threads.find((candidate) => candidate.id === targetId) : void 0;
9
+ return react.useMemo(
10
+ () => thread ? {
11
+ threadId: thread.id,
12
+ attention: thread.attention,
13
+ nextAction: thread.nextAction,
14
+ attentionSource: thread.attentionSource,
15
+ pullRequests: thread.pullRequests,
16
+ isUnread: thread.isUnread
17
+ } : null,
18
+ [thread]
19
+ );
20
+ }
21
+ exports.useAthenaSessionAttention = useAthenaSessionAttention;
22
+ //# sourceMappingURL=use-athena-session-attention.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-athena-session-attention.cjs","sources":["../../src/threads/use-athena-session-attention.ts"],"sourcesContent":["import type {\n ConversationAttention,\n ConversationAttentionSource,\n ConversationPullRequest,\n} from '@athenaintel/agent-runtime';\nimport { useMemo } from 'react';\nimport { useAthenaStatewireThreadList } from './statewire-thread-list';\n\n/** What one session asks of the viewer, as the thread list currently knows it. */\nexport interface AthenaSessionAttention {\n threadId: string;\n /** Server-derived when reported, else the client fallback from the status planes. */\n attention: ConversationAttention | null;\n /** One line (≤ 80 chars) of what the viewer should do next, when the server derives one. */\n nextAction: string | null;\n attentionSource: ConversationAttentionSource | null;\n /** Pull requests the session opened with their live GitHub state, once fetched. */\n pullRequests: ConversationPullRequest[] | null;\n isUnread: boolean;\n}\n\n/**\n * The active (or given) thread's attention state, read from the thread-list\n * state `<AthenaProvider enableThreadList>` provides — for apps that render\n * their own rows or banners. The active thread is read from\n * `activeThread`, which a search cannot hide; another thread is read from the\n * listed rows. `null` outside thread-list mode and for a thread the list does\n * not hold (a never-started chat, a thread outside the loaded pages). Updates\n * live: the conversation index pushes attention changes the way it pushes\n * titles.\n */\nexport function useAthenaSessionAttention(threadId?: string): AthenaSessionAttention | null {\n const list = useAthenaStatewireThreadList();\n const targetId = threadId ?? list?.activeThreadId;\n const thread =\n list && targetId\n ? targetId === list.activeThreadId\n ? (list.activeThread ?? list.threads.find((candidate) => candidate.id === targetId))\n : list.threads.find((candidate) => candidate.id === targetId)\n : undefined;\n return useMemo<AthenaSessionAttention | null>(\n () =>\n thread\n ? {\n threadId: thread.id,\n attention: thread.attention,\n nextAction: thread.nextAction,\n attentionSource: thread.attentionSource,\n pullRequests: thread.pullRequests,\n isUnread: thread.isUnread,\n }\n : null,\n [thread],\n );\n}\n"],"names":["useAthenaStatewireThreadList","useMemo"],"mappings":";;;;AA+BO,SAAS,0BAA0B,UAAkD;AAC1F,QAAM,OAAOA,oBAAAA,6BAAA;AACb,QAAM,WAAW,aAAY,6BAAM;AACnC,QAAM,SACJ,QAAQ,WACJ,aAAa,KAAK,iBACf,KAAK,gBAAgB,KAAK,QAAQ,KAAK,CAAC,cAAc,UAAU,OAAO,QAAQ,IAChF,KAAK,QAAQ,KAAK,CAAC,cAAc,UAAU,OAAO,QAAQ,IAC5D;AACN,SAAOC,MAAAA;AAAAA,IACL,MACE,SACI;AAAA,MACE,UAAU,OAAO;AAAA,MACjB,WAAW,OAAO;AAAA,MAClB,YAAY,OAAO;AAAA,MACnB,iBAAiB,OAAO;AAAA,MACxB,cAAc,OAAO;AAAA,MACrB,UAAU,OAAO;AAAA,IAAA,IAEnB;AAAA,IACN,CAAC,MAAM;AAAA,EAAA;AAEX;;"}
@@ -0,0 +1,24 @@
1
+ import type { ConversationAttention, ConversationAttentionSource, ConversationPullRequest } from '@athenaintel/agent-runtime';
2
+ /** What one session asks of the viewer, as the thread list currently knows it. */
3
+ export interface AthenaSessionAttention {
4
+ threadId: string;
5
+ /** Server-derived when reported, else the client fallback from the status planes. */
6
+ attention: ConversationAttention | null;
7
+ /** One line (≤ 80 chars) of what the viewer should do next, when the server derives one. */
8
+ nextAction: string | null;
9
+ attentionSource: ConversationAttentionSource | null;
10
+ /** Pull requests the session opened with their live GitHub state, once fetched. */
11
+ pullRequests: ConversationPullRequest[] | null;
12
+ isUnread: boolean;
13
+ }
14
+ /**
15
+ * The active (or given) thread's attention state, read from the thread-list
16
+ * state `<AthenaProvider enableThreadList>` provides — for apps that render
17
+ * their own rows or banners. The active thread is read from
18
+ * `activeThread`, which a search cannot hide; another thread is read from the
19
+ * listed rows. `null` outside thread-list mode and for a thread the list does
20
+ * not hold (a never-started chat, a thread outside the loaded pages). Updates
21
+ * live: the conversation index pushes attention changes the way it pushes
22
+ * titles.
23
+ */
24
+ export declare function useAthenaSessionAttention(threadId?: string): AthenaSessionAttention | null;
@@ -0,0 +1,22 @@
1
+ import { useMemo } from "react";
2
+ import { useAthenaStatewireThreadList } from "./statewire-thread-list.js";
3
+ function useAthenaSessionAttention(threadId) {
4
+ const list = useAthenaStatewireThreadList();
5
+ const targetId = threadId ?? (list == null ? void 0 : list.activeThreadId);
6
+ const thread = list && targetId ? targetId === list.activeThreadId ? list.activeThread ?? list.threads.find((candidate) => candidate.id === targetId) : list.threads.find((candidate) => candidate.id === targetId) : void 0;
7
+ return useMemo(
8
+ () => thread ? {
9
+ threadId: thread.id,
10
+ attention: thread.attention,
11
+ nextAction: thread.nextAction,
12
+ attentionSource: thread.attentionSource,
13
+ pullRequests: thread.pullRequests,
14
+ isUnread: thread.isUnread
15
+ } : null,
16
+ [thread]
17
+ );
18
+ }
19
+ export {
20
+ useAthenaSessionAttention
21
+ };
22
+ //# sourceMappingURL=use-athena-session-attention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-athena-session-attention.js","sources":["../../src/threads/use-athena-session-attention.ts"],"sourcesContent":["import type {\n ConversationAttention,\n ConversationAttentionSource,\n ConversationPullRequest,\n} from '@athenaintel/agent-runtime';\nimport { useMemo } from 'react';\nimport { useAthenaStatewireThreadList } from './statewire-thread-list';\n\n/** What one session asks of the viewer, as the thread list currently knows it. */\nexport interface AthenaSessionAttention {\n threadId: string;\n /** Server-derived when reported, else the client fallback from the status planes. */\n attention: ConversationAttention | null;\n /** One line (≤ 80 chars) of what the viewer should do next, when the server derives one. */\n nextAction: string | null;\n attentionSource: ConversationAttentionSource | null;\n /** Pull requests the session opened with their live GitHub state, once fetched. */\n pullRequests: ConversationPullRequest[] | null;\n isUnread: boolean;\n}\n\n/**\n * The active (or given) thread's attention state, read from the thread-list\n * state `<AthenaProvider enableThreadList>` provides — for apps that render\n * their own rows or banners. The active thread is read from\n * `activeThread`, which a search cannot hide; another thread is read from the\n * listed rows. `null` outside thread-list mode and for a thread the list does\n * not hold (a never-started chat, a thread outside the loaded pages). Updates\n * live: the conversation index pushes attention changes the way it pushes\n * titles.\n */\nexport function useAthenaSessionAttention(threadId?: string): AthenaSessionAttention | null {\n const list = useAthenaStatewireThreadList();\n const targetId = threadId ?? list?.activeThreadId;\n const thread =\n list && targetId\n ? targetId === list.activeThreadId\n ? (list.activeThread ?? list.threads.find((candidate) => candidate.id === targetId))\n : list.threads.find((candidate) => candidate.id === targetId)\n : undefined;\n return useMemo<AthenaSessionAttention | null>(\n () =>\n thread\n ? {\n threadId: thread.id,\n attention: thread.attention,\n nextAction: thread.nextAction,\n attentionSource: thread.attentionSource,\n pullRequests: thread.pullRequests,\n isUnread: thread.isUnread,\n }\n : null,\n [thread],\n );\n}\n"],"names":[],"mappings":";;AA+BO,SAAS,0BAA0B,UAAkD;AAC1F,QAAM,OAAO,6BAAA;AACb,QAAM,WAAW,aAAY,6BAAM;AACnC,QAAM,SACJ,QAAQ,WACJ,aAAa,KAAK,iBACf,KAAK,gBAAgB,KAAK,QAAQ,KAAK,CAAC,cAAc,UAAU,OAAO,QAAQ,IAChF,KAAK,QAAQ,KAAK,CAAC,cAAc,UAAU,OAAO,QAAQ,IAC5D;AACN,SAAO;AAAA,IACL,MACE,SACI;AAAA,MACE,UAAU,OAAO;AAAA,MACjB,WAAW,OAAO;AAAA,MAClB,YAAY,OAAO;AAAA,MACnB,iBAAiB,OAAO;AAAA,MACxB,cAAc,OAAO;AAAA,MACrB,UAAU,OAAO;AAAA,IAAA,IAEnB;AAAA,IACN,CAAC,MAAM;AAAA,EAAA;AAEX;"}
@@ -5,19 +5,21 @@ const react = require("react");
5
5
  require("@assistant-ui/tap");
6
6
  require("statewire");
7
7
  require("../packages/agent-runtime/src/statewire-suspended.cjs");
8
+ require("../packages/agent-runtime/src/conversation-list.cjs");
8
9
  const useConversationList = require("../packages/agent-runtime/src/use-conversation-list.cjs");
9
10
  require("../packages/agent-runtime/src/statewire-state.cjs");
10
11
  require("../packages/agent-runtime/src/messages.cjs");
11
- require("@assistant-ui/react-statewire");
12
12
  require("@assistant-ui/store");
13
13
  require("../packages/agent-runtime/src/quota-safe-local-storage-session.cjs");
14
14
  require("../packages/agent-runtime/src/statewire-attach-observer.cjs");
15
15
  require("../packages/agent-runtime/src/thread-status.cjs");
16
+ require("@assistant-ui/react-statewire");
16
17
  require("@assistant-ui/core");
17
18
  require("../packages/agent-runtime/src/thread-handoff.cjs");
18
19
  const reactQuery = require("@tanstack/react-query");
19
20
  const api = require("./api.cjs");
20
21
  const conversationListFetcher = require("./conversation-list-fetcher.cjs");
22
+ const useThreadFavorite = require("./use-thread-favorite.cjs");
21
23
  function useRestConversationHistory({
22
24
  backendUrl,
23
25
  principalId,
@@ -53,12 +55,33 @@ function useRestConversationHistory({
53
55
  }) => api.setThreadReadState(backendUrl, await getAuth(), { threadId, isUnread }),
54
56
  onSuccess: (result) => list.patchConversation(result.thread_id, { isUnread: result.is_unread })
55
57
  });
58
+ const patchConversation = list.patchConversation;
59
+ const favorite = useThreadFavorite.useThreadFavorite({
60
+ backendUrl,
61
+ principalId,
62
+ getAuth,
63
+ rows: list.conversations,
64
+ onServerTruth: ({ threadId, isFavorited }) => patchConversation(threadId, { isFavorited })
65
+ });
66
+ const favoriteOverrides = favorite.overrides;
67
+ const conversations = react.useMemo(
68
+ () => useThreadFavorite.withFavoriteOverrides(list.conversations, favoriteOverrides),
69
+ [list.conversations, favoriteOverrides]
70
+ );
56
71
  return {
57
72
  ...list,
73
+ conversations,
58
74
  archiveConversation: archive.mutate,
59
75
  archivePending: archive.isPending,
60
76
  setConversationReadState: readState.mutate,
61
- readStatePending: readState.isPending
77
+ readStatePending: readState.isPending,
78
+ /** Pin or unpin a conversation; read-only on a backend without the route (`favoriteToggleAvailable`). */
79
+ setConversationFavorited: favorite.setFavorited,
80
+ favoritePending: favorite.isPending,
81
+ /** The conversations whose pin toggle is in flight. */
82
+ favoritePendingThreadIds: favorite.pendingThreadIds,
83
+ /** `false` once the backend answered 404 / 405 to the favorite route. */
84
+ favoriteToggleAvailable: favorite.isAvailable
62
85
  };
63
86
  }
64
87
  const GENERIC_TITLES = /* @__PURE__ */ new Set([
@@ -1 +1 @@
1
- {"version":3,"file":"use-rest-conversation-history.cjs","sources":["../../src/threads/use-rest-conversation-history.ts"],"sourcesContent":["import {\n type ConversationSummary,\n useConversationList,\n} from '@athenaintel/agent-runtime';\nimport { useMutation } from '@tanstack/react-query';\nimport { useMemo } from 'react';\nimport { archiveThread, setThreadReadState } from './api';\nimport { createRestConversationListFetcher } from './conversation-list-fetcher';\n\nexport type RestConversationAuth = {\n apiKey?: string;\n token?: string | null;\n};\n\nexport interface UseRestConversationHistoryOptions {\n backendUrl: string;\n /** Non-secret identity included in the query cache boundary. */\n principalId: string;\n getAuth: () => RestConversationAuth | Promise<RestConversationAuth>;\n search?: string;\n enabled?: boolean;\n pageSize?: number;\n}\n\n/**\n * One inclusive, paginated conversation controller for satellite clients.\n * No app/channel filter is applied: authorization determines the user's rows,\n * while origin and automation metadata remain visible for UI filtering.\n */\nexport function useRestConversationHistory({\n backendUrl,\n principalId,\n getAuth,\n search,\n enabled = true,\n pageSize = 50,\n}: UseRestConversationHistoryOptions) {\n const fetchPage = useMemo(\n () => createRestConversationListFetcher({ backendUrl, getAuth }),\n [backendUrl, getAuth],\n );\n const list = useConversationList({\n fetchPage,\n filter: search?.trim() ? { search: search.trim() } : {},\n pageSize,\n scopeKey: `${backendUrl}::${principalId || 'unknown-principal'}`,\n enabled: enabled && principalId.length > 0,\n });\n\n const archive = useMutation({\n mutationKey: ['conversation-history', 'archive', backendUrl, principalId],\n mutationFn: async (threadId: string) => {\n await archiveThread(backendUrl, await getAuth(), threadId);\n return threadId;\n },\n onSuccess: (threadId) => list.removeConversation(threadId),\n });\n\n const readState = useMutation({\n mutationKey: ['conversation-history', 'read-state', backendUrl, principalId],\n mutationFn: async ({\n threadId,\n isUnread,\n }: {\n threadId: string;\n isUnread: boolean;\n }) => setThreadReadState(backendUrl, await getAuth(), { threadId, isUnread }),\n onSuccess: (result) =>\n list.patchConversation(result.thread_id, { isUnread: result.is_unread }),\n });\n\n return {\n ...list,\n archiveConversation: archive.mutate,\n archivePending: archive.isPending,\n setConversationReadState: readState.mutate,\n readStatePending: readState.isPending,\n };\n}\n\nconst GENERIC_TITLES = new Set([\n 'new chat',\n 'new conversation',\n 'untitled',\n 'untitled conversation',\n]);\n\n/** Prefer a useful server title, then a normalized last-message preview. */\nexport function conversationDisplayTitle(\n conversation: Pick<ConversationSummary, 'title' | 'preview'>,\n): string {\n const title = conversation.title.trim();\n if (title && !GENERIC_TITLES.has(title.toLowerCase())) return title;\n const preview = conversation.preview?.replace(/\\s+/g, ' ').trim();\n if (preview) return preview.length > 80 ? `${preview.slice(0, 77)}…` : preview;\n return 'Untitled conversation';\n}\n"],"names":["useMemo","createRestConversationListFetcher","useConversationList","useMutation","archiveThread","setThreadReadState"],"mappings":";;;;;;;;;;;;;;;;;;;;AA6BO,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,WAAW;AACb,GAAsC;AACpC,QAAM,YAAYA,MAAAA;AAAAA,IAChB,MAAMC,0DAAkC,EAAE,YAAY,SAAS;AAAA,IAC/D,CAAC,YAAY,OAAO;AAAA,EAAA;AAEtB,QAAM,OAAOC,oBAAAA,oBAAoB;AAAA,IAC/B;AAAA,IACA,SAAQ,iCAAQ,UAAS,EAAE,QAAQ,OAAO,KAAA,EAAK,IAAM,CAAA;AAAA,IACrD;AAAA,IACA,UAAU,GAAG,UAAU,KAAK,eAAe,mBAAmB;AAAA,IAC9D,SAAS,WAAW,YAAY,SAAS;AAAA,EAAA,CAC1C;AAED,QAAM,UAAUC,WAAAA,YAAY;AAAA,IAC1B,aAAa,CAAC,wBAAwB,WAAW,YAAY,WAAW;AAAA,IACxE,YAAY,OAAO,aAAqB;AACtC,YAAMC,IAAAA,cAAc,YAAY,MAAM,QAAA,GAAW,QAAQ;AACzD,aAAO;AAAA,IACT;AAAA,IACA,WAAW,CAAC,aAAa,KAAK,mBAAmB,QAAQ;AAAA,EAAA,CAC1D;AAED,QAAM,YAAYD,WAAAA,YAAY;AAAA,IAC5B,aAAa,CAAC,wBAAwB,cAAc,YAAY,WAAW;AAAA,IAC3E,YAAY,OAAO;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,MAIIE,IAAAA,mBAAmB,YAAY,MAAM,WAAW,EAAE,UAAU,UAAU;AAAA,IAC5E,WAAW,CAAC,WACV,KAAK,kBAAkB,OAAO,WAAW,EAAE,UAAU,OAAO,UAAA,CAAW;AAAA,EAAA,CAC1E;AAED,SAAO;AAAA,IACL,GAAG;AAAA,IACH,qBAAqB,QAAQ;AAAA,IAC7B,gBAAgB,QAAQ;AAAA,IACxB,0BAA0B,UAAU;AAAA,IACpC,kBAAkB,UAAU;AAAA,EAAA;AAEhC;AAEA,MAAM,qCAAqB,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,SAAS,yBACd,cACQ;;AACR,QAAM,QAAQ,aAAa,MAAM,KAAA;AACjC,MAAI,SAAS,CAAC,eAAe,IAAI,MAAM,YAAA,CAAa,EAAG,QAAO;AAC9D,QAAM,WAAU,kBAAa,YAAb,mBAAsB,QAAQ,QAAQ,KAAK;AAC3D,MAAI,QAAS,QAAO,QAAQ,SAAS,KAAK,GAAG,QAAQ,MAAM,GAAG,EAAE,CAAC,MAAM;AACvE,SAAO;AACT;;;"}
1
+ {"version":3,"file":"use-rest-conversation-history.cjs","sources":["../../src/threads/use-rest-conversation-history.ts"],"sourcesContent":["import {\n type ConversationSummary,\n useConversationList,\n} from '@athenaintel/agent-runtime';\nimport { useMutation } from '@tanstack/react-query';\nimport { useMemo } from 'react';\nimport { archiveThread, setThreadReadState } from './api';\nimport { createRestConversationListFetcher } from './conversation-list-fetcher';\nimport { useThreadFavorite, withFavoriteOverrides } from './use-thread-favorite';\n\nexport type RestConversationAuth = {\n apiKey?: string;\n token?: string | null;\n};\n\nexport interface UseRestConversationHistoryOptions {\n backendUrl: string;\n /** Non-secret identity included in the query cache boundary. */\n principalId: string;\n getAuth: () => RestConversationAuth | Promise<RestConversationAuth>;\n search?: string;\n enabled?: boolean;\n pageSize?: number;\n}\n\n/**\n * One inclusive, paginated conversation controller for satellite clients.\n * No app/channel filter is applied: authorization determines the user's rows,\n * while origin and automation metadata remain visible for UI filtering.\n */\nexport function useRestConversationHistory({\n backendUrl,\n principalId,\n getAuth,\n search,\n enabled = true,\n pageSize = 50,\n}: UseRestConversationHistoryOptions) {\n const fetchPage = useMemo(\n () => createRestConversationListFetcher({ backendUrl, getAuth }),\n [backendUrl, getAuth],\n );\n const list = useConversationList({\n fetchPage,\n filter: search?.trim() ? { search: search.trim() } : {},\n pageSize,\n scopeKey: `${backendUrl}::${principalId || 'unknown-principal'}`,\n enabled: enabled && principalId.length > 0,\n });\n\n const archive = useMutation({\n mutationKey: ['conversation-history', 'archive', backendUrl, principalId],\n mutationFn: async (threadId: string) => {\n await archiveThread(backendUrl, await getAuth(), threadId);\n return threadId;\n },\n onSuccess: (threadId) => list.removeConversation(threadId),\n });\n\n const readState = useMutation({\n mutationKey: ['conversation-history', 'read-state', backendUrl, principalId],\n mutationFn: async ({\n threadId,\n isUnread,\n }: {\n threadId: string;\n isUnread: boolean;\n }) => setThreadReadState(backendUrl, await getAuth(), { threadId, isUnread }),\n onSuccess: (result) =>\n list.patchConversation(result.thread_id, { isUnread: result.is_unread }),\n });\n\n // The pin flips optimistically and the server's answer is patched into the\n // page; the override drops once the page reports it.\n const patchConversation = list.patchConversation;\n const favorite = useThreadFavorite({\n backendUrl,\n principalId,\n getAuth,\n rows: list.conversations,\n onServerTruth: ({ threadId, isFavorited }) => patchConversation(threadId, { isFavorited }),\n });\n const favoriteOverrides = favorite.overrides;\n const conversations = useMemo(\n () => withFavoriteOverrides(list.conversations, favoriteOverrides),\n [list.conversations, favoriteOverrides],\n );\n\n return {\n ...list,\n conversations,\n archiveConversation: archive.mutate,\n archivePending: archive.isPending,\n setConversationReadState: readState.mutate,\n readStatePending: readState.isPending,\n /** Pin or unpin a conversation; read-only on a backend without the route (`favoriteToggleAvailable`). */\n setConversationFavorited: favorite.setFavorited,\n favoritePending: favorite.isPending,\n /** The conversations whose pin toggle is in flight. */\n favoritePendingThreadIds: favorite.pendingThreadIds,\n /** `false` once the backend answered 404 / 405 to the favorite route. */\n favoriteToggleAvailable: favorite.isAvailable,\n };\n}\n\nconst GENERIC_TITLES = new Set([\n 'new chat',\n 'new conversation',\n 'untitled',\n 'untitled conversation',\n]);\n\n/** Prefer a useful server title, then a normalized last-message preview. */\nexport function conversationDisplayTitle(\n conversation: Pick<ConversationSummary, 'title' | 'preview'>,\n): string {\n const title = conversation.title.trim();\n if (title && !GENERIC_TITLES.has(title.toLowerCase())) return title;\n const preview = conversation.preview?.replace(/\\s+/g, ' ').trim();\n if (preview) return preview.length > 80 ? `${preview.slice(0, 77)}…` : preview;\n return 'Untitled conversation';\n}\n"],"names":["useMemo","createRestConversationListFetcher","useConversationList","useMutation","archiveThread","setThreadReadState","useThreadFavorite","withFavoriteOverrides"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA8BO,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,WAAW;AACb,GAAsC;AACpC,QAAM,YAAYA,MAAAA;AAAAA,IAChB,MAAMC,0DAAkC,EAAE,YAAY,SAAS;AAAA,IAC/D,CAAC,YAAY,OAAO;AAAA,EAAA;AAEtB,QAAM,OAAOC,oBAAAA,oBAAoB;AAAA,IAC/B;AAAA,IACA,SAAQ,iCAAQ,UAAS,EAAE,QAAQ,OAAO,KAAA,EAAK,IAAM,CAAA;AAAA,IACrD;AAAA,IACA,UAAU,GAAG,UAAU,KAAK,eAAe,mBAAmB;AAAA,IAC9D,SAAS,WAAW,YAAY,SAAS;AAAA,EAAA,CAC1C;AAED,QAAM,UAAUC,WAAAA,YAAY;AAAA,IAC1B,aAAa,CAAC,wBAAwB,WAAW,YAAY,WAAW;AAAA,IACxE,YAAY,OAAO,aAAqB;AACtC,YAAMC,IAAAA,cAAc,YAAY,MAAM,QAAA,GAAW,QAAQ;AACzD,aAAO;AAAA,IACT;AAAA,IACA,WAAW,CAAC,aAAa,KAAK,mBAAmB,QAAQ;AAAA,EAAA,CAC1D;AAED,QAAM,YAAYD,WAAAA,YAAY;AAAA,IAC5B,aAAa,CAAC,wBAAwB,cAAc,YAAY,WAAW;AAAA,IAC3E,YAAY,OAAO;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,MAIIE,IAAAA,mBAAmB,YAAY,MAAM,WAAW,EAAE,UAAU,UAAU;AAAA,IAC5E,WAAW,CAAC,WACV,KAAK,kBAAkB,OAAO,WAAW,EAAE,UAAU,OAAO,UAAA,CAAW;AAAA,EAAA,CAC1E;AAID,QAAM,oBAAoB,KAAK;AAC/B,QAAM,WAAWC,kBAAAA,kBAAkB;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,KAAK;AAAA,IACX,eAAe,CAAC,EAAE,UAAU,YAAA,MAAkB,kBAAkB,UAAU,EAAE,YAAA,CAAa;AAAA,EAAA,CAC1F;AACD,QAAM,oBAAoB,SAAS;AACnC,QAAM,gBAAgBN,MAAAA;AAAAA,IACpB,MAAMO,wCAAsB,KAAK,eAAe,iBAAiB;AAAA,IACjE,CAAC,KAAK,eAAe,iBAAiB;AAAA,EAAA;AAGxC,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA,qBAAqB,QAAQ;AAAA,IAC7B,gBAAgB,QAAQ;AAAA,IACxB,0BAA0B,UAAU;AAAA,IACpC,kBAAkB,UAAU;AAAA;AAAA,IAE5B,0BAA0B,SAAS;AAAA,IACnC,iBAAiB,SAAS;AAAA;AAAA,IAE1B,0BAA0B,SAAS;AAAA;AAAA,IAEnC,yBAAyB,SAAS;AAAA,EAAA;AAEtC;AAEA,MAAM,qCAAqB,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,SAAS,yBACd,cACQ;;AACR,QAAM,QAAQ,aAAa,MAAM,KAAA;AACjC,MAAI,SAAS,CAAC,eAAe,IAAI,MAAM,YAAA,CAAa,EAAG,QAAO;AAC9D,QAAM,WAAU,kBAAa,YAAb,mBAAsB,QAAQ,QAAQ,KAAK;AAC3D,MAAI,QAAS,QAAO,QAAQ,SAAS,KAAK,GAAG,QAAQ,MAAM,GAAG,EAAE,CAAC,MAAM;AACvE,SAAO;AACT;;;"}
@@ -18,7 +18,6 @@ export interface UseRestConversationHistoryOptions {
18
18
  * while origin and automation metadata remain visible for UI filtering.
19
19
  */
20
20
  export declare function useRestConversationHistory({ backendUrl, principalId, getAuth, search, enabled, pageSize, }: UseRestConversationHistoryOptions): {
21
- conversations: ConversationSummary[];
22
21
  totalCount: number | null;
23
22
  isLoading: boolean;
24
23
  isFetching: boolean;
@@ -31,6 +30,7 @@ export declare function useRestConversationHistory({ backendUrl, principalId, ge
31
30
  prependConversation: (item: ConversationSummary) => void;
32
31
  removeConversation: (threadId: string) => void;
33
32
  patchConversation: (threadId: string, patch: Partial<ConversationSummary>) => void;
33
+ conversations: ConversationSummary[];
34
34
  archiveConversation: import("@tanstack/react-query").UseMutateFunction<string, Error, string, unknown>;
35
35
  archivePending: boolean;
36
36
  setConversationReadState: import("@tanstack/react-query").UseMutateFunction<import("./api").ThreadReadStateResponse, Error, {
@@ -38,6 +38,13 @@ export declare function useRestConversationHistory({ backendUrl, principalId, ge
38
38
  isUnread: boolean;
39
39
  }, unknown>;
40
40
  readStatePending: boolean;
41
+ /** Pin or unpin a conversation; read-only on a backend without the route (`favoriteToggleAvailable`). */
42
+ setConversationFavorited: (target: import("./use-thread-favorite").ThreadFavoriteTarget) => void;
43
+ favoritePending: boolean;
44
+ /** The conversations whose pin toggle is in flight. */
45
+ favoritePendingThreadIds: ReadonlySet<string>;
46
+ /** `false` once the backend answered 404 / 405 to the favorite route. */
47
+ favoriteToggleAvailable: boolean;
41
48
  };
42
49
  /** Prefer a useful server title, then a normalized last-message preview. */
43
50
  export declare function conversationDisplayTitle(conversation: Pick<ConversationSummary, 'title' | 'preview'>): string;
@@ -3,19 +3,21 @@ import { useMemo } from "react";
3
3
  import "@assistant-ui/tap";
4
4
  import "statewire";
5
5
  import "../packages/agent-runtime/src/statewire-suspended.js";
6
+ import "../packages/agent-runtime/src/conversation-list.js";
6
7
  import { useConversationList } from "../packages/agent-runtime/src/use-conversation-list.js";
7
8
  import "../packages/agent-runtime/src/statewire-state.js";
8
9
  import "../packages/agent-runtime/src/messages.js";
9
- import "@assistant-ui/react-statewire";
10
10
  import "@assistant-ui/store";
11
11
  import "../packages/agent-runtime/src/quota-safe-local-storage-session.js";
12
12
  import "../packages/agent-runtime/src/statewire-attach-observer.js";
13
13
  import "../packages/agent-runtime/src/thread-status.js";
14
+ import "@assistant-ui/react-statewire";
14
15
  import "@assistant-ui/core";
15
16
  import "../packages/agent-runtime/src/thread-handoff.js";
16
17
  import { useMutation } from "@tanstack/react-query";
17
18
  import { archiveThread, setThreadReadState } from "./api.js";
18
19
  import { createRestConversationListFetcher } from "./conversation-list-fetcher.js";
20
+ import { useThreadFavorite, withFavoriteOverrides } from "./use-thread-favorite.js";
19
21
  function useRestConversationHistory({
20
22
  backendUrl,
21
23
  principalId,
@@ -51,12 +53,33 @@ function useRestConversationHistory({
51
53
  }) => setThreadReadState(backendUrl, await getAuth(), { threadId, isUnread }),
52
54
  onSuccess: (result) => list.patchConversation(result.thread_id, { isUnread: result.is_unread })
53
55
  });
56
+ const patchConversation = list.patchConversation;
57
+ const favorite = useThreadFavorite({
58
+ backendUrl,
59
+ principalId,
60
+ getAuth,
61
+ rows: list.conversations,
62
+ onServerTruth: ({ threadId, isFavorited }) => patchConversation(threadId, { isFavorited })
63
+ });
64
+ const favoriteOverrides = favorite.overrides;
65
+ const conversations = useMemo(
66
+ () => withFavoriteOverrides(list.conversations, favoriteOverrides),
67
+ [list.conversations, favoriteOverrides]
68
+ );
54
69
  return {
55
70
  ...list,
71
+ conversations,
56
72
  archiveConversation: archive.mutate,
57
73
  archivePending: archive.isPending,
58
74
  setConversationReadState: readState.mutate,
59
- readStatePending: readState.isPending
75
+ readStatePending: readState.isPending,
76
+ /** Pin or unpin a conversation; read-only on a backend without the route (`favoriteToggleAvailable`). */
77
+ setConversationFavorited: favorite.setFavorited,
78
+ favoritePending: favorite.isPending,
79
+ /** The conversations whose pin toggle is in flight. */
80
+ favoritePendingThreadIds: favorite.pendingThreadIds,
81
+ /** `false` once the backend answered 404 / 405 to the favorite route. */
82
+ favoriteToggleAvailable: favorite.isAvailable
60
83
  };
61
84
  }
62
85
  const GENERIC_TITLES = /* @__PURE__ */ new Set([
@@ -1 +1 @@
1
- {"version":3,"file":"use-rest-conversation-history.js","sources":["../../src/threads/use-rest-conversation-history.ts"],"sourcesContent":["import {\n type ConversationSummary,\n useConversationList,\n} from '@athenaintel/agent-runtime';\nimport { useMutation } from '@tanstack/react-query';\nimport { useMemo } from 'react';\nimport { archiveThread, setThreadReadState } from './api';\nimport { createRestConversationListFetcher } from './conversation-list-fetcher';\n\nexport type RestConversationAuth = {\n apiKey?: string;\n token?: string | null;\n};\n\nexport interface UseRestConversationHistoryOptions {\n backendUrl: string;\n /** Non-secret identity included in the query cache boundary. */\n principalId: string;\n getAuth: () => RestConversationAuth | Promise<RestConversationAuth>;\n search?: string;\n enabled?: boolean;\n pageSize?: number;\n}\n\n/**\n * One inclusive, paginated conversation controller for satellite clients.\n * No app/channel filter is applied: authorization determines the user's rows,\n * while origin and automation metadata remain visible for UI filtering.\n */\nexport function useRestConversationHistory({\n backendUrl,\n principalId,\n getAuth,\n search,\n enabled = true,\n pageSize = 50,\n}: UseRestConversationHistoryOptions) {\n const fetchPage = useMemo(\n () => createRestConversationListFetcher({ backendUrl, getAuth }),\n [backendUrl, getAuth],\n );\n const list = useConversationList({\n fetchPage,\n filter: search?.trim() ? { search: search.trim() } : {},\n pageSize,\n scopeKey: `${backendUrl}::${principalId || 'unknown-principal'}`,\n enabled: enabled && principalId.length > 0,\n });\n\n const archive = useMutation({\n mutationKey: ['conversation-history', 'archive', backendUrl, principalId],\n mutationFn: async (threadId: string) => {\n await archiveThread(backendUrl, await getAuth(), threadId);\n return threadId;\n },\n onSuccess: (threadId) => list.removeConversation(threadId),\n });\n\n const readState = useMutation({\n mutationKey: ['conversation-history', 'read-state', backendUrl, principalId],\n mutationFn: async ({\n threadId,\n isUnread,\n }: {\n threadId: string;\n isUnread: boolean;\n }) => setThreadReadState(backendUrl, await getAuth(), { threadId, isUnread }),\n onSuccess: (result) =>\n list.patchConversation(result.thread_id, { isUnread: result.is_unread }),\n });\n\n return {\n ...list,\n archiveConversation: archive.mutate,\n archivePending: archive.isPending,\n setConversationReadState: readState.mutate,\n readStatePending: readState.isPending,\n };\n}\n\nconst GENERIC_TITLES = new Set([\n 'new chat',\n 'new conversation',\n 'untitled',\n 'untitled conversation',\n]);\n\n/** Prefer a useful server title, then a normalized last-message preview. */\nexport function conversationDisplayTitle(\n conversation: Pick<ConversationSummary, 'title' | 'preview'>,\n): string {\n const title = conversation.title.trim();\n if (title && !GENERIC_TITLES.has(title.toLowerCase())) return title;\n const preview = conversation.preview?.replace(/\\s+/g, ' ').trim();\n if (preview) return preview.length > 80 ? `${preview.slice(0, 77)}…` : preview;\n return 'Untitled conversation';\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AA6BO,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,WAAW;AACb,GAAsC;AACpC,QAAM,YAAY;AAAA,IAChB,MAAM,kCAAkC,EAAE,YAAY,SAAS;AAAA,IAC/D,CAAC,YAAY,OAAO;AAAA,EAAA;AAEtB,QAAM,OAAO,oBAAoB;AAAA,IAC/B;AAAA,IACA,SAAQ,iCAAQ,UAAS,EAAE,QAAQ,OAAO,KAAA,EAAK,IAAM,CAAA;AAAA,IACrD;AAAA,IACA,UAAU,GAAG,UAAU,KAAK,eAAe,mBAAmB;AAAA,IAC9D,SAAS,WAAW,YAAY,SAAS;AAAA,EAAA,CAC1C;AAED,QAAM,UAAU,YAAY;AAAA,IAC1B,aAAa,CAAC,wBAAwB,WAAW,YAAY,WAAW;AAAA,IACxE,YAAY,OAAO,aAAqB;AACtC,YAAM,cAAc,YAAY,MAAM,QAAA,GAAW,QAAQ;AACzD,aAAO;AAAA,IACT;AAAA,IACA,WAAW,CAAC,aAAa,KAAK,mBAAmB,QAAQ;AAAA,EAAA,CAC1D;AAED,QAAM,YAAY,YAAY;AAAA,IAC5B,aAAa,CAAC,wBAAwB,cAAc,YAAY,WAAW;AAAA,IAC3E,YAAY,OAAO;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,MAII,mBAAmB,YAAY,MAAM,WAAW,EAAE,UAAU,UAAU;AAAA,IAC5E,WAAW,CAAC,WACV,KAAK,kBAAkB,OAAO,WAAW,EAAE,UAAU,OAAO,UAAA,CAAW;AAAA,EAAA,CAC1E;AAED,SAAO;AAAA,IACL,GAAG;AAAA,IACH,qBAAqB,QAAQ;AAAA,IAC7B,gBAAgB,QAAQ;AAAA,IACxB,0BAA0B,UAAU;AAAA,IACpC,kBAAkB,UAAU;AAAA,EAAA;AAEhC;AAEA,MAAM,qCAAqB,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,SAAS,yBACd,cACQ;;AACR,QAAM,QAAQ,aAAa,MAAM,KAAA;AACjC,MAAI,SAAS,CAAC,eAAe,IAAI,MAAM,YAAA,CAAa,EAAG,QAAO;AAC9D,QAAM,WAAU,kBAAa,YAAb,mBAAsB,QAAQ,QAAQ,KAAK;AAC3D,MAAI,QAAS,QAAO,QAAQ,SAAS,KAAK,GAAG,QAAQ,MAAM,GAAG,EAAE,CAAC,MAAM;AACvE,SAAO;AACT;"}
1
+ {"version":3,"file":"use-rest-conversation-history.js","sources":["../../src/threads/use-rest-conversation-history.ts"],"sourcesContent":["import {\n type ConversationSummary,\n useConversationList,\n} from '@athenaintel/agent-runtime';\nimport { useMutation } from '@tanstack/react-query';\nimport { useMemo } from 'react';\nimport { archiveThread, setThreadReadState } from './api';\nimport { createRestConversationListFetcher } from './conversation-list-fetcher';\nimport { useThreadFavorite, withFavoriteOverrides } from './use-thread-favorite';\n\nexport type RestConversationAuth = {\n apiKey?: string;\n token?: string | null;\n};\n\nexport interface UseRestConversationHistoryOptions {\n backendUrl: string;\n /** Non-secret identity included in the query cache boundary. */\n principalId: string;\n getAuth: () => RestConversationAuth | Promise<RestConversationAuth>;\n search?: string;\n enabled?: boolean;\n pageSize?: number;\n}\n\n/**\n * One inclusive, paginated conversation controller for satellite clients.\n * No app/channel filter is applied: authorization determines the user's rows,\n * while origin and automation metadata remain visible for UI filtering.\n */\nexport function useRestConversationHistory({\n backendUrl,\n principalId,\n getAuth,\n search,\n enabled = true,\n pageSize = 50,\n}: UseRestConversationHistoryOptions) {\n const fetchPage = useMemo(\n () => createRestConversationListFetcher({ backendUrl, getAuth }),\n [backendUrl, getAuth],\n );\n const list = useConversationList({\n fetchPage,\n filter: search?.trim() ? { search: search.trim() } : {},\n pageSize,\n scopeKey: `${backendUrl}::${principalId || 'unknown-principal'}`,\n enabled: enabled && principalId.length > 0,\n });\n\n const archive = useMutation({\n mutationKey: ['conversation-history', 'archive', backendUrl, principalId],\n mutationFn: async (threadId: string) => {\n await archiveThread(backendUrl, await getAuth(), threadId);\n return threadId;\n },\n onSuccess: (threadId) => list.removeConversation(threadId),\n });\n\n const readState = useMutation({\n mutationKey: ['conversation-history', 'read-state', backendUrl, principalId],\n mutationFn: async ({\n threadId,\n isUnread,\n }: {\n threadId: string;\n isUnread: boolean;\n }) => setThreadReadState(backendUrl, await getAuth(), { threadId, isUnread }),\n onSuccess: (result) =>\n list.patchConversation(result.thread_id, { isUnread: result.is_unread }),\n });\n\n // The pin flips optimistically and the server's answer is patched into the\n // page; the override drops once the page reports it.\n const patchConversation = list.patchConversation;\n const favorite = useThreadFavorite({\n backendUrl,\n principalId,\n getAuth,\n rows: list.conversations,\n onServerTruth: ({ threadId, isFavorited }) => patchConversation(threadId, { isFavorited }),\n });\n const favoriteOverrides = favorite.overrides;\n const conversations = useMemo(\n () => withFavoriteOverrides(list.conversations, favoriteOverrides),\n [list.conversations, favoriteOverrides],\n );\n\n return {\n ...list,\n conversations,\n archiveConversation: archive.mutate,\n archivePending: archive.isPending,\n setConversationReadState: readState.mutate,\n readStatePending: readState.isPending,\n /** Pin or unpin a conversation; read-only on a backend without the route (`favoriteToggleAvailable`). */\n setConversationFavorited: favorite.setFavorited,\n favoritePending: favorite.isPending,\n /** The conversations whose pin toggle is in flight. */\n favoritePendingThreadIds: favorite.pendingThreadIds,\n /** `false` once the backend answered 404 / 405 to the favorite route. */\n favoriteToggleAvailable: favorite.isAvailable,\n };\n}\n\nconst GENERIC_TITLES = new Set([\n 'new chat',\n 'new conversation',\n 'untitled',\n 'untitled conversation',\n]);\n\n/** Prefer a useful server title, then a normalized last-message preview. */\nexport function conversationDisplayTitle(\n conversation: Pick<ConversationSummary, 'title' | 'preview'>,\n): string {\n const title = conversation.title.trim();\n if (title && !GENERIC_TITLES.has(title.toLowerCase())) return title;\n const preview = conversation.preview?.replace(/\\s+/g, ' ').trim();\n if (preview) return preview.length > 80 ? `${preview.slice(0, 77)}…` : preview;\n return 'Untitled conversation';\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AA8BO,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,WAAW;AACb,GAAsC;AACpC,QAAM,YAAY;AAAA,IAChB,MAAM,kCAAkC,EAAE,YAAY,SAAS;AAAA,IAC/D,CAAC,YAAY,OAAO;AAAA,EAAA;AAEtB,QAAM,OAAO,oBAAoB;AAAA,IAC/B;AAAA,IACA,SAAQ,iCAAQ,UAAS,EAAE,QAAQ,OAAO,KAAA,EAAK,IAAM,CAAA;AAAA,IACrD;AAAA,IACA,UAAU,GAAG,UAAU,KAAK,eAAe,mBAAmB;AAAA,IAC9D,SAAS,WAAW,YAAY,SAAS;AAAA,EAAA,CAC1C;AAED,QAAM,UAAU,YAAY;AAAA,IAC1B,aAAa,CAAC,wBAAwB,WAAW,YAAY,WAAW;AAAA,IACxE,YAAY,OAAO,aAAqB;AACtC,YAAM,cAAc,YAAY,MAAM,QAAA,GAAW,QAAQ;AACzD,aAAO;AAAA,IACT;AAAA,IACA,WAAW,CAAC,aAAa,KAAK,mBAAmB,QAAQ;AAAA,EAAA,CAC1D;AAED,QAAM,YAAY,YAAY;AAAA,IAC5B,aAAa,CAAC,wBAAwB,cAAc,YAAY,WAAW;AAAA,IAC3E,YAAY,OAAO;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,MAII,mBAAmB,YAAY,MAAM,WAAW,EAAE,UAAU,UAAU;AAAA,IAC5E,WAAW,CAAC,WACV,KAAK,kBAAkB,OAAO,WAAW,EAAE,UAAU,OAAO,UAAA,CAAW;AAAA,EAAA,CAC1E;AAID,QAAM,oBAAoB,KAAK;AAC/B,QAAM,WAAW,kBAAkB;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,KAAK;AAAA,IACX,eAAe,CAAC,EAAE,UAAU,YAAA,MAAkB,kBAAkB,UAAU,EAAE,YAAA,CAAa;AAAA,EAAA,CAC1F;AACD,QAAM,oBAAoB,SAAS;AACnC,QAAM,gBAAgB;AAAA,IACpB,MAAM,sBAAsB,KAAK,eAAe,iBAAiB;AAAA,IACjE,CAAC,KAAK,eAAe,iBAAiB;AAAA,EAAA;AAGxC,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA,qBAAqB,QAAQ;AAAA,IAC7B,gBAAgB,QAAQ;AAAA,IACxB,0BAA0B,UAAU;AAAA,IACpC,kBAAkB,UAAU;AAAA;AAAA,IAE5B,0BAA0B,SAAS;AAAA,IACnC,iBAAiB,SAAS;AAAA;AAAA,IAE1B,0BAA0B,SAAS;AAAA;AAAA,IAEnC,yBAAyB,SAAS;AAAA,EAAA;AAEtC;AAEA,MAAM,qCAAqB,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,SAAS,yBACd,cACQ;;AACR,QAAM,QAAQ,aAAa,MAAM,KAAA;AACjC,MAAI,SAAS,CAAC,eAAe,IAAI,MAAM,YAAA,CAAa,EAAG,QAAO;AAC9D,QAAM,WAAU,kBAAa,YAAb,mBAAsB,QAAQ,QAAQ,KAAK;AAC3D,MAAI,QAAS,QAAO,QAAQ,SAAS,KAAK,GAAG,QAAQ,MAAM,GAAG,EAAE,CAAC,MAAM;AACvE,SAAO;AACT;"}
@@ -3,21 +3,23 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  require("../packages/agent-runtime/src/generated/runconfig-contract.cjs");
4
4
  const react = require("react");
5
5
  const conversationIndex = require("../packages/agent-runtime/src/conversation-index.cjs");
6
+ require("../packages/agent-runtime/src/conversation-list.cjs");
6
7
  const reactQuery = require("@tanstack/react-query");
7
8
  require("../packages/agent-runtime/src/statewire-state.cjs");
8
9
  require("../packages/agent-runtime/src/messages.cjs");
9
- require("@assistant-ui/react-statewire");
10
+ require("statewire");
10
11
  require("@assistant-ui/store");
11
12
  require("../packages/agent-runtime/src/quota-safe-local-storage-session.cjs");
12
13
  require("../packages/agent-runtime/src/statewire-suspended.cjs");
13
14
  require("@assistant-ui/tap");
14
15
  require("../packages/agent-runtime/src/statewire-attach-observer.cjs");
15
- require("statewire");
16
16
  require("../packages/agent-runtime/src/thread-status.cjs");
17
+ require("@assistant-ui/react-statewire");
17
18
  require("@assistant-ui/core");
18
19
  require("../packages/agent-runtime/src/thread-handoff.cjs");
19
20
  const api = require("./api.cjs");
20
21
  const useRestConversationHistory = require("./use-rest-conversation-history.cjs");
22
+ const useThreadFavorite = require("./use-thread-favorite.cjs");
21
23
  function conversationSummaryFromIndexEntry(entry) {
22
24
  return {
23
25
  threadId: entry.thread_id,
@@ -116,24 +118,39 @@ function useStatewireConversationHistory({
116
118
  return next.size === current.size ? current : next;
117
119
  });
118
120
  }, [archivedThreadIds.size, index.entries]);
119
- const conversations = react.useMemo(() => {
120
- return conversationHistorySource({
121
+ const sourceConversations = react.useMemo(
122
+ () => conversationHistorySource({
121
123
  indexEntries: index.entries,
122
124
  restConversations: legacyHistory.conversations,
123
125
  supportsStatewirePaging,
124
126
  hasSearch
125
- }).filter((conversation) => !archivedThreadIds.has(conversation.threadId)).map((conversation) => ({
126
- ...conversation,
127
- isUnread: readOverrides[conversation.threadId] ?? conversation.isUnread
128
- }));
129
- }, [
130
- archivedThreadIds,
131
- index.entries,
132
- legacyHistory.conversations,
133
- hasSearch,
134
- readOverrides,
135
- supportsStatewirePaging
136
- ]);
127
+ }).filter((conversation) => !archivedThreadIds.has(conversation.threadId)),
128
+ [
129
+ archivedThreadIds,
130
+ index.entries,
131
+ legacyHistory.conversations,
132
+ hasSearch,
133
+ supportsStatewirePaging
134
+ ]
135
+ );
136
+ const patchLegacyConversation = legacyHistory.patchConversation;
137
+ const favorite = useThreadFavorite.useThreadFavorite({
138
+ backendUrl,
139
+ principalId,
140
+ getAuth,
141
+ rows: sourceConversations,
142
+ onServerTruth: ({ threadId, isFavorited }) => patchLegacyConversation(threadId, { isFavorited })
143
+ });
144
+ const favoriteOverrides = favorite.overrides;
145
+ const conversations = react.useMemo(
146
+ () => useThreadFavorite.withFavoriteOverrides(sourceConversations, favoriteOverrides).map(
147
+ (conversation) => ({
148
+ ...conversation,
149
+ isUnread: readOverrides[conversation.threadId] ?? conversation.isUnread
150
+ })
151
+ ),
152
+ [sourceConversations, favoriteOverrides, readOverrides]
153
+ );
137
154
  const refreshMutation = reactQuery.useMutation({
138
155
  mutationKey: [
139
156
  "conversation-index",
@@ -236,6 +253,13 @@ function useStatewireConversationHistory({
236
253
  archivePending: archive.isPending,
237
254
  setConversationReadState: readState.mutate,
238
255
  readStatePending: readState.isPending,
256
+ /** Pin or unpin a conversation; read-only on a backend without the route (`favoriteToggleAvailable`). */
257
+ setConversationFavorited: favorite.setFavorited,
258
+ favoritePending: favorite.isPending,
259
+ /** The conversations whose pin toggle is in flight. */
260
+ favoritePendingThreadIds: favorite.pendingThreadIds,
261
+ /** `false` once the backend answered 404 / 405 to the favorite route. */
262
+ favoriteToggleAvailable: favorite.isAvailable,
239
263
  connection: index.connection
240
264
  };
241
265
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-statewire-conversation-history.cjs","sources":["../../src/threads/use-statewire-conversation-history.ts"],"sourcesContent":["import {\n\ttype ConversationIndexEntry,\n\ttype ConversationSummary,\n\ttype UseConversationIndexOptions,\n\tuseConversationIndex,\n} from \"@athenaintel/agent-runtime\";\nimport { useMutation } from \"@tanstack/react-query\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { archiveThread, setThreadReadState } from \"./api\";\nimport {\n\ttype RestConversationAuth,\n\tuseRestConversationHistory,\n} from \"./use-rest-conversation-history\";\n\nexport interface UseStatewireConversationHistoryOptions {\n\tbackendUrl: string;\n\tconversationIndexBaseUrl: string;\n\t/** The workspace being viewed; the index is one instance per (workspace, user). */\n\tworkspaceId: string;\n\tprincipalId: string;\n\tgetAuth: () => RestConversationAuth | Promise<RestConversationAuth>;\n\theaders: UseConversationIndexOptions[\"headers\"];\n\tsearch?: string;\n\tenabled?: boolean;\n\tsse?: boolean;\n}\n\n/** Convert the replicated wire row into the shared cross-surface contract. */\nexport function conversationSummaryFromIndexEntry(\n\tentry: ConversationIndexEntry,\n): ConversationSummary {\n\treturn {\n\t\tthreadId: entry.thread_id,\n\t\ttitle: entry.title,\n\t\tupdatedAt: entry.updated_at,\n\t\tcreatedAt: null,\n\t\tstate: entry.state,\n\t\truntime: entry.runtime,\n\t\tsourceChannel: entry.start_channel,\n\t\tassetType: entry.asset_type,\n\t\tassetId: entry.asset_id ?? null,\n\t\tappId: entry.app_id ?? null,\n\t\ttriggerType: entry.trigger_type ?? null,\n\t\tpreview: entry.last_message_preview ?? null,\n\t\tmessageCount: entry.message_count ?? null,\n\t\tisUnread: entry.is_unread ?? false,\n\t\tsessionStatus: entry.session_status ?? null,\n\t\trunStatus: entry.run_status ?? null,\n\t};\n}\n\nexport function conversationHistorySource({\n\tindexEntries,\n\trestConversations,\n\tsupportsStatewirePaging,\n\thasSearch,\n}: {\n\tindexEntries: readonly ConversationIndexEntry[] | null;\n\trestConversations: readonly ConversationSummary[];\n\tsupportsStatewirePaging: boolean;\n\thasSearch: boolean;\n}): ConversationSummary[] {\n\t// Search is server-backed so it covers the user's entire authorized history,\n\t// including rows older than the Statewire live window. An empty query uses\n\t// Statewire as the render source; REST only supplements rolling old hosts.\n\tif (hasSearch) return [...restConversations];\n\tconst statewireItems = (indexEntries ?? []).map(\n\t\tconversationSummaryFromIndexEntry,\n\t);\n\tif (supportsStatewirePaging) return statewireItems;\n\tconst statewireIds = new Set(statewireItems.map((item) => item.threadId));\n\treturn [\n\t\t...statewireItems,\n\t\t...restConversations.filter(\n\t\t\t(conversation) => !statewireIds.has(conversation.threadId),\n\t\t),\n\t];\n}\n\nexport function shouldUseRestConversationHistory({\n\thasSearch,\n\tindexEntries,\n\tsupportsStatewirePaging,\n\tconnectionStatus,\n}: {\n\thasSearch: boolean;\n\tindexEntries: readonly ConversationIndexEntry[] | null;\n\tsupportsStatewirePaging: boolean;\n\tconnectionStatus: string;\n}): boolean {\n\tif (hasSearch) return true;\n\tif (supportsStatewirePaging) return false;\n\t// An old host is identifiable once its snapshot arrives. A transport that\n\t// fails or is parked before any snapshot also needs the independent REST\n\t// path; otherwise entries stays null and the history panel loads forever.\n\treturn (\n\t\tindexEntries !== null ||\n\t\tconnectionStatus === \"reconnecting\" ||\n\t\tconnectionStatus === \"gone\"\n\t);\n}\n\n/**\n * Inclusive satellite conversation history backed by the user's Pinned\n * Statewire instance. The replicated window is the render source; refreshes,\n * pagination, thread creation, lifecycle changes, and title changes arrive on\n * that same channel. A non-empty search deliberately uses the authorized REST\n * list endpoint so matches are not limited to the replicated newest window.\n */\nexport function useStatewireConversationHistory({\n\tbackendUrl,\n\tconversationIndexBaseUrl,\n\tworkspaceId,\n\tprincipalId,\n\tgetAuth,\n\theaders,\n\tsearch,\n\tenabled = true,\n\tsse = false,\n}: UseStatewireConversationHistoryOptions) {\n\tconst index = useConversationIndex({\n\t\tbaseUrl: conversationIndexBaseUrl,\n\t\tworkspaceId: workspaceId || \"pending-workspace\",\n\t\tuserId: principalId || \"pending-user\",\n\t\theaders,\n\t\tenabled: enabled && workspaceId.length > 0 && principalId.length > 0,\n\t\tsse,\n\t});\n\t// Rolling deploy compatibility: the pre-paging index snapshot has no\n\t// total_count. It still renders live rows immediately, while the old REST\n\t// pager remains available only until the upgraded host activates.\n\tconst supportsStatewirePaging = index.totalCount !== null;\n\tconst normalizedSearch = search?.trim().toLowerCase() ?? \"\";\n\tconst hasSearch = normalizedSearch.length > 0;\n\tconst usesRestHistory = shouldUseRestConversationHistory({\n\t\thasSearch,\n\t\tindexEntries: index.entries,\n\t\tsupportsStatewirePaging,\n\t\tconnectionStatus: index.connection.status,\n\t});\n\tconst legacyHistory = useRestConversationHistory({\n\t\tbackendUrl,\n\t\tprincipalId,\n\t\tgetAuth,\n\t\tsearch,\n\t\tenabled: enabled && principalId.length > 0 && usesRestHistory,\n\t});\n\tconst [readOverrides, setReadOverrides] = useState<Record<string, boolean>>(\n\t\t{},\n\t);\n\tconst [archivedThreadIds, setArchivedThreadIds] = useState<\n\t\tReadonlySet<string>\n\t>(() => new Set());\n\n\tuseEffect(() => {\n\t\tif (index.entries === null || archivedThreadIds.size === 0) return;\n\t\tconst visibleIds = new Set(index.entries.map((entry) => entry.thread_id));\n\t\tsetArchivedThreadIds((current) => {\n\t\t\tconst next = new Set(\n\t\t\t\t[...current].filter((threadId) => visibleIds.has(threadId)),\n\t\t\t);\n\t\t\treturn next.size === current.size ? current : next;\n\t\t});\n\t}, [archivedThreadIds.size, index.entries]);\n\n\tconst conversations = useMemo(() => {\n\t\treturn conversationHistorySource({\n\t\t\tindexEntries: index.entries,\n\t\t\trestConversations: legacyHistory.conversations,\n\t\t\tsupportsStatewirePaging,\n\t\t\thasSearch,\n\t\t})\n\t\t\t.filter((conversation) => !archivedThreadIds.has(conversation.threadId))\n\t\t\t.map((conversation) => ({\n\t\t\t\t...conversation,\n\t\t\t\tisUnread: readOverrides[conversation.threadId] ?? conversation.isUnread,\n\t\t\t}));\n\t}, [\n\t\tarchivedThreadIds,\n\t\tindex.entries,\n\t\tlegacyHistory.conversations,\n\t\thasSearch,\n\t\treadOverrides,\n\t\tsupportsStatewirePaging,\n\t]);\n\n\tconst refreshMutation = useMutation({\n\t\tmutationKey: [\n\t\t\t\"conversation-index\",\n\t\t\t\"refresh\",\n\t\t\tconversationIndexBaseUrl,\n\t\t\tprincipalId,\n\t\t],\n\t\tmutationFn: () =>\n\t\t\tusesRestHistory ? legacyHistory.refresh() : index.refresh(),\n\t});\n\tconst loadMoreMutation = useMutation({\n\t\tmutationKey: [\n\t\t\t\"conversation-index\",\n\t\t\t\"load-more\",\n\t\t\tconversationIndexBaseUrl,\n\t\t\tprincipalId,\n\t\t],\n\t\tmutationFn: () =>\n\t\t\tusesRestHistory ? legacyHistory.fetchNextPage() : index.loadMore(),\n\t});\n\tconst archive = useMutation({\n\t\tmutationKey: [\"conversation-history\", \"archive\", backendUrl, principalId],\n\t\tmutationFn: async (threadId: string) => {\n\t\t\tawait archiveThread(backendUrl, await getAuth(), threadId);\n\t\t\treturn threadId;\n\t\t},\n\t\tonMutate: (threadId) => {\n\t\t\tsetArchivedThreadIds((current) => new Set(current).add(threadId));\n\t\t},\n\t\tonError: (_error, threadId) => {\n\t\t\tsetArchivedThreadIds((current) => {\n\t\t\t\tconst next = new Set(current);\n\t\t\t\tnext.delete(threadId);\n\t\t\t\treturn next;\n\t\t\t});\n\t\t},\n\t\tonSuccess: () => {\n\t\t\trefreshMutation.mutate();\n\t\t},\n\t});\n\tconst readState = useMutation({\n\t\tmutationKey: [\n\t\t\t\"conversation-history\",\n\t\t\t\"read-state\",\n\t\t\tbackendUrl,\n\t\t\tprincipalId,\n\t\t],\n\t\tmutationFn: async ({\n\t\t\tthreadId,\n\t\t\tisUnread,\n\t\t}: {\n\t\t\tthreadId: string;\n\t\t\tisUnread: boolean;\n\t\t}) =>\n\t\t\tsetThreadReadState(backendUrl, await getAuth(), { threadId, isUnread }),\n\t\tonMutate: ({ threadId, isUnread }) => {\n\t\t\tconst previous = readOverrides[threadId];\n\t\t\tsetReadOverrides((current) => ({ ...current, [threadId]: isUnread }));\n\t\t\treturn { previous, threadId };\n\t\t},\n\t\tonError: (_error, _variables, context) => {\n\t\t\tif (!context) return;\n\t\t\tsetReadOverrides((current) => {\n\t\t\t\tconst next = { ...current };\n\t\t\t\tif (context.previous === undefined) delete next[context.threadId];\n\t\t\t\telse next[context.threadId] = context.previous;\n\t\t\t\treturn next;\n\t\t\t});\n\t\t},\n\t\tonSuccess: (result) => {\n\t\t\tsetReadOverrides((current) => ({\n\t\t\t\t...current,\n\t\t\t\t[result.thread_id]: result.is_unread,\n\t\t\t}));\n\t\t\trefreshMutation.mutate(undefined, {\n\t\t\t\tonSettled: () => {\n\t\t\t\t\tsetReadOverrides((current) => {\n\t\t\t\t\t\tconst next = { ...current };\n\t\t\t\t\t\tdelete next[result.thread_id];\n\t\t\t\t\t\treturn next;\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\t});\n\n\tconst refresh = useCallback(\n\t\t() => refreshMutation.mutateAsync(),\n\t\t[refreshMutation.mutateAsync],\n\t);\n\tconst fetchNextPage = useCallback(\n\t\t() => loadMoreMutation.mutateAsync(),\n\t\t[loadMoreMutation.mutateAsync],\n\t);\n\n\treturn {\n\t\tconversations,\n\t\ttotalCount: usesRestHistory ? legacyHistory.totalCount : index.totalCount,\n\t\tisLoading:\n\t\t\tenabled &&\n\t\t\t(usesRestHistory\n\t\t\t\t? !legacyHistory.hasResolvedFirstPage\n\t\t\t\t: index.entries === null),\n\t\tisFetching:\n\t\t\trefreshMutation.isPending ||\n\t\t\tloadMoreMutation.isPending ||\n\t\t\tlegacyHistory.isFetching,\n\t\tisRefreshing: refreshMutation.isPending,\n\t\thasResolvedFirstPage: usesRestHistory\n\t\t\t? legacyHistory.hasResolvedFirstPage\n\t\t\t: index.entries !== null,\n\t\thasNextPage: usesRestHistory\n\t\t\t? legacyHistory.hasNextPage\n\t\t\t: index.hasNextPage,\n\t\tisFetchingNextPage: loadMoreMutation.isPending,\n\t\tfetchNextPage,\n\t\trefresh,\n\t\tarchiveConversation: archive.mutate,\n\t\tarchivePending: archive.isPending,\n\t\tsetConversationReadState: readState.mutate,\n\t\treadStatePending: readState.isPending,\n\t\tconnection: index.connection,\n\t};\n}\n"],"names":["useConversationIndex","useRestConversationHistory","useState","useEffect","useMemo","useMutation","archiveThread","setThreadReadState","useCallback"],"mappings":"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"}
1
+ {"version":3,"file":"use-statewire-conversation-history.cjs","sources":["../../src/threads/use-statewire-conversation-history.ts"],"sourcesContent":["import {\n\ttype ConversationIndexEntry,\n\ttype ConversationSummary,\n\ttype UseConversationIndexOptions,\n\tuseConversationIndex,\n} from \"@athenaintel/agent-runtime\";\nimport { useMutation } from \"@tanstack/react-query\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { archiveThread, setThreadReadState } from \"./api\";\nimport {\n\ttype RestConversationAuth,\n\tuseRestConversationHistory,\n} from \"./use-rest-conversation-history\";\nimport { useThreadFavorite, withFavoriteOverrides } from \"./use-thread-favorite\";\n\nexport interface UseStatewireConversationHistoryOptions {\n\tbackendUrl: string;\n\tconversationIndexBaseUrl: string;\n\t/** The workspace being viewed; the index is one instance per (workspace, user). */\n\tworkspaceId: string;\n\tprincipalId: string;\n\tgetAuth: () => RestConversationAuth | Promise<RestConversationAuth>;\n\theaders: UseConversationIndexOptions[\"headers\"];\n\tsearch?: string;\n\tenabled?: boolean;\n\tsse?: boolean;\n}\n\n/** Convert the replicated wire row into the shared cross-surface contract. */\nexport function conversationSummaryFromIndexEntry(\n\tentry: ConversationIndexEntry,\n): ConversationSummary {\n\treturn {\n\t\tthreadId: entry.thread_id,\n\t\ttitle: entry.title,\n\t\tupdatedAt: entry.updated_at,\n\t\tcreatedAt: null,\n\t\tstate: entry.state,\n\t\truntime: entry.runtime,\n\t\tsourceChannel: entry.start_channel,\n\t\tassetType: entry.asset_type,\n\t\tassetId: entry.asset_id ?? null,\n\t\tappId: entry.app_id ?? null,\n\t\ttriggerType: entry.trigger_type ?? null,\n\t\tpreview: entry.last_message_preview ?? null,\n\t\tmessageCount: entry.message_count ?? null,\n\t\tisUnread: entry.is_unread ?? false,\n\t\tsessionStatus: entry.session_status ?? null,\n\t\trunStatus: entry.run_status ?? null,\n\t};\n}\n\nexport function conversationHistorySource({\n\tindexEntries,\n\trestConversations,\n\tsupportsStatewirePaging,\n\thasSearch,\n}: {\n\tindexEntries: readonly ConversationIndexEntry[] | null;\n\trestConversations: readonly ConversationSummary[];\n\tsupportsStatewirePaging: boolean;\n\thasSearch: boolean;\n}): ConversationSummary[] {\n\t// Search is server-backed so it covers the user's entire authorized history,\n\t// including rows older than the Statewire live window. An empty query uses\n\t// Statewire as the render source; REST only supplements rolling old hosts.\n\tif (hasSearch) return [...restConversations];\n\tconst statewireItems = (indexEntries ?? []).map(\n\t\tconversationSummaryFromIndexEntry,\n\t);\n\tif (supportsStatewirePaging) return statewireItems;\n\tconst statewireIds = new Set(statewireItems.map((item) => item.threadId));\n\treturn [\n\t\t...statewireItems,\n\t\t...restConversations.filter(\n\t\t\t(conversation) => !statewireIds.has(conversation.threadId),\n\t\t),\n\t];\n}\n\nexport function shouldUseRestConversationHistory({\n\thasSearch,\n\tindexEntries,\n\tsupportsStatewirePaging,\n\tconnectionStatus,\n}: {\n\thasSearch: boolean;\n\tindexEntries: readonly ConversationIndexEntry[] | null;\n\tsupportsStatewirePaging: boolean;\n\tconnectionStatus: string;\n}): boolean {\n\tif (hasSearch) return true;\n\tif (supportsStatewirePaging) return false;\n\t// An old host is identifiable once its snapshot arrives. A transport that\n\t// fails or is parked before any snapshot also needs the independent REST\n\t// path; otherwise entries stays null and the history panel loads forever.\n\treturn (\n\t\tindexEntries !== null ||\n\t\tconnectionStatus === \"reconnecting\" ||\n\t\tconnectionStatus === \"gone\"\n\t);\n}\n\n/**\n * Inclusive satellite conversation history backed by the user's Pinned\n * Statewire instance. The replicated window is the render source; refreshes,\n * pagination, thread creation, lifecycle changes, and title changes arrive on\n * that same channel. A non-empty search deliberately uses the authorized REST\n * list endpoint so matches are not limited to the replicated newest window.\n */\nexport function useStatewireConversationHistory({\n\tbackendUrl,\n\tconversationIndexBaseUrl,\n\tworkspaceId,\n\tprincipalId,\n\tgetAuth,\n\theaders,\n\tsearch,\n\tenabled = true,\n\tsse = false,\n}: UseStatewireConversationHistoryOptions) {\n\tconst index = useConversationIndex({\n\t\tbaseUrl: conversationIndexBaseUrl,\n\t\tworkspaceId: workspaceId || \"pending-workspace\",\n\t\tuserId: principalId || \"pending-user\",\n\t\theaders,\n\t\tenabled: enabled && workspaceId.length > 0 && principalId.length > 0,\n\t\tsse,\n\t});\n\t// Rolling deploy compatibility: the pre-paging index snapshot has no\n\t// total_count. It still renders live rows immediately, while the old REST\n\t// pager remains available only until the upgraded host activates.\n\tconst supportsStatewirePaging = index.totalCount !== null;\n\tconst normalizedSearch = search?.trim().toLowerCase() ?? \"\";\n\tconst hasSearch = normalizedSearch.length > 0;\n\tconst usesRestHistory = shouldUseRestConversationHistory({\n\t\thasSearch,\n\t\tindexEntries: index.entries,\n\t\tsupportsStatewirePaging,\n\t\tconnectionStatus: index.connection.status,\n\t});\n\tconst legacyHistory = useRestConversationHistory({\n\t\tbackendUrl,\n\t\tprincipalId,\n\t\tgetAuth,\n\t\tsearch,\n\t\tenabled: enabled && principalId.length > 0 && usesRestHistory,\n\t});\n\tconst [readOverrides, setReadOverrides] = useState<Record<string, boolean>>(\n\t\t{},\n\t);\n\tconst [archivedThreadIds, setArchivedThreadIds] = useState<\n\t\tReadonlySet<string>\n\t>(() => new Set());\n\n\tuseEffect(() => {\n\t\tif (index.entries === null || archivedThreadIds.size === 0) return;\n\t\tconst visibleIds = new Set(index.entries.map((entry) => entry.thread_id));\n\t\tsetArchivedThreadIds((current) => {\n\t\t\tconst next = new Set(\n\t\t\t\t[...current].filter((threadId) => visibleIds.has(threadId)),\n\t\t\t);\n\t\t\treturn next.size === current.size ? current : next;\n\t\t});\n\t}, [archivedThreadIds.size, index.entries]);\n\n\tconst sourceConversations = useMemo(\n\t\t() =>\n\t\t\tconversationHistorySource({\n\t\t\t\tindexEntries: index.entries,\n\t\t\t\trestConversations: legacyHistory.conversations,\n\t\t\t\tsupportsStatewirePaging,\n\t\t\t\thasSearch,\n\t\t\t}).filter((conversation) => !archivedThreadIds.has(conversation.threadId)),\n\t\t[\n\t\t\tarchivedThreadIds,\n\t\t\tindex.entries,\n\t\t\tlegacyHistory.conversations,\n\t\t\thasSearch,\n\t\t\tsupportsStatewirePaging,\n\t\t],\n\t);\n\t// The pin flips optimistically over both sources; the Statewire index\n\t// pushes the confirmed value, the REST page is patched with it, and the\n\t// override drops once the rendered source reports it.\n\tconst patchLegacyConversation = legacyHistory.patchConversation;\n\tconst favorite = useThreadFavorite({\n\t\tbackendUrl,\n\t\tprincipalId,\n\t\tgetAuth,\n\t\trows: sourceConversations,\n\t\tonServerTruth: ({ threadId, isFavorited }) =>\n\t\t\tpatchLegacyConversation(threadId, { isFavorited }),\n\t});\n\tconst favoriteOverrides = favorite.overrides;\n\n\tconst conversations = useMemo(\n\t\t() =>\n\t\t\twithFavoriteOverrides(sourceConversations, favoriteOverrides).map(\n\t\t\t\t(conversation) => ({\n\t\t\t\t\t...conversation,\n\t\t\t\t\tisUnread:\n\t\t\t\t\t\treadOverrides[conversation.threadId] ?? conversation.isUnread,\n\t\t\t\t}),\n\t\t\t),\n\t\t[sourceConversations, favoriteOverrides, readOverrides],\n\t);\n\n\tconst refreshMutation = useMutation({\n\t\tmutationKey: [\n\t\t\t\"conversation-index\",\n\t\t\t\"refresh\",\n\t\t\tconversationIndexBaseUrl,\n\t\t\tprincipalId,\n\t\t],\n\t\tmutationFn: () =>\n\t\t\tusesRestHistory ? legacyHistory.refresh() : index.refresh(),\n\t});\n\tconst loadMoreMutation = useMutation({\n\t\tmutationKey: [\n\t\t\t\"conversation-index\",\n\t\t\t\"load-more\",\n\t\t\tconversationIndexBaseUrl,\n\t\t\tprincipalId,\n\t\t],\n\t\tmutationFn: () =>\n\t\t\tusesRestHistory ? legacyHistory.fetchNextPage() : index.loadMore(),\n\t});\n\tconst archive = useMutation({\n\t\tmutationKey: [\"conversation-history\", \"archive\", backendUrl, principalId],\n\t\tmutationFn: async (threadId: string) => {\n\t\t\tawait archiveThread(backendUrl, await getAuth(), threadId);\n\t\t\treturn threadId;\n\t\t},\n\t\tonMutate: (threadId) => {\n\t\t\tsetArchivedThreadIds((current) => new Set(current).add(threadId));\n\t\t},\n\t\tonError: (_error, threadId) => {\n\t\t\tsetArchivedThreadIds((current) => {\n\t\t\t\tconst next = new Set(current);\n\t\t\t\tnext.delete(threadId);\n\t\t\t\treturn next;\n\t\t\t});\n\t\t},\n\t\tonSuccess: () => {\n\t\t\trefreshMutation.mutate();\n\t\t},\n\t});\n\tconst readState = useMutation({\n\t\tmutationKey: [\n\t\t\t\"conversation-history\",\n\t\t\t\"read-state\",\n\t\t\tbackendUrl,\n\t\t\tprincipalId,\n\t\t],\n\t\tmutationFn: async ({\n\t\t\tthreadId,\n\t\t\tisUnread,\n\t\t}: {\n\t\t\tthreadId: string;\n\t\t\tisUnread: boolean;\n\t\t}) =>\n\t\t\tsetThreadReadState(backendUrl, await getAuth(), { threadId, isUnread }),\n\t\tonMutate: ({ threadId, isUnread }) => {\n\t\t\tconst previous = readOverrides[threadId];\n\t\t\tsetReadOverrides((current) => ({ ...current, [threadId]: isUnread }));\n\t\t\treturn { previous, threadId };\n\t\t},\n\t\tonError: (_error, _variables, context) => {\n\t\t\tif (!context) return;\n\t\t\tsetReadOverrides((current) => {\n\t\t\t\tconst next = { ...current };\n\t\t\t\tif (context.previous === undefined) delete next[context.threadId];\n\t\t\t\telse next[context.threadId] = context.previous;\n\t\t\t\treturn next;\n\t\t\t});\n\t\t},\n\t\tonSuccess: (result) => {\n\t\t\tsetReadOverrides((current) => ({\n\t\t\t\t...current,\n\t\t\t\t[result.thread_id]: result.is_unread,\n\t\t\t}));\n\t\t\trefreshMutation.mutate(undefined, {\n\t\t\t\tonSettled: () => {\n\t\t\t\t\tsetReadOverrides((current) => {\n\t\t\t\t\t\tconst next = { ...current };\n\t\t\t\t\t\tdelete next[result.thread_id];\n\t\t\t\t\t\treturn next;\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\t});\n\n\tconst refresh = useCallback(\n\t\t() => refreshMutation.mutateAsync(),\n\t\t[refreshMutation.mutateAsync],\n\t);\n\tconst fetchNextPage = useCallback(\n\t\t() => loadMoreMutation.mutateAsync(),\n\t\t[loadMoreMutation.mutateAsync],\n\t);\n\n\treturn {\n\t\tconversations,\n\t\ttotalCount: usesRestHistory ? legacyHistory.totalCount : index.totalCount,\n\t\tisLoading:\n\t\t\tenabled &&\n\t\t\t(usesRestHistory\n\t\t\t\t? !legacyHistory.hasResolvedFirstPage\n\t\t\t\t: index.entries === null),\n\t\tisFetching:\n\t\t\trefreshMutation.isPending ||\n\t\t\tloadMoreMutation.isPending ||\n\t\t\tlegacyHistory.isFetching,\n\t\tisRefreshing: refreshMutation.isPending,\n\t\thasResolvedFirstPage: usesRestHistory\n\t\t\t? legacyHistory.hasResolvedFirstPage\n\t\t\t: index.entries !== null,\n\t\thasNextPage: usesRestHistory\n\t\t\t? legacyHistory.hasNextPage\n\t\t\t: index.hasNextPage,\n\t\tisFetchingNextPage: loadMoreMutation.isPending,\n\t\tfetchNextPage,\n\t\trefresh,\n\t\tarchiveConversation: archive.mutate,\n\t\tarchivePending: archive.isPending,\n\t\tsetConversationReadState: readState.mutate,\n\t\treadStatePending: readState.isPending,\n\t\t/** Pin or unpin a conversation; read-only on a backend without the route (`favoriteToggleAvailable`). */\n\t\tsetConversationFavorited: favorite.setFavorited,\n\t\tfavoritePending: favorite.isPending,\n\t\t/** The conversations whose pin toggle is in flight. */\n\t\tfavoritePendingThreadIds: favorite.pendingThreadIds,\n\t\t/** `false` once the backend answered 404 / 405 to the favorite route. */\n\t\tfavoriteToggleAvailable: favorite.isAvailable,\n\t\tconnection: index.connection,\n\t};\n}\n"],"names":["useConversationIndex","useRestConversationHistory","useState","useEffect","useMemo","useThreadFavorite","withFavoriteOverrides","useMutation","archiveThread","setThreadReadState","useCallback"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA6BO,SAAS,kCACf,OACsB;AACtB,SAAO;AAAA,IACN,UAAU,MAAM;AAAA,IAChB,OAAO,MAAM;AAAA,IACb,WAAW,MAAM;AAAA,IACjB,WAAW;AAAA,IACX,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,IACf,eAAe,MAAM;AAAA,IACrB,WAAW,MAAM;AAAA,IACjB,SAAS,MAAM,YAAY;AAAA,IAC3B,OAAO,MAAM,UAAU;AAAA,IACvB,aAAa,MAAM,gBAAgB;AAAA,IACnC,SAAS,MAAM,wBAAwB;AAAA,IACvC,cAAc,MAAM,iBAAiB;AAAA,IACrC,UAAU,MAAM,aAAa;AAAA,IAC7B,eAAe,MAAM,kBAAkB;AAAA,IACvC,WAAW,MAAM,cAAc;AAAA,EAAA;AAEjC;AAEO,SAAS,0BAA0B;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAK0B;AAIzB,MAAI,UAAW,QAAO,CAAC,GAAG,iBAAiB;AAC3C,QAAM,kBAAkB,gBAAgB,CAAA,GAAI;AAAA,IAC3C;AAAA,EAAA;AAED,MAAI,wBAAyB,QAAO;AACpC,QAAM,eAAe,IAAI,IAAI,eAAe,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC;AACxE,SAAO;AAAA,IACN,GAAG;AAAA,IACH,GAAG,kBAAkB;AAAA,MACpB,CAAC,iBAAiB,CAAC,aAAa,IAAI,aAAa,QAAQ;AAAA,IAAA;AAAA,EAC1D;AAEF;AAEO,SAAS,iCAAiC;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAKY;AACX,MAAI,UAAW,QAAO;AACtB,MAAI,wBAAyB,QAAO;AAIpC,SACC,iBAAiB,QACjB,qBAAqB,kBACrB,qBAAqB;AAEvB;AASO,SAAS,gCAAgC;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,MAAM;AACP,GAA2C;AAC1C,QAAM,QAAQA,kBAAAA,qBAAqB;AAAA,IAClC,SAAS;AAAA,IACT,aAAa,eAAe;AAAA,IAC5B,QAAQ,eAAe;AAAA,IACvB;AAAA,IACA,SAAS,WAAW,YAAY,SAAS,KAAK,YAAY,SAAS;AAAA,IACnE;AAAA,EAAA,CACA;AAID,QAAM,0BAA0B,MAAM,eAAe;AACrD,QAAM,oBAAmB,iCAAQ,OAAO,kBAAiB;AACzD,QAAM,YAAY,iBAAiB,SAAS;AAC5C,QAAM,kBAAkB,iCAAiC;AAAA,IACxD;AAAA,IACA,cAAc,MAAM;AAAA,IACpB;AAAA,IACA,kBAAkB,MAAM,WAAW;AAAA,EAAA,CACnC;AACD,QAAM,gBAAgBC,2BAAAA,2BAA2B;AAAA,IAChD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,WAAW,YAAY,SAAS,KAAK;AAAA,EAAA,CAC9C;AACD,QAAM,CAAC,eAAe,gBAAgB,IAAIC,MAAAA;AAAAA,IACzC,CAAA;AAAA,EAAC;AAEF,QAAM,CAAC,mBAAmB,oBAAoB,IAAIA,MAAAA,SAEhD,MAAM,oBAAI,KAAK;AAEjBC,QAAAA,UAAU,MAAM;AACf,QAAI,MAAM,YAAY,QAAQ,kBAAkB,SAAS,EAAG;AAC5D,UAAM,aAAa,IAAI,IAAI,MAAM,QAAQ,IAAI,CAAC,UAAU,MAAM,SAAS,CAAC;AACxE,yBAAqB,CAAC,YAAY;AACjC,YAAM,OAAO,IAAI;AAAA,QAChB,CAAC,GAAG,OAAO,EAAE,OAAO,CAAC,aAAa,WAAW,IAAI,QAAQ,CAAC;AAAA,MAAA;AAE3D,aAAO,KAAK,SAAS,QAAQ,OAAO,UAAU;AAAA,IAC/C,CAAC;AAAA,EACF,GAAG,CAAC,kBAAkB,MAAM,MAAM,OAAO,CAAC;AAE1C,QAAM,sBAAsBC,MAAAA;AAAAA,IAC3B,MACC,0BAA0B;AAAA,MACzB,cAAc,MAAM;AAAA,MACpB,mBAAmB,cAAc;AAAA,MACjC;AAAA,MACA;AAAA,IAAA,CACA,EAAE,OAAO,CAAC,iBAAiB,CAAC,kBAAkB,IAAI,aAAa,QAAQ,CAAC;AAAA,IAC1E;AAAA,MACC;AAAA,MACA,MAAM;AAAA,MACN,cAAc;AAAA,MACd;AAAA,MACA;AAAA,IAAA;AAAA,EACD;AAKD,QAAM,0BAA0B,cAAc;AAC9C,QAAM,WAAWC,kBAAAA,kBAAkB;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN,eAAe,CAAC,EAAE,UAAU,YAAA,MAC3B,wBAAwB,UAAU,EAAE,YAAA,CAAa;AAAA,EAAA,CAClD;AACD,QAAM,oBAAoB,SAAS;AAEnC,QAAM,gBAAgBD,MAAAA;AAAAA,IACrB,MACCE,wCAAsB,qBAAqB,iBAAiB,EAAE;AAAA,MAC7D,CAAC,kBAAkB;AAAA,QAClB,GAAG;AAAA,QACH,UACC,cAAc,aAAa,QAAQ,KAAK,aAAa;AAAA,MAAA;AAAA,IACvD;AAAA,IAEF,CAAC,qBAAqB,mBAAmB,aAAa;AAAA,EAAA;AAGvD,QAAM,kBAAkBC,WAAAA,YAAY;AAAA,IACnC,aAAa;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,YAAY,MACX,kBAAkB,cAAc,QAAA,IAAY,MAAM,QAAA;AAAA,EAAQ,CAC3D;AACD,QAAM,mBAAmBA,WAAAA,YAAY;AAAA,IACpC,aAAa;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,YAAY,MACX,kBAAkB,cAAc,cAAA,IAAkB,MAAM,SAAA;AAAA,EAAS,CAClE;AACD,QAAM,UAAUA,WAAAA,YAAY;AAAA,IAC3B,aAAa,CAAC,wBAAwB,WAAW,YAAY,WAAW;AAAA,IACxE,YAAY,OAAO,aAAqB;AACvC,YAAMC,IAAAA,cAAc,YAAY,MAAM,QAAA,GAAW,QAAQ;AACzD,aAAO;AAAA,IACR;AAAA,IACA,UAAU,CAAC,aAAa;AACvB,2BAAqB,CAAC,YAAY,IAAI,IAAI,OAAO,EAAE,IAAI,QAAQ,CAAC;AAAA,IACjE;AAAA,IACA,SAAS,CAAC,QAAQ,aAAa;AAC9B,2BAAqB,CAAC,YAAY;AACjC,cAAM,OAAO,IAAI,IAAI,OAAO;AAC5B,aAAK,OAAO,QAAQ;AACpB,eAAO;AAAA,MACR,CAAC;AAAA,IACF;AAAA,IACA,WAAW,MAAM;AAChB,sBAAgB,OAAA;AAAA,IACjB;AAAA,EAAA,CACA;AACD,QAAM,YAAYD,WAAAA,YAAY;AAAA,IAC7B,aAAa;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,YAAY,OAAO;AAAA,MAClB;AAAA,MACA;AAAA,IAAA,MAKAE,IAAAA,mBAAmB,YAAY,MAAM,WAAW,EAAE,UAAU,UAAU;AAAA,IACvE,UAAU,CAAC,EAAE,UAAU,eAAe;AACrC,YAAM,WAAW,cAAc,QAAQ;AACvC,uBAAiB,CAAC,aAAa,EAAE,GAAG,SAAS,CAAC,QAAQ,GAAG,SAAA,EAAW;AACpE,aAAO,EAAE,UAAU,SAAA;AAAA,IACpB;AAAA,IACA,SAAS,CAAC,QAAQ,YAAY,YAAY;AACzC,UAAI,CAAC,QAAS;AACd,uBAAiB,CAAC,YAAY;AAC7B,cAAM,OAAO,EAAE,GAAG,QAAA;AAClB,YAAI,QAAQ,aAAa,OAAW,QAAO,KAAK,QAAQ,QAAQ;AAAA,YAC3D,MAAK,QAAQ,QAAQ,IAAI,QAAQ;AACtC,eAAO;AAAA,MACR,CAAC;AAAA,IACF;AAAA,IACA,WAAW,CAAC,WAAW;AACtB,uBAAiB,CAAC,aAAa;AAAA,QAC9B,GAAG;AAAA,QACH,CAAC,OAAO,SAAS,GAAG,OAAO;AAAA,MAAA,EAC1B;AACF,sBAAgB,OAAO,QAAW;AAAA,QACjC,WAAW,MAAM;AAChB,2BAAiB,CAAC,YAAY;AAC7B,kBAAM,OAAO,EAAE,GAAG,QAAA;AAClB,mBAAO,KAAK,OAAO,SAAS;AAC5B,mBAAO;AAAA,UACR,CAAC;AAAA,QACF;AAAA,MAAA,CACA;AAAA,IACF;AAAA,EAAA,CACA;AAED,QAAM,UAAUC,MAAAA;AAAAA,IACf,MAAM,gBAAgB,YAAA;AAAA,IACtB,CAAC,gBAAgB,WAAW;AAAA,EAAA;AAE7B,QAAM,gBAAgBA,MAAAA;AAAAA,IACrB,MAAM,iBAAiB,YAAA;AAAA,IACvB,CAAC,iBAAiB,WAAW;AAAA,EAAA;AAG9B,SAAO;AAAA,IACN;AAAA,IACA,YAAY,kBAAkB,cAAc,aAAa,MAAM;AAAA,IAC/D,WACC,YACC,kBACE,CAAC,cAAc,uBACf,MAAM,YAAY;AAAA,IACtB,YACC,gBAAgB,aAChB,iBAAiB,aACjB,cAAc;AAAA,IACf,cAAc,gBAAgB;AAAA,IAC9B,sBAAsB,kBACnB,cAAc,uBACd,MAAM,YAAY;AAAA,IACrB,aAAa,kBACV,cAAc,cACd,MAAM;AAAA,IACT,oBAAoB,iBAAiB;AAAA,IACrC;AAAA,IACA;AAAA,IACA,qBAAqB,QAAQ;AAAA,IAC7B,gBAAgB,QAAQ;AAAA,IACxB,0BAA0B,UAAU;AAAA,IACpC,kBAAkB,UAAU;AAAA;AAAA,IAE5B,0BAA0B,SAAS;AAAA,IACnC,iBAAiB,SAAS;AAAA;AAAA,IAE1B,0BAA0B,SAAS;AAAA;AAAA,IAEnC,yBAAyB,SAAS;AAAA,IAClC,YAAY,MAAM;AAAA,EAAA;AAEpB;;;;;"}