@flamingo-stack/openframe-frontend-core 0.0.457 → 0.0.458-snapshot.20260720215024

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 (290) hide show
  1. package/dist/chat-protocol/decode.d.ts +58 -0
  2. package/dist/chat-protocol/decode.d.ts.map +1 -0
  3. package/dist/chat-protocol/encode.d.ts +44 -0
  4. package/dist/chat-protocol/encode.d.ts.map +1 -0
  5. package/dist/chat-protocol/env-flag.d.ts +35 -0
  6. package/dist/chat-protocol/env-flag.d.ts.map +1 -0
  7. package/dist/chat-protocol/events.d.ts +192 -0
  8. package/dist/chat-protocol/events.d.ts.map +1 -0
  9. package/dist/chat-protocol/frames.d.ts +206 -0
  10. package/dist/chat-protocol/frames.d.ts.map +1 -0
  11. package/dist/chat-protocol/index.cjs +493 -0
  12. package/dist/chat-protocol/index.cjs.map +1 -0
  13. package/dist/chat-protocol/index.d.ts +18 -0
  14. package/dist/chat-protocol/index.d.ts.map +1 -0
  15. package/dist/chat-protocol/index.js +477 -0
  16. package/dist/chat-protocol/index.js.map +1 -0
  17. package/dist/chat-protocol/ip-normalize.d.ts +44 -0
  18. package/dist/chat-protocol/ip-normalize.d.ts.map +1 -0
  19. package/dist/chat-protocol/nats-decoder.d.ts +26 -0
  20. package/dist/chat-protocol/nats-decoder.d.ts.map +1 -0
  21. package/dist/{chunk-SQ3CP2IV.cjs → chunk-3N3TMZ62.cjs} +7 -7
  22. package/dist/{chunk-SQ3CP2IV.cjs.map → chunk-3N3TMZ62.cjs.map} +1 -1
  23. package/dist/{chunk-I7M6KCCS.js → chunk-4R6H2U77.js} +4 -4
  24. package/dist/{chunk-CCT5VCRP.cjs → chunk-4XDWNAZW.cjs} +9 -9
  25. package/dist/{chunk-CCT5VCRP.cjs.map → chunk-4XDWNAZW.cjs.map} +1 -1
  26. package/dist/{chunk-KQ36EHE4.cjs → chunk-7KSEINAE.cjs} +15 -15
  27. package/dist/{chunk-KQ36EHE4.cjs.map → chunk-7KSEINAE.cjs.map} +1 -1
  28. package/dist/{chunk-FM5DTJPA.cjs → chunk-ADD3FPDG.cjs} +6851 -4713
  29. package/dist/chunk-ADD3FPDG.cjs.map +1 -0
  30. package/dist/{chunk-RSW42GTZ.js → chunk-APF5QBAU.js} +2 -2
  31. package/dist/{chunk-YCIVMKRS.cjs → chunk-BLZZXZNM.cjs} +26 -26
  32. package/dist/{chunk-YCIVMKRS.cjs.map → chunk-BLZZXZNM.cjs.map} +1 -1
  33. package/dist/{chunk-YHB4MNUX.cjs → chunk-BQ7PMWLM.cjs} +28 -27
  34. package/dist/chunk-BQ7PMWLM.cjs.map +1 -0
  35. package/dist/{chunk-GJ7BN4MI.js → chunk-CGNDAJ7V.js} +4 -4
  36. package/dist/{chunk-R5IQJXA3.js → chunk-F2NMWV44.js} +2 -2
  37. package/dist/{chunk-EOVQVVDJ.cjs → chunk-F7F43U7Q.cjs} +11 -11
  38. package/dist/{chunk-EOVQVVDJ.cjs.map → chunk-F7F43U7Q.cjs.map} +1 -1
  39. package/dist/{chunk-2ZW2ZPDW.js → chunk-LAOO7XHO.js} +2 -2
  40. package/dist/{chunk-O7F7RZFN.js → chunk-LJKZIIK4.js} +6 -5
  41. package/dist/chunk-LJKZIIK4.js.map +1 -0
  42. package/dist/{chunk-AJGZ3YSR.js → chunk-OR5AMXYN.js} +2 -2
  43. package/dist/{chunk-5XFJG3TC.cjs → chunk-P5JQDFRM.cjs} +60 -60
  44. package/dist/chunk-P5JQDFRM.cjs.map +1 -0
  45. package/dist/{chunk-U4APSAAR.cjs → chunk-PXZ7YQ5V.cjs} +5 -5
  46. package/dist/{chunk-U4APSAAR.cjs.map → chunk-PXZ7YQ5V.cjs.map} +1 -1
  47. package/dist/{chunk-WXDEMTLD.js → chunk-QO2XD3FR.js} +2 -2
  48. package/dist/{chunk-C2TDHJBO.cjs → chunk-QSGZE6AZ.cjs} +37 -37
  49. package/dist/{chunk-C2TDHJBO.cjs.map → chunk-QSGZE6AZ.cjs.map} +1 -1
  50. package/dist/{chunk-NSL5MNS4.js → chunk-REWRUV3K.js} +11644 -9506
  51. package/dist/chunk-REWRUV3K.js.map +1 -0
  52. package/dist/{chunk-BDTQIIJR.cjs → chunk-TA7MFVUS.cjs} +31 -31
  53. package/dist/{chunk-BDTQIIJR.cjs.map → chunk-TA7MFVUS.cjs.map} +1 -1
  54. package/dist/{chunk-4423DHRX.js → chunk-UI7CIUAR.js} +2 -2
  55. package/dist/{chunk-5BKBG653.js → chunk-VXJH5TDN.js} +10 -10
  56. package/dist/chunk-VXJH5TDN.js.map +1 -0
  57. package/dist/{chunk-AB65PWU2.cjs → chunk-WCE66C2Z.cjs} +62 -62
  58. package/dist/{chunk-AB65PWU2.cjs.map → chunk-WCE66C2Z.cjs.map} +1 -1
  59. package/dist/{chunk-KXIIUE75.js → chunk-XF5ROTMQ.js} +2 -2
  60. package/dist/components/case-studies/index.cjs +8 -8
  61. package/dist/components/case-studies/index.js +2 -2
  62. package/dist/components/chart.d.ts +1 -1
  63. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  64. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  65. package/dist/components/chat/hooks/index.d.ts +0 -1
  66. package/dist/components/chat/hooks/index.d.ts.map +1 -1
  67. package/dist/components/chat/hooks/use-chat.d.ts.map +1 -1
  68. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +15 -48
  69. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  70. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +3 -47
  71. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  72. package/dist/components/chat/index.cjs +10 -2
  73. package/dist/components/chat/index.cjs.map +1 -1
  74. package/dist/components/chat/index.d.ts +1 -0
  75. package/dist/components/chat/index.d.ts.map +1 -1
  76. package/dist/components/chat/index.js +21 -13
  77. package/dist/components/chat/stream/chat-dialog-store.d.ts +184 -0
  78. package/dist/components/chat/stream/chat-dialog-store.d.ts.map +1 -0
  79. package/dist/components/chat/stream/chat-stream-reducer.d.ts +367 -0
  80. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -0
  81. package/dist/components/chat/stream/delta-batcher.d.ts +51 -0
  82. package/dist/components/chat/stream/delta-batcher.d.ts.map +1 -0
  83. package/dist/components/chat/stream/index.d.ts +17 -0
  84. package/dist/components/chat/stream/index.d.ts.map +1 -0
  85. package/dist/components/chat/stream/message-mutations.d.ts +77 -0
  86. package/dist/components/chat/stream/message-mutations.d.ts.map +1 -0
  87. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts +22 -0
  88. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts.map +1 -0
  89. package/dist/components/chat/types/api.types.d.ts +1 -81
  90. package/dist/components/chat/types/api.types.d.ts.map +1 -1
  91. package/dist/components/chat/types/processing.types.d.ts +2 -90
  92. package/dist/components/chat/types/processing.types.d.ts.map +1 -1
  93. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts +34 -6
  94. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
  95. package/dist/components/chat/utils/index.d.ts +2 -3
  96. package/dist/components/chat/utils/index.d.ts.map +1 -1
  97. package/dist/components/chat/utils/process-historical-messages.d.ts +59 -11
  98. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  99. package/dist/components/contact/index.cjs +3 -3
  100. package/dist/components/contact/index.js +2 -2
  101. package/dist/components/docs/index.cjs +5 -5
  102. package/dist/components/docs/index.js +4 -4
  103. package/dist/components/embeds/index.cjs +3 -3
  104. package/dist/components/embeds/index.js +2 -2
  105. package/dist/components/faq/index.cjs +3 -3
  106. package/dist/components/faq/index.js +2 -2
  107. package/dist/components/features/index.cjs +2 -2
  108. package/dist/components/features/index.js +1 -1
  109. package/dist/components/features/time-tracker/time-tracker-panel.d.ts.map +1 -1
  110. package/dist/components/help-center-pages/index.cjs +20 -20
  111. package/dist/components/help-center-pages/index.js +11 -11
  112. package/dist/components/index.cjs +171 -129
  113. package/dist/components/index.cjs.map +1 -1
  114. package/dist/components/index.js +63 -21
  115. package/dist/components/index.js.map +1 -1
  116. package/dist/components/layout/page-container.d.ts.map +1 -1
  117. package/dist/components/navigation/index.cjs +2 -2
  118. package/dist/components/navigation/index.js +1 -1
  119. package/dist/components/onboarding-guides/index.cjs +5 -5
  120. package/dist/components/onboarding-guides/index.js +4 -4
  121. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
  122. package/dist/components/related-content/index.cjs +3 -3
  123. package/dist/components/related-content/index.js +2 -2
  124. package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
  125. package/dist/components/tickets/index.cjs +5 -5
  126. package/dist/components/tickets/index.js +4 -4
  127. package/dist/components/ui/index.cjs +44 -2
  128. package/dist/components/ui/index.cjs.map +1 -1
  129. package/dist/components/ui/index.d.ts +1 -2
  130. package/dist/components/ui/index.d.ts.map +1 -1
  131. package/dist/components/ui/index.js +55 -13
  132. package/dist/components/ui/markdown/base-components.d.ts +56 -0
  133. package/dist/components/ui/markdown/base-components.d.ts.map +1 -0
  134. package/dist/components/ui/markdown/engine.d.ts +99 -0
  135. package/dist/components/ui/markdown/engine.d.ts.map +1 -0
  136. package/dist/components/ui/markdown/heading-ids.d.ts +65 -0
  137. package/dist/components/ui/markdown/heading-ids.d.ts.map +1 -0
  138. package/dist/components/ui/markdown/index.d.ts +18 -0
  139. package/dist/components/ui/markdown/index.d.ts.map +1 -0
  140. package/dist/components/ui/markdown/mermaid-diagram.d.ts +79 -0
  141. package/dist/components/ui/markdown/mermaid-diagram.d.ts.map +1 -0
  142. package/dist/components/ui/markdown/rich/embed-overrides.d.ts +14 -0
  143. package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +1 -0
  144. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts +40 -0
  145. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts.map +1 -0
  146. package/dist/components/ui/markdown/rich/shortcodes.d.ts +8 -0
  147. package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +1 -0
  148. package/dist/components/ui/markdown/sanitize.d.ts +153 -0
  149. package/dist/components/ui/markdown/sanitize.d.ts.map +1 -0
  150. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts +23 -0
  151. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts.map +1 -0
  152. package/dist/components/ui/markdown/streaming.d.ts +78 -0
  153. package/dist/components/ui/markdown/streaming.d.ts.map +1 -0
  154. package/dist/components/ui/markdown/text-size.d.ts +34 -0
  155. package/dist/components/ui/markdown/text-size.d.ts.map +1 -0
  156. package/dist/components/ui/modal-v2.d.ts.map +1 -1
  157. package/dist/components/ui/modal.d.ts.map +1 -1
  158. package/dist/index.cjs +60 -2
  159. package/dist/index.cjs.map +1 -1
  160. package/dist/index.js +71 -13
  161. package/dist/utils/index.cjs +169 -40
  162. package/dist/utils/index.cjs.map +1 -1
  163. package/dist/utils/index.d.ts +1 -0
  164. package/dist/utils/index.d.ts.map +1 -1
  165. package/dist/utils/index.js +162 -41
  166. package/dist/utils/index.js.map +1 -1
  167. package/dist/utils/markdown-fences.d.ts +42 -0
  168. package/dist/utils/markdown-fences.d.ts.map +1 -0
  169. package/dist/utils/markdown-heading-id.d.ts +85 -0
  170. package/dist/utils/markdown-heading-id.d.ts.map +1 -0
  171. package/dist/utils/markdown-section-extractor.d.ts.map +1 -1
  172. package/package.json +7 -1
  173. package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +307 -0
  174. package/src/chat-protocol/__tests__/chat-protocol.test.ts +515 -0
  175. package/src/chat-protocol/__tests__/env-flag.test.ts +52 -0
  176. package/src/chat-protocol/__tests__/ip-normalize.test.ts +136 -0
  177. package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +228 -0
  178. package/src/chat-protocol/decode.ts +277 -0
  179. package/src/chat-protocol/encode.ts +71 -0
  180. package/src/chat-protocol/env-flag.ts +39 -0
  181. package/src/chat-protocol/events.ts +228 -0
  182. package/src/chat-protocol/frames.ts +238 -0
  183. package/src/chat-protocol/index.ts +21 -0
  184. package/src/chat-protocol/ip-normalize.ts +146 -0
  185. package/src/chat-protocol/nats-decoder.ts +252 -0
  186. package/src/components/chat/__tests__/chat-message-list.test.tsx +293 -5
  187. package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +207 -0
  188. package/src/components/chat/__tests__/chat-pending-turn.test.tsx +209 -0
  189. package/src/components/chat/chat-message-enhanced.tsx +111 -17
  190. package/src/components/chat/chat-message-list.tsx +367 -113
  191. package/src/components/chat/embeddable-chat.tsx +6 -0
  192. package/src/components/chat/hooks/.index.md +1 -1
  193. package/src/components/chat/hooks/.use-nats-chat-adapter.md +1 -1
  194. package/src/components/chat/hooks/__tests__/__snapshots__/persisted-state-v1-golden.test.ts.snap +297 -0
  195. package/src/components/chat/hooks/__tests__/__snapshots__/sse-stream-golden.test.ts.snap +0 -0
  196. package/src/components/chat/hooks/__tests__/persisted-state-v1-golden.test.ts +272 -0
  197. package/src/components/chat/hooks/__tests__/sse-stream-golden.test.ts +318 -0
  198. package/src/components/chat/hooks/index.ts +0 -1
  199. package/src/components/chat/hooks/use-chat.ts +8 -0
  200. package/src/components/chat/hooks/use-nats-chat-adapter.ts +243 -822
  201. package/src/components/chat/hooks/use-sse-chat-adapter.ts +318 -666
  202. package/src/components/chat/index.ts +5 -0
  203. package/src/components/chat/stream/__tests__/__snapshots__/chat-stream-reducer-golden.test.ts.snap +937 -0
  204. package/src/components/chat/stream/__tests__/chat-dialog-store.test.ts +756 -0
  205. package/src/components/chat/stream/__tests__/chat-stream-reducer-golden.test.ts +460 -0
  206. package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +894 -0
  207. package/src/components/chat/stream/__tests__/delta-batcher.test.ts +255 -0
  208. package/src/components/chat/stream/__tests__/use-chat-stream-reducer.test.ts +98 -0
  209. package/src/components/chat/stream/chat-dialog-store.ts +536 -0
  210. package/src/components/chat/stream/chat-stream-reducer.ts +1796 -0
  211. package/src/components/chat/stream/delta-batcher.ts +159 -0
  212. package/src/components/chat/stream/index.ts +55 -0
  213. package/src/components/chat/stream/message-mutations.ts +338 -0
  214. package/src/components/chat/stream/use-chat-stream-reducer.ts +126 -0
  215. package/src/components/chat/thinking-display.tsx +1 -1
  216. package/src/components/chat/types/.api.types.md +1 -3
  217. package/src/components/chat/types/.processing.types.md +6 -23
  218. package/src/components/chat/types/api.types.ts +6 -73
  219. package/src/components/chat/types/processing.types.ts +11 -52
  220. package/src/components/chat/utils/.index.md +0 -5
  221. package/src/components/chat/utils/__tests__/__snapshots__/process-historical-messages-golden.test.ts.snap +418 -0
  222. package/src/components/chat/utils/__tests__/__snapshots__/segment-accumulator-golden.test.ts.snap +599 -0
  223. package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +317 -0
  224. package/src/components/chat/utils/__tests__/segment-accumulator-golden.test.ts +270 -0
  225. package/src/components/chat/utils/extract-incomplete-message-state.ts +63 -5
  226. package/src/components/chat/utils/index.ts +5 -8
  227. package/src/components/chat/utils/process-historical-messages.ts +352 -383
  228. package/src/components/features/notifications/notification-drawer.tsx +4 -2
  229. package/src/components/features/time-tracker/time-tracker-panel.tsx +8 -2
  230. package/src/components/layout/page-container.tsx +5 -1
  231. package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +22 -4
  232. package/src/components/shared/legal-document/legal-document-page.tsx +1 -1
  233. package/src/components/shared/product-release/release-detail-page.tsx +19 -4
  234. package/src/components/ui/__tests__/__snapshots__/markdown-parity.test.tsx.snap +2420 -0
  235. package/src/components/ui/__tests__/markdown-parity.test.tsx +2528 -0
  236. package/src/components/ui/index.ts +1 -2
  237. package/src/components/ui/markdown/__tests__/mermaid-security.test.ts +115 -0
  238. package/src/components/ui/markdown/__tests__/mermaid-stale-render.test.tsx +95 -0
  239. package/src/components/ui/markdown/__tests__/sanitize-invariant.test.ts +309 -0
  240. package/src/components/ui/markdown/__tests__/streaming.test.tsx +388 -0
  241. package/src/components/ui/markdown/base-components.tsx +341 -0
  242. package/src/components/ui/markdown/engine.tsx +315 -0
  243. package/src/components/ui/markdown/heading-ids.ts +239 -0
  244. package/src/components/ui/markdown/index.ts +49 -0
  245. package/src/components/ui/markdown/mermaid-diagram.tsx +291 -0
  246. package/src/components/ui/markdown/rich/embed-overrides.tsx +199 -0
  247. package/src/components/ui/markdown/rich/rich-markdown-renderer.tsx +184 -0
  248. package/src/components/ui/markdown/rich/shortcodes.ts +170 -0
  249. package/src/components/ui/markdown/sanitize.ts +3499 -0
  250. package/src/components/ui/markdown/simple-markdown-renderer.tsx +28 -0
  251. package/src/components/ui/markdown/streaming.ts +362 -0
  252. package/src/components/ui/markdown/text-size.ts +106 -0
  253. package/src/components/ui/modal-v2.tsx +6 -0
  254. package/src/components/ui/modal.tsx +3 -0
  255. package/src/components/ui/release-changelog-section.tsx +1 -1
  256. package/src/components/ui/ticket-info-section.tsx +1 -1
  257. package/src/utils/index.ts +1 -0
  258. package/src/utils/markdown-fences.ts +127 -0
  259. package/src/utils/markdown-heading-id.ts +348 -0
  260. package/src/utils/markdown-section-extractor.ts +41 -56
  261. package/dist/chunk-5BKBG653.js.map +0 -1
  262. package/dist/chunk-5XFJG3TC.cjs.map +0 -1
  263. package/dist/chunk-FM5DTJPA.cjs.map +0 -1
  264. package/dist/chunk-NSL5MNS4.js.map +0 -1
  265. package/dist/chunk-O7F7RZFN.js.map +0 -1
  266. package/dist/chunk-YHB4MNUX.cjs.map +0 -1
  267. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts +0 -6
  268. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +0 -1
  269. package/dist/components/chat/utils/chunk-parser.d.ts +0 -25
  270. package/dist/components/chat/utils/chunk-parser.d.ts.map +0 -1
  271. package/dist/components/ui/rich-markdown-renderer.d.ts +0 -34
  272. package/dist/components/ui/rich-markdown-renderer.d.ts.map +0 -1
  273. package/dist/components/ui/simple-markdown-renderer.d.ts +0 -73
  274. package/dist/components/ui/simple-markdown-renderer.d.ts.map +0 -1
  275. package/src/components/chat/hooks/.use-realtime-chunk-processor.md +0 -50
  276. package/src/components/chat/hooks/use-realtime-chunk-processor.ts +0 -479
  277. package/src/components/chat/utils/.chunk-parser.md +0 -51
  278. package/src/components/chat/utils/chunk-parser.ts +0 -255
  279. package/src/components/ui/.simple-markdown-renderer.md +0 -40
  280. package/src/components/ui/rich-markdown-renderer.tsx +0 -1223
  281. package/src/components/ui/simple-markdown-renderer.tsx +0 -965
  282. /package/dist/{chunk-I7M6KCCS.js.map → chunk-4R6H2U77.js.map} +0 -0
  283. /package/dist/{chunk-RSW42GTZ.js.map → chunk-APF5QBAU.js.map} +0 -0
  284. /package/dist/{chunk-GJ7BN4MI.js.map → chunk-CGNDAJ7V.js.map} +0 -0
  285. /package/dist/{chunk-R5IQJXA3.js.map → chunk-F2NMWV44.js.map} +0 -0
  286. /package/dist/{chunk-2ZW2ZPDW.js.map → chunk-LAOO7XHO.js.map} +0 -0
  287. /package/dist/{chunk-AJGZ3YSR.js.map → chunk-OR5AMXYN.js.map} +0 -0
  288. /package/dist/{chunk-WXDEMTLD.js.map → chunk-QO2XD3FR.js.map} +0 -0
  289. /package/dist/{chunk-4423DHRX.js.map → chunk-UI7CIUAR.js.map} +0 -0
  290. /package/dist/{chunk-KXIIUE75.js.map → chunk-XF5ROTMQ.js.map} +0 -0
@@ -0,0 +1,297 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`PersistedChatState v1 — golden rehydration (migration contract) > rehydration round-trips: the persist callback re-saves an equivalent v1 blob 1`] = `
4
+ {
5
+ "messages": [
6
+ {
7
+ "content": "open a ticket for the broken printer",
8
+ "id": "user-1752915600000",
9
+ "name": "You",
10
+ "role": "user",
11
+ "timestamp": "2026-07-19T09:00:00.000Z",
12
+ },
13
+ {
14
+ "content": [
15
+ {
16
+ "text": "I'll open a ticket for you.",
17
+ "type": "text",
18
+ },
19
+ {
20
+ "data": {
21
+ "approvalType": "create_ticket",
22
+ "command": "Create ticket",
23
+ "fields": [
24
+ {
25
+ "label": "Subject",
26
+ "value": "Broken printer",
27
+ },
28
+ {
29
+ "label": "Priority",
30
+ "value": "High",
31
+ },
32
+ ],
33
+ "requestId": "prop-1",
34
+ },
35
+ "status": "approved",
36
+ "type": "approval_request",
37
+ },
38
+ ],
39
+ "id": "assistant-1752915600000",
40
+ "name": "Mingo AI",
41
+ "role": "assistant",
42
+ "timestamp": "2026-07-19T09:00:05.000Z",
43
+ },
44
+ {
45
+ "content": "",
46
+ "hidden": true,
47
+ "id": "user-1752915610000",
48
+ "name": "You",
49
+ "role": "user",
50
+ "timestamp": "2026-07-19T09:00:10.000Z",
51
+ },
52
+ {
53
+ "chatRefs": {
54
+ "ticket:77": {
55
+ "id": "77",
56
+ "metadata": {
57
+ "status": "OPEN",
58
+ },
59
+ "title": "Broken printer",
60
+ "type": "ticket",
61
+ "url": null,
62
+ },
63
+ },
64
+ "content": [
65
+ {
66
+ "text": "✅ Approved — ticket created: [card://ticket:77]
67
+
68
+ Now, to triage faster: which floor is the printer on?",
69
+ "type": "text",
70
+ },
71
+ ],
72
+ "id": "assistant-1752915610000",
73
+ "name": "Mingo AI",
74
+ "role": "assistant",
75
+ "timestamp": "2026-07-19T09:00:12.000Z",
76
+ },
77
+ {
78
+ "content": "[internal-auto-continuation] The user just approved create_ticket (ticket #77). Ask diagnostic follow-ups.",
79
+ "hidden": true,
80
+ "id": "user-1752915612000",
81
+ "name": "You",
82
+ "role": "user",
83
+ "timestamp": "2026-07-19T09:00:12.500Z",
84
+ },
85
+ {
86
+ "content": [
87
+ {
88
+ "text": "Also: is the printer showing any error code on its display?",
89
+ "type": "text",
90
+ },
91
+ ],
92
+ "id": "assistant-1752915613000",
93
+ "name": "Mingo AI",
94
+ "role": "assistant",
95
+ "timestamp": "2026-07-19T09:00:13.000Z",
96
+ },
97
+ {
98
+ "content": "what is the pricing?",
99
+ "id": "user-1752915700000",
100
+ "name": "You",
101
+ "role": "user",
102
+ "timestamp": "2026-07-19T09:01:40.000Z",
103
+ },
104
+ {
105
+ "content": [
106
+ {
107
+ "text": "Checking pricing docs…",
108
+ "type": "thinking",
109
+ },
110
+ {
111
+ "text": "Pricing starts at $99/month per site.",
112
+ "type": "text",
113
+ },
114
+ ],
115
+ "id": "assistant-1752915700000",
116
+ "name": "Mingo AI",
117
+ "role": "assistant",
118
+ "timestamp": "2026-07-19T09:01:45.000Z",
119
+ },
120
+ ],
121
+ "refs": [
122
+ [
123
+ 0,
124
+ {
125
+ "ticket:77": {
126
+ "id": "77",
127
+ "metadata": {
128
+ "status": "OPEN",
129
+ },
130
+ "title": "Broken printer",
131
+ "type": "ticket",
132
+ "url": null,
133
+ },
134
+ },
135
+ ],
136
+ ],
137
+ "sendCount": 2,
138
+ "sources": [
139
+ [
140
+ 1,
141
+ [
142
+ {
143
+ "documentType": "markdown",
144
+ "index": 1,
145
+ "name": "Pricing overview",
146
+ "path": "docs/pricing.md",
147
+ "sourceRepo": "markdown",
148
+ },
149
+ ],
150
+ ],
151
+ ],
152
+ }
153
+ `;
154
+
155
+ exports[`PersistedChatState v1 — golden rehydration (migration contract) > restores messages / refs / sources from a hand-crafted v1 blob (snapshot) 1`] = `
156
+ {
157
+ "isLoading": false,
158
+ "messages": [
159
+ {
160
+ "content": "open a ticket for the broken printer",
161
+ "id": "user-1752915600000",
162
+ "role": "user",
163
+ },
164
+ {
165
+ "chatRefs": {
166
+ "ticket:77": {
167
+ "id": "77",
168
+ "metadata": {
169
+ "status": "OPEN",
170
+ },
171
+ "title": "Broken printer",
172
+ "type": "ticket",
173
+ "url": null,
174
+ },
175
+ },
176
+ "content": "I'll open a ticket for you.",
177
+ "id": "assistant-1752915600000",
178
+ "role": "assistant",
179
+ "segments": [
180
+ {
181
+ "text": "I'll open a ticket for you.",
182
+ "type": "text",
183
+ },
184
+ {
185
+ "data": {
186
+ "approvalType": "create_ticket",
187
+ "command": "Create ticket",
188
+ "fields": [
189
+ {
190
+ "label": "Subject",
191
+ "value": "Broken printer",
192
+ },
193
+ {
194
+ "label": "Priority",
195
+ "value": "High",
196
+ },
197
+ ],
198
+ "requestId": "prop-1",
199
+ },
200
+ "status": "approved",
201
+ "type": "approval_request",
202
+ },
203
+ ],
204
+ },
205
+ {
206
+ "content": "",
207
+ "hidden": true,
208
+ "id": "user-1752915610000",
209
+ "role": "user",
210
+ },
211
+ {
212
+ "chatRefs": {
213
+ "ticket:77": {
214
+ "id": "77",
215
+ "metadata": {
216
+ "status": "OPEN",
217
+ },
218
+ "title": "Broken printer",
219
+ "type": "ticket",
220
+ "url": null,
221
+ },
222
+ },
223
+ "content": "✅ Approved — ticket created: [card://ticket:77]
224
+
225
+ Now, to triage faster: which floor is the printer on?",
226
+ "id": "assistant-1752915610000",
227
+ "role": "assistant",
228
+ "segments": [
229
+ {
230
+ "text": "✅ Approved — ticket created: [card://ticket:77]
231
+
232
+ Now, to triage faster: which floor is the printer on?",
233
+ "type": "text",
234
+ },
235
+ ],
236
+ },
237
+ {
238
+ "content": "[internal-auto-continuation] The user just approved create_ticket (ticket #77). Ask diagnostic follow-ups.",
239
+ "hidden": true,
240
+ "id": "user-1752915612000",
241
+ "role": "user",
242
+ },
243
+ {
244
+ "chatRefs": {
245
+ "ticket:77": {
246
+ "id": "77",
247
+ "metadata": {
248
+ "status": "OPEN",
249
+ },
250
+ "title": "Broken printer",
251
+ "type": "ticket",
252
+ "url": null,
253
+ },
254
+ },
255
+ "content": "Also: is the printer showing any error code on its display?",
256
+ "id": "assistant-1752915613000",
257
+ "role": "assistant",
258
+ "segments": [
259
+ {
260
+ "text": "Also: is the printer showing any error code on its display?",
261
+ "type": "text",
262
+ },
263
+ ],
264
+ },
265
+ {
266
+ "content": "what is the pricing?",
267
+ "id": "user-1752915700000",
268
+ "role": "user",
269
+ },
270
+ {
271
+ "content": "Pricing starts at $99/month per site.",
272
+ "id": "assistant-1752915700000",
273
+ "role": "assistant",
274
+ "segments": [
275
+ {
276
+ "text": "Checking pricing docs…",
277
+ "type": "thinking",
278
+ },
279
+ {
280
+ "text": "Pricing starts at $99/month per site.",
281
+ "type": "text",
282
+ },
283
+ ],
284
+ "sources": [
285
+ {
286
+ "documentType": "markdown",
287
+ "index": 1,
288
+ "name": "Pricing overview",
289
+ "path": "docs/pricing.md",
290
+ "sourceRepo": "markdown",
291
+ },
292
+ ],
293
+ },
294
+ ],
295
+ "streamingPhase": "idle",
296
+ }
297
+ `;
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Phase-0 GOLDEN CHARACTERIZATION TESTS — PersistedChatState v1.
3
+ *
4
+ * The SSE chat adapter persists its conversation under
5
+ * `mingo-chat-<source>-v1` (see `use-sse-chat-adapter.ts`:
6
+ * chatStorageKey / loadPersistedChat / savePersistedChat). This file
7
+ * seeds localStorage with a hand-crafted, REALISTIC v1 blob and pins the
8
+ * exact rehydrated state the hook exposes. This blob is the MIGRATION
9
+ * CONTRACT for the future unified reducer: whatever replaces the three
10
+ * stream-reading layers must restore this v1 shape identically (or
11
+ * migrate it losslessly).
12
+ *
13
+ * Blob contents:
14
+ * - 2 visible user sends (sendCount: 2)
15
+ * - 4 assistant messages — send #0 fans out to main reply (approval
16
+ * card) + post-approve receipt + continuation bubble (with a hidden
17
+ * approval-action user turn in between); send #1 is a plain RAG answer
18
+ * - sendIdx-keyed refs map with a card ref (ticket:77 on send #0)
19
+ * - sendIdx-keyed sources map (pricing source on send #1)
20
+ * - a NON-hidden auto-continuation directive bubble that the loader's
21
+ * forward-migration must flip to hidden:true
22
+ */
23
+
24
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
25
+ import { renderHook } from '@testing-library/react'
26
+ import { createElement, type ReactNode } from 'react'
27
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
28
+ import { useSseChatAdapter } from '../use-sse-chat-adapter'
29
+ import { ChatRuntimeContext, type ChatRuntime } from '../../../../contexts/chat-runtime-context'
30
+ import { AUTO_CONTINUATION_DIRECTIVE_PREFIX } from '../../utils/auto-continuation-directive'
31
+
32
+ const SOURCE = 'goldensrc'
33
+ const STORAGE_KEY = `mingo-chat-${SOURCE}-v1`
34
+
35
+ const runtime: ChatRuntime = {
36
+ endpoints: {
37
+ chatStreamUrl: '/api/docs/chat',
38
+ approvalToolUrl: '/api/chat/agent/confirm-tool',
39
+ commandsUrl: '/api/docs/commands',
40
+ buildListUrl: () => null,
41
+ attachmentUploadUrl: '/api/storage/generate-upload-url',
42
+ attachmentViewUrlPrefix: '/api/storage/view/chat-attachments/',
43
+ identityUrl: '/api/chat/identity',
44
+ },
45
+ navigation: { mode: 'host' },
46
+ source: SOURCE,
47
+ }
48
+
49
+ function wrapper({ children }: { children: ReactNode }) {
50
+ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
51
+ return createElement(
52
+ QueryClientProvider,
53
+ { client: queryClient },
54
+ createElement(ChatRuntimeContext.Provider, { value: runtime }, children),
55
+ )
56
+ }
57
+
58
+ /**
59
+ * Hand-crafted v1 blob — REALISTIC shapes recorded from the live flow.
60
+ * Timestamps are ISO strings on disk (loadPersistedChat revives them to
61
+ * Date objects in place).
62
+ */
63
+ const V1_BLOB = {
64
+ messages: [
65
+ // ── send #0: "open a ticket" ────────────────────────────────────
66
+ {
67
+ id: 'user-1752915600000',
68
+ role: 'user',
69
+ name: 'You',
70
+ content: 'open a ticket for the broken printer',
71
+ timestamp: '2026-07-19T09:00:00.000Z',
72
+ },
73
+ {
74
+ // Main reply: preamble text + (resolved) approval card.
75
+ id: 'assistant-1752915600000',
76
+ role: 'assistant',
77
+ name: 'Mingo AI',
78
+ content: [
79
+ { type: 'text', text: "I'll open a ticket for you." },
80
+ {
81
+ type: 'approval_request',
82
+ data: {
83
+ command: 'Create ticket',
84
+ fields: [
85
+ { label: 'Subject', value: 'Broken printer' },
86
+ { label: 'Priority', value: 'High' },
87
+ ],
88
+ requestId: 'prop-1',
89
+ approvalType: 'create_ticket',
90
+ },
91
+ status: 'approved',
92
+ },
93
+ ],
94
+ timestamp: '2026-07-19T09:00:05.000Z',
95
+ },
96
+ {
97
+ // Hidden approval-action turn (content '' — the Approve click).
98
+ id: 'user-1752915610000',
99
+ role: 'user',
100
+ name: 'You',
101
+ content: '',
102
+ hidden: true,
103
+ timestamp: '2026-07-19T09:00:10.000Z',
104
+ },
105
+ {
106
+ // Post-approve receipt + card marker; chatRefs stamped on-message.
107
+ id: 'assistant-1752915610000',
108
+ role: 'assistant',
109
+ name: 'Mingo AI',
110
+ content: [
111
+ {
112
+ type: 'text',
113
+ text: '✅ Approved — ticket created: [card://ticket:77]\n\nNow, to triage faster: which floor is the printer on?',
114
+ },
115
+ ],
116
+ chatRefs: {
117
+ 'ticket:77': {
118
+ type: 'ticket',
119
+ id: '77',
120
+ title: 'Broken printer',
121
+ url: null,
122
+ metadata: { status: 'OPEN' },
123
+ },
124
+ },
125
+ timestamp: '2026-07-19T09:00:12.000Z',
126
+ },
127
+ {
128
+ // Auto-continuation directive bubble persisted by an OLD lib version
129
+ // WITHOUT hidden:true — the loader's forward-migration must flip it.
130
+ id: 'user-1752915612000',
131
+ role: 'user',
132
+ name: 'You',
133
+ content: `${AUTO_CONTINUATION_DIRECTIVE_PREFIX} The user just approved create_ticket (ticket #77). Ask diagnostic follow-ups.`,
134
+ timestamp: '2026-07-19T09:00:12.500Z',
135
+ },
136
+ {
137
+ // Continuation prose for send #0 (third assistant message).
138
+ id: 'assistant-1752915613000',
139
+ role: 'assistant',
140
+ name: 'Mingo AI',
141
+ content: [{ type: 'text', text: 'Also: is the printer showing any error code on its display?' }],
142
+ timestamp: '2026-07-19T09:00:13.000Z',
143
+ },
144
+ // ── send #1: plain RAG answer ───────────────────────────────────
145
+ {
146
+ id: 'user-1752915700000',
147
+ role: 'user',
148
+ name: 'You',
149
+ content: 'what is the pricing?',
150
+ timestamp: '2026-07-19T09:01:40.000Z',
151
+ },
152
+ {
153
+ id: 'assistant-1752915700000',
154
+ role: 'assistant',
155
+ name: 'Mingo AI',
156
+ content: [
157
+ { type: 'thinking', text: 'Checking pricing docs…' },
158
+ { type: 'text', text: 'Pricing starts at $99/month per site.' },
159
+ ],
160
+ timestamp: '2026-07-19T09:01:45.000Z',
161
+ },
162
+ ],
163
+ // sendIdx-keyed maps (entries arrays, as serialized by savePersistedChat).
164
+ sources: [
165
+ [
166
+ 1,
167
+ [
168
+ {
169
+ index: 1,
170
+ name: 'Pricing overview',
171
+ path: 'docs/pricing.md',
172
+ documentType: 'markdown',
173
+ sourceRepo: 'markdown',
174
+ },
175
+ ],
176
+ ],
177
+ ],
178
+ refs: [
179
+ [
180
+ 0,
181
+ {
182
+ 'ticket:77': {
183
+ type: 'ticket',
184
+ id: '77',
185
+ title: 'Broken printer',
186
+ url: null,
187
+ metadata: { status: 'OPEN' },
188
+ },
189
+ },
190
+ ],
191
+ ],
192
+ sendCount: 2,
193
+ }
194
+
195
+ beforeEach(() => {
196
+ window.localStorage.clear()
197
+ vi.spyOn(Date, 'now').mockReturnValue(1_700_000_000_000)
198
+ })
199
+
200
+ afterEach(() => {
201
+ vi.restoreAllMocks()
202
+ })
203
+
204
+ describe('PersistedChatState v1 — golden rehydration (migration contract)', () => {
205
+ it('restores messages / refs / sources from a hand-crafted v1 blob (snapshot)', () => {
206
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(V1_BLOB))
207
+
208
+ const { result } = renderHook(() => useSseChatAdapter(undefined, { active: false }), {
209
+ wrapper,
210
+ })
211
+
212
+ // Pin the FULL rehydrated public state:
213
+ // - hidden approval-action turn + migrated directive bubble carry hidden:true
214
+ // - all THREE assistant messages of send #0 resolve chatRefs['ticket:77']
215
+ // (message-bound copy on the receipt, refs-map fallback for the others)
216
+ // - send #1's assistant message carries the sources array
217
+ expect({
218
+ messages: result.current.messages,
219
+ streamingPhase: result.current.streamingPhase,
220
+ isLoading: result.current.isLoading,
221
+ }).toMatchSnapshot()
222
+ })
223
+
224
+ it('forward-migration flips a non-hidden auto-continuation directive bubble to hidden', () => {
225
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(V1_BLOB))
226
+ const { result } = renderHook(() => useSseChatAdapter(undefined, { active: false }), {
227
+ wrapper,
228
+ })
229
+ const directive = result.current.messages.find((m) =>
230
+ m.content.startsWith(AUTO_CONTINUATION_DIRECTIVE_PREFIX),
231
+ )
232
+ expect(directive?.hidden).toBe(true)
233
+ })
234
+
235
+ it('hidden user turns do NOT advance the sendIdx used for sources/refs lookup', () => {
236
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(V1_BLOB))
237
+ const { result } = renderHook(() => useSseChatAdapter(undefined, { active: false }), {
238
+ wrapper,
239
+ })
240
+ const assistants = result.current.messages.filter((m) => m.role === 'assistant')
241
+ // Send #0 fans out into THREE assistant bubbles (main reply +
242
+ // post-approve receipt + continuation); send #1 adds a fourth.
243
+ expect(assistants).toHaveLength(4)
244
+ // All send-#0 assistant bubbles resolve the ticket ref…
245
+ expect(assistants[0].chatRefs?.['ticket:77']?.id).toBe('77')
246
+ expect(assistants[1].chatRefs?.['ticket:77']?.id).toBe('77')
247
+ expect(assistants[2].chatRefs?.['ticket:77']?.id).toBe('77')
248
+ // …and the send-#1 bubble gets the pricing sources instead.
249
+ expect(assistants[3].sources?.[0]?.name).toBe('Pricing overview')
250
+ expect(assistants[3].chatRefs).toBeUndefined()
251
+ })
252
+
253
+ it('a corrupt blob falls back to an empty conversation', () => {
254
+ window.localStorage.setItem(STORAGE_KEY, '{not json')
255
+ const { result } = renderHook(() => useSseChatAdapter(undefined, { active: false }), {
256
+ wrapper,
257
+ })
258
+ expect(result.current.messages).toEqual([])
259
+ })
260
+
261
+ it('rehydration round-trips: the persist callback re-saves an equivalent v1 blob', () => {
262
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(V1_BLOB))
263
+ renderHook(() => useSseChatAdapter(undefined, { active: false }), { wrapper })
264
+
265
+ // Mounting fires onMessagesChange → savePersistedChat with the loaded
266
+ // state (timestamps re-serialized to ISO strings, directive bubble now
267
+ // hidden:true). Pin the re-saved wire shape — THIS is the exact v1 blob
268
+ // a future reducer will find on disk.
269
+ const resaved = JSON.parse(window.localStorage.getItem(STORAGE_KEY)!)
270
+ expect(resaved).toMatchSnapshot()
271
+ })
272
+ })