@flamingo-stack/openframe-frontend-core 0.0.587-1973.5528.1 → 0.0.587-2000.5532.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/events.d.ts +2 -8
- package/dist/chat-protocol/events.d.ts.map +1 -1
- package/dist/chat-protocol/index.cjs +0 -188
- package/dist/chat-protocol/index.cjs.map +1 -1
- package/dist/chat-protocol/index.js +0 -188
- package/dist/chat-protocol/index.js.map +1 -1
- package/dist/chat-protocol/nats-decoder.d.ts.map +1 -1
- package/dist/{chunk-5VCDP6UA.cjs → chunk-3FHWLQDG.cjs} +10 -10
- package/dist/{chunk-5VCDP6UA.cjs.map → chunk-3FHWLQDG.cjs.map} +1 -1
- package/dist/{chunk-RE3KVI2Q.js → chunk-3PDTGDOA.js} +5 -5
- package/dist/{chunk-5X7AJ67N.js → chunk-436G4DI4.js} +3 -3
- package/dist/{chunk-BLC4JOFC.cjs → chunk-4IIFSFJ3.cjs} +15 -15
- package/dist/{chunk-BLC4JOFC.cjs.map → chunk-4IIFSFJ3.cjs.map} +1 -1
- package/dist/{chunk-JR37UTA2.cjs → chunk-5O5JMGEN.cjs} +33 -33
- package/dist/{chunk-JR37UTA2.cjs.map → chunk-5O5JMGEN.cjs.map} +1 -1
- package/dist/{chunk-7A6J4JTW.js → chunk-6BFEJXMI.js} +3 -3
- package/dist/{chunk-S2UY2C4A.cjs → chunk-7WIKOL25.cjs} +10 -10
- package/dist/{chunk-S2UY2C4A.cjs.map → chunk-7WIKOL25.cjs.map} +1 -1
- package/dist/{chunk-DVMQV3M4.cjs → chunk-A5PXHMCJ.cjs} +69 -69
- package/dist/{chunk-DVMQV3M4.cjs.map → chunk-A5PXHMCJ.cjs.map} +1 -1
- package/dist/{chunk-RQ5PYSRJ.js → chunk-BLJEPNMJ.js} +4 -4
- package/dist/{chunk-4MVGFYRE.js → chunk-D7YISHXC.js} +190 -534
- package/dist/chunk-D7YISHXC.js.map +1 -0
- package/dist/{chunk-4S7GMJYA.cjs → chunk-DMQMRFGS.cjs} +21 -21
- package/dist/{chunk-4S7GMJYA.cjs.map → chunk-DMQMRFGS.cjs.map} +1 -1
- package/dist/{chunk-AIUIIGO3.cjs → chunk-DTQCZ6AR.cjs} +24 -24
- package/dist/{chunk-AIUIIGO3.cjs.map → chunk-DTQCZ6AR.cjs.map} +1 -1
- package/dist/{chunk-QKAUBYU5.cjs → chunk-E7NFHRLX.cjs} +3 -3
- package/dist/{chunk-QKAUBYU5.cjs.map → chunk-E7NFHRLX.cjs.map} +1 -1
- package/dist/{chunk-WFWWQO2I.js → chunk-F3YRPKUA.js} +2 -2
- package/dist/{chunk-EKKR5ZFB.cjs → chunk-G45RGWHF.cjs} +543 -887
- package/dist/chunk-G45RGWHF.cjs.map +1 -0
- package/dist/{chunk-ZRFSUCAH.js → chunk-HCWZP44O.js} +5 -3
- package/dist/{chunk-ZRFSUCAH.js.map → chunk-HCWZP44O.js.map} +1 -1
- package/dist/{chunk-NEI7XMCM.cjs → chunk-IGBNEPTD.cjs} +9 -9
- package/dist/{chunk-NEI7XMCM.cjs.map → chunk-IGBNEPTD.cjs.map} +1 -1
- package/dist/{chunk-3UJMZADA.cjs → chunk-KQ5JQVYL.cjs} +43 -43
- package/dist/{chunk-3UJMZADA.cjs.map → chunk-KQ5JQVYL.cjs.map} +1 -1
- package/dist/{chunk-FDWGO244.js → chunk-LODCKZ72.js} +2 -2
- package/dist/{chunk-ULOMJH7C.cjs → chunk-MF7TMFVB.cjs} +5 -5
- package/dist/{chunk-ULOMJH7C.cjs.map → chunk-MF7TMFVB.cjs.map} +1 -1
- package/dist/{chunk-DSXD77KX.cjs → chunk-MXCWOMNT.cjs} +3 -3
- package/dist/{chunk-DSXD77KX.cjs.map → chunk-MXCWOMNT.cjs.map} +1 -1
- package/dist/{chunk-E2QFZDCP.js → chunk-OKD7MX7Y.js} +5 -5
- package/dist/{chunk-OW3FUJ2I.js → chunk-P24OI2CD.js} +3 -3
- package/dist/{chunk-QSO24MEK.js → chunk-R2U2D7LU.js} +2 -2
- package/dist/{chunk-KQWI25NP.js → chunk-SI57FQZ2.js} +2 -2
- package/dist/{chunk-JQO47UHC.js → chunk-SVR75PFW.js} +5 -5
- package/dist/{chunk-UYHETUX3.cjs → chunk-UBSIENYF.cjs} +162 -160
- package/dist/chunk-UBSIENYF.cjs.map +1 -0
- package/dist/{chunk-OZRT3ZI3.js → chunk-V4WUSOGS.js} +3 -3
- package/dist/{chunk-UODM3HJC.js → chunk-VWNU2PFX.js} +3 -3
- package/dist/{chunk-SCVIBVFS.cjs → chunk-WQXWH7LF.cjs} +4 -4
- package/dist/{chunk-SCVIBVFS.cjs.map → chunk-WQXWH7LF.cjs.map} +1 -1
- package/dist/{chunk-7COFWOCJ.cjs → chunk-XNEPABUD.cjs} +22 -22
- package/dist/{chunk-7COFWOCJ.cjs.map → chunk-XNEPABUD.cjs.map} +1 -1
- package/dist/{chunk-QYDIVL4X.js → chunk-YB5DFQKD.js} +6 -6
- package/dist/components/case-studies/index.cjs +9 -9
- package/dist/components/case-studies/index.js +3 -3
- 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/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +1 -4
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-slash-commands.d.ts +3 -3
- package/dist/components/chat/hooks/use-slash-commands.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +28 -2
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/chat/remark-strip-citations.d.ts.map +1 -1
- package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -1
- package/dist/components/chat/tool-execution-display.d.ts.map +1 -1
- package/dist/components/chat/types/component.types.d.ts +0 -4
- package/dist/components/chat/types/component.types.d.ts.map +1 -1
- package/dist/components/chat/types/message.types.d.ts +1 -35
- package/dist/components/chat/types/message.types.d.ts.map +1 -1
- package/dist/components/chat/types/unified-chat-state.types.d.ts +3 -3
- package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
- package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +4 -4
- package/dist/components/contact/index.js +3 -3
- package/dist/components/docs/index.cjs +7 -7
- package/dist/components/docs/index.js +6 -6
- package/dist/components/embeds/index.cjs +4 -4
- package/dist/components/embeds/index.js +3 -3
- package/dist/components/faq/index.cjs +4 -4
- package/dist/components/faq/index.js +3 -3
- package/dist/components/features/index.cjs +3 -3
- package/dist/components/features/index.js +2 -2
- 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 +36 -36
- package/dist/components/index.js +13 -13
- package/dist/components/meeting-scheduler/index.cjs +21 -21
- package/dist/components/meeting-scheduler/index.js +2 -2
- package/dist/components/navigation/index.cjs +6 -6
- package/dist/components/navigation/index.js +5 -5
- package/dist/components/onboarding-guides/index.cjs +6 -6
- package/dist/components/onboarding-guides/index.js +5 -5
- package/dist/components/tickets/index.cjs +7 -7
- package/dist/components/tickets/index.js +6 -6
- package/dist/components/ui/data-table/use-data-table.d.ts.map +1 -1
- package/dist/components/ui/hover-dropdown.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +3 -3
- package/dist/components/ui/index.js +2 -2
- package/dist/index.cjs +7 -7
- package/dist/index.js +6 -6
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -2
- package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +0 -130
- package/src/chat-protocol/events.ts +1 -9
- package/src/chat-protocol/nats-decoder.ts +0 -7
- package/src/components/chat/__tests__/chat-message-list.test.tsx +0 -70
- package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +0 -86
- package/src/components/chat/chat-message-enhanced.tsx +13 -28
- package/src/components/chat/chat-message-list.tsx +22 -27
- package/src/components/chat/embeddable-chat.tsx +36 -55
- package/src/components/chat/entity-cards/dispatch.tsx +17 -86
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +0 -7
- package/src/components/chat/hooks/use-slash-commands.ts +5 -9
- package/src/components/chat/hooks/use-sse-chat-adapter.ts +29 -2
- package/src/components/chat/remark-strip-citations.ts +3 -5
- package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +0 -56
- package/src/components/chat/stream/chat-stream-reducer.ts +0 -31
- package/src/components/chat/tool-execution-display.tsx +2 -5
- package/src/components/chat/types/component.types.ts +0 -4
- package/src/components/chat/types/message.types.ts +0 -39
- package/src/components/chat/types/unified-chat-state.types.ts +3 -4
- package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +0 -57
- package/src/components/chat/utils/process-historical-messages.ts +1 -19
- package/src/components/ui/__tests__/data-table-compiler-identity.test.tsx +74 -0
- package/src/components/ui/data-table/use-data-table.ts +36 -1
- package/src/components/ui/hover-dropdown.tsx +3 -8
- package/dist/chat-protocol/source-metadata.d.ts +0 -5
- package/dist/chat-protocol/source-metadata.d.ts.map +0 -1
- package/dist/chunk-4MVGFYRE.js.map +0 -1
- package/dist/chunk-EKKR5ZFB.cjs.map +0 -1
- package/dist/chunk-UYHETUX3.cjs.map +0 -1
- package/dist/components/chat/utils/chat-source-metadata.d.ts +0 -5
- package/dist/components/chat/utils/chat-source-metadata.d.ts.map +0 -1
- package/src/chat-protocol/__tests__/source-metadata.test.ts +0 -74
- package/src/chat-protocol/source-metadata.ts +0 -202
- package/src/components/chat/__tests__/embeddable-chat-sources.test.tsx +0 -411
- package/src/components/chat/__tests__/remark-strip-citations.test.ts +0 -52
- package/src/components/chat/__tests__/tool-execution-display.test.tsx +0 -93
- package/src/components/chat/entity-cards/__tests__/dispatch-video-refs.test.tsx +0 -74
- package/src/components/chat/hooks/__tests__/use-slash-commands.test.ts +0 -64
- package/src/components/chat/utils/chat-source-metadata.ts +0 -19
- /package/dist/{chunk-RE3KVI2Q.js.map → chunk-3PDTGDOA.js.map} +0 -0
- /package/dist/{chunk-5X7AJ67N.js.map → chunk-436G4DI4.js.map} +0 -0
- /package/dist/{chunk-7A6J4JTW.js.map → chunk-6BFEJXMI.js.map} +0 -0
- /package/dist/{chunk-RQ5PYSRJ.js.map → chunk-BLJEPNMJ.js.map} +0 -0
- /package/dist/{chunk-WFWWQO2I.js.map → chunk-F3YRPKUA.js.map} +0 -0
- /package/dist/{chunk-FDWGO244.js.map → chunk-LODCKZ72.js.map} +0 -0
- /package/dist/{chunk-E2QFZDCP.js.map → chunk-OKD7MX7Y.js.map} +0 -0
- /package/dist/{chunk-OW3FUJ2I.js.map → chunk-P24OI2CD.js.map} +0 -0
- /package/dist/{chunk-QSO24MEK.js.map → chunk-R2U2D7LU.js.map} +0 -0
- /package/dist/{chunk-KQWI25NP.js.map → chunk-SI57FQZ2.js.map} +0 -0
- /package/dist/{chunk-JQO47UHC.js.map → chunk-SVR75PFW.js.map} +0 -0
- /package/dist/{chunk-OZRT3ZI3.js.map → chunk-V4WUSOGS.js.map} +0 -0
- /package/dist/{chunk-UODM3HJC.js.map → chunk-VWNU2PFX.js.map} +0 -0
- /package/dist/{chunk-QYDIVL4X.js.map → chunk-YB5DFQKD.js.map} +0 -0
|
@@ -808,14 +808,13 @@ function SourceChips({
|
|
|
808
808
|
const [expanded, setExpanded] = useState(false);
|
|
809
809
|
|
|
810
810
|
if (cited.length === 0) {
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
if (visible.length === 0) return null;
|
|
811
|
+
const fallback = uncited.slice(0, FALLBACK_TOP_RETRIEVED);
|
|
812
|
+
if (fallback.length === 0) return null;
|
|
814
813
|
return (
|
|
815
814
|
<div className="mt-2 flex flex-col gap-1.5 border-t border-ods-border pt-2">
|
|
816
815
|
<span className="uppercase text-ods-text-muted text-h6">Top retrieved sources</span>
|
|
817
|
-
<div className=
|
|
818
|
-
{
|
|
816
|
+
<div className="flex flex-wrap gap-1.5">
|
|
817
|
+
{fallback.map(src => (
|
|
819
818
|
<SourceChip
|
|
820
819
|
key={src.index}
|
|
821
820
|
src={src}
|
|
@@ -825,16 +824,6 @@ function SourceChips({
|
|
|
825
824
|
onDiscuss={onDiscuss}
|
|
826
825
|
/>
|
|
827
826
|
))}
|
|
828
|
-
{hiddenCount > 0 && (
|
|
829
|
-
<button
|
|
830
|
-
onClick={() => setExpanded(!expanded)}
|
|
831
|
-
className="inline-flex cursor-pointer items-center gap-1 rounded border border-ods-accent bg-ods-card px-2 py-0.5 text-ods-accent transition-colors text-h6 hover:bg-ods-accent/10"
|
|
832
|
-
aria-expanded={expanded}
|
|
833
|
-
aria-label={expanded ? 'Show fewer sources' : `Show ${hiddenCount} additional retrieved sources`}
|
|
834
|
-
>
|
|
835
|
-
{expanded ? 'Show less' : `+${hiddenCount} more retrieved ${hiddenCount === 1 ? 'source' : 'sources'}`}
|
|
836
|
-
</button>
|
|
837
|
-
)}
|
|
838
827
|
</div>
|
|
839
828
|
</div>
|
|
840
829
|
);
|
|
@@ -1590,10 +1579,6 @@ function EmbeddableChatInner({
|
|
|
1590
1579
|
// explicitly — see `Message.hidden` and `chat-message-list`'s skip.
|
|
1591
1580
|
...(m.hidden ? { hidden: true } : {}),
|
|
1592
1581
|
...(m.scrollAnchor ? { scrollAnchor: m.scrollAnchor } : {}),
|
|
1593
|
-
// Preserve same-turn rich metadata. Source chips and entity-card
|
|
1594
|
-
// renderers consume it after this raw-to-Message projection.
|
|
1595
|
-
...(m.sources && m.sources.length > 0 ? { sources: m.sources } : {}),
|
|
1596
|
-
...(m.refs && m.refs.length > 0 ? { refs: m.refs } : {}),
|
|
1597
1582
|
// Forward attached context items so the user bubble renders its chips.
|
|
1598
1583
|
...(m.contextItems && m.contextItems.length > 0 ? { contextItems: m.contextItems } : {}),
|
|
1599
1584
|
};
|
|
@@ -1844,41 +1829,24 @@ function EmbeddableChatInner({
|
|
|
1844
1829
|
return out;
|
|
1845
1830
|
}, [commandsById, enabledSet]);
|
|
1846
1831
|
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
.sort((a, b) => (seenOrder.get(a.index) ?? 0) - (seenOrder.get(b.index) ?? 0));
|
|
1866
|
-
const uncited = message.sources.filter(s => !seenOrder.has(s.index));
|
|
1867
|
-
return (
|
|
1868
|
-
<div className="flex-shrink-0 pb-2">
|
|
1869
|
-
<SourceChips
|
|
1870
|
-
cited={cited}
|
|
1871
|
-
uncited={uncited}
|
|
1872
|
-
baseRoute={resolvedBaseRoute}
|
|
1873
|
-
chipBasePlatform={chipBasePlatform}
|
|
1874
|
-
onClose={handleNavigationClose}
|
|
1875
|
-
onDiscuss={discussRef}
|
|
1876
|
-
/>
|
|
1877
|
-
</div>
|
|
1878
|
-
);
|
|
1879
|
-
},
|
|
1880
|
-
[chatLoading, messages.length, resolvedBaseRoute, chipBasePlatform, handleNavigationClose, discussRef],
|
|
1881
|
-
);
|
|
1832
|
+
// Find sources for the last assistant message; split into cited / uncited.
|
|
1833
|
+
const lastAssistantMsg = [...rawMessages].reverse().find(m => m.role === 'assistant');
|
|
1834
|
+
const lastSources = useMemo(() => {
|
|
1835
|
+
if (chatLoading) return undefined;
|
|
1836
|
+
const sources = lastAssistantMsg?.sources;
|
|
1837
|
+
if (!sources || sources.length === 0) return undefined;
|
|
1838
|
+
const content = lastAssistantMsg?.content || '';
|
|
1839
|
+
const citationOrder = [...content.matchAll(/\[(\d+)\]/g)].map(m => parseInt(m[1], 10));
|
|
1840
|
+
const seenOrder = new Map<number, number>();
|
|
1841
|
+
citationOrder.forEach(idx => {
|
|
1842
|
+
if (!seenOrder.has(idx)) seenOrder.set(idx, seenOrder.size);
|
|
1843
|
+
});
|
|
1844
|
+
const cited = sources
|
|
1845
|
+
.filter(s => seenOrder.has(s.index))
|
|
1846
|
+
.sort((a, b) => (seenOrder.get(a.index) ?? 0) - (seenOrder.get(b.index) ?? 0));
|
|
1847
|
+
const uncited = sources.filter(s => !seenOrder.has(s.index));
|
|
1848
|
+
return { cited, uncited };
|
|
1849
|
+
}, [lastAssistantMsg, chatLoading]);
|
|
1882
1850
|
|
|
1883
1851
|
// Host node for in-panel Radix portals (see the body wrapper below).
|
|
1884
1852
|
const [portalHost, setPortalHost] = useState<HTMLDivElement | null>(null);
|
|
@@ -2422,7 +2390,6 @@ function EmbeddableChatInner({
|
|
|
2422
2390
|
resolveContextIcon={resolveContextIcon}
|
|
2423
2391
|
renderContextItem={renderContextItem}
|
|
2424
2392
|
renderMention={renderMention}
|
|
2425
|
-
renderAfterMessage={renderSourcesAfterMessage}
|
|
2426
2393
|
// Gated on `chatLoading` for the same reason the composer
|
|
2427
2394
|
// is: no second send while a turn is in flight. Passive
|
|
2428
2395
|
// demo hosts (previewMode) stay read-only.
|
|
@@ -2460,6 +2427,20 @@ function EmbeddableChatInner({
|
|
|
2460
2427
|
onLoadMore={loadMoreMessages}
|
|
2461
2428
|
/>
|
|
2462
2429
|
)}
|
|
2430
|
+
{lastSources &&
|
|
2431
|
+
(lastSources.cited.length > 0 || lastSources.uncited.length > 0) &&
|
|
2432
|
+
!chatLoading && (
|
|
2433
|
+
<div className="flex-shrink-0 pb-2">
|
|
2434
|
+
<SourceChips
|
|
2435
|
+
cited={lastSources.cited}
|
|
2436
|
+
uncited={lastSources.uncited}
|
|
2437
|
+
baseRoute={resolvedBaseRoute}
|
|
2438
|
+
chipBasePlatform={chipBasePlatform}
|
|
2439
|
+
onClose={handleNavigationClose}
|
|
2440
|
+
onDiscuss={discussRef}
|
|
2441
|
+
/>
|
|
2442
|
+
</div>
|
|
2443
|
+
)}
|
|
2463
2444
|
</div>
|
|
2464
2445
|
) : activeMode === 'mingo' ? (
|
|
2465
2446
|
/* Figma node 7532:222444 — default (Mingo-mode) empty state:
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
import React, { type ReactNode } from 'react';
|
|
33
|
-
import { isSourceMetadataCardRef } from '../../../chat-protocol/source-metadata';
|
|
34
33
|
import { useRequiredChatRuntime } from '../../../contexts/chat-runtime-context';
|
|
35
34
|
import Image from '../../../embed-shims/next-image';
|
|
36
35
|
import { useRouter } from '../../../embed-shims/next-navigation';
|
|
@@ -42,7 +41,6 @@ import {
|
|
|
42
41
|
formatTimeWithTimezone,
|
|
43
42
|
formatDurationFromRange,
|
|
44
43
|
} from '../../../utils/format';
|
|
45
|
-
import { extractYouTubeId } from '../../features/video';
|
|
46
44
|
import { MingoIcon } from '../../icons';
|
|
47
45
|
import { ArrowRightUpIcon } from '../../icons-v2-generated/arrows/arrow-right-up-icon';
|
|
48
46
|
import { ClickupLogoIcon } from '../../icons-v2-generated/brand-logos/clickup-logo-icon';
|
|
@@ -424,15 +422,6 @@ function hubspotStatusToVariant(status: string | undefined): MingoInfoCardStatus
|
|
|
424
422
|
* which renders the marker as plain text (same anti-hallucination
|
|
425
423
|
* posture as every other card type).
|
|
426
424
|
*/
|
|
427
|
-
function validatedHttpsUrl(value: unknown): string | null {
|
|
428
|
-
if (typeof value !== 'string') return null;
|
|
429
|
-
try {
|
|
430
|
-
return new URL(value).protocol === 'https:' ? value : null;
|
|
431
|
-
} catch {
|
|
432
|
-
return null;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
425
|
function decodeVideoMarkerId(id: string): { videoUrl?: string; youtubeUrl?: string } | null {
|
|
437
426
|
// Prefix checks FIRST: an id like `mux-1234567` (11 chars) would also
|
|
438
427
|
// satisfy the bare-YouTube-id pattern below.
|
|
@@ -450,8 +439,8 @@ function decodeVideoMarkerId(id: string): { videoUrl?: string; youtubeUrl?: stri
|
|
|
450
439
|
const url = atob(b64);
|
|
451
440
|
// https-only — the id is model-emitted text; never let a marker
|
|
452
441
|
// smuggle a non-https src into a player.
|
|
453
|
-
|
|
454
|
-
return
|
|
442
|
+
if (!/^https:\/\//.test(url)) return null;
|
|
443
|
+
return { videoUrl: url };
|
|
455
444
|
} catch {
|
|
456
445
|
return null;
|
|
457
446
|
}
|
|
@@ -460,29 +449,6 @@ function decodeVideoMarkerId(id: string): { videoUrl?: string; youtubeUrl?: stri
|
|
|
460
449
|
return null;
|
|
461
450
|
}
|
|
462
451
|
|
|
463
|
-
function validatedVideoMetadata(chatRef: ChatRef): Record<string, unknown> | null {
|
|
464
|
-
const input = chatRef.metadata ?? {};
|
|
465
|
-
const metadata: Record<string, unknown> = {};
|
|
466
|
-
for (const key of ['videoUrl', 'highlightVideoUrl', 'videoPoster', 'highlightVideoPoster'] as const) {
|
|
467
|
-
const url = validatedHttpsUrl(input[key]);
|
|
468
|
-
if (url) metadata[key] = url;
|
|
469
|
-
}
|
|
470
|
-
const youtubeUrl = input.youtubeUrl;
|
|
471
|
-
if (typeof youtubeUrl === 'string' && /^[A-Za-z0-9_-]{11}$/.test(youtubeUrl)) {
|
|
472
|
-
metadata.youtubeUrl = youtubeUrl;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const safeUrl = validatedHttpsUrl(chatRef.url);
|
|
476
|
-
if (safeUrl) {
|
|
477
|
-
const youtubeId = extractYouTubeId(safeUrl);
|
|
478
|
-
delete metadata.videoUrl;
|
|
479
|
-
delete metadata.youtubeUrl;
|
|
480
|
-
metadata[youtubeId ? 'youtubeUrl' : 'videoUrl'] = youtubeId ?? safeUrl;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
return Object.keys(metadata).length > 0 ? metadata : null;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
452
|
/** Read hero-video metadata off a FETCHED item — the API-driven
|
|
487
453
|
* replacement for the retired refs-frame `metadata.videoUrl` check. Two item
|
|
488
454
|
* shapes: ChatRef-shaped items (per-object hydration routes) carry the
|
|
@@ -1262,10 +1228,6 @@ interface ChatCardRegistryEntry {
|
|
|
1262
1228
|
* point — a host that re-homes a type must be able to say so even when
|
|
1263
1229
|
* the row already carries the content host's own url. */
|
|
1264
1230
|
noComposedHref?: boolean;
|
|
1265
|
-
/** Render a document card from same-turn SOURCES metadata when its optional
|
|
1266
|
-
* hydration endpoint has no row. Only doc refs opt in: their source record
|
|
1267
|
-
* already carries the complete title/path/navigation fallback. */
|
|
1268
|
-
renderFromSourceMetadata?: boolean;
|
|
1269
1231
|
}
|
|
1270
1232
|
|
|
1271
1233
|
/**
|
|
@@ -1288,7 +1250,6 @@ function refHydratedEntry(
|
|
|
1288
1250
|
docType: string,
|
|
1289
1251
|
label: string,
|
|
1290
1252
|
renderRef: (displayRef: ChatRef, opts: ChatCardRenderOptions) => React.ReactNode,
|
|
1291
|
-
renderFromSourceMetadata = false,
|
|
1292
1253
|
): ChatCardRegistryEntry {
|
|
1293
1254
|
return {
|
|
1294
1255
|
label,
|
|
@@ -1298,19 +1259,6 @@ function refHydratedEntry(
|
|
|
1298
1259
|
fallbackHref: (item: { url?: string | null }) => item?.url ?? null,
|
|
1299
1260
|
skeleton: () => <MingoInfoCardSkeleton />,
|
|
1300
1261
|
render: (item, chatRef, opts) => renderRef(fetchedItemDisplayRef(item, chatRef), opts),
|
|
1301
|
-
renderFromSourceMetadata,
|
|
1302
|
-
};
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
function sourceMetadataCardItem(chatRef: ChatRef): ChatCardItem {
|
|
1306
|
-
return {
|
|
1307
|
-
type: chatRef.type,
|
|
1308
|
-
id: chatRef.id,
|
|
1309
|
-
title: chatRef.title,
|
|
1310
|
-
url: chatRef.url,
|
|
1311
|
-
targetPlatform: chatRef.targetPlatform,
|
|
1312
|
-
sourceRepo: chatRef.sourceRepo,
|
|
1313
|
-
metadata: chatRef.metadata,
|
|
1314
1262
|
};
|
|
1315
1263
|
}
|
|
1316
1264
|
|
|
@@ -1506,18 +1454,12 @@ const CHAT_CARD_REGISTRY: Record<string, ChatCardRegistryEntry> = {
|
|
|
1506
1454
|
hubspot_ticket_self: refHydratedEntry('hubspot_ticket_self', 'HubSpot ticket (self)', (displayRef, opts) => (
|
|
1507
1455
|
<HubspotTicketChatCard chatRef={displayRef} isNewTab={opts.isNewTab} discuss={opts.discuss} />
|
|
1508
1456
|
)),
|
|
1509
|
-
data_room_doc: refHydratedEntry(
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
),
|
|
1515
|
-
markdown: refHydratedEntry(
|
|
1516
|
-
'markdown',
|
|
1517
|
-
'Doc page (markdown)',
|
|
1518
|
-
(displayRef, opts) => <DataRoomDocChatCard chatRef={displayRef} isNewTab={opts.isNewTab} discuss={opts.discuss} />,
|
|
1519
|
-
true,
|
|
1520
|
-
),
|
|
1457
|
+
data_room_doc: refHydratedEntry('data_room_doc', 'Data-room doc', (displayRef, opts) => (
|
|
1458
|
+
<DataRoomDocChatCard chatRef={displayRef} isNewTab={opts.isNewTab} discuss={opts.discuss} />
|
|
1459
|
+
)),
|
|
1460
|
+
markdown: refHydratedEntry('markdown', 'Doc page (markdown)', (displayRef, opts) => (
|
|
1461
|
+
<DataRoomDocChatCard chatRef={displayRef} isNewTab={opts.isNewTab} discuss={opts.discuss} />
|
|
1462
|
+
)),
|
|
1521
1463
|
// Body-synthesized video ids (`shortVideoId` content hashes / YouTube
|
|
1522
1464
|
// ids) have no backing table row to fetch — ref-only by nature.
|
|
1523
1465
|
video: {
|
|
@@ -1960,13 +1902,6 @@ export function ChatCardLoader({
|
|
|
1960
1902
|
}
|
|
1961
1903
|
if (isLoading) return <>{fetchEntry.skeleton?.() ?? null}</>;
|
|
1962
1904
|
if (!item) {
|
|
1963
|
-
const hasSourceMetadata =
|
|
1964
|
-
fetchEntry.renderFromSourceMetadata &&
|
|
1965
|
-
isSourceMetadataCardRef(chatRef) &&
|
|
1966
|
-
typeof finalChatRef.metadata?.path === 'string';
|
|
1967
|
-
if (hasSourceMetadata) {
|
|
1968
|
-
return finish(fetchEntry.render(sourceMetadataCardItem(finalChatRef), finalChatRef, renderOpts));
|
|
1969
|
-
}
|
|
1970
1905
|
// FAIL LOUD (explicit product decision 2026-08-13): a card that
|
|
1971
1906
|
// cannot hydrate renders a VISIBLE error card — silent removal made
|
|
1972
1907
|
// broken answers look fine. Three distinguishable states:
|
|
@@ -2048,28 +1983,24 @@ export function renderChatInlineEntityCard(
|
|
|
2048
1983
|
/>
|
|
2049
1984
|
);
|
|
2050
1985
|
|
|
2051
|
-
//
|
|
2052
|
-
//
|
|
2053
|
-
//
|
|
2054
|
-
// through to the loader
|
|
1986
|
+
// Embedded videos are SELF-DESCRIBING: the marker id alone recovers the
|
|
1987
|
+
// playable URL (`decodeVideoMarkerId`), so the player renders on ANY
|
|
1988
|
+
// transport with zero refs/fetch. Undecodable ids (legacy sha1 hashes,
|
|
1989
|
+
// hallucinations) fall through to the loader → plain-text degrade.
|
|
2055
1990
|
// Hero videos on FETCHED entity types (webinars, releases, …) are
|
|
2056
1991
|
// handled post-fetch inside ChatCardLoader (`itemVideoMetadata`) — the
|
|
2057
|
-
// marker
|
|
1992
|
+
// marker's ref plays no part in either path.
|
|
2058
1993
|
if (chatRef.type === 'video') {
|
|
2059
|
-
const enrichedMetadata = validatedVideoMetadata(chatRef);
|
|
2060
1994
|
const decoded = decodeVideoMarkerId(chatRef.id);
|
|
2061
|
-
|
|
2062
|
-
if (playableMetadata) {
|
|
1995
|
+
if (decoded) {
|
|
2063
1996
|
const displayRef: ChatRef = {
|
|
2064
1997
|
...chatRef,
|
|
2065
1998
|
title: chatRef.title && chatRef.title !== chatRef.id ? chatRef.title : 'Video',
|
|
2066
1999
|
url:
|
|
2067
2000
|
chatRef.url ??
|
|
2068
|
-
|
|
2069
|
-
(
|
|
2070
|
-
|
|
2071
|
-
: null),
|
|
2072
|
-
metadata: playableMetadata,
|
|
2001
|
+
decoded.videoUrl ??
|
|
2002
|
+
(decoded.youtubeUrl ? `https://www.youtube.com/watch?v=${decoded.youtubeUrl}` : null),
|
|
2003
|
+
metadata: { ...(chatRef.metadata ?? {}), ...decoded },
|
|
2073
2004
|
};
|
|
2074
2005
|
return (
|
|
2075
2006
|
<BlockCard inline={<ChatInlineVideoPill chatRef={displayRef} />}>
|
|
@@ -61,7 +61,6 @@ import type {
|
|
|
61
61
|
MessageSegment,
|
|
62
62
|
NatsMessageType,
|
|
63
63
|
ChatApprovalStatus,
|
|
64
|
-
ChatSource,
|
|
65
64
|
} from '../types';
|
|
66
65
|
import type { DialogItem } from '../types/component.types';
|
|
67
66
|
import type {
|
|
@@ -353,8 +352,6 @@ export function mapProcessedToUnified(
|
|
|
353
352
|
role: 'user' | 'assistant' | 'error';
|
|
354
353
|
content: string | MessageSegment[];
|
|
355
354
|
name?: string;
|
|
356
|
-
sources?: ChatSource[];
|
|
357
|
-
refs?: ChatRef[];
|
|
358
355
|
}>,
|
|
359
356
|
): UnifiedChatMessage[] {
|
|
360
357
|
const out: UnifiedChatMessage[] = [];
|
|
@@ -366,16 +363,12 @@ export function mapProcessedToUnified(
|
|
|
366
363
|
role: m.role,
|
|
367
364
|
content: '',
|
|
368
365
|
segments: m.content,
|
|
369
|
-
...(m.sources ? { sources: m.sources } : {}),
|
|
370
|
-
...(m.refs ? { refs: m.refs } : {}),
|
|
371
366
|
});
|
|
372
367
|
} else {
|
|
373
368
|
out.push({
|
|
374
369
|
id: m.id,
|
|
375
370
|
role: m.role,
|
|
376
371
|
content: m.content,
|
|
377
|
-
...(m.sources ? { sources: m.sources } : {}),
|
|
378
|
-
...(m.refs ? { refs: m.refs } : {}),
|
|
379
372
|
});
|
|
380
373
|
}
|
|
381
374
|
}
|
|
@@ -24,12 +24,10 @@ import type { SlashCommandActionId, SlashCommandSummaryAction, SlashCommandSumma
|
|
|
24
24
|
// canonical declaration.
|
|
25
25
|
export type { SlashCommandActionId, SlashCommandSummaryAction, SlashCommandSummary };
|
|
26
26
|
|
|
27
|
-
const COMMANDS_MAX_RESULTS = 10;
|
|
28
|
-
|
|
29
27
|
/**
|
|
30
|
-
* Fetch
|
|
31
|
-
*
|
|
32
|
-
*
|
|
28
|
+
* Fetch slash-command suggestions for the CURRENT deployment platform,
|
|
29
|
+
* matching id-prefix `prefix`. The chat source is server-resolved — the
|
|
30
|
+
* client sends NO `source` param on the URL.
|
|
33
31
|
*
|
|
34
32
|
* `commandsUrl` is required — supplied by `useChatRuntime().endpoints.commandsUrl`
|
|
35
33
|
* at the call site. NO default value.
|
|
@@ -58,15 +56,13 @@ export async function fetchSlashCommands(
|
|
|
58
56
|
): Promise<SlashCommandSummary[]> {
|
|
59
57
|
try {
|
|
60
58
|
const url = new URL(commandsUrl, window.location.origin);
|
|
59
|
+
if (prefix) url.searchParams.set('q', prefix);
|
|
61
60
|
// `headers: {}` opts out of the default `Content-Type: application/json`
|
|
62
61
|
// — this is a bare GET with no body, so no content-type is needed.
|
|
63
62
|
const res = await embedAuthedFetch(url.toString(), { signal, headers: {} });
|
|
64
63
|
if (!res.ok) return [];
|
|
65
64
|
const data = (await res.json()) as { commands?: SlashCommandSummary[] };
|
|
66
|
-
|
|
67
|
-
const normalizedPrefix = prefix.trim().toLowerCase();
|
|
68
|
-
if (!normalizedPrefix) return commands;
|
|
69
|
-
return commands.filter(({ id }) => id.startsWith(normalizedPrefix)).slice(0, COMMANDS_MAX_RESULTS);
|
|
65
|
+
return data.commands ?? [];
|
|
70
66
|
} catch (err) {
|
|
71
67
|
// Cancellation (unmount / dep change) MUST propagate so react-query treats
|
|
72
68
|
// it as cancelled, not as a successful empty result. Every OTHER failure
|
|
@@ -63,7 +63,7 @@ import type { ChatDialogStore } from '../stream/chat-dialog-store';
|
|
|
63
63
|
import type { ChatStreamReducerOptions } from '../stream/chat-stream-reducer';
|
|
64
64
|
import { useChatStreamReducer } from '../stream/use-chat-stream-reducer';
|
|
65
65
|
import type { DialogItem } from '../types/component.types';
|
|
66
|
-
import type {
|
|
66
|
+
import type { Message, MessageSegment } from '../types/message.types';
|
|
67
67
|
import type {
|
|
68
68
|
UnifiedChatState,
|
|
69
69
|
UnifiedChatMessage,
|
|
@@ -81,7 +81,6 @@ import { useSlashCommandRegistry, type SlashCommandSummary } from './use-slash-c
|
|
|
81
81
|
// Canonical home of the per-turn meta row moved to the stream module in
|
|
82
82
|
// Phase 3; re-exported here to keep the legacy import path stable.
|
|
83
83
|
export type { ChatTurnMeta } from '../stream/chat-stream-reducer';
|
|
84
|
-
export type { ChatSource } from '../types/message.types';
|
|
85
84
|
|
|
86
85
|
// ─── Stable no-op references for the Guide-mode dialog-management stubs ──
|
|
87
86
|
// Plain module-scope constants so the adapter's return identity stays
|
|
@@ -129,6 +128,34 @@ const noopBumpMetaTick = (): void => {};
|
|
|
129
128
|
* hub-side platform-utils, not in lib). */
|
|
130
129
|
export type DocSource = string;
|
|
131
130
|
|
|
131
|
+
export interface ChatSource {
|
|
132
|
+
index: number;
|
|
133
|
+
name: string;
|
|
134
|
+
path: string;
|
|
135
|
+
documentType: string;
|
|
136
|
+
externalUrl?: string;
|
|
137
|
+
/** Platform that owns the destination at `externalUrl`. */
|
|
138
|
+
targetPlatform?: string | null;
|
|
139
|
+
/** Primary-key value for single-row chips. */
|
|
140
|
+
id?: string;
|
|
141
|
+
/** Per-row items for grouped chips. */
|
|
142
|
+
items?: Array<{
|
|
143
|
+
id: string;
|
|
144
|
+
documentType: string;
|
|
145
|
+
name: string;
|
|
146
|
+
externalUrl?: string;
|
|
147
|
+
targetPlatform?: string | null;
|
|
148
|
+
/** In-app doc-tree path for markdown / data-room-doc rows so the
|
|
149
|
+
* grouped chip's anchor can trigger an in-page doc-tree swap via
|
|
150
|
+
* `handleChatNavClick` (parity with single-row chips + cards). */
|
|
151
|
+
path?: string | null;
|
|
152
|
+
}>;
|
|
153
|
+
/** RagTableConfig.id for this source. */
|
|
154
|
+
sourceRepo?: string;
|
|
155
|
+
/** Optional display label override returned by the chat API. */
|
|
156
|
+
label?: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
132
159
|
export interface DocChatMessage {
|
|
133
160
|
id: string;
|
|
134
161
|
role: 'user' | 'assistant';
|
|
@@ -22,16 +22,13 @@ import type { Plugin } from 'unified';
|
|
|
22
22
|
import { visit } from 'unist-util-visit';
|
|
23
23
|
|
|
24
24
|
const CITATION_REGEX = /(^|\s)\[\d{1,2}(?:\s*,\s*\d{1,2})*\](?=[\s.,;:!?)\]]|$)/g;
|
|
25
|
-
const CITATION_LIST_REGEX =
|
|
26
|
-
/(^|\s)(?:\[\d{1,2}(?:\s*,\s*\d{1,2})*\]\s*,\s*)+(?:(?:and|or)\s+)?\[\d{1,2}(?:\s*,\s*\d{1,2})*\](?=[\s.,;:!?)\]]|$)/g;
|
|
27
25
|
|
|
28
26
|
export const remarkStripCitations: Plugin<[], Root> = () => {
|
|
29
27
|
return (tree: Root) => {
|
|
30
28
|
visit(tree, 'text', (node: Text, index, parent) => {
|
|
31
29
|
if (!parent || typeof index !== 'number') return;
|
|
32
30
|
if (!node.value || !node.value.includes('[')) return;
|
|
33
|
-
const
|
|
34
|
-
const withoutCitations = withoutCitationLists.replace(CITATION_REGEX, '$1');
|
|
31
|
+
const withoutCitations = node.value.replace(CITATION_REGEX, '$1');
|
|
35
32
|
// No citation matched → leave the node untouched, INCLUDING its
|
|
36
33
|
// whitespace (collapsing unconditionally would rewrite deliberate
|
|
37
34
|
// spacing in prose like `array[1] value`).
|
|
@@ -44,7 +41,8 @@ export const remarkStripCitations: Plugin<[], Root> = () => {
|
|
|
44
41
|
// leaf and moves on to the next sibling, never re-entering this slot.
|
|
45
42
|
parent.children.splice(index, 1, {
|
|
46
43
|
...node,
|
|
47
|
-
|
|
44
|
+
// Collapse doubled spaces the removal can leave mid-sentence.
|
|
45
|
+
value: withoutCitations.replace(/ {2,}/g, ' '),
|
|
48
46
|
});
|
|
49
47
|
});
|
|
50
48
|
};
|
|
@@ -1289,59 +1289,3 @@ describe('createChatStreamReducer — ask cards', () => {
|
|
|
1289
1289
|
expect(last.segments?.map(s => s.type)).toEqual(['ask', 'ask']);
|
|
1290
1290
|
});
|
|
1291
1291
|
});
|
|
1292
|
-
|
|
1293
|
-
describe('createChatStreamReducer — Mingo source metadata', () => {
|
|
1294
|
-
const sources = [
|
|
1295
|
-
{
|
|
1296
|
-
index: 1,
|
|
1297
|
-
name: 'Enroll devices',
|
|
1298
|
-
path: 'docs/enroll.md',
|
|
1299
|
-
documentType: 'markdown',
|
|
1300
|
-
},
|
|
1301
|
-
];
|
|
1302
|
-
|
|
1303
|
-
it('attaches metadata received before text to the same assistant turn', () => {
|
|
1304
|
-
const reducer = createChatStreamReducer({ transport: 'nats' });
|
|
1305
|
-
const refs = [
|
|
1306
|
-
{
|
|
1307
|
-
type: 'video',
|
|
1308
|
-
id: 'mux-9b6586b494',
|
|
1309
|
-
title: 'Community demo',
|
|
1310
|
-
url: 'https://stream.mux.com/playback-id.m3u8',
|
|
1311
|
-
metadata: { videoUrl: 'https://stream.mux.com/playback-id.m3u8' },
|
|
1312
|
-
},
|
|
1313
|
-
];
|
|
1314
|
-
|
|
1315
|
-
reducer.apply({ type: 'turn-start', seq: 1 });
|
|
1316
|
-
reducer.apply({ type: 'sources', sources, refs, seq: 2 });
|
|
1317
|
-
reducer.apply({ type: 'text-delta', text: 'Follow the guide [1].', seq: 3 });
|
|
1318
|
-
|
|
1319
|
-
expect(reducer.state.messages).toHaveLength(1);
|
|
1320
|
-
expect(reducer.state.messages[0]).toMatchObject({ sources, refs });
|
|
1321
|
-
});
|
|
1322
|
-
|
|
1323
|
-
it('deduplicates cumulative metadata and does not leak it into the next turn', () => {
|
|
1324
|
-
const reducer = createChatStreamReducer({ transport: 'nats' });
|
|
1325
|
-
|
|
1326
|
-
reducer.apply({ type: 'turn-start', seq: 1 });
|
|
1327
|
-
reducer.apply({ type: 'sources', sources, seq: 2 });
|
|
1328
|
-
reducer.apply({
|
|
1329
|
-
type: 'sources',
|
|
1330
|
-
sources: [
|
|
1331
|
-
{ ...sources[0], name: 'Duplicate' },
|
|
1332
|
-
{ index: 2, name: 'Install agent', path: 'docs/install.md', documentType: 'markdown' },
|
|
1333
|
-
],
|
|
1334
|
-
seq: 3,
|
|
1335
|
-
});
|
|
1336
|
-
reducer.apply({ type: 'text-delta', text: 'First answer [1] [2].', seq: 4 });
|
|
1337
|
-
reducer.apply({ type: 'turn-end', seq: 5 });
|
|
1338
|
-
reducer.apply({ type: 'turn-start', seq: 6 });
|
|
1339
|
-
reducer.apply({ type: 'text-delta', text: 'Second answer.', seq: 7 });
|
|
1340
|
-
|
|
1341
|
-
expect(reducer.state.messages[0].sources).toEqual([
|
|
1342
|
-
sources[0],
|
|
1343
|
-
{ index: 2, name: 'Install agent', path: 'docs/install.md', documentType: 'markdown' },
|
|
1344
|
-
]);
|
|
1345
|
-
expect(reducer.state.messages[1].sources).toBeUndefined();
|
|
1346
|
-
});
|
|
1347
|
-
});
|
|
@@ -60,7 +60,6 @@ import {
|
|
|
60
60
|
type ParticipantEvent,
|
|
61
61
|
type UsageEvent,
|
|
62
62
|
} from '../../../chat-protocol/events';
|
|
63
|
-
import type { ChatRef } from '../chat-ref.types';
|
|
64
63
|
import type {
|
|
65
64
|
ApprovalBatchSegment,
|
|
66
65
|
ApprovalRequestSegment,
|
|
@@ -71,7 +70,6 @@ import type {
|
|
|
71
70
|
ToolExecutionSegment,
|
|
72
71
|
ExecutingToolState,
|
|
73
72
|
} from '../types';
|
|
74
|
-
import type { ChatSource } from '../types/message.types';
|
|
75
73
|
import type { PendingApproval } from '../types/processing.types';
|
|
76
74
|
import type {
|
|
77
75
|
DialogTokenUsage,
|
|
@@ -80,7 +78,6 @@ import type {
|
|
|
80
78
|
UnifiedUsageBreakdown,
|
|
81
79
|
} from '../types/unified-chat-state.types';
|
|
82
80
|
import { approvalDisplaysInline } from '../utils/approval-display';
|
|
83
|
-
import { mergeChatRefs, mergeChatSources } from '../utils/chat-source-metadata';
|
|
84
81
|
import {
|
|
85
82
|
type MessageSegmentAccumulator,
|
|
86
83
|
createMessageSegmentAccumulator,
|
|
@@ -679,9 +676,6 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
|
|
|
679
676
|
let sendCount = 0;
|
|
680
677
|
let sseCurrentText = '';
|
|
681
678
|
|
|
682
|
-
let pendingSources: ChatSource[] | undefined;
|
|
683
|
-
let pendingRefs: ChatRef[] | undefined;
|
|
684
|
-
|
|
685
679
|
// ── Snapshot cache ─────────────────────────────────────────────────────
|
|
686
680
|
let stateCache: ChatReducerState | null = null;
|
|
687
681
|
function invalidate(): void {
|
|
@@ -779,18 +773,6 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
|
|
|
779
773
|
setMessagesInternal(stampTrailingAssistantSeq(updateTrailingAssistant(messages, segments), seq));
|
|
780
774
|
}
|
|
781
775
|
|
|
782
|
-
function attachPendingSourceMetadata(): void {
|
|
783
|
-
if (!pendingSources && !pendingRefs) return;
|
|
784
|
-
const last = messages[messages.length - 1];
|
|
785
|
-
if (!last || last.role !== 'assistant') return;
|
|
786
|
-
const next = {
|
|
787
|
-
...last,
|
|
788
|
-
...(pendingSources ? { sources: pendingSources } : {}),
|
|
789
|
-
...(pendingRefs ? { refs: pendingRefs } : {}),
|
|
790
|
-
};
|
|
791
|
-
setMessagesInternal([...messages.slice(0, -1), next]);
|
|
792
|
-
}
|
|
793
|
-
|
|
794
776
|
function applyStreamStartToState(): void {
|
|
795
777
|
setPhaseInternal('streaming');
|
|
796
778
|
// A new stream must never overwrite a COMPLETED trailing assistant
|
|
@@ -984,8 +966,6 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
|
|
|
984
966
|
|
|
985
967
|
switch (event.type) {
|
|
986
968
|
case 'turn-start': {
|
|
987
|
-
pendingSources = undefined;
|
|
988
|
-
pendingRefs = undefined;
|
|
989
969
|
isInStream = true;
|
|
990
970
|
hasEverStreamed = true;
|
|
991
971
|
turnStartedAt = Date.now();
|
|
@@ -1001,15 +981,6 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
|
|
|
1001
981
|
emit('onStreamEnd');
|
|
1002
982
|
setPhaseInternal('idle');
|
|
1003
983
|
accumulator.resetSegments();
|
|
1004
|
-
pendingSources = undefined;
|
|
1005
|
-
pendingRefs = undefined;
|
|
1006
|
-
break;
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
case 'sources': {
|
|
1010
|
-
pendingSources = mergeChatSources(pendingSources, event.sources);
|
|
1011
|
-
if (event.refs) pendingRefs = mergeChatRefs(pendingRefs, event.refs);
|
|
1012
|
-
attachPendingSourceMetadata();
|
|
1013
984
|
break;
|
|
1014
985
|
}
|
|
1015
986
|
|
|
@@ -1046,12 +1017,10 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
|
|
|
1046
1017
|
if (isInStream || !hasEverStreamed) {
|
|
1047
1018
|
emitSegments(segments);
|
|
1048
1019
|
applySegmentsToState(segments, withSeqMeta(undefined));
|
|
1049
|
-
attachPendingSourceMetadata();
|
|
1050
1020
|
} else {
|
|
1051
1021
|
const delta: MessageSegment[] = [{ type: kind, text: event.text }];
|
|
1052
1022
|
emitSegments(delta, { append: true });
|
|
1053
1023
|
applySegmentsToState(delta, withSeqMeta({ append: true }));
|
|
1054
|
-
attachPendingSourceMetadata();
|
|
1055
1024
|
}
|
|
1056
1025
|
break;
|
|
1057
1026
|
}
|
|
@@ -54,9 +54,6 @@ const ToolExecutionDisplay = forwardRef<HTMLDivElement, ToolExecutionDisplayProp
|
|
|
54
54
|
|
|
55
55
|
const hasResult = isExecuted && typeof message.result === 'string' && message.result.length > 0;
|
|
56
56
|
const hasBody = argEntries.length > 0 || hasResult || isExecuting;
|
|
57
|
-
const isConciseRemoteRead =
|
|
58
|
-
!message.integratedToolType && !!message.toolTitle?.trim() && argEntries.length === 0 && !hasResult;
|
|
59
|
-
const isStaticRow = isClientView || isConciseRemoteRead;
|
|
60
57
|
|
|
61
58
|
const renderStatusIcon = () => {
|
|
62
59
|
if (isExecuting) return <DotsLoaderIcon size={16} className="text-ods-text-secondary" />;
|
|
@@ -85,7 +82,7 @@ const ToolExecutionDisplay = forwardRef<HTMLDivElement, ToolExecutionDisplayProp
|
|
|
85
82
|
{previewText}
|
|
86
83
|
</div>
|
|
87
84
|
<div className="flex h-5 w-5 shrink-0 items-center justify-center">{renderStatusIcon()}</div>
|
|
88
|
-
{!
|
|
85
|
+
{!isClientView && (
|
|
89
86
|
<div className="flex h-5 w-5 shrink-0 items-center justify-center">
|
|
90
87
|
<ExpandChevron expanded={expanded} />
|
|
91
88
|
</div>
|
|
@@ -104,7 +101,7 @@ const ToolExecutionDisplay = forwardRef<HTMLDivElement, ToolExecutionDisplayProp
|
|
|
104
101
|
)}
|
|
105
102
|
{...props}
|
|
106
103
|
>
|
|
107
|
-
{
|
|
104
|
+
{isClientView ? (
|
|
108
105
|
// Client (Fae end-user): static, non-expandable row — no chevron, no
|
|
109
106
|
// body. Just the explanation + status; command/args/result are hidden.
|
|
110
107
|
<div className="flex w-full items-start gap-[var(--spacing-system-xs)] p-[var(--spacing-system-s)] text-left">
|