@flamingo-stack/openframe-frontend-core 0.0.492-1506.3956.1 → 0.0.492
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/{chunk-KX3K2EH4.cjs → chunk-2XAAFAQT.cjs} +11 -11
- package/dist/{chunk-KX3K2EH4.cjs.map → chunk-2XAAFAQT.cjs.map} +1 -1
- package/dist/{chunk-JHEIOXXZ.js → chunk-4AWRPTKE.js} +5 -6
- package/dist/chunk-4AWRPTKE.js.map +1 -0
- package/dist/{chunk-PTXZB2KW.js → chunk-4J3CB4KO.js} +2 -2
- package/dist/{chunk-7N7YZCHR.cjs → chunk-4MLOMMDX.cjs} +5519 -7555
- package/dist/chunk-4MLOMMDX.cjs.map +1 -0
- package/dist/{chunk-MEMRQTOI.js → chunk-56J7RLMM.js} +10434 -12470
- package/dist/chunk-56J7RLMM.js.map +1 -0
- package/dist/{chunk-MEIGDK7M.cjs → chunk-5ICPQAUI.cjs} +60 -60
- package/dist/chunk-5ICPQAUI.cjs.map +1 -0
- package/dist/{chunk-BEEPU44B.js → chunk-5RHJVPL7.js} +10 -10
- package/dist/chunk-5RHJVPL7.js.map +1 -0
- package/dist/{chunk-XOFE2RT4.js → chunk-BTIRYGDT.js} +4 -4
- package/dist/{chunk-JSEJ5MH5.cjs → chunk-DY6AJNVK.cjs} +5 -5
- package/dist/{chunk-JSEJ5MH5.cjs.map → chunk-DY6AJNVK.cjs.map} +1 -1
- package/dist/{chunk-PLB6CQSO.js → chunk-FG5ALORQ.js} +2 -2
- package/dist/{chunk-QG2EBIH5.cjs → chunk-J2J36PEN.cjs} +31 -31
- package/dist/{chunk-QG2EBIH5.cjs.map → chunk-J2J36PEN.cjs.map} +1 -1
- package/dist/{chunk-A6FPYAUD.cjs → chunk-LIRXKEOE.cjs} +15 -15
- package/dist/{chunk-A6FPYAUD.cjs.map → chunk-LIRXKEOE.cjs.map} +1 -1
- package/dist/{chunk-MSL5NE3N.cjs → chunk-LVDI7R7T.cjs} +37 -37
- package/dist/{chunk-MSL5NE3N.cjs.map → chunk-LVDI7R7T.cjs.map} +1 -1
- package/dist/{chunk-MPUQFRJG.cjs → chunk-M4SCYPCR.cjs} +7 -7
- package/dist/{chunk-MPUQFRJG.cjs.map → chunk-M4SCYPCR.cjs.map} +1 -1
- package/dist/{chunk-QDGB2DKY.cjs → chunk-O6S6Q52W.cjs} +26 -26
- package/dist/{chunk-QDGB2DKY.cjs.map → chunk-O6S6Q52W.cjs.map} +1 -1
- package/dist/{chunk-W56JYDSC.cjs → chunk-OFFYASUY.cjs} +27 -28
- package/dist/chunk-OFFYASUY.cjs.map +1 -0
- package/dist/{chunk-NCAN6VUF.js → chunk-P3ITKTUE.js} +2 -2
- package/dist/{chunk-M6FEGPQJ.cjs → chunk-PI5BMKV3.cjs} +62 -62
- package/dist/{chunk-M6FEGPQJ.cjs.map → chunk-PI5BMKV3.cjs.map} +1 -1
- package/dist/{chunk-34TDA22K.js → chunk-QHRNQTW5.js} +2 -2
- package/dist/{chunk-OL3MJKUI.js → chunk-SZWMJIH3.js} +2 -2
- package/dist/{chunk-MZRIRQYV.js → chunk-U5LU56JE.js} +2 -2
- package/dist/{chunk-C65NCIMV.cjs → chunk-VML66FJQ.cjs} +9 -9
- package/dist/{chunk-C65NCIMV.cjs.map → chunk-VML66FJQ.cjs.map} +1 -1
- package/dist/{chunk-3LKB722U.js → chunk-WKWZ7LVC.js} +2 -2
- package/dist/{chunk-J3VBVR4W.js → chunk-WPFLV4QJ.js} +4 -4
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- 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 +1 -0
- 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 +48 -15
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts +6 -0
- package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +1 -0
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +57 -10
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +2 -10
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.d.ts +0 -1
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +13 -21
- package/dist/components/chat/types/api.types.d.ts +81 -1
- package/dist/components/chat/types/api.types.d.ts.map +1 -1
- package/dist/components/chat/types/processing.types.d.ts +93 -2
- package/dist/components/chat/types/processing.types.d.ts.map +1 -1
- package/dist/components/chat/utils/chunk-parser.d.ts +25 -0
- package/dist/components/chat/utils/chunk-parser.d.ts.map +1 -0
- package/dist/components/chat/utils/extract-incomplete-message-state.d.ts +6 -34
- package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
- package/dist/components/chat/utils/index.d.ts +3 -2
- package/dist/components/chat/utils/index.d.ts.map +1 -1
- package/dist/components/chat/utils/process-historical-messages.d.ts +11 -59
- package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/floating-walkthrough-video.d.ts.map +1 -1
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/help-center-pages/index.cjs +20 -20
- package/dist/components/help-center-pages/index.js +11 -11
- package/dist/components/index.cjs +130 -172
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +21 -63
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/navigation/navigation-sidebar-item.d.ts +23 -1
- package/dist/components/navigation/navigation-sidebar-item.d.ts.map +1 -1
- package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +5 -5
- package/dist/components/tickets/index.js +4 -4
- package/dist/components/ui/data-table/data-table-column-filter.d.ts.map +1 -1
- package/dist/components/ui/data-table/data-table-header.d.ts.map +1 -1
- package/dist/components/ui/data-table/data-table-skeleton.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +2 -44
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.d.ts +2 -1
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +13 -55
- package/dist/components/ui/rich-markdown-renderer.d.ts +34 -0
- package/dist/components/ui/rich-markdown-renderer.d.ts.map +1 -0
- package/dist/components/ui/simple-markdown-renderer.d.ts +73 -0
- package/dist/components/ui/simple-markdown-renderer.d.ts.map +1 -0
- package/dist/components/ui/tab-navigation.d.ts.map +1 -1
- package/dist/index.cjs +2 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +13 -71
- package/dist/utils/index.cjs +40 -169
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +41 -162
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/markdown-section-extractor.d.ts.map +1 -1
- package/package.json +1 -7
- package/src/components/chat/__tests__/chat-message-list.test.tsx +5 -293
- package/src/components/chat/chat-message-enhanced.tsx +20 -122
- package/src/components/chat/chat-message-list.tsx +109 -358
- package/src/components/chat/embeddable-chat.tsx +0 -6
- package/src/components/chat/hooks/.index.md +33 -30
- package/src/components/chat/hooks/.use-nats-chat-adapter.md +55 -36
- package/src/components/chat/hooks/.use-realtime-chunk-processor.md +77 -0
- package/src/components/chat/hooks/index.ts +1 -0
- package/src/components/chat/hooks/use-chat.ts +0 -8
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +825 -243
- package/src/components/chat/hooks/use-realtime-chunk-processor.ts +489 -0
- package/src/components/chat/hooks/use-sse-chat-adapter.ts +684 -380
- package/src/components/chat/index.ts +0 -5
- package/src/components/chat/thinking-display.tsx +1 -1
- package/src/components/chat/types/.api.types.md +49 -39
- package/src/components/chat/types/.processing.types.md +58 -29
- package/src/components/chat/types/api.types.ts +73 -6
- package/src/components/chat/types/processing.types.ts +53 -11
- package/src/components/chat/utils/.chunk-parser.md +62 -0
- package/src/components/chat/utils/.index.md +40 -31
- package/src/components/chat/utils/chunk-parser.ts +262 -0
- package/src/components/chat/utils/extract-incomplete-message-state.ts +5 -63
- package/src/components/chat/utils/index.ts +8 -5
- package/src/components/chat/utils/process-historical-messages.ts +386 -358
- package/src/components/features/floating-walkthrough-video.tsx +3 -1
- package/src/components/navigation/navigation-sidebar-item.tsx +80 -30
- package/src/components/navigation/navigation-sidebar.tsx +106 -46
- package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +4 -22
- package/src/components/shared/legal-document/legal-document-page.tsx +1 -1
- package/src/components/shared/product-release/release-detail-page.tsx +4 -19
- package/src/components/ui/.simple-markdown-renderer.md +52 -0
- package/src/components/ui/__tests__/tab-navigation.test.tsx +159 -0
- package/src/components/ui/data-table/data-table-column-filter.tsx +7 -1
- package/src/components/ui/data-table/data-table-header.tsx +4 -1
- package/src/components/ui/data-table/data-table-skeleton.tsx +7 -0
- package/src/components/ui/index.ts +2 -1
- package/src/components/ui/release-changelog-section.tsx +1 -1
- package/src/components/ui/rich-markdown-renderer.tsx +1223 -0
- package/src/components/ui/simple-markdown-renderer.tsx +964 -0
- package/src/components/ui/tab-navigation.tsx +370 -171
- package/src/components/ui/ticket-info-section.tsx +1 -1
- package/src/utils/index.ts +0 -1
- package/src/utils/markdown-section-extractor.ts +56 -41
- package/dist/chat-protocol/decode.d.ts +0 -58
- package/dist/chat-protocol/decode.d.ts.map +0 -1
- package/dist/chat-protocol/encode.d.ts +0 -44
- package/dist/chat-protocol/encode.d.ts.map +0 -1
- package/dist/chat-protocol/env-flag.d.ts +0 -35
- package/dist/chat-protocol/env-flag.d.ts.map +0 -1
- package/dist/chat-protocol/events.d.ts +0 -207
- package/dist/chat-protocol/events.d.ts.map +0 -1
- package/dist/chat-protocol/frames.d.ts +0 -213
- package/dist/chat-protocol/frames.d.ts.map +0 -1
- package/dist/chat-protocol/index.cjs +0 -500
- package/dist/chat-protocol/index.cjs.map +0 -1
- package/dist/chat-protocol/index.d.ts +0 -18
- package/dist/chat-protocol/index.d.ts.map +0 -1
- package/dist/chat-protocol/index.js +0 -484
- package/dist/chat-protocol/index.js.map +0 -1
- package/dist/chat-protocol/ip-normalize.d.ts +0 -44
- package/dist/chat-protocol/ip-normalize.d.ts.map +0 -1
- package/dist/chat-protocol/nats-decoder.d.ts +0 -26
- package/dist/chat-protocol/nats-decoder.d.ts.map +0 -1
- package/dist/chunk-7N7YZCHR.cjs.map +0 -1
- package/dist/chunk-BEEPU44B.js.map +0 -1
- package/dist/chunk-JHEIOXXZ.js.map +0 -1
- package/dist/chunk-MEIGDK7M.cjs.map +0 -1
- package/dist/chunk-MEMRQTOI.js.map +0 -1
- package/dist/chunk-W56JYDSC.cjs.map +0 -1
- package/dist/components/chat/stream/chat-dialog-store.d.ts +0 -184
- package/dist/components/chat/stream/chat-dialog-store.d.ts.map +0 -1
- package/dist/components/chat/stream/chat-stream-reducer.d.ts +0 -367
- package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +0 -1
- package/dist/components/chat/stream/delta-batcher.d.ts +0 -51
- package/dist/components/chat/stream/delta-batcher.d.ts.map +0 -1
- package/dist/components/chat/stream/index.d.ts +0 -17
- package/dist/components/chat/stream/index.d.ts.map +0 -1
- package/dist/components/chat/stream/message-mutations.d.ts +0 -77
- package/dist/components/chat/stream/message-mutations.d.ts.map +0 -1
- package/dist/components/chat/stream/use-chat-stream-reducer.d.ts +0 -22
- package/dist/components/chat/stream/use-chat-stream-reducer.d.ts.map +0 -1
- package/dist/components/ui/markdown/base-components.d.ts +0 -51
- package/dist/components/ui/markdown/base-components.d.ts.map +0 -1
- package/dist/components/ui/markdown/engine.d.ts +0 -99
- package/dist/components/ui/markdown/engine.d.ts.map +0 -1
- package/dist/components/ui/markdown/heading-ids.d.ts +0 -65
- package/dist/components/ui/markdown/heading-ids.d.ts.map +0 -1
- package/dist/components/ui/markdown/index.d.ts +0 -18
- package/dist/components/ui/markdown/index.d.ts.map +0 -1
- package/dist/components/ui/markdown/mermaid-diagram.d.ts +0 -79
- package/dist/components/ui/markdown/mermaid-diagram.d.ts.map +0 -1
- package/dist/components/ui/markdown/rich/embed-overrides.d.ts +0 -14
- package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +0 -1
- package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts +0 -40
- package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts.map +0 -1
- package/dist/components/ui/markdown/rich/shortcodes.d.ts +0 -8
- package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +0 -1
- package/dist/components/ui/markdown/sanitize.d.ts +0 -153
- package/dist/components/ui/markdown/sanitize.d.ts.map +0 -1
- package/dist/components/ui/markdown/simple-markdown-renderer.d.ts +0 -23
- package/dist/components/ui/markdown/simple-markdown-renderer.d.ts.map +0 -1
- package/dist/components/ui/markdown/streaming.d.ts +0 -78
- package/dist/components/ui/markdown/streaming.d.ts.map +0 -1
- package/dist/components/ui/markdown/text-size.d.ts +0 -34
- package/dist/components/ui/markdown/text-size.d.ts.map +0 -1
- package/dist/utils/markdown-fences.d.ts +0 -42
- package/dist/utils/markdown-fences.d.ts.map +0 -1
- package/dist/utils/markdown-heading-id.d.ts +0 -85
- package/dist/utils/markdown-heading-id.d.ts.map +0 -1
- package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +0 -317
- package/src/chat-protocol/__tests__/chat-protocol.test.ts +0 -516
- package/src/chat-protocol/__tests__/env-flag.test.ts +0 -52
- package/src/chat-protocol/__tests__/ip-normalize.test.ts +0 -136
- package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +0 -232
- package/src/chat-protocol/decode.ts +0 -278
- package/src/chat-protocol/encode.ts +0 -71
- package/src/chat-protocol/env-flag.ts +0 -39
- package/src/chat-protocol/events.ts +0 -245
- package/src/chat-protocol/frames.ts +0 -245
- package/src/chat-protocol/index.ts +0 -21
- package/src/chat-protocol/ip-normalize.ts +0 -146
- package/src/chat-protocol/nats-decoder.ts +0 -258
- package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +0 -207
- package/src/components/chat/__tests__/chat-pending-turn.test.tsx +0 -230
- package/src/components/chat/hooks/__tests__/__snapshots__/conversation-id-persistence-golden.test.ts.snap +0 -37
- 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 +0 -438
- package/src/components/chat/hooks/__tests__/sse-stream-golden.test.ts +0 -318
- package/src/components/chat/stream/__tests__/__snapshots__/chat-stream-reducer-golden.test.ts.snap +0 -940
- package/src/components/chat/stream/__tests__/chat-dialog-store.test.ts +0 -756
- package/src/components/chat/stream/__tests__/chat-stream-reducer-golden.test.ts +0 -460
- package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +0 -973
- package/src/components/chat/stream/__tests__/delta-batcher.test.ts +0 -255
- package/src/components/chat/stream/__tests__/use-chat-stream-reducer.test.ts +0 -98
- package/src/components/chat/stream/chat-dialog-store.ts +0 -536
- package/src/components/chat/stream/chat-stream-reducer.ts +0 -1808
- package/src/components/chat/stream/delta-batcher.ts +0 -168
- package/src/components/chat/stream/index.ts +0 -55
- package/src/components/chat/stream/message-mutations.ts +0 -340
- package/src/components/chat/stream/use-chat-stream-reducer.ts +0 -126
- package/src/components/chat/utils/__tests__/__snapshots__/process-historical-messages-golden.test.ts.snap +0 -420
- package/src/components/chat/utils/__tests__/__snapshots__/segment-accumulator-golden.test.ts.snap +0 -605
- package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +0 -394
- package/src/components/chat/utils/__tests__/segment-accumulator-golden.test.ts +0 -270
- package/src/components/ui/__tests__/__snapshots__/markdown-parity.test.tsx.snap +0 -2420
- package/src/components/ui/__tests__/markdown-parity.test.tsx +0 -2528
- package/src/components/ui/markdown/__tests__/mermaid-security.test.ts +0 -115
- package/src/components/ui/markdown/__tests__/mermaid-stale-render.test.tsx +0 -95
- package/src/components/ui/markdown/__tests__/sanitize-invariant.test.ts +0 -309
- package/src/components/ui/markdown/__tests__/streaming.test.tsx +0 -388
- package/src/components/ui/markdown/base-components.tsx +0 -360
- package/src/components/ui/markdown/engine.tsx +0 -315
- package/src/components/ui/markdown/heading-ids.ts +0 -239
- package/src/components/ui/markdown/index.ts +0 -49
- package/src/components/ui/markdown/mermaid-diagram.tsx +0 -291
- package/src/components/ui/markdown/rich/embed-overrides.tsx +0 -199
- package/src/components/ui/markdown/rich/rich-markdown-renderer.tsx +0 -184
- package/src/components/ui/markdown/rich/shortcodes.ts +0 -170
- package/src/components/ui/markdown/sanitize.ts +0 -3499
- package/src/components/ui/markdown/simple-markdown-renderer.tsx +0 -28
- package/src/components/ui/markdown/streaming.ts +0 -362
- package/src/components/ui/markdown/text-size.ts +0 -106
- package/src/utils/markdown-fences.ts +0 -127
- package/src/utils/markdown-heading-id.ts +0 -348
- /package/dist/{chunk-PTXZB2KW.js.map → chunk-4J3CB4KO.js.map} +0 -0
- /package/dist/{chunk-XOFE2RT4.js.map → chunk-BTIRYGDT.js.map} +0 -0
- /package/dist/{chunk-PLB6CQSO.js.map → chunk-FG5ALORQ.js.map} +0 -0
- /package/dist/{chunk-NCAN6VUF.js.map → chunk-P3ITKTUE.js.map} +0 -0
- /package/dist/{chunk-34TDA22K.js.map → chunk-QHRNQTW5.js.map} +0 -0
- /package/dist/{chunk-OL3MJKUI.js.map → chunk-SZWMJIH3.js.map} +0 -0
- /package/dist/{chunk-MZRIRQYV.js.map → chunk-U5LU56JE.js.map} +0 -0
- /package/dist/{chunk-3LKB722U.js.map → chunk-WKWZ7LVC.js.map} +0 -0
- /package/dist/{chunk-J3VBVR4W.js.map → chunk-WPFLV4QJ.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-W56JYDSC.cjs","../src/components/onboarding-guides/onboarding-guides-catalog-view.tsx","../src/components/onboarding-guides/onboarding-guides-catalog-skeleton.tsx","../src/components/onboarding-guides/onboarding-guide-detail-view.tsx","../src/components/onboarding-guides/hooks/use-onboarding-guides.ts"],"names":["jsx","jsxs"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-W56JYDSC.cjs","sourcesContent":[null,"'use client'\n\n/**\n * Public-facing catalog view for `/onboarding-guides`.\n *\n * Two data modes:\n * - **controlled** (hub SSR): pass `initialGuides` + `initialSections`\n * (server-fetched; section changes re-render the host with new props).\n * - **self-fetching** (config-only embed): omit the data props and pass\n * `guidesEndpoint` + `sectionsEndpoint` (the api routes). The view fetches\n * the guides (refetching on `?section=` change) + the section list itself —\n * no host data layer. (Plain fetch + useEffect, the DeliveryLists pattern.)\n *\n * The view renders ONLY its body + its OWN controls (the RAG `<DocSearchBar>` +\n * dynamic section pills — onboarding's controls are content-driven, unlike the\n * static search/filter the `DevSectionView` chrome ships). It does NOT render the\n * page chrome: the CALLER wraps it in `<DevSectionPage sectionKey=\"onboarding\">`\n * (hero + back button), exactly like the roadmap / releases / delivery pages — so\n * every dev-center surface composes the same way and the view stays embeddable in\n * tabbed contexts. Card hrefs flow through `runtime.composeContentUrl`.\n */\n\nimport { useMemo, useTransition, type ReactNode } from 'react'\nimport { GraduationCap } from 'lucide-react'\n\nimport { useRouter, useSearchParams } from '../../embed-shims'\nimport { useSelfFetch } from '../../hooks/use-self-fetch'\nimport { DocSearchBar, useDocSearch } from '../shared/doc-search'\nimport { FilterPillRow } from '../ui/filter-pill-row'\nimport { LoadError } from '../ui/error-state'\nimport { OnboardingGuideCard } from '../chat/entity-cards/onboarding-guide-card'\nimport { OnboardingGuidesCatalogSkeleton } from './onboarding-guides-catalog-skeleton'\nimport { useChatRuntime } from '../../contexts/chat-runtime-context'\nimport type { OnboardingGuide } from '../chat/types/entities/onboarding-guide'\nimport { resolveContentHref } from '../../utils/content-href'\n\ntype SectionSummary = { section: string; section_order: number; count: number }\n\nexport interface OnboardingGuidesCatalogViewProps {\n /** Controlled / SSR: server-fetched guides. Omit + pass `guidesEndpoint`\n * for the self-fetching config-only mode. */\n initialGuides?: OnboardingGuide[]\n initialSections?: SectionSummary[]\n initialSection?: string\n /** Self-fetch: GET list endpoint (the api route). Appends `?section=`. */\n guidesEndpoint?: string\n /** Self-fetch: GET section-summary endpoint (the api route). */\n sectionsEndpoint?: string\n /** Optional per-row card renderer override. */\n renderCard?: (guide: OnboardingGuide) => ReactNode\n /** Base path the catalog is mounted under (fallback href prefix + `?section=`\n * push target). Default `/onboarding-guides`. */\n basePath?: string\n}\n\nexport function OnboardingGuidesCatalogView({\n initialGuides,\n initialSections,\n initialSection = '',\n guidesEndpoint,\n sectionsEndpoint,\n renderCard,\n basePath = '/onboarding-guides',\n}: OnboardingGuidesCatalogViewProps) {\n const router = useRouter()\n const searchParams = useSearchParams()\n const [isPending, startTransition] = useTransition()\n const runtime = useChatRuntime()\n\n // Self-fetch only when the host supplied NO data but DID give an endpoint.\n const selfFetch = initialGuides === undefined && guidesEndpoint !== undefined\n // Active section: from the URL when self-fetching, from the prop (host RSC\n // already read the URL server-side) when controlled.\n const sectionValue = selfFetch ? searchParams.get('section') ?? '' : initialSection\n const activeSection = sectionValue || 'all'\n\n // Self-fetch the guides (refetched whenever `?section=` changes — it's folded\n // into the url) + the section summaries. Controlled mode passes `null` urls so\n // the hook never fetches. The guides fetch carries the error/retry affordance.\n const guidesQs = sectionValue ? `?section=${encodeURIComponent(sectionValue)}` : ''\n const guidesRes = useSelfFetch<{ data?: OnboardingGuide[] }>(\n selfFetch && guidesEndpoint ? `${guidesEndpoint}${guidesQs}` : null,\n )\n const sectionsRes = useSelfFetch<SectionSummary[]>(\n selfFetch && sectionsEndpoint ? sectionsEndpoint : null,\n )\n\n // Invariant: `guidesRes.data` is `null` until the first fetch resolves, then\n // `{ data: [...] }`. The `?.data ?? []` chain collapses never-fetched (null) and\n // legitimately-empty ([]) to the same `[]` — so the first-load skeleton gate below\n // keys on the OUTER `guidesRes.data == null` to distinguish them (an empty section\n // toggle keeps the chrome; only a true cold start shows the full-page skeleton).\n const guides = initialGuides ?? guidesRes.data?.data ?? []\n const sections = initialSections ?? (Array.isArray(sectionsRes.data) ? sectionsRes.data : [])\n const isLoading = selfFetch ? guidesRes.isLoading : false\n\n // Section grouping. Data arrives already filtered (server-side `?section=`\n // for the host, or our `?section=` fetch); this just buckets visible rows.\n const grouped = useMemo(() => {\n const map = new Map<string, { section_order: number; guides: OnboardingGuide[] }>()\n for (const g of guides) {\n const existing = map.get(g.section)\n if (existing) {\n if (g.section_order < existing.section_order) existing.section_order = g.section_order\n existing.guides.push(g)\n } else {\n map.set(g.section, { section_order: g.section_order, guides: [g] })\n }\n }\n for (const entry of map.values()) {\n entry.guides.sort((a, b) => a.step_order - b.step_order || a.title.localeCompare(b.title))\n }\n return Array.from(map.entries())\n .map(([section, info]) => ({ section, ...info }))\n .sort((a, b) => a.section_order - b.section_order || a.section.localeCompare(b.section))\n }, [guides])\n\n const sectionFilterOptions = useMemo(\n () => [\n { value: 'all', label: `All (${guides.length})` },\n ...sections.map((s) => ({ value: s.section, label: `${s.section} (${s.count})` })),\n ],\n [guides.length, sections],\n )\n\n // Section pill change → push `?section=X`. Controlled mode: host RSC\n // re-fetches. Self-fetch mode: our guides effect re-fetches on the URL change.\n const setSection = (value: string) => {\n const params = new URLSearchParams(searchParams.toString())\n if (value === 'all') {\n params.delete('section')\n } else {\n params.set('section', value)\n }\n const qs = params.toString()\n startTransition(() => {\n router.push(qs ? `${basePath}?${qs}` : basePath)\n })\n }\n\n // Search bar — scoped to onboarding-guides only via RAG-search `tableIds`.\n const source = runtime?.source ?? 'openframe'\n const docSearch = useDocSearch({\n source,\n baseRoute: basePath,\n onNavigate: (path) => router.push(path),\n tableIds: ['onboarding-guides'],\n })\n\n // Per-row card renderer — runtime-composed href, fallback to relative.\n const defaultRenderCard = (guide: OnboardingGuide) => {\n const cta = resolveContentHref(runtime?.composeContentUrl, {\n type: 'onboarding_guide',\n slug: guide.slug,\n basePath,\n platforms: guide.onboarding_guide_platforms,\n })\n return (\n <OnboardingGuideCard\n guide={guide}\n href={cta.href}\n targetPlatform={cta.targetPlatform}\n size=\"catalog\"\n />\n )\n }\n const renderCardFn = renderCard ?? defaultRenderCard\n\n const preControls = (\n <div className=\"space-y-4\">\n <DocSearchBar\n placeholder=\"Search onboarding guides, releases, case studies…\"\n query={docSearch.query}\n onQueryChange={docSearch.setQuery}\n results={docSearch.results}\n isLoading={docSearch.isLoading}\n onResultSelect={docSearch.handleResultSelect}\n showDropdown={docSearch.keepDropdownOpen}\n />\n {sections.length > 0 && (\n <FilterPillRow\n label=\"Section\"\n selectedValue={activeSection}\n onValueChange={setSection}\n options={sectionFilterOptions}\n />\n )}\n </div>\n )\n\n // A failed guides fetch → a RETRYABLE error (not the indistinguishable-from-\n // empty \"no guides found\" state) — parity with the sibling self-fetch views.\n if (selfFetch && guidesRes.error) {\n return <LoadError message=\"Failed to load onboarding guides.\" onRetry={guidesRes.reload} />\n }\n // Full-page skeleton ONLY on the very first load (no data has loaded yet) — a\n // later section toggle that legitimately returns ZERO guides keeps the page\n // chrome (search + section pills) and dims the grid via the className below.\n if (selfFetch && guidesRes.data == null && isLoading) {\n return <OnboardingGuidesCatalogSkeleton />\n }\n\n return (\n <div className=\"w-full flex flex-col gap-10\">\n {preControls}\n {guides.length === 0 ? (\n <div className=\"text-center py-16\">\n <GraduationCap className=\"h-12 w-12 text-ods-text-secondary mx-auto mb-4\" />\n <h2 className=\"text-ods-text-primary text-h3 mb-2\">\n No onboarding guides found\n </h2>\n <p className=\"text-ods-text-secondary text-h6\">\n {activeSection !== 'all'\n ? 'No guides in this section yet.'\n : \"We're working on the onboarding library. Check back soon.\"}\n </p>\n </div>\n ) : (\n <div className={isPending || isLoading ? 'opacity-60 transition-opacity space-y-10' : 'space-y-10'}>\n {grouped.map((sec) => (\n <section key={sec.section} className=\"space-y-4\">\n <h2 className=\"text-h3 text-ods-text-primary flex items-center gap-2\">\n {sec.section}\n <span className=\"inline-flex items-center justify-center rounded-full bg-ods-text-secondary/20 text-ods-text-secondary text-h6 px-2 py-0.5\">\n {sec.guides.length}\n </span>\n </h2>\n {/* HORIZONTAL catalog list — single column so consecutive steps\n read top-to-bottom; a grid would visually reorder them. */}\n <ul className=\"flex flex-col gap-4\">\n {sec.guides.map((guide) => (\n <li key={guide.id}>{renderCardFn(guide)}</li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n )}\n </div>\n )\n}\n","'use client'\n\nimport { OnboardingGuideCardSkeleton } from '../chat/entity-cards/onboarding-guide-card'\n\n/**\n * Loading skeleton for `/onboarding-guides` — CHROME-LESS, exactly like the\n * loaded `<OnboardingGuidesCatalogView>`. The HOST page owns the\n * `<DevSectionPage sectionKey=\"onboarding\">` shell (hero + back button);\n * this component renders only what replaces the view: the search/pill\n * placeholder block and the section card lists.\n *\n * HISTORY: this used to mount its own `<DevSectionPage>` — nested inside the\n * page's shell that double-rendered the hero + back button during loading\n * (the view was made chrome-less in the page restructure; the skeleton was\n * missed). Keep BOTH chrome-less.\n *\n * Wrapper mirrors the loaded view's `w-full flex flex-col gap-10`; per-card\n * height matches the loaded catalog card so resolve shifts are zero.\n */\nexport function OnboardingGuidesCatalogSkeleton() {\n return (\n <div className=\"w-full flex flex-col gap-10 animate-pulse\">\n {/* Search input placeholder — matches `<SearchInput>` h-12 — plus the\n section pill row (~74 px incl. padding), same as the loaded\n preControls block. */}\n <div className=\"space-y-4\">\n <div className=\"h-12 w-full bg-ods-card border border-ods-border rounded-md\" />\n <div className=\"flex flex-wrap items-center gap-3 p-4 bg-ods-card border border-ods-border rounded-lg\">\n <div className=\"h-4 w-14 bg-ods-border/60 rounded\" />\n {[0, 1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-10 w-24 bg-ods-card border border-ods-border rounded-md\"\n />\n ))}\n </div>\n </div>\n <div className=\"space-y-10\">\n {[4, 3, 3].map((cardCount, sectionIdx) => (\n <section key={sectionIdx} className=\"space-y-4\">\n <h2 className=\"text-h3 tracking-[-0.36px] text-ods-text-primary flex items-center gap-2\">\n <span className=\"h-6 w-40 bg-ods-border/70 rounded\" />\n <span className=\"h-5 w-8 bg-ods-text-secondary/20 rounded-full\" />\n </h2>\n <ul className=\"flex flex-col gap-4\">\n {Array.from({ length: cardCount }).map((_, cardIdx) => (\n <li key={cardIdx}>\n <OnboardingGuideCardSkeleton size=\"catalog\" />\n </li>\n ))}\n </ul>\n </section>\n ))}\n </div>\n </div>\n )\n}\n","'use client'\n\n/**\n * Public-facing detail view for `/onboarding-guides/<slug>`.\n *\n * Two modes:\n * - **controlled** (hub SSR): pass `initialData` (a server-fetched guide).\n * - **self-fetching** (config-only embed): pass `slug` + `guideEndpoint`\n * (the api route) and the view fetches the guide itself — no host data\n * layer. (Plain fetch + useEffect, the DeliveryLists pattern.)\n *\n * Everything else flows through lib primitives + the ChatRuntime context\n * (markdown, video warmup, captions, related-card hrefs via\n * `runtime.composeContentUrl`). Optional `MarkdownRenderer` lets hosts swap a\n * renderer with extra plugins.\n */\n\nimport { Fragment, type ComponentType, type ReactNode } from 'react'\nimport { PageLayout } from '../layout/page-layout'\n\nimport { useRouter } from '../../embed-shims'\n// PageShell (max-w-[1920px]) — the guide detail content must share the SAME\n// container sizing as the related-content/FAQ rail the host page renders\n// below it (which uses the wide shell). ArticleDetailLayout (1280px) made\n// the detail block visibly narrower than the rail.\nimport { PageShell } from '../layout/article-detail-layout'\nimport { DetailPageSkeleton } from '../shared/detail-page-skeleton'\nimport { EntityVideoSection } from '../features/entity-video-section'\nimport { VideoBitesStrip } from '../features/video-bites-strip'\nimport { toStripProfile } from '../features/video-bites-shared'\nimport { useVideoWarmup } from '../features/use-video-warmup'\nimport { getCaptionsUrl } from '../features/captions-url'\nimport { RichMarkdownRenderer } from '../ui/markdown'\nimport { EntityTagBadges } from '../features/entity-tag-badges'\nimport { LoadError } from '../ui/error-state'\nimport { ArticleAuthorByline } from '../shared/article-author-byline'\nimport { EntityAuthorCard } from '../chat/entity-cards/entity-author-card'\nimport { OnboardingGuideCard } from '../chat/entity-cards/onboarding-guide-card'\nimport { useChatRuntime } from '../../contexts/chat-runtime-context'\nimport type { OnboardingGuide } from '../chat/types/entities/onboarding-guide'\nimport type { VideoTeaser } from '../../types/video-processing'\nimport { resolveContentHref } from '../../utils/content-href'\nimport { buildOgPlaceholderUrl } from '../../utils/og-placeholder'\nimport { useSelfFetch } from '../../hooks/use-self-fetch'\n\nexport interface OnboardingGuideDetailViewProps {\n /** Server-fetched guide (controlled / SSR mode). Omit it and pass\n * `slug` + `guideEndpoint` for the self-fetching config-only mode. */\n initialData?: OnboardingGuide\n /** Self-fetch: the guide's slug (the host reads it from its route). */\n slug?: string\n /** Self-fetch: builds the GET url for a single guide (the api route).\n * e.g. `(s) => \\`/content/api/onboarding-guides/${s}\\``. */\n guideEndpoint?: (slug: string) => string\n related?: OnboardingGuide[]\n /** Link target for the author name in the metadata grid — the host\n * computes it (public author page; absent ⇒ plain text). */\n authorHref?: string\n /** Bio paragraph for the end-of-article author byline. Defaults to the\n * guide payload's `author.about` (hubs that hydrate it can omit this). */\n authorBio?: string | null\n /** Byline fallback paragraph when the bio is empty — the host passes its\n * platform-aware copy (the lib has no config awareness). Absent ⇒ the\n * byline renders nothing below the name when the bio is empty. */\n fallbackBio?: string | null\n /** Optional markdown renderer override. Defaults to lib\n * `<RichMarkdownRenderer>`. */\n MarkdownRenderer?: ComponentType<{ content: string }>\n /** Optional per-row related-card renderer override. */\n renderRelatedCard?: (guide: OnboardingGuide) => ReactNode\n /** Back-link target. Defaults to `basePath`. */\n backHref?: string\n /** Back-link label. Defaults to \"Back to Getting Started\". */\n backLabel?: string\n /** Base path the related-card hrefs default to when\n * `runtime.composeContentUrl` is not wired. Default `/onboarding-guides`. */\n basePath?: string\n /** Optional slot rendered inside the page chrome, BELOW the article + related\n * guides — e.g. the hub's cross-type related-content / FAQ rail. Lets the hub\n * mount this view directly (no local wrapper); embedders omit it. */\n relatedContent?: ReactNode\n /** Render the standalone `<PageShell>`. Default true. Pass false when the host\n * layout already provides the page container — only the padding box renders,\n * avoiding a nested `<main>`. */\n shell?: boolean\n}\n\nexport function OnboardingGuideDetailView({\n authorHref,\n authorBio,\n fallbackBio,\n initialData,\n slug,\n guideEndpoint,\n related = [],\n MarkdownRenderer = RichMarkdownRenderer,\n renderRelatedCard,\n backHref,\n backLabel = 'Back to Getting Started',\n basePath = '/onboarding-guides',\n relatedContent,\n shell = true,\n}: OnboardingGuideDetailViewProps) {\n const resolvedBackHref = backHref ?? basePath\n const runtime = useChatRuntime()\n const router = useRouter()\n // `shell` true → standalone `<PageShell>`; false → padding-only box (no nested\n // <main>) for hosts whose layout already provides the container.\n const renderShell = (node: ReactNode) =>\n shell ? <PageShell>{node}</PageShell> : <div className=\"page-shell-content\">{node}</div>\n\n // Controlled (hub SSR `initialData`) OR self-fetch by slug (config-only embed).\n const url = initialData ? null : slug && guideEndpoint ? guideEndpoint(slug) : null\n const { data: guide, isLoading, error, reload } = useSelfFetch<OnboardingGuide>(url, { initialData })\n\n // Hooks must run unconditionally — feed them optional-chained values so they\n // no-op while the guide is still loading.\n const { ref: videoWarmupRef } = useVideoWarmup<HTMLDivElement>({\n videoUrl: guide?.main_video_url,\n supabaseStorageOrigin: runtime?.endpoints.supabaseStorageOrigin,\n })\n\n if (error || (!guide && !isLoading)) {\n return renderShell(<LoadError message=\"Guide not found.\" onRetry={reload} />)\n }\n if (!guide) {\n // Skeleton (not a bare \"Loading…\") for parity with every other shared view —\n // catalog, roadmap, releases all render a skeleton on first load, so the detail\n // page shouldn't flash text then content. `bare` + `PageShell` so the loading\n // state matches the loaded page's full width / padding / min-height.\n return renderShell(\n // Match the loaded page's top offset (TitleBlock's\n // `pt-[var(--spacing-system-l)]`) so content doesn't jump on load.\n <div className=\"pt-[var(--spacing-system-l)]\">\n <DetailPageSkeleton bare showImageGallery={false} />\n </div>\n )\n }\n\n const captionsUrl = getCaptionsUrl('onboarding_guide', guide.id, guide.srt_content)\n\n const videoPoster =\n guide.main_video_thumbnail ||\n guide.featured_image ||\n guide.og_image_url ||\n // `buildOgPlaceholderUrl` always returns a usable string (relative route at\n // worst), so it's the terminal fallback — no trailing `|| undefined` needed.\n buildOgPlaceholderUrl(runtime?.endpoints, guide.title, { aspect: 'wide' })\n\n const defaultRenderRelatedCard = (g: OnboardingGuide) => {\n const cta = resolveContentHref(runtime?.composeContentUrl, {\n type: 'onboarding_guide',\n slug: g.slug,\n basePath,\n platforms: g.onboarding_guide_platforms,\n })\n return <OnboardingGuideCard guide={g} href={cta.href} targetPlatform={cta.targetPlatform} />\n }\n const renderRelatedCardFn = renderRelatedCard ?? defaultRenderRelatedCard\n\n return renderShell(\n <PageLayout\n title={guide.title}\n titleSize=\"h1\"\n titleWrap\n backButton={{ label: backLabel, onClick: () => router.push(resolvedBackHref) }}\n >\n {/* Tags — flat onboarding_guide_tags[] from entity_tags. */}\n <EntityTagBadges tags={(guide as any).onboarding_guide_tags} />\n\n {/* Metadata grid — Section · Step | Published | Author. */}\n <EntityAuthorCard\n author={guide.author}\n authorHref={authorHref}\n publishedAt={guide.published_at}\n extraCells={[\n {\n value: `${guide.section} · Step ${guide.step_order}`,\n label: 'Section',\n uppercase: false,\n },\n ]}\n />\n\n {/* Video. `main_video_url` (Mux/MP4) and `youtube_url` are independent\n columns — either populated renders the player. */}\n {(guide.main_video_url || guide.youtube_url) && (\n <div ref={videoWarmupRef}>\n <EntityVideoSection\n mainVideoUrl={guide.main_video_url}\n youtubeUrl={guide.youtube_url || undefined}\n highlightVideoUrl={guide.highlight_video_url}\n mainVideoPoster={videoPoster}\n highlightVideoThumbnail={guide.highlight_video_thumbnail || undefined}\n videoSummary={undefined}\n videoBites={undefined}\n title={guide.title}\n srtContent={guide.srt_content}\n captionsUrl={captionsUrl}\n MarkdownRenderer={MarkdownRenderer}\n />\n </div>\n )}\n\n {/* Markdown body */}\n {guide.content && (\n <div className=\"space-y-4\">\n <MarkdownRenderer content={guide.content} />\n </div>\n )}\n\n {/* Video Bites — unified strip; renders nothing when no published bites. */}\n <VideoBitesStrip\n bites={(guide.video_bites as VideoTeaser[] | null) ?? []}\n filterPublished={true}\n showTitle={false}\n profile={toStripProfile(guide.author)}\n href={`/onboarding-guides/${guide.slug}`}\n />\n\n {/* End-of-article author byline (avatar + linked name + bio) — the\n author DESCRIPTION block every article-shaped detail page renders.\n Hidden when the guide has no author (the byline returns null). */}\n <ArticleAuthorByline\n author={guide.author?.full_name ?? null}\n avatar={guide.author?.avatar_url}\n jobTitle={guide.author?.job_title}\n bio={authorBio ?? guide.author?.about}\n href={authorHref}\n fallbackBio={fallbackBio}\n />\n\n {/* Related — same-section, ordered by step. */}\n {related.length > 0 && (\n <div className=\"space-y-4 pt-8 border-t border-ods-border\">\n <h2 className=\"text-h3 tracking-[-0.36px] text-ods-text-primary\">\n More in {guide.section}\n </h2>\n <ul className=\"flex flex-col gap-3\">\n {related.map((r) => (\n <li key={r.id}>{renderRelatedCardFn(r)}</li>\n ))}\n </ul>\n </div>\n )}\n\n {/*\n * Host slot — cross-type related-content / FAQ rail.\n *\n * KEYED DELIBERATELY. This is one member of PageLayout's static children\n * array, and the only member whose element may be created in a SERVER\n * component and crossed over the RSC boundary. Static JSX children are\n * normally exempt from the key warning because `jsxs` runs\n * `validateChildKeys` and stamps `_store.validated = 1` on each element —\n * but a slot arriving over the wire is referenced as a lazy chunk wrapper,\n * so validation marks the WRAPPER and the fulfilled path never copies the\n * flag onto the revived element. It reaches the reconciler with\n * `validated: 0, key: null` and warns, blaming PageLayout (the owner of the\n * enclosing element) rather than anything actually at fault.\n *\n * A keyed Fragment emits no DOM, so rendered output is byte-identical.\n * Do NOT \"fix\" this in PageLayout via `React.Children.toArray` — that would\n * auto-key every consumer's children and change reconciliation identity for\n * all of them, and that file carries an explicit FROZEN contract.\n */}\n <Fragment key=\"related-content\">{relatedContent}</Fragment>\n </PageLayout>\n )\n}\n","'use client'\n\n/**\n * React Query hooks for Onboarding Guides — PUBLIC reads only.\n *\n * Admin mutations (create/update/delete/publish/stats/save-highlight)\n * stay HUB-side in `hooks/use-onboarding-guides.ts` and import the\n * `onboardingGuideKeys.admin` sub-namespace from this module so the\n * cache namespace `['onboarding-guides']` has a single source of truth.\n *\n * Endpoints (`/api/onboarding-guides*`) are host-supplied — non-Next.js\n * embedders must reverse-proxy the same routes. Same precedent as the\n * tickets list hook.\n */\n\nimport { useQuery } from '@tanstack/react-query'\n\nimport type {\n OnboardingGuide,\n OnboardingGuideFilters,\n OnboardingGuideListResponse,\n OnboardingGuideSectionSummary,\n} from '../../chat/types/entities/onboarding-guide'\n\n/**\n * Cache key builder for the `['onboarding-guides']` namespace.\n *\n * Includes BOTH public-read sub-keys (`lists`, `list`, `details`,\n * `detail`, `sections`) AND the `admin` sub-namespace. Admin mutations\n * live hub-side but invalidate against this same builder so a single\n * `qc.invalidateQueries({ queryKey: onboardingGuideKeys.all })` from\n * an admin hook also clears the public read cache.\n */\nexport const onboardingGuideKeys = {\n all: ['onboarding-guides'] as const,\n lists: () => [...onboardingGuideKeys.all, 'list'] as const,\n list: (filters: OnboardingGuideFilters) =>\n [...onboardingGuideKeys.lists(), filters] as const,\n details: () => [...onboardingGuideKeys.all, 'detail'] as const,\n detail: (slug: string) => [...onboardingGuideKeys.details(), slug] as const,\n sections: () => [...onboardingGuideKeys.all, 'sections'] as const,\n admin: {\n all: ['admin', 'onboarding-guides'] as const,\n lists: () => ['admin', 'onboarding-guides', 'list'] as const,\n detail: (slug: string) =>\n ['admin', 'onboarding-guide', slug] as const,\n stats: () => ['admin', 'onboarding-guides', 'stats'] as const,\n },\n}\n\nexport function useOnboardingGuides(filters?: OnboardingGuideFilters) {\n return useQuery({\n queryKey: onboardingGuideKeys.list(filters || {}),\n queryFn: async (): Promise<OnboardingGuideListResponse> => {\n const params = new URLSearchParams()\n if (filters?.search) params.set('search', filters.search)\n if (filters?.section) params.set('section', filters.section)\n if (filters?.limit) params.set('limit', filters.limit.toString())\n if (filters?.offset) params.set('offset', filters.offset.toString())\n const res = await fetch(`/api/onboarding-guides?${params}`)\n if (!res.ok) throw new Error('Failed to fetch onboarding guides')\n return res.json()\n },\n })\n}\n\nexport function useOnboardingGuide(slug: string | undefined) {\n return useQuery({\n queryKey: onboardingGuideKeys.detail(slug || ''),\n queryFn: async (): Promise<OnboardingGuide> => {\n const res = await fetch(`/api/onboarding-guides/${slug}`)\n if (!res.ok) throw new Error('Failed to fetch onboarding guide')\n return res.json()\n },\n enabled: !!slug,\n })\n}\n\nexport function useOnboardingGuideSections() {\n return useQuery({\n queryKey: onboardingGuideKeys.sections(),\n queryFn: async (): Promise<OnboardingGuideSectionSummary[]> => {\n const res = await fetch('/api/onboarding-guides/sections')\n if (!res.ok)\n throw new Error('Failed to fetch onboarding-guide sections')\n return res.json()\n },\n staleTime: 0,\n })\n}\n"]}
|
|
@@ -1,184 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,367 +0,0 @@
|
|
|
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
|