@athenaintel/react 0.41.3 → 0.42.2

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 (479) hide show
  1. package/README.md +132 -10
  2. package/dist/agent-runtime/src/asset-attachment.d.ts +98 -0
  3. package/dist/agent-runtime/src/athena-asset-drag.d.ts +32 -0
  4. package/dist/agent-runtime/src/auth-denial.d.ts +29 -0
  5. package/dist/agent-runtime/src/chat-outbox-attachments.cjs +41 -0
  6. package/dist/agent-runtime/src/chat-outbox-attachments.cjs.map +1 -0
  7. package/dist/agent-runtime/src/chat-outbox-attachments.d.ts +38 -0
  8. package/dist/agent-runtime/src/chat-outbox-attachments.js +41 -0
  9. package/dist/agent-runtime/src/chat-outbox-attachments.js.map +1 -0
  10. package/dist/agent-runtime/src/chat-outbox-signals.cjs +63 -0
  11. package/dist/agent-runtime/src/chat-outbox-signals.cjs.map +1 -0
  12. package/dist/agent-runtime/src/chat-outbox-signals.d.ts +12 -0
  13. package/dist/agent-runtime/src/chat-outbox-signals.js +63 -0
  14. package/dist/agent-runtime/src/chat-outbox-signals.js.map +1 -0
  15. package/dist/agent-runtime/src/chat-outbox-store.cjs +509 -0
  16. package/dist/agent-runtime/src/chat-outbox-store.cjs.map +1 -0
  17. package/dist/agent-runtime/src/chat-outbox-store.d.ts +203 -0
  18. package/dist/agent-runtime/src/chat-outbox-store.js +509 -0
  19. package/dist/agent-runtime/src/chat-outbox-store.js.map +1 -0
  20. package/dist/agent-runtime/src/chat-outbox-thread.cjs +136 -0
  21. package/dist/agent-runtime/src/chat-outbox-thread.cjs.map +1 -0
  22. package/dist/agent-runtime/src/chat-outbox-thread.d.ts +118 -0
  23. package/dist/agent-runtime/src/chat-outbox-thread.js +136 -0
  24. package/dist/agent-runtime/src/chat-outbox-thread.js.map +1 -0
  25. package/dist/agent-runtime/src/chat-outbox.cjs +696 -0
  26. package/dist/agent-runtime/src/chat-outbox.cjs.map +1 -0
  27. package/dist/agent-runtime/src/chat-outbox.d.ts +285 -0
  28. package/dist/agent-runtime/src/chat-outbox.js +696 -0
  29. package/dist/agent-runtime/src/chat-outbox.js.map +1 -0
  30. package/dist/agent-runtime/src/chat-socket-reachability.d.ts +45 -0
  31. package/dist/agent-runtime/src/client-tool-bridge.d.ts +152 -0
  32. package/dist/agent-runtime/src/client-tools.d.ts +133 -0
  33. package/dist/agent-runtime/src/composer-markdown.cjs +10 -0
  34. package/dist/agent-runtime/src/composer-markdown.cjs.map +1 -1
  35. package/dist/agent-runtime/src/composer-markdown.d.ts +108 -0
  36. package/dist/agent-runtime/src/composer-markdown.js +10 -0
  37. package/dist/agent-runtime/src/composer-markdown.js.map +1 -1
  38. package/dist/agent-runtime/src/config-mutations.d.ts +51 -0
  39. package/dist/agent-runtime/src/conversation-index.d.ts +110 -0
  40. package/dist/agent-runtime/src/conversation-list.d.ts +343 -0
  41. package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs +3 -2
  42. package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs.map +1 -1
  43. package/dist/agent-runtime/src/deep-agent-transport-recipe.d.ts +166 -0
  44. package/dist/agent-runtime/src/deep-agent-transport-recipe.js +3 -2
  45. package/dist/agent-runtime/src/deep-agent-transport-recipe.js.map +1 -1
  46. package/dist/agent-runtime/src/desktop-handoff.d.ts +139 -0
  47. package/dist/agent-runtime/src/durable-thread-snapshot-cache.d.ts +60 -0
  48. package/dist/agent-runtime/src/edit-command.d.ts +156 -0
  49. package/dist/agent-runtime/src/errors.d.ts +174 -0
  50. package/dist/agent-runtime/src/execute-output.d.ts +31 -0
  51. package/dist/agent-runtime/src/generated/runconfig-contract.cjs +16 -2
  52. package/dist/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  53. package/dist/agent-runtime/src/generated/runconfig-contract.d.ts +202 -0
  54. package/dist/agent-runtime/src/generated/runconfig-contract.js +16 -2
  55. package/dist/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  56. package/dist/agent-runtime/src/github-wake-event.d.ts +77 -0
  57. package/dist/agent-runtime/src/index.d.ts +74 -0
  58. package/dist/agent-runtime/src/interrupts.cjs +17 -0
  59. package/dist/agent-runtime/src/interrupts.cjs.map +1 -1
  60. package/dist/agent-runtime/src/interrupts.d.ts +348 -0
  61. package/dist/agent-runtime/src/interrupts.js +17 -0
  62. package/dist/agent-runtime/src/interrupts.js.map +1 -1
  63. package/dist/agent-runtime/src/lane-follow.d.ts +121 -0
  64. package/dist/agent-runtime/src/lane-history.cjs +209 -0
  65. package/dist/agent-runtime/src/lane-history.cjs.map +1 -0
  66. package/dist/agent-runtime/src/lane-history.d.ts +175 -0
  67. package/dist/agent-runtime/src/lane-history.js +209 -0
  68. package/dist/agent-runtime/src/lane-history.js.map +1 -0
  69. package/dist/agent-runtime/src/lane-images.d.ts +184 -0
  70. package/dist/agent-runtime/src/message-author.d.ts +43 -0
  71. package/dist/agent-runtime/src/message-text.cjs +15 -0
  72. package/dist/agent-runtime/src/message-text.cjs.map +1 -0
  73. package/dist/agent-runtime/src/message-text.d.ts +47 -0
  74. package/dist/agent-runtime/src/message-text.js +15 -0
  75. package/dist/agent-runtime/src/message-text.js.map +1 -0
  76. package/dist/agent-runtime/src/message-window.cjs +15 -1
  77. package/dist/agent-runtime/src/message-window.cjs.map +1 -1
  78. package/dist/agent-runtime/src/message-window.d.ts +159 -0
  79. package/dist/agent-runtime/src/message-window.js +16 -2
  80. package/dist/agent-runtime/src/message-window.js.map +1 -1
  81. package/dist/agent-runtime/src/messages.cjs +123 -8
  82. package/dist/agent-runtime/src/messages.cjs.map +1 -1
  83. package/dist/agent-runtime/src/messages.d.ts +621 -0
  84. package/dist/agent-runtime/src/messages.js +123 -8
  85. package/dist/agent-runtime/src/messages.js.map +1 -1
  86. package/dist/agent-runtime/src/model-context.d.ts +22 -0
  87. package/dist/agent-runtime/src/offline-status.d.ts +157 -0
  88. package/dist/agent-runtime/src/optimistic-content.d.ts +21 -0
  89. package/dist/agent-runtime/src/protocol.cjs +12 -4
  90. package/dist/agent-runtime/src/protocol.cjs.map +1 -1
  91. package/dist/agent-runtime/src/protocol.d.ts +119 -0
  92. package/dist/agent-runtime/src/protocol.js +13 -5
  93. package/dist/agent-runtime/src/protocol.js.map +1 -1
  94. package/dist/agent-runtime/src/ptc-subcalls.d.ts +51 -0
  95. package/dist/agent-runtime/src/queue.d.ts +138 -0
  96. package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs +10 -0
  97. package/dist/agent-runtime/src/quota-safe-local-storage-session.cjs.map +1 -1
  98. package/dist/agent-runtime/src/quota-safe-local-storage-session.d.ts +161 -0
  99. package/dist/agent-runtime/src/quota-safe-local-storage-session.js +10 -0
  100. package/dist/agent-runtime/src/quota-safe-local-storage-session.js.map +1 -1
  101. package/dist/agent-runtime/src/running-tool-placeholder.cjs +11 -0
  102. package/dist/agent-runtime/src/running-tool-placeholder.cjs.map +1 -0
  103. package/dist/agent-runtime/src/running-tool-placeholder.d.ts +25 -0
  104. package/dist/agent-runtime/src/running-tool-placeholder.js +11 -0
  105. package/dist/agent-runtime/src/running-tool-placeholder.js.map +1 -0
  106. package/dist/agent-runtime/src/runtime-messages.d.ts +104 -0
  107. package/dist/agent-runtime/src/screen-point.d.ts +151 -0
  108. package/dist/agent-runtime/src/sealed-idb.cjs +112 -0
  109. package/dist/agent-runtime/src/sealed-idb.cjs.map +1 -0
  110. package/dist/agent-runtime/src/sealed-idb.d.ts +106 -0
  111. package/dist/agent-runtime/src/sealed-idb.js +112 -0
  112. package/dist/agent-runtime/src/sealed-idb.js.map +1 -0
  113. package/dist/agent-runtime/src/send-recovery.d.ts +40 -0
  114. package/dist/agent-runtime/src/session-lane-scope.cjs +25 -0
  115. package/dist/agent-runtime/src/session-lane-scope.cjs.map +1 -0
  116. package/dist/agent-runtime/src/session-lane-scope.d.ts +59 -0
  117. package/dist/agent-runtime/src/session-lane-scope.js +25 -0
  118. package/dist/agent-runtime/src/session-lane-scope.js.map +1 -0
  119. package/dist/agent-runtime/src/statewire-attach-observer.d.ts +194 -0
  120. package/dist/agent-runtime/src/statewire-diagnostics.d.ts +74 -0
  121. package/dist/agent-runtime/src/statewire-state.d.ts +29 -0
  122. package/dist/agent-runtime/src/statewire-suspended.cjs.map +1 -1
  123. package/dist/agent-runtime/src/statewire-suspended.d.ts +27 -0
  124. package/dist/agent-runtime/src/statewire-suspended.js.map +1 -1
  125. package/dist/agent-runtime/src/statewire-transport.d.ts +40 -0
  126. package/dist/agent-runtime/src/streaming-markdown.d.ts +46 -0
  127. package/dist/agent-runtime/src/subagent-transcripts.cjs +209 -0
  128. package/dist/agent-runtime/src/subagent-transcripts.cjs.map +1 -0
  129. package/dist/agent-runtime/src/subagent-transcripts.d.ts +71 -0
  130. package/dist/agent-runtime/src/subagent-transcripts.js +209 -0
  131. package/dist/agent-runtime/src/subagent-transcripts.js.map +1 -0
  132. package/dist/agent-runtime/src/subagents.cjs +253 -0
  133. package/dist/agent-runtime/src/subagents.cjs.map +1 -0
  134. package/dist/agent-runtime/src/subagents.d.ts +311 -0
  135. package/dist/agent-runtime/src/subagents.js +253 -0
  136. package/dist/agent-runtime/src/subagents.js.map +1 -0
  137. package/dist/agent-runtime/src/subscription-wake-event.d.ts +111 -0
  138. package/dist/agent-runtime/src/thread-extras.d.ts +43 -0
  139. package/dist/agent-runtime/src/thread-handoff.d.ts +364 -0
  140. package/dist/agent-runtime/src/thread-prewarm-controller.d.ts +76 -0
  141. package/dist/agent-runtime/src/thread-prewarm.d.ts +118 -0
  142. package/dist/agent-runtime/src/thread-snapshot-cache.d.ts +99 -0
  143. package/dist/agent-runtime/src/thread-snapshot-mirror.cjs +46 -117
  144. package/dist/agent-runtime/src/thread-snapshot-mirror.cjs.map +1 -1
  145. package/dist/agent-runtime/src/thread-snapshot-mirror.d.ts +101 -0
  146. package/dist/agent-runtime/src/thread-snapshot-mirror.js +17 -88
  147. package/dist/agent-runtime/src/thread-snapshot-mirror.js.map +1 -1
  148. package/dist/agent-runtime/src/thread-status.d.ts +53 -0
  149. package/dist/agent-runtime/src/tool-metadata.d.ts +146 -0
  150. package/dist/agent-runtime/src/tool-names.cjs +71 -0
  151. package/dist/agent-runtime/src/tool-names.cjs.map +1 -1
  152. package/dist/agent-runtime/src/tool-names.d.ts +67 -0
  153. package/dist/agent-runtime/src/tool-names.js +72 -1
  154. package/dist/agent-runtime/src/tool-names.js.map +1 -1
  155. package/dist/agent-runtime/src/tool-outputs.d.ts +35 -0
  156. package/dist/agent-runtime/src/tool-result.cjs +1 -0
  157. package/dist/agent-runtime/src/tool-result.cjs.map +1 -1
  158. package/dist/agent-runtime/src/tool-result.d.ts +126 -0
  159. package/dist/agent-runtime/src/tool-result.js +1 -0
  160. package/dist/agent-runtime/src/tool-result.js.map +1 -1
  161. package/dist/agent-runtime/src/types.d.ts +159 -0
  162. package/dist/agent-runtime/src/use-conversation-list.d.ts +76 -0
  163. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs +136 -0
  164. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.cjs.map +1 -0
  165. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.d.ts +64 -0
  166. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js +136 -0
  167. package/dist/agent-runtime/src/use-deep-agent-chat-outbox.js.map +1 -0
  168. package/dist/agent-runtime/src/use-deep-agent-config.d.ts +169 -0
  169. package/dist/agent-runtime/src/use-deep-agent-edit-submit.cjs +13 -0
  170. package/dist/agent-runtime/src/use-deep-agent-edit-submit.cjs.map +1 -1
  171. package/dist/agent-runtime/src/use-deep-agent-edit-submit.d.ts +22 -0
  172. package/dist/agent-runtime/src/use-deep-agent-edit-submit.js +13 -0
  173. package/dist/agent-runtime/src/use-deep-agent-edit-submit.js.map +1 -1
  174. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs +37 -2
  175. package/dist/agent-runtime/src/use-deep-agent-runtime.cjs.map +1 -1
  176. package/dist/agent-runtime/src/use-deep-agent-runtime.d.ts +177 -0
  177. package/dist/agent-runtime/src/use-deep-agent-runtime.js +38 -3
  178. package/dist/agent-runtime/src/use-deep-agent-runtime.js.map +1 -1
  179. package/dist/agent-runtime/src/use-deep-agent-thread.cjs +39 -13
  180. package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  181. package/dist/agent-runtime/src/use-deep-agent-thread.d.ts +352 -0
  182. package/dist/agent-runtime/src/use-deep-agent-thread.js +39 -13
  183. package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  184. package/dist/agent-runtime/src/use-deep-agent-voice.cjs +1019 -0
  185. package/dist/agent-runtime/src/use-deep-agent-voice.cjs.map +1 -0
  186. package/dist/agent-runtime/src/use-deep-agent-voice.d.ts +409 -0
  187. package/dist/agent-runtime/src/use-deep-agent-voice.js +1019 -0
  188. package/dist/agent-runtime/src/use-deep-agent-voice.js.map +1 -0
  189. package/dist/agent-runtime/src/use-deep-agent-window-sync.d.ts +102 -0
  190. package/dist/agent-runtime/src/use-historical-edit-attachments.d.ts +2 -0
  191. package/dist/agent-runtime/src/use-thread-handoff-flow.d.ts +84 -0
  192. package/dist/agent-runtime/src/voice-presentation.cjs +2 -0
  193. package/dist/agent-runtime/src/voice-presentation.cjs.map +1 -1
  194. package/dist/agent-runtime/src/voice-presentation.d.ts +80 -0
  195. package/dist/agent-runtime/src/voice-presentation.js +2 -0
  196. package/dist/agent-runtime/src/voice-presentation.js.map +1 -1
  197. package/dist/agent-runtime/src/voice-turn-grouping.d.ts +28 -0
  198. package/dist/agent-runtime/src/voice-wire-types.d.ts +15 -0
  199. package/dist/agent-runtime/src/withdrawn-turns.d.ts +97 -0
  200. package/dist/catalog/runtime-catalog.js +1 -1
  201. package/dist/chat/AthenaChat.cjs +192 -103
  202. package/dist/chat/AthenaChat.cjs.map +1 -1
  203. package/dist/chat/AthenaChat.d.ts +9 -0
  204. package/dist/chat/AthenaChat.js +194 -105
  205. package/dist/chat/AthenaChat.js.map +1 -1
  206. package/dist/chat/StatewireApprovalCard.d.ts +1 -1
  207. package/dist/chat/StatewireClientToolBridge.d.ts +1 -1
  208. package/dist/chat/StatewireQueuedMessages.cjs +11 -4
  209. package/dist/chat/StatewireQueuedMessages.cjs.map +1 -1
  210. package/dist/chat/StatewireQueuedMessages.d.ts +1 -1
  211. package/dist/chat/StatewireQueuedMessages.js +11 -4
  212. package/dist/chat/StatewireQueuedMessages.js.map +1 -1
  213. package/dist/chat/chat-outbox-rows.cjs +223 -0
  214. package/dist/chat/chat-outbox-rows.cjs.map +1 -0
  215. package/dist/chat/chat-outbox-rows.d.ts +29 -0
  216. package/dist/chat/chat-outbox-rows.js +223 -0
  217. package/dist/chat/chat-outbox-rows.js.map +1 -0
  218. package/dist/chat/chat-voice-controls.cjs +128 -0
  219. package/dist/chat/chat-voice-controls.cjs.map +1 -0
  220. package/dist/chat/chat-voice-controls.d.ts +19 -0
  221. package/dist/chat/chat-voice-controls.js +128 -0
  222. package/dist/chat/chat-voice-controls.js.map +1 -0
  223. package/dist/chat/statewire-approval.d.ts +1 -1
  224. package/dist/chat/statewire-client-tools.d.ts +1 -1
  225. package/dist/chat/super-grouping/SuperGroupingCard.cjs +3 -1
  226. package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
  227. package/dist/chat/super-grouping/SuperGroupingCard.d.ts +7 -1
  228. package/dist/chat/super-grouping/SuperGroupingCard.js +3 -1
  229. package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
  230. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +7 -2
  231. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
  232. package/dist/chat/super-grouping/SuperGroupingTranscript.d.ts +2 -2
  233. package/dist/chat/super-grouping/SuperGroupingTranscript.js +7 -2
  234. package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
  235. package/dist/chat/super-grouping/activity-timeline.cjs +15 -8
  236. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  237. package/dist/chat/super-grouping/activity-timeline.d.ts +18 -8
  238. package/dist/chat/super-grouping/activity-timeline.js +15 -8
  239. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  240. package/dist/chat/use-statewire-approval-request.cjs +36 -0
  241. package/dist/chat/use-statewire-approval-request.cjs.map +1 -1
  242. package/dist/chat/use-statewire-approval-request.d.ts +49 -1
  243. package/dist/chat/use-statewire-approval-request.js +39 -3
  244. package/dist/chat/use-statewire-approval-request.js.map +1 -1
  245. package/dist/chat-ui/composer/tiptap-composer.cjs +133 -14
  246. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  247. package/dist/chat-ui/composer/tiptap-composer.d.ts +33 -1
  248. package/dist/chat-ui/composer/tiptap-composer.js +135 -16
  249. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  250. package/dist/chat-ui/header/conversation-history-panel.d.ts +1 -1
  251. package/dist/chat-ui/index.d.ts +1 -1
  252. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs +2 -1
  253. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs.map +1 -1
  254. package/dist/chat-ui/mentions/suggestions/data/api-client.d.ts +3 -1
  255. package/dist/chat-ui/mentions/suggestions/data/api-client.js +2 -1
  256. package/dist/chat-ui/mentions/suggestions/data/api-client.js.map +1 -1
  257. package/dist/chat-ui/mentions/use-account-workspace-id.cjs +29 -51
  258. package/dist/chat-ui/mentions/use-account-workspace-id.cjs.map +1 -1
  259. package/dist/chat-ui/mentions/use-account-workspace-id.d.ts +2 -2
  260. package/dist/chat-ui/mentions/use-account-workspace-id.js +30 -52
  261. package/dist/chat-ui/mentions/use-account-workspace-id.js.map +1 -1
  262. package/dist/chat-ui/messages/message-components.cjs +15 -0
  263. package/dist/chat-ui/messages/message-components.cjs.map +1 -1
  264. package/dist/chat-ui/messages/message-components.js +15 -0
  265. package/dist/chat-ui/messages/message-components.js.map +1 -1
  266. package/dist/chat-ui/messages/runtime-message.d.ts +1 -1
  267. package/dist/chat-ui/messages/statewire-continue-run.d.ts +1 -1
  268. package/dist/chat-ui/messages/user-edit-composer.cjs +15 -2
  269. package/dist/chat-ui/messages/user-edit-composer.cjs.map +1 -1
  270. package/dist/chat-ui/messages/user-edit-composer.d.ts +4 -2
  271. package/dist/chat-ui/messages/user-edit-composer.js +15 -2
  272. package/dist/chat-ui/messages/user-edit-composer.js.map +1 -1
  273. package/dist/chat-ui/messages/wake-marker.d.ts +1 -1
  274. package/dist/chat-ui/rich-text/chat-text-schema.cjs +1 -0
  275. package/dist/chat-ui/rich-text/chat-text-schema.cjs.map +1 -1
  276. package/dist/chat-ui/rich-text/chat-text-schema.d.ts +3 -0
  277. package/dist/chat-ui/rich-text/chat-text-schema.js +1 -0
  278. package/dist/chat-ui/rich-text/chat-text-schema.js.map +1 -1
  279. package/dist/chat-ui/statewire.cjs +1 -0
  280. package/dist/chat-ui/statewire.cjs.map +1 -1
  281. package/dist/chat-ui/statewire.d.ts +2 -2
  282. package/dist/chat-ui/statewire.js +2 -1
  283. package/dist/chat-ui/thread/composer-send-or-stop.cjs +2 -1
  284. package/dist/chat-ui/thread/composer-send-or-stop.cjs.map +1 -1
  285. package/dist/chat-ui/thread/composer-send-or-stop.d.ts +2 -0
  286. package/dist/chat-ui/thread/composer-send-or-stop.js +2 -1
  287. package/dist/chat-ui/thread/composer-send-or-stop.js.map +1 -1
  288. package/dist/chat-ui/thread/statewire-history-edge.d.ts +1 -1
  289. package/dist/composer/AttachmentContext.cjs +8 -4
  290. package/dist/composer/AttachmentContext.cjs.map +1 -1
  291. package/dist/composer/AttachmentContext.d.ts +7 -0
  292. package/dist/composer/AttachmentContext.js +8 -4
  293. package/dist/composer/AttachmentContext.js.map +1 -1
  294. package/dist/composer/build-composed-message.cjs +34 -0
  295. package/dist/composer/build-composed-message.cjs.map +1 -1
  296. package/dist/composer/build-composed-message.d.ts +20 -0
  297. package/dist/composer/build-composed-message.js +35 -1
  298. package/dist/composer/build-composed-message.js.map +1 -1
  299. package/dist/composer/composer-handoff.cjs +39 -2
  300. package/dist/composer/composer-handoff.cjs.map +1 -1
  301. package/dist/composer/composer-handoff.d.ts +28 -3
  302. package/dist/composer/composer-handoff.js +39 -2
  303. package/dist/composer/composer-handoff.js.map +1 -1
  304. package/dist/composer/useAttachFiles.cjs +15 -2
  305. package/dist/composer/useAttachFiles.cjs.map +1 -1
  306. package/dist/composer/useAttachFiles.d.ts +11 -0
  307. package/dist/composer/useAttachFiles.js +15 -2
  308. package/dist/composer/useAttachFiles.js.map +1 -1
  309. package/dist/composer/useSendMessage.cjs.map +1 -1
  310. package/dist/composer/useSendMessage.d.ts +4 -3
  311. package/dist/composer/useSendMessage.js.map +1 -1
  312. package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
  313. package/dist/diagnostics/athena-diagnostics.d.ts +3 -1
  314. package/dist/diagnostics/athena-diagnostics.js.map +1 -1
  315. package/dist/index.cjs +13 -0
  316. package/dist/index.cjs.map +1 -1
  317. package/dist/index.d.ts +14 -7
  318. package/dist/index.js +14 -1
  319. package/dist/index.js.map +1 -1
  320. package/dist/offline/chat-outbox.cjs +57 -0
  321. package/dist/offline/chat-outbox.cjs.map +1 -0
  322. package/dist/offline/chat-outbox.d.ts +80 -0
  323. package/dist/offline/chat-outbox.js +57 -0
  324. package/dist/offline/chat-outbox.js.map +1 -0
  325. package/dist/offline/offline-history.d.ts +1 -1
  326. package/dist/provider/AthenaProvider.cjs +162 -8
  327. package/dist/provider/AthenaProvider.cjs.map +1 -1
  328. package/dist/provider/AthenaProvider.d.ts +15 -1
  329. package/dist/provider/AthenaProvider.js +163 -9
  330. package/dist/provider/AthenaProvider.js.map +1 -1
  331. package/dist/provider/statewire-window-context.d.ts +1 -1
  332. package/dist/react/package.json.cjs +1 -1
  333. package/dist/react/package.json.js +1 -1
  334. package/dist/references/src/anchors/file-line-range.d.ts +58 -0
  335. package/dist/references/src/anchors/format.d.ts +21 -0
  336. package/dist/references/src/anchors/message-anchors.d.ts +71 -0
  337. package/dist/references/src/anchors/parse.d.ts +8 -0
  338. package/dist/references/src/anchors/registry-contract.d.ts +24 -0
  339. package/dist/references/src/anchors/registry.d.ts +19 -0
  340. package/dist/references/src/anchors/schemas.d.ts +194 -0
  341. package/dist/references/src/anchors/serialize.d.ts +8 -0
  342. package/dist/references/src/anchors/time.d.ts +78 -0
  343. package/dist/references/src/anchors/types.d.ts +474 -0
  344. package/dist/references/src/asset-bounds.d.ts +27 -0
  345. package/dist/references/src/asset-links.d.ts +23 -0
  346. package/dist/references/src/asset-reference.d.ts +16 -0
  347. package/dist/references/src/formats/format-catalog.d.ts +79 -0
  348. package/dist/references/src/formats/markdown/parse-reference-markdown.d.ts +28 -0
  349. package/dist/references/src/formats/markdown/reference-display-name.d.ts +6 -0
  350. package/dist/references/src/formats/object/asset-object.d.ts +9 -0
  351. package/dist/references/src/formats/object/callable-object.d.ts +6 -0
  352. package/dist/references/src/formats/object/decode-reference-object-payload.d.ts +3 -0
  353. package/dist/references/src/formats/object/external-url-object.d.ts +9 -0
  354. package/dist/references/src/formats/object/group-object.d.ts +9 -0
  355. package/dist/references/src/formats/object/navigation-object.d.ts +9 -0
  356. package/dist/references/src/formats/object/parse-reference-object.d.ts +11 -0
  357. package/dist/references/src/formats/object/person-object.d.ts +9 -0
  358. package/dist/references/src/formats/uri/asset-mention-uri.d.ts +9 -0
  359. package/dist/references/src/formats/uri/asset-spaces-url.d.ts +9 -0
  360. package/dist/references/src/formats/uri/callable-mention-uris.d.ts +9 -0
  361. package/dist/references/src/formats/uri/detect-uri-wire.d.ts +9 -0
  362. package/dist/references/src/formats/uri/group-mention-uri.d.ts +12 -0
  363. package/dist/references/src/formats/uri/navigation-spaces-url.d.ts +14 -0
  364. package/dist/references/src/formats/uri/parse-reference-uri.d.ts +20 -0
  365. package/dist/references/src/formats/uri/person-mention-uri.d.ts +9 -0
  366. package/dist/references/src/formats/uri/template-placeholder.d.ts +12 -0
  367. package/dist/references/src/index.d.ts +47 -0
  368. package/dist/references/src/legacy/index.d.ts +14 -0
  369. package/dist/references/src/legacy/pre-reference-asset-url.d.ts +57 -0
  370. package/dist/references/src/legacy/pre-reference-citation-bridge.d.ts +22 -0
  371. package/dist/references/src/legacy/pre-reference-citation.d.ts +138 -0
  372. package/dist/references/src/legacy/pre-reference-external-url.d.ts +31 -0
  373. package/dist/references/src/legacy/pre-reference-format.d.ts +8 -0
  374. package/dist/references/src/legacy/pre-reference-mention.d.ts +27 -0
  375. package/dist/references/src/legacy/pre-reference-object.d.ts +8 -0
  376. package/dist/references/src/legacy/pre-reference-uri.d.ts +12 -0
  377. package/dist/references/src/legacy.d.ts +2 -0
  378. package/dist/references/src/mention-href-patterns.d.ts +19 -0
  379. package/dist/references/src/mention-node/index.d.ts +5 -0
  380. package/dist/references/src/mention-node/mention-node.d.ts +31 -0
  381. package/dist/references/src/mention-node/resolve-node-attrs.d.ts +27 -0
  382. package/dist/references/src/parse-reference.d.ts +23 -0
  383. package/dist/references/src/schemas.d.ts +463 -0
  384. package/dist/references/src/serialize-reference.d.ts +50 -0
  385. package/dist/references/src/sheet-name.d.ts +10 -0
  386. package/dist/references/src/tool-option-schemas.d.ts +597 -0
  387. package/dist/references/src/types.d.ts +125 -0
  388. package/dist/references/src/version-ref.d.ts +30 -0
  389. package/dist/runtime/statewire-lifecycle.d.ts +1 -1
  390. package/dist/runtime/statewire-model-lane.d.ts +1 -1
  391. package/dist/runtime/statewire-server-lane-context.d.ts +1 -1
  392. package/dist/runtime/statewire-session-key.cjs +7 -0
  393. package/dist/runtime/statewire-session-key.cjs.map +1 -1
  394. package/dist/runtime/statewire-session-key.d.ts +19 -0
  395. package/dist/runtime/statewire-session-key.js +8 -1
  396. package/dist/runtime/statewire-session-key.js.map +1 -1
  397. package/dist/runtime/statewire-session-lane.cjs +48 -0
  398. package/dist/runtime/statewire-session-lane.cjs.map +1 -0
  399. package/dist/runtime/statewire-session-lane.d.ts +29 -0
  400. package/dist/runtime/statewire-session-lane.js +48 -0
  401. package/dist/runtime/statewire-session-lane.js.map +1 -0
  402. package/dist/runtime/statewire-thread-config-lane.d.ts +1 -1
  403. package/dist/runtime/useAthenaStatewireRuntime.cjs +19 -5
  404. package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
  405. package/dist/runtime/useAthenaStatewireRuntime.d.ts +15 -1
  406. package/dist/runtime/useAthenaStatewireRuntime.js +20 -6
  407. package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
  408. package/dist/styles.css +1 -1
  409. package/dist/threads/api.d.ts +1 -1
  410. package/dist/threads/conversation-list-fetcher.d.ts +1 -1
  411. package/dist/threads/index-triggered-refresh.cjs +164 -0
  412. package/dist/threads/index-triggered-refresh.cjs.map +1 -0
  413. package/dist/threads/index-triggered-refresh.d.ts +70 -0
  414. package/dist/threads/index-triggered-refresh.js +164 -0
  415. package/dist/threads/index-triggered-refresh.js.map +1 -0
  416. package/dist/threads/offline-session-list.d.ts +1 -1
  417. package/dist/threads/pull-request-display.d.ts +1 -1
  418. package/dist/threads/session-list-model.d.ts +1 -1
  419. package/dist/threads/session-list-tab-share.cjs +150 -0
  420. package/dist/threads/session-list-tab-share.cjs.map +1 -0
  421. package/dist/threads/session-list-tab-share.d.ts +73 -0
  422. package/dist/threads/session-list-tab-share.js +150 -0
  423. package/dist/threads/session-list-tab-share.js.map +1 -0
  424. package/dist/threads/session-row-parts.d.ts +1 -1
  425. package/dist/threads/sessions-query.d.ts +1 -1
  426. package/dist/threads/statewire-thread-list.cjs +42 -16
  427. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  428. package/dist/threads/statewire-thread-list.d.ts +11 -2
  429. package/dist/threads/statewire-thread-list.js +43 -17
  430. package/dist/threads/statewire-thread-list.js.map +1 -1
  431. package/dist/threads/thread-prefetcher.d.ts +1 -1
  432. package/dist/threads/use-athena-session-attention.d.ts +1 -1
  433. package/dist/threads/use-athena-sessions.cjs +32 -22
  434. package/dist/threads/use-athena-sessions.cjs.map +1 -1
  435. package/dist/threads/use-athena-sessions.js +33 -23
  436. package/dist/threads/use-athena-sessions.js.map +1 -1
  437. package/dist/threads/use-rest-conversation-history.d.ts +1 -1
  438. package/dist/threads/use-statewire-conversation-history.d.ts +5 -5
  439. package/dist/tiptap-markdown-table/src/markdown-table-serializer.d.ts +56 -0
  440. package/dist/tools/NestedPtcMessages.cjs +14 -9
  441. package/dist/tools/NestedPtcMessages.cjs.map +1 -1
  442. package/dist/tools/NestedPtcMessages.d.ts +9 -0
  443. package/dist/tools/NestedPtcMessages.js +6 -1
  444. package/dist/tools/NestedPtcMessages.js.map +1 -1
  445. package/dist/tools/index.d.ts +1 -1
  446. package/dist/tools/primitives.d.ts +1 -1
  447. package/dist/tools/subagent-task-tool-ui.cjs +494 -0
  448. package/dist/tools/subagent-task-tool-ui.cjs.map +1 -0
  449. package/dist/tools/subagent-task-tool-ui.d.ts +109 -0
  450. package/dist/tools/subagent-task-tool-ui.js +494 -0
  451. package/dist/tools/subagent-task-tool-ui.js.map +1 -0
  452. package/dist/tools/tool-fallback.d.ts +1 -1
  453. package/dist/tools/tool-icons.d.ts +1 -1
  454. package/dist/tools/tool-summary.d.ts +1 -1
  455. package/dist/tools/tool-uis.cjs +4 -0
  456. package/dist/tools/tool-uis.cjs.map +1 -1
  457. package/dist/tools/tool-uis.js +4 -0
  458. package/dist/tools/tool-uis.js.map +1 -1
  459. package/dist/voice/chat.cjs +6 -0
  460. package/dist/voice/chat.cjs.map +1 -0
  461. package/dist/voice/chat.d.ts +2 -0
  462. package/dist/voice/chat.js +6 -0
  463. package/dist/voice/chat.js.map +1 -0
  464. package/dist/voice/livekit-join.cjs +119 -0
  465. package/dist/voice/livekit-join.cjs.map +1 -0
  466. package/dist/voice/livekit-join.d.ts +8 -0
  467. package/dist/voice/livekit-join.js +119 -0
  468. package/dist/voice/livekit-join.js.map +1 -0
  469. package/dist/voice/livekit.cjs +32 -0
  470. package/dist/voice/livekit.cjs.map +1 -0
  471. package/dist/voice/livekit.d.ts +80 -0
  472. package/dist/voice/livekit.js +32 -0
  473. package/dist/voice/livekit.js.map +1 -0
  474. package/dist/voice/use-chat-voice-enabled.cjs +43 -0
  475. package/dist/voice/use-chat-voice-enabled.cjs.map +1 -0
  476. package/dist/voice/use-chat-voice-enabled.d.ts +9 -0
  477. package/dist/voice/use-chat-voice-enabled.js +43 -0
  478. package/dist/voice/use-chat-voice-enabled.js.map +1 -0
  479. package/package.json +22 -16
@@ -1 +1 @@
1
- {"version":3,"file":"thread-snapshot-mirror.cjs","sources":["../../../../agent-runtime/src/thread-snapshot-mirror.ts"],"sourcesContent":["import type { DeepAgentStreamValues } from \"./types\";\n\n/**\n * A durable, encrypted mirror of the thread snapshot cache (`thread-snapshot-cache.ts`) in the\n * browser's IndexedDB, so a reload paints a recently visited thread from its last-known messages,\n * and a thread can be read while the transport cannot connect at all.\n *\n * - **Sealed.** Each record is AES-GCM-256 with a fresh IV under the host's key for its owner\n * (`getKey`: Olympus a non-extractable key in its own keystore, Midas a key main keeps with the\n * OS keychain). The owner and the thread are hashed for the index and bound to the record as\n * additional data, so the database names no one and a record moved to another owner or thread\n * fails to open (and is dropped). No key, no mirror: every write and read is then a no-op.\n * - **Bounded.** Writes are debounced per thread and never taken mid-stream (a state with a live\n * run is replaced by its resting successor within seconds), transcripts longer than\n * `maxMessages` are not kept (an older, shorter record is removed rather than left to seed),\n * and an owner keeps its `maxPerOwner` most recent threads.\n * - **Never in the way.** Every failure degrades to \"no mirror\": a blocked or missing IndexedDB\n * (private browsing, quota, SSR) must never break the wire path.\n * - **Versioned.** A record a newer build wrote is never read, overwritten or pruned by this one;\n * only removing the person's records altogether (an identity change, a sign-out) removes it.\n */\n\nexport const THREAD_SNAPSHOT_MIRROR_DATABASE = \"athena-thread-snapshots:v2\";\n/** The sealed record's format; older builds leave a newer one alone. */\nexport const THREAD_SNAPSHOT_MIRROR_FORMAT = 1;\n/** Threads mirrored per owner; the least recently captured beyond this is pruned after each write. */\nexport const MAX_MIRRORED_SNAPSHOTS_PER_OWNER = 30;\n/** Transcripts longer than this are not mirrored — sealing them would cost more than the seed saves. */\nexport const MAX_MIRRORED_SNAPSHOT_MESSAGES = 150;\n/** Trailing debounce per thread: a streaming thread remembers a state per token. */\nexport const SNAPSHOT_MIRROR_DEBOUNCE_MS = 1_500;\n\nconst DATABASE_VERSION = 1;\nconst SNAPSHOTS_STORE = \"snapshots\";\nconst LISTS_STORE = \"lists\";\nconst BY_OWNER = \"byOwner\";\nconst BY_OWNER_AT = \"byOwnerAt\";\nconst IV_BYTES = 12;\n\nexport interface MirroredThreadSnapshot {\n threadId: string;\n state: DeepAgentStreamValues;\n /** Capture time (epoch ms). */\n at: number;\n}\n\nexport interface MirroredList {\n value: unknown;\n /** When it was written (epoch ms). */\n at: number;\n}\n\nexport interface ThreadSnapshotMirror {\n /** The owner's mirrored snapshots, oldest first. Resolves `[]` on any fault. */\n load(ownerId: string): Promise<MirroredThreadSnapshot[]>;\n /** Mirror a remembered state (debounced per thread; mid-stream states are skipped). */\n write(input: { ownerId: string; threadId: string; state: DeepAgentStreamValues; at: number }): void;\n remove(input: { ownerId: string; threadId: string }): void;\n /** A session list's page as the host last read it, for reading it offline. */\n writeList(input: { ownerId: string; listKey: string; value: unknown }): void;\n loadList(input: { ownerId: string; listKey: string }): Promise<MirroredList | null>;\n /** Drop every record of one owner (an identity change). */\n clearOwner(ownerId: string): Promise<void>;\n /** Drop every record (a sign-out, a workspace switch). */\n clearAll(): Promise<void>;\n /** Write debounced records now (tests, page hide). */\n flush(): Promise<void>;\n}\n\n/**\n * What a key is asked for: reading what is already kept (`read`) or keeping something new\n * (`write`). A host may let what it kept be read where it would keep nothing new, e.g. while its\n * deployment's policy for device copies is not known yet.\n */\nexport type ThreadSnapshotKeyPurpose = \"read\" | \"write\";\n\nexport interface ThreadSnapshotMirrorOptions {\n /** The owner's AES-GCM key, or `null` when the host keeps no mirror for it (no key, policy off). */\n getKey: (ownerId: string, purpose: ThreadSnapshotKeyPurpose) => Promise<CryptoKey | null>;\n /** An `IDBFactory` (the browser's `indexedDB` by default; a fake one in tests). */\n factory?: object;\n databaseName?: string;\n debounceMs?: number;\n maxPerOwner?: number;\n maxMessages?: number;\n}\n\n// This package compiles without the DOM lib (React Native honesty, `globals.d.ts`), so the part of\n// IndexedDB the mirror uses is described structurally here; the mirror runs only where it exists.\ntype IdbListener = ((event?: unknown) => void) | null;\ninterface IdbRequest<T> {\n readonly result: T;\n readonly error: unknown;\n onsuccess: IdbListener;\n onerror: IdbListener;\n}\ninterface IdbOpenRequest extends IdbRequest<IdbDatabase> {\n onupgradeneeded: IdbListener;\n onblocked: IdbListener;\n}\ninterface IdbFactory {\n open(name: string, version?: number): IdbOpenRequest;\n}\ninterface IdbCursor {\n readonly primaryKey: unknown;\n continue(): void;\n}\ninterface IdbIndex {\n getAll(query?: unknown): IdbRequest<unknown[]>;\n getAllKeys(query?: unknown): IdbRequest<unknown[]>;\n openKeyCursor(range?: unknown, direction?: string): IdbRequest<IdbCursor | null>;\n}\ninterface IdbObjectStore {\n put(value: unknown): IdbRequest<unknown>;\n get(key: unknown): IdbRequest<unknown>;\n delete(key: unknown): IdbRequest<unknown>;\n clear(): IdbRequest<unknown>;\n index(name: string): IdbIndex;\n createIndex(name: string, keyPath: string | string[], options?: { unique?: boolean }): unknown;\n}\ninterface IdbTransaction {\n objectStore(name: string): IdbObjectStore;\n readonly error: unknown;\n oncomplete: IdbListener;\n onabort: IdbListener;\n onerror: IdbListener;\n}\ninterface IdbDatabase {\n readonly objectStoreNames: { contains(name: string): boolean };\n transaction(stores: string | string[], mode?: \"readonly\" | \"readwrite\"): IdbTransaction;\n createObjectStore(name: string, options?: { keyPath: string }): IdbObjectStore;\n onversionchange: IdbListener;\n addEventListener(type: string, listener: () => void): void;\n close(): void;\n}\ninterface IdbKeyRangeStatic {\n bound(lower: unknown, upper: unknown): unknown;\n}\n\nconst idbGlobals = globalThis as { indexedDB?: object; IDBKeyRange?: IdbKeyRangeStatic };\n\ninterface SnapshotRecord {\n key: string;\n owner: string;\n at: number;\n format: number;\n sealed: Uint8Array;\n}\n\ninterface ListRecord {\n key: string;\n owner: string;\n at: number;\n format: number;\n sealed: Uint8Array;\n}\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\nasync function sha256Hex(value: string): Promise<string> {\n const digest = await crypto.subtle.digest(\"SHA-256\", encoder.encode(value));\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nasync function sealJson({ key, value, aad }: { key: CryptoKey; value: unknown; aad: string }) {\n const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));\n const ciphertext = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv, additionalData: encoder.encode(aad) },\n key,\n encoder.encode(JSON.stringify(value)),\n ),\n );\n const sealed = new Uint8Array(IV_BYTES + ciphertext.byteLength);\n sealed.set(iv, 0);\n sealed.set(ciphertext, IV_BYTES);\n return sealed;\n}\n\nasync function openJson({ key, sealed, aad }: { key: CryptoKey; sealed: Uint8Array; aad: string }) {\n const iv = sealed.subarray(0, IV_BYTES) as Uint8Array<ArrayBuffer>;\n const plaintext = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv, additionalData: encoder.encode(aad) },\n key,\n sealed.subarray(IV_BYTES) as Uint8Array<ArrayBuffer>,\n );\n return JSON.parse(decoder.decode(plaintext)) as unknown;\n}\n\nfunction requestToPromise<T>(request: IdbRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error(\"IndexedDB request failed\"));\n });\n}\n\nfunction transactionDone(tx: IdbTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onabort = () => reject(tx.error ?? new Error(\"IndexedDB transaction aborted\"));\n tx.onerror = () => reject(tx.error ?? new Error(\"IndexedDB transaction failed\"));\n });\n}\n\nfunction openDatabase(factory: IdbFactory, name: string): Promise<IdbDatabase | null> {\n return new Promise((resolve) => {\n try {\n const request = factory.open(name, DATABASE_VERSION);\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(SNAPSHOTS_STORE)) {\n const store = db.createObjectStore(SNAPSHOTS_STORE, { keyPath: \"key\" });\n store.createIndex(BY_OWNER, \"owner\", { unique: false });\n store.createIndex(BY_OWNER_AT, [\"owner\", \"at\"], { unique: false });\n }\n if (!db.objectStoreNames.contains(LISTS_STORE)) {\n const lists = db.createObjectStore(LISTS_STORE, { keyPath: \"key\" });\n lists.createIndex(BY_OWNER, \"owner\", { unique: false });\n }\n };\n request.onsuccess = () => {\n const db = request.result;\n // Another page deleting or upgrading the database: step aside.\n db.onversionchange = () => db.close();\n resolve(db);\n };\n // A database a newer build upgraded past this version is left alone.\n request.onerror = () => resolve(null);\n request.onblocked = () => resolve(null);\n } catch {\n resolve(null);\n }\n });\n}\n\n/**\n * Whether a remembered state is a resting transcript: no run in flight (its own or an out-of-band\n * one) and its messages present. Only such a state is kept, and read while Athena can't be reached;\n * a mid-stream state is a partial transcript.\n */\nexport function isRestingThreadSnapshot(state: DeepAgentStreamValues): boolean {\n const run = state.runs?.[0];\n if (run && run.status === \"running\") return false;\n if (state.outOfBandRunActive === true) return false;\n return Array.isArray(state.messages);\n}\n\n/**\n * What the mirror does with a remembered state: `persist` a resting transcript within the size\n * bound; `skip` a mid-stream state (it arrives per token and its resting successor follows within\n * seconds — the last resting record stays); `invalidate` when the transcript has outgrown the\n * mirror — a stale, shorter record would otherwise seed the next reload until the hello.\n */\nexport function classifyThreadSnapshotForMirror(\n state: DeepAgentStreamValues,\n maxMessages: number = MAX_MIRRORED_SNAPSHOT_MESSAGES,\n): \"persist\" | \"skip\" | \"invalidate\" {\n if (!isRestingThreadSnapshot(state)) return \"skip\";\n return (state.messages?.length ?? 0) <= maxMessages ? \"persist\" : \"invalidate\";\n}\n\n/** A record this build may replace or delete: none, or one in a format it writes itself. */\nfunction isOwnFormat(record: unknown): boolean {\n if (typeof record !== \"object\" || record === null) return true;\n const format = (record as { format?: unknown }).format;\n return typeof format !== \"number\" || format <= THREAD_SNAPSHOT_MIRROR_FORMAT;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function createThreadSnapshotMirror({\n getKey,\n factory = idbGlobals.indexedDB,\n databaseName = THREAD_SNAPSHOT_MIRROR_DATABASE,\n debounceMs = SNAPSHOT_MIRROR_DEBOUNCE_MS,\n maxPerOwner = MAX_MIRRORED_SNAPSHOTS_PER_OWNER,\n maxMessages = MAX_MIRRORED_SNAPSHOT_MESSAGES,\n}: ThreadSnapshotMirrorOptions): ThreadSnapshotMirror {\n let databasePromise: Promise<IdbDatabase | null> | null = null;\n const database = (): Promise<IdbDatabase | null> => {\n if (!factory || typeof crypto === \"undefined\" || !crypto.subtle) return Promise.resolve(null);\n databasePromise ??= openDatabase(factory as IdbFactory, databaseName).then((db) => {\n if (db) {\n const forget = () => {\n databasePromise = null;\n };\n db.addEventListener(\"versionchange\", forget);\n db.addEventListener(\"close\", forget);\n } else {\n databasePromise = null;\n }\n return db;\n });\n return databasePromise;\n };\n const keyOf = async (ownerId: string, purpose: ThreadSnapshotKeyPurpose): Promise<CryptoKey | null> => {\n try {\n return await getKey(ownerId, purpose);\n } catch {\n return null;\n }\n };\n const aadOf = (kind: string, owner: string, key: string): string =>\n `athena-thread-snapshot|${THREAD_SNAPSHOT_MIRROR_FORMAT}|${kind}|${owner}|${key}`;\n\n const pending = new Map<\n string,\n { input: { ownerId: string; threadId: string; state: DeepAgentStreamValues; at: number }; timer: ReturnType<typeof setTimeout> }\n >();\n const pendingLists = new Map<\n string,\n { input: { ownerId: string; listKey: string; value: unknown }; timer: ReturnType<typeof setTimeout> }\n >();\n // Writes are serialized so a flush never races a clear.\n let queue: Promise<void> = Promise.resolve();\n const enqueue = (task: () => Promise<void>): Promise<void> => {\n queue = queue.then(task, task).catch(() => {});\n return queue;\n };\n\n const pruneOwner = async (db: IdbDatabase, owner: string): Promise<void> => {\n const KeyRange = idbGlobals.IDBKeyRange;\n if (!KeyRange) return;\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n const store = tx.objectStore(SNAPSHOTS_STORE);\n const index = store.index(BY_OWNER_AT);\n // Key cursor, newest first: bookkeeping never loads the sealed payloads of the records it keeps.\n const range = KeyRange.bound([owner, Number.NEGATIVE_INFINITY], [owner, Number.POSITIVE_INFINITY]);\n const beyond: unknown[] = [];\n await new Promise<void>((resolve, reject) => {\n let kept = 0;\n const request = index.openKeyCursor(range, \"prev\");\n request.onsuccess = () => {\n const cursor = request.result;\n if (!cursor) {\n resolve();\n return;\n }\n kept += 1;\n if (kept > maxPerOwner) beyond.push(cursor.primaryKey);\n cursor.continue();\n };\n request.onerror = () => reject(request.error ?? new Error(\"IndexedDB cursor failed\"));\n });\n // A newer build's record is left to that build's own bound.\n for (const key of beyond) {\n if (isOwnFormat(await requestToPromise(store.get(key)))) store.delete(key);\n }\n await transactionDone(tx);\n };\n\n const put = (input: { ownerId: string; threadId: string; state: DeepAgentStreamValues; at: number }) =>\n enqueue(async () => {\n const db = await database();\n if (!db) return;\n const cryptoKey = await keyOf(input.ownerId, \"write\");\n if (!cryptoKey) return;\n const owner = await sha256Hex(input.ownerId);\n const key = await sha256Hex(`${input.ownerId}|${input.threadId}`);\n const sealed = await sealJson({\n key: cryptoKey,\n value: { threadId: input.threadId, state: input.state },\n aad: aadOf(\"snapshot\", owner, key),\n });\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n const store = tx.objectStore(SNAPSHOTS_STORE);\n // A newer build's record of the thread stays as that build wrote it.\n if (isOwnFormat(await requestToPromise(store.get(key)))) {\n store.put({\n key,\n owner,\n at: input.at,\n format: THREAD_SNAPSHOT_MIRROR_FORMAT,\n sealed,\n } satisfies SnapshotRecord);\n }\n await transactionDone(tx);\n await pruneOwner(db, owner);\n });\n\n const deleteSnapshot = (input: { ownerId: string; threadId: string }) =>\n enqueue(async () => {\n const db = await database();\n if (!db) return;\n const key = await sha256Hex(`${input.ownerId}|${input.threadId}`);\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n const store = tx.objectStore(SNAPSHOTS_STORE);\n if (isOwnFormat(await requestToPromise(store.get(key)))) store.delete(key);\n await transactionDone(tx);\n });\n\n const pendingKey = (ownerId: string, threadId: string) => `${ownerId}\\u0000${threadId}`;\n\n const flushPending = (key: string): Promise<void> => {\n const entry = pending.get(key);\n if (!entry) return Promise.resolve();\n pending.delete(key);\n clearTimeout(entry.timer);\n return put(entry.input);\n };\n\n const putList = (input: { ownerId: string; listKey: string; value: unknown }) =>\n enqueue(async () => {\n const db = await database();\n if (!db) return;\n const cryptoKey = await keyOf(input.ownerId, \"write\");\n if (!cryptoKey) return;\n const owner = await sha256Hex(input.ownerId);\n const key = await sha256Hex(`${input.ownerId}|list|${input.listKey}`);\n const sealed = await sealJson({ key: cryptoKey, value: input.value, aad: aadOf(\"list\", owner, key) });\n const tx = db.transaction(LISTS_STORE, \"readwrite\");\n const store = tx.objectStore(LISTS_STORE);\n if (isOwnFormat(await requestToPromise(store.get(key)))) {\n store.put({\n key,\n owner,\n at: Date.now(),\n format: THREAD_SNAPSHOT_MIRROR_FORMAT,\n sealed,\n } satisfies ListRecord);\n }\n await transactionDone(tx);\n });\n\n const flushList = (key: string): Promise<void> => {\n const entry = pendingLists.get(key);\n if (!entry) return Promise.resolve();\n pendingLists.delete(key);\n clearTimeout(entry.timer);\n return putList(entry.input);\n };\n\n const dropPendingOf = (ownerId: string | null): void => {\n for (const [key, entry] of pending) {\n if (ownerId === null || entry.input.ownerId === ownerId) {\n clearTimeout(entry.timer);\n pending.delete(key);\n }\n }\n for (const [key, entry] of pendingLists) {\n if (ownerId === null || entry.input.ownerId === ownerId) {\n clearTimeout(entry.timer);\n pendingLists.delete(key);\n }\n }\n };\n\n return {\n async load(ownerId) {\n try {\n const db = await database();\n if (!db) return [];\n const cryptoKey = await keyOf(ownerId, \"read\");\n if (!cryptoKey) return [];\n const owner = await sha256Hex(ownerId);\n const records = (await requestToPromise(\n db.transaction(SNAPSHOTS_STORE, \"readonly\").objectStore(SNAPSHOTS_STORE).index(BY_OWNER).getAll(owner),\n )) as SnapshotRecord[];\n const loaded: MirroredThreadSnapshot[] = [];\n const unreadable: string[] = [];\n for (const record of records) {\n if (!record || record.format > THREAD_SNAPSHOT_MIRROR_FORMAT) continue;\n try {\n const value = await openJson({\n key: cryptoKey,\n sealed: record.sealed,\n aad: aadOf(\"snapshot\", owner, record.key),\n });\n if (\n isRecord(value) &&\n typeof value.threadId === \"string\" &&\n isRecord(value.state) &&\n typeof record.at === \"number\"\n ) {\n loaded.push({ threadId: value.threadId, state: value.state as DeepAgentStreamValues, at: record.at });\n }\n } catch {\n unreadable.push(record.key);\n }\n }\n if (unreadable.length > 0) {\n void enqueue(async () => {\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n for (const key of unreadable) tx.objectStore(SNAPSHOTS_STORE).delete(key);\n await transactionDone(tx);\n });\n }\n return loaded.sort((a, b) => a.at - b.at);\n } catch {\n return [];\n }\n },\n write(input) {\n const key = pendingKey(input.ownerId, input.threadId);\n const verdict = classifyThreadSnapshotForMirror(input.state, maxMessages);\n if (verdict === \"invalidate\") {\n const existing = pending.get(key);\n if (existing) clearTimeout(existing.timer);\n pending.delete(key);\n void deleteSnapshot(input);\n return;\n }\n if (verdict === \"skip\") return;\n const existing = pending.get(key);\n if (existing) clearTimeout(existing.timer);\n pending.set(key, {\n input,\n timer: setTimeout(() => {\n void flushPending(key);\n }, debounceMs),\n });\n },\n remove(input) {\n const key = pendingKey(input.ownerId, input.threadId);\n const existing = pending.get(key);\n if (existing) clearTimeout(existing.timer);\n pending.delete(key);\n void deleteSnapshot(input);\n },\n writeList(input) {\n const key = pendingKey(input.ownerId, `list:${input.listKey}`);\n const existing = pendingLists.get(key);\n if (existing) clearTimeout(existing.timer);\n pendingLists.set(key, {\n input,\n timer: setTimeout(() => {\n void flushList(key);\n }, debounceMs),\n });\n },\n async loadList({ ownerId, listKey }) {\n try {\n const db = await database();\n if (!db) return null;\n const cryptoKey = await keyOf(ownerId, \"read\");\n if (!cryptoKey) return null;\n const owner = await sha256Hex(ownerId);\n const key = await sha256Hex(`${ownerId}|list|${listKey}`);\n const record = (await requestToPromise(\n db.transaction(LISTS_STORE, \"readonly\").objectStore(LISTS_STORE).get(key),\n )) as ListRecord | undefined;\n if (!record || record.format > THREAD_SNAPSHOT_MIRROR_FORMAT || record.owner !== owner) return null;\n try {\n const value = await openJson({ key: cryptoKey, sealed: record.sealed, aad: aadOf(\"list\", owner, key) });\n return { value, at: record.at };\n } catch {\n return null;\n }\n } catch {\n return null;\n }\n },\n clearOwner(ownerId) {\n dropPendingOf(ownerId);\n return enqueue(async () => {\n const db = await database();\n if (!db) return;\n const owner = await sha256Hex(ownerId);\n const tx = db.transaction([SNAPSHOTS_STORE, LISTS_STORE], \"readwrite\");\n for (const storeName of [SNAPSHOTS_STORE, LISTS_STORE]) {\n const store = tx.objectStore(storeName);\n const keys = await requestToPromise(store.index(BY_OWNER).getAllKeys(owner));\n for (const key of keys) store.delete(key);\n }\n await transactionDone(tx);\n });\n },\n clearAll() {\n dropPendingOf(null);\n return enqueue(async () => {\n const db = await database();\n if (!db) return;\n const tx = db.transaction([SNAPSHOTS_STORE, LISTS_STORE], \"readwrite\");\n tx.objectStore(SNAPSHOTS_STORE).clear();\n tx.objectStore(LISTS_STORE).clear();\n await transactionDone(tx);\n });\n },\n async flush() {\n await Promise.all([...pending.keys()].map(flushPending));\n await Promise.all([...pendingLists.keys()].map(flushList));\n await queue;\n },\n };\n}\n"],"names":["existing"],"mappings":"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"}
1
+ {"version":3,"file":"thread-snapshot-mirror.cjs","sources":["../../../../agent-runtime/src/thread-snapshot-mirror.ts"],"sourcesContent":["import {\n type IdbDatabase,\n idbGlobals,\n lazySealedDatabase,\n openJson,\n requestToPromise,\n sealJson,\n sha256Hex,\n transactionDone,\n} from \"./sealed-idb\";\nimport type { DeepAgentStreamValues } from \"./types\";\n\n/**\n * A durable, encrypted mirror of the thread snapshot cache (`thread-snapshot-cache.ts`) in the\n * browser's IndexedDB, so a reload paints a recently visited thread from its last-known messages,\n * and a thread can be read while the transport cannot connect at all.\n *\n * - **Sealed.** Each record is AES-GCM-256 with a fresh IV under the host's key for its owner\n * (`getKey`: Olympus a non-extractable key in its own keystore, Midas a key main keeps with the\n * OS keychain). The owner and the thread are hashed for the index and bound to the record as\n * additional data, so the database names no one and a record moved to another owner or thread\n * fails to open (and is dropped). No key, no mirror: every write and read is then a no-op.\n * - **Bounded.** Writes are debounced per thread and never taken mid-stream (a state with a live\n * run is replaced by its resting successor within seconds), transcripts longer than\n * `maxMessages` are not kept (an older, shorter record is removed rather than left to seed),\n * and an owner keeps its `maxPerOwner` most recent threads.\n * - **Never in the way.** Every failure degrades to \"no mirror\": a blocked or missing IndexedDB\n * (private browsing, quota, SSR) must never break the wire path.\n * - **Versioned.** A record a newer build wrote is never read, overwritten or pruned by this one;\n * only removing the person's records altogether (an identity change, a sign-out) removes it.\n */\n\nexport const THREAD_SNAPSHOT_MIRROR_DATABASE = \"athena-thread-snapshots:v2\";\n/** The sealed record's format; older builds leave a newer one alone. */\nexport const THREAD_SNAPSHOT_MIRROR_FORMAT = 1;\n/** Threads mirrored per owner; the least recently captured beyond this is pruned after each write. */\nexport const MAX_MIRRORED_SNAPSHOTS_PER_OWNER = 30;\n/** Transcripts longer than this are not mirrored — sealing them would cost more than the seed saves. */\nexport const MAX_MIRRORED_SNAPSHOT_MESSAGES = 150;\n/** Trailing debounce per thread: a streaming thread remembers a state per token. */\nexport const SNAPSHOT_MIRROR_DEBOUNCE_MS = 1_500;\n\nconst DATABASE_VERSION = 1;\nconst SNAPSHOTS_STORE = \"snapshots\";\nconst LISTS_STORE = \"lists\";\nconst BY_OWNER = \"byOwner\";\nconst BY_OWNER_AT = \"byOwnerAt\";\n\nexport interface MirroredThreadSnapshot {\n threadId: string;\n state: DeepAgentStreamValues;\n /** Capture time (epoch ms). */\n at: number;\n}\n\nexport interface MirroredList {\n value: unknown;\n /** When it was written (epoch ms). */\n at: number;\n}\n\nexport interface ThreadSnapshotMirror {\n /** The owner's mirrored snapshots, oldest first. Resolves `[]` on any fault. */\n load(ownerId: string): Promise<MirroredThreadSnapshot[]>;\n /** Mirror a remembered state (debounced per thread; mid-stream states are skipped). */\n write(input: { ownerId: string; threadId: string; state: DeepAgentStreamValues; at: number }): void;\n remove(input: { ownerId: string; threadId: string }): void;\n /** A session list's page as the host last read it, for reading it offline. */\n writeList(input: { ownerId: string; listKey: string; value: unknown }): void;\n loadList(input: { ownerId: string; listKey: string }): Promise<MirroredList | null>;\n /** Drop every record of one owner (an identity change). */\n clearOwner(ownerId: string): Promise<void>;\n /** Drop every record (a sign-out, a workspace switch). */\n clearAll(): Promise<void>;\n /** Write debounced records now (tests, page hide). */\n flush(): Promise<void>;\n}\n\n/**\n * What a key is asked for: reading what is already kept (`read`) or keeping something new\n * (`write`). A host may let what it kept be read where it would keep nothing new, e.g. while its\n * deployment's policy for device copies is not known yet.\n */\nexport type ThreadSnapshotKeyPurpose = \"read\" | \"write\";\n\nexport interface ThreadSnapshotMirrorOptions {\n /** The owner's AES-GCM key, or `null` when the host keeps no mirror for it (no key, policy off). */\n getKey: (ownerId: string, purpose: ThreadSnapshotKeyPurpose) => Promise<CryptoKey | null>;\n /** An `IDBFactory` (the browser's `indexedDB` by default; a fake one in tests). */\n factory?: object;\n databaseName?: string;\n debounceMs?: number;\n maxPerOwner?: number;\n maxMessages?: number;\n}\n\ninterface SnapshotRecord {\n key: string;\n owner: string;\n at: number;\n format: number;\n sealed: Uint8Array;\n}\n\ninterface ListRecord {\n key: string;\n owner: string;\n at: number;\n format: number;\n sealed: Uint8Array;\n}\n\nfunction upgradeMirrorDatabase(db: IdbDatabase): void {\n if (!db.objectStoreNames.contains(SNAPSHOTS_STORE)) {\n const store = db.createObjectStore(SNAPSHOTS_STORE, { keyPath: \"key\" });\n store.createIndex(BY_OWNER, \"owner\", { unique: false });\n store.createIndex(BY_OWNER_AT, [\"owner\", \"at\"], { unique: false });\n }\n if (!db.objectStoreNames.contains(LISTS_STORE)) {\n const lists = db.createObjectStore(LISTS_STORE, { keyPath: \"key\" });\n lists.createIndex(BY_OWNER, \"owner\", { unique: false });\n }\n}\n\n/**\n * Whether a remembered state is a resting transcript: no run in flight (its own or an out-of-band\n * one) and its messages present. Only such a state is kept, and read while Athena can't be reached;\n * a mid-stream state is a partial transcript.\n */\nexport function isRestingThreadSnapshot(state: DeepAgentStreamValues): boolean {\n const run = state.runs?.[0];\n if (run && run.status === \"running\") return false;\n if (state.outOfBandRunActive === true) return false;\n return Array.isArray(state.messages);\n}\n\n/**\n * What the mirror does with a remembered state: `persist` a resting transcript within the size\n * bound; `skip` a mid-stream state (it arrives per token and its resting successor follows within\n * seconds — the last resting record stays); `invalidate` when the transcript has outgrown the\n * mirror — a stale, shorter record would otherwise seed the next reload until the hello.\n */\nexport function classifyThreadSnapshotForMirror(\n state: DeepAgentStreamValues,\n maxMessages: number = MAX_MIRRORED_SNAPSHOT_MESSAGES,\n): \"persist\" | \"skip\" | \"invalidate\" {\n if (!isRestingThreadSnapshot(state)) return \"skip\";\n return (state.messages?.length ?? 0) <= maxMessages ? \"persist\" : \"invalidate\";\n}\n\n/** A record this build may replace or delete: none, or one in a format it writes itself. */\nfunction isOwnFormat(record: unknown): boolean {\n if (typeof record !== \"object\" || record === null) return true;\n const format = (record as { format?: unknown }).format;\n return typeof format !== \"number\" || format <= THREAD_SNAPSHOT_MIRROR_FORMAT;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function createThreadSnapshotMirror({\n getKey,\n factory = idbGlobals.indexedDB,\n databaseName = THREAD_SNAPSHOT_MIRROR_DATABASE,\n debounceMs = SNAPSHOT_MIRROR_DEBOUNCE_MS,\n maxPerOwner = MAX_MIRRORED_SNAPSHOTS_PER_OWNER,\n maxMessages = MAX_MIRRORED_SNAPSHOT_MESSAGES,\n}: ThreadSnapshotMirrorOptions): ThreadSnapshotMirror {\n const database = lazySealedDatabase({\n factory,\n name: databaseName,\n version: DATABASE_VERSION,\n upgrade: upgradeMirrorDatabase,\n });\n const keyOf = async (ownerId: string, purpose: ThreadSnapshotKeyPurpose): Promise<CryptoKey | null> => {\n try {\n return await getKey(ownerId, purpose);\n } catch {\n return null;\n }\n };\n const aadOf = (kind: string, owner: string, key: string): string =>\n `athena-thread-snapshot|${THREAD_SNAPSHOT_MIRROR_FORMAT}|${kind}|${owner}|${key}`;\n\n const pending = new Map<\n string,\n { input: { ownerId: string; threadId: string; state: DeepAgentStreamValues; at: number }; timer: ReturnType<typeof setTimeout> }\n >();\n const pendingLists = new Map<\n string,\n { input: { ownerId: string; listKey: string; value: unknown }; timer: ReturnType<typeof setTimeout> }\n >();\n // Writes are serialized so a flush never races a clear.\n let queue: Promise<void> = Promise.resolve();\n const enqueue = (task: () => Promise<void>): Promise<void> => {\n queue = queue.then(task, task).catch(() => {});\n return queue;\n };\n\n const pruneOwner = async (db: IdbDatabase, owner: string): Promise<void> => {\n const KeyRange = idbGlobals.IDBKeyRange;\n if (!KeyRange) return;\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n const store = tx.objectStore(SNAPSHOTS_STORE);\n const index = store.index(BY_OWNER_AT);\n // Key cursor, newest first: bookkeeping never loads the sealed payloads of the records it keeps.\n const range = KeyRange.bound([owner, Number.NEGATIVE_INFINITY], [owner, Number.POSITIVE_INFINITY]);\n const beyond: unknown[] = [];\n await new Promise<void>((resolve, reject) => {\n let kept = 0;\n const request = index.openKeyCursor(range, \"prev\");\n request.onsuccess = () => {\n const cursor = request.result;\n if (!cursor) {\n resolve();\n return;\n }\n kept += 1;\n if (kept > maxPerOwner) beyond.push(cursor.primaryKey);\n cursor.continue();\n };\n request.onerror = () => reject(request.error ?? new Error(\"IndexedDB cursor failed\"));\n });\n // A newer build's record is left to that build's own bound.\n for (const key of beyond) {\n if (isOwnFormat(await requestToPromise(store.get(key)))) store.delete(key);\n }\n await transactionDone(tx);\n };\n\n const put = (input: { ownerId: string; threadId: string; state: DeepAgentStreamValues; at: number }) =>\n enqueue(async () => {\n const db = await database();\n if (!db) return;\n const cryptoKey = await keyOf(input.ownerId, \"write\");\n if (!cryptoKey) return;\n const owner = await sha256Hex(input.ownerId);\n const key = await sha256Hex(`${input.ownerId}|${input.threadId}`);\n const sealed = await sealJson({\n key: cryptoKey,\n value: { threadId: input.threadId, state: input.state },\n aad: aadOf(\"snapshot\", owner, key),\n });\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n const store = tx.objectStore(SNAPSHOTS_STORE);\n // A newer build's record of the thread stays as that build wrote it.\n if (isOwnFormat(await requestToPromise(store.get(key)))) {\n store.put({\n key,\n owner,\n at: input.at,\n format: THREAD_SNAPSHOT_MIRROR_FORMAT,\n sealed,\n } satisfies SnapshotRecord);\n }\n await transactionDone(tx);\n await pruneOwner(db, owner);\n });\n\n const deleteSnapshot = (input: { ownerId: string; threadId: string }) =>\n enqueue(async () => {\n const db = await database();\n if (!db) return;\n const key = await sha256Hex(`${input.ownerId}|${input.threadId}`);\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n const store = tx.objectStore(SNAPSHOTS_STORE);\n if (isOwnFormat(await requestToPromise(store.get(key)))) store.delete(key);\n await transactionDone(tx);\n });\n\n const pendingKey = (ownerId: string, threadId: string) => `${ownerId}\\u0000${threadId}`;\n\n const flushPending = (key: string): Promise<void> => {\n const entry = pending.get(key);\n if (!entry) return Promise.resolve();\n pending.delete(key);\n clearTimeout(entry.timer);\n return put(entry.input);\n };\n\n const putList = (input: { ownerId: string; listKey: string; value: unknown }) =>\n enqueue(async () => {\n const db = await database();\n if (!db) return;\n const cryptoKey = await keyOf(input.ownerId, \"write\");\n if (!cryptoKey) return;\n const owner = await sha256Hex(input.ownerId);\n const key = await sha256Hex(`${input.ownerId}|list|${input.listKey}`);\n const sealed = await sealJson({ key: cryptoKey, value: input.value, aad: aadOf(\"list\", owner, key) });\n const tx = db.transaction(LISTS_STORE, \"readwrite\");\n const store = tx.objectStore(LISTS_STORE);\n if (isOwnFormat(await requestToPromise(store.get(key)))) {\n store.put({\n key,\n owner,\n at: Date.now(),\n format: THREAD_SNAPSHOT_MIRROR_FORMAT,\n sealed,\n } satisfies ListRecord);\n }\n await transactionDone(tx);\n });\n\n const flushList = (key: string): Promise<void> => {\n const entry = pendingLists.get(key);\n if (!entry) return Promise.resolve();\n pendingLists.delete(key);\n clearTimeout(entry.timer);\n return putList(entry.input);\n };\n\n const dropPendingOf = (ownerId: string | null): void => {\n for (const [key, entry] of pending) {\n if (ownerId === null || entry.input.ownerId === ownerId) {\n clearTimeout(entry.timer);\n pending.delete(key);\n }\n }\n for (const [key, entry] of pendingLists) {\n if (ownerId === null || entry.input.ownerId === ownerId) {\n clearTimeout(entry.timer);\n pendingLists.delete(key);\n }\n }\n };\n\n return {\n async load(ownerId) {\n try {\n const db = await database();\n if (!db) return [];\n const cryptoKey = await keyOf(ownerId, \"read\");\n if (!cryptoKey) return [];\n const owner = await sha256Hex(ownerId);\n const records = (await requestToPromise(\n db.transaction(SNAPSHOTS_STORE, \"readonly\").objectStore(SNAPSHOTS_STORE).index(BY_OWNER).getAll(owner),\n )) as SnapshotRecord[];\n const loaded: MirroredThreadSnapshot[] = [];\n const unreadable: string[] = [];\n for (const record of records) {\n if (!record || record.format > THREAD_SNAPSHOT_MIRROR_FORMAT) continue;\n try {\n const value = await openJson({\n key: cryptoKey,\n sealed: record.sealed,\n aad: aadOf(\"snapshot\", owner, record.key),\n });\n if (\n isRecord(value) &&\n typeof value.threadId === \"string\" &&\n isRecord(value.state) &&\n typeof record.at === \"number\"\n ) {\n loaded.push({ threadId: value.threadId, state: value.state as DeepAgentStreamValues, at: record.at });\n }\n } catch {\n unreadable.push(record.key);\n }\n }\n if (unreadable.length > 0) {\n void enqueue(async () => {\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n for (const key of unreadable) tx.objectStore(SNAPSHOTS_STORE).delete(key);\n await transactionDone(tx);\n });\n }\n return loaded.sort((a, b) => a.at - b.at);\n } catch {\n return [];\n }\n },\n write(input) {\n const key = pendingKey(input.ownerId, input.threadId);\n const verdict = classifyThreadSnapshotForMirror(input.state, maxMessages);\n if (verdict === \"invalidate\") {\n const existing = pending.get(key);\n if (existing) clearTimeout(existing.timer);\n pending.delete(key);\n void deleteSnapshot(input);\n return;\n }\n if (verdict === \"skip\") return;\n const existing = pending.get(key);\n if (existing) clearTimeout(existing.timer);\n pending.set(key, {\n input,\n timer: setTimeout(() => {\n void flushPending(key);\n }, debounceMs),\n });\n },\n remove(input) {\n const key = pendingKey(input.ownerId, input.threadId);\n const existing = pending.get(key);\n if (existing) clearTimeout(existing.timer);\n pending.delete(key);\n void deleteSnapshot(input);\n },\n writeList(input) {\n const key = pendingKey(input.ownerId, `list:${input.listKey}`);\n const existing = pendingLists.get(key);\n if (existing) clearTimeout(existing.timer);\n pendingLists.set(key, {\n input,\n timer: setTimeout(() => {\n void flushList(key);\n }, debounceMs),\n });\n },\n async loadList({ ownerId, listKey }) {\n try {\n const db = await database();\n if (!db) return null;\n const cryptoKey = await keyOf(ownerId, \"read\");\n if (!cryptoKey) return null;\n const owner = await sha256Hex(ownerId);\n const key = await sha256Hex(`${ownerId}|list|${listKey}`);\n const record = (await requestToPromise(\n db.transaction(LISTS_STORE, \"readonly\").objectStore(LISTS_STORE).get(key),\n )) as ListRecord | undefined;\n if (!record || record.format > THREAD_SNAPSHOT_MIRROR_FORMAT || record.owner !== owner) return null;\n try {\n const value = await openJson({ key: cryptoKey, sealed: record.sealed, aad: aadOf(\"list\", owner, key) });\n return { value, at: record.at };\n } catch {\n return null;\n }\n } catch {\n return null;\n }\n },\n clearOwner(ownerId) {\n dropPendingOf(ownerId);\n return enqueue(async () => {\n const db = await database();\n if (!db) return;\n const owner = await sha256Hex(ownerId);\n const tx = db.transaction([SNAPSHOTS_STORE, LISTS_STORE], \"readwrite\");\n for (const storeName of [SNAPSHOTS_STORE, LISTS_STORE]) {\n const store = tx.objectStore(storeName);\n const keys = await requestToPromise(store.index(BY_OWNER).getAllKeys(owner));\n for (const key of keys) store.delete(key);\n }\n await transactionDone(tx);\n });\n },\n clearAll() {\n dropPendingOf(null);\n return enqueue(async () => {\n const db = await database();\n if (!db) return;\n const tx = db.transaction([SNAPSHOTS_STORE, LISTS_STORE], \"readwrite\");\n tx.objectStore(SNAPSHOTS_STORE).clear();\n tx.objectStore(LISTS_STORE).clear();\n await transactionDone(tx);\n });\n },\n async flush() {\n await Promise.all([...pending.keys()].map(flushPending));\n await Promise.all([...pendingLists.keys()].map(flushList));\n await queue;\n },\n };\n}\n"],"names":["idbGlobals","lazySealedDatabase","requestToPromise","transactionDone","sha256Hex","sealJson","openJson","existing"],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ import type { DeepAgentStreamValues } from "./types";
2
+ /**
3
+ * A durable, encrypted mirror of the thread snapshot cache (`thread-snapshot-cache.ts`) in the
4
+ * browser's IndexedDB, so a reload paints a recently visited thread from its last-known messages,
5
+ * and a thread can be read while the transport cannot connect at all.
6
+ *
7
+ * - **Sealed.** Each record is AES-GCM-256 with a fresh IV under the host's key for its owner
8
+ * (`getKey`: Olympus a non-extractable key in its own keystore, Midas a key main keeps with the
9
+ * OS keychain). The owner and the thread are hashed for the index and bound to the record as
10
+ * additional data, so the database names no one and a record moved to another owner or thread
11
+ * fails to open (and is dropped). No key, no mirror: every write and read is then a no-op.
12
+ * - **Bounded.** Writes are debounced per thread and never taken mid-stream (a state with a live
13
+ * run is replaced by its resting successor within seconds), transcripts longer than
14
+ * `maxMessages` are not kept (an older, shorter record is removed rather than left to seed),
15
+ * and an owner keeps its `maxPerOwner` most recent threads.
16
+ * - **Never in the way.** Every failure degrades to "no mirror": a blocked or missing IndexedDB
17
+ * (private browsing, quota, SSR) must never break the wire path.
18
+ * - **Versioned.** A record a newer build wrote is never read, overwritten or pruned by this one;
19
+ * only removing the person's records altogether (an identity change, a sign-out) removes it.
20
+ */
21
+ export declare const THREAD_SNAPSHOT_MIRROR_DATABASE = "athena-thread-snapshots:v2";
22
+ /** The sealed record's format; older builds leave a newer one alone. */
23
+ export declare const THREAD_SNAPSHOT_MIRROR_FORMAT = 1;
24
+ /** Threads mirrored per owner; the least recently captured beyond this is pruned after each write. */
25
+ export declare const MAX_MIRRORED_SNAPSHOTS_PER_OWNER = 30;
26
+ /** Transcripts longer than this are not mirrored — sealing them would cost more than the seed saves. */
27
+ export declare const MAX_MIRRORED_SNAPSHOT_MESSAGES = 150;
28
+ /** Trailing debounce per thread: a streaming thread remembers a state per token. */
29
+ export declare const SNAPSHOT_MIRROR_DEBOUNCE_MS = 1500;
30
+ export interface MirroredThreadSnapshot {
31
+ threadId: string;
32
+ state: DeepAgentStreamValues;
33
+ /** Capture time (epoch ms). */
34
+ at: number;
35
+ }
36
+ export interface MirroredList {
37
+ value: unknown;
38
+ /** When it was written (epoch ms). */
39
+ at: number;
40
+ }
41
+ export interface ThreadSnapshotMirror {
42
+ /** The owner's mirrored snapshots, oldest first. Resolves `[]` on any fault. */
43
+ load(ownerId: string): Promise<MirroredThreadSnapshot[]>;
44
+ /** Mirror a remembered state (debounced per thread; mid-stream states are skipped). */
45
+ write(input: {
46
+ ownerId: string;
47
+ threadId: string;
48
+ state: DeepAgentStreamValues;
49
+ at: number;
50
+ }): void;
51
+ remove(input: {
52
+ ownerId: string;
53
+ threadId: string;
54
+ }): void;
55
+ /** A session list's page as the host last read it, for reading it offline. */
56
+ writeList(input: {
57
+ ownerId: string;
58
+ listKey: string;
59
+ value: unknown;
60
+ }): void;
61
+ loadList(input: {
62
+ ownerId: string;
63
+ listKey: string;
64
+ }): Promise<MirroredList | null>;
65
+ /** Drop every record of one owner (an identity change). */
66
+ clearOwner(ownerId: string): Promise<void>;
67
+ /** Drop every record (a sign-out, a workspace switch). */
68
+ clearAll(): Promise<void>;
69
+ /** Write debounced records now (tests, page hide). */
70
+ flush(): Promise<void>;
71
+ }
72
+ /**
73
+ * What a key is asked for: reading what is already kept (`read`) or keeping something new
74
+ * (`write`). A host may let what it kept be read where it would keep nothing new, e.g. while its
75
+ * deployment's policy for device copies is not known yet.
76
+ */
77
+ export type ThreadSnapshotKeyPurpose = "read" | "write";
78
+ export interface ThreadSnapshotMirrorOptions {
79
+ /** The owner's AES-GCM key, or `null` when the host keeps no mirror for it (no key, policy off). */
80
+ getKey: (ownerId: string, purpose: ThreadSnapshotKeyPurpose) => Promise<CryptoKey | null>;
81
+ /** An `IDBFactory` (the browser's `indexedDB` by default; a fake one in tests). */
82
+ factory?: object;
83
+ databaseName?: string;
84
+ debounceMs?: number;
85
+ maxPerOwner?: number;
86
+ maxMessages?: number;
87
+ }
88
+ /**
89
+ * Whether a remembered state is a resting transcript: no run in flight (its own or an out-of-band
90
+ * one) and its messages present. Only such a state is kept, and read while Athena can't be reached;
91
+ * a mid-stream state is a partial transcript.
92
+ */
93
+ export declare function isRestingThreadSnapshot(state: DeepAgentStreamValues): boolean;
94
+ /**
95
+ * What the mirror does with a remembered state: `persist` a resting transcript within the size
96
+ * bound; `skip` a mid-stream state (it arrives per token and its resting successor follows within
97
+ * seconds — the last resting record stays); `invalidate` when the transcript has outgrown the
98
+ * mirror — a stale, shorter record would otherwise seed the next reload until the hello.
99
+ */
100
+ export declare function classifyThreadSnapshotForMirror(state: DeepAgentStreamValues, maxMessages?: number): "persist" | "skip" | "invalidate";
101
+ export declare function createThreadSnapshotMirror({ getKey, factory, databaseName, debounceMs, maxPerOwner, maxMessages, }: ThreadSnapshotMirrorOptions): ThreadSnapshotMirror;
@@ -1,3 +1,4 @@
1
+ import { sha256Hex, requestToPromise, openJson, transactionDone, idbGlobals, lazySealedDatabase, sealJson } from "./sealed-idb.js";
1
2
  const THREAD_SNAPSHOT_MIRROR_DATABASE = "athena-thread-snapshots:v2";
2
3
  const THREAD_SNAPSHOT_MIRROR_FORMAT = 1;
3
4
  const MAX_MIRRORED_SNAPSHOTS_PER_OWNER = 30;
@@ -8,77 +9,16 @@ const SNAPSHOTS_STORE = "snapshots";
8
9
  const LISTS_STORE = "lists";
9
10
  const BY_OWNER = "byOwner";
10
11
  const BY_OWNER_AT = "byOwnerAt";
11
- const IV_BYTES = 12;
12
- const idbGlobals = globalThis;
13
- const encoder = new TextEncoder();
14
- const decoder = new TextDecoder();
15
- async function sha256Hex(value) {
16
- const digest = await crypto.subtle.digest("SHA-256", encoder.encode(value));
17
- return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
18
- }
19
- async function sealJson({ key, value, aad }) {
20
- const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
21
- const ciphertext = new Uint8Array(
22
- await crypto.subtle.encrypt(
23
- { name: "AES-GCM", iv, additionalData: encoder.encode(aad) },
24
- key,
25
- encoder.encode(JSON.stringify(value))
26
- )
27
- );
28
- const sealed = new Uint8Array(IV_BYTES + ciphertext.byteLength);
29
- sealed.set(iv, 0);
30
- sealed.set(ciphertext, IV_BYTES);
31
- return sealed;
32
- }
33
- async function openJson({ key, sealed, aad }) {
34
- const iv = sealed.subarray(0, IV_BYTES);
35
- const plaintext = await crypto.subtle.decrypt(
36
- { name: "AES-GCM", iv, additionalData: encoder.encode(aad) },
37
- key,
38
- sealed.subarray(IV_BYTES)
39
- );
40
- return JSON.parse(decoder.decode(plaintext));
41
- }
42
- function requestToPromise(request) {
43
- return new Promise((resolve, reject) => {
44
- request.onsuccess = () => resolve(request.result);
45
- request.onerror = () => reject(request.error ?? new Error("IndexedDB request failed"));
46
- });
47
- }
48
- function transactionDone(tx) {
49
- return new Promise((resolve, reject) => {
50
- tx.oncomplete = () => resolve();
51
- tx.onabort = () => reject(tx.error ?? new Error("IndexedDB transaction aborted"));
52
- tx.onerror = () => reject(tx.error ?? new Error("IndexedDB transaction failed"));
53
- });
54
- }
55
- function openDatabase(factory, name) {
56
- return new Promise((resolve) => {
57
- try {
58
- const request = factory.open(name, DATABASE_VERSION);
59
- request.onupgradeneeded = () => {
60
- const db = request.result;
61
- if (!db.objectStoreNames.contains(SNAPSHOTS_STORE)) {
62
- const store = db.createObjectStore(SNAPSHOTS_STORE, { keyPath: "key" });
63
- store.createIndex(BY_OWNER, "owner", { unique: false });
64
- store.createIndex(BY_OWNER_AT, ["owner", "at"], { unique: false });
65
- }
66
- if (!db.objectStoreNames.contains(LISTS_STORE)) {
67
- const lists = db.createObjectStore(LISTS_STORE, { keyPath: "key" });
68
- lists.createIndex(BY_OWNER, "owner", { unique: false });
69
- }
70
- };
71
- request.onsuccess = () => {
72
- const db = request.result;
73
- db.onversionchange = () => db.close();
74
- resolve(db);
75
- };
76
- request.onerror = () => resolve(null);
77
- request.onblocked = () => resolve(null);
78
- } catch {
79
- resolve(null);
80
- }
81
- });
12
+ function upgradeMirrorDatabase(db) {
13
+ if (!db.objectStoreNames.contains(SNAPSHOTS_STORE)) {
14
+ const store = db.createObjectStore(SNAPSHOTS_STORE, { keyPath: "key" });
15
+ store.createIndex(BY_OWNER, "owner", { unique: false });
16
+ store.createIndex(BY_OWNER_AT, ["owner", "at"], { unique: false });
17
+ }
18
+ if (!db.objectStoreNames.contains(LISTS_STORE)) {
19
+ const lists = db.createObjectStore(LISTS_STORE, { keyPath: "key" });
20
+ lists.createIndex(BY_OWNER, "owner", { unique: false });
21
+ }
82
22
  }
83
23
  function isRestingThreadSnapshot(state) {
84
24
  var _a;
@@ -108,23 +48,12 @@ function createThreadSnapshotMirror({
108
48
  maxPerOwner = MAX_MIRRORED_SNAPSHOTS_PER_OWNER,
109
49
  maxMessages = MAX_MIRRORED_SNAPSHOT_MESSAGES
110
50
  }) {
111
- let databasePromise = null;
112
- const database = () => {
113
- if (!factory || typeof crypto === "undefined" || !crypto.subtle) return Promise.resolve(null);
114
- databasePromise ?? (databasePromise = openDatabase(factory, databaseName).then((db) => {
115
- if (db) {
116
- const forget = () => {
117
- databasePromise = null;
118
- };
119
- db.addEventListener("versionchange", forget);
120
- db.addEventListener("close", forget);
121
- } else {
122
- databasePromise = null;
123
- }
124
- return db;
125
- }));
126
- return databasePromise;
127
- };
51
+ const database = lazySealedDatabase({
52
+ factory,
53
+ name: databaseName,
54
+ version: DATABASE_VERSION,
55
+ upgrade: upgradeMirrorDatabase
56
+ });
128
57
  const keyOf = async (ownerId, purpose) => {
129
58
  try {
130
59
  return await getKey(ownerId, purpose);
@@ -1 +1 @@
1
- {"version":3,"file":"thread-snapshot-mirror.js","sources":["../../../../agent-runtime/src/thread-snapshot-mirror.ts"],"sourcesContent":["import type { DeepAgentStreamValues } from \"./types\";\n\n/**\n * A durable, encrypted mirror of the thread snapshot cache (`thread-snapshot-cache.ts`) in the\n * browser's IndexedDB, so a reload paints a recently visited thread from its last-known messages,\n * and a thread can be read while the transport cannot connect at all.\n *\n * - **Sealed.** Each record is AES-GCM-256 with a fresh IV under the host's key for its owner\n * (`getKey`: Olympus a non-extractable key in its own keystore, Midas a key main keeps with the\n * OS keychain). The owner and the thread are hashed for the index and bound to the record as\n * additional data, so the database names no one and a record moved to another owner or thread\n * fails to open (and is dropped). No key, no mirror: every write and read is then a no-op.\n * - **Bounded.** Writes are debounced per thread and never taken mid-stream (a state with a live\n * run is replaced by its resting successor within seconds), transcripts longer than\n * `maxMessages` are not kept (an older, shorter record is removed rather than left to seed),\n * and an owner keeps its `maxPerOwner` most recent threads.\n * - **Never in the way.** Every failure degrades to \"no mirror\": a blocked or missing IndexedDB\n * (private browsing, quota, SSR) must never break the wire path.\n * - **Versioned.** A record a newer build wrote is never read, overwritten or pruned by this one;\n * only removing the person's records altogether (an identity change, a sign-out) removes it.\n */\n\nexport const THREAD_SNAPSHOT_MIRROR_DATABASE = \"athena-thread-snapshots:v2\";\n/** The sealed record's format; older builds leave a newer one alone. */\nexport const THREAD_SNAPSHOT_MIRROR_FORMAT = 1;\n/** Threads mirrored per owner; the least recently captured beyond this is pruned after each write. */\nexport const MAX_MIRRORED_SNAPSHOTS_PER_OWNER = 30;\n/** Transcripts longer than this are not mirrored — sealing them would cost more than the seed saves. */\nexport const MAX_MIRRORED_SNAPSHOT_MESSAGES = 150;\n/** Trailing debounce per thread: a streaming thread remembers a state per token. */\nexport const SNAPSHOT_MIRROR_DEBOUNCE_MS = 1_500;\n\nconst DATABASE_VERSION = 1;\nconst SNAPSHOTS_STORE = \"snapshots\";\nconst LISTS_STORE = \"lists\";\nconst BY_OWNER = \"byOwner\";\nconst BY_OWNER_AT = \"byOwnerAt\";\nconst IV_BYTES = 12;\n\nexport interface MirroredThreadSnapshot {\n threadId: string;\n state: DeepAgentStreamValues;\n /** Capture time (epoch ms). */\n at: number;\n}\n\nexport interface MirroredList {\n value: unknown;\n /** When it was written (epoch ms). */\n at: number;\n}\n\nexport interface ThreadSnapshotMirror {\n /** The owner's mirrored snapshots, oldest first. Resolves `[]` on any fault. */\n load(ownerId: string): Promise<MirroredThreadSnapshot[]>;\n /** Mirror a remembered state (debounced per thread; mid-stream states are skipped). */\n write(input: { ownerId: string; threadId: string; state: DeepAgentStreamValues; at: number }): void;\n remove(input: { ownerId: string; threadId: string }): void;\n /** A session list's page as the host last read it, for reading it offline. */\n writeList(input: { ownerId: string; listKey: string; value: unknown }): void;\n loadList(input: { ownerId: string; listKey: string }): Promise<MirroredList | null>;\n /** Drop every record of one owner (an identity change). */\n clearOwner(ownerId: string): Promise<void>;\n /** Drop every record (a sign-out, a workspace switch). */\n clearAll(): Promise<void>;\n /** Write debounced records now (tests, page hide). */\n flush(): Promise<void>;\n}\n\n/**\n * What a key is asked for: reading what is already kept (`read`) or keeping something new\n * (`write`). A host may let what it kept be read where it would keep nothing new, e.g. while its\n * deployment's policy for device copies is not known yet.\n */\nexport type ThreadSnapshotKeyPurpose = \"read\" | \"write\";\n\nexport interface ThreadSnapshotMirrorOptions {\n /** The owner's AES-GCM key, or `null` when the host keeps no mirror for it (no key, policy off). */\n getKey: (ownerId: string, purpose: ThreadSnapshotKeyPurpose) => Promise<CryptoKey | null>;\n /** An `IDBFactory` (the browser's `indexedDB` by default; a fake one in tests). */\n factory?: object;\n databaseName?: string;\n debounceMs?: number;\n maxPerOwner?: number;\n maxMessages?: number;\n}\n\n// This package compiles without the DOM lib (React Native honesty, `globals.d.ts`), so the part of\n// IndexedDB the mirror uses is described structurally here; the mirror runs only where it exists.\ntype IdbListener = ((event?: unknown) => void) | null;\ninterface IdbRequest<T> {\n readonly result: T;\n readonly error: unknown;\n onsuccess: IdbListener;\n onerror: IdbListener;\n}\ninterface IdbOpenRequest extends IdbRequest<IdbDatabase> {\n onupgradeneeded: IdbListener;\n onblocked: IdbListener;\n}\ninterface IdbFactory {\n open(name: string, version?: number): IdbOpenRequest;\n}\ninterface IdbCursor {\n readonly primaryKey: unknown;\n continue(): void;\n}\ninterface IdbIndex {\n getAll(query?: unknown): IdbRequest<unknown[]>;\n getAllKeys(query?: unknown): IdbRequest<unknown[]>;\n openKeyCursor(range?: unknown, direction?: string): IdbRequest<IdbCursor | null>;\n}\ninterface IdbObjectStore {\n put(value: unknown): IdbRequest<unknown>;\n get(key: unknown): IdbRequest<unknown>;\n delete(key: unknown): IdbRequest<unknown>;\n clear(): IdbRequest<unknown>;\n index(name: string): IdbIndex;\n createIndex(name: string, keyPath: string | string[], options?: { unique?: boolean }): unknown;\n}\ninterface IdbTransaction {\n objectStore(name: string): IdbObjectStore;\n readonly error: unknown;\n oncomplete: IdbListener;\n onabort: IdbListener;\n onerror: IdbListener;\n}\ninterface IdbDatabase {\n readonly objectStoreNames: { contains(name: string): boolean };\n transaction(stores: string | string[], mode?: \"readonly\" | \"readwrite\"): IdbTransaction;\n createObjectStore(name: string, options?: { keyPath: string }): IdbObjectStore;\n onversionchange: IdbListener;\n addEventListener(type: string, listener: () => void): void;\n close(): void;\n}\ninterface IdbKeyRangeStatic {\n bound(lower: unknown, upper: unknown): unknown;\n}\n\nconst idbGlobals = globalThis as { indexedDB?: object; IDBKeyRange?: IdbKeyRangeStatic };\n\ninterface SnapshotRecord {\n key: string;\n owner: string;\n at: number;\n format: number;\n sealed: Uint8Array;\n}\n\ninterface ListRecord {\n key: string;\n owner: string;\n at: number;\n format: number;\n sealed: Uint8Array;\n}\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\nasync function sha256Hex(value: string): Promise<string> {\n const digest = await crypto.subtle.digest(\"SHA-256\", encoder.encode(value));\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nasync function sealJson({ key, value, aad }: { key: CryptoKey; value: unknown; aad: string }) {\n const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));\n const ciphertext = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv, additionalData: encoder.encode(aad) },\n key,\n encoder.encode(JSON.stringify(value)),\n ),\n );\n const sealed = new Uint8Array(IV_BYTES + ciphertext.byteLength);\n sealed.set(iv, 0);\n sealed.set(ciphertext, IV_BYTES);\n return sealed;\n}\n\nasync function openJson({ key, sealed, aad }: { key: CryptoKey; sealed: Uint8Array; aad: string }) {\n const iv = sealed.subarray(0, IV_BYTES) as Uint8Array<ArrayBuffer>;\n const plaintext = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv, additionalData: encoder.encode(aad) },\n key,\n sealed.subarray(IV_BYTES) as Uint8Array<ArrayBuffer>,\n );\n return JSON.parse(decoder.decode(plaintext)) as unknown;\n}\n\nfunction requestToPromise<T>(request: IdbRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error(\"IndexedDB request failed\"));\n });\n}\n\nfunction transactionDone(tx: IdbTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onabort = () => reject(tx.error ?? new Error(\"IndexedDB transaction aborted\"));\n tx.onerror = () => reject(tx.error ?? new Error(\"IndexedDB transaction failed\"));\n });\n}\n\nfunction openDatabase(factory: IdbFactory, name: string): Promise<IdbDatabase | null> {\n return new Promise((resolve) => {\n try {\n const request = factory.open(name, DATABASE_VERSION);\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(SNAPSHOTS_STORE)) {\n const store = db.createObjectStore(SNAPSHOTS_STORE, { keyPath: \"key\" });\n store.createIndex(BY_OWNER, \"owner\", { unique: false });\n store.createIndex(BY_OWNER_AT, [\"owner\", \"at\"], { unique: false });\n }\n if (!db.objectStoreNames.contains(LISTS_STORE)) {\n const lists = db.createObjectStore(LISTS_STORE, { keyPath: \"key\" });\n lists.createIndex(BY_OWNER, \"owner\", { unique: false });\n }\n };\n request.onsuccess = () => {\n const db = request.result;\n // Another page deleting or upgrading the database: step aside.\n db.onversionchange = () => db.close();\n resolve(db);\n };\n // A database a newer build upgraded past this version is left alone.\n request.onerror = () => resolve(null);\n request.onblocked = () => resolve(null);\n } catch {\n resolve(null);\n }\n });\n}\n\n/**\n * Whether a remembered state is a resting transcript: no run in flight (its own or an out-of-band\n * one) and its messages present. Only such a state is kept, and read while Athena can't be reached;\n * a mid-stream state is a partial transcript.\n */\nexport function isRestingThreadSnapshot(state: DeepAgentStreamValues): boolean {\n const run = state.runs?.[0];\n if (run && run.status === \"running\") return false;\n if (state.outOfBandRunActive === true) return false;\n return Array.isArray(state.messages);\n}\n\n/**\n * What the mirror does with a remembered state: `persist` a resting transcript within the size\n * bound; `skip` a mid-stream state (it arrives per token and its resting successor follows within\n * seconds — the last resting record stays); `invalidate` when the transcript has outgrown the\n * mirror — a stale, shorter record would otherwise seed the next reload until the hello.\n */\nexport function classifyThreadSnapshotForMirror(\n state: DeepAgentStreamValues,\n maxMessages: number = MAX_MIRRORED_SNAPSHOT_MESSAGES,\n): \"persist\" | \"skip\" | \"invalidate\" {\n if (!isRestingThreadSnapshot(state)) return \"skip\";\n return (state.messages?.length ?? 0) <= maxMessages ? \"persist\" : \"invalidate\";\n}\n\n/** A record this build may replace or delete: none, or one in a format it writes itself. */\nfunction isOwnFormat(record: unknown): boolean {\n if (typeof record !== \"object\" || record === null) return true;\n const format = (record as { format?: unknown }).format;\n return typeof format !== \"number\" || format <= THREAD_SNAPSHOT_MIRROR_FORMAT;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function createThreadSnapshotMirror({\n getKey,\n factory = idbGlobals.indexedDB,\n databaseName = THREAD_SNAPSHOT_MIRROR_DATABASE,\n debounceMs = SNAPSHOT_MIRROR_DEBOUNCE_MS,\n maxPerOwner = MAX_MIRRORED_SNAPSHOTS_PER_OWNER,\n maxMessages = MAX_MIRRORED_SNAPSHOT_MESSAGES,\n}: ThreadSnapshotMirrorOptions): ThreadSnapshotMirror {\n let databasePromise: Promise<IdbDatabase | null> | null = null;\n const database = (): Promise<IdbDatabase | null> => {\n if (!factory || typeof crypto === \"undefined\" || !crypto.subtle) return Promise.resolve(null);\n databasePromise ??= openDatabase(factory as IdbFactory, databaseName).then((db) => {\n if (db) {\n const forget = () => {\n databasePromise = null;\n };\n db.addEventListener(\"versionchange\", forget);\n db.addEventListener(\"close\", forget);\n } else {\n databasePromise = null;\n }\n return db;\n });\n return databasePromise;\n };\n const keyOf = async (ownerId: string, purpose: ThreadSnapshotKeyPurpose): Promise<CryptoKey | null> => {\n try {\n return await getKey(ownerId, purpose);\n } catch {\n return null;\n }\n };\n const aadOf = (kind: string, owner: string, key: string): string =>\n `athena-thread-snapshot|${THREAD_SNAPSHOT_MIRROR_FORMAT}|${kind}|${owner}|${key}`;\n\n const pending = new Map<\n string,\n { input: { ownerId: string; threadId: string; state: DeepAgentStreamValues; at: number }; timer: ReturnType<typeof setTimeout> }\n >();\n const pendingLists = new Map<\n string,\n { input: { ownerId: string; listKey: string; value: unknown }; timer: ReturnType<typeof setTimeout> }\n >();\n // Writes are serialized so a flush never races a clear.\n let queue: Promise<void> = Promise.resolve();\n const enqueue = (task: () => Promise<void>): Promise<void> => {\n queue = queue.then(task, task).catch(() => {});\n return queue;\n };\n\n const pruneOwner = async (db: IdbDatabase, owner: string): Promise<void> => {\n const KeyRange = idbGlobals.IDBKeyRange;\n if (!KeyRange) return;\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n const store = tx.objectStore(SNAPSHOTS_STORE);\n const index = store.index(BY_OWNER_AT);\n // Key cursor, newest first: bookkeeping never loads the sealed payloads of the records it keeps.\n const range = KeyRange.bound([owner, Number.NEGATIVE_INFINITY], [owner, Number.POSITIVE_INFINITY]);\n const beyond: unknown[] = [];\n await new Promise<void>((resolve, reject) => {\n let kept = 0;\n const request = index.openKeyCursor(range, \"prev\");\n request.onsuccess = () => {\n const cursor = request.result;\n if (!cursor) {\n resolve();\n return;\n }\n kept += 1;\n if (kept > maxPerOwner) beyond.push(cursor.primaryKey);\n cursor.continue();\n };\n request.onerror = () => reject(request.error ?? new Error(\"IndexedDB cursor failed\"));\n });\n // A newer build's record is left to that build's own bound.\n for (const key of beyond) {\n if (isOwnFormat(await requestToPromise(store.get(key)))) store.delete(key);\n }\n await transactionDone(tx);\n };\n\n const put = (input: { ownerId: string; threadId: string; state: DeepAgentStreamValues; at: number }) =>\n enqueue(async () => {\n const db = await database();\n if (!db) return;\n const cryptoKey = await keyOf(input.ownerId, \"write\");\n if (!cryptoKey) return;\n const owner = await sha256Hex(input.ownerId);\n const key = await sha256Hex(`${input.ownerId}|${input.threadId}`);\n const sealed = await sealJson({\n key: cryptoKey,\n value: { threadId: input.threadId, state: input.state },\n aad: aadOf(\"snapshot\", owner, key),\n });\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n const store = tx.objectStore(SNAPSHOTS_STORE);\n // A newer build's record of the thread stays as that build wrote it.\n if (isOwnFormat(await requestToPromise(store.get(key)))) {\n store.put({\n key,\n owner,\n at: input.at,\n format: THREAD_SNAPSHOT_MIRROR_FORMAT,\n sealed,\n } satisfies SnapshotRecord);\n }\n await transactionDone(tx);\n await pruneOwner(db, owner);\n });\n\n const deleteSnapshot = (input: { ownerId: string; threadId: string }) =>\n enqueue(async () => {\n const db = await database();\n if (!db) return;\n const key = await sha256Hex(`${input.ownerId}|${input.threadId}`);\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n const store = tx.objectStore(SNAPSHOTS_STORE);\n if (isOwnFormat(await requestToPromise(store.get(key)))) store.delete(key);\n await transactionDone(tx);\n });\n\n const pendingKey = (ownerId: string, threadId: string) => `${ownerId}\\u0000${threadId}`;\n\n const flushPending = (key: string): Promise<void> => {\n const entry = pending.get(key);\n if (!entry) return Promise.resolve();\n pending.delete(key);\n clearTimeout(entry.timer);\n return put(entry.input);\n };\n\n const putList = (input: { ownerId: string; listKey: string; value: unknown }) =>\n enqueue(async () => {\n const db = await database();\n if (!db) return;\n const cryptoKey = await keyOf(input.ownerId, \"write\");\n if (!cryptoKey) return;\n const owner = await sha256Hex(input.ownerId);\n const key = await sha256Hex(`${input.ownerId}|list|${input.listKey}`);\n const sealed = await sealJson({ key: cryptoKey, value: input.value, aad: aadOf(\"list\", owner, key) });\n const tx = db.transaction(LISTS_STORE, \"readwrite\");\n const store = tx.objectStore(LISTS_STORE);\n if (isOwnFormat(await requestToPromise(store.get(key)))) {\n store.put({\n key,\n owner,\n at: Date.now(),\n format: THREAD_SNAPSHOT_MIRROR_FORMAT,\n sealed,\n } satisfies ListRecord);\n }\n await transactionDone(tx);\n });\n\n const flushList = (key: string): Promise<void> => {\n const entry = pendingLists.get(key);\n if (!entry) return Promise.resolve();\n pendingLists.delete(key);\n clearTimeout(entry.timer);\n return putList(entry.input);\n };\n\n const dropPendingOf = (ownerId: string | null): void => {\n for (const [key, entry] of pending) {\n if (ownerId === null || entry.input.ownerId === ownerId) {\n clearTimeout(entry.timer);\n pending.delete(key);\n }\n }\n for (const [key, entry] of pendingLists) {\n if (ownerId === null || entry.input.ownerId === ownerId) {\n clearTimeout(entry.timer);\n pendingLists.delete(key);\n }\n }\n };\n\n return {\n async load(ownerId) {\n try {\n const db = await database();\n if (!db) return [];\n const cryptoKey = await keyOf(ownerId, \"read\");\n if (!cryptoKey) return [];\n const owner = await sha256Hex(ownerId);\n const records = (await requestToPromise(\n db.transaction(SNAPSHOTS_STORE, \"readonly\").objectStore(SNAPSHOTS_STORE).index(BY_OWNER).getAll(owner),\n )) as SnapshotRecord[];\n const loaded: MirroredThreadSnapshot[] = [];\n const unreadable: string[] = [];\n for (const record of records) {\n if (!record || record.format > THREAD_SNAPSHOT_MIRROR_FORMAT) continue;\n try {\n const value = await openJson({\n key: cryptoKey,\n sealed: record.sealed,\n aad: aadOf(\"snapshot\", owner, record.key),\n });\n if (\n isRecord(value) &&\n typeof value.threadId === \"string\" &&\n isRecord(value.state) &&\n typeof record.at === \"number\"\n ) {\n loaded.push({ threadId: value.threadId, state: value.state as DeepAgentStreamValues, at: record.at });\n }\n } catch {\n unreadable.push(record.key);\n }\n }\n if (unreadable.length > 0) {\n void enqueue(async () => {\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n for (const key of unreadable) tx.objectStore(SNAPSHOTS_STORE).delete(key);\n await transactionDone(tx);\n });\n }\n return loaded.sort((a, b) => a.at - b.at);\n } catch {\n return [];\n }\n },\n write(input) {\n const key = pendingKey(input.ownerId, input.threadId);\n const verdict = classifyThreadSnapshotForMirror(input.state, maxMessages);\n if (verdict === \"invalidate\") {\n const existing = pending.get(key);\n if (existing) clearTimeout(existing.timer);\n pending.delete(key);\n void deleteSnapshot(input);\n return;\n }\n if (verdict === \"skip\") return;\n const existing = pending.get(key);\n if (existing) clearTimeout(existing.timer);\n pending.set(key, {\n input,\n timer: setTimeout(() => {\n void flushPending(key);\n }, debounceMs),\n });\n },\n remove(input) {\n const key = pendingKey(input.ownerId, input.threadId);\n const existing = pending.get(key);\n if (existing) clearTimeout(existing.timer);\n pending.delete(key);\n void deleteSnapshot(input);\n },\n writeList(input) {\n const key = pendingKey(input.ownerId, `list:${input.listKey}`);\n const existing = pendingLists.get(key);\n if (existing) clearTimeout(existing.timer);\n pendingLists.set(key, {\n input,\n timer: setTimeout(() => {\n void flushList(key);\n }, debounceMs),\n });\n },\n async loadList({ ownerId, listKey }) {\n try {\n const db = await database();\n if (!db) return null;\n const cryptoKey = await keyOf(ownerId, \"read\");\n if (!cryptoKey) return null;\n const owner = await sha256Hex(ownerId);\n const key = await sha256Hex(`${ownerId}|list|${listKey}`);\n const record = (await requestToPromise(\n db.transaction(LISTS_STORE, \"readonly\").objectStore(LISTS_STORE).get(key),\n )) as ListRecord | undefined;\n if (!record || record.format > THREAD_SNAPSHOT_MIRROR_FORMAT || record.owner !== owner) return null;\n try {\n const value = await openJson({ key: cryptoKey, sealed: record.sealed, aad: aadOf(\"list\", owner, key) });\n return { value, at: record.at };\n } catch {\n return null;\n }\n } catch {\n return null;\n }\n },\n clearOwner(ownerId) {\n dropPendingOf(ownerId);\n return enqueue(async () => {\n const db = await database();\n if (!db) return;\n const owner = await sha256Hex(ownerId);\n const tx = db.transaction([SNAPSHOTS_STORE, LISTS_STORE], \"readwrite\");\n for (const storeName of [SNAPSHOTS_STORE, LISTS_STORE]) {\n const store = tx.objectStore(storeName);\n const keys = await requestToPromise(store.index(BY_OWNER).getAllKeys(owner));\n for (const key of keys) store.delete(key);\n }\n await transactionDone(tx);\n });\n },\n clearAll() {\n dropPendingOf(null);\n return enqueue(async () => {\n const db = await database();\n if (!db) return;\n const tx = db.transaction([SNAPSHOTS_STORE, LISTS_STORE], \"readwrite\");\n tx.objectStore(SNAPSHOTS_STORE).clear();\n tx.objectStore(LISTS_STORE).clear();\n await transactionDone(tx);\n });\n },\n async flush() {\n await Promise.all([...pending.keys()].map(flushPending));\n await Promise.all([...pendingLists.keys()].map(flushList));\n await queue;\n },\n };\n}\n"],"names":["existing"],"mappings":"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"}
1
+ {"version":3,"file":"thread-snapshot-mirror.js","sources":["../../../../agent-runtime/src/thread-snapshot-mirror.ts"],"sourcesContent":["import {\n type IdbDatabase,\n idbGlobals,\n lazySealedDatabase,\n openJson,\n requestToPromise,\n sealJson,\n sha256Hex,\n transactionDone,\n} from \"./sealed-idb\";\nimport type { DeepAgentStreamValues } from \"./types\";\n\n/**\n * A durable, encrypted mirror of the thread snapshot cache (`thread-snapshot-cache.ts`) in the\n * browser's IndexedDB, so a reload paints a recently visited thread from its last-known messages,\n * and a thread can be read while the transport cannot connect at all.\n *\n * - **Sealed.** Each record is AES-GCM-256 with a fresh IV under the host's key for its owner\n * (`getKey`: Olympus a non-extractable key in its own keystore, Midas a key main keeps with the\n * OS keychain). The owner and the thread are hashed for the index and bound to the record as\n * additional data, so the database names no one and a record moved to another owner or thread\n * fails to open (and is dropped). No key, no mirror: every write and read is then a no-op.\n * - **Bounded.** Writes are debounced per thread and never taken mid-stream (a state with a live\n * run is replaced by its resting successor within seconds), transcripts longer than\n * `maxMessages` are not kept (an older, shorter record is removed rather than left to seed),\n * and an owner keeps its `maxPerOwner` most recent threads.\n * - **Never in the way.** Every failure degrades to \"no mirror\": a blocked or missing IndexedDB\n * (private browsing, quota, SSR) must never break the wire path.\n * - **Versioned.** A record a newer build wrote is never read, overwritten or pruned by this one;\n * only removing the person's records altogether (an identity change, a sign-out) removes it.\n */\n\nexport const THREAD_SNAPSHOT_MIRROR_DATABASE = \"athena-thread-snapshots:v2\";\n/** The sealed record's format; older builds leave a newer one alone. */\nexport const THREAD_SNAPSHOT_MIRROR_FORMAT = 1;\n/** Threads mirrored per owner; the least recently captured beyond this is pruned after each write. */\nexport const MAX_MIRRORED_SNAPSHOTS_PER_OWNER = 30;\n/** Transcripts longer than this are not mirrored — sealing them would cost more than the seed saves. */\nexport const MAX_MIRRORED_SNAPSHOT_MESSAGES = 150;\n/** Trailing debounce per thread: a streaming thread remembers a state per token. */\nexport const SNAPSHOT_MIRROR_DEBOUNCE_MS = 1_500;\n\nconst DATABASE_VERSION = 1;\nconst SNAPSHOTS_STORE = \"snapshots\";\nconst LISTS_STORE = \"lists\";\nconst BY_OWNER = \"byOwner\";\nconst BY_OWNER_AT = \"byOwnerAt\";\n\nexport interface MirroredThreadSnapshot {\n threadId: string;\n state: DeepAgentStreamValues;\n /** Capture time (epoch ms). */\n at: number;\n}\n\nexport interface MirroredList {\n value: unknown;\n /** When it was written (epoch ms). */\n at: number;\n}\n\nexport interface ThreadSnapshotMirror {\n /** The owner's mirrored snapshots, oldest first. Resolves `[]` on any fault. */\n load(ownerId: string): Promise<MirroredThreadSnapshot[]>;\n /** Mirror a remembered state (debounced per thread; mid-stream states are skipped). */\n write(input: { ownerId: string; threadId: string; state: DeepAgentStreamValues; at: number }): void;\n remove(input: { ownerId: string; threadId: string }): void;\n /** A session list's page as the host last read it, for reading it offline. */\n writeList(input: { ownerId: string; listKey: string; value: unknown }): void;\n loadList(input: { ownerId: string; listKey: string }): Promise<MirroredList | null>;\n /** Drop every record of one owner (an identity change). */\n clearOwner(ownerId: string): Promise<void>;\n /** Drop every record (a sign-out, a workspace switch). */\n clearAll(): Promise<void>;\n /** Write debounced records now (tests, page hide). */\n flush(): Promise<void>;\n}\n\n/**\n * What a key is asked for: reading what is already kept (`read`) or keeping something new\n * (`write`). A host may let what it kept be read where it would keep nothing new, e.g. while its\n * deployment's policy for device copies is not known yet.\n */\nexport type ThreadSnapshotKeyPurpose = \"read\" | \"write\";\n\nexport interface ThreadSnapshotMirrorOptions {\n /** The owner's AES-GCM key, or `null` when the host keeps no mirror for it (no key, policy off). */\n getKey: (ownerId: string, purpose: ThreadSnapshotKeyPurpose) => Promise<CryptoKey | null>;\n /** An `IDBFactory` (the browser's `indexedDB` by default; a fake one in tests). */\n factory?: object;\n databaseName?: string;\n debounceMs?: number;\n maxPerOwner?: number;\n maxMessages?: number;\n}\n\ninterface SnapshotRecord {\n key: string;\n owner: string;\n at: number;\n format: number;\n sealed: Uint8Array;\n}\n\ninterface ListRecord {\n key: string;\n owner: string;\n at: number;\n format: number;\n sealed: Uint8Array;\n}\n\nfunction upgradeMirrorDatabase(db: IdbDatabase): void {\n if (!db.objectStoreNames.contains(SNAPSHOTS_STORE)) {\n const store = db.createObjectStore(SNAPSHOTS_STORE, { keyPath: \"key\" });\n store.createIndex(BY_OWNER, \"owner\", { unique: false });\n store.createIndex(BY_OWNER_AT, [\"owner\", \"at\"], { unique: false });\n }\n if (!db.objectStoreNames.contains(LISTS_STORE)) {\n const lists = db.createObjectStore(LISTS_STORE, { keyPath: \"key\" });\n lists.createIndex(BY_OWNER, \"owner\", { unique: false });\n }\n}\n\n/**\n * Whether a remembered state is a resting transcript: no run in flight (its own or an out-of-band\n * one) and its messages present. Only such a state is kept, and read while Athena can't be reached;\n * a mid-stream state is a partial transcript.\n */\nexport function isRestingThreadSnapshot(state: DeepAgentStreamValues): boolean {\n const run = state.runs?.[0];\n if (run && run.status === \"running\") return false;\n if (state.outOfBandRunActive === true) return false;\n return Array.isArray(state.messages);\n}\n\n/**\n * What the mirror does with a remembered state: `persist` a resting transcript within the size\n * bound; `skip` a mid-stream state (it arrives per token and its resting successor follows within\n * seconds — the last resting record stays); `invalidate` when the transcript has outgrown the\n * mirror — a stale, shorter record would otherwise seed the next reload until the hello.\n */\nexport function classifyThreadSnapshotForMirror(\n state: DeepAgentStreamValues,\n maxMessages: number = MAX_MIRRORED_SNAPSHOT_MESSAGES,\n): \"persist\" | \"skip\" | \"invalidate\" {\n if (!isRestingThreadSnapshot(state)) return \"skip\";\n return (state.messages?.length ?? 0) <= maxMessages ? \"persist\" : \"invalidate\";\n}\n\n/** A record this build may replace or delete: none, or one in a format it writes itself. */\nfunction isOwnFormat(record: unknown): boolean {\n if (typeof record !== \"object\" || record === null) return true;\n const format = (record as { format?: unknown }).format;\n return typeof format !== \"number\" || format <= THREAD_SNAPSHOT_MIRROR_FORMAT;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function createThreadSnapshotMirror({\n getKey,\n factory = idbGlobals.indexedDB,\n databaseName = THREAD_SNAPSHOT_MIRROR_DATABASE,\n debounceMs = SNAPSHOT_MIRROR_DEBOUNCE_MS,\n maxPerOwner = MAX_MIRRORED_SNAPSHOTS_PER_OWNER,\n maxMessages = MAX_MIRRORED_SNAPSHOT_MESSAGES,\n}: ThreadSnapshotMirrorOptions): ThreadSnapshotMirror {\n const database = lazySealedDatabase({\n factory,\n name: databaseName,\n version: DATABASE_VERSION,\n upgrade: upgradeMirrorDatabase,\n });\n const keyOf = async (ownerId: string, purpose: ThreadSnapshotKeyPurpose): Promise<CryptoKey | null> => {\n try {\n return await getKey(ownerId, purpose);\n } catch {\n return null;\n }\n };\n const aadOf = (kind: string, owner: string, key: string): string =>\n `athena-thread-snapshot|${THREAD_SNAPSHOT_MIRROR_FORMAT}|${kind}|${owner}|${key}`;\n\n const pending = new Map<\n string,\n { input: { ownerId: string; threadId: string; state: DeepAgentStreamValues; at: number }; timer: ReturnType<typeof setTimeout> }\n >();\n const pendingLists = new Map<\n string,\n { input: { ownerId: string; listKey: string; value: unknown }; timer: ReturnType<typeof setTimeout> }\n >();\n // Writes are serialized so a flush never races a clear.\n let queue: Promise<void> = Promise.resolve();\n const enqueue = (task: () => Promise<void>): Promise<void> => {\n queue = queue.then(task, task).catch(() => {});\n return queue;\n };\n\n const pruneOwner = async (db: IdbDatabase, owner: string): Promise<void> => {\n const KeyRange = idbGlobals.IDBKeyRange;\n if (!KeyRange) return;\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n const store = tx.objectStore(SNAPSHOTS_STORE);\n const index = store.index(BY_OWNER_AT);\n // Key cursor, newest first: bookkeeping never loads the sealed payloads of the records it keeps.\n const range = KeyRange.bound([owner, Number.NEGATIVE_INFINITY], [owner, Number.POSITIVE_INFINITY]);\n const beyond: unknown[] = [];\n await new Promise<void>((resolve, reject) => {\n let kept = 0;\n const request = index.openKeyCursor(range, \"prev\");\n request.onsuccess = () => {\n const cursor = request.result;\n if (!cursor) {\n resolve();\n return;\n }\n kept += 1;\n if (kept > maxPerOwner) beyond.push(cursor.primaryKey);\n cursor.continue();\n };\n request.onerror = () => reject(request.error ?? new Error(\"IndexedDB cursor failed\"));\n });\n // A newer build's record is left to that build's own bound.\n for (const key of beyond) {\n if (isOwnFormat(await requestToPromise(store.get(key)))) store.delete(key);\n }\n await transactionDone(tx);\n };\n\n const put = (input: { ownerId: string; threadId: string; state: DeepAgentStreamValues; at: number }) =>\n enqueue(async () => {\n const db = await database();\n if (!db) return;\n const cryptoKey = await keyOf(input.ownerId, \"write\");\n if (!cryptoKey) return;\n const owner = await sha256Hex(input.ownerId);\n const key = await sha256Hex(`${input.ownerId}|${input.threadId}`);\n const sealed = await sealJson({\n key: cryptoKey,\n value: { threadId: input.threadId, state: input.state },\n aad: aadOf(\"snapshot\", owner, key),\n });\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n const store = tx.objectStore(SNAPSHOTS_STORE);\n // A newer build's record of the thread stays as that build wrote it.\n if (isOwnFormat(await requestToPromise(store.get(key)))) {\n store.put({\n key,\n owner,\n at: input.at,\n format: THREAD_SNAPSHOT_MIRROR_FORMAT,\n sealed,\n } satisfies SnapshotRecord);\n }\n await transactionDone(tx);\n await pruneOwner(db, owner);\n });\n\n const deleteSnapshot = (input: { ownerId: string; threadId: string }) =>\n enqueue(async () => {\n const db = await database();\n if (!db) return;\n const key = await sha256Hex(`${input.ownerId}|${input.threadId}`);\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n const store = tx.objectStore(SNAPSHOTS_STORE);\n if (isOwnFormat(await requestToPromise(store.get(key)))) store.delete(key);\n await transactionDone(tx);\n });\n\n const pendingKey = (ownerId: string, threadId: string) => `${ownerId}\\u0000${threadId}`;\n\n const flushPending = (key: string): Promise<void> => {\n const entry = pending.get(key);\n if (!entry) return Promise.resolve();\n pending.delete(key);\n clearTimeout(entry.timer);\n return put(entry.input);\n };\n\n const putList = (input: { ownerId: string; listKey: string; value: unknown }) =>\n enqueue(async () => {\n const db = await database();\n if (!db) return;\n const cryptoKey = await keyOf(input.ownerId, \"write\");\n if (!cryptoKey) return;\n const owner = await sha256Hex(input.ownerId);\n const key = await sha256Hex(`${input.ownerId}|list|${input.listKey}`);\n const sealed = await sealJson({ key: cryptoKey, value: input.value, aad: aadOf(\"list\", owner, key) });\n const tx = db.transaction(LISTS_STORE, \"readwrite\");\n const store = tx.objectStore(LISTS_STORE);\n if (isOwnFormat(await requestToPromise(store.get(key)))) {\n store.put({\n key,\n owner,\n at: Date.now(),\n format: THREAD_SNAPSHOT_MIRROR_FORMAT,\n sealed,\n } satisfies ListRecord);\n }\n await transactionDone(tx);\n });\n\n const flushList = (key: string): Promise<void> => {\n const entry = pendingLists.get(key);\n if (!entry) return Promise.resolve();\n pendingLists.delete(key);\n clearTimeout(entry.timer);\n return putList(entry.input);\n };\n\n const dropPendingOf = (ownerId: string | null): void => {\n for (const [key, entry] of pending) {\n if (ownerId === null || entry.input.ownerId === ownerId) {\n clearTimeout(entry.timer);\n pending.delete(key);\n }\n }\n for (const [key, entry] of pendingLists) {\n if (ownerId === null || entry.input.ownerId === ownerId) {\n clearTimeout(entry.timer);\n pendingLists.delete(key);\n }\n }\n };\n\n return {\n async load(ownerId) {\n try {\n const db = await database();\n if (!db) return [];\n const cryptoKey = await keyOf(ownerId, \"read\");\n if (!cryptoKey) return [];\n const owner = await sha256Hex(ownerId);\n const records = (await requestToPromise(\n db.transaction(SNAPSHOTS_STORE, \"readonly\").objectStore(SNAPSHOTS_STORE).index(BY_OWNER).getAll(owner),\n )) as SnapshotRecord[];\n const loaded: MirroredThreadSnapshot[] = [];\n const unreadable: string[] = [];\n for (const record of records) {\n if (!record || record.format > THREAD_SNAPSHOT_MIRROR_FORMAT) continue;\n try {\n const value = await openJson({\n key: cryptoKey,\n sealed: record.sealed,\n aad: aadOf(\"snapshot\", owner, record.key),\n });\n if (\n isRecord(value) &&\n typeof value.threadId === \"string\" &&\n isRecord(value.state) &&\n typeof record.at === \"number\"\n ) {\n loaded.push({ threadId: value.threadId, state: value.state as DeepAgentStreamValues, at: record.at });\n }\n } catch {\n unreadable.push(record.key);\n }\n }\n if (unreadable.length > 0) {\n void enqueue(async () => {\n const tx = db.transaction(SNAPSHOTS_STORE, \"readwrite\");\n for (const key of unreadable) tx.objectStore(SNAPSHOTS_STORE).delete(key);\n await transactionDone(tx);\n });\n }\n return loaded.sort((a, b) => a.at - b.at);\n } catch {\n return [];\n }\n },\n write(input) {\n const key = pendingKey(input.ownerId, input.threadId);\n const verdict = classifyThreadSnapshotForMirror(input.state, maxMessages);\n if (verdict === \"invalidate\") {\n const existing = pending.get(key);\n if (existing) clearTimeout(existing.timer);\n pending.delete(key);\n void deleteSnapshot(input);\n return;\n }\n if (verdict === \"skip\") return;\n const existing = pending.get(key);\n if (existing) clearTimeout(existing.timer);\n pending.set(key, {\n input,\n timer: setTimeout(() => {\n void flushPending(key);\n }, debounceMs),\n });\n },\n remove(input) {\n const key = pendingKey(input.ownerId, input.threadId);\n const existing = pending.get(key);\n if (existing) clearTimeout(existing.timer);\n pending.delete(key);\n void deleteSnapshot(input);\n },\n writeList(input) {\n const key = pendingKey(input.ownerId, `list:${input.listKey}`);\n const existing = pendingLists.get(key);\n if (existing) clearTimeout(existing.timer);\n pendingLists.set(key, {\n input,\n timer: setTimeout(() => {\n void flushList(key);\n }, debounceMs),\n });\n },\n async loadList({ ownerId, listKey }) {\n try {\n const db = await database();\n if (!db) return null;\n const cryptoKey = await keyOf(ownerId, \"read\");\n if (!cryptoKey) return null;\n const owner = await sha256Hex(ownerId);\n const key = await sha256Hex(`${ownerId}|list|${listKey}`);\n const record = (await requestToPromise(\n db.transaction(LISTS_STORE, \"readonly\").objectStore(LISTS_STORE).get(key),\n )) as ListRecord | undefined;\n if (!record || record.format > THREAD_SNAPSHOT_MIRROR_FORMAT || record.owner !== owner) return null;\n try {\n const value = await openJson({ key: cryptoKey, sealed: record.sealed, aad: aadOf(\"list\", owner, key) });\n return { value, at: record.at };\n } catch {\n return null;\n }\n } catch {\n return null;\n }\n },\n clearOwner(ownerId) {\n dropPendingOf(ownerId);\n return enqueue(async () => {\n const db = await database();\n if (!db) return;\n const owner = await sha256Hex(ownerId);\n const tx = db.transaction([SNAPSHOTS_STORE, LISTS_STORE], \"readwrite\");\n for (const storeName of [SNAPSHOTS_STORE, LISTS_STORE]) {\n const store = tx.objectStore(storeName);\n const keys = await requestToPromise(store.index(BY_OWNER).getAllKeys(owner));\n for (const key of keys) store.delete(key);\n }\n await transactionDone(tx);\n });\n },\n clearAll() {\n dropPendingOf(null);\n return enqueue(async () => {\n const db = await database();\n if (!db) return;\n const tx = db.transaction([SNAPSHOTS_STORE, LISTS_STORE], \"readwrite\");\n tx.objectStore(SNAPSHOTS_STORE).clear();\n tx.objectStore(LISTS_STORE).clear();\n await transactionDone(tx);\n });\n },\n async flush() {\n await Promise.all([...pending.keys()].map(flushPending));\n await Promise.all([...pendingLists.keys()].map(flushList));\n await queue;\n },\n };\n}\n"],"names":["existing"],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { StatewireThread } from '@assistant-ui/react-statewire';
2
+ import type { DeepAgentConnectionState } from './types';
3
+ export type DeepAgentThreadStatus = StatewireThread.ThreadStatus;
4
+ /**
5
+ * Runtime-scoped thread status, as the library presents it: `"loading"` /
6
+ * `"ready"` / the run's status replicated by the host, with a mid-run
7
+ * connection loss presented as `"evicted"` (server-update pod drain) or
8
+ * `"disconnected"` instead of `"running"`. Throws outside a statewire aui
9
+ * provider, matching react-statewire 0.13's removed `useStatewireThreadStatus`.
10
+ */
11
+ export declare function useDeepAgentThreadStatus(): DeepAgentThreadStatus;
12
+ /** The two presented statuses that hide a live run behind a channel problem. */
13
+ export type DeepAgentStaleRunReason = Extract<DeepAgentThreadStatus, 'disconnected' | 'evicted'>;
14
+ /** A first connection attempt can present an optimistic run without a stale warning. */
15
+ export declare function resolveStaleRunReason({ status, connection, }: {
16
+ status: DeepAgentThreadStatus;
17
+ connection?: DeepAgentConnectionState | null;
18
+ }): DeepAgentStaleRunReason | null;
19
+ /**
20
+ * Whether the thread has a live run to present — the composer's Running bar
21
+ * and its Stop/Queue slot.
22
+ *
23
+ * The library derives `thread.isRunning` from the PRESENTED status, so a run
24
+ * that is live on the host but sits behind a channel that has not connected
25
+ * yet reads as not running. Behind a first dial that is exactly the run the
26
+ * client just seeded — switching back to a thread that was running when you
27
+ * left, or a brand-new chat's optimistic first send — and nothing is stale:
28
+ * the channel is still attaching. Hiding the run for that round-trip flashes
29
+ * Ready and a Send button before Running comes back. A run behind a reconnect
30
+ * or an eviction is presented by the stale treatment (`resolveStaleRunReason`),
31
+ * never as live.
32
+ */
33
+ export declare function resolveRunLive({ status, connection, }: {
34
+ status: DeepAgentThreadStatus;
35
+ connection?: DeepAgentConnectionState | null;
36
+ }): boolean;
37
+ /**
38
+ * `thread.isRunning` for a `useAuiState` selector, folded through
39
+ * `resolveRunLive` when the thread is a statewire thread. A thread without a
40
+ * statewire status (another runtime, a test stub) keeps the library's answer.
41
+ */
42
+ export declare function readRunLive(thread: {
43
+ isRunning: boolean;
44
+ extras?: unknown;
45
+ }, connection: DeepAgentConnectionState | null): boolean;
46
+ /**
47
+ * The active thread's channel, for `readRunLive`'s first-dial verdict. Each
48
+ * host provides it from its own lifecycle state (`AthenaProvider`, the
49
+ * Chrome extension's lifecycle provider); `null` means "no channel known",
50
+ * which folds a `disconnected` status as stale.
51
+ */
52
+ export declare const DeepAgentConnectionContext: import("react").Context<DeepAgentConnectionState | null>;
53
+ export declare function useDeepAgentConnection(): DeepAgentConnectionState | null;