@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,146 @@
1
+ /**
2
+ * `normalizeIpForBucketKey` — THE cross-repo normalizer for turning a raw
3
+ * client-IP candidate (`x-forwarded-for` hop, socket remote address, proxy
4
+ * header) into a stable rate-limit / dedup bucket key.
5
+ *
6
+ * WHY IT LIVES HERE: producer and consumer sit in DIFFERENT repos (the app
7
+ * emits the candidate, the hub buckets it) and each had grown its own
8
+ * normalizer. They disagreed — one unwrapped `::ffff:1.2.3.4` to the bare
9
+ * IPv4 and stripped `%zone`, the other kept the zone and only lower-cased the
10
+ * mapped form — so ONE visitor could land in TWO buckets depending on which
11
+ * side normalized. Both repos already depend on this package, and
12
+ * `./chat-protocol` is its server-safe subpath (no React, no browser APIs),
13
+ * so this is the one place both sides can share.
14
+ *
15
+ * Contract:
16
+ * - input longer than `IP_BUCKET_KEY_MAX_LENGTH` → `null` (an unbounded
17
+ * header must never become an unbounded map key);
18
+ * - `[…]` brackets and a trailing `]:port` are stripped, as is a bare
19
+ * `:port` on an IPv4 (`203.0.113.4:8080`) — an unbracketed IPv6 with a
20
+ * bare port stays ambiguous and is not stripped;
21
+ * - a `%zone` suffix is stripped (link-local scope is per-host, not part of
22
+ * the peer's identity);
23
+ * - IPv4-mapped IPv6 (`::ffff:203.0.113.4`) collapses to the bare IPv4, so
24
+ * the same peer buckets identically over either stack;
25
+ * - IPv6 is lower-cased;
26
+ * - IPv4 octets are range-checked and REJECTED when zero-padded (`.04`
27
+ * would otherwise bucket separately from `.4`), and IPv6 is
28
+ * charset/shape-checked (one `::` at most, ≤8 groups, an embedded IPv4
29
+ * only in the last group, no dangling separator); anything else returns
30
+ * `null` rather than a junk bucket key.
31
+ *
32
+ * It does NOT canonicalize IPv6 zero-compression (`2001:db8::1` vs
33
+ * `2001:0db8:0:0:0:0:0:1`) — peers do not spell their own address two ways
34
+ * within one deployment, and a full expander is more surface than the bucket
35
+ * key warrants.
36
+ */
37
+
38
+ /** Longest accepted candidate. A full IPv6 + zone fits well inside this. */
39
+ export const IP_BUCKET_KEY_MAX_LENGTH = 64
40
+
41
+ function isIpv4(value: string): boolean {
42
+ const parts = value.split('.')
43
+ if (parts.length !== 4) return false
44
+ for (const part of parts) {
45
+ if (!/^\d{1,3}$/.test(part)) return false
46
+ // Leading zeros are a SECOND spelling of the same address (`203.0.113.04`),
47
+ // so accepting them verbatim splits one peer across two buckets. Reject
48
+ // rather than canonicalize: no legitimate emitter zero-pads, and some
49
+ // resolvers read a `0`-prefixed octet as octal, i.e. it is not even
50
+ // reliably the same address.
51
+ if (part.length > 1 && part.startsWith('0')) return false
52
+ if (Number(part) > 255) return false
53
+ }
54
+ return true
55
+ }
56
+
57
+ function isIpv6(value: string): boolean {
58
+ if (!value.includes(':')) return false
59
+ // Charset gate first: hex digits, separators, and the dots of a trailing
60
+ // embedded IPv4. Anything else (a hostname, a header injection) is out.
61
+ if (!/^[0-9a-f:.]+$/.test(value)) return false
62
+ if (value.includes(':::')) return false
63
+ // A dangling separator (`2001:db8::1:`) leaves an EMPTY trailing group that
64
+ // is not part of a `::` compression — malformed, not an address.
65
+ if (value.endsWith(':') && !value.endsWith('::')) return false
66
+ if (value.startsWith(':') && !value.startsWith('::')) return false
67
+ const compressions = value.split('::').length - 1
68
+ if (compressions > 1) return false
69
+ const groups = value.split(':')
70
+ if (groups.length > 8) return false
71
+ for (let i = 0; i < groups.length; i += 1) {
72
+ const group = groups[i]
73
+ if (group === '') continue
74
+ if (group.includes('.')) {
75
+ // An embedded IPv4 occupies the LAST two groups; anywhere else
76
+ // (`1.2.3.4::1`) it is malformed.
77
+ if (i !== groups.length - 1) return false
78
+ if (!isIpv4(group)) return false
79
+ continue
80
+ }
81
+ if (!/^[0-9a-f]{1,4}$/.test(group)) return false
82
+ }
83
+ // Uncompressed form must be exactly 8 groups (a trailing embedded IPv4
84
+ // occupies two, so 7 written groups is also complete).
85
+ if (compressions === 0) {
86
+ const embedsIpv4 = groups[groups.length - 1]?.includes('.') ?? false
87
+ if (groups.length !== (embedsIpv4 ? 7 : 8)) return false
88
+ }
89
+ return true
90
+ }
91
+
92
+ /**
93
+ * Normalize one IP candidate to its canonical bucket key, or `null` when the
94
+ * value is not a usable address.
95
+ */
96
+ export function normalizeIpForBucketKey(value: string): string | null {
97
+ if (typeof value !== 'string') return null
98
+ let candidate = value.trim()
99
+ if (candidate === '' || candidate.length > IP_BUCKET_KEY_MAX_LENGTH) return null
100
+
101
+ // `[2001:db8::1]` / `[::1]:443` — the bracketed forms a proxy emits so the
102
+ // port is unambiguous.
103
+ if (candidate.startsWith('[')) {
104
+ const close = candidate.indexOf(']')
105
+ if (close === -1) return null
106
+ const trailer = candidate.slice(close + 1)
107
+ if (trailer !== '' && !/^:\d{1,5}$/.test(trailer)) return null
108
+ candidate = candidate.slice(1, close)
109
+ }
110
+
111
+ // `%eth0` / `%25eth0` scope id — host-local, never part of peer identity.
112
+ const zone = candidate.indexOf('%')
113
+ if (zone !== -1) candidate = candidate.slice(0, zone)
114
+ if (candidate === '') return null
115
+
116
+ // UNBRACKETED `203.0.113.4:8080` — Azure App Service / Front Door and several
117
+ // CDNs write the client hop that way. Rejecting it left the caller with NO
118
+ // candidate at all, collapsing every visitor into the single egress bucket:
119
+ // exactly the silent regression this module exists to prevent. Stripped ONLY
120
+ // when the remainder is a valid IPv4; an unbracketed IPv6 with a bare port
121
+ // (`::1:443`) is genuinely ambiguous — `:443` is equally a final group — so
122
+ // that shape is left to the IPv6 path.
123
+ const portSplit = candidate.lastIndexOf(':')
124
+ if (portSplit > 0 && /^\d{1,5}$/.test(candidate.slice(portSplit + 1))) {
125
+ const head = candidate.slice(0, portSplit)
126
+ if (isIpv4(head)) candidate = head
127
+ }
128
+
129
+ if (isIpv4(candidate)) return candidate
130
+
131
+ const lowered = candidate.toLowerCase()
132
+
133
+ // IPv4-mapped IPv6 → the bare IPv4 (same peer, one bucket). EVERY spelling
134
+ // is handled: the compressed `::ffff:1.2.3.4` that dual-stack listeners
135
+ // actually emit, the written-out `0:0:0:0:0:ffff:1.2.3.4` that a hop doing
136
+ // its own (non-compressing) formatting can produce, and the ZERO-PADDED
137
+ // `0000:0000:0000:0000:0000:ffff:1.2.3.4` that a fixed-width formatter
138
+ // produces — leading zeros in an IPv6 group are purely cosmetic, so all
139
+ // three name the same peer. Matching only some of them would split one peer
140
+ // across two buckets — the exact failure this module exists to prevent.
141
+ const mapped = /^(?:(?:0{1,4}:){5}|::)ffff:(\d{1,3}(?:\.\d{1,3}){3})$/.exec(lowered)
142
+ if (mapped) return isIpv4(mapped[1]) ? mapped[1] : null
143
+
144
+ if (isIpv6(lowered)) return lowered
145
+ return null
146
+ }
@@ -0,0 +1,264 @@
1
+ /**
2
+ * NATS chunk → normalized `ChatStreamEvent` decoder.
3
+ *
4
+ * THE live NATS reading path — the ONLY chunk parser in the codebase. Every
5
+ * consumer (this lib's `useNatsChatAdapter`, the hub, the product app) feeds
6
+ * raw chunks through `decodeNatsChunk` into `createChatStreamReducer`. The
7
+ * legacy `components/chat/utils/chunk-parser.ts` (`parseChunkToAction`) that
8
+ * this module superseded was DELETED — do not reintroduce a second decoder.
9
+ *
10
+ * Chunks map onto the transport-agnostic event union, with the JetStream
11
+ * `streamSeq` lifted into the `seq` envelope (the reducer's idempotency gate
12
+ * keys off it).
13
+ *
14
+ * Behavior is pinned by `__tests__/nats-decoder-golden.test.ts`.
15
+ *
16
+ * Server-safe: no React, no browser APIs.
17
+ */
18
+
19
+ import { MESSAGE_TYPE } from '../components/chat/types/message.types'
20
+ import type { ApprovalToolCall, ChatStreamEvent } from './events'
21
+
22
+ /** Minimal structural view of a NATS chunk (see `ChunkData` in
23
+ * `src/components/chat/types/network.types.ts`). */
24
+ type NatsChunk = Record<string, any>
25
+
26
+ /** Coerce the wire's `toolCalls[]` into the batch-approval shape, dropping
27
+ * non-object entries and defaulting every field. */
28
+ function normalizeToolCalls(raw: unknown): ApprovalToolCall[] {
29
+ if (!Array.isArray(raw)) return []
30
+ return raw
31
+ .filter((item): item is Record<string, any> => !!item && typeof item === 'object')
32
+ .map((item) => ({
33
+ toolExecutionRequestId: String(item.toolExecutionRequestId ?? ''),
34
+ toolName: String(item.toolName ?? ''),
35
+ toolTitle: typeof item.toolTitle === 'string' ? item.toolTitle : undefined,
36
+ toolExplanation:
37
+ typeof item.toolExplanation === 'string' ? item.toolExplanation : undefined,
38
+ toolType: typeof item.toolType === 'string' ? item.toolType : undefined,
39
+ requiresApproval: item.requiresApproval === true,
40
+ approvalType: typeof item.approvalType === 'string' ? item.approvalType : null,
41
+ toolCallArguments:
42
+ item.toolCallArguments && typeof item.toolCallArguments === 'object'
43
+ ? (item.toolCallArguments as Record<string, any>)
44
+ : null,
45
+ }))
46
+ }
47
+
48
+ /**
49
+ * Parse one raw NATS chunk into a normalized event. Returns `null` for
50
+ * unknown/malformed chunks — an unrecognized `type`, a missing required
51
+ * field, or a non-object payload are all tolerated as no-ops rather than
52
+ * throwing, so a backend that adds a chunk type can't break the stream.
53
+ */
54
+ export function decodeNatsChunk(chunk: unknown): ChatStreamEvent | null {
55
+ if (!chunk || typeof chunk !== 'object') return null
56
+
57
+ const data = chunk as NatsChunk
58
+ const type = String(data.type || '')
59
+ // JetStream stream sequence → the generic `seq` envelope.
60
+ const seq: { seq?: number } =
61
+ typeof data.streamSeq === 'number' ? { seq: data.streamSeq } : {}
62
+
63
+ switch (type) {
64
+ case MESSAGE_TYPE.MESSAGE_START:
65
+ return { type: 'turn-start', ...seq }
66
+
67
+ case MESSAGE_TYPE.MESSAGE_END:
68
+ return { type: 'turn-end', ...seq }
69
+
70
+ case MESSAGE_TYPE.AI_METADATA: {
71
+ const providerName = data.providerName || data.provider
72
+ if (typeof data.modelName === 'string' && typeof providerName === 'string') {
73
+ return {
74
+ type: 'metadata',
75
+ modelLabel: data.modelDisplayName,
76
+ modelName: data.modelName,
77
+ provider: providerName,
78
+ contextWindowMaxTokens:
79
+ typeof data.contextWindow === 'number' ? data.contextWindow : 0,
80
+ ...seq,
81
+ }
82
+ }
83
+ return null
84
+ }
85
+
86
+ case MESSAGE_TYPE.TEXT:
87
+ if (typeof data.text === 'string') {
88
+ return { type: 'text-delta', text: data.text, ...seq }
89
+ }
90
+ return null
91
+
92
+ case MESSAGE_TYPE.THINKING:
93
+ if (typeof data.text === 'string') {
94
+ return { type: 'thinking-delta', text: data.text, ...seq }
95
+ }
96
+ return null
97
+
98
+ case MESSAGE_TYPE.GUIDE:
99
+ if (typeof data.text === 'string') {
100
+ return { type: 'guide-delta', text: data.text, ...seq }
101
+ }
102
+ return null
103
+
104
+ case MESSAGE_TYPE.EXECUTING_TOOL:
105
+ return {
106
+ type: 'tool-execution',
107
+ data: {
108
+ type: 'EXECUTING_TOOL',
109
+ integratedToolType: data.integratedToolType || '',
110
+ toolFunction: data.toolFunction || '',
111
+ toolTitle: typeof data.title === 'string' ? data.title : undefined,
112
+ // The human-readable "why this tool is running" line rendered inside
113
+ // the tool card. It rides ONLY the EXECUTING chunk — the EXECUTED one
114
+ // never carries it, and the accumulator's merge preserves whatever
115
+ // the EXECUTING segment held. Dropping it here therefore blanks the
116
+ // card for the whole lifetime of the run.
117
+ toolExplanation: typeof data.toolExplanation === 'string' ? data.toolExplanation : undefined,
118
+ parameters: data.parameters,
119
+ toolExecutionRequestId:
120
+ typeof data.toolExecutionRequestId === 'string'
121
+ ? data.toolExecutionRequestId
122
+ : undefined,
123
+ },
124
+ ...seq,
125
+ }
126
+
127
+ case MESSAGE_TYPE.EXECUTED_TOOL:
128
+ return {
129
+ type: 'tool-execution',
130
+ data: {
131
+ type: 'EXECUTED_TOOL',
132
+ integratedToolType: data.integratedToolType || '',
133
+ toolFunction: data.toolFunction || '',
134
+ toolTitle: typeof data.title === 'string' ? data.title : undefined,
135
+ parameters: data.parameters,
136
+ result: data.result,
137
+ success: data.success,
138
+ toolExecutionRequestId:
139
+ typeof data.toolExecutionRequestId === 'string'
140
+ ? data.toolExecutionRequestId
141
+ : undefined,
142
+ },
143
+ ...seq,
144
+ }
145
+
146
+ case MESSAGE_TYPE.APPROVAL_REQUEST: {
147
+ const requestId = data.approvalRequestId || data.approval_request_id || ''
148
+ const approvalType = data.approvalType || 'USER'
149
+ const toolCalls = normalizeToolCalls(data.toolCalls)
150
+
151
+ if (toolCalls.length > 0) {
152
+ return { type: 'approval-request', requestId, approvalType, toolCalls, ...seq }
153
+ }
154
+
155
+ return {
156
+ type: 'approval-request',
157
+ requestId,
158
+ approvalType,
159
+ command: data.command || '',
160
+ explanation: data.explanation,
161
+ ...seq,
162
+ }
163
+ }
164
+
165
+ case MESSAGE_TYPE.APPROVAL_RESULT: {
166
+ // Realtime chunks carry the resolver's name as `displayName`; the
167
+ // persisted GraphQL message exposes the same value as
168
+ // `resolvedByName`. Accept either so realtime and history-replay
169
+ // render "by {name}" identically.
170
+ const resolvedByName =
171
+ typeof data.resolvedByName === 'string'
172
+ ? data.resolvedByName
173
+ : typeof data.displayName === 'string'
174
+ ? data.displayName
175
+ : undefined
176
+ return {
177
+ type: 'approval-resolved',
178
+ requestId: data.approvalRequestId || data.approval_request_id || '',
179
+ status: data.approved === true ? 'approved' : 'rejected',
180
+ approvalType: data.approvalType || 'CLIENT',
181
+ resolvedByName,
182
+ ...seq,
183
+ }
184
+ }
185
+
186
+ case MESSAGE_TYPE.ERROR:
187
+ return {
188
+ type: 'error',
189
+ title: data.error || 'An error occurred',
190
+ details: data.details,
191
+ ...seq,
192
+ }
193
+
194
+ case MESSAGE_TYPE.MESSAGE_REQUEST:
195
+ return {
196
+ type: 'participant',
197
+ kind: 'message-request',
198
+ text: String(data.text || ''),
199
+ ownerType: typeof data.ownerType === 'string' ? data.ownerType : undefined,
200
+ displayName: typeof data.displayName === 'string' ? data.displayName : undefined,
201
+ userId: typeof data.userId === 'string' ? data.userId : undefined,
202
+ // Wire shape carries no label; fall back to the id (parity with
203
+ // the history path in `process-historical-messages.ts`).
204
+ contextItems: Array.isArray(data.contextItems)
205
+ ? (data.contextItems as Array<{ type?: unknown; id?: unknown }>)
206
+ .filter((it) => typeof it?.type === 'string' && typeof it?.id === 'string')
207
+ .map((it) => ({
208
+ type: it.type as string,
209
+ id: it.id as string,
210
+ label: it.id as string,
211
+ }))
212
+ : undefined,
213
+ ...seq,
214
+ }
215
+
216
+ case MESSAGE_TYPE.TOKEN_USAGE:
217
+ return {
218
+ type: 'token-usage',
219
+ inputTokensSize: data.inputTokensSize ?? 0,
220
+ outputTokensSize: data.outputTokensSize ?? 0,
221
+ totalTokensSize: data.totalTokensSize ?? 0,
222
+ contextSize: data.contextSize ?? 0,
223
+ ...seq,
224
+ }
225
+
226
+ case MESSAGE_TYPE.CONTEXT_COMPACTION_START:
227
+ return { type: 'compaction', phase: 'start', ...seq }
228
+
229
+ case MESSAGE_TYPE.CONTEXT_COMPACTION_END:
230
+ return {
231
+ type: 'compaction',
232
+ phase: 'end',
233
+ summary: typeof data.text === 'string' ? data.text : undefined,
234
+ ...seq,
235
+ }
236
+
237
+ case MESSAGE_TYPE.SYSTEM:
238
+ if (typeof data.text === 'string') {
239
+ return { type: 'participant', kind: 'system', text: data.text, ...seq }
240
+ }
241
+ return null
242
+
243
+ case MESSAGE_TYPE.DIRECT_MESSAGE:
244
+ if (typeof data.text === 'string') {
245
+ return {
246
+ type: 'participant',
247
+ kind: 'direct-message',
248
+ text: data.text,
249
+ ownerType: typeof data.ownerType === 'string' ? data.ownerType : undefined,
250
+ displayName:
251
+ typeof data.displayName === 'string' ? data.displayName : undefined,
252
+ userId: typeof data.userId === 'string' ? data.userId : undefined,
253
+ ...seq,
254
+ }
255
+ }
256
+ return null
257
+
258
+ case MESSAGE_TYPE.DIALOG_CLOSED:
259
+ return { type: 'dialog-closed', ...seq }
260
+
261
+ default:
262
+ return null
263
+ }
264
+ }
@@ -1,25 +1,27 @@
1
- <!-- source-hash: bcd59a730239fa164668aa97e210c356 -->
1
+ <!-- source-hash: d9436810c5dd4b6f6250d2c8f91f5212 -->
2
2
  Renders a scrollable, auto-anchoring chat message list with streaming indicators, infinite scroll upward pagination, and stick-to-bottom physics for AI chat interfaces.
3
3
 
4
4
  ## Key Components
5
5
 
6
6
  - **`ChatMessageList`** — Primary `forwardRef` component wrapping `useStickToBottom` for spring-physics scroll anchoring during token streaming
7
7
  - **`STREAMING_WORDS`** — Flamingo-themed cycling phrases displayed alongside the typing/streaming indicator
8
+ - **`BOTTOM_THRESHOLD_PX`** / **`FOLLOW_RETAIN_MS`** — The at-bottom band (mirrors the library's `STICK_TO_BOTTOM_OFFSET_PX`) and the chase window each follow re-assert opens (mirrors its `RETAIN_ANIMATION_DURATION_MS`)
8
9
  - **`hasNonEmptyContent`** — Utility predicate that checks whether a `MessageContent` value (string or `MessageSegment[]`) contains visible text
9
- - **`AnchorWatcher`** / **`disposeAnchorWatcher`** — Interface and idempotent teardown helper for the prepend-anchor `ResizeObserver` settle window
10
+ - **`AnchorWatcher`** / **`disposeAnchorWatcher`** — Interface and idempotent teardown helper for the top-anchor `ResizeObserver` settle window
10
11
  - **`ChatMessageEnhanced`** — Per-message renderer (avatars, approval variants, entity cards, context icons, mentions)
11
12
  - **`ChatMessageListSkeleton`** — Placeholder skeleton shown during initial load
12
- - **`CyclingPhrase`** — Animated word-cycling component used inside the streaming indicator
13
- - **`DotsLoaderIcon`** — Animated dots icon displayed while `isTyping` is active
13
+ - **`CyclingPhrase`** / **`DotsLoaderIcon`** The footer-pinned streaming indicator, rendered OUTSIDE the scroller so it can't jitter the thread
14
14
 
15
15
  ## Scroll Architecture
16
16
 
17
17
  | Concern | Owner |
18
18
  |---|---|
19
19
  | Streaming follow (spring) | `useStickToBottom` |
20
+ | Follow INTENT (arm / disarm / re-assert) | `followBottomRef` + growth watch |
20
21
  | Dialog change / first load / new user message | Force-scroll `useEffect` |
21
22
  | Load-older prepend anchoring | `loadOlderAnchorRef` + geometry delta |
22
23
  | Top-sentinel infinite scroll | `sentinelRef` + `IntersectionObserver` |
24
+ | Jump-to-bottom affordance | `atBottom` (measured geometry, NOT the library flag) |
23
25
  | Passive demo hard-pin | `pinBottom` prop path |
24
26
 
25
27
  ## Usage Example
@@ -53,5 +55,7 @@ export function MyChatView() {
53
55
 
54
56
  - Requires `use-stick-to-bottom` (StackBlitz Labs) — the same library used by Vercel AI SDK's `<Conversation>` and bolt.new
55
57
  - `initial: false` disables mount-time auto-scroll; the dialog-change `useEffect` owns first-paint positioning
56
- - Image attachments trigger a `requestAnimationFrame` follow-up scroll to handle Next.js `<Image>` progressive dimensioning
57
- - `pinBottom` bypasses smart-follow for scripted demos (Fae/Mingo) where no human controls scroll
58
+ - The follow lock exists because this list's layout silently loses the library's own `isAtBottom` (siblings below the scroller change its box; a card settling out of its skeleton produces a resize-driven scroll the library reads as a gesture). Re-asserts pass `ignoreEscapes` and the library's COALESCING options (`wait: true` + `duration`) so per-frame growth folds into ONE spring instead of spawning competing chains
59
+ - Growth is detected by an rAF poll on the scroller's `scrollHeight`, not only by `ResizeObserver`: the observed content node is a snapshot that React can swap out, after which the observer watches a detached element and streamed tokens grow the thread silently
60
+ - `isLoading` is in the deps of every effect that touches the scroller (follow, load-more sentinel, imperative handle): the loading branch returns the skeleton, so those effects bail on null refs and would otherwise never re-run for hosts whose `isLoading` outlives `hasNextPage` turning true
61
+ - `pinBottom` bypasses smart-follow for scripted demos (Fae/Mingo) where no human controls scroll