@flamingo-stack/openframe-frontend-core 0.0.490 → 0.0.492-1506.3956.1
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.
- package/dist/chat-protocol/decode.d.ts +58 -0
- package/dist/chat-protocol/decode.d.ts.map +1 -0
- package/dist/chat-protocol/encode.d.ts +44 -0
- package/dist/chat-protocol/encode.d.ts.map +1 -0
- package/dist/chat-protocol/env-flag.d.ts +35 -0
- package/dist/chat-protocol/env-flag.d.ts.map +1 -0
- package/dist/chat-protocol/events.d.ts +207 -0
- package/dist/chat-protocol/events.d.ts.map +1 -0
- package/dist/chat-protocol/frames.d.ts +213 -0
- package/dist/chat-protocol/frames.d.ts.map +1 -0
- package/dist/chat-protocol/index.cjs +500 -0
- package/dist/chat-protocol/index.cjs.map +1 -0
- package/dist/chat-protocol/index.d.ts +18 -0
- package/dist/chat-protocol/index.d.ts.map +1 -0
- package/dist/chat-protocol/index.js +484 -0
- package/dist/chat-protocol/index.js.map +1 -0
- package/dist/chat-protocol/ip-normalize.d.ts +44 -0
- package/dist/chat-protocol/ip-normalize.d.ts.map +1 -0
- package/dist/chat-protocol/nats-decoder.d.ts +26 -0
- package/dist/chat-protocol/nats-decoder.d.ts.map +1 -0
- package/dist/{chunk-WLM3F3WU.js → chunk-34TDA22K.js} +3 -3
- package/dist/{chunk-EGXCWZYP.js → chunk-3LKB722U.js} +3 -3
- package/dist/{chunk-RW2LPL72.js → chunk-6WTCJI5R.js} +2 -2
- package/dist/{chunk-VUXNXWOQ.cjs → chunk-7N7YZCHR.cjs} +7503 -5167
- package/dist/chunk-7N7YZCHR.cjs.map +1 -0
- package/dist/{chunk-AW63ET2A.cjs → chunk-A6FPYAUD.cjs} +19 -19
- package/dist/{chunk-AW63ET2A.cjs.map → chunk-A6FPYAUD.cjs.map} +1 -1
- package/dist/{chunk-BZMOEWWS.js → chunk-BEEPU44B.js} +12 -12
- package/dist/chunk-BEEPU44B.js.map +1 -0
- package/dist/{chunk-EJQUYZXF.cjs → chunk-C65NCIMV.cjs} +12 -12
- package/dist/{chunk-EJQUYZXF.cjs.map → chunk-C65NCIMV.cjs.map} +1 -1
- package/dist/{chunk-VL2YMDPH.js → chunk-J3VBVR4W.js} +6 -6
- package/dist/{chunk-AIRNG27G.js → chunk-JHEIOXXZ.js} +8 -7
- package/dist/chunk-JHEIOXXZ.js.map +1 -0
- package/dist/{chunk-D5LQMRPU.cjs → chunk-JSEJ5MH5.cjs} +5 -5
- package/dist/{chunk-D5LQMRPU.cjs.map → chunk-JSEJ5MH5.cjs.map} +1 -1
- package/dist/{chunk-FY5XAW3H.cjs → chunk-KX3K2EH4.cjs} +18 -18
- package/dist/{chunk-FY5XAW3H.cjs.map → chunk-KX3K2EH4.cjs.map} +1 -1
- package/dist/{chunk-3IEVA5AO.cjs → chunk-M6FEGPQJ.cjs} +93 -93
- package/dist/{chunk-3IEVA5AO.cjs.map → chunk-M6FEGPQJ.cjs.map} +1 -1
- package/dist/{chunk-7I42V2OD.cjs → chunk-MEIGDK7M.cjs} +74 -74
- package/dist/chunk-MEIGDK7M.cjs.map +1 -0
- package/dist/{chunk-OQIMH2WQ.js → chunk-MEMRQTOI.js} +12263 -9927
- package/dist/chunk-MEMRQTOI.js.map +1 -0
- package/dist/{chunk-GUTYUMGV.cjs → chunk-MPUQFRJG.cjs} +12 -12
- package/dist/{chunk-GUTYUMGV.cjs.map → chunk-MPUQFRJG.cjs.map} +1 -1
- package/dist/{chunk-UZZT65PQ.cjs → chunk-MSL5NE3N.cjs} +40 -40
- package/dist/{chunk-UZZT65PQ.cjs.map → chunk-MSL5NE3N.cjs.map} +1 -1
- package/dist/{chunk-6CJ2K7BD.js → chunk-MZRIRQYV.js} +3 -3
- package/dist/{chunk-NGEFYPY7.js → chunk-NCAN6VUF.js} +4 -4
- package/dist/{chunk-6QK3ONDD.js → chunk-OL3MJKUI.js} +2 -2
- package/dist/{chunk-MPYDX5WX.js → chunk-PLB6CQSO.js} +4 -4
- package/dist/{chunk-6CFFISUC.js → chunk-PTXZB2KW.js} +3 -3
- package/dist/{chunk-GMPYR3MX.cjs → chunk-QDGB2DKY.cjs} +33 -33
- package/dist/{chunk-GMPYR3MX.cjs.map → chunk-QDGB2DKY.cjs.map} +1 -1
- package/dist/{chunk-2MKUHJ3O.cjs → chunk-QG2EBIH5.cjs} +40 -40
- package/dist/{chunk-2MKUHJ3O.cjs.map → chunk-QG2EBIH5.cjs.map} +1 -1
- package/dist/{chunk-GZK7FWJJ.js → chunk-QKWOBV55.js} +12 -12
- package/dist/chunk-QKWOBV55.js.map +1 -0
- package/dist/{chunk-STYA77PH.cjs → chunk-SGL6URIJ.cjs} +12 -12
- package/dist/chunk-SGL6URIJ.cjs.map +1 -0
- package/dist/{chunk-KP5KRRD6.cjs → chunk-THC3K262.cjs} +3 -3
- package/dist/{chunk-KP5KRRD6.cjs.map → chunk-THC3K262.cjs.map} +1 -1
- package/dist/{chunk-BIKOJ4FH.cjs → chunk-TTYJAZ7P.cjs} +58 -32
- package/dist/chunk-TTYJAZ7P.cjs.map +1 -0
- package/dist/{chunk-IEZ3YBAP.cjs → chunk-W56JYDSC.cjs} +34 -33
- package/dist/chunk-W56JYDSC.cjs.map +1 -0
- package/dist/{chunk-PB3BTZDU.js → chunk-WEUNPE3S.js} +139 -113
- package/dist/chunk-WEUNPE3S.js.map +1 -0
- package/dist/{chunk-VDZO6IDR.js → chunk-XOFE2RT4.js} +5 -5
- package/dist/components/case-studies/index.cjs +10 -10
- package/dist/components/case-studies/index.js +4 -4
- package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
- package/dist/components/chat/chat-message-list.d.ts.map +1 -1
- package/dist/components/chat/hooks/index.d.ts +0 -1
- package/dist/components/chat/hooks/index.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-chat.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +15 -48
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +10 -57
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +12 -4
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.d.ts +1 -0
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +23 -15
- package/dist/components/chat/stream/chat-dialog-store.d.ts +184 -0
- package/dist/components/chat/stream/chat-dialog-store.d.ts.map +1 -0
- package/dist/components/chat/stream/chat-stream-reducer.d.ts +367 -0
- package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -0
- package/dist/components/chat/stream/delta-batcher.d.ts +51 -0
- package/dist/components/chat/stream/delta-batcher.d.ts.map +1 -0
- package/dist/components/chat/stream/index.d.ts +17 -0
- package/dist/components/chat/stream/index.d.ts.map +1 -0
- package/dist/components/chat/stream/message-mutations.d.ts +77 -0
- package/dist/components/chat/stream/message-mutations.d.ts.map +1 -0
- package/dist/components/chat/stream/use-chat-stream-reducer.d.ts +22 -0
- package/dist/components/chat/stream/use-chat-stream-reducer.d.ts.map +1 -0
- package/dist/components/chat/types/api.types.d.ts +1 -81
- package/dist/components/chat/types/api.types.d.ts.map +1 -1
- package/dist/components/chat/types/processing.types.d.ts +2 -93
- package/dist/components/chat/types/processing.types.d.ts.map +1 -1
- package/dist/components/chat/utils/extract-incomplete-message-state.d.ts +34 -6
- package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
- package/dist/components/chat/utils/index.d.ts +2 -3
- package/dist/components/chat/utils/index.d.ts.map +1 -1
- package/dist/components/chat/utils/process-historical-messages.d.ts +59 -11
- package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +5 -5
- package/dist/components/contact/index.js +4 -4
- package/dist/components/docs/index.cjs +7 -7
- package/dist/components/docs/index.js +6 -6
- package/dist/components/embeds/index.cjs +5 -5
- package/dist/components/embeds/index.js +4 -4
- package/dist/components/faq/index.cjs +6 -6
- package/dist/components/faq/index.js +5 -5
- package/dist/components/features/index.cjs +4 -4
- package/dist/components/features/index.js +3 -3
- package/dist/components/features/select-button.d.ts.map +1 -1
- package/dist/components/help-center-pages/index.cjs +23 -23
- package/dist/components/help-center-pages/index.js +14 -14
- package/dist/components/index.cjs +203 -161
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +66 -24
- package/dist/components/index.js.map +1 -1
- package/dist/components/layout/page-layout.d.ts +7 -1
- package/dist/components/layout/page-layout.d.ts.map +1 -1
- package/dist/components/layout/title-block.d.ts +23 -1
- package/dist/components/layout/title-block.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +4 -4
- package/dist/components/navigation/index.js +3 -3
- package/dist/components/onboarding-guides/index.cjs +8 -8
- package/dist/components/onboarding-guides/index.js +7 -7
- package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
- package/dist/components/related-content/index.cjs +6 -6
- package/dist/components/related-content/index.js +5 -5
- package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +7 -7
- package/dist/components/tickets/index.js +6 -6
- package/dist/components/ui/autocomplete.d.ts.map +1 -1
- package/dist/components/ui/checkbox-block.d.ts.map +1 -1
- package/dist/components/ui/date-picker.d.ts.map +1 -1
- package/dist/components/ui/field-wrapper.d.ts +10 -1
- package/dist/components/ui/field-wrapper.d.ts.map +1 -1
- package/dist/components/ui/file-manager/index.cjs +50 -50
- package/dist/components/ui/file-manager/index.js +1 -1
- package/dist/components/ui/floating-tooltip.d.ts +8 -1
- package/dist/components/ui/floating-tooltip.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +46 -4
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.d.ts +1 -2
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +57 -15
- package/dist/components/ui/input-trigger.d.ts.map +1 -1
- package/dist/components/ui/input.d.ts.map +1 -1
- package/dist/components/ui/markdown/base-components.d.ts +51 -0
- package/dist/components/ui/markdown/base-components.d.ts.map +1 -0
- package/dist/components/ui/markdown/engine.d.ts +99 -0
- package/dist/components/ui/markdown/engine.d.ts.map +1 -0
- package/dist/components/ui/markdown/heading-ids.d.ts +65 -0
- package/dist/components/ui/markdown/heading-ids.d.ts.map +1 -0
- package/dist/components/ui/markdown/index.d.ts +18 -0
- package/dist/components/ui/markdown/index.d.ts.map +1 -0
- package/dist/components/ui/markdown/mermaid-diagram.d.ts +79 -0
- package/dist/components/ui/markdown/mermaid-diagram.d.ts.map +1 -0
- package/dist/components/ui/markdown/rich/embed-overrides.d.ts +14 -0
- package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +1 -0
- package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts +40 -0
- package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts.map +1 -0
- package/dist/components/ui/markdown/rich/shortcodes.d.ts +8 -0
- package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +1 -0
- package/dist/components/ui/markdown/sanitize.d.ts +153 -0
- package/dist/components/ui/markdown/sanitize.d.ts.map +1 -0
- package/dist/components/ui/markdown/simple-markdown-renderer.d.ts +23 -0
- package/dist/components/ui/markdown/simple-markdown-renderer.d.ts.map +1 -0
- package/dist/components/ui/markdown/streaming.d.ts +78 -0
- package/dist/components/ui/markdown/streaming.d.ts.map +1 -0
- package/dist/components/ui/markdown/text-size.d.ts +34 -0
- package/dist/components/ui/markdown/text-size.d.ts.map +1 -0
- package/dist/components/ui/page-actions.d.ts +7 -1
- package/dist/components/ui/page-actions.d.ts.map +1 -1
- package/dist/components/ui/radio-group.d.ts.map +1 -1
- package/dist/components/ui/search-input.d.ts.map +1 -1
- package/dist/components/ui/select.d.ts.map +1 -1
- package/dist/components/ui/tab-navigation.d.ts +10 -1
- package/dist/components/ui/tab-navigation.d.ts.map +1 -1
- package/dist/components/ui/tag-search-input.d.ts.map +1 -1
- package/dist/components/ui/textarea.d.ts.map +1 -1
- package/dist/components/ui/truncate-text.d.ts +2 -2
- package/dist/components/ui/truncate-text.d.ts.map +1 -1
- package/dist/hooks/index.cjs +4 -2
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.js +3 -1
- package/dist/hooks/ui/index.d.ts +1 -0
- package/dist/hooks/ui/index.d.ts.map +1 -1
- package/dist/hooks/ui/use-is-truncated.d.ts +33 -0
- package/dist/hooks/ui/use-is-truncated.d.ts.map +1 -0
- package/dist/index.cjs +64 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +75 -15
- package/dist/utils/index.cjs +169 -40
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +162 -41
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/markdown-fences.d.ts +42 -0
- package/dist/utils/markdown-fences.d.ts.map +1 -0
- package/dist/utils/markdown-heading-id.d.ts +85 -0
- package/dist/utils/markdown-heading-id.d.ts.map +1 -0
- package/dist/utils/markdown-section-extractor.d.ts.map +1 -1
- package/package.json +7 -1
- package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +317 -0
- package/src/chat-protocol/__tests__/chat-protocol.test.ts +516 -0
- package/src/chat-protocol/__tests__/env-flag.test.ts +52 -0
- package/src/chat-protocol/__tests__/ip-normalize.test.ts +136 -0
- package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +232 -0
- package/src/chat-protocol/decode.ts +278 -0
- package/src/chat-protocol/encode.ts +71 -0
- package/src/chat-protocol/env-flag.ts +39 -0
- package/src/chat-protocol/events.ts +245 -0
- package/src/chat-protocol/frames.ts +245 -0
- package/src/chat-protocol/index.ts +21 -0
- package/src/chat-protocol/ip-normalize.ts +146 -0
- package/src/chat-protocol/nats-decoder.ts +258 -0
- package/src/components/chat/__tests__/chat-message-list.test.tsx +293 -5
- package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +207 -0
- package/src/components/chat/__tests__/chat-pending-turn.test.tsx +230 -0
- package/src/components/chat/chat-message-enhanced.tsx +122 -20
- package/src/components/chat/chat-message-list.tsx +358 -109
- package/src/components/chat/embeddable-chat.tsx +6 -0
- package/src/components/chat/hooks/.index.md +30 -33
- package/src/components/chat/hooks/.use-nats-chat-adapter.md +36 -55
- package/src/components/chat/hooks/__tests__/__snapshots__/conversation-id-persistence-golden.test.ts.snap +37 -0
- package/src/components/chat/hooks/__tests__/__snapshots__/sse-stream-golden.test.ts.snap +0 -0
- package/src/components/chat/hooks/__tests__/conversation-id-persistence-golden.test.ts +438 -0
- package/src/components/chat/hooks/__tests__/sse-stream-golden.test.ts +318 -0
- package/src/components/chat/hooks/index.ts +0 -1
- package/src/components/chat/hooks/use-chat.ts +8 -0
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +243 -825
- package/src/components/chat/hooks/use-sse-chat-adapter.ts +380 -684
- package/src/components/chat/index.ts +5 -0
- package/src/components/chat/stream/__tests__/__snapshots__/chat-stream-reducer-golden.test.ts.snap +940 -0
- package/src/components/chat/stream/__tests__/chat-dialog-store.test.ts +756 -0
- package/src/components/chat/stream/__tests__/chat-stream-reducer-golden.test.ts +460 -0
- package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +973 -0
- package/src/components/chat/stream/__tests__/delta-batcher.test.ts +255 -0
- package/src/components/chat/stream/__tests__/use-chat-stream-reducer.test.ts +98 -0
- package/src/components/chat/stream/chat-dialog-store.ts +536 -0
- package/src/components/chat/stream/chat-stream-reducer.ts +1808 -0
- package/src/components/chat/stream/delta-batcher.ts +168 -0
- package/src/components/chat/stream/index.ts +55 -0
- package/src/components/chat/stream/message-mutations.ts +340 -0
- package/src/components/chat/stream/use-chat-stream-reducer.ts +126 -0
- package/src/components/chat/thinking-display.tsx +1 -1
- package/src/components/chat/types/.api.types.md +39 -49
- package/src/components/chat/types/.processing.types.md +29 -58
- package/src/components/chat/types/api.types.ts +6 -73
- package/src/components/chat/types/processing.types.ts +11 -53
- package/src/components/chat/utils/.index.md +31 -40
- package/src/components/chat/utils/__tests__/__snapshots__/process-historical-messages-golden.test.ts.snap +420 -0
- package/src/components/chat/utils/__tests__/__snapshots__/segment-accumulator-golden.test.ts.snap +605 -0
- package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +394 -0
- package/src/components/chat/utils/__tests__/segment-accumulator-golden.test.ts +270 -0
- package/src/components/chat/utils/extract-incomplete-message-state.ts +63 -5
- package/src/components/chat/utils/index.ts +5 -8
- package/src/components/chat/utils/process-historical-messages.ts +358 -386
- package/src/components/features/select-button.tsx +4 -1
- package/src/components/layout/page-layout.tsx +16 -0
- package/src/components/layout/title-block.tsx +106 -7
- package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +22 -4
- package/src/components/shared/legal-document/legal-document-page.tsx +1 -1
- package/src/components/shared/product-release/release-detail-page.tsx +19 -4
- package/src/components/ui/.field-wrapper.md +5 -4
- package/src/components/ui/.tab-navigation.md +14 -1
- package/src/components/ui/__tests__/__snapshots__/markdown-parity.test.tsx.snap +2420 -0
- package/src/components/ui/__tests__/markdown-parity.test.tsx +2528 -0
- package/src/components/ui/autocomplete.tsx +13 -1
- package/src/components/ui/button/button.tsx +1 -1
- package/src/components/ui/checkbox-block.tsx +27 -3
- package/src/components/ui/date-picker.tsx +15 -1
- package/src/components/ui/field-wrapper.tsx +12 -6
- package/src/components/ui/floating-tooltip.tsx +9 -1
- package/src/components/ui/index.ts +1 -2
- package/src/components/ui/input-trigger.tsx +8 -1
- package/src/components/ui/input.tsx +8 -1
- package/src/components/ui/markdown/__tests__/mermaid-security.test.ts +115 -0
- package/src/components/ui/markdown/__tests__/mermaid-stale-render.test.tsx +95 -0
- package/src/components/ui/markdown/__tests__/sanitize-invariant.test.ts +309 -0
- package/src/components/ui/markdown/__tests__/streaming.test.tsx +388 -0
- package/src/components/ui/markdown/base-components.tsx +360 -0
- package/src/components/ui/markdown/engine.tsx +315 -0
- package/src/components/ui/markdown/heading-ids.ts +239 -0
- package/src/components/ui/markdown/index.ts +49 -0
- package/src/components/ui/markdown/mermaid-diagram.tsx +291 -0
- package/src/components/ui/markdown/rich/embed-overrides.tsx +199 -0
- package/src/components/ui/markdown/rich/rich-markdown-renderer.tsx +184 -0
- package/src/components/ui/markdown/rich/shortcodes.ts +170 -0
- package/src/components/ui/markdown/sanitize.ts +3499 -0
- package/src/components/ui/markdown/simple-markdown-renderer.tsx +28 -0
- package/src/components/ui/markdown/streaming.ts +362 -0
- package/src/components/ui/markdown/text-size.ts +106 -0
- package/src/components/ui/page-actions.tsx +142 -30
- package/src/components/ui/radio-group.tsx +28 -3
- package/src/components/ui/release-changelog-section.tsx +1 -1
- package/src/components/ui/search-input.tsx +2 -1
- package/src/components/ui/select.tsx +7 -0
- package/src/components/ui/tab-navigation.tsx +26 -4
- package/src/components/ui/tag-search-input.tsx +9 -2
- package/src/components/ui/textarea.tsx +8 -1
- package/src/components/ui/ticket-info-section.tsx +1 -1
- package/src/components/ui/truncate-text.tsx +3 -18
- package/src/hooks/ui/index.ts +1 -0
- package/src/hooks/ui/use-is-truncated.ts +66 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/markdown-fences.ts +127 -0
- package/src/utils/markdown-heading-id.ts +348 -0
- package/src/utils/markdown-section-extractor.ts +41 -56
- package/dist/chunk-7I42V2OD.cjs.map +0 -1
- package/dist/chunk-AIRNG27G.js.map +0 -1
- package/dist/chunk-BIKOJ4FH.cjs.map +0 -1
- package/dist/chunk-BZMOEWWS.js.map +0 -1
- package/dist/chunk-GZK7FWJJ.js.map +0 -1
- package/dist/chunk-IEZ3YBAP.cjs.map +0 -1
- package/dist/chunk-OQIMH2WQ.js.map +0 -1
- package/dist/chunk-PB3BTZDU.js.map +0 -1
- package/dist/chunk-STYA77PH.cjs.map +0 -1
- package/dist/chunk-VUXNXWOQ.cjs.map +0 -1
- package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts +0 -6
- package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +0 -1
- package/dist/components/chat/utils/chunk-parser.d.ts +0 -25
- package/dist/components/chat/utils/chunk-parser.d.ts.map +0 -1
- package/dist/components/ui/rich-markdown-renderer.d.ts +0 -34
- package/dist/components/ui/rich-markdown-renderer.d.ts.map +0 -1
- package/dist/components/ui/simple-markdown-renderer.d.ts +0 -73
- package/dist/components/ui/simple-markdown-renderer.d.ts.map +0 -1
- package/src/components/chat/hooks/.use-realtime-chunk-processor.md +0 -77
- package/src/components/chat/hooks/use-realtime-chunk-processor.ts +0 -489
- package/src/components/chat/utils/.chunk-parser.md +0 -62
- package/src/components/chat/utils/chunk-parser.ts +0 -262
- package/src/components/ui/.simple-markdown-renderer.md +0 -52
- package/src/components/ui/rich-markdown-renderer.tsx +0 -1223
- package/src/components/ui/simple-markdown-renderer.tsx +0 -964
- /package/dist/{chunk-WLM3F3WU.js.map → chunk-34TDA22K.js.map} +0 -0
- /package/dist/{chunk-EGXCWZYP.js.map → chunk-3LKB722U.js.map} +0 -0
- /package/dist/{chunk-RW2LPL72.js.map → chunk-6WTCJI5R.js.map} +0 -0
- /package/dist/{chunk-VL2YMDPH.js.map → chunk-J3VBVR4W.js.map} +0 -0
- /package/dist/{chunk-6CJ2K7BD.js.map → chunk-MZRIRQYV.js.map} +0 -0
- /package/dist/{chunk-NGEFYPY7.js.map → chunk-NCAN6VUF.js.map} +0 -0
- /package/dist/{chunk-6QK3ONDD.js.map → chunk-OL3MJKUI.js.map} +0 -0
- /package/dist/{chunk-MPYDX5WX.js.map → chunk-PLB6CQSO.js.map} +0 -0
- /package/dist/{chunk-6CFFISUC.js.map → chunk-PTXZB2KW.js.map} +0 -0
- /package/dist/{chunk-VDZO6IDR.js.map → chunk-XOFE2RT4.js.map} +0 -0
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Utility for processing historical messages from GraphQL/API
|
|
3
|
-
*
|
|
2
|
+
* Utility for processing historical messages from GraphQL/API into
|
|
3
|
+
* display-ready format.
|
|
4
|
+
*
|
|
5
|
+
* Phase 3 of the chat unification reduced this file to DECODE + ENVELOPE:
|
|
6
|
+
*
|
|
7
|
+
* - `decodeHistoricalMessageData` maps one persisted `MessageData` item
|
|
8
|
+
* into the shared `ChatStreamEvent` vocabulary (historical items share
|
|
9
|
+
* the MESSAGE_TYPE vocabulary with NATS chunks but are NOT full chunk
|
|
10
|
+
* envelopes — e.g. compaction summaries ride in `summary`, not `text`,
|
|
11
|
+
* and APPROVAL_RESULT rows carry `resolvedByName` directly);
|
|
12
|
+
* - `applyHistoryEvent` replays each decoded event into the shared
|
|
13
|
+
* per-turn kernel (`MessageSegmentAccumulator` — the same kernel the
|
|
14
|
+
* master `createChatStreamReducer` instantiates) with the
|
|
15
|
+
* history-specific approval semantics (display-all default, pending
|
|
16
|
+
* tracking + `flushPendingApprovals`, `approvalStatuses` overrides);
|
|
17
|
+
* - the ENVELOPE (user/assistant flush-grouping, OWNER_TYPE author /
|
|
18
|
+
* display-name / avatar resolution, standalone SYSTEM handling,
|
|
19
|
+
* per-message contextItems mapping, per-turn streamSeq MAX) lives ONCE
|
|
20
|
+
* in `processHistory` — the previous near-duplicate second copy was
|
|
21
|
+
* deleted (`processHistoricalMessagesWithErrors` is an alias; the two
|
|
22
|
+
* snapshots were byte-identical).
|
|
23
|
+
*
|
|
24
|
+
* Adapters then feed the processed rows into the reducer via
|
|
25
|
+
* `initializeWithState` (see `useNatsChatAdapter.loadDialogHistory`).
|
|
4
26
|
*/
|
|
5
27
|
|
|
6
28
|
import {
|
|
@@ -15,6 +37,7 @@ import {
|
|
|
15
37
|
type MessageData,
|
|
16
38
|
type MessageOwner,
|
|
17
39
|
} from '../types'
|
|
40
|
+
import type { ChatStreamEvent } from '../../../chat-protocol/events'
|
|
18
41
|
import { MessageSegmentAccumulator, createMessageSegmentAccumulator } from './message-segment-accumulator'
|
|
19
42
|
import { getCommandText } from './tool-call-helpers'
|
|
20
43
|
|
|
@@ -53,255 +76,67 @@ function pushStandaloneMessages(
|
|
|
53
76
|
})
|
|
54
77
|
}
|
|
55
78
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
export interface ProcessHistoricalMessagesResult {
|
|
60
|
-
messages: ProcessedMessage[]
|
|
61
|
-
escalatedApprovals: Map<
|
|
62
|
-
string,
|
|
63
|
-
{ command: string; explanation?: string; approvalType: string; toolCalls?: PendingToolCallData[] }
|
|
64
|
-
>
|
|
65
|
-
}
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// Decode — persisted MessageData item → shared ChatStreamEvent vocabulary
|
|
81
|
+
// =============================================================================
|
|
66
82
|
|
|
67
83
|
/**
|
|
68
|
-
*
|
|
84
|
+
* Map one persisted `MessageData` item to a normalized `ChatStreamEvent`.
|
|
85
|
+
* Thin shim over the NATS chunk decoder's core mapping — the differences
|
|
86
|
+
* are exactly the persisted-row divergences from the realtime envelope:
|
|
87
|
+
*
|
|
88
|
+
* - compaction summaries arrive in `summary` (realtime: `text`);
|
|
89
|
+
* - APPROVAL_RESULT rows carry `resolvedByName` (realtime chunks carry
|
|
90
|
+
* the resolver's name as `displayName`);
|
|
91
|
+
* - `approvalType` defaults to 'CLIENT' (legacy history parity;
|
|
92
|
+
* realtime defaults APPROVAL_REQUEST to 'USER');
|
|
93
|
+
* - batch `toolCalls` pass through verbatim (already the persisted
|
|
94
|
+
* `PendingToolCallData` shape — no normalization pass).
|
|
95
|
+
*
|
|
96
|
+
* Returns `null` for rows the assistant-turn path doesn't decode (SYSTEM
|
|
97
|
+
* is handled by the envelope's standalone path; user TEXT rows are handled
|
|
98
|
+
* by the envelope's user path).
|
|
69
99
|
*/
|
|
70
|
-
export function
|
|
71
|
-
messages: HistoricalMessage[],
|
|
72
|
-
options: MessageProcessingOptions = {}
|
|
73
|
-
): ProcessHistoricalMessagesResult {
|
|
74
|
-
const {
|
|
75
|
-
assistantName = 'Fae',
|
|
76
|
-
assistantType = 'fae',
|
|
77
|
-
assistantAvatar,
|
|
78
|
-
onApprove,
|
|
79
|
-
onReject,
|
|
80
|
-
chatTypeFilter,
|
|
81
|
-
approvalStatuses = {},
|
|
82
|
-
// An omitted option means "display every approval type" — the original
|
|
83
|
-
// history semantics. Deliberately NOT defaulted to the realtime
|
|
84
|
-
// processor's ['CLIENT']: consumers that pass a wider list to their
|
|
85
|
-
// realtime processor but omit it on the history path would silently lose
|
|
86
|
-
// pending non-CLIENT approval cards on every reload/reconnect refetch
|
|
87
|
-
// (they also ignore `escalatedApprovals`). Realtime/history parity is
|
|
88
|
-
// opt-in: pass the same explicit list to both.
|
|
89
|
-
displayApprovalTypes,
|
|
90
|
-
batchApprovalsEnabled,
|
|
91
|
-
} = options
|
|
92
|
-
|
|
93
|
-
const processedMessages: ProcessedMessage[] = []
|
|
94
|
-
const accumulator = createMessageSegmentAccumulator({ onApprove, onReject })
|
|
95
|
-
const escalatedApprovals = new Map<
|
|
96
|
-
string,
|
|
97
|
-
{ command: string; explanation?: string; approvalType: string; toolCalls?: PendingToolCallData[] }
|
|
98
|
-
>()
|
|
99
|
-
|
|
100
|
-
let currentAssistantId: string | null = null
|
|
101
|
-
let currentAssistantTimestamp: Date | null = null
|
|
102
|
-
let lastAssistantId: string | null = null
|
|
103
|
-
// MAX persisted seq across the rows grouped into the current assistant turn
|
|
104
|
-
// — carried onto the flushed message's streamSeq for per-role merge coverage.
|
|
105
|
-
let currentAssistantStreamSeq: number | undefined
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Flush the current assistant message to processedMessages.
|
|
109
|
-
* Uses the LAST message ID in the group for stable React keys across page boundaries.
|
|
110
|
-
*/
|
|
111
|
-
const flushAssistantMessage = () => {
|
|
112
|
-
const idToUse = lastAssistantId || currentAssistantId
|
|
113
|
-
if (idToUse && accumulator.hasContent()) {
|
|
114
|
-
processedMessages.push({
|
|
115
|
-
id: idToUse,
|
|
116
|
-
role: 'assistant',
|
|
117
|
-
content: accumulator.getSegments(),
|
|
118
|
-
name: assistantName,
|
|
119
|
-
assistantType,
|
|
120
|
-
authorType: assistantType as AuthorType,
|
|
121
|
-
timestamp: currentAssistantTimestamp || new Date(),
|
|
122
|
-
avatar: assistantAvatar,
|
|
123
|
-
...(currentAssistantStreamSeq !== undefined ? { streamSeq: currentAssistantStreamSeq } : {}),
|
|
124
|
-
})
|
|
125
|
-
accumulator.resetSegments()
|
|
126
|
-
}
|
|
127
|
-
// Reset grouping identity + seq UNCONDITIONALLY — even on an EMPTY flush (an
|
|
128
|
-
// assistant turn whose only data was a filtered/escalated approval renders
|
|
129
|
-
// nothing, so `hasContent()` is false and nothing is pushed). Left inside
|
|
130
|
-
// the push-block, a stale id/timestamp and (worse) a stale
|
|
131
|
-
// `currentAssistantStreamSeq` bleed into the NEXT assistant turn: `!currentAssistantId`
|
|
132
|
-
// stays false so it keeps the old id/timestamp, and `Math.max` inflates its
|
|
133
|
-
// streamSeq — which then over-covers synthetics in the history merge.
|
|
134
|
-
currentAssistantId = null
|
|
135
|
-
currentAssistantTimestamp = null
|
|
136
|
-
lastAssistantId = null
|
|
137
|
-
currentAssistantStreamSeq = undefined
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
messages.forEach((msg, index) => {
|
|
141
|
-
// Filter by chat type if specified
|
|
142
|
-
if (chatTypeFilter && msg.chatType !== chatTypeFilter) return
|
|
143
|
-
|
|
144
|
-
const messageDataArray = Array.isArray(msg.messageData)
|
|
145
|
-
? msg.messageData
|
|
146
|
-
: msg.messageData
|
|
147
|
-
? [msg.messageData]
|
|
148
|
-
: []
|
|
149
|
-
|
|
150
|
-
const hasStandaloneData = messageDataArray.some((data) =>
|
|
151
|
-
data.type === MESSAGE_TYPE.SYSTEM
|
|
152
|
-
)
|
|
153
|
-
if (hasStandaloneData) {
|
|
154
|
-
flushAssistantMessage()
|
|
155
|
-
pushStandaloneMessages(processedMessages, msg, messageDataArray)
|
|
156
|
-
return
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const isUserMessage =
|
|
160
|
-
msg.owner?.type === OWNER_TYPE.CLIENT || msg.owner?.type === OWNER_TYPE.ADMIN
|
|
161
|
-
|
|
162
|
-
if (isUserMessage) {
|
|
163
|
-
flushAssistantMessage()
|
|
164
|
-
|
|
165
|
-
const userAuthorType: AuthorType = msg.owner?.type === OWNER_TYPE.ADMIN ? 'admin' : 'user'
|
|
166
|
-
messageDataArray.forEach((data) => {
|
|
167
|
-
if (data.type === MESSAGE_TYPE.TEXT && 'text' in data && data.text) {
|
|
168
|
-
// `TextData.contextItems` (server: `[{ type, id }]`) — the entity
|
|
169
|
-
// context the user attached to this message. Surface it so the bubble
|
|
170
|
-
// renders its chip strip from history (no label on the wire → fall
|
|
171
|
-
// back to the id, matching the realtime path).
|
|
172
|
-
const rawContext = (data as { contextItems?: Array<{ type?: unknown; id?: unknown }> }).contextItems
|
|
173
|
-
const contextItems = Array.isArray(rawContext)
|
|
174
|
-
? rawContext
|
|
175
|
-
.filter((c) => typeof c?.type === 'string' && typeof c?.id === 'string')
|
|
176
|
-
.map((c) => ({ type: c.type as string, id: c.id as string, label: c.id as string }))
|
|
177
|
-
: undefined
|
|
178
|
-
processedMessages.push({
|
|
179
|
-
id: msg.id,
|
|
180
|
-
role: 'user',
|
|
181
|
-
content: data.text,
|
|
182
|
-
name: getOwnerDisplayName(msg.owner),
|
|
183
|
-
avatar: getOwnerAvatar(msg.owner),
|
|
184
|
-
authorType: userAuthorType,
|
|
185
|
-
timestamp: new Date(msg.createdAt),
|
|
186
|
-
...(contextItems && contextItems.length > 0 ? { contextItems } : {}),
|
|
187
|
-
...(typeof msg.lastChunkStreamSeq === 'number' ? { streamSeq: msg.lastChunkStreamSeq } : {}),
|
|
188
|
-
})
|
|
189
|
-
}
|
|
190
|
-
})
|
|
191
|
-
} else {
|
|
192
|
-
if (!currentAssistantId) {
|
|
193
|
-
currentAssistantId = msg.id
|
|
194
|
-
currentAssistantTimestamp = new Date(msg.createdAt)
|
|
195
|
-
}
|
|
196
|
-
lastAssistantId = msg.id
|
|
197
|
-
if (typeof msg.lastChunkStreamSeq === 'number') {
|
|
198
|
-
currentAssistantStreamSeq =
|
|
199
|
-
currentAssistantStreamSeq === undefined
|
|
200
|
-
? msg.lastChunkStreamSeq
|
|
201
|
-
: Math.max(currentAssistantStreamSeq, msg.lastChunkStreamSeq)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
messageDataArray.forEach((data) => {
|
|
205
|
-
processMessageData(
|
|
206
|
-
data,
|
|
207
|
-
accumulator,
|
|
208
|
-
approvalStatuses,
|
|
209
|
-
{ displayApprovalTypes, batchApprovalsEnabled },
|
|
210
|
-
escalatedApprovals,
|
|
211
|
-
)
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
// Check if we should flush (next message is from user or last message)
|
|
215
|
-
const nextMsg = messages[index + 1]
|
|
216
|
-
const isLastMessage = index === messages.length - 1
|
|
217
|
-
const nextIsFromUser =
|
|
218
|
-
nextMsg &&
|
|
219
|
-
(nextMsg.owner?.type === OWNER_TYPE.CLIENT || nextMsg.owner?.type === OWNER_TYPE.ADMIN)
|
|
220
|
-
|
|
221
|
-
if (isLastMessage || nextIsFromUser) {
|
|
222
|
-
flushAssistantMessage()
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
flushAssistantMessage()
|
|
228
|
-
|
|
229
|
-
const pendingApprovalSegments = accumulator.flushPendingApprovals()
|
|
230
|
-
if (pendingApprovalSegments.length > 0) {
|
|
231
|
-
processedMessages.push({
|
|
232
|
-
id: `pending-approvals-${Date.now()}`,
|
|
233
|
-
role: 'assistant',
|
|
234
|
-
content: pendingApprovalSegments,
|
|
235
|
-
name: assistantName,
|
|
236
|
-
assistantType,
|
|
237
|
-
timestamp: new Date(),
|
|
238
|
-
avatar: assistantAvatar,
|
|
239
|
-
})
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return {
|
|
243
|
-
messages: processedMessages,
|
|
244
|
-
escalatedApprovals: escalatedApprovals
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Process a single message data item into segments
|
|
250
|
-
*/
|
|
251
|
-
function processMessageData(
|
|
252
|
-
data: MessageData,
|
|
253
|
-
accumulator: MessageSegmentAccumulator,
|
|
254
|
-
approvalStatuses: Record<string, string>,
|
|
255
|
-
options: MessageProcessingOptions = {},
|
|
256
|
-
escalatedApprovals?: Map<
|
|
257
|
-
string,
|
|
258
|
-
{ command: string; explanation?: string; approvalType: string; toolCalls?: PendingToolCallData[] }
|
|
259
|
-
>
|
|
260
|
-
): void {
|
|
261
|
-
// batchApprovalsEnabled is owned by the consumer (oss-tenant chat client /
|
|
262
|
-
// openframe-frontend tickets). Defaults to ON so consumers that haven't
|
|
263
|
-
// wired the flag yet get the batch UI; pass `false` to force legacy.
|
|
264
|
-
const { displayApprovalTypes, batchApprovalsEnabled = true } = options
|
|
100
|
+
export function decodeHistoricalMessageData(data: MessageData): ChatStreamEvent | null {
|
|
265
101
|
switch (data.type) {
|
|
266
102
|
case MESSAGE_TYPE.TEXT:
|
|
267
103
|
if ('text' in data && data.text) {
|
|
268
|
-
|
|
104
|
+
return { type: 'text-delta', text: data.text }
|
|
269
105
|
}
|
|
270
|
-
|
|
106
|
+
return null
|
|
271
107
|
|
|
272
108
|
case MESSAGE_TYPE.THINKING:
|
|
273
109
|
if ('text' in data && data.text) {
|
|
274
|
-
|
|
110
|
+
return { type: 'thinking-delta', text: data.text }
|
|
275
111
|
}
|
|
276
|
-
|
|
112
|
+
return null
|
|
277
113
|
|
|
278
114
|
case MESSAGE_TYPE.GUIDE:
|
|
279
115
|
if ('text' in data && data.text) {
|
|
280
|
-
|
|
116
|
+
return { type: 'guide-delta', text: data.text }
|
|
281
117
|
}
|
|
282
|
-
|
|
118
|
+
return null
|
|
283
119
|
|
|
284
120
|
case MESSAGE_TYPE.EXECUTING_TOOL:
|
|
285
121
|
if ('integratedToolType' in data) {
|
|
286
|
-
|
|
287
|
-
type: '
|
|
122
|
+
return {
|
|
123
|
+
type: 'tool-execution',
|
|
288
124
|
data: {
|
|
289
125
|
type: 'EXECUTING_TOOL',
|
|
290
126
|
integratedToolType: data.integratedToolType || '',
|
|
291
127
|
toolFunction: data.toolFunction || '',
|
|
292
128
|
toolTitle: typeof data.title === 'string' ? data.title : undefined,
|
|
293
|
-
toolExplanation: typeof data.toolExplanation === 'string' ? data.toolExplanation : undefined,
|
|
294
129
|
parameters: data.parameters,
|
|
295
130
|
toolExecutionRequestId: data.toolExecutionRequestId,
|
|
296
131
|
},
|
|
297
|
-
}
|
|
132
|
+
}
|
|
298
133
|
}
|
|
299
|
-
|
|
134
|
+
return null
|
|
300
135
|
|
|
301
136
|
case MESSAGE_TYPE.EXECUTED_TOOL:
|
|
302
137
|
if ('integratedToolType' in data) {
|
|
303
|
-
|
|
304
|
-
type: '
|
|
138
|
+
return {
|
|
139
|
+
type: 'tool-execution',
|
|
305
140
|
data: {
|
|
306
141
|
type: 'EXECUTED_TOOL',
|
|
307
142
|
integratedToolType: data.integratedToolType || '',
|
|
@@ -312,218 +147,298 @@ function processMessageData(
|
|
|
312
147
|
success: data.success,
|
|
313
148
|
toolExecutionRequestId: data.toolExecutionRequestId,
|
|
314
149
|
},
|
|
315
|
-
}
|
|
150
|
+
}
|
|
316
151
|
}
|
|
317
|
-
|
|
152
|
+
return null
|
|
318
153
|
|
|
319
154
|
case MESSAGE_TYPE.APPROVAL_REQUEST:
|
|
320
155
|
if ('approvalRequestId' in data && data.approvalRequestId) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
:
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (isBatch) {
|
|
329
|
-
const status = (approvalStatuses[data.approvalRequestId] as ChatApprovalStatus) || 'pending'
|
|
330
|
-
if (batchApprovalsEnabled) {
|
|
331
|
-
accumulator.addApprovalBatch(data.approvalRequestId, approvalType, toolCalls!, status)
|
|
332
|
-
} else {
|
|
333
|
-
// Flag OFF — unfold batch into N legacy approval cards (same id).
|
|
334
|
-
for (const call of toolCalls!) {
|
|
335
|
-
if (!call.requiresApproval) continue
|
|
336
|
-
accumulator.addApprovalRequest(
|
|
337
|
-
data.approvalRequestId,
|
|
338
|
-
getCommandText(call),
|
|
339
|
-
call.toolExplanation,
|
|
340
|
-
approvalType,
|
|
341
|
-
status,
|
|
342
|
-
)
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
} else {
|
|
346
|
-
// The resolution may already be known to the consumer (realtime
|
|
347
|
-
// flipped it and fed it back via `approvalStatuses`) while the
|
|
348
|
-
// matching APPROVAL_RESULT row is absent from the fetched history
|
|
349
|
-
// pages. Without this, the single-approval path tracks it as
|
|
350
|
-
// pending and `flushPendingApprovals()` resurrects it as a stale
|
|
351
|
-
// sticky card on every history re-process. Mirror the batch path
|
|
352
|
-
// and honor `approvalStatuses`.
|
|
353
|
-
const resolvedStatus = approvalStatuses[data.approvalRequestId] as ChatApprovalStatus | undefined
|
|
354
|
-
if (resolvedStatus === 'approved' || resolvedStatus === 'rejected') {
|
|
355
|
-
accumulator.addApprovalRequest(
|
|
356
|
-
data.approvalRequestId,
|
|
357
|
-
data.command || '',
|
|
358
|
-
data.explanation,
|
|
359
|
-
approvalType,
|
|
360
|
-
resolvedStatus,
|
|
361
|
-
)
|
|
362
|
-
} else {
|
|
363
|
-
accumulator.trackApprovalRequest(data.approvalRequestId, {
|
|
364
|
-
command: data.command || '',
|
|
365
|
-
explanation: data.explanation,
|
|
366
|
-
approvalType,
|
|
367
|
-
})
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
} else {
|
|
371
|
-
escalatedApprovals?.set(data.approvalRequestId, {
|
|
372
|
-
command: data.command || '',
|
|
373
|
-
explanation: data.explanation,
|
|
374
|
-
approvalType,
|
|
375
|
-
...(isBatch ? { toolCalls } : {}),
|
|
376
|
-
})
|
|
156
|
+
return {
|
|
157
|
+
type: 'approval-request',
|
|
158
|
+
requestId: data.approvalRequestId,
|
|
159
|
+
approvalType: data.approvalType || 'CLIENT',
|
|
160
|
+
command: data.command || '',
|
|
161
|
+
explanation: data.explanation,
|
|
162
|
+
...(Array.isArray(data.toolCalls) ? { toolCalls: data.toolCalls } : {}),
|
|
377
163
|
}
|
|
378
164
|
}
|
|
379
|
-
|
|
165
|
+
return null
|
|
380
166
|
|
|
381
167
|
case MESSAGE_TYPE.APPROVAL_RESULT:
|
|
382
168
|
if ('approvalRequestId' in data && data.approvalRequestId) {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
169
|
+
return {
|
|
170
|
+
type: 'approval-resolved',
|
|
171
|
+
requestId: data.approvalRequestId,
|
|
172
|
+
status: data.approved ? 'approved' : 'rejected',
|
|
173
|
+
approvalType: data.approvalType,
|
|
174
|
+
resolvedByName: 'resolvedByName' in data ? data.resolvedByName : undefined,
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return null
|
|
178
|
+
|
|
179
|
+
case MESSAGE_TYPE.ERROR:
|
|
180
|
+
if ('error' in data) {
|
|
181
|
+
return {
|
|
182
|
+
type: 'error',
|
|
183
|
+
title: data.error || 'An error occurred',
|
|
184
|
+
details: 'details' in data ? data.details : undefined,
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return null
|
|
188
|
+
|
|
189
|
+
case MESSAGE_TYPE.CONTEXT_COMPACTION_START:
|
|
190
|
+
return { type: 'compaction', phase: 'start' }
|
|
191
|
+
|
|
192
|
+
case MESSAGE_TYPE.CONTEXT_COMPACTION_END:
|
|
193
|
+
return {
|
|
194
|
+
type: 'compaction',
|
|
195
|
+
phase: 'end',
|
|
196
|
+
summary: 'summary' in data && typeof data.summary === 'string' ? data.summary : undefined,
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
case MESSAGE_TYPE.SYSTEM:
|
|
200
|
+
if ('text' in data && data.text) {
|
|
201
|
+
return { type: 'participant', kind: 'system', text: data.text }
|
|
202
|
+
}
|
|
203
|
+
return null
|
|
204
|
+
|
|
205
|
+
default:
|
|
206
|
+
// Unknown message type — ignore.
|
|
207
|
+
return null
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// =============================================================================
|
|
212
|
+
// Replay — decoded event → per-turn kernel (history approval semantics)
|
|
213
|
+
// =============================================================================
|
|
214
|
+
|
|
215
|
+
type EscalatedApprovals = Map<
|
|
216
|
+
string,
|
|
217
|
+
{ command: string; explanation?: string; approvalType: string; toolCalls?: PendingToolCallData[] }
|
|
218
|
+
>
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Replay one decoded event into the shared per-turn segment kernel with the
|
|
222
|
+
* HISTORY approval semantics (an omitted `displayApprovalTypes` means
|
|
223
|
+
* "display every approval type" — the original history behavior; the
|
|
224
|
+
* realtime reducer defaults to `['CLIENT']`).
|
|
225
|
+
*/
|
|
226
|
+
function applyHistoryEvent(
|
|
227
|
+
event: ChatStreamEvent,
|
|
228
|
+
accumulator: MessageSegmentAccumulator,
|
|
229
|
+
approvalStatuses: Record<string, string>,
|
|
230
|
+
options: MessageProcessingOptions,
|
|
231
|
+
escalatedApprovals?: EscalatedApprovals,
|
|
232
|
+
): void {
|
|
233
|
+
// batchApprovalsEnabled is owned by the consumer (oss-tenant chat client /
|
|
234
|
+
// openframe-frontend tickets). Defaults to ON so consumers that haven't
|
|
235
|
+
// wired the flag yet get the batch UI; pass `false` to force legacy.
|
|
236
|
+
const { displayApprovalTypes, batchApprovalsEnabled = true } = options
|
|
237
|
+
|
|
238
|
+
switch (event.type) {
|
|
239
|
+
case 'text-delta':
|
|
240
|
+
accumulator.appendText(event.text)
|
|
241
|
+
break
|
|
242
|
+
|
|
243
|
+
case 'thinking-delta':
|
|
244
|
+
accumulator.appendThinking(event.text)
|
|
245
|
+
break
|
|
246
|
+
|
|
247
|
+
case 'guide-delta':
|
|
248
|
+
accumulator.appendGuide(event.text)
|
|
249
|
+
break
|
|
250
|
+
|
|
251
|
+
case 'tool-execution':
|
|
252
|
+
accumulator.addToolExecution({ type: 'tool_execution', data: event.data })
|
|
253
|
+
break
|
|
387
254
|
|
|
388
|
-
|
|
255
|
+
case 'approval-request': {
|
|
256
|
+
const approvalType = event.approvalType || 'CLIENT'
|
|
257
|
+
const toolCalls = event.toolCalls as PendingToolCallData[] | undefined
|
|
258
|
+
const isBatch = !!toolCalls && toolCalls.length > 0
|
|
259
|
+
|
|
260
|
+
if (!displayApprovalTypes || displayApprovalTypes.includes(approvalType)) {
|
|
261
|
+
if (isBatch) {
|
|
262
|
+
const status = (approvalStatuses[event.requestId] as ChatApprovalStatus) || 'pending'
|
|
389
263
|
if (batchApprovalsEnabled) {
|
|
390
|
-
accumulator.addApprovalBatch(
|
|
391
|
-
data.approvalRequestId,
|
|
392
|
-
escalatedData.approvalType,
|
|
393
|
-
escalatedData.toolCalls,
|
|
394
|
-
status,
|
|
395
|
-
undefined,
|
|
396
|
-
resolvedByName,
|
|
397
|
-
)
|
|
264
|
+
accumulator.addApprovalBatch(event.requestId, approvalType, toolCalls!, status)
|
|
398
265
|
} else {
|
|
399
|
-
|
|
266
|
+
// Flag OFF — unfold batch into N legacy approval cards (same id).
|
|
267
|
+
for (const call of toolCalls!) {
|
|
400
268
|
if (!call.requiresApproval) continue
|
|
401
269
|
accumulator.addApprovalRequest(
|
|
402
|
-
|
|
270
|
+
event.requestId,
|
|
403
271
|
getCommandText(call),
|
|
404
272
|
call.toolExplanation,
|
|
405
|
-
|
|
273
|
+
approvalType,
|
|
406
274
|
status,
|
|
407
275
|
)
|
|
408
276
|
}
|
|
409
277
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
278
|
+
} else {
|
|
279
|
+
// The resolution may already be known to the consumer (realtime
|
|
280
|
+
// flipped it and fed it back via `approvalStatuses`) while the
|
|
281
|
+
// matching APPROVAL_RESULT row is absent from the fetched history
|
|
282
|
+
// pages. Without this, the single-approval path tracks it as
|
|
283
|
+
// pending and `flushPendingApprovals()` resurrects it as a stale
|
|
284
|
+
// sticky card on every history re-process. Mirror the batch path
|
|
285
|
+
// and honor `approvalStatuses`.
|
|
286
|
+
const resolvedStatus = approvalStatuses[event.requestId] as ChatApprovalStatus | undefined
|
|
287
|
+
if (resolvedStatus === 'approved' || resolvedStatus === 'rejected') {
|
|
288
|
+
accumulator.addApprovalRequest(
|
|
289
|
+
event.requestId,
|
|
290
|
+
event.command || '',
|
|
291
|
+
event.explanation,
|
|
292
|
+
approvalType,
|
|
293
|
+
resolvedStatus,
|
|
294
|
+
)
|
|
295
|
+
} else {
|
|
296
|
+
accumulator.trackApprovalRequest(event.requestId, {
|
|
297
|
+
command: event.command || '',
|
|
298
|
+
explanation: event.explanation,
|
|
299
|
+
approvalType,
|
|
300
|
+
})
|
|
301
|
+
}
|
|
421
302
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
accumulator.processApprovalResult(
|
|
431
|
-
data.approvalRequestId,
|
|
432
|
-
status === 'approved',
|
|
433
|
-
data.approvalType || 'USER'
|
|
434
|
-
)
|
|
303
|
+
} else {
|
|
304
|
+
escalatedApprovals?.set(event.requestId, {
|
|
305
|
+
command: event.command || '',
|
|
306
|
+
explanation: event.explanation,
|
|
307
|
+
approvalType,
|
|
308
|
+
...(isBatch ? { toolCalls } : {}),
|
|
309
|
+
})
|
|
435
310
|
}
|
|
436
311
|
break
|
|
312
|
+
}
|
|
437
313
|
|
|
438
|
-
case
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
314
|
+
case 'approval-resolved': {
|
|
315
|
+
const requestId = event.requestId
|
|
316
|
+
if (!requestId) break
|
|
317
|
+
const existingStatus = approvalStatuses[requestId] as ChatApprovalStatus | undefined
|
|
318
|
+
const status: ChatApprovalStatus = existingStatus || event.status
|
|
319
|
+
const resolvedByName = event.resolvedByName
|
|
320
|
+
const escalatedData = escalatedApprovals?.get(requestId)
|
|
321
|
+
|
|
322
|
+
if (escalatedData?.toolCalls && escalatedData.toolCalls.length > 0) {
|
|
323
|
+
if (batchApprovalsEnabled) {
|
|
324
|
+
accumulator.addApprovalBatch(
|
|
325
|
+
requestId,
|
|
326
|
+
escalatedData.approvalType,
|
|
327
|
+
escalatedData.toolCalls,
|
|
328
|
+
status,
|
|
329
|
+
undefined,
|
|
330
|
+
resolvedByName,
|
|
331
|
+
)
|
|
332
|
+
} else {
|
|
333
|
+
for (const call of escalatedData.toolCalls) {
|
|
334
|
+
if (!call.requiresApproval) continue
|
|
335
|
+
accumulator.addApprovalRequest(
|
|
336
|
+
requestId,
|
|
337
|
+
getCommandText(call),
|
|
338
|
+
call.toolExplanation,
|
|
339
|
+
escalatedData.approvalType,
|
|
340
|
+
status,
|
|
341
|
+
)
|
|
446
342
|
}
|
|
447
343
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
message
|
|
451
|
-
)
|
|
344
|
+
escalatedApprovals?.delete(requestId)
|
|
345
|
+
break
|
|
452
346
|
}
|
|
453
|
-
break
|
|
454
347
|
|
|
455
|
-
|
|
456
|
-
|
|
348
|
+
if (escalatedData) {
|
|
349
|
+
accumulator.trackApprovalRequest(requestId, {
|
|
350
|
+
command: escalatedData.command,
|
|
351
|
+
explanation: escalatedData.explanation,
|
|
352
|
+
approvalType: escalatedData.approvalType,
|
|
353
|
+
})
|
|
354
|
+
escalatedApprovals?.delete(requestId)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// If a segment with this id is already present (batch or legacy), just
|
|
358
|
+
// flip its status. updateApprovalStatus matches both `approval_batch`
|
|
359
|
+
// and `approval_request` segments.
|
|
360
|
+
const before = accumulator.getSegments()
|
|
361
|
+
const after = accumulator.updateApprovalStatus(requestId, status, resolvedByName)
|
|
362
|
+
const updatedExisting = before.some((s, i) => after[i] !== s)
|
|
363
|
+
if (updatedExisting) break
|
|
364
|
+
|
|
365
|
+
accumulator.processApprovalResult(
|
|
366
|
+
requestId,
|
|
367
|
+
status === 'approved',
|
|
368
|
+
event.approvalType || 'USER',
|
|
369
|
+
)
|
|
457
370
|
break
|
|
371
|
+
}
|
|
458
372
|
|
|
459
|
-
case
|
|
460
|
-
|
|
461
|
-
|
|
373
|
+
case 'error': {
|
|
374
|
+
let message: string | undefined
|
|
375
|
+
if (event.details) {
|
|
376
|
+
try {
|
|
377
|
+
message = JSON.parse(event.details)?.error?.message
|
|
378
|
+
} catch {
|
|
379
|
+
message = event.details
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
accumulator.addError(event.title, message)
|
|
462
383
|
break
|
|
463
384
|
}
|
|
464
385
|
|
|
386
|
+
case 'compaction':
|
|
387
|
+
if (event.phase === 'start') {
|
|
388
|
+
accumulator.addContextCompaction()
|
|
389
|
+
} else {
|
|
390
|
+
accumulator.completeContextCompaction(event.summary)
|
|
391
|
+
}
|
|
392
|
+
break
|
|
393
|
+
|
|
465
394
|
default:
|
|
466
|
-
//
|
|
395
|
+
// Participant/system rows are envelope concerns; everything else is
|
|
396
|
+
// realtime-only vocabulary that never appears in persisted rows.
|
|
467
397
|
break
|
|
468
398
|
}
|
|
469
399
|
}
|
|
470
400
|
|
|
401
|
+
// =============================================================================
|
|
402
|
+
// Envelope — ONE implementation (previous duplicate deleted)
|
|
403
|
+
// =============================================================================
|
|
404
|
+
|
|
471
405
|
/**
|
|
472
|
-
*
|
|
473
|
-
* Returns a separate array of error messages that should be displayed
|
|
406
|
+
* Result type for historical message processing
|
|
474
407
|
*/
|
|
475
|
-
export
|
|
476
|
-
messages:
|
|
477
|
-
|
|
478
|
-
): ProcessedMessage[] {
|
|
479
|
-
const { assistantName = 'Fae', assistantType = 'fae', assistantAvatar, chatTypeFilter } = options
|
|
480
|
-
|
|
481
|
-
const errorMessages: ProcessedMessage[] = []
|
|
482
|
-
|
|
483
|
-
messages.forEach((msg) => {
|
|
484
|
-
if (chatTypeFilter && msg.chatType !== chatTypeFilter) return
|
|
485
|
-
|
|
486
|
-
const messageDataArray = Array.isArray(msg.messageData)
|
|
487
|
-
? msg.messageData
|
|
488
|
-
: msg.messageData
|
|
489
|
-
? [msg.messageData]
|
|
490
|
-
: []
|
|
491
|
-
|
|
492
|
-
messageDataArray.forEach((data) => {
|
|
493
|
-
if (data.type === MESSAGE_TYPE.ERROR) {
|
|
494
|
-
errorMessages.push({
|
|
495
|
-
id: `${msg.id}-error`,
|
|
496
|
-
role: 'error',
|
|
497
|
-
content: 'error' in data && data.error ? data.error : 'An error occurred',
|
|
498
|
-
name: assistantName,
|
|
499
|
-
assistantType,
|
|
500
|
-
timestamp: new Date(msg.createdAt),
|
|
501
|
-
avatar: assistantAvatar,
|
|
502
|
-
})
|
|
503
|
-
}
|
|
504
|
-
})
|
|
505
|
-
})
|
|
506
|
-
|
|
507
|
-
return errorMessages
|
|
408
|
+
export interface ProcessHistoricalMessagesResult {
|
|
409
|
+
messages: ProcessedMessage[]
|
|
410
|
+
escalatedApprovals: EscalatedApprovals
|
|
508
411
|
}
|
|
509
412
|
|
|
510
413
|
/**
|
|
511
|
-
* Process
|
|
414
|
+
* Process an array of historical messages into display-ready format
|
|
512
415
|
*/
|
|
513
|
-
export function
|
|
416
|
+
export function processHistoricalMessages(
|
|
514
417
|
messages: HistoricalMessage[],
|
|
515
|
-
options: MessageProcessingOptions = {}
|
|
418
|
+
options: MessageProcessingOptions = {},
|
|
516
419
|
): ProcessHistoricalMessagesResult {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
420
|
+
const {
|
|
421
|
+
assistantName = 'Fae',
|
|
422
|
+
assistantType = 'fae',
|
|
423
|
+
assistantAvatar,
|
|
424
|
+
onApprove,
|
|
425
|
+
onReject,
|
|
426
|
+
chatTypeFilter,
|
|
427
|
+
approvalStatuses = {},
|
|
428
|
+
// An omitted option means "display every approval type" — the original
|
|
429
|
+
// history semantics. Deliberately NOT defaulted to the realtime
|
|
430
|
+
// reducer's ['CLIENT']: consumers that pass a wider list to their
|
|
431
|
+
// realtime path but omit it on the history path would silently lose
|
|
432
|
+
// pending non-CLIENT approval cards on every reload/reconnect refetch
|
|
433
|
+
// (they also ignore `escalatedApprovals`). Realtime/history parity is
|
|
434
|
+
// opt-in: pass the same explicit list to both.
|
|
435
|
+
displayApprovalTypes,
|
|
436
|
+
batchApprovalsEnabled,
|
|
437
|
+
} = options
|
|
520
438
|
|
|
521
439
|
const processedMessages: ProcessedMessage[] = []
|
|
522
440
|
const accumulator = createMessageSegmentAccumulator({ onApprove, onReject })
|
|
523
|
-
const escalatedApprovals = new Map
|
|
524
|
-
string,
|
|
525
|
-
{ command: string; explanation?: string; approvalType: string; toolCalls?: PendingToolCallData[] }
|
|
526
|
-
>()
|
|
441
|
+
const escalatedApprovals: EscalatedApprovals = new Map()
|
|
527
442
|
|
|
528
443
|
let currentAssistantId: string | null = null
|
|
529
444
|
let currentAssistantTimestamp: Date | null = null
|
|
@@ -532,6 +447,10 @@ export function processHistoricalMessagesWithErrors(
|
|
|
532
447
|
// — carried onto the flushed message's streamSeq for per-role merge coverage.
|
|
533
448
|
let currentAssistantStreamSeq: number | undefined
|
|
534
449
|
|
|
450
|
+
/**
|
|
451
|
+
* Flush the current assistant message to processedMessages.
|
|
452
|
+
* Uses the LAST message ID in the group for stable React keys across page boundaries.
|
|
453
|
+
*/
|
|
535
454
|
const flushAssistantMessage = () => {
|
|
536
455
|
const idToUse = lastAssistantId || currentAssistantId
|
|
537
456
|
if (idToUse && accumulator.hasContent()) {
|
|
@@ -562,6 +481,7 @@ export function processHistoricalMessagesWithErrors(
|
|
|
562
481
|
}
|
|
563
482
|
|
|
564
483
|
messages.forEach((msg, index) => {
|
|
484
|
+
// Filter by chat type if specified
|
|
565
485
|
if (chatTypeFilter && msg.chatType !== chatTypeFilter) return
|
|
566
486
|
|
|
567
487
|
const messageDataArray = Array.isArray(msg.messageData)
|
|
@@ -625,8 +545,10 @@ export function processHistoricalMessagesWithErrors(
|
|
|
625
545
|
}
|
|
626
546
|
|
|
627
547
|
messageDataArray.forEach((data) => {
|
|
628
|
-
|
|
629
|
-
|
|
548
|
+
const event = decodeHistoricalMessageData(data)
|
|
549
|
+
if (!event) return
|
|
550
|
+
applyHistoryEvent(
|
|
551
|
+
event,
|
|
630
552
|
accumulator,
|
|
631
553
|
approvalStatuses,
|
|
632
554
|
{ displayApprovalTypes, batchApprovalsEnabled },
|
|
@@ -634,6 +556,7 @@ export function processHistoricalMessagesWithErrors(
|
|
|
634
556
|
)
|
|
635
557
|
})
|
|
636
558
|
|
|
559
|
+
// Check if we should flush (next message is from user or last message)
|
|
637
560
|
const nextMsg = messages[index + 1]
|
|
638
561
|
const isLastMessage = index === messages.length - 1
|
|
639
562
|
const nextIsFromUser =
|
|
@@ -666,3 +589,52 @@ export function processHistoricalMessagesWithErrors(
|
|
|
666
589
|
escalatedApprovals: escalatedApprovals
|
|
667
590
|
}
|
|
668
591
|
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Extract error messages from historical messages
|
|
595
|
+
* Returns a separate array of error messages that should be displayed
|
|
596
|
+
*/
|
|
597
|
+
export function extractErrorMessages(
|
|
598
|
+
messages: HistoricalMessage[],
|
|
599
|
+
options: MessageProcessingOptions = {}
|
|
600
|
+
): ProcessedMessage[] {
|
|
601
|
+
const { assistantName = 'Fae', assistantType = 'fae', assistantAvatar, chatTypeFilter } = options
|
|
602
|
+
|
|
603
|
+
const errorMessages: ProcessedMessage[] = []
|
|
604
|
+
|
|
605
|
+
messages.forEach((msg) => {
|
|
606
|
+
if (chatTypeFilter && msg.chatType !== chatTypeFilter) return
|
|
607
|
+
|
|
608
|
+
const messageDataArray = Array.isArray(msg.messageData)
|
|
609
|
+
? msg.messageData
|
|
610
|
+
: msg.messageData
|
|
611
|
+
? [msg.messageData]
|
|
612
|
+
: []
|
|
613
|
+
|
|
614
|
+
messageDataArray.forEach((data) => {
|
|
615
|
+
if (data.type === MESSAGE_TYPE.ERROR) {
|
|
616
|
+
errorMessages.push({
|
|
617
|
+
id: `${msg.id}-error`,
|
|
618
|
+
role: 'error',
|
|
619
|
+
content: 'error' in data && data.error ? data.error : 'An error occurred',
|
|
620
|
+
name: assistantName,
|
|
621
|
+
assistantType,
|
|
622
|
+
timestamp: new Date(msg.createdAt),
|
|
623
|
+
avatar: assistantAvatar,
|
|
624
|
+
})
|
|
625
|
+
}
|
|
626
|
+
})
|
|
627
|
+
})
|
|
628
|
+
|
|
629
|
+
return errorMessages
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Process messages and include error messages in the correct order.
|
|
634
|
+
*
|
|
635
|
+
* HISTORICAL ALIAS: this was a byte-identical second copy of
|
|
636
|
+
* `processHistoricalMessages` (both goldens snapshot identical output on the
|
|
637
|
+
* same corpus). The duplicate envelope was deleted in Phase 3 — kept as an
|
|
638
|
+
* alias for the established import sites.
|
|
639
|
+
*/
|
|
640
|
+
export const processHistoricalMessagesWithErrors = processHistoricalMessages
|