@flamingo-stack/openframe-frontend-core 0.0.508 → 0.0.509

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 (306) 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 +216 -0
  8. package/dist/chat-protocol/events.d.ts.map +1 -0
  9. package/dist/chat-protocol/frames.d.ts +213 -0
  10. package/dist/chat-protocol/frames.d.ts.map +1 -0
  11. package/dist/chat-protocol/index.cjs +506 -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 +490 -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-3SQ5KXHQ.cjs → chunk-2T4GTV25.cjs} +9 -9
  22. package/dist/{chunk-3SQ5KXHQ.cjs.map → chunk-2T4GTV25.cjs.map} +1 -1
  23. package/dist/{chunk-CWAOV2FK.cjs → chunk-3CGZPAGR.cjs} +3 -3
  24. package/dist/{chunk-CWAOV2FK.cjs.map → chunk-3CGZPAGR.cjs.map} +1 -1
  25. package/dist/{chunk-TLMJHMXJ.js → chunk-3QSR22IC.js} +12 -12
  26. package/dist/chunk-3QSR22IC.js.map +1 -0
  27. package/dist/{chunk-TWYNR4TZ.cjs → chunk-4G5TLHJD.cjs} +7141 -4799
  28. package/dist/chunk-4G5TLHJD.cjs.map +1 -0
  29. package/dist/{chunk-ADPMHWOE.js → chunk-72XAID7Y.js} +4 -4
  30. package/dist/{chunk-PAGKRNWK.js → chunk-7WZHBQ4J.js} +2 -2
  31. package/dist/{chunk-OCEKO5CW.js → chunk-AEELJC4N.js} +12106 -9764
  32. package/dist/chunk-AEELJC4N.js.map +1 -0
  33. package/dist/{chunk-Z42BGM6Q.cjs → chunk-BT2WWDFP.cjs} +5 -5
  34. package/dist/{chunk-Z42BGM6Q.cjs.map → chunk-BT2WWDFP.cjs.map} +1 -1
  35. package/dist/{chunk-7LEL3RIX.cjs → chunk-C7OY3PQP.cjs} +11 -11
  36. package/dist/{chunk-7LEL3RIX.cjs.map → chunk-C7OY3PQP.cjs.map} +1 -1
  37. package/dist/{chunk-RHGGEKPQ.cjs → chunk-CIOLOQKQ.cjs} +4 -4
  38. package/dist/{chunk-RHGGEKPQ.cjs.map → chunk-CIOLOQKQ.cjs.map} +1 -1
  39. package/dist/{chunk-TSZHM74B.js → chunk-DC2TKS7C.js} +2 -2
  40. package/dist/{chunk-M2GOR3XQ.cjs → chunk-DYPYR7FR.cjs} +87 -87
  41. package/dist/{chunk-M2GOR3XQ.cjs.map → chunk-DYPYR7FR.cjs.map} +1 -1
  42. package/dist/{chunk-I64ABCDX.js → chunk-I2B6X77L.js} +2 -2
  43. package/dist/{chunk-NIZDKTGL.cjs → chunk-K5MSCMDK.cjs} +37 -37
  44. package/dist/{chunk-NIZDKTGL.cjs.map → chunk-K5MSCMDK.cjs.map} +1 -1
  45. package/dist/{chunk-DTYRYB2N.js → chunk-K6QTZ7CZ.js} +2 -2
  46. package/dist/{chunk-4LTMXDUS.js → chunk-KBN7PMFT.js} +2 -2
  47. package/dist/{chunk-TBMV7I5N.js → chunk-KXZO2WZX.js} +2 -2
  48. package/dist/{chunk-QTYZMP6D.cjs → chunk-LBAKLT6T.cjs} +62 -62
  49. package/dist/chunk-LBAKLT6T.cjs.map +1 -0
  50. package/dist/{chunk-KEBYLU3U.js → chunk-LFUPI7VX.js} +2 -2
  51. package/dist/{chunk-LDTR4IVZ.cjs → chunk-M37PBG4N.cjs} +31 -31
  52. package/dist/{chunk-LDTR4IVZ.cjs.map → chunk-M37PBG4N.cjs.map} +1 -1
  53. package/dist/{chunk-WA7RR64F.js → chunk-PO5O654H.js} +6 -5
  54. package/dist/chunk-PO5O654H.js.map +1 -0
  55. package/dist/{chunk-WK4N5VBX.cjs → chunk-QJJISLPA.cjs} +28 -27
  56. package/dist/chunk-QJJISLPA.cjs.map +1 -0
  57. package/dist/{chunk-2F44PLTV.cjs → chunk-T7MCSA55.cjs} +26 -26
  58. package/dist/{chunk-2F44PLTV.cjs.map → chunk-T7MCSA55.cjs.map} +1 -1
  59. package/dist/{chunk-WJQHJD7J.js → chunk-TWSKTJHW.js} +2 -2
  60. package/dist/{chunk-ZSQHZYCO.cjs → chunk-UQDMJ6DH.cjs} +13 -13
  61. package/dist/{chunk-ZSQHZYCO.cjs.map → chunk-UQDMJ6DH.cjs.map} +1 -1
  62. package/dist/{chunk-TZRUCD56.js → chunk-V3ZAAFE3.js} +5 -5
  63. package/dist/{chunk-LRGHJPET.js → chunk-XGIFOHKE.js} +2 -2
  64. package/dist/{chunk-POOMO3PA.cjs → chunk-YQEPRYT5.cjs} +7 -7
  65. package/dist/{chunk-POOMO3PA.cjs.map → chunk-YQEPRYT5.cjs.map} +1 -1
  66. package/dist/components/case-studies/index.cjs +8 -8
  67. package/dist/components/case-studies/index.js +2 -2
  68. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  69. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  70. package/dist/components/chat/hooks/index.d.ts +0 -1
  71. package/dist/components/chat/hooks/index.d.ts.map +1 -1
  72. package/dist/components/chat/hooks/use-chat.d.ts.map +1 -1
  73. package/dist/components/chat/hooks/use-chunk-catchup.d.ts.map +1 -1
  74. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +15 -48
  75. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  76. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +10 -57
  77. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  78. package/dist/components/chat/index.cjs +10 -2
  79. package/dist/components/chat/index.cjs.map +1 -1
  80. package/dist/components/chat/index.d.ts +1 -0
  81. package/dist/components/chat/index.d.ts.map +1 -1
  82. package/dist/components/chat/index.js +21 -13
  83. package/dist/components/chat/stream/chat-dialog-store.d.ts +184 -0
  84. package/dist/components/chat/stream/chat-dialog-store.d.ts.map +1 -0
  85. package/dist/components/chat/stream/chat-stream-reducer.d.ts +380 -0
  86. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -0
  87. package/dist/components/chat/stream/delta-batcher.d.ts +51 -0
  88. package/dist/components/chat/stream/delta-batcher.d.ts.map +1 -0
  89. package/dist/components/chat/stream/index.d.ts +17 -0
  90. package/dist/components/chat/stream/index.d.ts.map +1 -0
  91. package/dist/components/chat/stream/message-mutations.d.ts +77 -0
  92. package/dist/components/chat/stream/message-mutations.d.ts.map +1 -0
  93. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts +22 -0
  94. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts.map +1 -0
  95. package/dist/components/chat/types/api.types.d.ts +1 -81
  96. package/dist/components/chat/types/api.types.d.ts.map +1 -1
  97. package/dist/components/chat/types/processing.types.d.ts +2 -93
  98. package/dist/components/chat/types/processing.types.d.ts.map +1 -1
  99. package/dist/components/chat/types/unified-chat-state.types.d.ts +15 -0
  100. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  101. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts +34 -6
  102. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
  103. package/dist/components/chat/utils/history-merge.d.ts.map +1 -1
  104. package/dist/components/chat/utils/index.d.ts +2 -3
  105. package/dist/components/chat/utils/index.d.ts.map +1 -1
  106. package/dist/components/chat/utils/process-historical-messages.d.ts +59 -11
  107. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  108. package/dist/components/contact/index.cjs +3 -3
  109. package/dist/components/contact/index.js +2 -2
  110. package/dist/components/docs/index.cjs +5 -5
  111. package/dist/components/docs/index.js +4 -4
  112. package/dist/components/embeds/index.cjs +3 -3
  113. package/dist/components/embeds/index.js +2 -2
  114. package/dist/components/faq/index.cjs +3 -3
  115. package/dist/components/faq/index.js +2 -2
  116. package/dist/components/features/index.cjs +2 -2
  117. package/dist/components/features/index.js +1 -1
  118. package/dist/components/help-center-pages/index.cjs +22 -22
  119. package/dist/components/help-center-pages/index.js +13 -13
  120. package/dist/components/index.cjs +174 -132
  121. package/dist/components/index.cjs.map +1 -1
  122. package/dist/components/index.js +65 -23
  123. package/dist/components/index.js.map +1 -1
  124. package/dist/components/meeting-scheduler/index.cjs +34 -34
  125. package/dist/components/meeting-scheduler/index.js +3 -3
  126. package/dist/components/navigation/index.cjs +2 -2
  127. package/dist/components/navigation/index.js +1 -1
  128. package/dist/components/onboarding-guides/index.cjs +5 -5
  129. package/dist/components/onboarding-guides/index.js +4 -4
  130. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
  131. package/dist/components/related-content/index.cjs +3 -3
  132. package/dist/components/related-content/index.js +2 -2
  133. package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
  134. package/dist/components/tickets/index.cjs +6 -6
  135. package/dist/components/tickets/index.js +5 -5
  136. package/dist/components/ui/index.cjs +44 -2
  137. package/dist/components/ui/index.cjs.map +1 -1
  138. package/dist/components/ui/index.d.ts +1 -2
  139. package/dist/components/ui/index.d.ts.map +1 -1
  140. package/dist/components/ui/index.js +55 -13
  141. package/dist/components/ui/markdown/base-components.d.ts +51 -0
  142. package/dist/components/ui/markdown/base-components.d.ts.map +1 -0
  143. package/dist/components/ui/markdown/engine.d.ts +99 -0
  144. package/dist/components/ui/markdown/engine.d.ts.map +1 -0
  145. package/dist/components/ui/markdown/heading-ids.d.ts +65 -0
  146. package/dist/components/ui/markdown/heading-ids.d.ts.map +1 -0
  147. package/dist/components/ui/markdown/index.d.ts +18 -0
  148. package/dist/components/ui/markdown/index.d.ts.map +1 -0
  149. package/dist/components/ui/markdown/mermaid-diagram.d.ts +79 -0
  150. package/dist/components/ui/markdown/mermaid-diagram.d.ts.map +1 -0
  151. package/dist/components/ui/markdown/rich/embed-overrides.d.ts +14 -0
  152. package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +1 -0
  153. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts +40 -0
  154. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts.map +1 -0
  155. package/dist/components/ui/markdown/rich/shortcodes.d.ts +8 -0
  156. package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +1 -0
  157. package/dist/components/ui/markdown/sanitize.d.ts +153 -0
  158. package/dist/components/ui/markdown/sanitize.d.ts.map +1 -0
  159. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts +23 -0
  160. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts.map +1 -0
  161. package/dist/components/ui/markdown/streaming.d.ts +78 -0
  162. package/dist/components/ui/markdown/streaming.d.ts.map +1 -0
  163. package/dist/components/ui/markdown/text-size.d.ts +34 -0
  164. package/dist/components/ui/markdown/text-size.d.ts.map +1 -0
  165. package/dist/index.cjs +60 -2
  166. package/dist/index.cjs.map +1 -1
  167. package/dist/index.js +71 -13
  168. package/dist/utils/index.cjs +169 -40
  169. package/dist/utils/index.cjs.map +1 -1
  170. package/dist/utils/index.d.ts +1 -0
  171. package/dist/utils/index.d.ts.map +1 -1
  172. package/dist/utils/index.js +162 -41
  173. package/dist/utils/index.js.map +1 -1
  174. package/dist/utils/markdown-fences.d.ts +42 -0
  175. package/dist/utils/markdown-fences.d.ts.map +1 -0
  176. package/dist/utils/markdown-heading-id.d.ts +85 -0
  177. package/dist/utils/markdown-heading-id.d.ts.map +1 -0
  178. package/dist/utils/markdown-section-extractor.d.ts.map +1 -1
  179. package/package.json +7 -1
  180. package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +319 -0
  181. package/src/chat-protocol/__tests__/chat-protocol.test.ts +516 -0
  182. package/src/chat-protocol/__tests__/env-flag.test.ts +52 -0
  183. package/src/chat-protocol/__tests__/ip-normalize.test.ts +136 -0
  184. package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +237 -0
  185. package/src/chat-protocol/decode.ts +278 -0
  186. package/src/chat-protocol/encode.ts +71 -0
  187. package/src/chat-protocol/env-flag.ts +39 -0
  188. package/src/chat-protocol/events.ts +254 -0
  189. package/src/chat-protocol/frames.ts +245 -0
  190. package/src/chat-protocol/index.ts +21 -0
  191. package/src/chat-protocol/ip-normalize.ts +146 -0
  192. package/src/chat-protocol/nats-decoder.ts +264 -0
  193. package/src/components/chat/.chat-message-list.md +10 -6
  194. package/src/components/chat/__tests__/chat-message-list.test.tsx +450 -9
  195. package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +207 -0
  196. package/src/components/chat/__tests__/chat-pending-turn.test.tsx +250 -0
  197. package/src/components/chat/chat-message-enhanced.tsx +122 -20
  198. package/src/components/chat/chat-message-list.tsx +515 -133
  199. package/src/components/chat/embeddable-chat.tsx +6 -0
  200. package/src/components/chat/hooks/.index.md +30 -33
  201. package/src/components/chat/hooks/.use-nats-chat-adapter.md +36 -55
  202. package/src/components/chat/hooks/__tests__/__snapshots__/conversation-id-persistence-golden.test.ts.snap +37 -0
  203. package/src/components/chat/hooks/__tests__/__snapshots__/sse-stream-golden.test.ts.snap +0 -0
  204. package/src/components/chat/hooks/__tests__/chunk-catchup-dialog-staleness.test.ts +92 -0
  205. package/src/components/chat/hooks/__tests__/conversation-id-persistence-golden.test.ts +438 -0
  206. package/src/components/chat/hooks/__tests__/sse-stream-golden.test.ts +318 -0
  207. package/src/components/chat/hooks/index.ts +0 -1
  208. package/src/components/chat/hooks/use-chat.ts +8 -0
  209. package/src/components/chat/hooks/use-chunk-catchup.ts +26 -1
  210. package/src/components/chat/hooks/use-nats-chat-adapter.ts +254 -825
  211. package/src/components/chat/hooks/use-sse-chat-adapter.ts +380 -684
  212. package/src/components/chat/index.ts +5 -0
  213. package/src/components/chat/stream/__tests__/__snapshots__/chat-stream-reducer-golden.test.ts.snap +944 -0
  214. package/src/components/chat/stream/__tests__/chat-dialog-store.test.ts +806 -0
  215. package/src/components/chat/stream/__tests__/chat-stream-reducer-golden.test.ts +467 -0
  216. package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +1330 -0
  217. package/src/components/chat/stream/__tests__/delta-batcher.test.ts +255 -0
  218. package/src/components/chat/stream/__tests__/use-chat-stream-reducer.test.ts +105 -0
  219. package/src/components/chat/stream/chat-dialog-store.ts +536 -0
  220. package/src/components/chat/stream/chat-stream-reducer.ts +1928 -0
  221. package/src/components/chat/stream/delta-batcher.ts +168 -0
  222. package/src/components/chat/stream/index.ts +55 -0
  223. package/src/components/chat/stream/message-mutations.ts +340 -0
  224. package/src/components/chat/stream/use-chat-stream-reducer.ts +126 -0
  225. package/src/components/chat/thinking-display.tsx +1 -1
  226. package/src/components/chat/types/.api.types.md +39 -49
  227. package/src/components/chat/types/.processing.types.md +29 -58
  228. package/src/components/chat/types/.unified-chat-state.types.md +2 -2
  229. package/src/components/chat/types/api.types.ts +6 -73
  230. package/src/components/chat/types/processing.types.ts +11 -53
  231. package/src/components/chat/types/unified-chat-state.types.ts +16 -0
  232. package/src/components/chat/utils/.extract-incomplete-message-state.md +21 -3
  233. package/src/components/chat/utils/.history-merge.md +44 -3
  234. package/src/components/chat/utils/.index.md +31 -40
  235. package/src/components/chat/utils/__tests__/__snapshots__/process-historical-messages-golden.test.ts.snap +420 -0
  236. package/src/components/chat/utils/__tests__/__snapshots__/segment-accumulator-golden.test.ts.snap +605 -0
  237. package/src/components/chat/utils/__tests__/extract-incomplete-message-state.test.ts +106 -0
  238. package/src/components/chat/utils/__tests__/history-merge.test.ts +160 -0
  239. package/src/components/chat/utils/__tests__/process-historical-messages-approvals.test.ts +61 -0
  240. package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +394 -0
  241. package/src/components/chat/utils/__tests__/segment-accumulator-golden.test.ts +270 -0
  242. package/src/components/chat/utils/extract-incomplete-message-state.ts +93 -6
  243. package/src/components/chat/utils/history-merge.ts +85 -5
  244. package/src/components/chat/utils/index.ts +5 -8
  245. package/src/components/chat/utils/process-historical-messages.ts +361 -385
  246. package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +22 -4
  247. package/src/components/shared/legal-document/legal-document-page.tsx +1 -1
  248. package/src/components/shared/product-release/release-detail-page.tsx +19 -4
  249. package/src/components/ui/__tests__/__snapshots__/markdown-parity.test.tsx.snap +2420 -0
  250. package/src/components/ui/__tests__/markdown-parity.test.tsx +2540 -0
  251. package/src/components/ui/index.ts +1 -2
  252. package/src/components/ui/markdown/__tests__/mermaid-security.test.ts +176 -0
  253. package/src/components/ui/markdown/__tests__/mermaid-stale-render.test.tsx +151 -0
  254. package/src/components/ui/markdown/__tests__/sanitize-invariant.test.ts +354 -0
  255. package/src/components/ui/markdown/__tests__/sanitize-render.test.tsx +118 -0
  256. package/src/components/ui/markdown/__tests__/streaming.test.tsx +436 -0
  257. package/src/components/ui/markdown/base-components.tsx +360 -0
  258. package/src/components/ui/markdown/engine.tsx +315 -0
  259. package/src/components/ui/markdown/heading-ids.ts +239 -0
  260. package/src/components/ui/markdown/index.ts +49 -0
  261. package/src/components/ui/markdown/mermaid-diagram.tsx +323 -0
  262. package/src/components/ui/markdown/rich/embed-overrides.tsx +199 -0
  263. package/src/components/ui/markdown/rich/rich-markdown-renderer.tsx +184 -0
  264. package/src/components/ui/markdown/rich/shortcodes.ts +170 -0
  265. package/src/components/ui/markdown/sanitize.ts +3584 -0
  266. package/src/components/ui/markdown/simple-markdown-renderer.tsx +28 -0
  267. package/src/components/ui/markdown/streaming.ts +390 -0
  268. package/src/components/ui/markdown/text-size.ts +106 -0
  269. package/src/components/ui/release-changelog-section.tsx +1 -1
  270. package/src/components/ui/ticket-info-section.tsx +1 -1
  271. package/src/utils/index.ts +1 -0
  272. package/src/utils/markdown-fences.ts +127 -0
  273. package/src/utils/markdown-heading-id.ts +348 -0
  274. package/src/utils/markdown-section-extractor.ts +41 -56
  275. package/dist/chunk-OCEKO5CW.js.map +0 -1
  276. package/dist/chunk-QTYZMP6D.cjs.map +0 -1
  277. package/dist/chunk-TLMJHMXJ.js.map +0 -1
  278. package/dist/chunk-TWYNR4TZ.cjs.map +0 -1
  279. package/dist/chunk-WA7RR64F.js.map +0 -1
  280. package/dist/chunk-WK4N5VBX.cjs.map +0 -1
  281. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts +0 -6
  282. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +0 -1
  283. package/dist/components/chat/utils/chunk-parser.d.ts +0 -25
  284. package/dist/components/chat/utils/chunk-parser.d.ts.map +0 -1
  285. package/dist/components/ui/rich-markdown-renderer.d.ts +0 -34
  286. package/dist/components/ui/rich-markdown-renderer.d.ts.map +0 -1
  287. package/dist/components/ui/simple-markdown-renderer.d.ts +0 -73
  288. package/dist/components/ui/simple-markdown-renderer.d.ts.map +0 -1
  289. package/src/components/chat/hooks/.use-realtime-chunk-processor.md +0 -77
  290. package/src/components/chat/hooks/use-realtime-chunk-processor.ts +0 -489
  291. package/src/components/chat/utils/.chunk-parser.md +0 -62
  292. package/src/components/chat/utils/chunk-parser.ts +0 -262
  293. package/src/components/ui/.simple-markdown-renderer.md +0 -52
  294. package/src/components/ui/rich-markdown-renderer.tsx +0 -1223
  295. package/src/components/ui/simple-markdown-renderer.tsx +0 -964
  296. /package/dist/{chunk-ADPMHWOE.js.map → chunk-72XAID7Y.js.map} +0 -0
  297. /package/dist/{chunk-PAGKRNWK.js.map → chunk-7WZHBQ4J.js.map} +0 -0
  298. /package/dist/{chunk-TSZHM74B.js.map → chunk-DC2TKS7C.js.map} +0 -0
  299. /package/dist/{chunk-I64ABCDX.js.map → chunk-I2B6X77L.js.map} +0 -0
  300. /package/dist/{chunk-DTYRYB2N.js.map → chunk-K6QTZ7CZ.js.map} +0 -0
  301. /package/dist/{chunk-4LTMXDUS.js.map → chunk-KBN7PMFT.js.map} +0 -0
  302. /package/dist/{chunk-TBMV7I5N.js.map → chunk-KXZO2WZX.js.map} +0 -0
  303. /package/dist/{chunk-KEBYLU3U.js.map → chunk-LFUPI7VX.js.map} +0 -0
  304. /package/dist/{chunk-WJQHJD7J.js.map → chunk-TWSKTJHW.js.map} +0 -0
  305. /package/dist/{chunk-TZRUCD56.js.map → chunk-V3ZAAFE3.js.map} +0 -0
  306. /package/dist/{chunk-LRGHJPET.js.map → chunk-XGIFOHKE.js.map} +0 -0
@@ -0,0 +1,207 @@
1
+ /**
2
+ * REGRESSION: `ChatMessageEnhanced` must hand the markdown engine a
3
+ * REFERENTIALLY STABLE `componentOverrides` map across a streamed turn.
4
+ *
5
+ * The engine memoizes each COMPLETED block (`StreamingBlockRenderer`) on its
6
+ * `components` prop identity. `ChatMessageEnhanced` is the sole production
7
+ * caller, and it used to defeat that memo one level up: `cardComponentOverrides`
8
+ * listed `renderingPlan` in its deps, `renderingPlan` is memoized on `segments`,
9
+ * and the stream reducer replaces `segments` on EVERY text delta. So the
10
+ * override map got a new identity per token → the engine's `components` memo
11
+ * re-created → every completed block re-parsed on every token.
12
+ *
13
+ * The engine's own streaming test cannot catch this: it passes a module-scope
14
+ * stable override map. This test drives the real component with the real
15
+ * per-delta `content` churn a reducer produces, and asserts at the SEAM (the
16
+ * props the component hands the renderer) rather than through the engine's
17
+ * internals — the two are separately owned.
18
+ */
19
+
20
+ import React from 'react'
21
+ import { describe, it, expect, vi } from 'vitest'
22
+ import { render } from '@testing-library/react'
23
+ import type { MessageSegment } from '../types'
24
+ import type { ChatRef } from '../chat-ref.types'
25
+
26
+ /** Every `componentOverrides` / `additionalRemarkPlugins` identity the
27
+ * component handed the markdown renderer, in render order. */
28
+ const seenOverrides: unknown[] = []
29
+ const seenPlugins: unknown[] = []
30
+
31
+ vi.mock('../../ui/markdown/simple-markdown-renderer', () => ({
32
+ SimpleMarkdownRenderer: ({
33
+ content,
34
+ componentOverrides,
35
+ additionalRemarkPlugins,
36
+ }: {
37
+ content: string
38
+ componentOverrides?: unknown
39
+ additionalRemarkPlugins?: unknown
40
+ }) => {
41
+ seenOverrides.push(componentOverrides)
42
+ seenPlugins.push(additionalRemarkPlugins)
43
+ return <div data-testid="md">{content}</div>
44
+ },
45
+ }))
46
+
47
+ import { ChatMessageEnhanced } from '../chat-message-enhanced'
48
+
49
+ function NavLinkAnchor({ href, children }: React.AnchorHTMLAttributes<HTMLAnchorElement>) {
50
+ return <a href={href}>{children}</a>
51
+ }
52
+ const CHAT_REFS: Record<string, ChatRef> = {}
53
+ const renderMention = () => null
54
+
55
+ /** Fresh array per call — the reducer replaces `segments` on every delta. */
56
+ const segmentsFor = (tail: string): MessageSegment[] => [
57
+ { type: 'text', text: `First paragraph, already complete.\n\n${tail}` },
58
+ ]
59
+
60
+ describe('ChatMessageEnhanced — override identity across a streamed turn', () => {
61
+ it('keeps componentOverrides + remark plugins referentially stable per token', () => {
62
+ seenOverrides.length = 0
63
+ seenPlugins.length = 0
64
+
65
+ const props = {
66
+ role: 'assistant' as const,
67
+ isTyping: true,
68
+ chatRefs: CHAT_REFS,
69
+ renderMention,
70
+ NavLinkAnchor,
71
+ }
72
+
73
+ const view = render(<ChatMessageEnhanced {...props} content={segmentsFor('Tail')} />)
74
+ expect(seenOverrides.length).toBeGreaterThan(0)
75
+ const firstOverrides = seenOverrides[0]
76
+ const firstPlugins = seenPlugins[0]
77
+ expect(firstOverrides).toBeTruthy()
78
+
79
+ let tail = 'Tail'
80
+ for (let i = 0; i < 10; i += 1) {
81
+ tail += ` token${i}`
82
+ view.rerender(<ChatMessageEnhanced {...props} content={segmentsFor(tail)} />)
83
+ }
84
+
85
+ // ONE identity for the whole turn. Before the fix this produced a fresh
86
+ // object on each of the 11 renders.
87
+ expect(new Set(seenOverrides).size).toBe(1)
88
+ expect(seenOverrides.every((o) => o === firstOverrides)).toBe(true)
89
+ expect(new Set(seenPlugins).size).toBe(1)
90
+ expect(seenPlugins.every((p) => p === firstPlugins)).toBe(true)
91
+
92
+ // Sanity: the tail really did stream through.
93
+ expect(view.container.textContent).toContain('token9')
94
+ })
95
+
96
+ it('still resolves card markers through the CURRENT plan (ref-read, not closure)', () => {
97
+ seenOverrides.length = 0
98
+ const refs: Record<string, ChatRef> = {
99
+ 'blog:abc': { type: 'blog', id: 'abc', title: 'Resolved Title', url: null },
100
+ }
101
+ render(
102
+ <ChatMessageEnhanced
103
+ role="assistant"
104
+ content={[{ type: 'text', text: 'See [card://blog:abc] for more.' }]}
105
+ chatRefs={refs}
106
+ NavLinkAnchor={NavLinkAnchor}
107
+ />,
108
+ )
109
+ const overrides = seenOverrides[0] as { a: React.FC<{ href: string; children?: React.ReactNode }> }
110
+ const out = render(<>{overrides.a({ href: 'card://blog:abc', children: 'x' })}</>)
111
+ expect(out.container.textContent).toBe('Resolved Title')
112
+ })
113
+
114
+ /**
115
+ * REGRESSION (round 3): a ref that resolves AFTER the message finished
116
+ * streaming must still reach the pill. The override reads `chatRefs`
117
+ * through a ref, so its own closure is fine — but the markdown engine
118
+ * memoizes completed blocks on the override map's IDENTITY. With
119
+ * `chatRefs` excluded from the override memo's deps entirely, the map kept
120
+ * its old identity, the engine bailed, and the marker rendered its raw-id
121
+ * fallback permanently.
122
+ */
123
+ it('gives componentOverrides a NEW identity when the ref set grows (completed message)', () => {
124
+ seenOverrides.length = 0
125
+ const content: MessageSegment[] = [{ type: 'text', text: 'See [card://blog:late] for more.' }]
126
+ const emptyRefs: Record<string, ChatRef> = {}
127
+ const props = {
128
+ role: 'assistant' as const,
129
+ isTyping: false,
130
+ content,
131
+ NavLinkAnchor,
132
+ }
133
+
134
+ const view = render(<ChatMessageEnhanced {...props} chatRefs={emptyRefs} />)
135
+ const before = seenOverrides[seenOverrides.length - 1] as {
136
+ a: React.FC<{ href: string; children?: React.ReactNode }>
137
+ }
138
+ // Unresolved: the raw card id is surfaced so the breakage is visible.
139
+ expect(render(<>{before.a({ href: 'card://blog:late', children: 'x' })}</>).container.textContent)
140
+ .toBe('late')
141
+
142
+ // Same content, same everything — only the refs map gains the entry.
143
+ const resolvedRefs: Record<string, ChatRef> = {
144
+ 'blog:late': { type: 'blog', id: 'late', title: 'Late Title', url: null },
145
+ }
146
+ view.rerender(<ChatMessageEnhanced {...props} chatRefs={resolvedRefs} />)
147
+
148
+ const after = seenOverrides[seenOverrides.length - 1] as {
149
+ a: React.FC<{ href: string; children?: React.ReactNode }>
150
+ }
151
+ expect(after).not.toBe(before)
152
+ expect(render(<>{after.a({ href: 'card://blog:late', children: 'x' })}</>).container.textContent)
153
+ .toBe('Late Title')
154
+ })
155
+
156
+ /**
157
+ * REGRESSION (round 5): a later refs frame REPLACES the map for a send index
158
+ * with the SAME key set but richer values — the `title` going from the raw
159
+ * cardId to the resolved document title is the exact production case. A
160
+ * key-set-only token produced an identical string, so the override map kept
161
+ * its identity, the engine's completed-block memo bailed, and the pill stayed
162
+ * on its fallback forever. The token fingerprints VALUES now.
163
+ */
164
+ it('gives componentOverrides a NEW identity when a ref VALUE is enriched (same key set)', () => {
165
+ seenOverrides.length = 0
166
+ const content: MessageSegment[] = [{ type: 'text', text: 'See [card://blog:abc].' }]
167
+ const props = { role: 'assistant' as const, isTyping: false, content, NavLinkAnchor }
168
+
169
+ // First frame: the ref exists but its title is still the raw card id.
170
+ const stub: Record<string, ChatRef> = {
171
+ 'blog:abc': { type: 'blog', id: 'abc', title: 'abc', url: null },
172
+ }
173
+ const view = render(<ChatMessageEnhanced {...props} chatRefs={stub} />)
174
+ const before = seenOverrides[seenOverrides.length - 1] as {
175
+ a: React.FC<{ href: string; children?: React.ReactNode }>
176
+ }
177
+ expect(render(<>{before.a({ href: 'card://blog:abc', children: 'x' })}</>).container.textContent)
178
+ .toBe('abc')
179
+
180
+ // Second frame: SAME key set, enriched value.
181
+ const enriched: Record<string, ChatRef> = {
182
+ 'blog:abc': { type: 'blog', id: 'abc', title: 'Resolved Document Title', url: null },
183
+ }
184
+ view.rerender(<ChatMessageEnhanced {...props} chatRefs={enriched} />)
185
+
186
+ const after = seenOverrides[seenOverrides.length - 1] as {
187
+ a: React.FC<{ href: string; children?: React.ReactNode }>
188
+ }
189
+ expect(after).not.toBe(before)
190
+ expect(render(<>{after.a({ href: 'card://blog:abc', children: 'x' })}</>).container.textContent)
191
+ .toBe('Resolved Document Title')
192
+ })
193
+
194
+ /** …but a same-KEY refs object (new identity, same entries) must NOT churn
195
+ * the override map — that is the streaming-stability guarantee above. */
196
+ it('keeps the override identity when only the refs object identity changes', () => {
197
+ seenOverrides.length = 0
198
+ const content: MessageSegment[] = [{ type: 'text', text: 'See [card://blog:abc].' }]
199
+ const ref: ChatRef = { type: 'blog', id: 'abc', title: 'T', url: null }
200
+ const props = { role: 'assistant' as const, content, NavLinkAnchor }
201
+
202
+ const view = render(<ChatMessageEnhanced {...props} chatRefs={{ 'blog:abc': ref }} />)
203
+ view.rerender(<ChatMessageEnhanced {...props} chatRefs={{ 'blog:abc': ref }} />)
204
+
205
+ expect(new Set(seenOverrides).size).toBe(1)
206
+ })
207
+ })
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Pending-turn + hidden-row contracts.
3
+ *
4
+ * Two reader-visible defects this pins against:
5
+ *
6
+ * 1. Approving a tool call minted an author-label row with NO body
7
+ * ("Michael Assraf:" then nothing). The approval decision is
8
+ * out-of-band metadata, so the reducer must not materialize a user
9
+ * row for it at all — fixing it at render would leave dead state.
10
+ * 2. While the model was thinking, the transcript showed a second,
11
+ * empty "Mingo:" row ABOVE the footer progress indicator — two
12
+ * owners for one pending turn, and an `aria-live` log announcing a
13
+ * bare "Mingo". Exactly one affordance must be present.
14
+ *
15
+ * Plus the two idioms that must SURVIVE both fixes: a hidden row that
16
+ * carries real text (auto-continuation directive) stays in state while
17
+ * never rendering, and the SYSTEM name-only row still renders.
18
+ */
19
+
20
+ import { render } from '@testing-library/react'
21
+ import { describe, expect, it, vi } from 'vitest'
22
+ import { createChatStreamReducer } from '../stream/chat-stream-reducer'
23
+ import {
24
+ AUTO_CONTINUATION_DIRECTIVE_PREFIX,
25
+ buildAutoContinuationDirective,
26
+ } from '../utils/auto-continuation-directive'
27
+ import type { Message } from '../types/message.types'
28
+
29
+ // ─── Render harness (mirrors chat-message-list.test.tsx) ────────────────────
30
+
31
+ const scrollToBottom = vi.fn()
32
+ const stopScroll = vi.fn()
33
+
34
+ function makeRef() {
35
+ const fn = ((el: HTMLElement | null) => {
36
+ ;(fn as { current: HTMLElement | null }).current = el
37
+ }) as ((el: HTMLElement | null) => void) & { current: HTMLElement | null }
38
+ fn.current = null
39
+ return fn
40
+ }
41
+
42
+ vi.mock('use-stick-to-bottom', () => ({
43
+ useStickToBottom: () => ({
44
+ scrollRef: makeRef(),
45
+ contentRef: makeRef(),
46
+ scrollToBottom,
47
+ stopScroll,
48
+ }),
49
+ }))
50
+
51
+ // Row stub that renders the author label the way the real component does
52
+ // (`Name:`), so "labeled row with no body" is directly assertable.
53
+ vi.mock('../chat-message-enhanced', () => ({
54
+ ChatMessageEnhanced: ({ name, content }: { name?: string; content: unknown }) => (
55
+ <div data-testid="row">
56
+ <span data-testid="row-name">{name ?? ''}</span>
57
+ <span data-testid="row-body">
58
+ {typeof content === 'string'
59
+ ? content
60
+ : Array.isArray(content)
61
+ ? content
62
+ .map((s) => (s && (s as { type?: string }).type === 'text' ? (s as { text: string }).text : '[seg]'))
63
+ .join('')
64
+ : ''}
65
+ </span>
66
+ </div>
67
+ ),
68
+ }))
69
+
70
+ import { ChatMessageList } from '../chat-message-list'
71
+
72
+ class ObserverStub {
73
+ observe() {}
74
+ unobserve() {}
75
+ disconnect() {}
76
+ }
77
+ vi.stubGlobal('IntersectionObserver', ObserverStub)
78
+ vi.stubGlobal('ResizeObserver', ObserverStub)
79
+
80
+ const names = (c: HTMLElement) =>
81
+ Array.from(c.querySelectorAll('[data-testid="row-name"]')).map((n) => n.textContent)
82
+ const bodies = (c: HTMLElement) =>
83
+ Array.from(c.querySelectorAll('[data-testid="row-body"]')).map((n) => n.textContent)
84
+
85
+ // ─── State-level contracts ─────────────────────────────────────────────────
86
+
87
+ describe('approval sends mint no visible user row', () => {
88
+ it('beginSseSend with empty text pushes only the assistant placeholder', () => {
89
+ const r = createChatStreamReducer({ transport: 'sse' })
90
+ // What `cardApprove` triggers: ('', { hidden: true, approvalAction }).
91
+ r.beginSseSend({ text: '', hidden: true, assistantName: 'Mingo AI' })
92
+
93
+ expect(r.state.messages).toHaveLength(1)
94
+ expect(r.state.messages[0].role).toBe('assistant')
95
+ expect(r.state.messages.some((m) => m.role === 'user')).toBe(false)
96
+ expect(r.state.streamingPhase).toBe('thinking')
97
+ })
98
+
99
+ it('pushOptimisticSend with empty text likewise mints no user row', () => {
100
+ const r = createChatStreamReducer({ transport: 'nats' })
101
+ r.pushOptimisticSend('', true)
102
+
103
+ expect(r.state.messages).toHaveLength(1)
104
+ expect(r.state.messages[0].role).toBe('assistant')
105
+ })
106
+
107
+ it('a normal send still mints the user row', () => {
108
+ const r = createChatStreamReducer({ transport: 'sse' })
109
+ r.beginSseSend({ text: 'hello', assistantName: 'Mingo AI' })
110
+
111
+ expect(r.state.messages).toHaveLength(2)
112
+ expect(r.state.messages[0]).toMatchObject({ role: 'user', content: 'hello' })
113
+ expect(r.state.messages[0].hidden).toBeUndefined()
114
+ })
115
+ })
116
+
117
+ describe('hidden rows that carry text', () => {
118
+ it('an auto-continuation directive IS state (the model sees it) but is flagged hidden', () => {
119
+ const directive = buildAutoContinuationDirective('create_ticket', { ticketId: '42' })
120
+ expect(directive.startsWith(AUTO_CONTINUATION_DIRECTIVE_PREFIX)).toBe(true)
121
+
122
+ const r = createChatStreamReducer({ transport: 'sse' })
123
+ r.beginSseSend({ text: directive, hidden: true, assistantName: 'Mingo AI' })
124
+
125
+ const userRow = r.state.messages.find((m) => m.role === 'user')
126
+ expect(userRow?.content).toBe(directive)
127
+ expect(userRow?.hidden).toBe(true)
128
+ })
129
+
130
+ it('never renders — the raw directive text must not reach the transcript', () => {
131
+ const directive = buildAutoContinuationDirective('create_ticket', { ticketId: '42' })
132
+ const messages: Message[] = [
133
+ { id: 'u1', role: 'user', name: 'Michael Assraf', content: 'my laptop is dead' },
134
+ { id: 'h1', role: 'user', name: 'Michael Assraf', content: directive, hidden: true },
135
+ { id: 'a1', role: 'assistant', name: 'Mingo', content: 'Opened a ticket.' },
136
+ ]
137
+ const { container } = render(<ChatMessageList dialogId="d" messages={messages} />)
138
+
139
+ expect(container.textContent).not.toContain(AUTO_CONTINUATION_DIRECTIVE_PREFIX)
140
+ expect(bodies(container)).toEqual(['my laptop is dead', 'Opened a ticket.'])
141
+ })
142
+ })
143
+
144
+ // ─── Render-level contracts ────────────────────────────────────────────────
145
+
146
+ describe('a thinking turn has exactly ONE pending affordance', () => {
147
+ const pending: Message[] = [
148
+ { id: 'u1', role: 'user', name: 'Michael Assraf', content: 'what is broken?' },
149
+ // `Message` carries its body in `content` — there is no `segments` field on
150
+ // it (that is `UnifiedChatMessage`), and the render path never reads one.
151
+ // An empty `content` IS the empty pending turn.
152
+ { id: 'a1', role: 'assistant', name: 'Mingo', content: '' },
153
+ ]
154
+
155
+ it('does not render the empty labeled assistant row while typing', () => {
156
+ const { container } = render(
157
+ <ChatMessageList dialogId="d" messages={pending} isTyping />,
158
+ )
159
+ // The bare "Mingo" label must be gone from the transcript entirely —
160
+ // removed from the DOM, not visually hidden (the list is an aria-live
161
+ // log; a zero-height node would still be announced).
162
+ expect(names(container)).toEqual(['Michael Assraf'])
163
+ expect(bodies(container)).toEqual(['what is broken?'])
164
+ // …and the single remaining owner is the polite status indicator.
165
+ expect(container.querySelectorAll('[role="status"]')).toHaveLength(1)
166
+ })
167
+
168
+ it('renders the assistant row as soon as it carries text', () => {
169
+ const withText: Message[] = [
170
+ pending[0],
171
+ { id: 'a1', role: 'assistant', name: 'Mingo', content: [{ type: 'text', text: 'Chec' }] },
172
+ ]
173
+ const { container } = render(
174
+ <ChatMessageList dialogId="d" messages={withText} isTyping />,
175
+ )
176
+ expect(names(container)).toEqual(['Michael Assraf', 'Mingo'])
177
+ })
178
+
179
+ it('renders the assistant row when it carries a NON-text segment (tool call)', () => {
180
+ const withTool: Message[] = [
181
+ pending[0],
182
+ {
183
+ id: 'a1',
184
+ role: 'assistant',
185
+ name: 'Mingo',
186
+ // The REAL `ToolExecutionSegment` shape — `{ type, data }`, not flat
187
+ // fields. The previous fixture put `toolExecutionRequestId` / `status`
188
+ // at the top level and needed an `as` cast to compile at all (the cast
189
+ // was the tell: the two types did not overlap). It passed only because
190
+ // the empty-pending-turn predicate looks at segment TYPE; the moment it
191
+ // inspected `data`, this test would have kept passing while the real
192
+ // path broke.
193
+ content: [
194
+ {
195
+ type: 'tool_execution',
196
+ data: {
197
+ type: 'EXECUTING_TOOL',
198
+ integratedToolType: 'SHELL',
199
+ toolFunction: 'run_command',
200
+ toolTitle: 'Run command',
201
+ parameters: { cmd: 'ls' },
202
+ toolExecutionRequestId: 'e1',
203
+ },
204
+ },
205
+ ],
206
+ },
207
+ ]
208
+ const { container } = render(
209
+ <ChatMessageList dialogId="d" messages={withTool} isTyping />,
210
+ )
211
+ expect(names(container)).toEqual(['Michael Assraf', 'Mingo'])
212
+ })
213
+
214
+ it('renders the assistant row when it carries ONLY a guide segment', () => {
215
+ // A `guide` body is the whole visible answer for a how-to question. If
216
+ // the empty-pending-turn predicate ever counted it as "nothing visible"
217
+ // (e.g. by widening its text-only clause), a guide-only turn would render
218
+ // NOTHING until the turn ended — the footer loader would be the sole
219
+ // affordance while a fully-formed guide card sat in state.
220
+ const withGuide: Message[] = [
221
+ pending[0],
222
+ {
223
+ id: 'a1',
224
+ role: 'assistant',
225
+ name: 'Mingo',
226
+ content: [{ type: 'guide', text: '## Enroll a device' }],
227
+ },
228
+ ]
229
+ const { container } = render(
230
+ <ChatMessageList dialogId="d" messages={withGuide} isTyping />,
231
+ )
232
+ expect(names(container)).toEqual(['Michael Assraf', 'Mingo'])
233
+ })
234
+
235
+ it('renders a completed empty-ish assistant row when NOT typing', () => {
236
+ const { container } = render(<ChatMessageList dialogId="d" messages={pending} />)
237
+ expect(names(container)).toEqual(['Michael Assraf', 'Mingo'])
238
+ })
239
+ })
240
+
241
+ describe('the SYSTEM name-only row is a legitimate label-without-body', () => {
242
+ it('still renders', () => {
243
+ const messages: Message[] = [
244
+ { id: 'u1', role: 'user', name: 'Michael Assraf', content: 'hi' },
245
+ { id: 's1', role: 'user', name: 'Technician joined', content: '', authorType: 'system' },
246
+ ]
247
+ const { container } = render(<ChatMessageList dialogId="d" messages={messages} isTyping />)
248
+ expect(names(container)).toContain('Technician joined')
249
+ })
250
+ })