@flamingo-stack/openframe-frontend-core 0.0.488 → 0.0.490-1506.3944.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 +196 -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 +494 -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 +478 -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-AJNDDXKC.js → chunk-4NWARV5I.js} +3 -3
- package/dist/{chunk-GQX6LFFA.cjs → chunk-5CJR2GZ4.cjs} +42 -42
- package/dist/{chunk-GQX6LFFA.cjs.map → chunk-5CJR2GZ4.cjs.map} +1 -1
- package/dist/{chunk-EUYIFALO.cjs → chunk-5EYRXSY5.cjs} +14 -14
- package/dist/{chunk-EUYIFALO.cjs.map → chunk-5EYRXSY5.cjs.map} +1 -1
- package/dist/{chunk-5RXKOGH6.cjs → chunk-5LMGFI4J.cjs} +7011 -4777
- package/dist/chunk-5LMGFI4J.cjs.map +1 -0
- package/dist/{chunk-KFQXO7QB.js → chunk-6GT557KN.js} +1 -1
- package/dist/chunk-6GT557KN.js.map +1 -0
- package/dist/{chunk-77DFPD35.js → chunk-BE6DK7QY.js} +12267 -10033
- package/dist/chunk-BE6DK7QY.js.map +1 -0
- package/dist/{chunk-A7IPON2V.cjs → chunk-BIKOJ4FH.cjs} +5 -5
- package/dist/{chunk-A7IPON2V.cjs.map → chunk-BIKOJ4FH.cjs.map} +1 -1
- package/dist/{chunk-HQYJINFV.js → chunk-CORIBKHG.js} +4 -4
- package/dist/{chunk-YEEHB4T6.cjs → chunk-CSYSYJCF.cjs} +40 -40
- package/dist/{chunk-YEEHB4T6.cjs.map → chunk-CSYSYJCF.cjs.map} +1 -1
- package/dist/{chunk-SIKCXKGB.cjs → chunk-DSDZ5T4P.cjs} +7 -7
- package/dist/{chunk-SIKCXKGB.cjs.map → chunk-DSDZ5T4P.cjs.map} +1 -1
- package/dist/{chunk-PNXNWGBY.js → chunk-GTWO5NAY.js} +2 -2
- package/dist/{chunk-SCCTJ2UI.js → chunk-HSACATCD.js} +4 -4
- package/dist/{chunk-PECGZWQR.cjs → chunk-KP5KRRD6.cjs} +3 -3
- package/dist/{chunk-PECGZWQR.cjs.map → chunk-KP5KRRD6.cjs.map} +1 -1
- package/dist/{chunk-2B6773QM.cjs → chunk-LILQJ3UZ.cjs} +1 -1
- package/dist/chunk-LILQJ3UZ.cjs.map +1 -0
- package/dist/{chunk-RZSA6MZL.cjs → chunk-NWX7MMF5.cjs} +15 -15
- package/dist/{chunk-RZSA6MZL.cjs.map → chunk-NWX7MMF5.cjs.map} +1 -1
- package/dist/{chunk-V3CNNMY3.cjs → chunk-P4WTZU6L.cjs} +18 -18
- package/dist/{chunk-V3CNNMY3.cjs.map → chunk-P4WTZU6L.cjs.map} +1 -1
- package/dist/{chunk-KLYHZRAD.js → chunk-PB3BTZDU.js} +2 -2
- package/dist/{chunk-FZOBQG6M.cjs → chunk-Q6E7VJ4W.cjs} +37 -36
- package/dist/chunk-Q6E7VJ4W.cjs.map +1 -0
- package/dist/{chunk-EYEIVYYF.cjs → chunk-QSCHCZL3.cjs} +7 -7
- package/dist/{chunk-EYEIVYYF.cjs.map → chunk-QSCHCZL3.cjs.map} +1 -1
- package/dist/{chunk-YJSKQ2ZV.js → chunk-RQLBHKXN.js} +3 -3
- package/dist/{chunk-CWQACI4Z.js → chunk-RW2LPL72.js} +2 -2
- package/dist/{chunk-DX6AOHNU.cjs → chunk-S2NHJDKQ.cjs} +77 -77
- package/dist/{chunk-DX6AOHNU.cjs.map → chunk-S2NHJDKQ.cjs.map} +1 -1
- package/dist/{chunk-AHDJMEEN.js → chunk-T2EGSZFN.js} +9 -8
- package/dist/chunk-T2EGSZFN.js.map +1 -0
- package/dist/{chunk-S52LJNMU.js → chunk-TKN5667N.js} +13 -13
- package/dist/chunk-TKN5667N.js.map +1 -0
- package/dist/{chunk-3BFX6HJM.js → chunk-UUYRJPGJ.js} +6 -6
- package/dist/{chunk-F6BYUO5U.js → chunk-VHG6AX5V.js} +3 -3
- package/dist/{chunk-FOQV6RCO.cjs → chunk-VQ523ORK.cjs} +29 -29
- package/dist/{chunk-FOQV6RCO.cjs.map → chunk-VQ523ORK.cjs.map} +1 -1
- package/dist/{chunk-PXF5J24T.js → chunk-XQ2B74SI.js} +2 -2
- package/dist/{chunk-H7JX2BG7.cjs → chunk-YHOIAA2E.cjs} +76 -76
- package/dist/chunk-YHOIAA2E.cjs.map +1 -0
- package/dist/{chunk-LLWBPPZ4.js → chunk-YZ2OIYQ3.js} +6 -6
- 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-history-hydration.d.ts +47 -0
- package/dist/components/chat/hooks/use-chat-history-hydration.d.ts.map +1 -0
- package/dist/components/chat/hooks/use-chat.d.ts +1 -0
- 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 -53
- 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 -90
- package/dist/components/chat/types/processing.types.d.ts.map +1 -1
- package/dist/components/chat/types/unified-chat-state.types.d.ts +5 -0
- package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
- package/dist/components/chat/utils/chat-conversation-storage.d.ts +34 -0
- package/dist/components/chat/utils/chat-conversation-storage.d.ts.map +1 -0
- 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/help-center-pages/index.cjs +23 -23
- package/dist/components/help-center-pages/index.js +14 -14
- package/dist/components/index.cjs +182 -140
- 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/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/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/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/contexts/chat-runtime-context.d.ts +8 -0
- package/dist/contexts/chat-runtime-context.d.ts.map +1 -1
- package/dist/contexts/index.cjs +2 -2
- package/dist/contexts/index.js +1 -1
- package/dist/hooks/index.cjs +3 -3
- package/dist/hooks/index.js +2 -2
- package/dist/index.cjs +62 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +73 -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 +307 -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 +228 -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 +232 -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 +252 -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 +209 -0
- package/src/components/chat/chat-message-enhanced.tsx +111 -17
- 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-history-hydration.ts +142 -0
- package/src/components/chat/hooks/use-chat.ts +20 -0
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +243 -823
- package/src/components/chat/hooks/use-sse-chat-adapter.ts +417 -774
- 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 +894 -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 +1796 -0
- package/src/components/chat/stream/delta-batcher.ts +159 -0
- package/src/components/chat/stream/index.ts +55 -0
- package/src/components/chat/stream/message-mutations.ts +338 -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 -52
- package/src/components/chat/types/unified-chat-state.types.ts +6 -0
- 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 +317 -0
- package/src/components/chat/utils/__tests__/segment-accumulator-golden.test.ts +270 -0
- package/src/components/chat/utils/chat-conversation-storage.ts +83 -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 +352 -384
- 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/__tests__/__snapshots__/markdown-parity.test.tsx.snap +2420 -0
- package/src/components/ui/__tests__/markdown-parity.test.tsx +2528 -0
- package/src/components/ui/index.ts +1 -2
- 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/release-changelog-section.tsx +1 -1
- package/src/components/ui/ticket-info-section.tsx +1 -1
- package/src/contexts/chat-runtime-context.tsx +8 -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-2B6773QM.cjs.map +0 -1
- package/dist/chunk-5RXKOGH6.cjs.map +0 -1
- package/dist/chunk-77DFPD35.js.map +0 -1
- package/dist/chunk-AHDJMEEN.js.map +0 -1
- package/dist/chunk-FZOBQG6M.cjs.map +0 -1
- package/dist/chunk-H7JX2BG7.cjs.map +0 -1
- package/dist/chunk-KFQXO7QB.js.map +0 -1
- package/dist/chunk-S52LJNMU.js.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 -479
- package/src/components/chat/utils/.chunk-parser.md +0 -62
- package/src/components/chat/utils/chunk-parser.ts +0 -256
- 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-AJNDDXKC.js.map → chunk-4NWARV5I.js.map} +0 -0
- /package/dist/{chunk-HQYJINFV.js.map → chunk-CORIBKHG.js.map} +0 -0
- /package/dist/{chunk-PNXNWGBY.js.map → chunk-GTWO5NAY.js.map} +0 -0
- /package/dist/{chunk-SCCTJ2UI.js.map → chunk-HSACATCD.js.map} +0 -0
- /package/dist/{chunk-KLYHZRAD.js.map → chunk-PB3BTZDU.js.map} +0 -0
- /package/dist/{chunk-YJSKQ2ZV.js.map → chunk-RQLBHKXN.js.map} +0 -0
- /package/dist/{chunk-CWQACI4Z.js.map → chunk-RW2LPL72.js.map} +0 -0
- /package/dist/{chunk-3BFX6HJM.js.map → chunk-UUYRJPGJ.js.map} +0 -0
- /package/dist/{chunk-F6BYUO5U.js.map → chunk-VHG6AX5V.js.map} +0 -0
- /package/dist/{chunk-PXF5J24T.js.map → chunk-XQ2B74SI.js.map} +0 -0
- /package/dist/{chunk-LLWBPPZ4.js.map → chunk-YZ2OIYQ3.js.map} +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createChatDialogStore — framework-free registry of `ChatStreamReducer`
|
|
3
|
+
* instances keyed by `(dialogId, side)`. Reducer state PERSISTS across
|
|
4
|
+
* component unmounts (the store outlives React), and the store is the
|
|
5
|
+
* `useSyncExternalStore` source for the `useChatStreamReducer` wrapper.
|
|
6
|
+
*
|
|
7
|
+
* CROSS-SIDE PROJECTION: exactly two operations fan out across sides of the
|
|
8
|
+
* same dialogId —
|
|
9
|
+
* 1. approval RESOLUTION by requestId (`approval-resolved` events), and
|
|
10
|
+
* 2. tool-execution MERGE by execId (`tool-execution` events)
|
|
11
|
+
* — implemented as idempotent pure projections over the other side's state
|
|
12
|
+
* (recomputed from state, never re-injected into the other side's seq
|
|
13
|
+
* stream; per-side seq gates stay untouched). Everything else — text,
|
|
14
|
+
* thinking, participant rows — is strictly per-side.
|
|
15
|
+
*
|
|
16
|
+
* LIFECYCLE / MEMORY: the reducer map is LRU-capped (`maxReducers`, default
|
|
17
|
+
* `DEFAULT_MAX_REDUCERS`) and evicted through the same path as `remove()`.
|
|
18
|
+
* Without a cap an agent cycling hundreds of dialogs would retain a reducer
|
|
19
|
+
* (thread + accumulator) per dialog for the process's lifetime; under SSR,
|
|
20
|
+
* where the store is module-scoped, that growth is shared across every
|
|
21
|
+
* request. `getSnapshot` is PURE — it never creates an entry — and
|
|
22
|
+
* `getServerSnapshot` always returns the shared frozen `EMPTY_STATE`.
|
|
23
|
+
*
|
|
24
|
+
* EVICTION SAFETY (LRU recency alone is NOT liveness):
|
|
25
|
+
* 1. RETAINED keys are never evicted. `retain(dialogId, side)` returns a
|
|
26
|
+
* release fn; the `useChatStreamReducer` hook calls it for as long as it
|
|
27
|
+
* is mounted, so a quiet-but-visible panel can't be dropped just because
|
|
28
|
+
* ten other keys were touched. NON-React hosts must call it too.
|
|
29
|
+
* Because the hook retains in an EFFECT but takes its reducer during
|
|
30
|
+
* RENDER, a key created by a public `getReducer` is protected from
|
|
31
|
+
* creation until its FIRST `retain()` (bounded by
|
|
32
|
+
* `MAX_PENDING_RETAIN_KEYS`) — see `pendingRetain`.
|
|
33
|
+
* 2. A reducer whose `streamingPhase !== 'idle'` is never evicted — dropping
|
|
34
|
+
* mid-turn would recreate an EMPTY reducer on the next `getReducer`
|
|
35
|
+
* while the host's seq cursor still says "caught up", i.e. the thread
|
|
36
|
+
* visually vanishes with no path back.
|
|
37
|
+
* 3. Eviction hands the host the dropped instance's PARKED state
|
|
38
|
+
* (`onEvict(..., { messages, approvalStatuses, lastAppliedSeq,
|
|
39
|
+
* pendingEchoes })`). A recreated reducer is pristine, so re-seeding
|
|
40
|
+
* messages alone would
|
|
41
|
+
* resurrect resolved approvals as actionable and reset the seq gate to
|
|
42
|
+
* `-Infinity`; the parked payload is what makes the round-trip lossless.
|
|
43
|
+
* 4. Eviction NEVER notifies synchronously. `getReducer` is called during
|
|
44
|
+
* render (by the hook), and notifying there means updating other
|
|
45
|
+
* components mid-render. The notify is deferred to a microtask.
|
|
46
|
+
*/
|
|
47
|
+
import { type ChatReducerState, type ChatStreamReducer, type ChatStreamReducerOptions, type PendingEcho } from './chat-stream-reducer';
|
|
48
|
+
import type { ChatStreamEvent } from '../../../chat-protocol/events';
|
|
49
|
+
export type ChatDialogSide = string;
|
|
50
|
+
/** Factory for a key's reducer options, consulted ONCE at creation. A plain
|
|
51
|
+
* `() => options` thunk remains assignable. */
|
|
52
|
+
export type CreateReducerOptionsFn = (dialogId: string, side: ChatDialogSide) => ChatStreamReducerOptions;
|
|
53
|
+
export interface ChatDialogStore {
|
|
54
|
+
/** Create-or-get the reducer for `(dialogId, side)`. `createOptions` is
|
|
55
|
+
* consulted ONLY when the instance is first created. */
|
|
56
|
+
getReducer(dialogId: string, side?: ChatDialogSide, createOptions?: CreateReducerOptionsFn): ChatStreamReducer;
|
|
57
|
+
/** Apply one event to a side, then fan out the cross-side projections to
|
|
58
|
+
* the other sides of the same dialog. */
|
|
59
|
+
apply(dialogId: string, side: ChatDialogSide, event: ChatStreamEvent): void;
|
|
60
|
+
/** Run adapter commands against a reducer; subscribers are notified. */
|
|
61
|
+
mutate<T>(dialogId: string, side: ChatDialogSide, fn: (reducer: ChatStreamReducer) => T): T;
|
|
62
|
+
subscribe(listener: () => void): () => void;
|
|
63
|
+
/**
|
|
64
|
+
* Referentially-stable snapshot for `useSyncExternalStore`. PURE — an
|
|
65
|
+
* unknown key returns the shared frozen `EMPTY_STATE` rather than
|
|
66
|
+
* creating a reducer (a create-on-read would let SSR renders accumulate
|
|
67
|
+
* reducers in a process-global map, and would make React's snapshot read
|
|
68
|
+
* a mutation).
|
|
69
|
+
*/
|
|
70
|
+
getSnapshot(dialogId: string, side?: ChatDialogSide): ChatReducerState;
|
|
71
|
+
/** `useSyncExternalStore`'s server getter — always `EMPTY_STATE`. There is
|
|
72
|
+
* no per-request store on the server, so a live thread cannot be rendered
|
|
73
|
+
* server-side; the client hydrates from the real reducer. */
|
|
74
|
+
getServerSnapshot(): ChatReducerState;
|
|
75
|
+
/** Drop a side's reducer (or every side of the dialog when omitted). */
|
|
76
|
+
remove(dialogId: string, side?: ChatDialogSide): void;
|
|
77
|
+
/**
|
|
78
|
+
* PIN `(dialogId, side)` against LRU eviction while a consumer is live.
|
|
79
|
+
* Returns the release fn (idempotent; refcounted, so nested retains are
|
|
80
|
+
* safe). Creation is NOT implied — retaining an absent key simply means the
|
|
81
|
+
* key is protected once it exists.
|
|
82
|
+
*/
|
|
83
|
+
retain(dialogId: string, side?: ChatDialogSide): () => void;
|
|
84
|
+
/**
|
|
85
|
+
* POLICY retain: keep exactly `keys` pinned by this store-level set and
|
|
86
|
+
* release every key the set previously pinned. Independent of the refcounted
|
|
87
|
+
* `retain()` handles a host's components hold — the two compose.
|
|
88
|
+
*
|
|
89
|
+
* Exists because "retain this set, release the rest" is generic store
|
|
90
|
+
* policy that every multi-panel host otherwise re-derives (diffing its own
|
|
91
|
+
* key list against a map of release fns). Retains BEFORE releasing, so a key
|
|
92
|
+
* present in both the old and the new set never momentarily drops to zero
|
|
93
|
+
* retains and becomes evictable mid-swap.
|
|
94
|
+
*/
|
|
95
|
+
setRetained(keys: Array<{
|
|
96
|
+
dialogId: string;
|
|
97
|
+
side?: ChatDialogSide;
|
|
98
|
+
}>): void;
|
|
99
|
+
}
|
|
100
|
+
export declare const DEFAULT_DIALOG_SIDE: ChatDialogSide;
|
|
101
|
+
/** Most-recently-used reducer keys retained before eviction. Ten covers
|
|
102
|
+
* every realistic multi-panel / dialog-switching session while bounding an
|
|
103
|
+
* agent that cycles hundreds of dialogs. */
|
|
104
|
+
export declare const DEFAULT_MAX_REDUCERS = 10;
|
|
105
|
+
/**
|
|
106
|
+
* Hard cap on keys held in the "created by `getReducer`, `retain()` still
|
|
107
|
+
* pending" protection set — the ONLY thing bounding the map while retains are
|
|
108
|
+
* outstanding, since that protection ends at the retain rather than on a timer.
|
|
109
|
+
*
|
|
110
|
+
* Deliberately far above `maxReducers`: a host may legitimately mount MORE
|
|
111
|
+
* panels than the LRU cap (once their effects run, retained keys already let
|
|
112
|
+
* the map exceed it), so capping the pending set at `maxReducers` would blank
|
|
113
|
+
* exactly the panels round 10 fixed. It is a backstop against a caller that
|
|
114
|
+
* reads a reducer once and drops it — or a concurrent render React discards
|
|
115
|
+
* before any effect runs — not a per-commit budget. Past it, the oldest pending
|
|
116
|
+
* key falls through to ordinary LRU.
|
|
117
|
+
*/
|
|
118
|
+
export declare const MAX_PENDING_RETAIN_KEYS = 256;
|
|
119
|
+
export interface CreateChatDialogStoreOptions {
|
|
120
|
+
/** LRU cap on retained reducers (keys are `(dialogId, side)` pairs).
|
|
121
|
+
* Values < 1 are clamped to 1. Default `DEFAULT_MAX_REDUCERS`. */
|
|
122
|
+
maxReducers?: number;
|
|
123
|
+
/**
|
|
124
|
+
* Options applied to any reducer this store creates WITHOUT an explicit
|
|
125
|
+
* per-call `createOptions` — i.e. every reducer first materialized by
|
|
126
|
+
* `apply()` or `mutate()`. Hosts with non-default reducer semantics
|
|
127
|
+
* (`ownEchoIncludesAdmin`, `selfUserId`, approve/reject `callbacks`, …)
|
|
128
|
+
* MUST set this: creation options are consulted once, so a bare
|
|
129
|
+
* apply-created instance would keep those semantics missing for its whole
|
|
130
|
+
* lifetime, including for a later `getReducer(..., options)` caller.
|
|
131
|
+
*/
|
|
132
|
+
defaultCreateOptions?: CreateReducerOptionsFn;
|
|
133
|
+
/**
|
|
134
|
+
* Called when LRU eviction actually DROPS a key. The store knows this
|
|
135
|
+
* exactly; without publishing it, hosts are left inferring eviction by
|
|
136
|
+
* comparing reducer OBJECT IDENTITY across `getReducer` calls and then
|
|
137
|
+
* suppressing the "pristine empty" snapshot that a silently-recreated
|
|
138
|
+
* reducer returns — archaeology about a fact this callback states outright.
|
|
139
|
+
*
|
|
140
|
+
* Fires only for LRU eviction. Host-initiated `remove()` needs no
|
|
141
|
+
* notification: the caller already knows which key it dropped.
|
|
142
|
+
*
|
|
143
|
+
* Called AFTER the key is gone, from `getReducer` (i.e. potentially during a
|
|
144
|
+
* React render) — treat it as a "schedule a refetch/rehydrate" signal, not a
|
|
145
|
+
* place to set state synchronously.
|
|
146
|
+
*
|
|
147
|
+
* `parked` carries the dropped reducer's non-refetchable state so the host
|
|
148
|
+
* can restore it on the recreated instance (`initializeWithState(messages,
|
|
149
|
+
* { approvalStatuses, lastAppliedSeq, pendingEchoes })`). Refetching
|
|
150
|
+
* messages alone is NOT
|
|
151
|
+
* equivalent: a recreated reducer starts with `approvalStatuses = {}` and
|
|
152
|
+
* `lastAppliedSeq = -Infinity`, so a resolved approval whose APPROVAL_RESULT
|
|
153
|
+
* row is not in the refetched history page re-renders as ACTIONABLE (the
|
|
154
|
+
* exact hazard `resetForDialogSwitch` preserves that map to avoid), and a
|
|
155
|
+
* replay from the host's own cursor re-applies events the dropped instance
|
|
156
|
+
* had already consumed.
|
|
157
|
+
*/
|
|
158
|
+
onEvict?: (dialogId: string, side: ChatDialogSide, parked: EvictedReducerState) => void;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Snapshot of an LRU-evicted reducer's state, captured just before the
|
|
162
|
+
* instance is dropped. Everything here either cannot be refetched
|
|
163
|
+
* (`lastAppliedSeq`) or is not reliably present in a refetched history page
|
|
164
|
+
* (`approvalStatuses`); `messages` rides along so a host that keeps its own
|
|
165
|
+
* copy does not have to.
|
|
166
|
+
*/
|
|
167
|
+
export interface EvictedReducerState {
|
|
168
|
+
messages: ChatReducerState['messages'];
|
|
169
|
+
approvalStatuses: ChatReducerState['approvalStatuses'];
|
|
170
|
+
/** `-Infinity` when the instance never applied a seq-carrying event. */
|
|
171
|
+
lastAppliedSeq: number;
|
|
172
|
+
/**
|
|
173
|
+
* Optimistic-echo entries still armed at eviction time. A key dropped
|
|
174
|
+
* between `pushOptimisticSend` and its `MESSAGE_REQUEST` echo would
|
|
175
|
+
* otherwise leave the recreated reducer with nothing armed, and the echo
|
|
176
|
+
* renders a DUPLICATE user bubble. Replay via
|
|
177
|
+
* `initializeWithState(messages, { pendingEchoes })`; the reducer drops
|
|
178
|
+
* entries already past `OWN_ECHO_AUTHOR_TTL_MS`. Empty for a key with no
|
|
179
|
+
* send in flight (the common case).
|
|
180
|
+
*/
|
|
181
|
+
pendingEchoes: readonly PendingEcho[];
|
|
182
|
+
}
|
|
183
|
+
export declare function createChatDialogStore(options?: CreateChatDialogStoreOptions): ChatDialogStore;
|
|
184
|
+
//# sourceMappingURL=chat-dialog-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-dialog-store.d.ts","sourceRoot":"","sources":["../../../../src/components/chat/stream/chat-dialog-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,WAAW,EACjB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAEpE,MAAM,MAAM,cAAc,GAAG,MAAM,CAAA;AAEnC;gDACgD;AAChD,MAAM,MAAM,sBAAsB,GAAG,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,cAAc,KACjB,wBAAwB,CAAA;AAE7B,MAAM,WAAW,eAAe;IAC9B;6DACyD;IACzD,UAAU,CACR,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,cAAc,EACrB,aAAa,CAAC,EAAE,sBAAsB,GACrC,iBAAiB,CAAA;IACpB;8CAC0C;IAC1C,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAAA;IAC3E,wEAAwE;IACxE,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,CAAC,GAAG,CAAC,CAAA;IAC3F,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C;;;;;;OAMG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,gBAAgB,CAAA;IACtE;;kEAE8D;IAC9D,iBAAiB,IAAI,gBAAgB,CAAA;IACrC,wEAAwE;IACxE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;IACrD;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,MAAM,IAAI,CAAA;IAC3D;;;;;;;;;;OAUG;IACH,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,cAAc,CAAA;KAAE,CAAC,GAAG,IAAI,CAAA;CAC5E;AAED,eAAO,MAAM,mBAAmB,EAAE,cAAuB,CAAA;AAEzD;;6CAE6C;AAC7C,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAA;AAE1C,MAAM,WAAW,4BAA4B;IAC3C;uEACmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;;;OAQG;IACH,oBAAoB,CAAC,EAAE,sBAAsB,CAAA;IAC7C;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,OAAO,CAAC,EAAE,CACR,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,mBAAmB,KACxB,IAAI,CAAA;CACV;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAA;IACtC,gBAAgB,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,CAAA;IACtD,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAA;IACtB;;;;;;;;OAQG;IACH,aAAa,EAAE,SAAS,WAAW,EAAE,CAAA;CACtC;AAyBD,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,4BAAiC,GACzC,eAAe,CAgTjB"}
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createChatStreamReducer — THE master chat-stream reader (Phase 3 of the
|
|
3
|
+
* chat unification). One accumulation path for every event source:
|
|
4
|
+
*
|
|
5
|
+
* SSE bytes → createSseFrameDecoder ─┐
|
|
6
|
+
* NATS chunk → decodeNatsChunk ─┼→ ChatStreamEvent → reducer.apply()
|
|
7
|
+
* history → decodeHistoricalMessageData (per-item vocabulary) →
|
|
8
|
+
* envelope grouping → reducer.initializeWithState()
|
|
9
|
+
*
|
|
10
|
+
* The reducer absorbs, verbatim, the semantics that used to be spread over
|
|
11
|
+
* three layers:
|
|
12
|
+
* - `MessageSegmentAccumulator` stays as the internal per-turn segment
|
|
13
|
+
* kernel (instantiated here — its goldens remain valid);
|
|
14
|
+
* - the deleted `useRealtimeChunkProcessor`'s switch (in-stream vs post-END
|
|
15
|
+
* routing, agent-busy outside a message window, escalated approvals,
|
|
16
|
+
* compaction upsert, direct-mode barrier) — surfaced as
|
|
17
|
+
* `ChatReducerEffect`s for consumers that want the callback contract;
|
|
18
|
+
* - `useNatsChatAdapter`'s message-mutation callbacks (trailing-assistant
|
|
19
|
+
* replace/append, compaction upsert, cross-message tool merge, approval
|
|
20
|
+
* flip, participant dedup) — now the reducer's own state transitions via
|
|
21
|
+
* `./message-mutations`;
|
|
22
|
+
* - `useSseChatAdapter`/`useChat`'s SSE turn kernel (cumulative text
|
|
23
|
+
* replace, front-inserted thinking, approval card, `decision_resolved`
|
|
24
|
+
* receipt + cardRef stamping, sendIdx-keyed sources/refs/meta maps).
|
|
25
|
+
*
|
|
26
|
+
* Idempotency:
|
|
27
|
+
* - events whose `seq` ≤ the last applied seq are dropped (per instance);
|
|
28
|
+
* - participant rows additionally dedup via a one-shot optimistic-echo
|
|
29
|
+
* list and a short same-author content window (seq-less transports);
|
|
30
|
+
* - value-level no-op merges return prior references (see
|
|
31
|
+
* `message-mutations.ts`).
|
|
32
|
+
*
|
|
33
|
+
* TRANSPORT ORDERING ASSUMPTION (the seq gate, see `apply()`): every
|
|
34
|
+
* transport feeding this reducer is AT-LEAST-ONCE and IN-ORDER. JetStream
|
|
35
|
+
* redelivers (hence the gate) but never reorders within a stream, and the
|
|
36
|
+
* catchup back-fill replays a monotonically increasing range. So `seq <=
|
|
37
|
+
* lastAppliedSeq` means "already applied", and dropping is correct.
|
|
38
|
+
* A genuinely out-of-order arrival (seq 5 then 4) would be discarded
|
|
39
|
+
* PERMANENTLY — accepted deliberately: no transport here reorders, and an
|
|
40
|
+
* out-of-order buffer would add latency + unbounded state to guard against
|
|
41
|
+
* a case that cannot occur. If a reordering transport is ever added, this
|
|
42
|
+
* gate is the place that must change (do not paper over it downstream).
|
|
43
|
+
*
|
|
44
|
+
* `resolvePendingApprovalForExecution` (the accumulator's implicit
|
|
45
|
+
* approve-on-execution) is reachable ONLY on `transport: 'nats'` — the SSE
|
|
46
|
+
* kernel never routes tool executions through the accumulator.
|
|
47
|
+
*
|
|
48
|
+
* PURE + framework-free: no React, no timers, no network. Side effects the
|
|
49
|
+
* UI needs (approve/reject POSTs) enter as opaque callbacks stamped onto
|
|
50
|
+
* approval segments.
|
|
51
|
+
*/
|
|
52
|
+
import { type AccumulatorCallbacks } from '../utils/message-segment-accumulator';
|
|
53
|
+
import { type ScrollAnchor } from '../utils/scroll-anchor';
|
|
54
|
+
import type { ChatApprovalStatus, MessageSegment, PendingToolCallData, ToolExecutionSegment, ExecutingToolState } from '../types';
|
|
55
|
+
import type { PendingApproval } from '../types/processing.types';
|
|
56
|
+
import type { DialogTokenUsage, StreamingPhase, UnifiedChatMessage, UnifiedUsageBreakdown } from '../types/unified-chat-state.types';
|
|
57
|
+
import { type ChatStreamEvent } from '../../../chat-protocol/events';
|
|
58
|
+
import type { ChatRef } from '../chat-ref.types';
|
|
59
|
+
/** Per-turn metadata extracted from the streamed metadata/usage frames
|
|
60
|
+
* (SSE transport). Canonical home — `use-sse-chat-adapter` re-exports it. */
|
|
61
|
+
export interface ChatTurnMeta {
|
|
62
|
+
provider: string | null;
|
|
63
|
+
modelLabel: string | null;
|
|
64
|
+
contextWindowMaxTokens: number | null;
|
|
65
|
+
/** Input tokens (from usage:start). Includes cached tokens. */
|
|
66
|
+
inputTokens: number | null;
|
|
67
|
+
/** Output tokens (from the trailing usage frame). */
|
|
68
|
+
outputTokens: number | null;
|
|
69
|
+
/** Cache hit % (read / total-input × 100). Only known after stream end. */
|
|
70
|
+
cacheHitRatePct: number | null;
|
|
71
|
+
/** Cross-call usage breakdown extracted from the trailing usage frame. */
|
|
72
|
+
breakdown: UnifiedUsageBreakdown | null;
|
|
73
|
+
/** Per-message viewport-positioning hint. */
|
|
74
|
+
scrollAnchor: ScrollAnchor | null;
|
|
75
|
+
routedComplexity: string | null;
|
|
76
|
+
routedThinkingBudget: number | null;
|
|
77
|
+
}
|
|
78
|
+
/** Single source of truth for a fresh `ChatTurnMeta` row. */
|
|
79
|
+
export declare function createEmptyTurnMeta(): ChatTurnMeta;
|
|
80
|
+
/** SSE per-send maps, keyed by the send counter (`sendIdx`). Each user send
|
|
81
|
+
* produces ONE server-side refs/sources entry but can fan out to MULTIPLE
|
|
82
|
+
* assistant messages client-side — the adapter maps every following
|
|
83
|
+
* assistant message back to its send's entry. */
|
|
84
|
+
export interface ChatTurnMetaState {
|
|
85
|
+
meta: Map<number, ChatTurnMeta>;
|
|
86
|
+
sources: Map<number, unknown[]>;
|
|
87
|
+
refs: Map<number, Record<string, ChatRef>>;
|
|
88
|
+
sendCount: number;
|
|
89
|
+
}
|
|
90
|
+
export interface ChatReducerState {
|
|
91
|
+
messages: UnifiedChatMessage[];
|
|
92
|
+
streamingPhase: StreamingPhase;
|
|
93
|
+
turnMeta: ChatTurnMetaState;
|
|
94
|
+
dialogTokenUsage?: DialogTokenUsage | null;
|
|
95
|
+
liveModel?: {
|
|
96
|
+
provider: string | null;
|
|
97
|
+
modelLabel: string | null;
|
|
98
|
+
contextWindowMaxTokens: number | null;
|
|
99
|
+
} | null;
|
|
100
|
+
approvalStatuses: Record<string, ChatApprovalStatus>;
|
|
101
|
+
}
|
|
102
|
+
/** Escalated-approval bookkeeping entry (mirrors the legacy processor). */
|
|
103
|
+
export interface EscalatedApprovalData {
|
|
104
|
+
command: string;
|
|
105
|
+
explanation?: string;
|
|
106
|
+
approvalType: string;
|
|
107
|
+
toolCalls?: PendingToolCallData[];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Callback-visible effect produced by `apply()`. Names + args mirror the
|
|
111
|
+
* legacy `RealtimeChunkCallbacks` contract 1:1 — the contract the deleted
|
|
112
|
+
* `useRealtimeChunkProcessor` wrapper exposed, now pinned directly by
|
|
113
|
+
* `__tests__/chat-stream-reducer-golden.test.ts`.
|
|
114
|
+
*
|
|
115
|
+
* `segments-after-approval-result` is the one conditional emission: the
|
|
116
|
+
* legacy processor emitted a cumulative `onSegmentsUpdate` for a
|
|
117
|
+
* non-escalated APPROVAL_RESULT only when the consumer had NOT wired
|
|
118
|
+
* `onApprovalResolved` — the effect sink resolves that at dispatch time.
|
|
119
|
+
*/
|
|
120
|
+
export interface ChatReducerEffect {
|
|
121
|
+
name: 'onStreamStart' | 'onStreamEnd' | 'onMetadata' | 'onSegmentsUpdate' | 'onError' | 'onUserMessage' | 'onTokenUsage' | 'onDirectMessage' | 'onSystemMessage' | 'onEscalatedApproval' | 'onEscalatedApprovalResult' | 'onApprovalResolved' | 'onToolExecuted' | 'onAgentBusy' | 'onDialogClosed' | 'segments-after-approval-result';
|
|
122
|
+
args: unknown[];
|
|
123
|
+
}
|
|
124
|
+
export interface ChatStreamReducerOptions {
|
|
125
|
+
/** Which transport's turn kernel drives `apply()`. Default `'nats'`. */
|
|
126
|
+
transport?: 'sse' | 'nats';
|
|
127
|
+
/** Seed for the approval-status map (request-id → status). */
|
|
128
|
+
approvalStatuses?: Record<string, ChatApprovalStatus>;
|
|
129
|
+
/** Batch APPROVAL_REQUESTs render as one card (default true) or unfold. */
|
|
130
|
+
batchApprovalsEnabled?: boolean;
|
|
131
|
+
/** Approval types displayed inline; others escalate. Default ['CLIENT']. */
|
|
132
|
+
displayApprovalTypes?: string[];
|
|
133
|
+
/** Opaque approve/reject handlers stamped onto approval segments. */
|
|
134
|
+
callbacks?: AccumulatorCallbacks;
|
|
135
|
+
/** Engage the direct-mode barrier optimistically (host-known takeover). */
|
|
136
|
+
isDirectMode?: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Legacy-processor parity knob: post-MESSAGE_END tool chunks route
|
|
139
|
+
* cross-message (`onToolExecuted` effect + `applyToolExecutionToMessages`)
|
|
140
|
+
* only when the consumer wired the cross-message updater; otherwise they
|
|
141
|
+
* fall through the accumulator like the pre-callback code path. Default
|
|
142
|
+
* true (both first-party adapters wire it).
|
|
143
|
+
*/
|
|
144
|
+
crossMessageToolRouting?: boolean;
|
|
145
|
+
/** Effect sink for callback-contract consumers (compat wrapper). */
|
|
146
|
+
onEffect?: (effect: ChatReducerEffect) => void;
|
|
147
|
+
/** Notified after every state mutation (the dialog store wires this). */
|
|
148
|
+
onChange?: () => void;
|
|
149
|
+
/**
|
|
150
|
+
* Whether an ADMIN-authored `MESSAGE_REQUEST` may be consumed as OUR OWN
|
|
151
|
+
* optimistic echo (see `pushOptimisticSend`). Default `false`.
|
|
152
|
+
*
|
|
153
|
+
* Who the local operator is decides this, and it differs per host:
|
|
154
|
+
* - Hub website chat / ticket CLIENT side: the local user is NEVER the
|
|
155
|
+
* admin, so an ADMIN-authored inbound row is a technician's reply and
|
|
156
|
+
* must ALWAYS render. Consuming it as an echo would silently delete a
|
|
157
|
+
* real message whenever its text happened to match ours. → `false`.
|
|
158
|
+
* - OpenFrame product app (Mingo + ticket ADMIN side): the operator IS
|
|
159
|
+
* the admin, so their own sends echo back as ADMIN and MUST be
|
|
160
|
+
* deduped, or every message renders twice. → `true`.
|
|
161
|
+
*
|
|
162
|
+
* Only ever applies to text the host itself registered via
|
|
163
|
+
* `pushOptimisticSend`, so enabling it cannot drop a message the host
|
|
164
|
+
* did not just send.
|
|
165
|
+
*/
|
|
166
|
+
ownEchoIncludesAdmin?: boolean;
|
|
167
|
+
/**
|
|
168
|
+
* The LOCAL user's id, either as a value or as a GETTER resolved at event
|
|
169
|
+
* time. When it resolves to a value, an inbound `MESSAGE_REQUEST` that
|
|
170
|
+
* DECLARES a different author (`event.userId !== selfUserId`) may not be
|
|
171
|
+
* consumed as our own optimistic echo.
|
|
172
|
+
*
|
|
173
|
+
* Without it the echo list matches on RAW TEXT alone, which on a shared
|
|
174
|
+
* ADMIN side (two technicians in one ticket, `ownEchoIncludesAdmin: true`)
|
|
175
|
+
* can silently delete a colleague's message: if OUR send never echoes back,
|
|
176
|
+
* its entry stays armed and the next identical text from anyone — canned
|
|
177
|
+
* replies like "ok" / "done" / "on it" make this routine — is swallowed.
|
|
178
|
+
*
|
|
179
|
+
* The guard FAILS OPEN, deliberately: a row that carries NO `userId` is not
|
|
180
|
+
* "someone else's", it is UNATTRIBUTED, and rejecting it would disable
|
|
181
|
+
* dedup entirely (→ every send rendered twice) on any transport whose
|
|
182
|
+
* decoder does not surface the author id. Such a row falls back to the
|
|
183
|
+
* text + `OWN_ECHO_TTL_MS` behaviour instead, and a one-shot `console.warn`
|
|
184
|
+
* makes the missing id observable.
|
|
185
|
+
*
|
|
186
|
+
* Pass a FUNCTION whenever the id can change or arrive late (auth
|
|
187
|
+
* rehydration, logout / login-as-another-user without a reload): reducers
|
|
188
|
+
* are retained for the store's lifetime, so a value captured at creation
|
|
189
|
+
* time can go stale and silently disable the guard.
|
|
190
|
+
*/
|
|
191
|
+
selfUserId?: string | (() => string | undefined);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* How long an un-consumed optimistic-echo entry stays armed **on the
|
|
195
|
+
* UNATTRIBUTED path**. Long enough to cover any realistic send → server-echo
|
|
196
|
+
* round trip, short enough that a send whose echo never lands cannot swallow
|
|
197
|
+
* an unrelated identical message later in the conversation.
|
|
198
|
+
*
|
|
199
|
+
* A row that is DECLARED ours (`selfUserId` set and `event.userId ===
|
|
200
|
+
* selfUserId`) gets the LONGER `OWN_ECHO_AUTHOR_TTL_MS` instead — see there.
|
|
201
|
+
*/
|
|
202
|
+
export declare const OWN_ECHO_TTL_MS = 30000;
|
|
203
|
+
/**
|
|
204
|
+
* How long an un-consumed entry stays armed on the AUTHOR-MATCHED path
|
|
205
|
+
* (`selfUserId` set, inbound row declares the same author).
|
|
206
|
+
*
|
|
207
|
+
* The author check rules out CROSS-technician theft, but NOT the same user on
|
|
208
|
+
* a second tab or device: a row THIS tab did not originate looks exactly like
|
|
209
|
+
* an author-matched echo. So the entry still needs an upper bound — without
|
|
210
|
+
* one, a send whose echo never lands (dropped frame, backend text
|
|
211
|
+
* normalization, reconnect gap) leaves the entry armed for the reducer's
|
|
212
|
+
* whole lifetime, and the next identical text from the same user on ANOTHER
|
|
213
|
+
* tab is silently swallowed. With `MAX_PENDING_ECHOES` armed slots and canned
|
|
214
|
+
* replies ("ok", "done", "on it") being exactly the recurring strings, that is
|
|
215
|
+
* a routine message-LOSS bug, strictly worse than the duplicate row it trades
|
|
216
|
+
* against.
|
|
217
|
+
*
|
|
218
|
+
* Ten minutes is the bound: generously longer than the slow echo the author
|
|
219
|
+
* path exists to protect (a JetStream catch-up replay after a network gap
|
|
220
|
+
* arrives long after the send, and its rows carry `seq`, so the seq-less
|
|
221
|
+
* content-dedup fallback cannot rescue it), and far shorter than the
|
|
222
|
+
* "same text again an hour later" window in which a stale entry does damage.
|
|
223
|
+
*
|
|
224
|
+
* It is a BACKSTOP, not the usual bound: `turn-end` disarms entries much
|
|
225
|
+
* sooner (see `purgeEchoesAtTurnEnd`). The ten minutes only ever apply to a
|
|
226
|
+
* dialog that never reports a turn boundary at all.
|
|
227
|
+
*/
|
|
228
|
+
export declare const OWN_ECHO_AUTHOR_TTL_MS: number;
|
|
229
|
+
/** One armed optimistic-echo entry: the sent text and the wall-clock ms it was
|
|
230
|
+
* armed at. Exported because the LRU-eviction round trip PARKS these (see
|
|
231
|
+
* `getPendingEchoes` / `InitializeExtras.pendingEchoes`). */
|
|
232
|
+
export interface PendingEcho {
|
|
233
|
+
text: string;
|
|
234
|
+
at: number;
|
|
235
|
+
}
|
|
236
|
+
export interface InitializeExtras {
|
|
237
|
+
existingSegments?: MessageSegment[];
|
|
238
|
+
pendingApprovals?: Map<string, PendingApproval>;
|
|
239
|
+
executingTools?: Map<string, ExecutingToolState>;
|
|
240
|
+
escalatedApprovals?: Map<string, EscalatedApprovalData>;
|
|
241
|
+
/**
|
|
242
|
+
* Approval statuses PARKED from a previous instance of this key (LRU
|
|
243
|
+
* eviction hands them to `onEvict`). Merged with the same state-monotonic
|
|
244
|
+
* precedence as `mergeApprovalStatuses` — a resolved approval must not come
|
|
245
|
+
* back as actionable, which is exactly why `resetForDialogSwitch` preserves
|
|
246
|
+
* this map rather than clearing it.
|
|
247
|
+
*/
|
|
248
|
+
approvalStatuses?: Record<string, ChatApprovalStatus>;
|
|
249
|
+
/**
|
|
250
|
+
* Seq cursor PARKED from a previous instance of this key. A recreated
|
|
251
|
+
* reducer starts at `-Infinity`, so a host that replays from its own cursor
|
|
252
|
+
* would re-apply already-applied events; restoring the parked value keeps
|
|
253
|
+
* `apply()`'s idempotency gate intact across an eviction. Only ever moves
|
|
254
|
+
* the gate FORWARD (a lower value is ignored).
|
|
255
|
+
*/
|
|
256
|
+
lastAppliedSeq?: number;
|
|
257
|
+
/**
|
|
258
|
+
* Armed optimistic-echo entries PARKED from a previous instance of this key.
|
|
259
|
+
* Without them, a key LRU-evicted between `pushOptimisticSend` and its
|
|
260
|
+
* `MESSAGE_REQUEST` echo leaves the replacement reducer with nothing armed,
|
|
261
|
+
* so the echo renders a DUPLICATE user bubble. Entries already past
|
|
262
|
+
* `OWN_ECHO_AUTHOR_TTL_MS` are dropped on restore (an expired entry could
|
|
263
|
+
* only swallow an unrelated identical message), and the list is capped at
|
|
264
|
+
* `MAX_PENDING_ECHOES` exactly as the live path is.
|
|
265
|
+
*/
|
|
266
|
+
pendingEchoes?: readonly PendingEcho[];
|
|
267
|
+
/**
|
|
268
|
+
* Whether the restored thread is a RESUMED dialog (a `MESSAGE_START` already
|
|
269
|
+
* fired server-side), which makes post-stream continuation chunks append
|
|
270
|
+
* into the existing bubble instead of taking the cold-start cumulative path.
|
|
271
|
+
* Defaults to "the restored thread is non-empty" — an eviction restore of a
|
|
272
|
+
* key that never streamed must NOT claim it did, or a cold text-delta with
|
|
273
|
+
* no preceding `turn-start` appends into a bubble that was never spawned.
|
|
274
|
+
* Pass explicitly only to override that derivation.
|
|
275
|
+
*/
|
|
276
|
+
resumed?: boolean;
|
|
277
|
+
}
|
|
278
|
+
export interface BeginSseSendOptions {
|
|
279
|
+
text: string;
|
|
280
|
+
hidden?: boolean;
|
|
281
|
+
userName?: string;
|
|
282
|
+
assistantName?: string;
|
|
283
|
+
assistantAvatar?: string;
|
|
284
|
+
}
|
|
285
|
+
export interface ChatStreamReducer {
|
|
286
|
+
/** Apply one normalized stream event. */
|
|
287
|
+
apply(event: ChatStreamEvent): void;
|
|
288
|
+
/** Current immutable state snapshot (stable identity between mutations). */
|
|
289
|
+
readonly state: ChatReducerState;
|
|
290
|
+
/** Full reset — thread, per-turn kernel, dedup sets, seq gate, maps. */
|
|
291
|
+
reset(): void;
|
|
292
|
+
/**
|
|
293
|
+
* Seed the thread (history hydration / persisted-state rehydration) and
|
|
294
|
+
* optionally the per-turn kernel + escalated approvals (resume of an
|
|
295
|
+
* incomplete turn). `messages: null` keeps the current thread. Escalated
|
|
296
|
+
* entries are re-surfaced via `onEscalatedApproval` effects. Marks the
|
|
297
|
+
* instance as having streamed so continuation chunks append.
|
|
298
|
+
*/
|
|
299
|
+
initializeWithState(messages: UnifiedChatMessage[] | null, extras?: InitializeExtras): void;
|
|
300
|
+
setMessages(messages: UnifiedChatMessage[]): void;
|
|
301
|
+
prependMessages(messages: UnifiedChatMessage[]): void;
|
|
302
|
+
/** Optimistic local send: user bubble (+ echo record) + assistant placeholder. */
|
|
303
|
+
pushOptimisticSend(text: string, hidden?: boolean): void;
|
|
304
|
+
/** Wipe thread + per-turn kernel, keep approval statuses (legacy clear). */
|
|
305
|
+
clearThread(): void;
|
|
306
|
+
/** Per-dialog reset (keeps approval statuses — request-ids are global). */
|
|
307
|
+
resetForDialogSwitch(): void;
|
|
308
|
+
beginSseSend(options: BeginSseSendOptions): void;
|
|
309
|
+
endSseTurn(): void;
|
|
310
|
+
failSseTurn(errorMessage: string): void;
|
|
311
|
+
seedSseMaps(seed: {
|
|
312
|
+
sources?: Array<[number, unknown[]]>;
|
|
313
|
+
refs?: Array<[number, Record<string, ChatRef>]>;
|
|
314
|
+
sendCount?: number;
|
|
315
|
+
}): void;
|
|
316
|
+
setPhase(phase: StreamingPhase): void;
|
|
317
|
+
setApprovalStatus(requestId: string, status: ChatApprovalStatus | null): void;
|
|
318
|
+
/**
|
|
319
|
+
* CANONICAL merge of a PERSISTED status map (host-side store, history
|
|
320
|
+
* hydration, dialog-switch top-up) into this reducer's map.
|
|
321
|
+
*
|
|
322
|
+
* Precedence is STATE-MONOTONIC, not source-based, because either side can
|
|
323
|
+
* be the stale one:
|
|
324
|
+
* - persisted lags (a snapshot taken before the stream resolved the
|
|
325
|
+
* request) → the stream's resolved status must win, or a just-approved
|
|
326
|
+
* card would be downgraded back to `pending` and re-arm its buttons;
|
|
327
|
+
* - the STREAM lags (this tab saw the request, the operator resolved it in
|
|
328
|
+
* a SECOND TAB, and the persisted map is the one carrying the truth) →
|
|
329
|
+
* the persisted resolution must win, or the card stays actionable and
|
|
330
|
+
* invites a second approve on an already-resolved request.
|
|
331
|
+
*
|
|
332
|
+
* With statuses being exactly `pending | approved | rejected | cancelled`,
|
|
333
|
+
* "resolved beats pending" is a total order that gets both directions
|
|
334
|
+
* right: take the persisted value when ours is `pending` and theirs isn't;
|
|
335
|
+
* keep ours otherwise. Persisted entries still fill in every request-id the
|
|
336
|
+
* stream hasn't seen (other dialogs, pre-session history).
|
|
337
|
+
*/
|
|
338
|
+
mergeApprovalStatuses(persisted: Record<string, ChatApprovalStatus>): void;
|
|
339
|
+
setDirectMode(isDirectMode: boolean): void;
|
|
340
|
+
setDialogTokenUsage(usage: DialogTokenUsage | null): void;
|
|
341
|
+
/** +1/-1 windows during catchup replay (suppresses agent-busy locks). */
|
|
342
|
+
adjustAgentBusySuppression(delta: number): void;
|
|
343
|
+
armAdoptTrailingAssistant(value: boolean): void;
|
|
344
|
+
projectApprovalResolution(requestId: string, status: ChatApprovalStatus, resolvedByName?: string | null): void;
|
|
345
|
+
projectToolExecution(segment: ToolExecutionSegment): void;
|
|
346
|
+
/**
|
|
347
|
+
* The seq gate's current value (`-Infinity` before anything seq-carrying was
|
|
348
|
+
* applied). Not part of `state` — it is bookkeeping, not render input — but
|
|
349
|
+
* a host that PARKS a reducer (LRU eviction, dialog swap) needs it to
|
|
350
|
+
* restore idempotency on the recreated instance via
|
|
351
|
+
* `initializeWithState(messages, { lastAppliedSeq })`.
|
|
352
|
+
*/
|
|
353
|
+
getLastAppliedSeq(): number;
|
|
354
|
+
/**
|
|
355
|
+
* Currently-armed optimistic-echo entries. Same rationale as
|
|
356
|
+
* `getLastAppliedSeq`: not render input, but a host that PARKS a reducer
|
|
357
|
+
* needs them to restore echo dedup on the recreated instance via
|
|
358
|
+
* `initializeWithState(messages, { pendingEchoes })` — otherwise an echo
|
|
359
|
+
* in flight across the eviction renders a duplicate user bubble.
|
|
360
|
+
*/
|
|
361
|
+
getPendingEchoes(): readonly PendingEcho[];
|
|
362
|
+
getSegments(): MessageSegment[];
|
|
363
|
+
updateApprovalStatus(requestId: string, status: ChatApprovalStatus, resolvedByName?: string | null): MessageSegment[];
|
|
364
|
+
getPendingEscalated(): Map<string, EscalatedApprovalData>;
|
|
365
|
+
}
|
|
366
|
+
export declare function createChatStreamReducer(options?: ChatStreamReducerOptions): ChatStreamReducer;
|
|
367
|
+
//# sourceMappingURL=chat-stream-reducer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-stream-reducer.d.ts","sourceRoot":"","sources":["../../../../src/components/chat/stream/chat-stream-reducer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createDeltaBatcher — framework-free, append-only batching of `text-delta`
|
|
3
|
+
* and `thinking-delta` events.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS IS SHARED (and not a hook-private detail): every host that drives a
|
|
6
|
+
* `ChatStreamReducer` from a raw transport needs the SAME batching, and a
|
|
7
|
+
* copy-paste of it drifts. The React wrapper (`useChatStreamReducer`) and any
|
|
8
|
+
* non-React mirror in a product app both consume this one implementation.
|
|
9
|
+
*
|
|
10
|
+
* Behaviour:
|
|
11
|
+
* - `push(event, key)` queues delta events and returns true; anything else
|
|
12
|
+
* returns false so the caller can flush + apply it synchronously (ordering:
|
|
13
|
+
* completion state must always land on fully-applied deltas).
|
|
14
|
+
* - CONSECUTIVE same-type deltas are COALESCED into one event (the reducer's
|
|
15
|
+
* append semantics distribute over concatenation), so a 60-delta/sec turn
|
|
16
|
+
* costs ONE `applyOne` per frame instead of 60. `text-delta` additionally
|
|
17
|
+
* requires a matching `leading` flag before coalescing.
|
|
18
|
+
* - A flush is scheduled on `requestAnimationFrame` when available, with a
|
|
19
|
+
* timer fallback that is ALWAYS armed (rAF pauses in background tabs, and a
|
|
20
|
+
* hidden chat panel must still keep its thread current).
|
|
21
|
+
* - `key` is an opaque routing token handed back to `applyOne`. When it
|
|
22
|
+
* changes, the pending batch is flushed against the PREVIOUS key first, so
|
|
23
|
+
* deltas never land on the wrong dialog/side.
|
|
24
|
+
*/
|
|
25
|
+
import type { ChatStreamEvent, TextDeltaEvent, ThinkingDeltaEvent } from '../../../chat-protocol/events';
|
|
26
|
+
export declare const DELTA_FLUSH_FALLBACK_MS = 50;
|
|
27
|
+
export type DeltaEvent = TextDeltaEvent | ThinkingDeltaEvent;
|
|
28
|
+
export declare function isDeltaEvent(event: ChatStreamEvent): event is DeltaEvent;
|
|
29
|
+
export interface CreateDeltaBatcherOptions<K> {
|
|
30
|
+
/** Apply one (possibly coalesced) delta against `key`'s reducer. */
|
|
31
|
+
applyOne: (event: DeltaEvent, key: K | undefined) => void;
|
|
32
|
+
/** Called after a flush that applied at least one delta. */
|
|
33
|
+
onFlushed?: (appliedCount: number) => void;
|
|
34
|
+
/** Timer fallback in ms (default `DELTA_FLUSH_FALLBACK_MS`). */
|
|
35
|
+
fallbackMs?: number;
|
|
36
|
+
}
|
|
37
|
+
export interface DeltaBatcher<K = unknown> {
|
|
38
|
+
/**
|
|
39
|
+
* Queue `event` when it is a delta (returns true). Non-delta events are NOT
|
|
40
|
+
* queued and return false — the caller flushes and applies them itself.
|
|
41
|
+
*/
|
|
42
|
+
push(event: ChatStreamEvent, key?: K): boolean;
|
|
43
|
+
/** Synchronously apply the pending batch and cancel any scheduled flush. */
|
|
44
|
+
flush(): void;
|
|
45
|
+
/** Flush and release timers — call on teardown so tail deltas aren't lost. */
|
|
46
|
+
dispose(): void;
|
|
47
|
+
/** Queued (post-coalescing) delta count. Test/diagnostic surface. */
|
|
48
|
+
readonly pendingCount: number;
|
|
49
|
+
}
|
|
50
|
+
export declare function createDeltaBatcher<K = unknown>({ applyOne, onFlushed, fallbackMs, }: CreateDeltaBatcherOptions<K>): DeltaBatcher<K>;
|
|
51
|
+
//# sourceMappingURL=delta-batcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delta-batcher.d.ts","sourceRoot":"","sources":["../../../../src/components/chat/stream/delta-batcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAExG,eAAO,MAAM,uBAAuB,KAAK,CAAA;AAEzC,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,kBAAkB,CAAA;AAE5D,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,KAAK,IAAI,UAAU,CAExE;AAED,MAAM,WAAW,yBAAyB,CAAC,CAAC;IAC1C,oEAAoE;IACpE,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,CAAA;IACzD,4DAA4D;IAC5D,SAAS,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO;IACvC;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAA;IAC9C,4EAA4E;IAC5E,KAAK,IAAI,IAAI,CAAA;IACb,8EAA8E;IAC9E,OAAO,IAAI,IAAI,CAAA;IACf,qEAAqE;IACrE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC9B;AAED,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,OAAO,EAAE,EAC9C,QAAQ,EACR,SAAS,EACT,UAAoC,GACrC,EAAE,yBAAyB,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAgGhD"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat stream module — ONE master reader for every chat event source
|
|
3
|
+
* (SSE decoder, NATS chunk decoder, historical replay). See
|
|
4
|
+
* `chat-stream-reducer.ts` for the architecture note.
|
|
5
|
+
*
|
|
6
|
+
* NOTE: the pure message-mutation helpers (`appendToTrailingAssistant`,
|
|
7
|
+
* `applyToolExecutionToMessages`, …) are intentionally NOT star-exported
|
|
8
|
+
* here — their legacy public home is `use-nats-chat-adapter`, which
|
|
9
|
+
* re-exports them from `./message-mutations`; exporting the same bindings
|
|
10
|
+
* from two star-exported barrels would make them ambiguous and drop them
|
|
11
|
+
* from the chat barrel entirely.
|
|
12
|
+
*/
|
|
13
|
+
export { createChatStreamReducer, createEmptyTurnMeta, type BeginSseSendOptions, type ChatReducerEffect, type ChatReducerState, type ChatStreamReducer, type ChatStreamReducerOptions, type ChatTurnMetaState, type EscalatedApprovalData, type InitializeExtras, type PendingEcho, } from './chat-stream-reducer';
|
|
14
|
+
export { createChatDialogStore, DEFAULT_DIALOG_SIDE, DEFAULT_MAX_REDUCERS, MAX_PENDING_RETAIN_KEYS, type ChatDialogSide, type ChatDialogStore, type CreateChatDialogStoreOptions, type EvictedReducerState, } from './chat-dialog-store';
|
|
15
|
+
export { createDeltaBatcher, type CreateDeltaBatcherOptions, type DeltaBatcher, type DeltaEvent, } from './delta-batcher';
|
|
16
|
+
export { useChatStreamReducer, type UseChatStreamReducerOptions, type UseChatStreamReducerReturn, } from './use-chat-stream-reducer';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/chat/stream/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,4BAA4B,EACjC,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAA;AAO5B,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EACjB,KAAK,UAAU,GAChB,MAAM,iBAAiB,CAAA;AAExB,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,GAChC,MAAM,2BAA2B,CAAA"}
|