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

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 (281) hide show
  1. package/dist/{chunk-BLZZXZNM.cjs → chunk-5USSSRAL.cjs} +26 -26
  2. package/dist/{chunk-BLZZXZNM.cjs.map → chunk-5USSSRAL.cjs.map} +1 -1
  3. package/dist/{chunk-CGNDAJ7V.js → chunk-6B27O3ZB.js} +4 -4
  4. package/dist/{chunk-P5JQDFRM.cjs → chunk-7IRPMNRL.cjs} +60 -60
  5. package/dist/chunk-7IRPMNRL.cjs.map +1 -0
  6. package/dist/{chunk-4XDWNAZW.cjs → chunk-DQ3EHM6G.cjs} +9 -9
  7. package/dist/{chunk-4XDWNAZW.cjs.map → chunk-DQ3EHM6G.cjs.map} +1 -1
  8. package/dist/{chunk-7KSEINAE.cjs → chunk-FTXDIDNX.cjs} +15 -15
  9. package/dist/{chunk-7KSEINAE.cjs.map → chunk-FTXDIDNX.cjs.map} +1 -1
  10. package/dist/{chunk-4R6H2U77.js → chunk-H7ETLG65.js} +4 -4
  11. package/dist/{chunk-LAOO7XHO.js → chunk-HWTUVLYB.js} +2 -2
  12. package/dist/{chunk-F2NMWV44.js → chunk-IDKDOQZL.js} +2 -2
  13. package/dist/{chunk-BQ7PMWLM.cjs → chunk-KIIOBK7G.cjs} +27 -28
  14. package/dist/chunk-KIIOBK7G.cjs.map +1 -0
  15. package/dist/{chunk-QO2XD3FR.js → chunk-KQGFKG6Y.js} +2 -2
  16. package/dist/{chunk-3N3TMZ62.cjs → chunk-L676XVP4.cjs} +7 -7
  17. package/dist/{chunk-3N3TMZ62.cjs.map → chunk-L676XVP4.cjs.map} +1 -1
  18. package/dist/{chunk-F7F43U7Q.cjs → chunk-LDAOPWXZ.cjs} +11 -11
  19. package/dist/{chunk-F7F43U7Q.cjs.map → chunk-LDAOPWXZ.cjs.map} +1 -1
  20. package/dist/{chunk-ADD3FPDG.cjs → chunk-MKLP2SOZ.cjs} +4886 -7004
  21. package/dist/chunk-MKLP2SOZ.cjs.map +1 -0
  22. package/dist/{chunk-VXJH5TDN.js → chunk-QL7ODLJA.js} +10 -10
  23. package/dist/chunk-QL7ODLJA.js.map +1 -0
  24. package/dist/{chunk-OR5AMXYN.js → chunk-R5QYMUKN.js} +2 -2
  25. package/dist/{chunk-REWRUV3K.js → chunk-TNLFAJPA.js} +9673 -11791
  26. package/dist/chunk-TNLFAJPA.js.map +1 -0
  27. package/dist/{chunk-XF5ROTMQ.js → chunk-U2YDR5DT.js} +2 -2
  28. package/dist/{chunk-PXZ7YQ5V.cjs → chunk-VTQSB6QB.cjs} +5 -5
  29. package/dist/{chunk-PXZ7YQ5V.cjs.map → chunk-VTQSB6QB.cjs.map} +1 -1
  30. package/dist/{chunk-UI7CIUAR.js → chunk-WGJPTFRH.js} +2 -2
  31. package/dist/{chunk-WCE66C2Z.cjs → chunk-XUNJILJT.cjs} +62 -62
  32. package/dist/{chunk-WCE66C2Z.cjs.map → chunk-XUNJILJT.cjs.map} +1 -1
  33. package/dist/{chunk-APF5QBAU.js → chunk-YQBPIUZU.js} +2 -2
  34. package/dist/{chunk-TA7MFVUS.cjs → chunk-YV2YA3QH.cjs} +31 -31
  35. package/dist/{chunk-TA7MFVUS.cjs.map → chunk-YV2YA3QH.cjs.map} +1 -1
  36. package/dist/{chunk-QSGZE6AZ.cjs → chunk-ZDJ4ZUQ4.cjs} +37 -37
  37. package/dist/{chunk-QSGZE6AZ.cjs.map → chunk-ZDJ4ZUQ4.cjs.map} +1 -1
  38. package/dist/{chunk-LJKZIIK4.js → chunk-ZHQQRYMB.js} +5 -6
  39. package/dist/chunk-ZHQQRYMB.js.map +1 -0
  40. package/dist/components/case-studies/index.cjs +8 -8
  41. package/dist/components/case-studies/index.js +2 -2
  42. package/dist/components/chart.d.ts +1 -1
  43. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  44. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  45. package/dist/components/chat/hooks/index.d.ts +1 -0
  46. package/dist/components/chat/hooks/index.d.ts.map +1 -1
  47. package/dist/components/chat/hooks/use-chat.d.ts.map +1 -1
  48. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +48 -15
  49. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  50. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts +6 -0
  51. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +1 -0
  52. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +47 -3
  53. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  54. package/dist/components/chat/index.cjs +2 -10
  55. package/dist/components/chat/index.cjs.map +1 -1
  56. package/dist/components/chat/index.d.ts +0 -1
  57. package/dist/components/chat/index.d.ts.map +1 -1
  58. package/dist/components/chat/index.js +13 -21
  59. package/dist/components/chat/types/api.types.d.ts +81 -1
  60. package/dist/components/chat/types/api.types.d.ts.map +1 -1
  61. package/dist/components/chat/types/processing.types.d.ts +90 -2
  62. package/dist/components/chat/types/processing.types.d.ts.map +1 -1
  63. package/dist/components/chat/utils/chunk-parser.d.ts +25 -0
  64. package/dist/components/chat/utils/chunk-parser.d.ts.map +1 -0
  65. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts +6 -34
  66. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
  67. package/dist/components/chat/utils/index.d.ts +3 -2
  68. package/dist/components/chat/utils/index.d.ts.map +1 -1
  69. package/dist/components/chat/utils/process-historical-messages.d.ts +11 -59
  70. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  71. package/dist/components/contact/index.cjs +3 -3
  72. package/dist/components/contact/index.js +2 -2
  73. package/dist/components/docs/index.cjs +5 -5
  74. package/dist/components/docs/index.js +4 -4
  75. package/dist/components/embeds/index.cjs +3 -3
  76. package/dist/components/embeds/index.js +2 -2
  77. package/dist/components/faq/index.cjs +3 -3
  78. package/dist/components/faq/index.js +2 -2
  79. package/dist/components/features/index.cjs +2 -2
  80. package/dist/components/features/index.js +1 -1
  81. package/dist/components/help-center-pages/index.cjs +20 -20
  82. package/dist/components/help-center-pages/index.js +11 -11
  83. package/dist/components/index.cjs +129 -171
  84. package/dist/components/index.cjs.map +1 -1
  85. package/dist/components/index.js +21 -63
  86. package/dist/components/index.js.map +1 -1
  87. package/dist/components/navigation/index.cjs +2 -2
  88. package/dist/components/navigation/index.js +1 -1
  89. package/dist/components/onboarding-guides/index.cjs +5 -5
  90. package/dist/components/onboarding-guides/index.js +4 -4
  91. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
  92. package/dist/components/related-content/index.cjs +3 -3
  93. package/dist/components/related-content/index.js +2 -2
  94. package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
  95. package/dist/components/tickets/index.cjs +5 -5
  96. package/dist/components/tickets/index.js +4 -4
  97. package/dist/components/ui/index.cjs +2 -44
  98. package/dist/components/ui/index.cjs.map +1 -1
  99. package/dist/components/ui/index.d.ts +2 -1
  100. package/dist/components/ui/index.d.ts.map +1 -1
  101. package/dist/components/ui/index.js +13 -55
  102. package/dist/components/ui/rich-markdown-renderer.d.ts +34 -0
  103. package/dist/components/ui/rich-markdown-renderer.d.ts.map +1 -0
  104. package/dist/components/ui/simple-markdown-renderer.d.ts +73 -0
  105. package/dist/components/ui/simple-markdown-renderer.d.ts.map +1 -0
  106. package/dist/index.cjs +2 -60
  107. package/dist/index.cjs.map +1 -1
  108. package/dist/index.js +13 -71
  109. package/dist/utils/index.cjs +40 -169
  110. package/dist/utils/index.cjs.map +1 -1
  111. package/dist/utils/index.d.ts +0 -1
  112. package/dist/utils/index.d.ts.map +1 -1
  113. package/dist/utils/index.js +41 -162
  114. package/dist/utils/index.js.map +1 -1
  115. package/dist/utils/markdown-section-extractor.d.ts.map +1 -1
  116. package/package.json +1 -7
  117. package/src/components/chat/__tests__/chat-message-list.test.tsx +5 -293
  118. package/src/components/chat/chat-message-enhanced.tsx +17 -111
  119. package/src/components/chat/chat-message-list.tsx +113 -367
  120. package/src/components/chat/embeddable-chat.tsx +0 -6
  121. package/src/components/chat/hooks/.index.md +1 -1
  122. package/src/components/chat/hooks/.use-nats-chat-adapter.md +1 -1
  123. package/src/components/chat/hooks/.use-realtime-chunk-processor.md +50 -0
  124. package/src/components/chat/hooks/index.ts +1 -0
  125. package/src/components/chat/hooks/use-chat.ts +0 -8
  126. package/src/components/chat/hooks/use-nats-chat-adapter.ts +822 -243
  127. package/src/components/chat/hooks/use-realtime-chunk-processor.ts +479 -0
  128. package/src/components/chat/hooks/use-sse-chat-adapter.ts +666 -318
  129. package/src/components/chat/index.ts +0 -5
  130. package/src/components/chat/thinking-display.tsx +1 -1
  131. package/src/components/chat/types/.api.types.md +3 -1
  132. package/src/components/chat/types/.processing.types.md +23 -6
  133. package/src/components/chat/types/api.types.ts +73 -6
  134. package/src/components/chat/types/processing.types.ts +52 -11
  135. package/src/components/chat/utils/.chunk-parser.md +51 -0
  136. package/src/components/chat/utils/.index.md +5 -0
  137. package/src/components/chat/utils/chunk-parser.ts +255 -0
  138. package/src/components/chat/utils/extract-incomplete-message-state.ts +5 -63
  139. package/src/components/chat/utils/index.ts +8 -5
  140. package/src/components/chat/utils/process-historical-messages.ts +383 -352
  141. package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +4 -22
  142. package/src/components/shared/legal-document/legal-document-page.tsx +1 -1
  143. package/src/components/shared/product-release/release-detail-page.tsx +4 -19
  144. package/src/components/ui/.simple-markdown-renderer.md +40 -0
  145. package/src/components/ui/index.ts +2 -1
  146. package/src/components/ui/release-changelog-section.tsx +1 -1
  147. package/src/components/ui/rich-markdown-renderer.tsx +1223 -0
  148. package/src/components/ui/simple-markdown-renderer.tsx +965 -0
  149. package/src/components/ui/ticket-info-section.tsx +1 -1
  150. package/src/utils/index.ts +0 -1
  151. package/src/utils/markdown-section-extractor.ts +56 -41
  152. package/dist/chat-protocol/decode.d.ts +0 -58
  153. package/dist/chat-protocol/decode.d.ts.map +0 -1
  154. package/dist/chat-protocol/encode.d.ts +0 -44
  155. package/dist/chat-protocol/encode.d.ts.map +0 -1
  156. package/dist/chat-protocol/env-flag.d.ts +0 -35
  157. package/dist/chat-protocol/env-flag.d.ts.map +0 -1
  158. package/dist/chat-protocol/events.d.ts +0 -192
  159. package/dist/chat-protocol/events.d.ts.map +0 -1
  160. package/dist/chat-protocol/frames.d.ts +0 -206
  161. package/dist/chat-protocol/frames.d.ts.map +0 -1
  162. package/dist/chat-protocol/index.cjs +0 -493
  163. package/dist/chat-protocol/index.cjs.map +0 -1
  164. package/dist/chat-protocol/index.d.ts +0 -18
  165. package/dist/chat-protocol/index.d.ts.map +0 -1
  166. package/dist/chat-protocol/index.js +0 -477
  167. package/dist/chat-protocol/index.js.map +0 -1
  168. package/dist/chat-protocol/ip-normalize.d.ts +0 -44
  169. package/dist/chat-protocol/ip-normalize.d.ts.map +0 -1
  170. package/dist/chat-protocol/nats-decoder.d.ts +0 -26
  171. package/dist/chat-protocol/nats-decoder.d.ts.map +0 -1
  172. package/dist/chunk-ADD3FPDG.cjs.map +0 -1
  173. package/dist/chunk-BQ7PMWLM.cjs.map +0 -1
  174. package/dist/chunk-LJKZIIK4.js.map +0 -1
  175. package/dist/chunk-P5JQDFRM.cjs.map +0 -1
  176. package/dist/chunk-REWRUV3K.js.map +0 -1
  177. package/dist/chunk-VXJH5TDN.js.map +0 -1
  178. package/dist/components/chat/stream/chat-dialog-store.d.ts +0 -184
  179. package/dist/components/chat/stream/chat-dialog-store.d.ts.map +0 -1
  180. package/dist/components/chat/stream/chat-stream-reducer.d.ts +0 -367
  181. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +0 -1
  182. package/dist/components/chat/stream/delta-batcher.d.ts +0 -51
  183. package/dist/components/chat/stream/delta-batcher.d.ts.map +0 -1
  184. package/dist/components/chat/stream/index.d.ts +0 -17
  185. package/dist/components/chat/stream/index.d.ts.map +0 -1
  186. package/dist/components/chat/stream/message-mutations.d.ts +0 -77
  187. package/dist/components/chat/stream/message-mutations.d.ts.map +0 -1
  188. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts +0 -22
  189. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts.map +0 -1
  190. package/dist/components/ui/markdown/base-components.d.ts +0 -56
  191. package/dist/components/ui/markdown/base-components.d.ts.map +0 -1
  192. package/dist/components/ui/markdown/engine.d.ts +0 -99
  193. package/dist/components/ui/markdown/engine.d.ts.map +0 -1
  194. package/dist/components/ui/markdown/heading-ids.d.ts +0 -65
  195. package/dist/components/ui/markdown/heading-ids.d.ts.map +0 -1
  196. package/dist/components/ui/markdown/index.d.ts +0 -18
  197. package/dist/components/ui/markdown/index.d.ts.map +0 -1
  198. package/dist/components/ui/markdown/mermaid-diagram.d.ts +0 -79
  199. package/dist/components/ui/markdown/mermaid-diagram.d.ts.map +0 -1
  200. package/dist/components/ui/markdown/rich/embed-overrides.d.ts +0 -14
  201. package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +0 -1
  202. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts +0 -40
  203. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts.map +0 -1
  204. package/dist/components/ui/markdown/rich/shortcodes.d.ts +0 -8
  205. package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +0 -1
  206. package/dist/components/ui/markdown/sanitize.d.ts +0 -153
  207. package/dist/components/ui/markdown/sanitize.d.ts.map +0 -1
  208. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts +0 -23
  209. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts.map +0 -1
  210. package/dist/components/ui/markdown/streaming.d.ts +0 -78
  211. package/dist/components/ui/markdown/streaming.d.ts.map +0 -1
  212. package/dist/components/ui/markdown/text-size.d.ts +0 -34
  213. package/dist/components/ui/markdown/text-size.d.ts.map +0 -1
  214. package/dist/utils/markdown-fences.d.ts +0 -42
  215. package/dist/utils/markdown-fences.d.ts.map +0 -1
  216. package/dist/utils/markdown-heading-id.d.ts +0 -85
  217. package/dist/utils/markdown-heading-id.d.ts.map +0 -1
  218. package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +0 -307
  219. package/src/chat-protocol/__tests__/chat-protocol.test.ts +0 -515
  220. package/src/chat-protocol/__tests__/env-flag.test.ts +0 -52
  221. package/src/chat-protocol/__tests__/ip-normalize.test.ts +0 -136
  222. package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +0 -228
  223. package/src/chat-protocol/decode.ts +0 -277
  224. package/src/chat-protocol/encode.ts +0 -71
  225. package/src/chat-protocol/env-flag.ts +0 -39
  226. package/src/chat-protocol/events.ts +0 -228
  227. package/src/chat-protocol/frames.ts +0 -238
  228. package/src/chat-protocol/index.ts +0 -21
  229. package/src/chat-protocol/ip-normalize.ts +0 -146
  230. package/src/chat-protocol/nats-decoder.ts +0 -252
  231. package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +0 -207
  232. package/src/components/chat/__tests__/chat-pending-turn.test.tsx +0 -209
  233. package/src/components/chat/hooks/__tests__/__snapshots__/persisted-state-v1-golden.test.ts.snap +0 -297
  234. package/src/components/chat/hooks/__tests__/__snapshots__/sse-stream-golden.test.ts.snap +0 -0
  235. package/src/components/chat/hooks/__tests__/persisted-state-v1-golden.test.ts +0 -272
  236. package/src/components/chat/hooks/__tests__/sse-stream-golden.test.ts +0 -318
  237. package/src/components/chat/stream/__tests__/__snapshots__/chat-stream-reducer-golden.test.ts.snap +0 -937
  238. package/src/components/chat/stream/__tests__/chat-dialog-store.test.ts +0 -756
  239. package/src/components/chat/stream/__tests__/chat-stream-reducer-golden.test.ts +0 -460
  240. package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +0 -894
  241. package/src/components/chat/stream/__tests__/delta-batcher.test.ts +0 -255
  242. package/src/components/chat/stream/__tests__/use-chat-stream-reducer.test.ts +0 -98
  243. package/src/components/chat/stream/chat-dialog-store.ts +0 -536
  244. package/src/components/chat/stream/chat-stream-reducer.ts +0 -1796
  245. package/src/components/chat/stream/delta-batcher.ts +0 -159
  246. package/src/components/chat/stream/index.ts +0 -55
  247. package/src/components/chat/stream/message-mutations.ts +0 -338
  248. package/src/components/chat/stream/use-chat-stream-reducer.ts +0 -126
  249. package/src/components/chat/utils/__tests__/__snapshots__/process-historical-messages-golden.test.ts.snap +0 -418
  250. package/src/components/chat/utils/__tests__/__snapshots__/segment-accumulator-golden.test.ts.snap +0 -599
  251. package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +0 -317
  252. package/src/components/chat/utils/__tests__/segment-accumulator-golden.test.ts +0 -270
  253. package/src/components/ui/__tests__/__snapshots__/markdown-parity.test.tsx.snap +0 -2420
  254. package/src/components/ui/__tests__/markdown-parity.test.tsx +0 -2528
  255. package/src/components/ui/markdown/__tests__/mermaid-security.test.ts +0 -115
  256. package/src/components/ui/markdown/__tests__/mermaid-stale-render.test.tsx +0 -95
  257. package/src/components/ui/markdown/__tests__/sanitize-invariant.test.ts +0 -309
  258. package/src/components/ui/markdown/__tests__/streaming.test.tsx +0 -388
  259. package/src/components/ui/markdown/base-components.tsx +0 -341
  260. package/src/components/ui/markdown/engine.tsx +0 -315
  261. package/src/components/ui/markdown/heading-ids.ts +0 -239
  262. package/src/components/ui/markdown/index.ts +0 -49
  263. package/src/components/ui/markdown/mermaid-diagram.tsx +0 -291
  264. package/src/components/ui/markdown/rich/embed-overrides.tsx +0 -199
  265. package/src/components/ui/markdown/rich/rich-markdown-renderer.tsx +0 -184
  266. package/src/components/ui/markdown/rich/shortcodes.ts +0 -170
  267. package/src/components/ui/markdown/sanitize.ts +0 -3499
  268. package/src/components/ui/markdown/simple-markdown-renderer.tsx +0 -28
  269. package/src/components/ui/markdown/streaming.ts +0 -362
  270. package/src/components/ui/markdown/text-size.ts +0 -106
  271. package/src/utils/markdown-fences.ts +0 -127
  272. package/src/utils/markdown-heading-id.ts +0 -348
  273. /package/dist/{chunk-CGNDAJ7V.js.map → chunk-6B27O3ZB.js.map} +0 -0
  274. /package/dist/{chunk-4R6H2U77.js.map → chunk-H7ETLG65.js.map} +0 -0
  275. /package/dist/{chunk-LAOO7XHO.js.map → chunk-HWTUVLYB.js.map} +0 -0
  276. /package/dist/{chunk-F2NMWV44.js.map → chunk-IDKDOQZL.js.map} +0 -0
  277. /package/dist/{chunk-QO2XD3FR.js.map → chunk-KQGFKG6Y.js.map} +0 -0
  278. /package/dist/{chunk-OR5AMXYN.js.map → chunk-R5QYMUKN.js.map} +0 -0
  279. /package/dist/{chunk-XF5ROTMQ.js.map → chunk-U2YDR5DT.js.map} +0 -0
  280. /package/dist/{chunk-UI7CIUAR.js.map → chunk-WGJPTFRH.js.map} +0 -0
  281. /package/dist/{chunk-APF5QBAU.js.map → chunk-YQBPIUZU.js.map} +0 -0
@@ -74,11 +74,6 @@ export * from './types'
74
74
  // Chat hooks
75
75
  export * from './hooks'
76
76
 
77
- // Chat stream module — master reducer + dialog store + React wrapper
78
- // (Phase 3 of the chat unification; ONE accumulation path for SSE + NATS
79
- // + history replay).
80
- export * from './stream'
81
-
82
77
  // Chat utilities
83
78
  export * from './utils'
84
79
 
@@ -4,7 +4,7 @@ import { forwardRef, useState } from "react"
4
4
 
5
5
  import { cn } from "../../utils/cn"
6
6
  import { DotsLoaderIcon } from "../icons-v2-generated"
7
- import { SimpleMarkdownRenderer } from "../ui/markdown/simple-markdown-renderer"
7
+ import { SimpleMarkdownRenderer } from "../ui/simple-markdown-renderer"
8
8
  import { ExpandChevron } from "./expand-chevron"
9
9
  import { useCollapsible } from "./hooks/use-collapsible"
10
10
  import type { ThinkingDisplayProps } from "./types"
@@ -9,6 +9,7 @@ Defines TypeScript interfaces for API interactions and React hook contracts used
9
9
  | `UseChunkCatchupOptions` | Configure chunk catch-up replay with optional custom fetch function |
10
10
  | `UseNatsDialogSubscriptionOptions` | Core NATS WebSocket subscription config with reconnection backoff tuning |
11
11
  | `UseJetStreamDialogSubscriptionOptions` | JetStream-based subscription with sequence-based resume support |
12
+ | `UseRealtimeChunkProcessorOptions` | Controls how streaming chunks are parsed, including approval routing and batch mode |
12
13
 
13
14
  ### Hook Return Types
14
15
  | Interface | Purpose |
@@ -16,6 +17,7 @@ Defines TypeScript interfaces for API interactions and React hook contracts used
16
17
  | `UseChunkCatchupReturn` | Exposes catchup, processing, and buffering controls |
17
18
  | `UseNatsDialogSubscriptionReturn` | Connection state and reconnection counter |
18
19
  | `UseJetStreamDialogSubscriptionReturn` | Connection state plus current stream sequence |
20
+ | `UseRealtimeChunkProcessorReturn` | Chunk processing, segment access, and approval status management |
19
21
 
20
22
  ### Callbacks
21
23
  - **`RealtimeChunkCallbacks`** — Rich event callback map for streaming events: `onStreamStart`, `onStreamEnd`, `onSegmentsUpdate`, `onTokenUsage`, `onApprove`, `onReject`, `onEscalatedApproval`, `onApprovalResolved`, `onToolExecuted`, `onDialogClosed`, and more.
@@ -29,7 +31,7 @@ Defines TypeScript interfaces for API interactions and React hook contracts used
29
31
  ## Usage Example
30
32
 
31
33
  ```typescript
32
- const options: ChatStreamReducerOptions = {
34
+ const options: UseRealtimeChunkProcessorOptions = {
33
35
  callbacks: {
34
36
  onSegmentsUpdate: (segments, meta) => {
35
37
  if (meta?.append) appendToLastMessage(segments)
@@ -5,9 +5,13 @@ Defines TypeScript types for message processing pipeline operations, including c
5
5
 
6
6
  | Export | Type | Description |
7
7
  |--------|------|-------------|
8
+ | `ParsedChunkAction` | Union type | Discriminated union of all possible chunk parse results (text, tool execution, approvals, metadata, etc.) |
8
9
  | `AccumulatorState` | Interface | Stateful container tracking message segments, pending approvals, and executing tools during stream processing |
9
10
  | `PendingApproval` | Interface | Represents an approval request awaiting user action (command, explanation, type) |
10
11
  | `MessageProcessingOptions` | Interface | Configuration for assistant rendering, approval callbacks, chat filtering, and batch approval UI |
12
+ | `ChunkProcessor` | Interface | Contract for processing raw `ChunkData` into `ParsedChunkAction` results |
13
+ | `ChunkProcessorOptions` | Interface | Lifecycle callbacks for chunk processing events |
14
+ | `MessageTransformer` | Interface | Contract for transforming raw inputs into `ProcessedMessage` objects |
11
15
  | `StreamProcessor` | Interface | Full lifecycle control (start/stop/pause/resume/reset) for stream consumers |
12
16
  | `StreamState` | Interface | Snapshot of stream health: active status, buffered chunks, error log, and last activity timestamp |
13
17
  | `BufferManager` | Interface | CRUD-style contract for managing `ChunkData` buffers |
@@ -17,11 +21,29 @@ Defines TypeScript types for message processing pipeline operations, including c
17
21
 
18
22
  ```typescript
19
23
  import type {
24
+ ParsedChunkAction,
20
25
  AccumulatorState,
21
26
  MessageProcessingOptions,
22
27
  StreamState
23
28
  } from './processing.types'
24
29
 
30
+ // Handling a parsed chunk action via discriminated union
31
+ function handleChunkAction(action: ParsedChunkAction): void {
32
+ switch (action.action) {
33
+ case 'text':
34
+ console.log('Received text:', action.text)
35
+ break
36
+ case 'tool_execution':
37
+ console.log('Tool executing:', action.segment)
38
+ break
39
+ case 'approval_batch':
40
+ console.log('Batch approval request:', action.requestId, action.toolCalls)
41
+ break
42
+ case 'error':
43
+ console.error('Processing error:', action.error, action.details)
44
+ break
45
+ }
46
+ }
25
47
 
26
48
  // Initializing accumulator state
27
49
  const state: AccumulatorState = {
@@ -43,9 +65,4 @@ const options: MessageProcessingOptions = {
43
65
  logRejection(requestId)
44
66
  }
45
67
  }
46
- ```
47
-
48
- > NOTE: the wire-chunk vocabulary now lives in `src/chat-protocol/events.ts`
49
- > (`ChatStreamEvent`, produced by `decodeNatsChunk`). The old
50
- > `ParsedChunkAction` union and the `ChunkProcessor` contract were deleted
51
- > together with the legacy `chunk-parser`.
68
+ ```
@@ -213,12 +213,79 @@ export interface RealtimeChunkCallbacks {
213
213
  onDialogClosed?: () => void
214
214
  }
215
215
 
216
- // NOTE: `UseRealtimeChunkProcessorOptions` / `UseRealtimeChunkProcessorReturn`
217
- // were DELETED along with the `useRealtimeChunkProcessor` compat hook. The
218
- // equivalents are `ChatStreamReducerOptions` + `ChatStreamReducer` in
219
- // `../stream/chat-stream-reducer`. `RealtimeChunkCallbacks` above SURVIVES:
220
- // it is still the canonical description of the effect names/args the reducer
221
- // emits via `ChatReducerEffect`.
216
+ export interface UseRealtimeChunkProcessorOptions {
217
+ callbacks: RealtimeChunkCallbacks
218
+ /**
219
+ * Filter approval types that should be displayed directly
220
+ * Others will trigger onEscalatedApproval
221
+ * Default: ['CLIENT']
222
+ */
223
+ displayApprovalTypes?: string[]
224
+ /**
225
+ * Map of existing approval statuses
226
+ */
227
+ approvalStatuses?: Record<string, ChatApprovalStatus>
228
+ /**
229
+ * Initialize accumulator with existing state from incomplete historical message
230
+ * Used to continue building messages across page refreshes or reconnections
231
+ */
232
+ initialState?: {
233
+ /** Existing segments to continue building upon */
234
+ existingSegments?: MessageSegment[]
235
+ /** Pending approvals that haven't been resolved */
236
+ pendingApprovals?: Map<string, { command: string; explanation?: string; approvalType: string }>
237
+ /** Executing tools waiting for completion */
238
+ executingTools?: Map<string, ExecutingToolState>
239
+ /** Escalated approvals */
240
+ escalatedApprovals?: Map<
241
+ string,
242
+ { command: string; explanation?: string; approvalType: string; toolCalls?: PendingToolCallData[] }
243
+ >
244
+ }
245
+ /**
246
+ * Consumer-owned (e.g. set in `openframe-oss-tenant` chat client via the
247
+ * `'batch-approvals'` feature flag and forwarded here). The lib does NOT
248
+ * default this to a batch-on behavior — when omitted it falls back to the
249
+ * legacy single-card rendering.
250
+ *
251
+ * When true: `APPROVAL_REQUEST` chunks containing `toolCalls[]` are rendered
252
+ * as a single batch card. When false / omitted: the batch is split into N
253
+ * legacy approval cards (one per tool that requires approval), all sharing
254
+ * the same `approvalRequestId`. Tools with `requiresApproval=false` are
255
+ * dropped from the UI in the unfolded mode.
256
+ */
257
+ batchApprovalsEnabled?: boolean
258
+ /**
259
+ * Engages the direct-mode barrier optimistically: once true, all AI-assistant
260
+ * chunks (text, thinking, tool executions, approvals, metadata, errors, etc.)
261
+ * are dropped and any open stream is torn down, leaving only human/control
262
+ * messages. A `DIRECT_MESSAGE` chunk engages the same barrier automatically
263
+ * regardless of this flag. Pass this from a host that already knows the dialog
264
+ * switched to direct mode (e.g. an optimistic mode toggle) to close the race
265
+ * window before the server emits the first `DIRECT_MESSAGE`.
266
+ *
267
+ * Do NOT set this when the host reconstructs history by replaying chunks
268
+ * through `processChunk` — a static flag would drop legitimate pre-switch AI
269
+ * history. Rely on the in-order `DIRECT_MESSAGE` detection instead.
270
+ */
271
+ isDirectMode?: boolean
272
+ }
273
+
274
+ export interface UseRealtimeChunkProcessorReturn {
275
+ /** Process a single chunk */
276
+ processChunk: (chunk: unknown) => void
277
+ /** Get current segments */
278
+ getSegments: () => MessageSegment[]
279
+ /** Reset the accumulator */
280
+ reset: () => void
281
+ /** Update approval status for a request */
282
+ updateApprovalStatus: (requestId: string, status: ChatApprovalStatus, resolvedByName?: string | null) => MessageSegment[]
283
+ /** Get pending approval requests */
284
+ getPendingApprovals: () => Map<
285
+ string,
286
+ { command: string; explanation?: string; approvalType: string; toolCalls?: PendingToolCallData[] }
287
+ >
288
+ }
222
289
 
223
290
  // ========== API Request Types ==========
224
291
 
@@ -3,14 +3,37 @@
3
3
  * Contains types for message parsing, accumulation, and processing
4
4
  */
5
5
 
6
- import type { MessageSegment, PendingToolCallData, ExecutingToolState } from './message.types'
6
+ import type { MessageSegment, ProcessedMessage, ToolExecutionSegment, TokenUsageData, PendingToolCallData, ExecutingToolState } from './message.types'
7
7
  import type { ChatApprovalStatus, AssistantType } from './chat.types'
8
- import type { ChunkData } from './network.types'
9
-
10
- // NOTE: the `ParsedChunkAction` union that used to live here was DELETED
11
- // alongside the legacy `chunk-parser`. The wire → normalized-event
12
- // vocabulary is now `ChatStreamEvent` in `src/chat-protocol/events.ts`,
13
- // produced by `decodeNatsChunk`.
8
+ import type { ChunkData, NatsMessageType } from './network.types'
9
+
10
+ // ========== Parsed Chunk Result Types ==========
11
+
12
+ export type ParsedChunkAction =
13
+ | { action: 'message_start' }
14
+ | { action: 'message_end' }
15
+ | { action: 'error'; error: string; details?: string }
16
+ | { action: 'metadata'; modelDisplayName: string; modelName: string; providerName: string; contextWindow: number }
17
+ | { action: 'text'; text: string }
18
+ | { action: 'thinking'; text: string }
19
+ | { action: 'tool_execution'; segment: ToolExecutionSegment }
20
+ | { action: 'approval_request'; requestId: string; command: string; explanation?: string; approvalType: string }
21
+ | { action: 'approval_batch'; requestId: string; approvalType: string; toolCalls: PendingToolCallData[] }
22
+ | { action: 'approval_result'; requestId: string; approved: boolean; approvalType: string; resolvedByName?: string | null }
23
+ | {
24
+ action: 'message_request'
25
+ text: string
26
+ ownerType?: string
27
+ displayName?: string
28
+ userId?: string
29
+ contextItems?: Array<{ type: string; id: string }>
30
+ }
31
+ | { action: 'token_usage'; data: TokenUsageData }
32
+ | { action: 'direct_message'; text: string; ownerType?: string; displayName?: string; userId?: string }
33
+ | { action: 'system'; text: string }
34
+ | { action: 'context_compaction_start' }
35
+ | { action: 'context_compaction_end'; summary?: string }
36
+ | { action: 'dialog_closed' }
14
37
 
15
38
  // ========== Accumulator State ==========
16
39
 
@@ -58,13 +81,31 @@ export interface MessageProcessingOptions {
58
81
  batchApprovalsEnabled?: boolean
59
82
  }
60
83
 
61
- // NOTE: `ChunkProcessorOptions` (the options bag of the deleted
62
- // `ChunkProcessor`) and `MessageTransformer` were DELETED — both were left
63
- // behind by the chunk-processor removal with zero references in lib, hub, or
64
- // app.
84
+ // ========== Chunk Processing Types ==========
85
+
86
+ export interface ChunkProcessor {
87
+ processChunk: (chunk: ChunkData, messageType: NatsMessageType) => ParsedChunkAction | null
88
+ reset: () => void
89
+ }
90
+
91
+ export interface ChunkProcessorOptions {
92
+ onMessageStart?: () => void
93
+ onMessageEnd?: () => void
94
+ onError?: (error: string, details?: string) => void
95
+ onText?: (text: string) => void
96
+ onToolExecution?: (segment: MessageSegment) => void
97
+ onApprovalRequest?: (data: any) => void
98
+ onApprovalResult?: (data: any) => void
99
+ onMetadata?: (data: any) => void
100
+ }
65
101
 
66
102
  // ========== Message Transformation Types ==========
67
103
 
104
+ export interface MessageTransformer {
105
+ transform: (input: any) => ProcessedMessage | null
106
+ batch: (inputs: any[]) => ProcessedMessage[]
107
+ }
108
+
68
109
  export interface TransformationOptions {
69
110
  preserveOriginal?: boolean
70
111
  mergeSegments?: boolean
@@ -0,0 +1,51 @@
1
+ <!-- source-hash: 637d22f77dbdb02b04013632095ddfdb -->
2
+ Utilities for parsing raw NATS message chunks into typed, structured action objects consumed by the AI message pipeline.
3
+
4
+ ## Key Components
5
+
6
+ ### `parseChunkToAction(chunk)`
7
+ Core export that maps a raw `ChunkData` object to a `ParsedChunkAction` union type. Uses a `switch` on `MESSAGE_TYPE` to handle all supported message variants:
8
+
9
+ | Action | Description |
10
+ |---|---|
11
+ | `message_start` / `message_end` | Lifecycle control signals |
12
+ | `metadata` | AI model/provider info |
13
+ | `text` / `thinking` / `system` | Content streaming |
14
+ | `tool_execution` | Tool start/completion events |
15
+ | `approval_request` / `approval_batch` | Single or multi-tool approval flows |
16
+ | `approval_result` | Approval outcome |
17
+ | `token_usage` | Token consumption stats |
18
+ | `context_compaction_start/end` | Context window management |
19
+ | `direct_message` | Owner-attributed messages |
20
+ | `dialog_closed` | Session termination |
21
+
22
+ Returns `null` for unknown or malformed chunks.
23
+
24
+ ### `normalizeToolCalls(raw)` *(internal)*
25
+ Coerces an unknown array into `PendingToolCallData[]`, safely defaulting missing fields and enforcing correct types.
26
+
27
+ ### Guard Helpers
28
+ Lightweight boolean checks for common chunk classifications:
29
+
30
+ - `isControlChunk(chunk)` — start/end signals
31
+ - `isErrorChunk(chunk)` — error type
32
+ - `isMetadataChunk(chunk)` — AI metadata
33
+ - `extractTextFromChunk(chunk)` — returns text content or `null`
34
+
35
+ ## Usage Example
36
+
37
+ ```typescript
38
+ import { parseChunkToAction, isErrorChunk } from './chunk-parser'
39
+
40
+ // Incoming NATS message payload
41
+ const raw = { type: 'TEXT', text: 'Hello from Mingo AI' }
42
+
43
+ if (isErrorChunk(raw)) {
44
+ console.error('Stream error received')
45
+ }
46
+
47
+ const action = parseChunkToAction(raw)
48
+ if (action?.action === 'text') {
49
+ renderText(action.text) // "Hello from Mingo AI"
50
+ }
51
+ ```
@@ -5,6 +5,7 @@ Barrel export module that consolidates and re-exports all message processing, ch
5
5
 
6
6
  | Category | Exports |
7
7
  |----------|---------|
8
+ | **Chunk Parsing** | `parseChunkToAction`, `isControlChunk`, `isErrorChunk`, `isMetadataChunk`, `extractTextFromChunk` |
8
9
  | **Message Accumulation** | `MessageSegmentAccumulator`, `createMessageSegmentAccumulator`, `AccumulatorCallbacks` |
9
10
  | **Historical Messages** | `processHistoricalMessages`, `extractErrorMessages`, `processHistoricalMessagesWithErrors` |
10
11
  | **Chat Navigation** | `handleChatNavClick`, `resolveExternalNavigation`, `decideNewTab`, `computeIsNewTab`, `buildAnchorProps` |
@@ -19,6 +20,7 @@ Barrel export module that consolidates and re-exports all message processing, ch
19
20
 
20
21
  ```typescript
21
22
  import {
23
+ parseChunkToAction,
22
24
  createMessageSegmentAccumulator,
23
25
  handleChatNavClick,
24
26
  getChatProxyAuth,
@@ -28,6 +30,9 @@ import {
28
30
  getStatusColorScheme,
29
31
  } from '@flamingo/chat-utils'
30
32
 
33
+ // Parse a streaming chunk from the AI response
34
+ const action = parseChunkToAction(rawChunk)
35
+
31
36
  // Accumulate streamed message segments
32
37
  const accumulator = createMessageSegmentAccumulator({
33
38
  onSegmentComplete: (segment) => console.log('Segment:', segment),
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Utilities for parsing NATS chunks into structured actions
3
+ */
4
+
5
+ import { MESSAGE_TYPE, type ChunkData, type ParsedChunkAction, type ToolExecutionSegment, type PendingToolCallData } from '../types'
6
+
7
+ function normalizeToolCalls(raw: unknown): PendingToolCallData[] {
8
+ if (!Array.isArray(raw)) return []
9
+ return raw
10
+ .filter((item): item is Record<string, any> => !!item && typeof item === 'object')
11
+ .map((item) => ({
12
+ toolExecutionRequestId: String(item.toolExecutionRequestId ?? ''),
13
+ toolName: String(item.toolName ?? ''),
14
+ toolTitle: typeof item.toolTitle === 'string' ? item.toolTitle : undefined,
15
+ toolExplanation: typeof item.toolExplanation === 'string' ? item.toolExplanation : undefined,
16
+ toolType: typeof item.toolType === 'string' ? item.toolType : undefined,
17
+ requiresApproval: item.requiresApproval === true,
18
+ approvalType: typeof item.approvalType === 'string' ? item.approvalType : null,
19
+ toolCallArguments:
20
+ item.toolCallArguments && typeof item.toolCallArguments === 'object'
21
+ ? (item.toolCallArguments as Record<string, any>)
22
+ : null,
23
+ }))
24
+ }
25
+
26
+ /**
27
+ * Parse a raw NATS chunk into a structured action
28
+ */
29
+ export function parseChunkToAction(chunk: unknown): ParsedChunkAction | null {
30
+ if (!chunk || typeof chunk !== 'object') return null
31
+
32
+ const data = chunk as ChunkData
33
+ const type = String(data.type || '')
34
+
35
+ switch (type) {
36
+ case MESSAGE_TYPE.MESSAGE_START:
37
+ return { action: 'message_start' }
38
+
39
+ case MESSAGE_TYPE.MESSAGE_END:
40
+ return { action: 'message_end' }
41
+
42
+ case MESSAGE_TYPE.AI_METADATA: {
43
+ const providerName = data.providerName || data.provider
44
+ if (typeof data.modelName === 'string' && typeof providerName === 'string') {
45
+ return {
46
+ action: 'metadata',
47
+ modelDisplayName: data.modelDisplayName,
48
+ modelName: data.modelName,
49
+ providerName,
50
+ contextWindow: typeof data.contextWindow === 'number' ? data.contextWindow : 0,
51
+ }
52
+ }
53
+ return null
54
+ }
55
+
56
+ case MESSAGE_TYPE.TEXT:
57
+ if (typeof data.text === 'string') {
58
+ return { action: 'text', text: data.text }
59
+ }
60
+ return null
61
+
62
+ case MESSAGE_TYPE.THINKING:
63
+ if (typeof data.text === 'string') {
64
+ return { action: 'thinking', text: data.text }
65
+ }
66
+ return null
67
+
68
+ case MESSAGE_TYPE.EXECUTING_TOOL:
69
+ return {
70
+ action: 'tool_execution',
71
+ segment: {
72
+ type: 'tool_execution',
73
+ data: {
74
+ type: 'EXECUTING_TOOL',
75
+ integratedToolType: data.integratedToolType || '',
76
+ toolFunction: data.toolFunction || '',
77
+ toolTitle: typeof data.title === 'string' ? data.title : undefined,
78
+ parameters: data.parameters,
79
+ toolExecutionRequestId: typeof data.toolExecutionRequestId === 'string' ? data.toolExecutionRequestId : undefined,
80
+ }
81
+ }
82
+ }
83
+
84
+ case MESSAGE_TYPE.EXECUTED_TOOL:
85
+ return {
86
+ action: 'tool_execution',
87
+ segment: {
88
+ type: 'tool_execution',
89
+ data: {
90
+ type: 'EXECUTED_TOOL',
91
+ integratedToolType: data.integratedToolType || '',
92
+ toolFunction: data.toolFunction || '',
93
+ toolTitle: typeof data.title === 'string' ? data.title : undefined,
94
+ parameters: data.parameters,
95
+ result: data.result,
96
+ success: data.success,
97
+ toolExecutionRequestId: typeof data.toolExecutionRequestId === 'string' ? data.toolExecutionRequestId : undefined,
98
+ }
99
+ }
100
+ }
101
+
102
+ case MESSAGE_TYPE.APPROVAL_REQUEST: {
103
+ const requestId = data.approvalRequestId || data.approval_request_id || ''
104
+ const approvalType = data.approvalType || 'USER'
105
+ const toolCalls = normalizeToolCalls(data.toolCalls)
106
+
107
+ if (toolCalls.length > 0) {
108
+ return {
109
+ action: 'approval_batch',
110
+ requestId,
111
+ approvalType,
112
+ toolCalls,
113
+ }
114
+ }
115
+
116
+ return {
117
+ action: 'approval_request',
118
+ requestId,
119
+ command: data.command || '',
120
+ explanation: data.explanation,
121
+ approvalType,
122
+ }
123
+ }
124
+
125
+ case MESSAGE_TYPE.APPROVAL_RESULT: {
126
+ // The realtime NATS chunk carries the resolver's name as `displayName`; the
127
+ // persisted GraphQL message exposes the same value as `resolvedByName`. Accept
128
+ // either so realtime and history-replay render "by {name}" identically.
129
+ const resolvedByName =
130
+ typeof data.resolvedByName === 'string'
131
+ ? data.resolvedByName
132
+ : typeof data.displayName === 'string'
133
+ ? data.displayName
134
+ : undefined
135
+ return {
136
+ action: 'approval_result',
137
+ requestId: data.approvalRequestId || data.approval_request_id || '',
138
+ approved: data.approved === true,
139
+ approvalType: data.approvalType || 'CLIENT',
140
+ resolvedByName,
141
+ }
142
+ }
143
+
144
+ case MESSAGE_TYPE.ERROR:
145
+ return {
146
+ action: 'error',
147
+ error: data.error || 'An error occurred',
148
+ details: data.details,
149
+ }
150
+
151
+ case MESSAGE_TYPE.MESSAGE_REQUEST:
152
+ return {
153
+ action: 'message_request',
154
+ text: String(data.text || ''),
155
+ ownerType: typeof data.ownerType === 'string' ? data.ownerType : undefined,
156
+ displayName: typeof data.displayName === 'string' ? data.displayName : undefined,
157
+ userId: typeof data.userId === 'string' ? data.userId : undefined,
158
+ // Entity-context refs the user attached to this message (backend
159
+ // `MESSAGE_REQUEST` chunk → `contextItems: [{ type, id }]`). The wire
160
+ // shape carries no label; the host resolves display text + icon. `label`
161
+ // is REQUIRED on `ChatContextItem`, so fall back to the id (same as the
162
+ // historical path in `process-historical-messages.ts`) — keeps both
163
+ // message sources producing an identical shape.
164
+ contextItems: Array.isArray(data.contextItems)
165
+ ? (data.contextItems as Array<{ type?: unknown; id?: unknown }>)
166
+ .filter((it) => typeof it?.type === 'string' && typeof it?.id === 'string')
167
+ .map((it) => ({ type: it.type as string, id: it.id as string, label: it.id as string }))
168
+ : undefined,
169
+ }
170
+
171
+ case MESSAGE_TYPE.TOKEN_USAGE:
172
+ return {
173
+ action: 'token_usage',
174
+ data: {
175
+ inputTokensSize: data.inputTokensSize ?? 0,
176
+ outputTokensSize: data.outputTokensSize ?? 0,
177
+ totalTokensSize: data.totalTokensSize ?? 0,
178
+ contextSize: data.contextSize ?? 0,
179
+ },
180
+ }
181
+
182
+ case MESSAGE_TYPE.CONTEXT_COMPACTION_START:
183
+ return { action: 'context_compaction_start' }
184
+
185
+ case MESSAGE_TYPE.CONTEXT_COMPACTION_END:
186
+ return {
187
+ action: 'context_compaction_end',
188
+ summary: typeof data.text === 'string' ? data.text : undefined,
189
+ }
190
+
191
+ case MESSAGE_TYPE.SYSTEM:
192
+ if (typeof data.text === 'string') {
193
+ return { action: 'system', text: data.text }
194
+ }
195
+ return null
196
+
197
+ case MESSAGE_TYPE.DIRECT_MESSAGE:
198
+ if (typeof data.text === 'string') {
199
+ return {
200
+ action: 'direct_message',
201
+ text: data.text,
202
+ ownerType: typeof data.ownerType === 'string' ? data.ownerType : undefined,
203
+ displayName: typeof data.displayName === 'string' ? data.displayName : undefined,
204
+ userId: typeof data.userId === 'string' ? data.userId : undefined,
205
+ }
206
+ }
207
+ return null
208
+
209
+ case MESSAGE_TYPE.DIALOG_CLOSED:
210
+ return { action: 'dialog_closed' }
211
+
212
+ default:
213
+ return null
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Check if a chunk represents a control message (start/end)
219
+ */
220
+ export function isControlChunk(chunk: unknown): boolean {
221
+ if (!chunk || typeof chunk !== 'object') return false
222
+ const data = chunk as ChunkData
223
+ const type = String(data.type || '')
224
+ return type === MESSAGE_TYPE.MESSAGE_START || type === MESSAGE_TYPE.MESSAGE_END
225
+ }
226
+
227
+ /**
228
+ * Check if a chunk represents an error
229
+ */
230
+ export function isErrorChunk(chunk: unknown): boolean {
231
+ if (!chunk || typeof chunk !== 'object') return false
232
+ const data = chunk as ChunkData
233
+ return data.type === MESSAGE_TYPE.ERROR
234
+ }
235
+
236
+ /**
237
+ * Check if a chunk represents metadata
238
+ */
239
+ export function isMetadataChunk(chunk: unknown): boolean {
240
+ if (!chunk || typeof chunk !== 'object') return false
241
+ const data = chunk as ChunkData
242
+ return data.type === MESSAGE_TYPE.AI_METADATA
243
+ }
244
+
245
+ /**
246
+ * Extract text content from a chunk if it's a text chunk
247
+ */
248
+ export function extractTextFromChunk(chunk: unknown): string | null {
249
+ if (!chunk || typeof chunk !== 'object') return null
250
+ const data = chunk as ChunkData
251
+ if (data.type === MESSAGE_TYPE.TEXT && typeof data.text === 'string') {
252
+ return data.text
253
+ }
254
+ return null
255
+ }